summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--library/std/src/sys/common/alloc.rs54
-rw-r--r--library/std/src/sys/common/mod.rs13
-rw-r--r--library/std/src/sys/hermit/alloc.rs31
-rw-r--r--library/std/src/sys/hermit/args.rs94
-rw-r--r--library/std/src/sys/hermit/condvar.rs90
-rw-r--r--library/std/src/sys/hermit/env.rs9
-rw-r--r--library/std/src/sys/hermit/fd.rs87
-rw-r--r--library/std/src/sys/hermit/fs.rs408
-rw-r--r--library/std/src/sys/hermit/memchr.rs1
-rw-r--r--library/std/src/sys/hermit/mod.rs156
-rw-r--r--library/std/src/sys/hermit/mutex.rs216
-rw-r--r--library/std/src/sys/hermit/net.rs492
-rw-r--r--library/std/src/sys/hermit/os.rs178
-rw-r--r--library/std/src/sys/hermit/rwlock.rs144
-rw-r--r--library/std/src/sys/hermit/stdio.rs120
-rw-r--r--library/std/src/sys/hermit/thread.rs112
-rw-r--r--library/std/src/sys/hermit/thread_local_dtor.rs36
-rw-r--r--library/std/src/sys/hermit/time.rs156
-rw-r--r--library/std/src/sys/itron/abi.rs197
-rw-r--r--library/std/src/sys/itron/condvar.rs297
-rw-r--r--library/std/src/sys/itron/error.rs159
-rw-r--r--library/std/src/sys/itron/mutex.rs93
-rw-r--r--library/std/src/sys/itron/spin.rs163
-rw-r--r--library/std/src/sys/itron/task.rs44
-rw-r--r--library/std/src/sys/itron/thread.rs349
-rw-r--r--library/std/src/sys/itron/time.rs114
-rw-r--r--library/std/src/sys/itron/time/tests.rs33
-rw-r--r--library/std/src/sys/itron/wait_flag.rs72
-rw-r--r--library/std/src/sys/mod.rs78
-rw-r--r--library/std/src/sys/sgx/abi/entry.S372
-rw-r--r--library/std/src/sys/sgx/abi/mem.rs93
-rw-r--r--library/std/src/sys/sgx/abi/mod.rs108
-rw-r--r--library/std/src/sys/sgx/abi/panic.rs42
-rw-r--r--library/std/src/sys/sgx/abi/reloc.rs32
-rw-r--r--library/std/src/sys/sgx/abi/thread.rs13
-rw-r--r--library/std/src/sys/sgx/abi/tls/mod.rs132
-rw-r--r--library/std/src/sys/sgx/abi/tls/sync_bitset.rs85
-rw-r--r--library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs25
-rw-r--r--library/std/src/sys/sgx/abi/usercalls/alloc.rs732
-rw-r--r--library/std/src/sys/sgx/abi/usercalls/mod.rs323
-rw-r--r--library/std/src/sys/sgx/abi/usercalls/raw.rs251
-rw-r--r--library/std/src/sys/sgx/abi/usercalls/tests.rs30
-rw-r--r--library/std/src/sys/sgx/alloc.rs98
-rw-r--r--library/std/src/sys/sgx/args.rs59
-rw-r--r--library/std/src/sys/sgx/condvar.rs45
-rw-r--r--library/std/src/sys/sgx/env.rs9
-rw-r--r--library/std/src/sys/sgx/fd.rs84
-rw-r--r--library/std/src/sys/sgx/memchr.rs1
-rw-r--r--library/std/src/sys/sgx/mod.rs167
-rw-r--r--library/std/src/sys/sgx/mutex.rs62
-rw-r--r--library/std/src/sys/sgx/net.rs541
-rw-r--r--library/std/src/sys/sgx/os.rs140
-rw-r--r--library/std/src/sys/sgx/path.rs25
-rw-r--r--library/std/src/sys/sgx/rwlock.rs212
-rw-r--r--library/std/src/sys/sgx/rwlock/tests.rs31
-rw-r--r--library/std/src/sys/sgx/stdio.rs88
-rw-r--r--library/std/src/sys/sgx/thread.rs152
-rw-r--r--library/std/src/sys/sgx/thread_local_key.rs28
-rw-r--r--library/std/src/sys/sgx/time.rs46
-rw-r--r--library/std/src/sys/sgx/waitqueue/mod.rs240
-rw-r--r--library/std/src/sys/sgx/waitqueue/spin_mutex.rs80
-rw-r--r--library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs23
-rw-r--r--library/std/src/sys/sgx/waitqueue/tests.rs20
-rw-r--r--library/std/src/sys/sgx/waitqueue/unsafe_list.rs156
-rw-r--r--library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs105
-rw-r--r--library/std/src/sys/solid/abi/fs.rs53
-rw-r--r--library/std/src/sys/solid/abi/mod.rs65
-rw-r--r--library/std/src/sys/solid/abi/sockets.rs277
-rw-r--r--library/std/src/sys/solid/alloc.rs32
-rw-r--r--library/std/src/sys/solid/env.rs9
-rw-r--r--library/std/src/sys/solid/error.rs55
-rw-r--r--library/std/src/sys/solid/fs.rs574
-rw-r--r--library/std/src/sys/solid/io.rs77
-rw-r--r--library/std/src/sys/solid/memchr.rs21
-rw-r--r--library/std/src/sys/solid/mod.rs92
-rw-r--r--library/std/src/sys/solid/net.rs469
-rw-r--r--library/std/src/sys/solid/os.rs193
-rw-r--r--library/std/src/sys/solid/path.rs25
-rw-r--r--library/std/src/sys/solid/rwlock.rs95
-rw-r--r--library/std/src/sys/solid/stdio.rs80
-rw-r--r--library/std/src/sys/solid/thread_local_dtor.rs50
-rw-r--r--library/std/src/sys/solid/thread_local_key.rs26
-rw-r--r--library/std/src/sys/solid/time.rs56
-rw-r--r--library/std/src/sys/unix/alloc.rs101
-rw-r--r--library/std/src/sys/unix/android.rs81
-rw-r--r--library/std/src/sys/unix/args.rs261
-rw-r--r--library/std/src/sys/unix/cmath.rs33
-rw-r--r--library/std/src/sys/unix/env.rs219
-rw-r--r--library/std/src/sys/unix/fd.rs330
-rw-r--r--library/std/src/sys/unix/fd/tests.rs10
-rw-r--r--library/std/src/sys/unix/fs.rs1878
-rw-r--r--library/std/src/sys/unix/futex.rs303
-rw-r--r--library/std/src/sys/unix/io.rs76
-rw-r--r--library/std/src/sys/unix/kernel_copy.rs686
-rw-r--r--library/std/src/sys/unix/kernel_copy/tests.rs270
-rw-r--r--library/std/src/sys/unix/l4re.rs551
-rw-r--r--library/std/src/sys/unix/locks/fuchsia_mutex.rs165
-rw-r--r--library/std/src/sys/unix/locks/futex_condvar.rs58
-rw-r--r--library/std/src/sys/unix/locks/futex_mutex.rs101
-rw-r--r--library/std/src/sys/unix/locks/futex_rwlock.rs322
-rw-r--r--library/std/src/sys/unix/locks/mod.rs31
-rw-r--r--library/std/src/sys/unix/locks/pthread_condvar.rs222
-rw-r--r--library/std/src/sys/unix/locks/pthread_mutex.rs135
-rw-r--r--library/std/src/sys/unix/locks/pthread_rwlock.rs173
-rw-r--r--library/std/src/sys/unix/memchr.rs40
-rw-r--r--library/std/src/sys/unix/mod.rs361
-rw-r--r--library/std/src/sys/unix/net.rs512
-rw-r--r--library/std/src/sys/unix/os.rs680
-rw-r--r--library/std/src/sys/unix/os/tests.rs23
-rw-r--r--library/std/src/sys/unix/os_str.rs266
-rw-r--r--library/std/src/sys/unix/os_str/tests.rs10
-rw-r--r--library/std/src/sys/unix/path.rs63
-rw-r--r--library/std/src/sys/unix/pipe.rs151
-rw-r--r--library/std/src/sys/unix/process/mod.rs24
-rw-r--r--library/std/src/sys/unix/process/process_common.rs523
-rw-r--r--library/std/src/sys/unix/process/process_common/tests.rs124
-rw-r--r--library/std/src/sys/unix/process/process_fuchsia.rs327
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs836
-rw-r--r--library/std/src/sys/unix/process/process_unix/tests.rs62
-rw-r--r--library/std/src/sys/unix/process/process_unsupported.rs118
-rw-r--r--library/std/src/sys/unix/process/process_vxworks.rs262
-rw-r--r--library/std/src/sys/unix/process/zircon.rs309
-rw-r--r--library/std/src/sys/unix/rand.rs301
-rw-r--r--library/std/src/sys/unix/stack_overflow.rs208
-rw-r--r--library/std/src/sys/unix/stdio.rs141
-rw-r--r--library/std/src/sys/unix/thread.rs889
-rw-r--r--library/std/src/sys/unix/thread_local_dtor.rs100
-rw-r--r--library/std/src/sys/unix/thread_local_key.rs34
-rw-r--r--library/std/src/sys/unix/thread_parker.rs281
-rw-r--r--library/std/src/sys/unix/time.rs346
-rw-r--r--library/std/src/sys/unix/weak.rs205
-rw-r--r--library/std/src/sys/unsupported/alloc.rs22
-rw-r--r--library/std/src/sys/unsupported/args.rs36
-rw-r--r--library/std/src/sys/unsupported/common.rs36
-rw-r--r--library/std/src/sys/unsupported/env.rs9
-rw-r--r--library/std/src/sys/unsupported/fs.rs324
-rw-r--r--library/std/src/sys/unsupported/io.rs47
-rw-r--r--library/std/src/sys/unsupported/locks/condvar.rs27
-rw-r--r--library/std/src/sys/unsupported/locks/mod.rs6
-rw-r--r--library/std/src/sys/unsupported/locks/mutex.rs36
-rw-r--r--library/std/src/sys/unsupported/locks/rwlock.rs66
-rw-r--r--library/std/src/sys/unsupported/mod.rs27
-rw-r--r--library/std/src/sys/unsupported/net.rs366
-rw-r--r--library/std/src/sys/unsupported/os.rs105
-rw-r--r--library/std/src/sys/unsupported/pipe.rs37
-rw-r--r--library/std/src/sys/unsupported/process.rs211
-rw-r--r--library/std/src/sys/unsupported/stdio.rs59
-rw-r--r--library/std/src/sys/unsupported/thread.rs46
-rw-r--r--library/std/src/sys/unsupported/thread_local_dtor.rs9
-rw-r--r--library/std/src/sys/unsupported/thread_local_key.rs26
-rw-r--r--library/std/src/sys/unsupported/time.rs45
-rw-r--r--library/std/src/sys/wasi/args.rs62
-rw-r--r--library/std/src/sys/wasi/env.rs9
-rw-r--r--library/std/src/sys/wasi/fd.rs307
-rw-r--r--library/std/src/sys/wasi/fs.rs798
-rw-r--r--library/std/src/sys/wasi/io.rs73
-rw-r--r--library/std/src/sys/wasi/mod.rs100
-rw-r--r--library/std/src/sys/wasi/net.rs527
-rw-r--r--library/std/src/sys/wasi/os.rs243
-rw-r--r--library/std/src/sys/wasi/stdio.rs112
-rw-r--r--library/std/src/sys/wasi/thread.rs81
-rw-r--r--library/std/src/sys/wasi/time.rs65
-rw-r--r--library/std/src/sys/wasm/alloc.rs166
-rw-r--r--library/std/src/sys/wasm/atomics/futex.rs34
-rw-r--r--library/std/src/sys/wasm/atomics/thread.rs55
-rw-r--r--library/std/src/sys/wasm/env.rs9
-rw-r--r--library/std/src/sys/wasm/mod.rs77
-rw-r--r--library/std/src/sys/windows/alloc.rs246
-rw-r--r--library/std/src/sys/windows/alloc/tests.rs9
-rw-r--r--library/std/src/sys/windows/args.rs406
-rw-r--r--library/std/src/sys/windows/args/tests.rs91
-rw-r--r--library/std/src/sys/windows/c.rs1340
-rw-r--r--library/std/src/sys/windows/c/errors.rs1883
-rw-r--r--library/std/src/sys/windows/cmath.rs92
-rw-r--r--library/std/src/sys/windows/compat.rs273
-rw-r--r--library/std/src/sys/windows/env.rs9
-rw-r--r--library/std/src/sys/windows/fs.rs1399
-rw-r--r--library/std/src/sys/windows/handle.rs335
-rw-r--r--library/std/src/sys/windows/handle/tests.rs22
-rw-r--r--library/std/src/sys/windows/io.rs80
-rw-r--r--library/std/src/sys/windows/locks/condvar.rs52
-rw-r--r--library/std/src/sys/windows/locks/mod.rs6
-rw-r--r--library/std/src/sys/windows/locks/mutex.rs57
-rw-r--r--library/std/src/sys/windows/locks/rwlock.rs42
-rw-r--r--library/std/src/sys/windows/memchr.rs5
-rw-r--r--library/std/src/sys/windows/mod.rs323
-rw-r--r--library/std/src/sys/windows/net.rs476
-rw-r--r--library/std/src/sys/windows/os.rs328
-rw-r--r--library/std/src/sys/windows/os/tests.rs13
-rw-r--r--library/std/src/sys/windows/os_str.rs226
-rw-r--r--library/std/src/sys/windows/path.rs333
-rw-r--r--library/std/src/sys/windows/path/tests.rs137
-rw-r--r--library/std/src/sys/windows/pipe.rs538
-rw-r--r--library/std/src/sys/windows/process.rs847
-rw-r--r--library/std/src/sys/windows/process/tests.rs222
-rw-r--r--library/std/src/sys/windows/rand.rs35
-rw-r--r--library/std/src/sys/windows/stack_overflow.rs42
-rw-r--r--library/std/src/sys/windows/stack_overflow_uwp.rs11
-rw-r--r--library/std/src/sys/windows/stdio.rs422
-rw-r--r--library/std/src/sys/windows/stdio_uwp.rs87
-rw-r--r--library/std/src/sys/windows/thread.rs125
-rw-r--r--library/std/src/sys/windows/thread_local_dtor.rs28
-rw-r--r--library/std/src/sys/windows/thread_local_key.rs238
-rw-r--r--library/std/src/sys/windows/thread_parker.rs255
-rw-r--r--library/std/src/sys/windows/time.rs224
-rw-r--r--library/std/src/sys_common/backtrace.rs183
-rw-r--r--library/std/src/sys_common/condvar.rs56
-rw-r--r--library/std/src/sys_common/condvar/check.rs57
-rw-r--r--library/std/src/sys_common/fs.rs51
-rw-r--r--library/std/src/sys_common/io.rs49
-rw-r--r--library/std/src/sys_common/lazy_box.rs90
-rw-r--r--library/std/src/sys_common/memchr.rs51
-rw-r--r--library/std/src/sys_common/memchr/tests.rs86
-rw-r--r--library/std/src/sys_common/mod.rs89
-rw-r--r--library/std/src/sys_common/mutex.rs93
-rw-r--r--library/std/src/sys_common/net.rs737
-rw-r--r--library/std/src/sys_common/net/tests.rs19
-rw-r--r--library/std/src/sys_common/process.rs119
-rw-r--r--library/std/src/sys_common/remutex.rs200
-rw-r--r--library/std/src/sys_common/remutex/tests.rs77
-rw-r--r--library/std/src/sys_common/rwlock.rs130
-rw-r--r--library/std/src/sys_common/tests.rs6
-rw-r--r--library/std/src/sys_common/thread.rs18
-rw-r--r--library/std/src/sys_common/thread_info.rs47
-rw-r--r--library/std/src/sys_common/thread_local_dtor.rs49
-rw-r--r--library/std/src/sys_common/thread_local_key.rs237
-rw-r--r--library/std/src/sys_common/thread_local_key/tests.rs34
-rw-r--r--library/std/src/sys_common/thread_parker/futex.rs97
-rw-r--r--library/std/src/sys_common/thread_parker/generic.rs125
-rw-r--r--library/std/src/sys_common/thread_parker/mod.rs22
-rw-r--r--library/std/src/sys_common/thread_parker/wait_flag.rs102
-rw-r--r--library/std/src/sys_common/wtf8.rs926
-rw-r--r--library/std/src/sys_common/wtf8/tests.rs409
233 files changed, 43771 insertions, 0 deletions
diff --git a/library/std/src/sys/common/alloc.rs b/library/std/src/sys/common/alloc.rs
new file mode 100644
index 000000000..e8e7c51cb
--- /dev/null
+++ b/library/std/src/sys/common/alloc.rs
@@ -0,0 +1,54 @@
+use crate::alloc::{GlobalAlloc, Layout, System};
+use crate::cmp;
+use crate::ptr;
+
+// The minimum alignment guaranteed by the architecture. This value is used to
+// add fast paths for low alignment values.
+#[cfg(all(any(
+ target_arch = "x86",
+ target_arch = "arm",
+ target_arch = "mips",
+ target_arch = "powerpc",
+ target_arch = "powerpc64",
+ target_arch = "sparc",
+ target_arch = "asmjs",
+ target_arch = "wasm32",
+ target_arch = "hexagon",
+ all(target_arch = "riscv32", not(target_os = "espidf")),
+ all(target_arch = "xtensa", not(target_os = "espidf")),
+)))]
+pub const MIN_ALIGN: usize = 8;
+#[cfg(all(any(
+ target_arch = "x86_64",
+ target_arch = "aarch64",
+ target_arch = "mips64",
+ target_arch = "s390x",
+ target_arch = "sparc64",
+ target_arch = "riscv64",
+ target_arch = "wasm64",
+)))]
+pub const MIN_ALIGN: usize = 16;
+// The allocator on the esp-idf platform guarantees 4 byte alignment.
+#[cfg(all(any(
+ all(target_arch = "riscv32", target_os = "espidf"),
+ all(target_arch = "xtensa", target_os = "espidf"),
+)))]
+pub const MIN_ALIGN: usize = 4;
+
+pub unsafe fn realloc_fallback(
+ alloc: &System,
+ ptr: *mut u8,
+ old_layout: Layout,
+ new_size: usize,
+) -> *mut u8 {
+ // Docs for GlobalAlloc::realloc require this to be valid:
+ let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
+
+ let new_ptr = GlobalAlloc::alloc(alloc, new_layout);
+ if !new_ptr.is_null() {
+ let size = cmp::min(old_layout.size(), new_size);
+ ptr::copy_nonoverlapping(ptr, new_ptr, size);
+ GlobalAlloc::dealloc(alloc, ptr, old_layout);
+ }
+ new_ptr
+}
diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs
new file mode 100644
index 000000000..ff64d2aa8
--- /dev/null
+++ b/library/std/src/sys/common/mod.rs
@@ -0,0 +1,13 @@
+// This module contains code that is shared between all platforms, mostly utility or fallback code.
+// This explicitly does not include code that is shared between only a few platforms,
+// such as when reusing an implementation from `unix` or `unsupported`.
+// In those cases the desired code should be included directly using the #[path] attribute,
+// not moved to this module.
+//
+// Currently `sys_common` contains a lot of code that should live in this module,
+// ideally `sys_common` would only contain platform-independent abstractions on top of `sys`.
+// Progress on this is tracked in #84187.
+
+#![allow(dead_code)]
+
+pub mod alloc;
diff --git a/library/std/src/sys/hermit/alloc.rs b/library/std/src/sys/hermit/alloc.rs
new file mode 100644
index 000000000..d153914e7
--- /dev/null
+++ b/library/std/src/sys/hermit/alloc.rs
@@ -0,0 +1,31 @@
+use crate::alloc::{GlobalAlloc, Layout, System};
+use crate::ptr;
+use crate::sys::hermit::abi;
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+ #[inline]
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ abi::malloc(layout.size(), layout.align())
+ }
+
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+ let addr = abi::malloc(layout.size(), layout.align());
+
+ if !addr.is_null() {
+ ptr::write_bytes(addr, 0x00, layout.size());
+ }
+
+ addr
+ }
+
+ #[inline]
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ abi::free(ptr, layout.size(), layout.align())
+ }
+
+ #[inline]
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ abi::realloc(ptr, layout.size(), layout.align(), new_size)
+ }
+}
diff --git a/library/std/src/sys/hermit/args.rs b/library/std/src/sys/hermit/args.rs
new file mode 100644
index 000000000..1c7e1dd8d
--- /dev/null
+++ b/library/std/src/sys/hermit/args.rs
@@ -0,0 +1,94 @@
+use crate::ffi::OsString;
+use crate::fmt;
+use crate::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>,
+}
+
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.iter.as_slice().fmt(f)
+ }
+}
+
+impl !Send for Args {}
+impl !Sync for 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()
+ }
+}
+
+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 super::Args;
+ use crate::ffi::{CStr, OsString};
+ use crate::os::unix::ffi::OsStringExt;
+ use crate::ptr;
+
+ use crate::sys_common::mutex::StaticMutex;
+
+ static mut ARGC: isize = 0;
+ static mut ARGV: *const *const u8 = ptr::null();
+ static LOCK: StaticMutex = StaticMutex::new();
+
+ pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ let _guard = LOCK.lock();
+ ARGC = argc;
+ ARGV = argv;
+ }
+
+ pub unsafe fn cleanup() {
+ let _guard = LOCK.lock();
+ ARGC = 0;
+ ARGV = ptr::null();
+ }
+
+ pub fn args() -> Args {
+ Args { iter: clone().into_iter() }
+ }
+
+ fn clone() -> Vec<OsString> {
+ unsafe {
+ let _guard = LOCK.lock();
+ (0..ARGC)
+ .map(|i| {
+ let cstr = CStr::from_ptr(*ARGV.offset(i) as *const i8);
+ OsStringExt::from_vec(cstr.to_bytes().to_vec())
+ })
+ .collect()
+ }
+ }
+}
diff --git a/library/std/src/sys/hermit/condvar.rs b/library/std/src/sys/hermit/condvar.rs
new file mode 100644
index 000000000..22059ca0d
--- /dev/null
+++ b/library/std/src/sys/hermit/condvar.rs
@@ -0,0 +1,90 @@
+use crate::ffi::c_void;
+use crate::ptr;
+use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
+use crate::sys::hermit::abi;
+use crate::sys::locks::Mutex;
+use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+use crate::time::Duration;
+
+// The implementation is inspired by Andrew D. Birrell's paper
+// "Implementing Condition Variables with Semaphores"
+
+pub struct Condvar {
+ counter: AtomicUsize,
+ sem1: *const c_void,
+ sem2: *const c_void,
+}
+
+pub(crate) type MovableCondvar = LazyBox<Condvar>;
+
+impl LazyInit for Condvar {
+ fn init() -> Box<Self> {
+ Box::new(Self::new())
+ }
+}
+
+unsafe impl Send for Condvar {}
+unsafe impl Sync for Condvar {}
+
+impl Condvar {
+ pub fn new() -> Self {
+ let mut condvar =
+ Self { counter: AtomicUsize::new(0), sem1: ptr::null(), sem2: ptr::null() };
+ unsafe {
+ let _ = abi::sem_init(&mut condvar.sem1, 0);
+ let _ = abi::sem_init(&mut condvar.sem2, 0);
+ }
+ condvar
+ }
+
+ pub unsafe fn notify_one(&self) {
+ if self.counter.load(SeqCst) > 0 {
+ self.counter.fetch_sub(1, SeqCst);
+ abi::sem_post(self.sem1);
+ abi::sem_timedwait(self.sem2, 0);
+ }
+ }
+
+ pub unsafe fn notify_all(&self) {
+ let counter = self.counter.swap(0, SeqCst);
+ for _ in 0..counter {
+ abi::sem_post(self.sem1);
+ }
+ for _ in 0..counter {
+ abi::sem_timedwait(self.sem2, 0);
+ }
+ }
+
+ pub unsafe fn wait(&self, mutex: &Mutex) {
+ self.counter.fetch_add(1, SeqCst);
+ mutex.unlock();
+ abi::sem_timedwait(self.sem1, 0);
+ abi::sem_post(self.sem2);
+ mutex.lock();
+ }
+
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ self.counter.fetch_add(1, SeqCst);
+ mutex.unlock();
+ let millis = dur.as_millis().min(u32::MAX as u128) as u32;
+
+ let res = if millis > 0 {
+ abi::sem_timedwait(self.sem1, millis)
+ } else {
+ abi::sem_trywait(self.sem1)
+ };
+
+ abi::sem_post(self.sem2);
+ mutex.lock();
+ res == 0
+ }
+}
+
+impl Drop for Condvar {
+ fn drop(&mut self) {
+ unsafe {
+ let _ = abi::sem_destroy(self.sem1);
+ let _ = abi::sem_destroy(self.sem2);
+ }
+ }
+}
diff --git a/library/std/src/sys/hermit/env.rs b/library/std/src/sys/hermit/env.rs
new file mode 100644
index 000000000..7a0fcb31e
--- /dev/null
+++ b/library/std/src/sys/hermit/env.rs
@@ -0,0 +1,9 @@
+pub mod os {
+ pub const FAMILY: &str = "";
+ pub const OS: &str = "hermit";
+ pub const DLL_PREFIX: &str = "";
+ pub const DLL_SUFFIX: &str = "";
+ pub const DLL_EXTENSION: &str = "";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
diff --git a/library/std/src/sys/hermit/fd.rs b/library/std/src/sys/hermit/fd.rs
new file mode 100644
index 000000000..c400f5f2c
--- /dev/null
+++ b/library/std/src/sys/hermit/fd.rs
@@ -0,0 +1,87 @@
+#![unstable(reason = "not public", issue = "none", feature = "fd")]
+
+use crate::io::{self, Read};
+use crate::mem;
+use crate::sys::cvt;
+use crate::sys::hermit::abi;
+use crate::sys::unsupported;
+use crate::sys_common::AsInner;
+
+#[derive(Debug)]
+pub struct FileDesc {
+ fd: i32,
+}
+
+impl FileDesc {
+ pub fn new(fd: i32) -> FileDesc {
+ FileDesc { fd }
+ }
+
+ pub fn raw(&self) -> i32 {
+ self.fd
+ }
+
+ /// Extracts the actual file descriptor without closing it.
+ pub fn into_raw(self) -> i32 {
+ let fd = self.fd;
+ mem::forget(self);
+ fd
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ let result = unsafe { abi::read(self.fd, buf.as_mut_ptr(), buf.len()) };
+ cvt(result as i32)
+ }
+
+ 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 result = unsafe { abi::write(self.fd, buf.as_ptr(), buf.len()) };
+ cvt(result as i32)
+ }
+
+ pub fn duplicate(&self) -> io::Result<FileDesc> {
+ self.duplicate_path(&[])
+ }
+ pub fn duplicate_path(&self, _path: &[u8]) -> io::Result<FileDesc> {
+ unsupported()
+ }
+
+ pub fn nonblocking(&self) -> io::Result<bool> {
+ Ok(false)
+ }
+
+ pub fn set_cloexec(&self) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> {
+ unsupported()
+ }
+}
+
+impl<'a> Read for &'a FileDesc {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ (**self).read(buf)
+ }
+}
+
+impl AsInner<i32> for FileDesc {
+ fn as_inner(&self) -> &i32 {
+ &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 { abi::close(self.fd) };
+ }
+}
diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs
new file mode 100644
index 000000000..fa9a7fb19
--- /dev/null
+++ b/library/std/src/sys/hermit/fs.rs
@@ -0,0 +1,408 @@
+use crate::ffi::{CStr, CString, OsString};
+use crate::fmt;
+use crate::hash::{Hash, Hasher};
+use crate::io::{self, Error, ErrorKind};
+use crate::io::{IoSlice, IoSliceMut, ReadBuf, SeekFrom};
+use crate::os::unix::ffi::OsStrExt;
+use crate::path::{Path, PathBuf};
+use crate::sys::cvt;
+use crate::sys::hermit::abi;
+use crate::sys::hermit::abi::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY};
+use crate::sys::hermit::fd::FileDesc;
+use crate::sys::time::SystemTime;
+use crate::sys::unsupported;
+
+pub use crate::sys_common::fs::{copy, try_exists};
+//pub use crate::sys_common::fs::remove_dir_all;
+
+fn cstr(path: &Path) -> io::Result<CString> {
+ Ok(CString::new(path.as_os_str().as_bytes())?)
+}
+
+#[derive(Debug)]
+pub struct File(FileDesc);
+
+pub struct FileAttr(!);
+
+pub struct ReadDir(!);
+
+pub struct DirEntry(!);
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions {
+ // generic
+ read: bool,
+ write: bool,
+ append: bool,
+ truncate: bool,
+ create: bool,
+ create_new: bool,
+ // system-specific
+ mode: i32,
+}
+
+pub struct FilePermissions(!);
+
+pub struct FileType(!);
+
+#[derive(Debug)]
+pub struct DirBuilder {}
+
+impl FileAttr {
+ pub fn size(&self) -> u64 {
+ self.0
+ }
+
+ pub fn perm(&self) -> FilePermissions {
+ self.0
+ }
+
+ pub fn file_type(&self) -> FileType {
+ self.0
+ }
+
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ self.0
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ self.0
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ self.0
+ }
+}
+
+impl Clone for FileAttr {
+ fn clone(&self) -> FileAttr {
+ self.0
+ }
+}
+
+impl FilePermissions {
+ pub fn readonly(&self) -> bool {
+ self.0
+ }
+
+ pub fn set_readonly(&mut self, _readonly: bool) {
+ self.0
+ }
+}
+
+impl Clone for FilePermissions {
+ fn clone(&self) -> FilePermissions {
+ self.0
+ }
+}
+
+impl PartialEq for FilePermissions {
+ fn eq(&self, _other: &FilePermissions) -> bool {
+ self.0
+ }
+}
+
+impl Eq for FilePermissions {}
+
+impl fmt::Debug for FilePermissions {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+impl FileType {
+ pub fn is_dir(&self) -> bool {
+ self.0
+ }
+
+ pub fn is_file(&self) -> bool {
+ self.0
+ }
+
+ pub fn is_symlink(&self) -> bool {
+ self.0
+ }
+}
+
+impl Clone for FileType {
+ fn clone(&self) -> FileType {
+ self.0
+ }
+}
+
+impl Copy for FileType {}
+
+impl PartialEq for FileType {
+ fn eq(&self, _other: &FileType) -> bool {
+ self.0
+ }
+}
+
+impl Eq for FileType {}
+
+impl Hash for FileType {
+ fn hash<H: Hasher>(&self, _h: &mut H) {
+ self.0
+ }
+}
+
+impl fmt::Debug for FileType {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+impl fmt::Debug for ReadDir {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+impl Iterator for ReadDir {
+ type Item = io::Result<DirEntry>;
+
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ self.0
+ }
+}
+
+impl DirEntry {
+ pub fn path(&self) -> PathBuf {
+ self.0
+ }
+
+ pub fn file_name(&self) -> OsString {
+ self.0
+ }
+
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ self.0
+ }
+
+ pub fn file_type(&self) -> io::Result<FileType> {
+ self.0
+ }
+}
+
+impl OpenOptions {
+ pub fn new() -> OpenOptions {
+ OpenOptions {
+ // generic
+ read: false,
+ write: false,
+ append: false,
+ truncate: false,
+ create: false,
+ create_new: false,
+ // system-specific
+ mode: 0x777,
+ }
+ }
+
+ 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;
+ }
+
+ fn get_access_mode(&self) -> io::Result<i32> {
+ match (self.read, self.write, self.append) {
+ (true, false, false) => Ok(O_RDONLY),
+ (false, true, false) => Ok(O_WRONLY),
+ (true, true, false) => Ok(O_RDWR),
+ (false, _, true) => Ok(O_WRONLY | O_APPEND),
+ (true, _, true) => Ok(O_RDWR | O_APPEND),
+ (false, false, false) => {
+ Err(io::const_io_error!(ErrorKind::InvalidInput, "invalid access mode"))
+ }
+ }
+ }
+
+ fn get_creation_mode(&self) -> io::Result<i32> {
+ match (self.write, self.append) {
+ (true, false) => {}
+ (false, false) => {
+ if self.truncate || self.create || self.create_new {
+ return Err(io::const_io_error!(
+ ErrorKind::InvalidInput,
+ "invalid creation mode",
+ ));
+ }
+ }
+ (_, true) => {
+ if self.truncate && !self.create_new {
+ return Err(io::const_io_error!(
+ ErrorKind::InvalidInput,
+ "invalid creation mode",
+ ));
+ }
+ }
+ }
+
+ Ok(match (self.create, self.truncate, self.create_new) {
+ (false, false, false) => 0,
+ (true, false, false) => O_CREAT,
+ (false, true, false) => O_TRUNC,
+ (true, true, false) => O_CREAT | O_TRUNC,
+ (_, _, true) => O_CREAT | 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 mut flags = opts.get_access_mode()?;
+ flags = flags | opts.get_creation_mode()?;
+
+ let mode;
+ if flags & O_CREAT == O_CREAT {
+ mode = opts.mode;
+ } else {
+ mode = 0;
+ }
+
+ let fd = unsafe { cvt(abi::open(path.as_ptr(), flags, mode))? };
+ Ok(File(FileDesc::new(fd as i32)))
+ }
+
+ pub fn file_attr(&self) -> io::Result<FileAttr> {
+ Err(Error::from_raw_os_error(22))
+ }
+
+ pub fn fsync(&self) -> io::Result<()> {
+ Err(Error::from_raw_os_error(22))
+ }
+
+ pub fn datasync(&self) -> io::Result<()> {
+ self.fsync()
+ }
+
+ pub fn truncate(&self, _size: u64) -> io::Result<()> {
+ Err(Error::from_raw_os_error(22))
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ crate::io::default_read_vectored(|buf| self.read(buf), bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ false
+ }
+
+ pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ crate::io::default_read_buf(|buf| self.read(buf), buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ crate::io::default_write_vectored(|buf| self.write(buf), bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ false
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ Ok(())
+ }
+
+ pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
+ Err(Error::from_raw_os_error(22))
+ }
+
+ pub fn duplicate(&self) -> io::Result<File> {
+ Err(Error::from_raw_os_error(22))
+ }
+
+ pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+ Err(Error::from_raw_os_error(22))
+ }
+}
+
+impl DirBuilder {
+ pub fn new() -> DirBuilder {
+ DirBuilder {}
+ }
+
+ pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
+ unsupported()
+ }
+}
+
+pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
+ unsupported()
+}
+
+pub fn unlink(path: &Path) -> io::Result<()> {
+ let name = cstr(path)?;
+ let _ = unsafe { cvt(abi::unlink(name.as_ptr()))? };
+ Ok(())
+}
+
+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()
+ Ok(())
+}
+
+pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn link(_original: &Path, _link: &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()
+}
diff --git a/library/std/src/sys/hermit/memchr.rs b/library/std/src/sys/hermit/memchr.rs
new file mode 100644
index 000000000..996748219
--- /dev/null
+++ b/library/std/src/sys/hermit/memchr.rs
@@ -0,0 +1 @@
+pub use core::slice::memchr::{memchr, memrchr};
diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs
new file mode 100644
index 000000000..60b7a973c
--- /dev/null
+++ b/library/std/src/sys/hermit/mod.rs
@@ -0,0 +1,156 @@
+//! System bindings for HermitCore
+//!
+//! This module contains the facade (aka platform-specific) implementations of
+//! OS level functionality for HermitCore.
+//!
+//! 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!
+
+#![allow(unsafe_op_in_unsafe_fn)]
+
+use crate::intrinsics;
+use crate::os::raw::c_char;
+
+pub mod alloc;
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+pub mod env;
+pub mod fd;
+pub mod fs;
+#[path = "../unsupported/io.rs"]
+pub mod io;
+pub mod memchr;
+pub mod net;
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+#[path = "../unix/path.rs"]
+pub mod path;
+#[path = "../unsupported/pipe.rs"]
+pub mod pipe;
+#[path = "../unsupported/process.rs"]
+pub mod process;
+pub mod stdio;
+pub mod thread;
+pub mod thread_local_dtor;
+#[path = "../unsupported/thread_local_key.rs"]
+pub mod thread_local_key;
+pub mod time;
+
+mod condvar;
+mod mutex;
+mod rwlock;
+
+pub mod locks {
+ pub use super::condvar::*;
+ pub use super::mutex::*;
+ pub use super::rwlock::*;
+}
+
+use crate::io::ErrorKind;
+
+#[allow(unused_extern_crates)]
+pub extern crate hermit_abi as abi;
+
+pub fn unsupported<T>() -> crate::io::Result<T> {
+ Err(unsupported_err())
+}
+
+pub fn unsupported_err() -> crate::io::Error {
+ crate::io::const_io_error!(
+ crate::io::ErrorKind::Unsupported,
+ "operation not supported on HermitCore yet",
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn floor(x: f64) -> f64 {
+ unsafe { intrinsics::floorf64(x) }
+}
+
+pub fn abort_internal() -> ! {
+ unsafe {
+ abi::abort();
+ }
+}
+
+// FIXME: just a workaround to test the system
+pub fn hashmap_random_keys() -> (u64, u64) {
+ (1, 2)
+}
+
+// This function is needed by the panic runtime. The symbol is named in
+// pre-link args for the target specification, so keep that in sync.
+#[cfg(not(test))]
+#[no_mangle]
+// NB. used by both libunwind and libpanic_abort
+pub extern "C" fn __rust_abort() {
+ abort_internal();
+}
+
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ let _ = net::init();
+ args::init(argc, argv);
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+ args::cleanup();
+}
+
+#[cfg(not(test))]
+#[no_mangle]
+pub unsafe extern "C" fn runtime_entry(
+ argc: i32,
+ argv: *const *const c_char,
+ env: *const *const c_char,
+) -> ! {
+ use crate::sys::hermit::thread_local_dtor::run_dtors;
+ extern "C" {
+ fn main(argc: isize, argv: *const *const c_char) -> i32;
+ }
+
+ // initialize environment
+ os::init_environment(env as *const *const i8);
+
+ let result = main(argc as isize, argv);
+
+ run_dtors();
+ abi::exit(result);
+}
+
+pub fn decode_error_kind(errno: i32) -> ErrorKind {
+ match errno {
+ x if x == 13 as i32 => ErrorKind::PermissionDenied,
+ x if x == 98 as i32 => ErrorKind::AddrInUse,
+ x if x == 99 as i32 => ErrorKind::AddrNotAvailable,
+ x if x == 11 as i32 => ErrorKind::WouldBlock,
+ x if x == 103 as i32 => ErrorKind::ConnectionAborted,
+ x if x == 111 as i32 => ErrorKind::ConnectionRefused,
+ x if x == 104 as i32 => ErrorKind::ConnectionReset,
+ x if x == 17 as i32 => ErrorKind::AlreadyExists,
+ x if x == 4 as i32 => ErrorKind::Interrupted,
+ x if x == 22 as i32 => ErrorKind::InvalidInput,
+ x if x == 2 as i32 => ErrorKind::NotFound,
+ x if x == 107 as i32 => ErrorKind::NotConnected,
+ x if x == 1 as i32 => ErrorKind::PermissionDenied,
+ x if x == 32 as i32 => ErrorKind::BrokenPipe,
+ x if x == 110 as i32 => ErrorKind::TimedOut,
+ _ => ErrorKind::Uncategorized,
+ }
+}
+
+pub fn cvt(result: i32) -> crate::io::Result<usize> {
+ if result < 0 { Err(crate::io::Error::from_raw_os_error(-result)) } else { Ok(result as usize) }
+}
diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs
new file mode 100644
index 000000000..eb15a04ff
--- /dev/null
+++ b/library/std/src/sys/hermit/mutex.rs
@@ -0,0 +1,216 @@
+use crate::cell::UnsafeCell;
+use crate::collections::VecDeque;
+use crate::hint;
+use crate::ops::{Deref, DerefMut, Drop};
+use crate::ptr;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+use crate::sys::hermit::abi;
+
+/// This type provides a lock based on busy waiting to realize mutual exclusion
+///
+/// # Description
+///
+/// This structure behaves a lot like a common mutex. There are some differences:
+///
+/// - By using busy waiting, it can be used outside the runtime.
+/// - It is a so called ticket lock and is completely fair.
+#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
+#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
+struct Spinlock<T: ?Sized> {
+ queue: AtomicUsize,
+ dequeue: AtomicUsize,
+ data: UnsafeCell<T>,
+}
+
+unsafe impl<T: ?Sized + Send> Sync for Spinlock<T> {}
+unsafe impl<T: ?Sized + Send> Send for Spinlock<T> {}
+
+/// A guard to which the protected data can be accessed
+///
+/// When the guard falls out of scope it will release the lock.
+struct SpinlockGuard<'a, T: ?Sized + 'a> {
+ dequeue: &'a AtomicUsize,
+ data: &'a mut T,
+}
+
+impl<T> Spinlock<T> {
+ pub const fn new(user_data: T) -> Spinlock<T> {
+ Spinlock {
+ queue: AtomicUsize::new(0),
+ dequeue: AtomicUsize::new(1),
+ data: UnsafeCell::new(user_data),
+ }
+ }
+
+ #[inline]
+ fn obtain_lock(&self) {
+ let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1;
+ let mut counter: u16 = 0;
+ while self.dequeue.load(Ordering::SeqCst) != ticket {
+ counter += 1;
+ if counter < 100 {
+ hint::spin_loop();
+ } else {
+ counter = 0;
+ unsafe {
+ abi::yield_now();
+ }
+ }
+ }
+ }
+
+ #[inline]
+ pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> {
+ self.obtain_lock();
+ SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() }
+ }
+}
+
+impl<T: ?Sized + Default> Default for Spinlock<T> {
+ fn default() -> Spinlock<T> {
+ Spinlock::new(Default::default())
+ }
+}
+
+impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ &*self.data
+ }
+}
+
+impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut *self.data
+ }
+}
+
+impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
+ /// The dropping of the SpinlockGuard will release the lock it was created from.
+ fn drop(&mut self) {
+ self.dequeue.fetch_add(1, Ordering::SeqCst);
+ }
+}
+
+/// Realize a priority queue for tasks
+struct PriorityQueue {
+ queues: [Option<VecDeque<abi::Tid>>; abi::NO_PRIORITIES],
+ prio_bitmap: u64,
+}
+
+impl PriorityQueue {
+ pub const fn new() -> PriorityQueue {
+ PriorityQueue {
+ queues: [
+ None, None, None, None, None, None, None, None, None, None, None, None, None, None,
+ None, None, None, None, None, None, None, None, None, None, None, None, None, None,
+ None, None, None,
+ ],
+ prio_bitmap: 0,
+ }
+ }
+
+ /// Add a task id by its priority to the queue
+ pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) {
+ let i: usize = prio.into().into();
+ self.prio_bitmap |= (1 << i) as u64;
+ if let Some(queue) = &mut self.queues[i] {
+ queue.push_back(id);
+ } else {
+ let mut queue = VecDeque::new();
+ queue.push_back(id);
+ self.queues[i] = Some(queue);
+ }
+ }
+
+ fn pop_from_queue(&mut self, queue_index: usize) -> Option<abi::Tid> {
+ if let Some(queue) = &mut self.queues[queue_index] {
+ let id = queue.pop_front();
+
+ if queue.is_empty() {
+ self.prio_bitmap &= !(1 << queue_index as u64);
+ }
+
+ id
+ } else {
+ None
+ }
+ }
+
+ /// Pop the task handle with the highest priority from the queue
+ pub fn pop(&mut self) -> Option<abi::Tid> {
+ for i in 0..abi::NO_PRIORITIES {
+ if self.prio_bitmap & (1 << i) != 0 {
+ return self.pop_from_queue(i);
+ }
+ }
+
+ None
+ }
+}
+
+struct MutexInner {
+ locked: bool,
+ blocked_task: PriorityQueue,
+}
+
+impl MutexInner {
+ pub const fn new() -> MutexInner {
+ MutexInner { locked: false, blocked_task: PriorityQueue::new() }
+ }
+}
+
+pub struct Mutex {
+ inner: Spinlock<MutexInner>,
+}
+
+pub type MovableMutex = Mutex;
+
+unsafe impl Send for Mutex {}
+unsafe impl Sync for Mutex {}
+
+impl Mutex {
+ pub const fn new() -> Mutex {
+ Mutex { inner: Spinlock::new(MutexInner::new()) }
+ }
+
+ #[inline]
+ pub unsafe fn init(&mut self) {}
+
+ #[inline]
+ pub unsafe fn lock(&self) {
+ loop {
+ let mut guard = self.inner.lock();
+ if guard.locked == false {
+ guard.locked = true;
+ return;
+ } else {
+ let prio = abi::get_priority();
+ let id = abi::getpid();
+
+ guard.blocked_task.push(prio, id);
+ abi::block_current_task();
+ drop(guard);
+ abi::yield_now();
+ }
+ }
+ }
+
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ let mut guard = self.inner.lock();
+ guard.locked = false;
+ if let Some(tid) = guard.blocked_task.pop() {
+ abi::wakeup_task(tid);
+ }
+ }
+
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ let mut guard = self.inner.lock();
+ if guard.locked == false {
+ guard.locked = true;
+ }
+ guard.locked
+ }
+}
diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs
new file mode 100644
index 000000000..745476171
--- /dev/null
+++ b/library/std/src/sys/hermit/net.rs
@@ -0,0 +1,492 @@
+use crate::fmt;
+use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
+use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+use crate::str;
+use crate::sync::Arc;
+use crate::sys::hermit::abi;
+use crate::sys::hermit::abi::IpAddress::{Ipv4, Ipv6};
+use crate::sys::unsupported;
+use crate::sys_common::AsInner;
+use crate::time::Duration;
+
+/// Checks whether the HermitCore's socket interface has been started already, and
+/// if not, starts it.
+pub fn init() -> io::Result<()> {
+ if abi::network_init() < 0 {
+ return Err(io::const_io_error!(
+ ErrorKind::Uncategorized,
+ "Unable to initialize network interface",
+ ));
+ }
+
+ Ok(())
+}
+
+#[derive(Debug, Clone)]
+pub struct Socket(abi::Handle);
+
+impl AsInner<abi::Handle> for Socket {
+ fn as_inner(&self) -> &abi::Handle {
+ &self.0
+ }
+}
+
+impl Drop for Socket {
+ fn drop(&mut self) {
+ let _ = abi::tcpstream::close(self.0);
+ }
+}
+
+// Arc is used to count the number of used sockets.
+// Only if all sockets are released, the drop
+// method will close the socket.
+#[derive(Clone)]
+pub struct TcpStream(Arc<Socket>);
+
+impl TcpStream {
+ pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
+ let addr = addr?;
+
+ match abi::tcpstream::connect(addr.ip().to_string().as_bytes(), addr.port(), None) {
+ Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
+ _ => Err(io::const_io_error!(
+ ErrorKind::Uncategorized,
+ "Unable to initiate a connection on a socket",
+ )),
+ }
+ }
+
+ pub fn connect_timeout(saddr: &SocketAddr, duration: Duration) -> io::Result<TcpStream> {
+ match abi::tcpstream::connect(
+ saddr.ip().to_string().as_bytes(),
+ saddr.port(),
+ Some(duration.as_millis() as u64),
+ ) {
+ Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
+ _ => Err(io::const_io_error!(
+ ErrorKind::Uncategorized,
+ "Unable to initiate a connection on a socket",
+ )),
+ }
+ }
+
+ pub fn set_read_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
+ abi::tcpstream::set_read_timeout(*self.0.as_inner(), duration.map(|d| d.as_millis() as u64))
+ .map_err(|_| {
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to set timeout value")
+ })
+ }
+
+ pub fn set_write_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
+ abi::tcpstream::set_write_timeout(
+ *self.0.as_inner(),
+ duration.map(|d| d.as_millis() as u64),
+ )
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "Unable to set timeout value"))
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ let duration = abi::tcpstream::get_read_timeout(*self.0.as_inner()).map_err(|_| {
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to determine timeout value")
+ })?;
+
+ Ok(duration.map(|d| Duration::from_millis(d)))
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ let duration = abi::tcpstream::get_write_timeout(*self.0.as_inner()).map_err(|_| {
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to determine timeout value")
+ })?;
+
+ Ok(duration.map(|d| Duration::from_millis(d)))
+ }
+
+ pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+ abi::tcpstream::peek(*self.0.as_inner(), buf)
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "peek failed"))
+ }
+
+ pub fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
+ self.read_vectored(&mut [IoSliceMut::new(buffer)])
+ }
+
+ pub fn read_vectored(&self, ioslice: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ let mut size: usize = 0;
+
+ for i in ioslice.iter_mut() {
+ let ret = abi::tcpstream::read(*self.0.as_inner(), &mut i[0..]).map_err(|_| {
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to read on socket")
+ })?;
+
+ if ret != 0 {
+ size += ret;
+ }
+ }
+
+ Ok(size)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn write(&self, buffer: &[u8]) -> io::Result<usize> {
+ self.write_vectored(&[IoSlice::new(buffer)])
+ }
+
+ pub fn write_vectored(&self, ioslice: &[IoSlice<'_>]) -> io::Result<usize> {
+ let mut size: usize = 0;
+
+ for i in ioslice.iter() {
+ size += abi::tcpstream::write(*self.0.as_inner(), i).map_err(|_| {
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to write on socket")
+ })?;
+ }
+
+ Ok(size)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ let (ipaddr, port) = abi::tcpstream::peer_addr(*self.0.as_inner())
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "peer_addr failed"))?;
+
+ let saddr = match ipaddr {
+ Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
+ Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
+ _ => {
+ return Err(io::const_io_error!(ErrorKind::Uncategorized, "peer_addr failed"));
+ }
+ };
+
+ Ok(saddr)
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+ abi::tcpstream::shutdown(*self.0.as_inner(), how as i32)
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to shutdown socket"))
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpStream> {
+ Ok(self.clone())
+ }
+
+ pub fn set_linger(&self, _linger: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn set_nodelay(&self, mode: bool) -> io::Result<()> {
+ abi::tcpstream::set_nodelay(*self.0.as_inner(), mode)
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "set_nodelay failed"))
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ abi::tcpstream::nodelay(*self.0.as_inner())
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "nodelay failed"))
+ }
+
+ pub fn set_ttl(&self, tll: u32) -> io::Result<()> {
+ abi::tcpstream::set_tll(*self.0.as_inner(), tll)
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to set TTL"))
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ abi::tcpstream::get_tll(*self.0.as_inner())
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to get TTL"))
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> {
+ abi::tcpstream::set_nonblocking(*self.0.as_inner(), mode).map_err(|_| {
+ io::const_io_error!(ErrorKind::Uncategorized, "unable to set blocking mode")
+ })
+ }
+}
+
+impl fmt::Debug for TcpStream {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ Ok(())
+ }
+}
+
+#[derive(Clone)]
+pub struct TcpListener(SocketAddr);
+
+impl TcpListener {
+ pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
+ let addr = addr?;
+
+ Ok(TcpListener(*addr))
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ Ok(self.0)
+ }
+
+ pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+ let (handle, ipaddr, port) = abi::tcplistener::accept(self.0.port())
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "accept failed"))?;
+ let saddr = match ipaddr {
+ Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
+ Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
+ _ => {
+ return Err(io::const_io_error!(ErrorKind::Uncategorized, "accept failed"));
+ }
+ };
+
+ Ok((TcpStream(Arc::new(Socket(handle))), saddr))
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpListener> {
+ Ok(self.clone())
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn only_v6(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+}
+
+impl fmt::Debug for TcpListener {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ Ok(())
+ }
+}
+
+pub struct UdpSocket(abi::Handle);
+
+impl UdpSocket {
+ pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+ unsupported()
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unsupported()
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unsupported()
+ }
+
+ pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn duplicate(&self) -> io::Result<UdpSocket> {
+ unsupported()
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn broadcast(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
+ unsupported()
+ }
+}
+
+impl fmt::Debug for UdpSocket {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ Ok(())
+ }
+}
+
+pub struct LookupHost(!);
+
+impl LookupHost {
+ pub fn port(&self) -> u16 {
+ self.0
+ }
+}
+
+impl Iterator for LookupHost {
+ type Item = SocketAddr;
+ fn next(&mut self) -> Option<SocketAddr> {
+ self.0
+ }
+}
+
+impl TryFrom<&str> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(_v: &str) -> io::Result<LookupHost> {
+ unsupported()
+ }
+}
+
+impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> {
+ unsupported()
+ }
+}
+
+#[allow(nonstandard_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/library/std/src/sys/hermit/os.rs b/library/std/src/sys/hermit/os.rs
new file mode 100644
index 000000000..8f927df85
--- /dev/null
+++ b/library/std/src/sys/hermit/os.rs
@@ -0,0 +1,178 @@
+use crate::collections::HashMap;
+use crate::error::Error as StdError;
+use crate::ffi::{CStr, OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::marker::PhantomData;
+use crate::os::unix::ffi::OsStringExt;
+use crate::path::{self, PathBuf};
+use crate::str;
+use crate::sync::Mutex;
+use crate::sys::hermit::abi;
+use crate::sys::memchr;
+use crate::sys::unsupported;
+use crate::vec;
+
+pub fn errno() -> i32 {
+ 0
+}
+
+pub fn error_string(_errno: i32) -> String {
+ "operation successful".to_string()
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
+ panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+ type Item = PathBuf;
+ fn next(&mut self) -> Option<PathBuf> {
+ 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 hermit yet".fmt(f)
+ }
+}
+
+impl StdError for JoinPathsError {
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "not supported on hermit yet"
+ }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+static mut ENV: Option<Mutex<HashMap<OsString, OsString>>> = None;
+
+pub fn init_environment(env: *const *const i8) {
+ unsafe {
+ ENV = Some(Mutex::new(HashMap::new()));
+
+ if env.is_null() {
+ return;
+ }
+
+ let mut guard = ENV.as_ref().unwrap().lock().unwrap();
+ let mut environ = env;
+ while !(*environ).is_null() {
+ if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) {
+ guard.insert(key, value);
+ }
+ environ = environ.add(1);
+ }
+ }
+
+ 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 struct Env {
+ iter: vec::IntoIter<(OsString, OsString)>,
+}
+
+impl !Send for Env {}
+impl !Sync for Env {}
+
+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 {
+ unsafe {
+ let guard = ENV.as_ref().unwrap().lock().unwrap();
+ let mut result = Vec::new();
+
+ for (key, value) in guard.iter() {
+ result.push((key.clone(), value.clone()));
+ }
+
+ return Env { iter: result.into_iter() };
+ }
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+ unsafe { ENV.as_ref().unwrap().lock().unwrap().get_mut(k).cloned() }
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+ unsafe {
+ let (k, v) = (k.to_owned(), v.to_owned());
+ ENV.as_ref().unwrap().lock().unwrap().insert(k, v);
+ }
+ Ok(())
+}
+
+pub fn unsetenv(k: &OsStr) -> io::Result<()> {
+ unsafe {
+ ENV.as_ref().unwrap().lock().unwrap().remove(k);
+ }
+ Ok(())
+}
+
+pub fn temp_dir() -> PathBuf {
+ panic!("no filesystem on hermit")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ None
+}
+
+pub fn exit(code: i32) -> ! {
+ unsafe {
+ abi::exit(code);
+ }
+}
+
+pub fn getpid() -> u32 {
+ unsafe { abi::getpid() }
+}
diff --git a/library/std/src/sys/hermit/rwlock.rs b/library/std/src/sys/hermit/rwlock.rs
new file mode 100644
index 000000000..9701bab1f
--- /dev/null
+++ b/library/std/src/sys/hermit/rwlock.rs
@@ -0,0 +1,144 @@
+use crate::cell::UnsafeCell;
+use crate::sys::locks::{MovableCondvar, Mutex};
+use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+
+pub struct RwLock {
+ lock: Mutex,
+ cond: MovableCondvar,
+ state: UnsafeCell<State>,
+}
+
+pub type MovableRwLock = RwLock;
+
+enum State {
+ Unlocked,
+ Reading(usize),
+ Writing,
+}
+
+unsafe impl Send for RwLock {}
+unsafe impl Sync for RwLock {}
+
+// This rwlock implementation is a relatively simple implementation which has a
+// condition variable for readers/writers as well as a mutex protecting the
+// internal state of the lock. A current downside of the implementation is that
+// unlocking the lock will notify *all* waiters rather than just readers or just
+// writers. This can cause lots of "thundering stampede" problems. While
+// hopefully correct this implementation is very likely to want to be changed in
+// the future.
+
+impl RwLock {
+ pub const fn new() -> RwLock {
+ RwLock {
+ lock: Mutex::new(),
+ cond: MovableCondvar::new(),
+ state: UnsafeCell::new(State::Unlocked),
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read(&self) {
+ self.lock.lock();
+ while !(*self.state.get()).inc_readers() {
+ self.cond.wait(&self.lock);
+ }
+ self.lock.unlock();
+ }
+
+ #[inline]
+ pub unsafe fn try_read(&self) -> bool {
+ self.lock.lock();
+ let ok = (*self.state.get()).inc_readers();
+ self.lock.unlock();
+ return ok;
+ }
+
+ #[inline]
+ pub unsafe fn write(&self) {
+ self.lock.lock();
+ while !(*self.state.get()).inc_writers() {
+ self.cond.wait(&self.lock);
+ }
+ self.lock.unlock();
+ }
+
+ #[inline]
+ pub unsafe fn try_write(&self) -> bool {
+ self.lock.lock();
+ let ok = (*self.state.get()).inc_writers();
+ self.lock.unlock();
+ return ok;
+ }
+
+ #[inline]
+ pub unsafe fn read_unlock(&self) {
+ self.lock.lock();
+ let notify = (*self.state.get()).dec_readers();
+ self.lock.unlock();
+ if notify {
+ // FIXME: should only wake up one of these some of the time
+ self.cond.notify_all();
+ }
+ }
+
+ #[inline]
+ pub unsafe fn write_unlock(&self) {
+ self.lock.lock();
+ (*self.state.get()).dec_writers();
+ self.lock.unlock();
+ // FIXME: should only wake up one of these some of the time
+ self.cond.notify_all();
+ }
+}
+
+impl State {
+ fn inc_readers(&mut self) -> bool {
+ match *self {
+ State::Unlocked => {
+ *self = State::Reading(1);
+ true
+ }
+ State::Reading(ref mut cnt) => {
+ *cnt += 1;
+ true
+ }
+ State::Writing => false,
+ }
+ }
+
+ fn inc_writers(&mut self) -> bool {
+ match *self {
+ State::Unlocked => {
+ *self = State::Writing;
+ true
+ }
+ State::Reading(_) | State::Writing => false,
+ }
+ }
+
+ fn dec_readers(&mut self) -> bool {
+ let zero = match *self {
+ State::Reading(ref mut cnt) => {
+ *cnt -= 1;
+ *cnt == 0
+ }
+ State::Unlocked | State::Writing => invalid(),
+ };
+ if zero {
+ *self = State::Unlocked;
+ }
+ zero
+ }
+
+ fn dec_writers(&mut self) {
+ match *self {
+ State::Writing => {}
+ State::Unlocked | State::Reading(_) => invalid(),
+ }
+ *self = State::Unlocked;
+ }
+}
+
+fn invalid() -> ! {
+ panic!("inconsistent rwlock");
+}
diff --git a/library/std/src/sys/hermit/stdio.rs b/library/std/src/sys/hermit/stdio.rs
new file mode 100644
index 000000000..514de1df6
--- /dev/null
+++ b/library/std/src/sys/hermit/stdio.rs
@@ -0,0 +1,120 @@
+use crate::io;
+use crate::io::{IoSlice, IoSliceMut};
+use crate::sys::hermit::abi;
+
+pub struct Stdin;
+pub struct Stdout;
+pub struct Stderr;
+
+impl Stdin {
+ pub const fn new() -> Stdin {
+ Stdin
+ }
+}
+
+impl io::Read for Stdin {
+ fn read(&mut self, data: &mut [u8]) -> io::Result<usize> {
+ self.read_vectored(&mut [IoSliceMut::new(data)])
+ }
+
+ fn read_vectored(&mut self, _data: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ Ok(0)
+ }
+
+ #[inline]
+ fn is_read_vectored(&self) -> bool {
+ true
+ }
+}
+
+impl Stdout {
+ pub const fn new() -> Stdout {
+ Stdout
+ }
+}
+
+impl io::Write for Stdout {
+ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+ let len;
+
+ unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) }
+
+ if len < 0 {
+ Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print"))
+ } else {
+ Ok(len as usize)
+ }
+ }
+
+ fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> {
+ let len;
+
+ unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) }
+
+ if len < 0 {
+ Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print"))
+ } else {
+ Ok(len as usize)
+ }
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Stderr {
+ pub const fn new() -> Stderr {
+ Stderr
+ }
+}
+
+impl io::Write for Stderr {
+ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+ let len;
+
+ unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) }
+
+ if len < 0 {
+ Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print"))
+ } else {
+ Ok(len as usize)
+ }
+ }
+
+ fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> {
+ let len;
+
+ unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) }
+
+ if len < 0 {
+ Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print"))
+ } else {
+ Ok(len as usize)
+ }
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+pub const STDIN_BUF_SIZE: usize = 0;
+
+pub fn is_ebadf(_err: &io::Error) -> bool {
+ true
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+ Some(Stderr::new())
+}
diff --git a/library/std/src/sys/hermit/thread.rs b/library/std/src/sys/hermit/thread.rs
new file mode 100644
index 000000000..e53a1fea6
--- /dev/null
+++ b/library/std/src/sys/hermit/thread.rs
@@ -0,0 +1,112 @@
+#![allow(dead_code)]
+
+use super::unsupported;
+use crate::ffi::CStr;
+use crate::io;
+use crate::mem;
+use crate::num::NonZeroUsize;
+use crate::sys::hermit::abi;
+use crate::sys::hermit::thread_local_dtor::run_dtors;
+use crate::time::Duration;
+
+pub type Tid = abi::Tid;
+
+pub struct Thread {
+ tid: Tid,
+}
+
+unsafe impl Send for Thread {}
+unsafe impl Sync for Thread {}
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 1 << 20;
+
+impl Thread {
+ pub unsafe fn new_with_coreid(
+ stack: usize,
+ p: Box<dyn FnOnce()>,
+ core_id: isize,
+ ) -> io::Result<Thread> {
+ let p = Box::into_raw(box p);
+ let tid = abi::spawn2(
+ thread_start,
+ p as usize,
+ abi::Priority::into(abi::NORMAL_PRIO),
+ stack,
+ core_id,
+ );
+
+ return if tid == 0 {
+ // The thread failed to start and as a result p was not consumed. Therefore, it is
+ // safe to reconstruct the box so that it gets deallocated.
+ drop(Box::from_raw(p));
+ Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Unable to create thread!"))
+ } else {
+ Ok(Thread { tid: tid })
+ };
+
+ extern "C" fn thread_start(main: usize) {
+ unsafe {
+ // Finally, let's run some code.
+ Box::from_raw(main as *mut Box<dyn FnOnce()>)();
+
+ // run all destructors
+ run_dtors();
+ }
+ }
+ }
+
+ pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+ Thread::new_with_coreid(stack, p, -1 /* = no specific core */)
+ }
+
+ #[inline]
+ pub fn yield_now() {
+ unsafe {
+ abi::yield_now();
+ }
+ }
+
+ #[inline]
+ pub fn set_name(_name: &CStr) {
+ // nope
+ }
+
+ #[inline]
+ pub fn sleep(dur: Duration) {
+ unsafe {
+ abi::usleep(dur.as_micros() as u64);
+ }
+ }
+
+ pub fn join(self) {
+ unsafe {
+ let _ = abi::join(self.tid);
+ }
+ }
+
+ #[inline]
+ pub fn id(&self) -> Tid {
+ self.tid
+ }
+
+ #[inline]
+ pub fn into_id(self) -> Tid {
+ let id = self.tid;
+ mem::forget(self);
+ id
+ }
+}
+
+pub fn available_parallelism() -> io::Result<NonZeroUsize> {
+ unsupported()
+}
+
+pub mod guard {
+ pub type Guard = !;
+ pub unsafe fn current() -> Option<Guard> {
+ None
+ }
+ pub unsafe fn init() -> Option<Guard> {
+ None
+ }
+}
diff --git a/library/std/src/sys/hermit/thread_local_dtor.rs b/library/std/src/sys/hermit/thread_local_dtor.rs
new file mode 100644
index 000000000..9b683fce1
--- /dev/null
+++ b/library/std/src/sys/hermit/thread_local_dtor.rs
@@ -0,0 +1,36 @@
+#![cfg(target_thread_local)]
+#![unstable(feature = "thread_local_internals", issue = "none")]
+
+// Simplify dtor registration by using a list of destructors.
+// The this solution works like the implementation of macOS and
+// doesn't additional OS support
+
+use crate::cell::Cell;
+use crate::ptr;
+
+#[thread_local]
+static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
+
+type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
+
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+ if DTORS.get().is_null() {
+ let v: Box<List> = box Vec::new();
+ DTORS.set(Box::into_raw(v));
+ }
+
+ let list: &mut List = &mut *DTORS.get();
+ list.push((t, dtor));
+}
+
+// every thread call this function to run through all possible destructors
+pub unsafe fn run_dtors() {
+ let mut ptr = DTORS.replace(ptr::null_mut());
+ while !ptr.is_null() {
+ let list = Box::from_raw(ptr);
+ for (ptr, dtor) in list.into_iter() {
+ dtor(ptr);
+ }
+ ptr = DTORS.replace(ptr::null_mut());
+ }
+}
diff --git a/library/std/src/sys/hermit/time.rs b/library/std/src/sys/hermit/time.rs
new file mode 100644
index 000000000..c17e6c8af
--- /dev/null
+++ b/library/std/src/sys/hermit/time.rs
@@ -0,0 +1,156 @@
+#![allow(dead_code)]
+
+use crate::cmp::Ordering;
+use crate::sys::hermit::abi;
+use crate::sys::hermit::abi::timespec;
+use crate::sys::hermit::abi::{CLOCK_MONOTONIC, CLOCK_REALTIME, NSEC_PER_SEC};
+use crate::time::Duration;
+use core::hash::{Hash, Hasher};
+
+#[derive(Copy, Clone, Debug)]
+struct Timespec {
+ t: timespec,
+}
+
+impl Timespec {
+ const fn zero() -> Timespec {
+ Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } }
+ }
+
+ 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 checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
+ 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))?;
+
+ // 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)?;
+ }
+ Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } })
+ }
+
+ fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
+ 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))?;
+
+ // 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)?;
+ }
+ Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } })
+ }
+}
+
+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, Debug, Hash)]
+pub struct Instant {
+ t: Timespec,
+}
+
+impl Instant {
+ pub fn now() -> Instant {
+ let mut time: Timespec = Timespec::zero();
+ let _ = unsafe { abi::clock_gettime(CLOCK_MONOTONIC, &mut time.t as *mut timespec) };
+
+ Instant { t: time }
+ }
+
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ self.t.sub_timespec(&other.t).ok()
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_add_duration(other)? })
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_sub_duration(other)? })
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+pub struct SystemTime {
+ t: Timespec,
+}
+
+pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
+
+impl SystemTime {
+ pub fn now() -> SystemTime {
+ let mut time: Timespec = Timespec::zero();
+ let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, &mut time.t as *mut timespec) };
+
+ SystemTime { t: time }
+ }
+
+ pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
+ self.t.sub_timespec(&other.t)
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime { t: self.t.checked_add_duration(other)? })
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime { t: self.t.checked_sub_duration(other)? })
+ }
+}
diff --git a/library/std/src/sys/itron/abi.rs b/library/std/src/sys/itron/abi.rs
new file mode 100644
index 000000000..5eb14bb7e
--- /dev/null
+++ b/library/std/src/sys/itron/abi.rs
@@ -0,0 +1,197 @@
+//! ABI for μITRON derivatives
+pub type int_t = crate::os::raw::c_int;
+pub type uint_t = crate::os::raw::c_uint;
+pub type bool_t = int_t;
+
+/// Kernel object ID
+pub type ID = int_t;
+
+/// The current task.
+pub const TSK_SELF: ID = 0;
+
+/// Relative time
+pub type RELTIM = u32;
+
+/// Timeout (a valid `RELTIM` value or `TMO_FEVR`)
+pub type TMO = u32;
+
+/// The infinite timeout value
+pub const TMO_FEVR: TMO = TMO::MAX;
+
+/// The maximum valid value of `RELTIM`
+pub const TMAX_RELTIM: RELTIM = 4_000_000_000;
+
+/// System time
+pub type SYSTIM = u64;
+
+/// Error code type
+pub type ER = int_t;
+
+/// Error code type, `ID` on success
+pub type ER_ID = int_t;
+
+/// Service call operational mode
+pub type MODE = uint_t;
+
+/// OR waiting condition for an eventflag
+pub const TWF_ORW: MODE = 0x01;
+
+/// Object attributes
+pub type ATR = uint_t;
+
+/// FIFO wait order
+pub const TA_FIFO: ATR = 0;
+/// Only one task is allowed to be in the waiting state for the eventflag
+pub const TA_WSGL: ATR = 0;
+/// The eventflag’s bit pattern is cleared when a task is released from the
+/// waiting state for that eventflag.
+pub const TA_CLR: ATR = 0x04;
+
+/// Bit pattern of an eventflag
+pub type FLGPTN = uint_t;
+
+/// Task or interrupt priority
+pub type PRI = int_t;
+
+/// The special value of `PRI` representing the current task's priority.
+pub const TPRI_SELF: PRI = 0;
+
+/// Use the priority inheritance protocol
+#[cfg(target_os = "solid_asp3")]
+pub const TA_INHERIT: ATR = 0x02;
+
+/// Activate the task on creation
+pub const TA_ACT: ATR = 0x01;
+
+/// The maximum count of a semaphore
+pub const TMAX_MAXSEM: uint_t = uint_t::MAX;
+
+/// Callback parameter
+pub type EXINF = isize;
+
+/// Task entrypoint
+pub type TASK = Option<unsafe extern "C" fn(EXINF)>;
+
+// Error codes
+pub const E_OK: ER = 0;
+pub const E_SYS: ER = -5;
+pub const E_NOSPT: ER = -9;
+pub const E_RSFN: ER = -10;
+pub const E_RSATR: ER = -11;
+pub const E_PAR: ER = -17;
+pub const E_ID: ER = -18;
+pub const E_CTX: ER = -25;
+pub const E_MACV: ER = -26;
+pub const E_OACV: ER = -27;
+pub const E_ILUSE: ER = -28;
+pub const E_NOMEM: ER = -33;
+pub const E_NOID: ER = -34;
+pub const E_NORES: ER = -35;
+pub const E_OBJ: ER = -41;
+pub const E_NOEXS: ER = -42;
+pub const E_QOVR: ER = -43;
+pub const E_RLWAI: ER = -49;
+pub const E_TMOUT: ER = -50;
+pub const E_DLT: ER = -51;
+pub const E_CLS: ER = -52;
+pub const E_RASTER: ER = -53;
+pub const E_WBLK: ER = -57;
+pub const E_BOVR: ER = -58;
+pub const E_COMM: ER = -65;
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct T_CSEM {
+ pub sematr: ATR,
+ pub isemcnt: uint_t,
+ pub maxsem: uint_t,
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct T_CFLG {
+ pub flgatr: ATR,
+ pub iflgptn: FLGPTN,
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct T_CMTX {
+ pub mtxatr: ATR,
+ pub ceilpri: PRI,
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct T_CTSK {
+ pub tskatr: ATR,
+ pub exinf: EXINF,
+ pub task: TASK,
+ pub itskpri: PRI,
+ pub stksz: usize,
+ pub stk: *mut u8,
+}
+
+extern "C" {
+ #[link_name = "__asp3_acre_tsk"]
+ pub fn acre_tsk(pk_ctsk: *const T_CTSK) -> ER_ID;
+ #[link_name = "__asp3_get_tid"]
+ pub fn get_tid(p_tskid: *mut ID) -> ER;
+ #[link_name = "__asp3_dly_tsk"]
+ pub fn dly_tsk(dlytim: RELTIM) -> ER;
+ #[link_name = "__asp3_ter_tsk"]
+ pub fn ter_tsk(tskid: ID) -> ER;
+ #[link_name = "__asp3_del_tsk"]
+ pub fn del_tsk(tskid: ID) -> ER;
+ #[link_name = "__asp3_get_pri"]
+ pub fn get_pri(tskid: ID, p_tskpri: *mut PRI) -> ER;
+ #[link_name = "__asp3_rot_rdq"]
+ pub fn rot_rdq(tskpri: PRI) -> ER;
+ #[link_name = "__asp3_slp_tsk"]
+ pub fn slp_tsk() -> ER;
+ #[link_name = "__asp3_tslp_tsk"]
+ pub fn tslp_tsk(tmout: TMO) -> ER;
+ #[link_name = "__asp3_wup_tsk"]
+ pub fn wup_tsk(tskid: ID) -> ER;
+ #[link_name = "__asp3_unl_cpu"]
+ pub fn unl_cpu() -> ER;
+ #[link_name = "__asp3_dis_dsp"]
+ pub fn dis_dsp() -> ER;
+ #[link_name = "__asp3_ena_dsp"]
+ pub fn ena_dsp() -> ER;
+ #[link_name = "__asp3_sns_dsp"]
+ pub fn sns_dsp() -> bool_t;
+ #[link_name = "__asp3_get_tim"]
+ pub fn get_tim(p_systim: *mut SYSTIM) -> ER;
+ #[link_name = "__asp3_acre_flg"]
+ pub fn acre_flg(pk_cflg: *const T_CFLG) -> ER_ID;
+ #[link_name = "__asp3_del_flg"]
+ pub fn del_flg(flgid: ID) -> ER;
+ #[link_name = "__asp3_set_flg"]
+ pub fn set_flg(flgid: ID, setptn: FLGPTN) -> ER;
+ #[link_name = "__asp3_clr_flg"]
+ pub fn clr_flg(flgid: ID, clrptn: FLGPTN) -> ER;
+ #[link_name = "__asp3_wai_flg"]
+ pub fn wai_flg(flgid: ID, waiptn: FLGPTN, wfmode: MODE, p_flgptn: *mut FLGPTN) -> ER;
+ #[link_name = "__asp3_twai_flg"]
+ pub fn twai_flg(
+ flgid: ID,
+ waiptn: FLGPTN,
+ wfmode: MODE,
+ p_flgptn: *mut FLGPTN,
+ tmout: TMO,
+ ) -> ER;
+ #[link_name = "__asp3_acre_mtx"]
+ pub fn acre_mtx(pk_cmtx: *const T_CMTX) -> ER_ID;
+ #[link_name = "__asp3_del_mtx"]
+ pub fn del_mtx(tskid: ID) -> ER;
+ #[link_name = "__asp3_loc_mtx"]
+ pub fn loc_mtx(mtxid: ID) -> ER;
+ #[link_name = "__asp3_ploc_mtx"]
+ pub fn ploc_mtx(mtxid: ID) -> ER;
+ #[link_name = "__asp3_tloc_mtx"]
+ pub fn tloc_mtx(mtxid: ID, tmout: TMO) -> ER;
+ #[link_name = "__asp3_unl_mtx"]
+ pub fn unl_mtx(mtxid: ID) -> ER;
+ pub fn exd_tsk() -> ER;
+}
diff --git a/library/std/src/sys/itron/condvar.rs b/library/std/src/sys/itron/condvar.rs
new file mode 100644
index 000000000..008cd8fb1
--- /dev/null
+++ b/library/std/src/sys/itron/condvar.rs
@@ -0,0 +1,297 @@
+//! POSIX conditional variable implementation based on user-space wait queues.
+use super::{abi, error::expect_success_aborting, spin::SpinMutex, task, time::with_tmos_strong};
+use crate::{mem::replace, ptr::NonNull, sys::locks::Mutex, time::Duration};
+
+// The implementation is inspired by the queue-based implementation shown in
+// Andrew D. Birrell's paper "Implementing Condition Variables with Semaphores"
+
+pub struct Condvar {
+ waiters: SpinMutex<waiter_queue::WaiterQueue>,
+}
+
+unsafe impl Send for Condvar {}
+unsafe impl Sync for Condvar {}
+
+pub type MovableCondvar = Condvar;
+
+impl Condvar {
+ #[inline]
+ pub const fn new() -> Condvar {
+ Condvar { waiters: SpinMutex::new(waiter_queue::WaiterQueue::new()) }
+ }
+
+ #[inline]
+ pub unsafe fn init(&mut self) {}
+
+ pub unsafe fn notify_one(&self) {
+ self.waiters.with_locked(|waiters| {
+ if let Some(task) = waiters.pop_front() {
+ // Unpark the task
+ match unsafe { abi::wup_tsk(task) } {
+ // The task already has a token.
+ abi::E_QOVR => {}
+ // Can't undo the effect; abort the program on failure
+ er => {
+ expect_success_aborting(er, &"wup_tsk");
+ }
+ }
+ }
+ });
+ }
+
+ pub unsafe fn notify_all(&self) {
+ self.waiters.with_locked(|waiters| {
+ while let Some(task) = waiters.pop_front() {
+ // Unpark the task
+ match unsafe { abi::wup_tsk(task) } {
+ // The task already has a token.
+ abi::E_QOVR => {}
+ // Can't undo the effect; abort the program on failure
+ er => {
+ expect_success_aborting(er, &"wup_tsk");
+ }
+ }
+ }
+ });
+ }
+
+ pub unsafe fn wait(&self, mutex: &Mutex) {
+ // Construct `Waiter`.
+ let mut waiter = waiter_queue::Waiter::new();
+ let waiter = NonNull::from(&mut waiter);
+
+ self.waiters.with_locked(|waiters| unsafe {
+ waiters.insert(waiter);
+ });
+
+ unsafe { mutex.unlock() };
+
+ // Wait until `waiter` is removed from the queue
+ loop {
+ // Park the current task
+ expect_success_aborting(unsafe { abi::slp_tsk() }, &"slp_tsk");
+
+ if !self.waiters.with_locked(|waiters| unsafe { waiters.is_queued(waiter) }) {
+ break;
+ }
+ }
+
+ unsafe { mutex.lock() };
+ }
+
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ // Construct and pin `Waiter`
+ let mut waiter = waiter_queue::Waiter::new();
+ let waiter = NonNull::from(&mut waiter);
+
+ self.waiters.with_locked(|waiters| unsafe {
+ waiters.insert(waiter);
+ });
+
+ unsafe { mutex.unlock() };
+
+ // Park the current task and do not wake up until the timeout elapses
+ // or the task gets woken up by `notify_*`
+ match with_tmos_strong(dur, |tmo| {
+ let er = unsafe { abi::tslp_tsk(tmo) };
+ if er == 0 {
+ // We were unparked. Are we really dequeued?
+ if self.waiters.with_locked(|waiters| unsafe { waiters.is_queued(waiter) }) {
+ // No we are not. Continue waiting.
+ return abi::E_TMOUT;
+ }
+ }
+ er
+ }) {
+ abi::E_TMOUT => {}
+ er => {
+ expect_success_aborting(er, &"tslp_tsk");
+ }
+ }
+
+ // Remove `waiter` from `self.waiters`. If `waiter` is still in
+ // `waiters`, it means we woke up because of a timeout. Otherwise,
+ // we woke up because of `notify_*`.
+ let success = self.waiters.with_locked(|waiters| unsafe { !waiters.remove(waiter) });
+
+ unsafe { mutex.lock() };
+ success
+ }
+}
+
+mod waiter_queue {
+ use super::*;
+
+ pub struct WaiterQueue {
+ head: Option<ListHead>,
+ }
+
+ #[derive(Copy, Clone)]
+ struct ListHead {
+ first: NonNull<Waiter>,
+ last: NonNull<Waiter>,
+ }
+
+ unsafe impl Send for ListHead {}
+ unsafe impl Sync for ListHead {}
+
+ pub struct Waiter {
+ // These fields are only accessed through `&[mut] WaiterQueue`.
+ /// The waiting task's ID. Will be zeroed when the task is woken up
+ /// and removed from a queue.
+ task: abi::ID,
+ priority: abi::PRI,
+ prev: Option<NonNull<Waiter>>,
+ next: Option<NonNull<Waiter>>,
+ }
+
+ unsafe impl Send for Waiter {}
+ unsafe impl Sync for Waiter {}
+
+ impl Waiter {
+ #[inline]
+ pub fn new() -> Self {
+ let task = task::current_task_id();
+ let priority = task::task_priority(abi::TSK_SELF);
+
+ // Zeroness of `Waiter::task` indicates whether the `Waiter` is
+ // linked to a queue or not. This invariant is important for
+ // the correctness.
+ debug_assert_ne!(task, 0);
+
+ Self { task, priority, prev: None, next: None }
+ }
+ }
+
+ impl WaiterQueue {
+ #[inline]
+ pub const fn new() -> Self {
+ Self { head: None }
+ }
+
+ /// # Safety
+ ///
+ /// - The caller must own `*waiter_ptr`. The caller will lose the
+ /// ownership until `*waiter_ptr` is removed from `self`.
+ ///
+ /// - `*waiter_ptr` must be valid until it's removed from the queue.
+ ///
+ /// - `*waiter_ptr` must not have been previously inserted to a `WaiterQueue`.
+ ///
+ pub unsafe fn insert(&mut self, mut waiter_ptr: NonNull<Waiter>) {
+ unsafe {
+ let waiter = waiter_ptr.as_mut();
+
+ debug_assert!(waiter.prev.is_none());
+ debug_assert!(waiter.next.is_none());
+
+ if let Some(head) = &mut self.head {
+ // Find the insertion position and insert `waiter`
+ let insert_after = {
+ let mut cursor = head.last;
+ loop {
+ if waiter.priority >= cursor.as_ref().priority {
+ // `cursor` and all previous waiters have the same or higher
+ // priority than `current_task_priority`. Insert the new
+ // waiter right after `cursor`.
+ break Some(cursor);
+ }
+ cursor = if let Some(prev) = cursor.as_ref().prev {
+ prev
+ } else {
+ break None;
+ };
+ }
+ };
+
+ if let Some(mut insert_after) = insert_after {
+ // Insert `waiter` after `insert_after`
+ let insert_before = insert_after.as_ref().next;
+
+ waiter.prev = Some(insert_after);
+ insert_after.as_mut().next = Some(waiter_ptr);
+
+ waiter.next = insert_before;
+ if let Some(mut insert_before) = insert_before {
+ insert_before.as_mut().prev = Some(waiter_ptr);
+ } else {
+ head.last = waiter_ptr;
+ }
+ } else {
+ // Insert `waiter` to the front
+ waiter.next = Some(head.first);
+ head.first.as_mut().prev = Some(waiter_ptr);
+ head.first = waiter_ptr;
+ }
+ } else {
+ // `waiter` is the only element
+ self.head = Some(ListHead { first: waiter_ptr, last: waiter_ptr });
+ }
+ }
+ }
+
+ /// Given a `Waiter` that was previously inserted to `self`, remove
+ /// it from `self` if it's still there.
+ #[inline]
+ pub unsafe fn remove(&mut self, mut waiter_ptr: NonNull<Waiter>) -> bool {
+ unsafe {
+ let waiter = waiter_ptr.as_mut();
+ if waiter.task != 0 {
+ let head = self.head.as_mut().unwrap();
+
+ match (waiter.prev, waiter.next) {
+ (Some(mut prev), Some(mut next)) => {
+ prev.as_mut().next = Some(next);
+ next.as_mut().prev = Some(prev);
+ }
+ (None, Some(mut next)) => {
+ head.first = next;
+ next.as_mut().prev = None;
+ }
+ (Some(mut prev), None) => {
+ prev.as_mut().next = None;
+ head.last = prev;
+ }
+ (None, None) => {
+ self.head = None;
+ }
+ }
+
+ waiter.task = 0;
+
+ true
+ } else {
+ false
+ }
+ }
+ }
+
+ /// Given a `Waiter` that was previously inserted to `self`, return a
+ /// flag indicating whether it's still in `self`.
+ #[inline]
+ pub unsafe fn is_queued(&self, waiter: NonNull<Waiter>) -> bool {
+ unsafe { waiter.as_ref().task != 0 }
+ }
+
+ #[inline]
+ pub fn pop_front(&mut self) -> Option<abi::ID> {
+ unsafe {
+ let head = self.head.as_mut()?;
+ let waiter = head.first.as_mut();
+
+ // Get the ID
+ let id = replace(&mut waiter.task, 0);
+
+ // Unlink the waiter
+ if let Some(mut next) = waiter.next {
+ head.first = next;
+ next.as_mut().prev = None;
+ } else {
+ self.head = None;
+ }
+
+ Some(id)
+ }
+ }
+ }
+}
diff --git a/library/std/src/sys/itron/error.rs b/library/std/src/sys/itron/error.rs
new file mode 100644
index 000000000..830c60d32
--- /dev/null
+++ b/library/std/src/sys/itron/error.rs
@@ -0,0 +1,159 @@
+use crate::{fmt, io::ErrorKind};
+
+use super::abi;
+
+/// Wraps a μITRON error code.
+#[derive(Debug, Copy, Clone)]
+pub struct ItronError {
+ er: abi::ER,
+}
+
+impl ItronError {
+ /// Construct `ItronError` from the specified error code. Returns `None` if the
+ /// error code does not represent a failure or warning.
+ #[inline]
+ pub fn new(er: abi::ER) -> Option<Self> {
+ if er < 0 { Some(Self { er }) } else { None }
+ }
+
+ /// Returns `Ok(er)` if `er` represents a success or `Err(_)` otherwise.
+ #[inline]
+ pub fn err_if_negative(er: abi::ER) -> Result<abi::ER, Self> {
+ if let Some(error) = Self::new(er) { Err(error) } else { Ok(er) }
+ }
+
+ /// Get the raw error code.
+ #[inline]
+ pub fn as_raw(&self) -> abi::ER {
+ self.er
+ }
+}
+
+impl fmt::Display for ItronError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Allow the platforms to extend `error_name`
+ if let Some(name) = crate::sys::error::error_name(self.er) {
+ write!(f, "{} ({})", name, self.er)
+ } else {
+ write!(f, "{}", self.er)
+ }
+ }
+}
+
+/// Describe the specified μITRON error code. Returns `None` if it's an
+/// undefined error code.
+pub fn error_name(er: abi::ER) -> Option<&'static str> {
+ match er {
+ // Success
+ er if er >= 0 => None,
+
+ // μITRON 4.0
+ abi::E_SYS => Some("system error"),
+ abi::E_NOSPT => Some("unsupported function"),
+ abi::E_RSFN => Some("reserved function code"),
+ abi::E_RSATR => Some("reserved attribute"),
+ abi::E_PAR => Some("parameter error"),
+ abi::E_ID => Some("invalid ID number"),
+ abi::E_CTX => Some("context error"),
+ abi::E_MACV => Some("memory access violation"),
+ abi::E_OACV => Some("object access violation"),
+ abi::E_ILUSE => Some("illegal service call use"),
+ abi::E_NOMEM => Some("insufficient memory"),
+ abi::E_NOID => Some("no ID number available"),
+ abi::E_OBJ => Some("object state error"),
+ abi::E_NOEXS => Some("non-existent object"),
+ abi::E_QOVR => Some("queue overflow"),
+ abi::E_RLWAI => Some("forced release from waiting"),
+ abi::E_TMOUT => Some("polling failure or timeout"),
+ abi::E_DLT => Some("waiting object deleted"),
+ abi::E_CLS => Some("waiting object state changed"),
+ abi::E_WBLK => Some("non-blocking code accepted"),
+ abi::E_BOVR => Some("buffer overflow"),
+
+ // The TOPPERS third generation kernels
+ abi::E_NORES => Some("insufficient system resources"),
+ abi::E_RASTER => Some("termination request raised"),
+ abi::E_COMM => Some("communication failure"),
+
+ _ => None,
+ }
+}
+
+pub fn decode_error_kind(er: abi::ER) -> ErrorKind {
+ match er {
+ // Success
+ er if er >= 0 => ErrorKind::Uncategorized,
+
+ // μITRON 4.0
+ // abi::E_SYS
+ abi::E_NOSPT => ErrorKind::Unsupported, // Some("unsupported function"),
+ abi::E_RSFN => ErrorKind::InvalidInput, // Some("reserved function code"),
+ abi::E_RSATR => ErrorKind::InvalidInput, // Some("reserved attribute"),
+ abi::E_PAR => ErrorKind::InvalidInput, // Some("parameter error"),
+ abi::E_ID => ErrorKind::NotFound, // Some("invalid ID number"),
+ // abi::E_CTX
+ abi::E_MACV => ErrorKind::PermissionDenied, // Some("memory access violation"),
+ abi::E_OACV => ErrorKind::PermissionDenied, // Some("object access violation"),
+ // abi::E_ILUSE
+ abi::E_NOMEM => ErrorKind::OutOfMemory, // Some("insufficient memory"),
+ abi::E_NOID => ErrorKind::OutOfMemory, // Some("no ID number available"),
+ // abi::E_OBJ
+ abi::E_NOEXS => ErrorKind::NotFound, // Some("non-existent object"),
+ // abi::E_QOVR
+ abi::E_RLWAI => ErrorKind::Interrupted, // Some("forced release from waiting"),
+ abi::E_TMOUT => ErrorKind::TimedOut, // Some("polling failure or timeout"),
+ // abi::E_DLT
+ // abi::E_CLS
+ // abi::E_WBLK
+ // abi::E_BOVR
+
+ // The TOPPERS third generation kernels
+ abi::E_NORES => ErrorKind::OutOfMemory, // Some("insufficient system resources"),
+ // abi::E_RASTER
+ // abi::E_COMM
+ _ => ErrorKind::Uncategorized,
+ }
+}
+
+/// Similar to `ItronError::err_if_negative(er).expect()` except that, while
+/// panicking, it prints the message to `panic_output` and aborts the program
+/// instead. This ensures the error message is not obscured by double
+/// panicking.
+///
+/// This is useful for diagnosing creation failures of synchronization
+/// primitives that are used by `std`'s internal mechanisms. Such failures
+/// are common when the system is mis-configured to provide a too-small pool for
+/// kernel objects.
+#[inline]
+pub fn expect_success(er: abi::ER, msg: &&str) -> abi::ER {
+ match ItronError::err_if_negative(er) {
+ Ok(x) => x,
+ Err(e) => fail(e, msg),
+ }
+}
+
+/// Similar to `ItronError::err_if_negative(er).expect()` but aborts instead.
+///
+/// Use this where panicking is not allowed or the effect of the failure
+/// would be persistent.
+#[inline]
+pub fn expect_success_aborting(er: abi::ER, msg: &&str) -> abi::ER {
+ match ItronError::err_if_negative(er) {
+ Ok(x) => x,
+ Err(e) => fail_aborting(e, msg),
+ }
+}
+
+#[cold]
+pub fn fail(e: impl fmt::Display, msg: &&str) -> ! {
+ if crate::thread::panicking() {
+ fail_aborting(e, msg)
+ } else {
+ panic!("{} failed: {}", *msg, e)
+ }
+}
+
+#[cold]
+pub fn fail_aborting(e: impl fmt::Display, msg: &&str) -> ! {
+ rtabort!("{} failed: {}", *msg, e)
+}
diff --git a/library/std/src/sys/itron/mutex.rs b/library/std/src/sys/itron/mutex.rs
new file mode 100644
index 000000000..715e94c3b
--- /dev/null
+++ b/library/std/src/sys/itron/mutex.rs
@@ -0,0 +1,93 @@
+//! Mutex implementation backed by μITRON mutexes. Assumes `acre_mtx` and
+//! `TA_INHERIT` are available.
+use super::{
+ abi,
+ error::{expect_success, expect_success_aborting, fail, ItronError},
+ spin::SpinIdOnceCell,
+};
+
+pub struct Mutex {
+ /// The ID of the underlying mutex object
+ mtx: SpinIdOnceCell<()>,
+}
+
+pub type MovableMutex = Mutex;
+
+/// Create a mutex object. This function never panics.
+fn new_mtx() -> Result<abi::ID, ItronError> {
+ ItronError::err_if_negative(unsafe {
+ abi::acre_mtx(&abi::T_CMTX {
+ // Priority inheritance mutex
+ mtxatr: abi::TA_INHERIT,
+ // Unused
+ ceilpri: 0,
+ })
+ })
+}
+
+impl Mutex {
+ #[inline]
+ pub const fn new() -> Mutex {
+ Mutex { mtx: SpinIdOnceCell::new() }
+ }
+
+ pub unsafe fn init(&mut self) {
+ // Initialize `self.mtx` eagerly
+ let id = new_mtx().unwrap_or_else(|e| fail(e, &"acre_mtx"));
+ unsafe { self.mtx.set_unchecked((id, ())) };
+ }
+
+ /// Get the inner mutex's ID, which is lazily created.
+ fn raw(&self) -> abi::ID {
+ match self.mtx.get_or_try_init(|| new_mtx().map(|id| (id, ()))) {
+ Ok((id, ())) => id,
+ Err(e) => fail(e, &"acre_mtx"),
+ }
+ }
+
+ pub unsafe fn lock(&self) {
+ let mtx = self.raw();
+ expect_success(unsafe { abi::loc_mtx(mtx) }, &"loc_mtx");
+ }
+
+ pub unsafe fn unlock(&self) {
+ let mtx = unsafe { self.mtx.get_unchecked().0 };
+ expect_success_aborting(unsafe { abi::unl_mtx(mtx) }, &"unl_mtx");
+ }
+
+ pub unsafe fn try_lock(&self) -> bool {
+ let mtx = self.raw();
+ match unsafe { abi::ploc_mtx(mtx) } {
+ abi::E_TMOUT => false,
+ er => {
+ expect_success(er, &"ploc_mtx");
+ true
+ }
+ }
+ }
+}
+
+impl Drop for Mutex {
+ fn drop(&mut self) {
+ if let Some(mtx) = self.mtx.get().map(|x| x.0) {
+ expect_success_aborting(unsafe { abi::del_mtx(mtx) }, &"del_mtx");
+ }
+ }
+}
+
+pub(super) struct MutexGuard<'a>(&'a Mutex);
+
+impl<'a> MutexGuard<'a> {
+ #[inline]
+ pub(super) fn lock(x: &'a Mutex) -> Self {
+ unsafe { x.lock() };
+ Self(x)
+ }
+}
+
+impl Drop for MutexGuard<'_> {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe { self.0.unlock() };
+ }
+}
diff --git a/library/std/src/sys/itron/spin.rs b/library/std/src/sys/itron/spin.rs
new file mode 100644
index 000000000..44d409444
--- /dev/null
+++ b/library/std/src/sys/itron/spin.rs
@@ -0,0 +1,163 @@
+use super::abi;
+use crate::{
+ cell::UnsafeCell,
+ mem::MaybeUninit,
+ sync::atomic::{AtomicBool, AtomicUsize, Ordering},
+};
+
+/// A mutex implemented by `dis_dsp` (for intra-core synchronization) and a
+/// spinlock (for inter-core synchronization).
+pub struct SpinMutex<T = ()> {
+ locked: AtomicBool,
+ data: UnsafeCell<T>,
+}
+
+impl<T> SpinMutex<T> {
+ #[inline]
+ pub const fn new(x: T) -> Self {
+ Self { locked: AtomicBool::new(false), data: UnsafeCell::new(x) }
+ }
+
+ /// Acquire a lock.
+ #[inline]
+ pub fn with_locked<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
+ struct SpinMutexGuard<'a>(&'a AtomicBool);
+
+ impl Drop for SpinMutexGuard<'_> {
+ #[inline]
+ fn drop(&mut self) {
+ self.0.store(false, Ordering::Release);
+ unsafe { abi::ena_dsp() };
+ }
+ }
+
+ let _guard;
+ if unsafe { abi::sns_dsp() } == 0 {
+ let er = unsafe { abi::dis_dsp() };
+ debug_assert!(er >= 0);
+
+ // Wait until the current processor acquires a lock.
+ while self.locked.swap(true, Ordering::Acquire) {}
+
+ _guard = SpinMutexGuard(&self.locked);
+ }
+
+ f(unsafe { &mut *self.data.get() })
+ }
+}
+
+/// `OnceCell<(abi::ID, T)>` implemented by `dis_dsp` (for intra-core
+/// synchronization) and a spinlock (for inter-core synchronization).
+///
+/// It's assumed that `0` is not a valid ID, and all kernel
+/// object IDs fall into range `1..=usize::MAX`.
+pub struct SpinIdOnceCell<T = ()> {
+ id: AtomicUsize,
+ spin: SpinMutex<()>,
+ extra: UnsafeCell<MaybeUninit<T>>,
+}
+
+const ID_UNINIT: usize = 0;
+
+impl<T> SpinIdOnceCell<T> {
+ #[inline]
+ pub const fn new() -> Self {
+ Self {
+ id: AtomicUsize::new(ID_UNINIT),
+ extra: UnsafeCell::new(MaybeUninit::uninit()),
+ spin: SpinMutex::new(()),
+ }
+ }
+
+ #[inline]
+ pub fn get(&self) -> Option<(abi::ID, &T)> {
+ match self.id.load(Ordering::Acquire) {
+ ID_UNINIT => None,
+ id => Some((id as abi::ID, unsafe { (&*self.extra.get()).assume_init_ref() })),
+ }
+ }
+
+ #[inline]
+ pub fn get_mut(&mut self) -> Option<(abi::ID, &mut T)> {
+ match *self.id.get_mut() {
+ ID_UNINIT => None,
+ id => Some((id as abi::ID, unsafe { (&mut *self.extra.get()).assume_init_mut() })),
+ }
+ }
+
+ #[inline]
+ pub unsafe fn get_unchecked(&self) -> (abi::ID, &T) {
+ (self.id.load(Ordering::Acquire) as abi::ID, unsafe {
+ (&*self.extra.get()).assume_init_ref()
+ })
+ }
+
+ /// Assign the content without checking if it's already initialized or
+ /// being initialized.
+ pub unsafe fn set_unchecked(&self, (id, extra): (abi::ID, T)) {
+ debug_assert!(self.get().is_none());
+
+ // Assumption: A positive `abi::ID` fits in `usize`.
+ debug_assert!(id >= 0);
+ debug_assert!(usize::try_from(id).is_ok());
+ let id = id as usize;
+
+ unsafe { *self.extra.get() = MaybeUninit::new(extra) };
+ self.id.store(id, Ordering::Release);
+ }
+
+ /// Gets the contents of the cell, initializing it with `f` if
+ /// the cell was empty. If the cell was empty and `f` failed, an
+ /// error is returned.
+ ///
+ /// Warning: `f` must not perform a blocking operation, which
+ /// includes panicking.
+ #[inline]
+ pub fn get_or_try_init<F, E>(&self, f: F) -> Result<(abi::ID, &T), E>
+ where
+ F: FnOnce() -> Result<(abi::ID, T), E>,
+ {
+ // Fast path
+ if let Some(x) = self.get() {
+ return Ok(x);
+ }
+
+ self.initialize(f)?;
+
+ debug_assert!(self.get().is_some());
+
+ // Safety: The inner value has been initialized
+ Ok(unsafe { self.get_unchecked() })
+ }
+
+ fn initialize<F, E>(&self, f: F) -> Result<(), E>
+ where
+ F: FnOnce() -> Result<(abi::ID, T), E>,
+ {
+ self.spin.with_locked(|_| {
+ if self.id.load(Ordering::Relaxed) == ID_UNINIT {
+ let (initialized_id, initialized_extra) = f()?;
+
+ // Assumption: A positive `abi::ID` fits in `usize`.
+ debug_assert!(initialized_id >= 0);
+ debug_assert!(usize::try_from(initialized_id).is_ok());
+ let initialized_id = initialized_id as usize;
+
+ // Store the initialized contents. Use the release ordering to
+ // make sure the write is visible to the callers of `get`.
+ unsafe { *self.extra.get() = MaybeUninit::new(initialized_extra) };
+ self.id.store(initialized_id, Ordering::Release);
+ }
+ Ok(())
+ })
+ }
+}
+
+impl<T> Drop for SpinIdOnceCell<T> {
+ #[inline]
+ fn drop(&mut self) {
+ if self.get_mut().is_some() {
+ unsafe { (&mut *self.extra.get()).assume_init_drop() };
+ }
+ }
+}
diff --git a/library/std/src/sys/itron/task.rs b/library/std/src/sys/itron/task.rs
new file mode 100644
index 000000000..94beb50a2
--- /dev/null
+++ b/library/std/src/sys/itron/task.rs
@@ -0,0 +1,44 @@
+use super::{
+ abi,
+ error::{fail, fail_aborting, ItronError},
+};
+
+use crate::mem::MaybeUninit;
+
+/// Get the ID of the task in Running state. Panics on failure.
+#[inline]
+pub fn current_task_id() -> abi::ID {
+ try_current_task_id().unwrap_or_else(|e| fail(e, &"get_tid"))
+}
+
+/// Get the ID of the task in Running state. Aborts on failure.
+#[inline]
+pub fn current_task_id_aborting() -> abi::ID {
+ try_current_task_id().unwrap_or_else(|e| fail_aborting(e, &"get_tid"))
+}
+
+/// Get the ID of the task in Running state.
+#[inline]
+pub fn try_current_task_id() -> Result<abi::ID, ItronError> {
+ unsafe {
+ let mut out = MaybeUninit::uninit();
+ ItronError::err_if_negative(abi::get_tid(out.as_mut_ptr()))?;
+ Ok(out.assume_init())
+ }
+}
+
+/// Get the specified task's priority. Panics on failure.
+#[inline]
+pub fn task_priority(task: abi::ID) -> abi::PRI {
+ try_task_priority(task).unwrap_or_else(|e| fail(e, &"get_pri"))
+}
+
+/// Get the specified task's priority.
+#[inline]
+pub fn try_task_priority(task: abi::ID) -> Result<abi::PRI, ItronError> {
+ unsafe {
+ let mut out = MaybeUninit::uninit();
+ ItronError::err_if_negative(abi::get_pri(task, out.as_mut_ptr()))?;
+ Ok(out.assume_init())
+ }
+}
diff --git a/library/std/src/sys/itron/thread.rs b/library/std/src/sys/itron/thread.rs
new file mode 100644
index 000000000..d28f57f33
--- /dev/null
+++ b/library/std/src/sys/itron/thread.rs
@@ -0,0 +1,349 @@
+//! Thread implementation backed by μITRON tasks. Assumes `acre_tsk` and
+//! `exd_tsk` are available.
+use super::{
+ abi,
+ error::{expect_success, expect_success_aborting, ItronError},
+ task,
+ time::dur2reltims,
+};
+use crate::{
+ cell::UnsafeCell,
+ ffi::CStr,
+ hint, io,
+ mem::ManuallyDrop,
+ sync::atomic::{AtomicUsize, Ordering},
+ sys::thread_local_dtor::run_dtors,
+ time::Duration,
+};
+
+pub struct Thread {
+ inner: ManuallyDrop<Box<ThreadInner>>,
+
+ /// The ID of the underlying task.
+ task: abi::ID,
+}
+
+/// State data shared between a parent thread and child thread. It's dropped on
+/// a transition to one of the final states.
+struct ThreadInner {
+ /// This field is used on thread creation to pass a closure from
+ /// `Thread::new` to the created task.
+ start: UnsafeCell<ManuallyDrop<Box<dyn FnOnce()>>>,
+
+ /// A state machine. Each transition is annotated with `[...]` in the
+ /// source code.
+ ///
+ /// ```text
+ ///
+ /// <P>: parent, <C>: child, (?): don't-care
+ ///
+ /// DETACHED (-1) --------------------> EXITED (?)
+ /// <C>finish/exd_tsk
+ /// ^
+ /// |
+ /// | <P>detach
+ /// |
+ ///
+ /// INIT (0) -----------------------> FINISHED (-1)
+ /// <C>finish
+ /// | |
+ /// | <P>join/slp_tsk | <P>join/del_tsk
+ /// | | <P>detach/del_tsk
+ /// v v
+ ///
+ /// JOINING JOINED (?)
+ /// (parent_tid)
+ /// ^
+ /// \ /
+ /// \ <C>finish/wup_tsk / <P>slp_tsk-complete/ter_tsk
+ /// \ / & del_tsk
+ /// \ /
+ /// '--> JOIN_FINALIZE ---'
+ /// (-1)
+ ///
+ lifecycle: AtomicUsize,
+}
+
+// Safety: The only `!Sync` field, `ThreadInner::start`, is only touched by
+// the task represented by `ThreadInner`.
+unsafe impl Sync for ThreadInner {}
+
+const LIFECYCLE_INIT: usize = 0;
+const LIFECYCLE_FINISHED: usize = usize::MAX;
+const LIFECYCLE_DETACHED: usize = usize::MAX;
+const LIFECYCLE_JOIN_FINALIZE: usize = usize::MAX;
+const LIFECYCLE_DETACHED_OR_JOINED: usize = usize::MAX;
+const LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE: usize = usize::MAX;
+// there's no single value for `JOINING`
+
+// 64KiB for 32-bit ISAs, 128KiB for 64-bit ISAs.
+pub const DEFAULT_MIN_STACK_SIZE: usize = 0x4000 * crate::mem::size_of::<usize>();
+
+impl Thread {
+ /// # Safety
+ ///
+ /// See `thread::Builder::spawn_unchecked` for safety requirements.
+ pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+ let inner = Box::new(ThreadInner {
+ start: UnsafeCell::new(ManuallyDrop::new(p)),
+ lifecycle: AtomicUsize::new(LIFECYCLE_INIT),
+ });
+
+ unsafe extern "C" fn trampoline(exinf: isize) {
+ // Safety: `ThreadInner` is alive at this point
+ let inner = unsafe { &*(exinf as *const ThreadInner) };
+
+ // Safety: Since `trampoline` is called only once for each
+ // `ThreadInner` and only `trampoline` touches `start`,
+ // `start` contains contents and is safe to mutably borrow.
+ let p = unsafe { ManuallyDrop::take(&mut *inner.start.get()) };
+ p();
+
+ // Fix the current thread's state just in case, so that the
+ // destructors won't abort
+ // Safety: Not really unsafe
+ let _ = unsafe { abi::unl_cpu() };
+ let _ = unsafe { abi::ena_dsp() };
+
+ // Run TLS destructors now because they are not
+ // called automatically for terminated tasks.
+ unsafe { run_dtors() };
+
+ let old_lifecycle = inner
+ .lifecycle
+ .swap(LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE, Ordering::Release);
+
+ match old_lifecycle {
+ LIFECYCLE_DETACHED => {
+ // [DETACHED → EXITED]
+ // No one will ever join, so we'll ask the collector task to
+ // delete the task.
+
+ // In this case, `inner`'s ownership has been moved to us,
+ // And we are responsible for dropping it. The acquire
+ // ordering is not necessary because the parent thread made
+ // no memory access needing synchronization since the call
+ // to `acre_tsk`.
+ // Safety: See above.
+ let _ = unsafe { Box::from_raw(inner as *const _ as *mut ThreadInner) };
+
+ // Safety: There are no pinned references to the stack
+ unsafe { terminate_and_delete_current_task() };
+ }
+ LIFECYCLE_INIT => {
+ // [INIT → FINISHED]
+ // The parent hasn't decided whether to join or detach this
+ // thread yet. Whichever option the parent chooses,
+ // it'll have to delete this task.
+ // Since the parent might drop `*inner` as soon as it sees
+ // `FINISHED`, the release ordering must be used in the
+ // above `swap` call.
+ }
+ parent_tid => {
+ // Since the parent might drop `*inner` and terminate us as
+ // soon as it sees `JOIN_FINALIZE`, the release ordering
+ // must be used in the above `swap` call.
+
+ // [JOINING → JOIN_FINALIZE]
+ // Wake up the parent task.
+ expect_success(
+ unsafe {
+ let mut er = abi::wup_tsk(parent_tid as _);
+ if er == abi::E_QOVR {
+ // `E_QOVR` indicates there's already
+ // a parking token
+ er = abi::E_OK;
+ }
+ er
+ },
+ &"wup_tsk",
+ );
+ }
+ }
+ }
+
+ let inner_ptr = (&*inner) as *const ThreadInner;
+
+ let new_task = ItronError::err_if_negative(unsafe {
+ abi::acre_tsk(&abi::T_CTSK {
+ // Activate this task immediately
+ tskatr: abi::TA_ACT,
+ exinf: inner_ptr as abi::EXINF,
+ // The entry point
+ task: Some(trampoline),
+ // Inherit the calling task's base priority
+ itskpri: abi::TPRI_SELF,
+ stksz: stack,
+ // Let the kernel allocate the stack,
+ stk: crate::ptr::null_mut(),
+ })
+ })
+ .map_err(|e| e.as_io_error())?;
+
+ Ok(Self { inner: ManuallyDrop::new(inner), task: new_task })
+ }
+
+ pub fn yield_now() {
+ expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq");
+ }
+
+ pub fn set_name(_name: &CStr) {
+ // nope
+ }
+
+ pub fn sleep(dur: Duration) {
+ for timeout in dur2reltims(dur) {
+ expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk");
+ }
+ }
+
+ pub fn join(mut self) {
+ let inner = &*self.inner;
+ // Get the current task ID. Panicking here would cause a resource leak,
+ // so just abort on failure.
+ let current_task = task::current_task_id_aborting();
+ debug_assert!(usize::try_from(current_task).is_ok());
+ debug_assert_ne!(current_task as usize, LIFECYCLE_INIT);
+ debug_assert_ne!(current_task as usize, LIFECYCLE_DETACHED);
+
+ let current_task = current_task as usize;
+
+ match inner.lifecycle.swap(current_task, Ordering::Acquire) {
+ LIFECYCLE_INIT => {
+ // [INIT → JOINING]
+ // The child task will transition the state to `JOIN_FINALIZE`
+ // and wake us up.
+ loop {
+ expect_success_aborting(unsafe { abi::slp_tsk() }, &"slp_tsk");
+ // To synchronize with the child task's memory accesses to
+ // `inner` up to the point of the assignment of
+ // `JOIN_FINALIZE`, `Ordering::Acquire` must be used for the
+ // `load`.
+ if inner.lifecycle.load(Ordering::Acquire) == LIFECYCLE_JOIN_FINALIZE {
+ break;
+ }
+ }
+
+ // [JOIN_FINALIZE → JOINED]
+ }
+ LIFECYCLE_FINISHED => {
+ // [FINISHED → JOINED]
+ // To synchronize with the child task's memory accesses to
+ // `inner` up to the point of the assignment of `FINISHED`,
+ // `Ordering::Acquire` must be used for the above `swap` call`.
+ }
+ _ => unsafe { hint::unreachable_unchecked() },
+ }
+
+ // Terminate and delete the task
+ // Safety: `self.task` still represents a task we own (because this
+ // method or `detach_inner` is called only once for each
+ // `Thread`). The task indicated that it's safe to delete by
+ // entering the `FINISHED` or `JOIN_FINALIZE` state.
+ unsafe { terminate_and_delete_task(self.task) };
+
+ // In either case, we are responsible for dropping `inner`.
+ // Safety: The contents of `self.inner` will not be accessed hereafter
+ let _inner = unsafe { ManuallyDrop::take(&mut self.inner) };
+
+ // Skip the destructor (because it would attempt to detach the thread)
+ crate::mem::forget(self);
+ }
+}
+
+impl Drop for Thread {
+ fn drop(&mut self) {
+ // Detach the thread.
+ match self.inner.lifecycle.swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::Acquire) {
+ LIFECYCLE_INIT => {
+ // [INIT → DETACHED]
+ // When the time comes, the child will figure out that no
+ // one will ever join it.
+ // The ownership of `self.inner` is moved to the child thread.
+ // However, the release ordering is not necessary because we
+ // made no memory access needing synchronization since the call
+ // to `acre_tsk`.
+ }
+ LIFECYCLE_FINISHED => {
+ // [FINISHED → JOINED]
+ // The task has already decided that we should delete the task.
+ // To synchronize with the child task's memory accesses to
+ // `inner` up to the point of the assignment of `FINISHED`,
+ // the acquire ordering is required for the above `swap` call.
+
+ // Terminate and delete the task
+ // Safety: `self.task` still represents a task we own (because
+ // this method or `join_inner` is called only once for
+ // each `Thread`). The task indicated that it's safe to
+ // delete by entering the `FINISHED` state.
+ unsafe { terminate_and_delete_task(self.task) };
+
+ // Wwe are responsible for dropping `inner`.
+ // Safety: The contents of `self.inner` will not be accessed
+ // hereafter
+ unsafe { ManuallyDrop::drop(&mut self.inner) };
+ }
+ _ => unsafe { hint::unreachable_unchecked() },
+ }
+ }
+}
+
+pub mod guard {
+ pub type Guard = !;
+ pub unsafe fn current() -> Option<Guard> {
+ None
+ }
+ pub unsafe fn init() -> Option<Guard> {
+ None
+ }
+}
+
+/// Terminate and delete the specified task.
+///
+/// This function will abort if `deleted_task` refers to the calling task.
+///
+/// It is assumed that the specified task is solely managed by the caller -
+/// i.e., other threads must not "resuscitate" the specified task or delete it
+/// prematurely while this function is still in progress. It is allowed for the
+/// specified task to exit by its own.
+///
+/// # Safety
+///
+/// The task must be safe to terminate. This is in general not true
+/// because there might be pinned references to the task's stack.
+unsafe fn terminate_and_delete_task(deleted_task: abi::ID) {
+ // Terminate the task
+ // Safety: Upheld by the caller
+ match unsafe { abi::ter_tsk(deleted_task) } {
+ // Indicates the task is already dormant, ignore it
+ abi::E_OBJ => {}
+ er => {
+ expect_success_aborting(er, &"ter_tsk");
+ }
+ }
+
+ // Delete the task
+ // Safety: Upheld by the caller
+ expect_success_aborting(unsafe { abi::del_tsk(deleted_task) }, &"del_tsk");
+}
+
+/// Terminate and delete the calling task.
+///
+/// Atomicity is not required - i.e., it can be assumed that other threads won't
+/// `ter_tsk` the calling task while this function is still in progress. (This
+/// property makes it easy to implement this operation on μITRON-derived kernels
+/// that don't support `exd_tsk`.)
+///
+/// # Safety
+///
+/// The task must be safe to terminate. This is in general not true
+/// because there might be pinned references to the task's stack.
+unsafe fn terminate_and_delete_current_task() -> ! {
+ expect_success_aborting(unsafe { abi::exd_tsk() }, &"exd_tsk");
+ // Safety: `exd_tsk` never returns on success
+ unsafe { crate::hint::unreachable_unchecked() };
+}
+
+pub fn available_parallelism() -> io::Result<crate::num::NonZeroUsize> {
+ super::unsupported()
+}
diff --git a/library/std/src/sys/itron/time.rs b/library/std/src/sys/itron/time.rs
new file mode 100644
index 000000000..427ea0d80
--- /dev/null
+++ b/library/std/src/sys/itron/time.rs
@@ -0,0 +1,114 @@
+use super::{abi, error::expect_success};
+use crate::{mem::MaybeUninit, time::Duration};
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Instant(abi::SYSTIM);
+
+impl Instant {
+ pub fn now() -> Instant {
+ // Safety: The provided pointer is valid
+ unsafe {
+ let mut out = MaybeUninit::uninit();
+ expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim");
+ Instant(out.assume_init())
+ }
+ }
+
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ self.0.checked_sub(other.0).map(|ticks| {
+ // `SYSTIM` is measured in microseconds
+ Duration::from_micros(ticks)
+ })
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ // `SYSTIM` is measured in microseconds
+ let ticks = other.as_micros();
+
+ Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ // `SYSTIM` is measured in microseconds
+ let ticks = other.as_micros();
+
+ Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?))
+ }
+}
+
+/// Split `Duration` into zero or more `RELTIM`s.
+#[inline]
+pub fn dur2reltims(dur: Duration) -> impl Iterator<Item = abi::RELTIM> {
+ // `RELTIM` is microseconds
+ let mut ticks = dur.as_micros();
+
+ crate::iter::from_fn(move || {
+ if ticks == 0 {
+ None
+ } else if ticks <= abi::TMAX_RELTIM as u128 {
+ Some(crate::mem::replace(&mut ticks, 0) as abi::RELTIM)
+ } else {
+ ticks -= abi::TMAX_RELTIM as u128;
+ Some(abi::TMAX_RELTIM)
+ }
+ })
+}
+
+/// Split `Duration` into one or more `TMO`s.
+#[inline]
+fn dur2tmos(dur: Duration) -> impl Iterator<Item = abi::TMO> {
+ // `TMO` is microseconds
+ let mut ticks = dur.as_micros();
+ let mut end = false;
+
+ crate::iter::from_fn(move || {
+ if end {
+ None
+ } else if ticks <= abi::TMAX_RELTIM as u128 {
+ end = true;
+ Some(crate::mem::replace(&mut ticks, 0) as abi::TMO)
+ } else {
+ ticks -= abi::TMAX_RELTIM as u128;
+ Some(abi::TMAX_RELTIM)
+ }
+ })
+}
+
+/// Split `Duration` into one or more API calls with timeout.
+#[inline]
+pub fn with_tmos(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -> abi::ER {
+ let mut er = abi::E_TMOUT;
+ for tmo in dur2tmos(dur) {
+ er = f(tmo);
+ if er != abi::E_TMOUT {
+ break;
+ }
+ }
+ er
+}
+
+/// Split `Duration` into one or more API calls with timeout. This function can
+/// handle spurious wakeups.
+#[inline]
+pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -> abi::ER {
+ // `TMO` and `SYSTIM` are microseconds.
+ // Clamp at `SYSTIM::MAX` for performance reasons. This shouldn't cause
+ // a problem in practice. (`u64::MAX` μs ≈ 584942 years)
+ let ticks = dur.as_micros().min(abi::SYSTIM::MAX as u128) as abi::SYSTIM;
+
+ let start = Instant::now().0;
+ let mut elapsed = 0;
+ let mut er = abi::E_TMOUT;
+ while elapsed <= ticks {
+ er = f(elapsed.min(abi::TMAX_RELTIM as abi::SYSTIM) as abi::TMO);
+ if er != abi::E_TMOUT {
+ break;
+ }
+ elapsed = Instant::now().0.wrapping_sub(start);
+ }
+
+ er
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/library/std/src/sys/itron/time/tests.rs b/library/std/src/sys/itron/time/tests.rs
new file mode 100644
index 000000000..d14035d9d
--- /dev/null
+++ b/library/std/src/sys/itron/time/tests.rs
@@ -0,0 +1,33 @@
+use super::*;
+
+fn reltim2dur(t: u64) -> Duration {
+ Duration::from_micros(t)
+}
+
+#[test]
+fn test_dur2reltims() {
+ assert_eq!(dur2reltims(reltim2dur(0)).collect::<Vec<_>>(), vec![]);
+ assert_eq!(dur2reltims(reltim2dur(42)).collect::<Vec<_>>(), vec![42]);
+ assert_eq!(
+ dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(),
+ vec![abi::TMAX_RELTIM]
+ );
+ assert_eq!(
+ dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(),
+ vec![abi::TMAX_RELTIM, 10000]
+ );
+}
+
+#[test]
+fn test_dur2tmos() {
+ assert_eq!(dur2tmos(reltim2dur(0)).collect::<Vec<_>>(), vec![0]);
+ assert_eq!(dur2tmos(reltim2dur(42)).collect::<Vec<_>>(), vec![42]);
+ assert_eq!(
+ dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(),
+ vec![abi::TMAX_RELTIM]
+ );
+ assert_eq!(
+ dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(),
+ vec![abi::TMAX_RELTIM, 10000]
+ );
+}
diff --git a/library/std/src/sys/itron/wait_flag.rs b/library/std/src/sys/itron/wait_flag.rs
new file mode 100644
index 000000000..e432edd20
--- /dev/null
+++ b/library/std/src/sys/itron/wait_flag.rs
@@ -0,0 +1,72 @@
+use crate::mem::MaybeUninit;
+use crate::time::Duration;
+
+use super::{
+ abi,
+ error::{expect_success, fail},
+ time::with_tmos,
+};
+
+const CLEAR: abi::FLGPTN = 0;
+const RAISED: abi::FLGPTN = 1;
+
+/// A thread parking primitive that is not susceptible to race conditions,
+/// but provides no atomic ordering guarantees and allows only one `raise` per wait.
+pub struct WaitFlag {
+ flag: abi::ID,
+}
+
+impl WaitFlag {
+ /// Creates a new wait flag.
+ pub fn new() -> WaitFlag {
+ let flag = expect_success(
+ unsafe {
+ abi::acre_flg(&abi::T_CFLG {
+ flgatr: abi::TA_FIFO | abi::TA_WSGL | abi::TA_CLR,
+ iflgptn: CLEAR,
+ })
+ },
+ &"acre_flg",
+ );
+
+ WaitFlag { flag }
+ }
+
+ /// Wait for the wait flag to be raised.
+ pub fn wait(&self) {
+ let mut token = MaybeUninit::uninit();
+ expect_success(
+ unsafe { abi::wai_flg(self.flag, RAISED, abi::TWF_ORW, token.as_mut_ptr()) },
+ &"wai_flg",
+ );
+ }
+
+ /// Wait for the wait flag to be raised or the timeout to occur.
+ ///
+ /// Returns whether the flag was raised (`true`) or the operation timed out (`false`).
+ pub fn wait_timeout(&self, dur: Duration) -> bool {
+ let mut token = MaybeUninit::uninit();
+ let res = with_tmos(dur, |tmout| unsafe {
+ abi::twai_flg(self.flag, RAISED, abi::TWF_ORW, token.as_mut_ptr(), tmout)
+ });
+
+ match res {
+ abi::E_OK => true,
+ abi::E_TMOUT => false,
+ error => fail(error, &"twai_flg"),
+ }
+ }
+
+ /// Raise the wait flag.
+ ///
+ /// Calls to this function should be balanced with the number of successful waits.
+ pub fn raise(&self) {
+ expect_success(unsafe { abi::set_flg(self.flag, RAISED) }, &"set_flg");
+ }
+}
+
+impl Drop for WaitFlag {
+ fn drop(&mut self) {
+ expect_success(unsafe { abi::del_flg(self.flag) }, &"del_flg");
+ }
+}
diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs
new file mode 100644
index 000000000..167c918c9
--- /dev/null
+++ b/library/std/src/sys/mod.rs
@@ -0,0 +1,78 @@
+//! Platform-dependent platform abstraction.
+//!
+//! The `std::sys` module is the abstracted interface through which
+//! `std` talks to the underlying operating system. It has different
+//! implementations for different operating system families, today
+//! just Unix and Windows, and initial support for Redox.
+//!
+//! The centralization of platform-specific code in this module is
+//! enforced by the "platform abstraction layer" tidy script in
+//! `tools/tidy/src/pal.rs`.
+//!
+//! This module is closely related to the platform-independent system
+//! integration code in `std::sys_common`. See that module's
+//! documentation for details.
+//!
+//! In the future it would be desirable for the independent
+//! implementations of this module to be extracted to their own crates
+//! that `std` can link to, thus enabling their implementation
+//! out-of-tree via crate replacement. Though due to the complex
+//! inter-dependencies within `std` that will be a challenging goal to
+//! achieve.
+
+#![allow(missing_debug_implementations)]
+
+mod common;
+
+cfg_if::cfg_if! {
+ if #[cfg(unix)] {
+ mod unix;
+ pub use self::unix::*;
+ } else if #[cfg(windows)] {
+ mod windows;
+ pub use self::windows::*;
+ } else if #[cfg(target_os = "solid_asp3")] {
+ mod solid;
+ pub use self::solid::*;
+ } else if #[cfg(target_os = "hermit")] {
+ mod hermit;
+ pub use self::hermit::*;
+ } else if #[cfg(target_os = "wasi")] {
+ mod wasi;
+ pub use self::wasi::*;
+ } else if #[cfg(target_family = "wasm")] {
+ mod wasm;
+ pub use self::wasm::*;
+ } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
+ mod sgx;
+ pub use self::sgx::*;
+ } else {
+ mod unsupported;
+ pub use self::unsupported::*;
+ }
+}
+
+// Import essential modules from platforms used in `std::os` when documenting.
+//
+// Note that on some platforms those modules don't compile
+// (missing things in `libc` which is empty), so they are not included in `std::os` and can be
+// omitted here as well.
+
+#[cfg(doc)]
+#[cfg(not(any(
+ all(target_arch = "wasm32", not(target_os = "wasi")),
+ all(target_vendor = "fortanix", target_env = "sgx")
+)))]
+cfg_if::cfg_if! {
+ if #[cfg(not(windows))] {
+ // On non-Windows platforms (aka linux/osx/etc) pull in a "minimal"
+ // amount of windows goop which ends up compiling
+
+ #[macro_use]
+ #[path = "windows/compat.rs"]
+ pub mod compat;
+
+ #[path = "windows/c.rs"]
+ pub mod c;
+ }
+}
diff --git a/library/std/src/sys/sgx/abi/entry.S b/library/std/src/sys/sgx/abi/entry.S
new file mode 100644
index 000000000..f61bcf06f
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/entry.S
@@ -0,0 +1,372 @@
+/* This symbol is used at runtime to figure out the virtual address that the */
+/* enclave is loaded at. */
+.section absolute
+.global IMAGE_BASE
+IMAGE_BASE:
+
+.section ".note.x86_64-fortanix-unknown-sgx", "", @note
+ .align 4
+ .long 1f - 0f /* name length (not including padding) */
+ .long 3f - 2f /* desc length (not including padding) */
+ .long 1 /* type = NT_VERSION */
+0: .asciz "toolchain-version" /* name */
+1: .align 4
+2: .long 1 /* desc - toolchain version number, 32-bit LE */
+3: .align 4
+
+.section .rodata
+/* The XSAVE area needs to be a large chunk of readable memory, but since we are */
+/* going to restore everything to its initial state (XSTATE_BV=0), only certain */
+/* parts need to have a defined value. In particular: */
+/* */
+/* * MXCSR in the legacy area. This register is always restored if RFBM[1] or */
+/* RFBM[2] is set, regardless of the value of XSTATE_BV */
+/* * XSAVE header */
+.align 64
+.Lxsave_clear:
+.org .+24
+.Lxsave_mxcsr:
+ .short 0x1f80
+
+/* We can store a bunch of data in the gap between MXCSR and the XSAVE header */
+
+/* The following symbols point at read-only data that will be filled in by the */
+/* post-linker. */
+
+/* When using this macro, don't forget to adjust the linker version script! */
+.macro globvar name:req size:req
+ .global \name
+ .protected \name
+ .align \size
+ .size \name , \size
+ \name :
+ .org .+\size
+.endm
+ /* The base address (relative to enclave start) of the heap area */
+ globvar HEAP_BASE 8
+ /* The heap size in bytes */
+ globvar HEAP_SIZE 8
+ /* Value of the RELA entry in the dynamic table */
+ globvar RELA 8
+ /* Value of the RELACOUNT entry in the dynamic table */
+ globvar RELACOUNT 8
+ /* The enclave size in bytes */
+ globvar ENCLAVE_SIZE 8
+ /* The base address (relative to enclave start) of the enclave configuration area */
+ globvar CFGDATA_BASE 8
+ /* Non-zero if debugging is enabled, zero otherwise */
+ globvar DEBUG 1
+ /* The base address (relative to enclave start) of the enclave text section */
+ globvar TEXT_BASE 8
+ /* The size in bytes of enclacve text section */
+ globvar TEXT_SIZE 8
+ /* The base address (relative to enclave start) of the enclave .eh_frame_hdr section */
+ globvar EH_FRM_HDR_OFFSET 8
+ /* The size in bytes of enclave .eh_frame_hdr section */
+ globvar EH_FRM_HDR_LEN 8
+ /* The base address (relative to enclave start) of the enclave .eh_frame section */
+ globvar EH_FRM_OFFSET 8
+ /* The size in bytes of enclacve .eh_frame section */
+ globvar EH_FRM_LEN 8
+
+.org .Lxsave_clear+512
+.Lxsave_header:
+ .int 0, 0 /* XSTATE_BV */
+ .int 0, 0 /* XCOMP_BV */
+ .org .+48 /* reserved bits */
+
+.data
+.Laborted:
+ .byte 0
+
+/* TCS local storage section */
+.equ tcsls_tos, 0x00 /* initialized by loader to *offset* from image base to TOS */
+.equ tcsls_flags, 0x08 /* initialized by loader */
+.equ tcsls_flag_secondary, 0 /* initialized by loader; 0 = standard TCS, 1 = secondary TCS */
+.equ tcsls_flag_init_once, 1 /* initialized by loader to 0 */
+/* 14 unused bits */
+.equ tcsls_user_fcw, 0x0a
+.equ tcsls_user_mxcsr, 0x0c
+.equ tcsls_last_rsp, 0x10 /* initialized by loader to 0 */
+.equ tcsls_panic_last_rsp, 0x18 /* initialized by loader to 0 */
+.equ tcsls_debug_panic_buf_ptr, 0x20 /* initialized by loader to 0 */
+.equ tcsls_user_rsp, 0x28
+.equ tcsls_user_retip, 0x30
+.equ tcsls_user_rbp, 0x38
+.equ tcsls_user_r12, 0x40
+.equ tcsls_user_r13, 0x48
+.equ tcsls_user_r14, 0x50
+.equ tcsls_user_r15, 0x58
+.equ tcsls_tls_ptr, 0x60
+.equ tcsls_tcs_addr, 0x68
+
+.macro load_tcsls_flag_secondary_bool reg:req comments:vararg
+ .ifne tcsls_flag_secondary /* to convert to a bool, must be the first bit */
+ .abort
+ .endif
+ mov $(1<<tcsls_flag_secondary),%e\reg
+ and %gs:tcsls_flags,%\reg
+.endm
+
+/* We place the ELF entry point in a separate section so it can be removed by
+ elf2sgxs */
+.section .text_no_sgx, "ax"
+.Lelf_entry_error_msg:
+ .ascii "Error: This file is an SGX enclave which cannot be executed as a standard Linux binary.\nSee the installation guide at https://edp.fortanix.com/docs/installation/guide/ on how to use 'cargo run' or follow the steps at https://edp.fortanix.com/docs/tasks/deployment/ for manual deployment.\n"
+.Lelf_entry_error_msg_end:
+
+.global elf_entry
+.type elf_entry,function
+elf_entry:
+/* print error message */
+ movq $2,%rdi /* write to stderr (fd 2) */
+ lea .Lelf_entry_error_msg(%rip),%rsi
+ movq $.Lelf_entry_error_msg_end-.Lelf_entry_error_msg,%rdx
+.Lelf_entry_call:
+ movq $1,%rax /* write() syscall */
+ syscall
+ test %rax,%rax
+ jle .Lelf_exit /* exit on error */
+ add %rax,%rsi
+ sub %rax,%rdx /* all chars written? */
+ jnz .Lelf_entry_call
+
+.Lelf_exit:
+ movq $60,%rax /* exit() syscall */
+ movq $1,%rdi /* exit code 1 */
+ syscall
+ ud2 /* should not be reached */
+/* end elf_entry */
+
+/* This code needs to be called *after* the enclave stack has been setup. */
+/* There are 3 places where this needs to happen, so this is put in a macro. */
+.macro entry_sanitize_final
+/* Sanitize rflags received from user */
+/* - DF flag: x86-64 ABI requires DF to be unset at function entry/exit */
+/* - AC flag: AEX on misaligned memory accesses leaks side channel info */
+ pushfq
+ andq $~0x40400, (%rsp)
+ popfq
+/* check for abort */
+ bt $0,.Laborted(%rip)
+ jc .Lreentry_panic
+.endm
+
+.text
+.global sgx_entry
+.type sgx_entry,function
+sgx_entry:
+/* save user registers */
+ mov %rcx,%gs:tcsls_user_retip
+ mov %rsp,%gs:tcsls_user_rsp
+ mov %rbp,%gs:tcsls_user_rbp
+ mov %r12,%gs:tcsls_user_r12
+ mov %r13,%gs:tcsls_user_r13
+ mov %r14,%gs:tcsls_user_r14
+ mov %r15,%gs:tcsls_user_r15
+ mov %rbx,%gs:tcsls_tcs_addr
+ stmxcsr %gs:tcsls_user_mxcsr
+ fnstcw %gs:tcsls_user_fcw
+
+/* check for debug buffer pointer */
+ testb $0xff,DEBUG(%rip)
+ jz .Lskip_debug_init
+ mov %r10,%gs:tcsls_debug_panic_buf_ptr
+.Lskip_debug_init:
+/* reset cpu state */
+ mov %rdx, %r10
+ mov $-1, %rax
+ mov $-1, %rdx
+ xrstor .Lxsave_clear(%rip)
+ mov %r10, %rdx
+
+/* check if returning from usercall */
+ mov %gs:tcsls_last_rsp,%r11
+ test %r11,%r11
+ jnz .Lusercall_ret
+/* setup stack */
+ mov %gs:tcsls_tos,%rsp /* initially, RSP is not set to the correct value */
+ /* here. This is fixed below under "adjust stack". */
+/* check for thread init */
+ bts $tcsls_flag_init_once,%gs:tcsls_flags
+ jc .Lskip_init
+/* adjust stack */
+ lea IMAGE_BASE(%rip),%rax
+ add %rax,%rsp
+ mov %rsp,%gs:tcsls_tos
+ entry_sanitize_final
+/* call tcs_init */
+/* store caller-saved registers in callee-saved registers */
+ mov %rdi,%rbx
+ mov %rsi,%r12
+ mov %rdx,%r13
+ mov %r8,%r14
+ mov %r9,%r15
+ load_tcsls_flag_secondary_bool di /* RDI = tcs_init() argument: secondary: bool */
+ call tcs_init
+/* reload caller-saved registers */
+ mov %rbx,%rdi
+ mov %r12,%rsi
+ mov %r13,%rdx
+ mov %r14,%r8
+ mov %r15,%r9
+ jmp .Lafter_init
+.Lskip_init:
+ entry_sanitize_final
+.Lafter_init:
+/* call into main entry point */
+ load_tcsls_flag_secondary_bool cx /* RCX = entry() argument: secondary: bool */
+ call entry /* RDI, RSI, RDX, R8, R9 passed in from userspace */
+ mov %rax,%rsi /* RSI = return value */
+ /* NOP: mov %rdx,%rdx */ /* RDX = return value */
+ xor %rdi,%rdi /* RDI = normal exit */
+.Lexit:
+/* clear general purpose register state */
+ /* RAX overwritten by ENCLU */
+ /* RBX set later */
+ /* RCX overwritten by ENCLU */
+ /* RDX contains return value */
+ /* RSP set later */
+ /* RBP set later */
+ /* RDI contains exit mode */
+ /* RSI contains return value */
+ xor %r8,%r8
+ xor %r9,%r9
+ xor %r10,%r10
+ xor %r11,%r11
+ /* R12 ~ R15 set by sgx_exit */
+.Lsgx_exit:
+/* clear extended register state */
+ mov %rdx, %rcx /* save RDX */
+ mov $-1, %rax
+ mov %rax, %rdx
+ xrstor .Lxsave_clear(%rip)
+ mov %rcx, %rdx /* restore RDX */
+/* clear flags */
+ pushq $0
+ popfq
+/* restore user registers */
+ mov %gs:tcsls_user_r12,%r12
+ mov %gs:tcsls_user_r13,%r13
+ mov %gs:tcsls_user_r14,%r14
+ mov %gs:tcsls_user_r15,%r15
+ mov %gs:tcsls_user_retip,%rbx
+ mov %gs:tcsls_user_rsp,%rsp
+ mov %gs:tcsls_user_rbp,%rbp
+ fldcw %gs:tcsls_user_fcw
+ ldmxcsr %gs:tcsls_user_mxcsr
+/* exit enclave */
+ mov $0x4,%eax /* EEXIT */
+ enclu
+/* end sgx_entry */
+
+.Lreentry_panic:
+ orq $8,%rsp
+ jmp abort_reentry
+
+/* This *MUST* be called with 6 parameters, otherwise register information */
+/* might leak! */
+.global usercall
+usercall:
+ test %rcx,%rcx /* check `abort` function argument */
+ jnz .Lusercall_abort /* abort is set, jump to abort code (unlikely forward conditional) */
+ jmp .Lusercall_save_state /* non-aborting usercall */
+.Lusercall_abort:
+/* set aborted bit */
+ movb $1,.Laborted(%rip)
+/* save registers in DEBUG mode, so that debugger can reconstruct the stack */
+ testb $0xff,DEBUG(%rip)
+ jz .Lusercall_noreturn
+.Lusercall_save_state:
+/* save callee-saved state */
+ push %r15
+ push %r14
+ push %r13
+ push %r12
+ push %rbp
+ push %rbx
+ sub $8, %rsp
+ fstcw 4(%rsp)
+ stmxcsr (%rsp)
+ movq %rsp,%gs:tcsls_last_rsp
+.Lusercall_noreturn:
+/* clear general purpose register state */
+ /* RAX overwritten by ENCLU */
+ /* RBX set by sgx_exit */
+ /* RCX overwritten by ENCLU */
+ /* RDX contains parameter */
+ /* RSP set by sgx_exit */
+ /* RBP set by sgx_exit */
+ /* RDI contains parameter */
+ /* RSI contains parameter */
+ /* R8 contains parameter */
+ /* R9 contains parameter */
+ xor %r10,%r10
+ xor %r11,%r11
+ /* R12 ~ R15 set by sgx_exit */
+/* extended registers/flags cleared by sgx_exit */
+/* exit */
+ jmp .Lsgx_exit
+.Lusercall_ret:
+ movq $0,%gs:tcsls_last_rsp
+/* restore callee-saved state, cf. "save" above */
+ mov %r11,%rsp
+ ldmxcsr (%rsp)
+ fldcw 4(%rsp)
+ add $8, %rsp
+ entry_sanitize_final
+ pop %rbx
+ pop %rbp
+ pop %r12
+ pop %r13
+ pop %r14
+ pop %r15
+/* return */
+ mov %rsi,%rax /* RAX = return value */
+ /* NOP: mov %rdx,%rdx */ /* RDX = return value */
+ pop %r11
+ lfence
+ jmp *%r11
+
+/*
+The following functions need to be defined externally:
+```
+// Called by entry code on re-entry after exit
+extern "C" fn abort_reentry() -> !;
+
+// Called once when a TCS is first entered
+extern "C" fn tcs_init(secondary: bool);
+
+// Standard TCS entrypoint
+extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64);
+```
+*/
+
+.global get_tcs_addr
+get_tcs_addr:
+ mov %gs:tcsls_tcs_addr,%rax
+ pop %r11
+ lfence
+ jmp *%r11
+
+.global get_tls_ptr
+get_tls_ptr:
+ mov %gs:tcsls_tls_ptr,%rax
+ pop %r11
+ lfence
+ jmp *%r11
+
+.global set_tls_ptr
+set_tls_ptr:
+ mov %rdi,%gs:tcsls_tls_ptr
+ pop %r11
+ lfence
+ jmp *%r11
+
+.global take_debug_panic_buf_ptr
+take_debug_panic_buf_ptr:
+ xor %rax,%rax
+ xchg %gs:tcsls_debug_panic_buf_ptr,%rax
+ pop %r11
+ lfence
+ jmp *%r11
diff --git a/library/std/src/sys/sgx/abi/mem.rs b/library/std/src/sys/sgx/abi/mem.rs
new file mode 100644
index 000000000..18e6d5b3f
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/mem.rs
@@ -0,0 +1,93 @@
+use core::arch::asm;
+
+// Do not remove inline: will result in relocation failure
+#[inline(always)]
+pub(crate) unsafe fn rel_ptr<T>(offset: u64) -> *const T {
+ (image_base() + offset) as *const T
+}
+
+// Do not remove inline: will result in relocation failure
+#[inline(always)]
+pub(crate) unsafe fn rel_ptr_mut<T>(offset: u64) -> *mut T {
+ (image_base() + offset) as *mut T
+}
+
+extern "C" {
+ static ENCLAVE_SIZE: usize;
+ static HEAP_BASE: u64;
+ static HEAP_SIZE: usize;
+}
+
+/// Returns the base memory address of the heap
+pub(crate) fn heap_base() -> *const u8 {
+ unsafe { rel_ptr_mut(HEAP_BASE) }
+}
+
+/// Returns the size of the heap
+pub(crate) fn heap_size() -> usize {
+ unsafe { HEAP_SIZE }
+}
+
+// Do not remove inline: will result in relocation failure
+// For the same reason we use inline ASM here instead of an extern static to
+// locate the base
+/// Returns address at which current enclave is loaded.
+#[inline(always)]
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn image_base() -> u64 {
+ let base: u64;
+ unsafe {
+ asm!(
+ "lea IMAGE_BASE(%rip), {}",
+ lateout(reg) base,
+ options(att_syntax, nostack, preserves_flags, nomem, pure),
+ )
+ };
+ base
+}
+
+/// Returns `true` if the specified memory range is in the enclave.
+///
+/// For safety, this function also checks whether the range given overflows,
+/// returning `false` if so.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn is_enclave_range(p: *const u8, len: usize) -> bool {
+ let start = p as usize;
+
+ // Subtract one from `len` when calculating `end` in case `p + len` is
+ // exactly at the end of addressable memory (`p + len` would overflow, but
+ // the range is still valid).
+ let end = if len == 0 {
+ start
+ } else if let Some(end) = start.checked_add(len - 1) {
+ end
+ } else {
+ return false;
+ };
+
+ let base = image_base() as usize;
+ start >= base && end <= base + (unsafe { ENCLAVE_SIZE } - 1) // unsafe ok: link-time constant
+}
+
+/// Returns `true` if the specified memory range is in userspace.
+///
+/// For safety, this function also checks whether the range given overflows,
+/// returning `false` if so.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn is_user_range(p: *const u8, len: usize) -> bool {
+ let start = p as usize;
+
+ // Subtract one from `len` when calculating `end` in case `p + len` is
+ // exactly at the end of addressable memory (`p + len` would overflow, but
+ // the range is still valid).
+ let end = if len == 0 {
+ start
+ } else if let Some(end) = start.checked_add(len - 1) {
+ end
+ } else {
+ return false;
+ };
+
+ let base = image_base() as usize;
+ end < base || start > base + (unsafe { ENCLAVE_SIZE } - 1) // unsafe ok: link-time constant
+}
diff --git a/library/std/src/sys/sgx/abi/mod.rs b/library/std/src/sys/sgx/abi/mod.rs
new file mode 100644
index 000000000..9508c3874
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/mod.rs
@@ -0,0 +1,108 @@
+#![cfg_attr(test, allow(unused))] // RT initialization logic is not compiled for test
+
+use crate::io::Write;
+use core::arch::global_asm;
+use core::sync::atomic::{AtomicUsize, Ordering};
+
+// runtime features
+pub(super) mod panic;
+mod reloc;
+
+// library features
+pub mod mem;
+pub mod thread;
+pub mod tls;
+#[macro_use]
+pub mod usercalls;
+
+#[cfg(not(test))]
+global_asm!(include_str!("entry.S"), options(att_syntax));
+
+#[repr(C)]
+struct EntryReturn(u64, u64);
+
+#[cfg(not(test))]
+#[no_mangle]
+unsafe extern "C" fn tcs_init(secondary: bool) {
+ // Be very careful when changing this code: it runs before the binary has been
+ // relocated. Any indirect accesses to symbols will likely fail.
+ const UNINIT: usize = 0;
+ const BUSY: usize = 1;
+ const DONE: usize = 2;
+ // Three-state spin-lock
+ static RELOC_STATE: AtomicUsize = AtomicUsize::new(UNINIT);
+
+ if secondary && RELOC_STATE.load(Ordering::Relaxed) != DONE {
+ rtabort!("Entered secondary TCS before main TCS!")
+ }
+
+ // Try to atomically swap UNINIT with BUSY. The returned state can be:
+ match RELOC_STATE.compare_exchange(UNINIT, BUSY, Ordering::Acquire, Ordering::Acquire) {
+ // This thread just obtained the lock and other threads will observe BUSY
+ Ok(_) => {
+ reloc::relocate_elf_rela();
+ RELOC_STATE.store(DONE, Ordering::Release);
+ }
+ // We need to wait until the initialization is done.
+ Err(BUSY) => {
+ while RELOC_STATE.load(Ordering::Acquire) == BUSY {
+ core::hint::spin_loop();
+ }
+ }
+ // Initialization is done.
+ Err(DONE) => {}
+ _ => unreachable!(),
+ }
+}
+
+// FIXME: this item should only exist if this is linked into an executable
+// (main function exists). If this is a library, the crate author should be
+// able to specify this
+#[cfg(not(test))]
+#[no_mangle]
+extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> EntryReturn {
+ // FIXME: how to support TLS in library mode?
+ let tls = Box::new(tls::Tls::new());
+ let tls_guard = unsafe { tls.activate() };
+
+ if secondary {
+ let join_notifier = super::thread::Thread::entry();
+ drop(tls_guard);
+ drop(join_notifier);
+
+ EntryReturn(0, 0)
+ } else {
+ extern "C" {
+ fn main(argc: isize, argv: *const *const u8) -> isize;
+ }
+
+ // check entry is being called according to ABI
+ rtassert!(p3 == 0);
+ rtassert!(p4 == 0);
+ rtassert!(p5 == 0);
+
+ unsafe {
+ // The actual types of these arguments are `p1: *const Arg, p2:
+ // usize`. We can't currently customize the argument list of Rust's
+ // main function, so we pass these in as the standard pointer-sized
+ // values in `argc` and `argv`.
+ let ret = main(p2 as _, p1 as _);
+ exit_with_code(ret)
+ }
+ }
+}
+
+pub(super) fn exit_with_code(code: isize) -> ! {
+ if code != 0 {
+ if let Some(mut out) = panic::SgxPanicOutput::new() {
+ let _ = write!(out, "Exited with status code {code}");
+ }
+ }
+ usercalls::exit(code != 0);
+}
+
+#[cfg(not(test))]
+#[no_mangle]
+extern "C" fn abort_reentry() -> ! {
+ usercalls::exit(false)
+}
diff --git a/library/std/src/sys/sgx/abi/panic.rs b/library/std/src/sys/sgx/abi/panic.rs
new file mode 100644
index 000000000..229b3b329
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/panic.rs
@@ -0,0 +1,42 @@
+use super::usercalls::alloc::UserRef;
+use crate::cmp;
+use crate::io::{self, Write};
+use crate::mem;
+
+extern "C" {
+ fn take_debug_panic_buf_ptr() -> *mut u8;
+ static DEBUG: u8;
+}
+
+pub(crate) struct SgxPanicOutput(Option<&'static mut UserRef<[u8]>>);
+
+fn empty_user_slice() -> &'static mut UserRef<[u8]> {
+ unsafe { UserRef::from_raw_parts_mut(1 as *mut u8, 0) }
+}
+
+impl SgxPanicOutput {
+ pub(crate) fn new() -> Option<Self> {
+ if unsafe { DEBUG == 0 } { None } else { Some(SgxPanicOutput(None)) }
+ }
+
+ fn init(&mut self) -> &mut &'static mut UserRef<[u8]> {
+ self.0.get_or_insert_with(|| unsafe {
+ let ptr = take_debug_panic_buf_ptr();
+ if ptr.is_null() { empty_user_slice() } else { UserRef::from_raw_parts_mut(ptr, 1024) }
+ })
+ }
+}
+
+impl Write for SgxPanicOutput {
+ fn write(&mut self, src: &[u8]) -> io::Result<usize> {
+ let dst = mem::replace(self.init(), empty_user_slice());
+ let written = cmp::min(src.len(), dst.len());
+ dst[..written].copy_from_enclave(&src[..written]);
+ self.0 = Some(&mut dst[written..]);
+ Ok(written)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
diff --git a/library/std/src/sys/sgx/abi/reloc.rs b/library/std/src/sys/sgx/abi/reloc.rs
new file mode 100644
index 000000000..02dff0ad2
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/reloc.rs
@@ -0,0 +1,32 @@
+use super::mem;
+use crate::slice::from_raw_parts;
+
+const R_X86_64_RELATIVE: u32 = 8;
+
+#[repr(packed)]
+struct Rela<T> {
+ offset: T,
+ info: T,
+ addend: T,
+}
+
+pub fn relocate_elf_rela() {
+ extern "C" {
+ static RELA: u64;
+ static RELACOUNT: usize;
+ }
+
+ if unsafe { RELACOUNT } == 0 {
+ return;
+ } // unsafe ok: link-time constant
+
+ let relas = unsafe {
+ from_raw_parts::<Rela<u64>>(mem::rel_ptr(RELA), RELACOUNT) // unsafe ok: link-time constant
+ };
+ for rela in relas {
+ if rela.info != (/*0 << 32 |*/R_X86_64_RELATIVE as u64) {
+ rtabort!("Invalid relocation");
+ }
+ unsafe { *mem::rel_ptr_mut::<*const ()>(rela.offset) = mem::rel_ptr(rela.addend) };
+ }
+}
diff --git a/library/std/src/sys/sgx/abi/thread.rs b/library/std/src/sys/sgx/abi/thread.rs
new file mode 100644
index 000000000..ef55b821a
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/thread.rs
@@ -0,0 +1,13 @@
+use fortanix_sgx_abi::Tcs;
+
+/// Gets the ID for the current thread. The ID is guaranteed to be unique among
+/// all currently running threads in the enclave, and it is guaranteed to be
+/// constant for the lifetime of the thread. More specifically for SGX, there
+/// is a one-to-one correspondence of the ID to the address of the TCS.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn current() -> Tcs {
+ extern "C" {
+ fn get_tcs_addr() -> Tcs;
+ }
+ unsafe { get_tcs_addr() }
+}
diff --git a/library/std/src/sys/sgx/abi/tls/mod.rs b/library/std/src/sys/sgx/abi/tls/mod.rs
new file mode 100644
index 000000000..13d96e9a6
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/tls/mod.rs
@@ -0,0 +1,132 @@
+mod sync_bitset;
+
+use self::sync_bitset::*;
+use crate::cell::Cell;
+use crate::mem;
+use crate::num::NonZeroUsize;
+use crate::ptr;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+
+#[cfg(target_pointer_width = "64")]
+const USIZE_BITS: usize = 64;
+const TLS_KEYS: usize = 128; // Same as POSIX minimum
+const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS;
+
+#[cfg_attr(test, linkage = "available_externally")]
+#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_KEY_IN_USEE"]
+static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT;
+macro_rules! dup {
+ ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* ));
+ (() $($val:tt)*) => ([$($val),*])
+}
+#[cfg_attr(test, linkage = "available_externally")]
+#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_DESTRUCTORE"]
+static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) (AtomicUsize::new(0)));
+
+extern "C" {
+ fn get_tls_ptr() -> *const u8;
+ fn set_tls_ptr(tls: *const u8);
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub struct Key(NonZeroUsize);
+
+impl Key {
+ fn to_index(self) -> usize {
+ self.0.get() - 1
+ }
+
+ fn from_index(index: usize) -> Self {
+ Key(NonZeroUsize::new(index + 1).unwrap())
+ }
+
+ pub fn as_usize(self) -> usize {
+ self.0.get()
+ }
+
+ pub fn from_usize(index: usize) -> Self {
+ Key(NonZeroUsize::new(index).unwrap())
+ }
+}
+
+#[repr(C)]
+pub struct Tls {
+ data: [Cell<*mut u8>; TLS_KEYS],
+}
+
+pub struct ActiveTls<'a> {
+ tls: &'a Tls,
+}
+
+impl<'a> Drop for ActiveTls<'a> {
+ fn drop(&mut self) {
+ let value_with_destructor = |key: usize| {
+ let ptr = TLS_DESTRUCTOR[key].load(Ordering::Relaxed);
+ unsafe { mem::transmute::<_, Option<unsafe extern "C" fn(*mut u8)>>(ptr) }
+ .map(|dtor| (&self.tls.data[key], dtor))
+ };
+
+ let mut any_non_null_dtor = true;
+ while any_non_null_dtor {
+ any_non_null_dtor = false;
+ for (value, dtor) in TLS_KEY_IN_USE.iter().filter_map(&value_with_destructor) {
+ let value = value.replace(ptr::null_mut());
+ if !value.is_null() {
+ any_non_null_dtor = true;
+ unsafe { dtor(value) }
+ }
+ }
+ }
+ }
+}
+
+impl Tls {
+ pub fn new() -> Tls {
+ Tls { data: dup!((* * * * * * *) (Cell::new(ptr::null_mut()))) }
+ }
+
+ pub unsafe fn activate(&self) -> ActiveTls<'_> {
+ // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition.
+ unsafe { set_tls_ptr(self as *const Tls as _) };
+ ActiveTls { tls: self }
+ }
+
+ #[allow(unused)]
+ pub unsafe fn activate_persistent(self: Box<Self>) {
+ // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition.
+ unsafe { set_tls_ptr((&*self) as *const Tls as _) };
+ mem::forget(self);
+ }
+
+ unsafe fn current<'a>() -> &'a Tls {
+ // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition.
+ unsafe { &*(get_tls_ptr() as *const Tls) }
+ }
+
+ pub fn create(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
+ let index = if let Some(index) = TLS_KEY_IN_USE.set() {
+ index
+ } else {
+ rtabort!("TLS limit exceeded")
+ };
+ TLS_DESTRUCTOR[index].store(dtor.map_or(0, |f| f as usize), Ordering::Relaxed);
+ Key::from_index(index)
+ }
+
+ pub fn set(key: Key, value: *mut u8) {
+ let index = key.to_index();
+ rtassert!(TLS_KEY_IN_USE.get(index));
+ unsafe { Self::current() }.data[index].set(value);
+ }
+
+ pub fn get(key: Key) -> *mut u8 {
+ let index = key.to_index();
+ rtassert!(TLS_KEY_IN_USE.get(index));
+ unsafe { Self::current() }.data[index].get()
+ }
+
+ pub fn destroy(key: Key) {
+ TLS_KEY_IN_USE.clear(key.to_index());
+ }
+}
diff --git a/library/std/src/sys/sgx/abi/tls/sync_bitset.rs b/library/std/src/sys/sgx/abi/tls/sync_bitset.rs
new file mode 100644
index 000000000..4eeff8f6e
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/tls/sync_bitset.rs
@@ -0,0 +1,85 @@
+#[cfg(test)]
+mod tests;
+
+use super::{TLS_KEYS_BITSET_SIZE, USIZE_BITS};
+use crate::iter::{Enumerate, Peekable};
+use crate::slice::Iter;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+
+/// A bitset that can be used synchronously.
+pub(super) struct SyncBitset([AtomicUsize; TLS_KEYS_BITSET_SIZE]);
+
+pub(super) const SYNC_BITSET_INIT: SyncBitset =
+ SyncBitset([AtomicUsize::new(0), AtomicUsize::new(0)]);
+
+impl SyncBitset {
+ pub fn get(&self, index: usize) -> bool {
+ let (hi, lo) = Self::split(index);
+ (self.0[hi].load(Ordering::Relaxed) & lo) != 0
+ }
+
+ /// Not atomic.
+ pub fn iter(&self) -> SyncBitsetIter<'_> {
+ SyncBitsetIter { iter: self.0.iter().enumerate().peekable(), elem_idx: 0 }
+ }
+
+ pub fn clear(&self, index: usize) {
+ let (hi, lo) = Self::split(index);
+ self.0[hi].fetch_and(!lo, Ordering::Relaxed);
+ }
+
+ /// Sets any unset bit. Not atomic. Returns `None` if all bits were
+ /// observed to be set.
+ pub fn set(&self) -> Option<usize> {
+ 'elems: for (idx, elem) in self.0.iter().enumerate() {
+ let mut current = elem.load(Ordering::Relaxed);
+ loop {
+ if 0 == !current {
+ continue 'elems;
+ }
+ let trailing_ones = (!current).trailing_zeros() as usize;
+ match elem.compare_exchange(
+ current,
+ current | (1 << trailing_ones),
+ Ordering::AcqRel,
+ Ordering::Relaxed,
+ ) {
+ Ok(_) => return Some(idx * USIZE_BITS + trailing_ones),
+ Err(previous) => current = previous,
+ }
+ }
+ }
+ None
+ }
+
+ fn split(index: usize) -> (usize, usize) {
+ (index / USIZE_BITS, 1 << (index % USIZE_BITS))
+ }
+}
+
+pub(super) struct SyncBitsetIter<'a> {
+ iter: Peekable<Enumerate<Iter<'a, AtomicUsize>>>,
+ elem_idx: usize,
+}
+
+impl<'a> Iterator for SyncBitsetIter<'a> {
+ type Item = usize;
+
+ fn next(&mut self) -> Option<usize> {
+ self.iter.peek().cloned().and_then(|(idx, elem)| {
+ let elem = elem.load(Ordering::Relaxed);
+ let low_mask = (1 << self.elem_idx) - 1;
+ let next = elem & !low_mask;
+ let next_idx = next.trailing_zeros() as usize;
+ self.elem_idx = next_idx + 1;
+ if self.elem_idx >= 64 {
+ self.elem_idx = 0;
+ self.iter.next();
+ }
+ match next_idx {
+ 64 => self.next(),
+ _ => Some(idx * USIZE_BITS + next_idx),
+ }
+ })
+ }
+}
diff --git a/library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs b/library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs
new file mode 100644
index 000000000..d7eb2e139
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs
@@ -0,0 +1,25 @@
+use super::*;
+
+fn test_data(bitset: [usize; 2], bit_indices: &[usize]) {
+ let set = SyncBitset([AtomicUsize::new(bitset[0]), AtomicUsize::new(bitset[1])]);
+ assert_eq!(set.iter().collect::<Vec<_>>(), bit_indices);
+ for &i in bit_indices {
+ assert!(set.get(i));
+ }
+}
+
+#[test]
+fn iter() {
+ test_data([0b0110_1001, 0], &[0, 3, 5, 6]);
+ test_data([0x8000_0000_0000_0000, 0x8000_0000_0000_0001], &[63, 64, 127]);
+ test_data([0, 0], &[]);
+}
+
+#[test]
+fn set_get_clear() {
+ let set = SYNC_BITSET_INIT;
+ let key = set.set().unwrap();
+ assert!(set.get(key));
+ set.clear(key);
+ assert!(!set.get(key));
+}
diff --git a/library/std/src/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs
new file mode 100644
index 000000000..ea24fedd0
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/usercalls/alloc.rs
@@ -0,0 +1,732 @@
+#![allow(unused)]
+
+use crate::arch::asm;
+use crate::cell::UnsafeCell;
+use crate::cmp;
+use crate::convert::TryInto;
+use crate::mem;
+use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut};
+use crate::ptr::{self, NonNull};
+use crate::slice;
+use crate::slice::SliceIndex;
+
+use super::super::mem::{is_enclave_range, is_user_range};
+use fortanix_sgx_abi::*;
+
+/// A type that can be safely read from or written to userspace.
+///
+/// Non-exhaustive list of specific requirements for reading and writing:
+/// * **Type is `Copy`** (and therefore also not `Drop`). Copies will be
+/// created when copying from/to userspace. Destructors will not be called.
+/// * **No references or Rust-style owned pointers** (`Vec`, `Arc`, etc.). When
+/// reading from userspace, references into enclave memory must not be
+/// created. Also, only enclave memory is considered managed by the Rust
+/// compiler's static analysis. When reading from userspace, there can be no
+/// guarantee that the value correctly adheres to the expectations of the
+/// type. When writing to userspace, memory addresses of data in enclave
+/// memory must not be leaked for confidentiality reasons. `User` and
+/// `UserRef` are also not allowed for the same reasons.
+/// * **No fat pointers.** When reading from userspace, the size or vtable
+/// pointer could be automatically interpreted and used by the code. When
+/// writing to userspace, memory addresses of data in enclave memory (such
+/// as vtable pointers) must not be leaked for confidentiality reasons.
+///
+/// Non-exhaustive list of specific requirements for reading from userspace:
+/// * **Any bit pattern is valid** for this type (no `enum`s). There can be no
+/// guarantee that the value correctly adheres to the expectations of the
+/// type, so any value must be valid for this type.
+///
+/// Non-exhaustive list of specific requirements for writing to userspace:
+/// * **No pointers to enclave memory.** Memory addresses of data in enclave
+/// memory must not be leaked for confidentiality reasons.
+/// * **No internal padding.** Padding might contain previously-initialized
+/// secret data stored at that memory location and must not be leaked for
+/// confidentiality reasons.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub unsafe trait UserSafeSized: Copy + Sized {}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+unsafe impl UserSafeSized for u8 {}
+#[unstable(feature = "sgx_platform", issue = "56975")]
+unsafe impl<T> UserSafeSized for FifoDescriptor<T> {}
+#[unstable(feature = "sgx_platform", issue = "56975")]
+unsafe impl UserSafeSized for ByteBuffer {}
+#[unstable(feature = "sgx_platform", issue = "56975")]
+unsafe impl UserSafeSized for Usercall {}
+#[unstable(feature = "sgx_platform", issue = "56975")]
+unsafe impl UserSafeSized for Return {}
+#[unstable(feature = "sgx_platform", issue = "56975")]
+unsafe impl<T: UserSafeSized> UserSafeSized for [T; 2] {}
+
+/// A type that can be represented in memory as one or more `UserSafeSized`s.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub unsafe trait UserSafe {
+ /// Equivalent to `mem::align_of::<Self>`.
+ fn align_of() -> usize;
+
+ /// Construct a pointer to `Self` given a memory range in user space.
+ ///
+ /// N.B., this takes a size, not a length!
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure the memory range is in user memory, is the
+ /// correct size and is correctly aligned and points to the right type.
+ unsafe fn from_raw_sized_unchecked(ptr: *mut u8, size: usize) -> *mut Self;
+
+ /// Construct a pointer to `Self` given a memory range.
+ ///
+ /// N.B., this takes a size, not a length!
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure the memory range points to the correct type.
+ ///
+ /// # Panics
+ ///
+ /// This function panics if:
+ ///
+ /// * the pointer is not aligned.
+ /// * the pointer is null.
+ /// * the pointed-to range does not fit in the address space.
+ /// * the pointed-to range is not in user memory.
+ unsafe fn from_raw_sized(ptr: *mut u8, size: usize) -> NonNull<Self> {
+ assert!(ptr.wrapping_add(size) >= ptr);
+ // SAFETY: The caller has guaranteed the pointer is valid
+ let ret = unsafe { Self::from_raw_sized_unchecked(ptr, size) };
+ unsafe {
+ Self::check_ptr(ret);
+ NonNull::new_unchecked(ret as _)
+ }
+ }
+
+ /// Checks if a pointer may point to `Self` in user memory.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure the memory range points to the correct type and
+ /// length (if this is a slice).
+ ///
+ /// # Panics
+ ///
+ /// This function panics if:
+ ///
+ /// * the pointer is not aligned.
+ /// * the pointer is null.
+ /// * the pointed-to range is not in user memory.
+ unsafe fn check_ptr(ptr: *const Self) {
+ let is_aligned = |p| -> bool { 0 == (p as usize) & (Self::align_of() - 1) };
+
+ assert!(is_aligned(ptr as *const u8));
+ assert!(is_user_range(ptr as _, mem::size_of_val(unsafe { &*ptr })));
+ assert!(!ptr.is_null());
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+unsafe impl<T: UserSafeSized> UserSafe for T {
+ fn align_of() -> usize {
+ mem::align_of::<T>()
+ }
+
+ unsafe fn from_raw_sized_unchecked(ptr: *mut u8, size: usize) -> *mut Self {
+ assert_eq!(size, mem::size_of::<T>());
+ ptr as _
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+unsafe impl<T: UserSafeSized> UserSafe for [T] {
+ fn align_of() -> usize {
+ mem::align_of::<T>()
+ }
+
+ /// # Safety
+ /// Behavior is undefined if any of these conditions are violated:
+ /// * `ptr` must be [valid] for writes of `size` many bytes, and it must be
+ /// properly aligned.
+ ///
+ /// [valid]: core::ptr#safety
+ /// # Panics
+ ///
+ /// This function panics if:
+ ///
+ /// * the element size is not a factor of the size
+ unsafe fn from_raw_sized_unchecked(ptr: *mut u8, size: usize) -> *mut Self {
+ let elem_size = mem::size_of::<T>();
+ assert_eq!(size % elem_size, 0);
+ let len = size / elem_size;
+ // SAFETY: The caller must uphold the safety contract for `from_raw_sized_unchecked`
+ unsafe { slice::from_raw_parts_mut(ptr as _, len) }
+ }
+}
+
+/// A reference to some type in userspace memory. `&UserRef<T>` is equivalent
+/// to `&T` in enclave memory. Access to the memory is only allowed by copying
+/// to avoid TOCTTOU issues. After copying, code should make sure to completely
+/// check the value before use.
+///
+/// It is also possible to obtain a mutable reference `&mut UserRef<T>`. Unlike
+/// regular mutable references, these are not exclusive. Userspace may always
+/// write to the backing memory at any time, so it can't be assumed that there
+/// the pointed-to memory is uniquely borrowed. The two different reference types
+/// are used solely to indicate intent: a mutable reference is for writing to
+/// user memory, an immutable reference for reading from user memory.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub struct UserRef<T: ?Sized>(UnsafeCell<T>);
+/// An owned type in userspace memory. `User<T>` is equivalent to `Box<T>` in
+/// enclave memory. Access to the memory is only allowed by copying to avoid
+/// TOCTTOU issues. The user memory will be freed when the value is dropped.
+/// After copying, code should make sure to completely check the value before
+/// use.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub struct User<T: UserSafe + ?Sized>(NonNull<UserRef<T>>);
+
+trait NewUserRef<T: ?Sized> {
+ unsafe fn new_userref(v: T) -> Self;
+}
+
+impl<T: ?Sized> NewUserRef<*mut T> for NonNull<UserRef<T>> {
+ unsafe fn new_userref(v: *mut T) -> Self {
+ // SAFETY: The caller has guaranteed the pointer is valid
+ unsafe { NonNull::new_unchecked(v as _) }
+ }
+}
+
+impl<T: ?Sized> NewUserRef<NonNull<T>> for NonNull<UserRef<T>> {
+ unsafe fn new_userref(v: NonNull<T>) -> Self {
+ // SAFETY: The caller has guaranteed the pointer is valid
+ unsafe { NonNull::new_userref(v.as_ptr()) }
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<T: ?Sized> User<T>
+where
+ T: UserSafe,
+{
+ // This function returns memory that is practically uninitialized, but is
+ // not considered "unspecified" or "undefined" for purposes of an
+ // optimizing compiler. This is achieved by returning a pointer from
+ // from outside as obtained by `super::alloc`.
+ fn new_uninit_bytes(size: usize) -> Self {
+ unsafe {
+ // Mustn't call alloc with size 0.
+ let ptr = if size > 0 {
+ // `copy_to_userspace` is more efficient when data is 8-byte aligned
+ let alignment = cmp::max(T::align_of(), 8);
+ rtunwrap!(Ok, super::alloc(size, alignment)) as _
+ } else {
+ T::align_of() as _ // dangling pointer ok for size 0
+ };
+ if let Ok(v) = crate::panic::catch_unwind(|| T::from_raw_sized(ptr, size)) {
+ User(NonNull::new_userref(v))
+ } else {
+ rtabort!("Got invalid pointer from alloc() usercall")
+ }
+ }
+ }
+
+ /// Copies `val` into freshly allocated space in user memory.
+ pub fn new_from_enclave(val: &T) -> Self {
+ unsafe {
+ let mut user = Self::new_uninit_bytes(mem::size_of_val(val));
+ user.copy_from_enclave(val);
+ user
+ }
+ }
+
+ /// Creates an owned `User<T>` from a raw pointer.
+ ///
+ /// # Safety
+ /// The caller must ensure `ptr` points to `T`, is freeable with the `free`
+ /// usercall and the alignment of `T`, and is uniquely owned.
+ ///
+ /// # Panics
+ /// This function panics if:
+ ///
+ /// * The pointer is not aligned
+ /// * The pointer is null
+ /// * The pointed-to range is not in user memory
+ pub unsafe fn from_raw(ptr: *mut T) -> Self {
+ // SAFETY: the caller must uphold the safety contract for `from_raw`.
+ unsafe { T::check_ptr(ptr) };
+ User(unsafe { NonNull::new_userref(ptr) })
+ }
+
+ /// Converts this value into a raw pointer. The value will no longer be
+ /// automatically freed.
+ pub fn into_raw(self) -> *mut T {
+ let ret = self.0;
+ mem::forget(self);
+ ret.as_ptr() as _
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<T> User<T>
+where
+ T: UserSafe,
+{
+ /// Allocate space for `T` in user memory.
+ pub fn uninitialized() -> Self {
+ Self::new_uninit_bytes(mem::size_of::<T>())
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<T> User<[T]>
+where
+ [T]: UserSafe,
+{
+ /// Allocate space for a `[T]` of `n` elements in user memory.
+ pub fn uninitialized(n: usize) -> Self {
+ Self::new_uninit_bytes(n * mem::size_of::<T>())
+ }
+
+ /// Creates an owned `User<[T]>` from a raw thin pointer and a slice length.
+ ///
+ /// # Safety
+ /// The caller must ensure `ptr` points to `len` elements of `T`, is
+ /// freeable with the `free` usercall and the alignment of `T`, and is
+ /// uniquely owned.
+ ///
+ /// # Panics
+ /// This function panics if:
+ ///
+ /// * The pointer is not aligned
+ /// * The pointer is null
+ /// * The pointed-to range does not fit in the address space
+ /// * The pointed-to range is not in user memory
+ pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self {
+ User(unsafe {
+ NonNull::new_userref(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()))
+ })
+ }
+}
+
+/// Copies `len` bytes of data from enclave pointer `src` to userspace `dst`
+///
+/// This function mitigates stale data vulnerabilities by ensuring all writes to untrusted memory are either:
+/// - preceded by the VERW instruction and followed by the MFENCE; LFENCE instruction sequence
+/// - or are in multiples of 8 bytes, aligned to an 8-byte boundary
+///
+/// # Panics
+/// This function panics if:
+///
+/// * The `src` pointer is null
+/// * The `dst` pointer is null
+/// * The `src` memory range is not in enclave memory
+/// * The `dst` memory range is not in user memory
+///
+/// # References
+/// - https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00615.html
+/// - https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/processor-mmio-stale-data-vulnerabilities.html#inpage-nav-3-2-2
+pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize) {
+ unsafe fn copy_bytewise_to_userspace(src: *const u8, dst: *mut u8, len: usize) {
+ unsafe {
+ let mut seg_sel: u16 = 0;
+ for off in 0..len {
+ asm!("
+ mov %ds, ({seg_sel})
+ verw ({seg_sel})
+ movb {val}, ({dst})
+ mfence
+ lfence
+ ",
+ val = in(reg_byte) *src.offset(off as isize),
+ dst = in(reg) dst.offset(off as isize),
+ seg_sel = in(reg) &mut seg_sel,
+ options(nostack, att_syntax)
+ );
+ }
+ }
+ }
+
+ unsafe fn copy_aligned_quadwords_to_userspace(src: *const u8, dst: *mut u8, len: usize) {
+ unsafe {
+ asm!(
+ "rep movsq (%rsi), (%rdi)",
+ inout("rcx") len / 8 => _,
+ inout("rdi") dst => _,
+ inout("rsi") src => _,
+ options(att_syntax, nostack, preserves_flags)
+ );
+ }
+ }
+ assert!(!src.is_null());
+ assert!(!dst.is_null());
+ assert!(is_enclave_range(src, len));
+ assert!(is_user_range(dst, len));
+ assert!(len < isize::MAX as usize);
+ assert!(!(src as usize).overflowing_add(len).1);
+ assert!(!(dst as usize).overflowing_add(len).1);
+
+ if len < 8 {
+ // Can't align on 8 byte boundary: copy safely byte per byte
+ unsafe {
+ copy_bytewise_to_userspace(src, dst, len);
+ }
+ } else if len % 8 == 0 && dst as usize % 8 == 0 {
+ // Copying 8-byte aligned quadwords: copy quad word per quad word
+ unsafe {
+ copy_aligned_quadwords_to_userspace(src, dst, len);
+ }
+ } else {
+ // Split copies into three parts:
+ // +--------+
+ // | small0 | Chunk smaller than 8 bytes
+ // +--------+
+ // | big | Chunk 8-byte aligned, and size a multiple of 8 bytes
+ // +--------+
+ // | small1 | Chunk smaller than 8 bytes
+ // +--------+
+
+ unsafe {
+ // Copy small0
+ let small0_size = (8 - dst as usize % 8) as u8;
+ let small0_src = src;
+ let small0_dst = dst;
+ copy_bytewise_to_userspace(small0_src as _, small0_dst, small0_size as _);
+
+ // Copy big
+ let small1_size = ((len - small0_size as usize) % 8) as u8;
+ let big_size = len - small0_size as usize - small1_size as usize;
+ let big_src = src.offset(small0_size as _);
+ let big_dst = dst.offset(small0_size as _);
+ copy_aligned_quadwords_to_userspace(big_src as _, big_dst, big_size);
+
+ // Copy small1
+ let small1_src = src.offset(big_size as isize + small0_size as isize);
+ let small1_dst = dst.offset(big_size as isize + small0_size as isize);
+ copy_bytewise_to_userspace(small1_src, small1_dst, small1_size as _);
+ }
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<T: ?Sized> UserRef<T>
+where
+ T: UserSafe,
+{
+ /// Creates a `&UserRef<[T]>` from a raw pointer.
+ ///
+ /// # Safety
+ /// The caller must ensure `ptr` points to `T`.
+ ///
+ /// # Panics
+ /// This function panics if:
+ ///
+ /// * The pointer is not aligned
+ /// * The pointer is null
+ /// * The pointed-to range is not in user memory
+ pub unsafe fn from_ptr<'a>(ptr: *const T) -> &'a Self {
+ // SAFETY: The caller must uphold the safety contract for `from_ptr`.
+ unsafe { T::check_ptr(ptr) };
+ unsafe { &*(ptr as *const Self) }
+ }
+
+ /// Creates a `&mut UserRef<[T]>` from a raw pointer. See the struct
+ /// documentation for the nuances regarding a `&mut UserRef<T>`.
+ ///
+ /// # Safety
+ /// The caller must ensure `ptr` points to `T`.
+ ///
+ /// # Panics
+ /// This function panics if:
+ ///
+ /// * The pointer is not aligned
+ /// * The pointer is null
+ /// * The pointed-to range is not in user memory
+ pub unsafe fn from_mut_ptr<'a>(ptr: *mut T) -> &'a mut Self {
+ // SAFETY: The caller must uphold the safety contract for `from_mut_ptr`.
+ unsafe { T::check_ptr(ptr) };
+ unsafe { &mut *(ptr as *mut Self) }
+ }
+
+ /// Copies `val` into user memory.
+ ///
+ /// # Panics
+ /// This function panics if the destination doesn't have the same size as
+ /// the source. This can happen for dynamically-sized types such as slices.
+ pub fn copy_from_enclave(&mut self, val: &T) {
+ unsafe {
+ assert_eq!(mem::size_of_val(val), mem::size_of_val(&*self.0.get()));
+ copy_to_userspace(
+ val as *const T as *const u8,
+ self.0.get() as *mut T as *mut u8,
+ mem::size_of_val(val),
+ );
+ }
+ }
+
+ /// Copies the value from user memory and place it into `dest`.
+ ///
+ /// # Panics
+ /// This function panics if the destination doesn't have the same size as
+ /// the source. This can happen for dynamically-sized types such as slices.
+ pub fn copy_to_enclave(&self, dest: &mut T) {
+ unsafe {
+ assert_eq!(mem::size_of_val(dest), mem::size_of_val(&*self.0.get()));
+ ptr::copy(
+ self.0.get() as *const T as *const u8,
+ dest as *mut T as *mut u8,
+ mem::size_of_val(dest),
+ );
+ }
+ }
+
+ /// Obtain a raw pointer from this reference.
+ pub fn as_raw_ptr(&self) -> *const T {
+ self as *const _ as _
+ }
+
+ /// Obtain a raw pointer from this reference.
+ pub fn as_raw_mut_ptr(&mut self) -> *mut T {
+ self as *mut _ as _
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<T> UserRef<T>
+where
+ T: UserSafe,
+{
+ /// Copies the value from user memory into enclave memory.
+ pub fn to_enclave(&self) -> T {
+ unsafe { ptr::read(self.0.get()) }
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<T> UserRef<[T]>
+where
+ [T]: UserSafe,
+{
+ /// Creates a `&UserRef<[T]>` from a raw thin pointer and a slice length.
+ ///
+ /// # Safety
+ /// The caller must ensure `ptr` points to `n` elements of `T`.
+ ///
+ /// # Panics
+ /// This function panics if:
+ ///
+ /// * The pointer is not aligned
+ /// * The pointer is null
+ /// * The pointed-to range does not fit in the address space
+ /// * The pointed-to range is not in user memory
+ pub unsafe fn from_raw_parts<'a>(ptr: *const T, len: usize) -> &'a Self {
+ // SAFETY: The caller must uphold the safety contract for `from_raw_parts`.
+ unsafe {
+ &*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()).as_ptr() as *const Self)
+ }
+ }
+
+ /// Creates a `&mut UserRef<[T]>` from a raw thin pointer and a slice length.
+ /// See the struct documentation for the nuances regarding a
+ /// `&mut UserRef<T>`.
+ ///
+ /// # Safety
+ /// The caller must ensure `ptr` points to `n` elements of `T`.
+ ///
+ /// # Panics
+ /// This function panics if:
+ ///
+ /// * The pointer is not aligned
+ /// * The pointer is null
+ /// * The pointed-to range does not fit in the address space
+ /// * The pointed-to range is not in user memory
+ pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut T, len: usize) -> &'a mut Self {
+ // SAFETY: The caller must uphold the safety contract for `from_raw_parts_mut`.
+ unsafe {
+ &mut *(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()).as_ptr() as *mut Self)
+ }
+ }
+
+ /// Obtain a raw pointer to the first element of this user slice.
+ pub fn as_ptr(&self) -> *const T {
+ self.0.get() as _
+ }
+
+ /// Obtain a raw pointer to the first element of this user slice.
+ pub fn as_mut_ptr(&mut self) -> *mut T {
+ self.0.get() as _
+ }
+
+ /// Obtain the number of elements in this user slice.
+ pub fn len(&self) -> usize {
+ unsafe { (*self.0.get()).len() }
+ }
+
+ /// Copies the value from user memory and place it into `dest`. Afterwards,
+ /// `dest` will contain exactly `self.len()` elements.
+ ///
+ /// # Panics
+ /// This function panics if the destination doesn't have the same size as
+ /// the source. This can happen for dynamically-sized types such as slices.
+ pub fn copy_to_enclave_vec(&self, dest: &mut Vec<T>) {
+ if let Some(missing) = self.len().checked_sub(dest.capacity()) {
+ dest.reserve(missing)
+ }
+ // SAFETY: We reserve enough space above.
+ unsafe { dest.set_len(self.len()) };
+ self.copy_to_enclave(&mut dest[..]);
+ }
+
+ /// Copies the value from user memory into a vector in enclave memory.
+ pub fn to_enclave(&self) -> Vec<T> {
+ let mut ret = Vec::with_capacity(self.len());
+ self.copy_to_enclave_vec(&mut ret);
+ ret
+ }
+
+ /// Returns an iterator over the slice.
+ pub fn iter(&self) -> Iter<'_, T>
+ where
+ T: UserSafe, // FIXME: should be implied by [T]: UserSafe?
+ {
+ unsafe { Iter((&*self.as_raw_ptr()).iter()) }
+ }
+
+ /// Returns an iterator that allows modifying each value.
+ pub fn iter_mut(&mut self) -> IterMut<'_, T>
+ where
+ T: UserSafe, // FIXME: should be implied by [T]: UserSafe?
+ {
+ unsafe { IterMut((&mut *self.as_raw_mut_ptr()).iter_mut()) }
+ }
+}
+
+/// Immutable user slice iterator
+///
+/// This struct is created by the `iter` method on `UserRef<[T]>`.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub struct Iter<'a, T: 'a + UserSafe>(slice::Iter<'a, T>);
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<'a, T: UserSafe> Iterator for Iter<'a, T> {
+ type Item = &'a UserRef<T>;
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ unsafe { self.0.next().map(|e| UserRef::from_ptr(e)) }
+ }
+}
+
+/// Mutable user slice iterator
+///
+/// This struct is created by the `iter_mut` method on `UserRef<[T]>`.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub struct IterMut<'a, T: 'a + UserSafe>(slice::IterMut<'a, T>);
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<'a, T: UserSafe> Iterator for IterMut<'a, T> {
+ type Item = &'a mut UserRef<T>;
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ unsafe { self.0.next().map(|e| UserRef::from_mut_ptr(e)) }
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<T: ?Sized> Deref for User<T>
+where
+ T: UserSafe,
+{
+ type Target = UserRef<T>;
+
+ fn deref(&self) -> &Self::Target {
+ unsafe { &*self.0.as_ptr() }
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<T: ?Sized> DerefMut for User<T>
+where
+ T: UserSafe,
+{
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ unsafe { &mut *self.0.as_ptr() }
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<T: ?Sized> Drop for User<T>
+where
+ T: UserSafe,
+{
+ fn drop(&mut self) {
+ unsafe {
+ let ptr = (*self.0.as_ptr()).0.get();
+ super::free(ptr as _, mem::size_of_val(&mut *ptr), T::align_of());
+ }
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<T: CoerceUnsized<U>, U> CoerceUnsized<UserRef<U>> for UserRef<T> {}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<T, I> Index<I> for UserRef<[T]>
+where
+ [T]: UserSafe,
+ I: SliceIndex<[T]>,
+ I::Output: UserSafe,
+{
+ type Output = UserRef<I::Output>;
+
+ #[inline]
+ fn index(&self, index: I) -> &UserRef<I::Output> {
+ unsafe {
+ if let Some(slice) = index.get(&*self.as_raw_ptr()) {
+ UserRef::from_ptr(slice)
+ } else {
+ rtabort!("index out of range for user slice");
+ }
+ }
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl<T, I> IndexMut<I> for UserRef<[T]>
+where
+ [T]: UserSafe,
+ I: SliceIndex<[T]>,
+ I::Output: UserSafe,
+{
+ #[inline]
+ fn index_mut(&mut self, index: I) -> &mut UserRef<I::Output> {
+ unsafe {
+ if let Some(slice) = index.get_mut(&mut *self.as_raw_mut_ptr()) {
+ UserRef::from_mut_ptr(slice)
+ } else {
+ rtabort!("index out of range for user slice");
+ }
+ }
+ }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+impl UserRef<super::raw::ByteBuffer> {
+ /// Copies the user memory range pointed to by the user `ByteBuffer` to
+ /// enclave memory.
+ ///
+ /// # Panics
+ /// This function panics if, in the user `ByteBuffer`:
+ ///
+ /// * The pointer is null
+ /// * The pointed-to range does not fit in the address space
+ /// * The pointed-to range is not in user memory
+ pub fn copy_user_buffer(&self) -> Vec<u8> {
+ unsafe {
+ let buf = self.to_enclave();
+ if buf.len > 0 {
+ User::from_raw_parts(buf.data as _, buf.len).to_enclave()
+ } else {
+ // Mustn't look at `data` or call `free` if `len` is `0`.
+ Vec::with_capacity(0)
+ }
+ }
+ }
+}
diff --git a/library/std/src/sys/sgx/abi/usercalls/mod.rs b/library/std/src/sys/sgx/abi/usercalls/mod.rs
new file mode 100644
index 000000000..79d1db5e1
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/usercalls/mod.rs
@@ -0,0 +1,323 @@
+use crate::cmp;
+use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult};
+use crate::sys::rand::rdrand64;
+use crate::time::{Duration, Instant};
+
+pub(crate) mod alloc;
+#[macro_use]
+pub(crate) mod raw;
+#[cfg(test)]
+mod tests;
+
+use self::raw::*;
+
+/// Usercall `read`. See the ABI documentation for more information.
+///
+/// This will do a single `read` usercall and scatter the read data among
+/// `bufs`. To read to a single buffer, just pass a slice of length one.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn read(fd: Fd, bufs: &mut [IoSliceMut<'_>]) -> IoResult<usize> {
+ unsafe {
+ let total_len = bufs.iter().fold(0usize, |sum, buf| sum.saturating_add(buf.len()));
+ let mut userbuf = alloc::User::<[u8]>::uninitialized(total_len);
+ let ret_len = raw::read(fd, userbuf.as_mut_ptr(), userbuf.len()).from_sgx_result()?;
+ let userbuf = &userbuf[..ret_len];
+ let mut index = 0;
+ for buf in bufs {
+ let end = cmp::min(index + buf.len(), userbuf.len());
+ if let Some(buflen) = end.checked_sub(index) {
+ userbuf[index..end].copy_to_enclave(&mut buf[..buflen]);
+ index += buf.len();
+ } else {
+ break;
+ }
+ }
+ Ok(userbuf.len())
+ }
+}
+
+/// Usercall `read_alloc`. See the ABI documentation for more information.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn read_alloc(fd: Fd) -> IoResult<Vec<u8>> {
+ unsafe {
+ let userbuf = ByteBuffer { data: crate::ptr::null_mut(), len: 0 };
+ let mut userbuf = alloc::User::new_from_enclave(&userbuf);
+ raw::read_alloc(fd, userbuf.as_raw_mut_ptr()).from_sgx_result()?;
+ Ok(userbuf.copy_user_buffer())
+ }
+}
+
+/// Usercall `write`. See the ABI documentation for more information.
+///
+/// This will do a single `write` usercall and gather the written data from
+/// `bufs`. To write from a single buffer, just pass a slice of length one.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn write(fd: Fd, bufs: &[IoSlice<'_>]) -> IoResult<usize> {
+ unsafe {
+ let total_len = bufs.iter().fold(0usize, |sum, buf| sum.saturating_add(buf.len()));
+ let mut userbuf = alloc::User::<[u8]>::uninitialized(total_len);
+ let mut index = 0;
+ for buf in bufs {
+ let end = cmp::min(index + buf.len(), userbuf.len());
+ if let Some(buflen) = end.checked_sub(index) {
+ userbuf[index..end].copy_from_enclave(&buf[..buflen]);
+ index += buf.len();
+ } else {
+ break;
+ }
+ }
+ raw::write(fd, userbuf.as_ptr(), userbuf.len()).from_sgx_result()
+ }
+}
+
+/// Usercall `flush`. See the ABI documentation for more information.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn flush(fd: Fd) -> IoResult<()> {
+ unsafe { raw::flush(fd).from_sgx_result() }
+}
+
+/// Usercall `close`. See the ABI documentation for more information.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn close(fd: Fd) {
+ unsafe { raw::close(fd) }
+}
+
+fn string_from_bytebuffer(buf: &alloc::UserRef<ByteBuffer>, usercall: &str, arg: &str) -> String {
+ String::from_utf8(buf.copy_user_buffer())
+ .unwrap_or_else(|_| rtabort!("Usercall {usercall}: expected {arg} to be valid UTF-8"))
+}
+
+/// Usercall `bind_stream`. See the ABI documentation for more information.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn bind_stream(addr: &str) -> IoResult<(Fd, String)> {
+ unsafe {
+ let addr_user = alloc::User::new_from_enclave(addr.as_bytes());
+ let mut local = alloc::User::<ByteBuffer>::uninitialized();
+ let fd = raw::bind_stream(addr_user.as_ptr(), addr_user.len(), local.as_raw_mut_ptr())
+ .from_sgx_result()?;
+ let local = string_from_bytebuffer(&local, "bind_stream", "local_addr");
+ Ok((fd, local))
+ }
+}
+
+/// Usercall `accept_stream`. See the ABI documentation for more information.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn accept_stream(fd: Fd) -> IoResult<(Fd, String, String)> {
+ unsafe {
+ let mut bufs = alloc::User::<[ByteBuffer; 2]>::uninitialized();
+ let mut buf_it = alloc::UserRef::iter_mut(&mut *bufs); // FIXME: can this be done
+ // without forcing coercion?
+ let (local, peer) = (buf_it.next().unwrap(), buf_it.next().unwrap());
+ let fd = raw::accept_stream(fd, local.as_raw_mut_ptr(), peer.as_raw_mut_ptr())
+ .from_sgx_result()?;
+ let local = string_from_bytebuffer(&local, "accept_stream", "local_addr");
+ let peer = string_from_bytebuffer(&peer, "accept_stream", "peer_addr");
+ Ok((fd, local, peer))
+ }
+}
+
+/// Usercall `connect_stream`. See the ABI documentation for more information.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn connect_stream(addr: &str) -> IoResult<(Fd, String, String)> {
+ unsafe {
+ let addr_user = alloc::User::new_from_enclave(addr.as_bytes());
+ let mut bufs = alloc::User::<[ByteBuffer; 2]>::uninitialized();
+ let mut buf_it = alloc::UserRef::iter_mut(&mut *bufs); // FIXME: can this be done
+ // without forcing coercion?
+ let (local, peer) = (buf_it.next().unwrap(), buf_it.next().unwrap());
+ let fd = raw::connect_stream(
+ addr_user.as_ptr(),
+ addr_user.len(),
+ local.as_raw_mut_ptr(),
+ peer.as_raw_mut_ptr(),
+ )
+ .from_sgx_result()?;
+ let local = string_from_bytebuffer(&local, "connect_stream", "local_addr");
+ let peer = string_from_bytebuffer(&peer, "connect_stream", "peer_addr");
+ Ok((fd, local, peer))
+ }
+}
+
+/// Usercall `launch_thread`. See the ABI documentation for more information.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub unsafe fn launch_thread() -> IoResult<()> {
+ // SAFETY: The caller must uphold the safety contract for `launch_thread`.
+ unsafe { raw::launch_thread().from_sgx_result() }
+}
+
+/// Usercall `exit`. See the ABI documentation for more information.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn exit(panic: bool) -> ! {
+ unsafe { raw::exit(panic) }
+}
+
+/// Usercall `wait`. See the ABI documentation for more information.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> {
+ if timeout != WAIT_NO && timeout != WAIT_INDEFINITE {
+ // We don't want people to rely on accuracy of timeouts to make
+ // security decisions in an SGX enclave. That's why we add a random
+ // amount not exceeding +/- 10% to the timeout value to discourage
+ // people from relying on accuracy of timeouts while providing a way
+ // to make things work in other cases. Note that in the SGX threat
+ // model the enclave runner which is serving the wait usercall is not
+ // trusted to ensure accurate timeouts.
+ if let Ok(timeout_signed) = i64::try_from(timeout) {
+ let tenth = timeout_signed / 10;
+ let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0);
+ timeout = timeout_signed.saturating_add(deviation) as _;
+ }
+ }
+ unsafe { raw::wait(event_mask, timeout).from_sgx_result() }
+}
+
+/// This function makes an effort to wait for a non-spurious event at least as
+/// long as `duration`. Note that in general there is no guarantee about accuracy
+/// of time and timeouts in SGX model. The enclave runner serving usercalls may
+/// lie about current time and/or ignore timeout values.
+///
+/// Once the event is observed, `should_wake_up` will be used to determine
+/// whether or not the event was spurious.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn wait_timeout<F>(event_mask: u64, duration: Duration, should_wake_up: F)
+where
+ F: Fn() -> bool,
+{
+ // Calls the wait usercall and checks the result. Returns true if event was
+ // returned, and false if WouldBlock/TimedOut was returned.
+ // If duration is None, it will use WAIT_NO.
+ fn wait_checked(event_mask: u64, duration: Option<Duration>) -> bool {
+ let timeout = duration.map_or(raw::WAIT_NO, |duration| {
+ cmp::min((u64::MAX - 1) as u128, duration.as_nanos()) as u64
+ });
+ match wait(event_mask, timeout) {
+ Ok(eventset) => {
+ if event_mask == 0 {
+ rtabort!("expected wait() to return Err, found Ok.");
+ }
+ rtassert!(eventset != 0 && eventset & !event_mask == 0);
+ true
+ }
+ Err(e) => {
+ rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock);
+ false
+ }
+ }
+ }
+
+ match wait_checked(event_mask, Some(duration)) {
+ false => return, // timed out
+ true if should_wake_up() => return, // woken up
+ true => {} // spurious event
+ }
+
+ // Drain all cached events.
+ // Note that `event_mask != 0` is implied if we get here.
+ loop {
+ match wait_checked(event_mask, None) {
+ false => break, // no more cached events
+ true if should_wake_up() => return, // woken up
+ true => {} // spurious event
+ }
+ }
+
+ // Continue waiting, but take note of time spent waiting so we don't wait
+ // forever. We intentionally don't call `Instant::now()` before this point
+ // to avoid the cost of the `insecure_time` usercall in case there are no
+ // spurious wakeups.
+
+ let start = Instant::now();
+ let mut remaining = duration;
+ loop {
+ match wait_checked(event_mask, Some(remaining)) {
+ false => return, // timed out
+ true if should_wake_up() => return, // woken up
+ true => {} // spurious event
+ }
+ remaining = match duration.checked_sub(start.elapsed()) {
+ Some(remaining) => remaining,
+ None => break,
+ }
+ }
+}
+
+/// Usercall `send`. See the ABI documentation for more information.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn send(event_set: u64, tcs: Option<Tcs>) -> IoResult<()> {
+ unsafe { raw::send(event_set, tcs).from_sgx_result() }
+}
+
+/// Usercall `insecure_time`. See the ABI documentation for more information.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn insecure_time() -> Duration {
+ let t = unsafe { raw::insecure_time() };
+ Duration::new(t / 1_000_000_000, (t % 1_000_000_000) as _)
+}
+
+/// Usercall `alloc`. See the ABI documentation for more information.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn alloc(size: usize, alignment: usize) -> IoResult<*mut u8> {
+ unsafe { raw::alloc(size, alignment).from_sgx_result() }
+}
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+#[doc(inline)]
+pub use self::raw::free;
+
+fn check_os_error(err: Result) -> i32 {
+ // FIXME: not sure how to make sure all variants of Error are covered
+ if err == Error::NotFound as _
+ || err == Error::PermissionDenied as _
+ || err == Error::ConnectionRefused as _
+ || err == Error::ConnectionReset as _
+ || err == Error::ConnectionAborted as _
+ || err == Error::NotConnected as _
+ || err == Error::AddrInUse as _
+ || err == Error::AddrNotAvailable as _
+ || err == Error::BrokenPipe as _
+ || err == Error::AlreadyExists as _
+ || err == Error::WouldBlock as _
+ || err == Error::InvalidInput as _
+ || err == Error::InvalidData as _
+ || err == Error::TimedOut as _
+ || err == Error::WriteZero as _
+ || err == Error::Interrupted as _
+ || err == Error::Other as _
+ || err == Error::UnexpectedEof as _
+ || ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&err)
+ {
+ err
+ } else {
+ rtabort!("Usercall: returned invalid error value {err}")
+ }
+}
+
+trait FromSgxResult {
+ type Return;
+
+ fn from_sgx_result(self) -> IoResult<Self::Return>;
+}
+
+impl<T> FromSgxResult for (Result, T) {
+ type Return = T;
+
+ fn from_sgx_result(self) -> IoResult<Self::Return> {
+ if self.0 == RESULT_SUCCESS {
+ Ok(self.1)
+ } else {
+ Err(IoError::from_raw_os_error(check_os_error(self.0)))
+ }
+ }
+}
+
+impl FromSgxResult for Result {
+ type Return = ();
+
+ fn from_sgx_result(self) -> IoResult<Self::Return> {
+ if self == RESULT_SUCCESS {
+ Ok(())
+ } else {
+ Err(IoError::from_raw_os_error(check_os_error(self)))
+ }
+ }
+}
diff --git a/library/std/src/sys/sgx/abi/usercalls/raw.rs b/library/std/src/sys/sgx/abi/usercalls/raw.rs
new file mode 100644
index 000000000..4267b96cc
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/usercalls/raw.rs
@@ -0,0 +1,251 @@
+#![allow(unused)]
+
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub use fortanix_sgx_abi::*;
+
+use crate::num::NonZeroU64;
+use crate::ptr::NonNull;
+
+#[repr(C)]
+struct UsercallReturn(u64, u64);
+
+extern "C" {
+ fn usercall(nr: NonZeroU64, p1: u64, p2: u64, abort: u64, p3: u64, p4: u64) -> UsercallReturn;
+}
+
+/// Performs the raw usercall operation as defined in the ABI calling convention.
+///
+/// # Safety
+///
+/// The caller must ensure to pass parameters appropriate for the usercall `nr`
+/// and to observe all requirements specified in the ABI.
+///
+/// # Panics
+///
+/// Panics if `nr` is `0`.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+#[inline]
+pub unsafe fn do_usercall(
+ nr: NonZeroU64,
+ p1: u64,
+ p2: u64,
+ p3: u64,
+ p4: u64,
+ abort: bool,
+) -> (u64, u64) {
+ let UsercallReturn(a, b) = unsafe { usercall(nr, p1, p2, abort as _, p3, p4) };
+ (a, b)
+}
+
+type Register = u64;
+
+trait RegisterArgument {
+ fn from_register(_: Register) -> Self;
+ fn into_register(self) -> Register;
+}
+
+trait ReturnValue {
+ fn from_registers(call: &'static str, regs: (Register, Register)) -> Self;
+}
+
+macro_rules! define_usercalls {
+ ($(fn $f:ident($($n:ident: $t:ty),*) $(-> $r:tt)*; )*) => {
+ /// Usercall numbers as per the ABI.
+ #[repr(u64)]
+ #[unstable(feature = "sgx_platform", issue = "56975")]
+ #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
+ #[allow(missing_docs, non_camel_case_types)]
+ #[non_exhaustive]
+ pub enum Usercalls {
+ #[doc(hidden)]
+ __enclave_usercalls_invalid = 0,
+ $($f,)*
+ }
+
+ $(enclave_usercalls_internal_define_usercalls!(def fn $f($($n: $t),*) $(-> $r)*);)*
+ };
+}
+
+macro_rules! define_ra {
+ (< $i:ident > $t:ty) => {
+ impl<$i> RegisterArgument for $t {
+ fn from_register(a: Register) -> Self {
+ a as _
+ }
+ fn into_register(self) -> Register {
+ self as _
+ }
+ }
+ };
+ ($i:ty as $t:ty) => {
+ impl RegisterArgument for $t {
+ fn from_register(a: Register) -> Self {
+ a as $i as _
+ }
+ fn into_register(self) -> Register {
+ self as $i as _
+ }
+ }
+ };
+ ($t:ty) => {
+ impl RegisterArgument for $t {
+ fn from_register(a: Register) -> Self {
+ a as _
+ }
+ fn into_register(self) -> Register {
+ self as _
+ }
+ }
+ };
+}
+
+define_ra!(Register);
+define_ra!(i64);
+define_ra!(u32);
+define_ra!(u32 as i32);
+define_ra!(u16);
+define_ra!(u16 as i16);
+define_ra!(u8);
+define_ra!(u8 as i8);
+define_ra!(usize);
+define_ra!(usize as isize);
+define_ra!(<T> *const T);
+define_ra!(<T> *mut T);
+
+impl RegisterArgument for bool {
+ fn from_register(a: Register) -> bool {
+ if a != 0 { true } else { false }
+ }
+ fn into_register(self) -> Register {
+ self as _
+ }
+}
+
+impl<T: RegisterArgument> RegisterArgument for Option<NonNull<T>> {
+ fn from_register(a: Register) -> Option<NonNull<T>> {
+ NonNull::new(a as _)
+ }
+ fn into_register(self) -> Register {
+ self.map_or(0 as _, NonNull::as_ptr) as _
+ }
+}
+
+impl ReturnValue for ! {
+ fn from_registers(call: &'static str, _regs: (Register, Register)) -> Self {
+ rtabort!("Usercall {call}: did not expect to be re-entered");
+ }
+}
+
+impl ReturnValue for () {
+ fn from_registers(call: &'static str, usercall_retval: (Register, Register)) -> Self {
+ rtassert!(usercall_retval.0 == 0);
+ rtassert!(usercall_retval.1 == 0);
+ ()
+ }
+}
+
+impl<T: RegisterArgument> ReturnValue for T {
+ fn from_registers(call: &'static str, usercall_retval: (Register, Register)) -> Self {
+ rtassert!(usercall_retval.1 == 0);
+ T::from_register(usercall_retval.0)
+ }
+}
+
+impl<T: RegisterArgument, U: RegisterArgument> ReturnValue for (T, U) {
+ fn from_registers(_call: &'static str, regs: (Register, Register)) -> Self {
+ (T::from_register(regs.0), U::from_register(regs.1))
+ }
+}
+
+macro_rules! return_type_is_abort {
+ (!) => {
+ true
+ };
+ ($r:ty) => {
+ false
+ };
+}
+
+// In this macro: using `$r:tt` because `$r:ty` doesn't match ! in `return_type_is_abort`
+macro_rules! enclave_usercalls_internal_define_usercalls {
+ (def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty,
+ $n3:ident: $t3:ty, $n4:ident: $t4:ty) -> $r:tt) => (
+ /// This is the raw function definition, see the ABI documentation for
+ /// more information.
+ #[unstable(feature = "sgx_platform", issue = "56975")]
+ #[inline(always)]
+ pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3, $n4: $t4) -> $r {
+ ReturnValue::from_registers(stringify!($f), unsafe { do_usercall(
+ rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)),
+ RegisterArgument::into_register($n1),
+ RegisterArgument::into_register($n2),
+ RegisterArgument::into_register($n3),
+ RegisterArgument::into_register($n4),
+ return_type_is_abort!($r)
+ ) })
+ }
+ );
+ (def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty, $n3:ident: $t3:ty) -> $r:tt) => (
+ /// This is the raw function definition, see the ABI documentation for
+ /// more information.
+ #[unstable(feature = "sgx_platform", issue = "56975")]
+ #[inline(always)]
+ pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3) -> $r {
+ ReturnValue::from_registers(stringify!($f), unsafe { do_usercall(
+ rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)),
+ RegisterArgument::into_register($n1),
+ RegisterArgument::into_register($n2),
+ RegisterArgument::into_register($n3),
+ 0,
+ return_type_is_abort!($r)
+ ) })
+ }
+ );
+ (def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty) -> $r:tt) => (
+ /// This is the raw function definition, see the ABI documentation for
+ /// more information.
+ #[unstable(feature = "sgx_platform", issue = "56975")]
+ #[inline(always)]
+ pub unsafe fn $f($n1: $t1, $n2: $t2) -> $r {
+ ReturnValue::from_registers(stringify!($f), unsafe { do_usercall(
+ rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)),
+ RegisterArgument::into_register($n1),
+ RegisterArgument::into_register($n2),
+ 0,0,
+ return_type_is_abort!($r)
+ ) })
+ }
+ );
+ (def fn $f:ident($n1:ident: $t1:ty) -> $r:tt) => (
+ /// This is the raw function definition, see the ABI documentation for
+ /// more information.
+ #[unstable(feature = "sgx_platform", issue = "56975")]
+ #[inline(always)]
+ pub unsafe fn $f($n1: $t1) -> $r {
+ ReturnValue::from_registers(stringify!($f), unsafe { do_usercall(
+ rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)),
+ RegisterArgument::into_register($n1),
+ 0,0,0,
+ return_type_is_abort!($r)
+ ) })
+ }
+ );
+ (def fn $f:ident() -> $r:tt) => (
+ /// This is the raw function definition, see the ABI documentation for
+ /// more information.
+ #[unstable(feature = "sgx_platform", issue = "56975")]
+ #[inline(always)]
+ pub unsafe fn $f() -> $r {
+ ReturnValue::from_registers(stringify!($f), unsafe { do_usercall(
+ rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)),
+ 0,0,0,0,
+ return_type_is_abort!($r)
+ ) })
+ }
+ );
+ (def fn $f:ident($($n:ident: $t:ty),*)) => (
+ enclave_usercalls_internal_define_usercalls!(def fn $f($($n: $t),*) -> ());
+ );
+}
+
+invoke_with_usercalls!(define_usercalls);
diff --git a/library/std/src/sys/sgx/abi/usercalls/tests.rs b/library/std/src/sys/sgx/abi/usercalls/tests.rs
new file mode 100644
index 000000000..cbf7d7d54
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/usercalls/tests.rs
@@ -0,0 +1,30 @@
+use super::alloc::copy_to_userspace;
+use super::alloc::User;
+
+#[test]
+fn test_copy_function() {
+ let mut src = [0u8; 100];
+ let mut dst = User::<[u8]>::uninitialized(100);
+
+ for i in 0..src.len() {
+ src[i] = i as _;
+ }
+
+ for size in 0..48 {
+ // For all possible alignment
+ for offset in 0..8 {
+ // overwrite complete dst
+ dst.copy_from_enclave(&[0u8; 100]);
+
+ // Copy src[0..size] to dst + offset
+ unsafe { copy_to_userspace(src.as_ptr(), dst.as_mut_ptr().offset(offset), size) };
+
+ // Verify copy
+ for byte in 0..size {
+ unsafe {
+ assert_eq!(*dst.as_ptr().offset(offset + byte as isize), src[byte as usize]);
+ }
+ }
+ }
+ }
+}
diff --git a/library/std/src/sys/sgx/alloc.rs b/library/std/src/sys/sgx/alloc.rs
new file mode 100644
index 000000000..4aea28cb8
--- /dev/null
+++ b/library/std/src/sys/sgx/alloc.rs
@@ -0,0 +1,98 @@
+use crate::alloc::{GlobalAlloc, Layout, System};
+use crate::ptr;
+use crate::sys::sgx::abi::mem as sgx_mem;
+use core::sync::atomic::{AtomicBool, Ordering};
+
+use super::waitqueue::SpinMutex;
+
+// Using a SpinMutex because we never want to exit the enclave waiting for the
+// allocator.
+//
+// The current allocator here is the `dlmalloc` crate which we've got included
+// in the rust-lang/rust repository as a submodule. The crate is a port of
+// dlmalloc.c from C to Rust.
+#[cfg_attr(test, linkage = "available_externally")]
+#[export_name = "_ZN16__rust_internals3std3sys3sgx5alloc8DLMALLOCE"]
+static DLMALLOC: SpinMutex<dlmalloc::Dlmalloc<Sgx>> =
+ SpinMutex::new(dlmalloc::Dlmalloc::new_with_allocator(Sgx {}));
+
+struct Sgx;
+
+unsafe impl dlmalloc::Allocator for Sgx {
+ /// Allocs system resources
+ fn alloc(&self, _size: usize) -> (*mut u8, usize, u32) {
+ static INIT: AtomicBool = AtomicBool::new(false);
+
+ // No ordering requirement since this function is protected by the global lock.
+ if !INIT.swap(true, Ordering::Relaxed) {
+ (sgx_mem::heap_base() as _, sgx_mem::heap_size(), 0)
+ } else {
+ (ptr::null_mut(), 0, 0)
+ }
+ }
+
+ fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 {
+ ptr::null_mut()
+ }
+
+ fn free_part(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize) -> bool {
+ false
+ }
+
+ fn free(&self, _ptr: *mut u8, _size: usize) -> bool {
+ return false;
+ }
+
+ fn can_release_part(&self, _flags: u32) -> bool {
+ false
+ }
+
+ fn allocates_zeros(&self) -> bool {
+ false
+ }
+
+ fn page_size(&self) -> usize {
+ 0x1000
+ }
+}
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+ #[inline]
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ // SAFETY: the caller must uphold the safety contract for `malloc`
+ unsafe { DLMALLOC.lock().malloc(layout.size(), layout.align()) }
+ }
+
+ #[inline]
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+ // SAFETY: the caller must uphold the safety contract for `malloc`
+ unsafe { DLMALLOC.lock().calloc(layout.size(), layout.align()) }
+ }
+
+ #[inline]
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ // SAFETY: the caller must uphold the safety contract for `malloc`
+ unsafe { DLMALLOC.lock().free(ptr, layout.size(), layout.align()) }
+ }
+
+ #[inline]
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ // SAFETY: the caller must uphold the safety contract for `malloc`
+ unsafe { DLMALLOC.lock().realloc(ptr, layout.size(), layout.align(), new_size) }
+ }
+}
+
+// The following functions are needed by libunwind. These symbols are named
+// in pre-link args for the target specification, so keep that in sync.
+#[cfg(not(test))]
+#[no_mangle]
+pub unsafe extern "C" fn __rust_c_alloc(size: usize, align: usize) -> *mut u8 {
+ unsafe { crate::alloc::alloc(Layout::from_size_align_unchecked(size, align)) }
+}
+
+#[cfg(not(test))]
+#[no_mangle]
+pub unsafe extern "C" fn __rust_c_dealloc(ptr: *mut u8, size: usize, align: usize) {
+ unsafe { crate::alloc::dealloc(ptr, Layout::from_size_align_unchecked(size, align)) }
+}
diff --git a/library/std/src/sys/sgx/args.rs b/library/std/src/sys/sgx/args.rs
new file mode 100644
index 000000000..ef4176c4a
--- /dev/null
+++ b/library/std/src/sys/sgx/args.rs
@@ -0,0 +1,59 @@
+use super::abi::usercalls::{alloc, raw::ByteBuffer};
+use crate::ffi::OsString;
+use crate::fmt;
+use crate::slice;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+use crate::sys::os_str::Buf;
+use crate::sys_common::FromInner;
+
+#[cfg_attr(test, linkage = "available_externally")]
+#[export_name = "_ZN16__rust_internals3std3sys3sgx4args4ARGSE"]
+static ARGS: AtomicUsize = AtomicUsize::new(0);
+type ArgsStore = Vec<OsString>;
+
+#[cfg_attr(test, allow(dead_code))]
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ if argc != 0 {
+ let args = unsafe { alloc::User::<[ByteBuffer]>::from_raw_parts(argv as _, argc as _) };
+ let args = args
+ .iter()
+ .map(|a| OsString::from_inner(Buf { inner: a.copy_user_buffer() }))
+ .collect::<ArgsStore>();
+ ARGS.store(Box::into_raw(Box::new(args)) as _, Ordering::Relaxed);
+ }
+}
+
+pub fn args() -> Args {
+ let args = unsafe { (ARGS.load(Ordering::Relaxed) as *const ArgsStore).as_ref() };
+ if let Some(args) = args { Args(args.iter()) } else { Args([].iter()) }
+}
+
+pub struct Args(slice::Iter<'static, OsString>);
+
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.as_slice().fmt(f)
+ }
+}
+
+impl Iterator for Args {
+ type Item = OsString;
+ fn next(&mut self) -> Option<OsString> {
+ self.0.next().cloned()
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.0.size_hint()
+ }
+}
+
+impl ExactSizeIterator for Args {
+ fn len(&self) -> usize {
+ self.0.len()
+ }
+}
+
+impl DoubleEndedIterator for Args {
+ fn next_back(&mut self) -> Option<OsString> {
+ self.0.next_back().cloned()
+ }
+}
diff --git a/library/std/src/sys/sgx/condvar.rs b/library/std/src/sys/sgx/condvar.rs
new file mode 100644
index 000000000..36534e0ef
--- /dev/null
+++ b/library/std/src/sys/sgx/condvar.rs
@@ -0,0 +1,45 @@
+use crate::sys::locks::Mutex;
+use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+use crate::time::Duration;
+
+use super::waitqueue::{SpinMutex, WaitQueue, WaitVariable};
+
+pub struct Condvar {
+ inner: SpinMutex<WaitVariable<()>>,
+}
+
+pub(crate) type MovableCondvar = LazyBox<Condvar>;
+
+impl LazyInit for Condvar {
+ fn init() -> Box<Self> {
+ Box::new(Self::new())
+ }
+}
+
+impl Condvar {
+ pub const fn new() -> Condvar {
+ Condvar { inner: SpinMutex::new(WaitVariable::new(())) }
+ }
+
+ #[inline]
+ pub unsafe fn notify_one(&self) {
+ let _ = WaitQueue::notify_one(self.inner.lock());
+ }
+
+ #[inline]
+ pub unsafe fn notify_all(&self) {
+ let _ = WaitQueue::notify_all(self.inner.lock());
+ }
+
+ pub unsafe fn wait(&self, mutex: &Mutex) {
+ let guard = self.inner.lock();
+ WaitQueue::wait(guard, || unsafe { mutex.unlock() });
+ unsafe { mutex.lock() }
+ }
+
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ let success = WaitQueue::wait_timeout(&self.inner, dur, || unsafe { mutex.unlock() });
+ unsafe { mutex.lock() };
+ success
+ }
+}
diff --git a/library/std/src/sys/sgx/env.rs b/library/std/src/sys/sgx/env.rs
new file mode 100644
index 000000000..8043b7c52
--- /dev/null
+++ b/library/std/src/sys/sgx/env.rs
@@ -0,0 +1,9 @@
+pub mod os {
+ pub const FAMILY: &str = "";
+ pub const OS: &str = "";
+ pub const DLL_PREFIX: &str = "";
+ pub const DLL_SUFFIX: &str = ".sgxs";
+ pub const DLL_EXTENSION: &str = "sgxs";
+ pub const EXE_SUFFIX: &str = ".sgxs";
+ pub const EXE_EXTENSION: &str = "sgxs";
+}
diff --git a/library/std/src/sys/sgx/fd.rs b/library/std/src/sys/sgx/fd.rs
new file mode 100644
index 000000000..e5dc5b5ad
--- /dev/null
+++ b/library/std/src/sys/sgx/fd.rs
@@ -0,0 +1,84 @@
+use fortanix_sgx_abi::Fd;
+
+use super::abi::usercalls;
+use crate::io::{self, IoSlice, IoSliceMut};
+use crate::mem;
+use crate::sys::{AsInner, FromInner, IntoInner};
+
+#[derive(Debug)]
+pub struct FileDesc {
+ fd: Fd,
+}
+
+impl FileDesc {
+ pub fn new(fd: Fd) -> FileDesc {
+ FileDesc { fd: fd }
+ }
+
+ pub fn raw(&self) -> Fd {
+ self.fd
+ }
+
+ /// Extracts the actual file descriptor without closing it.
+ pub fn into_raw(self) -> Fd {
+ let fd = self.fd;
+ mem::forget(self);
+ fd
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ usercalls::read(self.fd, &mut [IoSliceMut::new(buf)])
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ usercalls::read(self.fd, bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ usercalls::write(self.fd, &[IoSlice::new(buf)])
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ usercalls::write(self.fd, bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ usercalls::flush(self.fd)
+ }
+}
+
+impl AsInner<Fd> for FileDesc {
+ fn as_inner(&self) -> &Fd {
+ &self.fd
+ }
+}
+
+impl IntoInner<Fd> for FileDesc {
+ fn into_inner(self) -> Fd {
+ let fd = self.fd;
+ mem::forget(self);
+ fd
+ }
+}
+
+impl FromInner<Fd> for FileDesc {
+ fn from_inner(fd: Fd) -> FileDesc {
+ FileDesc { fd }
+ }
+}
+
+impl Drop for FileDesc {
+ fn drop(&mut self) {
+ usercalls::close(self.fd)
+ }
+}
diff --git a/library/std/src/sys/sgx/memchr.rs b/library/std/src/sys/sgx/memchr.rs
new file mode 100644
index 000000000..996748219
--- /dev/null
+++ b/library/std/src/sys/sgx/memchr.rs
@@ -0,0 +1 @@
+pub use core::slice::memchr::{memchr, memrchr};
diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs
new file mode 100644
index 000000000..696400670
--- /dev/null
+++ b/library/std/src/sys/sgx/mod.rs
@@ -0,0 +1,167 @@
+//! System bindings for the Fortanix SGX platform
+//!
+//! This module contains the facade (aka platform-specific) implementations of
+//! OS level functionality for Fortanix SGX.
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::io::ErrorKind;
+use crate::sync::atomic::{AtomicBool, Ordering};
+
+pub mod abi;
+mod waitqueue;
+
+pub mod alloc;
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+pub mod env;
+pub mod fd;
+#[path = "../unsupported/fs.rs"]
+pub mod fs;
+#[path = "../unsupported/io.rs"]
+pub mod io;
+pub mod memchr;
+pub mod net;
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+pub mod path;
+#[path = "../unsupported/pipe.rs"]
+pub mod pipe;
+#[path = "../unsupported/process.rs"]
+pub mod process;
+pub mod stdio;
+pub mod thread;
+pub mod thread_local_key;
+pub mod time;
+
+mod condvar;
+mod mutex;
+mod rwlock;
+
+pub mod locks {
+ pub use super::condvar::*;
+ pub use super::mutex::*;
+ pub use super::rwlock::*;
+}
+
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ unsafe {
+ args::init(argc, argv);
+ }
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {}
+
+/// This function is used to implement functionality that simply doesn't exist.
+/// Programs relying on this functionality will need to deal with the error.
+pub fn unsupported<T>() -> crate::io::Result<T> {
+ Err(unsupported_err())
+}
+
+pub fn unsupported_err() -> crate::io::Error {
+ crate::io::const_io_error!(ErrorKind::Unsupported, "operation not supported on SGX yet")
+}
+
+/// This function is used to implement various functions that doesn't exist,
+/// but the lack of which might not be reason for error. If no error is
+/// returned, the program might very well be able to function normally. This is
+/// what happens when `SGX_INEFFECTIVE_ERROR` is set to `true`. If it is
+/// `false`, the behavior is the same as `unsupported`.
+pub fn sgx_ineffective<T>(v: T) -> crate::io::Result<T> {
+ static SGX_INEFFECTIVE_ERROR: AtomicBool = AtomicBool::new(false);
+ if SGX_INEFFECTIVE_ERROR.load(Ordering::Relaxed) {
+ Err(crate::io::const_io_error!(
+ ErrorKind::Uncategorized,
+ "operation can't be trusted to have any effect on SGX",
+ ))
+ } else {
+ Ok(v)
+ }
+}
+
+pub fn decode_error_kind(code: i32) -> ErrorKind {
+ use fortanix_sgx_abi::Error;
+
+ // FIXME: not sure how to make sure all variants of Error are covered
+ if code == Error::NotFound as _ {
+ ErrorKind::NotFound
+ } else if code == Error::PermissionDenied as _ {
+ ErrorKind::PermissionDenied
+ } else if code == Error::ConnectionRefused as _ {
+ ErrorKind::ConnectionRefused
+ } else if code == Error::ConnectionReset as _ {
+ ErrorKind::ConnectionReset
+ } else if code == Error::ConnectionAborted as _ {
+ ErrorKind::ConnectionAborted
+ } else if code == Error::NotConnected as _ {
+ ErrorKind::NotConnected
+ } else if code == Error::AddrInUse as _ {
+ ErrorKind::AddrInUse
+ } else if code == Error::AddrNotAvailable as _ {
+ ErrorKind::AddrNotAvailable
+ } else if code == Error::BrokenPipe as _ {
+ ErrorKind::BrokenPipe
+ } else if code == Error::AlreadyExists as _ {
+ ErrorKind::AlreadyExists
+ } else if code == Error::WouldBlock as _ {
+ ErrorKind::WouldBlock
+ } else if code == Error::InvalidInput as _ {
+ ErrorKind::InvalidInput
+ } else if code == Error::InvalidData as _ {
+ ErrorKind::InvalidData
+ } else if code == Error::TimedOut as _ {
+ ErrorKind::TimedOut
+ } else if code == Error::WriteZero as _ {
+ ErrorKind::WriteZero
+ } else if code == Error::Interrupted as _ {
+ ErrorKind::Interrupted
+ } else if code == Error::Other as _ {
+ ErrorKind::Uncategorized
+ } else if code == Error::UnexpectedEof as _ {
+ ErrorKind::UnexpectedEof
+ } else {
+ ErrorKind::Uncategorized
+ }
+}
+
+pub fn abort_internal() -> ! {
+ abi::usercalls::exit(true)
+}
+
+// This function is needed by the panic runtime. The symbol is named in
+// pre-link args for the target specification, so keep that in sync.
+#[cfg(not(test))]
+#[no_mangle]
+// NB. used by both libunwind and libpanic_abort
+pub extern "C" fn __rust_abort() {
+ abort_internal();
+}
+
+pub mod rand {
+ pub fn rdrand64() -> u64 {
+ unsafe {
+ let mut ret: u64 = 0;
+ for _ in 0..10 {
+ if crate::arch::x86_64::_rdrand64_step(&mut ret) == 1 {
+ return ret;
+ }
+ }
+ rtabort!("Failed to obtain random data");
+ }
+ }
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+ (self::rand::rdrand64(), self::rand::rdrand64())
+}
+
+pub use crate::sys_common::{AsInner, FromInner, IntoInner};
+
+pub trait TryIntoInner<Inner>: Sized {
+ fn try_into_inner(self) -> Result<Inner, Self>;
+}
diff --git a/library/std/src/sys/sgx/mutex.rs b/library/std/src/sys/sgx/mutex.rs
new file mode 100644
index 000000000..513cd77fd
--- /dev/null
+++ b/library/std/src/sys/sgx/mutex.rs
@@ -0,0 +1,62 @@
+use super::waitqueue::{try_lock_or_false, SpinMutex, WaitQueue, WaitVariable};
+use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+
+pub struct Mutex {
+ inner: SpinMutex<WaitVariable<bool>>,
+}
+
+// not movable: see UnsafeList implementation
+pub(crate) type MovableMutex = LazyBox<Mutex>;
+
+impl LazyInit for Mutex {
+ fn init() -> Box<Self> {
+ Box::new(Self::new())
+ }
+}
+
+// Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28
+impl Mutex {
+ pub const fn new() -> Mutex {
+ Mutex { inner: SpinMutex::new(WaitVariable::new(false)) }
+ }
+
+ #[inline]
+ pub unsafe fn init(&mut self) {}
+
+ #[inline]
+ pub unsafe fn lock(&self) {
+ let mut guard = self.inner.lock();
+ if *guard.lock_var() {
+ // Another thread has the lock, wait
+ WaitQueue::wait(guard, || {})
+ // Another thread has passed the lock to us
+ } else {
+ // We are just now obtaining the lock
+ *guard.lock_var_mut() = true;
+ }
+ }
+
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ let guard = self.inner.lock();
+ if let Err(mut guard) = WaitQueue::notify_one(guard) {
+ // No other waiters, unlock
+ *guard.lock_var_mut() = false;
+ } else {
+ // There was a thread waiting, just pass the lock
+ }
+ }
+
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ let mut guard = try_lock_or_false!(self.inner);
+ if *guard.lock_var() {
+ // Another thread has the lock
+ false
+ } else {
+ // We are just now obtaining the lock
+ *guard.lock_var_mut() = true;
+ true
+ }
+ }
+}
diff --git a/library/std/src/sys/sgx/net.rs b/library/std/src/sys/sgx/net.rs
new file mode 100644
index 000000000..4c4cd7d1d
--- /dev/null
+++ b/library/std/src/sys/sgx/net.rs
@@ -0,0 +1,541 @@
+use crate::error;
+use crate::fmt;
+use crate::io::{self, IoSlice, IoSliceMut};
+use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs};
+use crate::sync::Arc;
+use crate::sys::fd::FileDesc;
+use crate::sys::{sgx_ineffective, unsupported, AsInner, FromInner, IntoInner, TryIntoInner};
+use crate::time::Duration;
+
+use super::abi::usercalls;
+
+const DEFAULT_FAKE_TTL: u32 = 64;
+
+#[derive(Debug, Clone)]
+pub struct Socket {
+ inner: Arc<FileDesc>,
+ local_addr: Option<String>,
+}
+
+impl Socket {
+ fn new(fd: usercalls::raw::Fd, local_addr: String) -> Socket {
+ Socket { inner: Arc::new(FileDesc::new(fd)), local_addr: Some(local_addr) }
+ }
+}
+
+impl AsInner<FileDesc> for Socket {
+ fn as_inner(&self) -> &FileDesc {
+ &self.inner
+ }
+}
+
+impl TryIntoInner<FileDesc> for Socket {
+ fn try_into_inner(self) -> Result<FileDesc, Socket> {
+ let Socket { inner, local_addr } = self;
+ Arc::try_unwrap(inner).map_err(|inner| Socket { inner, local_addr })
+ }
+}
+
+impl FromInner<(FileDesc, Option<String>)> for Socket {
+ fn from_inner((inner, local_addr): (FileDesc, Option<String>)) -> Socket {
+ Socket { inner: Arc::new(inner), local_addr }
+ }
+}
+
+#[derive(Clone)]
+pub struct TcpStream {
+ inner: Socket,
+ peer_addr: Option<String>,
+}
+
+impl fmt::Debug for TcpStream {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut res = f.debug_struct("TcpStream");
+
+ if let Some(ref addr) = self.inner.local_addr {
+ res.field("addr", addr);
+ }
+
+ if let Some(ref peer) = self.peer_addr {
+ res.field("peer", peer);
+ }
+
+ res.field("fd", &self.inner.inner.as_inner()).finish()
+ }
+}
+
+fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result<String> {
+ match result {
+ Ok(saddr) => Ok(saddr.to_string()),
+ // need to downcast twice because io::Error::into_inner doesn't return the original
+ // value if the conversion fails
+ Err(e) => {
+ if e.get_ref().and_then(|e| e.downcast_ref::<NonIpSockAddr>()).is_some() {
+ Ok(e.into_inner().unwrap().downcast::<NonIpSockAddr>().unwrap().host)
+ } else {
+ Err(e)
+ }
+ }
+ }
+}
+
+fn addr_to_sockaddr(addr: &Option<String>) -> io::Result<SocketAddr> {
+ addr.as_ref()
+ .ok_or(io::ErrorKind::AddrNotAvailable)?
+ .to_socket_addrs()
+ // unwrap OK: if an iterator is returned, we're guaranteed to get exactly one entry
+ .map(|mut it| it.next().unwrap())
+}
+
+impl TcpStream {
+ pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
+ let addr = io_err_to_addr(addr)?;
+ let (fd, local_addr, peer_addr) = usercalls::connect_stream(&addr)?;
+ Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) })
+ }
+
+ pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result<TcpStream> {
+ if dur == Duration::default() {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "cannot set a 0 duration timeout",
+ ));
+ }
+ Self::connect(Ok(addr)) // FIXME: ignoring timeout
+ }
+
+ pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
+ match dur {
+ Some(dur) if dur == Duration::default() => {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "cannot set a 0 duration timeout",
+ ));
+ }
+ _ => sgx_ineffective(()),
+ }
+ }
+
+ pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
+ match dur {
+ Some(dur) if dur == Duration::default() => {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "cannot set a 0 duration timeout",
+ ));
+ }
+ _ => sgx_ineffective(()),
+ }
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ sgx_ineffective(None)
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ sgx_ineffective(None)
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ Ok(0)
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.inner.inner.read(buf)
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.inner.inner.read_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ self.inner.inner.is_read_vectored()
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.inner.inner.write(buf)
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.inner.inner.write_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ self.inner.inner.is_write_vectored()
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ addr_to_sockaddr(&self.peer_addr)
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ addr_to_sockaddr(&self.inner.local_addr)
+ }
+
+ pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+ sgx_ineffective(())
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpStream> {
+ Ok(self.clone())
+ }
+
+ pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
+ sgx_ineffective(())
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ sgx_ineffective(None)
+ }
+
+ pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+ sgx_ineffective(())
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ sgx_ineffective(false)
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ sgx_ineffective(())
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ sgx_ineffective(DEFAULT_FAKE_TTL)
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ Ok(None)
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ sgx_ineffective(())
+ }
+}
+
+impl AsInner<Socket> for TcpStream {
+ fn as_inner(&self) -> &Socket {
+ &self.inner
+ }
+}
+
+// `Inner` includes `peer_addr` so that a `TcpStream` maybe correctly
+// reconstructed if `Socket::try_into_inner` fails.
+impl IntoInner<(Socket, Option<String>)> for TcpStream {
+ fn into_inner(self) -> (Socket, Option<String>) {
+ (self.inner, self.peer_addr)
+ }
+}
+
+impl FromInner<(Socket, Option<String>)> for TcpStream {
+ fn from_inner((inner, peer_addr): (Socket, Option<String>)) -> TcpStream {
+ TcpStream { inner, peer_addr }
+ }
+}
+
+#[derive(Clone)]
+pub struct TcpListener {
+ inner: Socket,
+}
+
+impl fmt::Debug for TcpListener {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut res = f.debug_struct("TcpListener");
+
+ if let Some(ref addr) = self.inner.local_addr {
+ res.field("addr", addr);
+ }
+
+ res.field("fd", &self.inner.inner.as_inner()).finish()
+ }
+}
+
+impl TcpListener {
+ pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
+ let addr = io_err_to_addr(addr)?;
+ let (fd, local_addr) = usercalls::bind_stream(&addr)?;
+ Ok(TcpListener { inner: Socket::new(fd, local_addr) })
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ addr_to_sockaddr(&self.inner.local_addr)
+ }
+
+ pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+ let (fd, local_addr, peer_addr) = usercalls::accept_stream(self.inner.inner.raw())?;
+ let peer_addr = Some(peer_addr);
+ let ret_peer = addr_to_sockaddr(&peer_addr).unwrap_or_else(|_| ([0; 4], 0).into());
+ Ok((TcpStream { inner: Socket::new(fd, local_addr), peer_addr }, ret_peer))
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpListener> {
+ Ok(self.clone())
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ sgx_ineffective(())
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ sgx_ineffective(DEFAULT_FAKE_TTL)
+ }
+
+ pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+ sgx_ineffective(())
+ }
+
+ pub fn only_v6(&self) -> io::Result<bool> {
+ sgx_ineffective(false)
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ Ok(None)
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ sgx_ineffective(())
+ }
+}
+
+impl AsInner<Socket> for TcpListener {
+ fn as_inner(&self) -> &Socket {
+ &self.inner
+ }
+}
+
+impl IntoInner<Socket> for TcpListener {
+ fn into_inner(self) -> Socket {
+ self.inner
+ }
+}
+
+impl FromInner<Socket> for TcpListener {
+ fn from_inner(inner: Socket) -> TcpListener {
+ TcpListener { inner }
+ }
+}
+
+pub struct UdpSocket(!);
+
+impl UdpSocket {
+ pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+ unsupported()
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ self.0
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ self.0
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ self.0
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ self.0
+ }
+
+ pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn duplicate(&self) -> io::Result<UdpSocket> {
+ self.0
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0
+ }
+
+ pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn broadcast(&self) -> io::Result<bool> {
+ self.0
+ }
+
+ pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+ self.0
+ }
+
+ pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+ self.0
+ }
+
+ pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+ self.0
+ }
+
+ pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ self.0
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ self.0
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
+ self.0
+ }
+}
+
+impl fmt::Debug for UdpSocket {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+#[derive(Debug)]
+pub struct NonIpSockAddr {
+ host: String,
+}
+
+impl error::Error for NonIpSockAddr {
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "Failed to convert address to SocketAddr"
+ }
+}
+
+impl fmt::Display for NonIpSockAddr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Failed to convert address to SocketAddr: {}", self.host)
+ }
+}
+
+pub struct LookupHost(!);
+
+impl LookupHost {
+ fn new(host: String) -> io::Result<LookupHost> {
+ Err(io::Error::new(io::ErrorKind::Uncategorized, NonIpSockAddr { host }))
+ }
+
+ pub fn port(&self) -> u16 {
+ self.0
+ }
+}
+
+impl Iterator for LookupHost {
+ type Item = SocketAddr;
+ fn next(&mut self) -> Option<SocketAddr> {
+ self.0
+ }
+}
+
+impl TryFrom<&str> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(v: &str) -> io::Result<LookupHost> {
+ LookupHost::new(v.to_owned())
+ }
+}
+
+impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from((host, port): (&'a str, u16)) -> io::Result<LookupHost> {
+ LookupHost::new(format!("{host}:{port}"))
+ }
+}
+
+#[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 {}
+}
diff --git a/library/std/src/sys/sgx/os.rs b/library/std/src/sys/sgx/os.rs
new file mode 100644
index 000000000..5da0257f3
--- /dev/null
+++ b/library/std/src/sys/sgx/os.rs
@@ -0,0 +1,140 @@
+use fortanix_sgx_abi::{Error, RESULT_SUCCESS};
+
+use crate::collections::HashMap;
+use crate::error::Error as StdError;
+use crate::ffi::{OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::marker::PhantomData;
+use crate::path::{self, PathBuf};
+use crate::str;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+use crate::sync::Mutex;
+use crate::sync::Once;
+use crate::sys::{decode_error_kind, sgx_ineffective, unsupported};
+use crate::vec;
+
+pub fn errno() -> i32 {
+ RESULT_SUCCESS
+}
+
+pub fn error_string(errno: i32) -> String {
+ if errno == RESULT_SUCCESS {
+ "operation successful".into()
+ } else if ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&errno) {
+ format!("user-specified error {errno:08x}")
+ } else {
+ decode_error_kind(errno).as_str().into()
+ }
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+ sgx_ineffective(())
+}
+
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
+ panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+ type Item = PathBuf;
+ fn next(&mut self) -> Option<PathBuf> {
+ 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 in SGX yet".fmt(f)
+ }
+}
+
+impl StdError for JoinPathsError {
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "not supported in SGX yet"
+ }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+#[cfg_attr(test, linkage = "available_externally")]
+#[export_name = "_ZN16__rust_internals3std3sys3sgx2os3ENVE"]
+static ENV: AtomicUsize = AtomicUsize::new(0);
+#[cfg_attr(test, linkage = "available_externally")]
+#[export_name = "_ZN16__rust_internals3std3sys3sgx2os8ENV_INITE"]
+static ENV_INIT: Once = Once::new();
+type EnvStore = Mutex<HashMap<OsString, OsString>>;
+
+fn get_env_store() -> Option<&'static EnvStore> {
+ unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() }
+}
+
+fn create_env_store() -> &'static EnvStore {
+ ENV_INIT.call_once(|| {
+ ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed)
+ });
+ unsafe { &*(ENV.load(Ordering::Relaxed) as *const EnvStore) }
+}
+
+pub type Env = vec::IntoIter<(OsString, OsString)>;
+
+pub fn env() -> Env {
+ let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> {
+ map.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
+ };
+
+ get_env_store().map(|env| clone_to_vec(&env.lock().unwrap())).unwrap_or_default().into_iter()
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+ get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned())
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+ let (k, v) = (k.to_owned(), v.to_owned());
+ create_env_store().lock().unwrap().insert(k, v);
+ Ok(())
+}
+
+pub fn unsetenv(k: &OsStr) -> io::Result<()> {
+ if let Some(env) = get_env_store() {
+ env.lock().unwrap().remove(k);
+ }
+ Ok(())
+}
+
+pub fn temp_dir() -> PathBuf {
+ panic!("no filesystem in SGX")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ None
+}
+
+pub fn exit(code: i32) -> ! {
+ super::abi::exit_with_code(code as _)
+}
+
+pub fn getpid() -> u32 {
+ panic!("no pids in SGX")
+}
diff --git a/library/std/src/sys/sgx/path.rs b/library/std/src/sys/sgx/path.rs
new file mode 100644
index 000000000..c805c15e7
--- /dev/null
+++ b/library/std/src/sys/sgx/path.rs
@@ -0,0 +1,25 @@
+use crate::ffi::OsStr;
+use crate::io;
+use crate::path::{Path, PathBuf, Prefix};
+use crate::sys::unsupported;
+
+#[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: &str = "/";
+pub const MAIN_SEP: char = '/';
+
+pub(crate) fn absolute(_path: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
diff --git a/library/std/src/sys/sgx/rwlock.rs b/library/std/src/sys/sgx/rwlock.rs
new file mode 100644
index 000000000..a97fb9ab0
--- /dev/null
+++ b/library/std/src/sys/sgx/rwlock.rs
@@ -0,0 +1,212 @@
+#[cfg(test)]
+mod tests;
+
+use crate::num::NonZeroUsize;
+use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+
+use super::waitqueue::{
+ try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable,
+};
+use crate::mem;
+
+pub struct RwLock {
+ readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>,
+ writer: SpinMutex<WaitVariable<bool>>,
+}
+
+pub(crate) type MovableRwLock = LazyBox<RwLock>;
+
+impl LazyInit for RwLock {
+ fn init() -> Box<Self> {
+ Box::new(Self::new())
+ }
+}
+
+// Check at compile time that RwLock size matches C definition (see test_c_rwlock_initializer below)
+//
+// # Safety
+// Never called, as it is a compile time check.
+#[allow(dead_code)]
+unsafe fn rw_lock_size_assert(r: RwLock) {
+ unsafe { mem::transmute::<RwLock, [u8; 144]>(r) };
+}
+
+impl RwLock {
+ pub const fn new() -> RwLock {
+ RwLock {
+ readers: SpinMutex::new(WaitVariable::new(None)),
+ writer: SpinMutex::new(WaitVariable::new(false)),
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read(&self) {
+ let mut rguard = self.readers.lock();
+ let wguard = self.writer.lock();
+ if *wguard.lock_var() || !wguard.queue_empty() {
+ // Another thread has or is waiting for the write lock, wait
+ drop(wguard);
+ WaitQueue::wait(rguard, || {});
+ // Another thread has passed the lock to us
+ } else {
+ // No waiting writers, acquire the read lock
+ *rguard.lock_var_mut() =
+ NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
+ }
+ }
+
+ #[inline]
+ pub unsafe fn try_read(&self) -> bool {
+ let mut rguard = try_lock_or_false!(self.readers);
+ let wguard = try_lock_or_false!(self.writer);
+ if *wguard.lock_var() || !wguard.queue_empty() {
+ // Another thread has or is waiting for the write lock
+ false
+ } else {
+ // No waiting writers, acquire the read lock
+ *rguard.lock_var_mut() =
+ NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
+ true
+ }
+ }
+
+ #[inline]
+ pub unsafe fn write(&self) {
+ let rguard = self.readers.lock();
+ let mut wguard = self.writer.lock();
+ if *wguard.lock_var() || rguard.lock_var().is_some() {
+ // Another thread has the lock, wait
+ drop(rguard);
+ WaitQueue::wait(wguard, || {});
+ // Another thread has passed the lock to us
+ } else {
+ // We are just now obtaining the lock
+ *wguard.lock_var_mut() = true;
+ }
+ }
+
+ #[inline]
+ pub unsafe fn try_write(&self) -> bool {
+ let rguard = try_lock_or_false!(self.readers);
+ let mut wguard = try_lock_or_false!(self.writer);
+ if *wguard.lock_var() || rguard.lock_var().is_some() {
+ // Another thread has the lock
+ false
+ } else {
+ // We are just now obtaining the lock
+ *wguard.lock_var_mut() = true;
+ true
+ }
+ }
+
+ #[inline]
+ unsafe fn __read_unlock(
+ &self,
+ mut rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>,
+ wguard: SpinMutexGuard<'_, WaitVariable<bool>>,
+ ) {
+ *rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1);
+ if rguard.lock_var().is_some() {
+ // There are other active readers
+ } else {
+ if let Ok(mut wguard) = WaitQueue::notify_one(wguard) {
+ // A writer was waiting, pass the lock
+ *wguard.lock_var_mut() = true;
+ wguard.drop_after(rguard);
+ } else {
+ // No writers were waiting, the lock is released
+ rtassert!(rguard.queue_empty());
+ }
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read_unlock(&self) {
+ let rguard = self.readers.lock();
+ let wguard = self.writer.lock();
+ unsafe { self.__read_unlock(rguard, wguard) };
+ }
+
+ #[inline]
+ unsafe fn __write_unlock(
+ &self,
+ rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>,
+ wguard: SpinMutexGuard<'_, WaitVariable<bool>>,
+ ) {
+ match WaitQueue::notify_one(wguard) {
+ Err(mut wguard) => {
+ // No writers waiting, release the write lock
+ *wguard.lock_var_mut() = false;
+ if let Ok(mut rguard) = WaitQueue::notify_all(rguard) {
+ // One or more readers were waiting, pass the lock to them
+ if let NotifiedTcs::All { count } = rguard.notified_tcs() {
+ *rguard.lock_var_mut() = Some(count)
+ } else {
+ unreachable!() // called notify_all
+ }
+ rguard.drop_after(wguard);
+ } else {
+ // No readers waiting, the lock is released
+ }
+ }
+ Ok(wguard) => {
+ // There was a thread waiting for write, just pass the lock
+ wguard.drop_after(rguard);
+ }
+ }
+ }
+
+ #[inline]
+ pub unsafe fn write_unlock(&self) {
+ let rguard = self.readers.lock();
+ let wguard = self.writer.lock();
+ unsafe { self.__write_unlock(rguard, wguard) };
+ }
+
+ // only used by __rust_rwlock_unlock below
+ #[inline]
+ #[cfg_attr(test, allow(dead_code))]
+ unsafe fn unlock(&self) {
+ let rguard = self.readers.lock();
+ let wguard = self.writer.lock();
+ if *wguard.lock_var() == true {
+ unsafe { self.__write_unlock(rguard, wguard) };
+ } else {
+ unsafe { self.__read_unlock(rguard, wguard) };
+ }
+ }
+}
+
+// The following functions are needed by libunwind. These symbols are named
+// in pre-link args for the target specification, so keep that in sync.
+#[cfg(not(test))]
+const EINVAL: i32 = 22;
+
+#[cfg(not(test))]
+#[no_mangle]
+pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RwLock) -> i32 {
+ if p.is_null() {
+ return EINVAL;
+ }
+ unsafe { (*p).read() };
+ return 0;
+}
+
+#[cfg(not(test))]
+#[no_mangle]
+pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RwLock) -> i32 {
+ if p.is_null() {
+ return EINVAL;
+ }
+ unsafe { (*p).write() };
+ return 0;
+}
+#[cfg(not(test))]
+#[no_mangle]
+pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RwLock) -> i32 {
+ if p.is_null() {
+ return EINVAL;
+ }
+ unsafe { (*p).unlock() };
+ return 0;
+}
diff --git a/library/std/src/sys/sgx/rwlock/tests.rs b/library/std/src/sys/sgx/rwlock/tests.rs
new file mode 100644
index 000000000..479996115
--- /dev/null
+++ b/library/std/src/sys/sgx/rwlock/tests.rs
@@ -0,0 +1,31 @@
+use super::*;
+
+// Verify that the byte pattern libunwind uses to initialize an RwLock is
+// equivalent to the value of RwLock::new(). If the value changes,
+// `src/UnwindRustSgx.h` in libunwind needs to be changed too.
+#[test]
+fn test_c_rwlock_initializer() {
+ #[rustfmt::skip]
+ const C_RWLOCK_INIT: &[u8] = &[
+ /* 0x00 */ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x30 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x40 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x60 */ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ];
+
+ // For the test to work, we need the padding/unused bytes in RwLock to be
+ // initialized as 0. In practice, this is the case with statics.
+ static RUST_RWLOCK_INIT: RwLock = RwLock::new();
+
+ unsafe {
+ // If the assertion fails, that not necessarily an issue with the value
+ // of C_RWLOCK_INIT. It might just be an issue with the way padding
+ // bytes are initialized in the test code.
+ assert_eq!(&crate::mem::transmute_copy::<_, [u8; 144]>(&RUST_RWLOCK_INIT), C_RWLOCK_INIT);
+ };
+}
diff --git a/library/std/src/sys/sgx/stdio.rs b/library/std/src/sys/sgx/stdio.rs
new file mode 100644
index 000000000..2e680e740
--- /dev/null
+++ b/library/std/src/sys/sgx/stdio.rs
@@ -0,0 +1,88 @@
+use fortanix_sgx_abi as abi;
+
+use crate::io;
+#[cfg(not(test))]
+use crate::slice;
+#[cfg(not(test))]
+use crate::str;
+use crate::sys::fd::FileDesc;
+
+pub struct Stdin(());
+pub struct Stdout(());
+pub struct Stderr(());
+
+fn with_std_fd<F: FnOnce(&FileDesc) -> R, R>(fd: abi::Fd, f: F) -> R {
+ let fd = FileDesc::new(fd);
+ let ret = f(&fd);
+ fd.into_raw();
+ ret
+}
+
+impl Stdin {
+ pub const fn new() -> Stdin {
+ Stdin(())
+ }
+}
+
+impl io::Read for Stdin {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ with_std_fd(abi::FD_STDIN, |fd| fd.read(buf))
+ }
+}
+
+impl Stdout {
+ pub const fn new() -> Stdout {
+ Stdout(())
+ }
+}
+
+impl io::Write for Stdout {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ with_std_fd(abi::FD_STDOUT, |fd| fd.write(buf))
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ with_std_fd(abi::FD_STDOUT, |fd| fd.flush())
+ }
+}
+
+impl Stderr {
+ pub const fn new() -> Stderr {
+ Stderr(())
+ }
+}
+
+impl io::Write for Stderr {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ with_std_fd(abi::FD_STDERR, |fd| fd.write(buf))
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ with_std_fd(abi::FD_STDERR, |fd| fd.flush())
+ }
+}
+
+pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
+
+pub fn is_ebadf(err: &io::Error) -> bool {
+ // FIXME: Rust normally maps Unix EBADF to `Uncategorized`
+ err.raw_os_error() == Some(abi::Error::BrokenPipe as _)
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+ super::abi::panic::SgxPanicOutput::new()
+}
+
+// This function is needed by libunwind. The symbol is named in pre-link args
+// for the target specification, so keep that in sync.
+#[cfg(not(test))]
+#[no_mangle]
+pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) {
+ if s < 0 {
+ return;
+ }
+ let buf = unsafe { slice::from_raw_parts(m as *const u8, s as _) };
+ if let Ok(s) = str::from_utf8(&buf[..buf.iter().position(|&b| b == 0).unwrap_or(buf.len())]) {
+ eprint!("{s}");
+ }
+}
diff --git a/library/std/src/sys/sgx/thread.rs b/library/std/src/sys/sgx/thread.rs
new file mode 100644
index 000000000..d745a6196
--- /dev/null
+++ b/library/std/src/sys/sgx/thread.rs
@@ -0,0 +1,152 @@
+#![cfg_attr(test, allow(dead_code))] // why is this necessary?
+use super::unsupported;
+use crate::ffi::CStr;
+use crate::io;
+use crate::num::NonZeroUsize;
+use crate::time::Duration;
+
+use super::abi::usercalls;
+
+pub struct Thread(task_queue::JoinHandle);
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
+
+pub use self::task_queue::JoinNotifier;
+
+mod task_queue {
+ use super::wait_notify;
+ use crate::sync::{Mutex, MutexGuard, Once};
+
+ pub type JoinHandle = wait_notify::Waiter;
+
+ pub struct JoinNotifier(Option<wait_notify::Notifier>);
+
+ impl Drop for JoinNotifier {
+ fn drop(&mut self) {
+ self.0.take().unwrap().notify();
+ }
+ }
+
+ pub(super) struct Task {
+ p: Box<dyn FnOnce()>,
+ done: JoinNotifier,
+ }
+
+ impl Task {
+ pub(super) fn new(p: Box<dyn FnOnce()>) -> (Task, JoinHandle) {
+ let (done, recv) = wait_notify::new();
+ let done = JoinNotifier(Some(done));
+ (Task { p, done }, recv)
+ }
+
+ pub(super) fn run(self) -> JoinNotifier {
+ (self.p)();
+ self.done
+ }
+ }
+
+ #[cfg_attr(test, linkage = "available_externally")]
+ #[export_name = "_ZN16__rust_internals3std3sys3sgx6thread15TASK_QUEUE_INITE"]
+ static TASK_QUEUE_INIT: Once = Once::new();
+ #[cfg_attr(test, linkage = "available_externally")]
+ #[export_name = "_ZN16__rust_internals3std3sys3sgx6thread10TASK_QUEUEE"]
+ static mut TASK_QUEUE: Option<Mutex<Vec<Task>>> = None;
+
+ pub(super) fn lock() -> MutexGuard<'static, Vec<Task>> {
+ unsafe {
+ TASK_QUEUE_INIT.call_once(|| TASK_QUEUE = Some(Default::default()));
+ TASK_QUEUE.as_ref().unwrap().lock().unwrap()
+ }
+ }
+}
+
+/// This module provides a synchronization primitive that does not use thread
+/// local variables. This is needed for signaling that a thread has finished
+/// execution. The signal is sent once all TLS destructors have finished at
+/// which point no new thread locals should be created.
+pub mod wait_notify {
+ use super::super::waitqueue::{SpinMutex, WaitQueue, WaitVariable};
+ use crate::sync::Arc;
+
+ pub struct Notifier(Arc<SpinMutex<WaitVariable<bool>>>);
+
+ impl Notifier {
+ /// Notify the waiter. The waiter is either notified right away (if
+ /// currently blocked in `Waiter::wait()`) or later when it calls the
+ /// `Waiter::wait()` method.
+ pub fn notify(self) {
+ let mut guard = self.0.lock();
+ *guard.lock_var_mut() = true;
+ let _ = WaitQueue::notify_one(guard);
+ }
+ }
+
+ pub struct Waiter(Arc<SpinMutex<WaitVariable<bool>>>);
+
+ impl Waiter {
+ /// Wait for a notification. If `Notifier::notify()` has already been
+ /// called, this will return immediately, otherwise the current thread
+ /// is blocked until notified.
+ pub fn wait(self) {
+ let guard = self.0.lock();
+ if *guard.lock_var() {
+ return;
+ }
+ WaitQueue::wait(guard, || {});
+ }
+ }
+
+ pub fn new() -> (Notifier, Waiter) {
+ let inner = Arc::new(SpinMutex::new(WaitVariable::new(false)));
+ (Notifier(inner.clone()), Waiter(inner))
+ }
+}
+
+impl Thread {
+ // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+ pub unsafe fn new(_stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+ let mut queue_lock = task_queue::lock();
+ unsafe { usercalls::launch_thread()? };
+ let (task, handle) = task_queue::Task::new(p);
+ queue_lock.push(task);
+ Ok(Thread(handle))
+ }
+
+ pub(super) fn entry() -> JoinNotifier {
+ let mut pending_tasks = task_queue::lock();
+ let task = rtunwrap!(Some, pending_tasks.pop());
+ drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary
+ task.run()
+ }
+
+ pub fn yield_now() {
+ let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO));
+ rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock);
+ }
+
+ pub fn set_name(_name: &CStr) {
+ // FIXME: could store this pointer in TLS somewhere
+ }
+
+ pub fn sleep(dur: Duration) {
+ usercalls::wait_timeout(0, dur, || true);
+ }
+
+ pub fn join(self) {
+ self.0.wait();
+ }
+}
+
+pub fn available_parallelism() -> io::Result<NonZeroUsize> {
+ unsupported()
+}
+
+pub mod guard {
+ pub type Guard = !;
+ pub unsafe fn current() -> Option<Guard> {
+ None
+ }
+ pub unsafe fn init() -> Option<Guard> {
+ None
+ }
+}
diff --git a/library/std/src/sys/sgx/thread_local_key.rs b/library/std/src/sys/sgx/thread_local_key.rs
new file mode 100644
index 000000000..b21784475
--- /dev/null
+++ b/library/std/src/sys/sgx/thread_local_key.rs
@@ -0,0 +1,28 @@
+use super::abi::tls::{Key as AbiKey, Tls};
+
+pub type Key = usize;
+
+#[inline]
+pub unsafe fn create(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
+ Tls::create(dtor).as_usize()
+}
+
+#[inline]
+pub unsafe fn set(key: Key, value: *mut u8) {
+ Tls::set(AbiKey::from_usize(key), value)
+}
+
+#[inline]
+pub unsafe fn get(key: Key) -> *mut u8 {
+ Tls::get(AbiKey::from_usize(key))
+}
+
+#[inline]
+pub unsafe fn destroy(key: Key) {
+ Tls::destroy(AbiKey::from_usize(key))
+}
+
+#[inline]
+pub fn requires_synchronized_create() -> bool {
+ false
+}
diff --git a/library/std/src/sys/sgx/time.rs b/library/std/src/sys/sgx/time.rs
new file mode 100644
index 000000000..db4cf2804
--- /dev/null
+++ b/library/std/src/sys/sgx/time.rs
@@ -0,0 +1,46 @@
+use super::abi::usercalls;
+use crate::time::Duration;
+
+#[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(usercalls::insecure_time())
+ }
+
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ self.0.checked_sub(other.0)
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant(self.0.checked_add(*other)?))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant(self.0.checked_sub(*other)?))
+ }
+}
+
+impl SystemTime {
+ pub fn now() -> SystemTime {
+ SystemTime(usercalls::insecure_time())
+ }
+
+ 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 checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_add(*other)?))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_sub(*other)?))
+ }
+}
diff --git a/library/std/src/sys/sgx/waitqueue/mod.rs b/library/std/src/sys/sgx/waitqueue/mod.rs
new file mode 100644
index 000000000..61bb11d9a
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/mod.rs
@@ -0,0 +1,240 @@
+//! A simple queue implementation for synchronization primitives.
+//!
+//! This queue is used to implement condition variable and mutexes.
+//!
+//! Users of this API are expected to use the `WaitVariable<T>` type. Since
+//! that type is not `Sync`, it needs to be protected by e.g., a `SpinMutex` to
+//! allow shared access.
+//!
+//! Since userspace may send spurious wake-ups, the wakeup event state is
+//! recorded in the enclave. The wakeup event state is protected by a spinlock.
+//! The queue and associated wait state are stored in a `WaitVariable`.
+
+#[cfg(test)]
+mod tests;
+
+mod spin_mutex;
+mod unsafe_list;
+
+use crate::num::NonZeroUsize;
+use crate::ops::{Deref, DerefMut};
+use crate::time::Duration;
+
+use super::abi::thread;
+use super::abi::usercalls;
+use fortanix_sgx_abi::{Tcs, EV_UNPARK, WAIT_INDEFINITE};
+
+pub use self::spin_mutex::{try_lock_or_false, SpinMutex, SpinMutexGuard};
+use self::unsafe_list::{UnsafeList, UnsafeListEntry};
+
+/// An queue entry in a `WaitQueue`.
+struct WaitEntry {
+ /// TCS address of the thread that is waiting
+ tcs: Tcs,
+ /// Whether this thread has been notified to be awoken
+ wake: bool,
+}
+
+/// Data stored with a `WaitQueue` alongside it. This ensures accesses to the
+/// queue and the data are synchronized, since the type itself is not `Sync`.
+///
+/// Consumers of this API should use a synchronization primitive for shared
+/// access, such as `SpinMutex`.
+#[derive(Default)]
+pub struct WaitVariable<T> {
+ queue: WaitQueue,
+ lock: T,
+}
+
+impl<T> WaitVariable<T> {
+ pub const fn new(var: T) -> Self {
+ WaitVariable { queue: WaitQueue::new(), lock: var }
+ }
+
+ pub fn queue_empty(&self) -> bool {
+ self.queue.is_empty()
+ }
+
+ pub fn lock_var(&self) -> &T {
+ &self.lock
+ }
+
+ pub fn lock_var_mut(&mut self) -> &mut T {
+ &mut self.lock
+ }
+}
+
+#[derive(Copy, Clone)]
+pub enum NotifiedTcs {
+ Single(Tcs),
+ All { count: NonZeroUsize },
+}
+
+/// An RAII guard that will notify a set of target threads as well as unlock
+/// a mutex on drop.
+pub struct WaitGuard<'a, T: 'a> {
+ mutex_guard: Option<SpinMutexGuard<'a, WaitVariable<T>>>,
+ notified_tcs: NotifiedTcs,
+}
+
+/// A queue of threads that are waiting on some synchronization primitive.
+///
+/// `UnsafeList` entries are allocated on the waiting thread's stack. This
+/// avoids any global locking that might happen in the heap allocator. This is
+/// safe because the waiting thread will not return from that stack frame until
+/// after it is notified. The notifying thread ensures to clean up any
+/// references to the list entries before sending the wakeup event.
+pub struct WaitQueue {
+ // We use an inner Mutex here to protect the data in the face of spurious
+ // wakeups.
+ inner: UnsafeList<SpinMutex<WaitEntry>>,
+}
+unsafe impl Send for WaitQueue {}
+
+impl Default for WaitQueue {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<'a, T> WaitGuard<'a, T> {
+ /// Returns which TCSes will be notified when this guard drops.
+ pub fn notified_tcs(&self) -> NotifiedTcs {
+ self.notified_tcs
+ }
+
+ /// Drop this `WaitGuard`, after dropping another `guard`.
+ pub fn drop_after<U>(self, guard: U) {
+ drop(guard);
+ drop(self);
+ }
+}
+
+impl<'a, T> Deref for WaitGuard<'a, T> {
+ type Target = SpinMutexGuard<'a, WaitVariable<T>>;
+
+ fn deref(&self) -> &Self::Target {
+ self.mutex_guard.as_ref().unwrap()
+ }
+}
+
+impl<'a, T> DerefMut for WaitGuard<'a, T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.mutex_guard.as_mut().unwrap()
+ }
+}
+
+impl<'a, T> Drop for WaitGuard<'a, T> {
+ fn drop(&mut self) {
+ drop(self.mutex_guard.take());
+ let target_tcs = match self.notified_tcs {
+ NotifiedTcs::Single(tcs) => Some(tcs),
+ NotifiedTcs::All { .. } => None,
+ };
+ rtunwrap!(Ok, usercalls::send(EV_UNPARK, target_tcs));
+ }
+}
+
+impl WaitQueue {
+ pub const fn new() -> Self {
+ WaitQueue { inner: UnsafeList::new() }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+
+ /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait
+ /// until a wakeup event.
+ ///
+ /// This function does not return until this thread has been awoken.
+ pub fn wait<T, F: FnOnce()>(mut guard: SpinMutexGuard<'_, WaitVariable<T>>, before_wait: F) {
+ // very unsafe: check requirements of UnsafeList::push
+ unsafe {
+ let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry {
+ tcs: thread::current(),
+ wake: false,
+ }));
+ let entry = guard.queue.inner.push(&mut entry);
+ drop(guard);
+ before_wait();
+ while !entry.lock().wake {
+ // don't panic, this would invalidate `entry` during unwinding
+ let eventset = rtunwrap!(Ok, usercalls::wait(EV_UNPARK, WAIT_INDEFINITE));
+ rtassert!(eventset & EV_UNPARK == EV_UNPARK);
+ }
+ }
+ }
+
+ /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait
+ /// until a wakeup event or timeout. If event was observed, returns true.
+ /// If not, it will remove the calling thread from the wait queue.
+ pub fn wait_timeout<T, F: FnOnce()>(
+ lock: &SpinMutex<WaitVariable<T>>,
+ timeout: Duration,
+ before_wait: F,
+ ) -> bool {
+ // very unsafe: check requirements of UnsafeList::push
+ unsafe {
+ let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry {
+ tcs: thread::current(),
+ wake: false,
+ }));
+ let entry_lock = lock.lock().queue.inner.push(&mut entry);
+ before_wait();
+ usercalls::wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake);
+ // acquire the wait queue's lock first to avoid deadlock.
+ let mut guard = lock.lock();
+ let success = entry_lock.lock().wake;
+ if !success {
+ // nobody is waking us up, so remove our entry from the wait queue.
+ guard.queue.inner.remove(&mut entry);
+ }
+ success
+ }
+ }
+
+ /// Either find the next waiter on the wait queue, or return the mutex
+ /// guard unchanged.
+ ///
+ /// If a waiter is found, a `WaitGuard` is returned which will notify the
+ /// waiter when it is dropped.
+ pub fn notify_one<T>(
+ mut guard: SpinMutexGuard<'_, WaitVariable<T>>,
+ ) -> Result<WaitGuard<'_, T>, SpinMutexGuard<'_, WaitVariable<T>>> {
+ unsafe {
+ if let Some(entry) = guard.queue.inner.pop() {
+ let mut entry_guard = entry.lock();
+ let tcs = entry_guard.tcs;
+ entry_guard.wake = true;
+ drop(entry);
+ Ok(WaitGuard { mutex_guard: Some(guard), notified_tcs: NotifiedTcs::Single(tcs) })
+ } else {
+ Err(guard)
+ }
+ }
+ }
+
+ /// Either find any and all waiters on the wait queue, or return the mutex
+ /// guard unchanged.
+ ///
+ /// If at least one waiter is found, a `WaitGuard` is returned which will
+ /// notify all waiters when it is dropped.
+ pub fn notify_all<T>(
+ mut guard: SpinMutexGuard<'_, WaitVariable<T>>,
+ ) -> Result<WaitGuard<'_, T>, SpinMutexGuard<'_, WaitVariable<T>>> {
+ unsafe {
+ let mut count = 0;
+ while let Some(entry) = guard.queue.inner.pop() {
+ count += 1;
+ let mut entry_guard = entry.lock();
+ entry_guard.wake = true;
+ }
+ if let Some(count) = NonZeroUsize::new(count) {
+ Ok(WaitGuard { mutex_guard: Some(guard), notified_tcs: NotifiedTcs::All { count } })
+ } else {
+ Err(guard)
+ }
+ }
+ }
+}
diff --git a/library/std/src/sys/sgx/waitqueue/spin_mutex.rs b/library/std/src/sys/sgx/waitqueue/spin_mutex.rs
new file mode 100644
index 000000000..f6e851cca
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/spin_mutex.rs
@@ -0,0 +1,80 @@
+//! Trivial spinlock-based implementation of `sync::Mutex`.
+// FIXME: Perhaps use Intel TSX to avoid locking?
+
+#[cfg(test)]
+mod tests;
+
+use crate::cell::UnsafeCell;
+use crate::hint;
+use crate::ops::{Deref, DerefMut};
+use crate::sync::atomic::{AtomicBool, Ordering};
+
+#[derive(Default)]
+pub struct SpinMutex<T> {
+ value: UnsafeCell<T>,
+ lock: AtomicBool,
+}
+
+unsafe impl<T: Send> Send for SpinMutex<T> {}
+unsafe impl<T: Send> Sync for SpinMutex<T> {}
+
+pub struct SpinMutexGuard<'a, T: 'a> {
+ mutex: &'a SpinMutex<T>,
+}
+
+impl<'a, T> !Send for SpinMutexGuard<'a, T> {}
+unsafe impl<'a, T: Sync> Sync for SpinMutexGuard<'a, T> {}
+
+impl<T> SpinMutex<T> {
+ pub const fn new(value: T) -> Self {
+ SpinMutex { value: UnsafeCell::new(value), lock: AtomicBool::new(false) }
+ }
+
+ #[inline(always)]
+ pub fn lock(&self) -> SpinMutexGuard<'_, T> {
+ loop {
+ match self.try_lock() {
+ None => {
+ while self.lock.load(Ordering::Relaxed) {
+ hint::spin_loop()
+ }
+ }
+ Some(guard) => return guard,
+ }
+ }
+ }
+
+ #[inline(always)]
+ pub fn try_lock(&self) -> Option<SpinMutexGuard<'_, T>> {
+ if self.lock.compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire).is_ok() {
+ Some(SpinMutexGuard { mutex: self })
+ } else {
+ None
+ }
+ }
+}
+
+/// Lock the Mutex or return false.
+pub macro try_lock_or_false($e:expr) {
+ if let Some(v) = $e.try_lock() { v } else { return false }
+}
+
+impl<'a, T> Deref for SpinMutexGuard<'a, T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ unsafe { &*self.mutex.value.get() }
+ }
+}
+
+impl<'a, T> DerefMut for SpinMutexGuard<'a, T> {
+ fn deref_mut(&mut self) -> &mut T {
+ unsafe { &mut *self.mutex.value.get() }
+ }
+}
+
+impl<'a, T> Drop for SpinMutexGuard<'a, T> {
+ fn drop(&mut self) {
+ self.mutex.lock.store(false, Ordering::Release)
+ }
+}
diff --git a/library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs b/library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs
new file mode 100644
index 000000000..4c5994bea
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs
@@ -0,0 +1,23 @@
+#![allow(deprecated)]
+
+use super::*;
+use crate::sync::Arc;
+use crate::thread;
+use crate::time::Duration;
+
+#[test]
+fn sleep() {
+ let mutex = Arc::new(SpinMutex::<i32>::default());
+ let mutex2 = mutex.clone();
+ let guard = mutex.lock();
+ let t1 = thread::spawn(move || {
+ *mutex2.lock() = 1;
+ });
+
+ thread::sleep(Duration::from_millis(50));
+
+ assert_eq!(*guard, 0);
+ drop(guard);
+ t1.join().unwrap();
+ assert_eq!(*mutex.lock(), 1);
+}
diff --git a/library/std/src/sys/sgx/waitqueue/tests.rs b/library/std/src/sys/sgx/waitqueue/tests.rs
new file mode 100644
index 000000000..bf91fdd08
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/tests.rs
@@ -0,0 +1,20 @@
+use super::*;
+use crate::sync::Arc;
+use crate::thread;
+
+#[test]
+fn queue() {
+ let wq = Arc::new(SpinMutex::<WaitVariable<()>>::default());
+ let wq2 = wq.clone();
+
+ let locked = wq.lock();
+
+ let t1 = thread::spawn(move || {
+ // if we obtain the lock, the main thread should be waiting
+ assert!(WaitQueue::notify_one(wq2.lock()).is_ok());
+ });
+
+ WaitQueue::wait(locked, || {});
+
+ t1.join().unwrap();
+}
diff --git a/library/std/src/sys/sgx/waitqueue/unsafe_list.rs b/library/std/src/sys/sgx/waitqueue/unsafe_list.rs
new file mode 100644
index 000000000..c736cab57
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/unsafe_list.rs
@@ -0,0 +1,156 @@
+//! A doubly-linked list where callers are in charge of memory allocation
+//! of the nodes in the list.
+
+#[cfg(test)]
+mod tests;
+
+use crate::mem;
+use crate::ptr::NonNull;
+
+pub struct UnsafeListEntry<T> {
+ next: NonNull<UnsafeListEntry<T>>,
+ prev: NonNull<UnsafeListEntry<T>>,
+ value: Option<T>,
+}
+
+impl<T> UnsafeListEntry<T> {
+ fn dummy() -> Self {
+ UnsafeListEntry { next: NonNull::dangling(), prev: NonNull::dangling(), value: None }
+ }
+
+ pub fn new(value: T) -> Self {
+ UnsafeListEntry { value: Some(value), ..Self::dummy() }
+ }
+}
+
+// WARNING: self-referential struct!
+pub struct UnsafeList<T> {
+ head_tail: NonNull<UnsafeListEntry<T>>,
+ head_tail_entry: Option<UnsafeListEntry<T>>,
+}
+
+impl<T> UnsafeList<T> {
+ pub const fn new() -> Self {
+ unsafe { UnsafeList { head_tail: NonNull::new_unchecked(1 as _), head_tail_entry: None } }
+ }
+
+ /// # Safety
+ unsafe fn init(&mut self) {
+ if self.head_tail_entry.is_none() {
+ self.head_tail_entry = Some(UnsafeListEntry::dummy());
+ // SAFETY: `head_tail_entry` must be non-null, which it is because we assign it above.
+ self.head_tail =
+ unsafe { NonNull::new_unchecked(self.head_tail_entry.as_mut().unwrap()) };
+ // SAFETY: `self.head_tail` must meet all requirements for a mutable reference.
+ unsafe { self.head_tail.as_mut() }.next = self.head_tail;
+ unsafe { self.head_tail.as_mut() }.prev = self.head_tail;
+ }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ if self.head_tail_entry.is_some() {
+ let first = unsafe { self.head_tail.as_ref() }.next;
+ if first == self.head_tail {
+ // ,-------> /---------\ next ---,
+ // | |head_tail| |
+ // `--- prev \---------/ <-------`
+ // SAFETY: `self.head_tail` must meet all requirements for a reference.
+ unsafe { rtassert!(self.head_tail.as_ref().prev == first) };
+ true
+ } else {
+ false
+ }
+ } else {
+ true
+ }
+ }
+
+ /// Pushes an entry onto the back of the list.
+ ///
+ /// # Safety
+ ///
+ /// The entry must remain allocated until the entry is removed from the
+ /// list AND the caller who popped is done using the entry. Special
+ /// care must be taken in the caller of `push` to ensure unwinding does
+ /// not destroy the stack frame containing the entry.
+ pub unsafe fn push<'a>(&mut self, entry: &'a mut UnsafeListEntry<T>) -> &'a T {
+ unsafe { self.init() };
+
+ // BEFORE:
+ // /---------\ next ---> /---------\
+ // ... |prev_tail| |head_tail| ...
+ // \---------/ <--- prev \---------/
+ //
+ // AFTER:
+ // /---------\ next ---> /-----\ next ---> /---------\
+ // ... |prev_tail| |entry| |head_tail| ...
+ // \---------/ <--- prev \-----/ <--- prev \---------/
+ let mut entry = unsafe { NonNull::new_unchecked(entry) };
+ let mut prev_tail = mem::replace(&mut unsafe { self.head_tail.as_mut() }.prev, entry);
+ // SAFETY: `entry` must meet all requirements for a mutable reference.
+ unsafe { entry.as_mut() }.prev = prev_tail;
+ unsafe { entry.as_mut() }.next = self.head_tail;
+ // SAFETY: `prev_tail` must meet all requirements for a mutable reference.
+ unsafe { prev_tail.as_mut() }.next = entry;
+ // unwrap ok: always `Some` on non-dummy entries
+ unsafe { (*entry.as_ptr()).value.as_ref() }.unwrap()
+ }
+
+ /// Pops an entry from the front of the list.
+ ///
+ /// # Safety
+ ///
+ /// The caller must make sure to synchronize ending the borrow of the
+ /// return value and deallocation of the containing entry.
+ pub unsafe fn pop<'a>(&mut self) -> Option<&'a T> {
+ unsafe { self.init() };
+
+ if self.is_empty() {
+ None
+ } else {
+ // BEFORE:
+ // /---------\ next ---> /-----\ next ---> /------\
+ // ... |head_tail| |first| |second| ...
+ // \---------/ <--- prev \-----/ <--- prev \------/
+ //
+ // AFTER:
+ // /---------\ next ---> /------\
+ // ... |head_tail| |second| ...
+ // \---------/ <--- prev \------/
+ let mut first = unsafe { self.head_tail.as_mut() }.next;
+ let mut second = unsafe { first.as_mut() }.next;
+ unsafe { self.head_tail.as_mut() }.next = second;
+ unsafe { second.as_mut() }.prev = self.head_tail;
+ unsafe { first.as_mut() }.next = NonNull::dangling();
+ unsafe { first.as_mut() }.prev = NonNull::dangling();
+ // unwrap ok: always `Some` on non-dummy entries
+ Some(unsafe { (*first.as_ptr()).value.as_ref() }.unwrap())
+ }
+ }
+
+ /// Removes an entry from the list.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that `entry` has been pushed onto `self`
+ /// prior to this call and has not moved since then.
+ pub unsafe fn remove(&mut self, entry: &mut UnsafeListEntry<T>) {
+ rtassert!(!self.is_empty());
+ // BEFORE:
+ // /----\ next ---> /-----\ next ---> /----\
+ // ... |prev| |entry| |next| ...
+ // \----/ <--- prev \-----/ <--- prev \----/
+ //
+ // AFTER:
+ // /----\ next ---> /----\
+ // ... |prev| |next| ...
+ // \----/ <--- prev \----/
+ let mut prev = entry.prev;
+ let mut next = entry.next;
+ // SAFETY: `prev` and `next` must meet all requirements for a mutable reference.entry
+ unsafe { prev.as_mut() }.next = next;
+ unsafe { next.as_mut() }.prev = prev;
+ entry.next = NonNull::dangling();
+ entry.prev = NonNull::dangling();
+ }
+}
diff --git a/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs b/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs
new file mode 100644
index 000000000..c653dee17
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs
@@ -0,0 +1,105 @@
+use super::*;
+use crate::cell::Cell;
+
+/// # Safety
+/// List must be valid.
+unsafe fn assert_empty<T>(list: &mut UnsafeList<T>) {
+ assert!(unsafe { list.pop() }.is_none(), "assertion failed: list is not empty");
+}
+
+#[test]
+fn init_empty() {
+ unsafe {
+ assert_empty(&mut UnsafeList::<i32>::new());
+ }
+}
+
+#[test]
+fn push_pop() {
+ unsafe {
+ let mut node = UnsafeListEntry::new(1234);
+ let mut list = UnsafeList::new();
+ assert_eq!(list.push(&mut node), &1234);
+ assert_eq!(list.pop().unwrap(), &1234);
+ assert_empty(&mut list);
+ }
+}
+
+#[test]
+fn push_remove() {
+ unsafe {
+ let mut node = UnsafeListEntry::new(1234);
+ let mut list = UnsafeList::new();
+ assert_eq!(list.push(&mut node), &1234);
+ list.remove(&mut node);
+ assert_empty(&mut list);
+ }
+}
+
+#[test]
+fn push_remove_pop() {
+ unsafe {
+ let mut node1 = UnsafeListEntry::new(11);
+ let mut node2 = UnsafeListEntry::new(12);
+ let mut node3 = UnsafeListEntry::new(13);
+ let mut node4 = UnsafeListEntry::new(14);
+ let mut node5 = UnsafeListEntry::new(15);
+ let mut list = UnsafeList::new();
+ assert_eq!(list.push(&mut node1), &11);
+ assert_eq!(list.push(&mut node2), &12);
+ assert_eq!(list.push(&mut node3), &13);
+ assert_eq!(list.push(&mut node4), &14);
+ assert_eq!(list.push(&mut node5), &15);
+
+ list.remove(&mut node1);
+ assert_eq!(list.pop().unwrap(), &12);
+ list.remove(&mut node3);
+ assert_eq!(list.pop().unwrap(), &14);
+ list.remove(&mut node5);
+ assert_empty(&mut list);
+
+ assert_eq!(list.push(&mut node1), &11);
+ assert_eq!(list.pop().unwrap(), &11);
+ assert_empty(&mut list);
+
+ assert_eq!(list.push(&mut node3), &13);
+ assert_eq!(list.push(&mut node4), &14);
+ list.remove(&mut node3);
+ list.remove(&mut node4);
+ assert_empty(&mut list);
+ }
+}
+
+#[test]
+fn complex_pushes_pops() {
+ unsafe {
+ let mut node1 = UnsafeListEntry::new(1234);
+ let mut node2 = UnsafeListEntry::new(4567);
+ let mut node3 = UnsafeListEntry::new(9999);
+ let mut node4 = UnsafeListEntry::new(8642);
+ let mut list = UnsafeList::new();
+ list.push(&mut node1);
+ list.push(&mut node2);
+ assert_eq!(list.pop().unwrap(), &1234);
+ list.push(&mut node3);
+ assert_eq!(list.pop().unwrap(), &4567);
+ assert_eq!(list.pop().unwrap(), &9999);
+ assert_empty(&mut list);
+ list.push(&mut node4);
+ assert_eq!(list.pop().unwrap(), &8642);
+ assert_empty(&mut list);
+ }
+}
+
+#[test]
+fn cell() {
+ unsafe {
+ let mut node = UnsafeListEntry::new(Cell::new(0));
+ let mut list = UnsafeList::new();
+ let noderef = list.push(&mut node);
+ assert_eq!(noderef.get(), 0);
+ list.pop().unwrap().set(1);
+ assert_empty(&mut list);
+ assert_eq!(noderef.get(), 1);
+ }
+}
diff --git a/library/std/src/sys/solid/abi/fs.rs b/library/std/src/sys/solid/abi/fs.rs
new file mode 100644
index 000000000..32800bd9a
--- /dev/null
+++ b/library/std/src/sys/solid/abi/fs.rs
@@ -0,0 +1,53 @@
+//! `solid_fs.h`
+use crate::os::raw::{c_char, c_int, c_uchar};
+pub use libc::{
+ blksize_t, dev_t, ino_t, off_t, stat, time_t, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR,
+ O_TRUNC, O_WRONLY, SEEK_CUR, SEEK_END, SEEK_SET, S_IEXEC, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO,
+ S_IFMT, S_IFREG, S_IREAD, S_IWRITE,
+};
+
+pub const O_ACCMODE: c_int = 0x3;
+
+pub const SOLID_MAX_PATH: usize = 256;
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct dirent {
+ pub d_ino: ino_t,
+ pub d_type: c_uchar,
+ pub d_name: [c_char; 256usize],
+}
+
+pub const DT_UNKNOWN: c_uchar = 0;
+pub const DT_FIFO: c_uchar = 1;
+pub const DT_CHR: c_uchar = 2;
+pub const DT_DIR: c_uchar = 4;
+pub const DT_BLK: c_uchar = 6;
+pub const DT_REG: c_uchar = 8;
+pub const DT_LNK: c_uchar = 10;
+pub const DT_SOCK: c_uchar = 12;
+pub const DT_WHT: c_uchar = 14;
+
+pub type S_DIR = c_int;
+
+extern "C" {
+ pub fn SOLID_FS_Open(fd: *mut c_int, path: *const c_char, mode: c_int) -> c_int;
+ pub fn SOLID_FS_Close(fd: c_int) -> c_int;
+ pub fn SOLID_FS_Read(fd: c_int, buf: *mut u8, size: usize, result: *mut usize) -> c_int;
+ pub fn SOLID_FS_Write(fd: c_int, buf: *const u8, size: usize, result: *mut usize) -> c_int;
+ pub fn SOLID_FS_Lseek(fd: c_int, offset: off_t, whence: c_int) -> c_int;
+ pub fn SOLID_FS_Sync(fd: c_int) -> c_int;
+ pub fn SOLID_FS_Ftell(fd: c_int, result: *mut off_t) -> c_int;
+ pub fn SOLID_FS_Feof(fd: c_int, result: *mut c_int) -> c_int;
+ pub fn SOLID_FS_Fsize(fd: c_int, result: *mut usize) -> c_int;
+ pub fn SOLID_FS_Truncate(path: *const c_char, size: off_t) -> c_int;
+ pub fn SOLID_FS_OpenDir(path: *const c_char, pDir: *mut S_DIR) -> c_int;
+ pub fn SOLID_FS_CloseDir(dir: S_DIR) -> c_int;
+ pub fn SOLID_FS_ReadDir(dir: S_DIR, dirp: *mut dirent) -> c_int;
+ pub fn SOLID_FS_Stat(path: *const c_char, buf: *mut stat) -> c_int;
+ pub fn SOLID_FS_Unlink(path: *const c_char) -> c_int;
+ pub fn SOLID_FS_Rename(oldpath: *const c_char, newpath: *const c_char) -> c_int;
+ pub fn SOLID_FS_Chmod(path: *const c_char, mode: c_int) -> c_int;
+ pub fn SOLID_FS_Utime(path: *const c_char, time: time_t) -> c_int;
+ pub fn SOLID_FS_Mkdir(path: *const c_char) -> c_int;
+}
diff --git a/library/std/src/sys/solid/abi/mod.rs b/library/std/src/sys/solid/abi/mod.rs
new file mode 100644
index 000000000..8440d572c
--- /dev/null
+++ b/library/std/src/sys/solid/abi/mod.rs
@@ -0,0 +1,65 @@
+use crate::os::raw::c_int;
+
+mod fs;
+pub mod sockets;
+pub use self::fs::*;
+
+// `solid_types.h`
+pub use super::itron::abi::{ER, ER_ID, E_TMOUT, ID};
+
+pub const SOLID_ERR_NOTFOUND: ER = -1000;
+pub const SOLID_ERR_NOTSUPPORTED: ER = -1001;
+pub const SOLID_ERR_EBADF: ER = -1002;
+pub const SOLID_ERR_INVALIDCONTENT: ER = -1003;
+pub const SOLID_ERR_NOTUSED: ER = -1004;
+pub const SOLID_ERR_ALREADYUSED: ER = -1005;
+pub const SOLID_ERR_OUTOFBOUND: ER = -1006;
+pub const SOLID_ERR_BADSEQUENCE: ER = -1007;
+pub const SOLID_ERR_UNKNOWNDEVICE: ER = -1008;
+pub const SOLID_ERR_BUSY: ER = -1009;
+pub const SOLID_ERR_TIMEOUT: ER = -1010;
+pub const SOLID_ERR_INVALIDACCESS: ER = -1011;
+pub const SOLID_ERR_NOTREADY: ER = -1012;
+
+// `solid_rtc.h`
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct SOLID_RTC_TIME {
+ pub tm_sec: c_int,
+ pub tm_min: c_int,
+ pub tm_hour: c_int,
+ pub tm_mday: c_int,
+ pub tm_mon: c_int,
+ pub tm_year: c_int,
+ pub tm_wday: c_int,
+}
+
+extern "C" {
+ pub fn SOLID_RTC_ReadTime(time: *mut SOLID_RTC_TIME) -> c_int;
+}
+
+// `solid_log.h`
+extern "C" {
+ pub fn SOLID_LOG_write(s: *const u8, l: usize);
+}
+
+// `solid_mem.h`
+extern "C" {
+ pub fn SOLID_TLS_AddDestructor(id: i32, dtor: unsafe extern "C" fn(*mut u8));
+}
+
+// `solid_rng.h`
+extern "C" {
+ pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> c_int;
+}
+
+// `rwlock.h`
+extern "C" {
+ pub fn rwl_loc_rdl(id: ID) -> ER;
+ pub fn rwl_loc_wrl(id: ID) -> ER;
+ pub fn rwl_ploc_rdl(id: ID) -> ER;
+ pub fn rwl_ploc_wrl(id: ID) -> ER;
+ pub fn rwl_unl_rwl(id: ID) -> ER;
+ pub fn rwl_acre_rwl() -> ER_ID;
+ pub fn rwl_del_rwl(id: ID) -> ER;
+}
diff --git a/library/std/src/sys/solid/abi/sockets.rs b/library/std/src/sys/solid/abi/sockets.rs
new file mode 100644
index 000000000..eb06a6dd9
--- /dev/null
+++ b/library/std/src/sys/solid/abi/sockets.rs
@@ -0,0 +1,277 @@
+use crate::os::raw::{c_char, c_uint, c_void};
+pub use libc::{c_int, c_long, size_t, ssize_t, suseconds_t, time_t, timeval};
+
+pub const SOLID_NET_ERR_BASE: c_int = -2000;
+pub const EINPROGRESS: c_int = SOLID_NET_ERR_BASE - libc::EINPROGRESS;
+
+pub const AF_INET6: i32 = 10;
+pub const AF_INET: i32 = 2;
+pub const IPPROTO_IP: i32 = 0;
+pub const IPPROTO_IPV6: i32 = 41;
+pub const IPPROTO_TCP: i32 = 6;
+pub const IPV6_ADD_MEMBERSHIP: i32 = 12;
+pub const IPV6_DROP_MEMBERSHIP: i32 = 13;
+pub const IPV6_MULTICAST_LOOP: i32 = 19;
+pub const IPV6_V6ONLY: i32 = 27;
+pub const IP_TTL: i32 = 2;
+pub const IP_MULTICAST_TTL: i32 = 5;
+pub const IP_MULTICAST_LOOP: i32 = 7;
+pub const IP_ADD_MEMBERSHIP: i32 = 3;
+pub const IP_DROP_MEMBERSHIP: i32 = 4;
+pub const SHUT_RD: i32 = 0;
+pub const SHUT_RDWR: i32 = 2;
+pub const SHUT_WR: i32 = 1;
+pub const SOCK_DGRAM: i32 = 2;
+pub const SOCK_STREAM: i32 = 1;
+pub const SOL_SOCKET: i32 = 4095;
+pub const SO_BROADCAST: i32 = 32;
+pub const SO_ERROR: i32 = 4103;
+pub const SO_RCVTIMEO: i32 = 4102;
+pub const SO_REUSEADDR: i32 = 4;
+pub const SO_SNDTIMEO: i32 = 4101;
+pub const SO_LINGER: i32 = 128;
+pub const TCP_NODELAY: i32 = 1;
+pub const MSG_PEEK: c_int = 1;
+pub const FIONBIO: c_long = 0x8008667eu32 as c_long;
+pub const EAI_NONAME: i32 = -2200;
+pub const EAI_SERVICE: i32 = -2201;
+pub const EAI_FAIL: i32 = -2202;
+pub const EAI_MEMORY: i32 = -2203;
+pub const EAI_FAMILY: i32 = -2204;
+
+pub type sa_family_t = u8;
+pub type socklen_t = u32;
+pub type in_addr_t = u32;
+pub type in_port_t = u16;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct in_addr {
+ pub s_addr: in_addr_t,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct in6_addr {
+ pub s6_addr: [u8; 16],
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct ip_mreq {
+ pub imr_multiaddr: in_addr,
+ pub imr_interface: in_addr,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct ipv6_mreq {
+ pub ipv6mr_multiaddr: in6_addr,
+ pub ipv6mr_interface: c_uint,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct msghdr {
+ pub msg_name: *mut c_void,
+ pub msg_namelen: socklen_t,
+ pub msg_iov: *mut iovec,
+ pub msg_iovlen: c_int,
+ pub msg_control: *mut c_void,
+ pub msg_controllen: socklen_t,
+ pub msg_flags: c_int,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct sockaddr {
+ pub sa_len: u8,
+ pub sa_family: sa_family_t,
+ pub sa_data: [c_char; 14usize],
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct sockaddr_in {
+ pub sin_len: u8,
+ pub sin_family: sa_family_t,
+ pub sin_port: in_port_t,
+ pub sin_addr: in_addr,
+ pub sin_zero: [c_char; 8usize],
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct sockaddr_in6 {
+ pub sin6_len: u8,
+ 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,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct sockaddr_storage {
+ pub s2_len: u8,
+ pub ss_family: sa_family_t,
+ pub s2_data1: [c_char; 2usize],
+ pub s2_data2: [u32; 3usize],
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct addrinfo {
+ pub ai_flags: c_int,
+ pub ai_family: c_int,
+ pub ai_socktype: c_int,
+ pub ai_protocol: c_int,
+ pub ai_addrlen: socklen_t,
+ pub ai_addr: *mut sockaddr,
+ pub ai_canonname: *mut c_char,
+ pub ai_next: *mut addrinfo,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct linger {
+ pub l_onoff: c_int,
+ pub l_linger: c_int,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct iovec {
+ pub iov_base: *mut c_void,
+ pub iov_len: usize,
+}
+
+/// This value can be chosen by an application
+pub const SOLID_NET_FD_SETSIZE: usize = 1;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct fd_set {
+ pub num_fds: usize,
+ pub fds: [c_int; SOLID_NET_FD_SETSIZE],
+}
+
+extern "C" {
+ #[link_name = "SOLID_NET_StrError"]
+ pub fn strerror(errnum: c_int) -> *const c_char;
+
+ pub fn SOLID_NET_GetLastError() -> c_int;
+
+ #[link_name = "SOLID_NET_Accept"]
+ pub fn accept(s: c_int, addr: *mut sockaddr, addrlen: *mut socklen_t) -> c_int;
+
+ #[link_name = "SOLID_NET_Bind"]
+ pub fn bind(s: c_int, name: *const sockaddr, namelen: socklen_t) -> c_int;
+
+ #[link_name = "SOLID_NET_Connect"]
+ pub fn connect(s: c_int, name: *const sockaddr, namelen: socklen_t) -> c_int;
+
+ #[link_name = "SOLID_NET_Close"]
+ pub fn close(s: c_int) -> c_int;
+
+ #[link_name = "SOLID_NET_Dup"]
+ pub fn dup(s: c_int) -> c_int;
+
+ #[link_name = "SOLID_NET_GetPeerName"]
+ pub fn getpeername(s: c_int, name: *mut sockaddr, namelen: *mut socklen_t) -> c_int;
+
+ #[link_name = "SOLID_NET_GetSockName"]
+ pub fn getsockname(s: c_int, name: *mut sockaddr, namelen: *mut socklen_t) -> c_int;
+
+ #[link_name = "SOLID_NET_GetSockOpt"]
+ pub fn getsockopt(
+ s: c_int,
+ level: c_int,
+ optname: c_int,
+ optval: *mut c_void,
+ optlen: *mut socklen_t,
+ ) -> c_int;
+
+ #[link_name = "SOLID_NET_SetSockOpt"]
+ pub fn setsockopt(
+ s: c_int,
+ level: c_int,
+ optname: c_int,
+ optval: *const c_void,
+ optlen: socklen_t,
+ ) -> c_int;
+
+ #[link_name = "SOLID_NET_Ioctl"]
+ pub fn ioctl(s: c_int, cmd: c_long, argp: *mut c_void) -> c_int;
+
+ #[link_name = "SOLID_NET_Listen"]
+ pub fn listen(s: c_int, backlog: c_int) -> c_int;
+
+ #[link_name = "SOLID_NET_Recv"]
+ pub fn recv(s: c_int, mem: *mut c_void, len: size_t, flags: c_int) -> ssize_t;
+
+ #[link_name = "SOLID_NET_Read"]
+ pub fn read(s: c_int, mem: *mut c_void, len: size_t) -> ssize_t;
+
+ #[link_name = "SOLID_NET_Readv"]
+ pub fn readv(s: c_int, bufs: *const iovec, bufcnt: c_int) -> ssize_t;
+
+ #[link_name = "SOLID_NET_RecvFrom"]
+ pub fn recvfrom(
+ s: c_int,
+ mem: *mut c_void,
+ len: size_t,
+ flags: c_int,
+ from: *mut sockaddr,
+ fromlen: *mut socklen_t,
+ ) -> ssize_t;
+
+ #[link_name = "SOLID_NET_Send"]
+ pub fn send(s: c_int, mem: *const c_void, len: size_t, flags: c_int) -> ssize_t;
+
+ #[link_name = "SOLID_NET_SendMsg"]
+ pub fn sendmsg(s: c_int, message: *const msghdr, flags: c_int) -> ssize_t;
+
+ #[link_name = "SOLID_NET_SendTo"]
+ pub fn sendto(
+ s: c_int,
+ mem: *const c_void,
+ len: size_t,
+ flags: c_int,
+ to: *const sockaddr,
+ tolen: socklen_t,
+ ) -> ssize_t;
+
+ #[link_name = "SOLID_NET_Shutdown"]
+ pub fn shutdown(s: c_int, how: c_int) -> c_int;
+
+ #[link_name = "SOLID_NET_Socket"]
+ pub fn socket(domain: c_int, type_: c_int, protocol: c_int) -> c_int;
+
+ #[link_name = "SOLID_NET_Write"]
+ pub fn write(s: c_int, mem: *const c_void, len: size_t) -> ssize_t;
+
+ #[link_name = "SOLID_NET_Writev"]
+ pub fn writev(s: c_int, bufs: *const iovec, bufcnt: c_int) -> ssize_t;
+
+ #[link_name = "SOLID_NET_FreeAddrInfo"]
+ pub fn freeaddrinfo(ai: *mut addrinfo);
+
+ #[link_name = "SOLID_NET_GetAddrInfo"]
+ pub fn getaddrinfo(
+ nodename: *const c_char,
+ servname: *const c_char,
+ hints: *const addrinfo,
+ res: *mut *mut addrinfo,
+ ) -> c_int;
+
+ #[link_name = "SOLID_NET_Select"]
+ pub fn select(
+ maxfdp1: c_int,
+ readset: *mut fd_set,
+ writeset: *mut fd_set,
+ exceptset: *mut fd_set,
+ timeout: *mut timeval,
+ ) -> c_int;
+}
diff --git a/library/std/src/sys/solid/alloc.rs b/library/std/src/sys/solid/alloc.rs
new file mode 100644
index 000000000..d013bd876
--- /dev/null
+++ b/library/std/src/sys/solid/alloc.rs
@@ -0,0 +1,32 @@
+use crate::{
+ alloc::{GlobalAlloc, Layout, System},
+ sys::common::alloc::{realloc_fallback, MIN_ALIGN},
+};
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+ #[inline]
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+ unsafe { libc::malloc(layout.size()) as *mut u8 }
+ } else {
+ unsafe { libc::memalign(layout.align(), layout.size()) as *mut u8 }
+ }
+ }
+
+ #[inline]
+ unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
+ unsafe { libc::free(ptr as *mut libc::c_void) }
+ }
+
+ #[inline]
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ unsafe {
+ if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
+ libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
+ } else {
+ realloc_fallback(self, ptr, layout, new_size)
+ }
+ }
+ }
+}
diff --git a/library/std/src/sys/solid/env.rs b/library/std/src/sys/solid/env.rs
new file mode 100644
index 000000000..6855c113b
--- /dev/null
+++ b/library/std/src/sys/solid/env.rs
@@ -0,0 +1,9 @@
+pub mod os {
+ pub const FAMILY: &str = "itron";
+ pub const OS: &str = "solid";
+ pub const DLL_PREFIX: &str = "";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
diff --git a/library/std/src/sys/solid/error.rs b/library/std/src/sys/solid/error.rs
new file mode 100644
index 000000000..547b4f3a9
--- /dev/null
+++ b/library/std/src/sys/solid/error.rs
@@ -0,0 +1,55 @@
+use super::{abi, itron, net};
+use crate::io::ErrorKind;
+
+pub use self::itron::error::{expect_success, ItronError as SolidError};
+
+/// Describe the specified SOLID error code. Returns `None` if it's an
+/// undefined error code.
+///
+/// The SOLID error codes are a superset of μITRON error codes.
+pub fn error_name(er: abi::ER) -> Option<&'static str> {
+ match er {
+ // Success
+ er if er >= 0 => None,
+ er if er < abi::sockets::SOLID_NET_ERR_BASE => net::error_name(er),
+
+ abi::SOLID_ERR_NOTFOUND => Some("not found"),
+ abi::SOLID_ERR_NOTSUPPORTED => Some("not supported"),
+ abi::SOLID_ERR_EBADF => Some("bad flags"),
+ abi::SOLID_ERR_INVALIDCONTENT => Some("invalid content"),
+ abi::SOLID_ERR_NOTUSED => Some("not used"),
+ abi::SOLID_ERR_ALREADYUSED => Some("already used"),
+ abi::SOLID_ERR_OUTOFBOUND => Some("out of bounds"),
+ abi::SOLID_ERR_BADSEQUENCE => Some("bad sequence"),
+ abi::SOLID_ERR_UNKNOWNDEVICE => Some("unknown device"),
+ abi::SOLID_ERR_BUSY => Some("busy"),
+ abi::SOLID_ERR_TIMEOUT => Some("operation timed out"),
+ abi::SOLID_ERR_INVALIDACCESS => Some("invalid access"),
+ abi::SOLID_ERR_NOTREADY => Some("not ready"),
+
+ _ => itron::error::error_name(er),
+ }
+}
+
+pub fn decode_error_kind(er: abi::ER) -> ErrorKind {
+ match er {
+ // Success
+ er if er >= 0 => ErrorKind::Uncategorized,
+ er if er < abi::sockets::SOLID_NET_ERR_BASE => net::decode_error_kind(er),
+
+ abi::SOLID_ERR_NOTFOUND => ErrorKind::NotFound,
+ abi::SOLID_ERR_NOTSUPPORTED => ErrorKind::Unsupported,
+ abi::SOLID_ERR_EBADF => ErrorKind::InvalidInput,
+ abi::SOLID_ERR_INVALIDCONTENT => ErrorKind::InvalidData,
+ // abi::SOLID_ERR_NOTUSED
+ // abi::SOLID_ERR_ALREADYUSED
+ abi::SOLID_ERR_OUTOFBOUND => ErrorKind::InvalidInput,
+ // abi::SOLID_ERR_BADSEQUENCE
+ abi::SOLID_ERR_UNKNOWNDEVICE => ErrorKind::NotFound,
+ // abi::SOLID_ERR_BUSY
+ abi::SOLID_ERR_TIMEOUT => ErrorKind::TimedOut,
+ // abi::SOLID_ERR_INVALIDACCESS
+ // abi::SOLID_ERR_NOTREADY
+ _ => itron::error::decode_error_kind(er),
+ }
+}
diff --git a/library/std/src/sys/solid/fs.rs b/library/std/src/sys/solid/fs.rs
new file mode 100644
index 000000000..a2cbee4dc
--- /dev/null
+++ b/library/std/src/sys/solid/fs.rs
@@ -0,0 +1,574 @@
+use super::{abi, error};
+use crate::{
+ ffi::{CStr, CString, OsStr, OsString},
+ fmt,
+ io::{self, IoSlice, IoSliceMut, ReadBuf, SeekFrom},
+ mem::MaybeUninit,
+ os::raw::{c_int, c_short},
+ os::solid::ffi::OsStrExt,
+ path::{Path, PathBuf},
+ sync::Arc,
+ sys::time::SystemTime,
+ sys::unsupported,
+};
+
+pub use crate::sys_common::fs::try_exists;
+
+/// A file descriptor.
+#[derive(Clone, Copy)]
+#[rustc_layout_scalar_valid_range_start(0)]
+// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
+// 32-bit c_int. Below is -2, in two's complement, but that only works out
+// because c_int is 32 bits.
+#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
+struct FileDesc {
+ fd: c_int,
+}
+
+impl FileDesc {
+ #[inline]
+ fn new(fd: c_int) -> FileDesc {
+ assert_ne!(fd, -1i32);
+ // Safety: we just asserted that the value is in the valid range and
+ // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
+ unsafe { FileDesc { fd } }
+ }
+
+ #[inline]
+ fn raw(&self) -> c_int {
+ self.fd
+ }
+}
+
+pub struct File {
+ fd: FileDesc,
+}
+
+#[derive(Clone)]
+pub struct FileAttr {
+ stat: abi::stat,
+}
+
+// all DirEntry's will have a reference to this struct
+struct InnerReadDir {
+ dirp: abi::S_DIR,
+ root: PathBuf,
+}
+
+pub struct ReadDir {
+ inner: Arc<InnerReadDir>,
+}
+
+pub struct DirEntry {
+ entry: abi::dirent,
+ inner: Arc<InnerReadDir>,
+}
+
+#[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,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct FilePermissions(c_short);
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct FileType(c_short);
+
+#[derive(Debug)]
+pub struct DirBuilder {}
+
+impl FileAttr {
+ pub fn size(&self) -> u64 {
+ self.stat.st_size as u64
+ }
+
+ pub fn perm(&self) -> FilePermissions {
+ FilePermissions(self.stat.st_mode)
+ }
+
+ pub fn file_type(&self) -> FileType {
+ FileType(self.stat.st_mode)
+ }
+
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from_time_t(self.stat.st_mtime))
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from_time_t(self.stat.st_atime))
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from_time_t(self.stat.st_ctime))
+ }
+}
+
+impl FilePermissions {
+ pub fn readonly(&self) -> bool {
+ (self.0 & abi::S_IWRITE) == 0
+ }
+
+ pub fn set_readonly(&mut self, readonly: bool) {
+ if readonly {
+ self.0 &= !abi::S_IWRITE;
+ } else {
+ self.0 |= abi::S_IWRITE;
+ }
+ }
+}
+
+impl FileType {
+ pub fn is_dir(&self) -> bool {
+ self.is(abi::S_IFDIR)
+ }
+ pub fn is_file(&self) -> bool {
+ self.is(abi::S_IFREG)
+ }
+ pub fn is_symlink(&self) -> bool {
+ false
+ }
+
+ pub fn is(&self, mode: c_short) -> bool {
+ self.0 & abi::S_IFMT == mode
+ }
+}
+
+pub fn readdir(p: &Path) -> io::Result<ReadDir> {
+ unsafe {
+ let mut dir = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_OpenDir(
+ cstr(p)?.as_ptr(),
+ dir.as_mut_ptr(),
+ ))
+ .map_err(|e| e.as_io_error())?;
+ let inner = Arc::new(InnerReadDir { dirp: dir.assume_init(), root: p.to_owned() });
+ Ok(ReadDir { inner })
+ }
+}
+
+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.inner.root, f)
+ }
+}
+
+impl Iterator for ReadDir {
+ type Item = io::Result<DirEntry>;
+
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ unsafe {
+ let mut out_dirent = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_ReadDir(
+ self.inner.dirp,
+ out_dirent.as_mut_ptr(),
+ ))
+ .ok()?;
+ Some(Ok(DirEntry { entry: out_dirent.assume_init(), inner: Arc::clone(&self.inner) }))
+ }
+ }
+}
+
+impl Drop for InnerReadDir {
+ fn drop(&mut self) {
+ unsafe { abi::SOLID_FS_CloseDir(self.dirp) };
+ }
+}
+
+impl DirEntry {
+ pub fn path(&self) -> PathBuf {
+ self.inner.root.join(OsStr::from_bytes(
+ unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes(),
+ ))
+ }
+
+ pub fn file_name(&self) -> OsString {
+ OsStr::from_bytes(unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes())
+ .to_os_string()
+ }
+
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ lstat(&self.path())
+ }
+
+ pub fn file_type(&self) -> io::Result<FileType> {
+ match self.entry.d_type {
+ abi::DT_CHR => Ok(FileType(abi::S_IFCHR)),
+ abi::DT_FIFO => Ok(FileType(abi::S_IFIFO)),
+ abi::DT_REG => Ok(FileType(abi::S_IFREG)),
+ abi::DT_DIR => Ok(FileType(abi::S_IFDIR)),
+ abi::DT_BLK => Ok(FileType(abi::S_IFBLK)),
+ _ => lstat(&self.path()).map(|m| m.file_type()),
+ }
+ }
+}
+
+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,
+ }
+ }
+
+ 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) {}
+
+ fn get_access_mode(&self) -> io::Result<c_int> {
+ match (self.read, self.write, self.append) {
+ (true, false, false) => Ok(abi::O_RDONLY),
+ (false, true, false) => Ok(abi::O_WRONLY),
+ (true, true, false) => Ok(abi::O_RDWR),
+ (false, _, true) => Ok(abi::O_WRONLY | abi::O_APPEND),
+ (true, _, true) => Ok(abi::O_RDWR | abi::O_APPEND),
+ (false, false, false) => Err(io::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(io::Error::from_raw_os_error(libc::EINVAL));
+ }
+ }
+ (_, true) => {
+ if self.truncate && !self.create_new {
+ return Err(io::Error::from_raw_os_error(libc::EINVAL));
+ }
+ }
+ }
+
+ Ok(match (self.create, self.truncate, self.create_new) {
+ (false, false, false) => 0,
+ (true, false, false) => abi::O_CREAT,
+ (false, true, false) => abi::O_TRUNC,
+ (true, true, false) => abi::O_CREAT | abi::O_TRUNC,
+ (_, _, true) => abi::O_CREAT | abi::O_EXCL,
+ })
+ }
+}
+
+fn cstr(path: &Path) -> io::Result<CString> {
+ let path = path.as_os_str().as_bytes();
+
+ if !path.starts_with(br"\") {
+ // Relative paths aren't supported
+ return Err(crate::io::const_io_error!(
+ crate::io::ErrorKind::Unsupported,
+ "relative path is not supported on this platform",
+ ));
+ }
+
+ // Apply the thread-safety wrapper
+ const SAFE_PREFIX: &[u8] = br"\TS";
+ let wrapped_path = [SAFE_PREFIX, &path, &[0]].concat();
+
+ CString::from_vec_with_nul(wrapped_path).map_err(|_| {
+ crate::io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "path provided contains a nul byte",
+ )
+ })
+}
+
+impl File {
+ pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+ let flags = opts.get_access_mode()?
+ | opts.get_creation_mode()?
+ | (opts.custom_flags as c_int & !abi::O_ACCMODE);
+ unsafe {
+ let mut fd = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_Open(
+ fd.as_mut_ptr(),
+ cstr(path)?.as_ptr(),
+ flags,
+ ))
+ .map_err(|e| e.as_io_error())?;
+ Ok(File { fd: FileDesc::new(fd.assume_init()) })
+ }
+ }
+
+ pub fn file_attr(&self) -> io::Result<FileAttr> {
+ unsupported()
+ }
+
+ pub fn fsync(&self) -> io::Result<()> {
+ self.flush()
+ }
+
+ pub fn datasync(&self) -> io::Result<()> {
+ self.flush()
+ }
+
+ pub fn truncate(&self, _size: u64) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ unsafe {
+ let mut out_num_bytes = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_Read(
+ self.fd.raw(),
+ buf.as_mut_ptr(),
+ buf.len(),
+ out_num_bytes.as_mut_ptr(),
+ ))
+ .map_err(|e| e.as_io_error())?;
+ Ok(out_num_bytes.assume_init())
+ }
+ }
+
+ pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ unsafe {
+ let len = buf.remaining();
+ let mut out_num_bytes = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_Read(
+ self.fd.raw(),
+ buf.unfilled_mut().as_mut_ptr() as *mut u8,
+ len,
+ out_num_bytes.as_mut_ptr(),
+ ))
+ .map_err(|e| e.as_io_error())?;
+
+ // Safety: `out_num_bytes` is filled by the successful call to
+ // `SOLID_FS_Read`
+ let num_bytes_read = out_num_bytes.assume_init();
+
+ // Safety: `num_bytes_read` bytes were written to the unfilled
+ // portion of the buffer
+ buf.assume_init(num_bytes_read);
+
+ buf.add_filled(num_bytes_read);
+
+ Ok(())
+ }
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ crate::io::default_read_vectored(|buf| self.read(buf), bufs)
+ }
+
+ pub fn is_read_vectored(&self) -> bool {
+ false
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ unsafe {
+ let mut out_num_bytes = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_Write(
+ self.fd.raw(),
+ buf.as_ptr(),
+ buf.len(),
+ out_num_bytes.as_mut_ptr(),
+ ))
+ .map_err(|e| e.as_io_error())?;
+ Ok(out_num_bytes.assume_init())
+ }
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ crate::io::default_write_vectored(|buf| self.write(buf), bufs)
+ }
+
+ pub fn is_write_vectored(&self) -> bool {
+ false
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Sync(self.fd.raw()) })
+ .map_err(|e| e.as_io_error())?;
+ 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 `SOLID_FS_Lseek`.
+ SeekFrom::Start(off) => (abi::SEEK_SET, off as i64),
+ SeekFrom::End(off) => (abi::SEEK_END, off),
+ SeekFrom::Current(off) => (abi::SEEK_CUR, off),
+ };
+ error::SolidError::err_if_negative(unsafe {
+ abi::SOLID_FS_Lseek(self.fd.raw(), pos, whence)
+ })
+ .map_err(|e| e.as_io_error())?;
+
+ // Get the new offset
+ unsafe {
+ let mut out_offset = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_Ftell(
+ self.fd.raw(),
+ out_offset.as_mut_ptr(),
+ ))
+ .map_err(|e| e.as_io_error())?;
+ Ok(out_offset.assume_init() as u64)
+ }
+ }
+
+ pub fn duplicate(&self) -> io::Result<File> {
+ unsupported()
+ }
+
+ pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+ unsupported()
+ }
+}
+
+impl Drop for File {
+ fn drop(&mut self) {
+ unsafe { abi::SOLID_FS_Close(self.fd.raw()) };
+ }
+}
+
+impl DirBuilder {
+ pub fn new() -> DirBuilder {
+ DirBuilder {}
+ }
+
+ pub fn mkdir(&self, p: &Path) -> io::Result<()> {
+ error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Mkdir(cstr(p)?.as_ptr()) })
+ .map_err(|e| e.as_io_error())?;
+ Ok(())
+ }
+}
+
+impl fmt::Debug for File {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("File").field("fd", &self.fd.raw()).finish()
+ }
+}
+
+pub fn unlink(p: &Path) -> io::Result<()> {
+ if stat(p)?.file_type().is_dir() {
+ Err(io::const_io_error!(io::ErrorKind::IsADirectory, "is a directory"))
+ } else {
+ error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
+ .map_err(|e| e.as_io_error())?;
+ Ok(())
+ }
+}
+
+pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+ error::SolidError::err_if_negative(unsafe {
+ abi::SOLID_FS_Rename(cstr(old)?.as_ptr(), cstr(new)?.as_ptr())
+ })
+ .map_err(|e| e.as_io_error())?;
+ Ok(())
+}
+
+pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
+ error::SolidError::err_if_negative(unsafe {
+ abi::SOLID_FS_Chmod(cstr(p)?.as_ptr(), perm.0.into())
+ })
+ .map_err(|e| e.as_io_error())?;
+ Ok(())
+}
+
+pub fn rmdir(p: &Path) -> io::Result<()> {
+ if stat(p)?.file_type().is_dir() {
+ error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
+ .map_err(|e| e.as_io_error())?;
+ Ok(())
+ } else {
+ Err(io::const_io_error!(io::ErrorKind::NotADirectory, "not a directory"))
+ }
+}
+
+pub fn remove_dir_all(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(&child.path())?;
+ } else {
+ unlink(&child.path())?;
+ }
+ }
+ rmdir(path)
+}
+
+pub fn readlink(p: &Path) -> io::Result<PathBuf> {
+ // This target doesn't support symlinks
+ stat(p)?;
+ Err(io::const_io_error!(io::ErrorKind::InvalidInput, "not a symbolic link"))
+}
+
+pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
+ // This target doesn't support symlinks
+ unsupported()
+}
+
+pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
+ // This target doesn't support symlinks
+ unsupported()
+}
+
+pub fn stat(p: &Path) -> io::Result<FileAttr> {
+ // This target doesn't support symlinks
+ lstat(p)
+}
+
+pub fn lstat(p: &Path) -> io::Result<FileAttr> {
+ unsafe {
+ let mut out_stat = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_Stat(
+ cstr(p)?.as_ptr(),
+ out_stat.as_mut_ptr(),
+ ))
+ .map_err(|e| e.as_io_error())?;
+ Ok(FileAttr { stat: out_stat.assume_init() })
+ }
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+ use crate::fs::File;
+
+ let mut reader = File::open(from)?;
+ let mut writer = File::create(to)?;
+
+ io::copy(&mut reader, &mut writer)
+}
diff --git a/library/std/src/sys/solid/io.rs b/library/std/src/sys/solid/io.rs
new file mode 100644
index 000000000..9eb17a10d
--- /dev/null
+++ b/library/std/src/sys/solid/io.rs
@@ -0,0 +1,77 @@
+use crate::marker::PhantomData;
+use crate::slice;
+
+use super::abi::sockets::iovec;
+use libc::c_void;
+
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+pub struct IoSlice<'a> {
+ vec: iovec,
+ _p: PhantomData<&'a [u8]>,
+}
+
+impl<'a> IoSlice<'a> {
+ #[inline]
+ pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
+ IoSlice {
+ vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() },
+ _p: PhantomData,
+ }
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ if self.vec.iov_len < n {
+ panic!("advancing IoSlice beyond its length");
+ }
+
+ unsafe {
+ self.vec.iov_len -= n;
+ self.vec.iov_base = self.vec.iov_base.add(n);
+ }
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
+ }
+}
+
+#[repr(transparent)]
+pub struct IoSliceMut<'a> {
+ vec: iovec,
+ _p: PhantomData<&'a mut [u8]>,
+}
+
+impl<'a> IoSliceMut<'a> {
+ #[inline]
+ pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
+ IoSliceMut {
+ vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() },
+ _p: PhantomData,
+ }
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ if self.vec.iov_len < n {
+ panic!("advancing IoSliceMut beyond its length");
+ }
+
+ unsafe {
+ self.vec.iov_len -= n;
+ self.vec.iov_base = self.vec.iov_base.add(n);
+ }
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
+ }
+
+ #[inline]
+ pub fn as_mut_slice(&mut self) -> &mut [u8] {
+ unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) }
+ }
+}
diff --git a/library/std/src/sys/solid/memchr.rs b/library/std/src/sys/solid/memchr.rs
new file mode 100644
index 000000000..452b7a3de
--- /dev/null
+++ b/library/std/src/sys/solid/memchr.rs
@@ -0,0 +1,21 @@
+pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+ 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> {
+ 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)) }
+}
diff --git a/library/std/src/sys/solid/mod.rs b/library/std/src/sys/solid/mod.rs
new file mode 100644
index 000000000..778a589d1
--- /dev/null
+++ b/library/std/src/sys/solid/mod.rs
@@ -0,0 +1,92 @@
+#![allow(dead_code)]
+#![allow(missing_docs, nonstandard_style)]
+#![deny(unsafe_op_in_unsafe_fn)]
+
+mod abi;
+
+#[path = "../itron"]
+mod itron {
+ pub(super) mod abi;
+ pub mod condvar;
+ pub(super) mod error;
+ pub mod mutex;
+ pub(super) mod spin;
+ pub(super) mod task;
+ pub mod thread;
+ pub(super) mod time;
+ use super::unsupported;
+ pub mod wait_flag;
+}
+
+pub mod alloc;
+#[path = "../unsupported/args.rs"]
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+pub mod env;
+// `error` is `pub(crate)` so that it can be accessed by `itron/error.rs` as
+// `crate::sys::error`
+pub(crate) mod error;
+pub mod fs;
+pub mod io;
+pub mod net;
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+pub mod path;
+#[path = "../unsupported/pipe.rs"]
+pub mod pipe;
+#[path = "../unsupported/process.rs"]
+pub mod process;
+pub mod stdio;
+pub use self::itron::thread;
+pub mod memchr;
+pub mod thread_local_dtor;
+pub mod thread_local_key;
+pub mod time;
+pub use self::itron::wait_flag;
+
+mod rwlock;
+
+pub mod locks {
+ pub use super::itron::condvar::*;
+ pub use super::itron::mutex::*;
+ pub use super::rwlock::*;
+}
+
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
+
+// SAFETY: must be called only once during runtime cleanup.
+pub unsafe fn cleanup() {}
+
+pub fn unsupported<T>() -> crate::io::Result<T> {
+ Err(unsupported_err())
+}
+
+pub fn unsupported_err() -> crate::io::Error {
+ crate::io::const_io_error!(
+ crate::io::ErrorKind::Unsupported,
+ "operation not supported on this platform",
+ )
+}
+
+pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind {
+ error::decode_error_kind(code)
+}
+
+#[inline]
+pub fn abort_internal() -> ! {
+ unsafe { libc::abort() }
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+ unsafe {
+ let mut out = crate::mem::MaybeUninit::<[u64; 2]>::uninit();
+ let result = abi::SOLID_RNG_SampleRandomBytes(out.as_mut_ptr() as *mut u8, 16);
+ assert_eq!(result, 0, "SOLID_RNG_SampleRandomBytes failed: {result}");
+ let [x1, x2] = out.assume_init();
+ (x1, x2)
+ }
+}
diff --git a/library/std/src/sys/solid/net.rs b/library/std/src/sys/solid/net.rs
new file mode 100644
index 000000000..1b98ef993
--- /dev/null
+++ b/library/std/src/sys/solid/net.rs
@@ -0,0 +1,469 @@
+use super::abi;
+use crate::{
+ cmp,
+ ffi::CStr,
+ io::{self, ErrorKind, IoSlice, IoSliceMut},
+ mem,
+ net::{Shutdown, SocketAddr},
+ ptr, str,
+ sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr},
+ sys_common::{AsInner, FromInner, IntoInner},
+ time::Duration,
+};
+
+use self::netc::{sockaddr, socklen_t, MSG_PEEK};
+use libc::{c_int, c_void, size_t};
+
+pub mod netc {
+ pub use super::super::abi::sockets::*;
+}
+
+pub type wrlen_t = size_t;
+
+const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
+
+const fn max_iov() -> usize {
+ // Judging by the source code, it's unlimited, but specify a lower
+ // value just in case.
+ 1024
+}
+
+/// A file descriptor.
+#[rustc_layout_scalar_valid_range_start(0)]
+// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
+// 32-bit c_int. Below is -2, in two's complement, but that only works out
+// because c_int is 32 bits.
+#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
+struct FileDesc {
+ fd: c_int,
+}
+
+impl FileDesc {
+ #[inline]
+ fn new(fd: c_int) -> FileDesc {
+ assert_ne!(fd, -1i32);
+ // Safety: we just asserted that the value is in the valid range and
+ // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
+ unsafe { FileDesc { fd } }
+ }
+
+ #[inline]
+ fn raw(&self) -> c_int {
+ self.fd
+ }
+
+ /// Extracts the actual file descriptor without closing it.
+ #[inline]
+ fn into_raw(self) -> c_int {
+ let fd = self.fd;
+ mem::forget(self);
+ fd
+ }
+
+ fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ netc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT))
+ })?;
+ Ok(ret as usize)
+ }
+
+ fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ netc::readv(
+ self.fd,
+ bufs.as_ptr() as *const netc::iovec,
+ cmp::min(bufs.len(), max_iov()) as c_int,
+ )
+ })?;
+ Ok(ret as usize)
+ }
+
+ #[inline]
+ fn is_read_vectored(&self) -> bool {
+ true
+ }
+
+ fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ netc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT))
+ })?;
+ Ok(ret as usize)
+ }
+
+ fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ netc::writev(
+ self.fd,
+ bufs.as_ptr() as *const netc::iovec,
+ cmp::min(bufs.len(), max_iov()) as c_int,
+ )
+ })?;
+ Ok(ret as usize)
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ fn duplicate(&self) -> io::Result<FileDesc> {
+ cvt(unsafe { netc::dup(self.fd) }).map(Self::new)
+ }
+}
+
+impl AsInner<c_int> for FileDesc {
+ fn as_inner(&self) -> &c_int {
+ &self.fd
+ }
+}
+
+impl Drop for FileDesc {
+ fn drop(&mut self) {
+ unsafe { netc::close(self.fd) };
+ }
+}
+
+#[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(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 {
+ let msg: &dyn crate::fmt::Display = match err {
+ netc::EAI_NONAME => &"name or service not known",
+ netc::EAI_SERVICE => &"service not supported",
+ netc::EAI_FAIL => &"non-recoverable failure in name resolution",
+ netc::EAI_MEMORY => &"memory allocation failure",
+ netc::EAI_FAMILY => &"family not supported",
+ _ => &err,
+ };
+ Err(io::Error::new(
+ io::ErrorKind::Uncategorized,
+ &format!("failed to lookup address information: {msg}")[..],
+ ))
+ }
+}
+
+/// 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())
+}
+
+/// Returns the last error from the network subsystem.
+fn last_error() -> io::Error {
+ io::Error::from_raw_os_error(unsafe { netc::SOLID_NET_GetLastError() })
+}
+
+pub(super) fn error_name(er: abi::ER) -> Option<&'static str> {
+ unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok()
+}
+
+pub(super) fn decode_error_kind(er: abi::ER) -> ErrorKind {
+ let errno = netc::SOLID_NET_ERR_BASE - er;
+ 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,
+ libc::ENOSYS => ErrorKind::Unsupported,
+ libc::ENOMEM => ErrorKind::OutOfMemory,
+ libc::EAGAIN => ErrorKind::WouldBlock,
+
+ _ => ErrorKind::Uncategorized,
+ }
+}
+
+pub fn init() {}
+
+pub struct Socket(FileDesc);
+
+impl Socket {
+ pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
+ let fam = match *addr {
+ SocketAddr::V4(..) => netc::AF_INET,
+ SocketAddr::V6(..) => netc::AF_INET6,
+ };
+ Socket::new_raw(fam, ty)
+ }
+
+ pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> {
+ unsafe {
+ let fd = cvt(netc::socket(fam, ty, 0))?;
+ let fd = FileDesc::new(fd);
+ let socket = Socket(fd);
+
+ Ok(socket)
+ }
+ }
+
+ pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
+ self.set_nonblocking(true)?;
+ let r = unsafe {
+ let (addr, len) = addr.into_inner();
+ cvt(netc::connect(self.0.raw(), addr.as_ptr(), 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(netc::EINPROGRESS) => {}
+ Err(e) => return Err(e),
+ }
+
+ if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "cannot set a 0 duration timeout",
+ ));
+ }
+
+ let mut timeout =
+ netc::timeval { tv_sec: timeout.as_secs() as _, tv_usec: timeout.subsec_micros() as _ };
+ if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
+ timeout.tv_usec = 1;
+ }
+
+ let fds = netc::fd_set { num_fds: 1, fds: [self.0.raw()] };
+
+ let mut writefds = fds;
+ let mut errorfds = fds;
+
+ let n = unsafe {
+ cvt(netc::select(
+ self.0.raw() + 1,
+ ptr::null_mut(),
+ &mut writefds,
+ &mut errorfds,
+ &mut timeout,
+ ))?
+ };
+
+ match n {
+ 0 => Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")),
+ _ => {
+ let can_write = writefds.num_fds != 0;
+ if !can_write {
+ if let Some(e) = self.take_error()? {
+ return Err(e);
+ }
+ }
+ Ok(())
+ }
+ }
+ }
+
+ pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result<Socket> {
+ let fd = cvt_r(|| unsafe { netc::accept(self.0.raw(), storage, len) })?;
+ let fd = FileDesc::new(fd);
+ 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 {
+ netc::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)
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.0.read_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ self.0.is_read_vectored()
+ }
+
+ fn recv_from_with_flags(
+ &self,
+ buf: &mut [u8],
+ flags: c_int,
+ ) -> io::Result<(usize, SocketAddr)> {
+ let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() };
+ let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t;
+
+ let n = cvt(unsafe {
+ netc::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 write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.0.write_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ self.0.is_write_vectored()
+ }
+
+ pub fn set_timeout(&self, dur: Option<Duration>, kind: c_int) -> io::Result<()> {
+ let timeout = match dur {
+ Some(dur) => {
+ if dur.as_secs() == 0 && dur.subsec_nanos() == 0 {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "cannot set a 0 duration timeout",
+ ));
+ }
+
+ let secs = if dur.as_secs() > netc::c_long::MAX as u64 {
+ netc::c_long::MAX
+ } else {
+ dur.as_secs() as netc::c_long
+ };
+ let mut timeout = netc::timeval { tv_sec: secs, tv_usec: dur.subsec_micros() as _ };
+ if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
+ timeout.tv_usec = 1;
+ }
+ timeout
+ }
+ None => netc::timeval { tv_sec: 0, tv_usec: 0 },
+ };
+ setsockopt(self, netc::SOL_SOCKET, kind, timeout)
+ }
+
+ pub fn timeout(&self, kind: c_int) -> io::Result<Option<Duration>> {
+ let raw: netc::timeval = getsockopt(self, netc::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 => netc::SHUT_WR,
+ Shutdown::Read => netc::SHUT_RD,
+ Shutdown::Both => netc::SHUT_RDWR,
+ };
+ cvt(unsafe { netc::shutdown(self.0.raw(), how) })?;
+ Ok(())
+ }
+
+ pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
+ let linger = netc::linger {
+ l_onoff: linger.is_some() as netc::c_int,
+ l_linger: linger.unwrap_or_default().as_secs() as netc::c_int,
+ };
+
+ setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger)
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?;
+
+ Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
+ }
+
+ pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
+ setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int)
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?;
+ Ok(raw != 0)
+ }
+
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ let mut nonblocking = nonblocking as c_int;
+ cvt(unsafe {
+ netc::ioctl(*self.as_inner(), netc::FIONBIO, (&mut nonblocking) as *mut c_int as _)
+ })
+ .map(drop)
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?;
+ if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }
+ }
+
+ // This method is used by sys_common code to abstract over targets.
+ pub fn as_raw(&self) -> c_int {
+ *self.as_inner()
+ }
+}
+
+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()
+ }
+}
diff --git a/library/std/src/sys/solid/os.rs b/library/std/src/sys/solid/os.rs
new file mode 100644
index 000000000..b5649d6e0
--- /dev/null
+++ b/library/std/src/sys/solid/os.rs
@@ -0,0 +1,193 @@
+use super::unsupported;
+use crate::error::Error as StdError;
+use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::os::{
+ raw::{c_char, c_int},
+ solid::ffi::{OsStrExt, OsStringExt},
+};
+use crate::path::{self, PathBuf};
+use crate::sys_common::rwlock::StaticRwLock;
+use crate::vec;
+
+use super::{error, itron, memchr};
+
+// `solid` directly maps `errno`s to μITRON error codes.
+impl itron::error::ItronError {
+ #[inline]
+ pub(crate) fn as_io_error(self) -> crate::io::Error {
+ crate::io::Error::from_raw_os_error(self.as_raw())
+ }
+}
+
+pub fn errno() -> i32 {
+ 0
+}
+
+pub fn error_string(errno: i32) -> String {
+ if let Some(name) = error::error_name(errno) { name.to_owned() } else { format!("{errno}") }
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub struct SplitPaths<'a>(&'a !);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
+ panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+ type Item = PathBuf;
+ fn next(&mut self) -> Option<PathBuf> {
+ *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 this platform yet".fmt(f)
+ }
+}
+
+impl StdError for JoinPathsError {
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "not supported on this platform yet"
+ }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+static ENV_LOCK: StaticRwLock = StaticRwLock::new();
+
+pub struct Env {
+ iter: vec::IntoIter<(OsString, OsString)>,
+}
+
+impl !Send for Env {}
+impl !Sync for Env {}
+
+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 {
+ extern "C" {
+ static mut environ: *const *const c_char;
+ }
+
+ unsafe {
+ let _guard = ENV_LOCK.read();
+ let mut result = Vec::new();
+ if !environ.is_null() {
+ while !(*environ).is_null() {
+ if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+ result.push(key_value);
+ }
+ environ = environ.add(1);
+ }
+ }
+ return Env { iter: result.into_iter() };
+ }
+
+ 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) -> 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()).ok()?;
+ unsafe {
+ let _guard = ENV_LOCK.read();
+ let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
+ if s.is_null() {
+ None
+ } else {
+ Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
+ }
+ }
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+ let k = CString::new(k.as_bytes())?;
+ let v = CString::new(v.as_bytes())?;
+
+ unsafe {
+ let _guard = ENV_LOCK.write();
+ cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
+ }
+}
+
+pub fn unsetenv(n: &OsStr) -> io::Result<()> {
+ let nbuf = CString::new(n.as_bytes())?;
+
+ unsafe {
+ let _guard = ENV_LOCK.write();
+ cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop)
+ }
+}
+
+/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this
+/// function just returns a generic error.
+fn cvt_env(t: c_int) -> io::Result<c_int> {
+ if t == -1 { Err(io::const_io_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) }
+}
+
+pub fn temp_dir() -> PathBuf {
+ panic!("no standard temporary directory on this platform")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ None
+}
+
+pub fn exit(code: i32) -> ! {
+ rtabort!("exit({}) called", code);
+}
+
+pub fn getpid() -> u32 {
+ panic!("no pids on this platform")
+}
diff --git a/library/std/src/sys/solid/path.rs b/library/std/src/sys/solid/path.rs
new file mode 100644
index 000000000..7045c9be2
--- /dev/null
+++ b/library/std/src/sys/solid/path.rs
@@ -0,0 +1,25 @@
+use crate::ffi::OsStr;
+use crate::io;
+use crate::path::{Path, PathBuf, Prefix};
+use crate::sys::unsupported;
+
+#[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: &str = "\\";
+pub const MAIN_SEP: char = '\\';
+
+pub(crate) fn absolute(_path: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
diff --git a/library/std/src/sys/solid/rwlock.rs b/library/std/src/sys/solid/rwlock.rs
new file mode 100644
index 000000000..0a770cf03
--- /dev/null
+++ b/library/std/src/sys/solid/rwlock.rs
@@ -0,0 +1,95 @@
+//! A readers-writer lock implementation backed by the SOLID kernel extension.
+use super::{
+ abi,
+ itron::{
+ error::{expect_success, expect_success_aborting, fail, ItronError},
+ spin::SpinIdOnceCell,
+ },
+};
+
+pub struct RwLock {
+ /// The ID of the underlying mutex object
+ rwl: SpinIdOnceCell<()>,
+}
+
+pub type MovableRwLock = RwLock;
+
+// Safety: `num_readers` is protected by `mtx_num_readers`
+unsafe impl Send for RwLock {}
+unsafe impl Sync for RwLock {}
+
+fn new_rwl() -> Result<abi::ID, ItronError> {
+ ItronError::err_if_negative(unsafe { abi::rwl_acre_rwl() })
+}
+
+impl RwLock {
+ #[inline]
+ pub const fn new() -> RwLock {
+ RwLock { rwl: SpinIdOnceCell::new() }
+ }
+
+ /// Get the inner mutex's ID, which is lazily created.
+ fn raw(&self) -> abi::ID {
+ match self.rwl.get_or_try_init(|| new_rwl().map(|id| (id, ()))) {
+ Ok((id, ())) => id,
+ Err(e) => fail(e, &"rwl_acre_rwl"),
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read(&self) {
+ let rwl = self.raw();
+ expect_success(unsafe { abi::rwl_loc_rdl(rwl) }, &"rwl_loc_rdl");
+ }
+
+ #[inline]
+ pub unsafe fn try_read(&self) -> bool {
+ let rwl = self.raw();
+ match unsafe { abi::rwl_ploc_rdl(rwl) } {
+ abi::E_TMOUT => false,
+ er => {
+ expect_success(er, &"rwl_ploc_rdl");
+ true
+ }
+ }
+ }
+
+ #[inline]
+ pub unsafe fn write(&self) {
+ let rwl = self.raw();
+ expect_success(unsafe { abi::rwl_loc_wrl(rwl) }, &"rwl_loc_wrl");
+ }
+
+ #[inline]
+ pub unsafe fn try_write(&self) -> bool {
+ let rwl = self.raw();
+ match unsafe { abi::rwl_ploc_wrl(rwl) } {
+ abi::E_TMOUT => false,
+ er => {
+ expect_success(er, &"rwl_ploc_wrl");
+ true
+ }
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read_unlock(&self) {
+ let rwl = self.raw();
+ expect_success_aborting(unsafe { abi::rwl_unl_rwl(rwl) }, &"rwl_unl_rwl");
+ }
+
+ #[inline]
+ pub unsafe fn write_unlock(&self) {
+ let rwl = self.raw();
+ expect_success_aborting(unsafe { abi::rwl_unl_rwl(rwl) }, &"rwl_unl_rwl");
+ }
+}
+
+impl Drop for RwLock {
+ #[inline]
+ fn drop(&mut self) {
+ if let Some(rwl) = self.rwl.get().map(|x| x.0) {
+ expect_success_aborting(unsafe { abi::rwl_del_rwl(rwl) }, &"rwl_del_rwl");
+ }
+ }
+}
diff --git a/library/std/src/sys/solid/stdio.rs b/library/std/src/sys/solid/stdio.rs
new file mode 100644
index 000000000..50f017696
--- /dev/null
+++ b/library/std/src/sys/solid/stdio.rs
@@ -0,0 +1,80 @@
+use super::abi;
+use crate::io;
+
+pub struct Stdin;
+pub struct Stdout;
+pub struct Stderr;
+struct PanicOutput;
+
+impl Stdin {
+ pub const fn new() -> Stdin {
+ Stdin
+ }
+}
+
+impl io::Read for Stdin {
+ fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
+ Ok(0)
+ }
+}
+
+impl Stdout {
+ pub const fn new() -> Stdout {
+ Stdout
+ }
+}
+
+impl io::Write for Stdout {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) };
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Stderr {
+ pub const fn new() -> Stderr {
+ Stderr
+ }
+}
+
+impl io::Write for Stderr {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) };
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl PanicOutput {
+ pub const fn new() -> PanicOutput {
+ PanicOutput
+ }
+}
+
+impl io::Write for PanicOutput {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) };
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+pub const STDIN_BUF_SIZE: usize = 0;
+
+pub fn is_ebadf(_err: &io::Error) -> bool {
+ true
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+ Some(PanicOutput::new())
+}
diff --git a/library/std/src/sys/solid/thread_local_dtor.rs b/library/std/src/sys/solid/thread_local_dtor.rs
new file mode 100644
index 000000000..973564570
--- /dev/null
+++ b/library/std/src/sys/solid/thread_local_dtor.rs
@@ -0,0 +1,50 @@
+#![cfg(target_thread_local)]
+#![unstable(feature = "thread_local_internals", issue = "none")]
+
+// Simplify dtor registration by using a list of destructors.
+
+use super::{abi, itron::task};
+use crate::cell::Cell;
+use crate::ptr;
+
+#[thread_local]
+static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
+
+type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
+
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+ if DTORS.get().is_null() {
+ let tid = task::current_task_id_aborting();
+ let v: Box<List> = box Vec::new();
+ DTORS.set(Box::into_raw(v));
+
+ // Register `tls_dtor` to make sure the TLS destructors are called
+ // for tasks created by other means than `std::thread`
+ unsafe { abi::SOLID_TLS_AddDestructor(tid as i32, tls_dtor) };
+ }
+
+ let list: &mut List = unsafe { &mut *DTORS.get() };
+ list.push((t, dtor));
+}
+
+pub unsafe fn run_dtors() {
+ let ptr = DTORS.get();
+ if !ptr.is_null() {
+ // Swap the destructor list, call all registered destructors,
+ // and repeat this until the list becomes permanently empty.
+ while let Some(list) = Some(crate::mem::replace(unsafe { &mut *ptr }, Vec::new()))
+ .filter(|list| !list.is_empty())
+ {
+ for (ptr, dtor) in list.into_iter() {
+ unsafe { dtor(ptr) };
+ }
+ }
+
+ // Drop the destructor list
+ unsafe { Box::from_raw(DTORS.replace(ptr::null_mut())) };
+ }
+}
+
+unsafe extern "C" fn tls_dtor(_unused: *mut u8) {
+ unsafe { run_dtors() };
+}
diff --git a/library/std/src/sys/solid/thread_local_key.rs b/library/std/src/sys/solid/thread_local_key.rs
new file mode 100644
index 000000000..b17521f70
--- /dev/null
+++ b/library/std/src/sys/solid/thread_local_key.rs
@@ -0,0 +1,26 @@
+pub type Key = usize;
+
+#[inline]
+pub unsafe fn create(_dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
+ panic!("should not be used on the solid target");
+}
+
+#[inline]
+pub unsafe fn set(_key: Key, _value: *mut u8) {
+ panic!("should not be used on the solid target");
+}
+
+#[inline]
+pub unsafe fn get(_key: Key) -> *mut u8 {
+ panic!("should not be used on the solid target");
+}
+
+#[inline]
+pub unsafe fn destroy(_key: Key) {
+ panic!("should not be used on the solid target");
+}
+
+#[inline]
+pub fn requires_synchronized_create() -> bool {
+ panic!("should not be used on the solid target");
+}
diff --git a/library/std/src/sys/solid/time.rs b/library/std/src/sys/solid/time.rs
new file mode 100644
index 000000000..ce31cb45a
--- /dev/null
+++ b/library/std/src/sys/solid/time.rs
@@ -0,0 +1,56 @@
+use super::{abi, error::expect_success};
+use crate::{mem::MaybeUninit, time::Duration};
+
+pub use super::itron::time::Instant;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct SystemTime(abi::time_t);
+
+pub const UNIX_EPOCH: SystemTime = SystemTime(0);
+
+impl SystemTime {
+ pub fn now() -> SystemTime {
+ let rtc = unsafe {
+ let mut out = MaybeUninit::zeroed();
+ expect_success(abi::SOLID_RTC_ReadTime(out.as_mut_ptr()), &"SOLID_RTC_ReadTime");
+ out.assume_init()
+ };
+ let t = unsafe {
+ libc::mktime(&mut libc::tm {
+ tm_sec: rtc.tm_sec,
+ tm_min: rtc.tm_min,
+ tm_hour: rtc.tm_hour,
+ tm_mday: rtc.tm_mday,
+ tm_mon: rtc.tm_mon - 1,
+ tm_year: rtc.tm_year,
+ tm_wday: rtc.tm_wday,
+ tm_yday: 0,
+ tm_isdst: 0,
+ tm_gmtoff: 0,
+ tm_zone: crate::ptr::null_mut(),
+ })
+ };
+ assert_ne!(t, -1, "mktime failed");
+ SystemTime(t)
+ }
+
+ pub(super) fn from_time_t(t: abi::time_t) -> Self {
+ Self(t)
+ }
+
+ pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
+ if self.0 >= other.0 {
+ Ok(Duration::from_secs((self.0 as u64).wrapping_sub(other.0 as u64)))
+ } else {
+ Err(Duration::from_secs((other.0 as u64).wrapping_sub(self.0 as u64)))
+ }
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_add(other.as_secs().try_into().ok()?)?))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_sub(other.as_secs().try_into().ok()?)?))
+ }
+}
diff --git a/library/std/src/sys/unix/alloc.rs b/library/std/src/sys/unix/alloc.rs
new file mode 100644
index 000000000..9d6567c9f
--- /dev/null
+++ b/library/std/src/sys/unix/alloc.rs
@@ -0,0 +1,101 @@
+use crate::alloc::{GlobalAlloc, Layout, System};
+use crate::ptr;
+use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN};
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+ #[inline]
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ // jemalloc provides alignment less than MIN_ALIGN for small allocations.
+ // So only rely on MIN_ALIGN if size >= align.
+ // Also see <https://github.com/rust-lang/rust/issues/45955> and
+ // <https://github.com/rust-lang/rust/issues/62251#issuecomment-507580914>.
+ if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+ libc::malloc(layout.size()) as *mut u8
+ } else {
+ #[cfg(target_os = "macos")]
+ {
+ if layout.align() > (1 << 31) {
+ return ptr::null_mut();
+ }
+ }
+ aligned_malloc(&layout)
+ }
+ }
+
+ #[inline]
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+ // See the comment above in `alloc` for why this check looks the way it does.
+ if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+ libc::calloc(layout.size(), 1) as *mut u8
+ } else {
+ let ptr = self.alloc(layout);
+ if !ptr.is_null() {
+ ptr::write_bytes(ptr, 0, layout.size());
+ }
+ ptr
+ }
+ }
+
+ #[inline]
+ unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
+ libc::free(ptr as *mut libc::c_void)
+ }
+
+ #[inline]
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
+ libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
+ } else {
+ realloc_fallback(self, ptr, layout, new_size)
+ }
+ }
+}
+
+cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "redox",
+ target_os = "solaris",
+ target_os = "espidf",
+ target_os = "horizon"
+ ))] {
+ #[inline]
+ unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
+ // On android we currently target API level 9 which unfortunately
+ // doesn't have the `posix_memalign` API used below. Instead we use
+ // `memalign`, but this unfortunately has the property on some systems
+ // where the memory returned cannot be deallocated by `free`!
+ //
+ // Upon closer inspection, however, this appears to work just fine with
+ // Android, so for this platform we should be fine to call `memalign`
+ // (which is present in API level 9). Some helpful references could
+ // possibly be chromium using memalign [1], attempts at documenting that
+ // memalign + free is ok [2] [3], or the current source of chromium
+ // which still uses memalign on android [4].
+ //
+ // [1]: https://codereview.chromium.org/10796020/
+ // [2]: https://code.google.com/p/android/issues/detail?id=35391
+ // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579
+ // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/
+ // /memory/aligned_memory.cc
+ libc::memalign(layout.align(), layout.size()) as *mut u8
+ }
+ } else if #[cfg(target_os = "wasi")] {
+ #[inline]
+ unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
+ libc::aligned_alloc(layout.align(), layout.size()) as *mut u8
+ }
+ } else {
+ #[inline]
+ unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
+ let mut out = ptr::null_mut();
+ // posix_memalign requires that the alignment be a multiple of `sizeof(void*)`.
+ // Since these are all powers of 2, we can just use max.
+ let align = layout.align().max(crate::mem::size_of::<usize>());
+ let ret = libc::posix_memalign(&mut out, align, layout.size());
+ if ret != 0 { ptr::null_mut() } else { out as *mut u8 }
+ }
+ }
+}
diff --git a/library/std/src/sys/unix/android.rs b/library/std/src/sys/unix/android.rs
new file mode 100644
index 000000000..73ff10ab8
--- /dev/null
+++ b/library/std/src/sys/unix/android.rs
@@ -0,0 +1,81 @@
+//! 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, sighandler_t};
+
+use super::weak::weak;
+
+// 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() * crate::f32::consts::LOG2_E
+}
+
+#[cfg(not(test))]
+pub fn log2f64(f: f64) -> f64 {
+ f.ln() * crate::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)
+}
diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs
new file mode 100644
index 000000000..a342f0f5e
--- /dev/null
+++ b/library/std/src/sys/unix/args.rs
@@ -0,0 +1,261 @@
+//! 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 crate::ffi::OsString;
+use crate::fmt;
+use crate::vec;
+
+/// One-time global initialization.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ imp::init(argc, argv)
+}
+
+/// Returns the command line arguments
+pub fn args() -> Args {
+ imp::args()
+}
+
+pub struct Args {
+ iter: vec::IntoIter<OsString>,
+}
+
+impl !Send for Args {}
+impl !Sync for Args {}
+
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.iter.as_slice().fmt(f)
+ }
+}
+
+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()
+ }
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "emscripten",
+ target_os = "haiku",
+ target_os = "l4re",
+ target_os = "fuchsia",
+ target_os = "redox",
+ target_os = "vxworks",
+ target_os = "horizon"
+))]
+mod imp {
+ use super::Args;
+ use crate::ffi::{CStr, OsString};
+ use crate::os::unix::prelude::*;
+ use crate::ptr;
+ use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering};
+
+ // The system-provided argc and argv, which we store in static memory
+ // here so that we can defer the work of parsing them until its actually
+ // needed.
+ //
+ // Note that we never mutate argv/argc, the argv array, or the argv
+ // strings, which allows the code in this file to be very simple.
+ static ARGC: AtomicIsize = AtomicIsize::new(0);
+ static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut());
+
+ unsafe fn really_init(argc: isize, argv: *const *const u8) {
+ // These don't need to be ordered with each other or other stores,
+ // because they only hold the unmodified system-provide argv/argc.
+ ARGC.store(argc, Ordering::Relaxed);
+ ARGV.store(argv as *mut _, Ordering::Relaxed);
+ }
+
+ #[inline(always)]
+ pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
+ // On Linux-GNU, we rely on `ARGV_INIT_ARRAY` below to initialize
+ // `ARGC` and `ARGV`. But in Miri that does not actually happen so we
+ // still initialize here.
+ #[cfg(any(miri, not(all(target_os = "linux", target_env = "gnu"))))]
+ really_init(_argc, _argv);
+ }
+
+ /// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension.
+ /// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows.
+ #[cfg(all(target_os = "linux", target_env = "gnu"))]
+ #[used]
+ #[link_section = ".init_array.00099"]
+ static ARGV_INIT_ARRAY: extern "C" fn(
+ crate::os::raw::c_int,
+ *const *const u8,
+ *const *const u8,
+ ) = {
+ extern "C" fn init_wrapper(
+ argc: crate::os::raw::c_int,
+ argv: *const *const u8,
+ _envp: *const *const u8,
+ ) {
+ unsafe {
+ really_init(argc as isize, argv);
+ }
+ }
+ init_wrapper
+ };
+
+ pub fn args() -> Args {
+ Args { iter: clone().into_iter() }
+ }
+
+ fn clone() -> Vec<OsString> {
+ unsafe {
+ // Load ARGC and ARGV, which hold the unmodified system-provided
+ // argc/argv, so we can read the pointed-to memory without atomics
+ // or synchronization.
+ //
+ // If either ARGC or ARGV is still zero or null, then either there
+ // really are no arguments, or someone is asking for `args()`
+ // before initialization has completed, and we return an empty
+ // list.
+ let argv = ARGV.load(Ordering::Relaxed);
+ let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) };
+ (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()
+ }
+ }
+}
+
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+mod imp {
+ use super::Args;
+ use crate::ffi::CStr;
+
+ pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
+
+ #[cfg(target_os = "macos")]
+ pub fn args() -> Args {
+ use crate::os::unix::prelude::*;
+ extern "C" {
+ // 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() }
+ }
+
+ // 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(any(target_os = "ios", target_os = "watchos"))]
+ pub fn args() -> Args {
+ use crate::ffi::OsString;
+ use crate::mem;
+ use crate::str;
+
+ extern "C" {
+ fn sel_registerName(name: *const libc::c_uchar) -> Sel;
+ fn objc_getClass(class_name: *const libc::c_uchar) -> NsId;
+ }
+
+ #[cfg(target_arch = "aarch64")]
+ extern "C" {
+ fn objc_msgSend(obj: NsId, sel: Sel) -> NsId;
+ #[allow(clashing_extern_declarations)]
+ #[link_name = "objc_msgSend"]
+ fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId;
+ }
+
+ #[cfg(not(target_arch = "aarch64"))]
+ extern "C" {
+ fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId;
+ #[allow(clashing_extern_declarations)]
+ #[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() }
+ }
+}
+
+#[cfg(target_os = "espidf")]
+mod imp {
+ use super::Args;
+
+ #[inline(always)]
+ pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
+
+ pub fn args() -> Args {
+ Args { iter: Vec::new().into_iter() }
+ }
+}
diff --git a/library/std/src/sys/unix/cmath.rs b/library/std/src/sys/unix/cmath.rs
new file mode 100644
index 000000000..2bf80d7a4
--- /dev/null
+++ b/library/std/src/sys/unix/cmath.rs
@@ -0,0 +1,33 @@
+#![cfg(not(test))]
+
+// These symbols are all defined by `libm`,
+// or by `compiler-builtins` on unsupported platforms.
+
+extern "C" {
+ pub fn acos(n: f64) -> f64;
+ pub fn acosf(n: f32) -> f32;
+ pub fn asin(n: f64) -> f64;
+ pub fn asinf(n: f32) -> f32;
+ pub fn atan(n: f64) -> f64;
+ pub fn atan2(a: f64, b: f64) -> f64;
+ pub fn atan2f(a: f32, b: f32) -> f32;
+ pub fn atanf(n: f32) -> f32;
+ pub fn cbrt(n: f64) -> f64;
+ pub fn cbrtf(n: f32) -> f32;
+ pub fn cosh(n: f64) -> f64;
+ pub fn coshf(n: f32) -> f32;
+ pub fn expm1(n: f64) -> f64;
+ pub fn expm1f(n: f32) -> f32;
+ pub fn fdim(a: f64, b: f64) -> f64;
+ pub fn fdimf(a: f32, b: f32) -> f32;
+ pub fn hypot(x: f64, y: f64) -> f64;
+ pub fn hypotf(x: f32, y: f32) -> f32;
+ pub fn log1p(n: f64) -> f64;
+ pub fn log1pf(n: f32) -> f32;
+ pub fn sinh(n: f64) -> f64;
+ pub fn sinhf(n: f32) -> f32;
+ pub fn tan(n: f64) -> f64;
+ pub fn tanf(n: f32) -> f32;
+ pub fn tanh(n: f64) -> f64;
+ pub fn tanhf(n: f32) -> f32;
+}
diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs
new file mode 100644
index 000000000..c9ba661c8
--- /dev/null
+++ b/library/std/src/sys/unix/env.rs
@@ -0,0 +1,219 @@
+#[cfg(target_os = "linux")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "linux";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "macos")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "macos";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".dylib";
+ pub const DLL_EXTENSION: &str = "dylib";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "ios")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "ios";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".dylib";
+ pub const DLL_EXTENSION: &str = "dylib";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "watchos")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "watchos";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".dylib";
+ pub const DLL_EXTENSION: &str = "dylib";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "freebsd")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "freebsd";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "dragonfly")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "dragonfly";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "netbsd")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "netbsd";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "openbsd")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "openbsd";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "android")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "android";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "solaris")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "solaris";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "illumos")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "illumos";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "haiku")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "haiku";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "horizon")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "horizon";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = ".elf";
+ pub const EXE_EXTENSION: &str = "elf";
+}
+
+#[cfg(all(target_os = "emscripten", target_arch = "asmjs"))]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "emscripten";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = ".js";
+ pub const EXE_EXTENSION: &str = "js";
+}
+
+#[cfg(all(target_os = "emscripten", target_arch = "wasm32"))]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "emscripten";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = ".js";
+ pub const EXE_EXTENSION: &str = "js";
+}
+
+#[cfg(target_os = "fuchsia")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "fuchsia";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "l4re")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "l4re";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "redox")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "redox";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "vxworks")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "vxworks";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
+
+#[cfg(target_os = "espidf")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "espidf";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
new file mode 100644
index 000000000..30812dabb
--- /dev/null
+++ b/library/std/src/sys/unix/fd.rs
@@ -0,0 +1,330 @@
+#![unstable(reason = "not public", issue = "none", feature = "fd")]
+
+#[cfg(test)]
+mod tests;
+
+use crate::cmp;
+use crate::io::{self, IoSlice, IoSliceMut, Read, ReadBuf};
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
+use crate::sys::cvt;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "l4re"
+))]
+use libc::off64_t;
+#[cfg(not(any(
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "l4re",
+ target_os = "android"
+)))]
+use libc::off_t as off64_t;
+
+#[derive(Debug)]
+pub struct FileDesc(OwnedFd);
+
+// 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.
+#[cfg(target_os = "macos")]
+const READ_LIMIT: usize = libc::c_int::MAX as usize - 1;
+#[cfg(not(target_os = "macos"))]
+const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "watchos",
+))]
+const fn max_iov() -> usize {
+ libc::IOV_MAX as usize
+}
+
+#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
+const fn max_iov() -> usize {
+ libc::UIO_MAXIOV as usize
+}
+
+#[cfg(not(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "horizon",
+ target_os = "watchos",
+)))]
+const fn max_iov() -> usize {
+ 16 // The minimum value required by POSIX.
+}
+
+impl FileDesc {
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ libc::read(
+ self.as_raw_fd(),
+ buf.as_mut_ptr() as *mut libc::c_void,
+ cmp::min(buf.len(), READ_LIMIT),
+ )
+ })?;
+ Ok(ret as usize)
+ }
+
+ #[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ libc::readv(
+ self.as_raw_fd(),
+ bufs.as_ptr() as *const libc::iovec,
+ cmp::min(bufs.len(), max_iov()) as libc::c_int,
+ )
+ })?;
+ Ok(ret as usize)
+ }
+
+ #[cfg(any(target_os = "espidf", target_os = "horizon"))]
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ return crate::io::default_read_vectored(|b| self.read(b), bufs);
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ cfg!(not(any(target_os = "espidf", target_os = "horizon")))
+ }
+
+ 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(not(any(target_os = "linux", target_os = "android")))]
+ use libc::pread as pread64;
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ use libc::pread64;
+
+ unsafe {
+ cvt(pread64(
+ self.as_raw_fd(),
+ buf.as_mut_ptr() as *mut libc::c_void,
+ cmp::min(buf.len(), READ_LIMIT),
+ offset as off64_t,
+ ))
+ .map(|n| n as usize)
+ }
+ }
+
+ pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ let ret = cvt(unsafe {
+ libc::read(
+ self.as_raw_fd(),
+ buf.unfilled_mut().as_mut_ptr() as *mut libc::c_void,
+ cmp::min(buf.remaining(), READ_LIMIT),
+ )
+ })?;
+
+ // Safety: `ret` bytes were written to the initialized portion of the buffer
+ unsafe {
+ buf.assume_init(ret as usize);
+ }
+ buf.add_filled(ret as usize);
+ Ok(())
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ libc::write(
+ self.as_raw_fd(),
+ buf.as_ptr() as *const libc::c_void,
+ cmp::min(buf.len(), READ_LIMIT),
+ )
+ })?;
+ Ok(ret as usize)
+ }
+
+ #[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ libc::writev(
+ self.as_raw_fd(),
+ bufs.as_ptr() as *const libc::iovec,
+ cmp::min(bufs.len(), max_iov()) as libc::c_int,
+ )
+ })?;
+ Ok(ret as usize)
+ }
+
+ #[cfg(any(target_os = "espidf", target_os = "horizon"))]
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ return crate::io::default_write_vectored(|b| self.write(b), bufs);
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ cfg!(not(any(target_os = "espidf", target_os = "horizon")))
+ }
+
+ pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ use libc::pwrite as pwrite64;
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ use libc::pwrite64;
+
+ unsafe {
+ cvt(pwrite64(
+ self.as_raw_fd(),
+ buf.as_ptr() as *const libc::c_void,
+ cmp::min(buf.len(), READ_LIMIT),
+ offset as off64_t,
+ ))
+ .map(|n| n as usize)
+ }
+ }
+
+ #[cfg(target_os = "linux")]
+ pub fn get_cloexec(&self) -> io::Result<bool> {
+ unsafe { Ok((cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
+ }
+
+ #[cfg(not(any(
+ target_env = "newlib",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "l4re",
+ target_os = "linux",
+ target_os = "haiku",
+ target_os = "redox",
+ target_os = "vxworks"
+ )))]
+ pub fn set_cloexec(&self) -> io::Result<()> {
+ unsafe {
+ cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
+ Ok(())
+ }
+ }
+ #[cfg(any(
+ all(target_env = "newlib", not(any(target_os = "espidf", target_os = "horizon"))),
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "l4re",
+ target_os = "linux",
+ target_os = "haiku",
+ target_os = "redox",
+ target_os = "vxworks"
+ ))]
+ pub fn set_cloexec(&self) -> io::Result<()> {
+ unsafe {
+ let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
+ let new = previous | libc::FD_CLOEXEC;
+ if new != previous {
+ cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
+ }
+ Ok(())
+ }
+ }
+ #[cfg(any(target_os = "espidf", target_os = "horizon"))]
+ pub fn set_cloexec(&self) -> io::Result<()> {
+ // FD_CLOEXEC is not supported in ESP-IDF and Horizon OS but there's no need to,
+ // because neither supports spawning processes.
+ Ok(())
+ }
+
+ #[cfg(target_os = "linux")]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ unsafe {
+ let v = nonblocking as libc::c_int;
+ cvt(libc::ioctl(self.as_raw_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.as_raw_fd(), libc::F_GETFL))?;
+ let new = if nonblocking {
+ previous | libc::O_NONBLOCK
+ } else {
+ previous & !libc::O_NONBLOCK
+ };
+ if new != previous {
+ cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
+ }
+ Ok(())
+ }
+ }
+
+ #[inline]
+ pub fn duplicate(&self) -> io::Result<FileDesc> {
+ Ok(Self(self.0.try_clone()?))
+ }
+}
+
+impl<'a> Read for &'a FileDesc {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ (**self).read(buf)
+ }
+}
+
+impl AsInner<OwnedFd> for FileDesc {
+ fn as_inner(&self) -> &OwnedFd {
+ &self.0
+ }
+}
+
+impl IntoInner<OwnedFd> for FileDesc {
+ fn into_inner(self) -> OwnedFd {
+ self.0
+ }
+}
+
+impl FromInner<OwnedFd> for FileDesc {
+ fn from_inner(owned_fd: OwnedFd) -> Self {
+ Self(owned_fd)
+ }
+}
+
+impl AsFd for FileDesc {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+}
+
+impl AsRawFd for FileDesc {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for FileDesc {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
+impl FromRawFd for FileDesc {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ Self(FromRawFd::from_raw_fd(raw_fd))
+ }
+}
diff --git a/library/std/src/sys/unix/fd/tests.rs b/library/std/src/sys/unix/fd/tests.rs
new file mode 100644
index 000000000..5d17e4678
--- /dev/null
+++ b/library/std/src/sys/unix/fd/tests.rs
@@ -0,0 +1,10 @@
+use super::{FileDesc, IoSlice};
+use crate::os::unix::io::FromRawFd;
+use core::mem::ManuallyDrop;
+
+#[test]
+fn limit_vector_count() {
+ let stdout = ManuallyDrop::new(unsafe { FileDesc::from_raw_fd(1) });
+ let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();
+ assert!(stdout.write_vectored(&bufs).is_ok());
+}
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
new file mode 100644
index 000000000..b5cc8038c
--- /dev/null
+++ b/library/std/src/sys/unix/fs.rs
@@ -0,0 +1,1878 @@
+use crate::os::unix::prelude::*;
+
+use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::fmt;
+use crate::io::{self, Error, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
+use crate::mem;
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
+use crate::path::{Path, PathBuf};
+use crate::ptr;
+use crate::sync::Arc;
+use crate::sys::fd::FileDesc;
+use crate::sys::time::SystemTime;
+use crate::sys::{cvt, cvt_r};
+use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
+
+#[cfg(any(
+ all(target_os = "linux", target_env = "gnu"),
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+))]
+use crate::sys::weak::syscall;
+#[cfg(any(target_os = "android", target_os = "macos"))]
+use crate::sys::weak::weak;
+
+use libc::{c_int, mode_t};
+
+#[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ all(target_os = "linux", target_env = "gnu")
+))]
+use libc::c_char;
+#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
+use libc::dirfd;
+#[cfg(any(target_os = "linux", target_os = "emscripten"))]
+use libc::fstatat64;
+#[cfg(any(
+ target_os = "android",
+ target_os = "solaris",
+ target_os = "fuchsia",
+ target_os = "redox",
+ target_os = "illumos"
+))]
+use libc::readdir as readdir64;
+#[cfg(target_os = "linux")]
+use libc::readdir64;
+#[cfg(any(target_os = "emscripten", target_os = "l4re"))]
+use libc::readdir64_r;
+#[cfg(not(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "l4re",
+ target_os = "fuchsia",
+ target_os = "redox"
+)))]
+use libc::readdir_r as readdir64_r;
+#[cfg(target_os = "android")]
+use libc::{
+ dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64,
+ lstat as lstat64, off64_t, open as open64, stat as stat64,
+};
+#[cfg(not(any(
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "l4re",
+ target_os = "android"
+)))]
+use libc::{
+ dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64,
+ lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
+};
+#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
+use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64};
+
+pub use crate::sys_common::fs::try_exists;
+
+pub struct File(FileDesc);
+
+// FIXME: This should be available on Linux with all `target_env`.
+// But currently only glibc exposes `statx` fn and structs.
+// We don't want to import unverified raw C structs here directly.
+// https://github.com/rust-lang/rust/pull/67774
+macro_rules! cfg_has_statx {
+ ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => {
+ cfg_if::cfg_if! {
+ if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
+ $($then_tt)*
+ } else {
+ $($else_tt)*
+ }
+ }
+ };
+ ($($block_inner:tt)*) => {
+ #[cfg(all(target_os = "linux", target_env = "gnu"))]
+ {
+ $($block_inner)*
+ }
+ };
+}
+
+cfg_has_statx! {{
+ #[derive(Clone)]
+ pub struct FileAttr {
+ stat: stat64,
+ statx_extra_fields: Option<StatxExtraFields>,
+ }
+
+ #[derive(Clone)]
+ struct StatxExtraFields {
+ // This is needed to check if btime is supported by the filesystem.
+ stx_mask: u32,
+ stx_btime: libc::statx_timestamp,
+ // With statx, we can overcome 32-bit `time_t` too.
+ #[cfg(target_pointer_width = "32")]
+ stx_atime: libc::statx_timestamp,
+ #[cfg(target_pointer_width = "32")]
+ stx_ctime: libc::statx_timestamp,
+ #[cfg(target_pointer_width = "32")]
+ stx_mtime: libc::statx_timestamp,
+
+ }
+
+ // We prefer `statx` on Linux if available, which contains file creation time,
+ // as well as 64-bit timestamps of all kinds.
+ // Default `stat64` contains no creation time and may have 32-bit `time_t`.
+ unsafe fn try_statx(
+ fd: c_int,
+ path: *const c_char,
+ flags: i32,
+ mask: u32,
+ ) -> Option<io::Result<FileAttr>> {
+ use crate::sync::atomic::{AtomicU8, Ordering};
+
+ // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
+ // We store the availability in global to avoid unnecessary syscalls.
+ // 0: Unknown
+ // 1: Not available
+ // 2: Available
+ static STATX_STATE: AtomicU8 = AtomicU8::new(0);
+ syscall! {
+ fn statx(
+ fd: c_int,
+ pathname: *const c_char,
+ flags: c_int,
+ mask: libc::c_uint,
+ statxbuf: *mut libc::statx
+ ) -> c_int
+ }
+
+ match STATX_STATE.load(Ordering::Relaxed) {
+ 0 => {
+ // It is a trick to call `statx` with null pointers to check if the syscall
+ // is available. According to the manual, it is expected to fail with EFAULT.
+ // We do this mainly for performance, since it is nearly hundreds times
+ // faster than a normal successful call.
+ let err = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut()))
+ .err()
+ .and_then(|e| e.raw_os_error());
+ // We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited
+ // and returns `EPERM`. Listing all possible errors seems not a good idea.
+ // See: https://github.com/rust-lang/rust/issues/65662
+ if err != Some(libc::EFAULT) {
+ STATX_STATE.store(1, Ordering::Relaxed);
+ return None;
+ }
+ STATX_STATE.store(2, Ordering::Relaxed);
+ }
+ 1 => return None,
+ _ => {}
+ }
+
+ let mut buf: libc::statx = mem::zeroed();
+ if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) {
+ return Some(Err(err));
+ }
+
+ // We cannot fill `stat64` exhaustively because of private padding fields.
+ let mut stat: stat64 = mem::zeroed();
+ // `c_ulong` on gnu-mips, `dev_t` otherwise
+ stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _;
+ stat.st_ino = buf.stx_ino as libc::ino64_t;
+ stat.st_nlink = buf.stx_nlink as libc::nlink_t;
+ stat.st_mode = buf.stx_mode as libc::mode_t;
+ stat.st_uid = buf.stx_uid as libc::uid_t;
+ stat.st_gid = buf.stx_gid as libc::gid_t;
+ stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _;
+ stat.st_size = buf.stx_size as off64_t;
+ stat.st_blksize = buf.stx_blksize as libc::blksize_t;
+ stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t;
+ stat.st_atime = buf.stx_atime.tv_sec as libc::time_t;
+ // `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
+ stat.st_atime_nsec = buf.stx_atime.tv_nsec as _;
+ stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t;
+ stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _;
+ stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t;
+ stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _;
+
+ let extra = StatxExtraFields {
+ stx_mask: buf.stx_mask,
+ stx_btime: buf.stx_btime,
+ // Store full times to avoid 32-bit `time_t` truncation.
+ #[cfg(target_pointer_width = "32")]
+ stx_atime: buf.stx_atime,
+ #[cfg(target_pointer_width = "32")]
+ stx_ctime: buf.stx_ctime,
+ #[cfg(target_pointer_width = "32")]
+ stx_mtime: buf.stx_mtime,
+ };
+
+ Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) }))
+ }
+
+} else {
+ #[derive(Clone)]
+ pub struct FileAttr {
+ stat: stat64,
+ }
+}}
+
+// all DirEntry's will have a reference to this struct
+struct InnerReadDir {
+ dirp: Dir,
+ root: PathBuf,
+}
+
+pub struct ReadDir {
+ inner: Arc<InnerReadDir>,
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "fuchsia",
+ target_os = "redox",
+ )))]
+ end_of_stream: bool,
+}
+
+struct Dir(*mut libc::DIR);
+
+unsafe impl Send for Dir {}
+unsafe impl Sync for Dir {}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "fuchsia",
+ target_os = "redox"
+))]
+pub struct DirEntry {
+ dir: Arc<InnerReadDir>,
+ entry: dirent64_min,
+ // We need to store an owned copy of the entry name on platforms that use
+ // readdir() (not readdir_r()), because a) struct dirent may use a flexible
+ // array to store the name, b) it lives only until the next readdir() call.
+ name: CString,
+}
+
+// Define a minimal subset of fields we need from `dirent64`, especially since
+// we're not using the immediate `d_name` on these targets. Keeping this as an
+// `entry` field in `DirEntry` helps reduce the `cfg` boilerplate elsewhere.
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "fuchsia",
+ target_os = "redox"
+))]
+struct dirent64_min {
+ d_ino: u64,
+ #[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
+ d_type: u8,
+}
+
+#[cfg(not(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "fuchsia",
+ target_os = "redox"
+)))]
+pub struct DirEntry {
+ dir: Arc<InnerReadDir>,
+ // The full entry includes a fixed-length `d_name`.
+ entry: dirent64,
+}
+
+#[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)]
+pub struct FileTimes([libc::timespec; 2]);
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct FileType {
+ mode: mode_t,
+}
+
+#[derive(Debug)]
+pub struct DirBuilder {
+ mode: mode_t,
+}
+
+cfg_has_statx! {{
+ impl FileAttr {
+ fn from_stat64(stat: stat64) -> Self {
+ Self { stat, statx_extra_fields: None }
+ }
+
+ #[cfg(target_pointer_width = "32")]
+ pub fn stx_mtime(&self) -> Option<&libc::statx_timestamp> {
+ if let Some(ext) = &self.statx_extra_fields {
+ if (ext.stx_mask & libc::STATX_MTIME) != 0 {
+ return Some(&ext.stx_mtime);
+ }
+ }
+ None
+ }
+
+ #[cfg(target_pointer_width = "32")]
+ pub fn stx_atime(&self) -> Option<&libc::statx_timestamp> {
+ if let Some(ext) = &self.statx_extra_fields {
+ if (ext.stx_mask & libc::STATX_ATIME) != 0 {
+ return Some(&ext.stx_atime);
+ }
+ }
+ None
+ }
+
+ #[cfg(target_pointer_width = "32")]
+ pub fn stx_ctime(&self) -> Option<&libc::statx_timestamp> {
+ if let Some(ext) = &self.statx_extra_fields {
+ if (ext.stx_mask & libc::STATX_CTIME) != 0 {
+ return Some(&ext.stx_ctime);
+ }
+ }
+ None
+ }
+ }
+} else {
+ impl FileAttr {
+ fn from_stat64(stat: stat64) -> Self {
+ Self { stat }
+ }
+ }
+}}
+
+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::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64))
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64))
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64))
+ }
+}
+
+#[cfg(not(target_os = "netbsd"))]
+impl FileAttr {
+ #[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))]
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ #[cfg(target_pointer_width = "32")]
+ cfg_has_statx! {
+ if let Some(mtime) = self.stx_mtime() {
+ return Ok(SystemTime::new(mtime.tv_sec, mtime.tv_nsec as i64));
+ }
+ }
+
+ Ok(SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64))
+ }
+
+ #[cfg(any(target_os = "vxworks", target_os = "espidf"))]
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::new(self.stat.st_mtime as i64, 0))
+ }
+
+ #[cfg(target_os = "horizon")]
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(self.stat.st_mtim))
+ }
+
+ #[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))]
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ #[cfg(target_pointer_width = "32")]
+ cfg_has_statx! {
+ if let Some(atime) = self.stx_atime() {
+ return Ok(SystemTime::new(atime.tv_sec, atime.tv_nsec as i64));
+ }
+ }
+
+ Ok(SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64))
+ }
+
+ #[cfg(any(target_os = "vxworks", target_os = "espidf"))]
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::new(self.stat.st_atime as i64, 0))
+ }
+
+ #[cfg(target_os = "horizon")]
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(self.stat.st_atim))
+ }
+
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ ))]
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64))
+ }
+
+ #[cfg(not(any(
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ )))]
+ pub fn created(&self) -> io::Result<SystemTime> {
+ cfg_has_statx! {
+ if let Some(ext) = &self.statx_extra_fields {
+ return if (ext.stx_mask & libc::STATX_BTIME) != 0 {
+ Ok(SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64))
+ } else {
+ Err(io::const_io_error!(
+ io::ErrorKind::Uncategorized,
+ "creation time is not available for the filesystem",
+ ))
+ };
+ }
+ }
+
+ Err(io::const_io_error!(
+ io::ErrorKind::Unsupported,
+ "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 FileTimes {
+ pub fn set_accessed(&mut self, t: SystemTime) {
+ self.0[0] = t.t.to_timespec().expect("Invalid system time");
+ }
+
+ pub fn set_modified(&mut self, t: SystemTime) {
+ self.0[1] = t.t.to_timespec().expect("Invalid system time");
+ }
+}
+
+struct TimespecDebugAdapter<'a>(&'a libc::timespec);
+
+impl fmt::Debug for TimespecDebugAdapter<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("timespec")
+ .field("tv_sec", &self.0.tv_sec)
+ .field("tv_nsec", &self.0.tv_nsec)
+ .finish()
+ }
+}
+
+impl fmt::Debug for FileTimes {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FileTimes")
+ .field("accessed", &TimespecDebugAdapter(&self.0[0]))
+ .field("modified", &TimespecDebugAdapter(&self.0[1]))
+ .finish()
+ }
+}
+
+impl Default for FileTimes {
+ fn default() -> Self {
+ // Redox doesn't appear to support `UTIME_OMIT`, so we stub it out here, and always return
+ // an error in `set_times`.
+ // ESP-IDF does not support `futimens` at all and the behavior for that OS is therefore
+ // the same as for Redox.
+ #[cfg(any(target_os = "redox", target_os = "espidf"))]
+ let omit = libc::timespec { tv_sec: 0, tv_nsec: 0 };
+ #[cfg(not(any(target_os = "redox", target_os = "espidf")))]
+ let omit = libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ };
+ Self([omit; 2])
+ }
+}
+
+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.inner.root, f)
+ }
+}
+
+impl Iterator for ReadDir {
+ type Item = io::Result<DirEntry>;
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "solaris",
+ target_os = "fuchsia",
+ target_os = "redox",
+ target_os = "illumos"
+ ))]
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ unsafe {
+ loop {
+ // As of POSIX.1-2017, readdir() is not required to be thread safe; only
+ // readdir_r() is. However, readdir_r() cannot correctly handle platforms
+ // with unlimited or variable NAME_MAX. Many modern platforms guarantee
+ // thread safety for readdir() as long an individual DIR* is not accessed
+ // concurrently, which is sufficient for Rust.
+ super::os::set_errno(0);
+ let entry_ptr = readdir64(self.inner.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))),
+ };
+ }
+
+ // Only d_reclen bytes of *entry_ptr are valid, so we can't just copy the
+ // whole thing (#93384). Instead, copy everything except the name.
+ let mut copy: dirent64 = mem::zeroed();
+ // Can't dereference entry_ptr, so use the local entry to get
+ // offsetof(struct dirent, d_name)
+ let copy_bytes = &mut copy as *mut _ as *mut u8;
+ let copy_name = &mut copy.d_name as *mut _ as *mut u8;
+ let name_offset = copy_name.offset_from(copy_bytes) as usize;
+ let entry_bytes = entry_ptr as *const u8;
+ let entry_name = entry_bytes.add(name_offset);
+ ptr::copy_nonoverlapping(entry_bytes, copy_bytes, name_offset);
+
+ let entry = dirent64_min {
+ d_ino: copy.d_ino as u64,
+ #[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
+ d_type: copy.d_type as u8,
+ };
+
+ let ret = DirEntry {
+ entry,
+ // d_name is guaranteed to be null-terminated.
+ name: CStr::from_ptr(entry_name as *const _).to_owned(),
+ dir: Arc::clone(&self.inner),
+ };
+ if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
+ return Some(Ok(ret));
+ }
+ }
+ }
+ }
+
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "solaris",
+ target_os = "fuchsia",
+ target_os = "redox",
+ target_os = "illumos"
+ )))]
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ if self.end_of_stream {
+ return None;
+ }
+
+ unsafe {
+ let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) };
+ let mut entry_ptr = ptr::null_mut();
+ loop {
+ let err = readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr);
+ if err != 0 {
+ if entry_ptr.is_null() {
+ // We encountered an error (which will be returned in this iteration), but
+ // we also reached the end of the directory stream. The `end_of_stream`
+ // flag is enabled to make sure that we return `None` in the next iteration
+ // (instead of looping forever)
+ self.end_of_stream = true;
+ }
+ return Some(Err(Error::from_raw_os_error(err)));
+ }
+ 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.root.join(self.file_name_os_str())
+ }
+
+ pub fn file_name(&self) -> OsString {
+ self.file_name_os_str().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.dirp.0) })?;
+ let name = self.name_cstr().as_ptr();
+
+ cfg_has_statx! {
+ if let Some(ret) = unsafe { try_statx(
+ fd,
+ name,
+ libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
+ libc::STATX_ALL,
+ ) } {
+ return ret;
+ }
+ }
+
+ let mut stat: stat64 = unsafe { mem::zeroed() };
+ cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?;
+ Ok(FileAttr::from_stat64(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 = "illumos",
+ target_os = "haiku",
+ target_os = "vxworks"
+ ))]
+ pub fn file_type(&self) -> io::Result<FileType> {
+ self.metadata().map(|m| m.file_type())
+ }
+
+ #[cfg(not(any(
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "haiku",
+ target_os = "vxworks"
+ )))]
+ 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 }),
+ _ => self.metadata().map(|m| m.file_type()),
+ }
+ }
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "android",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "haiku",
+ target_os = "l4re",
+ target_os = "fuchsia",
+ target_os = "redox",
+ target_os = "vxworks",
+ target_os = "espidf",
+ target_os = "horizon"
+ ))]
+ pub fn ino(&self) -> u64 {
+ self.entry.d_ino as u64
+ }
+
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "openbsd",
+ 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 = "watchos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly"
+ ))]
+ fn name_bytes(&self) -> &[u8] {
+ use crate::slice;
+ unsafe {
+ slice::from_raw_parts(
+ self.entry.d_name.as_ptr() as *const u8,
+ self.entry.d_namlen as usize,
+ )
+ }
+ }
+ #[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly"
+ )))]
+ fn name_bytes(&self) -> &[u8] {
+ self.name_cstr().to_bytes()
+ }
+
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "fuchsia",
+ target_os = "redox"
+ )))]
+ fn name_cstr(&self) -> &CStr {
+ unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
+ }
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "fuchsia",
+ target_os = "redox"
+ ))]
+ fn name_cstr(&self) -> &CStr {
+ &self.name
+ }
+
+ pub fn file_name_os_str(&self) -> &OsStr {
+ OsStr::from_bytes(self.name_bytes())
+ }
+}
+
+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);
+ // The third argument of `open64` is documented to have type `mode_t`. On
+ // some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`.
+ // However, since this is a variadic function, C integer promotion rules mean that on
+ // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
+ let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
+ Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
+ }
+
+ pub fn file_attr(&self) -> io::Result<FileAttr> {
+ let fd = self.as_raw_fd();
+
+ cfg_has_statx! {
+ if let Some(ret) = unsafe { try_statx(
+ fd,
+ b"\0" as *const _ as *const c_char,
+ libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT,
+ libc::STATX_ALL,
+ ) } {
+ return ret;
+ }
+ }
+
+ let mut stat: stat64 = unsafe { mem::zeroed() };
+ cvt(unsafe { fstat64(fd, &mut stat) })?;
+ Ok(FileAttr::from_stat64(stat))
+ }
+
+ pub fn fsync(&self) -> io::Result<()> {
+ cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?;
+ return Ok(());
+
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+ unsafe fn os_fsync(fd: c_int) -> c_int {
+ libc::fcntl(fd, libc::F_FULLFSYNC)
+ }
+ #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "watchos")))]
+ unsafe fn os_fsync(fd: c_int) -> c_int {
+ libc::fsync(fd)
+ }
+ }
+
+ pub fn datasync(&self) -> io::Result<()> {
+ cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?;
+ return Ok(());
+
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+ unsafe fn os_datasync(fd: c_int) -> c_int {
+ libc::fcntl(fd, libc::F_FULLFSYNC)
+ }
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ unsafe fn os_datasync(fd: c_int) -> c_int {
+ libc::fdatasync(fd)
+ }
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "watchos",
+ )))]
+ unsafe fn os_datasync(fd: c_int) -> c_int {
+ libc::fsync(fd)
+ }
+ }
+
+ pub fn truncate(&self, size: u64) -> io::Result<()> {
+ let size: off64_t =
+ size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
+ cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.0.read_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ self.0.is_read_vectored()
+ }
+
+ pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
+ self.0.read_at(buf, offset)
+ }
+
+ pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ self.0.read_buf(buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.0.write_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ self.0.is_write_vectored()
+ }
+
+ 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),
+ };
+ let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos as off64_t, whence) })?;
+ Ok(n as u64)
+ }
+
+ pub fn duplicate(&self) -> io::Result<File> {
+ self.0.duplicate().map(File)
+ }
+
+ pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
+ cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?;
+ Ok(())
+ }
+
+ pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
+ cfg_if::cfg_if! {
+ if #[cfg(any(target_os = "redox", target_os = "espidf"))] {
+ // Redox doesn't appear to support `UTIME_OMIT`.
+ // ESP-IDF does not support `futimens` at all and the behavior for that OS is therefore
+ // the same as for Redox.
+ drop(times);
+ Err(io::const_io_error!(
+ io::ErrorKind::Unsupported,
+ "setting file times not supported",
+ ))
+ } else if #[cfg(any(target_os = "android", target_os = "macos"))] {
+ // futimens requires macOS 10.13, and Android API level 19
+ cvt(unsafe {
+ weak!(fn futimens(c_int, *const libc::timespec) -> c_int);
+ match futimens.get() {
+ Some(futimens) => futimens(self.as_raw_fd(), times.0.as_ptr()),
+ #[cfg(target_os = "macos")]
+ None => {
+ fn ts_to_tv(ts: &libc::timespec) -> libc::timeval {
+ libc::timeval {
+ tv_sec: ts.tv_sec,
+ tv_usec: (ts.tv_nsec / 1000) as _
+ }
+ }
+ let timevals = [ts_to_tv(&times.0[0]), ts_to_tv(&times.0[1])];
+ libc::futimes(self.as_raw_fd(), timevals.as_ptr())
+ }
+ // futimes requires even newer Android.
+ #[cfg(target_os = "android")]
+ None => return Err(io::const_io_error!(
+ io::ErrorKind::Unsupported,
+ "setting file times requires Android API level >= 19",
+ )),
+ }
+ })?;
+ Ok(())
+ } else {
+ cvt(unsafe { libc::futimens(self.as_raw_fd(), times.0.as_ptr()) })?;
+ 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(), 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 AsInner<FileDesc> for File {
+ fn as_inner(&self) -> &FileDesc {
+ &self.0
+ }
+}
+
+impl AsInnerMut<FileDesc> for File {
+ fn as_inner_mut(&mut self) -> &mut FileDesc {
+ &mut self.0
+ }
+}
+
+impl IntoInner<FileDesc> for File {
+ fn into_inner(self) -> FileDesc {
+ self.0
+ }
+}
+
+impl FromInner<FileDesc> for File {
+ fn from_inner(file_desc: FileDesc) -> Self {
+ Self(file_desc)
+ }
+}
+
+impl AsFd for File {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+}
+
+impl AsRawFd for File {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for File {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
+impl FromRawFd for File {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ Self(FromRawFd::from_raw_fd(raw_fd))
+ }
+}
+
+impl fmt::Debug for File {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ #[cfg(any(target_os = "linux", target_os = "netbsd"))]
+ 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()) };
+ 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(all(target_os = "freebsd", target_arch = "x86_64"))]
+ fn get_path(fd: c_int) -> Option<PathBuf> {
+ let info = Box::<libc::kinfo_file>::new_zeroed();
+ let mut info = unsafe { info.assume_init() };
+ info.kf_structsize = mem::size_of::<libc::kinfo_file>() as libc::c_int;
+ let n = unsafe { libc::fcntl(fd, libc::F_KINFO, &mut *info) };
+ if n == -1 {
+ return None;
+ }
+ let buf = unsafe { CStr::from_ptr(info.kf_path.as_mut_ptr()).to_bytes().to_vec() };
+ Some(PathBuf::from(OsString::from_vec(buf)))
+ }
+
+ #[cfg(target_os = "vxworks")]
+ fn get_path(fd: c_int) -> Option<PathBuf> {
+ let mut buf = vec![0; libc::PATH_MAX as usize];
+ let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
+ if n == -1 {
+ return None;
+ }
+ let l = buf.iter().position(|&c| c == 0).unwrap();
+ buf.truncate(l as usize);
+ Some(PathBuf::from(OsString::from_vec(buf)))
+ }
+
+ #[cfg(not(any(
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "vxworks",
+ all(target_os = "freebsd", target_arch = "x86_64"),
+ target_os = "netbsd"
+ )))]
+ 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", target_os = "vxworks"))]
+ 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", target_os = "vxworks")))]
+ fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
+ // FIXME(#24570): implement this for other Unix platforms
+ None
+ }
+
+ let fd = self.as_raw_fd();
+ 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());
+ if ptr.is_null() {
+ Err(Error::last_os_error())
+ } else {
+ let inner = InnerReadDir { dirp: Dir(ptr), root };
+ Ok(ReadDir {
+ inner: Arc::new(inner),
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "fuchsia",
+ target_os = "redox",
+ )))]
+ end_of_stream: false,
+ })
+ }
+ }
+}
+
+pub fn unlink(p: &Path) -> io::Result<()> {
+ let p = cstr(p)?;
+ 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(), 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(), perm.mode) })?;
+ Ok(())
+}
+
+pub fn rmdir(p: &Path) -> io::Result<()> {
+ let p = cstr(p)?;
+ cvt(unsafe { libc::rmdir(p.as_ptr()) })?;
+ Ok(())
+}
+
+pub fn readlink(p: &Path) -> io::Result<PathBuf> {
+ let c_path = cstr(p)?;
+ let p = c_path.as_ptr();
+
+ 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(original: &Path, link: &Path) -> io::Result<()> {
+ let original = cstr(original)?;
+ let link = cstr(link)?;
+ cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) })?;
+ Ok(())
+}
+
+pub fn link(original: &Path, link: &Path) -> io::Result<()> {
+ let original = cstr(original)?;
+ let link = cstr(link)?;
+ cfg_if::cfg_if! {
+ if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon"))] {
+ // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
+ // it implementation-defined whether `link` follows symlinks, so rely on the
+ // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
+ // Android has `linkat` on newer versions, but we happen to know `link`
+ // always has the correct behavior, so it's here as well.
+ cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
+ } else if #[cfg(target_os = "macos")] {
+ // On MacOS, older versions (<=10.9) lack support for linkat while newer
+ // versions have it. We want to use linkat if it is available, so we use weak!
+ // to check. `linkat` is preferable to `link` because it gives us a flag to
+ // specify how symlinks should be handled. We pass 0 as the flags argument,
+ // meaning it shouldn't follow symlinks.
+ weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
+
+ if let Some(f) = linkat.get() {
+ cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
+ } else {
+ cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
+ };
+ } else {
+ // Where we can, use `linkat` instead of `link`; see the comment above
+ // this one for details on why.
+ cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
+ }
+ }
+ Ok(())
+}
+
+pub fn stat(p: &Path) -> io::Result<FileAttr> {
+ let p = cstr(p)?;
+
+ cfg_has_statx! {
+ if let Some(ret) = unsafe { try_statx(
+ libc::AT_FDCWD,
+ p.as_ptr(),
+ libc::AT_STATX_SYNC_AS_STAT,
+ libc::STATX_ALL,
+ ) } {
+ return ret;
+ }
+ }
+
+ let mut stat: stat64 = unsafe { mem::zeroed() };
+ cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
+ Ok(FileAttr::from_stat64(stat))
+}
+
+pub fn lstat(p: &Path) -> io::Result<FileAttr> {
+ let p = cstr(p)?;
+
+ cfg_has_statx! {
+ if let Some(ret) = unsafe { try_statx(
+ libc::AT_FDCWD,
+ p.as_ptr(),
+ libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
+ libc::STATX_ALL,
+ ) } {
+ return ret;
+ }
+ }
+
+ let mut stat: stat64 = unsafe { mem::zeroed() };
+ cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
+ Ok(FileAttr::from_stat64(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(), ptr::null_mut());
+ if r.is_null() {
+ return Err(io::Error::last_os_error());
+ }
+ buf = CStr::from_ptr(r).to_bytes().to_vec();
+ libc::free(r as *mut _);
+ }
+ Ok(PathBuf::from(OsString::from_vec(buf)))
+}
+
+fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
+ use crate::fs::File;
+ use crate::sys_common::fs::NOT_FILE_ERROR;
+
+ let reader = File::open(from)?;
+ let metadata = reader.metadata()?;
+ if !metadata.is_file() {
+ return Err(NOT_FILE_ERROR);
+ }
+ Ok((reader, metadata))
+}
+
+#[cfg(target_os = "espidf")]
+fn open_to_and_set_permissions(
+ to: &Path,
+ reader_metadata: crate::fs::Metadata,
+) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
+ use crate::fs::OpenOptions;
+ let writer = OpenOptions::new().open(to)?;
+ let writer_metadata = writer.metadata()?;
+ Ok((writer, writer_metadata))
+}
+
+#[cfg(not(target_os = "espidf"))]
+fn open_to_and_set_permissions(
+ to: &Path,
+ reader_metadata: crate::fs::Metadata,
+) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
+ use crate::fs::OpenOptions;
+ use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt};
+
+ let perm = reader_metadata.permissions();
+ let writer = OpenOptions::new()
+ // create the file with the correct mode right away
+ .mode(perm.mode())
+ .write(true)
+ .create(true)
+ .truncate(true)
+ .open(to)?;
+ let writer_metadata = writer.metadata()?;
+ if writer_metadata.is_file() {
+ // Set the correct file permissions, in case the file already existed.
+ // Don't set the permissions on already existing non-files like
+ // pipes/FIFOs or device nodes.
+ writer.set_permissions(perm)?;
+ }
+ Ok((writer, writer_metadata))
+}
+
+#[cfg(not(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+)))]
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+ let (mut reader, reader_metadata) = open_from(from)?;
+ let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
+
+ io::copy(&mut reader, &mut writer)
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+ let (mut reader, reader_metadata) = open_from(from)?;
+ let max_len = u64::MAX;
+ let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
+
+ use super::kernel_copy::{copy_regular_files, CopyResult};
+
+ match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) {
+ CopyResult::Ended(bytes) => Ok(bytes),
+ CopyResult::Error(e, _) => Err(e),
+ CopyResult::Fallback(written) => match io::copy::generic_copy(&mut reader, &mut writer) {
+ Ok(bytes) => Ok(bytes + written),
+ Err(e) => Err(e),
+ },
+ }
+}
+
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+ use crate::sync::atomic::{AtomicBool, Ordering};
+
+ const COPYFILE_ACL: u32 = 1 << 0;
+ const COPYFILE_STAT: u32 = 1 << 1;
+ const COPYFILE_XATTR: u32 = 1 << 2;
+ const COPYFILE_DATA: u32 = 1 << 3;
+
+ const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL;
+ const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR;
+ const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA;
+
+ const COPYFILE_STATE_COPIED: u32 = 8;
+
+ #[allow(non_camel_case_types)]
+ type copyfile_state_t = *mut libc::c_void;
+ #[allow(non_camel_case_types)]
+ type copyfile_flags_t = u32;
+
+ extern "C" {
+ fn fcopyfile(
+ from: libc::c_int,
+ to: libc::c_int,
+ state: copyfile_state_t,
+ flags: copyfile_flags_t,
+ ) -> libc::c_int;
+ fn copyfile_state_alloc() -> copyfile_state_t;
+ fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int;
+ fn copyfile_state_get(
+ state: copyfile_state_t,
+ flag: u32,
+ dst: *mut libc::c_void,
+ ) -> libc::c_int;
+ }
+
+ struct FreeOnDrop(copyfile_state_t);
+ impl Drop for FreeOnDrop {
+ fn drop(&mut self) {
+ // The code below ensures that `FreeOnDrop` is never a null pointer
+ unsafe {
+ // `copyfile_state_free` returns -1 if the `to` or `from` files
+ // cannot be closed. However, this is not considered this an
+ // error.
+ copyfile_state_free(self.0);
+ }
+ }
+ }
+
+ // MacOS prior to 10.12 don't support `fclonefileat`
+ // We store the availability in a global to avoid unnecessary syscalls
+ static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true);
+ syscall! {
+ fn fclonefileat(
+ srcfd: libc::c_int,
+ dst_dirfd: libc::c_int,
+ dst: *const c_char,
+ flags: libc::c_int
+ ) -> libc::c_int
+ }
+
+ let (reader, reader_metadata) = open_from(from)?;
+
+ // Opportunistically attempt to create a copy-on-write clone of `from`
+ // using `fclonefileat`.
+ if HAS_FCLONEFILEAT.load(Ordering::Relaxed) {
+ let to = cstr(to)?;
+ let clonefile_result =
+ cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) });
+ match clonefile_result {
+ Ok(_) => return Ok(reader_metadata.len()),
+ Err(err) => match err.raw_os_error() {
+ // `fclonefileat` will fail on non-APFS volumes, if the
+ // destination already exists, or if the source and destination
+ // are on different devices. In all these cases `fcopyfile`
+ // should succeed.
+ Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (),
+ Some(libc::ENOSYS) => HAS_FCLONEFILEAT.store(false, Ordering::Relaxed),
+ _ => return Err(err),
+ },
+ }
+ }
+
+ // Fall back to using `fcopyfile` if `fclonefileat` does not succeed.
+ let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?;
+
+ // We ensure that `FreeOnDrop` never contains a null pointer so it is
+ // always safe to call `copyfile_state_free`
+ let state = unsafe {
+ let state = copyfile_state_alloc();
+ if state.is_null() {
+ return Err(crate::io::Error::last_os_error());
+ }
+ FreeOnDrop(state)
+ };
+
+ let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { COPYFILE_DATA };
+
+ cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?;
+
+ let mut bytes_copied: libc::off_t = 0;
+ cvt(unsafe {
+ copyfile_state_get(
+ state.0,
+ COPYFILE_STATE_COPIED,
+ &mut bytes_copied as *mut libc::off_t as *mut libc::c_void,
+ )
+ })?;
+ Ok(bytes_copied as u64)
+}
+
+pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
+ let path = cstr(path)?;
+ cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?;
+ Ok(())
+}
+
+pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
+ cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) })?;
+ Ok(())
+}
+
+pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
+ let path = cstr(path)?;
+ cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?;
+ Ok(())
+}
+
+#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
+pub fn chroot(dir: &Path) -> io::Result<()> {
+ let dir = cstr(dir)?;
+ cvt(unsafe { libc::chroot(dir.as_ptr()) })?;
+ Ok(())
+}
+
+pub use remove_dir_impl::remove_dir_all;
+
+// Fallback for REDOX, ESP-ID, Horizon, and Miri
+#[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri))]
+mod remove_dir_impl {
+ pub use crate::sys_common::fs::remove_dir_all;
+}
+
+// Modern implementation using openat(), unlinkat() and fdopendir()
+#[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri)))]
+mod remove_dir_impl {
+ use super::{cstr, lstat, Dir, DirEntry, InnerReadDir, ReadDir};
+ use crate::ffi::CStr;
+ use crate::io;
+ use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
+ use crate::os::unix::prelude::{OwnedFd, RawFd};
+ use crate::path::{Path, PathBuf};
+ use crate::sync::Arc;
+ use crate::sys::{cvt, cvt_r};
+
+ #[cfg(not(all(target_os = "macos", not(target_arch = "aarch64")),))]
+ use libc::{fdopendir, openat, unlinkat};
+ #[cfg(all(target_os = "macos", not(target_arch = "aarch64")))]
+ use macos_weak::{fdopendir, openat, unlinkat};
+
+ #[cfg(all(target_os = "macos", not(target_arch = "aarch64")))]
+ mod macos_weak {
+ use crate::sys::weak::weak;
+ use libc::{c_char, c_int, DIR};
+
+ fn get_openat_fn() -> Option<unsafe extern "C" fn(c_int, *const c_char, c_int) -> c_int> {
+ weak!(fn openat(c_int, *const c_char, c_int) -> c_int);
+ openat.get()
+ }
+
+ pub fn has_openat() -> bool {
+ get_openat_fn().is_some()
+ }
+
+ pub unsafe fn openat(dirfd: c_int, pathname: *const c_char, flags: c_int) -> c_int {
+ get_openat_fn().map(|openat| openat(dirfd, pathname, flags)).unwrap_or_else(|| {
+ crate::sys::unix::os::set_errno(libc::ENOSYS);
+ -1
+ })
+ }
+
+ pub unsafe fn fdopendir(fd: c_int) -> *mut DIR {
+ #[cfg(all(target_os = "macos", target_arch = "x86"))]
+ weak!(fn fdopendir(c_int) -> *mut DIR, "fdopendir$INODE64$UNIX2003");
+ #[cfg(all(target_os = "macos", target_arch = "x86_64"))]
+ weak!(fn fdopendir(c_int) -> *mut DIR, "fdopendir$INODE64");
+ fdopendir.get().map(|fdopendir| fdopendir(fd)).unwrap_or_else(|| {
+ crate::sys::unix::os::set_errno(libc::ENOSYS);
+ crate::ptr::null_mut()
+ })
+ }
+
+ pub unsafe fn unlinkat(dirfd: c_int, pathname: *const c_char, flags: c_int) -> c_int {
+ weak!(fn unlinkat(c_int, *const c_char, c_int) -> c_int);
+ unlinkat.get().map(|unlinkat| unlinkat(dirfd, pathname, flags)).unwrap_or_else(|| {
+ crate::sys::unix::os::set_errno(libc::ENOSYS);
+ -1
+ })
+ }
+ }
+
+ pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd> {
+ let fd = cvt_r(|| unsafe {
+ openat(
+ parent_fd.unwrap_or(libc::AT_FDCWD),
+ p.as_ptr(),
+ libc::O_CLOEXEC | libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_DIRECTORY,
+ )
+ })?;
+ Ok(unsafe { OwnedFd::from_raw_fd(fd) })
+ }
+
+ fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> {
+ let ptr = unsafe { fdopendir(dir_fd.as_raw_fd()) };
+ if ptr.is_null() {
+ return Err(io::Error::last_os_error());
+ }
+ let dirp = Dir(ptr);
+ // file descriptor is automatically closed by libc::closedir() now, so give up ownership
+ let new_parent_fd = dir_fd.into_raw_fd();
+ // a valid root is not needed because we do not call any functions involving the full path
+ // of the DirEntrys.
+ let dummy_root = PathBuf::new();
+ Ok((
+ ReadDir {
+ inner: Arc::new(InnerReadDir { dirp, root: dummy_root }),
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "fuchsia",
+ target_os = "redox",
+ )))]
+ end_of_stream: false,
+ },
+ new_parent_fd,
+ ))
+ }
+
+ #[cfg(any(
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "haiku",
+ target_os = "vxworks",
+ ))]
+ fn is_dir(_ent: &DirEntry) -> Option<bool> {
+ None
+ }
+
+ #[cfg(not(any(
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "haiku",
+ target_os = "vxworks",
+ )))]
+ fn is_dir(ent: &DirEntry) -> Option<bool> {
+ match ent.entry.d_type {
+ libc::DT_UNKNOWN => None,
+ libc::DT_DIR => Some(true),
+ _ => Some(false),
+ }
+ }
+
+ fn remove_dir_all_recursive(parent_fd: Option<RawFd>, path: &CStr) -> io::Result<()> {
+ // try opening as directory
+ let fd = match openat_nofollow_dironly(parent_fd, &path) {
+ Err(err) if matches!(err.raw_os_error(), Some(libc::ENOTDIR | libc::ELOOP)) => {
+ // not a directory - don't traverse further
+ // (for symlinks, older Linux kernels may return ELOOP instead of ENOTDIR)
+ return match parent_fd {
+ // unlink...
+ Some(parent_fd) => {
+ cvt(unsafe { unlinkat(parent_fd, path.as_ptr(), 0) }).map(drop)
+ }
+ // ...unless this was supposed to be the deletion root directory
+ None => Err(err),
+ };
+ }
+ result => result?,
+ };
+
+ // open the directory passing ownership of the fd
+ let (dir, fd) = fdreaddir(fd)?;
+ for child in dir {
+ let child = child?;
+ let child_name = child.name_cstr();
+ match is_dir(&child) {
+ Some(true) => {
+ remove_dir_all_recursive(Some(fd), child_name)?;
+ }
+ Some(false) => {
+ cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) })?;
+ }
+ None => {
+ // POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed
+ // if the process has the appropriate privileges. This however can causing orphaned
+ // directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing
+ // into it first instead of trying to unlink() it.
+ remove_dir_all_recursive(Some(fd), child_name)?;
+ }
+ }
+ }
+
+ // unlink the directory after removing its contents
+ cvt(unsafe {
+ unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), path.as_ptr(), libc::AT_REMOVEDIR)
+ })?;
+ Ok(())
+ }
+
+ fn remove_dir_all_modern(p: &Path) -> io::Result<()> {
+ // We cannot just call remove_dir_all_recursive() here because that would not delete a passed
+ // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
+ // into symlinks.
+ let attr = lstat(p)?;
+ if attr.file_type().is_symlink() {
+ crate::fs::remove_file(p)
+ } else {
+ remove_dir_all_recursive(None, &cstr(p)?)
+ }
+ }
+
+ #[cfg(not(all(target_os = "macos", not(target_arch = "aarch64"))))]
+ pub fn remove_dir_all(p: &Path) -> io::Result<()> {
+ remove_dir_all_modern(p)
+ }
+
+ #[cfg(all(target_os = "macos", not(target_arch = "aarch64")))]
+ pub fn remove_dir_all(p: &Path) -> io::Result<()> {
+ if macos_weak::has_openat() {
+ // openat() is available with macOS 10.10+, just like unlinkat() and fdopendir()
+ remove_dir_all_modern(p)
+ } else {
+ // fall back to classic implementation
+ crate::sys_common::fs::remove_dir_all(p)
+ }
+ }
+}
diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs
new file mode 100644
index 000000000..8d5b54021
--- /dev/null
+++ b/library/std/src/sys/unix/futex.rs
@@ -0,0 +1,303 @@
+#![cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ all(target_os = "emscripten", target_feature = "atomics"),
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ target_os = "fuchsia",
+))]
+
+use crate::sync::atomic::AtomicU32;
+use crate::time::Duration;
+
+/// Wait for a futex_wake operation to wake us.
+///
+/// Returns directly if the futex doesn't hold the expected value.
+///
+/// Returns false on timeout, and true in all other cases.
+#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
+pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
+ use super::time::Timespec;
+ use crate::ptr::null;
+ use crate::sync::atomic::Ordering::Relaxed;
+
+ // Calculate the timeout as an absolute timespec.
+ //
+ // Overflows are rounded up to an infinite timeout (None).
+ let timespec = timeout
+ .and_then(|d| Timespec::now(libc::CLOCK_MONOTONIC).checked_add_duration(&d))
+ .and_then(|t| t.to_timespec());
+
+ loop {
+ // No need to wait if the value already changed.
+ if futex.load(Relaxed) != expected {
+ return true;
+ }
+
+ let r = unsafe {
+ cfg_if::cfg_if! {
+ if #[cfg(target_os = "freebsd")] {
+ // FreeBSD doesn't have futex(), but it has
+ // _umtx_op(UMTX_OP_WAIT_UINT_PRIVATE), which is nearly
+ // identical. It supports absolute timeouts through a flag
+ // in the _umtx_time struct.
+ let umtx_timeout = timespec.map(|t| libc::_umtx_time {
+ _timeout: t,
+ _flags: libc::UMTX_ABSTIME,
+ _clockid: libc::CLOCK_MONOTONIC as u32,
+ });
+ let umtx_timeout_ptr = umtx_timeout.as_ref().map_or(null(), |t| t as *const _);
+ let umtx_timeout_size = umtx_timeout.as_ref().map_or(0, |t| crate::mem::size_of_val(t));
+ libc::_umtx_op(
+ futex as *const AtomicU32 as *mut _,
+ libc::UMTX_OP_WAIT_UINT_PRIVATE,
+ expected as libc::c_ulong,
+ crate::ptr::invalid_mut(umtx_timeout_size),
+ umtx_timeout_ptr as *mut _,
+ )
+ } else if #[cfg(any(target_os = "linux", target_os = "android"))] {
+ // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
+ // absolute time rather than a relative time.
+ libc::syscall(
+ libc::SYS_futex,
+ futex as *const AtomicU32,
+ libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG,
+ expected,
+ timespec.as_ref().map_or(null(), |t| t as *const libc::timespec),
+ null::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET.
+ !0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT.
+ )
+ } else {
+ compile_error!("unknown target_os");
+ }
+ }
+ };
+
+ match (r < 0).then(super::os::errno) {
+ Some(libc::ETIMEDOUT) => return false,
+ Some(libc::EINTR) => continue,
+ _ => return true,
+ }
+ }
+}
+
+/// Wake up one thread that's blocked on futex_wait on this futex.
+///
+/// Returns true if this actually woke up such a thread,
+/// or false if no thread was waiting on this futex.
+///
+/// On some platforms, this always returns false.
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+ let ptr = futex as *const AtomicU32;
+ let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
+ unsafe { libc::syscall(libc::SYS_futex, ptr, op, 1) > 0 }
+}
+
+/// Wake up all threads that are waiting on futex_wait on this futex.
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn futex_wake_all(futex: &AtomicU32) {
+ let ptr = futex as *const AtomicU32;
+ let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
+ unsafe {
+ libc::syscall(libc::SYS_futex, ptr, op, i32::MAX);
+ }
+}
+
+// FreeBSD doesn't tell us how many threads are woken up, so this always returns false.
+#[cfg(target_os = "freebsd")]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+ use crate::ptr::null_mut;
+ unsafe {
+ libc::_umtx_op(
+ futex as *const AtomicU32 as *mut _,
+ libc::UMTX_OP_WAKE_PRIVATE,
+ 1,
+ null_mut(),
+ null_mut(),
+ )
+ };
+ false
+}
+
+#[cfg(target_os = "freebsd")]
+pub fn futex_wake_all(futex: &AtomicU32) {
+ use crate::ptr::null_mut;
+ unsafe {
+ libc::_umtx_op(
+ futex as *const AtomicU32 as *mut _,
+ libc::UMTX_OP_WAKE_PRIVATE,
+ i32::MAX as libc::c_ulong,
+ null_mut(),
+ null_mut(),
+ )
+ };
+}
+
+#[cfg(target_os = "openbsd")]
+pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
+ use super::time::Timespec;
+ use crate::ptr::{null, null_mut};
+
+ // Overflows are rounded up to an infinite timeout (None).
+ let timespec = timeout
+ .and_then(|d| Timespec::zero().checked_add_duration(&d))
+ .and_then(|t| t.to_timespec());
+
+ let r = unsafe {
+ libc::futex(
+ futex as *const AtomicU32 as *mut u32,
+ libc::FUTEX_WAIT,
+ expected as i32,
+ timespec.as_ref().map_or(null(), |t| t as *const libc::timespec),
+ null_mut(),
+ )
+ };
+
+ r == 0 || super::os::errno() != libc::ETIMEDOUT
+}
+
+#[cfg(target_os = "openbsd")]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+ use crate::ptr::{null, null_mut};
+ unsafe {
+ libc::futex(futex as *const AtomicU32 as *mut u32, libc::FUTEX_WAKE, 1, null(), null_mut())
+ > 0
+ }
+}
+
+#[cfg(target_os = "openbsd")]
+pub fn futex_wake_all(futex: &AtomicU32) {
+ use crate::ptr::{null, null_mut};
+ unsafe {
+ libc::futex(
+ futex as *const AtomicU32 as *mut u32,
+ libc::FUTEX_WAKE,
+ i32::MAX,
+ null(),
+ null_mut(),
+ );
+ }
+}
+
+#[cfg(target_os = "dragonfly")]
+pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
+ // A timeout of 0 means infinite.
+ // We round smaller timeouts up to 1 millisecond.
+ // Overflows are rounded up to an infinite timeout.
+ let timeout_ms =
+ timeout.and_then(|d| Some(i32::try_from(d.as_millis()).ok()?.max(1))).unwrap_or(0);
+
+ let r = unsafe {
+ libc::umtx_sleep(futex as *const AtomicU32 as *const i32, expected as i32, timeout_ms)
+ };
+
+ r == 0 || super::os::errno() != libc::ETIMEDOUT
+}
+
+// DragonflyBSD doesn't tell us how many threads are woken up, so this always returns false.
+#[cfg(target_os = "dragonfly")]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+ unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, 1) };
+ false
+}
+
+#[cfg(target_os = "dragonfly")]
+pub fn futex_wake_all(futex: &AtomicU32) {
+ unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, i32::MAX) };
+}
+
+#[cfg(target_os = "emscripten")]
+extern "C" {
+ fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int;
+ fn emscripten_futex_wait(
+ addr: *const AtomicU32,
+ val: libc::c_uint,
+ max_wait_ms: libc::c_double,
+ ) -> libc::c_int;
+}
+
+#[cfg(target_os = "emscripten")]
+pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
+ unsafe {
+ emscripten_futex_wait(
+ futex,
+ expected,
+ timeout.map_or(f64::INFINITY, |d| d.as_secs_f64() * 1000.0),
+ ) != -libc::ETIMEDOUT
+ }
+}
+
+#[cfg(target_os = "emscripten")]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+ unsafe { emscripten_futex_wake(futex, 1) > 0 }
+}
+
+#[cfg(target_os = "emscripten")]
+pub fn futex_wake_all(futex: &AtomicU32) {
+ unsafe { emscripten_futex_wake(futex, i32::MAX) };
+}
+
+#[cfg(target_os = "fuchsia")]
+pub mod zircon {
+ pub type zx_futex_t = crate::sync::atomic::AtomicU32;
+ pub type zx_handle_t = u32;
+ pub type zx_status_t = i32;
+ pub type zx_time_t = i64;
+
+ pub const ZX_HANDLE_INVALID: zx_handle_t = 0;
+
+ pub const ZX_TIME_INFINITE: zx_time_t = zx_time_t::MAX;
+
+ pub const ZX_OK: zx_status_t = 0;
+ pub const ZX_ERR_INVALID_ARGS: zx_status_t = -10;
+ pub const ZX_ERR_BAD_HANDLE: zx_status_t = -11;
+ pub const ZX_ERR_WRONG_TYPE: zx_status_t = -12;
+ pub const ZX_ERR_BAD_STATE: zx_status_t = -20;
+ pub const ZX_ERR_TIMED_OUT: zx_status_t = -21;
+
+ extern "C" {
+ pub fn zx_clock_get_monotonic() -> zx_time_t;
+ pub fn zx_futex_wait(
+ value_ptr: *const zx_futex_t,
+ current_value: zx_futex_t,
+ new_futex_owner: zx_handle_t,
+ deadline: zx_time_t,
+ ) -> zx_status_t;
+ pub fn zx_futex_wake(value_ptr: *const zx_futex_t, wake_count: u32) -> zx_status_t;
+ pub fn zx_futex_wake_single_owner(value_ptr: *const zx_futex_t) -> zx_status_t;
+ pub fn zx_thread_self() -> zx_handle_t;
+ }
+}
+
+#[cfg(target_os = "fuchsia")]
+pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
+ use crate::convert::TryFrom;
+
+ // Sleep forever if the timeout is longer than fits in a i64.
+ let deadline = timeout
+ .and_then(|d| {
+ i64::try_from(d.as_nanos())
+ .ok()?
+ .checked_add(unsafe { zircon::zx_clock_get_monotonic() })
+ })
+ .unwrap_or(zircon::ZX_TIME_INFINITE);
+
+ unsafe {
+ zircon::zx_futex_wait(futex, AtomicU32::new(expected), zircon::ZX_HANDLE_INVALID, deadline)
+ != zircon::ZX_ERR_TIMED_OUT
+ }
+}
+
+// Fuchsia doesn't tell us how many threads are woken up, so this always returns false.
+#[cfg(target_os = "fuchsia")]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+ unsafe { zircon::zx_futex_wake(futex, 1) };
+ false
+}
+
+#[cfg(target_os = "fuchsia")]
+pub fn futex_wake_all(futex: &AtomicU32) {
+ unsafe { zircon::zx_futex_wake(futex, u32::MAX) };
+}
diff --git a/library/std/src/sys/unix/io.rs b/library/std/src/sys/unix/io.rs
new file mode 100644
index 000000000..deb5ee76b
--- /dev/null
+++ b/library/std/src/sys/unix/io.rs
@@ -0,0 +1,76 @@
+use crate::marker::PhantomData;
+use crate::slice;
+
+use libc::{c_void, iovec};
+
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+pub struct IoSlice<'a> {
+ vec: iovec,
+ _p: PhantomData<&'a [u8]>,
+}
+
+impl<'a> IoSlice<'a> {
+ #[inline]
+ pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
+ IoSlice {
+ vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() },
+ _p: PhantomData,
+ }
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ if self.vec.iov_len < n {
+ panic!("advancing IoSlice beyond its length");
+ }
+
+ unsafe {
+ self.vec.iov_len -= n;
+ self.vec.iov_base = self.vec.iov_base.add(n);
+ }
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
+ }
+}
+
+#[repr(transparent)]
+pub struct IoSliceMut<'a> {
+ vec: iovec,
+ _p: PhantomData<&'a mut [u8]>,
+}
+
+impl<'a> IoSliceMut<'a> {
+ #[inline]
+ pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
+ IoSliceMut {
+ vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() },
+ _p: PhantomData,
+ }
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ if self.vec.iov_len < n {
+ panic!("advancing IoSliceMut beyond its length");
+ }
+
+ unsafe {
+ self.vec.iov_len -= n;
+ self.vec.iov_base = self.vec.iov_base.add(n);
+ }
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
+ }
+
+ #[inline]
+ pub fn as_mut_slice(&mut self) -> &mut [u8] {
+ unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) }
+ }
+}
diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs
new file mode 100644
index 000000000..8f7abb55e
--- /dev/null
+++ b/library/std/src/sys/unix/kernel_copy.rs
@@ -0,0 +1,686 @@
+//! This module contains specializations that can offload `io::copy()` operations on file descriptor
+//! containing types (`File`, `TcpStream`, etc.) to more efficient syscalls than `read(2)` and `write(2)`.
+//!
+//! Specialization is only applied to wholly std-owned types so that user code can't observe
+//! that the `Read` and `Write` traits are not used.
+//!
+//! Since a copy operation involves a reader and writer side where each can consist of different types
+//! and also involve generic wrappers (e.g. `Take`, `BufReader`) it is not practical to specialize
+//! a single method on all possible combinations.
+//!
+//! Instead readers and writers are handled separately by the `CopyRead` and `CopyWrite` specialization
+//! traits and then specialized on by the `Copier::copy` method.
+//!
+//! `Copier` uses the specialization traits to unpack the underlying file descriptors and
+//! additional prerequisites and constraints imposed by the wrapper types.
+//!
+//! Once it has obtained all necessary pieces and brought any wrapper types into a state where they
+//! can be safely bypassed it will attempt to use the `copy_file_range(2)`,
+//! `sendfile(2)` or `splice(2)` syscalls to move data directly between file descriptors.
+//! Since those syscalls have requirements that cannot be fully checked in advance and
+//! gathering additional information about file descriptors would require additional syscalls
+//! anyway it simply attempts to use them one after another (guided by inaccurate hints) to
+//! figure out which one works and and falls back to the generic read-write copy loop if none of them
+//! does.
+//! Once a working syscall is found for a pair of file descriptors it will be called in a loop
+//! until the copy operation is completed.
+//!
+//! Advantages of using these syscalls:
+//!
+//! * fewer context switches since reads and writes are coalesced into a single syscall
+//! and more bytes are transferred per syscall. This translates to higher throughput
+//! and fewer CPU cycles, at least for sufficiently large transfers to amortize the initial probing.
+//! * `copy_file_range` creates reflink copies on CoW filesystems, thus moving less data and
+//! consuming less disk space
+//! * `sendfile` and `splice` can perform zero-copy IO under some circumstances while
+//! a naive copy loop would move every byte through the CPU.
+//!
+//! Drawbacks:
+//!
+//! * copy operations smaller than the default buffer size can under some circumstances, especially
+//! on older kernels, incur more syscalls than the naive approach would. As mentioned above
+//! the syscall selection is guided by hints to minimize this possibility but they are not perfect.
+//! * optimizations only apply to std types. If a user adds a custom wrapper type, e.g. to report
+//! progress, they can hit a performance cliff.
+//! * complexity
+
+use crate::cmp::min;
+use crate::fs::{File, Metadata};
+use crate::io::copy::generic_copy;
+use crate::io::{
+ BufRead, BufReader, BufWriter, Error, Read, Result, StderrLock, StdinLock, StdoutLock, Take,
+ Write,
+};
+use crate::mem::ManuallyDrop;
+use crate::net::TcpStream;
+use crate::os::unix::fs::FileTypeExt;
+use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use crate::os::unix::net::UnixStream;
+use crate::process::{ChildStderr, ChildStdin, ChildStdout};
+use crate::ptr;
+use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
+use crate::sys::cvt;
+use crate::sys::weak::syscall;
+use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV};
+
+#[cfg(test)]
+mod tests;
+
+pub(crate) fn copy_spec<R: Read + ?Sized, W: Write + ?Sized>(
+ read: &mut R,
+ write: &mut W,
+) -> Result<u64> {
+ let copier = Copier { read, write };
+ SpecCopy::copy(copier)
+}
+
+/// This type represents either the inferred `FileType` of a `RawFd` based on the source
+/// type from which it was extracted or the actual metadata
+///
+/// The methods on this type only provide hints, due to `AsRawFd` and `FromRawFd` the inferred
+/// type may be wrong.
+enum FdMeta {
+ /// We obtained the FD from a type that can contain any type of `FileType` and queried the metadata
+ /// because it is cheaper than probing all possible syscalls (reader side)
+ Metadata(Metadata),
+ Socket,
+ Pipe,
+ /// We don't have any metadata, e.g. because the original type was `File` which can represent
+ /// any `FileType` and we did not query the metadata either since it did not seem beneficial
+ /// (writer side)
+ NoneObtained,
+}
+
+impl FdMeta {
+ fn maybe_fifo(&self) -> bool {
+ match self {
+ FdMeta::Metadata(meta) => meta.file_type().is_fifo(),
+ FdMeta::Socket => false,
+ FdMeta::Pipe => true,
+ FdMeta::NoneObtained => true,
+ }
+ }
+
+ fn potential_sendfile_source(&self) -> bool {
+ match self {
+ // procfs erroneously shows 0 length on non-empty readable files.
+ // and if a file is truly empty then a `read` syscall will determine that and skip the write syscall
+ // thus there would be benefit from attempting sendfile
+ FdMeta::Metadata(meta)
+ if meta.file_type().is_file() && meta.len() > 0
+ || meta.file_type().is_block_device() =>
+ {
+ true
+ }
+ _ => false,
+ }
+ }
+
+ fn copy_file_range_candidate(&self) -> bool {
+ match self {
+ // copy_file_range will fail on empty procfs files. `read` can determine whether EOF has been reached
+ // without extra cost and skip the write, thus there is no benefit in attempting copy_file_range
+ FdMeta::Metadata(meta) if meta.is_file() && meta.len() > 0 => true,
+ FdMeta::NoneObtained => true,
+ _ => false,
+ }
+ }
+}
+
+struct CopyParams(FdMeta, Option<RawFd>);
+
+struct Copier<'a, 'b, R: Read + ?Sized, W: Write + ?Sized> {
+ read: &'a mut R,
+ write: &'b mut W,
+}
+
+trait SpecCopy {
+ fn copy(self) -> Result<u64>;
+}
+
+impl<R: Read + ?Sized, W: Write + ?Sized> SpecCopy for Copier<'_, '_, R, W> {
+ default fn copy(self) -> Result<u64> {
+ generic_copy(self.read, self.write)
+ }
+}
+
+impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
+ fn copy(self) -> Result<u64> {
+ let (reader, writer) = (self.read, self.write);
+ let r_cfg = reader.properties();
+ let w_cfg = writer.properties();
+
+ // before direct operations on file descriptors ensure that all source and sink buffers are empty
+ let mut flush = || -> crate::io::Result<u64> {
+ let bytes = reader.drain_to(writer, u64::MAX)?;
+ // BufWriter buffered bytes have already been accounted for in earlier write() calls
+ writer.flush()?;
+ Ok(bytes)
+ };
+
+ let mut written = 0u64;
+
+ if let (CopyParams(input_meta, Some(readfd)), CopyParams(output_meta, Some(writefd))) =
+ (r_cfg, w_cfg)
+ {
+ written += flush()?;
+ let max_write = reader.min_limit();
+
+ if input_meta.copy_file_range_candidate() && output_meta.copy_file_range_candidate() {
+ let result = copy_regular_files(readfd, writefd, max_write);
+ result.update_take(reader);
+
+ match result {
+ CopyResult::Ended(bytes_copied) => return Ok(bytes_copied + written),
+ CopyResult::Error(e, _) => return Err(e),
+ CopyResult::Fallback(bytes) => written += bytes,
+ }
+ }
+
+ // on modern kernels sendfile can copy from any mmapable type (some but not all regular files and block devices)
+ // to any writable file descriptor. On older kernels the writer side can only be a socket.
+ // So we just try and fallback if needed.
+ // If current file offsets + write sizes overflow it may also fail, we do not try to fix that and instead
+ // fall back to the generic copy loop.
+ if input_meta.potential_sendfile_source() {
+ let result = sendfile_splice(SpliceMode::Sendfile, readfd, writefd, max_write);
+ result.update_take(reader);
+
+ match result {
+ CopyResult::Ended(bytes_copied) => return Ok(bytes_copied + written),
+ CopyResult::Error(e, _) => return Err(e),
+ CopyResult::Fallback(bytes) => written += bytes,
+ }
+ }
+
+ if input_meta.maybe_fifo() || output_meta.maybe_fifo() {
+ let result = sendfile_splice(SpliceMode::Splice, readfd, writefd, max_write);
+ result.update_take(reader);
+
+ match result {
+ CopyResult::Ended(bytes_copied) => return Ok(bytes_copied + written),
+ CopyResult::Error(e, _) => return Err(e),
+ CopyResult::Fallback(0) => { /* use the fallback below */ }
+ CopyResult::Fallback(_) => {
+ unreachable!("splice should not return > 0 bytes on the fallback path")
+ }
+ }
+ }
+ }
+
+ // fallback if none of the more specialized syscalls wants to work with these file descriptors
+ match generic_copy(reader, writer) {
+ Ok(bytes) => Ok(bytes + written),
+ err => err,
+ }
+ }
+}
+
+#[rustc_specialization_trait]
+trait CopyRead: Read {
+ /// Implementations that contain buffers (i.e. `BufReader`) must transfer data from their internal
+ /// buffers into `writer` until either the buffers are emptied or `limit` bytes have been
+ /// transferred, whichever occurs sooner.
+ /// If nested buffers are present the outer buffers must be drained first.
+ ///
+ /// This is necessary to directly bypass the wrapper types while preserving the data order
+ /// when operating directly on the underlying file descriptors.
+ fn drain_to<W: Write>(&mut self, _writer: &mut W, _limit: u64) -> Result<u64> {
+ Ok(0)
+ }
+
+ /// Updates `Take` wrappers to remove the number of bytes copied.
+ fn taken(&mut self, _bytes: u64) {}
+
+ /// The minimum of the limit of all `Take<_>` wrappers, `u64::MAX` otherwise.
+ /// This method does not account for data `BufReader` buffers and would underreport
+ /// the limit of a `Take<BufReader<Take<_>>>` type. Thus its result is only valid
+ /// after draining the buffers via `drain_to`.
+ fn min_limit(&self) -> u64 {
+ u64::MAX
+ }
+
+ /// Extracts the file descriptor and hints/metadata, delegating through wrappers if necessary.
+ fn properties(&self) -> CopyParams;
+}
+
+#[rustc_specialization_trait]
+trait CopyWrite: Write {
+ /// Extracts the file descriptor and hints/metadata, delegating through wrappers if necessary.
+ fn properties(&self) -> CopyParams;
+}
+
+impl<T> CopyRead for &mut T
+where
+ T: CopyRead,
+{
+ fn drain_to<W: Write>(&mut self, writer: &mut W, limit: u64) -> Result<u64> {
+ (**self).drain_to(writer, limit)
+ }
+
+ fn taken(&mut self, bytes: u64) {
+ (**self).taken(bytes);
+ }
+
+ fn min_limit(&self) -> u64 {
+ (**self).min_limit()
+ }
+
+ fn properties(&self) -> CopyParams {
+ (**self).properties()
+ }
+}
+
+impl<T> CopyWrite for &mut T
+where
+ T: CopyWrite,
+{
+ fn properties(&self) -> CopyParams {
+ (**self).properties()
+ }
+}
+
+impl CopyRead for File {
+ fn properties(&self) -> CopyParams {
+ CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyRead for &File {
+ fn properties(&self) -> CopyParams {
+ CopyParams(fd_to_meta(*self), Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyWrite for File {
+ fn properties(&self) -> CopyParams {
+ CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyWrite for &File {
+ fn properties(&self) -> CopyParams {
+ CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyRead for TcpStream {
+ fn properties(&self) -> CopyParams {
+ // avoid the stat syscall since we can be fairly sure it's a socket
+ CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyRead for &TcpStream {
+ fn properties(&self) -> CopyParams {
+ // avoid the stat syscall since we can be fairly sure it's a socket
+ CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyWrite for TcpStream {
+ fn properties(&self) -> CopyParams {
+ // avoid the stat syscall since we can be fairly sure it's a socket
+ CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyWrite for &TcpStream {
+ fn properties(&self) -> CopyParams {
+ // avoid the stat syscall since we can be fairly sure it's a socket
+ CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyRead for UnixStream {
+ fn properties(&self) -> CopyParams {
+ // avoid the stat syscall since we can be fairly sure it's a socket
+ CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyRead for &UnixStream {
+ fn properties(&self) -> CopyParams {
+ // avoid the stat syscall since we can be fairly sure it's a socket
+ CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyWrite for UnixStream {
+ fn properties(&self) -> CopyParams {
+ // avoid the stat syscall since we can be fairly sure it's a socket
+ CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyWrite for &UnixStream {
+ fn properties(&self) -> CopyParams {
+ // avoid the stat syscall since we can be fairly sure it's a socket
+ CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyWrite for ChildStdin {
+ fn properties(&self) -> CopyParams {
+ CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyRead for ChildStdout {
+ fn properties(&self) -> CopyParams {
+ CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyRead for ChildStderr {
+ fn properties(&self) -> CopyParams {
+ CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyRead for StdinLock<'_> {
+ fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
+ let buf_reader = self.as_mut_buf();
+ let buf = buf_reader.buffer();
+ let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
+ let bytes_drained = buf.len();
+ writer.write_all(buf)?;
+ buf_reader.consume(bytes_drained);
+
+ Ok(bytes_drained as u64)
+ }
+
+ fn properties(&self) -> CopyParams {
+ CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyWrite for StdoutLock<'_> {
+ fn properties(&self) -> CopyParams {
+ CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd()))
+ }
+}
+
+impl CopyWrite for StderrLock<'_> {
+ fn properties(&self) -> CopyParams {
+ CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd()))
+ }
+}
+
+impl<T: CopyRead> CopyRead for Take<T> {
+ fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
+ let local_limit = self.limit();
+ let combined_limit = min(outer_limit, local_limit);
+ let bytes_drained = self.get_mut().drain_to(writer, combined_limit)?;
+ // update limit since read() was bypassed
+ self.set_limit(local_limit - bytes_drained);
+
+ Ok(bytes_drained)
+ }
+
+ fn taken(&mut self, bytes: u64) {
+ self.set_limit(self.limit() - bytes);
+ self.get_mut().taken(bytes);
+ }
+
+ fn min_limit(&self) -> u64 {
+ min(Take::limit(self), self.get_ref().min_limit())
+ }
+
+ fn properties(&self) -> CopyParams {
+ self.get_ref().properties()
+ }
+}
+
+impl<T: CopyRead> CopyRead for BufReader<T> {
+ fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
+ let buf = self.buffer();
+ let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
+ let bytes = buf.len();
+ writer.write_all(buf)?;
+ self.consume(bytes);
+
+ let remaining = outer_limit - bytes as u64;
+
+ // in case of nested bufreaders we also need to drain the ones closer to the source
+ let inner_bytes = self.get_mut().drain_to(writer, remaining)?;
+
+ Ok(bytes as u64 + inner_bytes)
+ }
+
+ fn taken(&mut self, bytes: u64) {
+ self.get_mut().taken(bytes);
+ }
+
+ fn min_limit(&self) -> u64 {
+ self.get_ref().min_limit()
+ }
+
+ fn properties(&self) -> CopyParams {
+ self.get_ref().properties()
+ }
+}
+
+impl<T: CopyWrite> CopyWrite for BufWriter<T> {
+ fn properties(&self) -> CopyParams {
+ self.get_ref().properties()
+ }
+}
+
+fn fd_to_meta<T: AsRawFd>(fd: &T) -> FdMeta {
+ let fd = fd.as_raw_fd();
+ let file: ManuallyDrop<File> = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });
+ match file.metadata() {
+ Ok(meta) => FdMeta::Metadata(meta),
+ Err(_) => FdMeta::NoneObtained,
+ }
+}
+
+pub(super) enum CopyResult {
+ Ended(u64),
+ Error(Error, u64),
+ Fallback(u64),
+}
+
+impl CopyResult {
+ fn update_take(&self, reader: &mut impl CopyRead) {
+ match *self {
+ CopyResult::Fallback(bytes)
+ | CopyResult::Ended(bytes)
+ | CopyResult::Error(_, bytes) => reader.taken(bytes),
+ }
+ }
+}
+
+/// Invalid file descriptor.
+///
+/// Valid file descriptors are guaranteed to be positive numbers (see `open()` manpage)
+/// while negative values are used to indicate errors.
+/// Thus -1 will never be overlap with a valid open file.
+const INVALID_FD: RawFd = -1;
+
+/// Linux-specific implementation that will attempt to use copy_file_range for copy offloading.
+/// As the name says, it only works on regular files.
+///
+/// Callers must handle fallback to a generic copy loop.
+/// `Fallback` may indicate non-zero number of bytes already written
+/// if one of the files' cursor +`max_len` would exceed u64::MAX (`EOVERFLOW`).
+pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> CopyResult {
+ use crate::cmp;
+
+ const NOT_PROBED: u8 = 0;
+ const UNAVAILABLE: u8 = 1;
+ const AVAILABLE: u8 = 2;
+
+ // Kernel prior to 4.5 don't have copy_file_range
+ // We store the availability in a global to avoid unnecessary syscalls
+ static HAS_COPY_FILE_RANGE: AtomicU8 = AtomicU8::new(NOT_PROBED);
+
+ syscall! {
+ 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::ssize_t
+ }
+
+ match HAS_COPY_FILE_RANGE.load(Ordering::Relaxed) {
+ NOT_PROBED => {
+ // EPERM can indicate seccomp filters or an immutable file.
+ // To distinguish these cases we probe with invalid file descriptors which should result in EBADF if the syscall is supported
+ // and some other error (ENOSYS or EPERM) if it's not available
+ let result = unsafe {
+ cvt(copy_file_range(INVALID_FD, ptr::null_mut(), INVALID_FD, ptr::null_mut(), 1, 0))
+ };
+
+ if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(EBADF))) {
+ HAS_COPY_FILE_RANGE.store(AVAILABLE, Ordering::Relaxed);
+ } else {
+ HAS_COPY_FILE_RANGE.store(UNAVAILABLE, Ordering::Relaxed);
+ return CopyResult::Fallback(0);
+ }
+ }
+ UNAVAILABLE => return CopyResult::Fallback(0),
+ _ => {}
+ };
+
+ let mut written = 0u64;
+ while written < max_len {
+ let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64);
+ // cap to 1GB chunks in case u64::MAX is passed as max_len and the file has a non-zero seek position
+ // this allows us to copy large chunks without hitting EOVERFLOW,
+ // unless someone sets a file offset close to u64::MAX - 1GB, in which case a fallback would be required
+ let bytes_to_copy = cmp::min(bytes_to_copy as usize, 0x4000_0000usize);
+ 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, ptr::null_mut(), writer, ptr::null_mut(), bytes_to_copy, 0))
+ };
+
+ match copy_result {
+ Ok(0) if written == 0 => {
+ // fallback to work around several kernel bugs where copy_file_range will fail to
+ // copy any bytes and return 0 instead of an error if
+ // - reading virtual files from the proc filesystem which appear to have 0 size
+ // but are not empty. noted in coreutils to affect kernels at least up to 5.6.19.
+ // - copying from an overlay filesystem in docker. reported to occur on fedora 32.
+ return CopyResult::Fallback(0);
+ }
+ Ok(0) => return CopyResult::Ended(written), // reached EOF
+ Ok(ret) => written += ret as u64,
+ Err(err) => {
+ return match err.raw_os_error() {
+ // when file offset + max_length > u64::MAX
+ Some(EOVERFLOW) => CopyResult::Fallback(written),
+ Some(ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF) if written == 0 => {
+ // Try fallback io::copy if either:
+ // - Kernel version is < 4.5 (ENOSYS¹)
+ // - Files are mounted on different fs (EXDEV)
+ // - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP)
+ // - copy_file_range file is immutable or syscall is blocked by seccomp¹ (EPERM)
+ // - copy_file_range cannot be used with pipes or device nodes (EINVAL)
+ // - the writer fd was opened with O_APPEND (EBADF²)
+ // and no bytes were written successfully yet. (All these errnos should
+ // not be returned if something was already written, but they happen in
+ // the wild, see #91152.)
+ //
+ // ¹ these cases should be detected by the initial probe but we handle them here
+ // anyway in case syscall interception changes during runtime
+ // ² actually invalid file descriptors would cause this too, but in that case
+ // the fallback code path is expected to encounter the same error again
+ CopyResult::Fallback(0)
+ }
+ _ => CopyResult::Error(err, written),
+ };
+ }
+ }
+ }
+ CopyResult::Ended(written)
+}
+
+#[derive(PartialEq)]
+enum SpliceMode {
+ Sendfile,
+ Splice,
+}
+
+/// performs splice or sendfile between file descriptors
+/// Does _not_ fall back to a generic copy loop.
+fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> CopyResult {
+ static HAS_SENDFILE: AtomicBool = AtomicBool::new(true);
+ static HAS_SPLICE: AtomicBool = AtomicBool::new(true);
+
+ // Android builds use feature level 14, but the libc wrapper for splice is
+ // gated on feature level 21+, so we have to invoke the syscall directly.
+ #[cfg(target_os = "android")]
+ syscall! {
+ fn splice(
+ srcfd: libc::c_int,
+ src_offset: *const i64,
+ dstfd: libc::c_int,
+ dst_offset: *const i64,
+ len: libc::size_t,
+ flags: libc::c_int
+ ) -> libc::ssize_t
+ }
+
+ #[cfg(target_os = "linux")]
+ use libc::splice;
+
+ match mode {
+ SpliceMode::Sendfile if !HAS_SENDFILE.load(Ordering::Relaxed) => {
+ return CopyResult::Fallback(0);
+ }
+ SpliceMode::Splice if !HAS_SPLICE.load(Ordering::Relaxed) => {
+ return CopyResult::Fallback(0);
+ }
+ _ => (),
+ }
+
+ let mut written = 0u64;
+ while written < len {
+ // according to its manpage that's the maximum size sendfile() will copy per invocation
+ let chunk_size = crate::cmp::min(len - written, 0x7ffff000_u64) as usize;
+
+ let result = match mode {
+ SpliceMode::Sendfile => {
+ cvt(unsafe { libc::sendfile(writer, reader, ptr::null_mut(), chunk_size) })
+ }
+ SpliceMode::Splice => cvt(unsafe {
+ splice(reader, ptr::null_mut(), writer, ptr::null_mut(), chunk_size, 0)
+ }),
+ };
+
+ match result {
+ Ok(0) => break, // EOF
+ Ok(ret) => written += ret as u64,
+ Err(err) => {
+ return match err.raw_os_error() {
+ Some(ENOSYS | EPERM) => {
+ // syscall not supported (ENOSYS)
+ // syscall is disallowed, e.g. by seccomp (EPERM)
+ match mode {
+ SpliceMode::Sendfile => HAS_SENDFILE.store(false, Ordering::Relaxed),
+ SpliceMode::Splice => HAS_SPLICE.store(false, Ordering::Relaxed),
+ }
+ assert_eq!(written, 0);
+ CopyResult::Fallback(0)
+ }
+ Some(EINVAL) => {
+ // splice/sendfile do not support this particular file descriptor (EINVAL)
+ assert_eq!(written, 0);
+ CopyResult::Fallback(0)
+ }
+ Some(os_err) if mode == SpliceMode::Sendfile && os_err == EOVERFLOW => {
+ CopyResult::Fallback(written)
+ }
+ _ => CopyResult::Error(err, written),
+ };
+ }
+ }
+ }
+ CopyResult::Ended(written)
+}
diff --git a/library/std/src/sys/unix/kernel_copy/tests.rs b/library/std/src/sys/unix/kernel_copy/tests.rs
new file mode 100644
index 000000000..3fe849e23
--- /dev/null
+++ b/library/std/src/sys/unix/kernel_copy/tests.rs
@@ -0,0 +1,270 @@
+use crate::fs::OpenOptions;
+use crate::io;
+use crate::io::Result;
+use crate::io::SeekFrom;
+use crate::io::{BufRead, Read, Seek, Write};
+use crate::os::unix::io::AsRawFd;
+use crate::sys_common::io::test::tmpdir;
+
+#[test]
+fn copy_specialization() -> Result<()> {
+ use crate::io::{BufReader, BufWriter};
+
+ let tmp_path = tmpdir();
+ let source_path = tmp_path.join("copy-spec.source");
+ let sink_path = tmp_path.join("copy-spec.sink");
+
+ let result: Result<()> = try {
+ let mut source = crate::fs::OpenOptions::new()
+ .read(true)
+ .write(true)
+ .create(true)
+ .truncate(true)
+ .open(&source_path)?;
+ source.write_all(b"abcdefghiklmnopqr")?;
+ source.seek(SeekFrom::Start(8))?;
+ let mut source = BufReader::with_capacity(8, source.take(5));
+ source.fill_buf()?;
+ assert_eq!(source.buffer(), b"iklmn");
+ source.get_mut().set_limit(6);
+ source.get_mut().get_mut().seek(SeekFrom::Start(1))?; // "bcdefg"
+ let mut source = source.take(10); // "iklmnbcdef"
+
+ let mut sink = crate::fs::OpenOptions::new()
+ .read(true)
+ .write(true)
+ .create(true)
+ .truncate(true)
+ .open(&sink_path)?;
+ sink.write_all(b"000000")?;
+ let mut sink = BufWriter::with_capacity(5, sink);
+ sink.write_all(b"wxyz")?;
+ assert_eq!(sink.buffer(), b"wxyz");
+
+ let copied = crate::io::copy(&mut source, &mut sink)?;
+ assert_eq!(copied, 10, "copy obeyed limit imposed by Take");
+ assert_eq!(sink.buffer().len(), 0, "sink buffer was flushed");
+ assert_eq!(source.limit(), 0, "outer Take was exhausted");
+ assert_eq!(source.get_ref().buffer().len(), 0, "source buffer should be drained");
+ assert_eq!(
+ source.get_ref().get_ref().limit(),
+ 1,
+ "inner Take allowed reading beyond end of file, some bytes should be left"
+ );
+
+ let mut sink = sink.into_inner()?;
+ sink.seek(SeekFrom::Start(0))?;
+ let mut copied = Vec::new();
+ sink.read_to_end(&mut copied)?;
+ assert_eq!(&copied, b"000000wxyziklmnbcdef");
+ };
+
+ let rm1 = crate::fs::remove_file(source_path);
+ let rm2 = crate::fs::remove_file(sink_path);
+
+ result.and(rm1).and(rm2)
+}
+
+#[test]
+fn copies_append_mode_sink() -> Result<()> {
+ let tmp_path = tmpdir();
+ let source_path = tmp_path.join("copies_append_mode.source");
+ let sink_path = tmp_path.join("copies_append_mode.sink");
+ let mut source =
+ OpenOptions::new().create(true).truncate(true).write(true).read(true).open(&source_path)?;
+ write!(source, "not empty")?;
+ source.seek(SeekFrom::Start(0))?;
+ let mut sink = OpenOptions::new().create(true).append(true).open(&sink_path)?;
+
+ let copied = crate::io::copy(&mut source, &mut sink)?;
+
+ assert_eq!(copied, 9);
+
+ Ok(())
+}
+
+#[bench]
+fn bench_file_to_file_copy(b: &mut test::Bencher) {
+ const BYTES: usize = 128 * 1024;
+ let temp_path = tmpdir();
+ let src_path = temp_path.join("file-copy-bench-src");
+ let mut src = crate::fs::OpenOptions::new()
+ .create(true)
+ .truncate(true)
+ .read(true)
+ .write(true)
+ .open(src_path)
+ .unwrap();
+ src.write(&vec![0u8; BYTES]).unwrap();
+
+ let sink_path = temp_path.join("file-copy-bench-sink");
+ let mut sink = crate::fs::OpenOptions::new()
+ .create(true)
+ .truncate(true)
+ .write(true)
+ .open(sink_path)
+ .unwrap();
+
+ b.bytes = BYTES as u64;
+ b.iter(|| {
+ src.seek(SeekFrom::Start(0)).unwrap();
+ sink.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(BYTES as u64, io::copy(&mut src, &mut sink).unwrap());
+ });
+}
+
+#[bench]
+fn bench_file_to_socket_copy(b: &mut test::Bencher) {
+ const BYTES: usize = 128 * 1024;
+ let temp_path = tmpdir();
+ let src_path = temp_path.join("pipe-copy-bench-src");
+ let mut src = OpenOptions::new()
+ .create(true)
+ .truncate(true)
+ .read(true)
+ .write(true)
+ .open(src_path)
+ .unwrap();
+ src.write(&vec![0u8; BYTES]).unwrap();
+
+ let sink_drainer = crate::net::TcpListener::bind("localhost:0").unwrap();
+ let mut sink = crate::net::TcpStream::connect(sink_drainer.local_addr().unwrap()).unwrap();
+ let mut sink_drainer = sink_drainer.accept().unwrap().0;
+
+ crate::thread::spawn(move || {
+ let mut sink_buf = vec![0u8; 1024 * 1024];
+ loop {
+ sink_drainer.read(&mut sink_buf[..]).unwrap();
+ }
+ });
+
+ b.bytes = BYTES as u64;
+ b.iter(|| {
+ src.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(BYTES as u64, io::copy(&mut src, &mut sink).unwrap());
+ });
+}
+
+#[bench]
+fn bench_file_to_uds_copy(b: &mut test::Bencher) {
+ const BYTES: usize = 128 * 1024;
+ let temp_path = tmpdir();
+ let src_path = temp_path.join("uds-copy-bench-src");
+ let mut src = OpenOptions::new()
+ .create(true)
+ .truncate(true)
+ .read(true)
+ .write(true)
+ .open(src_path)
+ .unwrap();
+ src.write(&vec![0u8; BYTES]).unwrap();
+
+ let (mut sink, mut sink_drainer) = crate::os::unix::net::UnixStream::pair().unwrap();
+
+ crate::thread::spawn(move || {
+ let mut sink_buf = vec![0u8; 1024 * 1024];
+ loop {
+ sink_drainer.read(&mut sink_buf[..]).unwrap();
+ }
+ });
+
+ b.bytes = BYTES as u64;
+ b.iter(|| {
+ src.seek(SeekFrom::Start(0)).unwrap();
+ assert_eq!(BYTES as u64, io::copy(&mut src, &mut sink).unwrap());
+ });
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[bench]
+fn bench_socket_pipe_socket_copy(b: &mut test::Bencher) {
+ use super::CopyResult;
+ use crate::io::ErrorKind;
+ use crate::process::{ChildStdin, ChildStdout};
+ use crate::sys_common::FromInner;
+
+ let (read_end, write_end) = crate::sys::pipe::anon_pipe().unwrap();
+
+ let mut read_end = ChildStdout::from_inner(read_end);
+ let write_end = ChildStdin::from_inner(write_end);
+
+ let acceptor = crate::net::TcpListener::bind("localhost:0").unwrap();
+ let mut remote_end = crate::net::TcpStream::connect(acceptor.local_addr().unwrap()).unwrap();
+
+ let local_end = crate::sync::Arc::new(acceptor.accept().unwrap().0);
+
+ // the data flow in this benchmark:
+ //
+ // socket(tx) local_source
+ // remote_end (write) +--------> (splice to)
+ // write_end
+ // +
+ // |
+ // | pipe
+ // v
+ // read_end
+ // remote_end (read) <---------+ (splice to) *
+ // socket(rx) local_end
+ //
+ // * benchmark loop using io::copy
+
+ crate::thread::spawn(move || {
+ let mut sink_buf = vec![0u8; 1024 * 1024];
+ remote_end.set_nonblocking(true).unwrap();
+ loop {
+ match remote_end.write(&mut sink_buf[..]) {
+ Err(err) if err.kind() == ErrorKind::WouldBlock => {}
+ Ok(_) => {}
+ err => {
+ err.expect("write failed");
+ }
+ };
+ match remote_end.read(&mut sink_buf[..]) {
+ Err(err) if err.kind() == ErrorKind::WouldBlock => {}
+ Ok(_) => {}
+ err => {
+ err.expect("read failed");
+ }
+ };
+ }
+ });
+
+ // check that splice works, otherwise the benchmark would hang
+ let probe = super::sendfile_splice(
+ super::SpliceMode::Splice,
+ local_end.as_raw_fd(),
+ write_end.as_raw_fd(),
+ 1,
+ );
+
+ match probe {
+ CopyResult::Ended(1) => {
+ // splice works
+ }
+ _ => {
+ eprintln!("splice failed, skipping benchmark");
+ return;
+ }
+ }
+
+ let local_source = local_end.clone();
+ crate::thread::spawn(move || {
+ loop {
+ super::sendfile_splice(
+ super::SpliceMode::Splice,
+ local_source.as_raw_fd(),
+ write_end.as_raw_fd(),
+ u64::MAX,
+ );
+ }
+ });
+
+ const BYTES: usize = 128 * 1024;
+ b.bytes = BYTES as u64;
+ b.iter(|| {
+ assert_eq!(
+ BYTES as u64,
+ io::copy(&mut (&mut read_end).take(BYTES as u64), &mut &*local_end).unwrap()
+ );
+ });
+}
diff --git a/library/std/src/sys/unix/l4re.rs b/library/std/src/sys/unix/l4re.rs
new file mode 100644
index 000000000..996758893
--- /dev/null
+++ b/library/std/src/sys/unix/l4re.rs
@@ -0,0 +1,551 @@
+macro_rules! unimpl {
+ () => {
+ return Err(io::const_io_error!(
+ io::ErrorKind::Unsupported,
+ "No networking available on L4Re.",
+ ));
+ };
+}
+
+pub mod net {
+ #![allow(warnings)]
+ use crate::fmt;
+ use crate::io::{self, IoSlice, IoSliceMut};
+ use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+ use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
+ use crate::sys::fd::FileDesc;
+ use crate::sys_common::{AsInner, FromInner, IntoInner};
+ use crate::time::Duration;
+
+ #[allow(unused_extern_crates)]
+ 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 read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn is_read_vectored(&self) -> bool {
+ false
+ }
+
+ 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 write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn is_write_vectored(&self) -> bool {
+ false
+ }
+
+ 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_linger(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ 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!();
+ }
+
+ // This is used by sys_common code to abstract over Windows and Unix.
+ pub fn as_raw(&self) -> RawFd {
+ self.as_raw_fd()
+ }
+ }
+
+ impl AsInner<FileDesc> for Socket {
+ fn as_inner(&self) -> &FileDesc {
+ &self.0
+ }
+ }
+
+ impl FromInner<FileDesc> for Socket {
+ fn from_inner(file_desc: FileDesc) -> Socket {
+ Socket(file_desc)
+ }
+ }
+
+ impl IntoInner<FileDesc> for Socket {
+ fn into_inner(self) -> FileDesc {
+ self.0
+ }
+ }
+
+ impl AsFd for Socket {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+ }
+
+ impl AsRawFd for Socket {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+ }
+
+ impl IntoRawFd for Socket {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+ }
+
+ impl FromRawFd for Socket {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ Self(FromRawFd::from_raw_fd(raw_fd))
+ }
+ }
+
+ pub struct TcpStream {
+ inner: Socket,
+ }
+
+ impl TcpStream {
+ pub fn connect(_: io::Result<&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 read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn is_read_vectored(&self) -> bool {
+ false
+ }
+
+ pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn is_write_vectored(&self) -> bool {
+ false
+ }
+
+ 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_linger(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ 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(_: io::Result<&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(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+ unimpl!();
+ }
+
+ pub fn socket(&self) -> &Socket {
+ &self.inner
+ }
+
+ pub fn into_socket(self) -> Socket {
+ self.inner
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ unimpl!();
+ }
+
+ 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, _: io::Result<&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
+ }
+ }
+
+ impl LookupHost {
+ pub fn port(&self) -> u16 {
+ 0 // unimplemented
+ }
+ }
+
+ unsafe impl Sync for LookupHost {}
+ unsafe impl Send for LookupHost {}
+
+ impl TryFrom<&str> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(_v: &str) -> io::Result<LookupHost> {
+ unimpl!();
+ }
+ }
+
+ impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> {
+ unimpl!();
+ }
+ }
+}
diff --git a/library/std/src/sys/unix/locks/fuchsia_mutex.rs b/library/std/src/sys/unix/locks/fuchsia_mutex.rs
new file mode 100644
index 000000000..ce427599c
--- /dev/null
+++ b/library/std/src/sys/unix/locks/fuchsia_mutex.rs
@@ -0,0 +1,165 @@
+//! A priority inheriting mutex for Fuchsia.
+//!
+//! This is a port of the [mutex in Fuchsia's libsync]. Contrary to the original,
+//! it does not abort the process when reentrant locking is detected, but deadlocks.
+//!
+//! Priority inheritance is achieved by storing the owning thread's handle in an
+//! atomic variable. Fuchsia's futex operations support setting an owner thread
+//! for a futex, which can boost that thread's priority while the futex is waited
+//! upon.
+//!
+//! libsync is licenced under the following BSD-style licence:
+//!
+//! Copyright 2016 The Fuchsia Authors.
+//!
+//! Redistribution and use in source and binary forms, with or without
+//! modification, are permitted provided that the following conditions are
+//! met:
+//!
+//! * Redistributions of source code must retain the above copyright
+//! notice, this list of conditions and the following disclaimer.
+//! * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+//! OWNER 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.
+//!
+//! [mutex in Fuchsia's libsync]: https://cs.opensource.google/fuchsia/fuchsia/+/main:zircon/system/ulib/sync/mutex.c
+
+use crate::sync::atomic::{
+ AtomicU32,
+ Ordering::{Acquire, Relaxed, Release},
+};
+use crate::sys::futex::zircon::{
+ zx_futex_wait, zx_futex_wake_single_owner, zx_handle_t, zx_thread_self, ZX_ERR_BAD_HANDLE,
+ ZX_ERR_BAD_STATE, ZX_ERR_INVALID_ARGS, ZX_ERR_TIMED_OUT, ZX_ERR_WRONG_TYPE, ZX_OK,
+ ZX_TIME_INFINITE,
+};
+
+// The lowest two bits of a `zx_handle_t` are always set, so the lowest bit is used to mark the
+// mutex as contested by clearing it.
+const CONTESTED_BIT: u32 = 1;
+// This can never be a valid `zx_handle_t`.
+const UNLOCKED: u32 = 0;
+
+pub type MovableMutex = Mutex;
+
+pub struct Mutex {
+ futex: AtomicU32,
+}
+
+#[inline]
+fn to_state(owner: zx_handle_t) -> u32 {
+ owner
+}
+
+#[inline]
+fn to_owner(state: u32) -> zx_handle_t {
+ state | CONTESTED_BIT
+}
+
+#[inline]
+fn is_contested(state: u32) -> bool {
+ state & CONTESTED_BIT == 0
+}
+
+#[inline]
+fn mark_contested(state: u32) -> u32 {
+ state & !CONTESTED_BIT
+}
+
+impl Mutex {
+ #[inline]
+ pub const fn new() -> Mutex {
+ Mutex { futex: AtomicU32::new(UNLOCKED) }
+ }
+
+ #[inline]
+ pub unsafe fn init(&mut self) {}
+
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ let thread_self = zx_thread_self();
+ self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed).is_ok()
+ }
+
+ #[inline]
+ pub unsafe fn lock(&self) {
+ let thread_self = zx_thread_self();
+ if let Err(state) =
+ self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed)
+ {
+ self.lock_contested(state, thread_self);
+ }
+ }
+
+ #[cold]
+ fn lock_contested(&self, mut state: u32, thread_self: zx_handle_t) {
+ let owned_state = mark_contested(to_state(thread_self));
+ loop {
+ // Mark the mutex as contested if it is not already.
+ let contested = mark_contested(state);
+ if is_contested(state)
+ || self.futex.compare_exchange(state, contested, Relaxed, Relaxed).is_ok()
+ {
+ // The mutex has been marked as contested, wait for the state to change.
+ unsafe {
+ match zx_futex_wait(
+ &self.futex,
+ AtomicU32::new(contested),
+ to_owner(state),
+ ZX_TIME_INFINITE,
+ ) {
+ ZX_OK | ZX_ERR_BAD_STATE | ZX_ERR_TIMED_OUT => (),
+ // Note that if a thread handle is reused after its associated thread
+ // exits without unlocking the mutex, an arbitrary thread's priority
+ // could be boosted by the wait, but there is currently no way to
+ // prevent that.
+ ZX_ERR_INVALID_ARGS | ZX_ERR_BAD_HANDLE | ZX_ERR_WRONG_TYPE => {
+ panic!(
+ "either the current thread is trying to lock a mutex it has
+ already locked, or the previous owner did not unlock the mutex
+ before exiting"
+ )
+ }
+ error => panic!("unexpected error in zx_futex_wait: {error}"),
+ }
+ }
+ }
+
+ // The state has changed or a wakeup occured, try to lock the mutex.
+ match self.futex.compare_exchange(UNLOCKED, owned_state, Acquire, Relaxed) {
+ Ok(_) => return,
+ Err(updated) => state = updated,
+ }
+ }
+ }
+
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ if is_contested(self.futex.swap(UNLOCKED, Release)) {
+ // The woken thread will mark the mutex as contested again,
+ // and return here, waking until there are no waiters left,
+ // in which case this is a noop.
+ self.wake();
+ }
+ }
+
+ #[cold]
+ fn wake(&self) {
+ unsafe {
+ zx_futex_wake_single_owner(&self.futex);
+ }
+ }
+}
diff --git a/library/std/src/sys/unix/locks/futex_condvar.rs b/library/std/src/sys/unix/locks/futex_condvar.rs
new file mode 100644
index 000000000..c0576c178
--- /dev/null
+++ b/library/std/src/sys/unix/locks/futex_condvar.rs
@@ -0,0 +1,58 @@
+use super::Mutex;
+use crate::sync::atomic::{AtomicU32, Ordering::Relaxed};
+use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all};
+use crate::time::Duration;
+
+pub type MovableCondvar = Condvar;
+
+pub struct Condvar {
+ // The value of this atomic is simply incremented on every notification.
+ // This is used by `.wait()` to not miss any notifications after
+ // unlocking the mutex and before waiting for notifications.
+ futex: AtomicU32,
+}
+
+impl Condvar {
+ #[inline]
+ pub const fn new() -> Self {
+ Self { futex: AtomicU32::new(0) }
+ }
+
+ // All the memory orderings here are `Relaxed`,
+ // because synchronization is done by unlocking and locking the mutex.
+
+ pub unsafe fn notify_one(&self) {
+ self.futex.fetch_add(1, Relaxed);
+ futex_wake(&self.futex);
+ }
+
+ pub unsafe fn notify_all(&self) {
+ self.futex.fetch_add(1, Relaxed);
+ futex_wake_all(&self.futex);
+ }
+
+ pub unsafe fn wait(&self, mutex: &Mutex) {
+ self.wait_optional_timeout(mutex, None);
+ }
+
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, timeout: Duration) -> bool {
+ self.wait_optional_timeout(mutex, Some(timeout))
+ }
+
+ unsafe fn wait_optional_timeout(&self, mutex: &Mutex, timeout: Option<Duration>) -> bool {
+ // Examine the notification counter _before_ we unlock the mutex.
+ let futex_value = self.futex.load(Relaxed);
+
+ // Unlock the mutex before going to sleep.
+ mutex.unlock();
+
+ // Wait, but only if there hasn't been any
+ // notification since we unlocked the mutex.
+ let r = futex_wait(&self.futex, futex_value, timeout);
+
+ // Lock the mutex again.
+ mutex.lock();
+
+ r
+ }
+}
diff --git a/library/std/src/sys/unix/locks/futex_mutex.rs b/library/std/src/sys/unix/locks/futex_mutex.rs
new file mode 100644
index 000000000..99ba86e5f
--- /dev/null
+++ b/library/std/src/sys/unix/locks/futex_mutex.rs
@@ -0,0 +1,101 @@
+use crate::sync::atomic::{
+ AtomicU32,
+ Ordering::{Acquire, Relaxed, Release},
+};
+use crate::sys::futex::{futex_wait, futex_wake};
+
+pub type MovableMutex = Mutex;
+
+pub struct Mutex {
+ /// 0: unlocked
+ /// 1: locked, no other threads waiting
+ /// 2: locked, and other threads waiting (contended)
+ futex: AtomicU32,
+}
+
+impl Mutex {
+ #[inline]
+ pub const fn new() -> Self {
+ Self { futex: AtomicU32::new(0) }
+ }
+
+ #[inline]
+ pub unsafe fn init(&mut self) {}
+
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_ok()
+ }
+
+ #[inline]
+ pub unsafe fn lock(&self) {
+ if self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_err() {
+ self.lock_contended();
+ }
+ }
+
+ #[cold]
+ fn lock_contended(&self) {
+ // Spin first to speed things up if the lock is released quickly.
+ let mut state = self.spin();
+
+ // If it's unlocked now, attempt to take the lock
+ // without marking it as contended.
+ if state == 0 {
+ match self.futex.compare_exchange(0, 1, Acquire, Relaxed) {
+ Ok(_) => return, // Locked!
+ Err(s) => state = s,
+ }
+ }
+
+ loop {
+ // Put the lock in contended state.
+ // We avoid an unnecessary write if it as already set to 2,
+ // to be friendlier for the caches.
+ if state != 2 && self.futex.swap(2, Acquire) == 0 {
+ // We changed it from 0 to 2, so we just succesfully locked it.
+ return;
+ }
+
+ // Wait for the futex to change state, assuming it is still 2.
+ futex_wait(&self.futex, 2, None);
+
+ // Spin again after waking up.
+ state = self.spin();
+ }
+ }
+
+ fn spin(&self) -> u32 {
+ let mut spin = 100;
+ loop {
+ // We only use `load` (and not `swap` or `compare_exchange`)
+ // while spinning, to be easier on the caches.
+ let state = self.futex.load(Relaxed);
+
+ // We stop spinning when the mutex is unlocked (0),
+ // but also when it's contended (2).
+ if state != 1 || spin == 0 {
+ return state;
+ }
+
+ crate::hint::spin_loop();
+ spin -= 1;
+ }
+ }
+
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ if self.futex.swap(0, Release) == 2 {
+ // We only wake up one thread. When that thread locks the mutex, it
+ // will mark the mutex as contended (2) (see lock_contended above),
+ // which makes sure that any other waiting threads will also be
+ // woken up eventually.
+ self.wake();
+ }
+ }
+
+ #[cold]
+ fn wake(&self) {
+ futex_wake(&self.futex);
+ }
+}
diff --git a/library/std/src/sys/unix/locks/futex_rwlock.rs b/library/std/src/sys/unix/locks/futex_rwlock.rs
new file mode 100644
index 000000000..b3bbbf743
--- /dev/null
+++ b/library/std/src/sys/unix/locks/futex_rwlock.rs
@@ -0,0 +1,322 @@
+use crate::sync::atomic::{
+ AtomicU32,
+ Ordering::{Acquire, Relaxed, Release},
+};
+use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all};
+
+pub type MovableRwLock = RwLock;
+
+pub struct RwLock {
+ // The state consists of a 30-bit reader counter, a 'readers waiting' flag, and a 'writers waiting' flag.
+ // Bits 0..30:
+ // 0: Unlocked
+ // 1..=0x3FFF_FFFE: Locked by N readers
+ // 0x3FFF_FFFF: Write locked
+ // Bit 30: Readers are waiting on this futex.
+ // Bit 31: Writers are waiting on the writer_notify futex.
+ state: AtomicU32,
+ // The 'condition variable' to notify writers through.
+ // Incremented on every signal.
+ writer_notify: AtomicU32,
+}
+
+const READ_LOCKED: u32 = 1;
+const MASK: u32 = (1 << 30) - 1;
+const WRITE_LOCKED: u32 = MASK;
+const MAX_READERS: u32 = MASK - 1;
+const READERS_WAITING: u32 = 1 << 30;
+const WRITERS_WAITING: u32 = 1 << 31;
+
+#[inline]
+fn is_unlocked(state: u32) -> bool {
+ state & MASK == 0
+}
+
+#[inline]
+fn is_write_locked(state: u32) -> bool {
+ state & MASK == WRITE_LOCKED
+}
+
+#[inline]
+fn has_readers_waiting(state: u32) -> bool {
+ state & READERS_WAITING != 0
+}
+
+#[inline]
+fn has_writers_waiting(state: u32) -> bool {
+ state & WRITERS_WAITING != 0
+}
+
+#[inline]
+fn is_read_lockable(state: u32) -> bool {
+ // This also returns false if the counter could overflow if we tried to read lock it.
+ //
+ // We don't allow read-locking if there's readers waiting, even if the lock is unlocked
+ // and there's no writers waiting. The only situation when this happens is after unlocking,
+ // at which point the unlocking thread might be waking up writers, which have priority over readers.
+ // The unlocking thread will clear the readers waiting bit and wake up readers, if necssary.
+ state & MASK < MAX_READERS && !has_readers_waiting(state) && !has_writers_waiting(state)
+}
+
+#[inline]
+fn has_reached_max_readers(state: u32) -> bool {
+ state & MASK == MAX_READERS
+}
+
+impl RwLock {
+ #[inline]
+ pub const fn new() -> Self {
+ Self { state: AtomicU32::new(0), writer_notify: AtomicU32::new(0) }
+ }
+
+ #[inline]
+ pub unsafe fn try_read(&self) -> bool {
+ self.state
+ .fetch_update(Acquire, Relaxed, |s| is_read_lockable(s).then(|| s + READ_LOCKED))
+ .is_ok()
+ }
+
+ #[inline]
+ pub unsafe fn read(&self) {
+ let state = self.state.load(Relaxed);
+ if !is_read_lockable(state)
+ || self
+ .state
+ .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed)
+ .is_err()
+ {
+ self.read_contended();
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read_unlock(&self) {
+ let state = self.state.fetch_sub(READ_LOCKED, Release) - READ_LOCKED;
+
+ // It's impossible for a reader to be waiting on a read-locked RwLock,
+ // except if there is also a writer waiting.
+ debug_assert!(!has_readers_waiting(state) || has_writers_waiting(state));
+
+ // Wake up a writer if we were the last reader and there's a writer waiting.
+ if is_unlocked(state) && has_writers_waiting(state) {
+ self.wake_writer_or_readers(state);
+ }
+ }
+
+ #[cold]
+ fn read_contended(&self) {
+ let mut state = self.spin_read();
+
+ loop {
+ // If we can lock it, lock it.
+ if is_read_lockable(state) {
+ match self.state.compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed)
+ {
+ Ok(_) => return, // Locked!
+ Err(s) => {
+ state = s;
+ continue;
+ }
+ }
+ }
+
+ // Check for overflow.
+ if has_reached_max_readers(state) {
+ panic!("too many active read locks on RwLock");
+ }
+
+ // Make sure the readers waiting bit is set before we go to sleep.
+ if !has_readers_waiting(state) {
+ if let Err(s) =
+ self.state.compare_exchange(state, state | READERS_WAITING, Relaxed, Relaxed)
+ {
+ state = s;
+ continue;
+ }
+ }
+
+ // Wait for the state to change.
+ futex_wait(&self.state, state | READERS_WAITING, None);
+
+ // Spin again after waking up.
+ state = self.spin_read();
+ }
+ }
+
+ #[inline]
+ pub unsafe fn try_write(&self) -> bool {
+ self.state
+ .fetch_update(Acquire, Relaxed, |s| is_unlocked(s).then(|| s + WRITE_LOCKED))
+ .is_ok()
+ }
+
+ #[inline]
+ pub unsafe fn write(&self) {
+ if self.state.compare_exchange_weak(0, WRITE_LOCKED, Acquire, Relaxed).is_err() {
+ self.write_contended();
+ }
+ }
+
+ #[inline]
+ pub unsafe fn write_unlock(&self) {
+ let state = self.state.fetch_sub(WRITE_LOCKED, Release) - WRITE_LOCKED;
+
+ debug_assert!(is_unlocked(state));
+
+ if has_writers_waiting(state) || has_readers_waiting(state) {
+ self.wake_writer_or_readers(state);
+ }
+ }
+
+ #[cold]
+ fn write_contended(&self) {
+ let mut state = self.spin_write();
+
+ let mut other_writers_waiting = 0;
+
+ loop {
+ // If it's unlocked, we try to lock it.
+ if is_unlocked(state) {
+ match self.state.compare_exchange_weak(
+ state,
+ state | WRITE_LOCKED | other_writers_waiting,
+ Acquire,
+ Relaxed,
+ ) {
+ Ok(_) => return, // Locked!
+ Err(s) => {
+ state = s;
+ continue;
+ }
+ }
+ }
+
+ // Set the waiting bit indicating that we're waiting on it.
+ if !has_writers_waiting(state) {
+ if let Err(s) =
+ self.state.compare_exchange(state, state | WRITERS_WAITING, Relaxed, Relaxed)
+ {
+ state = s;
+ continue;
+ }
+ }
+
+ // Other writers might be waiting now too, so we should make sure
+ // we keep that bit on once we manage lock it.
+ other_writers_waiting = WRITERS_WAITING;
+
+ // Examine the notification counter before we check if `state` has changed,
+ // to make sure we don't miss any notifications.
+ let seq = self.writer_notify.load(Acquire);
+
+ // Don't go to sleep if the lock has become available,
+ // or if the writers waiting bit is no longer set.
+ state = self.state.load(Relaxed);
+ if is_unlocked(state) || !has_writers_waiting(state) {
+ continue;
+ }
+
+ // Wait for the state to change.
+ futex_wait(&self.writer_notify, seq, None);
+
+ // Spin again after waking up.
+ state = self.spin_write();
+ }
+ }
+
+ /// Wake up waiting threads after unlocking.
+ ///
+ /// If both are waiting, this will wake up only one writer, but will fall
+ /// back to waking up readers if there was no writer to wake up.
+ #[cold]
+ fn wake_writer_or_readers(&self, mut state: u32) {
+ assert!(is_unlocked(state));
+
+ // The readers waiting bit might be turned on at any point now,
+ // since readers will block when there's anything waiting.
+ // Writers will just lock the lock though, regardless of the waiting bits,
+ // so we don't have to worry about the writer waiting bit.
+ //
+ // If the lock gets locked in the meantime, we don't have to do
+ // anything, because then the thread that locked the lock will take
+ // care of waking up waiters when it unlocks.
+
+ // If only writers are waiting, wake one of them up.
+ if state == WRITERS_WAITING {
+ match self.state.compare_exchange(state, 0, Relaxed, Relaxed) {
+ Ok(_) => {
+ self.wake_writer();
+ return;
+ }
+ Err(s) => {
+ // Maybe some readers are now waiting too. So, continue to the next `if`.
+ state = s;
+ }
+ }
+ }
+
+ // If both writers and readers are waiting, leave the readers waiting
+ // and only wake up one writer.
+ if state == READERS_WAITING + WRITERS_WAITING {
+ if self.state.compare_exchange(state, READERS_WAITING, Relaxed, Relaxed).is_err() {
+ // The lock got locked. Not our problem anymore.
+ return;
+ }
+ if self.wake_writer() {
+ return;
+ }
+ // No writers were actually blocked on futex_wait, so we continue
+ // to wake up readers instead, since we can't be sure if we notified a writer.
+ state = READERS_WAITING;
+ }
+
+ // If readers are waiting, wake them all up.
+ if state == READERS_WAITING {
+ if self.state.compare_exchange(state, 0, Relaxed, Relaxed).is_ok() {
+ futex_wake_all(&self.state);
+ }
+ }
+ }
+
+ /// This wakes one writer and returns true if we woke up a writer that was
+ /// blocked on futex_wait.
+ ///
+ /// If this returns false, it might still be the case that we notified a
+ /// writer that was about to go to sleep.
+ fn wake_writer(&self) -> bool {
+ self.writer_notify.fetch_add(1, Release);
+ futex_wake(&self.writer_notify)
+ // Note that FreeBSD and DragonFlyBSD don't tell us whether they woke
+ // up any threads or not, and always return `false` here. That still
+ // results in correct behaviour: it just means readers get woken up as
+ // well in case both readers and writers were waiting.
+ }
+
+ /// Spin for a while, but stop directly at the given condition.
+ #[inline]
+ fn spin_until(&self, f: impl Fn(u32) -> bool) -> u32 {
+ let mut spin = 100; // Chosen by fair dice roll.
+ loop {
+ let state = self.state.load(Relaxed);
+ if f(state) || spin == 0 {
+ return state;
+ }
+ crate::hint::spin_loop();
+ spin -= 1;
+ }
+ }
+
+ #[inline]
+ fn spin_write(&self) -> u32 {
+ // Stop spinning when it's unlocked or when there's waiting writers, to keep things somewhat fair.
+ self.spin_until(|state| is_unlocked(state) || has_writers_waiting(state))
+ }
+
+ #[inline]
+ fn spin_read(&self) -> u32 {
+ // Stop spinning when it's unlocked or read locked, or when there's waiting threads.
+ self.spin_until(|state| {
+ !is_write_locked(state) || has_readers_waiting(state) || has_writers_waiting(state)
+ })
+ }
+}
diff --git a/library/std/src/sys/unix/locks/mod.rs b/library/std/src/sys/unix/locks/mod.rs
new file mode 100644
index 000000000..f5f92f693
--- /dev/null
+++ b/library/std/src/sys/unix/locks/mod.rs
@@ -0,0 +1,31 @@
+cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ all(target_os = "emscripten", target_feature = "atomics"),
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ ))] {
+ mod futex_mutex;
+ mod futex_rwlock;
+ mod futex_condvar;
+ pub(crate) use futex_mutex::{Mutex, MovableMutex};
+ pub(crate) use futex_rwlock::{RwLock, MovableRwLock};
+ pub(crate) use futex_condvar::MovableCondvar;
+ } else if #[cfg(target_os = "fuchsia")] {
+ mod fuchsia_mutex;
+ mod futex_rwlock;
+ mod futex_condvar;
+ pub(crate) use fuchsia_mutex::{Mutex, MovableMutex};
+ pub(crate) use futex_rwlock::{RwLock, MovableRwLock};
+ pub(crate) use futex_condvar::MovableCondvar;
+ } else {
+ mod pthread_mutex;
+ mod pthread_rwlock;
+ mod pthread_condvar;
+ pub(crate) use pthread_mutex::{Mutex, MovableMutex};
+ pub(crate) use pthread_rwlock::{RwLock, MovableRwLock};
+ pub(crate) use pthread_condvar::MovableCondvar;
+ }
+}
diff --git a/library/std/src/sys/unix/locks/pthread_condvar.rs b/library/std/src/sys/unix/locks/pthread_condvar.rs
new file mode 100644
index 000000000..abf27e7db
--- /dev/null
+++ b/library/std/src/sys/unix/locks/pthread_condvar.rs
@@ -0,0 +1,222 @@
+use crate::cell::UnsafeCell;
+use crate::sys::locks::{pthread_mutex, Mutex};
+use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+use crate::time::Duration;
+
+pub struct Condvar {
+ inner: UnsafeCell<libc::pthread_cond_t>,
+}
+
+pub(crate) type MovableCondvar = LazyBox<Condvar>;
+
+unsafe impl Send for Condvar {}
+unsafe impl Sync for Condvar {}
+
+const TIMESPEC_MAX: libc::timespec =
+ libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
+
+fn saturating_cast_to_time_t(value: u64) -> libc::time_t {
+ if value > <libc::time_t>::MAX as u64 { <libc::time_t>::MAX } else { value as libc::time_t }
+}
+
+impl LazyInit for Condvar {
+ fn init() -> Box<Self> {
+ let mut condvar = Box::new(Self::new());
+ unsafe { condvar.init() };
+ condvar
+ }
+}
+
+impl Condvar {
+ pub const fn new() -> Condvar {
+ // 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) }
+ }
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "l4re",
+ target_os = "android",
+ target_os = "redox"
+ ))]
+ unsafe fn init(&mut self) {}
+
+ // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet
+ // So on that platform, init() should always be called
+ // Moreover, that platform does not have pthread_condattr_setclock support,
+ // hence that initialization should be skipped as well
+ //
+ // Similar story for the 3DS (horizon).
+ #[cfg(any(target_os = "espidf", target_os = "horizon"))]
+ unsafe fn init(&mut self) {
+ let r = libc::pthread_cond_init(self.inner.get(), crate::ptr::null());
+ assert_eq!(r, 0);
+ }
+
+ #[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "l4re",
+ target_os = "android",
+ target_os = "redox",
+ target_os = "espidf",
+ target_os = "horizon"
+ )))]
+ unsafe fn init(&mut self) {
+ use crate::mem::MaybeUninit;
+ let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
+ let r = libc::pthread_condattr_init(attr.as_mut_ptr());
+ assert_eq!(r, 0);
+ let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC);
+ assert_eq!(r, 0);
+ let r = libc::pthread_cond_init(self.inner.get(), attr.as_ptr());
+ assert_eq!(r, 0);
+ let r = libc::pthread_condattr_destroy(attr.as_mut_ptr());
+ assert_eq!(r, 0);
+ }
+
+ #[inline]
+ pub unsafe fn notify_one(&self) {
+ let r = libc::pthread_cond_signal(self.inner.get());
+ debug_assert_eq!(r, 0);
+ }
+
+ #[inline]
+ pub unsafe fn notify_all(&self) {
+ let r = libc::pthread_cond_broadcast(self.inner.get());
+ debug_assert_eq!(r, 0);
+ }
+
+ #[inline]
+ pub unsafe fn wait(&self, mutex: &Mutex) {
+ let r = libc::pthread_cond_wait(self.inner.get(), pthread_mutex::raw(mutex));
+ debug_assert_eq!(r, 0);
+ }
+
+ // 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 = "watchos",
+ target_os = "android",
+ target_os = "espidf",
+ target_os = "horizon"
+ )))]
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ use crate::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(), pthread_mutex::raw(mutex), &timeout);
+ assert!(r == libc::ETIMEDOUT || r == 0);
+ r == 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 = "watchos",
+ target_os = "android",
+ target_os = "espidf",
+ target_os = "horizon"
+ ))]
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, mut dur: Duration) -> bool {
+ use crate::ptr;
+ use crate::time::Instant;
+
+ // 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(), pthread_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]
+ #[cfg(not(target_os = "dragonfly"))]
+ unsafe fn destroy(&mut self) {
+ let r = libc::pthread_cond_destroy(self.inner.get());
+ debug_assert_eq!(r, 0);
+ }
+
+ #[inline]
+ #[cfg(target_os = "dragonfly")]
+ unsafe fn destroy(&mut self) {
+ 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);
+ }
+}
+
+impl Drop for Condvar {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe { self.destroy() };
+ }
+}
diff --git a/library/std/src/sys/unix/locks/pthread_mutex.rs b/library/std/src/sys/unix/locks/pthread_mutex.rs
new file mode 100644
index 000000000..98afee69b
--- /dev/null
+++ b/library/std/src/sys/unix/locks/pthread_mutex.rs
@@ -0,0 +1,135 @@
+use crate::cell::UnsafeCell;
+use crate::mem::{forget, MaybeUninit};
+use crate::sys::cvt_nz;
+use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+
+pub struct Mutex {
+ inner: UnsafeCell<libc::pthread_mutex_t>,
+}
+
+pub(crate) type MovableMutex = LazyBox<Mutex>;
+
+#[inline]
+pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
+ m.inner.get()
+}
+
+unsafe impl Send for Mutex {}
+unsafe impl Sync for Mutex {}
+
+impl LazyInit for Mutex {
+ fn init() -> Box<Self> {
+ let mut mutex = Box::new(Self::new());
+ unsafe { mutex.init() };
+ mutex
+ }
+
+ fn destroy(mutex: Box<Self>) {
+ // We're not allowed to pthread_mutex_destroy a locked mutex,
+ // so check first if it's unlocked.
+ if unsafe { mutex.try_lock() } {
+ unsafe { mutex.unlock() };
+ drop(mutex);
+ } else {
+ // The mutex is locked. This happens if a MutexGuard is leaked.
+ // In this case, we just leak the Mutex too.
+ forget(mutex);
+ }
+ }
+
+ fn cancel_init(_: Box<Self>) {
+ // In this case, we can just drop it without any checks,
+ // since it cannot have been locked yet.
+ }
+}
+
+impl Mutex {
+ pub const fn new() -> Mutex {
+ // Might be moved to a different address, so it is better to avoid
+ // initialization of potentially opaque OS data before it landed.
+ // Be very careful using this newly constructed `Mutex`, reentrant
+ // locking is undefined behavior until `init` is called!
+ Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
+ }
+ #[inline]
+ pub unsafe fn init(&mut self) {
+ // 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
+ // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html).
+ // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL
+ // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that
+ // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same
+ // as setting it to `PTHREAD_MUTEX_NORMAL`, but not setting any mode will result in
+ // a Mutex where re-locking is UB.
+ //
+ // 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 when 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.
+ let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit();
+ cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap();
+ let attr = PthreadMutexAttr(&mut attr);
+ cvt_nz(libc::pthread_mutexattr_settype(attr.0.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL))
+ .unwrap();
+ cvt_nz(libc::pthread_mutex_init(self.inner.get(), attr.0.as_ptr())).unwrap();
+ }
+ #[inline]
+ pub unsafe fn lock(&self) {
+ let r = libc::pthread_mutex_lock(self.inner.get());
+ debug_assert_eq!(r, 0);
+ }
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ let r = libc::pthread_mutex_unlock(self.inner.get());
+ debug_assert_eq!(r, 0);
+ }
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ libc::pthread_mutex_trylock(self.inner.get()) == 0
+ }
+ #[inline]
+ #[cfg(not(target_os = "dragonfly"))]
+ unsafe fn destroy(&mut self) {
+ let r = libc::pthread_mutex_destroy(self.inner.get());
+ debug_assert_eq!(r, 0);
+ }
+ #[inline]
+ #[cfg(target_os = "dragonfly")]
+ unsafe fn destroy(&mut self) {
+ 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);
+ }
+}
+
+impl Drop for Mutex {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe { self.destroy() };
+ }
+}
+
+pub(super) struct PthreadMutexAttr<'a>(pub &'a mut MaybeUninit<libc::pthread_mutexattr_t>);
+
+impl Drop for PthreadMutexAttr<'_> {
+ fn drop(&mut self) {
+ unsafe {
+ let result = libc::pthread_mutexattr_destroy(self.0.as_mut_ptr());
+ debug_assert_eq!(result, 0);
+ }
+ }
+}
diff --git a/library/std/src/sys/unix/locks/pthread_rwlock.rs b/library/std/src/sys/unix/locks/pthread_rwlock.rs
new file mode 100644
index 000000000..adfe2a883
--- /dev/null
+++ b/library/std/src/sys/unix/locks/pthread_rwlock.rs
@@ -0,0 +1,173 @@
+use crate::cell::UnsafeCell;
+use crate::mem::forget;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+
+pub struct RwLock {
+ inner: UnsafeCell<libc::pthread_rwlock_t>,
+ write_locked: UnsafeCell<bool>, // guarded by the `inner` RwLock
+ num_readers: AtomicUsize,
+}
+
+pub(crate) type MovableRwLock = LazyBox<RwLock>;
+
+unsafe impl Send for RwLock {}
+unsafe impl Sync for RwLock {}
+
+impl LazyInit for RwLock {
+ fn init() -> Box<Self> {
+ Box::new(Self::new())
+ }
+
+ fn destroy(mut rwlock: Box<Self>) {
+ // We're not allowed to pthread_rwlock_destroy a locked rwlock,
+ // so check first if it's unlocked.
+ if *rwlock.write_locked.get_mut() || *rwlock.num_readers.get_mut() != 0 {
+ // The rwlock is locked. This happens if a RwLock{Read,Write}Guard is leaked.
+ // In this case, we just leak the RwLock too.
+ forget(rwlock);
+ }
+ }
+
+ fn cancel_init(_: Box<Self>) {
+ // In this case, we can just drop it without any checks,
+ // since it cannot have been locked yet.
+ }
+}
+
+impl RwLock {
+ pub const fn new() -> RwLock {
+ RwLock {
+ inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER),
+ write_locked: UnsafeCell::new(false),
+ num_readers: AtomicUsize::new(0),
+ }
+ }
+ #[inline]
+ pub unsafe fn read(&self) {
+ let r = libc::pthread_rwlock_rdlock(self.inner.get());
+
+ // According to POSIX, when a thread tries to acquire this read lock
+ // while it already holds the write lock
+ // (or vice versa, or tries to acquire the write lock twice),
+ // "the call shall either deadlock or return [EDEADLK]"
+ // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html,
+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html).
+ // So, in principle, all we have to do here is check `r == 0` to be sure we properly
+ // got the lock.
+ //
+ // However, (at least) glibc before version 2.25 does not conform to this spec,
+ // and can return `r == 0` even when this thread already holds the write lock.
+ // We thus check for this situation ourselves and panic when detecting that a thread
+ // got the write lock more than once, or got a read and a write lock.
+ if r == libc::EAGAIN {
+ panic!("rwlock maximum reader count exceeded");
+ } else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) {
+ // Above, we make sure to only access `write_locked` when `r == 0` to avoid
+ // data races.
+ if r == 0 {
+ // `pthread_rwlock_rdlock` succeeded when it should not have.
+ self.raw_unlock();
+ }
+ panic!("rwlock read lock would result in deadlock");
+ } else {
+ // POSIX does not make guarantees about all the errors that may be returned.
+ // See issue #94705 for more details.
+ assert_eq!(r, 0, "unexpected error during rwlock read lock: {:?}", r);
+ self.num_readers.fetch_add(1, Ordering::Relaxed);
+ }
+ }
+ #[inline]
+ pub unsafe fn try_read(&self) -> bool {
+ let r = libc::pthread_rwlock_tryrdlock(self.inner.get());
+ if r == 0 {
+ if *self.write_locked.get() {
+ // `pthread_rwlock_tryrdlock` succeeded when it should not have.
+ self.raw_unlock();
+ false
+ } else {
+ self.num_readers.fetch_add(1, Ordering::Relaxed);
+ true
+ }
+ } else {
+ false
+ }
+ }
+ #[inline]
+ pub unsafe fn write(&self) {
+ let r = libc::pthread_rwlock_wrlock(self.inner.get());
+ // See comments above for why we check for EDEADLK and write_locked. For the same reason,
+ // we also need to check that there are no readers (tracked in `num_readers`).
+ if r == libc::EDEADLK
+ || (r == 0 && *self.write_locked.get())
+ || self.num_readers.load(Ordering::Relaxed) != 0
+ {
+ // Above, we make sure to only access `write_locked` when `r == 0` to avoid
+ // data races.
+ if r == 0 {
+ // `pthread_rwlock_wrlock` succeeded when it should not have.
+ self.raw_unlock();
+ }
+ panic!("rwlock write lock would result in deadlock");
+ } else {
+ // According to POSIX, for a properly initialized rwlock this can only
+ // return EDEADLK or 0. We rely on that.
+ debug_assert_eq!(r, 0);
+ }
+ *self.write_locked.get() = true;
+ }
+ #[inline]
+ pub unsafe fn try_write(&self) -> bool {
+ let r = libc::pthread_rwlock_trywrlock(self.inner.get());
+ if r == 0 {
+ if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 {
+ // `pthread_rwlock_trywrlock` succeeded when it should not have.
+ self.raw_unlock();
+ false
+ } else {
+ *self.write_locked.get() = true;
+ true
+ }
+ } else {
+ false
+ }
+ }
+ #[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) {
+ debug_assert!(!*self.write_locked.get());
+ self.num_readers.fetch_sub(1, Ordering::Relaxed);
+ self.raw_unlock();
+ }
+ #[inline]
+ pub unsafe fn write_unlock(&self) {
+ 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]
+ unsafe fn destroy(&mut self) {
+ 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);
+ }
+ }
+}
+
+impl Drop for RwLock {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe { self.destroy() };
+ }
+}
diff --git a/library/std/src/sys/unix/memchr.rs b/library/std/src/sys/unix/memchr.rs
new file mode 100644
index 000000000..73ba604ec
--- /dev/null
+++ b/library/std/src/sys/unix/memchr.rs
@@ -0,0 +1,40 @@
+// Original implementation taken from rust-memchr.
+// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
+
+pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+ 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.addr() - haystack.as_ptr().addr()) }
+}
+
+pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+ #[cfg(target_os = "linux")]
+ fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
+ // 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(),
+ )
+ };
+ // FIXME: this should *likely* use `offset_from`, but more
+ // investigation is needed (including running tests in miri).
+ if p.is_null() { None } else { Some(p.addr() - haystack.as_ptr().addr()) }
+ }
+
+ #[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/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs
new file mode 100644
index 000000000..3d0d91460
--- /dev/null
+++ b/library/std/src/sys/unix/mod.rs
@@ -0,0 +1,361 @@
+#![allow(missing_docs, nonstandard_style)]
+
+use crate::ffi::CStr;
+use crate::io::ErrorKind;
+
+pub use self::rand::hashmap_random_keys;
+
+#[cfg(not(target_os = "espidf"))]
+#[macro_use]
+pub mod weak;
+
+pub mod alloc;
+pub mod android;
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+pub mod env;
+pub mod fd;
+pub mod fs;
+pub mod futex;
+pub mod io;
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub mod kernel_copy;
+#[cfg(target_os = "l4re")]
+mod l4re;
+pub mod locks;
+pub mod memchr;
+#[cfg(not(target_os = "l4re"))]
+pub mod net;
+#[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 stack_overflow;
+pub mod stdio;
+pub mod thread;
+pub mod thread_local_dtor;
+pub mod thread_local_key;
+pub mod thread_parker;
+pub mod time;
+
+#[cfg(target_os = "espidf")]
+pub fn init(argc: isize, argv: *const *const u8) {}
+
+#[cfg(not(target_os = "espidf"))]
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ // The standard streams might be closed on application startup. To prevent
+ // std::io::{stdin, stdout,stderr} objects from using other unrelated file
+ // resources opened later, we reopen standards streams when they are closed.
+ sanitize_standard_fds();
+
+ // 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.
+ reset_sigpipe();
+
+ stack_overflow::init();
+ args::init(argc, argv);
+
+ // Normally, `thread::spawn` will call `Thread::set_name` but since this thread
+ // already exists, we have to call it ourselves. We only do this on macos
+ // because some unix-like operating systems such as Linux share process-id and
+ // thread-id for the main thread and so renaming the main thread will rename the
+ // process and we only want to enable this on platforms we've tested.
+ if cfg!(target_os = "macos") {
+ thread::Thread::set_name(&CStr::from_bytes_with_nul_unchecked(b"main\0"));
+ }
+
+ unsafe fn sanitize_standard_fds() {
+ // fast path with a single syscall for systems with poll()
+ #[cfg(not(any(
+ miri,
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "vxworks",
+ // The poll on Darwin doesn't set POLLNVAL for closed fds.
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "redox",
+ target_os = "l4re",
+ target_os = "horizon",
+ )))]
+ 'poll: {
+ use crate::sys::os::errno;
+ let pfds: &mut [_] = &mut [
+ libc::pollfd { fd: 0, events: 0, revents: 0 },
+ libc::pollfd { fd: 1, events: 0, revents: 0 },
+ libc::pollfd { fd: 2, events: 0, revents: 0 },
+ ];
+
+ while libc::poll(pfds.as_mut_ptr(), 3, 0) == -1 {
+ match errno() {
+ libc::EINTR => continue,
+ libc::EINVAL | libc::EAGAIN | libc::ENOMEM => {
+ // RLIMIT_NOFILE or temporary allocation failures
+ // may be preventing use of poll(), fall back to fcntl
+ break 'poll;
+ }
+ _ => libc::abort(),
+ }
+ }
+ for pfd in pfds {
+ if pfd.revents & libc::POLLNVAL == 0 {
+ continue;
+ }
+ if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
+ // If the stream is closed but we failed to reopen it, abort the
+ // process. Otherwise we wouldn't preserve the safety of
+ // operations on the corresponding Rust object Stdin, Stdout, or
+ // Stderr.
+ libc::abort();
+ }
+ }
+ return;
+ }
+
+ // fallback in case poll isn't available or limited by RLIMIT_NOFILE
+ #[cfg(not(any(
+ // The standard fds are always available in Miri.
+ miri,
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "vxworks",
+ target_os = "l4re",
+ target_os = "horizon",
+ )))]
+ {
+ use crate::sys::os::errno;
+ for fd in 0..3 {
+ if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
+ if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
+ // If the stream is closed but we failed to reopen it, abort the
+ // process. Otherwise we wouldn't preserve the safety of
+ // operations on the corresponding Rust object Stdin, Stdout, or
+ // Stderr.
+ libc::abort();
+ }
+ }
+ }
+ }
+ }
+
+ unsafe fn reset_sigpipe() {
+ #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))]
+ rtassert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
+ }
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+ stack_overflow::cleanup();
+}
+
+#[cfg(target_os = "android")]
+pub use crate::sys::android::signal;
+#[cfg(not(target_os = "android"))]
+pub use libc::signal;
+
+pub fn decode_error_kind(errno: i32) -> ErrorKind {
+ use ErrorKind::*;
+ match errno as libc::c_int {
+ libc::E2BIG => ArgumentListTooLong,
+ libc::EADDRINUSE => AddrInUse,
+ libc::EADDRNOTAVAIL => AddrNotAvailable,
+ libc::EBUSY => ResourceBusy,
+ libc::ECONNABORTED => ConnectionAborted,
+ libc::ECONNREFUSED => ConnectionRefused,
+ libc::ECONNRESET => ConnectionReset,
+ libc::EDEADLK => Deadlock,
+ libc::EDQUOT => FilesystemQuotaExceeded,
+ libc::EEXIST => AlreadyExists,
+ libc::EFBIG => FileTooLarge,
+ libc::EHOSTUNREACH => HostUnreachable,
+ libc::EINTR => Interrupted,
+ libc::EINVAL => InvalidInput,
+ libc::EISDIR => IsADirectory,
+ libc::ELOOP => FilesystemLoop,
+ libc::ENOENT => NotFound,
+ libc::ENOMEM => OutOfMemory,
+ libc::ENOSPC => StorageFull,
+ libc::ENOSYS => Unsupported,
+ libc::EMLINK => TooManyLinks,
+ libc::ENAMETOOLONG => InvalidFilename,
+ libc::ENETDOWN => NetworkDown,
+ libc::ENETUNREACH => NetworkUnreachable,
+ libc::ENOTCONN => NotConnected,
+ libc::ENOTDIR => NotADirectory,
+ libc::ENOTEMPTY => DirectoryNotEmpty,
+ libc::EPIPE => BrokenPipe,
+ libc::EROFS => ReadOnlyFilesystem,
+ libc::ESPIPE => NotSeekable,
+ libc::ESTALE => StaleNetworkFileHandle,
+ libc::ETIMEDOUT => TimedOut,
+ libc::ETXTBSY => ExecutableFileBusy,
+ libc::EXDEV => CrossesDevices,
+
+ libc::EACCES | libc::EPERM => PermissionDenied,
+
+ // 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 => WouldBlock,
+
+ _ => Uncategorized,
+ }
+}
+
+#[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) -> crate::io::Result<T> {
+ if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) }
+}
+
+pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T>
+where
+ T: IsMinusOne,
+ F: FnMut() -> T,
+{
+ loop {
+ match cvt(f()) {
+ Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
+ other => return other,
+ }
+ }
+}
+
+#[allow(dead_code)] // Not used on all platforms.
+pub fn cvt_nz(error: libc::c_int) -> crate::io::Result<()> {
+ if error == 0 { Ok(()) } else { Err(crate::io::Error::from_raw_os_error(error)) }
+}
+
+// libc::abort() will run the SIGABRT handler. That's fine because anyone who
+// installs a SIGABRT handler already has to expect it to run in Very Bad
+// situations (eg, malloc crashing).
+//
+// Current glibc's abort() function unblocks SIGABRT, raises SIGABRT, clears the
+// SIGABRT handler and raises it again, and then starts to get creative.
+//
+// See the public documentation for `intrinsics::abort()` and `process::abort()`
+// for further discussion.
+//
+// There is confusion about whether libc::abort() flushes stdio streams.
+// libc::abort() is required by ISO C 99 (7.14.1.1p5) to be async-signal-safe,
+// so flushing streams is at least extremely hard, if not entirely impossible.
+//
+// However, some versions of POSIX (eg IEEE Std 1003.1-2001) required abort to
+// do so. In 1003.1-2004 this was fixed.
+//
+// glibc's implementation did the flush, unsafely, before glibc commit
+// 91e7cf982d01 `abort: Do not flush stdio streams [BZ #15436]' by Florian
+// Weimer. According to glibc's NEWS:
+//
+// The abort function terminates the process immediately, without flushing
+// stdio streams. Previous glibc versions used to flush streams, resulting
+// in deadlocks and further data corruption. This change also affects
+// process aborts as the result of assertion failures.
+//
+// This is an accurate description of the problem. The only solution for
+// program with nontrivial use of C stdio is a fixed libc - one which does not
+// try to flush in abort - since even libc-internal errors, and assertion
+// failures generated from C, will go via abort().
+//
+// On systems with old, buggy, libcs, the impact can be severe for a
+// multithreaded C program. It is much less severe for Rust, because Rust
+// stdlib doesn't use libc stdio buffering. In a typical Rust program, which
+// does not use C stdio, even a buggy libc::abort() is, in fact, safe.
+pub fn abort_internal() -> ! {
+ unsafe { libc::abort() }
+}
+
+cfg_if::cfg_if! {
+ if #[cfg(target_os = "android")] {
+ #[link(name = "dl")]
+ #[link(name = "log")]
+ extern "C" {}
+ } else if #[cfg(target_os = "freebsd")] {
+ #[link(name = "execinfo")]
+ #[link(name = "pthread")]
+ extern "C" {}
+ } else if #[cfg(target_os = "netbsd")] {
+ #[link(name = "pthread")]
+ #[link(name = "rt")]
+ extern "C" {}
+ } else if #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))] {
+ #[link(name = "pthread")]
+ extern "C" {}
+ } else if #[cfg(target_os = "solaris")] {
+ #[link(name = "socket")]
+ #[link(name = "posix4")]
+ #[link(name = "pthread")]
+ #[link(name = "resolv")]
+ extern "C" {}
+ } else if #[cfg(target_os = "illumos")] {
+ #[link(name = "socket")]
+ #[link(name = "posix4")]
+ #[link(name = "pthread")]
+ #[link(name = "resolv")]
+ #[link(name = "nsl")]
+ // Use libumem for the (malloc-compatible) allocator
+ #[link(name = "umem")]
+ extern "C" {}
+ } else if #[cfg(target_os = "macos")] {
+ #[link(name = "System")]
+ // res_init and friends require -lresolv on macOS/iOS.
+ // See #41582 and https://blog.achernya.com/2013/03/os-x-has-silly-libsystem.html
+ #[link(name = "resolv")]
+ extern "C" {}
+ } else if #[cfg(any(target_os = "ios", target_os = "watchos"))] {
+ #[link(name = "System")]
+ #[link(name = "objc")]
+ #[link(name = "Security", kind = "framework")]
+ #[link(name = "Foundation", kind = "framework")]
+ #[link(name = "resolv")]
+ extern "C" {}
+ } else if #[cfg(target_os = "fuchsia")] {
+ #[link(name = "zircon")]
+ #[link(name = "fdio")]
+ extern "C" {}
+ } else if #[cfg(all(target_os = "linux", target_env = "uclibc"))] {
+ #[link(name = "dl")]
+ extern "C" {}
+ }
+}
+
+#[cfg(any(target_os = "espidf", target_os = "horizon"))]
+mod unsupported {
+ use crate::io;
+
+ pub fn unsupported<T>() -> io::Result<T> {
+ Err(unsupported_err())
+ }
+
+ pub fn unsupported_err() -> io::Error {
+ io::const_io_error!(io::ErrorKind::Unsupported, "operation not supported on this platform",)
+ }
+}
diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
new file mode 100644
index 000000000..462a45b01
--- /dev/null
+++ b/library/std/src/sys/unix/net.rs
@@ -0,0 +1,512 @@
+use crate::cmp;
+use crate::ffi::CStr;
+use crate::io::{self, IoSlice, IoSliceMut};
+use crate::mem;
+use crate::net::{Shutdown, SocketAddr};
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
+use crate::str;
+use crate::sys::fd::FileDesc;
+use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr};
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+use crate::time::{Duration, Instant};
+
+use libc::{c_int, c_void, size_t, sockaddr, socklen_t, MSG_PEEK};
+
+cfg_if::cfg_if! {
+ if #[cfg(target_vendor = "apple")] {
+ use libc::SO_LINGER_SEC as SO_LINGER;
+ } else {
+ use libc::SO_LINGER;
+ }
+}
+
+pub use crate::sys::{cvt, cvt_r};
+
+#[allow(unused_extern_crates)]
+pub extern crate libc as netc;
+
+pub type wrlen_t = size_t;
+
+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();
+
+ #[cfg(not(target_os = "espidf"))]
+ if err == libc::EAI_SYSTEM {
+ return Err(io::Error::last_os_error());
+ }
+
+ #[cfg(not(target_os = "espidf"))]
+ let detail = unsafe {
+ str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap().to_owned()
+ };
+
+ #[cfg(target_os = "espidf")]
+ let detail = "";
+
+ Err(io::Error::new(
+ io::ErrorKind::Uncategorized,
+ &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 {
+ cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))] {
+ // On platforms that support it we pass the SOCK_CLOEXEC
+ // flag to atomically create the socket and set it as
+ // CLOEXEC. On Linux this was added in 2.6.27.
+ let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?;
+ Ok(Socket(FileDesc::from_raw_fd(fd)))
+ } else {
+ let fd = cvt(libc::socket(fam, ty, 0))?;
+ let fd = FileDesc::from_raw_fd(fd);
+ fd.set_cloexec()?;
+ let socket = Socket(fd);
+
+ // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt`
+ // flag to disable `SIGPIPE` emission on socket.
+ #[cfg(target_vendor = "apple")]
+ setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?;
+
+ Ok(socket)
+ }
+ }
+ }
+ }
+
+ #[cfg(not(target_os = "vxworks"))]
+ pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> {
+ unsafe {
+ let mut fds = [0, 0];
+
+ cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))] {
+ // Like above, set cloexec atomically
+ cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?;
+ Ok((Socket(FileDesc::from_raw_fd(fds[0])), Socket(FileDesc::from_raw_fd(fds[1]))))
+ } else {
+ cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?;
+ let a = FileDesc::from_raw_fd(fds[0]);
+ let b = FileDesc::from_raw_fd(fds[1]);
+ a.set_cloexec()?;
+ b.set_cloexec()?;
+ Ok((Socket(a), Socket(b)))
+ }
+ }
+ }
+ }
+
+ #[cfg(target_os = "vxworks")]
+ pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> {
+ unimplemented!()
+ }
+
+ pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
+ self.set_nonblocking(true)?;
+ let r = unsafe {
+ let (addr, len) = addr.into_inner();
+ cvt(libc::connect(self.as_raw_fd(), addr.as_ptr(), 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.as_raw_fd(), events: libc::POLLOUT, revents: 0 };
+
+ if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "cannot set a 0 duration timeout",
+ ));
+ }
+
+ let start = Instant::now();
+
+ loop {
+ let elapsed = start.elapsed();
+ if elapsed >= timeout {
+ return Err(io::const_io_error!(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 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::const_io_error!(
+ io::ErrorKind::Uncategorized,
+ "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
+ // platforms that support it. On Linux, this was added in 2.6.28,
+ // glibc 2.10 and musl 0.9.5.
+ cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))] {
+ unsafe {
+ let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?;
+ Ok(Socket(FileDesc::from_raw_fd(fd)))
+ }
+ } else {
+ unsafe {
+ let fd = cvt_r(|| libc::accept(self.as_raw_fd(), storage, len))?;
+ let fd = FileDesc::from_raw_fd(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.as_raw_fd(), 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)
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.0.read_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ self.0.is_read_vectored()
+ }
+
+ 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.as_raw_fd(),
+ 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)
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> {
+ let n = cvt(unsafe { libc::recvmsg(self.as_raw_fd(), msg, libc::MSG_CMSG_CLOEXEC) })?;
+ Ok(n as usize)
+ }
+
+ 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 write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.0.write_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ self.0.is_write_vectored()
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> {
+ let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?;
+ Ok(n as usize)
+ }
+
+ 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::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "cannot set a 0 duration timeout",
+ ));
+ }
+
+ let secs = if dur.as_secs() > libc::time_t::MAX as u64 {
+ libc::time_t::MAX
+ } else {
+ dur.as_secs() as libc::time_t
+ };
+ let mut timeout = libc::timeval {
+ tv_sec: secs,
+ tv_usec: dur.subsec_micros() 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.as_raw_fd(), how) })?;
+ Ok(())
+ }
+
+ pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
+ let linger = libc::linger {
+ l_onoff: linger.is_some() as libc::c_int,
+ l_linger: linger.unwrap_or_default().as_secs() as libc::c_int,
+ };
+
+ setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger)
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ let val: libc::linger = getsockopt(self, libc::SOL_SOCKET, SO_LINGER)?;
+
+ Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
+ }
+
+ 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)
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux",))]
+ pub fn set_passcred(&self, passcred: bool) -> io::Result<()> {
+ setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int)
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux",))]
+ pub fn passcred(&self) -> io::Result<bool> {
+ let passcred: libc::c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED)?;
+ Ok(passcred != 0)
+ }
+
+ #[cfg(target_os = "netbsd")]
+ pub fn set_passcred(&self, passcred: bool) -> io::Result<()> {
+ setsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS, passcred as libc::c_int)
+ }
+
+ #[cfg(target_os = "netbsd")]
+ pub fn passcred(&self) -> io::Result<bool> {
+ let passcred: libc::c_int = getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)?;
+ Ok(passcred != 0)
+ }
+
+ #[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ let mut nonblocking = nonblocking as libc::c_int;
+ cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop)
+ }
+
+ #[cfg(any(target_os = "solaris", target_os = "illumos"))]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ // FIONBIO is inadequate for sockets on illumos/Solaris, so use the
+ // fcntl(F_[GS]ETFL)-based method provided by FileDesc instead.
+ self.0.set_nonblocking(nonblocking)
+ }
+
+ 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))) }
+ }
+
+ // This is used by sys_common code to abstract over Windows and Unix.
+ pub fn as_raw(&self) -> RawFd {
+ self.as_raw_fd()
+ }
+}
+
+impl AsInner<FileDesc> for Socket {
+ fn as_inner(&self) -> &FileDesc {
+ &self.0
+ }
+}
+
+impl IntoInner<FileDesc> for Socket {
+ fn into_inner(self) -> FileDesc {
+ self.0
+ }
+}
+
+impl FromInner<FileDesc> for Socket {
+ fn from_inner(file_desc: FileDesc) -> Self {
+ Self(file_desc)
+ }
+}
+
+impl AsFd for Socket {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+}
+
+impl AsRawFd for Socket {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for Socket {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
+impl FromRawFd for Socket {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ Self(FromRawFd::from_raw_fd(raw_fd))
+ }
+}
+
+// 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(all(target_os = "linux", target_env = "gnu"))]
+fn on_resolver_failure() {
+ use crate::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(all(target_os = "linux", target_env = "gnu")))]
+fn on_resolver_failure() {}
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
new file mode 100644
index 000000000..46545a083
--- /dev/null
+++ b/library/std/src/sys/unix/os.rs
@@ -0,0 +1,680 @@
+//! Implementation of `std::os` functionality for unix systems
+
+#![allow(unused_imports)] // lots of cfg code here
+
+#[cfg(test)]
+mod tests;
+
+use crate::os::unix::prelude::*;
+
+use crate::error::Error as StdError;
+use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::iter;
+use crate::mem;
+use crate::path::{self, PathBuf};
+use crate::ptr;
+use crate::slice;
+use crate::str;
+use crate::sys::cvt;
+use crate::sys::fd;
+use crate::sys::memchr;
+use crate::sys_common::rwlock::{StaticRwLock, StaticRwLockReadGuard};
+use crate::vec;
+
+#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
+use crate::sys::weak::weak;
+
+use libc::{c_char, c_int, c_void};
+
+const TMPBUF_SZ: usize = 128;
+
+cfg_if::cfg_if! {
+ if #[cfg(target_os = "redox")] {
+ const PATH_SEPARATOR: u8 = b';';
+ } else {
+ const PATH_SEPARATOR: u8 = b':';
+ }
+}
+
+extern "C" {
+ #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))]
+ #[cfg_attr(
+ any(
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "l4re"
+ ),
+ link_name = "__errno_location"
+ )]
+ #[cfg_attr(
+ any(
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "android",
+ target_os = "redox",
+ target_env = "newlib"
+ ),
+ link_name = "__errno"
+ )]
+ #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")]
+ #[cfg_attr(
+ any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "watchos"),
+ 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(any(target_os = "dragonfly", target_os = "vxworks")))]
+pub fn errno() -> i32 {
+ unsafe { (*errno_location()) as i32 }
+}
+
+/// Sets the platform-specific value of errno
+#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks")))] // needed for readdir and syscall!
+#[allow(dead_code)] // but not all target cfgs actually end up using it
+pub fn set_errno(e: i32) {
+ unsafe { *errno_location() = e as c_int }
+}
+
+#[cfg(target_os = "vxworks")]
+pub fn errno() -> i32 {
+ unsafe { libc::errnoGet() }
+}
+
+#[cfg(target_os = "dragonfly")]
+pub fn errno() -> i32 {
+ extern "C" {
+ #[thread_local]
+ static errno: c_int;
+ }
+
+ unsafe { errno as i32 }
+}
+
+#[cfg(target_os = "dragonfly")]
+#[allow(dead_code)]
+pub fn set_errno(e: i32) {
+ extern "C" {
+ #[thread_local]
+ static mut errno: c_int;
+ }
+
+ unsafe {
+ errno = e;
+ }
+}
+
+/// Gets a detailed string description for the given error number.
+pub fn error_string(errno: i32) -> String {
+ extern "C" {
+ #[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()
+ }
+}
+
+#[cfg(target_os = "espidf")]
+pub fn getcwd() -> io::Result<PathBuf> {
+ Ok(PathBuf::from("/"))
+}
+
+#[cfg(not(target_os = "espidf"))]
+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 libc::c_char).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);
+ }
+ }
+}
+
+#[cfg(target_os = "espidf")]
+pub fn chdir(p: &path::Path) -> io::Result<()> {
+ super::unsupported::unsupported()
+}
+
+#[cfg(not(target_os = "espidf"))]
+pub fn chdir(p: &path::Path) -> io::Result<()> {
+ let p: &OsStr = p.as_ref();
+ let p = CString::new(p.as_bytes())?;
+ if unsafe { libc::chdir(p.as_ptr()) } != 0 {
+ return Err(io::Error::last_os_error());
+ }
+ 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_separator(b: &u8) -> bool {
+ *b == PATH_SEPARATOR
+ }
+ let unparsed = unparsed.as_bytes();
+ SplitPaths {
+ iter: unparsed
+ .split(is_separator 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();
+
+ for (i, path) in paths.enumerate() {
+ let path = path.as_ref().as_bytes();
+ if i > 0 {
+ joined.push(PATH_SEPARATOR)
+ }
+ if path.contains(&PATH_SEPARATOR) {
+ 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 {
+ write!(f, "path segment contains separator `{}`", char::from(PATH_SEPARATOR))
+ }
+}
+
+impl StdError for JoinPathsError {
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "failed to join paths"
+ }
+}
+
+#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ 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)))
+ }
+}
+
+#[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::const_io_error!(
+ io::ErrorKind::Uncategorized,
+ "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 crate::fs::read_link(curproc_exe);
+ }
+ Err(io::const_io_error!(
+ io::ErrorKind::Uncategorized,
+ "/proc/curproc/exe doesn't point to regular file.",
+ ))
+ }
+ sysctl().or_else(|_| procfs())
+}
+
+#[cfg(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::const_io_error!(
+ io::ErrorKind::Uncategorized,
+ "no current exe available",
+ ));
+ }
+ let argv0 = CStr::from_ptr(argv[0]).to_bytes();
+ if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') {
+ crate::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> {
+ match crate::fs::read_link("/proc/self/exe") {
+ Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::const_io_error!(
+ io::ErrorKind::Uncategorized,
+ "no /proc/self/exe available. Is /proc mounted?",
+ )),
+ other => other,
+ }
+}
+
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsafe {
+ let mut sz: u32 = 0;
+ libc::_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 = libc::_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", target_os = "illumos"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ if let Ok(path) = crate::fs::read_link("/proc/self/path/a.out") {
+ Ok(path)
+ } else {
+ unsafe {
+ let path = libc::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> {
+ unsafe {
+ let mut info: mem::MaybeUninit<libc::image_info> = mem::MaybeUninit::uninit();
+ let mut cookie: i32 = 0;
+ // the executable can be found at team id 0
+ let result = libc::_get_next_image_info(
+ 0,
+ &mut cookie,
+ info.as_mut_ptr(),
+ mem::size_of::<libc::image_info>(),
+ );
+ if result != 0 {
+ use crate::io::ErrorKind;
+ Err(io::const_io_error!(ErrorKind::Uncategorized, "Error getting executable path"))
+ } else {
+ let name = CStr::from_ptr((*info.as_ptr()).name.as_ptr()).to_bytes();
+ Ok(PathBuf::from(OsStr::from_bytes(name)))
+ }
+ }
+}
+
+#[cfg(target_os = "redox")]
+pub fn current_exe() -> io::Result<PathBuf> {
+ crate::fs::read_to_string("sys:exe").map(PathBuf::from)
+}
+
+#[cfg(target_os = "l4re")]
+pub fn current_exe() -> io::Result<PathBuf> {
+ use crate::io::ErrorKind;
+ Err(io::const_io_error!(ErrorKind::Unsupported, "Not yet implemented!"))
+}
+
+#[cfg(target_os = "vxworks")]
+pub fn current_exe() -> io::Result<PathBuf> {
+ #[cfg(test)]
+ use realstd::env;
+
+ #[cfg(not(test))]
+ use crate::env;
+
+ let exe_path = env::args().next().unwrap();
+ let path = path::Path::new(&exe_path);
+ path.canonicalize()
+}
+
+#[cfg(any(target_os = "espidf", target_os = "horizon"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ super::unsupported::unsupported()
+}
+
+#[cfg(target_os = "fuchsia")]
+pub fn current_exe() -> io::Result<PathBuf> {
+ use crate::io::ErrorKind;
+
+ #[cfg(test)]
+ use realstd::env;
+
+ #[cfg(not(test))]
+ use crate::env;
+
+ let exe_path = env::args().next().ok_or(io::const_io_error!(
+ ErrorKind::Uncategorized,
+ "an executable path was not found because no arguments were provided through argv"
+ ))?;
+ let path = PathBuf::from(exe_path);
+
+ // Prepend the current working directory to the path if it's not absolute.
+ if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) }
+}
+
+pub struct Env {
+ iter: vec::IntoIter<(OsString, OsString)>,
+}
+
+impl !Send for Env {}
+impl !Sync for Env {}
+
+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()
+ }
+}
+
+#[cfg(target_os = "macos")]
+pub unsafe fn environ() -> *mut *const *const c_char {
+ libc::_NSGetEnviron() as *mut *const *const c_char
+}
+
+#[cfg(not(target_os = "macos"))]
+pub unsafe fn environ() -> *mut *const *const c_char {
+ extern "C" {
+ static mut environ: *const *const c_char;
+ }
+ ptr::addr_of_mut!(environ)
+}
+
+static ENV_LOCK: StaticRwLock = StaticRwLock::new();
+
+pub fn env_read_lock() -> StaticRwLockReadGuard {
+ ENV_LOCK.read()
+}
+
+/// Returns a vector of (variable, value) byte-vector pairs for all the
+/// environment variables of the current process.
+pub fn env() -> Env {
+ unsafe {
+ let _guard = env_read_lock();
+ let mut environ = *environ();
+ let mut result = Vec::new();
+ if !environ.is_null() {
+ while !(*environ).is_null() {
+ if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+ result.push(key_value);
+ }
+ environ = environ.add(1);
+ }
+ }
+ return Env { iter: result.into_iter() };
+ }
+
+ 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) -> 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()).ok()?;
+ unsafe {
+ let _guard = env_read_lock();
+ let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
+ if s.is_null() {
+ None
+ } else {
+ Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
+ }
+ }
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+ let k = CString::new(k.as_bytes())?;
+ let v = CString::new(v.as_bytes())?;
+
+ unsafe {
+ let _guard = ENV_LOCK.write();
+ cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
+ }
+}
+
+pub fn unsetenv(n: &OsStr) -> io::Result<()> {
+ let nbuf = CString::new(n.as_bytes())?;
+
+ unsafe {
+ let _guard = ENV_LOCK.write();
+ cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
+ }
+}
+
+#[cfg(not(target_os = "espidf"))]
+pub fn page_size() -> usize {
+ unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
+}
+
+pub fn temp_dir() -> PathBuf {
+ crate::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> {
+ return crate::env::var_os("HOME").or_else(|| unsafe { fallback() }).map(PathBuf::from);
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "emscripten",
+ target_os = "redox",
+ target_os = "vxworks",
+ target_os = "espidf",
+ target_os = "horizon"
+ ))]
+ unsafe fn fallback() -> Option<OsString> {
+ None
+ }
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "emscripten",
+ target_os = "redox",
+ target_os = "vxworks",
+ target_os = "espidf",
+ target_os = "horizon"
+ )))]
+ 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) -> ! {
+ unsafe { libc::exit(code as c_int) }
+}
+
+pub fn getpid() -> u32 {
+ unsafe { libc::getpid() as u32 }
+}
+
+pub fn getppid() -> u32 {
+ unsafe { libc::getppid() as u32 }
+}
+
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
+pub fn glibc_version() -> Option<(usize, usize)> {
+ extern "C" {
+ fn gnu_get_libc_version() -> *const libc::c_char;
+ }
+ let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) };
+ if let Ok(version_str) = version_cstr.to_str() {
+ parse_glibc_version(version_str)
+ } 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(all(target_os = "linux", 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/library/std/src/sys/unix/os/tests.rs b/library/std/src/sys/unix/os/tests.rs
new file mode 100644
index 000000000..efc29955b
--- /dev/null
+++ b/library/std/src/sys/unix/os/tests.rs
@@ -0,0 +1,23 @@
+#[test]
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
+fn test_glibc_version() {
+ // This mostly just tests that the weak linkage doesn't panic wildly...
+ super::glibc_version();
+}
+
+#[test]
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
+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, super::parse_glibc_version(version_str));
+ }
+}
diff --git a/library/std/src/sys/unix/os_str.rs b/library/std/src/sys/unix/os_str.rs
new file mode 100644
index 000000000..ccbc18224
--- /dev/null
+++ b/library/std/src/sys/unix/os_str.rs
@@ -0,0 +1,266 @@
+//! The underlying OsString/OsStr implementation on Unix and many other
+//! systems: just a `Vec<u8>`/`[u8]`.
+
+use crate::borrow::Cow;
+use crate::collections::TryReserveError;
+use crate::fmt;
+use crate::fmt::Write;
+use crate::mem;
+use crate::rc::Rc;
+use crate::str;
+use crate::sync::Arc;
+use crate::sys_common::{AsInner, IntoInner};
+
+use core::str::lossy::{Utf8Lossy, Utf8LossyChunk};
+
+#[cfg(test)]
+#[path = "../unix/os_str/tests.rs"]
+mod tests;
+
+#[derive(Hash)]
+#[repr(transparent)]
+pub struct Buf {
+ pub inner: Vec<u8>,
+}
+
+#[repr(transparent)]
+pub struct Slice {
+ pub inner: [u8],
+}
+
+impl fmt::Debug for Slice {
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Writes out a valid unicode string with the correct escape sequences
+
+ formatter.write_str("\"")?;
+ for Utf8LossyChunk { valid, broken } in Utf8Lossy::from_bytes(&self.inner).chunks() {
+ for c in valid.chars().flat_map(|c| c.escape_debug()) {
+ formatter.write_char(c)?
+ }
+
+ for b in broken {
+ write!(formatter, "\\x{:02X}", b)?;
+ }
+ }
+ formatter.write_str("\"")
+ }
+}
+
+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 Clone for Buf {
+ #[inline]
+ fn clone(&self) -> Self {
+ Buf { inner: self.inner.clone() }
+ }
+
+ #[inline]
+ fn clone_from(&mut self, source: &Self) {
+ self.inner.clone_from(&source.inner)
+ }
+}
+
+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 try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
+ self.inner.try_reserve(additional)
+ }
+
+ #[inline]
+ pub fn reserve_exact(&mut self, additional: usize) {
+ self.inner.reserve_exact(additional)
+ }
+
+ #[inline]
+ pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
+ self.inner.try_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)
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &Slice {
+ // SAFETY: Slice just wraps [u8],
+ // and &*self.inner is &[u8], therefore
+ // transmuting &[u8] to &Slice is safe.
+ unsafe { mem::transmute(&*self.inner) }
+ }
+
+ #[inline]
+ pub fn as_mut_slice(&mut self) -> &mut Slice {
+ // SAFETY: Slice just wraps [u8],
+ // and &mut *self.inner is &mut [u8], therefore
+ // transmuting &mut [u8] to &mut Slice is safe.
+ unsafe { mem::transmute(&mut *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 {
+ #[inline]
+ fn from_u8_slice(s: &[u8]) -> &Slice {
+ unsafe { mem::transmute(s) }
+ }
+
+ #[inline]
+ 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() }
+ }
+
+ pub fn clone_into(&self, buf: &mut Buf) {
+ self.inner.clone_into(&mut buf.inner)
+ }
+
+ #[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) }
+ }
+
+ #[inline]
+ pub fn make_ascii_lowercase(&mut self) {
+ self.inner.make_ascii_lowercase()
+ }
+
+ #[inline]
+ pub fn make_ascii_uppercase(&mut self) {
+ self.inner.make_ascii_uppercase()
+ }
+
+ #[inline]
+ pub fn to_ascii_lowercase(&self) -> Buf {
+ Buf { inner: self.inner.to_ascii_lowercase() }
+ }
+
+ #[inline]
+ pub fn to_ascii_uppercase(&self) -> Buf {
+ Buf { inner: self.inner.to_ascii_uppercase() }
+ }
+
+ #[inline]
+ pub fn is_ascii(&self) -> bool {
+ self.inner.is_ascii()
+ }
+
+ #[inline]
+ pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
+ self.inner.eq_ignore_ascii_case(&other.inner)
+ }
+}
diff --git a/library/std/src/sys/unix/os_str/tests.rs b/library/std/src/sys/unix/os_str/tests.rs
new file mode 100644
index 000000000..213277f01
--- /dev/null
+++ b/library/std/src/sys/unix/os_str/tests.rs
@@ -0,0 +1,10 @@
+use super::*;
+
+#[test]
+fn slice_debug_output() {
+ let input = Slice::from_u8_slice(b"\xF0hello,\tworld");
+ let expected = r#""\xF0hello,\tworld""#;
+ let output = format!("{input:?}");
+
+ assert_eq!(output, expected);
+}
diff --git a/library/std/src/sys/unix/path.rs b/library/std/src/sys/unix/path.rs
new file mode 100644
index 000000000..a98a69e2d
--- /dev/null
+++ b/library/std/src/sys/unix/path.rs
@@ -0,0 +1,63 @@
+use crate::env;
+use crate::ffi::OsStr;
+use crate::io;
+use crate::path::{Path, PathBuf, Prefix};
+
+#[inline]
+pub fn is_sep_byte(b: u8) -> bool {
+ b == b'/'
+}
+
+#[inline]
+pub fn is_verbatim_sep(b: u8) -> bool {
+ b == b'/'
+}
+
+#[inline]
+pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
+ None
+}
+
+pub const MAIN_SEP_STR: &str = "/";
+pub const MAIN_SEP: char = '/';
+
+/// Make a POSIX path absolute without changing its semantics.
+pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
+ // This is mostly a wrapper around collecting `Path::components`, with
+ // exceptions made where this conflicts with the POSIX specification.
+ // See 4.13 Pathname Resolution, IEEE Std 1003.1-2017
+ // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
+
+ // Get the components, skipping the redundant leading "." component if it exists.
+ let mut components = path.strip_prefix(".").unwrap_or(path).components();
+ let path_os = path.as_os_str().bytes();
+
+ let mut normalized = if path.is_absolute() {
+ // "If a pathname begins with two successive <slash> characters, the
+ // first component following the leading <slash> characters may be
+ // interpreted in an implementation-defined manner, although more than
+ // two leading <slash> characters shall be treated as a single <slash>
+ // character."
+ if path_os.starts_with(b"//") && !path_os.starts_with(b"///") {
+ components.next();
+ PathBuf::from("//")
+ } else {
+ PathBuf::new()
+ }
+ } else {
+ env::current_dir()?
+ };
+ normalized.extend(components);
+
+ // "Interfaces using pathname resolution may specify additional constraints
+ // when a pathname that does not name an existing directory contains at
+ // least one non- <slash> character and contains one or more trailing
+ // <slash> characters".
+ // A trailing <slash> is also meaningful if "a symbolic link is
+ // encountered during pathname resolution".
+ if path_os.ends_with(b"/") {
+ normalized.push("");
+ }
+
+ Ok(normalized)
+}
diff --git a/library/std/src/sys/unix/pipe.rs b/library/std/src/sys/unix/pipe.rs
new file mode 100644
index 000000000..a56c275c9
--- /dev/null
+++ b/library/std/src/sys/unix/pipe.rs
@@ -0,0 +1,151 @@
+use crate::io::{self, IoSlice, IoSliceMut};
+use crate::mem;
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
+use crate::sys::fd::FileDesc;
+use crate::sys::{cvt, cvt_r};
+use crate::sys_common::IntoInner;
+
+////////////////////////////////////////////////////////////////////////////////
+// Anonymous pipes
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct AnonPipe(FileDesc);
+
+pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
+ let mut fds = [0; 2];
+
+ // The only known way right now to create atomically set the CLOEXEC flag is
+ // to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9
+ // and musl 0.9.3, and some other targets also have it.
+ cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "redox"
+ ))] {
+ unsafe {
+ cvt(libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC))?;
+ Ok((AnonPipe(FileDesc::from_raw_fd(fds[0])), AnonPipe(FileDesc::from_raw_fd(fds[1]))))
+ }
+ } else {
+ unsafe {
+ cvt(libc::pipe(fds.as_mut_ptr()))?;
+
+ let fd0 = FileDesc::from_raw_fd(fds[0]);
+ let fd1 = FileDesc::from_raw_fd(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 read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.0.read_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ self.0.is_read_vectored()
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.0.write_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ self.0.is_write_vectored()
+ }
+}
+
+impl IntoInner<FileDesc> for AnonPipe {
+ fn into_inner(self) -> FileDesc {
+ self.0
+ }
+}
+
+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_inner();
+ let p2 = p2.into_inner();
+ p1.set_nonblocking(true)?;
+ p2.set_nonblocking(true)?;
+
+ let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
+ fds[0].fd = p1.as_raw_fd();
+ fds[0].events = libc::POLLIN;
+ fds[1].fd = p2.as_raw_fd();
+ 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(drop);
+ }
+ if fds[1].revents != 0 && read(&p2, v2)? {
+ p1.set_nonblocking(false)?;
+ return p1.read_to_end(v1).map(drop);
+ }
+ }
+
+ // 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)
+ }
+ }
+ }
+ }
+}
+
+impl AsRawFd for AnonPipe {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl AsFd for AnonPipe {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+}
+
+impl IntoRawFd for AnonPipe {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
+impl FromRawFd for AnonPipe {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ Self(FromRawFd::from_raw_fd(raw_fd))
+ }
+}
diff --git a/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs
new file mode 100644
index 000000000..3701510f3
--- /dev/null
+++ b/library/std/src/sys/unix/process/mod.rs
@@ -0,0 +1,24 @@
+pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes};
+pub use self::process_inner::{ExitStatus, ExitStatusError, Process};
+pub use crate::ffi::OsString as EnvKey;
+pub use crate::sys_common::process::CommandEnvs;
+
+#[cfg_attr(any(target_os = "espidf", target_os = "horizon"), allow(unused))]
+mod process_common;
+
+cfg_if::cfg_if! {
+ if #[cfg(target_os = "fuchsia")] {
+ #[path = "process_fuchsia.rs"]
+ mod process_inner;
+ mod zircon;
+ } else if #[cfg(target_os = "vxworks")] {
+ #[path = "process_vxworks.rs"]
+ mod process_inner;
+ } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] {
+ #[path = "process_unsupported.rs"]
+ mod process_inner;
+ } else {
+ #[path = "process_unix.rs"]
+ mod process_inner;
+ }
+}
diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs
new file mode 100644
index 000000000..bca1b65a7
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -0,0 +1,523 @@
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
+use crate::os::unix::prelude::*;
+
+use crate::collections::BTreeMap;
+use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::path::Path;
+use crate::ptr;
+use crate::sys::fd::FileDesc;
+use crate::sys::fs::File;
+use crate::sys::pipe::{self, AnonPipe};
+use crate::sys_common::process::{CommandEnv, CommandEnvs};
+use crate::sys_common::IntoInner;
+
+#[cfg(not(target_os = "fuchsia"))]
+use crate::sys::fs::OpenOptions;
+
+use libc::{c_char, c_int, gid_t, pid_t, uid_t, EXIT_FAILURE, EXIT_SUCCESS};
+
+cfg_if::cfg_if! {
+ if #[cfg(target_os = "fuchsia")] {
+ // fuchsia doesn't have /dev/null
+ } else if #[cfg(target_os = "redox")] {
+ const DEV_NULL: &str = "null:\0";
+ } else if #[cfg(target_os = "vxworks")] {
+ const DEV_NULL: &str = "/null\0";
+ } else {
+ const DEV_NULL: &str = "/dev/null\0";
+ }
+}
+
+// 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://github.com/aosp-mirror/platform_bionic/blob/ad8dcd6023294b646e5a8288c0ed431b0845da49/libc/include/android/legacy_signal_inlines.h
+cfg_if::cfg_if! {
+ if #[cfg(target_os = "android")] {
+ pub unsafe fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int {
+ set.write_bytes(0u8, 1);
+ return 0;
+ }
+ #[allow(dead_code)]
+ pub unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int {
+ use crate::{slice, mem};
+
+ 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;
+ }
+ } else {
+ pub use libc::{sigemptyset, sigaddset};
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct Command {
+ program: CString,
+ args: Vec<CString>,
+ /// Exactly what will be passed to `execvp`.
+ ///
+ /// First element is a pointer to `program`, followed by pointers to
+ /// `args`, followed by a `null`. Be careful when modifying `program` or
+ /// `args` to properly update this as well.
+ argv: Argv,
+ env: CommandEnv,
+
+ cwd: Option<CString>,
+ uid: Option<uid_t>,
+ gid: Option<gid_t>,
+ saw_nul: bool,
+ closures: Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>>,
+ groups: Option<Box<[gid_t]>>,
+ stdin: Option<Stdio>,
+ stdout: Option<Stdio>,
+ stderr: Option<Stdio>,
+ #[cfg(target_os = "linux")]
+ create_pidfd: bool,
+ pgroup: Option<pid_t>,
+}
+
+// Create a new type for argv, so that we can make it `Send` and `Sync`
+struct Argv(Vec<*const c_char>);
+
+// It is safe to make `Argv` `Send` and `Sync`, because it contains
+// pointers to memory owned by `Command.args`
+unsafe impl Send for Argv {}
+unsafe impl Sync 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),
+
+ // On Fuchsia, null stdio is the default, so we simply don't specify
+ // any actions at the time of spawning.
+ #[cfg(target_os = "fuchsia")]
+ Null,
+}
+
+pub enum Stdio {
+ Inherit,
+ Null,
+ MakePipe,
+ Fd(FileDesc),
+}
+
+impl Command {
+ #[cfg(not(target_os = "linux"))]
+ 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()]),
+ args: vec![program.clone()],
+ program,
+ env: Default::default(),
+ cwd: None,
+ uid: None,
+ gid: None,
+ saw_nul,
+ closures: Vec::new(),
+ groups: None,
+ stdin: None,
+ stdout: None,
+ stderr: None,
+ pgroup: None,
+ }
+ }
+
+ #[cfg(target_os = "linux")]
+ 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()]),
+ args: vec![program.clone()],
+ program,
+ env: Default::default(),
+ cwd: None,
+ uid: None,
+ gid: None,
+ saw_nul,
+ closures: Vec::new(),
+ groups: None,
+ stdin: None,
+ stdout: None,
+ stderr: None,
+ create_pidfd: false,
+ pgroup: None,
+ }
+ }
+
+ pub fn set_arg_0(&mut self, arg: &OsStr) {
+ // Set a new arg0
+ let arg = os2c(arg, &mut self.saw_nul);
+ debug_assert!(self.argv.0.len() > 1);
+ self.argv.0[0] = arg.as_ptr();
+ self.args[0] = arg;
+ }
+
+ 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()] = 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 groups(&mut self, groups: &[gid_t]) {
+ self.groups = Some(Box::from(groups));
+ }
+ pub fn pgroup(&mut self, pgroup: pid_t) {
+ self.pgroup = Some(pgroup);
+ }
+
+ #[cfg(target_os = "linux")]
+ pub fn create_pidfd(&mut self, val: bool) {
+ self.create_pidfd = val;
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ #[allow(dead_code)]
+ pub fn get_create_pidfd(&self) -> bool {
+ false
+ }
+
+ #[cfg(target_os = "linux")]
+ pub fn get_create_pidfd(&self) -> bool {
+ self.create_pidfd
+ }
+
+ pub fn saw_nul(&self) -> bool {
+ self.saw_nul
+ }
+
+ pub fn get_program(&self) -> &OsStr {
+ OsStr::from_bytes(self.program.as_bytes())
+ }
+
+ pub fn get_args(&self) -> CommandArgs<'_> {
+ let mut iter = self.args.iter();
+ iter.next();
+ CommandArgs { iter }
+ }
+
+ pub fn get_envs(&self) -> CommandEnvs<'_> {
+ self.env.iter()
+ }
+
+ pub fn get_current_dir(&self) -> Option<&Path> {
+ self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes())))
+ }
+
+ pub fn get_argv(&self) -> &Vec<*const c_char> {
+ &self.argv.0
+ }
+
+ pub fn get_program_cstr(&self) -> &CStr {
+ &*self.program
+ }
+
+ #[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
+ }
+ #[allow(dead_code)]
+ pub fn get_groups(&self) -> Option<&[gid_t]> {
+ self.groups.as_deref()
+ }
+ #[allow(dead_code)]
+ pub fn get_pgroup(&self) -> Option<pid_t> {
+ self.pgroup
+ }
+
+ pub fn get_closures(&mut self) -> &mut Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>> {
+ &mut self.closures
+ }
+
+ pub unsafe fn pre_exec(&mut self, f: Box<dyn 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 {
+ &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()
+ }
+
+ #[allow(dead_code)]
+ pub fn program_is_path(&self) -> bool {
+ self.program.to_bytes().contains(&b'/')
+ }
+
+ 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<OsString, OsString>, saw_nul: &mut bool) -> CStringArray {
+ let mut result = CStringArray::with_capacity(env.len());
+ for (mut k, v) in env {
+ // 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.as_raw_fd() >= 0 && fd.as_raw_fd() <= libc::STDERR_FILENO {
+ Ok((ChildStdio::Owned(fd.duplicate()?), None))
+ } else {
+ Ok((ChildStdio::Explicit(fd.as_raw_fd()), None))
+ }
+ }
+
+ Stdio::MakePipe => {
+ let (reader, writer) = pipe::anon_pipe()?;
+ let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) };
+ Ok((ChildStdio::Owned(theirs.into_inner()), Some(ours)))
+ }
+
+ #[cfg(not(target_os = "fuchsia"))]
+ Stdio::Null => {
+ let mut opts = OpenOptions::new();
+ opts.read(readable);
+ opts.write(!readable);
+ let path = unsafe { CStr::from_ptr(DEV_NULL.as_ptr() as *const _) };
+ let fd = File::open_c(&path, &opts)?;
+ Ok((ChildStdio::Owned(fd.into_inner()), None))
+ }
+
+ #[cfg(target_os = "fuchsia")]
+ Stdio::Null => Ok((ChildStdio::Null, None)),
+ }
+ }
+}
+
+impl From<AnonPipe> for Stdio {
+ fn from(pipe: AnonPipe) -> Stdio {
+ Stdio::Fd(pipe.into_inner())
+ }
+}
+
+impl From<File> for Stdio {
+ fn from(file: File) -> Stdio {
+ Stdio::Fd(file.into_inner())
+ }
+}
+
+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.as_raw_fd()),
+
+ #[cfg(target_os = "fuchsia")]
+ ChildStdio::Null => None,
+ }
+ }
+}
+
+impl fmt::Debug for Command {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.program != self.args[0] {
+ write!(f, "[{:?}] ", self.program)?;
+ }
+ write!(f, "{:?}", self.args[0])?;
+
+ for arg in &self.args[1..] {
+ write!(f, " {:?}", arg)?;
+ }
+ Ok(())
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy)]
+pub struct ExitCode(u8);
+
+impl fmt::Debug for ExitCode {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_tuple("unix_exit_status").field(&self.0).finish()
+ }
+}
+
+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
+ }
+}
+
+impl From<u8> for ExitCode {
+ fn from(code: u8) -> Self {
+ Self(code)
+ }
+}
+
+pub struct CommandArgs<'a> {
+ iter: crate::slice::Iter<'a, CString>,
+}
+
+impl<'a> Iterator for CommandArgs<'a> {
+ type Item = &'a OsStr;
+ fn next(&mut self) -> Option<&'a OsStr> {
+ self.iter.next().map(|cs| OsStr::from_bytes(cs.as_bytes()))
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+impl<'a> ExactSizeIterator for CommandArgs<'a> {
+ fn len(&self) -> usize {
+ self.iter.len()
+ }
+ fn is_empty(&self) -> bool {
+ self.iter.is_empty()
+ }
+}
+
+impl<'a> fmt::Debug for CommandArgs<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().entries(self.iter.clone()).finish()
+ }
+}
diff --git a/library/std/src/sys/unix/process/process_common/tests.rs b/library/std/src/sys/unix/process/process_common/tests.rs
new file mode 100644
index 000000000..1956b3692
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_common/tests.rs
@@ -0,0 +1,124 @@
+use super::*;
+
+use crate::ffi::OsStr;
+use crate::mem;
+use crate::ptr;
+use crate::sys::{cvt, cvt_nz};
+
+macro_rules! t {
+ ($e:expr) => {
+ match $e {
+ Ok(t) => t,
+ Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
+ }
+ };
+}
+
+#[test]
+#[cfg_attr(
+ any(
+ // 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.
+ target_os = "macos",
+ // 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.
+ target_arch = "arm",
+ target_arch = "aarch64",
+ target_arch = "riscv64",
+ ),
+ 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 = mem::MaybeUninit::<libc::sigset_t>::uninit();
+ let mut old_set = mem::MaybeUninit::<libc::sigset_t>::uninit();
+ t!(cvt(sigemptyset(set.as_mut_ptr())));
+ t!(cvt(sigaddset(set.as_mut_ptr(), libc::SIGINT)));
+ t!(cvt_nz(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), old_set.as_mut_ptr())));
+
+ 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_nz(libc::pthread_sigmask(libc::SIG_SETMASK, old_set.as_ptr(), 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());
+ }
+}
+
+#[test]
+#[cfg_attr(
+ any(
+ // See test_process_mask
+ target_os = "macos",
+ target_arch = "arm",
+ target_arch = "aarch64",
+ target_arch = "riscv64",
+ ),
+ ignore
+)]
+fn test_process_group_posix_spawn() {
+ unsafe {
+ // Spawn a cat subprocess that's just going to hang since there is no I/O.
+ let mut cmd = Command::new(OsStr::new("cat"));
+ cmd.pgroup(0);
+ cmd.stdin(Stdio::MakePipe);
+ cmd.stdout(Stdio::MakePipe);
+ let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true));
+
+ // Check that we can kill its process group, which means there *is* one.
+ t!(cvt(libc::kill(-(cat.id() as libc::pid_t), libc::SIGINT)));
+
+ t!(cat.wait());
+ }
+}
+
+#[test]
+#[cfg_attr(
+ any(
+ // See test_process_mask
+ target_os = "macos",
+ target_arch = "arm",
+ target_arch = "aarch64",
+ target_arch = "riscv64",
+ ),
+ ignore
+)]
+fn test_process_group_no_posix_spawn() {
+ unsafe {
+ // Same as above, create hang-y cat. This time, force using the non-posix_spawnp path.
+ let mut cmd = Command::new(OsStr::new("cat"));
+ cmd.pgroup(0);
+ cmd.pre_exec(Box::new(|| Ok(()))); // pre_exec forces fork + exec
+ cmd.stdin(Stdio::MakePipe);
+ cmd.stdout(Stdio::MakePipe);
+ let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true));
+
+ // Check that we can kill its process group, which means there *is* one.
+ t!(cvt(libc::kill(-(cat.id() as libc::pid_t), libc::SIGINT)));
+
+ t!(cat.wait());
+ }
+}
diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs
new file mode 100644
index 000000000..73f5d3a61
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_fuchsia.rs
@@ -0,0 +1,327 @@
+use crate::fmt;
+use crate::io;
+use crate::mem;
+use crate::num::{NonZeroI32, NonZeroI64};
+use crate::ptr;
+
+use crate::sys::process::process_common::*;
+use crate::sys::process::zircon::{zx_handle_t, Handle};
+
+use libc::{c_int, size_t};
+
+////////////////////////////////////////////////////////////////////////////////
+// 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::const_io_error!(
+ 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::const_io_error!(
+ 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 crate::sys::process::zircon::*;
+
+ let envp = match maybe_envp {
+ // None means to clone the current environment, which is done in the
+ // flags below.
+ None => ptr::null(),
+ Some(envp) => envp.as_ptr(),
+ };
+
+ let make_action = |local_io: &ChildStdio, target_fd| -> io::Result<fdio_spawn_action_t> {
+ if let Some(local_fd) = local_io.fd() {
+ Ok(fdio_spawn_action_t {
+ action: FDIO_SPAWN_ACTION_TRANSFER_FD,
+ local_fd,
+ target_fd,
+ ..Default::default()
+ })
+ } else {
+ if let ChildStdio::Null = local_io {
+ // acts as no-op
+ return Ok(Default::default());
+ }
+
+ let mut handle = ZX_HANDLE_INVALID;
+ let status = fdio_fd_clone(target_fd, &mut handle);
+ if status == ERR_INVALID_ARGS || status == ERR_NOT_SUPPORTED {
+ // This descriptor is closed; skip it rather than generating an
+ // error.
+ return Ok(Default::default());
+ }
+ zx_cvt(status)?;
+
+ let mut cloned_fd = 0;
+ zx_cvt(fdio_fd_create(handle, &mut cloned_fd))?;
+
+ Ok(fdio_spawn_action_t {
+ action: FDIO_SPAWN_ACTION_TRANSFER_FD,
+ local_fd: cloned_fd as i32,
+ target_fd,
+ ..Default::default()
+ })
+ }
+ };
+
+ // Clone stdin, stdout, and stderr
+ let action1 = make_action(&stdio.stdin, 0)?;
+ let action2 = make_action(&stdio.stdout, 1)?;
+ let action3 = make_action(&stdio.stderr, 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(
+ ZX_HANDLE_INVALID,
+ FDIO_SPAWN_CLONE_JOB
+ | FDIO_SPAWN_CLONE_LDSVC
+ | FDIO_SPAWN_CLONE_NAMESPACE
+ | FDIO_SPAWN_CLONE_ENVIRON // this is ignored when envp is non-null
+ | FDIO_SPAWN_CLONE_UTC_CLOCK,
+ self.get_program_cstr().as_ptr(),
+ self.get_argv().as_ptr(),
+ envp,
+ actions.len() as size_t,
+ 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 crate::sys::process::zircon::*;
+
+ unsafe {
+ zx_cvt(zx_task_kill(self.handle.raw()))?;
+ }
+
+ Ok(())
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ use crate::default::Default;
+ use crate::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::const_io_error!(
+ io::ErrorKind::InvalidData,
+ "Failed to get exit status of process",
+ ));
+ }
+ Ok(ExitStatus(proc_info.return_code))
+ }
+
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+ use crate::default::Default;
+ use crate::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::const_io_error!(
+ io::ErrorKind::InvalidData,
+ "Failed to get exit status of process",
+ ));
+ }
+ Ok(Some(ExitStatus(proc_info.return_code)))
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatus(i64);
+
+impl ExitStatus {
+ pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
+ match NonZeroI64::try_from(self.0) {
+ /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
+ /* was zero, couldn't convert */ Err(_) => Ok(()),
+ }
+ }
+
+ pub fn code(&self) -> Option<i32> {
+ // FIXME: support extracting return code as an i64
+ self.0.try_into().ok()
+ }
+
+ pub fn signal(&self) -> Option<i32> {
+ None
+ }
+
+ // FIXME: The actually-Unix implementation in process_unix.rs uses WSTOPSIG, WCOREDUMP et al.
+ // I infer from the implementation of `success`, `code` and `signal` above that these are not
+ // available on Fuchsia.
+ //
+ // It does not appear that Fuchsia is Unix-like enough to implement ExitStatus (or indeed many
+ // other things from std::os::unix) properly. This veneer is always going to be a bodge. So
+ // while I don't know if these implementations are actually correct, I think they will do for
+ // now at least.
+ pub fn core_dumped(&self) -> bool {
+ false
+ }
+ pub fn stopped_signal(&self) -> Option<i32> {
+ None
+ }
+ pub fn continued(&self) -> bool {
+ false
+ }
+
+ pub fn into_raw(&self) -> c_int {
+ // We don't know what someone who calls into_raw() will do with this value, but it should
+ // have the conventional Unix representation. Despite the fact that this is not
+ // standardised in SuS or POSIX, all Unix systems encode the signal and exit status the
+ // same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behaviour on every
+ // Unix.)
+ //
+ // The caller of `std::os::unix::into_raw` is probably wanting a Unix exit status, and may
+ // do their own shifting and masking, or even pass the status to another computer running a
+ // different Unix variant.
+ //
+ // The other view would be to say that the caller on Fuchsia ought to know that `into_raw`
+ // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is
+ // not possible here because we must return a c_int because that's what Unix (including
+ // SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't
+ // necessarily fit.
+ //
+ // It seems to me that that the right answer would be to provide std::os::fuchsia with its
+ // own ExitStatusExt, rather that trying to provide a not very convincing imitation of
+ // Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But
+ // fixing this up that is beyond the scope of my efforts now.
+ let exit_status_as_if_unix: u8 = self.0.try_into().expect("Fuchsia process return code bigger than 8 bits, but std::os::unix::ExitStatusExt::into_raw() was called to try to convert the value into a traditional Unix-style wait status, which cannot represent values greater than 255.");
+ let wait_status_as_if_unix = (exit_status_as_if_unix as c_int) << 8;
+ wait_status_as_if_unix
+ }
+}
+
+/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
+impl From<c_int> for ExitStatus {
+ fn from(a: c_int) -> ExitStatus {
+ ExitStatus(a as i64)
+ }
+}
+
+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 ExitStatusError(NonZeroI64);
+
+impl Into<ExitStatus> for ExitStatusError {
+ fn into(self) -> ExitStatus {
+ ExitStatus(self.0.into())
+ }
+}
+
+impl ExitStatusError {
+ pub fn code(self) -> Option<NonZeroI32> {
+ // fixme: affected by the same bug as ExitStatus::code()
+ ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
+ }
+}
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
new file mode 100644
index 000000000..75bb92437
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -0,0 +1,836 @@
+use crate::fmt;
+use crate::io::{self, Error, ErrorKind};
+use crate::mem;
+use crate::num::NonZeroI32;
+use crate::ptr;
+use crate::sys;
+use crate::sys::cvt;
+use crate::sys::process::process_common::*;
+use core::ffi::NonZero_c_int;
+
+#[cfg(target_os = "linux")]
+use crate::os::linux::process::PidFd;
+
+#[cfg(target_os = "linux")]
+use crate::sys::weak::raw_syscall;
+
+#[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ all(target_os = "linux", target_env = "gnu"),
+ all(target_os = "linux", target_env = "musl"),
+))]
+use crate::sys::weak::weak;
+
+#[cfg(target_os = "vxworks")]
+use libc::RTP_ID as pid_t;
+
+#[cfg(not(target_os = "vxworks"))]
+use libc::{c_int, pid_t};
+
+#[cfg(not(any(target_os = "vxworks", target_os = "l4re")))]
+use libc::{gid_t, uid_t};
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+impl Command {
+ pub fn spawn(
+ &mut self,
+ default: Stdio,
+ needs_stdin: bool,
+ ) -> io::Result<(Process, StdioPipes)> {
+ const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX";
+
+ let envp = self.capture_env();
+
+ if self.saw_nul() {
+ return Err(io::const_io_error!(
+ 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()?;
+
+ // Whatever happens after the fork is almost for sure going to touch or
+ // look at the environment in one way or another (PATH in `execvp` or
+ // accessing the `environ` pointer ourselves). Make sure no other thread
+ // is accessing the environment when we do the fork itself.
+ //
+ // Note that as soon as we're done with the fork there's no need to hold
+ // a lock any more because the parent won't do anything and the child is
+ // in its own process. Thus the parent drops the lock guard while the child
+ // forgets it to avoid unlocking it on a new thread, which would be invalid.
+ let env_lock = sys::os::env_read_lock();
+ let (pid, pidfd) = unsafe { self.do_fork()? };
+
+ if pid == 0 {
+ crate::panic::always_abort();
+ mem::forget(env_lock);
+ drop(input);
+ let Err(err) = unsafe { self.do_exec(theirs, envp.as_ref()) };
+ let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
+ let errno = errno.to_be_bytes();
+ let bytes = [
+ errno[0],
+ errno[1],
+ errno[2],
+ errno[3],
+ 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
+ rtassert!(output.write(&bytes).is_ok());
+ unsafe { libc::_exit(1) }
+ }
+
+ drop(env_lock);
+ drop(output);
+
+ // Safety: We obtained the pidfd from calling `clone3` with
+ // `CLONE_PIDFD` so it's valid an otherwise unowned.
+ let mut p = unsafe { Process::new(pid, pidfd) };
+ let mut bytes = [0; 8];
+
+ // loop to handle EINTR
+ loop {
+ match input.read(&mut bytes) {
+ Ok(0) => return Ok((p, ours)),
+ Ok(8) => {
+ let (errno, footer) = bytes.split_at(4);
+ assert_eq!(
+ CLOEXEC_MSG_FOOTER, footer,
+ "Validation on the CLOEXEC pipe failed: {:?}",
+ bytes
+ );
+ let errno = i32::from_be_bytes(errno.try_into().unwrap());
+ 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")
+ }
+ }
+ }
+ }
+
+ // Attempts to fork the process. If successful, returns Ok((0, -1))
+ // in the child, and Ok((child_pid, -1)) in the parent.
+ #[cfg(not(target_os = "linux"))]
+ unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
+ cvt(libc::fork()).map(|res| (res, -1))
+ }
+
+ // Attempts to fork the process. If successful, returns Ok((0, -1))
+ // in the child, and Ok((child_pid, child_pidfd)) in the parent.
+ #[cfg(target_os = "linux")]
+ unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
+ use crate::sync::atomic::{AtomicBool, Ordering};
+
+ static HAS_CLONE3: AtomicBool = AtomicBool::new(true);
+ const CLONE_PIDFD: u64 = 0x00001000;
+
+ #[repr(C)]
+ struct clone_args {
+ flags: u64,
+ pidfd: u64,
+ child_tid: u64,
+ parent_tid: u64,
+ exit_signal: u64,
+ stack: u64,
+ stack_size: u64,
+ tls: u64,
+ set_tid: u64,
+ set_tid_size: u64,
+ cgroup: u64,
+ }
+
+ raw_syscall! {
+ fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long
+ }
+
+ // Bypassing libc for `clone3` can make further libc calls unsafe,
+ // so we use it sparingly for now. See #89522 for details.
+ // Some tools (e.g. sandboxing tools) may also expect `fork`
+ // rather than `clone3`.
+ let want_clone3_pidfd = self.get_create_pidfd();
+
+ // If we fail to create a pidfd for any reason, this will
+ // stay as -1, which indicates an error.
+ let mut pidfd: pid_t = -1;
+
+ // Attempt to use the `clone3` syscall, which supports more arguments
+ // (in particular, the ability to create a pidfd). If this fails,
+ // we will fall through this block to a call to `fork()`
+ if want_clone3_pidfd && HAS_CLONE3.load(Ordering::Relaxed) {
+ let mut args = clone_args {
+ flags: CLONE_PIDFD,
+ pidfd: &mut pidfd as *mut pid_t as u64,
+ child_tid: 0,
+ parent_tid: 0,
+ exit_signal: libc::SIGCHLD as u64,
+ stack: 0,
+ stack_size: 0,
+ tls: 0,
+ set_tid: 0,
+ set_tid_size: 0,
+ cgroup: 0,
+ };
+
+ let args_ptr = &mut args as *mut clone_args;
+ let args_size = crate::mem::size_of::<clone_args>();
+
+ let res = cvt(clone3(args_ptr, args_size));
+ match res {
+ Ok(n) => return Ok((n as pid_t, pidfd)),
+ Err(e) => match e.raw_os_error() {
+ // Multiple threads can race to execute this store,
+ // but that's fine - that just means that multiple threads
+ // will have tried and failed to execute the same syscall,
+ // with no other side effects.
+ Some(libc::ENOSYS) => HAS_CLONE3.store(false, Ordering::Relaxed),
+ // Fallback to fork if `EPERM` is returned. (e.g. blocked by seccomp)
+ Some(libc::EPERM) => {}
+ _ => return Err(e),
+ },
+ }
+ }
+
+ // Generally, we just call `fork`. If we get here after wanting `clone3`,
+ // then the syscall does not exist or we do not have permission to call it.
+ cvt(libc::fork()).map(|res| (res, pidfd))
+ }
+
+ pub fn exec(&mut self, default: Stdio) -> io::Error {
+ let envp = self.capture_env();
+
+ if self.saw_nul() {
+ return io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data",);
+ }
+
+ match self.setup_io(default, true) {
+ Ok((_, theirs)) => {
+ unsafe {
+ // Similar to when forking, we want to ensure that access to
+ // the environment is synchronized, so make sure to grab the
+ // environment lock before we try to exec.
+ let _lock = sys::os::env_read_lock();
+
+ let Err(e) = self.do_exec(theirs, envp.as_ref());
+ e
+ }
+ }
+ 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>,
+ ) -> Result<!, io::Error> {
+ use crate::sys::{self, cvt_r};
+
+ if let Some(fd) = stdio.stdin.fd() {
+ cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?;
+ }
+ if let Some(fd) = stdio.stdout.fd() {
+ cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?;
+ }
+ if let Some(fd) = stdio.stderr.fd() {
+ cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?;
+ }
+
+ #[cfg(not(target_os = "l4re"))]
+ {
+ if let Some(_g) = self.get_groups() {
+ //FIXME: Redox kernel does not support setgroups yet
+ #[cfg(not(target_os = "redox"))]
+ cvt(libc::setgroups(_g.len().try_into().unwrap(), _g.as_ptr()))?;
+ }
+ if let Some(u) = self.get_gid() {
+ 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. We only drop groups
+ // if the current uid is 0 and we weren't given an explicit
+ // set of 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.
+ //FIXME: Redox kernel does not support setgroups yet
+ #[cfg(not(target_os = "redox"))]
+ if libc::getuid() == 0 && self.get_groups().is_none() {
+ cvt(libc::setgroups(0, ptr::null()))?;
+ }
+ cvt(libc::setuid(u as uid_t))?;
+ }
+ }
+ if let Some(ref cwd) = *self.get_cwd() {
+ cvt(libc::chdir(cwd.as_ptr()))?;
+ }
+
+ if let Some(pgroup) = self.get_pgroup() {
+ cvt(libc::setpgid(0, pgroup))?;
+ }
+
+ // emscripten has no signal support.
+ #[cfg(not(target_os = "emscripten"))]
+ {
+ use crate::mem::MaybeUninit;
+ use crate::sys::cvt_nz;
+ // 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 = MaybeUninit::<libc::sigset_t>::uninit();
+ cvt(sigemptyset(set.as_mut_ptr()))?;
+ cvt_nz(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), ptr::null_mut()))?;
+
+ #[cfg(target_os = "android")] // see issue #88585
+ {
+ let mut action: libc::sigaction = mem::zeroed();
+ action.sa_sigaction = libc::SIG_DFL;
+ cvt(libc::sigaction(libc::SIGPIPE, &action, ptr::null_mut()))?;
+ }
+ #[cfg(not(target_os = "android"))]
+ {
+ let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
+ if ret == libc::SIG_ERR {
+ return Err(io::Error::last_os_error());
+ }
+ }
+ }
+
+ for callback in self.get_closures().iter_mut() {
+ callback()?;
+ }
+
+ // Although we're performing an exec here we may also return with an
+ // error from this function (without actually exec'ing) in which case we
+ // want to be sure to restore the global environment back to what it
+ // once was, ensuring that our temporary override, when free'd, doesn't
+ // corrupt our process's environment.
+ let mut _reset = None;
+ if let Some(envp) = maybe_envp {
+ struct Reset(*const *const libc::c_char);
+
+ impl Drop for Reset {
+ fn drop(&mut self) {
+ unsafe {
+ *sys::os::environ() = self.0;
+ }
+ }
+ }
+
+ _reset = Some(Reset(*sys::os::environ()));
+ *sys::os::environ() = envp.as_ptr();
+ }
+
+ libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
+ Err(io::Error::last_os_error())
+ }
+
+ #[cfg(not(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ all(target_os = "linux", target_env = "gnu"),
+ all(target_os = "linux", target_env = "musl"),
+ )))]
+ 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"),
+ all(target_os = "linux", target_env = "musl"),
+ ))]
+ fn posix_spawn(
+ &mut self,
+ stdio: &ChildPipes,
+ envp: Option<&CStringArray>,
+ ) -> io::Result<Option<Process>> {
+ use crate::mem::MaybeUninit;
+ use crate::sys::{self, cvt_nz};
+
+ if self.get_gid().is_some()
+ || self.get_uid().is_some()
+ || (self.env_saw_path() && !self.program_is_path())
+ || !self.get_closures().is_empty()
+ || self.get_groups().is_some()
+ || self.get_create_pidfd()
+ {
+ 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);
+ }
+ }
+
+ // Solaris, glibc 2.29+, and musl 1.24+ can set a new working directory,
+ // and maybe others will gain this non-POSIX function too. We'll check
+ // for this weak symbol as soon as it's needed, so we can return early
+ // otherwise to do a manual chdir before exec.
+ weak! {
+ fn posix_spawn_file_actions_addchdir_np(
+ *mut libc::posix_spawn_file_actions_t,
+ *const libc::c_char
+ ) -> libc::c_int
+ }
+ let addchdir = match self.get_cwd() {
+ Some(cwd) => {
+ if cfg!(target_os = "macos") {
+ // There is a bug in macOS where a relative executable
+ // path like "../myprogram" will cause `posix_spawn` to
+ // successfully launch the program, but erroneously return
+ // ENOENT when used with posix_spawn_file_actions_addchdir_np
+ // which was introduced in macOS 10.15.
+ return Ok(None);
+ }
+ match posix_spawn_file_actions_addchdir_np.get() {
+ Some(f) => Some((f, cwd)),
+ None => return Ok(None),
+ }
+ }
+ None => None,
+ };
+
+ let pgroup = self.get_pgroup();
+
+ // Safety: -1 indicates we don't have a pidfd.
+ let mut p = unsafe { Process::new(0, -1) };
+
+ struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit<libc::posix_spawn_file_actions_t>);
+
+ impl Drop for PosixSpawnFileActions<'_> {
+ fn drop(&mut self) {
+ unsafe {
+ libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr());
+ }
+ }
+ }
+
+ struct PosixSpawnattr<'a>(&'a mut MaybeUninit<libc::posix_spawnattr_t>);
+
+ impl Drop for PosixSpawnattr<'_> {
+ fn drop(&mut self) {
+ unsafe {
+ libc::posix_spawnattr_destroy(self.0.as_mut_ptr());
+ }
+ }
+ }
+
+ unsafe {
+ let mut attrs = MaybeUninit::uninit();
+ cvt_nz(libc::posix_spawnattr_init(attrs.as_mut_ptr()))?;
+ let attrs = PosixSpawnattr(&mut attrs);
+
+ let mut flags = 0;
+
+ let mut file_actions = MaybeUninit::uninit();
+ cvt_nz(libc::posix_spawn_file_actions_init(file_actions.as_mut_ptr()))?;
+ let file_actions = PosixSpawnFileActions(&mut file_actions);
+
+ if let Some(fd) = stdio.stdin.fd() {
+ cvt_nz(libc::posix_spawn_file_actions_adddup2(
+ file_actions.0.as_mut_ptr(),
+ fd,
+ libc::STDIN_FILENO,
+ ))?;
+ }
+ if let Some(fd) = stdio.stdout.fd() {
+ cvt_nz(libc::posix_spawn_file_actions_adddup2(
+ file_actions.0.as_mut_ptr(),
+ fd,
+ libc::STDOUT_FILENO,
+ ))?;
+ }
+ if let Some(fd) = stdio.stderr.fd() {
+ cvt_nz(libc::posix_spawn_file_actions_adddup2(
+ file_actions.0.as_mut_ptr(),
+ fd,
+ libc::STDERR_FILENO,
+ ))?;
+ }
+ if let Some((f, cwd)) = addchdir {
+ cvt_nz(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?;
+ }
+
+ if let Some(pgroup) = pgroup {
+ flags |= libc::POSIX_SPAWN_SETPGROUP;
+ cvt_nz(libc::posix_spawnattr_setpgroup(attrs.0.as_mut_ptr(), pgroup))?;
+ }
+
+ let mut set = MaybeUninit::<libc::sigset_t>::uninit();
+ cvt(sigemptyset(set.as_mut_ptr()))?;
+ cvt_nz(libc::posix_spawnattr_setsigmask(attrs.0.as_mut_ptr(), set.as_ptr()))?;
+ cvt(sigaddset(set.as_mut_ptr(), libc::SIGPIPE))?;
+ cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.0.as_mut_ptr(), set.as_ptr()))?;
+
+ flags |= libc::POSIX_SPAWN_SETSIGDEF | libc::POSIX_SPAWN_SETSIGMASK;
+ cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
+
+ // Make sure we synchronize access to the global `environ` resource
+ let _env_lock = sys::os::env_read_lock();
+ let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
+ cvt_nz(libc::posix_spawnp(
+ &mut p.pid,
+ self.get_program_cstr().as_ptr(),
+ file_actions.0.as_ptr(),
+ attrs.0.as_ptr(),
+ self.get_argv().as_ptr() as *const _,
+ envp as *const _,
+ ))?;
+ Ok(Some(p))
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+/// The unique ID of the process (this should never be negative).
+pub struct Process {
+ pid: pid_t,
+ status: Option<ExitStatus>,
+ // On Linux, stores the pidfd created for this child.
+ // This is None if the user did not request pidfd creation,
+ // or if the pidfd could not be created for some reason
+ // (e.g. the `clone3` syscall was not available).
+ #[cfg(target_os = "linux")]
+ pidfd: Option<PidFd>,
+}
+
+impl Process {
+ #[cfg(target_os = "linux")]
+ unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self {
+ use crate::os::unix::io::FromRawFd;
+ use crate::sys_common::FromInner;
+ // Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned.
+ let pidfd = (pidfd >= 0).then(|| PidFd::from_inner(sys::fd::FileDesc::from_raw_fd(pidfd)));
+ Process { pid, status: None, pidfd }
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ unsafe fn new(pid: pid_t, _pidfd: pid_t) -> Self {
+ Process { pid, status: None }
+ }
+
+ 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(io::const_io_error!(
+ ErrorKind::InvalidInput,
+ "invalid argument: can't kill an exited process",
+ ))
+ } else {
+ cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
+ }
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ use crate::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)))
+ }
+ }
+}
+
+/// Unix exit statuses
+//
+// This is not actually an "exit status" in Unix terminology. Rather, it is a "wait status".
+// See the discussion in comments and doc comments for `std::process::ExitStatus`.
+#[derive(PartialEq, Eq, Clone, Copy)]
+pub struct ExitStatus(c_int);
+
+impl fmt::Debug for ExitStatus {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_tuple("unix_wait_status").field(&self.0).finish()
+ }
+}
+
+impl ExitStatus {
+ pub fn new(status: c_int) -> ExitStatus {
+ ExitStatus(status)
+ }
+
+ fn exited(&self) -> bool {
+ libc::WIFEXITED(self.0)
+ }
+
+ pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
+ // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
+ // true on all actual versions of Unix, is widely assumed, and is specified in SuS
+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not
+ // true for a platform pretending to be Unix, the tests (our doctests, and also
+ // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
+ match NonZero_c_int::try_from(self.0) {
+ /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
+ /* was zero, couldn't convert */ Err(_) => Ok(()),
+ }
+ }
+
+ pub fn code(&self) -> Option<i32> {
+ self.exited().then(|| libc::WEXITSTATUS(self.0))
+ }
+
+ pub fn signal(&self) -> Option<i32> {
+ libc::WIFSIGNALED(self.0).then(|| libc::WTERMSIG(self.0))
+ }
+
+ pub fn core_dumped(&self) -> bool {
+ libc::WIFSIGNALED(self.0) && libc::WCOREDUMP(self.0)
+ }
+
+ pub fn stopped_signal(&self) -> Option<i32> {
+ libc::WIFSTOPPED(self.0).then(|| libc::WSTOPSIG(self.0))
+ }
+
+ pub fn continued(&self) -> bool {
+ libc::WIFCONTINUED(self.0)
+ }
+
+ pub fn into_raw(&self) -> c_int {
+ self.0
+ }
+}
+
+/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
+impl From<c_int> for ExitStatus {
+ fn from(a: c_int) -> ExitStatus {
+ ExitStatus(a)
+ }
+}
+
+/// Convert a signal number to a readable, searchable name.
+///
+/// This string should be displayed right after the signal number.
+/// If a signal is unrecognized, it returns the empty string, so that
+/// you just get the number like "0". If it is recognized, you'll get
+/// something like "9 (SIGKILL)".
+fn signal_string(signal: i32) -> &'static str {
+ match signal {
+ libc::SIGHUP => " (SIGHUP)",
+ libc::SIGINT => " (SIGINT)",
+ libc::SIGQUIT => " (SIGQUIT)",
+ libc::SIGILL => " (SIGILL)",
+ libc::SIGTRAP => " (SIGTRAP)",
+ libc::SIGABRT => " (SIGABRT)",
+ libc::SIGBUS => " (SIGBUS)",
+ libc::SIGFPE => " (SIGFPE)",
+ libc::SIGKILL => " (SIGKILL)",
+ libc::SIGUSR1 => " (SIGUSR1)",
+ libc::SIGSEGV => " (SIGSEGV)",
+ libc::SIGUSR2 => " (SIGUSR2)",
+ libc::SIGPIPE => " (SIGPIPE)",
+ libc::SIGALRM => " (SIGALRM)",
+ libc::SIGTERM => " (SIGTERM)",
+ libc::SIGCHLD => " (SIGCHLD)",
+ libc::SIGCONT => " (SIGCONT)",
+ libc::SIGSTOP => " (SIGSTOP)",
+ libc::SIGTSTP => " (SIGTSTP)",
+ libc::SIGTTIN => " (SIGTTIN)",
+ libc::SIGTTOU => " (SIGTTOU)",
+ libc::SIGURG => " (SIGURG)",
+ libc::SIGXCPU => " (SIGXCPU)",
+ libc::SIGXFSZ => " (SIGXFSZ)",
+ libc::SIGVTALRM => " (SIGVTALRM)",
+ libc::SIGPROF => " (SIGPROF)",
+ libc::SIGWINCH => " (SIGWINCH)",
+ #[cfg(not(target_os = "haiku"))]
+ libc::SIGIO => " (SIGIO)",
+ libc::SIGSYS => " (SIGSYS)",
+ // For information on Linux signals, run `man 7 signal`
+ #[cfg(all(
+ target_os = "linux",
+ any(
+ target_arch = "x86_64",
+ target_arch = "x86",
+ target_arch = "arm",
+ target_arch = "aarch64"
+ )
+ ))]
+ libc::SIGSTKFLT => " (SIGSTKFLT)",
+ #[cfg(target_os = "linux")]
+ libc::SIGPWR => " (SIGPWR)",
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "tvos",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "dragonfly"
+ ))]
+ libc::SIGEMT => " (SIGEMT)",
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "tvos",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "dragonfly"
+ ))]
+ libc::SIGINFO => " (SIGINFO)",
+ _ => "",
+ }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if let Some(code) = self.code() {
+ write!(f, "exit status: {code}")
+ } else if let Some(signal) = self.signal() {
+ let signal_string = signal_string(signal);
+ if self.core_dumped() {
+ write!(f, "signal: {signal}{signal_string} (core dumped)")
+ } else {
+ write!(f, "signal: {signal}{signal_string}")
+ }
+ } else if let Some(signal) = self.stopped_signal() {
+ let signal_string = signal_string(signal);
+ write!(f, "stopped (not terminated) by signal: {signal}{signal_string}")
+ } else if self.continued() {
+ write!(f, "continued (WIFCONTINUED)")
+ } else {
+ write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0)
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy)]
+pub struct ExitStatusError(NonZero_c_int);
+
+impl Into<ExitStatus> for ExitStatusError {
+ fn into(self) -> ExitStatus {
+ ExitStatus(self.0.into())
+ }
+}
+
+impl fmt::Debug for ExitStatusError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_tuple("unix_wait_status").field(&self.0).finish()
+ }
+}
+
+impl ExitStatusError {
+ pub fn code(self) -> Option<NonZeroI32> {
+ ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
+ }
+}
+
+#[cfg(target_os = "linux")]
+#[unstable(feature = "linux_pidfd", issue = "82971")]
+impl crate::os::linux::process::ChildExt for crate::process::Child {
+ fn pidfd(&self) -> io::Result<&PidFd> {
+ self.handle
+ .pidfd
+ .as_ref()
+ .ok_or_else(|| Error::new(ErrorKind::Other, "No pidfd was created."))
+ }
+
+ fn take_pidfd(&mut self) -> io::Result<PidFd> {
+ self.handle
+ .pidfd
+ .take()
+ .ok_or_else(|| Error::new(ErrorKind::Other, "No pidfd was created."))
+ }
+}
+
+#[cfg(test)]
+#[path = "process_unix/tests.rs"]
+mod tests;
diff --git a/library/std/src/sys/unix/process/process_unix/tests.rs b/library/std/src/sys/unix/process/process_unix/tests.rs
new file mode 100644
index 000000000..e0e2d478f
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_unix/tests.rs
@@ -0,0 +1,62 @@
+use crate::os::unix::process::{CommandExt, ExitStatusExt};
+use crate::panic::catch_unwind;
+use crate::process::Command;
+
+// Many of the other aspects of this situation, including heap alloc concurrency
+// safety etc., are tested in src/test/ui/process/process-panic-after-fork.rs
+
+#[test]
+fn exitstatus_display_tests() {
+ // In practice this is the same on every Unix.
+ // If some weird platform turns out to be different, and this test fails, use #[cfg].
+ use crate::os::unix::process::ExitStatusExt;
+ use crate::process::ExitStatus;
+
+ let t = |v, s| assert_eq!(s, format!("{}", <ExitStatus as ExitStatusExt>::from_raw(v)));
+
+ t(0x0000f, "signal: 15 (SIGTERM)");
+ t(0x0008b, "signal: 11 (SIGSEGV) (core dumped)");
+ t(0x00000, "exit status: 0");
+ t(0x0ff00, "exit status: 255");
+
+ // On MacOS, 0x0137f is WIFCONTINUED, not WIFSTOPPED. Probably *BSD is similar.
+ // https://github.com/rust-lang/rust/pull/82749#issuecomment-790525956
+ // The purpose of this test is to test our string formatting, not our understanding of the wait
+ // status magic numbers. So restrict these to Linux.
+ if cfg!(target_os = "linux") {
+ t(0x0137f, "stopped (not terminated) by signal: 19 (SIGSTOP)");
+ t(0x0ffff, "continued (WIFCONTINUED)");
+ }
+
+ // Testing "unrecognised wait status" is hard because the wait.h macros typically
+ // assume that the value came from wait and isn't mad. With the glibc I have here
+ // this works:
+ if cfg!(all(target_os = "linux", target_env = "gnu")) {
+ t(0x000ff, "unrecognised wait status: 255 0xff");
+ }
+}
+
+#[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn test_command_fork_no_unwind() {
+ let got = catch_unwind(|| {
+ let mut c = Command::new("echo");
+ c.arg("hi");
+ unsafe {
+ c.pre_exec(|| panic!("{}", "crash now!"));
+ }
+ let st = c.status().expect("failed to get command status");
+ dbg!(st);
+ st
+ });
+ dbg!(&got);
+ let status = got.expect("panic unexpectedly propagated");
+ dbg!(status);
+ let signal = status.signal().expect("expected child process to die of signal");
+ assert!(
+ signal == libc::SIGABRT
+ || signal == libc::SIGILL
+ || signal == libc::SIGTRAP
+ || signal == libc::SIGSEGV
+ );
+}
diff --git a/library/std/src/sys/unix/process/process_unsupported.rs b/library/std/src/sys/unix/process/process_unsupported.rs
new file mode 100644
index 000000000..72f9f3f9c
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_unsupported.rs
@@ -0,0 +1,118 @@
+use crate::fmt;
+use crate::io;
+use crate::num::NonZeroI32;
+use crate::sys::process::process_common::*;
+use crate::sys::unix::unsupported::*;
+use core::ffi::NonZero_c_int;
+
+use libc::{c_int, pid_t};
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+impl Command {
+ pub fn spawn(
+ &mut self,
+ _default: Stdio,
+ _needs_stdin: bool,
+ ) -> io::Result<(Process, StdioPipes)> {
+ unsupported()
+ }
+
+ pub fn exec(&mut self, _default: Stdio) -> io::Error {
+ unsupported_err()
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct Process {
+ _handle: pid_t,
+}
+
+impl Process {
+ pub fn id(&self) -> u32 {
+ 0
+ }
+
+ pub fn kill(&mut self) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ unsupported()
+ }
+
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+ unsupported()
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatus(c_int);
+
+impl ExitStatus {
+ #[cfg_attr(target_os = "horizon", allow(unused))]
+ pub fn success(&self) -> bool {
+ self.code() == Some(0)
+ }
+
+ pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
+ Err(ExitStatusError(1.try_into().unwrap()))
+ }
+
+ pub fn code(&self) -> Option<i32> {
+ None
+ }
+
+ pub fn signal(&self) -> Option<i32> {
+ None
+ }
+
+ pub fn core_dumped(&self) -> bool {
+ false
+ }
+
+ pub fn stopped_signal(&self) -> Option<i32> {
+ None
+ }
+
+ pub fn continued(&self) -> bool {
+ false
+ }
+
+ pub fn into_raw(&self) -> c_int {
+ 0
+ }
+}
+
+/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
+impl From<c_int> for ExitStatus {
+ fn from(a: c_int) -> ExitStatus {
+ ExitStatus(a as i32)
+ }
+}
+
+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 ExitStatusError(NonZero_c_int);
+
+impl Into<ExitStatus> for ExitStatusError {
+ fn into(self) -> ExitStatus {
+ ExitStatus(self.0.into())
+ }
+}
+
+impl ExitStatusError {
+ pub fn code(self) -> Option<NonZeroI32> {
+ ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
+ }
+}
diff --git a/library/std/src/sys/unix/process/process_vxworks.rs b/library/std/src/sys/unix/process/process_vxworks.rs
new file mode 100644
index 000000000..200ef6719
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_vxworks.rs
@@ -0,0 +1,262 @@
+use crate::fmt;
+use crate::io::{self, Error, ErrorKind};
+use crate::num::NonZeroI32;
+use crate::sys;
+use crate::sys::cvt;
+use crate::sys::process::process_common::*;
+use crate::sys_common::thread;
+use core::ffi::NonZero_c_int;
+use libc::RTP_ID;
+use libc::{self, c_char, c_int};
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+impl Command {
+ pub fn spawn(
+ &mut self,
+ default: Stdio,
+ needs_stdin: bool,
+ ) -> io::Result<(Process, StdioPipes)> {
+ use crate::sys::cvt_r;
+ let envp = self.capture_env();
+
+ if self.saw_nul() {
+ return Err(io::const_io_error!(
+ ErrorKind::InvalidInput,
+ "nul byte found in provided data",
+ ));
+ }
+ let (ours, theirs) = self.setup_io(default, needs_stdin)?;
+ let mut p = Process { pid: 0, status: None };
+
+ unsafe {
+ macro_rules! t {
+ ($e:expr) => {
+ match $e {
+ Ok(e) => e,
+ Err(e) => return Err(e.into()),
+ }
+ };
+ }
+
+ let mut orig_stdin = libc::STDIN_FILENO;
+ let mut orig_stdout = libc::STDOUT_FILENO;
+ let mut orig_stderr = libc::STDERR_FILENO;
+
+ if let Some(fd) = theirs.stdin.fd() {
+ orig_stdin = t!(cvt_r(|| libc::dup(libc::STDIN_FILENO)));
+ t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
+ }
+ if let Some(fd) = theirs.stdout.fd() {
+ orig_stdout = t!(cvt_r(|| libc::dup(libc::STDOUT_FILENO)));
+ t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
+ }
+ if let Some(fd) = theirs.stderr.fd() {
+ orig_stderr = t!(cvt_r(|| libc::dup(libc::STDERR_FILENO)));
+ t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
+ }
+
+ if let Some(ref cwd) = *self.get_cwd() {
+ t!(cvt(libc::chdir(cwd.as_ptr())));
+ }
+
+ // pre_exec closures are ignored on VxWorks
+ let _ = self.get_closures();
+
+ let c_envp = envp
+ .as_ref()
+ .map(|c| c.as_ptr())
+ .unwrap_or_else(|| *sys::os::environ() as *const _);
+ let stack_size = thread::min_stack();
+
+ // ensure that access to the environment is synchronized
+ let _lock = sys::os::env_read_lock();
+
+ let ret = libc::rtpSpawn(
+ self.get_program_cstr().as_ptr(),
+ self.get_argv().as_ptr() as *mut *const c_char, // argv
+ c_envp as *mut *const c_char,
+ 100 as c_int, // initial priority
+ stack_size, // initial stack size.
+ 0, // options
+ 0, // task options
+ );
+
+ // Because FileDesc was not used, each duplicated file descriptor
+ // needs to be closed manually
+ if orig_stdin != libc::STDIN_FILENO {
+ t!(cvt_r(|| libc::dup2(orig_stdin, libc::STDIN_FILENO)));
+ libc::close(orig_stdin);
+ }
+ if orig_stdout != libc::STDOUT_FILENO {
+ t!(cvt_r(|| libc::dup2(orig_stdout, libc::STDOUT_FILENO)));
+ libc::close(orig_stdout);
+ }
+ if orig_stderr != libc::STDERR_FILENO {
+ t!(cvt_r(|| libc::dup2(orig_stderr, libc::STDERR_FILENO)));
+ libc::close(orig_stderr);
+ }
+
+ if ret != libc::RTP_ID_ERROR {
+ p.pid = ret;
+ Ok((p, ours))
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+
+ pub fn exec(&mut self, default: Stdio) -> io::Error {
+ let ret = Command::spawn(self, default, false);
+ match ret {
+ Ok(t) => unsafe {
+ let mut status = 0 as c_int;
+ libc::waitpid(t.0.pid, &mut status, 0);
+ libc::exit(0);
+ },
+ Err(e) => e,
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+/// The unique id of the process (this should never be negative).
+pub struct Process {
+ pid: RTP_ID,
+ 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(io::const_io_error!(
+ ErrorKind::InvalidInput,
+ "invalid argument: can't kill an exited process",
+ ))
+ } else {
+ cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
+ }
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ use crate::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)))
+ }
+ }
+}
+
+/// 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 {
+ libc::WIFEXITED(self.0)
+ }
+
+ pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
+ // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
+ // true on all actual versions of Unix, is widely assumed, and is specified in SuS
+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not
+ // true for a platform pretending to be Unix, the tests (our doctests, and also
+ // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
+ match NonZero_c_int::try_from(self.0) {
+ Ok(failure) => Err(ExitStatusError(failure)),
+ Err(_) => Ok(()),
+ }
+ }
+
+ pub fn code(&self) -> Option<i32> {
+ if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None }
+ }
+
+ pub fn signal(&self) -> Option<i32> {
+ if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None }
+ }
+
+ pub fn core_dumped(&self) -> bool {
+ // This method is not yet properly implemented on VxWorks
+ false
+ }
+
+ pub fn stopped_signal(&self) -> Option<i32> {
+ if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None }
+ }
+
+ pub fn continued(&self) -> bool {
+ // This method is not yet properly implemented on VxWorks
+ false
+ }
+
+ pub fn into_raw(&self) -> c_int {
+ self.0
+ }
+}
+
+/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
+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 ExitStatusError(NonZero_c_int);
+
+impl Into<ExitStatus> for ExitStatusError {
+ fn into(self) -> ExitStatus {
+ ExitStatus(self.0.into())
+ }
+}
+
+impl ExitStatusError {
+ pub fn code(self) -> Option<NonZeroI32> {
+ ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
+ }
+}
diff --git a/library/std/src/sys/unix/process/zircon.rs b/library/std/src/sys/unix/process/zircon.rs
new file mode 100644
index 000000000..2e596486f
--- /dev/null
+++ b/library/std/src/sys/unix/process/zircon.rs
@@ -0,0 +1,309 @@
+#![allow(non_camel_case_types, unused)]
+
+use crate::io;
+use crate::mem::MaybeUninit;
+use crate::os::raw::c_char;
+
+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 = i64;
+pub const ZX_TIME_INFINITE: zx_time_t = i64::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;
+
+// The upper four bits gives the minor version.
+pub type zx_object_info_topic_t = u32;
+
+pub const ZX_INFO_PROCESS: zx_object_info_topic_t = 3 | (1 << 28);
+
+pub type zx_info_process_flags_t = u32;
+
+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");
+ }
+ }
+}
+
+// Returned for topic ZX_INFO_PROCESS
+#[derive(Default)]
+#[repr(C)]
+pub struct zx_info_process_t {
+ pub return_code: i64,
+ pub start_time: zx_time_t,
+ pub flags: zx_info_process_flags_t,
+ pub reserved1: u32,
+}
+
+extern "C" {
+ 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 "C" {
+ 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: size_t,
+ actions: *const fdio_spawn_action_t,
+ process: *mut zx_handle_t,
+ err_msg: *mut c_char,
+ ) -> zx_status_t;
+
+ pub fn fdio_fd_clone(fd: c_int, out_handle: *mut zx_handle_t) -> zx_status_t;
+ pub fn fdio_fd_create(handle: zx_handle_t, fd: *mut c_int) -> 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_UTC_CLOCK: u32 = 0x0020;
+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/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs
new file mode 100644
index 000000000..bf4920488
--- /dev/null
+++ b/library/std/src/sys/unix/rand.rs
@@ -0,0 +1,301 @@
+use crate::mem;
+use crate::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);
+ }
+ v
+}
+
+#[cfg(all(
+ unix,
+ not(target_os = "macos"),
+ not(target_os = "ios"),
+ not(target_os = "watchos"),
+ not(target_os = "openbsd"),
+ not(target_os = "freebsd"),
+ not(target_os = "netbsd"),
+ not(target_os = "fuchsia"),
+ not(target_os = "redox"),
+ not(target_os = "vxworks")
+))]
+mod imp {
+ use crate::fs::File;
+ use crate::io::Read;
+
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ use crate::sys::weak::syscall;
+
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
+ use crate::sync::atomic::{AtomicBool, Ordering};
+ use crate::sys::os::errno;
+
+ // A weak symbol allows interposition, e.g. for perf measurements that want to
+ // disable randomness for consistency. Otherwise, we'll try a raw syscall.
+ // (`getrandom` was added in glibc 2.25, musl 1.1.20, android API level 28)
+ syscall! {
+ fn getrandom(
+ buffer: *mut libc::c_void,
+ length: libc::size_t,
+ flags: libc::c_uint
+ ) -> libc::ssize_t
+ }
+
+ // This provides the best quality random numbers available at the given moment
+ // without ever blocking, and is preferable to falling back to /dev/urandom.
+ static GRND_INSECURE_AVAILABLE: AtomicBool = AtomicBool::new(true);
+ if GRND_INSECURE_AVAILABLE.load(Ordering::Relaxed) {
+ let ret = unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_INSECURE) };
+ if ret == -1 && errno() as libc::c_int == libc::EINVAL {
+ GRND_INSECURE_AVAILABLE.store(false, Ordering::Relaxed);
+ } else {
+ return ret;
+ }
+ }
+
+ unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) }
+ }
+
+ #[cfg(any(target_os = "espidf", target_os = "horizon"))]
+ fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
+ unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
+ }
+
+ #[cfg(not(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "espidf",
+ target_os = "horizon"
+ )))]
+ fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool {
+ false
+ }
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "espidf",
+ target_os = "horizon"
+ ))]
+ fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
+ use crate::sync::atomic::{AtomicBool, Ordering};
+ use crate::sys::os::errno;
+
+ static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false);
+ if GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed) {
+ return false;
+ }
+
+ 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::ENOSYS || err == libc::EPERM {
+ // Fall back to reading /dev/urandom if `getrandom` is not
+ // supported on the current kernel.
+ //
+ // Also fall back in case it is disabled by something like
+ // seccomp or inside of virtual machines.
+ GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed);
+ return false;
+ } else if err == libc::EAGAIN {
+ return false;
+ } else {
+ panic!("unexpected getrandom error: {err}");
+ }
+ } else {
+ read += result as usize;
+ }
+ }
+ true
+ }
+
+ 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 over a
+ // possibly predictable entropy pool.
+ if getrandom_fill_bytes(v) {
+ return;
+ }
+
+ // getrandom failed because it is permanently or temporarily (because
+ // of missing entropy) unavailable. Open /dev/urandom, read from it,
+ // and close it again.
+ 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 = "macos")]
+mod imp {
+ use crate::fs::File;
+ use crate::io::Read;
+ use crate::sys::os::errno;
+ use crate::sys::weak::weak;
+ use libc::{c_int, c_void, size_t};
+
+ fn getentropy_fill_bytes(v: &mut [u8]) -> bool {
+ weak!(fn getentropy(*mut c_void, size_t) -> c_int);
+
+ getentropy
+ .get()
+ .map(|f| {
+ // getentropy(2) permits a maximum buffer size of 256 bytes
+ for s in v.chunks_mut(256) {
+ let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) };
+ if ret == -1 {
+ panic!("unexpected getentropy error: {}", errno());
+ }
+ }
+ true
+ })
+ .unwrap_or(false)
+ }
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ if getentropy_fill_bytes(v) {
+ return;
+ }
+
+ // for older macos which doesn't support getentropy
+ 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 crate::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());
+ }
+ }
+ }
+}
+
+// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
+// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
+// from `/dev/random` and which runs on its own thread accessed via GCD.
+// This seems needlessly heavyweight for the purposes of generating two u64s
+// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is
+// only used on iOS where direct access to `/dev/urandom` is blocked by the
+// sandbox.
+#[cfg(any(target_os = "ios", target_os = "watchos"))]
+mod imp {
+ use crate::io;
+ use crate::ptr;
+ use libc::{c_int, size_t};
+
+ enum SecRandom {}
+
+ #[allow(non_upper_case_globals)]
+ const kSecRandomDefault: *const SecRandom = ptr::null();
+
+ extern "C" {
+ 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(any(target_os = "freebsd", target_os = "netbsd"))]
+mod imp {
+ use crate::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 "C" {
+ fn zx_cprng_draw(buffer: *mut u8, len: usize);
+ }
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) }
+ }
+}
+
+#[cfg(target_os = "redox")]
+mod imp {
+ use crate::fs::File;
+ use crate::io::Read;
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ // Open rand:, read from it, and close it again.
+ let mut file = File::open("rand:").expect("failed to open rand:");
+ file.read_exact(v).expect("failed to read rand:")
+ }
+}
+
+#[cfg(target_os = "vxworks")]
+mod imp {
+ use crate::io;
+ use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ static RNG_INIT: AtomicBool = AtomicBool::new(false);
+ while !RNG_INIT.load(Relaxed) {
+ let ret = unsafe { libc::randSecure() };
+ if ret < 0 {
+ panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
+ } else if ret > 0 {
+ RNG_INIT.store(true, Relaxed);
+ break;
+ }
+ unsafe { libc::usleep(10) };
+ }
+ let ret = unsafe {
+ libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int)
+ };
+ if ret < 0 {
+ panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
+ }
+ }
+}
diff --git a/library/std/src/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs
new file mode 100644
index 000000000..75a5c0f92
--- /dev/null
+++ b/library/std/src/sys/unix/stack_overflow.rs
@@ -0,0 +1,208 @@
+#![cfg_attr(test, allow(dead_code))]
+
+use self::imp::{drop_handler, make_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()
+ }
+
+ fn null() -> Handler {
+ Handler { data: crate::ptr::null_mut() }
+ }
+}
+
+impl Drop for Handler {
+ fn drop(&mut self) {
+ unsafe {
+ drop_handler(self.data);
+ }
+ }
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+))]
+mod imp {
+ use super::Handler;
+ use crate::io;
+ use crate::mem;
+ use crate::ptr;
+ use crate::thread;
+
+ use libc::MAP_FAILED;
+ use libc::{mmap, munmap};
+ use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL};
+ use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE};
+ use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV};
+
+ use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
+ use crate::sys::unix::os::page_size;
+ use crate::sys_common::thread_info;
+
+ // 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 "C" fn signal_handler(
+ signum: libc::c_int,
+ info: *mut libc::siginfo_t,
+ _data: *mut libc::c_void,
+ ) {
+ let guard = thread_info::stack_guard().unwrap_or(0..0);
+ let addr = (*info).si_addr() as usize;
+
+ // 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 {
+ rtprintpanic!(
+ "\nthread '{}' has overflowed its stack\n",
+ thread::current().name().unwrap_or("<unknown>")
+ );
+ 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 MAIN_ALTSTACK: AtomicPtr<libc::c_void> = AtomicPtr::new(ptr::null_mut());
+ static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false);
+
+ pub unsafe fn init() {
+ let mut action: sigaction = mem::zeroed();
+ for &signal in &[SIGSEGV, SIGBUS] {
+ sigaction(signal, ptr::null_mut(), &mut action);
+ // Configure our signal handler if one is not already set.
+ if action.sa_sigaction == SIG_DFL {
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+ action.sa_sigaction = signal_handler as sighandler_t;
+ sigaction(signal, &action, ptr::null_mut());
+ NEED_ALTSTACK.store(true, Ordering::Relaxed);
+ }
+ }
+
+ let handler = make_handler();
+ MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
+ mem::forget(handler);
+ }
+
+ pub unsafe fn cleanup() {
+ drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed));
+ }
+
+ unsafe fn get_stackp() -> *mut libc::c_void {
+ // OpenBSD requires this flag for stack mapping
+ // otherwise the said mapping will fail as a no-op on most systems
+ // and has a different meaning on FreeBSD
+ #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",))]
+ let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK;
+ #[cfg(not(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",)))]
+ let flags = MAP_PRIVATE | MAP_ANON;
+ let stackp =
+ mmap(ptr::null_mut(), SIGSTKSZ + page_size(), PROT_READ | PROT_WRITE, flags, -1, 0);
+ if stackp == MAP_FAILED {
+ panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error());
+ }
+ let guard_result = libc::mprotect(stackp, page_size(), PROT_NONE);
+ if guard_result != 0 {
+ panic!("failed to set up alternative stack guard page: {}", io::Error::last_os_error());
+ }
+ stackp.add(page_size())
+ }
+
+ unsafe fn get_stack() -> libc::stack_t {
+ libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ }
+ }
+
+ pub unsafe fn make_handler() -> Handler {
+ if !NEED_ALTSTACK.load(Ordering::Relaxed) {
+ return Handler::null();
+ }
+ 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::null()
+ }
+ }
+
+ pub unsafe fn drop_handler(data: *mut libc::c_void) {
+ if !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());
+ // We know from `get_stackp` that the alternate stack we installed is part of a mapping
+ // that started one page earlier, so walk back a page and unmap from there.
+ munmap(data.sub(page_size()), SIGSTKSZ + page_size());
+ }
+ }
+}
+
+#[cfg(not(any(
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+)))]
+mod imp {
+ pub unsafe fn init() {}
+
+ pub unsafe fn cleanup() {}
+
+ pub unsafe fn make_handler() -> super::Handler {
+ super::Handler::null()
+ }
+
+ pub unsafe fn drop_handler(_data: *mut libc::c_void) {}
+}
diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs
new file mode 100644
index 000000000..329f9433d
--- /dev/null
+++ b/library/std/src/sys/unix/stdio.rs
@@ -0,0 +1,141 @@
+use crate::io::{self, IoSlice, IoSliceMut};
+use crate::mem::ManuallyDrop;
+use crate::os::unix::io::{AsFd, BorrowedFd, FromRawFd};
+use crate::sys::fd::FileDesc;
+
+pub struct Stdin(());
+pub struct Stdout(());
+pub struct Stderr(());
+
+impl Stdin {
+ pub const fn new() -> Stdin {
+ Stdin(())
+ }
+}
+
+impl io::Read for Stdin {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read(buf) }
+ }
+
+ fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_vectored(bufs) }
+ }
+
+ #[inline]
+ fn is_read_vectored(&self) -> bool {
+ true
+ }
+}
+
+impl Stdout {
+ pub const fn new() -> Stdout {
+ Stdout(())
+ }
+}
+
+impl io::Write for Stdout {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write(buf) }
+ }
+
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ unsafe {
+ ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write_vectored(bufs)
+ }
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Stderr {
+ pub const fn new() -> Stderr {
+ Stderr(())
+ }
+}
+
+impl io::Write for Stderr {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write(buf) }
+ }
+
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ unsafe {
+ ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write_vectored(bufs)
+ }
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+pub fn is_ebadf(err: &io::Error) -> bool {
+ err.raw_os_error() == Some(libc::EBADF as i32)
+}
+
+pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
+
+pub fn panic_output() -> Option<impl io::Write> {
+ Some(Stderr::new())
+}
+
+#[stable(feature = "io_safety", since = "1.63.0")]
+impl AsFd for io::Stdin {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(libc::STDIN_FILENO) }
+ }
+}
+
+#[stable(feature = "io_safety", since = "1.63.0")]
+impl<'a> AsFd for io::StdinLock<'a> {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(libc::STDIN_FILENO) }
+ }
+}
+
+#[stable(feature = "io_safety", since = "1.63.0")]
+impl AsFd for io::Stdout {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) }
+ }
+}
+
+#[stable(feature = "io_safety", since = "1.63.0")]
+impl<'a> AsFd for io::StdoutLock<'a> {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) }
+ }
+}
+
+#[stable(feature = "io_safety", since = "1.63.0")]
+impl AsFd for io::Stderr {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) }
+ }
+}
+
+#[stable(feature = "io_safety", since = "1.63.0")]
+impl<'a> AsFd for io::StderrLock<'a> {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) }
+ }
+}
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
new file mode 100644
index 000000000..36a3fa602
--- /dev/null
+++ b/library/std/src/sys/unix/thread.rs
@@ -0,0 +1,889 @@
+use crate::cmp;
+use crate::ffi::CStr;
+use crate::io;
+use crate::mem;
+use crate::num::NonZeroUsize;
+use crate::ptr;
+use crate::sys::{os, stack_overflow};
+use crate::time::Duration;
+
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
+use crate::sys::weak::dlsym;
+#[cfg(any(target_os = "solaris", target_os = "illumos"))]
+use crate::sys::weak::weak;
+#[cfg(not(any(target_os = "l4re", target_os = "vxworks", target_os = "espidf")))]
+pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
+#[cfg(target_os = "l4re")]
+pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
+#[cfg(target_os = "vxworks")]
+pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024;
+#[cfg(target_os = "espidf")]
+pub const DEFAULT_MIN_STACK_SIZE: usize = 0; // 0 indicates that the stack size configured in the ESP-IDF menuconfig system should be used
+
+#[cfg(target_os = "fuchsia")]
+mod zircon {
+ type zx_handle_t = u32;
+ type zx_status_t = i32;
+ pub const ZX_PROP_NAME: u32 = 3;
+
+ extern "C" {
+ pub fn zx_object_set_property(
+ handle: zx_handle_t,
+ property: u32,
+ value: *const libc::c_void,
+ value_size: libc::size_t,
+ ) -> zx_status_t;
+ pub fn zx_thread_self() -> zx_handle_t;
+ }
+}
+
+pub struct Thread {
+ 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 {}
+
+impl Thread {
+ // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+ pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+ let p = Box::into_raw(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);
+
+ #[cfg(target_os = "espidf")]
+ if stack > 0 {
+ // Only set the stack if a non-zero value is passed
+ // 0 is used as an indication that the default stack size configured in the ESP-IDF menuconfig system should be used
+ assert_eq!(
+ libc::pthread_attr_setstacksize(&mut attr, cmp::max(stack, min_stack_size(&attr))),
+ 0
+ );
+ }
+
+ #[cfg(not(target_os = "espidf"))]
+ {
+ let stack_size = cmp::max(stack, min_stack_size(&attr));
+
+ match libc::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 *mut _);
+ // Note: if the thread creation fails and this assert fails, then p will
+ // be leaked. However, an alternative design could cause double-free
+ // which is clearly worse.
+ assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+
+ return if ret != 0 {
+ // The thread failed to start and as a result p was not consumed. Therefore, it is
+ // safe to reconstruct the box so that it gets deallocated.
+ drop(Box::from_raw(p));
+ Err(io::Error::from_raw_os_error(ret))
+ } else {
+ Ok(Thread { id: native })
+ };
+
+ extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
+ unsafe {
+ // Next, set up our stack overflow handler which may get triggered if we run
+ // out of stack.
+ let _handler = stack_overflow::Handler::new();
+ // Finally, let's run some code.
+ Box::from_raw(main as *mut Box<dyn FnOnce()>)();
+ }
+ 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 {
+ libc::prctl(
+ PR_SET_NAME,
+ name.as_ptr(),
+ 0 as libc::c_ulong,
+ 0 as libc::c_ulong,
+ 0 as libc::c_ulong,
+ );
+ }
+ }
+
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly", 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", target_os = "watchos"))]
+ 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 crate::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_os = "solaris", target_os = "illumos"))]
+ pub fn set_name(name: &CStr) {
+ weak! {
+ fn pthread_setname_np(
+ libc::pthread_t, *const libc::c_char
+ ) -> libc::c_int
+ }
+
+ if let Some(f) = pthread_setname_np.get() {
+ unsafe {
+ f(libc::pthread_self(), name.as_ptr());
+ }
+ }
+ }
+
+ #[cfg(target_os = "fuchsia")]
+ pub fn set_name(name: &CStr) {
+ use self::zircon::*;
+ unsafe {
+ zx_object_set_property(
+ zx_thread_self(),
+ ZX_PROP_NAME,
+ name.as_ptr() as *const libc::c_void,
+ name.to_bytes().len(),
+ );
+ }
+ }
+
+ #[cfg(target_os = "haiku")]
+ pub fn set_name(name: &CStr) {
+ unsafe {
+ let thread_self = libc::find_thread(ptr::null_mut());
+ libc::rename_thread(thread_self, name.as_ptr());
+ }
+ }
+
+ #[cfg(any(
+ target_env = "newlib",
+ target_os = "l4re",
+ target_os = "emscripten",
+ target_os = "redox",
+ target_os = "vxworks"
+ ))]
+ pub fn set_name(_name: &CStr) {
+ // Newlib, Emscripten, and VxWorks have no way to set a thread name.
+ }
+
+ #[cfg(not(target_os = "espidf"))]
+ 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 {
+ while secs > 0 || nsecs > 0 {
+ let mut ts = libc::timespec {
+ tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t,
+ tv_nsec: nsecs,
+ };
+ secs -= ts.tv_sec as u64;
+ let ts_ptr = &mut ts as *mut _;
+ if libc::nanosleep(ts_ptr, ts_ptr) == -1 {
+ assert_eq!(os::errno(), libc::EINTR);
+ secs += ts.tv_sec as u64;
+ nsecs = ts.tv_nsec;
+ } else {
+ nsecs = 0;
+ }
+ }
+ }
+ }
+
+ #[cfg(target_os = "espidf")]
+ pub fn sleep(dur: Duration) {
+ let mut micros = dur.as_micros();
+ unsafe {
+ while micros > 0 {
+ let st = if micros > u32::MAX as u128 { u32::MAX } else { micros as u32 };
+ libc::usleep(st);
+
+ micros -= st as u128;
+ }
+ }
+ }
+
+ 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));
+ }
+ }
+
+ pub fn id(&self) -> libc::pthread_t {
+ self.id
+ }
+
+ pub fn into_id(self) -> libc::pthread_t {
+ let id = self.id;
+ mem::forget(self);
+ id
+ }
+}
+
+impl Drop for Thread {
+ fn drop(&mut self) {
+ let ret = unsafe { libc::pthread_detach(self.id) };
+ debug_assert_eq!(ret, 0);
+ }
+}
+
+pub fn available_parallelism() -> io::Result<NonZeroUsize> {
+ cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "solaris",
+ target_os = "illumos",
+ ))] {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ {
+ let quota = cgroups::quota().max(1);
+ let mut set: libc::cpu_set_t = unsafe { mem::zeroed() };
+ unsafe {
+ if libc::sched_getaffinity(0, mem::size_of::<libc::cpu_set_t>(), &mut set) == 0 {
+ let count = libc::CPU_COUNT(&set) as usize;
+ let count = count.min(quota);
+ // SAFETY: affinity mask can't be empty and the quota gets clamped to a minimum of 1
+ return Ok(NonZeroUsize::new_unchecked(count));
+ }
+ }
+ }
+ match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } {
+ -1 => Err(io::Error::last_os_error()),
+ 0 => Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")),
+ cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }),
+ }
+ } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))] {
+ use crate::ptr;
+
+ let mut cpus: libc::c_uint = 0;
+ let mut cpus_size = crate::mem::size_of_val(&cpus);
+
+ unsafe {
+ cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
+ }
+
+ // Fallback approach in case of errors or no hardware threads.
+ if cpus < 1 {
+ let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
+ let res = unsafe {
+ libc::sysctl(
+ mib.as_mut_ptr(),
+ 2,
+ &mut cpus as *mut _ as *mut _,
+ &mut cpus_size as *mut _ as *mut _,
+ ptr::null_mut(),
+ 0,
+ )
+ };
+
+ // Handle errors if any.
+ if res == -1 {
+ return Err(io::Error::last_os_error());
+ } else if cpus == 0 {
+ return Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform"));
+ }
+ }
+ Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
+ } else if #[cfg(target_os = "openbsd")] {
+ use crate::ptr;
+
+ let mut cpus: libc::c_uint = 0;
+ let mut cpus_size = crate::mem::size_of_val(&cpus);
+ let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
+
+ let res = unsafe {
+ libc::sysctl(
+ mib.as_mut_ptr(),
+ 2,
+ &mut cpus as *mut _ as *mut _,
+ &mut cpus_size as *mut _ as *mut _,
+ ptr::null_mut(),
+ 0,
+ )
+ };
+
+ // Handle errors if any.
+ if res == -1 {
+ return Err(io::Error::last_os_error());
+ } else if cpus == 0 {
+ return Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform"));
+ }
+
+ Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
+ } else if #[cfg(target_os = "haiku")] {
+ // system_info cpu_count field gets the static data set at boot time with `smp_set_num_cpus`
+ // `get_system_info` calls then `smp_get_num_cpus`
+ unsafe {
+ let mut sinfo: libc::system_info = crate::mem::zeroed();
+ let res = libc::get_system_info(&mut sinfo);
+
+ if res != libc::B_OK {
+ return Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform"));
+ }
+
+ Ok(NonZeroUsize::new_unchecked(sinfo.cpu_count as usize))
+ }
+ } else {
+ // FIXME: implement on vxWorks, Redox, l4re
+ Err(io::const_io_error!(io::ErrorKind::Unsupported, "Getting the number of hardware threads is not supported on the target platform"))
+ }
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod cgroups {
+ //! Currently not covered
+ //! * cgroup v2 in non-standard mountpoints
+ //! * paths containing control characters or spaces, since those would be escaped in procfs
+ //! output and we don't unescape
+ use crate::borrow::Cow;
+ use crate::ffi::OsString;
+ use crate::fs::{try_exists, File};
+ use crate::io::Read;
+ use crate::io::{BufRead, BufReader};
+ use crate::os::unix::ffi::OsStringExt;
+ use crate::path::Path;
+ use crate::path::PathBuf;
+ use crate::str::from_utf8;
+
+ #[derive(PartialEq)]
+ enum Cgroup {
+ V1,
+ V2,
+ }
+
+ /// Returns cgroup CPU quota in core-equivalents, rounded down or usize::MAX if the quota cannot
+ /// be determined or is not set.
+ pub(super) fn quota() -> usize {
+ let mut quota = usize::MAX;
+ if cfg!(miri) {
+ // Attempting to open a file fails under default flags due to isolation.
+ // And Miri does not have parallelism anyway.
+ return quota;
+ }
+
+ let _: Option<()> = try {
+ let mut buf = Vec::with_capacity(128);
+ // find our place in the cgroup hierarchy
+ File::open("/proc/self/cgroup").ok()?.read_to_end(&mut buf).ok()?;
+ let (cgroup_path, version) =
+ buf.split(|&c| c == b'\n').fold(None, |previous, line| {
+ let mut fields = line.splitn(3, |&c| c == b':');
+ // 2nd field is a list of controllers for v1 or empty for v2
+ let version = match fields.nth(1) {
+ Some(b"") => Cgroup::V2,
+ Some(controllers)
+ if from_utf8(controllers)
+ .is_ok_and(|c| c.split(",").any(|c| c == "cpu")) =>
+ {
+ Cgroup::V1
+ }
+ _ => return previous,
+ };
+
+ // already-found v1 trumps v2 since it explicitly specifies its controllers
+ if previous.is_some() && version == Cgroup::V2 {
+ return previous;
+ }
+
+ let path = fields.last()?;
+ // skip leading slash
+ Some((path[1..].to_owned(), version))
+ })?;
+ let cgroup_path = PathBuf::from(OsString::from_vec(cgroup_path));
+
+ quota = match version {
+ Cgroup::V1 => quota_v1(cgroup_path),
+ Cgroup::V2 => quota_v2(cgroup_path),
+ };
+ };
+
+ quota
+ }
+
+ fn quota_v2(group_path: PathBuf) -> usize {
+ let mut quota = usize::MAX;
+
+ let mut path = PathBuf::with_capacity(128);
+ let mut read_buf = String::with_capacity(20);
+
+ // standard mount location defined in file-hierarchy(7) manpage
+ let cgroup_mount = "/sys/fs/cgroup";
+
+ path.push(cgroup_mount);
+ path.push(&group_path);
+
+ path.push("cgroup.controllers");
+
+ // skip if we're not looking at cgroup2
+ if matches!(try_exists(&path), Err(_) | Ok(false)) {
+ return usize::MAX;
+ };
+
+ path.pop();
+
+ let _: Option<()> = try {
+ while path.starts_with(cgroup_mount) {
+ path.push("cpu.max");
+
+ read_buf.clear();
+
+ if File::open(&path).and_then(|mut f| f.read_to_string(&mut read_buf)).is_ok() {
+ let raw_quota = read_buf.lines().next()?;
+ let mut raw_quota = raw_quota.split(' ');
+ let limit = raw_quota.next()?;
+ let period = raw_quota.next()?;
+ match (limit.parse::<usize>(), period.parse::<usize>()) {
+ (Ok(limit), Ok(period)) => {
+ quota = quota.min(limit / period);
+ }
+ _ => {}
+ }
+ }
+
+ path.pop(); // pop filename
+ path.pop(); // pop dir
+ }
+ };
+
+ quota
+ }
+
+ fn quota_v1(group_path: PathBuf) -> usize {
+ let mut quota = usize::MAX;
+ let mut path = PathBuf::with_capacity(128);
+ let mut read_buf = String::with_capacity(20);
+
+ // Hardcode commonly used locations mentioned in the cgroups(7) manpage
+ // if that doesn't work scan mountinfo and adjust `group_path` for bind-mounts
+ let mounts: &[fn(&Path) -> Option<(_, &Path)>] = &[
+ |p| Some((Cow::Borrowed("/sys/fs/cgroup/cpu"), p)),
+ |p| Some((Cow::Borrowed("/sys/fs/cgroup/cpu,cpuacct"), p)),
+ // this can be expensive on systems with tons of mountpoints
+ // but we only get to this point when /proc/self/cgroups explicitly indicated
+ // this process belongs to a cpu-controller cgroup v1 and the defaults didn't work
+ find_mountpoint,
+ ];
+
+ for mount in mounts {
+ let Some((mount, group_path)) = mount(&group_path) else { continue };
+
+ path.clear();
+ path.push(mount.as_ref());
+ path.push(&group_path);
+
+ // skip if we guessed the mount incorrectly
+ if matches!(try_exists(&path), Err(_) | Ok(false)) {
+ continue;
+ }
+
+ while path.starts_with(mount.as_ref()) {
+ let mut parse_file = |name| {
+ path.push(name);
+ read_buf.clear();
+
+ let f = File::open(&path);
+ path.pop(); // restore buffer before any early returns
+ f.ok()?.read_to_string(&mut read_buf).ok()?;
+ let parsed = read_buf.trim().parse::<usize>().ok()?;
+
+ Some(parsed)
+ };
+
+ let limit = parse_file("cpu.cfs_quota_us");
+ let period = parse_file("cpu.cfs_period_us");
+
+ match (limit, period) {
+ (Some(limit), Some(period)) => quota = quota.min(limit / period),
+ _ => {}
+ }
+
+ path.pop();
+ }
+
+ // we passed the try_exists above so we should have traversed the correct hierarchy
+ // when reaching this line
+ break;
+ }
+
+ quota
+ }
+
+ /// Scan mountinfo for cgroup v1 mountpoint with a cpu controller
+ ///
+ /// If the cgroupfs is a bind mount then `group_path` is adjusted to skip
+ /// over the already-included prefix
+ fn find_mountpoint(group_path: &Path) -> Option<(Cow<'static, str>, &Path)> {
+ let mut reader = BufReader::new(File::open("/proc/self/mountinfo").ok()?);
+ let mut line = String::with_capacity(256);
+ loop {
+ line.clear();
+ if reader.read_line(&mut line).ok()? == 0 {
+ break;
+ }
+
+ let line = line.trim();
+ let mut items = line.split(' ');
+
+ let sub_path = items.nth(3)?;
+ let mount_point = items.next()?;
+ let mount_opts = items.next_back()?;
+ let filesystem_type = items.nth_back(1)?;
+
+ if filesystem_type != "cgroup" || !mount_opts.split(',').any(|opt| opt == "cpu") {
+ // not a cgroup / not a cpu-controller
+ continue;
+ }
+
+ let sub_path = Path::new(sub_path).strip_prefix("/").ok()?;
+
+ if !group_path.starts_with(sub_path) {
+ // this is a bind-mount and the bound subdirectory
+ // does not contain the cgroup this process belongs to
+ continue;
+ }
+
+ let trimmed_group_path = group_path.strip_prefix(sub_path).ok()?;
+
+ return Some((Cow::Owned(mount_point.to_owned()), trimmed_group_path));
+ }
+
+ None
+ }
+}
+
+#[cfg(all(
+ not(target_os = "linux"),
+ not(target_os = "freebsd"),
+ not(target_os = "macos"),
+ not(target_os = "netbsd"),
+ not(target_os = "openbsd"),
+ not(target_os = "solaris")
+))]
+#[cfg_attr(test, allow(dead_code))]
+pub mod guard {
+ use crate::ops::Range;
+ pub type Guard = Range<usize>;
+ pub unsafe fn current() -> Option<Guard> {
+ None
+ }
+ pub unsafe fn init() -> Option<Guard> {
+ None
+ }
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"
+))]
+#[cfg_attr(test, allow(dead_code))]
+pub mod guard {
+ use libc::{mmap, mprotect};
+ use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
+
+ use crate::io;
+ use crate::ops::Range;
+ use crate::sync::atomic::{AtomicUsize, Ordering};
+ use crate::sys::os;
+
+ // This is initialized in init() and only read from after
+ static PAGE_SIZE: AtomicUsize = AtomicUsize::new(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 = crate::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 th = libc::pthread_self();
+ let stackptr = libc::pthread_get_stackaddr_np(th);
+ Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th)))
+ }
+
+ #[cfg(target_os = "openbsd")]
+ unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
+ let mut current_stack: libc::stack_t = crate::mem::zeroed();
+ assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0);
+
+ let stack_ptr = current_stack.ss_sp;
+ let stackaddr = if libc::pthread_main_np() == 1 {
+ // main thread
+ stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed)
+ } else {
+ // new thread
+ stack_ptr.addr() - current_stack.ss_size
+ };
+ Some(stack_ptr.with_addr(stackaddr))
+ }
+
+ #[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 = crate::mem::zeroed();
+ #[cfg(target_os = "freebsd")]
+ 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 = crate::ptr::null_mut();
+ let mut stacksize = 0;
+ assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0);
+ ret = Some(stackaddr);
+ }
+ if e == 0 || cfg!(target_os = "freebsd") {
+ 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> {
+ let page_size = PAGE_SIZE.load(Ordering::Relaxed);
+ assert!(page_size != 0);
+ let stackptr = get_stack_start()?;
+ let stackaddr = stackptr.addr();
+
+ // 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 % page_size;
+ Some(if remainder == 0 {
+ stackptr
+ } else {
+ stackptr.with_addr(stackaddr + page_size - remainder)
+ })
+ }
+
+ pub unsafe fn init() -> Option<Guard> {
+ let page_size = os::page_size();
+ PAGE_SIZE.store(page_size, Ordering::Relaxed);
+
+ if cfg!(all(target_os = "linux", not(target_env = "musl"))) {
+ // 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 stackptr = get_stack_start_aligned()?;
+ let stackaddr = stackptr.addr();
+ Some(stackaddr - page_size..stackaddr)
+ } else if cfg!(all(target_os = "linux", target_env = "musl")) {
+ // For the main thread, the musl's pthread_attr_getstack
+ // returns the current stack size, rather than maximum size
+ // it can eventually grow to. It cannot be used to determine
+ // the position of kernel's stack guard.
+ None
+ } else if cfg!(target_os = "freebsd") {
+ // FreeBSD's stack autogrows, and optionally includes a guard page
+ // at the bottom. If we try to remap the bottom of the stack
+ // ourselves, FreeBSD's guard page moves upwards. So we'll just use
+ // the builtin guard page.
+ let stackptr = get_stack_start_aligned()?;
+ let guardaddr = stackptr.addr();
+ // Technically the number of guard pages is tunable and controlled
+ // by the security.bsd.stack_guard_page sysctl, but there are
+ // few reasons to change it from the default. The default value has
+ // been 1 ever since FreeBSD 11.1 and 10.4.
+ const GUARD_PAGES: usize = 1;
+ let guard = guardaddr..guardaddr + GUARD_PAGES * page_size;
+ Some(guard)
+ } 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 stackptr = get_stack_start_aligned()?;
+ let result = mmap(
+ stackptr,
+ page_size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED,
+ -1,
+ 0,
+ );
+ if result != stackptr || result == MAP_FAILED {
+ panic!("failed to allocate a guard page: {}", io::Error::last_os_error());
+ }
+
+ let result = mprotect(stackptr, page_size, PROT_NONE);
+ if result != 0 {
+ panic!("failed to protect the guard page: {}", io::Error::last_os_error());
+ }
+
+ let guardaddr = stackptr.addr();
+
+ Some(guardaddr..guardaddr + page_size)
+ }
+ }
+
+ #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))]
+ pub unsafe fn current() -> Option<Guard> {
+ let stackptr = get_stack_start()?;
+ let stackaddr = stackptr.addr();
+ Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..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 = crate::mem::zeroed();
+ #[cfg(target_os = "freebsd")]
+ 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 {
+ if cfg!(all(target_os = "linux", target_env = "musl")) {
+ // musl versions before 1.1.19 always reported guard
+ // size obtained from pthread_attr_get_np as zero.
+ // Use page size as a fallback.
+ guardsize = PAGE_SIZE.load(Ordering::Relaxed);
+ } else {
+ panic!("there is no guard page");
+ }
+ }
+ let mut stackptr = crate::ptr::null_mut::<libc::c_void>();
+ let mut size = 0;
+ assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackptr, &mut size), 0);
+
+ let stackaddr = stackptr.addr();
+ ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd")) {
+ Some(stackaddr - guardsize..stackaddr)
+ } else if cfg!(all(target_os = "linux", target_env = "musl")) {
+ Some(stackaddr - guardsize..stackaddr)
+ } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))
+ {
+ // 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)
+ };
+ }
+ if e == 0 || cfg!(target_os = "freebsd") {
+ 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 bytes 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(all(target_os = "linux", target_env = "gnu"))]
+fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize {
+ // We use dlsym to avoid an ELF version dependency on GLIBC_PRIVATE. (#23628)
+ // We shouldn't really be using such an internal symbol, but there's currently
+ // no other way to account for the TLS size.
+ dlsym!(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(all(target_os = "linux", target_env = "gnu")), 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/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs
new file mode 100644
index 000000000..6e8be2a91
--- /dev/null
+++ b/library/std/src/sys/unix/thread_local_dtor.rs
@@ -0,0 +1,100 @@
+#![cfg(target_thread_local)]
+#![unstable(feature = "thread_local_internals", issue = "none")]
+
+//! Provides thread-local destructors without an associated "key", which
+//! can be more efficient.
+
+// 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.
+#[cfg(any(
+ target_os = "linux",
+ target_os = "fuchsia",
+ target_os = "redox",
+ target_os = "emscripten"
+))]
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+ use crate::mem;
+ use crate::sys_common::thread_local_dtor::register_dtor_fallback;
+
+ extern "C" {
+ #[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 "C" fn(
+ dtor: unsafe extern "C" 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);
+}
+
+// This implementation is very similar to register_dtor_fallback in
+// sys_common/thread_local.rs. The main difference is that we want to hook into
+// macOS's analog of the above linux function, _tlv_atexit. OSX will run the
+// registered dtors before any TLS slots get freed, and when the main thread
+// exits.
+//
+// Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The
+// workaround below is to register, via _tlv_atexit, a custom DTOR list once per
+// thread. thread_local dtors are pushed to the DTOR list without calling
+// _tlv_atexit.
+#[cfg(target_os = "macos")]
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+ use crate::cell::Cell;
+ use crate::ptr;
+
+ #[thread_local]
+ static REGISTERED: Cell<bool> = Cell::new(false);
+ if !REGISTERED.get() {
+ _tlv_atexit(run_dtors, ptr::null_mut());
+ REGISTERED.set(true);
+ }
+
+ type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
+
+ #[thread_local]
+ static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
+ if DTORS.get().is_null() {
+ let v: Box<List> = box Vec::new();
+ DTORS.set(Box::into_raw(v));
+ }
+
+ extern "C" {
+ fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
+ }
+
+ let list: &mut List = &mut *DTORS.get();
+ list.push((t, dtor));
+
+ unsafe extern "C" fn run_dtors(_: *mut u8) {
+ let mut ptr = DTORS.replace(ptr::null_mut());
+ while !ptr.is_null() {
+ let list = Box::from_raw(ptr);
+ for (ptr, dtor) in list.into_iter() {
+ dtor(ptr);
+ }
+ ptr = DTORS.replace(ptr::null_mut());
+ }
+ }
+}
+
+#[cfg(any(target_os = "vxworks", target_os = "horizon"))]
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+ use crate::sys_common::thread_local_dtor::register_dtor_fallback;
+ register_dtor_fallback(t, dtor);
+}
diff --git a/library/std/src/sys/unix/thread_local_key.rs b/library/std/src/sys/unix/thread_local_key.rs
new file mode 100644
index 000000000..2c5b94b1e
--- /dev/null
+++ b/library/std/src/sys/unix/thread_local_key.rs
@@ -0,0 +1,34 @@
+#![allow(dead_code)] // not used on all platforms
+
+use crate::mem;
+
+pub type Key = libc::pthread_key_t;
+
+#[inline]
+pub unsafe fn create(dtor: Option<unsafe extern "C" 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 set(key: Key, value: *mut u8) {
+ let r = libc::pthread_setspecific(key, value as *mut _);
+ debug_assert_eq!(r, 0);
+}
+
+#[inline]
+pub unsafe fn get(key: Key) -> *mut u8 {
+ libc::pthread_getspecific(key) as *mut u8
+}
+
+#[inline]
+pub unsafe fn destroy(key: Key) {
+ let r = libc::pthread_key_delete(key);
+ debug_assert_eq!(r, 0);
+}
+
+#[inline]
+pub fn requires_synchronized_create() -> bool {
+ false
+}
diff --git a/library/std/src/sys/unix/thread_parker.rs b/library/std/src/sys/unix/thread_parker.rs
new file mode 100644
index 000000000..ca1a7138f
--- /dev/null
+++ b/library/std/src/sys/unix/thread_parker.rs
@@ -0,0 +1,281 @@
+//! Thread parking without `futex` using the `pthread` synchronization primitives.
+
+#![cfg(not(any(
+ target_os = "linux",
+ target_os = "android",
+ all(target_os = "emscripten", target_feature = "atomics"),
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ target_os = "fuchsia",
+)))]
+
+use crate::cell::UnsafeCell;
+use crate::marker::PhantomPinned;
+use crate::pin::Pin;
+use crate::ptr::addr_of_mut;
+use crate::sync::atomic::AtomicUsize;
+use crate::sync::atomic::Ordering::SeqCst;
+use crate::time::Duration;
+
+const EMPTY: usize = 0;
+const PARKED: usize = 1;
+const NOTIFIED: usize = 2;
+
+unsafe fn lock(lock: *mut libc::pthread_mutex_t) {
+ let r = libc::pthread_mutex_lock(lock);
+ debug_assert_eq!(r, 0);
+}
+
+unsafe fn unlock(lock: *mut libc::pthread_mutex_t) {
+ let r = libc::pthread_mutex_unlock(lock);
+ debug_assert_eq!(r, 0);
+}
+
+unsafe fn notify_one(cond: *mut libc::pthread_cond_t) {
+ let r = libc::pthread_cond_signal(cond);
+ debug_assert_eq!(r, 0);
+}
+
+unsafe fn wait(cond: *mut libc::pthread_cond_t, lock: *mut libc::pthread_mutex_t) {
+ let r = libc::pthread_cond_wait(cond, lock);
+ debug_assert_eq!(r, 0);
+}
+
+const TIMESPEC_MAX: libc::timespec =
+ libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
+
+unsafe fn wait_timeout(
+ cond: *mut libc::pthread_cond_t,
+ lock: *mut libc::pthread_mutex_t,
+ dur: Duration,
+) {
+ // Use the system clock on systems that do not support pthread_condattr_setclock.
+ // This unfortunately results in problems when the system time changes.
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "espidf"
+ ))]
+ let (now, dur) = {
+ use super::time::SystemTime;
+ use crate::cmp::min;
+
+ // 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 `park_timeout`
+ // because of spurious wakeups.
+ let dur = min(dur, Duration::from_secs(1000 * 365 * 86400));
+ let now = SystemTime::now().t;
+ (now, dur)
+ };
+ // Use the monotonic clock on other systems.
+ #[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "espidf"
+ )))]
+ let (now, dur) = {
+ use super::time::Timespec;
+
+ (Timespec::now(libc::CLOCK_MONOTONIC), dur)
+ };
+
+ let timeout =
+ now.checked_add_duration(&dur).and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX);
+ let r = libc::pthread_cond_timedwait(cond, lock, &timeout);
+ debug_assert!(r == libc::ETIMEDOUT || r == 0);
+}
+
+pub struct Parker {
+ state: AtomicUsize,
+ lock: UnsafeCell<libc::pthread_mutex_t>,
+ cvar: UnsafeCell<libc::pthread_cond_t>,
+ // The `pthread` primitives require a stable address, so make this struct `!Unpin`.
+ _pinned: PhantomPinned,
+}
+
+impl Parker {
+ /// Construct the UNIX parker in-place.
+ ///
+ /// # Safety
+ /// The constructed parker must never be moved.
+ pub unsafe fn new(parker: *mut Parker) {
+ // Use the default mutex implementation to allow for simpler initialization.
+ // This could lead to undefined behaviour when deadlocking. This is avoided
+ // by not deadlocking. Note in particular the unlocking operation before any
+ // panic, as code after the panic could try to park again.
+ addr_of_mut!((*parker).state).write(AtomicUsize::new(EMPTY));
+ addr_of_mut!((*parker).lock).write(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER));
+
+ cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "l4re",
+ target_os = "android",
+ target_os = "redox"
+ ))] {
+ addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER));
+ } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] {
+ let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), crate::ptr::null());
+ assert_eq!(r, 0);
+ } else {
+ use crate::mem::MaybeUninit;
+ let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
+ let r = libc::pthread_condattr_init(attr.as_mut_ptr());
+ assert_eq!(r, 0);
+ let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC);
+ assert_eq!(r, 0);
+ let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), attr.as_ptr());
+ assert_eq!(r, 0);
+ let r = libc::pthread_condattr_destroy(attr.as_mut_ptr());
+ assert_eq!(r, 0);
+ }
+ }
+ }
+
+ // This implementation doesn't require `unsafe`, but other implementations
+ // may assume this is only called by the thread that owns the Parker.
+ pub unsafe fn park(self: Pin<&Self>) {
+ // If we were previously notified then we consume this notification and
+ // return quickly.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return;
+ }
+
+ // Otherwise we need to coordinate going to sleep
+ lock(self.lock.get());
+ match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => {
+ // We must read here, even though we know it will be `NOTIFIED`.
+ // This is because `unpark` may have been called again since we read
+ // `NOTIFIED` in the `compare_exchange` above. We must perform an
+ // acquire operation that synchronizes with that `unpark` to observe
+ // any writes it made before the call to unpark. To do that we must
+ // read from the write it made to `state`.
+ let old = self.state.swap(EMPTY, SeqCst);
+
+ unlock(self.lock.get());
+
+ assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+ return;
+ } // should consume this notification, so prohibit spurious wakeups in next park.
+ Err(_) => {
+ unlock(self.lock.get());
+
+ panic!("inconsistent park state")
+ }
+ }
+
+ loop {
+ wait(self.cvar.get(), self.lock.get());
+
+ match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) {
+ Ok(_) => break, // got a notification
+ Err(_) => {} // spurious wakeup, go back to sleep
+ }
+ }
+
+ unlock(self.lock.get());
+ }
+
+ // This implementation doesn't require `unsafe`, but other implementations
+ // may assume this is only called by the thread that owns the Parker. Use
+ // `Pin` to guarantee a stable address for the mutex and condition variable.
+ pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
+ // Like `park` above we have a fast path for an already-notified thread, and
+ // afterwards we start coordinating for a sleep.
+ // return quickly.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return;
+ }
+
+ lock(self.lock.get());
+ match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => {
+ // We must read again here, see `park`.
+ let old = self.state.swap(EMPTY, SeqCst);
+ unlock(self.lock.get());
+
+ assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+ return;
+ } // should consume this notification, so prohibit spurious wakeups in next park.
+ Err(_) => {
+ unlock(self.lock.get());
+ panic!("inconsistent park_timeout state")
+ }
+ }
+
+ // Wait with a timeout, and if we spuriously wake up or otherwise wake up
+ // from a notification we just want to unconditionally set the state back to
+ // empty, either consuming a notification or un-flagging ourselves as
+ // parked.
+ wait_timeout(self.cvar.get(), self.lock.get(), dur);
+
+ match self.state.swap(EMPTY, SeqCst) {
+ NOTIFIED => unlock(self.lock.get()), // got a notification, hurray!
+ PARKED => unlock(self.lock.get()), // no notification, alas
+ n => {
+ unlock(self.lock.get());
+ panic!("inconsistent park_timeout state: {n}")
+ }
+ }
+ }
+
+ pub fn unpark(self: Pin<&Self>) {
+ // To ensure the unparked thread will observe any writes we made
+ // before this call, we must perform a release operation that `park`
+ // can synchronize with. To do that we must write `NOTIFIED` even if
+ // `state` is already `NOTIFIED`. That is why this must be a swap
+ // rather than a compare-and-swap that returns if it reads `NOTIFIED`
+ // on failure.
+ match self.state.swap(NOTIFIED, SeqCst) {
+ EMPTY => return, // no one was waiting
+ NOTIFIED => return, // already unparked
+ PARKED => {} // gotta go wake someone up
+ _ => panic!("inconsistent state in unpark"),
+ }
+
+ // There is a period between when the parked thread sets `state` to
+ // `PARKED` (or last checked `state` in the case of a spurious wake
+ // up) and when it actually waits on `cvar`. If we were to notify
+ // during this period it would be ignored and then when the parked
+ // thread went to sleep it would never wake up. Fortunately, it has
+ // `lock` locked at this stage so we can acquire `lock` to wait until
+ // it is ready to receive the notification.
+ //
+ // Releasing `lock` before the call to `notify_one` means that when the
+ // parked thread wakes it doesn't get woken only to have to wait for us
+ // to release `lock`.
+ unsafe {
+ lock(self.lock.get());
+ unlock(self.lock.get());
+ notify_one(self.cvar.get());
+ }
+ }
+}
+
+impl Drop for Parker {
+ fn drop(&mut self) {
+ unsafe {
+ libc::pthread_cond_destroy(self.cvar.get_mut());
+ libc::pthread_mutex_destroy(self.lock.get_mut());
+ }
+ }
+}
+
+unsafe impl Sync for Parker {}
+unsafe impl Send for Parker {}
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
new file mode 100644
index 000000000..dff973f59
--- /dev/null
+++ b/library/std/src/sys/unix/time.rs
@@ -0,0 +1,346 @@
+use crate::fmt;
+use crate::time::Duration;
+
+pub use self::inner::Instant;
+
+const NSEC_PER_SEC: u64 = 1_000_000_000;
+pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct SystemTime {
+ pub(in crate::sys::unix) t: Timespec,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub(in crate::sys::unix) struct Timespec {
+ tv_sec: i64,
+ tv_nsec: i64,
+}
+
+impl SystemTime {
+ #[cfg_attr(target_os = "horizon", allow(unused))]
+ pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime {
+ SystemTime { t: Timespec::new(tv_sec, tv_nsec) }
+ }
+
+ pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
+ self.t.sub_timespec(&other.t)
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime { t: self.t.checked_add_duration(other)? })
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime { t: self.t.checked_sub_duration(other)? })
+ }
+}
+
+impl From<libc::timespec> for SystemTime {
+ fn from(t: libc::timespec) -> SystemTime {
+ SystemTime { t: Timespec::from(t) }
+ }
+}
+
+impl fmt::Debug for SystemTime {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SystemTime")
+ .field("tv_sec", &self.t.tv_sec)
+ .field("tv_nsec", &self.t.tv_nsec)
+ .finish()
+ }
+}
+
+impl Timespec {
+ pub const fn zero() -> Timespec {
+ Timespec { tv_sec: 0, tv_nsec: 0 }
+ }
+
+ fn new(tv_sec: i64, tv_nsec: i64) -> Timespec {
+ Timespec { tv_sec, tv_nsec }
+ }
+
+ pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
+ if self >= other {
+ // NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM
+ // to optimize it into a branchless form (see also #75545):
+ //
+ // 1. `self.tv_sec - other.tv_sec` shows up as a common expression
+ // in both branches, i.e. the `else` must have its `- 1`
+ // subtraction after the common one, not interleaved with it
+ // (it used to be `self.tv_sec - 1 - other.tv_sec`)
+ //
+ // 2. the `Duration::new` call (or any other additional complexity)
+ // is outside of the `if`-`else`, not duplicated in both branches
+ //
+ // Ideally this code could be rearranged such that it more
+ // directly expresses the lower-cost behavior we want from it.
+ let (secs, nsec) = if self.tv_nsec >= other.tv_nsec {
+ ((self.tv_sec - other.tv_sec) as u64, (self.tv_nsec - other.tv_nsec) as u32)
+ } else {
+ (
+ (self.tv_sec - other.tv_sec - 1) as u64,
+ self.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.tv_nsec as u32,
+ )
+ };
+
+ Ok(Duration::new(secs, nsec))
+ } else {
+ match other.sub_timespec(self) {
+ Ok(d) => Err(d),
+ Err(d) => Ok(d),
+ }
+ }
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
+ let mut secs = other
+ .as_secs()
+ .try_into() // <- target type would be `i64`
+ .ok()
+ .and_then(|secs| self.tv_sec.checked_add(secs))?;
+
+ // Nano calculations can't overflow because nanos are <1B which fit
+ // in a u32.
+ let mut nsec = other.subsec_nanos() + self.tv_nsec as u32;
+ if nsec >= NSEC_PER_SEC as u32 {
+ nsec -= NSEC_PER_SEC as u32;
+ secs = secs.checked_add(1)?;
+ }
+ Some(Timespec::new(secs, nsec as i64))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
+ let mut secs = other
+ .as_secs()
+ .try_into() // <- target type would be `i64`
+ .ok()
+ .and_then(|secs| self.tv_sec.checked_sub(secs))?;
+
+ // Similar to above, nanos can't overflow.
+ let mut nsec = self.tv_nsec as i32 - other.subsec_nanos() as i32;
+ if nsec < 0 {
+ nsec += NSEC_PER_SEC as i32;
+ secs = secs.checked_sub(1)?;
+ }
+ Some(Timespec::new(secs, nsec as i64))
+ }
+
+ #[allow(dead_code)]
+ pub fn to_timespec(&self) -> Option<libc::timespec> {
+ Some(libc::timespec {
+ tv_sec: self.tv_sec.try_into().ok()?,
+ tv_nsec: self.tv_nsec.try_into().ok()?,
+ })
+ }
+}
+
+impl From<libc::timespec> for Timespec {
+ fn from(t: libc::timespec) -> Timespec {
+ Timespec::new(t.tv_sec as i64, t.tv_nsec as i64)
+ }
+}
+
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+mod inner {
+ use crate::sync::atomic::{AtomicU64, Ordering};
+ use crate::sys::cvt;
+ use crate::sys_common::mul_div_u64;
+ use crate::time::Duration;
+
+ use super::{SystemTime, Timespec, NSEC_PER_SEC};
+
+ #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+ pub struct Instant {
+ t: u64,
+ }
+
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ struct mach_timebase_info {
+ numer: u32,
+ denom: u32,
+ }
+ type mach_timebase_info_t = *mut mach_timebase_info;
+ type kern_return_t = libc::c_int;
+
+ impl Instant {
+ pub fn now() -> Instant {
+ extern "C" {
+ fn mach_absolute_time() -> u64;
+ }
+ Instant { t: unsafe { mach_absolute_time() } }
+ }
+
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ let diff = self.t.checked_sub(other.t)?;
+ let info = info();
+ let nanos = mul_div_u64(diff, info.numer as u64, info.denom as u64);
+ Some(Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32))
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_add(checked_dur2intervals(other)?)? })
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_sub(checked_dur2intervals(other)?)? })
+ }
+ }
+
+ impl SystemTime {
+ pub fn now() -> SystemTime {
+ use crate::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);
+ }
+ }
+
+ impl From<libc::timeval> for Timespec {
+ fn from(t: libc::timeval) -> Timespec {
+ Timespec::new(t.tv_sec as i64, 1000 * t.tv_usec as i64)
+ }
+ }
+
+ impl From<libc::timeval> for SystemTime {
+ fn from(t: libc::timeval) -> SystemTime {
+ SystemTime { t: Timespec::from(t) }
+ }
+ }
+
+ fn checked_dur2intervals(dur: &Duration) -> Option<u64> {
+ let nanos =
+ dur.as_secs().checked_mul(NSEC_PER_SEC)?.checked_add(dur.subsec_nanos() as u64)?;
+ let info = info();
+ Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64))
+ }
+
+ fn info() -> mach_timebase_info {
+ // INFO_BITS conceptually is an `Option<mach_timebase_info>`. We can do
+ // this in 64 bits because we know 0 is never a valid value for the
+ // `denom` field.
+ //
+ // Encoding this as a single `AtomicU64` allows us to use `Relaxed`
+ // operations, as we are only interested in the effects on a single
+ // memory location.
+ static INFO_BITS: AtomicU64 = AtomicU64::new(0);
+
+ // If a previous thread has initialized `INFO_BITS`, use it.
+ let info_bits = INFO_BITS.load(Ordering::Relaxed);
+ if info_bits != 0 {
+ return info_from_bits(info_bits);
+ }
+
+ // ... otherwise learn for ourselves ...
+ extern "C" {
+ fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t;
+ }
+
+ let mut info = info_from_bits(0);
+ unsafe {
+ mach_timebase_info(&mut info);
+ }
+ INFO_BITS.store(info_to_bits(info), Ordering::Relaxed);
+ info
+ }
+
+ #[inline]
+ fn info_to_bits(info: mach_timebase_info) -> u64 {
+ ((info.denom as u64) << 32) | (info.numer as u64)
+ }
+
+ #[inline]
+ fn info_from_bits(bits: u64) -> mach_timebase_info {
+ mach_timebase_info { numer: bits as u32, denom: (bits >> 32) as u32 }
+ }
+}
+
+#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "watchos")))]
+mod inner {
+ use crate::fmt;
+ use crate::mem::MaybeUninit;
+ use crate::sys::cvt;
+ use crate::time::Duration;
+
+ use super::{SystemTime, Timespec};
+
+ #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ pub struct Instant {
+ t: Timespec,
+ }
+
+ impl Instant {
+ pub fn now() -> Instant {
+ Instant { t: Timespec::now(libc::CLOCK_MONOTONIC) }
+ }
+
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ self.t.sub_timespec(&other.t).ok()
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_add_duration(other)? })
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_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.tv_sec)
+ .field("tv_nsec", &self.t.tv_nsec)
+ .finish()
+ }
+ }
+
+ impl SystemTime {
+ pub fn now() -> SystemTime {
+ SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) }
+ }
+ }
+
+ #[cfg(not(any(target_os = "dragonfly", target_os = "espidf", target_os = "horizon")))]
+ pub type clock_t = libc::c_int;
+ #[cfg(any(target_os = "dragonfly", target_os = "espidf", target_os = "horizon"))]
+ pub type clock_t = libc::c_ulong;
+
+ impl Timespec {
+ pub fn now(clock: clock_t) -> Timespec {
+ // Try to use 64-bit time in preparation for Y2038.
+ #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32"))]
+ {
+ use crate::sys::weak::weak;
+
+ // __clock_gettime64 was added to 32-bit arches in glibc 2.34,
+ // and it handles both vDSO calls and ENOSYS fallbacks itself.
+ weak!(fn __clock_gettime64(libc::clockid_t, *mut __timespec64) -> libc::c_int);
+
+ #[repr(C)]
+ struct __timespec64 {
+ tv_sec: i64,
+ #[cfg(target_endian = "big")]
+ _padding: i32,
+ tv_nsec: i32,
+ #[cfg(target_endian = "little")]
+ _padding: i32,
+ }
+
+ if let Some(clock_gettime64) = __clock_gettime64.get() {
+ let mut t = MaybeUninit::uninit();
+ cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
+ let t = unsafe { t.assume_init() };
+ return Timespec { tv_sec: t.tv_sec, tv_nsec: t.tv_nsec as i64 };
+ }
+ }
+
+ let mut t = MaybeUninit::uninit();
+ cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap();
+ Timespec::from(unsafe { t.assume_init() })
+ }
+ }
+}
diff --git a/library/std/src/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs
new file mode 100644
index 000000000..e4ff21b25
--- /dev/null
+++ b/library/std/src/sys/unix/weak.rs
@@ -0,0 +1,205 @@
+//! 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 with ELF. Otherwise, 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) because it is GLIBC_PRIVATE. We now use `dlsym`
+//! for a runtime lookup of that symbol to avoid the ELF versioned dependency.
+
+// There are a variety of `#[cfg]`s controlling which targets are involved in
+// each instance of `weak!` and `syscall!`. Rather than trying to unify all of
+// that, we'll just allow that some unix targets don't use this module at all.
+#![allow(dead_code, unused_macros)]
+
+use crate::ffi::CStr;
+use crate::marker::PhantomData;
+use crate::mem;
+use crate::ptr;
+use crate::sync::atomic::{self, AtomicPtr, Ordering};
+
+// We can use true weak linkage on ELF targets.
+#[cfg(not(any(target_os = "macos", target_os = "ios")))]
+pub(crate) macro weak {
+ (fn $name:ident($($t:ty),*) -> $ret:ty) => (
+ let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
+ extern "C" {
+ #[linkage = "extern_weak"]
+ static $name: *const libc::c_void;
+ }
+ #[allow(unused_unsafe)]
+ ExternWeak::new(unsafe { $name })
+ };
+ )
+}
+
+// On non-ELF targets, use the dlsym approximation of weak linkage.
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+pub(crate) use self::dlsym as weak;
+
+pub(crate) struct ExternWeak<F> {
+ weak_ptr: *const libc::c_void,
+ _marker: PhantomData<F>,
+}
+
+impl<F> ExternWeak<F> {
+ #[inline]
+ pub(crate) fn new(weak_ptr: *const libc::c_void) -> Self {
+ ExternWeak { weak_ptr, _marker: PhantomData }
+ }
+}
+
+impl<F> ExternWeak<F> {
+ #[inline]
+ pub(crate) fn get(&self) -> Option<F> {
+ unsafe {
+ if self.weak_ptr.is_null() {
+ None
+ } else {
+ Some(mem::transmute_copy::<*const libc::c_void, F>(&self.weak_ptr))
+ }
+ }
+ }
+}
+
+pub(crate) macro dlsym {
+ (fn $name:ident($($t:ty),*) -> $ret:ty) => (
+ dlsym!(fn $name($($t),*) -> $ret, stringify!($name));
+ ),
+ (fn $name:ident($($t:ty),*) -> $ret:ty, $sym:expr) => (
+ static DLSYM: DlsymWeak<unsafe extern "C" fn($($t),*) -> $ret> =
+ DlsymWeak::new(concat!($sym, '\0'));
+ let $name = &DLSYM;
+ )
+}
+pub(crate) struct DlsymWeak<F> {
+ name: &'static str,
+ func: AtomicPtr<libc::c_void>,
+ _marker: PhantomData<F>,
+}
+
+impl<F> DlsymWeak<F> {
+ pub(crate) const fn new(name: &'static str) -> Self {
+ DlsymWeak { name, func: AtomicPtr::new(ptr::invalid_mut(1)), _marker: PhantomData }
+ }
+
+ #[inline]
+ pub(crate) fn get(&self) -> Option<F> {
+ unsafe {
+ // Relaxed is fine here because we fence before reading through the
+ // pointer (see the comment below).
+ match self.func.load(Ordering::Relaxed) {
+ func if func.addr() == 1 => self.initialize(),
+ func if func.is_null() => None,
+ func => {
+ let func = mem::transmute_copy::<*mut libc::c_void, F>(&func);
+ // The caller is presumably going to read through this value
+ // (by calling the function we've dlsymed). This means we'd
+ // need to have loaded it with at least C11's consume
+ // ordering in order to be guaranteed that the data we read
+ // from the pointer isn't from before the pointer was
+ // stored. Rust has no equivalent to memory_order_consume,
+ // so we use an acquire fence (sorry, ARM).
+ //
+ // Now, in practice this likely isn't needed even on CPUs
+ // where relaxed and consume mean different things. The
+ // symbols we're loading are probably present (or not) at
+ // init, and even if they aren't the runtime dynamic loader
+ // is extremely likely have sufficient barriers internally
+ // (possibly implicitly, for example the ones provided by
+ // invoking `mprotect`).
+ //
+ // That said, none of that's *guaranteed*, and so we fence.
+ atomic::fence(Ordering::Acquire);
+ Some(func)
+ }
+ }
+ }
+ }
+
+ // Cold because it should only happen during first-time initialization.
+ #[cold]
+ unsafe fn initialize(&self) -> Option<F> {
+ assert_eq!(mem::size_of::<F>(), mem::size_of::<*mut libc::c_void>());
+
+ let val = fetch(self.name);
+ // This synchronizes with the acquire fence in `get`.
+ self.func.store(val, Ordering::Release);
+
+ if val.is_null() { None } else { Some(mem::transmute_copy::<*mut libc::c_void, F>(&val)) }
+ }
+}
+
+unsafe fn fetch(name: &str) -> *mut libc::c_void {
+ let name = match CStr::from_bytes_with_nul(name.as_bytes()) {
+ Ok(cstr) => cstr,
+ Err(..) => return ptr::null_mut(),
+ };
+ libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr())
+}
+
+#[cfg(not(any(target_os = "linux", target_os = "android")))]
+pub(crate) macro syscall {
+ (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
+ unsafe fn $name($($arg_name: $t),*) -> $ret {
+ weak! { fn $name($($t),*) -> $ret }
+
+ if let Some(fun) = $name.get() {
+ fun($($arg_name),*)
+ } else {
+ super::os::set_errno(libc::ENOSYS);
+ -1
+ }
+ }
+ )
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub(crate) macro syscall {
+ (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
+ unsafe fn $name($($arg_name:$t),*) -> $ret {
+ weak! { fn $name($($t),*) -> $ret }
+
+ // Use a weak symbol from libc when possible, allowing `LD_PRELOAD`
+ // interposition, but if it's not found just use a raw syscall.
+ if let Some(fun) = $name.get() {
+ fun($($arg_name),*)
+ } else {
+ // This looks like a hack, but concat_idents only accepts idents
+ // (not paths).
+ use libc::*;
+
+ syscall(
+ concat_idents!(SYS_, $name),
+ $($arg_name),*
+ ) as $ret
+ }
+ }
+ )
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub(crate) macro raw_syscall {
+ (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
+ unsafe fn $name($($arg_name:$t),*) -> $ret {
+ // This looks like a hack, but concat_idents only accepts idents
+ // (not paths).
+ use libc::*;
+
+ syscall(
+ concat_idents!(SYS_, $name),
+ $($arg_name),*
+ ) as $ret
+ }
+ )
+}
diff --git a/library/std/src/sys/unsupported/alloc.rs b/library/std/src/sys/unsupported/alloc.rs
new file mode 100644
index 000000000..8d5d0a2f5
--- /dev/null
+++ b/library/std/src/sys/unsupported/alloc.rs
@@ -0,0 +1,22 @@
+use crate::alloc::{GlobalAlloc, Layout, System};
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+ #[inline]
+ unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
+ 0 as *mut u8
+ }
+
+ #[inline]
+ unsafe fn alloc_zeroed(&self, _layout: Layout) -> *mut u8 {
+ 0 as *mut u8
+ }
+
+ #[inline]
+ unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
+
+ #[inline]
+ unsafe fn realloc(&self, _ptr: *mut u8, _layout: Layout, _new_size: usize) -> *mut u8 {
+ 0 as *mut u8
+ }
+}
diff --git a/library/std/src/sys/unsupported/args.rs b/library/std/src/sys/unsupported/args.rs
new file mode 100644
index 000000000..a2d75a619
--- /dev/null
+++ b/library/std/src/sys/unsupported/args.rs
@@ -0,0 +1,36 @@
+use crate::ffi::OsString;
+use crate::fmt;
+
+pub struct Args {}
+
+pub fn args() -> Args {
+ Args {}
+}
+
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().finish()
+ }
+}
+
+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
+ }
+}
diff --git a/library/std/src/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs
new file mode 100644
index 000000000..4c9ade4a8
--- /dev/null
+++ b/library/std/src/sys/unsupported/common.rs
@@ -0,0 +1,36 @@
+use crate::io as std_io;
+
+pub mod memchr {
+ pub use core::slice::memchr::{memchr, memrchr};
+}
+
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {}
+
+pub fn unsupported<T>() -> std_io::Result<T> {
+ Err(unsupported_err())
+}
+
+pub fn unsupported_err() -> std_io::Error {
+ std_io::const_io_error!(
+ std_io::ErrorKind::Unsupported,
+ "operation not supported on this platform",
+ )
+}
+
+pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind {
+ crate::io::ErrorKind::Uncategorized
+}
+
+pub fn abort_internal() -> ! {
+ core::intrinsics::abort();
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+ (1, 2)
+}
diff --git a/library/std/src/sys/unsupported/env.rs b/library/std/src/sys/unsupported/env.rs
new file mode 100644
index 000000000..d2efec506
--- /dev/null
+++ b/library/std/src/sys/unsupported/env.rs
@@ -0,0 +1,9 @@
+pub mod os {
+ pub const FAMILY: &str = "";
+ pub const OS: &str = "";
+ pub const DLL_PREFIX: &str = "";
+ pub const DLL_SUFFIX: &str = "";
+ pub const DLL_EXTENSION: &str = "";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
diff --git a/library/std/src/sys/unsupported/fs.rs b/library/std/src/sys/unsupported/fs.rs
new file mode 100644
index 000000000..0e1a6257e
--- /dev/null
+++ b/library/std/src/sys/unsupported/fs.rs
@@ -0,0 +1,324 @@
+use crate::ffi::OsString;
+use crate::fmt;
+use crate::hash::{Hash, Hasher};
+use crate::io::{self, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
+use crate::path::{Path, PathBuf};
+use crate::sys::time::SystemTime;
+use crate::sys::unsupported;
+
+pub struct File(!);
+
+pub struct FileAttr(!);
+
+pub struct ReadDir(!);
+
+pub struct DirEntry(!);
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions {}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {}
+
+pub struct FilePermissions(!);
+
+pub struct FileType(!);
+
+#[derive(Debug)]
+pub struct DirBuilder {}
+
+impl FileAttr {
+ pub fn size(&self) -> u64 {
+ self.0
+ }
+
+ pub fn perm(&self) -> FilePermissions {
+ self.0
+ }
+
+ pub fn file_type(&self) -> FileType {
+ self.0
+ }
+
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ self.0
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ self.0
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ self.0
+ }
+}
+
+impl Clone for FileAttr {
+ fn clone(&self) -> FileAttr {
+ self.0
+ }
+}
+
+impl FilePermissions {
+ pub fn readonly(&self) -> bool {
+ self.0
+ }
+
+ pub fn set_readonly(&mut self, _readonly: bool) {
+ self.0
+ }
+}
+
+impl Clone for FilePermissions {
+ fn clone(&self) -> FilePermissions {
+ self.0
+ }
+}
+
+impl PartialEq for FilePermissions {
+ fn eq(&self, _other: &FilePermissions) -> bool {
+ self.0
+ }
+}
+
+impl Eq for FilePermissions {}
+
+impl fmt::Debug for FilePermissions {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+impl FileTimes {
+ pub fn set_accessed(&mut self, _t: SystemTime) {}
+ pub fn set_modified(&mut self, _t: SystemTime) {}
+}
+
+impl FileType {
+ pub fn is_dir(&self) -> bool {
+ self.0
+ }
+
+ pub fn is_file(&self) -> bool {
+ self.0
+ }
+
+ pub fn is_symlink(&self) -> bool {
+ self.0
+ }
+}
+
+impl Clone for FileType {
+ fn clone(&self) -> FileType {
+ self.0
+ }
+}
+
+impl Copy for FileType {}
+
+impl PartialEq for FileType {
+ fn eq(&self, _other: &FileType) -> bool {
+ self.0
+ }
+}
+
+impl Eq for FileType {}
+
+impl Hash for FileType {
+ fn hash<H: Hasher>(&self, _h: &mut H) {
+ self.0
+ }
+}
+
+impl fmt::Debug for FileType {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+impl fmt::Debug for ReadDir {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+impl Iterator for ReadDir {
+ type Item = io::Result<DirEntry>;
+
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ self.0
+ }
+}
+
+impl DirEntry {
+ pub fn path(&self) -> PathBuf {
+ self.0
+ }
+
+ pub fn file_name(&self) -> OsString {
+ self.0
+ }
+
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ self.0
+ }
+
+ pub fn file_type(&self) -> io::Result<FileType> {
+ 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> {
+ self.0
+ }
+
+ pub fn fsync(&self) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn datasync(&self) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn truncate(&self, _size: u64) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn is_read_vectored(&self) -> bool {
+ self.0
+ }
+
+ pub fn read_buf(&self, _buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn is_write_vectored(&self) -> bool {
+ self.0
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
+ self.0
+ }
+
+ pub fn duplicate(&self) -> io::Result<File> {
+ self.0
+ }
+
+ pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
+ 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 {
+ 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 try_exists(_path: &Path) -> io::Result<bool> {
+ unsupported()
+}
+
+pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn symlink(_original: &Path, _link: &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/library/std/src/sys/unsupported/io.rs b/library/std/src/sys/unsupported/io.rs
new file mode 100644
index 000000000..d5f475b43
--- /dev/null
+++ b/library/std/src/sys/unsupported/io.rs
@@ -0,0 +1,47 @@
+use crate::mem;
+
+#[derive(Copy, Clone)]
+pub struct IoSlice<'a>(&'a [u8]);
+
+impl<'a> IoSlice<'a> {
+ #[inline]
+ pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
+ IoSlice(buf)
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ self.0 = &self.0[n..]
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ self.0
+ }
+}
+
+pub struct IoSliceMut<'a>(&'a mut [u8]);
+
+impl<'a> IoSliceMut<'a> {
+ #[inline]
+ pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
+ IoSliceMut(buf)
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ let slice = mem::replace(&mut self.0, &mut []);
+ let (_, remaining) = slice.split_at_mut(n);
+ self.0 = remaining;
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ self.0
+ }
+
+ #[inline]
+ pub fn as_mut_slice(&mut self) -> &mut [u8] {
+ self.0
+ }
+}
diff --git a/library/std/src/sys/unsupported/locks/condvar.rs b/library/std/src/sys/unsupported/locks/condvar.rs
new file mode 100644
index 000000000..e703fd0d2
--- /dev/null
+++ b/library/std/src/sys/unsupported/locks/condvar.rs
@@ -0,0 +1,27 @@
+use crate::sys::locks::Mutex;
+use crate::time::Duration;
+
+pub struct Condvar {}
+
+pub type MovableCondvar = Condvar;
+
+impl Condvar {
+ #[inline]
+ pub const fn new() -> Condvar {
+ Condvar {}
+ }
+
+ #[inline]
+ pub unsafe fn notify_one(&self) {}
+
+ #[inline]
+ pub unsafe fn notify_all(&self) {}
+
+ pub unsafe fn wait(&self, _mutex: &Mutex) {
+ panic!("condvar wait not supported")
+ }
+
+ pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool {
+ panic!("condvar wait not supported");
+ }
+}
diff --git a/library/std/src/sys/unsupported/locks/mod.rs b/library/std/src/sys/unsupported/locks/mod.rs
new file mode 100644
index 000000000..d412ff152
--- /dev/null
+++ b/library/std/src/sys/unsupported/locks/mod.rs
@@ -0,0 +1,6 @@
+mod condvar;
+mod mutex;
+mod rwlock;
+pub use condvar::{Condvar, MovableCondvar};
+pub use mutex::{MovableMutex, Mutex};
+pub use rwlock::{MovableRwLock, RwLock};
diff --git a/library/std/src/sys/unsupported/locks/mutex.rs b/library/std/src/sys/unsupported/locks/mutex.rs
new file mode 100644
index 000000000..d7cb12e0c
--- /dev/null
+++ b/library/std/src/sys/unsupported/locks/mutex.rs
@@ -0,0 +1,36 @@
+use crate::cell::Cell;
+
+pub struct Mutex {
+ // This platform has no threads, so we can use a Cell here.
+ locked: Cell<bool>,
+}
+
+pub type MovableMutex = Mutex;
+
+unsafe impl Send for Mutex {}
+unsafe impl Sync for Mutex {} // no threads on this platform
+
+impl Mutex {
+ #[inline]
+ pub const fn new() -> Mutex {
+ Mutex { locked: Cell::new(false) }
+ }
+
+ #[inline]
+ pub unsafe fn init(&mut self) {}
+
+ #[inline]
+ pub unsafe fn lock(&self) {
+ assert_eq!(self.locked.replace(true), false, "cannot recursively acquire mutex");
+ }
+
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ self.locked.set(false);
+ }
+
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ self.locked.replace(true) == false
+ }
+}
diff --git a/library/std/src/sys/unsupported/locks/rwlock.rs b/library/std/src/sys/unsupported/locks/rwlock.rs
new file mode 100644
index 000000000..aca5fb715
--- /dev/null
+++ b/library/std/src/sys/unsupported/locks/rwlock.rs
@@ -0,0 +1,66 @@
+use crate::cell::Cell;
+
+pub struct RwLock {
+ // This platform has no threads, so we can use a Cell here.
+ mode: Cell<isize>,
+}
+
+pub type MovableRwLock = RwLock;
+
+unsafe impl Send for RwLock {}
+unsafe impl Sync for RwLock {} // no threads on this platform
+
+impl RwLock {
+ #[inline]
+ pub const fn new() -> RwLock {
+ RwLock { mode: Cell::new(0) }
+ }
+
+ #[inline]
+ pub unsafe fn read(&self) {
+ let m = self.mode.get();
+ if m >= 0 {
+ self.mode.set(m + 1);
+ } else {
+ rtabort!("rwlock locked for writing");
+ }
+ }
+
+ #[inline]
+ pub unsafe fn try_read(&self) -> bool {
+ let m = self.mode.get();
+ if m >= 0 {
+ self.mode.set(m + 1);
+ true
+ } else {
+ false
+ }
+ }
+
+ #[inline]
+ pub unsafe fn write(&self) {
+ if self.mode.replace(-1) != 0 {
+ rtabort!("rwlock locked for reading")
+ }
+ }
+
+ #[inline]
+ pub unsafe fn try_write(&self) -> bool {
+ if self.mode.get() == 0 {
+ self.mode.set(-1);
+ true
+ } else {
+ false
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read_unlock(&self) {
+ self.mode.set(self.mode.get() - 1);
+ }
+
+ #[inline]
+ pub unsafe fn write_unlock(&self) {
+ assert_eq!(self.mode.replace(0), -1);
+ }
+}
diff --git a/library/std/src/sys/unsupported/mod.rs b/library/std/src/sys/unsupported/mod.rs
new file mode 100644
index 000000000..7bf6d40b7
--- /dev/null
+++ b/library/std/src/sys/unsupported/mod.rs
@@ -0,0 +1,27 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+pub mod alloc;
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+pub mod env;
+pub mod fs;
+pub mod io;
+pub mod locks;
+pub mod net;
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+#[path = "../unix/path.rs"]
+pub mod path;
+pub mod pipe;
+pub mod process;
+pub mod stdio;
+pub mod thread;
+#[cfg(target_thread_local)]
+pub mod thread_local_dtor;
+pub mod thread_local_key;
+pub mod time;
+
+mod common;
+pub use common::*;
diff --git a/library/std/src/sys/unsupported/net.rs b/library/std/src/sys/unsupported/net.rs
new file mode 100644
index 000000000..a5204a084
--- /dev/null
+++ b/library/std/src/sys/unsupported/net.rs
@@ -0,0 +1,366 @@
+use crate::fmt;
+use crate::io::{self, IoSlice, IoSliceMut};
+use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+use crate::sys::unsupported;
+use crate::time::Duration;
+
+pub struct TcpStream(!);
+
+impl TcpStream {
+ pub fn connect(_: io::Result<&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<()> {
+ self.0
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn is_read_vectored(&self) -> bool {
+ self.0
+ }
+
+ pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn is_write_vectored(&self) -> bool {
+ self.0
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ self.0
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ self.0
+ }
+
+ pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpStream> {
+ self.0
+ }
+
+ pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ self.0
+ }
+
+ pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ self.0
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ self.0
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ self.0
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ self.0
+ }
+}
+
+impl fmt::Debug for TcpStream {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+pub struct TcpListener(!);
+
+impl TcpListener {
+ pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
+ unsupported()
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ self.0
+ }
+
+ pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+ self.0
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpListener> {
+ self.0
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ self.0
+ }
+
+ pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn only_v6(&self) -> io::Result<bool> {
+ self.0
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ self.0
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ self.0
+ }
+}
+
+impl fmt::Debug for TcpListener {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+pub struct UdpSocket(!);
+
+impl UdpSocket {
+ pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+ unsupported()
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ self.0
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ self.0
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ self.0
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ self.0
+ }
+
+ pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn duplicate(&self) -> io::Result<UdpSocket> {
+ self.0
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0
+ }
+
+ pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn broadcast(&self) -> io::Result<bool> {
+ self.0
+ }
+
+ pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+ self.0
+ }
+
+ pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+ self.0
+ }
+
+ pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+ self.0
+ }
+
+ pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ self.0
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ self.0
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
+ self.0
+ }
+}
+
+impl fmt::Debug for UdpSocket {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+pub struct LookupHost(!);
+
+impl LookupHost {
+ pub fn port(&self) -> u16 {
+ self.0
+ }
+}
+
+impl Iterator for LookupHost {
+ type Item = SocketAddr;
+ fn next(&mut self) -> Option<SocketAddr> {
+ self.0
+ }
+}
+
+impl TryFrom<&str> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(_v: &str) -> io::Result<LookupHost> {
+ unsupported()
+ }
+}
+
+impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> {
+ unsupported()
+ }
+}
+
+#[allow(nonstandard_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 {}
+}
diff --git a/library/std/src/sys/unsupported/os.rs b/library/std/src/sys/unsupported/os.rs
new file mode 100644
index 000000000..e150ae143
--- /dev/null
+++ b/library/std/src/sys/unsupported/os.rs
@@ -0,0 +1,105 @@
+use super::unsupported;
+use crate::error::Error as StdError;
+use crate::ffi::{OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::marker::PhantomData;
+use crate::path::{self, PathBuf};
+
+pub fn errno() -> i32 {
+ 0
+}
+
+pub fn error_string(_errno: i32) -> String {
+ "operation successful".to_string()
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
+ panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+ type Item = PathBuf;
+ fn next(&mut self) -> Option<PathBuf> {
+ 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 this platform yet".fmt(f)
+ }
+}
+
+impl StdError for JoinPathsError {
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "not supported on this platform yet"
+ }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub struct Env(!);
+
+impl Iterator for Env {
+ type Item = (OsString, OsString);
+ fn next(&mut self) -> Option<(OsString, OsString)> {
+ self.0
+ }
+}
+
+pub fn env() -> Env {
+ panic!("not supported on this platform")
+}
+
+pub fn getenv(_: &OsStr) -> Option<OsString> {
+ None
+}
+
+pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
+ Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
+}
+
+pub fn unsetenv(_: &OsStr) -> io::Result<()> {
+ Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
+}
+
+pub fn temp_dir() -> PathBuf {
+ panic!("no filesystem on this platform")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ None
+}
+
+pub fn exit(_code: i32) -> ! {
+ crate::intrinsics::abort()
+}
+
+pub fn getpid() -> u32 {
+ panic!("no pids on this platform")
+}
diff --git a/library/std/src/sys/unsupported/pipe.rs b/library/std/src/sys/unsupported/pipe.rs
new file mode 100644
index 000000000..25514c232
--- /dev/null
+++ b/library/std/src/sys/unsupported/pipe.rs
@@ -0,0 +1,37 @@
+use crate::io::{self, IoSlice, IoSliceMut};
+
+pub struct AnonPipe(!);
+
+impl AnonPipe {
+ pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn is_read_vectored(&self) -> bool {
+ self.0
+ }
+
+ pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.0
+ }
+
+ pub fn is_write_vectored(&self) -> bool {
+ self.0
+ }
+
+ pub fn diverge(&self) -> ! {
+ 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/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs
new file mode 100644
index 000000000..42a1ff730
--- /dev/null
+++ b/library/std/src/sys/unsupported/process.rs
@@ -0,0 +1,211 @@
+use crate::ffi::OsStr;
+use crate::fmt;
+use crate::io;
+use crate::marker::PhantomData;
+use crate::num::NonZeroI32;
+use crate::path::Path;
+use crate::sys::fs::File;
+use crate::sys::pipe::AnonPipe;
+use crate::sys::unsupported;
+use crate::sys_common::process::{CommandEnv, CommandEnvs};
+
+pub use crate::ffi::OsString as EnvKey;
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct Command {
+ env: CommandEnv,
+}
+
+// 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 {
+ &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 get_program(&self) -> &OsStr {
+ panic!("unsupported")
+ }
+
+ pub fn get_args(&self) -> CommandArgs<'_> {
+ CommandArgs { _p: PhantomData }
+ }
+
+ pub fn get_envs(&self) -> CommandEnvs<'_> {
+ self.env.iter()
+ }
+
+ pub fn get_current_dir(&self) -> Option<&Path> {
+ None
+ }
+
+ 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 {
+ panic!("unsupported")
+ }
+}
+
+impl fmt::Debug for Command {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ Ok(())
+ }
+}
+
+pub struct ExitStatus(!);
+
+impl ExitStatus {
+ pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
+ self.0
+ }
+
+ pub fn code(&self) -> Option<i32> {
+ self.0
+ }
+}
+
+impl Clone for ExitStatus {
+ fn clone(&self) -> ExitStatus {
+ self.0
+ }
+}
+
+impl Copy for ExitStatus {}
+
+impl PartialEq for ExitStatus {
+ fn eq(&self, _other: &ExitStatus) -> bool {
+ self.0
+ }
+}
+
+impl Eq for ExitStatus {}
+
+impl fmt::Debug for ExitStatus {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatusError(ExitStatus);
+
+impl Into<ExitStatus> for ExitStatusError {
+ fn into(self) -> ExitStatus {
+ self.0.0
+ }
+}
+
+impl ExitStatusError {
+ pub fn code(self) -> Option<NonZeroI32> {
+ self.0.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
+ }
+}
+
+impl From<u8> for ExitCode {
+ fn from(code: u8) -> Self {
+ match code {
+ 0 => Self::SUCCESS,
+ 1..=255 => Self::FAILURE,
+ }
+ }
+}
+
+pub struct Process(!);
+
+impl Process {
+ pub fn id(&self) -> u32 {
+ self.0
+ }
+
+ pub fn kill(&mut self) -> io::Result<()> {
+ self.0
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ self.0
+ }
+
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+ self.0
+ }
+}
+
+pub struct CommandArgs<'a> {
+ _p: PhantomData<&'a ()>,
+}
+
+impl<'a> Iterator for CommandArgs<'a> {
+ type Item = &'a OsStr;
+ fn next(&mut self) -> Option<&'a OsStr> {
+ None
+ }
+}
+
+impl<'a> ExactSizeIterator for CommandArgs<'a> {}
+
+impl<'a> fmt::Debug for CommandArgs<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().finish()
+ }
+}
diff --git a/library/std/src/sys/unsupported/stdio.rs b/library/std/src/sys/unsupported/stdio.rs
new file mode 100644
index 000000000..b5e3f5be9
--- /dev/null
+++ b/library/std/src/sys/unsupported/stdio.rs
@@ -0,0 +1,59 @@
+use crate::io;
+
+pub struct Stdin;
+pub struct Stdout;
+pub struct Stderr;
+
+impl Stdin {
+ pub const fn new() -> Stdin {
+ Stdin
+ }
+}
+
+impl io::Read for Stdin {
+ fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
+ Ok(0)
+ }
+}
+
+impl Stdout {
+ pub const fn new() -> Stdout {
+ Stdout
+ }
+}
+
+impl io::Write for Stdout {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Stderr {
+ pub const fn new() -> Stderr {
+ Stderr
+ }
+}
+
+impl io::Write for Stderr {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+pub const STDIN_BUF_SIZE: usize = 0;
+
+pub fn is_ebadf(_err: &io::Error) -> bool {
+ true
+}
+
+pub fn panic_output() -> Option<Vec<u8>> {
+ None
+}
diff --git a/library/std/src/sys/unsupported/thread.rs b/library/std/src/sys/unsupported/thread.rs
new file mode 100644
index 000000000..a8db251de
--- /dev/null
+++ b/library/std/src/sys/unsupported/thread.rs
@@ -0,0 +1,46 @@
+use super::unsupported;
+use crate::ffi::CStr;
+use crate::io;
+use crate::num::NonZeroUsize;
+use crate::time::Duration;
+
+pub struct Thread(!);
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
+
+impl Thread {
+ // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+ pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> 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) {
+ self.0
+ }
+}
+
+pub fn available_parallelism() -> io::Result<NonZeroUsize> {
+ unsupported()
+}
+
+pub mod guard {
+ pub type Guard = !;
+ pub unsafe fn current() -> Option<Guard> {
+ None
+ }
+ pub unsafe fn init() -> Option<Guard> {
+ None
+ }
+}
diff --git a/library/std/src/sys/unsupported/thread_local_dtor.rs b/library/std/src/sys/unsupported/thread_local_dtor.rs
new file mode 100644
index 000000000..85d660983
--- /dev/null
+++ b/library/std/src/sys/unsupported/thread_local_dtor.rs
@@ -0,0 +1,9 @@
+#![unstable(feature = "thread_local_internals", issue = "none")]
+
+pub unsafe fn register_dtor(_t: *mut u8, _dtor: unsafe extern "C" fn(*mut u8)) {
+ // FIXME: right now there is no concept of "thread exit", but this is likely
+ // going to show up at some point in the form of an exported symbol that the
+ // wasm runtime is going to be expected to call. For now we basically just
+ // ignore the arguments, but if such a function starts to exist it will
+ // likely look like the OSX implementation in `unix/fast_thread_local.rs`
+}
diff --git a/library/std/src/sys/unsupported/thread_local_key.rs b/library/std/src/sys/unsupported/thread_local_key.rs
new file mode 100644
index 000000000..c31b61cbf
--- /dev/null
+++ b/library/std/src/sys/unsupported/thread_local_key.rs
@@ -0,0 +1,26 @@
+pub type Key = usize;
+
+#[inline]
+pub unsafe fn create(_dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
+ panic!("should not be used on this target");
+}
+
+#[inline]
+pub unsafe fn set(_key: Key, _value: *mut u8) {
+ panic!("should not be used on this target");
+}
+
+#[inline]
+pub unsafe fn get(_key: Key) -> *mut u8 {
+ panic!("should not be used on this target");
+}
+
+#[inline]
+pub unsafe fn destroy(_key: Key) {
+ panic!("should not be used on this target");
+}
+
+#[inline]
+pub fn requires_synchronized_create() -> bool {
+ panic!("should not be used on this target");
+}
diff --git a/library/std/src/sys/unsupported/time.rs b/library/std/src/sys/unsupported/time.rs
new file mode 100644
index 000000000..6d67b538a
--- /dev/null
+++ b/library/std/src/sys/unsupported/time.rs
@@ -0,0 +1,45 @@
+use crate::time::Duration;
+
+#[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 {
+ panic!("time not implemented on this platform")
+ }
+
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ self.0.checked_sub(other.0)
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant(self.0.checked_add(*other)?))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant(self.0.checked_sub(*other)?))
+ }
+}
+
+impl SystemTime {
+ pub fn now() -> SystemTime {
+ panic!("time not implemented on this platform")
+ }
+
+ 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 checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_add(*other)?))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_sub(*other)?))
+ }
+}
diff --git a/library/std/src/sys/wasi/args.rs b/library/std/src/sys/wasi/args.rs
new file mode 100644
index 000000000..c42c310e3
--- /dev/null
+++ b/library/std/src/sys/wasi/args.rs
@@ -0,0 +1,62 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::ffi::{CStr, OsStr, OsString};
+use crate::fmt;
+use crate::os::wasi::ffi::OsStrExt;
+use crate::vec;
+
+pub struct Args {
+ iter: vec::IntoIter<OsString>,
+}
+
+impl !Send for Args {}
+impl !Sync for Args {}
+
+/// Returns the command line arguments
+pub fn args() -> Args {
+ Args { iter: maybe_args().unwrap_or(Vec::new()).into_iter() }
+}
+
+fn maybe_args() -> Option<Vec<OsString>> {
+ unsafe {
+ let (argc, buf_size) = wasi::args_sizes_get().ok()?;
+ let mut argv = Vec::with_capacity(argc);
+ let mut buf = Vec::with_capacity(buf_size);
+ wasi::args_get(argv.as_mut_ptr(), buf.as_mut_ptr()).ok()?;
+ argv.set_len(argc);
+ let mut ret = Vec::with_capacity(argc);
+ for ptr in argv {
+ let s = CStr::from_ptr(ptr.cast());
+ ret.push(OsStr::from_bytes(s.to_bytes()).to_owned());
+ }
+ Some(ret)
+ }
+}
+
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.iter.as_slice().fmt(f)
+ }
+}
+
+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/library/std/src/sys/wasi/env.rs b/library/std/src/sys/wasi/env.rs
new file mode 100644
index 000000000..730e356d7
--- /dev/null
+++ b/library/std/src/sys/wasi/env.rs
@@ -0,0 +1,9 @@
+pub mod os {
+ pub const FAMILY: &str = "";
+ pub const OS: &str = "";
+ pub const DLL_PREFIX: &str = "";
+ pub const DLL_SUFFIX: &str = ".wasm";
+ pub const DLL_EXTENSION: &str = "wasm";
+ pub const EXE_SUFFIX: &str = ".wasm";
+ pub const EXE_EXTENSION: &str = "wasm";
+}
diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs
new file mode 100644
index 000000000..0b9c8e61d
--- /dev/null
+++ b/library/std/src/sys/wasi/fd.rs
@@ -0,0 +1,307 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+#![allow(dead_code)]
+
+use super::err2io;
+use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
+use crate::mem;
+use crate::net::Shutdown;
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
+use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
+
+#[derive(Debug)]
+pub struct WasiFd {
+ fd: OwnedFd,
+}
+
+fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] {
+ assert_eq!(mem::size_of::<IoSliceMut<'_>>(), mem::size_of::<wasi::Iovec>());
+ assert_eq!(mem::align_of::<IoSliceMut<'_>>(), mem::align_of::<wasi::Iovec>());
+ // SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout
+ unsafe { mem::transmute(a) }
+}
+
+fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] {
+ assert_eq!(mem::size_of::<IoSlice<'_>>(), mem::size_of::<wasi::Ciovec>());
+ assert_eq!(mem::align_of::<IoSlice<'_>>(), mem::align_of::<wasi::Ciovec>());
+ // SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout
+ unsafe { mem::transmute(a) }
+}
+
+impl WasiFd {
+ pub fn datasync(&self) -> io::Result<()> {
+ unsafe { wasi::fd_datasync(self.as_raw_fd() as wasi::Fd).map_err(err2io) }
+ }
+
+ pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
+ unsafe { wasi::fd_pread(self.as_raw_fd() as wasi::Fd, iovec(bufs), offset).map_err(err2io) }
+ }
+
+ pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
+ unsafe {
+ wasi::fd_pwrite(self.as_raw_fd() as wasi::Fd, ciovec(bufs), offset).map_err(err2io)
+ }
+ }
+
+ pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) }
+ }
+
+ pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ unsafe { wasi::fd_write(self.as_raw_fd() as wasi::Fd, ciovec(bufs)).map_err(err2io) }
+ }
+
+ pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
+ let (whence, offset) = match pos {
+ SeekFrom::Start(pos) => (wasi::WHENCE_SET, pos as i64),
+ SeekFrom::End(pos) => (wasi::WHENCE_END, pos),
+ SeekFrom::Current(pos) => (wasi::WHENCE_CUR, pos),
+ };
+ unsafe { wasi::fd_seek(self.as_raw_fd() as wasi::Fd, offset, whence).map_err(err2io) }
+ }
+
+ pub fn tell(&self) -> io::Result<u64> {
+ unsafe { wasi::fd_tell(self.as_raw_fd() as wasi::Fd).map_err(err2io) }
+ }
+
+ // FIXME: __wasi_fd_fdstat_get
+
+ pub fn set_flags(&self, flags: wasi::Fdflags) -> io::Result<()> {
+ unsafe { wasi::fd_fdstat_set_flags(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) }
+ }
+
+ pub fn set_rights(&self, base: wasi::Rights, inheriting: wasi::Rights) -> io::Result<()> {
+ unsafe {
+ wasi::fd_fdstat_set_rights(self.as_raw_fd() as wasi::Fd, base, inheriting)
+ .map_err(err2io)
+ }
+ }
+
+ pub fn sync(&self) -> io::Result<()> {
+ unsafe { wasi::fd_sync(self.as_raw_fd() as wasi::Fd).map_err(err2io) }
+ }
+
+ pub fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> {
+ unsafe {
+ wasi::fd_advise(self.as_raw_fd() as wasi::Fd, offset, len, advice).map_err(err2io)
+ }
+ }
+
+ pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
+ unsafe { wasi::fd_allocate(self.as_raw_fd() as wasi::Fd, offset, len).map_err(err2io) }
+ }
+
+ pub fn create_directory(&self, path: &str) -> io::Result<()> {
+ unsafe { wasi::path_create_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) }
+ }
+
+ pub fn link(
+ &self,
+ old_flags: wasi::Lookupflags,
+ old_path: &str,
+ new_fd: &WasiFd,
+ new_path: &str,
+ ) -> io::Result<()> {
+ unsafe {
+ wasi::path_link(
+ self.as_raw_fd() as wasi::Fd,
+ old_flags,
+ old_path,
+ new_fd.as_raw_fd() as wasi::Fd,
+ new_path,
+ )
+ .map_err(err2io)
+ }
+ }
+
+ pub fn open(
+ &self,
+ dirflags: wasi::Lookupflags,
+ path: &str,
+ oflags: wasi::Oflags,
+ fs_rights_base: wasi::Rights,
+ fs_rights_inheriting: wasi::Rights,
+ fs_flags: wasi::Fdflags,
+ ) -> io::Result<WasiFd> {
+ unsafe {
+ wasi::path_open(
+ self.as_raw_fd() as wasi::Fd,
+ dirflags,
+ path,
+ oflags,
+ fs_rights_base,
+ fs_rights_inheriting,
+ fs_flags,
+ )
+ .map(|fd| WasiFd::from_raw_fd(fd as RawFd))
+ .map_err(err2io)
+ }
+ }
+
+ pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result<usize> {
+ unsafe {
+ wasi::fd_readdir(self.as_raw_fd() as wasi::Fd, buf.as_mut_ptr(), buf.len(), cookie)
+ .map_err(err2io)
+ }
+ }
+
+ pub fn readlink(&self, path: &str, buf: &mut [u8]) -> io::Result<usize> {
+ unsafe {
+ wasi::path_readlink(self.as_raw_fd() as wasi::Fd, path, buf.as_mut_ptr(), buf.len())
+ .map_err(err2io)
+ }
+ }
+
+ pub fn rename(&self, old_path: &str, new_fd: &WasiFd, new_path: &str) -> io::Result<()> {
+ unsafe {
+ wasi::path_rename(
+ self.as_raw_fd() as wasi::Fd,
+ old_path,
+ new_fd.as_raw_fd() as wasi::Fd,
+ new_path,
+ )
+ .map_err(err2io)
+ }
+ }
+
+ pub fn filestat_get(&self) -> io::Result<wasi::Filestat> {
+ unsafe { wasi::fd_filestat_get(self.as_raw_fd() as wasi::Fd).map_err(err2io) }
+ }
+
+ pub fn filestat_set_times(
+ &self,
+ atim: wasi::Timestamp,
+ mtim: wasi::Timestamp,
+ fstflags: wasi::Fstflags,
+ ) -> io::Result<()> {
+ unsafe {
+ wasi::fd_filestat_set_times(self.as_raw_fd() as wasi::Fd, atim, mtim, fstflags)
+ .map_err(err2io)
+ }
+ }
+
+ pub fn filestat_set_size(&self, size: u64) -> io::Result<()> {
+ unsafe { wasi::fd_filestat_set_size(self.as_raw_fd() as wasi::Fd, size).map_err(err2io) }
+ }
+
+ pub fn path_filestat_get(
+ &self,
+ flags: wasi::Lookupflags,
+ path: &str,
+ ) -> io::Result<wasi::Filestat> {
+ unsafe {
+ wasi::path_filestat_get(self.as_raw_fd() as wasi::Fd, flags, path).map_err(err2io)
+ }
+ }
+
+ pub fn path_filestat_set_times(
+ &self,
+ flags: wasi::Lookupflags,
+ path: &str,
+ atim: wasi::Timestamp,
+ mtim: wasi::Timestamp,
+ fstflags: wasi::Fstflags,
+ ) -> io::Result<()> {
+ unsafe {
+ wasi::path_filestat_set_times(
+ self.as_raw_fd() as wasi::Fd,
+ flags,
+ path,
+ atim,
+ mtim,
+ fstflags,
+ )
+ .map_err(err2io)
+ }
+ }
+
+ pub fn symlink(&self, old_path: &str, new_path: &str) -> io::Result<()> {
+ unsafe {
+ wasi::path_symlink(old_path, self.as_raw_fd() as wasi::Fd, new_path).map_err(err2io)
+ }
+ }
+
+ pub fn unlink_file(&self, path: &str) -> io::Result<()> {
+ unsafe { wasi::path_unlink_file(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) }
+ }
+
+ pub fn remove_directory(&self, path: &str) -> io::Result<()> {
+ unsafe { wasi::path_remove_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) }
+ }
+
+ pub fn sock_accept(&self, flags: wasi::Fdflags) -> io::Result<wasi::Fd> {
+ unsafe { wasi::sock_accept(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) }
+ }
+
+ pub fn sock_recv(
+ &self,
+ ri_data: &mut [IoSliceMut<'_>],
+ ri_flags: wasi::Riflags,
+ ) -> io::Result<(usize, wasi::Roflags)> {
+ unsafe {
+ wasi::sock_recv(self.as_raw_fd() as wasi::Fd, iovec(ri_data), ri_flags).map_err(err2io)
+ }
+ }
+
+ pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: wasi::Siflags) -> io::Result<usize> {
+ unsafe {
+ wasi::sock_send(self.as_raw_fd() as wasi::Fd, ciovec(si_data), si_flags).map_err(err2io)
+ }
+ }
+
+ pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> {
+ let how = match how {
+ Shutdown::Read => wasi::SDFLAGS_RD,
+ Shutdown::Write => wasi::SDFLAGS_WR,
+ Shutdown::Both => wasi::SDFLAGS_WR | wasi::SDFLAGS_RD,
+ };
+ unsafe { wasi::sock_shutdown(self.as_raw_fd() as wasi::Fd, how).map_err(err2io) }
+ }
+}
+
+impl AsInner<OwnedFd> for WasiFd {
+ fn as_inner(&self) -> &OwnedFd {
+ &self.fd
+ }
+}
+
+impl AsInnerMut<OwnedFd> for WasiFd {
+ fn as_inner_mut(&mut self) -> &mut OwnedFd {
+ &mut self.fd
+ }
+}
+
+impl IntoInner<OwnedFd> for WasiFd {
+ fn into_inner(self) -> OwnedFd {
+ self.fd
+ }
+}
+
+impl FromInner<OwnedFd> for WasiFd {
+ fn from_inner(owned_fd: OwnedFd) -> Self {
+ Self { fd: owned_fd }
+ }
+}
+
+impl AsFd for WasiFd {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.fd.as_fd()
+ }
+}
+
+impl AsRawFd for WasiFd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for WasiFd {
+ fn into_raw_fd(self) -> RawFd {
+ self.fd.into_raw_fd()
+ }
+}
+
+impl FromRawFd for WasiFd {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } }
+ }
+}
diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs
new file mode 100644
index 000000000..6614ae397
--- /dev/null
+++ b/library/std/src/sys/wasi/fs.rs
@@ -0,0 +1,798 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use super::fd::WasiFd;
+use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::fmt;
+use crate::io::{self, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
+use crate::iter;
+use crate::mem::{self, ManuallyDrop};
+use crate::os::raw::c_int;
+use crate::os::wasi::ffi::{OsStrExt, OsStringExt};
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
+use crate::path::{Path, PathBuf};
+use crate::ptr;
+use crate::sync::Arc;
+use crate::sys::time::SystemTime;
+use crate::sys::unsupported;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+
+pub use crate::sys_common::fs::try_exists;
+
+pub struct File {
+ fd: WasiFd,
+}
+
+#[derive(Clone)]
+pub struct FileAttr {
+ meta: wasi::Filestat,
+}
+
+pub struct ReadDir {
+ inner: Arc<ReadDirInner>,
+ cookie: Option<wasi::Dircookie>,
+ buf: Vec<u8>,
+ offset: usize,
+ cap: usize,
+}
+
+struct ReadDirInner {
+ root: PathBuf,
+ dir: File,
+}
+
+pub struct DirEntry {
+ meta: wasi::Dirent,
+ name: Vec<u8>,
+ inner: Arc<ReadDirInner>,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct OpenOptions {
+ read: bool,
+ write: bool,
+ append: bool,
+ dirflags: wasi::Lookupflags,
+ fdflags: wasi::Fdflags,
+ oflags: wasi::Oflags,
+ rights_base: Option<wasi::Rights>,
+ rights_inheriting: Option<wasi::Rights>,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct FilePermissions {
+ readonly: bool,
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {
+ accessed: Option<wasi::Timestamp>,
+ modified: Option<wasi::Timestamp>,
+}
+
+#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
+pub struct FileType {
+ bits: wasi::Filetype,
+}
+
+#[derive(Debug)]
+pub struct DirBuilder {}
+
+impl FileAttr {
+ pub fn size(&self) -> u64 {
+ self.meta.size
+ }
+
+ pub fn perm(&self) -> FilePermissions {
+ // not currently implemented in wasi yet
+ FilePermissions { readonly: false }
+ }
+
+ pub fn file_type(&self) -> FileType {
+ FileType { bits: self.meta.filetype }
+ }
+
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from_wasi_timestamp(self.meta.mtim))
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from_wasi_timestamp(self.meta.atim))
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from_wasi_timestamp(self.meta.ctim))
+ }
+
+ pub fn as_wasi(&self) -> &wasi::Filestat {
+ &self.meta
+ }
+}
+
+impl FilePermissions {
+ pub fn readonly(&self) -> bool {
+ self.readonly
+ }
+
+ pub fn set_readonly(&mut self, readonly: bool) {
+ self.readonly = readonly;
+ }
+}
+
+impl FileTimes {
+ pub fn set_accessed(&mut self, t: SystemTime) {
+ self.accessed = Some(t.to_wasi_timestamp_or_panic());
+ }
+
+ pub fn set_modified(&mut self, t: SystemTime) {
+ self.modified = Some(t.to_wasi_timestamp_or_panic());
+ }
+}
+
+impl FileType {
+ pub fn is_dir(&self) -> bool {
+ self.bits == wasi::FILETYPE_DIRECTORY
+ }
+
+ pub fn is_file(&self) -> bool {
+ self.bits == wasi::FILETYPE_REGULAR_FILE
+ }
+
+ pub fn is_symlink(&self) -> bool {
+ self.bits == wasi::FILETYPE_SYMBOLIC_LINK
+ }
+
+ pub fn bits(&self) -> wasi::Filetype {
+ self.bits
+ }
+}
+
+impl ReadDir {
+ fn new(dir: File, root: PathBuf) -> ReadDir {
+ ReadDir {
+ cookie: Some(0),
+ buf: vec![0; 128],
+ offset: 0,
+ cap: 0,
+ inner: Arc::new(ReadDirInner { dir, root }),
+ }
+ }
+}
+
+impl fmt::Debug for ReadDir {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("ReadDir").finish_non_exhaustive()
+ }
+}
+
+impl Iterator for ReadDir {
+ type Item = io::Result<DirEntry>;
+
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ loop {
+ // If we've reached the capacity of our buffer then we need to read
+ // some more from the OS, otherwise we pick up at our old offset.
+ let offset = if self.offset == self.cap {
+ let cookie = self.cookie.take()?;
+ match self.inner.dir.fd.readdir(&mut self.buf, cookie) {
+ Ok(bytes) => self.cap = bytes,
+ Err(e) => return Some(Err(e)),
+ }
+ self.offset = 0;
+ self.cookie = Some(cookie);
+
+ // If we didn't actually read anything, this is in theory the
+ // end of the directory.
+ if self.cap == 0 {
+ self.cookie = None;
+ return None;
+ }
+
+ 0
+ } else {
+ self.offset
+ };
+ let data = &self.buf[offset..self.cap];
+
+ // If we're not able to read a directory entry then that means it
+ // must have been truncated at the end of the buffer, so reset our
+ // offset so we can go back and reread into the buffer, picking up
+ // where we last left off.
+ let dirent_size = mem::size_of::<wasi::Dirent>();
+ if data.len() < dirent_size {
+ assert!(self.cookie.is_some());
+ assert!(self.buf.len() >= dirent_size);
+ self.offset = self.cap;
+ continue;
+ }
+ let (dirent, data) = data.split_at(dirent_size);
+ let dirent = unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) };
+
+ // If the file name was truncated, then we need to reinvoke
+ // `readdir` so we truncate our buffer to start over and reread this
+ // descriptor. Note that if our offset is 0 that means the file name
+ // is massive and we need a bigger buffer.
+ if data.len() < dirent.d_namlen as usize {
+ if offset == 0 {
+ let amt_to_add = self.buf.capacity();
+ self.buf.extend(iter::repeat(0).take(amt_to_add));
+ }
+ assert!(self.cookie.is_some());
+ self.offset = self.cap;
+ continue;
+ }
+ self.cookie = Some(dirent.d_next);
+ self.offset = offset + dirent_size + dirent.d_namlen as usize;
+
+ let name = &data[..(dirent.d_namlen as usize)];
+
+ // These names are skipped on all other platforms, so let's skip
+ // them here too
+ if name == b"." || name == b".." {
+ continue;
+ }
+
+ return Some(Ok(DirEntry {
+ meta: dirent,
+ name: name.to_vec(),
+ inner: self.inner.clone(),
+ }));
+ }
+ }
+}
+
+impl DirEntry {
+ pub fn path(&self) -> PathBuf {
+ let name = OsStr::from_bytes(&self.name);
+ self.inner.root.join(name)
+ }
+
+ pub fn file_name(&self) -> OsString {
+ OsString::from_vec(self.name.clone())
+ }
+
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ metadata_at(&self.inner.dir.fd, 0, OsStr::from_bytes(&self.name).as_ref())
+ }
+
+ pub fn file_type(&self) -> io::Result<FileType> {
+ Ok(FileType { bits: self.meta.d_type })
+ }
+
+ pub fn ino(&self) -> wasi::Inode {
+ self.meta.d_ino
+ }
+}
+
+impl OpenOptions {
+ pub fn new() -> OpenOptions {
+ let mut base = OpenOptions::default();
+ base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW;
+ return base;
+ }
+
+ pub fn read(&mut self, read: bool) {
+ self.read = read;
+ }
+
+ pub fn write(&mut self, write: bool) {
+ self.write = write;
+ }
+
+ pub fn truncate(&mut self, truncate: bool) {
+ self.oflag(wasi::OFLAGS_TRUNC, truncate);
+ }
+
+ pub fn create(&mut self, create: bool) {
+ self.oflag(wasi::OFLAGS_CREAT, create);
+ }
+
+ pub fn create_new(&mut self, create_new: bool) {
+ self.oflag(wasi::OFLAGS_EXCL, create_new);
+ self.oflag(wasi::OFLAGS_CREAT, create_new);
+ }
+
+ pub fn directory(&mut self, directory: bool) {
+ self.oflag(wasi::OFLAGS_DIRECTORY, directory);
+ }
+
+ fn oflag(&mut self, bit: wasi::Oflags, set: bool) {
+ if set {
+ self.oflags |= bit;
+ } else {
+ self.oflags &= !bit;
+ }
+ }
+
+ pub fn append(&mut self, append: bool) {
+ self.append = append;
+ self.fdflag(wasi::FDFLAGS_APPEND, append);
+ }
+
+ pub fn dsync(&mut self, set: bool) {
+ self.fdflag(wasi::FDFLAGS_DSYNC, set);
+ }
+
+ pub fn nonblock(&mut self, set: bool) {
+ self.fdflag(wasi::FDFLAGS_NONBLOCK, set);
+ }
+
+ pub fn rsync(&mut self, set: bool) {
+ self.fdflag(wasi::FDFLAGS_RSYNC, set);
+ }
+
+ pub fn sync(&mut self, set: bool) {
+ self.fdflag(wasi::FDFLAGS_SYNC, set);
+ }
+
+ fn fdflag(&mut self, bit: wasi::Fdflags, set: bool) {
+ if set {
+ self.fdflags |= bit;
+ } else {
+ self.fdflags &= !bit;
+ }
+ }
+
+ pub fn fs_rights_base(&mut self, rights: wasi::Rights) {
+ self.rights_base = Some(rights);
+ }
+
+ pub fn fs_rights_inheriting(&mut self, rights: wasi::Rights) {
+ self.rights_inheriting = Some(rights);
+ }
+
+ fn rights_base(&self) -> wasi::Rights {
+ if let Some(rights) = self.rights_base {
+ return rights;
+ }
+
+ // If rights haven't otherwise been specified try to pick a reasonable
+ // set. This can always be overridden by users via extension traits, and
+ // implementations may give us fewer rights silently than we ask for. So
+ // given that, just look at `read` and `write` and bucket permissions
+ // based on that.
+ let mut base = 0;
+ if self.read {
+ base |= wasi::RIGHTS_FD_READ;
+ base |= wasi::RIGHTS_FD_READDIR;
+ }
+ if self.write || self.append {
+ base |= wasi::RIGHTS_FD_WRITE;
+ base |= wasi::RIGHTS_FD_DATASYNC;
+ base |= wasi::RIGHTS_FD_ALLOCATE;
+ base |= wasi::RIGHTS_FD_FILESTAT_SET_SIZE;
+ }
+
+ // FIXME: some of these should probably be read-only or write-only...
+ base |= wasi::RIGHTS_FD_ADVISE;
+ base |= wasi::RIGHTS_FD_FDSTAT_SET_FLAGS;
+ base |= wasi::RIGHTS_FD_FILESTAT_GET;
+ base |= wasi::RIGHTS_FD_FILESTAT_SET_TIMES;
+ base |= wasi::RIGHTS_FD_SEEK;
+ base |= wasi::RIGHTS_FD_SYNC;
+ base |= wasi::RIGHTS_FD_TELL;
+ base |= wasi::RIGHTS_PATH_CREATE_DIRECTORY;
+ base |= wasi::RIGHTS_PATH_CREATE_FILE;
+ base |= wasi::RIGHTS_PATH_FILESTAT_GET;
+ base |= wasi::RIGHTS_PATH_LINK_SOURCE;
+ base |= wasi::RIGHTS_PATH_LINK_TARGET;
+ base |= wasi::RIGHTS_PATH_OPEN;
+ base |= wasi::RIGHTS_PATH_READLINK;
+ base |= wasi::RIGHTS_PATH_REMOVE_DIRECTORY;
+ base |= wasi::RIGHTS_PATH_RENAME_SOURCE;
+ base |= wasi::RIGHTS_PATH_RENAME_TARGET;
+ base |= wasi::RIGHTS_PATH_SYMLINK;
+ base |= wasi::RIGHTS_PATH_UNLINK_FILE;
+ base |= wasi::RIGHTS_POLL_FD_READWRITE;
+
+ return base;
+ }
+
+ fn rights_inheriting(&self) -> wasi::Rights {
+ self.rights_inheriting.unwrap_or_else(|| self.rights_base())
+ }
+
+ pub fn lookup_flags(&mut self, flags: wasi::Lookupflags) {
+ self.dirflags = flags;
+ }
+}
+
+impl File {
+ pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+ let (dir, file) = open_parent(path)?;
+ open_at(&dir, &file, opts)
+ }
+
+ pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
+ open_at(&self.fd, path, opts)
+ }
+
+ pub fn file_attr(&self) -> io::Result<FileAttr> {
+ self.fd.filestat_get().map(|meta| FileAttr { meta })
+ }
+
+ pub fn metadata_at(&self, flags: wasi::Lookupflags, path: &Path) -> io::Result<FileAttr> {
+ metadata_at(&self.fd, flags, path)
+ }
+
+ pub fn fsync(&self) -> io::Result<()> {
+ self.fd.sync()
+ }
+
+ pub fn datasync(&self) -> io::Result<()> {
+ self.fd.datasync()
+ }
+
+ pub fn truncate(&self, size: u64) -> io::Result<()> {
+ self.fd.filestat_set_size(size)
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.read_vectored(&mut [IoSliceMut::new(buf)])
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.fd.read(bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ crate::io::default_read_buf(|buf| self.read(buf), buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.write_vectored(&[IoSlice::new(buf)])
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.fd.write(bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ Ok(())
+ }
+
+ pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
+ self.fd.seek(pos)
+ }
+
+ pub fn duplicate(&self) -> io::Result<File> {
+ // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup
+ unsupported()
+ }
+
+ pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+ // Permissions haven't been fully figured out in wasi yet, so this is
+ // likely temporary
+ unsupported()
+ }
+
+ pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
+ self.fd.filestat_set_times(
+ times.accessed.unwrap_or(0),
+ times.modified.unwrap_or(0),
+ times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM)
+ | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM),
+ )
+ }
+
+ pub fn read_link(&self, file: &Path) -> io::Result<PathBuf> {
+ read_link(&self.fd, file)
+ }
+}
+
+impl AsInner<WasiFd> for File {
+ fn as_inner(&self) -> &WasiFd {
+ &self.fd
+ }
+}
+
+impl IntoInner<WasiFd> for File {
+ fn into_inner(self) -> WasiFd {
+ self.fd
+ }
+}
+
+impl FromInner<WasiFd> for File {
+ fn from_inner(fd: WasiFd) -> File {
+ File { fd }
+ }
+}
+
+impl AsFd for File {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.fd.as_fd()
+ }
+}
+
+impl AsRawFd for File {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for File {
+ fn into_raw_fd(self) -> RawFd {
+ self.fd.into_raw_fd()
+ }
+}
+
+impl FromRawFd for File {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } }
+ }
+}
+
+impl DirBuilder {
+ pub fn new() -> DirBuilder {
+ DirBuilder {}
+ }
+
+ pub fn mkdir(&self, p: &Path) -> io::Result<()> {
+ let (dir, file) = open_parent(p)?;
+ dir.create_directory(osstr2str(file.as_ref())?)
+ }
+}
+
+impl fmt::Debug for File {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("File").field("fd", &self.as_raw_fd()).finish()
+ }
+}
+
+pub fn readdir(p: &Path) -> io::Result<ReadDir> {
+ let mut opts = OpenOptions::new();
+ opts.directory(true);
+ opts.read(true);
+ let dir = File::open(p, &opts)?;
+ Ok(ReadDir::new(dir, p.to_path_buf()))
+}
+
+pub fn unlink(p: &Path) -> io::Result<()> {
+ let (dir, file) = open_parent(p)?;
+ dir.unlink_file(osstr2str(file.as_ref())?)
+}
+
+pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+ let (old, old_file) = open_parent(old)?;
+ let (new, new_file) = open_parent(new)?;
+ old.rename(osstr2str(old_file.as_ref())?, &new, osstr2str(new_file.as_ref())?)
+}
+
+pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
+ // Permissions haven't been fully figured out in wasi yet, so this is
+ // likely temporary
+ unsupported()
+}
+
+pub fn rmdir(p: &Path) -> io::Result<()> {
+ let (dir, file) = open_parent(p)?;
+ dir.remove_directory(osstr2str(file.as_ref())?)
+}
+
+pub fn readlink(p: &Path) -> io::Result<PathBuf> {
+ let (dir, file) = open_parent(p)?;
+ read_link(&dir, &file)
+}
+
+fn read_link(fd: &WasiFd, file: &Path) -> io::Result<PathBuf> {
+ // Try to get a best effort initial capacity for the vector we're going to
+ // fill. Note that if it's not a symlink we don't use a file to avoid
+ // allocating gigabytes if you read_link a huge movie file by accident.
+ // Additionally we add 1 to the initial size so if it doesn't change until
+ // when we call `readlink` the returned length will be less than the
+ // capacity, guaranteeing that we got all the data.
+ let meta = metadata_at(fd, 0, file)?;
+ let initial_size = if meta.file_type().is_symlink() {
+ (meta.size() as usize).saturating_add(1)
+ } else {
+ 1 // this'll fail in just a moment
+ };
+
+ // Now that we have an initial guess of how big to make our buffer, call
+ // `readlink` in a loop until it fails or reports it filled fewer bytes than
+ // we asked for, indicating we got everything.
+ let file = osstr2str(file.as_ref())?;
+ let mut destination = vec![0u8; initial_size];
+ loop {
+ let len = fd.readlink(file, &mut destination)?;
+ if len < destination.len() {
+ destination.truncate(len);
+ destination.shrink_to_fit();
+ return Ok(PathBuf::from(OsString::from_vec(destination)));
+ }
+ let amt_to_add = destination.len();
+ destination.extend(iter::repeat(0).take(amt_to_add));
+ }
+}
+
+pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
+ let (link, link_file) = open_parent(link)?;
+ link.symlink(osstr2str(original.as_ref())?, osstr2str(link_file.as_ref())?)
+}
+
+pub fn link(original: &Path, link: &Path) -> io::Result<()> {
+ let (original, original_file) = open_parent(original)?;
+ let (link, link_file) = open_parent(link)?;
+ // Pass 0 as the flags argument, meaning don't follow symlinks.
+ original.link(0, osstr2str(original_file.as_ref())?, &link, osstr2str(link_file.as_ref())?)
+}
+
+pub fn stat(p: &Path) -> io::Result<FileAttr> {
+ let (dir, file) = open_parent(p)?;
+ metadata_at(&dir, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, &file)
+}
+
+pub fn lstat(p: &Path) -> io::Result<FileAttr> {
+ let (dir, file) = open_parent(p)?;
+ metadata_at(&dir, 0, &file)
+}
+
+fn metadata_at(fd: &WasiFd, flags: wasi::Lookupflags, path: &Path) -> io::Result<FileAttr> {
+ let meta = fd.path_filestat_get(flags, osstr2str(path.as_ref())?)?;
+ Ok(FileAttr { meta })
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+ // This seems to not be in wasi's API yet, and we may need to end up
+ // emulating it ourselves. For now just return an error.
+ unsupported()
+}
+
+fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
+ let fd = fd.open(
+ opts.dirflags,
+ osstr2str(path.as_ref())?,
+ opts.oflags,
+ opts.rights_base(),
+ opts.rights_inheriting(),
+ opts.fdflags,
+ )?;
+ Ok(File { fd })
+}
+
+/// Attempts to open a bare path `p`.
+///
+/// WASI has no fundamental capability to do this. All syscalls and operations
+/// are relative to already-open file descriptors. The C library, however,
+/// manages a map of pre-opened file descriptors to their path, and then the C
+/// library provides an API to look at this. In other words, when you want to
+/// open a path `p`, you have to find a previously opened file descriptor in a
+/// global table and then see if `p` is relative to that file descriptor.
+///
+/// This function, if successful, will return two items:
+///
+/// * The first is a `ManuallyDrop<WasiFd>`. This represents a pre-opened file
+/// descriptor which we don't have ownership of, but we can use. You shouldn't
+/// actually drop the `fd`.
+///
+/// * The second is a path that should be a part of `p` and represents a
+/// relative traversal from the file descriptor specified to the desired
+/// location `p`.
+///
+/// If successful you can use the returned file descriptor to perform
+/// file-descriptor-relative operations on the path returned as well. The
+/// `rights` argument indicates what operations are desired on the returned file
+/// descriptor, and if successful the returned file descriptor should have the
+/// appropriate rights for performing `rights` actions.
+///
+/// Note that this can fail if `p` doesn't look like it can be opened relative
+/// to any pre-opened file descriptor.
+fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
+ let p = CString::new(p.as_os_str().as_bytes())?;
+ let mut buf = Vec::<u8>::with_capacity(512);
+ loop {
+ unsafe {
+ let mut relative_path = buf.as_ptr().cast();
+ let mut abs_prefix = ptr::null();
+ let fd = __wasilibc_find_relpath(
+ p.as_ptr(),
+ &mut abs_prefix,
+ &mut relative_path,
+ buf.capacity(),
+ );
+ if fd == -1 {
+ if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) {
+ // 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);
+ continue;
+ }
+ let msg = format!(
+ "failed to find a pre-opened file descriptor \
+ through which {:?} could be opened",
+ p
+ );
+ return Err(io::Error::new(io::ErrorKind::Uncategorized, msg));
+ }
+ let relative = CStr::from_ptr(relative_path).to_bytes().to_vec();
+
+ return Ok((
+ ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)),
+ PathBuf::from(OsString::from_vec(relative)),
+ ));
+ }
+ }
+
+ extern "C" {
+ pub fn __wasilibc_find_relpath(
+ path: *const libc::c_char,
+ abs_prefix: *mut *const libc::c_char,
+ relative_path: *mut *const libc::c_char,
+ relative_path_len: libc::size_t,
+ ) -> libc::c_int;
+ }
+}
+
+pub fn osstr2str(f: &OsStr) -> io::Result<&str> {
+ f.to_str()
+ .ok_or_else(|| io::const_io_error!(io::ErrorKind::Uncategorized, "input must be utf-8"))
+}
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+ use crate::fs::File;
+
+ let mut reader = File::open(from)?;
+ let mut writer = File::create(to)?;
+
+ io::copy(&mut reader, &mut writer)
+}
+
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+ let (parent, path) = open_parent(path)?;
+ remove_dir_all_recursive(&parent, &path)
+}
+
+fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> {
+ // Open up a file descriptor for the directory itself. Note that we don't
+ // follow symlinks here and we specifically open directories.
+ //
+ // At the root invocation of this function this will correctly handle
+ // symlinks passed to the top-level `remove_dir_all`. At the recursive
+ // level this will double-check that after the `readdir` call deduced this
+ // was a directory it's still a directory by the time we open it up.
+ //
+ // If the opened file was actually a symlink then the symlink is deleted,
+ // not the directory recursively.
+ let mut opts = OpenOptions::new();
+ opts.lookup_flags(0);
+ opts.directory(true);
+ opts.read(true);
+ let fd = open_at(parent, path, &opts)?;
+ if fd.file_attr()?.file_type().is_symlink() {
+ return parent.unlink_file(osstr2str(path.as_ref())?);
+ }
+
+ // this "root" is only used by `DirEntry::path` which we don't use below so
+ // it's ok for this to be a bogus value
+ let dummy_root = PathBuf::new();
+
+ // Iterate over all the entries in this directory, and travel recursively if
+ // necessary
+ for entry in ReadDir::new(fd, dummy_root) {
+ let entry = entry?;
+ let path = crate::str::from_utf8(&entry.name).map_err(|_| {
+ io::const_io_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found")
+ })?;
+
+ if entry.file_type()?.is_dir() {
+ remove_dir_all_recursive(&entry.inner.dir.fd, path.as_ref())?;
+ } else {
+ entry.inner.dir.fd.unlink_file(path)?;
+ }
+ }
+
+ // Once all this directory's contents are deleted it should be safe to
+ // delete the directory tiself.
+ parent.remove_directory(osstr2str(path.as_ref())?)
+}
diff --git a/library/std/src/sys/wasi/io.rs b/library/std/src/sys/wasi/io.rs
new file mode 100644
index 000000000..ee017d13a
--- /dev/null
+++ b/library/std/src/sys/wasi/io.rs
@@ -0,0 +1,73 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::marker::PhantomData;
+use crate::slice;
+
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+pub struct IoSlice<'a> {
+ vec: wasi::Ciovec,
+ _p: PhantomData<&'a [u8]>,
+}
+
+impl<'a> IoSlice<'a> {
+ #[inline]
+ pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
+ IoSlice { vec: wasi::Ciovec { buf: buf.as_ptr(), buf_len: buf.len() }, _p: PhantomData }
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ if self.vec.buf_len < n {
+ panic!("advancing IoSlice beyond its length");
+ }
+
+ unsafe {
+ self.vec.buf_len -= n;
+ self.vec.buf = self.vec.buf.add(n);
+ }
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) }
+ }
+}
+
+#[repr(transparent)]
+pub struct IoSliceMut<'a> {
+ vec: wasi::Iovec,
+ _p: PhantomData<&'a mut [u8]>,
+}
+
+impl<'a> IoSliceMut<'a> {
+ #[inline]
+ pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
+ IoSliceMut {
+ vec: wasi::Iovec { buf: buf.as_mut_ptr(), buf_len: buf.len() },
+ _p: PhantomData,
+ }
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ if self.vec.buf_len < n {
+ panic!("advancing IoSlice beyond its length");
+ }
+
+ unsafe {
+ self.vec.buf_len -= n;
+ self.vec.buf = self.vec.buf.add(n);
+ }
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) }
+ }
+
+ #[inline]
+ pub fn as_mut_slice(&mut self) -> &mut [u8] {
+ unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) }
+ }
+}
diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs
new file mode 100644
index 000000000..683a07a34
--- /dev/null
+++ b/library/std/src/sys/wasi/mod.rs
@@ -0,0 +1,100 @@
+//! 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 crate::io as std_io;
+use crate::mem;
+
+#[path = "../unix/alloc.rs"]
+pub mod alloc;
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+pub mod env;
+pub mod fd;
+pub mod fs;
+pub mod io;
+#[path = "../unsupported/locks/mod.rs"]
+pub mod locks;
+pub mod net;
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+#[path = "../unix/path.rs"]
+pub mod path;
+#[path = "../unsupported/pipe.rs"]
+pub mod pipe;
+#[path = "../unsupported/process.rs"]
+pub mod process;
+pub mod stdio;
+pub mod thread;
+#[path = "../unsupported/thread_local_dtor.rs"]
+pub mod thread_local_dtor;
+#[path = "../unsupported/thread_local_key.rs"]
+pub mod thread_local_key;
+pub mod time;
+
+#[path = "../unsupported/common.rs"]
+#[deny(unsafe_op_in_unsafe_fn)]
+#[allow(unused)]
+mod common;
+pub use common::*;
+
+pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind {
+ use std_io::ErrorKind::*;
+ if errno > u16::MAX as i32 || errno < 0 {
+ return Uncategorized;
+ }
+
+ match errno {
+ e if e == wasi::ERRNO_CONNREFUSED.raw().into() => ConnectionRefused,
+ e if e == wasi::ERRNO_CONNRESET.raw().into() => ConnectionReset,
+ e if e == wasi::ERRNO_PERM.raw().into() || e == wasi::ERRNO_ACCES.raw().into() => {
+ PermissionDenied
+ }
+ e if e == wasi::ERRNO_PIPE.raw().into() => BrokenPipe,
+ e if e == wasi::ERRNO_NOTCONN.raw().into() => NotConnected,
+ e if e == wasi::ERRNO_CONNABORTED.raw().into() => ConnectionAborted,
+ e if e == wasi::ERRNO_ADDRNOTAVAIL.raw().into() => AddrNotAvailable,
+ e if e == wasi::ERRNO_ADDRINUSE.raw().into() => AddrInUse,
+ e if e == wasi::ERRNO_NOENT.raw().into() => NotFound,
+ e if e == wasi::ERRNO_INTR.raw().into() => Interrupted,
+ e if e == wasi::ERRNO_INVAL.raw().into() => InvalidInput,
+ e if e == wasi::ERRNO_TIMEDOUT.raw().into() => TimedOut,
+ e if e == wasi::ERRNO_EXIST.raw().into() => AlreadyExists,
+ e if e == wasi::ERRNO_AGAIN.raw().into() => WouldBlock,
+ e if e == wasi::ERRNO_NOSYS.raw().into() => Unsupported,
+ e if e == wasi::ERRNO_NOMEM.raw().into() => OutOfMemory,
+ _ => Uncategorized,
+ }
+}
+
+pub fn abort_internal() -> ! {
+ unsafe { libc::abort() }
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+ let mut ret = (0u64, 0u64);
+ unsafe {
+ let base = &mut ret as *mut (u64, u64) as *mut u8;
+ let len = mem::size_of_val(&ret);
+ wasi::random_get(base, len).expect("random_get failure");
+ }
+ return ret;
+}
+
+fn err2io(err: wasi::Errno) -> std_io::Error {
+ std_io::Error::from_raw_os_error(err.raw().into())
+}
diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs
new file mode 100644
index 000000000..590d268c3
--- /dev/null
+++ b/library/std/src/sys/wasi/net.rs
@@ -0,0 +1,527 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use super::err2io;
+use super::fd::WasiFd;
+use crate::fmt;
+use crate::io::{self, IoSlice, IoSliceMut};
+use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
+use crate::sys::unsupported;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+use crate::time::Duration;
+
+pub struct Socket(WasiFd);
+
+pub struct TcpStream {
+ inner: Socket,
+}
+
+impl AsInner<WasiFd> for Socket {
+ fn as_inner(&self) -> &WasiFd {
+ &self.0
+ }
+}
+
+impl IntoInner<WasiFd> for Socket {
+ fn into_inner(self) -> WasiFd {
+ self.0
+ }
+}
+
+impl FromInner<WasiFd> for Socket {
+ fn from_inner(inner: WasiFd) -> Socket {
+ Socket(inner)
+ }
+}
+
+impl AsFd for Socket {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+}
+
+impl AsRawFd for Socket {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for Socket {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
+impl FromRawFd for Socket {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) }
+ }
+}
+
+impl TcpStream {
+ pub fn connect(_: io::Result<&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<()> {
+ unsupported()
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.read_vectored(&mut [IoSliceMut::new(buf)])
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.socket().as_inner().read(bufs)
+ }
+
+ pub fn is_read_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.write_vectored(&[IoSlice::new(buf)])
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.socket().as_inner().write(bufs)
+ }
+
+ pub fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpStream> {
+ unsupported()
+ }
+
+ pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, state: bool) -> io::Result<()> {
+ let fdstat = unsafe {
+ wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)?
+ };
+
+ let mut flags = fdstat.fs_flags;
+
+ if state {
+ flags |= wasi::FDFLAGS_NONBLOCK;
+ } else {
+ flags &= !wasi::FDFLAGS_NONBLOCK;
+ }
+
+ unsafe {
+ wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags)
+ .map_err(err2io)
+ }
+ }
+
+ pub fn socket(&self) -> &Socket {
+ &self.inner
+ }
+
+ pub fn into_socket(self) -> Socket {
+ self.inner
+ }
+}
+
+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 {
+ f.debug_struct("TcpStream").field("fd", &self.inner.as_raw_fd()).finish()
+ }
+}
+
+pub struct TcpListener {
+ inner: Socket,
+}
+
+impl TcpListener {
+ pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
+ unsupported()
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+ let fd = unsafe {
+ wasi::sock_accept(self.as_inner().as_inner().as_raw_fd() as _, 0).map_err(err2io)?
+ };
+
+ Ok((
+ TcpStream::from_inner(unsafe { Socket::from_raw_fd(fd as _) }),
+ // WASI has no concept of SocketAddr yet
+ // return an unspecified IPv4Addr
+ SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0),
+ ))
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpListener> {
+ unsupported()
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn only_v6(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, state: bool) -> io::Result<()> {
+ let fdstat = unsafe {
+ wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)?
+ };
+
+ let mut flags = fdstat.fs_flags;
+
+ if state {
+ flags |= wasi::FDFLAGS_NONBLOCK;
+ } else {
+ flags &= !wasi::FDFLAGS_NONBLOCK;
+ }
+
+ unsafe {
+ wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags)
+ .map_err(err2io)
+ }
+ }
+
+ pub fn socket(&self) -> &Socket {
+ &self.inner
+ }
+
+ pub fn into_socket(self) -> Socket {
+ self.inner
+ }
+}
+
+impl AsInner<Socket> for TcpListener {
+ fn as_inner(&self) -> &Socket {
+ &self.inner
+ }
+}
+
+impl IntoInner<Socket> for TcpListener {
+ fn into_inner(self) -> Socket {
+ self.inner
+ }
+}
+
+impl FromInner<Socket> for TcpListener {
+ fn from_inner(inner: Socket) -> TcpListener {
+ TcpListener { inner }
+ }
+}
+
+impl fmt::Debug for TcpListener {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("TcpListener").field("fd", &self.inner.as_raw_fd()).finish()
+ }
+}
+
+pub struct UdpSocket {
+ inner: Socket,
+}
+
+impl UdpSocket {
+ pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+ unsupported()
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unsupported()
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unsupported()
+ }
+
+ pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn duplicate(&self) -> io::Result<UdpSocket> {
+ unsupported()
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn broadcast(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn socket(&self) -> &Socket {
+ &self.inner
+ }
+
+ pub fn into_socket(self) -> Socket {
+ self.inner
+ }
+}
+
+impl AsInner<Socket> for UdpSocket {
+ fn as_inner(&self) -> &Socket {
+ &self.inner
+ }
+}
+
+impl IntoInner<Socket> for UdpSocket {
+ fn into_inner(self) -> Socket {
+ self.inner
+ }
+}
+
+impl FromInner<Socket> for UdpSocket {
+ fn from_inner(inner: Socket) -> UdpSocket {
+ UdpSocket { inner }
+ }
+}
+
+impl fmt::Debug for UdpSocket {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("UdpSocket").field("fd", &self.inner.as_raw_fd()).finish()
+ }
+}
+
+pub struct LookupHost(!);
+
+impl LookupHost {
+ pub fn port(&self) -> u16 {
+ self.0
+ }
+}
+
+impl Iterator for LookupHost {
+ type Item = SocketAddr;
+ fn next(&mut self) -> Option<SocketAddr> {
+ self.0
+ }
+}
+
+impl<'a> TryFrom<&'a str> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(_v: &'a str) -> io::Result<LookupHost> {
+ unsupported()
+ }
+}
+
+impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> {
+ unsupported()
+ }
+}
+
+#[allow(nonstandard_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 {}
+}
diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs
new file mode 100644
index 000000000..c5229a188
--- /dev/null
+++ b/library/std/src/sys/wasi/os.rs
@@ -0,0 +1,243 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::any::Any;
+use crate::error::Error as StdError;
+use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::marker::PhantomData;
+use crate::os::wasi::prelude::*;
+use crate::path::{self, PathBuf};
+use crate::str;
+use crate::sys::memchr;
+use crate::sys::unsupported;
+use crate::vec;
+
+// Add a few symbols not in upstream `libc` just yet.
+mod libc {
+ pub use libc::*;
+
+ extern "C" {
+ pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char;
+ pub fn chdir(dir: *const c_char) -> c_int;
+ }
+}
+
+#[cfg(not(target_feature = "atomics"))]
+pub unsafe fn env_lock() -> impl Any {
+ // No need for a lock if we're single-threaded, but this function will need
+ // to get implemented for multi-threaded scenarios
+}
+
+pub fn errno() -> i32 {
+ extern "C" {
+ #[thread_local]
+ static errno: libc::c_int;
+ }
+
+ unsafe { errno as i32 }
+}
+
+pub fn error_string(errno: i32) -> String {
+ let mut buf = [0 as libc::c_char; 1024];
+
+ let p = buf.as_mut_ptr();
+ unsafe {
+ if libc::strerror_r(errno as libc::c_int, p, buf.len()) < 0 {
+ panic!("strerror_r failure");
+ }
+ 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 libc::c_char).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()) == (0 as libc::c_int) {
+ true => Ok(()),
+ false => Err(io::Error::last_os_error()),
+ }
+ }
+}
+
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
+ panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+ type Item = PathBuf;
+ fn next(&mut self) -> Option<PathBuf> {
+ 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 {
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "not supported on wasm yet"
+ }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsupported()
+}
+pub struct Env {
+ iter: vec::IntoIter<(OsString, OsString)>,
+}
+
+impl !Send for Env {}
+impl !Sync for Env {}
+
+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()
+ }
+}
+
+pub fn env() -> Env {
+ unsafe {
+ let _guard = env_lock();
+ let mut environ = libc::environ;
+ let mut result = Vec::new();
+ if !environ.is_null() {
+ while !(*environ).is_null() {
+ if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+ result.push(key_value);
+ }
+ environ = environ.add(1);
+ }
+ }
+ return Env { iter: result.into_iter() };
+ }
+
+ // See src/libstd/sys/unix/os.rs, same as that
+ fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
+ 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) -> Option<OsString> {
+ let k = CString::new(k.as_bytes()).ok()?;
+ unsafe {
+ let _guard = env_lock();
+ let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
+ if s.is_null() {
+ None
+ } else {
+ Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
+ }
+ }
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+ let k = CString::new(k.as_bytes())?;
+ let v = CString::new(v.as_bytes())?;
+
+ unsafe {
+ let _guard = env_lock();
+ cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
+ }
+}
+
+pub fn unsetenv(n: &OsStr) -> io::Result<()> {
+ let nbuf = CString::new(n.as_bytes())?;
+
+ unsafe {
+ let _guard = env_lock();
+ cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
+ }
+}
+
+pub fn temp_dir() -> PathBuf {
+ panic!("no filesystem on wasm")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ None
+}
+
+pub fn exit(code: i32) -> ! {
+ unsafe { libc::exit(code) }
+}
+
+pub fn getpid() -> u32 {
+ panic!("unsupported");
+}
+
+#[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 }
+
+fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
+ if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) }
+}
diff --git a/library/std/src/sys/wasi/stdio.rs b/library/std/src/sys/wasi/stdio.rs
new file mode 100644
index 000000000..4cc0e4ed5
--- /dev/null
+++ b/library/std/src/sys/wasi/stdio.rs
@@ -0,0 +1,112 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use super::fd::WasiFd;
+use crate::io::{self, IoSlice, IoSliceMut};
+use crate::mem::ManuallyDrop;
+use crate::os::raw;
+use crate::os::wasi::io::{AsRawFd, FromRawFd};
+
+pub struct Stdin;
+pub struct Stdout;
+pub struct Stderr;
+
+impl Stdin {
+ pub const fn new() -> Stdin {
+ Stdin
+ }
+}
+
+impl AsRawFd for Stdin {
+ #[inline]
+ fn as_raw_fd(&self) -> raw::c_int {
+ 0
+ }
+}
+
+impl io::Read for Stdin {
+ fn read(&mut self, data: &mut [u8]) -> io::Result<usize> {
+ self.read_vectored(&mut [IoSliceMut::new(data)])
+ }
+
+ fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).read(data)
+ }
+
+ #[inline]
+ fn is_read_vectored(&self) -> bool {
+ true
+ }
+}
+
+impl Stdout {
+ pub const fn new() -> Stdout {
+ Stdout
+ }
+}
+
+impl AsRawFd for Stdout {
+ #[inline]
+ fn as_raw_fd(&self) -> raw::c_int {
+ 1
+ }
+}
+
+impl io::Write for Stdout {
+ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+ self.write_vectored(&[IoSlice::new(data)])
+ }
+
+ fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> {
+ ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data)
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Stderr {
+ pub const fn new() -> Stderr {
+ Stderr
+ }
+}
+
+impl AsRawFd for Stderr {
+ #[inline]
+ fn as_raw_fd(&self) -> raw::c_int {
+ 2
+ }
+}
+
+impl io::Write for Stderr {
+ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+ self.write_vectored(&[IoSlice::new(data)])
+ }
+
+ fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> {
+ ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data)
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
+
+pub fn is_ebadf(err: &io::Error) -> bool {
+ err.raw_os_error() == Some(wasi::ERRNO_BADF.raw().into())
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+ Some(Stderr::new())
+}
diff --git a/library/std/src/sys/wasi/thread.rs b/library/std/src/sys/wasi/thread.rs
new file mode 100644
index 000000000..e7a6ab4be
--- /dev/null
+++ b/library/std/src/sys/wasi/thread.rs
@@ -0,0 +1,81 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::ffi::CStr;
+use crate::io;
+use crate::mem;
+use crate::num::NonZeroUsize;
+use crate::sys::unsupported;
+use crate::time::Duration;
+
+pub struct Thread(!);
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
+
+impl Thread {
+ // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+ pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+ unsupported()
+ }
+
+ pub fn yield_now() {
+ let ret = unsafe { wasi::sched_yield() };
+ debug_assert_eq!(ret, Ok(()));
+ }
+
+ pub fn set_name(_name: &CStr) {
+ // nope
+ }
+
+ pub fn sleep(dur: Duration) {
+ let nanos = dur.as_nanos();
+ assert!(nanos <= u64::MAX as u128);
+
+ const USERDATA: wasi::Userdata = 0x0123_45678;
+
+ let clock = wasi::SubscriptionClock {
+ id: wasi::CLOCKID_MONOTONIC,
+ timeout: nanos as u64,
+ precision: 0,
+ flags: 0,
+ };
+
+ let in_ = wasi::Subscription {
+ userdata: USERDATA,
+ u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } },
+ };
+ unsafe {
+ let mut event: wasi::Event = mem::zeroed();
+ let res = wasi::poll_oneoff(&in_, &mut event, 1);
+ match (res, event) {
+ (
+ Ok(1),
+ wasi::Event {
+ userdata: USERDATA,
+ error: wasi::ERRNO_SUCCESS,
+ type_: wasi::EVENTTYPE_CLOCK,
+ ..
+ },
+ ) => {}
+ _ => panic!("thread::sleep(): unexpected result of poll_oneoff"),
+ }
+ }
+ }
+
+ pub fn join(self) {
+ self.0
+ }
+}
+
+pub fn available_parallelism() -> io::Result<NonZeroUsize> {
+ unsupported()
+}
+
+pub mod guard {
+ pub type Guard = !;
+ pub unsafe fn current() -> Option<Guard> {
+ None
+ }
+ pub unsafe fn init() -> Option<Guard> {
+ None
+ }
+}
diff --git a/library/std/src/sys/wasi/time.rs b/library/std/src/sys/wasi/time.rs
new file mode 100644
index 000000000..3d326e491
--- /dev/null
+++ b/library/std/src/sys/wasi/time.rs
@@ -0,0 +1,65 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::time::Duration;
+
+#[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));
+
+fn current_time(clock: wasi::Clockid) -> Duration {
+ let ts = unsafe {
+ wasi::clock_time_get(
+ clock, 1, // precision... seems ignored though?
+ )
+ .unwrap()
+ };
+ Duration::new((ts / 1_000_000_000) as u64, (ts % 1_000_000_000) as u32)
+}
+
+impl Instant {
+ pub fn now() -> Instant {
+ Instant(current_time(wasi::CLOCKID_MONOTONIC))
+ }
+
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ self.0.checked_sub(other.0)
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant(self.0.checked_add(*other)?))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant(self.0.checked_sub(*other)?))
+ }
+}
+
+impl SystemTime {
+ pub fn now() -> SystemTime {
+ SystemTime(current_time(wasi::CLOCKID_REALTIME))
+ }
+
+ pub fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime {
+ SystemTime(Duration::from_nanos(ts))
+ }
+
+ pub fn to_wasi_timestamp_or_panic(&self) -> wasi::Timestamp {
+ self.0.as_nanos().try_into().expect("time does not fit in WASI timestamp")
+ }
+
+ 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 checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_add(*other)?))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_sub(*other)?))
+ }
+}
diff --git a/library/std/src/sys/wasm/alloc.rs b/library/std/src/sys/wasm/alloc.rs
new file mode 100644
index 000000000..6dceb1689
--- /dev/null
+++ b/library/std/src/sys/wasm/alloc.rs
@@ -0,0 +1,166 @@
+//! This is an implementation of a global allocator on wasm targets when
+//! emscripten is not in use. In that situation there's no actual runtime for us
+//! to lean on for allocation, so instead we provide our own!
+//!
+//! The wasm instruction set has two instructions for getting the current
+//! amount of memory and growing the amount of memory. These instructions are the
+//! foundation on which we're able to build an allocator, so we do so! Note that
+//! the instructions are also pretty "global" and this is the "global" allocator
+//! after all!
+//!
+//! The current allocator here is the `dlmalloc` crate which we've got included
+//! in the rust-lang/rust repository as a submodule. The crate is a port of
+//! dlmalloc.c from C to Rust and is basically just so we can have "pure Rust"
+//! for now which is currently technically required (can't link with C yet).
+//!
+//! The crate itself provides a global allocator which on wasm has no
+//! synchronization as there are no threads!
+
+use crate::alloc::{GlobalAlloc, Layout, System};
+
+static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new();
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+ #[inline]
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access.
+ // Calling malloc() is safe because preconditions on this function match the trait method preconditions.
+ let _lock = lock::lock();
+ unsafe { DLMALLOC.malloc(layout.size(), layout.align()) }
+ }
+
+ #[inline]
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+ // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access.
+ // Calling calloc() is safe because preconditions on this function match the trait method preconditions.
+ let _lock = lock::lock();
+ unsafe { DLMALLOC.calloc(layout.size(), layout.align()) }
+ }
+
+ #[inline]
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access.
+ // Calling free() is safe because preconditions on this function match the trait method preconditions.
+ let _lock = lock::lock();
+ unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) }
+ }
+
+ #[inline]
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access.
+ // Calling realloc() is safe because preconditions on this function match the trait method preconditions.
+ let _lock = lock::lock();
+ unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) }
+ }
+}
+
+#[cfg(target_feature = "atomics")]
+mod lock {
+ use crate::sync::atomic::{AtomicI32, Ordering::SeqCst};
+
+ static LOCKED: AtomicI32 = AtomicI32::new(0);
+
+ pub struct DropLock;
+
+ pub fn lock() -> DropLock {
+ loop {
+ if LOCKED.swap(1, SeqCst) == 0 {
+ return DropLock;
+ }
+ // Ok so here's where things get a little depressing. At this point
+ // in time we need to synchronously acquire a lock, but we're
+ // contending with some other thread. Typically we'd execute some
+ // form of `i32.atomic.wait` like so:
+ //
+ // unsafe {
+ // let r = core::arch::wasm32::i32_atomic_wait(
+ // LOCKED.as_mut_ptr(),
+ // 1, // expected value
+ // -1, // timeout
+ // );
+ // debug_assert!(r == 0 || r == 1);
+ // }
+ //
+ // Unfortunately though in doing so we would cause issues for the
+ // main thread. The main thread in a web browser *cannot ever
+ // block*, no exceptions. This means that the main thread can't
+ // actually execute the `i32.atomic.wait` instruction.
+ //
+ // As a result if we want to work within the context of browsers we
+ // need to figure out some sort of allocation scheme for the main
+ // thread where when there's contention on the global malloc lock we
+ // do... something.
+ //
+ // Possible ideas include:
+ //
+ // 1. Attempt to acquire the global lock. If it fails, fall back to
+ // memory allocation via `memory.grow`. Later just ... somehow
+ // ... inject this raw page back into the main allocator as it
+ // gets sliced up over time. This strategy has the downside of
+ // forcing allocation of a page to happen whenever the main
+ // thread contents with other threads, which is unfortunate.
+ //
+ // 2. Maintain a form of "two level" allocator scheme where the main
+ // thread has its own allocator. Somehow this allocator would
+ // also be balanced with a global allocator, not only to have
+ // allocations cross between threads but also to ensure that the
+ // two allocators stay "balanced" in terms of free'd memory and
+ // such. This, however, seems significantly complicated.
+ //
+ // Out of a lack of other ideas, the current strategy implemented
+ // here is to simply spin. Typical spin loop algorithms have some
+ // form of "hint" here to the CPU that it's what we're doing to
+ // ensure that the CPU doesn't get too hot, but wasm doesn't have
+ // such an instruction.
+ //
+ // To be clear, spinning here is not a great solution.
+ // Another thread with the lock may take quite a long time to wake
+ // up. For example it could be in `memory.grow` or it could be
+ // evicted from the CPU for a timeslice like 10ms. For these periods
+ // of time our thread will "helpfully" sit here and eat CPU time
+ // until it itself is evicted or the lock holder finishes. This
+ // means we're just burning and wasting CPU time to no one's
+ // benefit.
+ //
+ // Spinning does have the nice properties, though, of being
+ // semantically correct, being fair to all threads for memory
+ // allocation, and being simple enough to implement.
+ //
+ // This will surely (hopefully) be replaced in the future with a
+ // real memory allocator that can handle the restriction of the main
+ // thread.
+ //
+ //
+ // FIXME: We can also possibly add an optimization here to detect
+ // when a thread is the main thread or not and block on all
+ // non-main-thread threads. Currently, however, we have no way
+ // of knowing which wasm thread is on the browser main thread, but
+ // if we could figure out we could at least somewhat mitigate the
+ // cost of this spinning.
+ }
+ }
+
+ impl Drop for DropLock {
+ fn drop(&mut self) {
+ let r = LOCKED.swap(0, SeqCst);
+ debug_assert_eq!(r, 1);
+
+ // Note that due to the above logic we don't actually need to wake
+ // anyone up, but if we did it'd likely look something like this:
+ //
+ // unsafe {
+ // core::arch::wasm32::atomic_notify(
+ // LOCKED.as_mut_ptr(),
+ // 1, // only one thread
+ // );
+ // }
+ }
+ }
+}
+
+#[cfg(not(target_feature = "atomics"))]
+mod lock {
+ #[inline]
+ pub fn lock() {} // no atomics, no threads, that's easy!
+}
diff --git a/library/std/src/sys/wasm/atomics/futex.rs b/library/std/src/sys/wasm/atomics/futex.rs
new file mode 100644
index 000000000..f4fbe9f48
--- /dev/null
+++ b/library/std/src/sys/wasm/atomics/futex.rs
@@ -0,0 +1,34 @@
+use crate::arch::wasm32;
+use crate::sync::atomic::AtomicU32;
+use crate::time::Duration;
+
+/// Wait for a futex_wake operation to wake us.
+///
+/// Returns directly if the futex doesn't hold the expected value.
+///
+/// Returns false on timeout, and true in all other cases.
+pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
+ let timeout = timeout.and_then(|t| t.as_nanos().try_into().ok()).unwrap_or(-1);
+ unsafe {
+ wasm32::memory_atomic_wait32(
+ futex as *const AtomicU32 as *mut i32,
+ expected as i32,
+ timeout,
+ ) < 2
+ }
+}
+
+/// Wake up one thread that's blocked on futex_wait on this futex.
+///
+/// Returns true if this actually woke up such a thread,
+/// or false if no thread was waiting on this futex.
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+ unsafe { wasm32::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, 1) > 0 }
+}
+
+/// Wake up all threads that are waiting on futex_wait on this futex.
+pub fn futex_wake_all(futex: &AtomicU32) {
+ unsafe {
+ wasm32::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, i32::MAX as u32);
+ }
+}
diff --git a/library/std/src/sys/wasm/atomics/thread.rs b/library/std/src/sys/wasm/atomics/thread.rs
new file mode 100644
index 000000000..714b70492
--- /dev/null
+++ b/library/std/src/sys/wasm/atomics/thread.rs
@@ -0,0 +1,55 @@
+use crate::ffi::CStr;
+use crate::io;
+use crate::num::NonZeroUsize;
+use crate::sys::unsupported;
+use crate::time::Duration;
+
+pub struct Thread(!);
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
+
+impl Thread {
+ // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+ pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+ unsupported()
+ }
+
+ pub fn yield_now() {}
+
+ pub fn set_name(_name: &CStr) {}
+
+ pub fn sleep(dur: Duration) {
+ use crate::arch::wasm32;
+ use crate::cmp;
+
+ // Use an atomic wait to block the current thread artificially with a
+ // timeout listed. Note that we should never be notified (return value
+ // of 0) or our comparison should never fail (return value of 1) so we
+ // should always only resume execution through a timeout (return value
+ // 2).
+ let mut nanos = dur.as_nanos();
+ while nanos > 0 {
+ let amt = cmp::min(i64::MAX as u128, nanos);
+ let mut x = 0;
+ let val = unsafe { wasm32::memory_atomic_wait32(&mut x, 0, amt as i64) };
+ debug_assert_eq!(val, 2);
+ nanos -= amt;
+ }
+ }
+
+ pub fn join(self) {}
+}
+
+pub fn available_parallelism() -> io::Result<NonZeroUsize> {
+ unsupported()
+}
+
+pub mod guard {
+ pub type Guard = !;
+ pub unsafe fn current() -> Option<Guard> {
+ None
+ }
+ pub unsafe fn init() -> Option<Guard> {
+ None
+ }
+}
diff --git a/library/std/src/sys/wasm/env.rs b/library/std/src/sys/wasm/env.rs
new file mode 100644
index 000000000..730e356d7
--- /dev/null
+++ b/library/std/src/sys/wasm/env.rs
@@ -0,0 +1,9 @@
+pub mod os {
+ pub const FAMILY: &str = "";
+ pub const OS: &str = "";
+ pub const DLL_PREFIX: &str = "";
+ pub const DLL_SUFFIX: &str = ".wasm";
+ pub const DLL_EXTENSION: &str = "wasm";
+ pub const EXE_SUFFIX: &str = ".wasm";
+ pub const EXE_EXTENSION: &str = "wasm";
+}
diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs
new file mode 100644
index 000000000..4159efe2a
--- /dev/null
+++ b/library/std/src/sys/wasm/mod.rs
@@ -0,0 +1,77 @@
+//! 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!
+
+#![deny(unsafe_op_in_unsafe_fn)]
+
+pub mod alloc;
+#[path = "../unsupported/args.rs"]
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+pub mod env;
+#[path = "../unsupported/fs.rs"]
+pub mod fs;
+#[path = "../unsupported/io.rs"]
+pub mod io;
+#[path = "../unsupported/net.rs"]
+pub mod net;
+#[path = "../unsupported/os.rs"]
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+#[path = "../unix/path.rs"]
+pub mod path;
+#[path = "../unsupported/pipe.rs"]
+pub mod pipe;
+#[path = "../unsupported/process.rs"]
+pub mod process;
+#[path = "../unsupported/stdio.rs"]
+pub mod stdio;
+#[path = "../unsupported/thread_local_dtor.rs"]
+pub mod thread_local_dtor;
+#[path = "../unsupported/thread_local_key.rs"]
+pub mod thread_local_key;
+#[path = "../unsupported/time.rs"]
+pub mod time;
+
+cfg_if::cfg_if! {
+ if #[cfg(target_feature = "atomics")] {
+ #[path = "../unix/locks"]
+ pub mod locks {
+ #![allow(unsafe_op_in_unsafe_fn)]
+ mod futex_condvar;
+ mod futex_mutex;
+ mod futex_rwlock;
+ pub(crate) use futex_condvar::{Condvar, MovableCondvar};
+ pub(crate) use futex_mutex::{Mutex, MovableMutex};
+ pub(crate) use futex_rwlock::{RwLock, MovableRwLock};
+ }
+ #[path = "atomics/futex.rs"]
+ pub mod futex;
+ #[path = "atomics/thread.rs"]
+ pub mod thread;
+ } else {
+ #[path = "../unsupported/locks/mod.rs"]
+ pub mod locks;
+ #[path = "../unsupported/thread.rs"]
+ pub mod thread;
+ }
+}
+
+#[path = "../unsupported/common.rs"]
+#[deny(unsafe_op_in_unsafe_fn)]
+mod common;
+pub use common::*;
diff --git a/library/std/src/sys/windows/alloc.rs b/library/std/src/sys/windows/alloc.rs
new file mode 100644
index 000000000..fdc81cdea
--- /dev/null
+++ b/library/std/src/sys/windows/alloc.rs
@@ -0,0 +1,246 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::alloc::{GlobalAlloc, Layout, System};
+use crate::ffi::c_void;
+use crate::ptr;
+use crate::sync::atomic::{AtomicPtr, Ordering};
+use crate::sys::c;
+use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN};
+
+#[cfg(test)]
+mod tests;
+
+// Heap memory management on Windows is done by using the system Heap API (heapapi.h)
+// See https://docs.microsoft.com/windows/win32/api/heapapi/
+
+// Flag to indicate that the memory returned by `HeapAlloc` should be zeroed.
+const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008;
+
+extern "system" {
+ // Get a handle to the default heap of the current process, or null if the operation fails.
+ //
+ // SAFETY: Successful calls to this function within the same process are assumed to
+ // always return the same handle, which remains valid for the entire lifetime of the process.
+ //
+ // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap
+ fn GetProcessHeap() -> c::HANDLE;
+
+ // Allocate a block of `dwBytes` bytes of memory from a given heap `hHeap`.
+ // The allocated memory may be uninitialized, or zeroed if `dwFlags` is
+ // set to `HEAP_ZERO_MEMORY`.
+ //
+ // Returns a pointer to the newly-allocated memory or null if the operation fails.
+ // The returned pointer will be aligned to at least `MIN_ALIGN`.
+ //
+ // SAFETY:
+ // - `hHeap` must be a non-null handle returned by `GetProcessHeap`.
+ // - `dwFlags` must be set to either zero or `HEAP_ZERO_MEMORY`.
+ //
+ // Note that `dwBytes` is allowed to be zero, contrary to some other allocators.
+ //
+ // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapalloc
+ fn HeapAlloc(hHeap: c::HANDLE, dwFlags: c::DWORD, dwBytes: c::SIZE_T) -> c::LPVOID;
+
+ // Reallocate a block of memory behind a given pointer `lpMem` from a given heap `hHeap`,
+ // to a block of at least `dwBytes` bytes, either shrinking the block in place,
+ // or allocating at a new location, copying memory, and freeing the original location.
+ //
+ // Returns a pointer to the reallocated memory or null if the operation fails.
+ // The returned pointer will be aligned to at least `MIN_ALIGN`.
+ // If the operation fails the given block will never have been freed.
+ //
+ // SAFETY:
+ // - `hHeap` must be a non-null handle returned by `GetProcessHeap`.
+ // - `dwFlags` must be set to zero.
+ // - `lpMem` must be a non-null pointer to an allocated block returned by `HeapAlloc` or
+ // `HeapReAlloc`, that has not already been freed.
+ // If the block was successfully reallocated at a new location, pointers pointing to
+ // the freed memory, such as `lpMem`, must not be dereferenced ever again.
+ //
+ // Note that `dwBytes` is allowed to be zero, contrary to some other allocators.
+ //
+ // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heaprealloc
+ fn HeapReAlloc(
+ hHeap: c::HANDLE,
+ dwFlags: c::DWORD,
+ lpMem: c::LPVOID,
+ dwBytes: c::SIZE_T,
+ ) -> c::LPVOID;
+
+ // Free a block of memory behind a given pointer `lpMem` from a given heap `hHeap`.
+ // Returns a nonzero value if the operation is successful, and zero if the operation fails.
+ //
+ // SAFETY:
+ // - `hHeap` must be a non-null handle returned by `GetProcessHeap`.
+ // - `dwFlags` must be set to zero.
+ // - `lpMem` must be a pointer to an allocated block returned by `HeapAlloc` or `HeapReAlloc`,
+ // that has not already been freed.
+ // If the block was successfully freed, pointers pointing to the freed memory, such as `lpMem`,
+ // must not be dereferenced ever again.
+ //
+ // Note that `lpMem` is allowed to be null, which will not cause the operation to fail.
+ //
+ // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapfree
+ fn HeapFree(hHeap: c::HANDLE, dwFlags: c::DWORD, lpMem: c::LPVOID) -> c::BOOL;
+}
+
+// Cached handle to the default heap of the current process.
+// Either a non-null handle returned by `GetProcessHeap`, or null when not yet initialized or `GetProcessHeap` failed.
+static HEAP: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
+
+// Get a handle to the default heap of the current process, or null if the operation fails.
+// If this operation is successful, `HEAP` will be successfully initialized and contain
+// a non-null handle returned by `GetProcessHeap`.
+#[inline]
+fn init_or_get_process_heap() -> c::HANDLE {
+ let heap = HEAP.load(Ordering::Relaxed);
+ if heap.is_null() {
+ // `HEAP` has not yet been successfully initialized
+ let heap = unsafe { GetProcessHeap() };
+ if !heap.is_null() {
+ // SAFETY: No locking is needed because within the same process,
+ // successful calls to `GetProcessHeap` will always return the same value, even on different threads.
+ HEAP.store(heap, Ordering::Release);
+
+ // SAFETY: `HEAP` contains a non-null handle returned by `GetProcessHeap`
+ heap
+ } else {
+ // Could not get the current process heap.
+ ptr::null_mut()
+ }
+ } else {
+ // SAFETY: `HEAP` contains a non-null handle returned by `GetProcessHeap`
+ heap
+ }
+}
+
+// Get a non-null handle to the default heap of the current process.
+// SAFETY: `HEAP` must have been successfully initialized.
+#[inline]
+unsafe fn get_process_heap() -> c::HANDLE {
+ HEAP.load(Ordering::Acquire)
+}
+
+// Header containing a pointer to the start of an allocated block.
+// SAFETY: Size and alignment must be <= `MIN_ALIGN`.
+#[repr(C)]
+struct Header(*mut u8);
+
+// Allocate a block of optionally zeroed memory for a given `layout`.
+// SAFETY: Returns a pointer satisfying the guarantees of `System` about allocated pointers,
+// or null if the operation fails. If this returns non-null `HEAP` will have been successfully
+// initialized.
+#[inline]
+unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 {
+ let heap = init_or_get_process_heap();
+ if heap.is_null() {
+ // Allocation has failed, could not get the current process heap.
+ return ptr::null_mut();
+ }
+
+ // Allocated memory will be either zeroed or uninitialized.
+ let flags = if zeroed { HEAP_ZERO_MEMORY } else { 0 };
+
+ if layout.align() <= MIN_ALIGN {
+ // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`.
+ // The returned pointer points to the start of an allocated block.
+ unsafe { HeapAlloc(heap, flags, layout.size()) as *mut u8 }
+ } else {
+ // Allocate extra padding in order to be able to satisfy the alignment.
+ let total = layout.align() + layout.size();
+
+ // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`.
+ let ptr = unsafe { HeapAlloc(heap, flags, total) as *mut u8 };
+ if ptr.is_null() {
+ // Allocation has failed.
+ return ptr::null_mut();
+ }
+
+ // Create a correctly aligned pointer offset from the start of the allocated block,
+ // and write a header before it.
+
+ let offset = layout.align() - (ptr.addr() & (layout.align() - 1));
+ // SAFETY: `MIN_ALIGN` <= `offset` <= `layout.align()` and the size of the allocated
+ // block is `layout.align() + layout.size()`. `aligned` will thus be a correctly aligned
+ // pointer inside the allocated block with at least `layout.size()` bytes after it and at
+ // least `MIN_ALIGN` bytes of padding before it.
+ let aligned = unsafe { ptr.add(offset) };
+ // SAFETY: Because the size and alignment of a header is <= `MIN_ALIGN` and `aligned`
+ // is aligned to at least `MIN_ALIGN` and has at least `MIN_ALIGN` bytes of padding before
+ // it, it is safe to write a header directly before it.
+ unsafe { ptr::write((aligned as *mut Header).offset(-1), Header(ptr)) };
+
+ // SAFETY: The returned pointer does not point to the to the start of an allocated block,
+ // but there is a header readable directly before it containing the location of the start
+ // of the block.
+ aligned
+ }
+}
+
+// All pointers returned by this allocator have, in addition to the guarantees of `GlobalAlloc`, the
+// following properties:
+//
+// If the pointer was allocated or reallocated with a `layout` specifying an alignment <= `MIN_ALIGN`
+// the pointer will be aligned to at least `MIN_ALIGN` and point to the start of the allocated block.
+//
+// If the pointer was allocated or reallocated with a `layout` specifying an alignment > `MIN_ALIGN`
+// the pointer will be aligned to the specified alignment and not point to the start of the allocated block.
+// Instead there will be a header readable directly before the returned pointer, containing the actual
+// location of the start of the block.
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+ #[inline]
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System`
+ let zeroed = false;
+ unsafe { allocate(layout, zeroed) }
+ }
+
+ #[inline]
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+ // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System`
+ let zeroed = true;
+ unsafe { allocate(layout, zeroed) }
+ }
+
+ #[inline]
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ let block = {
+ if layout.align() <= MIN_ALIGN {
+ ptr
+ } else {
+ // The location of the start of the block is stored in the padding before `ptr`.
+
+ // SAFETY: Because of the contract of `System`, `ptr` is guaranteed to be non-null
+ // and have a header readable directly before it.
+ unsafe { ptr::read((ptr as *mut Header).offset(-1)).0 }
+ }
+ };
+
+ // SAFETY: because `ptr` has been successfully allocated with this allocator,
+ // `HEAP` must have been successfully initialized.
+ let heap = unsafe { get_process_heap() };
+
+ // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`,
+ // `block` is a pointer to the start of an allocated block.
+ unsafe { HeapFree(heap, 0, block as c::LPVOID) };
+ }
+
+ #[inline]
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ if layout.align() <= MIN_ALIGN {
+ // SAFETY: because `ptr` has been successfully allocated with this allocator,
+ // `HEAP` must have been successfully initialized.
+ let heap = unsafe { get_process_heap() };
+
+ // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`,
+ // `ptr` is a pointer to the start of an allocated block.
+ // The returned pointer points to the start of an allocated block.
+ unsafe { HeapReAlloc(heap, 0, ptr as c::LPVOID, new_size) as *mut u8 }
+ } else {
+ // SAFETY: `realloc_fallback` is implemented using `dealloc` and `alloc`, which will
+ // correctly handle `ptr` and return a pointer satisfying the guarantees of `System`
+ unsafe { realloc_fallback(self, ptr, layout, new_size) }
+ }
+ }
+}
diff --git a/library/std/src/sys/windows/alloc/tests.rs b/library/std/src/sys/windows/alloc/tests.rs
new file mode 100644
index 000000000..674a3e1d9
--- /dev/null
+++ b/library/std/src/sys/windows/alloc/tests.rs
@@ -0,0 +1,9 @@
+use super::{Header, MIN_ALIGN};
+use crate::mem;
+
+#[test]
+fn alloc_header() {
+ // Header must fit in the padding before an aligned pointer
+ assert!(mem::size_of::<Header>() <= MIN_ALIGN);
+ assert!(mem::align_of::<Header>() <= MIN_ALIGN);
+}
diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs
new file mode 100644
index 000000000..01f262982
--- /dev/null
+++ b/library/std/src/sys/windows/args.rs
@@ -0,0 +1,406 @@
+//! The Windows command line is just a string
+//! <https://docs.microsoft.com/en-us/archive/blogs/larryosterman/the-windows-command-line-is-just-a-string>
+//!
+//! This module implements the parsing necessary to turn that string into a list of arguments.
+
+#[cfg(test)]
+mod tests;
+
+use crate::ffi::OsString;
+use crate::fmt;
+use crate::io;
+use crate::marker::PhantomData;
+use crate::num::NonZeroU16;
+use crate::os::windows::prelude::*;
+use crate::path::PathBuf;
+use crate::ptr::NonNull;
+use crate::sys::c;
+use crate::sys::process::ensure_no_nuls;
+use crate::sys::windows::os::current_exe;
+use crate::vec;
+
+use core::iter;
+
+/// This is the const equivalent to `NonZeroU16::new(n).unwrap()`
+///
+/// FIXME: This can be removed once `Option::unwrap` is stably const.
+/// See the `const_option` feature (#67441).
+const fn non_zero_u16(n: u16) -> NonZeroU16 {
+ match NonZeroU16::new(n) {
+ Some(n) => n,
+ None => panic!("called `unwrap` on a `None` value"),
+ }
+}
+
+pub fn args() -> Args {
+ // SAFETY: `GetCommandLineW` returns a pointer to a null terminated UTF-16
+ // string so it's safe for `WStrUnits` to use.
+ unsafe {
+ let lp_cmd_line = c::GetCommandLineW();
+ let parsed_args_list = parse_lp_cmd_line(WStrUnits::new(lp_cmd_line), || {
+ current_exe().map(PathBuf::into_os_string).unwrap_or_else(|_| OsString::new())
+ });
+
+ Args { parsed_args_list: parsed_args_list.into_iter() }
+ }
+}
+
+/// Implements the Windows command-line argument parsing algorithm.
+///
+/// Microsoft's documentation for the Windows CLI argument format can be found at
+/// <https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-160#parsing-c-command-line-arguments>
+///
+/// A more in-depth explanation is here:
+/// <https://daviddeley.com/autohotkey/parameters/parameters.htm#WIN>
+///
+/// Windows includes a function to do command line parsing in shell32.dll.
+/// However, this is not used for two reasons:
+///
+/// 1. Linking with that DLL causes the process to be registered as a GUI application.
+/// GUI applications add a bunch of overhead, even if no windows are drawn. See
+/// <https://randomascii.wordpress.com/2018/12/03/a-not-called-function-can-cause-a-5x-slowdown/>.
+///
+/// 2. It does not follow the modern C/C++ argv rules outlined in the first two links above.
+///
+/// This function was tested for equivalence to the C/C++ parsing rules using an
+/// extensive test suite available at
+/// <https://github.com/ChrisDenton/winarg/tree/std>.
+fn parse_lp_cmd_line<'a, F: Fn() -> OsString>(
+ lp_cmd_line: Option<WStrUnits<'a>>,
+ exe_name: F,
+) -> Vec<OsString> {
+ const BACKSLASH: NonZeroU16 = non_zero_u16(b'\\' as u16);
+ const QUOTE: NonZeroU16 = non_zero_u16(b'"' as u16);
+ const TAB: NonZeroU16 = non_zero_u16(b'\t' as u16);
+ const SPACE: NonZeroU16 = non_zero_u16(b' ' as u16);
+
+ let mut ret_val = Vec::new();
+ // If the cmd line pointer is null or it points to an empty string then
+ // return the name of the executable as argv[0].
+ if lp_cmd_line.as_ref().and_then(|cmd| cmd.peek()).is_none() {
+ ret_val.push(exe_name());
+ return ret_val;
+ }
+ let mut code_units = lp_cmd_line.unwrap();
+
+ // The executable name at the beginning is special.
+ let mut in_quotes = false;
+ let mut cur = Vec::new();
+ for w in &mut code_units {
+ match w {
+ // A quote mark always toggles `in_quotes` no matter what because
+ // there are no escape characters when parsing the executable name.
+ QUOTE => in_quotes = !in_quotes,
+ // If not `in_quotes` then whitespace ends argv[0].
+ SPACE | TAB if !in_quotes => break,
+ // In all other cases the code unit is taken literally.
+ _ => cur.push(w.get()),
+ }
+ }
+ // Skip whitespace.
+ code_units.advance_while(|w| w == SPACE || w == TAB);
+ ret_val.push(OsString::from_wide(&cur));
+
+ // Parse the arguments according to these rules:
+ // * All code units are taken literally except space, tab, quote and backslash.
+ // * When not `in_quotes`, space and tab separate arguments. Consecutive spaces and tabs are
+ // treated as a single separator.
+ // * A space or tab `in_quotes` is taken literally.
+ // * A quote toggles `in_quotes` mode unless it's escaped. An escaped quote is taken literally.
+ // * A quote can be escaped if preceded by an odd number of backslashes.
+ // * If any number of backslashes is immediately followed by a quote then the number of
+ // backslashes is halved (rounding down).
+ // * Backslashes not followed by a quote are all taken literally.
+ // * If `in_quotes` then a quote can also be escaped using another quote
+ // (i.e. two consecutive quotes become one literal quote).
+ let mut cur = Vec::new();
+ let mut in_quotes = false;
+ while let Some(w) = code_units.next() {
+ match w {
+ // If not `in_quotes`, a space or tab ends the argument.
+ SPACE | TAB if !in_quotes => {
+ ret_val.push(OsString::from_wide(&cur[..]));
+ cur.truncate(0);
+
+ // Skip whitespace.
+ code_units.advance_while(|w| w == SPACE || w == TAB);
+ }
+ // Backslashes can escape quotes or backslashes but only if consecutive backslashes are followed by a quote.
+ BACKSLASH => {
+ let backslash_count = code_units.advance_while(|w| w == BACKSLASH) + 1;
+ if code_units.peek() == Some(QUOTE) {
+ cur.extend(iter::repeat(BACKSLASH.get()).take(backslash_count / 2));
+ // The quote is escaped if there are an odd number of backslashes.
+ if backslash_count % 2 == 1 {
+ code_units.next();
+ cur.push(QUOTE.get());
+ }
+ } else {
+ // If there is no quote on the end then there is no escaping.
+ cur.extend(iter::repeat(BACKSLASH.get()).take(backslash_count));
+ }
+ }
+ // If `in_quotes` and not backslash escaped (see above) then a quote either
+ // unsets `in_quote` or is escaped by another quote.
+ QUOTE if in_quotes => match code_units.peek() {
+ // Two consecutive quotes when `in_quotes` produces one literal quote.
+ Some(QUOTE) => {
+ cur.push(QUOTE.get());
+ code_units.next();
+ }
+ // Otherwise set `in_quotes`.
+ Some(_) => in_quotes = false,
+ // The end of the command line.
+ // Push `cur` even if empty, which we do by breaking while `in_quotes` is still set.
+ None => break,
+ },
+ // If not `in_quotes` and not BACKSLASH escaped (see above) then a quote sets `in_quote`.
+ QUOTE => in_quotes = true,
+ // Everything else is always taken literally.
+ _ => cur.push(w.get()),
+ }
+ }
+ // Push the final argument, if any.
+ if !cur.is_empty() || in_quotes {
+ ret_val.push(OsString::from_wide(&cur[..]));
+ }
+ ret_val
+}
+
+pub struct Args {
+ parsed_args_list: vec::IntoIter<OsString>,
+}
+
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.parsed_args_list.as_slice().fmt(f)
+ }
+}
+
+impl Iterator for Args {
+ type Item = OsString;
+ fn next(&mut self) -> Option<OsString> {
+ self.parsed_args_list.next()
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.parsed_args_list.size_hint()
+ }
+}
+
+impl DoubleEndedIterator for Args {
+ fn next_back(&mut self) -> Option<OsString> {
+ self.parsed_args_list.next_back()
+ }
+}
+
+impl ExactSizeIterator for Args {
+ fn len(&self) -> usize {
+ self.parsed_args_list.len()
+ }
+}
+
+/// A safe iterator over a LPWSTR
+/// (aka a pointer to a series of UTF-16 code units terminated by a NULL).
+struct WStrUnits<'a> {
+ // The pointer must never be null...
+ lpwstr: NonNull<u16>,
+ // ...and the memory it points to must be valid for this lifetime.
+ lifetime: PhantomData<&'a [u16]>,
+}
+impl WStrUnits<'_> {
+ /// Create the iterator. Returns `None` if `lpwstr` is null.
+ ///
+ /// SAFETY: `lpwstr` must point to a null-terminated wide string that lives
+ /// at least as long as the lifetime of this struct.
+ unsafe fn new(lpwstr: *const u16) -> Option<Self> {
+ Some(Self { lpwstr: NonNull::new(lpwstr as _)?, lifetime: PhantomData })
+ }
+ fn peek(&self) -> Option<NonZeroU16> {
+ // SAFETY: It's always safe to read the current item because we don't
+ // ever move out of the array's bounds.
+ unsafe { NonZeroU16::new(*self.lpwstr.as_ptr()) }
+ }
+ /// Advance the iterator while `predicate` returns true.
+ /// Returns the number of items it advanced by.
+ fn advance_while<P: FnMut(NonZeroU16) -> bool>(&mut self, mut predicate: P) -> usize {
+ let mut counter = 0;
+ while let Some(w) = self.peek() {
+ if !predicate(w) {
+ break;
+ }
+ counter += 1;
+ self.next();
+ }
+ counter
+ }
+}
+impl Iterator for WStrUnits<'_> {
+ // This can never return zero as that marks the end of the string.
+ type Item = NonZeroU16;
+ fn next(&mut self) -> Option<NonZeroU16> {
+ // SAFETY: If NULL is reached we immediately return.
+ // Therefore it's safe to advance the pointer after that.
+ unsafe {
+ let next = self.peek()?;
+ self.lpwstr = NonNull::new_unchecked(self.lpwstr.as_ptr().add(1));
+ Some(next)
+ }
+ }
+}
+
+#[derive(Debug)]
+pub(crate) enum Arg {
+ /// Add quotes (if needed)
+ Regular(OsString),
+ /// Append raw string without quoting
+ Raw(OsString),
+}
+
+enum Quote {
+ // Every arg is quoted
+ Always,
+ // Whitespace and empty args are quoted
+ Auto,
+ // Arg appended without any changes (#29494)
+ Never,
+}
+
+pub(crate) fn append_arg(cmd: &mut Vec<u16>, arg: &Arg, force_quotes: bool) -> io::Result<()> {
+ let (arg, quote) = match arg {
+ Arg::Regular(arg) => (arg, if force_quotes { Quote::Always } else { Quote::Auto }),
+ Arg::Raw(arg) => (arg, Quote::Never),
+ };
+
+ // 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.bytes();
+ let (quote, escape) = match quote {
+ Quote::Always => (true, true),
+ Quote::Auto => {
+ (arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') || arg_bytes.is_empty(), true)
+ }
+ Quote::Never => (false, false),
+ };
+ if quote {
+ cmd.push('"' as u16);
+ }
+
+ let mut backslashes: usize = 0;
+ for x in arg.encode_wide() {
+ if escape {
+ if x == '\\' as u16 {
+ backslashes += 1;
+ } else {
+ if x == '"' as u16 {
+ // Add n+1 backslashes to total 2n+1 before internal '"'.
+ cmd.extend((0..=backslashes).map(|_| '\\' as u16));
+ }
+ backslashes = 0;
+ }
+ }
+ cmd.push(x);
+ }
+
+ if quote {
+ // Add n backslashes to total 2n before ending '"'.
+ cmd.extend((0..backslashes).map(|_| '\\' as u16));
+ cmd.push('"' as u16);
+ }
+ Ok(())
+}
+
+pub(crate) fn make_bat_command_line(
+ script: &[u16],
+ args: &[Arg],
+ force_quotes: bool,
+) -> io::Result<Vec<u16>> {
+ // Set the start of the command line to `cmd.exe /c "`
+ // It is necessary to surround the command in an extra pair of quotes,
+ // hence the trailing quote here. It will be closed after all arguments
+ // have been added.
+ let mut cmd: Vec<u16> = "cmd.exe /c \"".encode_utf16().collect();
+
+ // Push the script name surrounded by its quote pair.
+ cmd.push(b'"' as u16);
+ // Windows file names cannot contain a `"` character or end with `\\`.
+ // If the script name does then return an error.
+ if script.contains(&(b'"' as u16)) || script.last() == Some(&(b'\\' as u16)) {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "Windows file names may not contain `\"` or end with `\\`"
+ ));
+ }
+ cmd.extend_from_slice(script.strip_suffix(&[0]).unwrap_or(script));
+ cmd.push(b'"' as u16);
+
+ // Append the arguments.
+ // FIXME: This needs tests to ensure that the arguments are properly
+ // reconstructed by the batch script by default.
+ for arg in args {
+ cmd.push(' ' as u16);
+ append_arg(&mut cmd, arg, force_quotes)?;
+ }
+
+ // Close the quote we left opened earlier.
+ cmd.push(b'"' as u16);
+
+ Ok(cmd)
+}
+
+/// Takes a path and tries to return a non-verbatim path.
+///
+/// This is necessary because cmd.exe does not support verbatim paths.
+pub(crate) fn to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> {
+ use crate::ptr;
+ use crate::sys::windows::fill_utf16_buf;
+
+ // UTF-16 encoded code points, used in parsing and building UTF-16 paths.
+ // All of these are in the ASCII range so they can be cast directly to `u16`.
+ const SEP: u16 = b'\\' as _;
+ const QUERY: u16 = b'?' as _;
+ const COLON: u16 = b':' as _;
+ const U: u16 = b'U' as _;
+ const N: u16 = b'N' as _;
+ const C: u16 = b'C' as _;
+
+ // Early return if the path is too long to remove the verbatim prefix.
+ const LEGACY_MAX_PATH: usize = 260;
+ if path.len() > LEGACY_MAX_PATH {
+ return Ok(path);
+ }
+
+ match &path[..] {
+ // `\\?\C:\...` => `C:\...`
+ [SEP, SEP, QUERY, SEP, _, COLON, SEP, ..] => unsafe {
+ let lpfilename = path[4..].as_ptr();
+ fill_utf16_buf(
+ |buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()),
+ |full_path: &[u16]| {
+ if full_path == &path[4..path.len() - 1] { full_path.into() } else { path }
+ },
+ )
+ },
+ // `\\?\UNC\...` => `\\...`
+ [SEP, SEP, QUERY, SEP, U, N, C, SEP, ..] => unsafe {
+ // Change the `C` in `UNC\` to `\` so we can get a slice that starts with `\\`.
+ path[6] = b'\\' as u16;
+ let lpfilename = path[6..].as_ptr();
+ fill_utf16_buf(
+ |buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()),
+ |full_path: &[u16]| {
+ if full_path == &path[6..path.len() - 1] {
+ full_path.into()
+ } else {
+ // Restore the 'C' in "UNC".
+ path[6] = b'C' as u16;
+ path
+ }
+ },
+ )
+ },
+ // For everything else, leave the path unchanged.
+ _ => Ok(path),
+ }
+}
diff --git a/library/std/src/sys/windows/args/tests.rs b/library/std/src/sys/windows/args/tests.rs
new file mode 100644
index 000000000..82c32d08c
--- /dev/null
+++ b/library/std/src/sys/windows/args/tests.rs
@@ -0,0 +1,91 @@
+use crate::ffi::OsString;
+use crate::sys::windows::args::*;
+
+fn chk(string: &str, parts: &[&str]) {
+ let mut wide: Vec<u16> = OsString::from(string).encode_wide().collect();
+ wide.push(0);
+ let parsed =
+ unsafe { parse_lp_cmd_line(WStrUnits::new(wide.as_ptr()), || OsString::from("TEST.EXE")) };
+ let expected: Vec<OsString> = parts.iter().map(|k| OsString::from(k)).collect();
+ assert_eq!(parsed.as_slice(), expected.as_slice(), "{:?}", string);
+}
+
+#[test]
+fn empty() {
+ chk("", &["TEST.EXE"]);
+ chk("\0", &["TEST.EXE"]);
+}
+
+#[test]
+fn single_words() {
+ chk("EXE one_word", &["EXE", "one_word"]);
+ chk("EXE a", &["EXE", "a"]);
+ chk("EXE 😅", &["EXE", "😅"]);
+ chk("EXE 😅🤦", &["EXE", "😅🤦"]);
+}
+
+#[test]
+fn official_examples() {
+ chk(r#"EXE "abc" d e"#, &["EXE", "abc", "d", "e"]);
+ chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r"a\\\b", "de fg", "h"]);
+ chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]);
+ chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r"a\\b c", "d", "e"]);
+}
+
+#[test]
+fn whitespace_behavior() {
+ chk(" test", &["", "test"]);
+ chk(" test", &["", "test"]);
+ chk(" test test2", &["", "test", "test2"]);
+ chk(" test test2", &["", "test", "test2"]);
+ chk("test test2 ", &["test", "test2"]);
+ chk("test test2 ", &["test", "test2"]);
+ chk("test ", &["test"]);
+}
+
+#[test]
+fn genius_quotes() {
+ chk(r#"EXE "" """#, &["EXE", "", ""]);
+ chk(r#"EXE "" """"#, &["EXE", "", r#"""#]);
+ chk(
+ r#"EXE "this is """all""" in the same argument""#,
+ &["EXE", r#"this is "all" in the same argument"#],
+ );
+ chk(r#"EXE "a"""#, &["EXE", r#"a""#]);
+ chk(r#"EXE "a"" a"#, &["EXE", r#"a" a"#]);
+ // quotes cannot be escaped in command names
+ chk(r#""EXE" check"#, &["EXE", "check"]);
+ chk(r#""EXE check""#, &["EXE check"]);
+ chk(r#""EXE """for""" check"#, &["EXE for check"]);
+ chk(r#""EXE \"for\" check"#, &[r"EXE \for\ check"]);
+ chk(r#""EXE \" for \" check"#, &[r"EXE \", "for", r#"""#, "check"]);
+ chk(r#"E"X"E test"#, &["EXE", "test"]);
+ chk(r#"EX""E test"#, &["EXE", "test"]);
+}
+
+// from https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULESEX
+#[test]
+fn post_2008() {
+ chk("EXE CallMeIshmael", &["EXE", "CallMeIshmael"]);
+ chk(r#"EXE "Call Me Ishmael""#, &["EXE", "Call Me Ishmael"]);
+ chk(r#"EXE Cal"l Me I"shmael"#, &["EXE", "Call Me Ishmael"]);
+ chk(r#"EXE CallMe\"Ishmael"#, &["EXE", r#"CallMe"Ishmael"#]);
+ chk(r#"EXE "CallMe\"Ishmael""#, &["EXE", r#"CallMe"Ishmael"#]);
+ chk(r#"EXE "Call Me Ishmael\\""#, &["EXE", r"Call Me Ishmael\"]);
+ chk(r#"EXE "CallMe\\\"Ishmael""#, &["EXE", r#"CallMe\"Ishmael"#]);
+ chk(r#"EXE a\\\b"#, &["EXE", r"a\\\b"]);
+ chk(r#"EXE "a\\\b""#, &["EXE", r"a\\\b"]);
+ chk(r#"EXE "\"Call Me Ishmael\"""#, &["EXE", r#""Call Me Ishmael""#]);
+ chk(r#"EXE "C:\TEST A\\""#, &["EXE", r"C:\TEST A\"]);
+ chk(r#"EXE "\"C:\TEST A\\\"""#, &["EXE", r#""C:\TEST A\""#]);
+ chk(r#"EXE "a b c" d e"#, &["EXE", "a b c", "d", "e"]);
+ chk(r#"EXE "ab\"c" "\\" d"#, &["EXE", r#"ab"c"#, r"\", "d"]);
+ chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r"a\\\b", "de fg", "h"]);
+ chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]);
+ chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r"a\\b c", "d", "e"]);
+ // Double Double Quotes
+ chk(r#"EXE "a b c"""#, &["EXE", r#"a b c""#]);
+ chk(r#"EXE """CallMeIshmael""" b c"#, &["EXE", r#""CallMeIshmael""#, "b", "c"]);
+ chk(r#"EXE """Call Me Ishmael""""#, &["EXE", r#""Call Me Ishmael""#]);
+ chk(r#"EXE """"Call Me Ishmael"" b c"#, &["EXE", r#""Call"#, "Me", "Ishmael", "b", "c"]);
+}
diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
new file mode 100644
index 000000000..478068c73
--- /dev/null
+++ b/library/std/src/sys/windows/c.rs
@@ -0,0 +1,1340 @@
+//! C definitions used by libnative that don't belong in liblibc
+
+#![allow(nonstandard_style)]
+#![cfg_attr(test, allow(dead_code))]
+#![unstable(issue = "none", feature = "windows_c")]
+
+use crate::ffi::CStr;
+use crate::mem;
+use crate::os::raw::{c_char, c_int, c_long, c_longlong, c_uint, c_ulong, c_ushort};
+use crate::os::windows::io::{BorrowedHandle, HandleOrInvalid, HandleOrNull};
+use crate::ptr;
+use core::ffi::NonZero_c_ulong;
+
+use libc::{c_void, size_t, wchar_t};
+
+#[path = "c/errors.rs"] // c.rs is included from two places so we need to specify this
+mod errors;
+pub use errors::*;
+
+pub use self::EXCEPTION_DISPOSITION::*;
+pub use self::FILE_INFO_BY_HANDLE_CLASS::*;
+
+pub type DWORD_PTR = ULONG_PTR;
+pub type DWORD = c_ulong;
+pub type NonZeroDWORD = NonZero_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 CCHAR = c_char;
+pub type ULONG_PTR = usize;
+pub type ULONG = c_ulong;
+pub type NTSTATUS = LONG;
+pub type ACCESS_MASK = DWORD;
+
+pub type LPBOOL = *mut BOOL;
+pub type LPBYTE = *mut BYTE;
+pub type LPCSTR = *const CHAR;
+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 LPWSTR = *mut WCHAR;
+pub type LPFILETIME = *mut FILETIME;
+pub type LPSYSTEM_INFO = *mut SYSTEM_INFO;
+pub type LPWSABUF = *mut WSABUF;
+pub type LPWSAOVERLAPPED = *mut c_void;
+pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = *mut c_void;
+
+pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE;
+pub type PLARGE_INTEGER = *mut c_longlong;
+pub type PSRWLOCK = *mut SRWLOCK;
+
+pub type SOCKET = crate::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 CSTR_LESS_THAN: c_int = 1;
+pub const CSTR_EQUAL: c_int = 2;
+pub const CSTR_GREATER_THAN: c_int = 3;
+
+pub const FILE_ATTRIBUTE_READONLY: DWORD = 0x1;
+pub const FILE_ATTRIBUTE_DIRECTORY: DWORD = 0x10;
+pub const FILE_ATTRIBUTE_REPARSE_POINT: DWORD = 0x400;
+pub const INVALID_FILE_ATTRIBUTES: DWORD = DWORD::MAX;
+
+pub const FILE_SHARE_DELETE: DWORD = 0x4;
+pub const FILE_SHARE_READ: DWORD = 0x1;
+pub const FILE_SHARE_WRITE: DWORD = 0x2;
+
+pub const FILE_OPEN: ULONG = 0x00000001;
+pub const FILE_OPEN_REPARSE_POINT: ULONG = 0x200000;
+pub const OBJ_DONT_REPARSE: ULONG = 0x1000;
+
+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_LIST_DIRECTORY: DWORD = 0x1;
+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 DELETE: DWORD = 0x10000;
+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 WSA_FLAG_NO_HANDLE_INHERIT: DWORD = 0x80;
+
+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 MAX_PROTOCOL_CHAIN: DWORD = 7;
+
+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 PROGRESS_CONTINUE: DWORD = 0;
+
+pub const E_NOTIMPL: HRESULT = 0x80004001u32 as HRESULT;
+
+pub const INVALID_HANDLE_VALUE: HANDLE = ptr::invalid_mut(!0);
+
+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 SOCKET_ERROR: c_int = -1;
+pub const SOL_SOCKET: c_int = 0xffff;
+pub const SO_LINGER: c_int = 0x0080;
+pub const SO_RCVTIMEO: c_int = 0x1006;
+pub const SO_SNDTIMEO: c_int = 0x1005;
+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)]
+#[derive(Copy, Clone)]
+pub struct linger {
+ pub l_onoff: c_ushort,
+ pub l_linger: c_ushort,
+}
+
+#[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;
+
+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;
+
+pub const STACK_SIZE_PARAM_IS_A_RESERVATION: DWORD = 0x00010000;
+
+pub const STATUS_SUCCESS: NTSTATUS = 0x00000000;
+pub const STATUS_DELETE_PENDING: NTSTATUS = 0xc0000056_u32 as _;
+pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xc000000d_u32 as _;
+
+pub const STATUS_PENDING: NTSTATUS = 0x103 as _;
+pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _;
+pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002_u32 as _;
+
+// Equivalent to the `NT_SUCCESS` C preprocessor macro.
+// See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values
+pub fn nt_success(status: NTSTATUS) -> bool {
+ status >= 0
+}
+
+pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD = 0x00000002;
+
+#[repr(C)]
+pub struct UNICODE_STRING {
+ pub Length: u16,
+ pub MaximumLength: u16,
+ pub Buffer: *mut u16,
+}
+impl UNICODE_STRING {
+ pub fn from_ref(slice: &[u16]) -> Self {
+ let len = slice.len() * mem::size_of::<u16>();
+ Self { Length: len as _, MaximumLength: len as _, Buffer: slice.as_ptr() as _ }
+ }
+}
+#[repr(C)]
+pub struct OBJECT_ATTRIBUTES {
+ pub Length: ULONG,
+ pub RootDirectory: HANDLE,
+ pub ObjectName: *const UNICODE_STRING,
+ pub Attributes: ULONG,
+ pub SecurityDescriptor: *mut c_void,
+ pub SecurityQualityOfService: *mut c_void,
+}
+impl Default for OBJECT_ATTRIBUTES {
+ fn default() -> Self {
+ Self {
+ Length: mem::size_of::<Self>() as _,
+ RootDirectory: ptr::null_mut(),
+ ObjectName: ptr::null_mut(),
+ Attributes: 0,
+ SecurityDescriptor: ptr::null_mut(),
+ SecurityQualityOfService: ptr::null_mut(),
+ }
+ }
+}
+#[repr(C)]
+union IO_STATUS_BLOCK_union {
+ Status: NTSTATUS,
+ Pointer: *mut c_void,
+}
+impl Default for IO_STATUS_BLOCK_union {
+ fn default() -> Self {
+ let mut this = Self { Pointer: ptr::null_mut() };
+ this.Status = STATUS_PENDING;
+ this
+ }
+}
+#[repr(C)]
+#[derive(Default)]
+pub struct IO_STATUS_BLOCK {
+ u: IO_STATUS_BLOCK_union,
+ pub Information: usize,
+}
+impl IO_STATUS_BLOCK {
+ pub fn status(&self) -> NTSTATUS {
+ // SAFETY: If `self.u.Status` was set then this is obviously safe.
+ // If `self.u.Pointer` was set then this is the equivalent to converting
+ // the pointer to an integer, which is also safe.
+ // Currently the only safe way to construct `IO_STATUS_BLOCK` outside of
+ // this module is to call the `default` method, which sets the `Status`.
+ unsafe { self.u.Status }
+ }
+}
+
+pub type LPOVERLAPPED_COMPLETION_ROUTINE = unsafe extern "system" fn(
+ dwErrorCode: DWORD,
+ dwNumberOfBytesTransfered: DWORD,
+ lpOverlapped: *mut OVERLAPPED,
+);
+
+type IO_APC_ROUTINE = unsafe extern "system" fn(
+ ApcContext: *mut c_void,
+ IoStatusBlock: *mut IO_STATUS_BLOCK,
+ Reserved: ULONG,
+);
+
+#[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],
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub struct WSABUF {
+ pub len: ULONG,
+ pub buf: *mut CHAR,
+}
+
+#[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)]
+#[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
+ FileDispositionInfoEx = 21, // 0x15, Windows 10 version 1607
+ MaximumFileInfoByHandlesClass,
+}
+
+#[repr(C)]
+pub struct FILE_DISPOSITION_INFO {
+ pub DeleteFile: BOOLEAN,
+}
+
+pub const FILE_DISPOSITION_DELETE: DWORD = 0x1;
+pub const FILE_DISPOSITION_POSIX_SEMANTICS: DWORD = 0x2;
+pub const FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE: DWORD = 0x10;
+
+#[repr(C)]
+pub struct FILE_DISPOSITION_INFO_EX {
+ pub Flags: DWORD,
+}
+
+#[repr(C)]
+#[derive(Default)]
+pub struct FILE_ID_BOTH_DIR_INFO {
+ pub NextEntryOffset: DWORD,
+ pub FileIndex: DWORD,
+ pub CreationTime: LARGE_INTEGER,
+ pub LastAccessTime: LARGE_INTEGER,
+ pub LastWriteTime: LARGE_INTEGER,
+ pub ChangeTime: LARGE_INTEGER,
+ pub EndOfFile: LARGE_INTEGER,
+ pub AllocationSize: LARGE_INTEGER,
+ pub FileAttributes: DWORD,
+ pub FileNameLength: DWORD,
+ pub EaSize: DWORD,
+ pub ShortNameLength: CCHAR,
+ pub ShortName: [WCHAR; 12],
+ pub FileId: LARGE_INTEGER,
+ pub FileName: [WCHAR; 1],
+}
+#[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 = crate::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 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 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, Debug, Default)]
+pub struct FILETIME {
+ pub dwLowDateTime: DWORD,
+ pub dwHighDateTime: DWORD,
+}
+
+#[repr(C)]
+pub struct SYSTEM_INFO {
+ pub wProcessorArchitecture: WORD,
+ pub wReserved: WORD,
+ pub dwPageSize: DWORD,
+ pub lpMinimumApplicationAddress: LPVOID,
+ pub lpMaximumApplicationAddress: LPVOID,
+ pub dwActiveProcessorMask: DWORD_PTR,
+ pub dwNumberOfProcessors: DWORD,
+ pub dwProcessorType: DWORD,
+ pub dwAllocationGranularity: DWORD,
+ pub wProcessorLevel: WORD,
+ pub wProcessorRevision: WORD,
+}
+
+#[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)]
+#[allow(dead_code)] // we only use some variants
+pub enum ADDRESS_MODE {
+ AddrMode1616,
+ AddrMode1632,
+ AddrModeReal,
+ AddrModeFlat,
+}
+
+#[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)]
+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,
+}
+
+// Desktop specific functions & types
+cfg_if::cfg_if! {
+if #[cfg(not(target_vendor = "uwp"))] {
+ pub const EXCEPTION_CONTINUE_SEARCH: LONG = 0;
+ pub const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd;
+ pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15;
+
+ #[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],
+ }
+
+ pub enum CONTEXT {}
+
+ #[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)]
+ #[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)]
+ 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,
+ }
+
+ pub type LPBY_HANDLE_FILE_INFORMATION = *mut BY_HANDLE_FILE_INFORMATION;
+ pub type LPCVOID = *const c_void;
+
+ pub const HANDLE_FLAG_INHERIT: DWORD = 0x00000001;
+
+ pub const TOKEN_READ: DWORD = 0x20008;
+
+ #[link(name = "advapi32")]
+ extern "system" {
+ // Forbidden when targeting UWP
+ #[link_name = "SystemFunction036"]
+ pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN;
+
+ // Allowed but unused by UWP
+ pub fn OpenProcessToken(
+ ProcessHandle: HANDLE,
+ DesiredAccess: DWORD,
+ TokenHandle: *mut HANDLE,
+ ) -> BOOL;
+ }
+
+ #[link(name = "userenv")]
+ extern "system" {
+ // Allowed but unused by UWP
+ pub fn GetUserProfileDirectoryW(
+ hToken: HANDLE,
+ lpProfileDir: LPWSTR,
+ lpcchSize: *mut DWORD,
+ ) -> BOOL;
+ }
+
+ #[link(name = "kernel32")]
+ extern "system" {
+ // Functions forbidden when targeting UWP
+ 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;
+ // Allowed but unused by UWP
+ pub fn GetFileInformationByHandle(
+ hFile: HANDLE,
+ lpFileInformation: LPBY_HANDLE_FILE_INFORMATION,
+ ) -> BOOL;
+ pub fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) -> BOOL;
+ pub fn AddVectoredExceptionHandler(
+ FirstHandler: ULONG,
+ VectoredHandler: PVECTORED_EXCEPTION_HANDLER,
+ ) -> LPVOID;
+ pub fn CreateHardLinkW(
+ lpSymlinkFileName: LPCWSTR,
+ lpTargetFileName: LPCWSTR,
+ lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
+ ) -> BOOL;
+ pub fn SetThreadStackGuarantee(_size: *mut c_ulong) -> BOOL;
+ pub fn GetWindowsDirectoryW(lpBuffer: LPWSTR, uSize: UINT) -> UINT;
+ }
+}
+}
+
+// UWP specific functions & types
+cfg_if::cfg_if! {
+if #[cfg(target_vendor = "uwp")] {
+ #[repr(C)]
+ pub struct FILE_STANDARD_INFO {
+ pub AllocationSize: LARGE_INTEGER,
+ pub EndOfFile: LARGE_INTEGER,
+ pub NumberOfLinks: DWORD,
+ pub DeletePending: BOOLEAN,
+ pub Directory: BOOLEAN,
+ }
+}
+}
+
+// Shared between Desktop & UWP
+
+#[link(name = "kernel32")]
+extern "system" {
+ pub fn GetCurrentProcessId() -> DWORD;
+
+ pub fn GetSystemDirectoryW(lpBuffer: LPWSTR, uSize: UINT) -> UINT;
+ pub fn RemoveDirectoryW(lpPathName: LPCWSTR) -> BOOL;
+ pub fn SetFileAttributesW(lpFileName: LPCWSTR, dwFileAttributes: DWORD) -> BOOL;
+ pub fn SetFileTime(
+ hFile: BorrowedHandle<'_>,
+ lpCreationTime: Option<&FILETIME>,
+ lpLastAccessTime: Option<&FILETIME>,
+ lpLastWriteTime: Option<&FILETIME>,
+ ) -> BOOL;
+ pub fn SetLastError(dwErrCode: DWORD);
+ pub fn GetCommandLineW() -> LPWSTR;
+ pub fn GetTempPathW(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD;
+ 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,
+ ) -> HandleOrNull;
+ pub fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
+ pub fn SwitchToThread() -> BOOL;
+ pub fn Sleep(dwMilliseconds: DWORD);
+ pub fn SleepEx(dwMilliseconds: DWORD, bAlertable: BOOL) -> DWORD;
+ pub fn GetProcessId(handle: HANDLE) -> DWORD;
+ pub fn CopyFileExW(
+ lpExistingFileName: LPCWSTR,
+ lpNewFileName: LPCWSTR,
+ lpProgressRoutine: LPPROGRESS_ROUTINE,
+ lpData: LPVOID,
+ pbCancel: LPBOOL,
+ dwCopyFlags: DWORD,
+ ) -> BOOL;
+ 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 DuplicateHandle(
+ hSourceProcessHandle: HANDLE,
+ hSourceHandle: HANDLE,
+ hTargetProcessHandle: HANDLE,
+ lpTargetHandle: LPHANDLE,
+ dwDesiredAccess: DWORD,
+ bInheritHandle: BOOL,
+ dwOptions: DWORD,
+ ) -> BOOL;
+ pub fn ReadFile(
+ hFile: BorrowedHandle<'_>,
+ lpBuffer: LPVOID,
+ nNumberOfBytesToRead: DWORD,
+ lpNumberOfBytesRead: LPDWORD,
+ lpOverlapped: LPOVERLAPPED,
+ ) -> BOOL;
+ pub fn ReadFileEx(
+ hFile: BorrowedHandle<'_>,
+ lpBuffer: LPVOID,
+ nNumberOfBytesToRead: DWORD,
+ lpOverlapped: LPOVERLAPPED,
+ lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
+ ) -> BOOL;
+ pub fn WriteFileEx(
+ hFile: BorrowedHandle<'_>,
+ lpBuffer: LPVOID,
+ nNumberOfBytesToWrite: DWORD,
+ lpOverlapped: LPOVERLAPPED,
+ lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
+ ) -> BOOL;
+ pub fn CloseHandle(hObject: HANDLE) -> 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,
+ ) -> HandleOrInvalid;
+
+ 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;
+
+ pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void;
+ pub fn GetModuleHandleA(lpModuleName: LPCSTR) -> HMODULE;
+ pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
+
+ pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME);
+ pub fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO);
+
+ 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 CreateSymbolicLinkW(
+ lpSymlinkFileName: LPCWSTR,
+ lpTargetFileName: LPCWSTR,
+ dwFlags: DWORD,
+ ) -> BOOLEAN;
+ pub fn GetFinalPathNameByHandleW(
+ hFile: HANDLE,
+ lpszFilePath: LPCWSTR,
+ cchFilePath: DWORD,
+ dwFlags: DWORD,
+ ) -> DWORD;
+ pub fn GetFileInformationByHandleEx(
+ hFile: HANDLE,
+ fileInfoClass: FILE_INFO_BY_HANDLE_CLASS,
+ lpFileInformation: LPVOID,
+ dwBufferSize: DWORD,
+ ) -> BOOL;
+ pub fn SetFileInformationByHandle(
+ hFile: HANDLE,
+ FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
+ lpFileInformation: LPVOID,
+ dwBufferSize: DWORD,
+ ) -> BOOL;
+ pub fn SleepConditionVariableSRW(
+ ConditionVariable: PCONDITION_VARIABLE,
+ SRWLock: PSRWLOCK,
+ dwMilliseconds: DWORD,
+ Flags: ULONG,
+ ) -> BOOL;
+
+ pub fn WakeConditionVariable(ConditionVariable: PCONDITION_VARIABLE);
+ pub fn WakeAllConditionVariable(ConditionVariable: PCONDITION_VARIABLE);
+
+ pub fn AcquireSRWLockExclusive(SRWLock: PSRWLOCK);
+ pub fn AcquireSRWLockShared(SRWLock: PSRWLOCK);
+ pub fn ReleaseSRWLockExclusive(SRWLock: PSRWLOCK);
+ pub fn ReleaseSRWLockShared(SRWLock: PSRWLOCK);
+ pub fn TryAcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> BOOLEAN;
+ pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN;
+
+ pub fn CompareStringOrdinal(
+ lpString1: LPCWSTR,
+ cchCount1: c_int,
+ lpString2: LPCWSTR,
+ cchCount2: c_int,
+ bIgnoreCase: BOOL,
+ ) -> c_int;
+ pub fn GetFullPathNameW(
+ lpFileName: LPCWSTR,
+ nBufferLength: DWORD,
+ lpBuffer: LPWSTR,
+ lpFilePart: *mut LPWSTR,
+ ) -> DWORD;
+ pub fn GetFileAttributesW(lpFileName: LPCWSTR) -> DWORD;
+}
+
+#[link(name = "ws2_32")]
+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 WSASend(
+ s: SOCKET,
+ lpBuffers: LPWSABUF,
+ dwBufferCount: DWORD,
+ lpNumberOfBytesSent: LPDWORD,
+ dwFlags: DWORD,
+ lpOverlapped: LPWSAOVERLAPPED,
+ lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE,
+ ) -> c_int;
+ pub fn WSARecv(
+ s: SOCKET,
+ lpBuffers: LPWSABUF,
+ dwBufferCount: DWORD,
+ lpNumberOfBytesRecvd: LPDWORD,
+ lpFlags: LPDWORD,
+ lpOverlapped: LPWSAOVERLAPPED,
+ lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE,
+ ) -> c_int;
+ 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 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 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);
+ 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 = "bcrypt")]
+extern "system" {
+ // >= Vista / Server 2008
+ // https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
+ pub fn BCryptGenRandom(
+ hAlgorithm: LPVOID,
+ pBuffer: *mut u8,
+ cbBuffer: ULONG,
+ dwFlags: ULONG,
+ ) -> NTSTATUS;
+}
+
+// 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_with_fallback! {
+ pub static KERNEL32: &CStr = ansi_str!("kernel32");
+
+ // >= Win10 1607
+ // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription
+ pub fn SetThreadDescription(hThread: HANDLE,
+ lpThreadDescription: LPCWSTR) -> HRESULT {
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); E_NOTIMPL
+ }
+
+ // >= Win8 / Server 2012
+ // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime
+ pub fn GetSystemTimePreciseAsFileTime(lpSystemTimeAsFileTime: LPFILETIME)
+ -> () {
+ GetSystemTimeAsFileTime(lpSystemTimeAsFileTime)
+ }
+
+ // >= Win11 / Server 2022
+ // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2a
+ pub fn GetTempPath2W(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD {
+ GetTempPathW(nBufferLength, lpBuffer)
+ }
+}
+
+compat_fn_optional! {
+ pub static SYNCH_API: &CStr = ansi_str!("api-ms-win-core-synch-l1-2-0");
+
+ // >= Windows 8 / Server 2012
+ // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitonaddress
+ pub fn WaitOnAddress(
+ Address: LPVOID,
+ CompareAddress: LPVOID,
+ AddressSize: SIZE_T,
+ dwMilliseconds: DWORD
+ ) -> BOOL;
+ pub fn WakeByAddressSingle(Address: LPVOID) -> ();
+}
+
+compat_fn_with_fallback! {
+ pub static NTDLL: &CStr = ansi_str!("ntdll");
+
+ pub fn NtCreateFile(
+ FileHandle: *mut HANDLE,
+ DesiredAccess: ACCESS_MASK,
+ ObjectAttributes: *const OBJECT_ATTRIBUTES,
+ IoStatusBlock: *mut IO_STATUS_BLOCK,
+ AllocationSize: *mut i64,
+ FileAttributes: ULONG,
+ ShareAccess: ULONG,
+ CreateDisposition: ULONG,
+ CreateOptions: ULONG,
+ EaBuffer: *mut c_void,
+ EaLength: ULONG
+ ) -> NTSTATUS {
+ STATUS_NOT_IMPLEMENTED
+ }
+ pub fn NtReadFile(
+ FileHandle: BorrowedHandle<'_>,
+ Event: HANDLE,
+ ApcRoutine: Option<IO_APC_ROUTINE>,
+ ApcContext: *mut c_void,
+ IoStatusBlock: &mut IO_STATUS_BLOCK,
+ Buffer: *mut crate::mem::MaybeUninit<u8>,
+ Length: ULONG,
+ ByteOffset: Option<&LARGE_INTEGER>,
+ Key: Option<&ULONG>
+ ) -> NTSTATUS {
+ STATUS_NOT_IMPLEMENTED
+ }
+ pub fn NtWriteFile(
+ FileHandle: BorrowedHandle<'_>,
+ Event: HANDLE,
+ ApcRoutine: Option<IO_APC_ROUTINE>,
+ ApcContext: *mut c_void,
+ IoStatusBlock: &mut IO_STATUS_BLOCK,
+ Buffer: *const u8,
+ Length: ULONG,
+ ByteOffset: Option<&LARGE_INTEGER>,
+ Key: Option<&ULONG>
+ ) -> NTSTATUS {
+ STATUS_NOT_IMPLEMENTED
+ }
+ pub fn RtlNtStatusToDosError(
+ Status: NTSTATUS
+ ) -> ULONG {
+ Status as ULONG
+ }
+ pub fn NtCreateKeyedEvent(
+ KeyedEventHandle: LPHANDLE,
+ DesiredAccess: ACCESS_MASK,
+ ObjectAttributes: LPVOID,
+ Flags: ULONG
+ ) -> NTSTATUS {
+ panic!("keyed events not available")
+ }
+ pub fn NtReleaseKeyedEvent(
+ EventHandle: HANDLE,
+ Key: LPVOID,
+ Alertable: BOOLEAN,
+ Timeout: PLARGE_INTEGER
+ ) -> NTSTATUS {
+ panic!("keyed events not available")
+ }
+ pub fn NtWaitForKeyedEvent(
+ EventHandle: HANDLE,
+ Key: LPVOID,
+ Alertable: BOOLEAN,
+ Timeout: PLARGE_INTEGER
+ ) -> NTSTATUS {
+ panic!("keyed events not available")
+ }
+}
diff --git a/library/std/src/sys/windows/c/errors.rs b/library/std/src/sys/windows/c/errors.rs
new file mode 100644
index 000000000..23dcc119d
--- /dev/null
+++ b/library/std/src/sys/windows/c/errors.rs
@@ -0,0 +1,1883 @@
+// List of Windows system error codes with descriptions:
+// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes#system-error-codes
+
+#![allow(dead_code)]
+
+use super::{c_int, DWORD};
+
+pub const ERROR_DIRECTORY_NOT_SUPPORTED: DWORD = 336;
+pub const ERROR_DRIVER_CANCEL_TIMEOUT: DWORD = 594;
+pub const ERROR_DISK_QUOTA_EXCEEDED: DWORD = 1295;
+pub const ERROR_RESOURCE_CALL_TIMED_OUT: DWORD = 5910;
+pub const FRS_ERR_SYSVOL_POPULATE_TIMEOUT: DWORD = 8014;
+pub const DNS_ERROR_RECORD_TIMED_OUT: DWORD = 9705;
+
+// The followiung list was obtained from
+// `/usr/x86_64-w64-mingw32/include/winerror.h`
+// in the Debian package
+// mingw-w64_6.0.0-3_all.deb
+//
+// The header of that file says:
+// * This file has no copyright assigned and is placed in the Public Domain.
+// * This file is part of the mingw-w64 runtime package.
+// * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+//
+// The text here is the result of the following rune:
+// grep -P '#define ERROR' /usr/x86_64-w64-mingw32/include/winerror.h >>library/std/src/sys/windows/c/errors.rs
+// grep -P '#define WSA' /usr/x86_64-w64-mingw32/include/winerror.h >>library/std/src/sys/windows/c/errors.rs
+// and then using some manually-invented but rather obvious editor search-and-replace
+// invocations, plus some straightforward manual fixups, to turn it into Rust syntax
+// and remove all the duplicates from the manual table above.
+
+pub const ERROR_SUCCESS: DWORD = 0;
+pub const ERROR_INVALID_FUNCTION: DWORD = 1;
+pub const ERROR_FILE_NOT_FOUND: DWORD = 2;
+pub const ERROR_PATH_NOT_FOUND: DWORD = 3;
+pub const ERROR_TOO_MANY_OPEN_FILES: DWORD = 4;
+pub const ERROR_ACCESS_DENIED: DWORD = 5;
+pub const ERROR_INVALID_HANDLE: DWORD = 6;
+pub const ERROR_ARENA_TRASHED: DWORD = 7;
+pub const ERROR_NOT_ENOUGH_MEMORY: DWORD = 8;
+pub const ERROR_INVALID_BLOCK: DWORD = 9;
+pub const ERROR_BAD_ENVIRONMENT: DWORD = 10;
+pub const ERROR_BAD_FORMAT: DWORD = 11;
+pub const ERROR_INVALID_ACCESS: DWORD = 12;
+pub const ERROR_INVALID_DATA: DWORD = 13;
+pub const ERROR_OUTOFMEMORY: DWORD = 14;
+pub const ERROR_INVALID_DRIVE: DWORD = 15;
+pub const ERROR_CURRENT_DIRECTORY: DWORD = 16;
+pub const ERROR_NOT_SAME_DEVICE: DWORD = 17;
+pub const ERROR_NO_MORE_FILES: DWORD = 18;
+pub const ERROR_WRITE_PROTECT: DWORD = 19;
+pub const ERROR_BAD_UNIT: DWORD = 20;
+pub const ERROR_NOT_READY: DWORD = 21;
+pub const ERROR_BAD_COMMAND: DWORD = 22;
+pub const ERROR_CRC: DWORD = 23;
+pub const ERROR_BAD_LENGTH: DWORD = 24;
+pub const ERROR_SEEK: DWORD = 25;
+pub const ERROR_NOT_DOS_DISK: DWORD = 26;
+pub const ERROR_SECTOR_NOT_FOUND: DWORD = 27;
+pub const ERROR_OUT_OF_PAPER: DWORD = 28;
+pub const ERROR_WRITE_FAULT: DWORD = 29;
+pub const ERROR_READ_FAULT: DWORD = 30;
+pub const ERROR_GEN_FAILURE: DWORD = 31;
+pub const ERROR_SHARING_VIOLATION: DWORD = 32;
+pub const ERROR_LOCK_VIOLATION: DWORD = 33;
+pub const ERROR_WRONG_DISK: DWORD = 34;
+pub const ERROR_SHARING_BUFFER_EXCEEDED: DWORD = 36;
+pub const ERROR_HANDLE_EOF: DWORD = 38;
+pub const ERROR_HANDLE_DISK_FULL: DWORD = 39;
+pub const ERROR_NOT_SUPPORTED: DWORD = 50;
+pub const ERROR_REM_NOT_LIST: DWORD = 51;
+pub const ERROR_DUP_NAME: DWORD = 52;
+pub const ERROR_BAD_NETPATH: DWORD = 53;
+pub const ERROR_NETWORK_BUSY: DWORD = 54;
+pub const ERROR_DEV_NOT_EXIST: DWORD = 55;
+pub const ERROR_TOO_MANY_CMDS: DWORD = 56;
+pub const ERROR_ADAP_HDW_ERR: DWORD = 57;
+pub const ERROR_BAD_NET_RESP: DWORD = 58;
+pub const ERROR_UNEXP_NET_ERR: DWORD = 59;
+pub const ERROR_BAD_REM_ADAP: DWORD = 60;
+pub const ERROR_PRINTQ_FULL: DWORD = 61;
+pub const ERROR_NO_SPOOL_SPACE: DWORD = 62;
+pub const ERROR_PRINT_CANCELLED: DWORD = 63;
+pub const ERROR_NETNAME_DELETED: DWORD = 64;
+pub const ERROR_NETWORK_ACCESS_DENIED: DWORD = 65;
+pub const ERROR_BAD_DEV_TYPE: DWORD = 66;
+pub const ERROR_BAD_NET_NAME: DWORD = 67;
+pub const ERROR_TOO_MANY_NAMES: DWORD = 68;
+pub const ERROR_TOO_MANY_SESS: DWORD = 69;
+pub const ERROR_SHARING_PAUSED: DWORD = 70;
+pub const ERROR_REQ_NOT_ACCEP: DWORD = 71;
+pub const ERROR_REDIR_PAUSED: DWORD = 72;
+pub const ERROR_FILE_EXISTS: DWORD = 80;
+pub const ERROR_CANNOT_MAKE: DWORD = 82;
+pub const ERROR_FAIL_I24: DWORD = 83;
+pub const ERROR_OUT_OF_STRUCTURES: DWORD = 84;
+pub const ERROR_ALREADY_ASSIGNED: DWORD = 85;
+pub const ERROR_INVALID_PASSWORD: DWORD = 86;
+pub const ERROR_INVALID_PARAMETER: DWORD = 87;
+pub const ERROR_NET_WRITE_FAULT: DWORD = 88;
+pub const ERROR_NO_PROC_SLOTS: DWORD = 89;
+pub const ERROR_TOO_MANY_SEMAPHORES: DWORD = 100;
+pub const ERROR_EXCL_SEM_ALREADY_OWNED: DWORD = 101;
+pub const ERROR_SEM_IS_SET: DWORD = 102;
+pub const ERROR_TOO_MANY_SEM_REQUESTS: DWORD = 103;
+pub const ERROR_INVALID_AT_INTERRUPT_TIME: DWORD = 104;
+pub const ERROR_SEM_OWNER_DIED: DWORD = 105;
+pub const ERROR_SEM_USER_LIMIT: DWORD = 106;
+pub const ERROR_DISK_CHANGE: DWORD = 107;
+pub const ERROR_DRIVE_LOCKED: DWORD = 108;
+pub const ERROR_BROKEN_PIPE: DWORD = 109;
+pub const ERROR_OPEN_FAILED: DWORD = 110;
+pub const ERROR_BUFFER_OVERFLOW: DWORD = 111;
+pub const ERROR_DISK_FULL: DWORD = 112;
+pub const ERROR_NO_MORE_SEARCH_HANDLES: DWORD = 113;
+pub const ERROR_INVALID_TARGET_HANDLE: DWORD = 114;
+pub const ERROR_INVALID_CATEGORY: DWORD = 117;
+pub const ERROR_INVALID_VERIFY_SWITCH: DWORD = 118;
+pub const ERROR_BAD_DRIVER_LEVEL: DWORD = 119;
+pub const ERROR_CALL_NOT_IMPLEMENTED: DWORD = 120;
+pub const ERROR_SEM_TIMEOUT: DWORD = 121;
+pub const ERROR_INSUFFICIENT_BUFFER: DWORD = 122;
+pub const ERROR_INVALID_NAME: DWORD = 123;
+pub const ERROR_INVALID_LEVEL: DWORD = 124;
+pub const ERROR_NO_VOLUME_LABEL: DWORD = 125;
+pub const ERROR_MOD_NOT_FOUND: DWORD = 126;
+pub const ERROR_PROC_NOT_FOUND: DWORD = 127;
+pub const ERROR_WAIT_NO_CHILDREN: DWORD = 128;
+pub const ERROR_CHILD_NOT_COMPLETE: DWORD = 129;
+pub const ERROR_DIRECT_ACCESS_HANDLE: DWORD = 130;
+pub const ERROR_NEGATIVE_SEEK: DWORD = 131;
+pub const ERROR_SEEK_ON_DEVICE: DWORD = 132;
+pub const ERROR_IS_JOIN_TARGET: DWORD = 133;
+pub const ERROR_IS_JOINED: DWORD = 134;
+pub const ERROR_IS_SUBSTED: DWORD = 135;
+pub const ERROR_NOT_JOINED: DWORD = 136;
+pub const ERROR_NOT_SUBSTED: DWORD = 137;
+pub const ERROR_JOIN_TO_JOIN: DWORD = 138;
+pub const ERROR_SUBST_TO_SUBST: DWORD = 139;
+pub const ERROR_JOIN_TO_SUBST: DWORD = 140;
+pub const ERROR_SUBST_TO_JOIN: DWORD = 141;
+pub const ERROR_BUSY_DRIVE: DWORD = 142;
+pub const ERROR_SAME_DRIVE: DWORD = 143;
+pub const ERROR_DIR_NOT_ROOT: DWORD = 144;
+pub const ERROR_DIR_NOT_EMPTY: DWORD = 145;
+pub const ERROR_IS_SUBST_PATH: DWORD = 146;
+pub const ERROR_IS_JOIN_PATH: DWORD = 147;
+pub const ERROR_PATH_BUSY: DWORD = 148;
+pub const ERROR_IS_SUBST_TARGET: DWORD = 149;
+pub const ERROR_SYSTEM_TRACE: DWORD = 150;
+pub const ERROR_INVALID_EVENT_COUNT: DWORD = 151;
+pub const ERROR_TOO_MANY_MUXWAITERS: DWORD = 152;
+pub const ERROR_INVALID_LIST_FORMAT: DWORD = 153;
+pub const ERROR_LABEL_TOO_LONG: DWORD = 154;
+pub const ERROR_TOO_MANY_TCBS: DWORD = 155;
+pub const ERROR_SIGNAL_REFUSED: DWORD = 156;
+pub const ERROR_DISCARDED: DWORD = 157;
+pub const ERROR_NOT_LOCKED: DWORD = 158;
+pub const ERROR_BAD_THREADID_ADDR: DWORD = 159;
+pub const ERROR_BAD_ARGUMENTS: DWORD = 160;
+pub const ERROR_BAD_PATHNAME: DWORD = 161;
+pub const ERROR_SIGNAL_PENDING: DWORD = 162;
+pub const ERROR_MAX_THRDS_REACHED: DWORD = 164;
+pub const ERROR_LOCK_FAILED: DWORD = 167;
+pub const ERROR_BUSY: DWORD = 170;
+pub const ERROR_CANCEL_VIOLATION: DWORD = 173;
+pub const ERROR_ATOMIC_LOCKS_NOT_SUPPORTED: DWORD = 174;
+pub const ERROR_INVALID_SEGMENT_NUMBER: DWORD = 180;
+pub const ERROR_INVALID_ORDINAL: DWORD = 182;
+pub const ERROR_ALREADY_EXISTS: DWORD = 183;
+pub const ERROR_INVALID_FLAG_NUMBER: DWORD = 186;
+pub const ERROR_SEM_NOT_FOUND: DWORD = 187;
+pub const ERROR_INVALID_STARTING_CODESEG: DWORD = 188;
+pub const ERROR_INVALID_STACKSEG: DWORD = 189;
+pub const ERROR_INVALID_MODULETYPE: DWORD = 190;
+pub const ERROR_INVALID_EXE_SIGNATURE: DWORD = 191;
+pub const ERROR_EXE_MARKED_INVALID: DWORD = 192;
+pub const ERROR_BAD_EXE_FORMAT: DWORD = 193;
+pub const ERROR_ITERATED_DATA_EXCEEDS_64k: DWORD = 194;
+pub const ERROR_INVALID_MINALLOCSIZE: DWORD = 195;
+pub const ERROR_DYNLINK_FROM_INVALID_RING: DWORD = 196;
+pub const ERROR_IOPL_NOT_ENABLED: DWORD = 197;
+pub const ERROR_INVALID_SEGDPL: DWORD = 198;
+pub const ERROR_AUTODATASEG_EXCEEDS_64k: DWORD = 199;
+pub const ERROR_RING2SEG_MUST_BE_MOVABLE: DWORD = 200;
+pub const ERROR_RELOC_CHAIN_XEEDS_SEGLIM: DWORD = 201;
+pub const ERROR_INFLOOP_IN_RELOC_CHAIN: DWORD = 202;
+pub const ERROR_ENVVAR_NOT_FOUND: DWORD = 203;
+pub const ERROR_NO_SIGNAL_SENT: DWORD = 205;
+pub const ERROR_FILENAME_EXCED_RANGE: DWORD = 206;
+pub const ERROR_RING2_STACK_IN_USE: DWORD = 207;
+pub const ERROR_META_EXPANSION_TOO_LONG: DWORD = 208;
+pub const ERROR_INVALID_SIGNAL_NUMBER: DWORD = 209;
+pub const ERROR_THREAD_1_INACTIVE: DWORD = 210;
+pub const ERROR_LOCKED: DWORD = 212;
+pub const ERROR_TOO_MANY_MODULES: DWORD = 214;
+pub const ERROR_NESTING_NOT_ALLOWED: DWORD = 215;
+pub const ERROR_EXE_MACHINE_TYPE_MISMATCH: DWORD = 216;
+pub const ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY: DWORD = 217;
+pub const ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY: DWORD = 218;
+pub const ERROR_FILE_CHECKED_OUT: DWORD = 220;
+pub const ERROR_CHECKOUT_REQUIRED: DWORD = 221;
+pub const ERROR_BAD_FILE_TYPE: DWORD = 222;
+pub const ERROR_FILE_TOO_LARGE: DWORD = 223;
+pub const ERROR_FORMS_AUTH_REQUIRED: DWORD = 224;
+pub const ERROR_PIPE_LOCAL: DWORD = 229;
+pub const ERROR_BAD_PIPE: DWORD = 230;
+pub const ERROR_PIPE_BUSY: DWORD = 231;
+pub const ERROR_NO_DATA: DWORD = 232;
+pub const ERROR_PIPE_NOT_CONNECTED: DWORD = 233;
+pub const ERROR_MORE_DATA: DWORD = 234;
+pub const ERROR_VC_DISCONNECTED: DWORD = 240;
+pub const ERROR_INVALID_EA_NAME: DWORD = 254;
+pub const ERROR_EA_LIST_INCONSISTENT: DWORD = 255;
+pub const ERROR_NO_MORE_ITEMS: DWORD = 259;
+pub const ERROR_CANNOT_COPY: DWORD = 266;
+pub const ERROR_DIRECTORY: DWORD = 267;
+pub const ERROR_EAS_DIDNT_FIT: DWORD = 275;
+pub const ERROR_EA_FILE_CORRUPT: DWORD = 276;
+pub const ERROR_EA_TABLE_FULL: DWORD = 277;
+pub const ERROR_INVALID_EA_HANDLE: DWORD = 278;
+pub const ERROR_EAS_NOT_SUPPORTED: DWORD = 282;
+pub const ERROR_NOT_OWNER: DWORD = 288;
+pub const ERROR_TOO_MANY_POSTS: DWORD = 298;
+pub const ERROR_PARTIAL_COPY: DWORD = 299;
+pub const ERROR_OPLOCK_NOT_GRANTED: DWORD = 300;
+pub const ERROR_INVALID_OPLOCK_PROTOCOL: DWORD = 301;
+pub const ERROR_DISK_TOO_FRAGMENTED: DWORD = 302;
+pub const ERROR_DELETE_PENDING: DWORD = 303;
+pub const ERROR_INVALID_TOKEN: DWORD = 315;
+pub const ERROR_MR_MID_NOT_FOUND: DWORD = 317;
+pub const ERROR_SCOPE_NOT_FOUND: DWORD = 318;
+pub const ERROR_INVALID_ADDRESS: DWORD = 487;
+pub const ERROR_ARITHMETIC_OVERFLOW: DWORD = 534;
+pub const ERROR_PIPE_CONNECTED: DWORD = 535;
+pub const ERROR_PIPE_LISTENING: DWORD = 536;
+pub const ERROR_WAKE_SYSTEM: DWORD = 730;
+pub const ERROR_WAIT_1: DWORD = 731;
+pub const ERROR_WAIT_2: DWORD = 732;
+pub const ERROR_WAIT_3: DWORD = 733;
+pub const ERROR_WAIT_63: DWORD = 734;
+pub const ERROR_ABANDONED_WAIT_0: DWORD = 735;
+pub const ERROR_ABANDONED_WAIT_63: DWORD = 736;
+pub const ERROR_USER_APC: DWORD = 737;
+pub const ERROR_KERNEL_APC: DWORD = 738;
+pub const ERROR_ALERTED: DWORD = 739;
+pub const ERROR_EA_ACCESS_DENIED: DWORD = 994;
+pub const ERROR_OPERATION_ABORTED: DWORD = 995;
+pub const ERROR_IO_INCOMPLETE: DWORD = 996;
+pub const ERROR_IO_PENDING: DWORD = 997;
+pub const ERROR_NOACCESS: DWORD = 998;
+pub const ERROR_SWAPERROR: DWORD = 999;
+pub const ERROR_STACK_OVERFLOW: DWORD = 1001;
+pub const ERROR_INVALID_MESSAGE: DWORD = 1002;
+pub const ERROR_CAN_NOT_COMPLETE: DWORD = 1003;
+pub const ERROR_INVALID_FLAGS: DWORD = 1004;
+pub const ERROR_UNRECOGNIZED_VOLUME: DWORD = 1005;
+pub const ERROR_FILE_INVALID: DWORD = 1006;
+pub const ERROR_FULLSCREEN_MODE: DWORD = 1007;
+pub const ERROR_NO_TOKEN: DWORD = 1008;
+pub const ERROR_BADDB: DWORD = 1009;
+pub const ERROR_BADKEY: DWORD = 1010;
+pub const ERROR_CANTOPEN: DWORD = 1011;
+pub const ERROR_CANTREAD: DWORD = 1012;
+pub const ERROR_CANTWRITE: DWORD = 1013;
+pub const ERROR_REGISTRY_RECOVERED: DWORD = 1014;
+pub const ERROR_REGISTRY_CORRUPT: DWORD = 1015;
+pub const ERROR_REGISTRY_IO_FAILED: DWORD = 1016;
+pub const ERROR_NOT_REGISTRY_FILE: DWORD = 1017;
+pub const ERROR_KEY_DELETED: DWORD = 1018;
+pub const ERROR_NO_LOG_SPACE: DWORD = 1019;
+pub const ERROR_KEY_HAS_CHILDREN: DWORD = 1020;
+pub const ERROR_CHILD_MUST_BE_VOLATILE: DWORD = 1021;
+pub const ERROR_NOTIFY_ENUM_DIR: DWORD = 1022;
+pub const ERROR_DEPENDENT_SERVICES_RUNNING: DWORD = 1051;
+pub const ERROR_INVALID_SERVICE_CONTROL: DWORD = 1052;
+pub const ERROR_SERVICE_REQUEST_TIMEOUT: DWORD = 1053;
+pub const ERROR_SERVICE_NO_THREAD: DWORD = 1054;
+pub const ERROR_SERVICE_DATABASE_LOCKED: DWORD = 1055;
+pub const ERROR_SERVICE_ALREADY_RUNNING: DWORD = 1056;
+pub const ERROR_INVALID_SERVICE_ACCOUNT: DWORD = 1057;
+pub const ERROR_SERVICE_DISABLED: DWORD = 1058;
+pub const ERROR_CIRCULAR_DEPENDENCY: DWORD = 1059;
+pub const ERROR_SERVICE_DOES_NOT_EXIST: DWORD = 1060;
+pub const ERROR_SERVICE_CANNOT_ACCEPT_CTRL: DWORD = 1061;
+pub const ERROR_SERVICE_NOT_ACTIVE: DWORD = 1062;
+pub const ERROR_FAILED_SERVICE_CONTROLLER_CONNECT: DWORD = 1063;
+pub const ERROR_EXCEPTION_IN_SERVICE: DWORD = 1064;
+pub const ERROR_DATABASE_DOES_NOT_EXIST: DWORD = 1065;
+pub const ERROR_SERVICE_SPECIFIC_ERROR: DWORD = 1066;
+pub const ERROR_PROCESS_ABORTED: DWORD = 1067;
+pub const ERROR_SERVICE_DEPENDENCY_FAIL: DWORD = 1068;
+pub const ERROR_SERVICE_LOGON_FAILED: DWORD = 1069;
+pub const ERROR_SERVICE_START_HANG: DWORD = 1070;
+pub const ERROR_INVALID_SERVICE_LOCK: DWORD = 1071;
+pub const ERROR_SERVICE_MARKED_FOR_DELETE: DWORD = 1072;
+pub const ERROR_SERVICE_EXISTS: DWORD = 1073;
+pub const ERROR_ALREADY_RUNNING_LKG: DWORD = 1074;
+pub const ERROR_SERVICE_DEPENDENCY_DELETED: DWORD = 1075;
+pub const ERROR_BOOT_ALREADY_ACCEPTED: DWORD = 1076;
+pub const ERROR_SERVICE_NEVER_STARTED: DWORD = 1077;
+pub const ERROR_DUPLICATE_SERVICE_NAME: DWORD = 1078;
+pub const ERROR_DIFFERENT_SERVICE_ACCOUNT: DWORD = 1079;
+pub const ERROR_CANNOT_DETECT_DRIVER_FAILURE: DWORD = 1080;
+pub const ERROR_CANNOT_DETECT_PROCESS_ABORT: DWORD = 1081;
+pub const ERROR_NO_RECOVERY_PROGRAM: DWORD = 1082;
+pub const ERROR_SERVICE_NOT_IN_EXE: DWORD = 1083;
+pub const ERROR_NOT_SAFEBOOT_SERVICE: DWORD = 1084;
+pub const ERROR_END_OF_MEDIA: DWORD = 1100;
+pub const ERROR_FILEMARK_DETECTED: DWORD = 1101;
+pub const ERROR_BEGINNING_OF_MEDIA: DWORD = 1102;
+pub const ERROR_SETMARK_DETECTED: DWORD = 1103;
+pub const ERROR_NO_DATA_DETECTED: DWORD = 1104;
+pub const ERROR_PARTITION_FAILURE: DWORD = 1105;
+pub const ERROR_INVALID_BLOCK_LENGTH: DWORD = 1106;
+pub const ERROR_DEVICE_NOT_PARTITIONED: DWORD = 1107;
+pub const ERROR_UNABLE_TO_LOCK_MEDIA: DWORD = 1108;
+pub const ERROR_UNABLE_TO_UNLOAD_MEDIA: DWORD = 1109;
+pub const ERROR_MEDIA_CHANGED: DWORD = 1110;
+pub const ERROR_BUS_RESET: DWORD = 1111;
+pub const ERROR_NO_MEDIA_IN_DRIVE: DWORD = 1112;
+pub const ERROR_NO_UNICODE_TRANSLATION: DWORD = 1113;
+pub const ERROR_DLL_INIT_FAILED: DWORD = 1114;
+pub const ERROR_SHUTDOWN_IN_PROGRESS: DWORD = 1115;
+pub const ERROR_NO_SHUTDOWN_IN_PROGRESS: DWORD = 1116;
+pub const ERROR_IO_DEVICE: DWORD = 1117;
+pub const ERROR_SERIAL_NO_DEVICE: DWORD = 1118;
+pub const ERROR_IRQ_BUSY: DWORD = 1119;
+pub const ERROR_MORE_WRITES: DWORD = 1120;
+pub const ERROR_COUNTER_TIMEOUT: DWORD = 1121;
+pub const ERROR_FLOPPY_ID_MARK_NOT_FOUND: DWORD = 1122;
+pub const ERROR_FLOPPY_WRONG_CYLINDER: DWORD = 1123;
+pub const ERROR_FLOPPY_UNKNOWN_ERROR: DWORD = 1124;
+pub const ERROR_FLOPPY_BAD_REGISTERS: DWORD = 1125;
+pub const ERROR_DISK_RECALIBRATE_FAILED: DWORD = 1126;
+pub const ERROR_DISK_OPERATION_FAILED: DWORD = 1127;
+pub const ERROR_DISK_RESET_FAILED: DWORD = 1128;
+pub const ERROR_EOM_OVERFLOW: DWORD = 1129;
+pub const ERROR_NOT_ENOUGH_SERVER_MEMORY: DWORD = 1130;
+pub const ERROR_POSSIBLE_DEADLOCK: DWORD = 1131;
+pub const ERROR_MAPPED_ALIGNMENT: DWORD = 1132;
+pub const ERROR_SET_POWER_STATE_VETOED: DWORD = 1140;
+pub const ERROR_SET_POWER_STATE_FAILED: DWORD = 1141;
+pub const ERROR_TOO_MANY_LINKS: DWORD = 1142;
+pub const ERROR_OLD_WIN_VERSION: DWORD = 1150;
+pub const ERROR_APP_WRONG_OS: DWORD = 1151;
+pub const ERROR_SINGLE_INSTANCE_APP: DWORD = 1152;
+pub const ERROR_RMODE_APP: DWORD = 1153;
+pub const ERROR_INVALID_DLL: DWORD = 1154;
+pub const ERROR_NO_ASSOCIATION: DWORD = 1155;
+pub const ERROR_DDE_FAIL: DWORD = 1156;
+pub const ERROR_DLL_NOT_FOUND: DWORD = 1157;
+pub const ERROR_NO_MORE_USER_HANDLES: DWORD = 1158;
+pub const ERROR_MESSAGE_SYNC_ONLY: DWORD = 1159;
+pub const ERROR_SOURCE_ELEMENT_EMPTY: DWORD = 1160;
+pub const ERROR_DESTINATION_ELEMENT_FULL: DWORD = 1161;
+pub const ERROR_ILLEGAL_ELEMENT_ADDRESS: DWORD = 1162;
+pub const ERROR_MAGAZINE_NOT_PRESENT: DWORD = 1163;
+pub const ERROR_DEVICE_REINITIALIZATION_NEEDED: DWORD = 1164;
+pub const ERROR_DEVICE_REQUIRES_CLEANING: DWORD = 1165;
+pub const ERROR_DEVICE_DOOR_OPEN: DWORD = 1166;
+pub const ERROR_DEVICE_NOT_CONNECTED: DWORD = 1167;
+pub const ERROR_NOT_FOUND: DWORD = 1168;
+pub const ERROR_NO_MATCH: DWORD = 1169;
+pub const ERROR_SET_NOT_FOUND: DWORD = 1170;
+pub const ERROR_POINT_NOT_FOUND: DWORD = 1171;
+pub const ERROR_NO_TRACKING_SERVICE: DWORD = 1172;
+pub const ERROR_NO_VOLUME_ID: DWORD = 1173;
+pub const ERROR_UNABLE_TO_REMOVE_REPLACED: DWORD = 1175;
+pub const ERROR_UNABLE_TO_MOVE_REPLACEMENT: DWORD = 1176;
+pub const ERROR_UNABLE_TO_MOVE_REPLACEMENT_2: DWORD = 1177;
+pub const ERROR_JOURNAL_DELETE_IN_PROGRESS: DWORD = 1178;
+pub const ERROR_JOURNAL_NOT_ACTIVE: DWORD = 1179;
+pub const ERROR_POTENTIAL_FILE_FOUND: DWORD = 1180;
+pub const ERROR_JOURNAL_ENTRY_DELETED: DWORD = 1181;
+pub const ERROR_BAD_DEVICE: DWORD = 1200;
+pub const ERROR_CONNECTION_UNAVAIL: DWORD = 1201;
+pub const ERROR_DEVICE_ALREADY_REMEMBERED: DWORD = 1202;
+pub const ERROR_NO_NET_OR_BAD_PATH: DWORD = 1203;
+pub const ERROR_BAD_PROVIDER: DWORD = 1204;
+pub const ERROR_CANNOT_OPEN_PROFILE: DWORD = 1205;
+pub const ERROR_BAD_PROFILE: DWORD = 1206;
+pub const ERROR_NOT_CONTAINER: DWORD = 1207;
+pub const ERROR_EXTENDED_ERROR: DWORD = 1208;
+pub const ERROR_INVALID_GROUPNAME: DWORD = 1209;
+pub const ERROR_INVALID_COMPUTERNAME: DWORD = 1210;
+pub const ERROR_INVALID_EVENTNAME: DWORD = 1211;
+pub const ERROR_INVALID_DOMAINNAME: DWORD = 1212;
+pub const ERROR_INVALID_SERVICENAME: DWORD = 1213;
+pub const ERROR_INVALID_NETNAME: DWORD = 1214;
+pub const ERROR_INVALID_SHARENAME: DWORD = 1215;
+pub const ERROR_INVALID_PASSWORDNAME: DWORD = 1216;
+pub const ERROR_INVALID_MESSAGENAME: DWORD = 1217;
+pub const ERROR_INVALID_MESSAGEDEST: DWORD = 1218;
+pub const ERROR_SESSION_CREDENTIAL_CONFLICT: DWORD = 1219;
+pub const ERROR_REMOTE_SESSION_LIMIT_EXCEEDED: DWORD = 1220;
+pub const ERROR_DUP_DOMAINNAME: DWORD = 1221;
+pub const ERROR_NO_NETWORK: DWORD = 1222;
+pub const ERROR_CANCELLED: DWORD = 1223;
+pub const ERROR_USER_MAPPED_FILE: DWORD = 1224;
+pub const ERROR_CONNECTION_REFUSED: DWORD = 1225;
+pub const ERROR_GRACEFUL_DISCONNECT: DWORD = 1226;
+pub const ERROR_ADDRESS_ALREADY_ASSOCIATED: DWORD = 1227;
+pub const ERROR_ADDRESS_NOT_ASSOCIATED: DWORD = 1228;
+pub const ERROR_CONNECTION_INVALID: DWORD = 1229;
+pub const ERROR_CONNECTION_ACTIVE: DWORD = 1230;
+pub const ERROR_NETWORK_UNREACHABLE: DWORD = 1231;
+pub const ERROR_HOST_UNREACHABLE: DWORD = 1232;
+pub const ERROR_PROTOCOL_UNREACHABLE: DWORD = 1233;
+pub const ERROR_PORT_UNREACHABLE: DWORD = 1234;
+pub const ERROR_REQUEST_ABORTED: DWORD = 1235;
+pub const ERROR_CONNECTION_ABORTED: DWORD = 1236;
+pub const ERROR_RETRY: DWORD = 1237;
+pub const ERROR_CONNECTION_COUNT_LIMIT: DWORD = 1238;
+pub const ERROR_LOGIN_TIME_RESTRICTION: DWORD = 1239;
+pub const ERROR_LOGIN_WKSTA_RESTRICTION: DWORD = 1240;
+pub const ERROR_INCORRECT_ADDRESS: DWORD = 1241;
+pub const ERROR_ALREADY_REGISTERED: DWORD = 1242;
+pub const ERROR_SERVICE_NOT_FOUND: DWORD = 1243;
+pub const ERROR_NOT_AUTHENTICATED: DWORD = 1244;
+pub const ERROR_NOT_LOGGED_ON: DWORD = 1245;
+pub const ERROR_CONTINUE: DWORD = 1246;
+pub const ERROR_ALREADY_INITIALIZED: DWORD = 1247;
+pub const ERROR_NO_MORE_DEVICES: DWORD = 1248;
+pub const ERROR_NO_SUCH_SITE: DWORD = 1249;
+pub const ERROR_DOMAIN_CONTROLLER_EXISTS: DWORD = 1250;
+pub const ERROR_ONLY_IF_CONNECTED: DWORD = 1251;
+pub const ERROR_OVERRIDE_NOCHANGES: DWORD = 1252;
+pub const ERROR_BAD_USER_PROFILE: DWORD = 1253;
+pub const ERROR_NOT_SUPPORTED_ON_SBS: DWORD = 1254;
+pub const ERROR_SERVER_SHUTDOWN_IN_PROGRESS: DWORD = 1255;
+pub const ERROR_HOST_DOWN: DWORD = 1256;
+pub const ERROR_NON_ACCOUNT_SID: DWORD = 1257;
+pub const ERROR_NON_DOMAIN_SID: DWORD = 1258;
+pub const ERROR_APPHELP_BLOCK: DWORD = 1259;
+pub const ERROR_ACCESS_DISABLED_BY_POLICY: DWORD = 1260;
+pub const ERROR_REG_NAT_CONSUMPTION: DWORD = 1261;
+pub const ERROR_CSCSHARE_OFFLINE: DWORD = 1262;
+pub const ERROR_PKINIT_FAILURE: DWORD = 1263;
+pub const ERROR_SMARTCARD_SUBSYSTEM_FAILURE: DWORD = 1264;
+pub const ERROR_DOWNGRADE_DETECTED: DWORD = 1265;
+pub const ERROR_MACHINE_LOCKED: DWORD = 1271;
+pub const ERROR_CALLBACK_SUPPLIED_INVALID_DATA: DWORD = 1273;
+pub const ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED: DWORD = 1274;
+pub const ERROR_DRIVER_BLOCKED: DWORD = 1275;
+pub const ERROR_INVALID_IMPORT_OF_NON_DLL: DWORD = 1276;
+pub const ERROR_ACCESS_DISABLED_WEBBLADE: DWORD = 1277;
+pub const ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER: DWORD = 1278;
+pub const ERROR_RECOVERY_FAILURE: DWORD = 1279;
+pub const ERROR_ALREADY_FIBER: DWORD = 1280;
+pub const ERROR_ALREADY_THREAD: DWORD = 1281;
+pub const ERROR_STACK_BUFFER_OVERRUN: DWORD = 1282;
+pub const ERROR_PARAMETER_QUOTA_EXCEEDED: DWORD = 1283;
+pub const ERROR_DEBUGGER_INACTIVE: DWORD = 1284;
+pub const ERROR_DELAY_LOAD_FAILED: DWORD = 1285;
+pub const ERROR_VDM_DISALLOWED: DWORD = 1286;
+pub const ERROR_UNIDENTIFIED_ERROR: DWORD = 1287;
+pub const ERROR_NOT_ALL_ASSIGNED: DWORD = 1300;
+pub const ERROR_SOME_NOT_MAPPED: DWORD = 1301;
+pub const ERROR_NO_QUOTAS_FOR_ACCOUNT: DWORD = 1302;
+pub const ERROR_LOCAL_USER_SESSION_KEY: DWORD = 1303;
+pub const ERROR_NULL_LM_PASSWORD: DWORD = 1304;
+pub const ERROR_UNKNOWN_REVISION: DWORD = 1305;
+pub const ERROR_REVISION_MISMATCH: DWORD = 1306;
+pub const ERROR_INVALID_OWNER: DWORD = 1307;
+pub const ERROR_INVALID_PRIMARY_GROUP: DWORD = 1308;
+pub const ERROR_NO_IMPERSONATION_TOKEN: DWORD = 1309;
+pub const ERROR_CANT_DISABLE_MANDATORY: DWORD = 1310;
+pub const ERROR_NO_LOGON_SERVERS: DWORD = 1311;
+pub const ERROR_NO_SUCH_LOGON_SESSION: DWORD = 1312;
+pub const ERROR_NO_SUCH_PRIVILEGE: DWORD = 1313;
+pub const ERROR_PRIVILEGE_NOT_HELD: DWORD = 1314;
+pub const ERROR_INVALID_ACCOUNT_NAME: DWORD = 1315;
+pub const ERROR_USER_EXISTS: DWORD = 1316;
+pub const ERROR_NO_SUCH_USER: DWORD = 1317;
+pub const ERROR_GROUP_EXISTS: DWORD = 1318;
+pub const ERROR_NO_SUCH_GROUP: DWORD = 1319;
+pub const ERROR_MEMBER_IN_GROUP: DWORD = 1320;
+pub const ERROR_MEMBER_NOT_IN_GROUP: DWORD = 1321;
+pub const ERROR_LAST_ADMIN: DWORD = 1322;
+pub const ERROR_WRONG_PASSWORD: DWORD = 1323;
+pub const ERROR_ILL_FORMED_PASSWORD: DWORD = 1324;
+pub const ERROR_PASSWORD_RESTRICTION: DWORD = 1325;
+pub const ERROR_LOGON_FAILURE: DWORD = 1326;
+pub const ERROR_ACCOUNT_RESTRICTION: DWORD = 1327;
+pub const ERROR_INVALID_LOGON_HOURS: DWORD = 1328;
+pub const ERROR_INVALID_WORKSTATION: DWORD = 1329;
+pub const ERROR_PASSWORD_EXPIRED: DWORD = 1330;
+pub const ERROR_ACCOUNT_DISABLED: DWORD = 1331;
+pub const ERROR_NONE_MAPPED: DWORD = 1332;
+pub const ERROR_TOO_MANY_LUIDS_REQUESTED: DWORD = 1333;
+pub const ERROR_LUIDS_EXHAUSTED: DWORD = 1334;
+pub const ERROR_INVALID_SUB_AUTHORITY: DWORD = 1335;
+pub const ERROR_INVALID_ACL: DWORD = 1336;
+pub const ERROR_INVALID_SID: DWORD = 1337;
+pub const ERROR_INVALID_SECURITY_DESCR: DWORD = 1338;
+pub const ERROR_BAD_INHERITANCE_ACL: DWORD = 1340;
+pub const ERROR_SERVER_DISABLED: DWORD = 1341;
+pub const ERROR_SERVER_NOT_DISABLED: DWORD = 1342;
+pub const ERROR_INVALID_ID_AUTHORITY: DWORD = 1343;
+pub const ERROR_ALLOTTED_SPACE_EXCEEDED: DWORD = 1344;
+pub const ERROR_INVALID_GROUP_ATTRIBUTES: DWORD = 1345;
+pub const ERROR_BAD_IMPERSONATION_LEVEL: DWORD = 1346;
+pub const ERROR_CANT_OPEN_ANONYMOUS: DWORD = 1347;
+pub const ERROR_BAD_VALIDATION_CLASS: DWORD = 1348;
+pub const ERROR_BAD_TOKEN_TYPE: DWORD = 1349;
+pub const ERROR_NO_SECURITY_ON_OBJECT: DWORD = 1350;
+pub const ERROR_CANT_ACCESS_DOMAIN_INFO: DWORD = 1351;
+pub const ERROR_INVALID_SERVER_STATE: DWORD = 1352;
+pub const ERROR_INVALID_DOMAIN_STATE: DWORD = 1353;
+pub const ERROR_INVALID_DOMAIN_ROLE: DWORD = 1354;
+pub const ERROR_NO_SUCH_DOMAIN: DWORD = 1355;
+pub const ERROR_DOMAIN_EXISTS: DWORD = 1356;
+pub const ERROR_DOMAIN_LIMIT_EXCEEDED: DWORD = 1357;
+pub const ERROR_INTERNAL_DB_CORRUPTION: DWORD = 1358;
+pub const ERROR_INTERNAL_ERROR: DWORD = 1359;
+pub const ERROR_GENERIC_NOT_MAPPED: DWORD = 1360;
+pub const ERROR_BAD_DESCRIPTOR_FORMAT: DWORD = 1361;
+pub const ERROR_NOT_LOGON_PROCESS: DWORD = 1362;
+pub const ERROR_LOGON_SESSION_EXISTS: DWORD = 1363;
+pub const ERROR_NO_SUCH_PACKAGE: DWORD = 1364;
+pub const ERROR_BAD_LOGON_SESSION_STATE: DWORD = 1365;
+pub const ERROR_LOGON_SESSION_COLLISION: DWORD = 1366;
+pub const ERROR_INVALID_LOGON_TYPE: DWORD = 1367;
+pub const ERROR_CANNOT_IMPERSONATE: DWORD = 1368;
+pub const ERROR_RXACT_INVALID_STATE: DWORD = 1369;
+pub const ERROR_RXACT_COMMIT_FAILURE: DWORD = 1370;
+pub const ERROR_SPECIAL_ACCOUNT: DWORD = 1371;
+pub const ERROR_SPECIAL_GROUP: DWORD = 1372;
+pub const ERROR_SPECIAL_USER: DWORD = 1373;
+pub const ERROR_MEMBERS_PRIMARY_GROUP: DWORD = 1374;
+pub const ERROR_TOKEN_ALREADY_IN_USE: DWORD = 1375;
+pub const ERROR_NO_SUCH_ALIAS: DWORD = 1376;
+pub const ERROR_MEMBER_NOT_IN_ALIAS: DWORD = 1377;
+pub const ERROR_MEMBER_IN_ALIAS: DWORD = 1378;
+pub const ERROR_ALIAS_EXISTS: DWORD = 1379;
+pub const ERROR_LOGON_NOT_GRANTED: DWORD = 1380;
+pub const ERROR_TOO_MANY_SECRETS: DWORD = 1381;
+pub const ERROR_SECRET_TOO_LONG: DWORD = 1382;
+pub const ERROR_INTERNAL_DB_ERROR: DWORD = 1383;
+pub const ERROR_TOO_MANY_CONTEXT_IDS: DWORD = 1384;
+pub const ERROR_LOGON_TYPE_NOT_GRANTED: DWORD = 1385;
+pub const ERROR_NT_CROSS_ENCRYPTION_REQUIRED: DWORD = 1386;
+pub const ERROR_NO_SUCH_MEMBER: DWORD = 1387;
+pub const ERROR_INVALID_MEMBER: DWORD = 1388;
+pub const ERROR_TOO_MANY_SIDS: DWORD = 1389;
+pub const ERROR_LM_CROSS_ENCRYPTION_REQUIRED: DWORD = 1390;
+pub const ERROR_NO_INHERITANCE: DWORD = 1391;
+pub const ERROR_FILE_CORRUPT: DWORD = 1392;
+pub const ERROR_DISK_CORRUPT: DWORD = 1393;
+pub const ERROR_NO_USER_SESSION_KEY: DWORD = 1394;
+pub const ERROR_LICENSE_QUOTA_EXCEEDED: DWORD = 1395;
+pub const ERROR_WRONG_TARGET_NAME: DWORD = 1396;
+pub const ERROR_MUTUAL_AUTH_FAILED: DWORD = 1397;
+pub const ERROR_TIME_SKEW: DWORD = 1398;
+pub const ERROR_CURRENT_DOMAIN_NOT_ALLOWED: DWORD = 1399;
+pub const ERROR_INVALID_WINDOW_HANDLE: DWORD = 1400;
+pub const ERROR_INVALID_MENU_HANDLE: DWORD = 1401;
+pub const ERROR_INVALID_CURSOR_HANDLE: DWORD = 1402;
+pub const ERROR_INVALID_ACCEL_HANDLE: DWORD = 1403;
+pub const ERROR_INVALID_HOOK_HANDLE: DWORD = 1404;
+pub const ERROR_INVALID_DWP_HANDLE: DWORD = 1405;
+pub const ERROR_TLW_WITH_WSCHILD: DWORD = 1406;
+pub const ERROR_CANNOT_FIND_WND_CLASS: DWORD = 1407;
+pub const ERROR_WINDOW_OF_OTHER_THREAD: DWORD = 1408;
+pub const ERROR_HOTKEY_ALREADY_REGISTERED: DWORD = 1409;
+pub const ERROR_CLASS_ALREADY_EXISTS: DWORD = 1410;
+pub const ERROR_CLASS_DOES_NOT_EXIST: DWORD = 1411;
+pub const ERROR_CLASS_HAS_WINDOWS: DWORD = 1412;
+pub const ERROR_INVALID_INDEX: DWORD = 1413;
+pub const ERROR_INVALID_ICON_HANDLE: DWORD = 1414;
+pub const ERROR_PRIVATE_DIALOG_INDEX: DWORD = 1415;
+pub const ERROR_LISTBOX_ID_NOT_FOUND: DWORD = 1416;
+pub const ERROR_NO_WILDCARD_CHARACTERS: DWORD = 1417;
+pub const ERROR_CLIPBOARD_NOT_OPEN: DWORD = 1418;
+pub const ERROR_HOTKEY_NOT_REGISTERED: DWORD = 1419;
+pub const ERROR_WINDOW_NOT_DIALOG: DWORD = 1420;
+pub const ERROR_CONTROL_ID_NOT_FOUND: DWORD = 1421;
+pub const ERROR_INVALID_COMBOBOX_MESSAGE: DWORD = 1422;
+pub const ERROR_WINDOW_NOT_COMBOBOX: DWORD = 1423;
+pub const ERROR_INVALID_EDIT_HEIGHT: DWORD = 1424;
+pub const ERROR_DC_NOT_FOUND: DWORD = 1425;
+pub const ERROR_INVALID_HOOK_FILTER: DWORD = 1426;
+pub const ERROR_INVALID_FILTER_PROC: DWORD = 1427;
+pub const ERROR_HOOK_NEEDS_HMOD: DWORD = 1428;
+pub const ERROR_GLOBAL_ONLY_HOOK: DWORD = 1429;
+pub const ERROR_JOURNAL_HOOK_SET: DWORD = 1430;
+pub const ERROR_HOOK_NOT_INSTALLED: DWORD = 1431;
+pub const ERROR_INVALID_LB_MESSAGE: DWORD = 1432;
+pub const ERROR_SETCOUNT_ON_BAD_LB: DWORD = 1433;
+pub const ERROR_LB_WITHOUT_TABSTOPS: DWORD = 1434;
+pub const ERROR_DESTROY_OBJECT_OF_OTHER_THREAD: DWORD = 1435;
+pub const ERROR_CHILD_WINDOW_MENU: DWORD = 1436;
+pub const ERROR_NO_SYSTEM_MENU: DWORD = 1437;
+pub const ERROR_INVALID_MSGBOX_STYLE: DWORD = 1438;
+pub const ERROR_INVALID_SPI_VALUE: DWORD = 1439;
+pub const ERROR_SCREEN_ALREADY_LOCKED: DWORD = 1440;
+pub const ERROR_HWNDS_HAVE_DIFF_PARENT: DWORD = 1441;
+pub const ERROR_NOT_CHILD_WINDOW: DWORD = 1442;
+pub const ERROR_INVALID_GW_COMMAND: DWORD = 1443;
+pub const ERROR_INVALID_THREAD_ID: DWORD = 1444;
+pub const ERROR_NON_MDICHILD_WINDOW: DWORD = 1445;
+pub const ERROR_POPUP_ALREADY_ACTIVE: DWORD = 1446;
+pub const ERROR_NO_SCROLLBARS: DWORD = 1447;
+pub const ERROR_INVALID_SCROLLBAR_RANGE: DWORD = 1448;
+pub const ERROR_INVALID_SHOWWIN_COMMAND: DWORD = 1449;
+pub const ERROR_NO_SYSTEM_RESOURCES: DWORD = 1450;
+pub const ERROR_NONPAGED_SYSTEM_RESOURCES: DWORD = 1451;
+pub const ERROR_PAGED_SYSTEM_RESOURCES: DWORD = 1452;
+pub const ERROR_WORKING_SET_QUOTA: DWORD = 1453;
+pub const ERROR_PAGEFILE_QUOTA: DWORD = 1454;
+pub const ERROR_COMMITMENT_LIMIT: DWORD = 1455;
+pub const ERROR_MENU_ITEM_NOT_FOUND: DWORD = 1456;
+pub const ERROR_INVALID_KEYBOARD_HANDLE: DWORD = 1457;
+pub const ERROR_HOOK_TYPE_NOT_ALLOWED: DWORD = 1458;
+pub const ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION: DWORD = 1459;
+pub const ERROR_TIMEOUT: DWORD = 1460;
+pub const ERROR_INVALID_MONITOR_HANDLE: DWORD = 1461;
+pub const ERROR_INCORRECT_SIZE: DWORD = 1462;
+pub const ERROR_SYMLINK_CLASS_DISABLED: DWORD = 1463;
+pub const ERROR_SYMLINK_NOT_SUPPORTED: DWORD = 1464;
+pub const ERROR_XML_PARSE_ERROR: DWORD = 1465;
+pub const ERROR_XMLDSIG_ERROR: DWORD = 1466;
+pub const ERROR_RESTART_APPLICATION: DWORD = 1467;
+pub const ERROR_WRONG_COMPARTMENT: DWORD = 1468;
+pub const ERROR_AUTHIP_FAILURE: DWORD = 1469;
+pub const ERROR_NO_NVRAM_RESOURCES: DWORD = 1470;
+pub const ERROR_NOT_GUI_PROCESS: DWORD = 1471;
+pub const ERROR_EVENTLOG_FILE_CORRUPT: DWORD = 1500;
+pub const ERROR_EVENTLOG_CANT_START: DWORD = 1501;
+pub const ERROR_LOG_FILE_FULL: DWORD = 1502;
+pub const ERROR_EVENTLOG_FILE_CHANGED: DWORD = 1503;
+pub const ERROR_INSTALL_SERVICE_FAILURE: DWORD = 1601;
+pub const ERROR_INSTALL_USEREXIT: DWORD = 1602;
+pub const ERROR_INSTALL_FAILURE: DWORD = 1603;
+pub const ERROR_INSTALL_SUSPEND: DWORD = 1604;
+pub const ERROR_UNKNOWN_PRODUCT: DWORD = 1605;
+pub const ERROR_UNKNOWN_FEATURE: DWORD = 1606;
+pub const ERROR_UNKNOWN_COMPONENT: DWORD = 1607;
+pub const ERROR_UNKNOWN_PROPERTY: DWORD = 1608;
+pub const ERROR_INVALID_HANDLE_STATE: DWORD = 1609;
+pub const ERROR_BAD_CONFIGURATION: DWORD = 1610;
+pub const ERROR_INDEX_ABSENT: DWORD = 1611;
+pub const ERROR_INSTALL_SOURCE_ABSENT: DWORD = 1612;
+pub const ERROR_INSTALL_PACKAGE_VERSION: DWORD = 1613;
+pub const ERROR_PRODUCT_UNINSTALLED: DWORD = 1614;
+pub const ERROR_BAD_QUERY_SYNTAX: DWORD = 1615;
+pub const ERROR_INVALID_FIELD: DWORD = 1616;
+pub const ERROR_DEVICE_REMOVED: DWORD = 1617;
+pub const ERROR_INSTALL_ALREADY_RUNNING: DWORD = 1618;
+pub const ERROR_INSTALL_PACKAGE_OPEN_FAILED: DWORD = 1619;
+pub const ERROR_INSTALL_PACKAGE_INVALID: DWORD = 1620;
+pub const ERROR_INSTALL_UI_FAILURE: DWORD = 1621;
+pub const ERROR_INSTALL_LOG_FAILURE: DWORD = 1622;
+pub const ERROR_INSTALL_LANGUAGE_UNSUPPORTED: DWORD = 1623;
+pub const ERROR_INSTALL_TRANSFORM_FAILURE: DWORD = 1624;
+pub const ERROR_INSTALL_PACKAGE_REJECTED: DWORD = 1625;
+pub const ERROR_FUNCTION_NOT_CALLED: DWORD = 1626;
+pub const ERROR_FUNCTION_FAILED: DWORD = 1627;
+pub const ERROR_INVALID_TABLE: DWORD = 1628;
+pub const ERROR_DATATYPE_MISMATCH: DWORD = 1629;
+pub const ERROR_UNSUPPORTED_TYPE: DWORD = 1630;
+pub const ERROR_CREATE_FAILED: DWORD = 1631;
+pub const ERROR_INSTALL_TEMP_UNWRITABLE: DWORD = 1632;
+pub const ERROR_INSTALL_PLATFORM_UNSUPPORTED: DWORD = 1633;
+pub const ERROR_INSTALL_NOTUSED: DWORD = 1634;
+pub const ERROR_PATCH_PACKAGE_OPEN_FAILED: DWORD = 1635;
+pub const ERROR_PATCH_PACKAGE_INVALID: DWORD = 1636;
+pub const ERROR_PATCH_PACKAGE_UNSUPPORTED: DWORD = 1637;
+pub const ERROR_PRODUCT_VERSION: DWORD = 1638;
+pub const ERROR_INVALID_COMMAND_LINE: DWORD = 1639;
+pub const ERROR_INSTALL_REMOTE_DISALLOWED: DWORD = 1640;
+pub const ERROR_SUCCESS_REBOOT_INITIATED: DWORD = 1641;
+pub const ERROR_PATCH_TARGET_NOT_FOUND: DWORD = 1642;
+pub const ERROR_PATCH_PACKAGE_REJECTED: DWORD = 1643;
+pub const ERROR_INSTALL_TRANSFORM_REJECTED: DWORD = 1644;
+pub const ERROR_INSTALL_REMOTE_PROHIBITED: DWORD = 1645;
+pub const ERROR_INVALID_USER_BUFFER: DWORD = 1784;
+pub const ERROR_UNRECOGNIZED_MEDIA: DWORD = 1785;
+pub const ERROR_NO_TRUST_LSA_SECRET: DWORD = 1786;
+pub const ERROR_NO_TRUST_SAM_ACCOUNT: DWORD = 1787;
+pub const ERROR_TRUSTED_DOMAIN_FAILURE: DWORD = 1788;
+pub const ERROR_TRUSTED_RELATIONSHIP_FAILURE: DWORD = 1789;
+pub const ERROR_TRUST_FAILURE: DWORD = 1790;
+pub const ERROR_NETLOGON_NOT_STARTED: DWORD = 1792;
+pub const ERROR_ACCOUNT_EXPIRED: DWORD = 1793;
+pub const ERROR_REDIRECTOR_HAS_OPEN_HANDLES: DWORD = 1794;
+pub const ERROR_PRINTER_DRIVER_ALREADY_INSTALLED: DWORD = 1795;
+pub const ERROR_UNKNOWN_PORT: DWORD = 1796;
+pub const ERROR_UNKNOWN_PRINTER_DRIVER: DWORD = 1797;
+pub const ERROR_UNKNOWN_PRINTPROCESSOR: DWORD = 1798;
+pub const ERROR_INVALID_SEPARATOR_FILE: DWORD = 1799;
+pub const ERROR_INVALID_PRIORITY: DWORD = 1800;
+pub const ERROR_INVALID_PRINTER_NAME: DWORD = 1801;
+pub const ERROR_PRINTER_ALREADY_EXISTS: DWORD = 1802;
+pub const ERROR_INVALID_PRINTER_COMMAND: DWORD = 1803;
+pub const ERROR_INVALID_DATATYPE: DWORD = 1804;
+pub const ERROR_INVALID_ENVIRONMENT: DWORD = 1805;
+pub const ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT: DWORD = 1807;
+pub const ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT: DWORD = 1808;
+pub const ERROR_NOLOGON_SERVER_TRUST_ACCOUNT: DWORD = 1809;
+pub const ERROR_DOMAIN_TRUST_INCONSISTENT: DWORD = 1810;
+pub const ERROR_SERVER_HAS_OPEN_HANDLES: DWORD = 1811;
+pub const ERROR_RESOURCE_DATA_NOT_FOUND: DWORD = 1812;
+pub const ERROR_RESOURCE_TYPE_NOT_FOUND: DWORD = 1813;
+pub const ERROR_RESOURCE_NAME_NOT_FOUND: DWORD = 1814;
+pub const ERROR_RESOURCE_LANG_NOT_FOUND: DWORD = 1815;
+pub const ERROR_NOT_ENOUGH_QUOTA: DWORD = 1816;
+pub const ERROR_INVALID_TIME: DWORD = 1901;
+pub const ERROR_INVALID_FORM_NAME: DWORD = 1902;
+pub const ERROR_INVALID_FORM_SIZE: DWORD = 1903;
+pub const ERROR_ALREADY_WAITING: DWORD = 1904;
+pub const ERROR_PRINTER_DELETED: DWORD = 1905;
+pub const ERROR_INVALID_PRINTER_STATE: DWORD = 1906;
+pub const ERROR_PASSWORD_MUST_CHANGE: DWORD = 1907;
+pub const ERROR_DOMAIN_CONTROLLER_NOT_FOUND: DWORD = 1908;
+pub const ERROR_ACCOUNT_LOCKED_OUT: DWORD = 1909;
+pub const ERROR_NO_SITENAME: DWORD = 1919;
+pub const ERROR_CANT_ACCESS_FILE: DWORD = 1920;
+pub const ERROR_CANT_RESOLVE_FILENAME: DWORD = 1921;
+pub const ERROR_KM_DRIVER_BLOCKED: DWORD = 1930;
+pub const ERROR_CONTEXT_EXPIRED: DWORD = 1931;
+pub const ERROR_PER_USER_TRUST_QUOTA_EXCEEDED: DWORD = 1932;
+pub const ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED: DWORD = 1933;
+pub const ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED: DWORD = 1934;
+pub const ERROR_AUTHENTICATION_FIREWALL_FAILED: DWORD = 1935;
+pub const ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED: DWORD = 1936;
+pub const ERROR_INVALID_PIXEL_FORMAT: DWORD = 2000;
+pub const ERROR_BAD_DRIVER: DWORD = 2001;
+pub const ERROR_INVALID_WINDOW_STYLE: DWORD = 2002;
+pub const ERROR_METAFILE_NOT_SUPPORTED: DWORD = 2003;
+pub const ERROR_TRANSFORM_NOT_SUPPORTED: DWORD = 2004;
+pub const ERROR_CLIPPING_NOT_SUPPORTED: DWORD = 2005;
+pub const ERROR_INVALID_CMM: DWORD = 2010;
+pub const ERROR_INVALID_PROFILE: DWORD = 2011;
+pub const ERROR_TAG_NOT_FOUND: DWORD = 2012;
+pub const ERROR_TAG_NOT_PRESENT: DWORD = 2013;
+pub const ERROR_DUPLICATE_TAG: DWORD = 2014;
+pub const ERROR_PROFILE_NOT_ASSOCIATED_WITH_DEVICE: DWORD = 2015;
+pub const ERROR_PROFILE_NOT_FOUND: DWORD = 2016;
+pub const ERROR_INVALID_COLORSPACE: DWORD = 2017;
+pub const ERROR_ICM_NOT_ENABLED: DWORD = 2018;
+pub const ERROR_DELETING_ICM_XFORM: DWORD = 2019;
+pub const ERROR_INVALID_TRANSFORM: DWORD = 2020;
+pub const ERROR_COLORSPACE_MISMATCH: DWORD = 2021;
+pub const ERROR_INVALID_COLORINDEX: DWORD = 2022;
+pub const ERROR_CONNECTED_OTHER_PASSWORD: DWORD = 2108;
+pub const ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT: DWORD = 2109;
+pub const ERROR_BAD_USERNAME: DWORD = 2202;
+pub const ERROR_NOT_CONNECTED: DWORD = 2250;
+pub const ERROR_OPEN_FILES: DWORD = 2401;
+pub const ERROR_ACTIVE_CONNECTIONS: DWORD = 2402;
+pub const ERROR_DEVICE_IN_USE: DWORD = 2404;
+pub const ERROR_UNKNOWN_PRINT_MONITOR: DWORD = 3000;
+pub const ERROR_PRINTER_DRIVER_IN_USE: DWORD = 3001;
+pub const ERROR_SPOOL_FILE_NOT_FOUND: DWORD = 3002;
+pub const ERROR_SPL_NO_STARTDOC: DWORD = 3003;
+pub const ERROR_SPL_NO_ADDJOB: DWORD = 3004;
+pub const ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED: DWORD = 3005;
+pub const ERROR_PRINT_MONITOR_ALREADY_INSTALLED: DWORD = 3006;
+pub const ERROR_INVALID_PRINT_MONITOR: DWORD = 3007;
+pub const ERROR_PRINT_MONITOR_IN_USE: DWORD = 3008;
+pub const ERROR_PRINTER_HAS_JOBS_QUEUED: DWORD = 3009;
+pub const ERROR_SUCCESS_REBOOT_REQUIRED: DWORD = 3010;
+pub const ERROR_SUCCESS_RESTART_REQUIRED: DWORD = 3011;
+pub const ERROR_PRINTER_NOT_FOUND: DWORD = 3012;
+pub const ERROR_PRINTER_DRIVER_WARNED: DWORD = 3013;
+pub const ERROR_PRINTER_DRIVER_BLOCKED: DWORD = 3014;
+pub const ERROR_WINS_INTERNAL: DWORD = 4000;
+pub const ERROR_CAN_NOT_DEL_LOCAL_WINS: DWORD = 4001;
+pub const ERROR_STATIC_INIT: DWORD = 4002;
+pub const ERROR_INC_BACKUP: DWORD = 4003;
+pub const ERROR_FULL_BACKUP: DWORD = 4004;
+pub const ERROR_REC_NON_EXISTENT: DWORD = 4005;
+pub const ERROR_RPL_NOT_ALLOWED: DWORD = 4006;
+pub const ERROR_DHCP_ADDRESS_CONFLICT: DWORD = 4100;
+pub const ERROR_WMI_GUID_NOT_FOUND: DWORD = 4200;
+pub const ERROR_WMI_INSTANCE_NOT_FOUND: DWORD = 4201;
+pub const ERROR_WMI_ITEMID_NOT_FOUND: DWORD = 4202;
+pub const ERROR_WMI_TRY_AGAIN: DWORD = 4203;
+pub const ERROR_WMI_DP_NOT_FOUND: DWORD = 4204;
+pub const ERROR_WMI_UNRESOLVED_INSTANCE_REF: DWORD = 4205;
+pub const ERROR_WMI_ALREADY_ENABLED: DWORD = 4206;
+pub const ERROR_WMI_GUID_DISCONNECTED: DWORD = 4207;
+pub const ERROR_WMI_SERVER_UNAVAILABLE: DWORD = 4208;
+pub const ERROR_WMI_DP_FAILED: DWORD = 4209;
+pub const ERROR_WMI_INVALID_MOF: DWORD = 4210;
+pub const ERROR_WMI_INVALID_REGINFO: DWORD = 4211;
+pub const ERROR_WMI_ALREADY_DISABLED: DWORD = 4212;
+pub const ERROR_WMI_READ_ONLY: DWORD = 4213;
+pub const ERROR_WMI_SET_FAILURE: DWORD = 4214;
+pub const ERROR_INVALID_MEDIA: DWORD = 4300;
+pub const ERROR_INVALID_LIBRARY: DWORD = 4301;
+pub const ERROR_INVALID_MEDIA_POOL: DWORD = 4302;
+pub const ERROR_DRIVE_MEDIA_MISMATCH: DWORD = 4303;
+pub const ERROR_MEDIA_OFFLINE: DWORD = 4304;
+pub const ERROR_LIBRARY_OFFLINE: DWORD = 4305;
+pub const ERROR_EMPTY: DWORD = 4306;
+pub const ERROR_NOT_EMPTY: DWORD = 4307;
+pub const ERROR_MEDIA_UNAVAILABLE: DWORD = 4308;
+pub const ERROR_RESOURCE_DISABLED: DWORD = 4309;
+pub const ERROR_INVALID_CLEANER: DWORD = 4310;
+pub const ERROR_UNABLE_TO_CLEAN: DWORD = 4311;
+pub const ERROR_OBJECT_NOT_FOUND: DWORD = 4312;
+pub const ERROR_DATABASE_FAILURE: DWORD = 4313;
+pub const ERROR_DATABASE_FULL: DWORD = 4314;
+pub const ERROR_MEDIA_INCOMPATIBLE: DWORD = 4315;
+pub const ERROR_RESOURCE_NOT_PRESENT: DWORD = 4316;
+pub const ERROR_INVALID_OPERATION: DWORD = 4317;
+pub const ERROR_MEDIA_NOT_AVAILABLE: DWORD = 4318;
+pub const ERROR_DEVICE_NOT_AVAILABLE: DWORD = 4319;
+pub const ERROR_REQUEST_REFUSED: DWORD = 4320;
+pub const ERROR_INVALID_DRIVE_OBJECT: DWORD = 4321;
+pub const ERROR_LIBRARY_FULL: DWORD = 4322;
+pub const ERROR_MEDIUM_NOT_ACCESSIBLE: DWORD = 4323;
+pub const ERROR_UNABLE_TO_LOAD_MEDIUM: DWORD = 4324;
+pub const ERROR_UNABLE_TO_INVENTORY_DRIVE: DWORD = 4325;
+pub const ERROR_UNABLE_TO_INVENTORY_SLOT: DWORD = 4326;
+pub const ERROR_UNABLE_TO_INVENTORY_TRANSPORT: DWORD = 4327;
+pub const ERROR_TRANSPORT_FULL: DWORD = 4328;
+pub const ERROR_CONTROLLING_IEPORT: DWORD = 4329;
+pub const ERROR_UNABLE_TO_EJECT_MOUNTED_MEDIA: DWORD = 4330;
+pub const ERROR_CLEANER_SLOT_SET: DWORD = 4331;
+pub const ERROR_CLEANER_SLOT_NOT_SET: DWORD = 4332;
+pub const ERROR_CLEANER_CARTRIDGE_SPENT: DWORD = 4333;
+pub const ERROR_UNEXPECTED_OMID: DWORD = 4334;
+pub const ERROR_CANT_DELETE_LAST_ITEM: DWORD = 4335;
+pub const ERROR_MESSAGE_EXCEEDS_MAX_SIZE: DWORD = 4336;
+pub const ERROR_VOLUME_CONTAINS_SYS_FILES: DWORD = 4337;
+pub const ERROR_INDIGENOUS_TYPE: DWORD = 4338;
+pub const ERROR_NO_SUPPORTING_DRIVES: DWORD = 4339;
+pub const ERROR_CLEANER_CARTRIDGE_INSTALLED: DWORD = 4340;
+pub const ERROR_IEPORT_FULL: DWORD = 4341;
+pub const ERROR_FILE_OFFLINE: DWORD = 4350;
+pub const ERROR_REMOTE_STORAGE_NOT_ACTIVE: DWORD = 4351;
+pub const ERROR_REMOTE_STORAGE_MEDIA_ERROR: DWORD = 4352;
+pub const ERROR_NOT_A_REPARSE_POINT: DWORD = 4390;
+pub const ERROR_REPARSE_ATTRIBUTE_CONFLICT: DWORD = 4391;
+pub const ERROR_INVALID_REPARSE_DATA: DWORD = 4392;
+pub const ERROR_REPARSE_TAG_INVALID: DWORD = 4393;
+pub const ERROR_REPARSE_TAG_MISMATCH: DWORD = 4394;
+pub const ERROR_VOLUME_NOT_SIS_ENABLED: DWORD = 4500;
+pub const ERROR_DEPENDENT_RESOURCE_EXISTS: DWORD = 5001;
+pub const ERROR_DEPENDENCY_NOT_FOUND: DWORD = 5002;
+pub const ERROR_DEPENDENCY_ALREADY_EXISTS: DWORD = 5003;
+pub const ERROR_RESOURCE_NOT_ONLINE: DWORD = 5004;
+pub const ERROR_HOST_NODE_NOT_AVAILABLE: DWORD = 5005;
+pub const ERROR_RESOURCE_NOT_AVAILABLE: DWORD = 5006;
+pub const ERROR_RESOURCE_NOT_FOUND: DWORD = 5007;
+pub const ERROR_SHUTDOWN_CLUSTER: DWORD = 5008;
+pub const ERROR_CANT_EVICT_ACTIVE_NODE: DWORD = 5009;
+pub const ERROR_OBJECT_ALREADY_EXISTS: DWORD = 5010;
+pub const ERROR_OBJECT_IN_LIST: DWORD = 5011;
+pub const ERROR_GROUP_NOT_AVAILABLE: DWORD = 5012;
+pub const ERROR_GROUP_NOT_FOUND: DWORD = 5013;
+pub const ERROR_GROUP_NOT_ONLINE: DWORD = 5014;
+pub const ERROR_HOST_NODE_NOT_RESOURCE_OWNER: DWORD = 5015;
+pub const ERROR_HOST_NODE_NOT_GROUP_OWNER: DWORD = 5016;
+pub const ERROR_RESMON_CREATE_FAILED: DWORD = 5017;
+pub const ERROR_RESMON_ONLINE_FAILED: DWORD = 5018;
+pub const ERROR_RESOURCE_ONLINE: DWORD = 5019;
+pub const ERROR_QUORUM_RESOURCE: DWORD = 5020;
+pub const ERROR_NOT_QUORUM_CAPABLE: DWORD = 5021;
+pub const ERROR_CLUSTER_SHUTTING_DOWN: DWORD = 5022;
+pub const ERROR_INVALID_STATE: DWORD = 5023;
+pub const ERROR_RESOURCE_PROPERTIES_STORED: DWORD = 5024;
+pub const ERROR_NOT_QUORUM_CLASS: DWORD = 5025;
+pub const ERROR_CORE_RESOURCE: DWORD = 5026;
+pub const ERROR_QUORUM_RESOURCE_ONLINE_FAILED: DWORD = 5027;
+pub const ERROR_QUORUMLOG_OPEN_FAILED: DWORD = 5028;
+pub const ERROR_CLUSTERLOG_CORRUPT: DWORD = 5029;
+pub const ERROR_CLUSTERLOG_RECORD_EXCEEDS_MAXSIZE: DWORD = 5030;
+pub const ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE: DWORD = 5031;
+pub const ERROR_CLUSTERLOG_CHKPOINT_NOT_FOUND: DWORD = 5032;
+pub const ERROR_CLUSTERLOG_NOT_ENOUGH_SPACE: DWORD = 5033;
+pub const ERROR_QUORUM_OWNER_ALIVE: DWORD = 5034;
+pub const ERROR_NETWORK_NOT_AVAILABLE: DWORD = 5035;
+pub const ERROR_NODE_NOT_AVAILABLE: DWORD = 5036;
+pub const ERROR_ALL_NODES_NOT_AVAILABLE: DWORD = 5037;
+pub const ERROR_RESOURCE_FAILED: DWORD = 5038;
+pub const ERROR_CLUSTER_INVALID_NODE: DWORD = 5039;
+pub const ERROR_CLUSTER_NODE_EXISTS: DWORD = 5040;
+pub const ERROR_CLUSTER_JOIN_IN_PROGRESS: DWORD = 5041;
+pub const ERROR_CLUSTER_NODE_NOT_FOUND: DWORD = 5042;
+pub const ERROR_CLUSTER_LOCAL_NODE_NOT_FOUND: DWORD = 5043;
+pub const ERROR_CLUSTER_NETWORK_EXISTS: DWORD = 5044;
+pub const ERROR_CLUSTER_NETWORK_NOT_FOUND: DWORD = 5045;
+pub const ERROR_CLUSTER_NETINTERFACE_EXISTS: DWORD = 5046;
+pub const ERROR_CLUSTER_NETINTERFACE_NOT_FOUND: DWORD = 5047;
+pub const ERROR_CLUSTER_INVALID_REQUEST: DWORD = 5048;
+pub const ERROR_CLUSTER_INVALID_NETWORK_PROVIDER: DWORD = 5049;
+pub const ERROR_CLUSTER_NODE_DOWN: DWORD = 5050;
+pub const ERROR_CLUSTER_NODE_UNREACHABLE: DWORD = 5051;
+pub const ERROR_CLUSTER_NODE_NOT_MEMBER: DWORD = 5052;
+pub const ERROR_CLUSTER_JOIN_NOT_IN_PROGRESS: DWORD = 5053;
+pub const ERROR_CLUSTER_INVALID_NETWORK: DWORD = 5054;
+pub const ERROR_CLUSTER_NODE_UP: DWORD = 5056;
+pub const ERROR_CLUSTER_IPADDR_IN_USE: DWORD = 5057;
+pub const ERROR_CLUSTER_NODE_NOT_PAUSED: DWORD = 5058;
+pub const ERROR_CLUSTER_NO_SECURITY_CONTEXT: DWORD = 5059;
+pub const ERROR_CLUSTER_NETWORK_NOT_INTERNAL: DWORD = 5060;
+pub const ERROR_CLUSTER_NODE_ALREADY_UP: DWORD = 5061;
+pub const ERROR_CLUSTER_NODE_ALREADY_DOWN: DWORD = 5062;
+pub const ERROR_CLUSTER_NETWORK_ALREADY_ONLINE: DWORD = 5063;
+pub const ERROR_CLUSTER_NETWORK_ALREADY_OFFLINE: DWORD = 5064;
+pub const ERROR_CLUSTER_NODE_ALREADY_MEMBER: DWORD = 5065;
+pub const ERROR_CLUSTER_LAST_INTERNAL_NETWORK: DWORD = 5066;
+pub const ERROR_CLUSTER_NETWORK_HAS_DEPENDENTS: DWORD = 5067;
+pub const ERROR_INVALID_OPERATION_ON_QUORUM: DWORD = 5068;
+pub const ERROR_DEPENDENCY_NOT_ALLOWED: DWORD = 5069;
+pub const ERROR_CLUSTER_NODE_PAUSED: DWORD = 5070;
+pub const ERROR_NODE_CANT_HOST_RESOURCE: DWORD = 5071;
+pub const ERROR_CLUSTER_NODE_NOT_READY: DWORD = 5072;
+pub const ERROR_CLUSTER_NODE_SHUTTING_DOWN: DWORD = 5073;
+pub const ERROR_CLUSTER_JOIN_ABORTED: DWORD = 5074;
+pub const ERROR_CLUSTER_INCOMPATIBLE_VERSIONS: DWORD = 5075;
+pub const ERROR_CLUSTER_MAXNUM_OF_RESOURCES_EXCEEDED: DWORD = 5076;
+pub const ERROR_CLUSTER_SYSTEM_CONFIG_CHANGED: DWORD = 5077;
+pub const ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND: DWORD = 5078;
+pub const ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED: DWORD = 5079;
+pub const ERROR_CLUSTER_RESNAME_NOT_FOUND: DWORD = 5080;
+pub const ERROR_CLUSTER_NO_RPC_PACKAGES_REGISTERED: DWORD = 5081;
+pub const ERROR_CLUSTER_OWNER_NOT_IN_PREFLIST: DWORD = 5082;
+pub const ERROR_CLUSTER_DATABASE_SEQMISMATCH: DWORD = 5083;
+pub const ERROR_RESMON_INVALID_STATE: DWORD = 5084;
+pub const ERROR_CLUSTER_GUM_NOT_LOCKER: DWORD = 5085;
+pub const ERROR_QUORUM_DISK_NOT_FOUND: DWORD = 5086;
+pub const ERROR_DATABASE_BACKUP_CORRUPT: DWORD = 5087;
+pub const ERROR_CLUSTER_NODE_ALREADY_HAS_DFS_ROOT: DWORD = 5088;
+pub const ERROR_RESOURCE_PROPERTY_UNCHANGEABLE: DWORD = 5089;
+pub const ERROR_CLUSTER_MEMBERSHIP_INVALID_STATE: DWORD = 5890;
+pub const ERROR_CLUSTER_QUORUMLOG_NOT_FOUND: DWORD = 5891;
+pub const ERROR_CLUSTER_MEMBERSHIP_HALT: DWORD = 5892;
+pub const ERROR_CLUSTER_INSTANCE_ID_MISMATCH: DWORD = 5893;
+pub const ERROR_CLUSTER_NETWORK_NOT_FOUND_FOR_IP: DWORD = 5894;
+pub const ERROR_CLUSTER_PROPERTY_DATA_TYPE_MISMATCH: DWORD = 5895;
+pub const ERROR_CLUSTER_EVICT_WITHOUT_CLEANUP: DWORD = 5896;
+pub const ERROR_CLUSTER_PARAMETER_MISMATCH: DWORD = 5897;
+pub const ERROR_NODE_CANNOT_BE_CLUSTERED: DWORD = 5898;
+pub const ERROR_CLUSTER_WRONG_OS_VERSION: DWORD = 5899;
+pub const ERROR_CLUSTER_CANT_CREATE_DUP_CLUSTER_NAME: DWORD = 5900;
+pub const ERROR_CLUSCFG_ALREADY_COMMITTED: DWORD = 5901;
+pub const ERROR_CLUSCFG_ROLLBACK_FAILED: DWORD = 5902;
+pub const ERROR_CLUSCFG_SYSTEM_DISK_DRIVE_LETTER_CONFLICT: DWORD = 5903;
+pub const ERROR_CLUSTER_OLD_VERSION: DWORD = 5904;
+pub const ERROR_CLUSTER_MISMATCHED_COMPUTER_ACCT_NAME: DWORD = 5905;
+pub const ERROR_ENCRYPTION_FAILED: DWORD = 6000;
+pub const ERROR_DECRYPTION_FAILED: DWORD = 6001;
+pub const ERROR_FILE_ENCRYPTED: DWORD = 6002;
+pub const ERROR_NO_RECOVERY_POLICY: DWORD = 6003;
+pub const ERROR_NO_EFS: DWORD = 6004;
+pub const ERROR_WRONG_EFS: DWORD = 6005;
+pub const ERROR_NO_USER_KEYS: DWORD = 6006;
+pub const ERROR_FILE_NOT_ENCRYPTED: DWORD = 6007;
+pub const ERROR_NOT_EXPORT_FORMAT: DWORD = 6008;
+pub const ERROR_FILE_READ_ONLY: DWORD = 6009;
+pub const ERROR_DIR_EFS_DISALLOWED: DWORD = 6010;
+pub const ERROR_EFS_SERVER_NOT_TRUSTED: DWORD = 6011;
+pub const ERROR_BAD_RECOVERY_POLICY: DWORD = 6012;
+pub const ERROR_EFS_ALG_BLOB_TOO_BIG: DWORD = 6013;
+pub const ERROR_VOLUME_NOT_SUPPORT_EFS: DWORD = 6014;
+pub const ERROR_EFS_DISABLED: DWORD = 6015;
+pub const ERROR_EFS_VERSION_NOT_SUPPORT: DWORD = 6016;
+pub const ERROR_NO_BROWSER_SERVERS_FOUND: DWORD = 6118;
+pub const ERROR_CTX_WINSTATION_NAME_INVALID: DWORD = 7001;
+pub const ERROR_CTX_INVALID_PD: DWORD = 7002;
+pub const ERROR_CTX_PD_NOT_FOUND: DWORD = 7003;
+pub const ERROR_CTX_WD_NOT_FOUND: DWORD = 7004;
+pub const ERROR_CTX_CANNOT_MAKE_EVENTLOG_ENTRY: DWORD = 7005;
+pub const ERROR_CTX_SERVICE_NAME_COLLISION: DWORD = 7006;
+pub const ERROR_CTX_CLOSE_PENDING: DWORD = 7007;
+pub const ERROR_CTX_NO_OUTBUF: DWORD = 7008;
+pub const ERROR_CTX_MODEM_INF_NOT_FOUND: DWORD = 7009;
+pub const ERROR_CTX_INVALID_MODEMNAME: DWORD = 7010;
+pub const ERROR_CTX_MODEM_RESPONSE_ERROR: DWORD = 7011;
+pub const ERROR_CTX_MODEM_RESPONSE_TIMEOUT: DWORD = 7012;
+pub const ERROR_CTX_MODEM_RESPONSE_NO_CARRIER: DWORD = 7013;
+pub const ERROR_CTX_MODEM_RESPONSE_NO_DIALTONE: DWORD = 7014;
+pub const ERROR_CTX_MODEM_RESPONSE_BUSY: DWORD = 7015;
+pub const ERROR_CTX_MODEM_RESPONSE_VOICE: DWORD = 7016;
+pub const ERROR_CTX_TD_ERROR: DWORD = 7017;
+pub const ERROR_CTX_WINSTATION_NOT_FOUND: DWORD = 7022;
+pub const ERROR_CTX_WINSTATION_ALREADY_EXISTS: DWORD = 7023;
+pub const ERROR_CTX_WINSTATION_BUSY: DWORD = 7024;
+pub const ERROR_CTX_BAD_VIDEO_MODE: DWORD = 7025;
+pub const ERROR_CTX_GRAPHICS_INVALID: DWORD = 7035;
+pub const ERROR_CTX_LOGON_DISABLED: DWORD = 7037;
+pub const ERROR_CTX_NOT_CONSOLE: DWORD = 7038;
+pub const ERROR_CTX_CLIENT_QUERY_TIMEOUT: DWORD = 7040;
+pub const ERROR_CTX_CONSOLE_DISCONNECT: DWORD = 7041;
+pub const ERROR_CTX_CONSOLE_CONNECT: DWORD = 7042;
+pub const ERROR_CTX_SHADOW_DENIED: DWORD = 7044;
+pub const ERROR_CTX_WINSTATION_ACCESS_DENIED: DWORD = 7045;
+pub const ERROR_CTX_INVALID_WD: DWORD = 7049;
+pub const ERROR_CTX_SHADOW_INVALID: DWORD = 7050;
+pub const ERROR_CTX_SHADOW_DISABLED: DWORD = 7051;
+pub const ERROR_CTX_CLIENT_LICENSE_IN_USE: DWORD = 7052;
+pub const ERROR_CTX_CLIENT_LICENSE_NOT_SET: DWORD = 7053;
+pub const ERROR_CTX_LICENSE_NOT_AVAILABLE: DWORD = 7054;
+pub const ERROR_CTX_LICENSE_CLIENT_INVALID: DWORD = 7055;
+pub const ERROR_CTX_LICENSE_EXPIRED: DWORD = 7056;
+pub const ERROR_CTX_SHADOW_NOT_RUNNING: DWORD = 7057;
+pub const ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE: DWORD = 7058;
+pub const ERROR_ACTIVATION_COUNT_EXCEEDED: DWORD = 7059;
+pub const ERROR_DS_NOT_INSTALLED: DWORD = 8200;
+pub const ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY: DWORD = 8201;
+pub const ERROR_DS_NO_ATTRIBUTE_OR_VALUE: DWORD = 8202;
+pub const ERROR_DS_INVALID_ATTRIBUTE_SYNTAX: DWORD = 8203;
+pub const ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED: DWORD = 8204;
+pub const ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS: DWORD = 8205;
+pub const ERROR_DS_BUSY: DWORD = 8206;
+pub const ERROR_DS_UNAVAILABLE: DWORD = 8207;
+pub const ERROR_DS_NO_RIDS_ALLOCATED: DWORD = 8208;
+pub const ERROR_DS_NO_MORE_RIDS: DWORD = 8209;
+pub const ERROR_DS_INCORRECT_ROLE_OWNER: DWORD = 8210;
+pub const ERROR_DS_RIDMGR_INIT_ERROR: DWORD = 8211;
+pub const ERROR_DS_OBJ_CLASS_VIOLATION: DWORD = 8212;
+pub const ERROR_DS_CANT_ON_NON_LEAF: DWORD = 8213;
+pub const ERROR_DS_CANT_ON_RDN: DWORD = 8214;
+pub const ERROR_DS_CANT_MOD_OBJ_CLASS: DWORD = 8215;
+pub const ERROR_DS_CROSS_DOM_MOVE_ERROR: DWORD = 8216;
+pub const ERROR_DS_GC_NOT_AVAILABLE: DWORD = 8217;
+pub const ERROR_SHARED_POLICY: DWORD = 8218;
+pub const ERROR_POLICY_OBJECT_NOT_FOUND: DWORD = 8219;
+pub const ERROR_POLICY_ONLY_IN_DS: DWORD = 8220;
+pub const ERROR_PROMOTION_ACTIVE: DWORD = 8221;
+pub const ERROR_NO_PROMOTION_ACTIVE: DWORD = 8222;
+pub const ERROR_DS_OPERATIONS_ERROR: DWORD = 8224;
+pub const ERROR_DS_PROTOCOL_ERROR: DWORD = 8225;
+pub const ERROR_DS_TIMELIMIT_EXCEEDED: DWORD = 8226;
+pub const ERROR_DS_SIZELIMIT_EXCEEDED: DWORD = 8227;
+pub const ERROR_DS_ADMIN_LIMIT_EXCEEDED: DWORD = 8228;
+pub const ERROR_DS_COMPARE_FALSE: DWORD = 8229;
+pub const ERROR_DS_COMPARE_TRUE: DWORD = 8230;
+pub const ERROR_DS_AUTH_METHOD_NOT_SUPPORTED: DWORD = 8231;
+pub const ERROR_DS_STRONG_AUTH_REQUIRED: DWORD = 8232;
+pub const ERROR_DS_INAPPROPRIATE_AUTH: DWORD = 8233;
+pub const ERROR_DS_AUTH_UNKNOWN: DWORD = 8234;
+pub const ERROR_DS_REFERRAL: DWORD = 8235;
+pub const ERROR_DS_UNAVAILABLE_CRIT_EXTENSION: DWORD = 8236;
+pub const ERROR_DS_CONFIDENTIALITY_REQUIRED: DWORD = 8237;
+pub const ERROR_DS_INAPPROPRIATE_MATCHING: DWORD = 8238;
+pub const ERROR_DS_CONSTRAINT_VIOLATION: DWORD = 8239;
+pub const ERROR_DS_NO_SUCH_OBJECT: DWORD = 8240;
+pub const ERROR_DS_ALIAS_PROBLEM: DWORD = 8241;
+pub const ERROR_DS_INVALID_DN_SYNTAX: DWORD = 8242;
+pub const ERROR_DS_IS_LEAF: DWORD = 8243;
+pub const ERROR_DS_ALIAS_DEREF_PROBLEM: DWORD = 8244;
+pub const ERROR_DS_UNWILLING_TO_PERFORM: DWORD = 8245;
+pub const ERROR_DS_LOOP_DETECT: DWORD = 8246;
+pub const ERROR_DS_NAMING_VIOLATION: DWORD = 8247;
+pub const ERROR_DS_OBJECT_RESULTS_TOO_LARGE: DWORD = 8248;
+pub const ERROR_DS_AFFECTS_MULTIPLE_DSAS: DWORD = 8249;
+pub const ERROR_DS_SERVER_DOWN: DWORD = 8250;
+pub const ERROR_DS_LOCAL_ERROR: DWORD = 8251;
+pub const ERROR_DS_ENCODING_ERROR: DWORD = 8252;
+pub const ERROR_DS_DECODING_ERROR: DWORD = 8253;
+pub const ERROR_DS_FILTER_UNKNOWN: DWORD = 8254;
+pub const ERROR_DS_PARAM_ERROR: DWORD = 8255;
+pub const ERROR_DS_NOT_SUPPORTED: DWORD = 8256;
+pub const ERROR_DS_NO_RESULTS_RETURNED: DWORD = 8257;
+pub const ERROR_DS_CONTROL_NOT_FOUND: DWORD = 8258;
+pub const ERROR_DS_CLIENT_LOOP: DWORD = 8259;
+pub const ERROR_DS_REFERRAL_LIMIT_EXCEEDED: DWORD = 8260;
+pub const ERROR_DS_SORT_CONTROL_MISSING: DWORD = 8261;
+pub const ERROR_DS_OFFSET_RANGE_ERROR: DWORD = 8262;
+pub const ERROR_DS_ROOT_MUST_BE_NC: DWORD = 8301;
+pub const ERROR_DS_ADD_REPLICA_INHIBITED: DWORD = 8302;
+pub const ERROR_DS_ATT_NOT_DEF_IN_SCHEMA: DWORD = 8303;
+pub const ERROR_DS_MAX_OBJ_SIZE_EXCEEDED: DWORD = 8304;
+pub const ERROR_DS_OBJ_STRING_NAME_EXISTS: DWORD = 8305;
+pub const ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA: DWORD = 8306;
+pub const ERROR_DS_RDN_DOESNT_MATCH_SCHEMA: DWORD = 8307;
+pub const ERROR_DS_NO_REQUESTED_ATTS_FOUND: DWORD = 8308;
+pub const ERROR_DS_USER_BUFFER_TO_SMALL: DWORD = 8309;
+pub const ERROR_DS_ATT_IS_NOT_ON_OBJ: DWORD = 8310;
+pub const ERROR_DS_ILLEGAL_MOD_OPERATION: DWORD = 8311;
+pub const ERROR_DS_OBJ_TOO_LARGE: DWORD = 8312;
+pub const ERROR_DS_BAD_INSTANCE_TYPE: DWORD = 8313;
+pub const ERROR_DS_MASTERDSA_REQUIRED: DWORD = 8314;
+pub const ERROR_DS_OBJECT_CLASS_REQUIRED: DWORD = 8315;
+pub const ERROR_DS_MISSING_REQUIRED_ATT: DWORD = 8316;
+pub const ERROR_DS_ATT_NOT_DEF_FOR_CLASS: DWORD = 8317;
+pub const ERROR_DS_ATT_ALREADY_EXISTS: DWORD = 8318;
+pub const ERROR_DS_CANT_ADD_ATT_VALUES: DWORD = 8320;
+pub const ERROR_DS_SINGLE_VALUE_CONSTRAINT: DWORD = 8321;
+pub const ERROR_DS_RANGE_CONSTRAINT: DWORD = 8322;
+pub const ERROR_DS_ATT_VAL_ALREADY_EXISTS: DWORD = 8323;
+pub const ERROR_DS_CANT_REM_MISSING_ATT: DWORD = 8324;
+pub const ERROR_DS_CANT_REM_MISSING_ATT_VAL: DWORD = 8325;
+pub const ERROR_DS_ROOT_CANT_BE_SUBREF: DWORD = 8326;
+pub const ERROR_DS_NO_CHAINING: DWORD = 8327;
+pub const ERROR_DS_NO_CHAINED_EVAL: DWORD = 8328;
+pub const ERROR_DS_NO_PARENT_OBJECT: DWORD = 8329;
+pub const ERROR_DS_PARENT_IS_AN_ALIAS: DWORD = 8330;
+pub const ERROR_DS_CANT_MIX_MASTER_AND_REPS: DWORD = 8331;
+pub const ERROR_DS_CHILDREN_EXIST: DWORD = 8332;
+pub const ERROR_DS_OBJ_NOT_FOUND: DWORD = 8333;
+pub const ERROR_DS_ALIASED_OBJ_MISSING: DWORD = 8334;
+pub const ERROR_DS_BAD_NAME_SYNTAX: DWORD = 8335;
+pub const ERROR_DS_ALIAS_POINTS_TO_ALIAS: DWORD = 8336;
+pub const ERROR_DS_CANT_DEREF_ALIAS: DWORD = 8337;
+pub const ERROR_DS_OUT_OF_SCOPE: DWORD = 8338;
+pub const ERROR_DS_OBJECT_BEING_REMOVED: DWORD = 8339;
+pub const ERROR_DS_CANT_DELETE_DSA_OBJ: DWORD = 8340;
+pub const ERROR_DS_GENERIC_ERROR: DWORD = 8341;
+pub const ERROR_DS_DSA_MUST_BE_INT_MASTER: DWORD = 8342;
+pub const ERROR_DS_CLASS_NOT_DSA: DWORD = 8343;
+pub const ERROR_DS_INSUFF_ACCESS_RIGHTS: DWORD = 8344;
+pub const ERROR_DS_ILLEGAL_SUPERIOR: DWORD = 8345;
+pub const ERROR_DS_ATTRIBUTE_OWNED_BY_SAM: DWORD = 8346;
+pub const ERROR_DS_NAME_TOO_MANY_PARTS: DWORD = 8347;
+pub const ERROR_DS_NAME_TOO_LONG: DWORD = 8348;
+pub const ERROR_DS_NAME_VALUE_TOO_LONG: DWORD = 8349;
+pub const ERROR_DS_NAME_UNPARSEABLE: DWORD = 8350;
+pub const ERROR_DS_NAME_TYPE_UNKNOWN: DWORD = 8351;
+pub const ERROR_DS_NOT_AN_OBJECT: DWORD = 8352;
+pub const ERROR_DS_SEC_DESC_TOO_SHORT: DWORD = 8353;
+pub const ERROR_DS_SEC_DESC_INVALID: DWORD = 8354;
+pub const ERROR_DS_NO_DELETED_NAME: DWORD = 8355;
+pub const ERROR_DS_SUBREF_MUST_HAVE_PARENT: DWORD = 8356;
+pub const ERROR_DS_NCNAME_MUST_BE_NC: DWORD = 8357;
+pub const ERROR_DS_CANT_ADD_SYSTEM_ONLY: DWORD = 8358;
+pub const ERROR_DS_CLASS_MUST_BE_CONCRETE: DWORD = 8359;
+pub const ERROR_DS_INVALID_DMD: DWORD = 8360;
+pub const ERROR_DS_OBJ_GUID_EXISTS: DWORD = 8361;
+pub const ERROR_DS_NOT_ON_BACKLINK: DWORD = 8362;
+pub const ERROR_DS_NO_CROSSREF_FOR_NC: DWORD = 8363;
+pub const ERROR_DS_SHUTTING_DOWN: DWORD = 8364;
+pub const ERROR_DS_UNKNOWN_OPERATION: DWORD = 8365;
+pub const ERROR_DS_INVALID_ROLE_OWNER: DWORD = 8366;
+pub const ERROR_DS_COULDNT_CONTACT_FSMO: DWORD = 8367;
+pub const ERROR_DS_CROSS_NC_DN_RENAME: DWORD = 8368;
+pub const ERROR_DS_CANT_MOD_SYSTEM_ONLY: DWORD = 8369;
+pub const ERROR_DS_REPLICATOR_ONLY: DWORD = 8370;
+pub const ERROR_DS_OBJ_CLASS_NOT_DEFINED: DWORD = 8371;
+pub const ERROR_DS_OBJ_CLASS_NOT_SUBCLASS: DWORD = 8372;
+pub const ERROR_DS_NAME_REFERENCE_INVALID: DWORD = 8373;
+pub const ERROR_DS_CROSS_REF_EXISTS: DWORD = 8374;
+pub const ERROR_DS_CANT_DEL_MASTER_CROSSREF: DWORD = 8375;
+pub const ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD: DWORD = 8376;
+pub const ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX: DWORD = 8377;
+pub const ERROR_DS_DUP_RDN: DWORD = 8378;
+pub const ERROR_DS_DUP_OID: DWORD = 8379;
+pub const ERROR_DS_DUP_MAPI_ID: DWORD = 8380;
+pub const ERROR_DS_DUP_SCHEMA_ID_GUID: DWORD = 8381;
+pub const ERROR_DS_DUP_LDAP_DISPLAY_NAME: DWORD = 8382;
+pub const ERROR_DS_SEMANTIC_ATT_TEST: DWORD = 8383;
+pub const ERROR_DS_SYNTAX_MISMATCH: DWORD = 8384;
+pub const ERROR_DS_EXISTS_IN_MUST_HAVE: DWORD = 8385;
+pub const ERROR_DS_EXISTS_IN_MAY_HAVE: DWORD = 8386;
+pub const ERROR_DS_NONEXISTENT_MAY_HAVE: DWORD = 8387;
+pub const ERROR_DS_NONEXISTENT_MUST_HAVE: DWORD = 8388;
+pub const ERROR_DS_AUX_CLS_TEST_FAIL: DWORD = 8389;
+pub const ERROR_DS_NONEXISTENT_POSS_SUP: DWORD = 8390;
+pub const ERROR_DS_SUB_CLS_TEST_FAIL: DWORD = 8391;
+pub const ERROR_DS_BAD_RDN_ATT_ID_SYNTAX: DWORD = 8392;
+pub const ERROR_DS_EXISTS_IN_AUX_CLS: DWORD = 8393;
+pub const ERROR_DS_EXISTS_IN_SUB_CLS: DWORD = 8394;
+pub const ERROR_DS_EXISTS_IN_POSS_SUP: DWORD = 8395;
+pub const ERROR_DS_RECALCSCHEMA_FAILED: DWORD = 8396;
+pub const ERROR_DS_TREE_DELETE_NOT_FINISHED: DWORD = 8397;
+pub const ERROR_DS_CANT_DELETE: DWORD = 8398;
+pub const ERROR_DS_ATT_SCHEMA_REQ_ID: DWORD = 8399;
+pub const ERROR_DS_BAD_ATT_SCHEMA_SYNTAX: DWORD = 8400;
+pub const ERROR_DS_CANT_CACHE_ATT: DWORD = 8401;
+pub const ERROR_DS_CANT_CACHE_CLASS: DWORD = 8402;
+pub const ERROR_DS_CANT_REMOVE_ATT_CACHE: DWORD = 8403;
+pub const ERROR_DS_CANT_REMOVE_CLASS_CACHE: DWORD = 8404;
+pub const ERROR_DS_CANT_RETRIEVE_DN: DWORD = 8405;
+pub const ERROR_DS_MISSING_SUPREF: DWORD = 8406;
+pub const ERROR_DS_CANT_RETRIEVE_INSTANCE: DWORD = 8407;
+pub const ERROR_DS_CODE_INCONSISTENCY: DWORD = 8408;
+pub const ERROR_DS_DATABASE_ERROR: DWORD = 8409;
+pub const ERROR_DS_GOVERNSID_MISSING: DWORD = 8410;
+pub const ERROR_DS_MISSING_EXPECTED_ATT: DWORD = 8411;
+pub const ERROR_DS_NCNAME_MISSING_CR_REF: DWORD = 8412;
+pub const ERROR_DS_SECURITY_CHECKING_ERROR: DWORD = 8413;
+pub const ERROR_DS_SCHEMA_NOT_LOADED: DWORD = 8414;
+pub const ERROR_DS_SCHEMA_ALLOC_FAILED: DWORD = 8415;
+pub const ERROR_DS_ATT_SCHEMA_REQ_SYNTAX: DWORD = 8416;
+pub const ERROR_DS_GCVERIFY_ERROR: DWORD = 8417;
+pub const ERROR_DS_DRA_SCHEMA_MISMATCH: DWORD = 8418;
+pub const ERROR_DS_CANT_FIND_DSA_OBJ: DWORD = 8419;
+pub const ERROR_DS_CANT_FIND_EXPECTED_NC: DWORD = 8420;
+pub const ERROR_DS_CANT_FIND_NC_IN_CACHE: DWORD = 8421;
+pub const ERROR_DS_CANT_RETRIEVE_CHILD: DWORD = 8422;
+pub const ERROR_DS_SECURITY_ILLEGAL_MODIFY: DWORD = 8423;
+pub const ERROR_DS_CANT_REPLACE_HIDDEN_REC: DWORD = 8424;
+pub const ERROR_DS_BAD_HIERARCHY_FILE: DWORD = 8425;
+pub const ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED: DWORD = 8426;
+pub const ERROR_DS_CONFIG_PARAM_MISSING: DWORD = 8427;
+pub const ERROR_DS_COUNTING_AB_INDICES_FAILED: DWORD = 8428;
+pub const ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED: DWORD = 8429;
+pub const ERROR_DS_INTERNAL_FAILURE: DWORD = 8430;
+pub const ERROR_DS_UNKNOWN_ERROR: DWORD = 8431;
+pub const ERROR_DS_ROOT_REQUIRES_CLASS_TOP: DWORD = 8432;
+pub const ERROR_DS_REFUSING_FSMO_ROLES: DWORD = 8433;
+pub const ERROR_DS_MISSING_FSMO_SETTINGS: DWORD = 8434;
+pub const ERROR_DS_UNABLE_TO_SURRENDER_ROLES: DWORD = 8435;
+pub const ERROR_DS_DRA_GENERIC: DWORD = 8436;
+pub const ERROR_DS_DRA_INVALID_PARAMETER: DWORD = 8437;
+pub const ERROR_DS_DRA_BUSY: DWORD = 8438;
+pub const ERROR_DS_DRA_BAD_DN: DWORD = 8439;
+pub const ERROR_DS_DRA_BAD_NC: DWORD = 8440;
+pub const ERROR_DS_DRA_DN_EXISTS: DWORD = 8441;
+pub const ERROR_DS_DRA_INTERNAL_ERROR: DWORD = 8442;
+pub const ERROR_DS_DRA_INCONSISTENT_DIT: DWORD = 8443;
+pub const ERROR_DS_DRA_CONNECTION_FAILED: DWORD = 8444;
+pub const ERROR_DS_DRA_BAD_INSTANCE_TYPE: DWORD = 8445;
+pub const ERROR_DS_DRA_OUT_OF_MEM: DWORD = 8446;
+pub const ERROR_DS_DRA_MAIL_PROBLEM: DWORD = 8447;
+pub const ERROR_DS_DRA_REF_ALREADY_EXISTS: DWORD = 8448;
+pub const ERROR_DS_DRA_REF_NOT_FOUND: DWORD = 8449;
+pub const ERROR_DS_DRA_OBJ_IS_REP_SOURCE: DWORD = 8450;
+pub const ERROR_DS_DRA_DB_ERROR: DWORD = 8451;
+pub const ERROR_DS_DRA_NO_REPLICA: DWORD = 8452;
+pub const ERROR_DS_DRA_ACCESS_DENIED: DWORD = 8453;
+pub const ERROR_DS_DRA_NOT_SUPPORTED: DWORD = 8454;
+pub const ERROR_DS_DRA_RPC_CANCELLED: DWORD = 8455;
+pub const ERROR_DS_DRA_SOURCE_DISABLED: DWORD = 8456;
+pub const ERROR_DS_DRA_SINK_DISABLED: DWORD = 8457;
+pub const ERROR_DS_DRA_NAME_COLLISION: DWORD = 8458;
+pub const ERROR_DS_DRA_SOURCE_REINSTALLED: DWORD = 8459;
+pub const ERROR_DS_DRA_MISSING_PARENT: DWORD = 8460;
+pub const ERROR_DS_DRA_PREEMPTED: DWORD = 8461;
+pub const ERROR_DS_DRA_ABANDON_SYNC: DWORD = 8462;
+pub const ERROR_DS_DRA_SHUTDOWN: DWORD = 8463;
+pub const ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET: DWORD = 8464;
+pub const ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA: DWORD = 8465;
+pub const ERROR_DS_DRA_EXTN_CONNECTION_FAILED: DWORD = 8466;
+pub const ERROR_DS_INSTALL_SCHEMA_MISMATCH: DWORD = 8467;
+pub const ERROR_DS_DUP_LINK_ID: DWORD = 8468;
+pub const ERROR_DS_NAME_ERROR_RESOLVING: DWORD = 8469;
+pub const ERROR_DS_NAME_ERROR_NOT_FOUND: DWORD = 8470;
+pub const ERROR_DS_NAME_ERROR_NOT_UNIQUE: DWORD = 8471;
+pub const ERROR_DS_NAME_ERROR_NO_MAPPING: DWORD = 8472;
+pub const ERROR_DS_NAME_ERROR_DOMAIN_ONLY: DWORD = 8473;
+pub const ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING: DWORD = 8474;
+pub const ERROR_DS_CONSTRUCTED_ATT_MOD: DWORD = 8475;
+pub const ERROR_DS_WRONG_OM_OBJ_CLASS: DWORD = 8476;
+pub const ERROR_DS_DRA_REPL_PENDING: DWORD = 8477;
+pub const ERROR_DS_DS_REQUIRED: DWORD = 8478;
+pub const ERROR_DS_INVALID_LDAP_DISPLAY_NAME: DWORD = 8479;
+pub const ERROR_DS_NON_BASE_SEARCH: DWORD = 8480;
+pub const ERROR_DS_CANT_RETRIEVE_ATTS: DWORD = 8481;
+pub const ERROR_DS_BACKLINK_WITHOUT_LINK: DWORD = 8482;
+pub const ERROR_DS_EPOCH_MISMATCH: DWORD = 8483;
+pub const ERROR_DS_SRC_NAME_MISMATCH: DWORD = 8484;
+pub const ERROR_DS_SRC_AND_DST_NC_IDENTICAL: DWORD = 8485;
+pub const ERROR_DS_DST_NC_MISMATCH: DWORD = 8486;
+pub const ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC: DWORD = 8487;
+pub const ERROR_DS_SRC_GUID_MISMATCH: DWORD = 8488;
+pub const ERROR_DS_CANT_MOVE_DELETED_OBJECT: DWORD = 8489;
+pub const ERROR_DS_PDC_OPERATION_IN_PROGRESS: DWORD = 8490;
+pub const ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD: DWORD = 8491;
+pub const ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION: DWORD = 8492;
+pub const ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS: DWORD = 8493;
+pub const ERROR_DS_NC_MUST_HAVE_NC_PARENT: DWORD = 8494;
+pub const ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE: DWORD = 8495;
+pub const ERROR_DS_DST_DOMAIN_NOT_NATIVE: DWORD = 8496;
+pub const ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER: DWORD = 8497;
+pub const ERROR_DS_CANT_MOVE_ACCOUNT_GROUP: DWORD = 8498;
+pub const ERROR_DS_CANT_MOVE_RESOURCE_GROUP: DWORD = 8499;
+pub const ERROR_DS_INVALID_SEARCH_FLAG: DWORD = 8500;
+pub const ERROR_DS_NO_TREE_DELETE_ABOVE_NC: DWORD = 8501;
+pub const ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE: DWORD = 8502;
+pub const ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE: DWORD = 8503;
+pub const ERROR_DS_SAM_INIT_FAILURE: DWORD = 8504;
+pub const ERROR_DS_SENSITIVE_GROUP_VIOLATION: DWORD = 8505;
+pub const ERROR_DS_CANT_MOD_PRIMARYGROUPID: DWORD = 8506;
+pub const ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD: DWORD = 8507;
+pub const ERROR_DS_NONSAFE_SCHEMA_CHANGE: DWORD = 8508;
+pub const ERROR_DS_SCHEMA_UPDATE_DISALLOWED: DWORD = 8509;
+pub const ERROR_DS_CANT_CREATE_UNDER_SCHEMA: DWORD = 8510;
+pub const ERROR_DS_INSTALL_NO_SRC_SCH_VERSION: DWORD = 8511;
+pub const ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE: DWORD = 8512;
+pub const ERROR_DS_INVALID_GROUP_TYPE: DWORD = 8513;
+pub const ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN: DWORD = 8514;
+pub const ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN: DWORD = 8515;
+pub const ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER: DWORD = 8516;
+pub const ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER: DWORD = 8517;
+pub const ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER: DWORD = 8518;
+pub const ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER: DWORD = 8519;
+pub const ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER: DWORD = 8520;
+pub const ERROR_DS_HAVE_PRIMARY_MEMBERS: DWORD = 8521;
+pub const ERROR_DS_STRING_SD_CONVERSION_FAILED: DWORD = 8522;
+pub const ERROR_DS_NAMING_MASTER_GC: DWORD = 8523;
+pub const ERROR_DS_DNS_LOOKUP_FAILURE: DWORD = 8524;
+pub const ERROR_DS_COULDNT_UPDATE_SPNS: DWORD = 8525;
+pub const ERROR_DS_CANT_RETRIEVE_SD: DWORD = 8526;
+pub const ERROR_DS_KEY_NOT_UNIQUE: DWORD = 8527;
+pub const ERROR_DS_WRONG_LINKED_ATT_SYNTAX: DWORD = 8528;
+pub const ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD: DWORD = 8529;
+pub const ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY: DWORD = 8530;
+pub const ERROR_DS_CANT_START: DWORD = 8531;
+pub const ERROR_DS_INIT_FAILURE: DWORD = 8532;
+pub const ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION: DWORD = 8533;
+pub const ERROR_DS_SOURCE_DOMAIN_IN_FOREST: DWORD = 8534;
+pub const ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST: DWORD = 8535;
+pub const ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED: DWORD = 8536;
+pub const ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN: DWORD = 8537;
+pub const ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER: DWORD = 8538;
+pub const ERROR_DS_SRC_SID_EXISTS_IN_FOREST: DWORD = 8539;
+pub const ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH: DWORD = 8540;
+pub const ERROR_SAM_INIT_FAILURE: DWORD = 8541;
+pub const ERROR_DS_DRA_SCHEMA_INFO_SHIP: DWORD = 8542;
+pub const ERROR_DS_DRA_SCHEMA_CONFLICT: DWORD = 8543;
+pub const ERROR_DS_DRA_EARLIER_SCHEMA_CONFLICT: DWORD = 8544;
+pub const ERROR_DS_DRA_OBJ_NC_MISMATCH: DWORD = 8545;
+pub const ERROR_DS_NC_STILL_HAS_DSAS: DWORD = 8546;
+pub const ERROR_DS_GC_REQUIRED: DWORD = 8547;
+pub const ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY: DWORD = 8548;
+pub const ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS: DWORD = 8549;
+pub const ERROR_DS_CANT_ADD_TO_GC: DWORD = 8550;
+pub const ERROR_DS_NO_CHECKPOINT_WITH_PDC: DWORD = 8551;
+pub const ERROR_DS_SOURCE_AUDITING_NOT_ENABLED: DWORD = 8552;
+pub const ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC: DWORD = 8553;
+pub const ERROR_DS_INVALID_NAME_FOR_SPN: DWORD = 8554;
+pub const ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS: DWORD = 8555;
+pub const ERROR_DS_UNICODEPWD_NOT_IN_QUOTES: DWORD = 8556;
+pub const ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED: DWORD = 8557;
+pub const ERROR_DS_MUST_BE_RUN_ON_DST_DC: DWORD = 8558;
+pub const ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER: DWORD = 8559;
+pub const ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ: DWORD = 8560;
+pub const ERROR_DS_INIT_FAILURE_CONSOLE: DWORD = 8561;
+pub const ERROR_DS_SAM_INIT_FAILURE_CONSOLE: DWORD = 8562;
+pub const ERROR_DS_FOREST_VERSION_TOO_HIGH: DWORD = 8563;
+pub const ERROR_DS_DOMAIN_VERSION_TOO_HIGH: DWORD = 8564;
+pub const ERROR_DS_FOREST_VERSION_TOO_LOW: DWORD = 8565;
+pub const ERROR_DS_DOMAIN_VERSION_TOO_LOW: DWORD = 8566;
+pub const ERROR_DS_INCOMPATIBLE_VERSION: DWORD = 8567;
+pub const ERROR_DS_LOW_DSA_VERSION: DWORD = 8568;
+pub const ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN: DWORD = 8569;
+pub const ERROR_DS_NOT_SUPPORTED_SORT_ORDER: DWORD = 8570;
+pub const ERROR_DS_NAME_NOT_UNIQUE: DWORD = 8571;
+pub const ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4: DWORD = 8572;
+pub const ERROR_DS_OUT_OF_VERSION_STORE: DWORD = 8573;
+pub const ERROR_DS_INCOMPATIBLE_CONTROLS_USED: DWORD = 8574;
+pub const ERROR_DS_NO_REF_DOMAIN: DWORD = 8575;
+pub const ERROR_DS_RESERVED_LINK_ID: DWORD = 8576;
+pub const ERROR_DS_LINK_ID_NOT_AVAILABLE: DWORD = 8577;
+pub const ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER: DWORD = 8578;
+pub const ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE: DWORD = 8579;
+pub const ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC: DWORD = 8580;
+pub const ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG: DWORD = 8581;
+pub const ERROR_DS_MODIFYDN_WRONG_GRANDPARENT: DWORD = 8582;
+pub const ERROR_DS_NAME_ERROR_TRUST_REFERRAL: DWORD = 8583;
+pub const ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER: DWORD = 8584;
+pub const ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD: DWORD = 8585;
+pub const ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2: DWORD = 8586;
+pub const ERROR_DS_THREAD_LIMIT_EXCEEDED: DWORD = 8587;
+pub const ERROR_DS_NOT_CLOSEST: DWORD = 8588;
+pub const ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF: DWORD = 8589;
+pub const ERROR_DS_SINGLE_USER_MODE_FAILED: DWORD = 8590;
+pub const ERROR_DS_NTDSCRIPT_SYNTAX_ERROR: DWORD = 8591;
+pub const ERROR_DS_NTDSCRIPT_PROCESS_ERROR: DWORD = 8592;
+pub const ERROR_DS_DIFFERENT_REPL_EPOCHS: DWORD = 8593;
+pub const ERROR_DS_DRS_EXTENSIONS_CHANGED: DWORD = 8594;
+pub const ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR: DWORD = 8595;
+pub const ERROR_DS_NO_MSDS_INTID: DWORD = 8596;
+pub const ERROR_DS_DUP_MSDS_INTID: DWORD = 8597;
+pub const ERROR_DS_EXISTS_IN_RDNATTID: DWORD = 8598;
+pub const ERROR_DS_AUTHORIZATION_FAILED: DWORD = 8599;
+pub const ERROR_DS_INVALID_SCRIPT: DWORD = 8600;
+pub const ERROR_DS_REMOTE_CROSSREF_OP_FAILED: DWORD = 8601;
+pub const ERROR_DS_CROSS_REF_BUSY: DWORD = 8602;
+pub const ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN: DWORD = 8603;
+pub const ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC: DWORD = 8604;
+pub const ERROR_DS_DUPLICATE_ID_FOUND: DWORD = 8605;
+pub const ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT: DWORD = 8606;
+pub const ERROR_DS_GROUP_CONVERSION_ERROR: DWORD = 8607;
+pub const ERROR_DS_CANT_MOVE_APP_BASIC_GROUP: DWORD = 8608;
+pub const ERROR_DS_CANT_MOVE_APP_QUERY_GROUP: DWORD = 8609;
+pub const ERROR_DS_ROLE_NOT_VERIFIED: DWORD = 8610;
+pub const ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL: DWORD = 8611;
+pub const ERROR_DS_DOMAIN_RENAME_IN_PROGRESS: DWORD = 8612;
+pub const ERROR_DS_EXISTING_AD_CHILD_NC: DWORD = 8613;
+pub const ERROR_DS_REPL_LIFETIME_EXCEEDED: DWORD = 8614;
+pub const ERROR_DS_DISALLOWED_IN_SYSTEM_CONTAINER: DWORD = 8615;
+pub const ERROR_DS_LDAP_SEND_QUEUE_FULL: DWORD = 8616;
+pub const ERROR_DS_DRA_OUT_SCHEDULE_WINDOW: DWORD = 8617;
+pub const ERROR_SXS_SECTION_NOT_FOUND: DWORD = 14000;
+pub const ERROR_SXS_CANT_GEN_ACTCTX: DWORD = 14001;
+pub const ERROR_SXS_INVALID_ACTCTXDATA_FORMAT: DWORD = 14002;
+pub const ERROR_SXS_ASSEMBLY_NOT_FOUND: DWORD = 14003;
+pub const ERROR_SXS_MANIFEST_FORMAT_ERROR: DWORD = 14004;
+pub const ERROR_SXS_MANIFEST_PARSE_ERROR: DWORD = 14005;
+pub const ERROR_SXS_ACTIVATION_CONTEXT_DISABLED: DWORD = 14006;
+pub const ERROR_SXS_KEY_NOT_FOUND: DWORD = 14007;
+pub const ERROR_SXS_VERSION_CONFLICT: DWORD = 14008;
+pub const ERROR_SXS_WRONG_SECTION_TYPE: DWORD = 14009;
+pub const ERROR_SXS_THREAD_QUERIES_DISABLED: DWORD = 14010;
+pub const ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET: DWORD = 14011;
+pub const ERROR_SXS_UNKNOWN_ENCODING_GROUP: DWORD = 14012;
+pub const ERROR_SXS_UNKNOWN_ENCODING: DWORD = 14013;
+pub const ERROR_SXS_INVALID_XML_NAMESPACE_URI: DWORD = 14014;
+pub const ERROR_SXS_ROOT_MANIFEST_DEPENDENCY_NOT_INSTALLED: DWORD = 14015;
+pub const ERROR_SXS_LEAF_MANIFEST_DEPENDENCY_NOT_INSTALLED: DWORD = 14016;
+pub const ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE: DWORD = 14017;
+pub const ERROR_SXS_MANIFEST_MISSING_REQUIRED_DEFAULT_NAMESPACE: DWORD = 14018;
+pub const ERROR_SXS_MANIFEST_INVALID_REQUIRED_DEFAULT_NAMESPACE: DWORD = 14019;
+pub const ERROR_SXS_PRIVATE_MANIFEST_CROSS_PATH_WITH_REPARSE_POINT: DWORD = 14020;
+pub const ERROR_SXS_DUPLICATE_DLL_NAME: DWORD = 14021;
+pub const ERROR_SXS_DUPLICATE_WINDOWCLASS_NAME: DWORD = 14022;
+pub const ERROR_SXS_DUPLICATE_CLSID: DWORD = 14023;
+pub const ERROR_SXS_DUPLICATE_IID: DWORD = 14024;
+pub const ERROR_SXS_DUPLICATE_TLBID: DWORD = 14025;
+pub const ERROR_SXS_DUPLICATE_PROGID: DWORD = 14026;
+pub const ERROR_SXS_DUPLICATE_ASSEMBLY_NAME: DWORD = 14027;
+pub const ERROR_SXS_FILE_HASH_MISMATCH: DWORD = 14028;
+pub const ERROR_SXS_POLICY_PARSE_ERROR: DWORD = 14029;
+pub const ERROR_SXS_XML_E_MISSINGQUOTE: DWORD = 14030;
+pub const ERROR_SXS_XML_E_COMMENTSYNTAX: DWORD = 14031;
+pub const ERROR_SXS_XML_E_BADSTARTNAMECHAR: DWORD = 14032;
+pub const ERROR_SXS_XML_E_BADNAMECHAR: DWORD = 14033;
+pub const ERROR_SXS_XML_E_BADCHARINSTRING: DWORD = 14034;
+pub const ERROR_SXS_XML_E_XMLDECLSYNTAX: DWORD = 14035;
+pub const ERROR_SXS_XML_E_BADCHARDATA: DWORD = 14036;
+pub const ERROR_SXS_XML_E_MISSINGWHITESPACE: DWORD = 14037;
+pub const ERROR_SXS_XML_E_EXPECTINGTAGEND: DWORD = 14038;
+pub const ERROR_SXS_XML_E_MISSINGSEMICOLON: DWORD = 14039;
+pub const ERROR_SXS_XML_E_UNBALANCEDPAREN: DWORD = 14040;
+pub const ERROR_SXS_XML_E_INTERNALERROR: DWORD = 14041;
+pub const ERROR_SXS_XML_E_UNEXPECTED_WHITESPACE: DWORD = 14042;
+pub const ERROR_SXS_XML_E_INCOMPLETE_ENCODING: DWORD = 14043;
+pub const ERROR_SXS_XML_E_MISSING_PAREN: DWORD = 14044;
+pub const ERROR_SXS_XML_E_EXPECTINGCLOSEQUOTE: DWORD = 14045;
+pub const ERROR_SXS_XML_E_MULTIPLE_COLONS: DWORD = 14046;
+pub const ERROR_SXS_XML_E_INVALID_DECIMAL: DWORD = 14047;
+pub const ERROR_SXS_XML_E_INVALID_HEXIDECIMAL: DWORD = 14048;
+pub const ERROR_SXS_XML_E_INVALID_UNICODE: DWORD = 14049;
+pub const ERROR_SXS_XML_E_WHITESPACEORQUESTIONMARK: DWORD = 14050;
+pub const ERROR_SXS_XML_E_UNEXPECTEDENDTAG: DWORD = 14051;
+pub const ERROR_SXS_XML_E_UNCLOSEDTAG: DWORD = 14052;
+pub const ERROR_SXS_XML_E_DUPLICATEATTRIBUTE: DWORD = 14053;
+pub const ERROR_SXS_XML_E_MULTIPLEROOTS: DWORD = 14054;
+pub const ERROR_SXS_XML_E_INVALIDATROOTLEVEL: DWORD = 14055;
+pub const ERROR_SXS_XML_E_BADXMLDECL: DWORD = 14056;
+pub const ERROR_SXS_XML_E_MISSINGROOT: DWORD = 14057;
+pub const ERROR_SXS_XML_E_UNEXPECTEDEOF: DWORD = 14058;
+pub const ERROR_SXS_XML_E_BADPEREFINSUBSET: DWORD = 14059;
+pub const ERROR_SXS_XML_E_UNCLOSEDSTARTTAG: DWORD = 14060;
+pub const ERROR_SXS_XML_E_UNCLOSEDENDTAG: DWORD = 14061;
+pub const ERROR_SXS_XML_E_UNCLOSEDSTRING: DWORD = 14062;
+pub const ERROR_SXS_XML_E_UNCLOSEDCOMMENT: DWORD = 14063;
+pub const ERROR_SXS_XML_E_UNCLOSEDDECL: DWORD = 14064;
+pub const ERROR_SXS_XML_E_UNCLOSEDCDATA: DWORD = 14065;
+pub const ERROR_SXS_XML_E_RESERVEDNAMESPACE: DWORD = 14066;
+pub const ERROR_SXS_XML_E_INVALIDENCODING: DWORD = 14067;
+pub const ERROR_SXS_XML_E_INVALIDSWITCH: DWORD = 14068;
+pub const ERROR_SXS_XML_E_BADXMLCASE: DWORD = 14069;
+pub const ERROR_SXS_XML_E_INVALID_STANDALONE: DWORD = 14070;
+pub const ERROR_SXS_XML_E_UNEXPECTED_STANDALONE: DWORD = 14071;
+pub const ERROR_SXS_XML_E_INVALID_VERSION: DWORD = 14072;
+pub const ERROR_SXS_XML_E_MISSINGEQUALS: DWORD = 14073;
+pub const ERROR_SXS_PROTECTION_RECOVERY_FAILED: DWORD = 14074;
+pub const ERROR_SXS_PROTECTION_PUBLIC_KEY_TOO_SHORT: DWORD = 14075;
+pub const ERROR_SXS_PROTECTION_CATALOG_NOT_VALID: DWORD = 14076;
+pub const ERROR_SXS_UNTRANSLATABLE_HRESULT: DWORD = 14077;
+pub const ERROR_SXS_PROTECTION_CATALOG_FILE_MISSING: DWORD = 14078;
+pub const ERROR_SXS_MISSING_ASSEMBLY_IDENTITY_ATTRIBUTE: DWORD = 14079;
+pub const ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE_NAME: DWORD = 14080;
+pub const ERROR_SXS_ASSEMBLY_MISSING: DWORD = 14081;
+pub const ERROR_SXS_CORRUPT_ACTIVATION_STACK: DWORD = 14082;
+pub const ERROR_SXS_CORRUPTION: DWORD = 14083;
+pub const ERROR_SXS_EARLY_DEACTIVATION: DWORD = 14084;
+pub const ERROR_SXS_INVALID_DEACTIVATION: DWORD = 14085;
+pub const ERROR_SXS_MULTIPLE_DEACTIVATION: DWORD = 14086;
+pub const ERROR_SXS_PROCESS_TERMINATION_REQUESTED: DWORD = 14087;
+pub const ERROR_SXS_RELEASE_ACTIVATION_CONTEXT: DWORD = 14088;
+pub const ERROR_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY: DWORD = 14089;
+pub const ERROR_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE: DWORD = 14090;
+pub const ERROR_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME: DWORD = 14091;
+pub const ERROR_SXS_IDENTITY_DUPLICATE_ATTRIBUTE: DWORD = 14092;
+pub const ERROR_SXS_IDENTITY_PARSE_ERROR: DWORD = 14093;
+pub const ERROR_MALFORMED_SUBSTITUTION_STRING: DWORD = 14094;
+pub const ERROR_SXS_INCORRECT_PUBLIC_KEY_TOKEN: DWORD = 14095;
+pub const ERROR_UNMAPPED_SUBSTITUTION_STRING: DWORD = 14096;
+pub const ERROR_SXS_ASSEMBLY_NOT_LOCKED: DWORD = 14097;
+pub const ERROR_SXS_COMPONENT_STORE_CORRUPT: DWORD = 14098;
+pub const ERROR_ADVANCED_INSTALLER_FAILED: DWORD = 14099;
+pub const ERROR_XML_ENCODING_MISMATCH: DWORD = 14100;
+pub const ERROR_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT: DWORD = 14101;
+pub const ERROR_SXS_IDENTITIES_DIFFERENT: DWORD = 14102;
+pub const ERROR_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT: DWORD = 14103;
+pub const ERROR_SXS_FILE_NOT_PART_OF_ASSEMBLY: DWORD = 14104;
+pub const ERROR_SXS_MANIFEST_TOO_BIG: DWORD = 14105;
+pub const ERROR_SXS_SETTING_NOT_REGISTERED: DWORD = 14106;
+pub const ERROR_SXS_TRANSACTION_CLOSURE_INCOMPLETE: DWORD = 14107;
+pub const ERROR_SMI_PRIMITIVE_INSTALLER_FAILED: DWORD = 14108;
+pub const ERROR_GENERIC_COMMAND_FAILED: DWORD = 14109;
+pub const ERROR_SXS_FILE_HASH_MISSING: DWORD = 14110;
+pub const ERROR_IPSEC_QM_POLICY_EXISTS: DWORD = 13000;
+pub const ERROR_IPSEC_QM_POLICY_NOT_FOUND: DWORD = 13001;
+pub const ERROR_IPSEC_QM_POLICY_IN_USE: DWORD = 13002;
+pub const ERROR_IPSEC_MM_POLICY_EXISTS: DWORD = 13003;
+pub const ERROR_IPSEC_MM_POLICY_NOT_FOUND: DWORD = 13004;
+pub const ERROR_IPSEC_MM_POLICY_IN_USE: DWORD = 13005;
+pub const ERROR_IPSEC_MM_FILTER_EXISTS: DWORD = 13006;
+pub const ERROR_IPSEC_MM_FILTER_NOT_FOUND: DWORD = 13007;
+pub const ERROR_IPSEC_TRANSPORT_FILTER_EXISTS: DWORD = 13008;
+pub const ERROR_IPSEC_TRANSPORT_FILTER_NOT_FOUND: DWORD = 13009;
+pub const ERROR_IPSEC_MM_AUTH_EXISTS: DWORD = 13010;
+pub const ERROR_IPSEC_MM_AUTH_NOT_FOUND: DWORD = 13011;
+pub const ERROR_IPSEC_MM_AUTH_IN_USE: DWORD = 13012;
+pub const ERROR_IPSEC_DEFAULT_MM_POLICY_NOT_FOUND: DWORD = 13013;
+pub const ERROR_IPSEC_DEFAULT_MM_AUTH_NOT_FOUND: DWORD = 13014;
+pub const ERROR_IPSEC_DEFAULT_QM_POLICY_NOT_FOUND: DWORD = 13015;
+pub const ERROR_IPSEC_TUNNEL_FILTER_EXISTS: DWORD = 13016;
+pub const ERROR_IPSEC_TUNNEL_FILTER_NOT_FOUND: DWORD = 13017;
+pub const ERROR_IPSEC_MM_FILTER_PENDING_DELETION: DWORD = 13018;
+pub const ERROR_IPSEC_TRANSPORT_FILTER_PENDING_DELETION: DWORD = 13019;
+pub const ERROR_IPSEC_TUNNEL_FILTER_PENDING_DELETION: DWORD = 13020;
+pub const ERROR_IPSEC_MM_POLICY_PENDING_DELETION: DWORD = 13021;
+pub const ERROR_IPSEC_MM_AUTH_PENDING_DELETION: DWORD = 13022;
+pub const ERROR_IPSEC_QM_POLICY_PENDING_DELETION: DWORD = 13023;
+pub const ERROR_IPSEC_IKE_NEG_STATUS_BEGIN: DWORD = 13800;
+pub const ERROR_IPSEC_IKE_AUTH_FAIL: DWORD = 13801;
+pub const ERROR_IPSEC_IKE_ATTRIB_FAIL: DWORD = 13802;
+pub const ERROR_IPSEC_IKE_NEGOTIATION_PENDING: DWORD = 13803;
+pub const ERROR_IPSEC_IKE_GENERAL_PROCESSING_ERROR: DWORD = 13804;
+pub const ERROR_IPSEC_IKE_TIMED_OUT: DWORD = 13805;
+pub const ERROR_IPSEC_IKE_NO_CERT: DWORD = 13806;
+pub const ERROR_IPSEC_IKE_SA_DELETED: DWORD = 13807;
+pub const ERROR_IPSEC_IKE_SA_REAPED: DWORD = 13808;
+pub const ERROR_IPSEC_IKE_MM_ACQUIRE_DROP: DWORD = 13809;
+pub const ERROR_IPSEC_IKE_QM_ACQUIRE_DROP: DWORD = 13810;
+pub const ERROR_IPSEC_IKE_QUEUE_DROP_MM: DWORD = 13811;
+pub const ERROR_IPSEC_IKE_QUEUE_DROP_NO_MM: DWORD = 13812;
+pub const ERROR_IPSEC_IKE_DROP_NO_RESPONSE: DWORD = 13813;
+pub const ERROR_IPSEC_IKE_MM_DELAY_DROP: DWORD = 13814;
+pub const ERROR_IPSEC_IKE_QM_DELAY_DROP: DWORD = 13815;
+pub const ERROR_IPSEC_IKE_ERROR: DWORD = 13816;
+pub const ERROR_IPSEC_IKE_CRL_FAILED: DWORD = 13817;
+pub const ERROR_IPSEC_IKE_INVALID_KEY_USAGE: DWORD = 13818;
+pub const ERROR_IPSEC_IKE_INVALID_CERT_TYPE: DWORD = 13819;
+pub const ERROR_IPSEC_IKE_NO_PRIVATE_KEY: DWORD = 13820;
+pub const ERROR_IPSEC_IKE_DH_FAIL: DWORD = 13822;
+pub const ERROR_IPSEC_IKE_INVALID_HEADER: DWORD = 13824;
+pub const ERROR_IPSEC_IKE_NO_POLICY: DWORD = 13825;
+pub const ERROR_IPSEC_IKE_INVALID_SIGNATURE: DWORD = 13826;
+pub const ERROR_IPSEC_IKE_KERBEROS_ERROR: DWORD = 13827;
+pub const ERROR_IPSEC_IKE_NO_PUBLIC_KEY: DWORD = 13828;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR: DWORD = 13829;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_SA: DWORD = 13830;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_PROP: DWORD = 13831;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_TRANS: DWORD = 13832;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_KE: DWORD = 13833;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_ID: DWORD = 13834;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_CERT: DWORD = 13835;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_CERT_REQ: DWORD = 13836;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_HASH: DWORD = 13837;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_SIG: DWORD = 13838;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_NONCE: DWORD = 13839;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_NOTIFY: DWORD = 13840;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_DELETE: DWORD = 13841;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_VENDOR: DWORD = 13842;
+pub const ERROR_IPSEC_IKE_INVALID_PAYLOAD: DWORD = 13843;
+pub const ERROR_IPSEC_IKE_LOAD_SOFT_SA: DWORD = 13844;
+pub const ERROR_IPSEC_IKE_SOFT_SA_TORN_DOWN: DWORD = 13845;
+pub const ERROR_IPSEC_IKE_INVALID_COOKIE: DWORD = 13846;
+pub const ERROR_IPSEC_IKE_NO_PEER_CERT: DWORD = 13847;
+pub const ERROR_IPSEC_IKE_PEER_CRL_FAILED: DWORD = 13848;
+pub const ERROR_IPSEC_IKE_POLICY_CHANGE: DWORD = 13849;
+pub const ERROR_IPSEC_IKE_NO_MM_POLICY: DWORD = 13850;
+pub const ERROR_IPSEC_IKE_NOTCBPRIV: DWORD = 13851;
+pub const ERROR_IPSEC_IKE_SECLOADFAIL: DWORD = 13852;
+pub const ERROR_IPSEC_IKE_FAILSSPINIT: DWORD = 13853;
+pub const ERROR_IPSEC_IKE_FAILQUERYSSP: DWORD = 13854;
+pub const ERROR_IPSEC_IKE_SRVACQFAIL: DWORD = 13855;
+pub const ERROR_IPSEC_IKE_SRVQUERYCRED: DWORD = 13856;
+pub const ERROR_IPSEC_IKE_GETSPIFAIL: DWORD = 13857;
+pub const ERROR_IPSEC_IKE_INVALID_FILTER: DWORD = 13858;
+pub const ERROR_IPSEC_IKE_OUT_OF_MEMORY: DWORD = 13859;
+pub const ERROR_IPSEC_IKE_ADD_UPDATE_KEY_FAILED: DWORD = 13860;
+pub const ERROR_IPSEC_IKE_INVALID_POLICY: DWORD = 13861;
+pub const ERROR_IPSEC_IKE_UNKNOWN_DOI: DWORD = 13862;
+pub const ERROR_IPSEC_IKE_INVALID_SITUATION: DWORD = 13863;
+pub const ERROR_IPSEC_IKE_DH_FAILURE: DWORD = 13864;
+pub const ERROR_IPSEC_IKE_INVALID_GROUP: DWORD = 13865;
+pub const ERROR_IPSEC_IKE_ENCRYPT: DWORD = 13866;
+pub const ERROR_IPSEC_IKE_DECRYPT: DWORD = 13867;
+pub const ERROR_IPSEC_IKE_POLICY_MATCH: DWORD = 13868;
+pub const ERROR_IPSEC_IKE_UNSUPPORTED_ID: DWORD = 13869;
+pub const ERROR_IPSEC_IKE_INVALID_HASH: DWORD = 13870;
+pub const ERROR_IPSEC_IKE_INVALID_HASH_ALG: DWORD = 13871;
+pub const ERROR_IPSEC_IKE_INVALID_HASH_SIZE: DWORD = 13872;
+pub const ERROR_IPSEC_IKE_INVALID_ENCRYPT_ALG: DWORD = 13873;
+pub const ERROR_IPSEC_IKE_INVALID_AUTH_ALG: DWORD = 13874;
+pub const ERROR_IPSEC_IKE_INVALID_SIG: DWORD = 13875;
+pub const ERROR_IPSEC_IKE_LOAD_FAILED: DWORD = 13876;
+pub const ERROR_IPSEC_IKE_RPC_DELETE: DWORD = 13877;
+pub const ERROR_IPSEC_IKE_BENIGN_REINIT: DWORD = 13878;
+pub const ERROR_IPSEC_IKE_INVALID_RESPONDER_LIFETIME_NOTIFY: DWORD = 13879;
+pub const ERROR_IPSEC_IKE_INVALID_CERT_KEYLEN: DWORD = 13881;
+pub const ERROR_IPSEC_IKE_MM_LIMIT: DWORD = 13882;
+pub const ERROR_IPSEC_IKE_NEGOTIATION_DISABLED: DWORD = 13883;
+/*pub const ERROR_IPSEC_IKE_NEG_STATUS_END: DWORD = 13884)*/
+pub const ERROR_IPSEC_IKE_QM_LIMIT: DWORD = 13884;
+pub const ERROR_IPSEC_IKE_MM_EXPIRED: DWORD = 13885;
+pub const ERROR_IPSEC_IKE_PEER_MM_ASSUMED_INVALID: DWORD = 13886;
+pub const ERROR_IPSEC_IKE_CERT_CHAIN_POLICY_MISMATCH: DWORD = 13887;
+pub const ERROR_IPSEC_IKE_UNEXPECTED_MESSAGE_ID: DWORD = 13888;
+pub const ERROR_IPSEC_IKE_INVALID_AUTH_PAYLOAD: DWORD = 13889;
+pub const ERROR_IPSEC_IKE_DOS_COOKIE_SENT: DWORD = 13890;
+pub const ERROR_IPSEC_IKE_SHUTTING_DOWN: DWORD = 13891;
+pub const ERROR_IPSEC_IKE_CGA_AUTH_FAILED: DWORD = 13892;
+pub const ERROR_IPSEC_IKE_PROCESS_ERR_NATOA: DWORD = 13893;
+pub const ERROR_IPSEC_IKE_INVALID_MM_FOR_QM: DWORD = 13894;
+pub const ERROR_IPSEC_IKE_QM_EXPIRED: DWORD = 13895;
+pub const ERROR_IPSEC_IKE_TOO_MANY_FILTERS: DWORD = 13896;
+pub const ERROR_IPSEC_IKE_NEG_STATUS_END: DWORD = 13897;
+pub const ERROR_IPSEC_IKE_KILL_DUMMY_NAP_TUNNEL: DWORD = 13898;
+pub const ERROR_IPSEC_IKE_INNER_IP_ASSIGNMENT_FAILURE: DWORD = 13899;
+pub const ERROR_IPSEC_IKE_REQUIRE_CP_PAYLOAD_MISSING: DWORD = 13900;
+pub const ERROR_IPSEC_KEY_MODULE_IMPERSONATION_NEGOTIATION_PENDING: DWORD = 13901;
+pub const ERROR_IPSEC_IKE_COEXISTENCE_SUPPRESS: DWORD = 13902;
+pub const ERROR_IPSEC_IKE_RATELIMIT_DROP: DWORD = 13903;
+pub const ERROR_IPSEC_IKE_PEER_DOESNT_SUPPORT_MOBIKE: DWORD = 13904;
+pub const ERROR_IPSEC_IKE_AUTHORIZATION_FAILURE: DWORD = 13905;
+pub const ERROR_IPSEC_IKE_STRONG_CRED_AUTHORIZATION_FAILURE: DWORD = 13906;
+pub const ERROR_IPSEC_IKE_AUTHORIZATION_FAILURE_WITH_OPTIONAL_RETRY: DWORD = 13907;
+pub const ERROR_IPSEC_IKE_STRONG_CRED_AUTHORIZATION_AND_CERTMAP_FAILURE: DWORD = 13908;
+pub const ERROR_IPSEC_IKE_NEG_STATUS_EXTENDED_END: DWORD = 13909;
+pub const ERROR_IPSEC_BAD_SPI: DWORD = 13910;
+pub const ERROR_IPSEC_SA_LIFETIME_EXPIRED: DWORD = 13911;
+pub const ERROR_IPSEC_WRONG_SA: DWORD = 13912;
+pub const ERROR_IPSEC_REPLAY_CHECK_FAILED: DWORD = 13913;
+pub const ERROR_IPSEC_INVALID_PACKET: DWORD = 13914;
+pub const ERROR_IPSEC_INTEGRITY_CHECK_FAILED: DWORD = 13915;
+pub const ERROR_IPSEC_CLEAR_TEXT_DROP: DWORD = 13916;
+pub const ERROR_IPSEC_AUTH_FIREWALL_DROP: DWORD = 13917;
+pub const ERROR_IPSEC_THROTTLE_DROP: DWORD = 13918;
+pub const ERROR_IPSEC_DOSP_BLOCK: DWORD = 13925;
+pub const ERROR_IPSEC_DOSP_RECEIVED_MULTICAST: DWORD = 13926;
+pub const ERROR_IPSEC_DOSP_INVALID_PACKET: DWORD = 13927;
+pub const ERROR_IPSEC_DOSP_STATE_LOOKUP_FAILED: DWORD = 13928;
+pub const ERROR_IPSEC_DOSP_MAX_ENTRIES: DWORD = 13929;
+pub const ERROR_IPSEC_DOSP_KEYMOD_NOT_ALLOWED: DWORD = 13930;
+pub const ERROR_IPSEC_DOSP_NOT_INSTALLED: DWORD = 13931;
+pub const ERROR_IPSEC_DOSP_MAX_PER_IP_RATELIMIT_QUEUES: DWORD = 13932;
+pub const ERROR_EVT_INVALID_CHANNEL_PATH: DWORD = 15000;
+pub const ERROR_EVT_INVALID_QUERY: DWORD = 15001;
+pub const ERROR_EVT_PUBLISHER_METADATA_NOT_FOUND: DWORD = 15002;
+pub const ERROR_EVT_EVENT_TEMPLATE_NOT_FOUND: DWORD = 15003;
+pub const ERROR_EVT_INVALID_PUBLISHER_NAME: DWORD = 15004;
+pub const ERROR_EVT_INVALID_EVENT_DATA: DWORD = 15005;
+pub const ERROR_EVT_CHANNEL_NOT_FOUND: DWORD = 15007;
+pub const ERROR_EVT_MALFORMED_XML_TEXT: DWORD = 15008;
+pub const ERROR_EVT_SUBSCRIPTION_TO_DIRECT_CHANNEL: DWORD = 15009;
+pub const ERROR_EVT_CONFIGURATION_ERROR: DWORD = 15010;
+pub const ERROR_EVT_QUERY_RESULT_STALE: DWORD = 15011;
+pub const ERROR_EVT_QUERY_RESULT_INVALID_POSITION: DWORD = 15012;
+pub const ERROR_EVT_NON_VALIDATING_MSXML: DWORD = 15013;
+pub const ERROR_EVT_FILTER_ALREADYSCOPED: DWORD = 15014;
+pub const ERROR_EVT_FILTER_NOTELTSET: DWORD = 15015;
+pub const ERROR_EVT_FILTER_INVARG: DWORD = 15016;
+pub const ERROR_EVT_FILTER_INVTEST: DWORD = 15017;
+pub const ERROR_EVT_FILTER_INVTYPE: DWORD = 15018;
+pub const ERROR_EVT_FILTER_PARSEERR: DWORD = 15019;
+pub const ERROR_EVT_FILTER_UNSUPPORTEDOP: DWORD = 15020;
+pub const ERROR_EVT_FILTER_UNEXPECTEDTOKEN: DWORD = 15021;
+pub const ERROR_EVT_INVALID_OPERATION_OVER_ENABLED_DIRECT_CHANNEL: DWORD = 15022;
+pub const ERROR_EVT_INVALID_CHANNEL_PROPERTY_VALUE: DWORD = 15023;
+pub const ERROR_EVT_INVALID_PUBLISHER_PROPERTY_VALUE: DWORD = 15024;
+pub const ERROR_EVT_CHANNEL_CANNOT_ACTIVATE: DWORD = 15025;
+pub const ERROR_EVT_FILTER_TOO_COMPLEX: DWORD = 15026;
+pub const ERROR_EVT_MESSAGE_NOT_FOUND: DWORD = 15027;
+pub const ERROR_EVT_MESSAGE_ID_NOT_FOUND: DWORD = 15028;
+pub const ERROR_EVT_UNRESOLVED_VALUE_INSERT: DWORD = 15029;
+pub const ERROR_EVT_UNRESOLVED_PARAMETER_INSERT: DWORD = 15030;
+pub const ERROR_EVT_MAX_INSERTS_REACHED: DWORD = 15031;
+pub const ERROR_EVT_EVENT_DEFINITION_NOT_FOUND: DWORD = 15032;
+pub const ERROR_EVT_MESSAGE_LOCALE_NOT_FOUND: DWORD = 15033;
+pub const ERROR_EVT_VERSION_TOO_OLD: DWORD = 15034;
+pub const ERROR_EVT_VERSION_TOO_NEW: DWORD = 15035;
+pub const ERROR_EVT_CANNOT_OPEN_CHANNEL_OF_QUERY: DWORD = 15036;
+pub const ERROR_EVT_PUBLISHER_DISABLED: DWORD = 15037;
+pub const ERROR_EVT_FILTER_OUT_OF_RANGE: DWORD = 15038;
+pub const ERROR_EC_SUBSCRIPTION_CANNOT_ACTIVATE: DWORD = 15080;
+pub const ERROR_EC_LOG_DISABLED: DWORD = 15081;
+pub const ERROR_EC_CIRCULAR_FORWARDING: DWORD = 15082;
+pub const ERROR_EC_CREDSTORE_FULL: DWORD = 15083;
+pub const ERROR_EC_CRED_NOT_FOUND: DWORD = 15084;
+pub const ERROR_EC_NO_ACTIVE_CHANNEL: DWORD = 15085;
+pub const ERROR_MUI_FILE_NOT_FOUND: DWORD = 15100;
+pub const ERROR_MUI_INVALID_FILE: DWORD = 15101;
+pub const ERROR_MUI_INVALID_RC_CONFIG: DWORD = 15102;
+pub const ERROR_MUI_INVALID_LOCALE_NAME: DWORD = 15103;
+pub const ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME: DWORD = 15104;
+pub const ERROR_MUI_FILE_NOT_LOADED: DWORD = 15105;
+pub const ERROR_RESOURCE_ENUM_USER_STOP: DWORD = 15106;
+pub const ERROR_MUI_INTLSETTINGS_UILANG_NOT_INSTALLED: DWORD = 15107;
+pub const ERROR_MUI_INTLSETTINGS_INVALID_LOCALE_NAME: DWORD = 15108;
+pub const ERROR_MRM_RUNTIME_NO_DEFAULT_OR_NEUTRAL_RESOURCE: DWORD = 15110;
+pub const ERROR_MRM_INVALID_PRICONFIG: DWORD = 15111;
+pub const ERROR_MRM_INVALID_FILE_TYPE: DWORD = 15112;
+pub const ERROR_MRM_UNKNOWN_QUALIFIER: DWORD = 15113;
+pub const ERROR_MRM_INVALID_QUALIFIER_VALUE: DWORD = 15114;
+pub const ERROR_MRM_NO_CANDIDATE: DWORD = 15115;
+pub const ERROR_MRM_NO_MATCH_OR_DEFAULT_CANDIDATE: DWORD = 15116;
+pub const ERROR_MRM_RESOURCE_TYPE_MISMATCH: DWORD = 15117;
+pub const ERROR_MRM_DUPLICATE_MAP_NAME: DWORD = 15118;
+pub const ERROR_MRM_DUPLICATE_ENTRY: DWORD = 15119;
+pub const ERROR_MRM_INVALID_RESOURCE_IDENTIFIER: DWORD = 15120;
+pub const ERROR_MRM_FILEPATH_TOO_LONG: DWORD = 15121;
+pub const ERROR_MRM_UNSUPPORTED_DIRECTORY_TYPE: DWORD = 15122;
+pub const ERROR_MRM_INVALID_PRI_FILE: DWORD = 15126;
+pub const ERROR_MRM_NAMED_RESOURCE_NOT_FOUND: DWORD = 15127;
+pub const ERROR_MRM_MAP_NOT_FOUND: DWORD = 15135;
+pub const ERROR_MRM_UNSUPPORTED_PROFILE_TYPE: DWORD = 15136;
+pub const ERROR_MRM_INVALID_QUALIFIER_OPERATOR: DWORD = 15137;
+pub const ERROR_MRM_INDETERMINATE_QUALIFIER_VALUE: DWORD = 15138;
+pub const ERROR_MRM_AUTOMERGE_ENABLED: DWORD = 15139;
+pub const ERROR_MRM_TOO_MANY_RESOURCES: DWORD = 15140;
+pub const ERROR_MCA_INVALID_CAPABILITIES_STRING: DWORD = 15200;
+pub const ERROR_MCA_INVALID_VCP_VERSION: DWORD = 15201;
+pub const ERROR_MCA_MONITOR_VIOLATES_MCCS_SPECIFICATION: DWORD = 15202;
+pub const ERROR_MCA_MCCS_VERSION_MISMATCH: DWORD = 15203;
+pub const ERROR_MCA_UNSUPPORTED_MCCS_VERSION: DWORD = 15204;
+pub const ERROR_MCA_INTERNAL_ERROR: DWORD = 15205;
+pub const ERROR_MCA_INVALID_TECHNOLOGY_TYPE_RETURNED: DWORD = 15206;
+pub const ERROR_MCA_UNSUPPORTED_COLOR_TEMPERATURE: DWORD = 15207;
+pub const ERROR_AMBIGUOUS_SYSTEM_DEVICE: DWORD = 15250;
+pub const ERROR_SYSTEM_DEVICE_NOT_FOUND: DWORD = 15299;
+pub const ERROR_HASH_NOT_SUPPORTED: DWORD = 15300;
+pub const ERROR_HASH_NOT_PRESENT: DWORD = 15301;
+pub const ERROR_SECONDARY_IC_PROVIDER_NOT_REGISTERED: DWORD = 15321;
+pub const ERROR_GPIO_CLIENT_INFORMATION_INVALID: DWORD = 15322;
+pub const ERROR_GPIO_VERSION_NOT_SUPPORTED: DWORD = 15323;
+pub const ERROR_GPIO_INVALID_REGISTRATION_PACKET: DWORD = 15324;
+pub const ERROR_GPIO_OPERATION_DENIED: DWORD = 15325;
+pub const ERROR_GPIO_INCOMPATIBLE_CONNECT_MODE: DWORD = 15326;
+pub const ERROR_GPIO_INTERRUPT_ALREADY_UNMASKED: DWORD = 15327;
+pub const ERROR_CANNOT_SWITCH_RUNLEVEL: DWORD = 15400;
+pub const ERROR_INVALID_RUNLEVEL_SETTING: DWORD = 15401;
+pub const ERROR_RUNLEVEL_SWITCH_TIMEOUT: DWORD = 15402;
+pub const ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT: DWORD = 15403;
+pub const ERROR_RUNLEVEL_SWITCH_IN_PROGRESS: DWORD = 15404;
+pub const ERROR_SERVICES_FAILED_AUTOSTART: DWORD = 15405;
+pub const ERROR_COM_TASK_STOP_PENDING: DWORD = 15501;
+pub const ERROR_INSTALL_OPEN_PACKAGE_FAILED: DWORD = 15600;
+pub const ERROR_INSTALL_PACKAGE_NOT_FOUND: DWORD = 15601;
+pub const ERROR_INSTALL_INVALID_PACKAGE: DWORD = 15602;
+pub const ERROR_INSTALL_RESOLVE_DEPENDENCY_FAILED: DWORD = 15603;
+pub const ERROR_INSTALL_OUT_OF_DISK_SPACE: DWORD = 15604;
+pub const ERROR_INSTALL_NETWORK_FAILURE: DWORD = 15605;
+pub const ERROR_INSTALL_REGISTRATION_FAILURE: DWORD = 15606;
+pub const ERROR_INSTALL_DEREGISTRATION_FAILURE: DWORD = 15607;
+pub const ERROR_INSTALL_CANCEL: DWORD = 15608;
+pub const ERROR_INSTALL_FAILED: DWORD = 15609;
+pub const ERROR_REMOVE_FAILED: DWORD = 15610;
+pub const ERROR_PACKAGE_ALREADY_EXISTS: DWORD = 15611;
+pub const ERROR_NEEDS_REMEDIATION: DWORD = 15612;
+pub const ERROR_INSTALL_PREREQUISITE_FAILED: DWORD = 15613;
+pub const ERROR_PACKAGE_REPOSITORY_CORRUPTED: DWORD = 15614;
+pub const ERROR_INSTALL_POLICY_FAILURE: DWORD = 15615;
+pub const ERROR_PACKAGE_UPDATING: DWORD = 15616;
+pub const ERROR_DEPLOYMENT_BLOCKED_BY_POLICY: DWORD = 15617;
+pub const ERROR_PACKAGES_IN_USE: DWORD = 15618;
+pub const ERROR_RECOVERY_FILE_CORRUPT: DWORD = 15619;
+pub const ERROR_INVALID_STAGED_SIGNATURE: DWORD = 15620;
+pub const ERROR_DELETING_EXISTING_APPLICATIONDATA_STORE_FAILED: DWORD = 15621;
+pub const ERROR_INSTALL_PACKAGE_DOWNGRADE: DWORD = 15622;
+pub const ERROR_SYSTEM_NEEDS_REMEDIATION: DWORD = 15623;
+pub const ERROR_APPX_INTEGRITY_FAILURE_CLR_NGEN: DWORD = 15624;
+pub const ERROR_RESILIENCY_FILE_CORRUPT: DWORD = 15625;
+pub const ERROR_INSTALL_FIREWALL_SERVICE_NOT_RUNNING: DWORD = 15626;
+pub const ERROR_STATE_LOAD_STORE_FAILED: DWORD = 15800;
+pub const ERROR_STATE_GET_VERSION_FAILED: DWORD = 15801;
+pub const ERROR_STATE_SET_VERSION_FAILED: DWORD = 15802;
+pub const ERROR_STATE_STRUCTURED_RESET_FAILED: DWORD = 15803;
+pub const ERROR_STATE_OPEN_CONTAINER_FAILED: DWORD = 15804;
+pub const ERROR_STATE_CREATE_CONTAINER_FAILED: DWORD = 15805;
+pub const ERROR_STATE_DELETE_CONTAINER_FAILED: DWORD = 15806;
+pub const ERROR_STATE_READ_SETTING_FAILED: DWORD = 15807;
+pub const ERROR_STATE_WRITE_SETTING_FAILED: DWORD = 15808;
+pub const ERROR_STATE_DELETE_SETTING_FAILED: DWORD = 15809;
+pub const ERROR_STATE_QUERY_SETTING_FAILED: DWORD = 15810;
+pub const ERROR_STATE_READ_COMPOSITE_SETTING_FAILED: DWORD = 15811;
+pub const ERROR_STATE_WRITE_COMPOSITE_SETTING_FAILED: DWORD = 15812;
+pub const ERROR_STATE_ENUMERATE_CONTAINER_FAILED: DWORD = 15813;
+pub const ERROR_STATE_ENUMERATE_SETTINGS_FAILED: DWORD = 15814;
+pub const ERROR_STATE_COMPOSITE_SETTING_VALUE_SIZE_LIMIT_EXCEEDED: DWORD = 15815;
+pub const ERROR_STATE_SETTING_VALUE_SIZE_LIMIT_EXCEEDED: DWORD = 15816;
+pub const ERROR_STATE_SETTING_NAME_SIZE_LIMIT_EXCEEDED: DWORD = 15817;
+pub const ERROR_STATE_CONTAINER_NAME_SIZE_LIMIT_EXCEEDED: DWORD = 15818;
+pub const ERROR_API_UNAVAILABLE: DWORD = 15841;
+pub const ERROR_AUDITING_DISABLED: DWORD = 0xC0090001;
+pub const ERROR_ALL_SIDS_FILTERED: DWORD = 0xC0090002;
+
+pub const WSABASEERR: c_int = 10000;
+pub const WSAEINTR: c_int = WSABASEERR + 4;
+pub const WSAEBADF: c_int = WSABASEERR + 9;
+pub const WSAEACCES: c_int = WSABASEERR + 13;
+pub const WSAEFAULT: c_int = WSABASEERR + 14;
+pub const WSAEINVAL: c_int = WSABASEERR + 22;
+pub const WSAEMFILE: c_int = WSABASEERR + 24;
+pub const WSAEWOULDBLOCK: c_int = WSABASEERR + 35;
+pub const WSAEINPROGRESS: c_int = WSABASEERR + 36;
+pub const WSAEALREADY: c_int = WSABASEERR + 37;
+pub const WSAENOTSOCK: c_int = WSABASEERR + 38;
+pub const WSAEDESTADDRREQ: c_int = WSABASEERR + 39;
+pub const WSAEMSGSIZE: c_int = WSABASEERR + 40;
+pub const WSAEPROTOTYPE: c_int = WSABASEERR + 41;
+pub const WSAENOPROTOOPT: c_int = WSABASEERR + 42;
+pub const WSAEPROTONOSUPPORT: c_int = WSABASEERR + 43;
+pub const WSAESOCKTNOSUPPORT: c_int = WSABASEERR + 44;
+pub const WSAEOPNOTSUPP: c_int = WSABASEERR + 45;
+pub const WSAEPFNOSUPPORT: c_int = WSABASEERR + 46;
+pub const WSAEAFNOSUPPORT: c_int = WSABASEERR + 47;
+pub const WSAEADDRINUSE: c_int = WSABASEERR + 48;
+pub const WSAEADDRNOTAVAIL: c_int = WSABASEERR + 49;
+pub const WSAENETDOWN: c_int = WSABASEERR + 50;
+pub const WSAENETUNREACH: c_int = WSABASEERR + 51;
+pub const WSAENETRESET: c_int = WSABASEERR + 52;
+pub const WSAECONNABORTED: c_int = WSABASEERR + 53;
+pub const WSAECONNRESET: c_int = WSABASEERR + 54;
+pub const WSAENOBUFS: c_int = WSABASEERR + 55;
+pub const WSAEISCONN: c_int = WSABASEERR + 56;
+pub const WSAENOTCONN: c_int = WSABASEERR + 57;
+pub const WSAESHUTDOWN: c_int = WSABASEERR + 58;
+pub const WSAETOOMANYREFS: c_int = WSABASEERR + 59;
+pub const WSAETIMEDOUT: c_int = WSABASEERR + 60;
+pub const WSAECONNREFUSED: c_int = WSABASEERR + 61;
+pub const WSAELOOP: c_int = WSABASEERR + 62;
+pub const WSAENAMETOOLONG: c_int = WSABASEERR + 63;
+pub const WSAEHOSTDOWN: c_int = WSABASEERR + 64;
+pub const WSAEHOSTUNREACH: c_int = WSABASEERR + 65;
+pub const WSAENOTEMPTY: c_int = WSABASEERR + 66;
+pub const WSAEPROCLIM: c_int = WSABASEERR + 67;
+pub const WSAEUSERS: c_int = WSABASEERR + 68;
+pub const WSAEDQUOT: c_int = WSABASEERR + 69;
+pub const WSAESTALE: c_int = WSABASEERR + 70;
+pub const WSAEREMOTE: c_int = WSABASEERR + 71;
+pub const WSASYSNOTREADY: c_int = WSABASEERR + 91;
+pub const WSAVERNOTSUPPORTED: c_int = WSABASEERR + 92;
+pub const WSANOTINITIALISED: c_int = WSABASEERR + 93;
+pub const WSAEDISCON: c_int = WSABASEERR + 101;
+pub const WSAENOMORE: c_int = WSABASEERR + 102;
+pub const WSAECANCELLED: c_int = WSABASEERR + 103;
+pub const WSAEINVALIDPROCTABLE: c_int = WSABASEERR + 104;
+pub const WSAEINVALIDPROVIDER: c_int = WSABASEERR + 105;
+pub const WSAEPROVIDERFAILEDINIT: c_int = WSABASEERR + 106;
+pub const WSASYSCALLFAILURE: c_int = WSABASEERR + 107;
+pub const WSASERVICE_NOT_FOUND: c_int = WSABASEERR + 108;
+pub const WSATYPE_NOT_FOUND: c_int = WSABASEERR + 109;
+pub const WSA_E_NO_MORE: c_int = WSABASEERR + 110;
+pub const WSA_E_CANCELLED: c_int = WSABASEERR + 111;
+pub const WSAEREFUSED: c_int = WSABASEERR + 112;
+pub const WSAHOST_NOT_FOUND: c_int = WSABASEERR + 1001;
+pub const WSATRY_AGAIN: c_int = WSABASEERR + 1002;
+pub const WSANO_RECOVERY: c_int = WSABASEERR + 1003;
+pub const WSANO_DATA: c_int = WSABASEERR + 1004;
+pub const WSA_QOS_RECEIVERS: c_int = WSABASEERR + 1005;
+pub const WSA_QOS_SENDERS: c_int = WSABASEERR + 1006;
+pub const WSA_QOS_NO_SENDERS: c_int = WSABASEERR + 1007;
+pub const WSA_QOS_NO_RECEIVERS: c_int = WSABASEERR + 1008;
+pub const WSA_QOS_REQUEST_CONFIRMED: c_int = WSABASEERR + 1009;
+pub const WSA_QOS_ADMISSION_FAILURE: c_int = WSABASEERR + 1010;
+pub const WSA_QOS_POLICY_FAILURE: c_int = WSABASEERR + 1011;
+pub const WSA_QOS_BAD_STYLE: c_int = WSABASEERR + 1012;
+pub const WSA_QOS_BAD_OBJECT: c_int = WSABASEERR + 1013;
+pub const WSA_QOS_TRAFFIC_CTRL_ERROR: c_int = WSABASEERR + 1014;
+pub const WSA_QOS_GENERIC_ERROR: c_int = WSABASEERR + 1015;
+pub const WSA_QOS_ESERVICETYPE: c_int = WSABASEERR + 1016;
+pub const WSA_QOS_EFLOWSPEC: c_int = WSABASEERR + 1017;
+pub const WSA_QOS_EPROVSPECBUF: c_int = WSABASEERR + 1018;
+pub const WSA_QOS_EFILTERSTYLE: c_int = WSABASEERR + 1019;
+pub const WSA_QOS_EFILTERTYPE: c_int = WSABASEERR + 1020;
+pub const WSA_QOS_EFILTERCOUNT: c_int = WSABASEERR + 1021;
+pub const WSA_QOS_EOBJLENGTH: c_int = WSABASEERR + 1022;
+pub const WSA_QOS_EFLOWCOUNT: c_int = WSABASEERR + 1023;
+pub const WSA_QOS_EUNKNOWNPSOBJ: c_int = WSABASEERR + 1024;
+pub const WSA_QOS_EUNKOWNPSOBJ: c_int = WSA_QOS_EUNKNOWNPSOBJ;
+pub const WSA_QOS_EPOLICYOBJ: c_int = WSABASEERR + 1025;
+pub const WSA_QOS_EFLOWDESC: c_int = WSABASEERR + 1026;
+pub const WSA_QOS_EPSFLOWSPEC: c_int = WSABASEERR + 1027;
+pub const WSA_QOS_EPSFILTERSPEC: c_int = WSABASEERR + 1028;
+pub const WSA_QOS_ESDMODEOBJ: c_int = WSABASEERR + 1029;
+pub const WSA_QOS_ESHAPERATEOBJ: c_int = WSABASEERR + 1030;
+pub const WSA_QOS_RESERVED_PETYPE: c_int = WSABASEERR + 1031;
diff --git a/library/std/src/sys/windows/cmath.rs b/library/std/src/sys/windows/cmath.rs
new file mode 100644
index 000000000..1a5421fac
--- /dev/null
+++ b/library/std/src/sys/windows/cmath.rs
@@ -0,0 +1,92 @@
+#![cfg(not(test))]
+
+use libc::{c_double, c_float};
+
+extern "C" {
+ 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(all(target_env = "msvc", target_arch = "x86")))]
+mod shims {
+ use libc::c_float;
+
+ extern "C" {
+ 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 32-bit x86 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(all(target_env = "msvc", target_arch = "x86"))]
+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/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs
new file mode 100644
index 000000000..ccc90177a
--- /dev/null
+++ b/library/std/src/sys/windows/compat.rs
@@ -0,0 +1,273 @@
+//! A "compatibility layer" for supporting older versions of Windows
+//!
+//! The standard library uses some Windows API functions that are not present
+//! on older versions of Windows. (Note that the oldest version of Windows
+//! that Rust supports is Windows 7 (client) and Windows Server 2008 (server).)
+//! This module implements a form of delayed DLL import binding, using
+//! `GetModuleHandle` and `GetProcAddress` to look up DLL entry points at
+//! runtime.
+//!
+//! This implementation uses a static initializer to look up the DLL entry
+//! points. The CRT (C runtime) executes static initializers before `main`
+//! is called (for binaries) and before `DllMain` is called (for DLLs).
+//! This is the ideal time to look up DLL imports, because we are guaranteed
+//! that no other threads will attempt to call these entry points. Thus,
+//! we can look up the imports and store them in `static mut` fields
+//! without any synchronization.
+//!
+//! This has an additional advantage: Because the DLL import lookup happens
+//! at module initialization, the cost of these lookups is deterministic,
+//! and is removed from the code paths that actually call the DLL imports.
+//! That is, there is no unpredictable "cache miss" that occurs when calling
+//! a DLL import. For applications that benefit from predictable delays,
+//! this is a benefit. This also eliminates the comparison-and-branch
+//! from the hot path.
+//!
+//! Currently, the standard library uses only a small number of dynamic
+//! DLL imports. If this number grows substantially, then the cost of
+//! performing all of the lookups at initialization time might become
+//! substantial.
+//!
+//! The mechanism of registering a static initializer with the CRT is
+//! documented in
+//! [CRT Initialization](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-160).
+//! It works by contributing a global symbol to the `.CRT$XCU` section.
+//! The linker builds a table of all static initializer functions.
+//! The CRT startup code then iterates that table, calling each
+//! initializer function.
+//!
+//! # **WARNING!!*
+//! The environment that a static initializer function runs in is highly
+//! constrained. There are **many** restrictions on what static initializers
+//! can safely do. Static initializer functions **MUST NOT** do any of the
+//! following (this list is not comprehensive):
+//! * touch any other static field that is used by a different static
+//! initializer, because the order that static initializers run in
+//! is not defined.
+//! * call `LoadLibrary` or any other function that acquires the DLL
+//! loader lock.
+//! * call any Rust function or CRT function that touches any static
+//! (global) state.
+
+use crate::ffi::{c_void, CStr};
+use crate::ptr::NonNull;
+use crate::sys::c;
+
+/// Helper macro for creating CStrs from literals and symbol names.
+macro_rules! ansi_str {
+ (sym $ident:ident) => {{
+ #[allow(unused_unsafe)]
+ crate::sys::compat::const_cstr_from_bytes(concat!(stringify!($ident), "\0").as_bytes())
+ }};
+ ($lit:literal) => {{ crate::sys::compat::const_cstr_from_bytes(concat!($lit, "\0").as_bytes()) }};
+}
+
+/// Creates a C string wrapper from a byte slice, in a constant context.
+///
+/// This is a utility function used by the [`ansi_str`] macro.
+///
+/// # Panics
+///
+/// Panics if the slice is not null terminated or contains nulls, except as the last item
+pub(crate) const fn const_cstr_from_bytes(bytes: &'static [u8]) -> &'static CStr {
+ if !matches!(bytes.last(), Some(&0)) {
+ panic!("A CStr must be null terminated");
+ }
+ let mut i = 0;
+ // At this point `len()` is at least 1.
+ while i < bytes.len() - 1 {
+ if bytes[i] == 0 {
+ panic!("A CStr must not have interior nulls")
+ }
+ i += 1;
+ }
+ // SAFETY: The safety is ensured by the above checks.
+ unsafe { crate::ffi::CStr::from_bytes_with_nul_unchecked(bytes) }
+}
+
+#[used]
+#[link_section = ".CRT$XCU"]
+static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
+
+/// This is where the magic preloading of symbols happens.
+///
+/// Note that any functions included here will be unconditionally included in
+/// the final binary, regardless of whether or not they're actually used.
+///
+/// Therefore, this is limited to `compat_fn_optional` functions which must be
+/// preloaded and any functions which may be more time sensitive, even for the first call.
+unsafe extern "C" fn init() {
+ // There is no locking here. This code is executed before main() is entered, and
+ // is guaranteed to be single-threaded.
+ //
+ // DO NOT do anything interesting or complicated in this function! DO NOT call
+ // any Rust functions or CRT functions if those functions touch any global state,
+ // because this function runs during global initialization. For example, DO NOT
+ // do any dynamic allocation, don't call LoadLibrary, etc.
+
+ if let Some(synch) = Module::new(c::SYNCH_API) {
+ // These are optional and so we must manually attempt to load them
+ // before they can be used.
+ c::WaitOnAddress::preload(synch);
+ c::WakeByAddressSingle::preload(synch);
+ }
+
+ if let Some(kernel32) = Module::new(c::KERNEL32) {
+ // Preloading this means getting a precise time will be as fast as possible.
+ c::GetSystemTimePreciseAsFileTime::preload(kernel32);
+ }
+}
+
+/// Represents a loaded module.
+///
+/// Note that the modules std depends on must not be unloaded.
+/// Therefore a `Module` is always valid for the lifetime of std.
+#[derive(Copy, Clone)]
+pub(in crate::sys) struct Module(NonNull<c_void>);
+impl Module {
+ /// Try to get a handle to a loaded module.
+ ///
+ /// # SAFETY
+ ///
+ /// This should only be use for modules that exist for the lifetime of std
+ /// (e.g. kernel32 and ntdll).
+ pub unsafe fn new(name: &CStr) -> Option<Self> {
+ // SAFETY: A CStr is always null terminated.
+ let module = c::GetModuleHandleA(name.as_ptr());
+ NonNull::new(module).map(Self)
+ }
+
+ // Try to get the address of a function.
+ pub fn proc_address(self, name: &CStr) -> Option<NonNull<c_void>> {
+ // SAFETY:
+ // `self.0` will always be a valid module.
+ // A CStr is always null terminated.
+ let proc = unsafe { c::GetProcAddress(self.0.as_ptr(), name.as_ptr()) };
+ NonNull::new(proc)
+ }
+}
+
+/// Load a function or use a fallback implementation if that fails.
+macro_rules! compat_fn_with_fallback {
+ (pub static $module:ident: &CStr = $name:expr; $(
+ $(#[$meta:meta])*
+ pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block
+ )*) => (
+ pub static $module: &CStr = $name;
+ $(
+ $(#[$meta])*
+ pub mod $symbol {
+ #[allow(unused_imports)]
+ use super::*;
+ use crate::mem;
+ use crate::ffi::CStr;
+ use crate::sync::atomic::{AtomicPtr, Ordering};
+ use crate::sys::compat::Module;
+
+ type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
+
+ /// `PTR` contains a function pointer to one of three functions.
+ /// It starts with the `load` function.
+ /// When that is called it attempts to load the requested symbol.
+ /// If it succeeds, `PTR` is set to the address of that symbol.
+ /// If it fails, then `PTR` is set to `fallback`.
+ static PTR: AtomicPtr<c_void> = AtomicPtr::new(load as *mut _);
+
+ unsafe extern "system" fn load($($argname: $argtype),*) -> $rettype {
+ let func = load_from_module(Module::new($module));
+ func($($argname),*)
+ }
+
+ fn load_from_module(module: Option<Module>) -> F {
+ unsafe {
+ static SYMBOL_NAME: &CStr = ansi_str!(sym $symbol);
+ if let Some(f) = module.and_then(|m| m.proc_address(SYMBOL_NAME)) {
+ PTR.store(f.as_ptr(), Ordering::Relaxed);
+ mem::transmute(f)
+ } else {
+ PTR.store(fallback as *mut _, Ordering::Relaxed);
+ fallback
+ }
+ }
+ }
+
+ #[allow(unused_variables)]
+ unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype {
+ $fallback_body
+ }
+
+ #[allow(unused)]
+ pub(in crate::sys) fn preload(module: Module) {
+ load_from_module(Some(module));
+ }
+
+ #[inline(always)]
+ pub unsafe fn call($($argname: $argtype),*) -> $rettype {
+ let func: F = mem::transmute(PTR.load(Ordering::Relaxed));
+ func($($argname),*)
+ }
+ }
+ $(#[$meta])*
+ pub use $symbol::call as $symbol;
+ )*)
+}
+
+/// A function that either exists or doesn't.
+///
+/// NOTE: Optional functions must be preloaded in the `init` function above, or they will always be None.
+macro_rules! compat_fn_optional {
+ (pub static $module:ident: &CStr = $name:expr; $(
+ $(#[$meta:meta])*
+ pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty;
+ )*) => (
+ pub static $module: &CStr = $name;
+ $(
+ $(#[$meta])*
+ pub mod $symbol {
+ #[allow(unused_imports)]
+ use super::*;
+ use crate::mem;
+ use crate::sync::atomic::{AtomicPtr, Ordering};
+ use crate::sys::compat::Module;
+ use crate::ptr::{self, NonNull};
+
+ type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
+
+ /// `PTR` will either be `null()` or set to the loaded function.
+ static PTR: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
+
+ /// Only allow access to the function if it has loaded successfully.
+ #[inline(always)]
+ #[cfg(not(miri))]
+ pub fn option() -> Option<F> {
+ unsafe {
+ NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| mem::transmute(f))
+ }
+ }
+
+ // Miri does not understand the way we do preloading
+ // therefore load the function here instead.
+ #[cfg(miri)]
+ pub fn option() -> Option<F> {
+ let mut func = NonNull::new(PTR.load(Ordering::Relaxed));
+ if func.is_none() {
+ unsafe { Module::new($module).map(preload) };
+ func = NonNull::new(PTR.load(Ordering::Relaxed));
+ }
+ unsafe {
+ func.map(|f| mem::transmute(f))
+ }
+ }
+
+ #[allow(unused)]
+ pub(in crate::sys) fn preload(module: Module) {
+ unsafe {
+ static SYMBOL_NAME: &CStr = ansi_str!(sym $symbol);
+ if let Some(f) = module.proc_address(SYMBOL_NAME) {
+ PTR.store(f.as_ptr(), Ordering::Relaxed);
+ }
+ }
+ }
+ }
+ )*)
+}
diff --git a/library/std/src/sys/windows/env.rs b/library/std/src/sys/windows/env.rs
new file mode 100644
index 000000000..f0a99d620
--- /dev/null
+++ b/library/std/src/sys/windows/env.rs
@@ -0,0 +1,9 @@
+pub mod os {
+ pub const FAMILY: &str = "windows";
+ pub const OS: &str = "windows";
+ pub const DLL_PREFIX: &str = "";
+ pub const DLL_SUFFIX: &str = ".dll";
+ pub const DLL_EXTENSION: &str = "dll";
+ pub const EXE_SUFFIX: &str = ".exe";
+ pub const EXE_EXTENSION: &str = "exe";
+}
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
new file mode 100644
index 000000000..aed082b3e
--- /dev/null
+++ b/library/std/src/sys/windows/fs.rs
@@ -0,0 +1,1399 @@
+use crate::os::windows::prelude::*;
+
+use crate::ffi::OsString;
+use crate::fmt;
+use crate::io::{self, Error, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
+use crate::mem;
+use crate::os::windows::io::{AsHandle, BorrowedHandle};
+use crate::path::{Path, PathBuf};
+use crate::ptr;
+use crate::slice;
+use crate::sync::Arc;
+use crate::sys::handle::Handle;
+use crate::sys::time::SystemTime;
+use crate::sys::{c, cvt};
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+use crate::thread;
+
+use super::path::maybe_verbatim;
+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,
+ volume_serial_number: Option<u32>,
+ number_of_links: Option<u32>,
+ file_index: Option<u64>,
+}
+
+#[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,
+}
+
+unsafe impl Send for OpenOptions {}
+unsafe impl Sync for OpenOptions {}
+
+#[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: c::LPSECURITY_ATTRIBUTES,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct FilePermissions {
+ attrs: c::DWORD,
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {
+ accessed: Option<c::FILETIME>,
+ modified: Option<c::FILETIME>,
+}
+
+#[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(self.data.into())
+ }
+}
+
+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: ptr::null_mut(),
+ }
+ }
+
+ 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) {
+ // We have to set `SECURITY_SQOS_PRESENT` here, because one of the valid flags we can
+ // receive is `SECURITY_ANONYMOUS = 0x0`, which we can't check for later on.
+ self.security_qos_flags = flags | c::SECURITY_SQOS_PRESENT;
+ }
+ pub fn security_attributes(&mut self, attrs: c::LPSECURITY_ATTRIBUTES) {
+ self.security_attributes = attrs;
+ }
+
+ 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.create_new { c::FILE_FLAG_OPEN_REPARSE_POINT } else { 0 }
+ }
+}
+
+impl File {
+ pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+ let path = maybe_verbatim(path)?;
+ let handle = unsafe {
+ c::CreateFileW(
+ path.as_ptr(),
+ opts.get_access_mode()?,
+ opts.share_mode,
+ opts.security_attributes,
+ opts.get_creation_mode()?,
+ opts.get_flags_and_attributes(),
+ ptr::null_mut(),
+ )
+ };
+ if let Ok(handle) = handle.try_into() {
+ Ok(File { handle: Handle::from_inner(handle) })
+ } else {
+ Err(Error::last_os_error())
+ }
+ }
+
+ pub fn fsync(&self) -> io::Result<()> {
+ cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?;
+ 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.as_raw_handle(),
+ c::FileEndOfFileInfo,
+ &mut info as *mut _ as *mut _,
+ size as c::DWORD,
+ )
+ })?;
+ Ok(())
+ }
+
+ #[cfg(not(target_vendor = "uwp"))]
+ pub fn file_attr(&self) -> io::Result<FileAttr> {
+ unsafe {
+ let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed();
+ cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
+ let mut reparse_tag = 0;
+ if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
+ let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ if let Ok((_, buf)) = self.reparse_point(&mut b) {
+ reparse_tag = buf.ReparseTag;
+ }
+ }
+ Ok(FileAttr {
+ attributes: info.dwFileAttributes,
+ creation_time: info.ftCreationTime,
+ last_access_time: info.ftLastAccessTime,
+ last_write_time: info.ftLastWriteTime,
+ file_size: (info.nFileSizeLow as u64) | ((info.nFileSizeHigh as u64) << 32),
+ reparse_tag,
+ volume_serial_number: Some(info.dwVolumeSerialNumber),
+ number_of_links: Some(info.nNumberOfLinks),
+ file_index: Some(
+ (info.nFileIndexLow as u64) | ((info.nFileIndexHigh as u64) << 32),
+ ),
+ })
+ }
+ }
+
+ #[cfg(target_vendor = "uwp")]
+ pub fn file_attr(&self) -> io::Result<FileAttr> {
+ unsafe {
+ let mut info: c::FILE_BASIC_INFO = mem::zeroed();
+ let size = mem::size_of_val(&info);
+ cvt(c::GetFileInformationByHandleEx(
+ self.handle.as_raw_handle(),
+ c::FileBasicInfo,
+ &mut info as *mut _ as *mut libc::c_void,
+ size as c::DWORD,
+ ))?;
+ let mut attr = FileAttr {
+ attributes: info.FileAttributes,
+ creation_time: c::FILETIME {
+ dwLowDateTime: info.CreationTime as c::DWORD,
+ dwHighDateTime: (info.CreationTime >> 32) as c::DWORD,
+ },
+ last_access_time: c::FILETIME {
+ dwLowDateTime: info.LastAccessTime as c::DWORD,
+ dwHighDateTime: (info.LastAccessTime >> 32) as c::DWORD,
+ },
+ last_write_time: c::FILETIME {
+ dwLowDateTime: info.LastWriteTime as c::DWORD,
+ dwHighDateTime: (info.LastWriteTime >> 32) as c::DWORD,
+ },
+ file_size: 0,
+ reparse_tag: 0,
+ volume_serial_number: None,
+ number_of_links: None,
+ file_index: None,
+ };
+ let mut info: c::FILE_STANDARD_INFO = mem::zeroed();
+ let size = mem::size_of_val(&info);
+ cvt(c::GetFileInformationByHandleEx(
+ self.handle.as_raw_handle(),
+ c::FileStandardInfo,
+ &mut info as *mut _ as *mut libc::c_void,
+ size as c::DWORD,
+ ))?;
+ attr.file_size = info.AllocationSize as u64;
+ attr.number_of_links = Some(info.NumberOfLinks);
+ if attr.file_type().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_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.handle.read_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ self.handle.is_read_vectored()
+ }
+
+ pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
+ self.handle.read_at(buf, offset)
+ }
+
+ pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ self.handle.read_buf(buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.handle.write(buf)
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.handle.write_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ self.handle.is_write_vectored()
+ }
+
+ 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.as_raw_handle(), pos, &mut newpos, whence) })?;
+ Ok(newpos as u64)
+ }
+
+ pub fn duplicate(&self) -> io::Result<File> {
+ Ok(Self { handle: self.handle.try_clone()? })
+ }
+
+ 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.as_raw_handle(),
+ 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::const_io_error!(
+ io::ErrorKind::Uncategorized,
+ "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.as_raw_handle(),
+ c::FileBasicInfo,
+ &mut info as *mut _ as *mut _,
+ size as c::DWORD,
+ )
+ })?;
+ Ok(())
+ }
+
+ pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
+ let is_zero = |t: c::FILETIME| t.dwLowDateTime == 0 && t.dwHighDateTime == 0;
+ if times.accessed.map_or(false, is_zero) || times.modified.map_or(false, is_zero) {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "Cannot set file timestamp to 0",
+ ));
+ }
+ cvt(unsafe {
+ c::SetFileTime(self.as_handle(), None, times.accessed.as_ref(), times.modified.as_ref())
+ })?;
+ Ok(())
+ }
+
+ /// Get only basic file information such as attributes and file times.
+ fn basic_info(&self) -> io::Result<c::FILE_BASIC_INFO> {
+ unsafe {
+ let mut info: c::FILE_BASIC_INFO = mem::zeroed();
+ let size = mem::size_of_val(&info);
+ cvt(c::GetFileInformationByHandleEx(
+ self.handle.as_raw_handle(),
+ c::FileBasicInfo,
+ &mut info as *mut _ as *mut libc::c_void,
+ size as c::DWORD,
+ ))?;
+ Ok(info)
+ }
+ }
+ /// Delete using POSIX semantics.
+ ///
+ /// Files will be deleted as soon as the handle is closed. This is supported
+ /// for Windows 10 1607 (aka RS1) and later. However some filesystem
+ /// drivers will not support it even then, e.g. FAT32.
+ ///
+ /// If the operation is not supported for this filesystem or OS version
+ /// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`.
+ fn posix_delete(&self) -> io::Result<()> {
+ let mut info = c::FILE_DISPOSITION_INFO_EX {
+ Flags: c::FILE_DISPOSITION_DELETE
+ | c::FILE_DISPOSITION_POSIX_SEMANTICS
+ | c::FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
+ };
+ let size = mem::size_of_val(&info);
+ cvt(unsafe {
+ c::SetFileInformationByHandle(
+ self.handle.as_raw_handle(),
+ c::FileDispositionInfoEx,
+ &mut info as *mut _ as *mut _,
+ size as c::DWORD,
+ )
+ })?;
+ Ok(())
+ }
+
+ /// Delete a file using win32 semantics. The file won't actually be deleted
+ /// until all file handles are closed. However, marking a file for deletion
+ /// will prevent anyone from opening a new handle to the file.
+ fn win32_delete(&self) -> io::Result<()> {
+ let mut info = c::FILE_DISPOSITION_INFO { DeleteFile: c::TRUE as _ };
+ let size = mem::size_of_val(&info);
+ cvt(unsafe {
+ c::SetFileInformationByHandle(
+ self.handle.as_raw_handle(),
+ c::FileDispositionInfo,
+ &mut info as *mut _ as *mut _,
+ size as c::DWORD,
+ )
+ })?;
+ Ok(())
+ }
+
+ /// Fill the given buffer with as many directory entries as will fit.
+ /// This will remember its position and continue from the last call unless
+ /// `restart` is set to `true`.
+ ///
+ /// The returned bool indicates if there are more entries or not.
+ /// It is an error if `self` is not a directory.
+ ///
+ /// # Symlinks and other reparse points
+ ///
+ /// On Windows a file is either a directory or a non-directory.
+ /// A symlink directory is simply an empty directory with some "reparse" metadata attached.
+ /// So if you open a link (not its target) and iterate the directory,
+ /// you will always iterate an empty directory regardless of the target.
+ fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> io::Result<bool> {
+ let class =
+ if restart { c::FileIdBothDirectoryRestartInfo } else { c::FileIdBothDirectoryInfo };
+
+ unsafe {
+ let result = cvt(c::GetFileInformationByHandleEx(
+ self.handle.as_raw_handle(),
+ class,
+ buffer.as_mut_ptr().cast(),
+ buffer.capacity() as _,
+ ));
+ match result {
+ Ok(_) => Ok(true),
+ Err(e) if e.raw_os_error() == Some(c::ERROR_NO_MORE_FILES as _) => Ok(false),
+ Err(e) => Err(e),
+ }
+ }
+ }
+}
+
+/// A buffer for holding directory entries.
+struct DirBuff {
+ buffer: Vec<u8>,
+}
+impl DirBuff {
+ fn new() -> Self {
+ const BUFFER_SIZE: usize = 1024;
+ Self { buffer: vec![0_u8; BUFFER_SIZE] }
+ }
+ fn capacity(&self) -> usize {
+ self.buffer.len()
+ }
+ fn as_mut_ptr(&mut self) -> *mut u8 {
+ self.buffer.as_mut_ptr().cast()
+ }
+ /// Returns a `DirBuffIter`.
+ fn iter(&self) -> DirBuffIter<'_> {
+ DirBuffIter::new(self)
+ }
+}
+impl AsRef<[u8]> for DirBuff {
+ fn as_ref(&self) -> &[u8] {
+ &self.buffer
+ }
+}
+
+/// An iterator over entries stored in a `DirBuff`.
+///
+/// Currently only returns file names (UTF-16 encoded).
+struct DirBuffIter<'a> {
+ buffer: Option<&'a [u8]>,
+ cursor: usize,
+}
+impl<'a> DirBuffIter<'a> {
+ fn new(buffer: &'a DirBuff) -> Self {
+ Self { buffer: Some(buffer.as_ref()), cursor: 0 }
+ }
+}
+impl<'a> Iterator for DirBuffIter<'a> {
+ type Item = (&'a [u16], bool);
+ fn next(&mut self) -> Option<Self::Item> {
+ use crate::mem::size_of;
+ let buffer = &self.buffer?[self.cursor..];
+
+ // Get the name and next entry from the buffer.
+ // SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the
+ // last field (the file name) is unsized. So an offset has to be
+ // used to get the file name slice.
+ let (name, is_directory, next_entry) = unsafe {
+ let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
+ let next_entry = (*info).NextEntryOffset as usize;
+ let name = crate::slice::from_raw_parts(
+ (*info).FileName.as_ptr().cast::<u16>(),
+ (*info).FileNameLength as usize / size_of::<u16>(),
+ );
+ let is_directory = ((*info).FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
+
+ (name, is_directory, next_entry)
+ };
+
+ if next_entry == 0 {
+ self.buffer = None
+ } else {
+ self.cursor += next_entry
+ }
+
+ // Skip `.` and `..` pseudo entries.
+ const DOT: u16 = b'.' as u16;
+ match name {
+ [DOT] | [DOT, DOT] => self.next(),
+ _ => Some((name, is_directory)),
+ }
+ }
+}
+
+/// Open a link relative to the parent directory, ensure no symlinks are followed.
+fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<File> {
+ // This is implemented using the lower level `NtCreateFile` function as
+ // unfortunately opening a file relative to a parent is not supported by
+ // win32 functions. It is however a fundamental feature of the NT kernel.
+ //
+ // See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
+ unsafe {
+ let mut handle = ptr::null_mut();
+ let mut io_status = c::IO_STATUS_BLOCK::default();
+ let name_str = c::UNICODE_STRING::from_ref(name);
+ use crate::sync::atomic::{AtomicU32, Ordering};
+ // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been
+ // tricked into following a symlink. However, it may not be available in
+ // earlier versions of Windows.
+ static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE);
+ let object = c::OBJECT_ATTRIBUTES {
+ ObjectName: &name_str,
+ RootDirectory: parent.as_raw_handle(),
+ Attributes: ATTRIBUTES.load(Ordering::Relaxed),
+ ..c::OBJECT_ATTRIBUTES::default()
+ };
+ let status = c::NtCreateFile(
+ &mut handle,
+ access,
+ &object,
+ &mut io_status,
+ crate::ptr::null_mut(),
+ 0,
+ c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE,
+ c::FILE_OPEN,
+ // If `name` is a symlink then open the link rather than the target.
+ c::FILE_OPEN_REPARSE_POINT,
+ crate::ptr::null_mut(),
+ 0,
+ );
+ // Convert an NTSTATUS to the more familiar Win32 error codes (aka "DosError")
+ if c::nt_success(status) {
+ Ok(File::from_raw_handle(handle))
+ } else if status == c::STATUS_DELETE_PENDING {
+ // We make a special exception for `STATUS_DELETE_PENDING` because
+ // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is
+ // very unhelpful.
+ Err(io::Error::from_raw_os_error(c::ERROR_DELETE_PENDING as _))
+ } else if status == c::STATUS_INVALID_PARAMETER
+ && ATTRIBUTES.load(Ordering::Relaxed) == c::OBJ_DONT_REPARSE
+ {
+ // Try without `OBJ_DONT_REPARSE`. See above.
+ ATTRIBUTES.store(0, Ordering::Relaxed);
+ open_link_no_reparse(parent, name, access)
+ } else {
+ Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as _))
+ }
+ }
+}
+
+impl AsInner<Handle> for File {
+ fn as_inner(&self) -> &Handle {
+ &self.handle
+ }
+}
+
+impl IntoInner<Handle> for File {
+ fn into_inner(self) -> Handle {
+ self.handle
+ }
+}
+
+impl FromInner<Handle> for File {
+ fn from_inner(handle: Handle) -> File {
+ File { handle }
+ }
+}
+
+impl AsHandle for File {
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ self.as_inner().as_handle()
+ }
+}
+
+impl AsRawHandle for File {
+ fn as_raw_handle(&self) -> RawHandle {
+ self.as_inner().as_raw_handle()
+ }
+}
+
+impl IntoRawHandle for File {
+ fn into_raw_handle(self) -> RawHandle {
+ self.into_inner().into_raw_handle()
+ }
+}
+
+impl FromRawHandle for File {
+ unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
+ Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_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.as_raw_handle());
+ 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
+ }
+
+ 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)
+ }
+
+ pub fn volume_serial_number(&self) -> Option<u32> {
+ self.volume_serial_number
+ }
+
+ pub fn number_of_links(&self) -> Option<u32> {
+ self.number_of_links
+ }
+
+ pub fn file_index(&self) -> Option<u64> {
+ self.file_index
+ }
+}
+impl From<c::WIN32_FIND_DATAW> for FileAttr {
+ fn from(wfd: c::WIN32_FIND_DATAW) -> Self {
+ FileAttr {
+ attributes: wfd.dwFileAttributes,
+ creation_time: wfd.ftCreationTime,
+ last_access_time: wfd.ftLastAccessTime,
+ last_write_time: wfd.ftLastWriteTime,
+ file_size: ((wfd.nFileSizeHigh as u64) << 32) | (wfd.nFileSizeLow as u64),
+ reparse_tag: if wfd.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
+ // reserved unless this is a reparse point
+ wfd.dwReserved0
+ } else {
+ 0
+ },
+ volume_serial_number: None,
+ number_of_links: None,
+ file_index: None,
+ }
+ }
+}
+
+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 FileTimes {
+ pub fn set_accessed(&mut self, t: SystemTime) {
+ self.accessed = Some(t.into_inner());
+ }
+
+ pub fn set_modified(&mut self, t: SystemTime) {
+ self.modified = Some(t.into_inner());
+ }
+}
+
+impl FileType {
+ fn new(attrs: c::DWORD, reparse_tag: c::DWORD) -> FileType {
+ FileType { attributes: attrs, 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 = maybe_verbatim(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 = maybe_verbatim(&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 = maybe_verbatim(p)?;
+ cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?;
+ Ok(())
+}
+
+pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+ let old = maybe_verbatim(old)?;
+ let new = maybe_verbatim(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 = maybe_verbatim(p)?;
+ cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?;
+ Ok(())
+}
+
+/// Open a file or directory without following symlinks.
+fn open_link(path: &Path, access_mode: u32) -> io::Result<File> {
+ let mut opts = OpenOptions::new();
+ opts.access_mode(access_mode);
+ // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories.
+ // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target.
+ opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);
+ File::open(path, &opts)
+}
+
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+ let file = open_link(path, c::DELETE | c::FILE_LIST_DIRECTORY)?;
+
+ // Test if the file is not a directory or a symlink to a directory.
+ if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 {
+ return Err(io::Error::from_raw_os_error(c::ERROR_DIRECTORY as _));
+ }
+
+ match remove_dir_all_iterative(&file, File::posix_delete) {
+ Err(e) => {
+ if let Some(code) = e.raw_os_error() {
+ match code as u32 {
+ // If POSIX delete is not supported for this filesystem then fallback to win32 delete.
+ c::ERROR_NOT_SUPPORTED
+ | c::ERROR_INVALID_FUNCTION
+ | c::ERROR_INVALID_PARAMETER => {
+ remove_dir_all_iterative(&file, File::win32_delete)
+ }
+ _ => Err(e),
+ }
+ } else {
+ Err(e)
+ }
+ }
+ ok => ok,
+ }
+}
+
+fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io::Result<()> {
+ // When deleting files we may loop this many times when certain error conditions occur.
+ // This allows remove_dir_all to succeed when the error is temporary.
+ const MAX_RETRIES: u32 = 10;
+
+ let mut buffer = DirBuff::new();
+ let mut dirlist = vec![f.duplicate()?];
+
+ // FIXME: This is a hack so we can push to the dirlist vec after borrowing from it.
+ fn copy_handle(f: &File) -> mem::ManuallyDrop<File> {
+ unsafe { mem::ManuallyDrop::new(File::from_raw_handle(f.as_raw_handle())) }
+ }
+
+ let mut restart = true;
+ while let Some(dir) = dirlist.last() {
+ let dir = copy_handle(dir);
+
+ // Fill the buffer and iterate the entries.
+ let more_data = dir.fill_dir_buff(&mut buffer, restart)?;
+ restart = false;
+ for (name, is_directory) in buffer.iter() {
+ if is_directory {
+ let child_dir = open_link_no_reparse(
+ &dir,
+ name,
+ c::SYNCHRONIZE | c::DELETE | c::FILE_LIST_DIRECTORY,
+ )?;
+ dirlist.push(child_dir);
+ } else {
+ for i in 1..=MAX_RETRIES {
+ let result = open_link_no_reparse(&dir, name, c::SYNCHRONIZE | c::DELETE);
+ match result {
+ Ok(f) => delete(&f)?,
+ // Already deleted, so skip.
+ Err(e) if e.kind() == io::ErrorKind::NotFound => break,
+ // Retry a few times if the file is locked or a delete is already in progress.
+ Err(e)
+ if i < MAX_RETRIES
+ && (e.raw_os_error() == Some(c::ERROR_DELETE_PENDING as _)
+ || e.raw_os_error()
+ == Some(c::ERROR_SHARING_VIOLATION as _)) => {}
+ // Otherwise return the error.
+ Err(e) => return Err(e),
+ }
+ thread::yield_now();
+ }
+ }
+ }
+ // If there were no more files then delete the directory.
+ if !more_data {
+ if let Some(dir) = dirlist.pop() {
+ // Retry deleting a few times in case we need to wait for a file to be deleted.
+ for i in 1..=MAX_RETRIES {
+ let result = delete(&dir);
+ if let Err(e) = result {
+ if i == MAX_RETRIES || e.kind() != io::ErrorKind::DirectoryNotEmpty {
+ return Err(e);
+ }
+ thread::yield_now();
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+ Ok(())
+}
+
+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(original: &Path, link: &Path) -> io::Result<()> {
+ symlink_inner(original, link, false)
+}
+
+pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()> {
+ let original = to_u16s(original)?;
+ let link = maybe_verbatim(link)?;
+ 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(
+ link.as_ptr(),
+ original.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(link.as_ptr(), original.as_ptr(), flags) as c::BOOL
+ })?;
+ } else {
+ return Err(err);
+ }
+ }
+ Ok(())
+}
+
+#[cfg(not(target_vendor = "uwp"))]
+pub fn link(original: &Path, link: &Path) -> io::Result<()> {
+ let original = maybe_verbatim(original)?;
+ let link = maybe_verbatim(link)?;
+ cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?;
+ Ok(())
+}
+
+#[cfg(target_vendor = "uwp")]
+pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
+ return Err(io::const_io_error!(
+ io::ErrorKind::Unsupported,
+ "hard link are not supported on UWP",
+ ));
+}
+
+pub fn stat(path: &Path) -> io::Result<FileAttr> {
+ metadata(path, ReparsePoint::Follow)
+}
+
+pub fn lstat(path: &Path) -> io::Result<FileAttr> {
+ metadata(path, ReparsePoint::Open)
+}
+
+#[repr(u32)]
+#[derive(Clone, Copy, PartialEq, Eq)]
+enum ReparsePoint {
+ Follow = 0,
+ Open = c::FILE_FLAG_OPEN_REPARSE_POINT,
+}
+impl ReparsePoint {
+ fn as_flag(self) -> u32 {
+ self as u32
+ }
+}
+
+fn metadata(path: &Path, reparse: ReparsePoint) -> 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 | reparse.as_flag());
+
+ // Attempt to open the file normally.
+ // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`.
+ // If the fallback fails for any reason we return the original error.
+ match File::open(path, &opts) {
+ Ok(file) => file.file_attr(),
+ Err(e) if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as _) => {
+ // `ERROR_SHARING_VIOLATION` will almost never be returned.
+ // Usually if a file is locked you can still read some metadata.
+ // However, there are special system files, such as
+ // `C:\hiberfil.sys`, that are locked in a way that denies even that.
+ unsafe {
+ let path = maybe_verbatim(path)?;
+
+ // `FindFirstFileW` accepts wildcard file names.
+ // Fortunately wildcards are not valid file names and
+ // `ERROR_SHARING_VIOLATION` means the file exists (but is locked)
+ // therefore it's safe to assume the file name given does not
+ // include wildcards.
+ let mut wfd = mem::zeroed();
+ let handle = c::FindFirstFileW(path.as_ptr(), &mut wfd);
+
+ if handle == c::INVALID_HANDLE_VALUE {
+ // This can fail if the user does not have read access to the
+ // directory.
+ Err(e)
+ } else {
+ // We no longer need the find handle.
+ c::FindClose(handle);
+
+ // `FindFirstFileW` reads the cached file information from the
+ // directory. The downside is that this metadata may be outdated.
+ let attrs = FileAttr::from(wfd);
+ if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() {
+ Err(e)
+ } else {
+ Ok(attrs)
+ }
+ }
+ }
+ }
+ Err(e) => Err(e),
+ }
+}
+
+pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
+ let p = maybe_verbatim(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.as_raw_handle(), 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 = maybe_verbatim(from)?;
+ let pto = maybe_verbatim(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>>(
+ original: P,
+ junction: Q,
+) -> io::Result<()> {
+ symlink_junction_inner(original.as_ref(), junction.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(original: &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.as_inner().as_raw_handle();
+
+ 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(original.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(drop)
+ }
+}
+
+// Try to see if a file exists but, unlike `exists`, report I/O errors.
+pub fn try_exists(path: &Path) -> io::Result<bool> {
+ // Open the file to ensure any symlinks are followed to their target.
+ let mut opts = OpenOptions::new();
+ // No read, write, etc access rights are needed.
+ opts.access_mode(0);
+ // Backup semantics enables opening directories as well as files.
+ opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
+ match File::open(path, &opts) {
+ Err(e) => match e.kind() {
+ // The file definitely does not exist
+ io::ErrorKind::NotFound => Ok(false),
+
+ // `ERROR_SHARING_VIOLATION` means that the file has been locked by
+ // another process. This is often temporary so we simply report it
+ // as the file existing.
+ _ if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as i32) => Ok(true),
+
+ // Other errors such as `ERROR_ACCESS_DENIED` may indicate that the
+ // file exists. However, these types of errors are usually more
+ // permanent so we report them here.
+ _ => Err(e),
+ },
+ // The file was opened successfully therefore it must exist,
+ Ok(_) => Ok(true),
+ }
+}
diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs
new file mode 100644
index 000000000..e24b09cc9
--- /dev/null
+++ b/library/std/src/sys/windows/handle.rs
@@ -0,0 +1,335 @@
+#![unstable(issue = "none", feature = "windows_handle")]
+
+#[cfg(test)]
+mod tests;
+
+use crate::cmp;
+use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, Read, ReadBuf};
+use crate::mem;
+use crate::os::windows::io::{
+ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
+};
+use crate::ptr;
+use crate::sys::c;
+use crate::sys::cvt;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+
+/// An owned container for `HANDLE` object, closing them on Drop.
+///
+/// All methods are inherited through a `Deref` impl to `RawHandle`
+pub struct Handle(OwnedHandle);
+
+impl 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::from_raw_handle(event))
+ }
+ }
+ }
+}
+
+impl AsInner<OwnedHandle> for Handle {
+ fn as_inner(&self) -> &OwnedHandle {
+ &self.0
+ }
+}
+
+impl IntoInner<OwnedHandle> for Handle {
+ fn into_inner(self) -> OwnedHandle {
+ self.0
+ }
+}
+
+impl FromInner<OwnedHandle> for Handle {
+ fn from_inner(file_desc: OwnedHandle) -> Self {
+ Self(file_desc)
+ }
+}
+
+impl AsHandle for Handle {
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ self.0.as_handle()
+ }
+}
+
+impl AsRawHandle for Handle {
+ fn as_raw_handle(&self) -> RawHandle {
+ self.0.as_raw_handle()
+ }
+}
+
+impl IntoRawHandle for Handle {
+ fn into_raw_handle(self) -> RawHandle {
+ self.0.into_raw_handle()
+ }
+}
+
+impl FromRawHandle for Handle {
+ unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
+ Self(FromRawHandle::from_raw_handle(raw_handle))
+ }
+}
+
+impl Handle {
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ let res = unsafe { self.synchronous_read(buf.as_mut_ptr().cast(), buf.len(), None) };
+
+ match res {
+ Ok(read) => 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_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ crate::io::default_read_vectored(|buf| self.read(buf), bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ false
+ }
+
+ pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
+ let res =
+ unsafe { self.synchronous_read(buf.as_mut_ptr().cast(), buf.len(), Some(offset)) };
+
+ match res {
+ Ok(read) => 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 fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ let res = unsafe {
+ self.synchronous_read(buf.unfilled_mut().as_mut_ptr(), buf.remaining(), None)
+ };
+
+ match res {
+ Ok(read) => {
+ // Safety: `read` bytes were written to the initialized portion of the buffer
+ unsafe {
+ buf.assume_init(read as usize);
+ }
+ buf.add_filled(read as usize);
+ Ok(())
+ }
+
+ // 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(()),
+
+ 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 as usize) as c::DWORD;
+ let mut amt = 0;
+ let res = cvt(c::ReadFile(
+ self.as_handle(),
+ 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.as_raw_handle(), 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.as_raw_handle())).map(drop) }
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.synchronous_write(&buf, None)
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ crate::io::default_write_vectored(|buf| self.write(buf), bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ false
+ }
+
+ pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
+ self.synchronous_write(&buf, Some(offset))
+ }
+
+ pub fn try_clone(&self) -> io::Result<Self> {
+ Ok(Self(self.0.try_clone()?))
+ }
+
+ pub fn duplicate(
+ &self,
+ access: c::DWORD,
+ inherit: bool,
+ options: c::DWORD,
+ ) -> io::Result<Self> {
+ Ok(Self(self.0.as_handle().duplicate(access, inherit, options)?))
+ }
+
+ /// Performs a synchronous read.
+ ///
+ /// If the handle is opened for asynchronous I/O then this abort the process.
+ /// See #81357.
+ ///
+ /// If `offset` is `None` then the current file position is used.
+ unsafe fn synchronous_read(
+ &self,
+ buf: *mut mem::MaybeUninit<u8>,
+ len: usize,
+ offset: Option<u64>,
+ ) -> io::Result<usize> {
+ let mut io_status = c::IO_STATUS_BLOCK::default();
+
+ // The length is clamped at u32::MAX.
+ let len = cmp::min(len, c::DWORD::MAX as usize) as c::DWORD;
+ let status = c::NtReadFile(
+ self.as_handle(),
+ ptr::null_mut(),
+ None,
+ ptr::null_mut(),
+ &mut io_status,
+ buf,
+ len,
+ offset.map(|n| n as _).as_ref(),
+ None,
+ );
+
+ let status = if status == c::STATUS_PENDING {
+ c::WaitForSingleObject(self.as_raw_handle(), c::INFINITE);
+ io_status.status()
+ } else {
+ status
+ };
+ match status {
+ // If the operation has not completed then abort the process.
+ // Doing otherwise means that the buffer and stack may be written to
+ // after this function returns.
+ c::STATUS_PENDING => rtabort!("I/O error: operation failed to complete synchronously"),
+
+ // Return `Ok(0)` when there's nothing more to read.
+ c::STATUS_END_OF_FILE => Ok(0),
+
+ // Success!
+ status if c::nt_success(status) => Ok(io_status.Information),
+
+ status => {
+ let error = c::RtlNtStatusToDosError(status);
+ Err(io::Error::from_raw_os_error(error as _))
+ }
+ }
+ }
+
+ /// Performs a synchronous write.
+ ///
+ /// If the handle is opened for asynchronous I/O then this abort the process.
+ /// See #81357.
+ ///
+ /// If `offset` is `None` then the current file position is used.
+ fn synchronous_write(&self, buf: &[u8], offset: Option<u64>) -> io::Result<usize> {
+ let mut io_status = c::IO_STATUS_BLOCK::default();
+
+ // The length is clamped at u32::MAX.
+ let len = cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
+ let status = unsafe {
+ c::NtWriteFile(
+ self.as_handle(),
+ ptr::null_mut(),
+ None,
+ ptr::null_mut(),
+ &mut io_status,
+ buf.as_ptr(),
+ len,
+ offset.map(|n| n as _).as_ref(),
+ None,
+ )
+ };
+ let status = if status == c::STATUS_PENDING {
+ unsafe { c::WaitForSingleObject(self.as_raw_handle(), c::INFINITE) };
+ io_status.status()
+ } else {
+ status
+ };
+ match status {
+ // If the operation has not completed then abort the process.
+ // Doing otherwise means that the buffer may be read and the stack
+ // written to after this function returns.
+ c::STATUS_PENDING => rtabort!("I/O error: operation failed to complete synchronously"),
+
+ // Success!
+ status if c::nt_success(status) => Ok(io_status.Information),
+
+ status => {
+ let error = unsafe { c::RtlNtStatusToDosError(status) };
+ Err(io::Error::from_raw_os_error(error as _))
+ }
+ }
+ }
+}
+
+impl<'a> Read for &'a Handle {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ (**self).read(buf)
+ }
+
+ fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ (**self).read_vectored(bufs)
+ }
+}
diff --git a/library/std/src/sys/windows/handle/tests.rs b/library/std/src/sys/windows/handle/tests.rs
new file mode 100644
index 000000000..d836dae4c
--- /dev/null
+++ b/library/std/src/sys/windows/handle/tests.rs
@@ -0,0 +1,22 @@
+use crate::sys::pipe::{anon_pipe, Pipes};
+use crate::{thread, time};
+
+/// Test the synchronous fallback for overlapped I/O.
+#[test]
+fn overlapped_handle_fallback() {
+ // Create some pipes. `ours` will be asynchronous.
+ let Pipes { ours, theirs } = anon_pipe(true, false).unwrap();
+
+ let async_readable = ours.into_handle();
+ let sync_writeable = theirs.into_handle();
+
+ thread::scope(|_| {
+ thread::sleep(time::Duration::from_millis(100));
+ sync_writeable.write(b"hello world!").unwrap();
+ });
+
+ // The pipe buffer starts empty so reading won't complete synchronously unless
+ // our fallback path works.
+ let mut buffer = [0u8; 1024];
+ async_readable.read(&mut buffer).unwrap();
+}
diff --git a/library/std/src/sys/windows/io.rs b/library/std/src/sys/windows/io.rs
new file mode 100644
index 000000000..fb06df1f8
--- /dev/null
+++ b/library/std/src/sys/windows/io.rs
@@ -0,0 +1,80 @@
+use crate::marker::PhantomData;
+use crate::slice;
+use crate::sys::c;
+
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+pub struct IoSlice<'a> {
+ vec: c::WSABUF,
+ _p: PhantomData<&'a [u8]>,
+}
+
+impl<'a> IoSlice<'a> {
+ #[inline]
+ pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
+ assert!(buf.len() <= c::ULONG::MAX as usize);
+ IoSlice {
+ vec: c::WSABUF {
+ len: buf.len() as c::ULONG,
+ buf: buf.as_ptr() as *mut u8 as *mut c::CHAR,
+ },
+ _p: PhantomData,
+ }
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ if (self.vec.len as usize) < n {
+ panic!("advancing IoSlice beyond its length");
+ }
+
+ unsafe {
+ self.vec.len -= n as c::ULONG;
+ self.vec.buf = self.vec.buf.add(n);
+ }
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts(self.vec.buf as *mut u8, self.vec.len as usize) }
+ }
+}
+
+#[repr(transparent)]
+pub struct IoSliceMut<'a> {
+ vec: c::WSABUF,
+ _p: PhantomData<&'a mut [u8]>,
+}
+
+impl<'a> IoSliceMut<'a> {
+ #[inline]
+ pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
+ assert!(buf.len() <= c::ULONG::MAX as usize);
+ IoSliceMut {
+ vec: c::WSABUF { len: buf.len() as c::ULONG, buf: buf.as_mut_ptr() as *mut c::CHAR },
+ _p: PhantomData,
+ }
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ if (self.vec.len as usize) < n {
+ panic!("advancing IoSliceMut beyond its length");
+ }
+
+ unsafe {
+ self.vec.len -= n as c::ULONG;
+ self.vec.buf = self.vec.buf.add(n);
+ }
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts(self.vec.buf as *mut u8, self.vec.len as usize) }
+ }
+
+ #[inline]
+ pub fn as_mut_slice(&mut self) -> &mut [u8] {
+ unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.len as usize) }
+ }
+}
diff --git a/library/std/src/sys/windows/locks/condvar.rs b/library/std/src/sys/windows/locks/condvar.rs
new file mode 100644
index 000000000..be9a2abbe
--- /dev/null
+++ b/library/std/src/sys/windows/locks/condvar.rs
@@ -0,0 +1,52 @@
+use crate::cell::UnsafeCell;
+use crate::sys::c;
+use crate::sys::locks::{mutex, Mutex};
+use crate::sys::os;
+use crate::time::Duration;
+
+pub struct Condvar {
+ inner: UnsafeCell<c::CONDITION_VARIABLE>,
+}
+
+pub type MovableCondvar = Condvar;
+
+unsafe impl Send for Condvar {}
+unsafe impl Sync for Condvar {}
+
+impl Condvar {
+ #[inline]
+ pub const fn new() -> Condvar {
+ Condvar { inner: UnsafeCell::new(c::CONDITION_VARIABLE_INIT) }
+ }
+
+ #[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),
+ crate::sys::windows::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())
+ }
+}
diff --git a/library/std/src/sys/windows/locks/mod.rs b/library/std/src/sys/windows/locks/mod.rs
new file mode 100644
index 000000000..d412ff152
--- /dev/null
+++ b/library/std/src/sys/windows/locks/mod.rs
@@ -0,0 +1,6 @@
+mod condvar;
+mod mutex;
+mod rwlock;
+pub use condvar::{Condvar, MovableCondvar};
+pub use mutex::{MovableMutex, Mutex};
+pub use rwlock::{MovableRwLock, RwLock};
diff --git a/library/std/src/sys/windows/locks/mutex.rs b/library/std/src/sys/windows/locks/mutex.rs
new file mode 100644
index 000000000..f91e8f9f5
--- /dev/null
+++ b/library/std/src/sys/windows/locks/mutex.rs
@@ -0,0 +1,57 @@
+//! System Mutexes
+//!
+//! The Windows implementation of mutexes is a little odd and it might 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.
+
+use crate::cell::UnsafeCell;
+use crate::sys::c;
+
+pub struct Mutex {
+ srwlock: UnsafeCell<c::SRWLOCK>,
+}
+
+// Windows SRW Locks are movable (while not borrowed).
+pub type MovableMutex = Mutex;
+
+unsafe impl Send for Mutex {}
+unsafe impl Sync for Mutex {}
+
+#[inline]
+pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK {
+ m.srwlock.get()
+}
+
+impl Mutex {
+ #[inline]
+ pub const fn new() -> Mutex {
+ Mutex { srwlock: UnsafeCell::new(c::SRWLOCK_INIT) }
+ }
+ #[inline]
+ pub unsafe fn init(&mut self) {}
+
+ #[inline]
+ pub unsafe fn lock(&self) {
+ c::AcquireSRWLockExclusive(raw(self));
+ }
+
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ c::TryAcquireSRWLockExclusive(raw(self)) != 0
+ }
+
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ c::ReleaseSRWLockExclusive(raw(self));
+ }
+}
diff --git a/library/std/src/sys/windows/locks/rwlock.rs b/library/std/src/sys/windows/locks/rwlock.rs
new file mode 100644
index 000000000..fa5ffe574
--- /dev/null
+++ b/library/std/src/sys/windows/locks/rwlock.rs
@@ -0,0 +1,42 @@
+use crate::cell::UnsafeCell;
+use crate::sys::c;
+
+pub struct RwLock {
+ inner: UnsafeCell<c::SRWLOCK>,
+}
+
+pub type MovableRwLock = RwLock;
+
+unsafe impl Send for RwLock {}
+unsafe impl Sync for RwLock {}
+
+impl RwLock {
+ #[inline]
+ 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())
+ }
+}
diff --git a/library/std/src/sys/windows/memchr.rs b/library/std/src/sys/windows/memchr.rs
new file mode 100644
index 000000000..b9e5bcc1b
--- /dev/null
+++ b/library/std/src/sys/windows/memchr.rs
@@ -0,0 +1,5 @@
+// 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/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs
new file mode 100644
index 000000000..b3f6d2d0a
--- /dev/null
+++ b/library/std/src/sys/windows/mod.rs
@@ -0,0 +1,323 @@
+#![allow(missing_docs, nonstandard_style)]
+
+use crate::ffi::{CStr, OsStr, OsString};
+use crate::io::ErrorKind;
+use crate::os::windows::ffi::{OsStrExt, OsStringExt};
+use crate::path::PathBuf;
+use crate::time::Duration;
+
+pub use self::rand::hashmap_random_keys;
+
+#[macro_use]
+pub mod compat;
+
+pub mod alloc;
+pub mod args;
+pub mod c;
+pub mod cmath;
+pub mod env;
+pub mod fs;
+pub mod handle;
+pub mod io;
+pub mod locks;
+pub mod memchr;
+pub mod net;
+pub mod os;
+pub mod os_str;
+pub mod path;
+pub mod pipe;
+pub mod process;
+pub mod rand;
+pub mod thread;
+pub mod thread_local_dtor;
+pub mod thread_local_key;
+pub mod thread_parker;
+pub mod time;
+cfg_if::cfg_if! {
+ if #[cfg(not(target_vendor = "uwp"))] {
+ pub mod stdio;
+ pub mod stack_overflow;
+ } else {
+ pub mod stdio_uwp;
+ pub mod stack_overflow_uwp;
+ pub use self::stdio_uwp as stdio;
+ pub use self::stack_overflow_uwp as stack_overflow;
+ }
+}
+
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
+ stack_overflow::init();
+
+ // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already
+ // exists, we have to call it ourselves.
+ thread::Thread::set_name(&CStr::from_bytes_with_nul_unchecked(b"main\0"));
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+ net::cleanup();
+}
+
+pub fn decode_error_kind(errno: i32) -> ErrorKind {
+ use ErrorKind::*;
+
+ match errno as c::DWORD {
+ c::ERROR_ACCESS_DENIED => return PermissionDenied,
+ c::ERROR_ALREADY_EXISTS => return AlreadyExists,
+ c::ERROR_FILE_EXISTS => return AlreadyExists,
+ c::ERROR_BROKEN_PIPE => return BrokenPipe,
+ c::ERROR_FILE_NOT_FOUND => return NotFound,
+ c::ERROR_PATH_NOT_FOUND => return NotFound,
+ c::ERROR_NO_DATA => return BrokenPipe,
+ c::ERROR_INVALID_NAME => return InvalidFilename,
+ c::ERROR_INVALID_PARAMETER => return InvalidInput,
+ c::ERROR_NOT_ENOUGH_MEMORY | c::ERROR_OUTOFMEMORY => return OutOfMemory,
+ c::ERROR_SEM_TIMEOUT
+ | c::WAIT_TIMEOUT
+ | c::ERROR_DRIVER_CANCEL_TIMEOUT
+ | c::ERROR_OPERATION_ABORTED
+ | c::ERROR_SERVICE_REQUEST_TIMEOUT
+ | c::ERROR_COUNTER_TIMEOUT
+ | c::ERROR_TIMEOUT
+ | c::ERROR_RESOURCE_CALL_TIMED_OUT
+ | c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT
+ | c::ERROR_CTX_CLIENT_QUERY_TIMEOUT
+ | c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT
+ | c::ERROR_DS_TIMELIMIT_EXCEEDED
+ | c::DNS_ERROR_RECORD_TIMED_OUT
+ | c::ERROR_IPSEC_IKE_TIMED_OUT
+ | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT
+ | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return TimedOut,
+ c::ERROR_CALL_NOT_IMPLEMENTED => return Unsupported,
+ c::ERROR_HOST_UNREACHABLE => return HostUnreachable,
+ c::ERROR_NETWORK_UNREACHABLE => return NetworkUnreachable,
+ c::ERROR_DIRECTORY => return NotADirectory,
+ c::ERROR_DIRECTORY_NOT_SUPPORTED => return IsADirectory,
+ c::ERROR_DIR_NOT_EMPTY => return DirectoryNotEmpty,
+ c::ERROR_WRITE_PROTECT => return ReadOnlyFilesystem,
+ c::ERROR_DISK_FULL | c::ERROR_HANDLE_DISK_FULL => return StorageFull,
+ c::ERROR_SEEK_ON_DEVICE => return NotSeekable,
+ c::ERROR_DISK_QUOTA_EXCEEDED => return FilesystemQuotaExceeded,
+ c::ERROR_FILE_TOO_LARGE => return FileTooLarge,
+ c::ERROR_BUSY => return ResourceBusy,
+ c::ERROR_POSSIBLE_DEADLOCK => return Deadlock,
+ c::ERROR_NOT_SAME_DEVICE => return CrossesDevices,
+ c::ERROR_TOO_MANY_LINKS => return TooManyLinks,
+ c::ERROR_FILENAME_EXCED_RANGE => return InvalidFilename,
+ _ => {}
+ }
+
+ match errno {
+ c::WSAEACCES => PermissionDenied,
+ c::WSAEADDRINUSE => AddrInUse,
+ c::WSAEADDRNOTAVAIL => AddrNotAvailable,
+ c::WSAECONNABORTED => ConnectionAborted,
+ c::WSAECONNREFUSED => ConnectionRefused,
+ c::WSAECONNRESET => ConnectionReset,
+ c::WSAEINVAL => InvalidInput,
+ c::WSAENOTCONN => NotConnected,
+ c::WSAEWOULDBLOCK => WouldBlock,
+ c::WSAETIMEDOUT => TimedOut,
+ c::WSAEHOSTUNREACH => HostUnreachable,
+ c::WSAENETDOWN => NetworkDown,
+ c::WSAENETUNREACH => NetworkUnreachable,
+
+ _ => Uncategorized,
+ }
+}
+
+pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option<usize> {
+ let ptr = haystack.as_ptr();
+ let mut start = &haystack[..];
+
+ // For performance reasons unfold the loop eight times.
+ while start.len() >= 8 {
+ macro_rules! if_return {
+ ($($n:literal,)+) => {
+ $(
+ if start[$n] == needle {
+ return Some(((&start[$n] as *const u16).addr() - ptr.addr()) / 2);
+ }
+ )+
+ }
+ }
+
+ if_return!(0, 1, 2, 3, 4, 5, 6, 7,);
+
+ start = &start[8..];
+ }
+
+ for c in start {
+ if *c == needle {
+ return Some(((c as *const u16).addr() - ptr.addr()) / 2);
+ }
+ }
+ None
+}
+
+pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> crate::io::Result<Vec<u16>> {
+ fn inner(s: &OsStr) -> crate::io::Result<Vec<u16>> {
+ // Most paths are ASCII, so reserve capacity for as much as there are bytes
+ // in the OsStr plus one for the null-terminating character. We are not
+ // wasting bytes here as paths created by this function are primarily used
+ // in an ephemeral fashion.
+ let mut maybe_result = Vec::with_capacity(s.len() + 1);
+ maybe_result.extend(s.encode_wide());
+
+ if unrolled_find_u16s(0, &maybe_result).is_some() {
+ return Err(crate::io::const_io_error!(
+ 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) -> crate::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.
+ //
+ // This initial size also works around `GetFullPathNameW` returning
+ // incorrect size hints for some short paths:
+ // https://github.com/dylni/normpath/issues/5
+ 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(crate::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 if k == n {
+ // It is impossible to reach this point.
+ // On success, k is the returned string length excluding the null.
+ // On failure, k is the required buffer length including the null.
+ // Therefore k never equals n.
+ unreachable!();
+ } else {
+ return Ok(f2(&buf[..k]));
+ }
+ }
+ }
+}
+
+fn os2path(s: &[u16]) -> PathBuf {
+ PathBuf::from(OsString::from_wide(s))
+}
+
+pub fn truncate_utf16_at_nul(v: &[u16]) -> &[u16] {
+ match unrolled_find_u16s(0, v) {
+ // 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) -> crate::io::Result<I> {
+ if i.is_zero() { Err(crate::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 as u64 { c::INFINITE } else { ms as c::DWORD })
+ .unwrap_or(c::INFINITE)
+}
+
+/// Use `__fastfail` to abort the process
+///
+/// This is the same implementation as in libpanic_abort's `__rust_start_panic`. See
+/// that function for more information on `__fastfail`
+#[allow(unreachable_code)]
+pub fn abort_internal() -> ! {
+ #[allow(unused)]
+ const FAST_FAIL_FATAL_APP_EXIT: usize = 7;
+ #[cfg(not(miri))] // inline assembly does not work in Miri
+ unsafe {
+ cfg_if::cfg_if! {
+ if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
+ core::arch::asm!("int $$0x29", in("ecx") FAST_FAIL_FATAL_APP_EXIT);
+ crate::intrinsics::unreachable();
+ } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] {
+ core::arch::asm!(".inst 0xDEFB", in("r0") FAST_FAIL_FATAL_APP_EXIT);
+ crate::intrinsics::unreachable();
+ } else if #[cfg(target_arch = "aarch64")] {
+ core::arch::asm!("brk 0xF003", in("x0") FAST_FAIL_FATAL_APP_EXIT);
+ crate::intrinsics::unreachable();
+ }
+ }
+ }
+ crate::intrinsics::abort();
+}
diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs
new file mode 100644
index 000000000..e0701a498
--- /dev/null
+++ b/library/std/src/sys/windows/net.rs
@@ -0,0 +1,476 @@
+#![unstable(issue = "none", feature = "windows_net")]
+
+use crate::cmp;
+use crate::io::{self, IoSlice, IoSliceMut, Read};
+use crate::mem;
+use crate::net::{Shutdown, SocketAddr};
+use crate::os::windows::io::{
+ AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket,
+};
+use crate::ptr;
+use crate::sync::OnceLock;
+use crate::sys;
+use crate::sys::c;
+use crate::sys_common::net;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+use crate::time::Duration;
+
+use libc::{c_int, c_long, c_ulong, c_ushort};
+
+pub type wrlen_t = i32;
+
+pub mod netc {
+ pub use crate::sys::c::ADDRESS_FAMILY as sa_family_t;
+ pub use crate::sys::c::ADDRINFOA as addrinfo;
+ pub use crate::sys::c::SOCKADDR as sockaddr;
+ pub use crate::sys::c::SOCKADDR_STORAGE_LH as sockaddr_storage;
+ pub use crate::sys::c::*;
+}
+
+pub struct Socket(OwnedSocket);
+
+static WSA_CLEANUP: OnceLock<unsafe extern "system" fn() -> i32> = OnceLock::new();
+
+/// Checks whether the Windows socket interface has been started already, and
+/// if not, starts it.
+pub fn init() {
+ let _ = WSA_CLEANUP.get_or_init(|| unsafe {
+ let mut data: c::WSADATA = mem::zeroed();
+ let ret = c::WSAStartup(
+ 0x202, // version 2.2
+ &mut data,
+ );
+ assert_eq!(ret, 0);
+
+ // Only register `WSACleanup` if `WSAStartup` is actually ever called.
+ // Workaround to prevent linking to `WS2_32.dll` when no network functionality is used.
+ // See issue #85441.
+ c::WSACleanup
+ });
+}
+
+pub fn cleanup() {
+ // only perform cleanup if network functionality was actually initialized
+ if let Some(cleanup) = WSA_CLEANUP.get() {
+ unsafe {
+ cleanup();
+ }
+ }
+}
+
+/// 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 family = match *addr {
+ SocketAddr::V4(..) => c::AF_INET,
+ SocketAddr::V6(..) => c::AF_INET6,
+ };
+ let socket = unsafe {
+ c::WSASocketW(
+ family,
+ ty,
+ 0,
+ ptr::null_mut(),
+ 0,
+ c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT,
+ )
+ };
+
+ if socket != c::INVALID_SOCKET {
+ unsafe { Ok(Self::from_raw_socket(socket)) }
+ } else {
+ let error = unsafe { c::WSAGetLastError() };
+
+ if error != c::WSAEPROTOTYPE && error != c::WSAEINVAL {
+ return Err(io::Error::from_raw_os_error(error));
+ }
+
+ let socket =
+ unsafe { c::WSASocketW(family, ty, 0, ptr::null_mut(), 0, c::WSA_FLAG_OVERLAPPED) };
+
+ if socket == c::INVALID_SOCKET {
+ return Err(last_error());
+ }
+
+ unsafe {
+ let socket = Self::from_raw_socket(socket);
+ socket.0.set_no_inherit()?;
+ Ok(socket)
+ }
+ }
+ }
+
+ pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
+ self.set_nonblocking(true)?;
+ let result = {
+ let (addr, len) = addr.into_inner();
+ let result = unsafe { c::connect(self.as_raw_socket(), addr.as_ptr(), len) };
+ cvt(result).map(drop)
+ };
+ self.set_nonblocking(false)?;
+
+ match result {
+ Err(ref error) if error.kind() == io::ErrorKind::WouldBlock => {
+ if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
+ return Err(io::const_io_error!(
+ 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 = {
+ let mut fds = unsafe { mem::zeroed::<c::fd_set>() };
+ fds.fd_count = 1;
+ fds.fd_array[0] = self.as_raw_socket();
+ fds
+ };
+
+ let mut writefds = fds;
+ let mut errorfds = fds;
+
+ let count = {
+ let result = unsafe {
+ c::select(1, ptr::null_mut(), &mut writefds, &mut errorfds, &timeout)
+ };
+ cvt(result)?
+ };
+
+ match count {
+ 0 => Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")),
+ _ => {
+ if writefds.fd_count != 1 {
+ if let Some(e) = self.take_error()? {
+ return Err(e);
+ }
+ }
+
+ Ok(())
+ }
+ }
+ }
+ _ => result,
+ }
+ }
+
+ pub fn accept(&self, storage: *mut c::SOCKADDR, len: *mut c_int) -> io::Result<Socket> {
+ let socket = unsafe { c::accept(self.as_raw_socket(), storage, len) };
+
+ match socket {
+ c::INVALID_SOCKET => Err(last_error()),
+ _ => unsafe { Ok(Self::from_raw_socket(socket)) },
+ }
+ }
+
+ pub fn duplicate(&self) -> io::Result<Socket> {
+ Ok(Self(self.0.try_clone()?))
+ }
+
+ 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 length = cmp::min(buf.len(), i32::MAX as usize) as i32;
+ let result =
+ unsafe { c::recv(self.as_raw_socket(), buf.as_mut_ptr() as *mut _, length, flags) };
+
+ match result {
+ c::SOCKET_ERROR => {
+ let error = unsafe { c::WSAGetLastError() };
+
+ if error == c::WSAESHUTDOWN {
+ Ok(0)
+ } else {
+ Err(io::Error::from_raw_os_error(error))
+ }
+ }
+ _ => Ok(result as usize),
+ }
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.recv_with_flags(buf, 0)
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> 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 length = cmp::min(bufs.len(), c::DWORD::MAX as usize) as c::DWORD;
+ let mut nread = 0;
+ let mut flags = 0;
+ let result = unsafe {
+ c::WSARecv(
+ self.as_raw_socket(),
+ bufs.as_mut_ptr() as *mut c::WSABUF,
+ length,
+ &mut nread,
+ &mut flags,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ )
+ };
+
+ match result {
+ 0 => Ok(nread as usize),
+ _ => {
+ let error = unsafe { c::WSAGetLastError() };
+
+ if error == c::WSAESHUTDOWN {
+ Ok(0)
+ } else {
+ Err(io::Error::from_raw_os_error(error))
+ }
+ }
+ }
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ true
+ }
+
+ 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 = unsafe { mem::zeroed::<c::SOCKADDR_STORAGE_LH>() };
+ let mut addrlen = mem::size_of_val(&storage) as c::socklen_t;
+ let length = cmp::min(buf.len(), <wrlen_t>::MAX 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.
+ let result = unsafe {
+ c::recvfrom(
+ self.as_raw_socket(),
+ buf.as_mut_ptr() as *mut _,
+ length,
+ flags,
+ &mut storage as *mut _ as *mut _,
+ &mut addrlen,
+ )
+ };
+
+ match result {
+ c::SOCKET_ERROR => {
+ let error = unsafe { c::WSAGetLastError() };
+
+ if error == c::WSAESHUTDOWN {
+ Ok((0, net::sockaddr_to_addr(&storage, addrlen as usize)?))
+ } else {
+ Err(io::Error::from_raw_os_error(error))
+ }
+ }
+ _ => Ok((result 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 write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ let length = cmp::min(bufs.len(), c::DWORD::MAX as usize) as c::DWORD;
+ let mut nwritten = 0;
+ let result = unsafe {
+ c::WSASend(
+ self.as_raw_socket(),
+ bufs.as_ptr() as *const c::WSABUF as *mut _,
+ length,
+ &mut nwritten,
+ 0,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ )
+ };
+ cvt(result).map(|_| nwritten as usize)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ 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::const_io_error!(
+ 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)))
+ }
+ }
+
+ 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,
+ };
+ let result = unsafe { c::shutdown(self.as_raw_socket(), how) };
+ cvt(result).map(drop)
+ }
+
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ let mut nonblocking = nonblocking as c_ulong;
+ let result =
+ unsafe { c::ioctlsocket(self.as_raw_socket(), c::FIONBIO as c_int, &mut nonblocking) };
+ cvt(result).map(drop)
+ }
+
+ pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
+ let linger = c::linger {
+ l_onoff: linger.is_some() as c_ushort,
+ l_linger: linger.unwrap_or_default().as_secs() as c_ushort,
+ };
+
+ net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger)
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ let val: c::linger = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?;
+
+ Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
+ }
+
+ pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
+ net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL)
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ let raw: c::BOOL = 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))) }
+ }
+
+ // This is used by sys_common code to abstract over Windows and Unix.
+ pub fn as_raw(&self) -> RawSocket {
+ self.as_inner().as_raw_socket()
+ }
+}
+
+#[unstable(reason = "not public", issue = "none", feature = "fd_read")]
+impl<'a> Read for &'a Socket {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ (**self).read(buf)
+ }
+}
+
+impl AsInner<OwnedSocket> for Socket {
+ fn as_inner(&self) -> &OwnedSocket {
+ &self.0
+ }
+}
+
+impl FromInner<OwnedSocket> for Socket {
+ fn from_inner(sock: OwnedSocket) -> Socket {
+ Socket(sock)
+ }
+}
+
+impl IntoInner<OwnedSocket> for Socket {
+ fn into_inner(self) -> OwnedSocket {
+ self.0
+ }
+}
+
+impl AsSocket for Socket {
+ fn as_socket(&self) -> BorrowedSocket<'_> {
+ self.0.as_socket()
+ }
+}
+
+impl AsRawSocket for Socket {
+ fn as_raw_socket(&self) -> RawSocket {
+ self.0.as_raw_socket()
+ }
+}
+
+impl IntoRawSocket for Socket {
+ fn into_raw_socket(self) -> RawSocket {
+ self.0.into_raw_socket()
+ }
+}
+
+impl FromRawSocket for Socket {
+ unsafe fn from_raw_socket(raw_socket: RawSocket) -> Self {
+ Self(FromRawSocket::from_raw_socket(raw_socket))
+ }
+}
diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs
new file mode 100644
index 000000000..bcac996c0
--- /dev/null
+++ b/library/std/src/sys/windows/os.rs
@@ -0,0 +1,328 @@
+//! Implementation of `std::os` functionality for Windows.
+
+#![allow(nonstandard_style)]
+
+#[cfg(test)]
+mod tests;
+
+use crate::os::windows::prelude::*;
+
+use crate::error::Error as StdError;
+use crate::ffi::{OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::os::windows::ffi::EncodeWide;
+use crate::path::{self, PathBuf};
+use crate::ptr;
+use crate::slice;
+use crate::sys::{c, cvt};
+
+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://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a
+ if (errnum & c::FACILITY_NT_BIT as i32) != 0 {
+ // format according to https://support.microsoft.com/en-us/help/259693
+ const NTDLL_DLL: &[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.is_null() {
+ 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 {errnum} (FormatMessageW() returned error {fm_err})");
+ }
+
+ match String::from_utf16(&buf[..res]) {
+ Ok(mut msg) => {
+ // Trim trailing CRLF inserted by FormatMessageW
+ let len = msg.trim_end().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.is_null() {
+ 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 {
+ #[allow(deprecated)]
+ 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(drop)
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+ let k = to_u16s(k).ok()?;
+ super::fill_utf16_buf(
+ |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
+ |buf| OsStringExt::from_wide(buf),
+ )
+ .ok()
+}
+
+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(drop)
+}
+
+pub fn unsetenv(n: &OsStr) -> io::Result<()> {
+ let v = to_u16s(n)?;
+ cvt(unsafe { c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) }).map(drop)
+}
+
+pub fn temp_dir() -> PathBuf {
+ super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPath2W(sz, buf) }, super::os2path).unwrap()
+}
+
+#[cfg(not(target_vendor = "uwp"))]
+fn home_dir_crt() -> Option<PathBuf> {
+ unsafe {
+ // The magic constant -4 can be used as the token passed to GetUserProfileDirectoryW below
+ // instead of us having to go through these multiple steps to get a token. However this is
+ // not implemented on Windows 7, only Windows 8 and up. When we drop support for Windows 7
+ // we can simplify this code. See #90144 for details.
+ use crate::sys::handle::Handle;
+
+ 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::from_raw_handle(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()
+ }
+}
+
+#[cfg(target_vendor = "uwp")]
+fn home_dir_crt() -> Option<PathBuf> {
+ None
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ crate::env::var_os("HOME")
+ .or_else(|| crate::env::var_os("USERPROFILE"))
+ .map(PathBuf::from)
+ .or_else(|| home_dir_crt())
+}
+
+pub fn exit(code: i32) -> ! {
+ unsafe { c::ExitProcess(code as c::UINT) }
+}
+
+pub fn getpid() -> u32 {
+ unsafe { c::GetCurrentProcessId() as u32 }
+}
diff --git a/library/std/src/sys/windows/os/tests.rs b/library/std/src/sys/windows/os/tests.rs
new file mode 100644
index 000000000..458d6e11c
--- /dev/null
+++ b/library/std/src/sys/windows/os/tests.rs
@@ -0,0 +1,13 @@
+use crate::io::Error;
+use crate::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/library/std/src/sys/windows/os_str.rs b/library/std/src/sys/windows/os_str.rs
new file mode 100644
index 000000000..11883f150
--- /dev/null
+++ b/library/std/src/sys/windows/os_str.rs
@@ -0,0 +1,226 @@
+/// The underlying OsString/OsStr implementation on Windows is a
+/// wrapper around the "WTF-8" encoding; see the `wtf8` module for more.
+use crate::borrow::Cow;
+use crate::collections::TryReserveError;
+use crate::fmt;
+use crate::mem;
+use crate::rc::Rc;
+use crate::sync::Arc;
+use crate::sys_common::wtf8::{Wtf8, Wtf8Buf};
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+
+#[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)
+ }
+}
+
+#[repr(transparent)]
+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 {
+ // SAFETY: Slice is just a wrapper for Wtf8,
+ // and self.inner.as_slice() returns &Wtf8.
+ // Therefore, transmuting &Wtf8 to &Slice is safe.
+ unsafe { mem::transmute(self.inner.as_slice()) }
+ }
+
+ pub fn as_mut_slice(&mut self) -> &mut Slice {
+ // SAFETY: Slice is just a wrapper for Wtf8,
+ // and self.inner.as_mut_slice() returns &mut Wtf8.
+ // Therefore, transmuting &mut Wtf8 to &mut Slice is safe.
+ // Additionally, care should be taken to ensure the slice
+ // is always valid Wtf8.
+ unsafe { mem::transmute(self.inner.as_mut_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 try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
+ self.inner.try_reserve(additional)
+ }
+
+ pub fn reserve_exact(&mut self, additional: usize) {
+ self.inner.reserve_exact(additional)
+ }
+
+ pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
+ self.inner.try_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 {
+ #[inline]
+ 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 }
+ }
+
+ pub fn clone_into(&self, buf: &mut Buf) {
+ self.inner.clone_into(&mut buf.inner)
+ }
+
+ #[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) }
+ }
+
+ #[inline]
+ pub fn make_ascii_lowercase(&mut self) {
+ self.inner.make_ascii_lowercase()
+ }
+
+ #[inline]
+ pub fn make_ascii_uppercase(&mut self) {
+ self.inner.make_ascii_uppercase()
+ }
+
+ #[inline]
+ pub fn to_ascii_lowercase(&self) -> Buf {
+ Buf { inner: self.inner.to_ascii_lowercase() }
+ }
+
+ #[inline]
+ pub fn to_ascii_uppercase(&self) -> Buf {
+ Buf { inner: self.inner.to_ascii_uppercase() }
+ }
+
+ #[inline]
+ pub fn is_ascii(&self) -> bool {
+ self.inner.is_ascii()
+ }
+
+ #[inline]
+ pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
+ self.inner.eq_ignore_ascii_case(&other.inner)
+ }
+}
diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs
new file mode 100644
index 000000000..beeca1917
--- /dev/null
+++ b/library/std/src/sys/windows/path.rs
@@ -0,0 +1,333 @@
+use super::{c, fill_utf16_buf, to_u16s};
+use crate::ffi::{OsStr, OsString};
+use crate::io;
+use crate::mem;
+use crate::path::{Path, PathBuf, Prefix};
+use crate::ptr;
+
+#[cfg(test)]
+mod tests;
+
+pub const MAIN_SEP_STR: &str = "\\";
+pub const MAIN_SEP: char = '\\';
+
+/// # Safety
+///
+/// `bytes` must be a valid wtf8 encoded slice
+#[inline]
+unsafe fn bytes_as_os_str(bytes: &[u8]) -> &OsStr {
+ // &OsStr is layout compatible with &Slice, which is compatible with &Wtf8,
+ // which is compatible with &[u8].
+ mem::transmute(bytes)
+}
+
+#[inline]
+pub fn is_sep_byte(b: u8) -> bool {
+ b == b'/' || b == b'\\'
+}
+
+#[inline]
+pub fn is_verbatim_sep(b: u8) -> bool {
+ b == b'\\'
+}
+
+/// Returns true if `path` looks like a lone filename.
+pub(crate) fn is_file_name(path: &OsStr) -> bool {
+ !path.bytes().iter().copied().any(is_sep_byte)
+}
+pub(crate) fn has_trailing_slash(path: &OsStr) -> bool {
+ let is_verbatim = path.bytes().starts_with(br"\\?\");
+ let is_separator = if is_verbatim { is_verbatim_sep } else { is_sep_byte };
+ if let Some(&c) = path.bytes().last() { is_separator(c) } else { false }
+}
+
+/// Appends a suffix to a path.
+///
+/// Can be used to append an extension without removing an existing extension.
+pub(crate) fn append_suffix(path: PathBuf, suffix: &OsStr) -> PathBuf {
+ let mut path = OsString::from(path);
+ path.push(suffix);
+ path.into()
+}
+
+struct PrefixParser<'a, const LEN: usize> {
+ path: &'a OsStr,
+ prefix: [u8; LEN],
+}
+
+impl<'a, const LEN: usize> PrefixParser<'a, LEN> {
+ #[inline]
+ fn get_prefix(path: &OsStr) -> [u8; LEN] {
+ let mut prefix = [0; LEN];
+ // SAFETY: Only ASCII characters are modified.
+ for (i, &ch) in path.bytes().iter().take(LEN).enumerate() {
+ prefix[i] = if ch == b'/' { b'\\' } else { ch };
+ }
+ prefix
+ }
+
+ fn new(path: &'a OsStr) -> Self {
+ Self { path, prefix: Self::get_prefix(path) }
+ }
+
+ fn as_slice(&self) -> PrefixParserSlice<'a, '_> {
+ PrefixParserSlice {
+ path: self.path,
+ prefix: &self.prefix[..LEN.min(self.path.len())],
+ index: 0,
+ }
+ }
+}
+
+struct PrefixParserSlice<'a, 'b> {
+ path: &'a OsStr,
+ prefix: &'b [u8],
+ index: usize,
+}
+
+impl<'a> PrefixParserSlice<'a, '_> {
+ fn strip_prefix(&self, prefix: &str) -> Option<Self> {
+ self.prefix[self.index..]
+ .starts_with(prefix.as_bytes())
+ .then(|| Self { index: self.index + prefix.len(), ..*self })
+ }
+
+ fn prefix_bytes(&self) -> &'a [u8] {
+ &self.path.bytes()[..self.index]
+ }
+
+ fn finish(self) -> &'a OsStr {
+ // SAFETY: 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.
+ unsafe { bytes_as_os_str(&self.path.bytes()[self.index..]) }
+ }
+}
+
+pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> {
+ use Prefix::{DeviceNS, Disk, Verbatim, VerbatimDisk, VerbatimUNC, UNC};
+
+ let parser = PrefixParser::<8>::new(path);
+ let parser = parser.as_slice();
+ if let Some(parser) = parser.strip_prefix(r"\\") {
+ // \\
+
+ // The meaning of verbatim paths can change when they use a different
+ // separator.
+ if let Some(parser) = parser.strip_prefix(r"?\") && !parser.prefix_bytes().iter().any(|&x| x == b'/') {
+ // \\?\
+ if let Some(parser) = parser.strip_prefix(r"UNC\") {
+ // \\?\UNC\server\share
+
+ let path = parser.finish();
+ let (server, path) = parse_next_component(path, true);
+ let (share, _) = parse_next_component(path, true);
+
+ Some(VerbatimUNC(server, share))
+ } else {
+ let path = parser.finish();
+
+ // in verbatim paths only recognize an exact drive prefix
+ if let Some(drive) = parse_drive_exact(path) {
+ // \\?\C:
+ Some(VerbatimDisk(drive))
+ } else {
+ // \\?\prefix
+ let (prefix, _) = parse_next_component(path, true);
+ Some(Verbatim(prefix))
+ }
+ }
+ } else if let Some(parser) = parser.strip_prefix(r".\") {
+ // \\.\COM42
+ let path = parser.finish();
+ let (prefix, _) = parse_next_component(path, false);
+ Some(DeviceNS(prefix))
+ } else {
+ let path = parser.finish();
+ let (server, path) = parse_next_component(path, false);
+ let (share, _) = parse_next_component(path, false);
+
+ if !server.is_empty() && !share.is_empty() {
+ // \\server\share
+ Some(UNC(server, share))
+ } else {
+ // no valid prefix beginning with "\\" recognized
+ None
+ }
+ }
+ } else if let Some(drive) = parse_drive(path) {
+ // C:
+ Some(Disk(drive))
+ } else {
+ // no prefix
+ None
+ }
+}
+
+// Parses a drive prefix, e.g. "C:" and "C:\whatever"
+fn parse_drive(path: &OsStr) -> Option<u8> {
+ // In most DOS systems, it is not possible to have more than 26 drive letters.
+ // See <https://en.wikipedia.org/wiki/Drive_letter_assignment#Common_assignments>.
+ fn is_valid_drive_letter(drive: &u8) -> bool {
+ drive.is_ascii_alphabetic()
+ }
+
+ match path.bytes() {
+ [drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()),
+ _ => None,
+ }
+}
+
+// Parses a drive prefix exactly, e.g. "C:"
+fn parse_drive_exact(path: &OsStr) -> Option<u8> {
+ // only parse two bytes: the drive letter and the drive separator
+ if path.bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) {
+ parse_drive(path)
+ } else {
+ None
+ }
+}
+
+// Parse the next path component.
+//
+// Returns the next component and the rest of the path excluding the component and separator.
+// Does not recognize `/` as a separator character if `verbatim` is true.
+fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) {
+ let separator = if verbatim { is_verbatim_sep } else { is_sep_byte };
+
+ match path.bytes().iter().position(|&x| separator(x)) {
+ Some(separator_start) => {
+ let separator_end = separator_start + 1;
+
+ let component = &path.bytes()[..separator_start];
+
+ // Panic safe
+ // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index.
+ let path = &path.bytes()[separator_end..];
+
+ // SAFETY: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\')
+ // is encoded in a single byte, therefore `bytes[separator_start]` and
+ // `bytes[separator_end]` must be code point boundaries and thus
+ // `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices.
+ unsafe { (bytes_as_os_str(component), bytes_as_os_str(path)) }
+ }
+ None => (path, OsStr::new("")),
+ }
+}
+
+/// Returns a UTF-16 encoded path capable of bypassing the legacy `MAX_PATH` limits.
+///
+/// This path may or may not have a verbatim prefix.
+pub(crate) fn maybe_verbatim(path: &Path) -> io::Result<Vec<u16>> {
+ // Normally the MAX_PATH is 260 UTF-16 code units (including the NULL).
+ // However, for APIs such as CreateDirectory[1], the limit is 248.
+ //
+ // [1]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createdirectorya#parameters
+ const LEGACY_MAX_PATH: usize = 248;
+ // UTF-16 encoded code points, used in parsing and building UTF-16 paths.
+ // All of these are in the ASCII range so they can be cast directly to `u16`.
+ const SEP: u16 = b'\\' as _;
+ const ALT_SEP: u16 = b'/' as _;
+ const QUERY: u16 = b'?' as _;
+ const COLON: u16 = b':' as _;
+ const DOT: u16 = b'.' as _;
+ const U: u16 = b'U' as _;
+ const N: u16 = b'N' as _;
+ const C: u16 = b'C' as _;
+
+ // \\?\
+ const VERBATIM_PREFIX: &[u16] = &[SEP, SEP, QUERY, SEP];
+ // \??\
+ const NT_PREFIX: &[u16] = &[SEP, QUERY, QUERY, SEP];
+ // \\?\UNC\
+ const UNC_PREFIX: &[u16] = &[SEP, SEP, QUERY, SEP, U, N, C, SEP];
+
+ let mut path = to_u16s(path)?;
+ if path.starts_with(VERBATIM_PREFIX) || path.starts_with(NT_PREFIX) || path == &[0] {
+ // Early return for paths that are already verbatim or empty.
+ return Ok(path);
+ } else if path.len() < LEGACY_MAX_PATH {
+ // Early return if an absolute path is less < 260 UTF-16 code units.
+ // This is an optimization to avoid calling `GetFullPathNameW` unnecessarily.
+ match path.as_slice() {
+ // Starts with `D:`, `D:\`, `D:/`, etc.
+ // Does not match if the path starts with a `\` or `/`.
+ [drive, COLON, 0] | [drive, COLON, SEP | ALT_SEP, ..]
+ if *drive != SEP && *drive != ALT_SEP =>
+ {
+ return Ok(path);
+ }
+ // Starts with `\\`, `//`, etc
+ [SEP | ALT_SEP, SEP | ALT_SEP, ..] => return Ok(path),
+ _ => {}
+ }
+ }
+
+ // Firstly, get the absolute path using `GetFullPathNameW`.
+ // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew
+ let lpfilename = path.as_ptr();
+ fill_utf16_buf(
+ // SAFETY: `fill_utf16_buf` ensures the `buffer` and `size` are valid.
+ // `lpfilename` is a pointer to a null terminated string that is not
+ // invalidated until after `GetFullPathNameW` returns successfully.
+ |buffer, size| unsafe { c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()) },
+ |mut absolute| {
+ path.clear();
+
+ // Secondly, add the verbatim prefix. This is easier here because we know the
+ // path is now absolute and fully normalized (e.g. `/` has been changed to `\`).
+ let prefix = match absolute {
+ // C:\ => \\?\C:\
+ [_, COLON, SEP, ..] => VERBATIM_PREFIX,
+ // \\.\ => \\?\
+ [SEP, SEP, DOT, SEP, ..] => {
+ absolute = &absolute[4..];
+ VERBATIM_PREFIX
+ }
+ // Leave \\?\ and \??\ as-is.
+ [SEP, SEP, QUERY, SEP, ..] | [SEP, QUERY, QUERY, SEP, ..] => &[],
+ // \\ => \\?\UNC\
+ [SEP, SEP, ..] => {
+ absolute = &absolute[2..];
+ UNC_PREFIX
+ }
+ // Anything else we leave alone.
+ _ => &[],
+ };
+
+ path.reserve_exact(prefix.len() + absolute.len() + 1);
+ path.extend_from_slice(prefix);
+ path.extend_from_slice(absolute);
+ path.push(0);
+ },
+ )?;
+ Ok(path)
+}
+
+/// Make a Windows path absolute.
+pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
+ let path = path.as_os_str();
+ let prefix = parse_prefix(path);
+ // Verbatim paths should not be modified.
+ if prefix.map(|x| x.is_verbatim()).unwrap_or(false) {
+ // NULs in verbatim paths are rejected for consistency.
+ if path.bytes().contains(&0) {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "strings passed to WinAPI cannot contain NULs",
+ ));
+ }
+ return Ok(path.to_owned().into());
+ }
+
+ let path = to_u16s(path)?;
+ let lpfilename = path.as_ptr();
+ fill_utf16_buf(
+ // SAFETY: `fill_utf16_buf` ensures the `buffer` and `size` are valid.
+ // `lpfilename` is a pointer to a null terminated string that is not
+ // invalidated until after `GetFullPathNameW` returns successfully.
+ |buffer, size| unsafe { c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()) },
+ super::os2path,
+ )
+}
diff --git a/library/std/src/sys/windows/path/tests.rs b/library/std/src/sys/windows/path/tests.rs
new file mode 100644
index 000000000..6eab38cab
--- /dev/null
+++ b/library/std/src/sys/windows/path/tests.rs
@@ -0,0 +1,137 @@
+use super::*;
+
+#[test]
+fn test_parse_next_component() {
+ assert_eq!(
+ parse_next_component(OsStr::new(r"server\share"), true),
+ (OsStr::new(r"server"), OsStr::new(r"share"))
+ );
+
+ assert_eq!(
+ parse_next_component(OsStr::new(r"server/share"), true),
+ (OsStr::new(r"server/share"), OsStr::new(r""))
+ );
+
+ assert_eq!(
+ parse_next_component(OsStr::new(r"server/share"), false),
+ (OsStr::new(r"server"), OsStr::new(r"share"))
+ );
+
+ assert_eq!(
+ parse_next_component(OsStr::new(r"server\"), false),
+ (OsStr::new(r"server"), OsStr::new(r""))
+ );
+
+ assert_eq!(
+ parse_next_component(OsStr::new(r"\server\"), false),
+ (OsStr::new(r""), OsStr::new(r"server\"))
+ );
+
+ assert_eq!(
+ parse_next_component(OsStr::new(r"servershare"), false),
+ (OsStr::new(r"servershare"), OsStr::new(""))
+ );
+}
+
+#[test]
+fn verbatim() {
+ use crate::path::Path;
+ fn check(path: &str, expected: &str) {
+ let verbatim = maybe_verbatim(Path::new(path)).unwrap();
+ let verbatim = String::from_utf16_lossy(verbatim.strip_suffix(&[0]).unwrap());
+ assert_eq!(&verbatim, expected, "{}", path);
+ }
+
+ // Ensure long paths are correctly prefixed.
+ check(
+ r"C:\Program Files\Rust\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
+ r"\\?\C:\Program Files\Rust\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
+ );
+ check(
+ r"\\server\share\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
+ r"\\?\UNC\server\share\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
+ );
+ check(
+ r"\\.\PIPE\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
+ r"\\?\PIPE\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
+ );
+ // `\\?\` prefixed paths are left unchanged...
+ check(
+ r"\\?\verbatim.\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
+ r"\\?\verbatim.\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
+ );
+ // But `//?/` is not a verbatim prefix so it will be normalized.
+ check(
+ r"//?/E:/verbatim.\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
+ r"\\?\E:\verbatim\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
+ );
+
+ // For performance, short absolute paths are left unchanged.
+ check(r"C:\Program Files\Rust", r"C:\Program Files\Rust");
+ check(r"\\server\share", r"\\server\share");
+ check(r"\\.\COM1", r"\\.\COM1");
+
+ // Check that paths of length 247 are converted to verbatim.
+ // This is necessary for `CreateDirectory`.
+ check(
+ r"C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ r"\\?\C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ );
+
+ // Make sure opening a drive will work.
+ check("Z:", "Z:");
+
+ // A path that contains null is not a valid path.
+ assert!(maybe_verbatim(Path::new("\0")).is_err());
+}
+
+fn parse_prefix(path: &str) -> Option<Prefix<'_>> {
+ super::parse_prefix(OsStr::new(path))
+}
+
+#[test]
+fn test_parse_prefix_verbatim() {
+ let prefix = Some(Prefix::VerbatimDisk(b'C'));
+ assert_eq!(prefix, parse_prefix(r"\\?\C:/windows/system32/notepad.exe"));
+ assert_eq!(prefix, parse_prefix(r"\\?\C:\windows\system32\notepad.exe"));
+}
+
+#[test]
+fn test_parse_prefix_verbatim_device() {
+ let prefix = Some(Prefix::UNC(OsStr::new("?"), OsStr::new("C:")));
+ assert_eq!(prefix, parse_prefix(r"//?/C:/windows/system32/notepad.exe"));
+ assert_eq!(prefix, parse_prefix(r"//?/C:\windows\system32\notepad.exe"));
+ assert_eq!(prefix, parse_prefix(r"/\?\C:\windows\system32\notepad.exe"));
+ assert_eq!(prefix, parse_prefix(r"\\?/C:\windows\system32\notepad.exe"));
+}
+
+// See #93586 for more infomation.
+#[test]
+fn test_windows_prefix_components() {
+ use crate::path::Path;
+
+ let path = Path::new("C:");
+ let mut components = path.components();
+ let drive = components.next().expect("drive is expected here");
+ assert_eq!(drive.as_os_str(), OsStr::new("C:"));
+ assert_eq!(components.as_path(), Path::new(""));
+}
+
+/// See #101358.
+///
+/// Note that the exact behaviour here may change in the future.
+/// In which case this test will need to adjusted.
+#[test]
+fn broken_unc_path() {
+ use crate::path::Component;
+
+ let mut components = Path::new(r"\\foo\\bar\\").components();
+ assert_eq!(components.next(), Some(Component::RootDir));
+ assert_eq!(components.next(), Some(Component::Normal("foo".as_ref())));
+ assert_eq!(components.next(), Some(Component::Normal("bar".as_ref())));
+
+ let mut components = Path::new("//foo//bar//").components();
+ assert_eq!(components.next(), Some(Component::RootDir));
+ assert_eq!(components.next(), Some(Component::Normal("foo".as_ref())));
+ assert_eq!(components.next(), Some(Component::Normal("bar".as_ref())));
+}
diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs
new file mode 100644
index 000000000..013c776c4
--- /dev/null
+++ b/library/std/src/sys/windows/pipe.rs
@@ -0,0 +1,538 @@
+use crate::os::windows::prelude::*;
+
+use crate::ffi::OsStr;
+use crate::io::{self, IoSlice, IoSliceMut};
+use crate::mem;
+use crate::path::Path;
+use crate::ptr;
+use crate::slice;
+use crate::sync::atomic::AtomicUsize;
+use crate::sync::atomic::Ordering::SeqCst;
+use crate::sys::c;
+use crate::sys::fs::{File, OpenOptions};
+use crate::sys::handle::Handle;
+use crate::sys::hashmap_random_keys;
+use crate::sys_common::IntoInner;
+
+////////////////////////////////////////////////////////////////////////////////
+// Anonymous pipes
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct AnonPipe {
+ inner: Handle,
+}
+
+impl IntoInner<Handle> for AnonPipe {
+ fn into_inner(self) -> Handle {
+ self.inner
+ }
+}
+
+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 and
+/// `theirs` is writable. Conversely, if `ours_readable` is `false`, then `ours`
+/// is writable and `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, their_handle_inheritable: bool) -> io::Result<Pipes> {
+ // A 64kb pipe capacity is the same as a typical Linux default.
+ const PIPE_BUFFER_CAPACITY: u32 = 64 * 1024;
+
+ // 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,
+ PIPE_BUFFER_CAPACITY,
+ PIPE_BUFFER_CAPACITY,
+ 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::from_raw_handle(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 size = mem::size_of::<c::SECURITY_ATTRIBUTES>();
+ let mut sa = c::SECURITY_ATTRIBUTES {
+ nLength: size as c::DWORD,
+ lpSecurityDescriptor: ptr::null_mut(),
+ bInheritHandle: their_handle_inheritable as i32,
+ };
+ opts.security_attributes(&mut sa);
+ let theirs = File::open(Path::new(&name), &opts)?;
+ let theirs = AnonPipe { inner: theirs.into_inner() };
+
+ Ok(Pipes {
+ ours: AnonPipe { inner: ours },
+ theirs: AnonPipe { inner: theirs.into_inner() },
+ })
+ }
+}
+
+/// Takes an asynchronous source pipe and returns a synchronous pipe suitable
+/// for sending to a child process.
+///
+/// This is achieved by creating a new set of pipes and spawning a thread that
+/// relays messages between the source and the synchronous pipe.
+pub fn spawn_pipe_relay(
+ source: &AnonPipe,
+ ours_readable: bool,
+ their_handle_inheritable: bool,
+) -> io::Result<AnonPipe> {
+ // We need this handle to live for the lifetime of the thread spawned below.
+ let source = source.duplicate()?;
+
+ // create a new pair of anon pipes.
+ let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?;
+
+ // Spawn a thread that passes messages from one pipe to the other.
+ // Any errors will simply cause the thread to exit.
+ let (reader, writer) = if ours_readable { (ours, source) } else { (source, ours) };
+ crate::thread::spawn(move || {
+ let mut buf = [0_u8; 4096];
+ 'reader: while let Ok(len) = reader.read(&mut buf) {
+ if len == 0 {
+ break;
+ }
+ let mut start = 0;
+ while let Ok(written) = writer.write(&buf[start..len]) {
+ start += written;
+ if start == len {
+ continue 'reader;
+ }
+ }
+ break;
+ }
+ });
+
+ // Return the pipe that should be sent to the child process.
+ Ok(theirs)
+}
+
+fn random_number() -> usize {
+ static N: AtomicUsize = AtomicUsize::new(0);
+ loop {
+ if N.load(SeqCst) != 0 {
+ return N.fetch_add(1, SeqCst);
+ }
+
+ N.store(hashmap_random_keys().0 as usize, SeqCst);
+ }
+}
+
+// Abstracts over `ReadFileEx` and `WriteFileEx`
+type AlertableIoFn = unsafe extern "system" fn(
+ BorrowedHandle<'_>,
+ c::LPVOID,
+ c::DWORD,
+ c::LPOVERLAPPED,
+ c::LPOVERLAPPED_COMPLETION_ROUTINE,
+) -> c::BOOL;
+
+impl AnonPipe {
+ pub fn handle(&self) -> &Handle {
+ &self.inner
+ }
+ pub fn into_handle(self) -> Handle {
+ self.inner
+ }
+ fn duplicate(&self) -> io::Result<Self> {
+ self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner })
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ let result = unsafe {
+ let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
+ self.alertable_io_internal(c::ReadFileEx, buf.as_mut_ptr() as _, len)
+ };
+
+ match result {
+ // 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() == io::ErrorKind::BrokenPipe => Ok(0),
+ _ => result,
+ }
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.inner.read_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ self.inner.is_read_vectored()
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ unsafe {
+ let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
+ self.alertable_io_internal(c::WriteFileEx, buf.as_ptr() as _, len)
+ }
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.inner.write_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ self.inner.is_write_vectored()
+ }
+
+ /// Synchronizes asynchronous reads or writes using our anonymous pipe.
+ ///
+ /// This is a wrapper around [`ReadFileEx`] or [`WriteFileEx`] that uses
+ /// [Asynchronous Procedure Call] (APC) to synchronize reads or writes.
+ ///
+ /// Note: This should not be used for handles we don't create.
+ ///
+ /// # Safety
+ ///
+ /// `buf` must be a pointer to a buffer that's valid for reads or writes
+ /// up to `len` bytes. The `AlertableIoFn` must be either `ReadFileEx` or `WriteFileEx`
+ ///
+ /// [`ReadFileEx`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfileex
+ /// [`WriteFileEx`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefileex
+ /// [Asynchronous Procedure Call]: https://docs.microsoft.com/en-us/windows/win32/sync/asynchronous-procedure-calls
+ unsafe fn alertable_io_internal(
+ &self,
+ io: AlertableIoFn,
+ buf: c::LPVOID,
+ len: c::DWORD,
+ ) -> io::Result<usize> {
+ // Use "alertable I/O" to synchronize the pipe I/O.
+ // This has four steps.
+ //
+ // STEP 1: Start the asynchronous I/O operation.
+ // This simply calls either `ReadFileEx` or `WriteFileEx`,
+ // giving it a pointer to the buffer and callback function.
+ //
+ // STEP 2: Enter an alertable state.
+ // The callback set in step 1 will not be called until the thread
+ // enters an "alertable" state. This can be done using `SleepEx`.
+ //
+ // STEP 3: The callback
+ // Once the I/O is complete and the thread is in an alertable state,
+ // the callback will be run on the same thread as the call to
+ // `ReadFileEx` or `WriteFileEx` done in step 1.
+ // In the callback we simply set the result of the async operation.
+ //
+ // STEP 4: Return the result.
+ // At this point we'll have a result from the callback function
+ // and can simply return it. Note that we must not return earlier,
+ // while the I/O is still in progress.
+
+ // The result that will be set from the asynchronous callback.
+ let mut async_result: Option<AsyncResult> = None;
+ struct AsyncResult {
+ error: u32,
+ transfered: u32,
+ }
+
+ // STEP 3: The callback.
+ unsafe extern "system" fn callback(
+ dwErrorCode: u32,
+ dwNumberOfBytesTransfered: u32,
+ lpOverlapped: *mut c::OVERLAPPED,
+ ) {
+ // Set `async_result` using a pointer smuggled through `hEvent`.
+ let result = AsyncResult { error: dwErrorCode, transfered: dwNumberOfBytesTransfered };
+ *(*lpOverlapped).hEvent.cast::<Option<AsyncResult>>() = Some(result);
+ }
+
+ // STEP 1: Start the I/O operation.
+ let mut overlapped: c::OVERLAPPED = crate::mem::zeroed();
+ // `hEvent` is unused by `ReadFileEx` and `WriteFileEx`.
+ // Therefore the documentation suggests using it to smuggle a pointer to the callback.
+ overlapped.hEvent = &mut async_result as *mut _ as *mut _;
+
+ // Asynchronous read of the pipe.
+ // If successful, `callback` will be called once it completes.
+ let result = io(self.inner.as_handle(), buf, len, &mut overlapped, callback);
+ if result == c::FALSE {
+ // We can return here because the call failed.
+ // After this we must not return until the I/O completes.
+ return Err(io::Error::last_os_error());
+ }
+
+ // Wait indefinitely for the result.
+ let result = loop {
+ // STEP 2: Enter an alertable state.
+ // The second parameter of `SleepEx` is used to make this sleep alertable.
+ c::SleepEx(c::INFINITE, c::TRUE);
+ if let Some(result) = async_result {
+ break result;
+ }
+ };
+ // STEP 4: Return the result.
+ // `async_result` is always `Some` at this point
+ match result.error {
+ c::ERROR_SUCCESS => Ok(result.transfered as usize),
+ error => Err(io::Error::from_raw_os_error(error as _)),
+ }
+ }
+}
+
+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.as_raw_handle(), p2.event.as_raw_handle()];
+
+ // 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.as_raw_handle();
+ 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.
+ ///
+ /// Returns 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::take(self.dst);
+ 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().add(v.len()), v.capacity() - v.len())
+}
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
new file mode 100644
index 000000000..02d5af471
--- /dev/null
+++ b/library/std/src/sys/windows/process.rs
@@ -0,0 +1,847 @@
+#![unstable(feature = "process_internals", issue = "none")]
+
+#[cfg(test)]
+mod tests;
+
+use crate::cmp;
+use crate::collections::BTreeMap;
+use crate::env;
+use crate::env::consts::{EXE_EXTENSION, EXE_SUFFIX};
+use crate::ffi::{OsStr, OsString};
+use crate::fmt;
+use crate::io::{self, Error, ErrorKind};
+use crate::mem;
+use crate::num::NonZeroI32;
+use crate::os::windows::ffi::{OsStrExt, OsStringExt};
+use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle};
+use crate::path::{Path, PathBuf};
+use crate::ptr;
+use crate::sys::args::{self, Arg};
+use crate::sys::c;
+use crate::sys::c::NonZeroDWORD;
+use crate::sys::cvt;
+use crate::sys::fs::{File, OpenOptions};
+use crate::sys::handle::Handle;
+use crate::sys::path;
+use crate::sys::pipe::{self, AnonPipe};
+use crate::sys::stdio;
+use crate::sys_common::mutex::StaticMutex;
+use crate::sys_common::process::{CommandEnv, CommandEnvs};
+use crate::sys_common::IntoInner;
+
+use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS};
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+#[derive(Clone, Debug, Eq)]
+#[doc(hidden)]
+pub struct EnvKey {
+ os_string: OsString,
+ // This stores a UTF-16 encoded string to workaround the mismatch between
+ // Rust's OsString (WTF-8) and the Windows API string type (UTF-16).
+ // Normally converting on every API call is acceptable but here
+ // `c::CompareStringOrdinal` will be called for every use of `==`.
+ utf16: Vec<u16>,
+}
+
+impl EnvKey {
+ fn new<T: Into<OsString>>(key: T) -> Self {
+ EnvKey::from(key.into())
+ }
+}
+
+// Comparing Windows environment variable keys[1] are behaviourally the
+// composition of two operations[2]:
+//
+// 1. Case-fold both strings. This is done using a language-independent
+// uppercase mapping that's unique to Windows (albeit based on data from an
+// older Unicode spec). It only operates on individual UTF-16 code units so
+// surrogates are left unchanged. This uppercase mapping can potentially change
+// between Windows versions.
+//
+// 2. Perform an ordinal comparison of the strings. A comparison using ordinal
+// is just a comparison based on the numerical value of each UTF-16 code unit[3].
+//
+// Because the case-folding mapping is unique to Windows and not guaranteed to
+// be stable, we ask the OS to compare the strings for us. This is done by
+// calling `CompareStringOrdinal`[4] with `bIgnoreCase` set to `TRUE`.
+//
+// [1] https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#choosing-a-stringcomparison-member-for-your-method-call
+// [2] https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#stringtoupper-and-stringtolower
+// [3] https://docs.microsoft.com/en-us/dotnet/api/system.stringcomparison?view=net-5.0#System_StringComparison_Ordinal
+// [4] https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-comparestringordinal
+impl Ord for EnvKey {
+ fn cmp(&self, other: &Self) -> cmp::Ordering {
+ unsafe {
+ let result = c::CompareStringOrdinal(
+ self.utf16.as_ptr(),
+ self.utf16.len() as _,
+ other.utf16.as_ptr(),
+ other.utf16.len() as _,
+ c::TRUE,
+ );
+ match result {
+ c::CSTR_LESS_THAN => cmp::Ordering::Less,
+ c::CSTR_EQUAL => cmp::Ordering::Equal,
+ c::CSTR_GREATER_THAN => cmp::Ordering::Greater,
+ // `CompareStringOrdinal` should never fail so long as the parameters are correct.
+ _ => panic!("comparing environment keys failed: {}", Error::last_os_error()),
+ }
+ }
+ }
+}
+impl PartialOrd for EnvKey {
+ fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+impl PartialEq for EnvKey {
+ fn eq(&self, other: &Self) -> bool {
+ if self.utf16.len() != other.utf16.len() {
+ false
+ } else {
+ self.cmp(other) == cmp::Ordering::Equal
+ }
+ }
+}
+impl PartialOrd<str> for EnvKey {
+ fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
+ Some(self.cmp(&EnvKey::new(other)))
+ }
+}
+impl PartialEq<str> for EnvKey {
+ fn eq(&self, other: &str) -> bool {
+ if self.os_string.len() != other.len() {
+ false
+ } else {
+ self.cmp(&EnvKey::new(other)) == cmp::Ordering::Equal
+ }
+ }
+}
+
+// Environment variable keys should preserve their original case even though
+// they are compared using a caseless string mapping.
+impl From<OsString> for EnvKey {
+ fn from(k: OsString) -> Self {
+ EnvKey { utf16: k.encode_wide().collect(), os_string: k }
+ }
+}
+
+impl From<EnvKey> for OsString {
+ fn from(k: EnvKey) -> Self {
+ k.os_string
+ }
+}
+
+impl From<&OsStr> for EnvKey {
+ fn from(k: &OsStr) -> Self {
+ Self::from(k.to_os_string())
+ }
+}
+
+impl AsRef<OsStr> for EnvKey {
+ fn as_ref(&self) -> &OsStr {
+ &self.os_string
+ }
+}
+
+pub(crate) fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
+ if str.as_ref().encode_wide().any(|b| b == 0) {
+ Err(io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data"))
+ } else {
+ Ok(str)
+ }
+}
+
+pub struct Command {
+ program: OsString,
+ args: Vec<Arg>,
+ env: CommandEnv,
+ cwd: Option<OsString>,
+ flags: u32,
+ detach: bool, // not currently exposed in std::process
+ stdin: Option<Stdio>,
+ stdout: Option<Stdio>,
+ stderr: Option<Stdio>,
+ force_quotes_enabled: bool,
+}
+
+pub enum Stdio {
+ Inherit,
+ Null,
+ MakePipe,
+ Pipe(AnonPipe),
+ Handle(Handle),
+}
+
+pub struct StdioPipes {
+ pub stdin: Option<AnonPipe>,
+ pub stdout: Option<AnonPipe>,
+ pub stderr: Option<AnonPipe>,
+}
+
+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,
+ force_quotes_enabled: false,
+ }
+ }
+
+ pub fn arg(&mut self, arg: &OsStr) {
+ self.args.push(Arg::Regular(arg.to_os_string()))
+ }
+ pub fn env_mut(&mut self) -> &mut CommandEnv {
+ &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 force_quotes(&mut self, enabled: bool) {
+ self.force_quotes_enabled = enabled;
+ }
+
+ pub fn raw_arg(&mut self, command_str_to_append: &OsStr) {
+ self.args.push(Arg::Raw(command_str_to_append.to_os_string()))
+ }
+
+ pub fn get_program(&self) -> &OsStr {
+ &self.program
+ }
+
+ pub fn get_args(&self) -> CommandArgs<'_> {
+ let iter = self.args.iter();
+ CommandArgs { iter }
+ }
+
+ pub fn get_envs(&self) -> CommandEnvs<'_> {
+ self.env.iter()
+ }
+
+ pub fn get_current_dir(&self) -> Option<&Path> {
+ self.cwd.as_ref().map(|cwd| Path::new(cwd))
+ }
+
+ pub fn spawn(
+ &mut self,
+ default: Stdio,
+ needs_stdin: bool,
+ ) -> io::Result<(Process, StdioPipes)> {
+ let maybe_env = self.env.capture_if_changed();
+
+ let mut si = zeroed_startupinfo();
+ si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD;
+ si.dwFlags = c::STARTF_USESTDHANDLES;
+
+ let child_paths = if let Some(env) = maybe_env.as_ref() {
+ env.get(&EnvKey::new("PATH")).map(|s| s.as_os_str())
+ } else {
+ None
+ };
+ let program = resolve_exe(&self.program, || env::var_os("PATH"), child_paths)?;
+ // Case insensitive "ends_with" of UTF-16 encoded ".bat" or ".cmd"
+ let is_batch_file = matches!(
+ program.len().checked_sub(5).and_then(|i| program.get(i..)),
+ Some([46, 98 | 66, 97 | 65, 116 | 84, 0] | [46, 99 | 67, 109 | 77, 100 | 68, 0])
+ );
+ let (program, mut cmd_str) = if is_batch_file {
+ (
+ command_prompt()?,
+ args::make_bat_command_line(
+ &args::to_user_path(program)?,
+ &self.args,
+ self.force_quotes_enabled,
+ )?,
+ )
+ } else {
+ let cmd_str = make_command_line(&self.program, &self.args, self.force_quotes_enabled)?;
+ (program, cmd_str)
+ };
+ 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:
+ // https://support.microsoft.com/kb/315939
+ static CREATE_PROCESS_LOCK: StaticMutex = StaticMutex::new();
+
+ let _guard = unsafe { CREATE_PROCESS_LOCK.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.as_raw_handle();
+ si.hStdOutput = stdout.as_raw_handle();
+ si.hStdError = stderr.as_raw_handle();
+
+ unsafe {
+ cvt(c::CreateProcessW(
+ program.as_ptr(),
+ cmd_str.as_mut_ptr(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ c::TRUE,
+ flags,
+ envp,
+ dirp,
+ &mut si,
+ &mut pi,
+ ))
+ }?;
+
+ unsafe {
+ Ok((
+ Process {
+ handle: Handle::from_raw_handle(pi.hProcess),
+ main_thread_handle: Handle::from_raw_handle(pi.hThread),
+ },
+ pipes,
+ ))
+ }
+ }
+}
+
+impl fmt::Debug for Command {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.program.fmt(f)?;
+ for arg in &self.args {
+ f.write_str(" ")?;
+ match arg {
+ Arg::Regular(s) => s.fmt(f),
+ Arg::Raw(s) => f.write_str(&s.to_string_lossy()),
+ }?;
+ }
+ Ok(())
+ }
+}
+
+// Resolve `exe_path` to the executable name.
+//
+// * If the path is simply a file name then use the paths given by `search_paths` to find the executable.
+// * Otherwise use the `exe_path` as given.
+//
+// This function may also append `.exe` to the name. The rationale for doing so is as follows:
+//
+// It is a very strong convention that Windows executables have the `exe` extension.
+// In Rust, it is common to omit this extension.
+// Therefore this functions first assumes `.exe` was intended.
+// It falls back to the plain file name if a full path is given and the extension is omitted
+// or if only a file name is given and it already contains an extension.
+fn resolve_exe<'a>(
+ exe_path: &'a OsStr,
+ parent_paths: impl FnOnce() -> Option<OsString>,
+ child_paths: Option<&OsStr>,
+) -> io::Result<Vec<u16>> {
+ // Early return if there is no filename.
+ if exe_path.is_empty() || path::has_trailing_slash(exe_path) {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "program path has no file name",
+ ));
+ }
+ // Test if the file name has the `exe` extension.
+ // This does a case-insensitive `ends_with`.
+ let has_exe_suffix = if exe_path.len() >= EXE_SUFFIX.len() {
+ exe_path.bytes()[exe_path.len() - EXE_SUFFIX.len()..]
+ .eq_ignore_ascii_case(EXE_SUFFIX.as_bytes())
+ } else {
+ false
+ };
+
+ // If `exe_path` is an absolute path or a sub-path then don't search `PATH` for it.
+ if !path::is_file_name(exe_path) {
+ if has_exe_suffix {
+ // The application name is a path to a `.exe` file.
+ // Let `CreateProcessW` figure out if it exists or not.
+ return path::maybe_verbatim(Path::new(exe_path));
+ }
+ let mut path = PathBuf::from(exe_path);
+
+ // Append `.exe` if not already there.
+ path = path::append_suffix(path, EXE_SUFFIX.as_ref());
+ if let Some(path) = program_exists(&path) {
+ return Ok(path);
+ } else {
+ // It's ok to use `set_extension` here because the intent is to
+ // remove the extension that was just added.
+ path.set_extension("");
+ return path::maybe_verbatim(&path);
+ }
+ } else {
+ ensure_no_nuls(exe_path)?;
+ // From the `CreateProcessW` docs:
+ // > If the file name does not contain an extension, .exe is appended.
+ // Note that this rule only applies when searching paths.
+ let has_extension = exe_path.bytes().contains(&b'.');
+
+ // Search the directories given by `search_paths`.
+ let result = search_paths(parent_paths, child_paths, |mut path| {
+ path.push(&exe_path);
+ if !has_extension {
+ path.set_extension(EXE_EXTENSION);
+ }
+ program_exists(&path)
+ });
+ if let Some(path) = result {
+ return Ok(path);
+ }
+ }
+ // If we get here then the executable cannot be found.
+ Err(io::const_io_error!(io::ErrorKind::NotFound, "program not found"))
+}
+
+// Calls `f` for every path that should be used to find an executable.
+// Returns once `f` returns the path to an executable or all paths have been searched.
+fn search_paths<Paths, Exists>(
+ parent_paths: Paths,
+ child_paths: Option<&OsStr>,
+ mut exists: Exists,
+) -> Option<Vec<u16>>
+where
+ Paths: FnOnce() -> Option<OsString>,
+ Exists: FnMut(PathBuf) -> Option<Vec<u16>>,
+{
+ // 1. Child paths
+ // This is for consistency with Rust's historic behaviour.
+ if let Some(paths) = child_paths {
+ for path in env::split_paths(paths).filter(|p| !p.as_os_str().is_empty()) {
+ if let Some(path) = exists(path) {
+ return Some(path);
+ }
+ }
+ }
+
+ // 2. Application path
+ if let Ok(mut app_path) = env::current_exe() {
+ app_path.pop();
+ if let Some(path) = exists(app_path) {
+ return Some(path);
+ }
+ }
+
+ // 3 & 4. System paths
+ // SAFETY: This uses `fill_utf16_buf` to safely call the OS functions.
+ unsafe {
+ if let Ok(Some(path)) = super::fill_utf16_buf(
+ |buf, size| c::GetSystemDirectoryW(buf, size),
+ |buf| exists(PathBuf::from(OsString::from_wide(buf))),
+ ) {
+ return Some(path);
+ }
+ #[cfg(not(target_vendor = "uwp"))]
+ {
+ if let Ok(Some(path)) = super::fill_utf16_buf(
+ |buf, size| c::GetWindowsDirectoryW(buf, size),
+ |buf| exists(PathBuf::from(OsString::from_wide(buf))),
+ ) {
+ return Some(path);
+ }
+ }
+ }
+
+ // 5. Parent paths
+ if let Some(parent_paths) = parent_paths() {
+ for path in env::split_paths(&parent_paths).filter(|p| !p.as_os_str().is_empty()) {
+ if let Some(path) = exists(path) {
+ return Some(path);
+ }
+ }
+ }
+ None
+}
+
+/// Check if a file exists without following symlinks.
+fn program_exists(path: &Path) -> Option<Vec<u16>> {
+ unsafe {
+ let path = path::maybe_verbatim(path).ok()?;
+ // Getting attributes using `GetFileAttributesW` does not follow symlinks
+ // and it will almost always be successful if the link exists.
+ // There are some exceptions for special system files (e.g. the pagefile)
+ // but these are not executable.
+ if c::GetFileAttributesW(path.as_ptr()) == c::INVALID_FILE_ATTRIBUTES {
+ None
+ } else {
+ Some(path)
+ }
+ }
+}
+
+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_handle(stdio_id) {
+ Ok(io) => unsafe {
+ let io = Handle::from_raw_handle(io);
+ let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS);
+ io.into_raw_handle();
+ ret
+ },
+ Err(..) => unsafe { Ok(Handle::from_raw_handle(c::INVALID_HANDLE_VALUE)) },
+ },
+
+ Stdio::MakePipe => {
+ let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
+ let pipes = pipe::anon_pipe(ours_readable, true)?;
+ *pipe = Some(pipes.ours);
+ Ok(pipes.theirs.into_handle())
+ }
+
+ Stdio::Pipe(ref source) => {
+ let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
+ pipe::spawn_pipe_relay(source, ours_readable, true).map(AnonPipe::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_inner())
+ }
+ }
+ }
+}
+
+impl From<AnonPipe> for Stdio {
+ fn from(pipe: AnonPipe) -> Stdio {
+ Stdio::Pipe(pipe)
+ }
+}
+
+impl From<File> for Stdio {
+ fn from(file: File) -> Stdio {
+ Stdio::Handle(file.into_inner())
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// 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,
+ main_thread_handle: Handle,
+}
+
+impl Process {
+ pub fn kill(&mut self) -> io::Result<()> {
+ cvt(unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) })?;
+ Ok(())
+ }
+
+ pub fn id(&self) -> u32 {
+ unsafe { c::GetProcessId(self.handle.as_raw_handle()) as u32 }
+ }
+
+ pub fn main_thread_handle(&self) -> BorrowedHandle<'_> {
+ self.main_thread_handle.as_handle()
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ unsafe {
+ let res = c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE);
+ if res != c::WAIT_OBJECT_0 {
+ return Err(Error::last_os_error());
+ }
+ let mut status = 0;
+ cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?;
+ Ok(ExitStatus(status))
+ }
+ }
+
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+ unsafe {
+ match c::WaitForSingleObject(self.handle.as_raw_handle(), 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.as_raw_handle(), &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 exit_ok(&self) -> Result<(), ExitStatusError> {
+ match NonZeroDWORD::try_from(self.0) {
+ /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
+ /* was zero, couldn't convert */ Err(_) => Ok(()),
+ }
+ }
+ pub fn code(&self) -> Option<i32> {
+ Some(self.0 as i32)
+ }
+}
+
+/// Converts a raw `c::DWORD` to a type-safe `ExitStatus` by wrapping it without copying.
+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 {
+ // Windows exit codes with the high bit set typically mean some form of
+ // unhandled exception or warning. In this scenario printing the exit
+ // code in decimal doesn't always make sense because it's a very large
+ // and somewhat gibberish number. The hex code is a bit more
+ // recognizable and easier to search for, so print that.
+ if self.0 & 0x80000000 != 0 {
+ write!(f, "exit code: {:#x}", self.0)
+ } else {
+ write!(f, "exit code: {}", self.0)
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatusError(c::NonZeroDWORD);
+
+impl Into<ExitStatus> for ExitStatusError {
+ fn into(self) -> ExitStatus {
+ ExitStatus(self.0.into())
+ }
+}
+
+impl ExitStatusError {
+ pub fn code(self) -> Option<NonZeroI32> {
+ Some((u32::from(self.0) as i32).try_into().unwrap())
+ }
+}
+
+#[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
+ }
+}
+
+impl From<u8> for ExitCode {
+ fn from(code: u8) -> Self {
+ ExitCode(c::DWORD::from(code))
+ }
+}
+
+impl From<u32> for ExitCode {
+ fn from(code: u32) -> Self {
+ ExitCode(c::DWORD::from(code))
+ }
+}
+
+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(argv0: &OsStr, args: &[Arg], force_quotes: bool) -> 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 to avoid ambiguity when
+ // the child process parses its arguments.
+ // Note that quotes aren't escaped here because they can't be used in arg0.
+ // But that's ok because file paths can't contain quotes.
+ cmd.push(b'"' as u16);
+ cmd.extend(argv0.encode_wide());
+ cmd.push(b'"' as u16);
+
+ for arg in args {
+ cmd.push(' ' as u16);
+ args::append_arg(&mut cmd, arg, force_quotes)?;
+ }
+ Ok(cmd)
+}
+
+// Get `cmd.exe` for use with bat scripts, encoded as a UTF-16 string.
+fn command_prompt() -> io::Result<Vec<u16>> {
+ let mut system: Vec<u16> = super::fill_utf16_buf(
+ |buf, size| unsafe { c::GetSystemDirectoryW(buf, size) },
+ |buf| buf.into(),
+ )?;
+ system.extend("\\cmd.exe".encode_utf16().chain([0]));
+ Ok(system)
+}
+
+fn make_envp(maybe_env: Option<BTreeMap<EnvKey, 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();
+
+ // If there are no environment variables to set then signal this by
+ // pushing a null.
+ if env.is_empty() {
+ blk.push(0);
+ }
+
+ for (k, v) in env {
+ ensure_no_nuls(k.os_string)?;
+ blk.extend(k.utf16);
+ 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())),
+ }
+}
+
+pub struct CommandArgs<'a> {
+ iter: crate::slice::Iter<'a, Arg>,
+}
+
+impl<'a> Iterator for CommandArgs<'a> {
+ type Item = &'a OsStr;
+ fn next(&mut self) -> Option<&'a OsStr> {
+ self.iter.next().map(|arg| match arg {
+ Arg::Regular(s) | Arg::Raw(s) => s.as_ref(),
+ })
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+impl<'a> ExactSizeIterator for CommandArgs<'a> {
+ fn len(&self) -> usize {
+ self.iter.len()
+ }
+ fn is_empty(&self) -> bool {
+ self.iter.is_empty()
+ }
+}
+
+impl<'a> fmt::Debug for CommandArgs<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().entries(self.iter.clone()).finish()
+ }
+}
diff --git a/library/std/src/sys/windows/process/tests.rs b/library/std/src/sys/windows/process/tests.rs
new file mode 100644
index 000000000..3fc0c7524
--- /dev/null
+++ b/library/std/src/sys/windows/process/tests.rs
@@ -0,0 +1,222 @@
+use super::make_command_line;
+use super::Arg;
+use crate::env;
+use crate::ffi::{OsStr, OsString};
+use crate::process::Command;
+
+#[test]
+fn test_raw_args() {
+ let command_line = &make_command_line(
+ OsStr::new("quoted exe"),
+ &[
+ Arg::Regular(OsString::from("quote me")),
+ Arg::Raw(OsString::from("quote me *not*")),
+ Arg::Raw(OsString::from("\t\\")),
+ Arg::Raw(OsString::from("internal \\\"backslash-\"quote")),
+ Arg::Regular(OsString::from("optional-quotes")),
+ ],
+ false,
+ )
+ .unwrap();
+ assert_eq!(
+ String::from_utf16(command_line).unwrap(),
+ "\"quoted exe\" \"quote me\" quote me *not* \t\\ internal \\\"backslash-\"quote optional-quotes"
+ );
+}
+
+#[test]
+fn test_thread_handle() {
+ use crate::os::windows::io::BorrowedHandle;
+ use crate::os::windows::process::{ChildExt, CommandExt};
+ const CREATE_SUSPENDED: u32 = 0x00000004;
+
+ let p = Command::new("cmd").args(&["/C", "exit 0"]).creation_flags(CREATE_SUSPENDED).spawn();
+ assert!(p.is_ok());
+ let mut p = p.unwrap();
+
+ extern "system" {
+ fn ResumeThread(_: BorrowedHandle<'_>) -> u32;
+ }
+ unsafe {
+ ResumeThread(p.main_thread_handle());
+ }
+
+ crate::thread::sleep(crate::time::Duration::from_millis(100));
+
+ let res = p.try_wait();
+ assert!(res.is_ok());
+ assert!(res.unwrap().is_some());
+ assert!(p.try_wait().unwrap().unwrap().success());
+}
+
+#[test]
+fn test_make_command_line() {
+ fn test_wrapper(prog: &str, args: &[&str], force_quotes: bool) -> String {
+ let command_line = &make_command_line(
+ OsStr::new(prog),
+ &args.iter().map(|a| Arg::Regular(OsString::from(a))).collect::<Vec<_>>(),
+ force_quotes,
+ )
+ .unwrap();
+ String::from_utf16(command_line).unwrap()
+ }
+
+ assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"], false), "\"prog\" aaa bbb ccc");
+
+ assert_eq!(test_wrapper("prog", &[r"C:\"], false), r#""prog" C:\"#);
+ assert_eq!(test_wrapper("prog", &[r"2slashes\\"], false), r#""prog" 2slashes\\"#);
+ assert_eq!(test_wrapper("prog", &[r" C:\"], false), r#""prog" " C:\\""#);
+ assert_eq!(test_wrapper("prog", &[r" 2slashes\\"], false), r#""prog" " 2slashes\\\\""#);
+
+ assert_eq!(
+ test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"], false),
+ "\"C:\\Program Files\\blah\\blah.exe\" aaa"
+ );
+ assert_eq!(
+ test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa", "v*"], false),
+ "\"C:\\Program Files\\blah\\blah.exe\" aaa v*"
+ );
+ assert_eq!(
+ test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa", "v*"], true),
+ "\"C:\\Program Files\\blah\\blah.exe\" \"aaa\" \"v*\""
+ );
+ assert_eq!(
+ test_wrapper("C:\\Program Files\\test", &["aa\"bb"], false),
+ "\"C:\\Program Files\\test\" aa\\\"bb"
+ );
+ assert_eq!(test_wrapper("echo", &["a b c"], false), "\"echo\" \"a b c\"");
+ assert_eq!(
+ test_wrapper("echo", &["\" \\\" \\", "\\"], false),
+ "\"echo\" \"\\\" \\\\\\\" \\\\\" \\"
+ );
+ assert_eq!(
+ test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[], false),
+ "\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\""
+ );
+}
+
+// On Windows, environment args are case preserving but comparisons are case-insensitive.
+// See: #85242
+#[test]
+fn windows_env_unicode_case() {
+ let test_cases = [
+ ("ä", "Ä"),
+ ("ß", "SS"),
+ ("Ä", "Ö"),
+ ("Ä", "Ö"),
+ ("I", "İ"),
+ ("I", "i"),
+ ("I", "ı"),
+ ("i", "I"),
+ ("i", "İ"),
+ ("i", "ı"),
+ ("İ", "I"),
+ ("İ", "i"),
+ ("İ", "ı"),
+ ("ı", "I"),
+ ("ı", "i"),
+ ("ı", "İ"),
+ ("ä", "Ä"),
+ ("ß", "SS"),
+ ("Ä", "Ö"),
+ ("Ä", "Ö"),
+ ("I", "İ"),
+ ("I", "i"),
+ ("I", "ı"),
+ ("i", "I"),
+ ("i", "İ"),
+ ("i", "ı"),
+ ("İ", "I"),
+ ("İ", "i"),
+ ("İ", "ı"),
+ ("ı", "I"),
+ ("ı", "i"),
+ ("ı", "İ"),
+ ];
+ // Test that `cmd.env` matches `env::set_var` when setting two strings that
+ // may (or may not) be case-folded when compared.
+ for (a, b) in test_cases.iter() {
+ let mut cmd = Command::new("cmd");
+ cmd.env(a, "1");
+ cmd.env(b, "2");
+ env::set_var(a, "1");
+ env::set_var(b, "2");
+
+ for (key, value) in cmd.get_envs() {
+ assert_eq!(
+ env::var(key).ok(),
+ value.map(|s| s.to_string_lossy().into_owned()),
+ "command environment mismatch: {a} {b}",
+ );
+ }
+ }
+}
+
+// UWP applications run in a restricted environment which means this test may not work.
+#[cfg(not(target_vendor = "uwp"))]
+#[test]
+fn windows_exe_resolver() {
+ use super::resolve_exe;
+ use crate::io;
+ use crate::sys::fs::symlink;
+ use crate::sys_common::io::test::tmpdir;
+
+ let env_paths = || env::var_os("PATH");
+
+ // Test a full path, with and without the `exe` extension.
+ let mut current_exe = env::current_exe().unwrap();
+ assert!(resolve_exe(current_exe.as_ref(), env_paths, None).is_ok());
+ current_exe.set_extension("");
+ assert!(resolve_exe(current_exe.as_ref(), env_paths, None).is_ok());
+
+ // Test lone file names.
+ assert!(resolve_exe(OsStr::new("cmd"), env_paths, None).is_ok());
+ assert!(resolve_exe(OsStr::new("cmd.exe"), env_paths, None).is_ok());
+ assert!(resolve_exe(OsStr::new("cmd.EXE"), env_paths, None).is_ok());
+ assert!(resolve_exe(OsStr::new("fc"), env_paths, None).is_ok());
+
+ // Invalid file names should return InvalidInput.
+ assert_eq!(
+ resolve_exe(OsStr::new(""), env_paths, None).unwrap_err().kind(),
+ io::ErrorKind::InvalidInput
+ );
+ assert_eq!(
+ resolve_exe(OsStr::new("\0"), env_paths, None).unwrap_err().kind(),
+ io::ErrorKind::InvalidInput
+ );
+ // Trailing slash, therefore there's no file name component.
+ assert_eq!(
+ resolve_exe(OsStr::new(r"C:\Path\to\"), env_paths, None).unwrap_err().kind(),
+ io::ErrorKind::InvalidInput
+ );
+
+ /*
+ Some of the following tests may need to be changed if you are deliberately
+ changing the behaviour of `resolve_exe`.
+ */
+
+ let empty_paths = || None;
+
+ // The resolver looks in system directories even when `PATH` is empty.
+ assert!(resolve_exe(OsStr::new("cmd.exe"), empty_paths, None).is_ok());
+
+ // The application's directory is also searched.
+ let current_exe = env::current_exe().unwrap();
+ assert!(resolve_exe(current_exe.file_name().unwrap().as_ref(), empty_paths, None).is_ok());
+
+ // Create a temporary path and add a broken symlink.
+ let temp = tmpdir();
+ let mut exe_path = temp.path().to_owned();
+ exe_path.push("exists.exe");
+
+ // A broken symlink should still be resolved.
+ // Skip this check if not in CI and creating symlinks isn't possible.
+ let is_ci = env::var("CI").is_ok();
+ let result = symlink("<DOES NOT EXIST>".as_ref(), &exe_path);
+ if is_ci || result.is_ok() {
+ result.unwrap();
+ assert!(
+ resolve_exe(OsStr::new("exists.exe"), empty_paths, Some(temp.path().as_ref())).is_ok()
+ );
+ }
+}
diff --git a/library/std/src/sys/windows/rand.rs b/library/std/src/sys/windows/rand.rs
new file mode 100644
index 000000000..f8fd93a73
--- /dev/null
+++ b/library/std/src/sys/windows/rand.rs
@@ -0,0 +1,35 @@
+use crate::io;
+use crate::mem;
+use crate::ptr;
+use crate::sys::c;
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+ let mut v = (0, 0);
+ let ret = unsafe {
+ c::BCryptGenRandom(
+ ptr::null_mut(),
+ &mut v as *mut _ as *mut u8,
+ mem::size_of_val(&v) as c::ULONG,
+ c::BCRYPT_USE_SYSTEM_PREFERRED_RNG,
+ )
+ };
+ if ret != 0 { fallback_rng() } else { v }
+}
+
+/// Generate random numbers using the fallback RNG function (RtlGenRandom)
+#[cfg(not(target_vendor = "uwp"))]
+#[inline(never)]
+fn fallback_rng() -> (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 { v } else { panic!("fallback RNG broken: {}", io::Error::last_os_error()) }
+}
+
+/// We can't use RtlGenRandom with UWP, so there is no fallback
+#[cfg(target_vendor = "uwp")]
+#[inline(never)]
+fn fallback_rng() -> (u64, u64) {
+ panic!("fallback RNG broken: RtlGenRandom() not supported on UWP");
+}
diff --git a/library/std/src/sys/windows/stack_overflow.rs b/library/std/src/sys/windows/stack_overflow.rs
new file mode 100644
index 000000000..18a2a36ad
--- /dev/null
+++ b/library/std/src/sys/windows/stack_overflow.rs
@@ -0,0 +1,42 @@
+#![cfg_attr(test, allow(dead_code))]
+
+use crate::sys::c;
+use crate::thread;
+
+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
+ && 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 {
+ rtprintpanic!(
+ "\nthread '{}' has overflowed its stack\n",
+ thread::current().name().unwrap_or("<unknown>")
+ );
+ }
+ 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();
+}
diff --git a/library/std/src/sys/windows/stack_overflow_uwp.rs b/library/std/src/sys/windows/stack_overflow_uwp.rs
new file mode 100644
index 000000000..afdf7f566
--- /dev/null
+++ b/library/std/src/sys/windows/stack_overflow_uwp.rs
@@ -0,0 +1,11 @@
+#![cfg_attr(test, allow(dead_code))]
+
+pub struct Handler;
+
+impl Handler {
+ pub fn new() -> Handler {
+ Handler
+ }
+}
+
+pub unsafe fn init() {}
diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs
new file mode 100644
index 000000000..a001d6b98
--- /dev/null
+++ b/library/std/src/sys/windows/stdio.rs
@@ -0,0 +1,422 @@
+#![unstable(issue = "none", feature = "windows_stdio")]
+
+use crate::char::decode_utf16;
+use crate::cmp;
+use crate::io;
+use crate::os::windows::io::{FromRawHandle, IntoRawHandle};
+use crate::ptr;
+use crate::str;
+use crate::sys::c;
+use crate::sys::cvt;
+use crate::sys::handle::Handle;
+use core::str::utf8_char_width;
+
+// Don't cache handles but get them fresh for every read/write. This allows us to track changes to
+// the value over time (such as if a process calls `SetStdHandle` while it's running). See #40490.
+pub struct Stdin {
+ surrogate: u16,
+ incomplete_utf8: IncompleteUtf8,
+}
+
+pub struct Stdout {
+ incomplete_utf8: IncompleteUtf8,
+}
+
+pub struct Stderr {
+ incomplete_utf8: IncompleteUtf8,
+}
+
+struct IncompleteUtf8 {
+ bytes: [u8; 4],
+ len: u8,
+}
+
+impl IncompleteUtf8 {
+ // Implemented for use in Stdin::read.
+ fn read(&mut self, buf: &mut [u8]) -> usize {
+ // Write to buffer until the buffer is full or we run out of bytes.
+ let to_write = cmp::min(buf.len(), self.len as usize);
+ buf[..to_write].copy_from_slice(&self.bytes[..to_write]);
+
+ // Rotate the remaining bytes if not enough remaining space in buffer.
+ if usize::from(self.len) > buf.len() {
+ self.bytes.copy_within(to_write.., 0);
+ self.len -= to_write as u8;
+ } else {
+ self.len = 0;
+ }
+
+ to_write
+ }
+}
+
+// Apparently Windows doesn't handle large reads on stdin or writes to stdout/stderr well (see
+// #13304 for details).
+//
+// From MSDN (2011): "The storage for this buffer is allocated from a shared heap for the
+// process that is 64 KB in size. The maximum size of the buffer will depend on heap usage."
+//
+// We choose the cap at 8 KiB because libuv does the same, and it seems to be acceptable so far.
+const MAX_BUFFER_SIZE: usize = 8192;
+
+// The standard buffer size of BufReader for Stdin should be able to hold 3x more bytes than there
+// are `u16`'s in MAX_BUFFER_SIZE. This ensures the read data can always be completely decoded from
+// UTF-16 to UTF-8.
+pub const STDIN_BUF_SIZE: usize = MAX_BUFFER_SIZE / 2 * 3;
+
+pub fn get_handle(handle_id: c::DWORD) -> io::Result<c::HANDLE> {
+ let handle = unsafe { c::GetStdHandle(handle_id) };
+ 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 {
+ Ok(handle)
+ }
+}
+
+fn is_console(handle: c::HANDLE) -> bool {
+ // `GetConsoleMode` will return false (0) if this is a pipe (we don't care about the reported
+ // mode). This will only detect Windows Console, not other terminals connected to a pipe like
+ // MSYS. Which is exactly what we need, as only Windows Console needs a conversion to UTF-16.
+ let mut mode = 0;
+ unsafe { c::GetConsoleMode(handle, &mut mode) != 0 }
+}
+
+fn write(
+ handle_id: c::DWORD,
+ data: &[u8],
+ incomplete_utf8: &mut IncompleteUtf8,
+) -> io::Result<usize> {
+ if data.is_empty() {
+ return Ok(0);
+ }
+
+ let handle = get_handle(handle_id)?;
+ if !is_console(handle) {
+ unsafe {
+ let handle = Handle::from_raw_handle(handle);
+ let ret = handle.write(data);
+ handle.into_raw_handle(); // Don't close the handle
+ return ret;
+ }
+ }
+
+ if incomplete_utf8.len > 0 {
+ assert!(
+ incomplete_utf8.len < 4,
+ "Unexpected number of bytes for incomplete UTF-8 codepoint."
+ );
+ if data[0] >> 6 != 0b10 {
+ // not a continuation byte - reject
+ incomplete_utf8.len = 0;
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidData,
+ "Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
+ ));
+ }
+ incomplete_utf8.bytes[incomplete_utf8.len as usize] = data[0];
+ incomplete_utf8.len += 1;
+ let char_width = utf8_char_width(incomplete_utf8.bytes[0]);
+ if (incomplete_utf8.len as usize) < char_width {
+ // more bytes needed
+ return Ok(1);
+ }
+ let s = str::from_utf8(&incomplete_utf8.bytes[0..incomplete_utf8.len as usize]);
+ incomplete_utf8.len = 0;
+ match s {
+ Ok(s) => {
+ assert_eq!(char_width, s.len());
+ let written = write_valid_utf8_to_console(handle, s)?;
+ assert_eq!(written, s.len()); // guaranteed by write_valid_utf8_to_console() for single codepoint writes
+ return Ok(1);
+ }
+ Err(_) => {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidData,
+ "Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
+ ));
+ }
+ }
+ }
+
+ // As the console is meant for presenting text, we assume bytes of `data` are encoded as UTF-8,
+ // which needs to be encoded as UTF-16.
+ //
+ // If the data is not valid UTF-8 we write out as many bytes as are valid.
+ // If the first byte is invalid it is either first byte of a multi-byte sequence but the
+ // provided byte slice is too short or it is the first byte of an invalid multi-byte sequence.
+ let len = cmp::min(data.len(), MAX_BUFFER_SIZE / 2);
+ let utf8 = match str::from_utf8(&data[..len]) {
+ Ok(s) => s,
+ Err(ref e) if e.valid_up_to() == 0 => {
+ let first_byte_char_width = utf8_char_width(data[0]);
+ if first_byte_char_width > 1 && data.len() < first_byte_char_width {
+ incomplete_utf8.bytes[0] = data[0];
+ incomplete_utf8.len = 1;
+ return Ok(1);
+ } else {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidData,
+ "Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
+ ));
+ }
+ }
+ Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(),
+ };
+
+ write_valid_utf8_to_console(handle, utf8)
+}
+
+fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usize> {
+ let mut utf16 = [0u16; MAX_BUFFER_SIZE / 2];
+ let mut len_utf16 = 0;
+ for (chr, dest) in utf8.encode_utf16().zip(utf16.iter_mut()) {
+ *dest = chr;
+ len_utf16 += 1;
+ }
+ let utf16 = &utf16[..len_utf16];
+
+ let mut written = write_u16s(handle, &utf16)?;
+
+ // Figure out how many bytes of as UTF-8 were written away as UTF-16.
+ if written == utf16.len() {
+ Ok(utf8.len())
+ } else {
+ // Make sure we didn't end up writing only half of a surrogate pair (even though the chance
+ // is tiny). Because it is not possible for user code to re-slice `data` in such a way that
+ // a missing surrogate can be produced (and also because of the UTF-8 validation above),
+ // write the missing surrogate out now.
+ // Buffering it would mean we have to lie about the number of bytes written.
+ let first_char_remaining = utf16[written];
+ if first_char_remaining >= 0xDCEE && first_char_remaining <= 0xDFFF {
+ // low surrogate
+ // We just hope this works, and give up otherwise
+ let _ = write_u16s(handle, &utf16[written..written + 1]);
+ written += 1;
+ }
+ // Calculate the number of bytes of `utf8` that were actually written.
+ let mut count = 0;
+ for ch in utf16[..written].iter() {
+ count += match ch {
+ 0x0000..=0x007F => 1,
+ 0x0080..=0x07FF => 2,
+ 0xDCEE..=0xDFFF => 1, // Low surrogate. We already counted 3 bytes for the other.
+ _ => 3,
+ };
+ }
+ debug_assert!(String::from_utf16(&utf16[..written]).unwrap() == utf8[..count]);
+ Ok(count)
+ }
+}
+
+fn write_u16s(handle: c::HANDLE, data: &[u16]) -> io::Result<usize> {
+ let mut written = 0;
+ cvt(unsafe {
+ c::WriteConsoleW(
+ handle,
+ data.as_ptr() as c::LPCVOID,
+ data.len() as u32,
+ &mut written,
+ ptr::null_mut(),
+ )
+ })?;
+ Ok(written as usize)
+}
+
+impl Stdin {
+ pub const fn new() -> Stdin {
+ Stdin { surrogate: 0, incomplete_utf8: IncompleteUtf8::new() }
+ }
+}
+
+impl io::Read for Stdin {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let handle = get_handle(c::STD_INPUT_HANDLE)?;
+ if !is_console(handle) {
+ unsafe {
+ let handle = Handle::from_raw_handle(handle);
+ let ret = handle.read(buf);
+ handle.into_raw_handle(); // Don't close the handle
+ return ret;
+ }
+ }
+
+ // If there are bytes in the incomplete utf-8, start with those.
+ // (No-op if there is nothing in the buffer.)
+ let mut bytes_copied = self.incomplete_utf8.read(buf);
+
+ if bytes_copied == buf.len() {
+ return Ok(bytes_copied);
+ } else if buf.len() - bytes_copied < 4 {
+ // Not enough space to get a UTF-8 byte. We will use the incomplete UTF8.
+ let mut utf16_buf = [0u16; 1];
+ // Read one u16 character.
+ let read = read_u16s_fixup_surrogates(handle, &mut utf16_buf, 1, &mut self.surrogate)?;
+ // Read bytes, using the (now-empty) self.incomplete_utf8 as extra space.
+ let read_bytes = utf16_to_utf8(&utf16_buf[..read], &mut self.incomplete_utf8.bytes)?;
+
+ // Read in the bytes from incomplete_utf8 until the buffer is full.
+ self.incomplete_utf8.len = read_bytes as u8;
+ // No-op if no bytes.
+ bytes_copied += self.incomplete_utf8.read(&mut buf[bytes_copied..]);
+ Ok(bytes_copied)
+ } else {
+ let mut utf16_buf = [0u16; MAX_BUFFER_SIZE / 2];
+ // In the worst case, a UTF-8 string can take 3 bytes for every `u16` of a UTF-16. So
+ // we can read at most a third of `buf.len()` chars and uphold the guarantee no data gets
+ // lost.
+ let amount = cmp::min(buf.len() / 3, utf16_buf.len());
+ let read =
+ read_u16s_fixup_surrogates(handle, &mut utf16_buf, amount, &mut self.surrogate)?;
+
+ match utf16_to_utf8(&utf16_buf[..read], buf) {
+ Ok(value) => return Ok(bytes_copied + value),
+ Err(e) => return Err(e),
+ }
+ }
+ }
+}
+
+// We assume that if the last `u16` is an unpaired surrogate they got sliced apart by our
+// buffer size, and keep it around for the next read hoping to put them together.
+// This is a best effort, and might not work if we are not the only reader on Stdin.
+fn read_u16s_fixup_surrogates(
+ handle: c::HANDLE,
+ buf: &mut [u16],
+ mut amount: usize,
+ surrogate: &mut u16,
+) -> io::Result<usize> {
+ // Insert possibly remaining unpaired surrogate from last read.
+ let mut start = 0;
+ if *surrogate != 0 {
+ buf[0] = *surrogate;
+ *surrogate = 0;
+ start = 1;
+ if amount == 1 {
+ // Special case: `Stdin::read` guarantees we can always read at least one new `u16`
+ // and combine it with an unpaired surrogate, because the UTF-8 buffer is at least
+ // 4 bytes.
+ amount = 2;
+ }
+ }
+ let mut amount = read_u16s(handle, &mut buf[start..amount])? + start;
+
+ if amount > 0 {
+ let last_char = buf[amount - 1];
+ if last_char >= 0xD800 && last_char <= 0xDBFF {
+ // high surrogate
+ *surrogate = last_char;
+ amount -= 1;
+ }
+ }
+ Ok(amount)
+}
+
+fn read_u16s(handle: c::HANDLE, buf: &mut [u16]) -> io::Result<usize> {
+ // Configure the `pInputControl` parameter to not only return on `\r\n` but also Ctrl-Z, the
+ // traditional DOS method to indicate end of character stream / user input (SUB).
+ // See #38274 and https://stackoverflow.com/questions/43836040/win-api-readconsole.
+ const CTRL_Z: u16 = 0x1A;
+ const CTRL_Z_MASK: c::ULONG = 1 << CTRL_Z;
+ let mut input_control = c::CONSOLE_READCONSOLE_CONTROL {
+ nLength: crate::mem::size_of::<c::CONSOLE_READCONSOLE_CONTROL>() as c::ULONG,
+ nInitialChars: 0,
+ dwCtrlWakeupMask: CTRL_Z_MASK,
+ dwControlKeyState: 0,
+ };
+
+ let mut amount = 0;
+ loop {
+ cvt(unsafe {
+ c::SetLastError(0);
+ c::ReadConsoleW(
+ handle,
+ buf.as_mut_ptr() as c::LPVOID,
+ buf.len() as u32,
+ &mut amount,
+ &mut input_control as c::PCONSOLE_READCONSOLE_CONTROL,
+ )
+ })?;
+
+ // ReadConsoleW returns success with ERROR_OPERATION_ABORTED for Ctrl-C or Ctrl-Break.
+ // Explicitly check for that case here and try again.
+ if amount == 0 && unsafe { c::GetLastError() } == c::ERROR_OPERATION_ABORTED {
+ continue;
+ }
+ break;
+ }
+
+ if amount > 0 && buf[amount as usize - 1] == CTRL_Z {
+ amount -= 1;
+ }
+ Ok(amount as usize)
+}
+
+#[allow(unused)]
+fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result<usize> {
+ let mut written = 0;
+ for chr in decode_utf16(utf16.iter().cloned()) {
+ match chr {
+ Ok(chr) => {
+ chr.encode_utf8(&mut utf8[written..]);
+ written += chr.len_utf8();
+ }
+ Err(_) => {
+ // We can't really do any better than forget all data and return an error.
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidData,
+ "Windows stdin in console mode does not support non-UTF-16 input; \
+ encountered unpaired surrogate",
+ ));
+ }
+ }
+ }
+ Ok(written)
+}
+
+impl IncompleteUtf8 {
+ pub const fn new() -> IncompleteUtf8 {
+ IncompleteUtf8 { bytes: [0; 4], len: 0 }
+ }
+}
+
+impl Stdout {
+ pub const fn new() -> Stdout {
+ Stdout { incomplete_utf8: IncompleteUtf8::new() }
+ }
+}
+
+impl io::Write for Stdout {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ write(c::STD_OUTPUT_HANDLE, buf, &mut self.incomplete_utf8)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Stderr {
+ pub const fn new() -> Stderr {
+ Stderr { incomplete_utf8: IncompleteUtf8::new() }
+ }
+}
+
+impl io::Write for Stderr {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ write(c::STD_ERROR_HANDLE, buf, &mut self.incomplete_utf8)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+pub fn is_ebadf(err: &io::Error) -> bool {
+ err.raw_os_error() == Some(c::ERROR_INVALID_HANDLE as i32)
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+ Some(Stderr::new())
+}
diff --git a/library/std/src/sys/windows/stdio_uwp.rs b/library/std/src/sys/windows/stdio_uwp.rs
new file mode 100644
index 000000000..32550f796
--- /dev/null
+++ b/library/std/src/sys/windows/stdio_uwp.rs
@@ -0,0 +1,87 @@
+#![unstable(issue = "none", feature = "windows_stdio")]
+
+use crate::io;
+use crate::mem::ManuallyDrop;
+use crate::os::windows::io::FromRawHandle;
+use crate::sys::c;
+use crate::sys::handle::Handle;
+
+pub struct Stdin {}
+pub struct Stdout;
+pub struct Stderr;
+
+const MAX_BUFFER_SIZE: usize = 8192;
+pub const STDIN_BUF_SIZE: usize = MAX_BUFFER_SIZE / 2 * 3;
+
+pub fn get_handle(handle_id: c::DWORD) -> io::Result<c::HANDLE> {
+ let handle = unsafe { c::GetStdHandle(handle_id) };
+ 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 {
+ Ok(handle)
+ }
+}
+
+fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result<usize> {
+ let handle = get_handle(handle_id)?;
+ // SAFETY: The handle returned from `get_handle` must be valid and non-null.
+ let handle = unsafe { Handle::from_raw_handle(handle) };
+ ManuallyDrop::new(handle).write(data)
+}
+
+impl Stdin {
+ pub const fn new() -> Stdin {
+ Stdin {}
+ }
+}
+
+impl io::Read for Stdin {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let handle = get_handle(c::STD_INPUT_HANDLE)?;
+ // SAFETY: The handle returned from `get_handle` must be valid and non-null.
+ let handle = unsafe { Handle::from_raw_handle(handle) };
+ ManuallyDrop::new(handle).read(buf)
+ }
+}
+
+impl Stdout {
+ pub const fn new() -> Stdout {
+ Stdout
+ }
+}
+
+impl io::Write for Stdout {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ write(c::STD_OUTPUT_HANDLE, buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Stderr {
+ pub const fn new() -> Stderr {
+ Stderr
+ }
+}
+
+impl io::Write for Stderr {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ write(c::STD_ERROR_HANDLE, buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+pub fn is_ebadf(err: &io::Error) -> bool {
+ err.raw_os_error() == Some(c::ERROR_INVALID_HANDLE as i32)
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+ Some(Stderr::new())
+}
diff --git a/library/std/src/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs
new file mode 100644
index 000000000..c5c9e97e6
--- /dev/null
+++ b/library/std/src/sys/windows/thread.rs
@@ -0,0 +1,125 @@
+use crate::ffi::CStr;
+use crate::io;
+use crate::num::NonZeroUsize;
+use crate::os::windows::io::AsRawHandle;
+use crate::ptr;
+use crate::sys::c;
+use crate::sys::handle::Handle;
+use crate::sys::stack_overflow;
+use crate::sys_common::FromInner;
+use crate::time::Duration;
+
+use libc::c_void;
+
+use super::to_u16s;
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
+
+pub struct Thread {
+ handle: Handle,
+}
+
+impl Thread {
+ // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+ pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+ let p = Box::into_raw(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.
+ let ret = c::CreateThread(
+ ptr::null_mut(),
+ stack,
+ thread_start,
+ p as *mut _,
+ c::STACK_SIZE_PARAM_IS_A_RESERVATION,
+ ptr::null_mut(),
+ );
+
+ return if let Ok(handle) = ret.try_into() {
+ Ok(Thread { handle: Handle::from_inner(handle) })
+ } else {
+ // The thread failed to start and as a result p was not consumed. Therefore, it is
+ // safe to reconstruct the box so that it gets deallocated.
+ drop(Box::from_raw(p));
+ Err(io::Error::last_os_error())
+ };
+
+ extern "system" fn thread_start(main: *mut c_void) -> c::DWORD {
+ unsafe {
+ // Next, set up our stack overflow handler which may get triggered if we run
+ // out of stack.
+ let _handler = stack_overflow::Handler::new();
+ // Finally, let's run some code.
+ Box::from_raw(main as *mut Box<dyn FnOnce()>)();
+ }
+ 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.as_raw_handle(), 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
+ }
+}
+
+pub fn available_parallelism() -> io::Result<NonZeroUsize> {
+ let res = unsafe {
+ let mut sysinfo: c::SYSTEM_INFO = crate::mem::zeroed();
+ c::GetSystemInfo(&mut sysinfo);
+ sysinfo.dwNumberOfProcessors as usize
+ };
+ match res {
+ 0 => Err(io::const_io_error!(
+ io::ErrorKind::NotFound,
+ "The number of hardware threads is not known for the target platform",
+ )),
+ cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus) }),
+ }
+}
+
+#[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
+ }
+}
diff --git a/library/std/src/sys/windows/thread_local_dtor.rs b/library/std/src/sys/windows/thread_local_dtor.rs
new file mode 100644
index 000000000..25d1c6e8e
--- /dev/null
+++ b/library/std/src/sys/windows/thread_local_dtor.rs
@@ -0,0 +1,28 @@
+//! Implements thread-local destructors that are not associated with any
+//! particular data.
+
+#![unstable(feature = "thread_local_internals", issue = "none")]
+#![cfg(target_thread_local)]
+
+// Using a per-thread list avoids the problems in synchronizing global state.
+#[thread_local]
+static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
+
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+ DESTRUCTORS.push((t, dtor));
+}
+
+/// Runs destructors. This should not be called until thread exit.
+pub unsafe fn run_keyless_dtors() {
+ // Drop all the destructors.
+ //
+ // Note: While this is potentially an infinite loop, 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.
+ while let Some((ptr, dtor)) = DESTRUCTORS.pop() {
+ (dtor)(ptr);
+ }
+ // We're done so free the memory.
+ DESTRUCTORS = Vec::new();
+}
diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs
new file mode 100644
index 000000000..ec670238e
--- /dev/null
+++ b/library/std/src/sys/windows/thread_local_key.rs
@@ -0,0 +1,238 @@
+use crate::mem::ManuallyDrop;
+use crate::ptr;
+use crate::sync::atomic::AtomicPtr;
+use crate::sync::atomic::Ordering::SeqCst;
+use crate::sys::c;
+
+pub type Key = c::DWORD;
+pub type Dtor = unsafe extern "C" 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]: https://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);
+ }
+ 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 = ManuallyDrop::new(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, // nothing to drop, we successfully added the node to the list
+ 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();
+ #[cfg(target_thread_local)]
+ super::thread_local_dtor::run_keyless_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 "C" {
+ static _tls_used: u8;
+ }
+ crate::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/library/std/src/sys/windows/thread_parker.rs b/library/std/src/sys/windows/thread_parker.rs
new file mode 100644
index 000000000..d876e0f6f
--- /dev/null
+++ b/library/std/src/sys/windows/thread_parker.rs
@@ -0,0 +1,255 @@
+// Thread parker implementation for Windows.
+//
+// This uses WaitOnAddress and WakeByAddressSingle if available (Windows 8+).
+// This modern API is exactly the same as the futex syscalls the Linux thread
+// parker uses. When These APIs are available, the implementation of this
+// thread parker matches the Linux thread parker exactly.
+//
+// However, when the modern API is not available, this implementation falls
+// back to NT Keyed Events, which are similar, but have some important
+// differences. These are available since Windows XP.
+//
+// WaitOnAddress first checks the state of the thread parker to make sure it no
+// WakeByAddressSingle calls can be missed between updating the parker state
+// and calling the function.
+//
+// NtWaitForKeyedEvent does not have this option, and unconditionally blocks
+// without checking the parker state first. Instead, NtReleaseKeyedEvent
+// (unlike WakeByAddressSingle) *blocks* until it woke up a thread waiting for
+// it by NtWaitForKeyedEvent. This way, we can be sure no events are missed,
+// but we need to be careful not to block unpark() if park_timeout() was woken
+// up by a timeout instead of unpark().
+//
+// Unlike WaitOnAddress, NtWaitForKeyedEvent/NtReleaseKeyedEvent operate on a
+// HANDLE (created with NtCreateKeyedEvent). This means that we can be sure
+// a successfully awoken park() was awoken by unpark() and not a
+// NtReleaseKeyedEvent call from some other code, as these events are not only
+// matched by the key (address of the parker (state)), but also by this HANDLE.
+// We lazily allocate this handle the first time it is needed.
+//
+// The fast path (calling park() after unpark() was already called) and the
+// possible states are the same for both implementations. This is used here to
+// make sure the fast path does not even check which API to use, but can return
+// right away, independent of the used API. Only the slow paths (which will
+// actually block/wake a thread) check which API is available and have
+// different implementations.
+//
+// Unfortunately, NT Keyed Events are an undocumented Windows API. However:
+// - This API is relatively simple with obvious behaviour, and there are
+// several (unofficial) articles documenting the details. [1]
+// - `parking_lot` has been using this API for years (on Windows versions
+// before Windows 8). [2] Many big projects extensively use parking_lot,
+// such as servo and the Rust compiler itself.
+// - It is the underlying API used by Windows SRW locks and Windows critical
+// sections. [3] [4]
+// - The source code of the implementations of Wine, ReactOs, and Windows XP
+// are available and match the expected behaviour.
+// - The main risk with an undocumented API is that it might change in the
+// future. But since we only use it for older versions of Windows, that's not
+// a problem.
+// - Even if these functions do not block or wake as we expect (which is
+// unlikely, see all previous points), this implementation would still be
+// memory safe. The NT Keyed Events API is only used to sleep/block in the
+// right place.
+//
+// [1]: http://www.locklessinc.com/articles/keyed_events/
+// [2]: https://github.com/Amanieu/parking_lot/commit/43abbc964e
+// [3]: https://docs.microsoft.com/en-us/archive/msdn-magazine/2012/november/windows-with-c-the-evolution-of-synchronization-in-windows-and-c
+// [4]: Windows Internals, Part 1, ISBN 9780735671300
+
+use crate::pin::Pin;
+use crate::ptr;
+use crate::sync::atomic::{
+ AtomicI8, AtomicPtr,
+ Ordering::{Acquire, Relaxed, Release},
+};
+use crate::sys::{c, dur2timeout};
+use crate::time::Duration;
+
+pub struct Parker {
+ state: AtomicI8,
+}
+
+const PARKED: i8 = -1;
+const EMPTY: i8 = 0;
+const NOTIFIED: i8 = 1;
+
+// Notes about memory ordering:
+//
+// Memory ordering is only relevant for the relative ordering of operations
+// between different variables. Even Ordering::Relaxed guarantees a
+// monotonic/consistent order when looking at just a single atomic variable.
+//
+// So, since this parker is just a single atomic variable, we only need to look
+// at the ordering guarantees we need to provide to the 'outside world'.
+//
+// The only memory ordering guarantee that parking and unparking provide, is
+// that things which happened before unpark() are visible on the thread
+// returning from park() afterwards. Otherwise, it was effectively unparked
+// before unpark() was called while still consuming the 'token'.
+//
+// In other words, unpark() needs to synchronize with the part of park() that
+// consumes the token and returns.
+//
+// This is done with a release-acquire synchronization, by using
+// Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using
+// Ordering::Acquire when reading this state in park() after waking up.
+impl Parker {
+ /// Construct the Windows parker. The UNIX parker implementation
+ /// requires this to happen in-place.
+ pub unsafe fn new(parker: *mut Parker) {
+ parker.write(Self { state: AtomicI8::new(EMPTY) });
+ }
+
+ // Assumes this is only called by the thread that owns the Parker,
+ // which means that `self.state != PARKED`. This implementation doesn't require `Pin`,
+ // but other implementations do.
+ pub unsafe fn park(self: Pin<&Self>) {
+ // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
+ // first case.
+ if self.state.fetch_sub(1, Acquire) == NOTIFIED {
+ return;
+ }
+
+ if let Some(wait_on_address) = c::WaitOnAddress::option() {
+ loop {
+ // Wait for something to happen, assuming it's still set to PARKED.
+ wait_on_address(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE);
+ // Change NOTIFIED=>EMPTY but leave PARKED alone.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire).is_ok() {
+ // Actually woken up by unpark().
+ return;
+ } else {
+ // Spurious wake up. We loop to try again.
+ }
+ }
+ } else {
+ // Wait for unpark() to produce this event.
+ c::NtWaitForKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
+ // Set the state back to EMPTY (from either PARKED or NOTIFIED).
+ // Note that we don't just write EMPTY, but use swap() to also
+ // include an acquire-ordered read to synchronize with unpark()'s
+ // release-ordered write.
+ self.state.swap(EMPTY, Acquire);
+ }
+ }
+
+ // Assumes this is only called by the thread that owns the Parker,
+ // which means that `self.state != PARKED`. This implementation doesn't require `Pin`,
+ // but other implementations do.
+ pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) {
+ // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
+ // first case.
+ if self.state.fetch_sub(1, Acquire) == NOTIFIED {
+ return;
+ }
+
+ if let Some(wait_on_address) = c::WaitOnAddress::option() {
+ // Wait for something to happen, assuming it's still set to PARKED.
+ wait_on_address(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout));
+ // Set the state back to EMPTY (from either PARKED or NOTIFIED).
+ // Note that we don't just write EMPTY, but use swap() to also
+ // include an acquire-ordered read to synchronize with unpark()'s
+ // release-ordered write.
+ if self.state.swap(EMPTY, Acquire) == NOTIFIED {
+ // Actually woken up by unpark().
+ } else {
+ // Timeout or spurious wake up.
+ // We return either way, because we can't easily tell if it was the
+ // timeout or not.
+ }
+ } else {
+ // Need to wait for unpark() using NtWaitForKeyedEvent.
+ let handle = keyed_event_handle();
+
+ // NtWaitForKeyedEvent uses a unit of 100ns, and uses negative
+ // values to indicate a relative time on the monotonic clock.
+ // This is documented here for the underlying KeWaitForSingleObject function:
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-kewaitforsingleobject
+ let mut timeout = match i64::try_from((timeout.as_nanos() + 99) / 100) {
+ Ok(t) => -t,
+ Err(_) => i64::MIN,
+ };
+
+ // Wait for unpark() to produce this event.
+ let unparked =
+ c::NtWaitForKeyedEvent(handle, self.ptr(), 0, &mut timeout) == c::STATUS_SUCCESS;
+
+ // Set the state back to EMPTY (from either PARKED or NOTIFIED).
+ let prev_state = self.state.swap(EMPTY, Acquire);
+
+ if !unparked && prev_state == NOTIFIED {
+ // We were awoken by a timeout, not by unpark(), but the state
+ // was set to NOTIFIED, which means we *just* missed an
+ // unpark(), which is now blocked on us to wait for it.
+ // Wait for it to consume the event and unblock that thread.
+ c::NtWaitForKeyedEvent(handle, self.ptr(), 0, ptr::null_mut());
+ }
+ }
+ }
+
+ // This implementation doesn't require `Pin`, but other implementations do.
+ pub fn unpark(self: Pin<&Self>) {
+ // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and
+ // wake the thread in the first case.
+ //
+ // Note that even NOTIFIED=>NOTIFIED results in a write. This is on
+ // purpose, to make sure every unpark() has a release-acquire ordering
+ // with park().
+ if self.state.swap(NOTIFIED, Release) == PARKED {
+ if let Some(wake_by_address_single) = c::WakeByAddressSingle::option() {
+ unsafe {
+ wake_by_address_single(self.ptr());
+ }
+ } else {
+ // If we run NtReleaseKeyedEvent before the waiting thread runs
+ // NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up.
+ // If the waiting thread wakes up before we run NtReleaseKeyedEvent
+ // (e.g. due to a timeout), this blocks until we do wake up a thread.
+ // To prevent this thread from blocking indefinitely in that case,
+ // park_impl() will, after seeing the state set to NOTIFIED after
+ // waking up, call NtWaitForKeyedEvent again to unblock us.
+ unsafe {
+ c::NtReleaseKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
+ }
+ }
+ }
+ }
+
+ fn ptr(&self) -> c::LPVOID {
+ &self.state as *const _ as c::LPVOID
+ }
+}
+
+fn keyed_event_handle() -> c::HANDLE {
+ const INVALID: c::HANDLE = ptr::invalid_mut(!0);
+ static HANDLE: AtomicPtr<libc::c_void> = AtomicPtr::new(INVALID);
+ match HANDLE.load(Relaxed) {
+ INVALID => {
+ let mut handle = c::INVALID_HANDLE_VALUE;
+ unsafe {
+ match c::NtCreateKeyedEvent(
+ &mut handle,
+ c::GENERIC_READ | c::GENERIC_WRITE,
+ ptr::null_mut(),
+ 0,
+ ) {
+ c::STATUS_SUCCESS => {}
+ r => panic!("Unable to create keyed event handle: error {r}"),
+ }
+ }
+ match HANDLE.compare_exchange(INVALID, handle, Relaxed, Relaxed) {
+ Ok(_) => handle,
+ Err(h) => {
+ // Lost the race to another thread initializing HANDLE before we did.
+ // Closing our handle and using theirs instead.
+ unsafe {
+ c::CloseHandle(handle);
+ }
+ h
+ }
+ }
+ }
+ handle => handle,
+ }
+}
diff --git a/library/std/src/sys/windows/time.rs b/library/std/src/sys/windows/time.rs
new file mode 100644
index 000000000..b8209a854
--- /dev/null
+++ b/library/std/src/sys/windows/time.rs
@@ -0,0 +1,224 @@
+use crate::cmp::Ordering;
+use crate::fmt;
+use crate::mem;
+use crate::sys::c;
+use crate::sys_common::IntoInner;
+use crate::time::Duration;
+
+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 {
+ // This duration is relative to an arbitrary microsecond epoch
+ // from the winapi QueryPerformanceCounter function.
+ t: Duration,
+}
+
+#[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 {
+ // High precision timing on windows operates in "Performance Counter"
+ // units, as returned by the WINAPI QueryPerformanceCounter function.
+ // These relate to seconds by a factor of QueryPerformanceFrequency.
+ // In order to keep unit conversions out of normal interval math, we
+ // measure in QPC units and immediately convert to nanoseconds.
+ perf_counter::PerformanceCounterInstant::now().into()
+ }
+
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ // On windows there's a threshold below which we consider two timestamps
+ // equivalent due to measurement error. For more details + doc link,
+ // check the docs on epsilon.
+ let epsilon = perf_counter::PerformanceCounterInstant::epsilon();
+ if other.t > self.t && other.t - self.t <= epsilon {
+ Some(Duration::new(0, 0))
+ } else {
+ self.t.checked_sub(other.t)
+ }
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_add(*other)? })
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_sub(*other)? })
+ }
+}
+
+impl SystemTime {
+ pub fn now() -> SystemTime {
+ unsafe {
+ let mut t: SystemTime = mem::zeroed();
+ c::GetSystemTimePreciseAsFileTime(&mut t.t);
+ 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 checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+ let intervals = self.intervals().checked_add(checked_dur2intervals(other)?)?;
+ Some(SystemTime::from_intervals(intervals))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+ let intervals = self.intervals().checked_sub(checked_dur2intervals(other)?)?;
+ Some(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 }
+ }
+}
+
+impl IntoInner<c::FILETIME> for SystemTime {
+ fn into_inner(self) -> c::FILETIME {
+ self.t
+ }
+}
+
+impl Hash for SystemTime {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.intervals().hash(state)
+ }
+}
+
+fn checked_dur2intervals(dur: &Duration) -> Option<i64> {
+ dur.as_secs()
+ .checked_mul(INTERVALS_PER_SEC)?
+ .checked_add(dur.subsec_nanos() as u64 / 100)?
+ .try_into()
+ .ok()
+}
+
+fn intervals2dur(intervals: u64) -> Duration {
+ Duration::new(intervals / INTERVALS_PER_SEC, ((intervals % INTERVALS_PER_SEC) * 100) as u32)
+}
+
+mod perf_counter {
+ use super::NANOS_PER_SEC;
+ use crate::sync::atomic::{AtomicU64, Ordering};
+ use crate::sys::c;
+ use crate::sys::cvt;
+ use crate::sys_common::mul_div_u64;
+ use crate::time::Duration;
+
+ pub struct PerformanceCounterInstant {
+ ts: c::LARGE_INTEGER,
+ }
+ impl PerformanceCounterInstant {
+ pub fn now() -> Self {
+ Self { ts: query() }
+ }
+
+ // Per microsoft docs, the margin of error for cross-thread time comparisons
+ // using QueryPerformanceCounter is 1 "tick" -- defined as 1/frequency().
+ // Reference: https://docs.microsoft.com/en-us/windows/desktop/SysInfo
+ // /acquiring-high-resolution-time-stamps
+ pub fn epsilon() -> Duration {
+ let epsilon = NANOS_PER_SEC / (frequency() as u64);
+ Duration::from_nanos(epsilon)
+ }
+ }
+ impl From<PerformanceCounterInstant> for super::Instant {
+ fn from(other: PerformanceCounterInstant) -> Self {
+ let freq = frequency() as u64;
+ let instant_nsec = mul_div_u64(other.ts as u64, NANOS_PER_SEC, freq);
+ Self { t: Duration::from_nanos(instant_nsec) }
+ }
+ }
+
+ fn frequency() -> c::LARGE_INTEGER {
+ // Either the cached result of `QueryPerformanceFrequency` or `0` for
+ // uninitialized. Storing this as a single `AtomicU64` allows us to use
+ // `Relaxed` operations, as we are only interested in the effects on a
+ // single memory location.
+ static FREQUENCY: AtomicU64 = AtomicU64::new(0);
+
+ let cached = FREQUENCY.load(Ordering::Relaxed);
+ // If a previous thread has filled in this global state, use that.
+ if cached != 0 {
+ return cached as c::LARGE_INTEGER;
+ }
+ // ... otherwise learn for ourselves ...
+ let mut frequency = 0;
+ unsafe {
+ cvt(c::QueryPerformanceFrequency(&mut frequency)).unwrap();
+ }
+
+ FREQUENCY.store(frequency as u64, Ordering::Relaxed);
+ frequency
+ }
+
+ fn query() -> c::LARGE_INTEGER {
+ let mut qpc_value: c::LARGE_INTEGER = 0;
+ cvt(unsafe { c::QueryPerformanceCounter(&mut qpc_value) }).unwrap();
+ qpc_value
+ }
+}
diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs
new file mode 100644
index 000000000..31164afdc
--- /dev/null
+++ b/library/std/src/sys_common/backtrace.rs
@@ -0,0 +1,183 @@
+use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt};
+use crate::borrow::Cow;
+/// Common code for printing the backtrace in the same way across the different
+/// supported platforms.
+use crate::env;
+use crate::fmt;
+use crate::io;
+use crate::io::prelude::*;
+use crate::path::{self, Path, PathBuf};
+use crate::sys_common::mutex::StaticMutex;
+
+/// Max number of frames to print.
+const MAX_NB_FRAMES: usize = 100;
+
+// SAFETY: Don't attempt to lock this reentrantly.
+pub unsafe fn lock() -> impl Drop {
+ static LOCK: StaticMutex = StaticMutex::new();
+ LOCK.lock()
+}
+
+/// Prints the current backtrace.
+pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
+ // There are issues currently linking libbacktrace into tests, and in
+ // general during libstd's own unit tests we're not testing this path. In
+ // test mode immediately return here to optimize away any references to the
+ // libbacktrace symbols
+ if cfg!(test) {
+ return Ok(());
+ }
+
+ // Use a lock to prevent mixed output in multithreading context.
+ // Some platforms also requires it, like `SymFromAddr` on Windows.
+ unsafe {
+ let _lock = lock();
+ _print(w, format)
+ }
+}
+
+unsafe fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
+ struct DisplayBacktrace {
+ format: PrintFmt,
+ }
+ impl fmt::Display for DisplayBacktrace {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ unsafe { _print_fmt(fmt, self.format) }
+ }
+ }
+ write!(w, "{}", DisplayBacktrace { format })
+}
+
+unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
+ // Always 'fail' to get the cwd when running under Miri -
+ // this allows Miri to display backtraces in isolation mode
+ let cwd = if !cfg!(miri) { env::current_dir().ok() } else { None };
+
+ let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
+ output_filename(fmt, bows, print_fmt, cwd.as_ref())
+ };
+ writeln!(fmt, "stack backtrace:")?;
+ let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path);
+ bt_fmt.add_context()?;
+ let mut idx = 0;
+ let mut res = Ok(());
+ // Start immediately if we're not using a short backtrace.
+ let mut start = print_fmt != PrintFmt::Short;
+ backtrace_rs::trace_unsynchronized(|frame| {
+ if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
+ return false;
+ }
+
+ let mut hit = false;
+ let mut stop = false;
+ backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
+ hit = true;
+ if print_fmt == PrintFmt::Short {
+ if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
+ if start && sym.contains("__rust_begin_short_backtrace") {
+ stop = true;
+ return;
+ }
+ if sym.contains("__rust_end_short_backtrace") {
+ start = true;
+ return;
+ }
+ }
+ }
+
+ if start {
+ res = bt_fmt.frame().symbol(frame, symbol);
+ }
+ });
+ if stop {
+ return false;
+ }
+ if !hit && start {
+ res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
+ }
+
+ idx += 1;
+ res.is_ok()
+ });
+ res?;
+ bt_fmt.finish()?;
+ if print_fmt == PrintFmt::Short {
+ writeln!(
+ fmt,
+ "note: Some details are omitted, \
+ run with `RUST_BACKTRACE=full` for a verbose backtrace."
+ )?;
+ }
+ Ok(())
+}
+
+/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
+/// this is only inline(never) when backtraces in libstd are enabled, otherwise
+/// it's fine to optimize away.
+#[cfg_attr(feature = "backtrace", inline(never))]
+pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
+where
+ F: FnOnce() -> T,
+{
+ let result = f();
+
+ // prevent this frame from being tail-call optimised away
+ crate::hint::black_box(());
+
+ result
+}
+
+/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
+/// this is only inline(never) when backtraces in libstd are enabled, otherwise
+/// it's fine to optimize away.
+#[cfg_attr(feature = "backtrace", inline(never))]
+pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
+where
+ F: FnOnce() -> T,
+{
+ let result = f();
+
+ // prevent this frame from being tail-call optimised away
+ crate::hint::black_box(());
+
+ result
+}
+
+/// Prints the filename of the backtrace frame.
+///
+/// See also `output`.
+pub fn output_filename(
+ fmt: &mut fmt::Formatter<'_>,
+ bows: BytesOrWideString<'_>,
+ print_fmt: PrintFmt,
+ cwd: Option<&PathBuf>,
+) -> fmt::Result {
+ let file: Cow<'_, Path> = match bows {
+ #[cfg(unix)]
+ BytesOrWideString::Bytes(bytes) => {
+ use crate::os::unix::prelude::*;
+ Path::new(crate::ffi::OsStr::from_bytes(bytes)).into()
+ }
+ #[cfg(not(unix))]
+ BytesOrWideString::Bytes(bytes) => {
+ Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")).into()
+ }
+ #[cfg(windows)]
+ BytesOrWideString::Wide(wide) => {
+ use crate::os::windows::prelude::*;
+ Cow::Owned(crate::ffi::OsString::from_wide(wide).into())
+ }
+ #[cfg(not(windows))]
+ BytesOrWideString::Wide(_wide) => Path::new("<unknown>").into(),
+ };
+ if print_fmt == PrintFmt::Short && file.is_absolute() {
+ if let Some(cwd) = cwd {
+ if let Ok(stripped) = file.strip_prefix(&cwd) {
+ if let Some(s) = stripped.to_str() {
+ return write!(fmt, ".{}{s}", path::MAIN_SEPARATOR);
+ }
+ }
+ }
+ }
+ fmt::Display::fmt(&file.display(), fmt)
+}
diff --git a/library/std/src/sys_common/condvar.rs b/library/std/src/sys_common/condvar.rs
new file mode 100644
index 000000000..f3ac1061b
--- /dev/null
+++ b/library/std/src/sys_common/condvar.rs
@@ -0,0 +1,56 @@
+use crate::sys::locks as imp;
+use crate::sys_common::mutex::MovableMutex;
+use crate::time::Duration;
+
+mod check;
+
+type CondvarCheck = <imp::MovableMutex as check::CondvarCheck>::Check;
+
+/// An OS-based condition variable.
+pub struct Condvar {
+ inner: imp::MovableCondvar,
+ check: CondvarCheck,
+}
+
+impl Condvar {
+ /// Creates a new condition variable for use.
+ #[inline]
+ pub const fn new() -> Self {
+ Self { inner: imp::MovableCondvar::new(), check: CondvarCheck::new() }
+ }
+
+ /// Signals one waiter on this condition variable to wake up.
+ #[inline]
+ pub fn notify_one(&self) {
+ unsafe { self.inner.notify_one() };
+ }
+
+ /// Awakens all current waiters on this condition variable.
+ #[inline]
+ pub fn notify_all(&self) {
+ unsafe { self.inner.notify_all() };
+ }
+
+ /// Waits for a signal on the specified mutex.
+ ///
+ /// Behavior is undefined if the mutex is not locked by the current thread.
+ ///
+ /// May panic if used with more than one mutex.
+ #[inline]
+ pub unsafe fn wait(&self, mutex: &MovableMutex) {
+ self.check.verify(mutex);
+ self.inner.wait(mutex.raw())
+ }
+
+ /// Waits for a signal on the specified mutex with a timeout duration
+ /// specified by `dur` (a relative time into the future).
+ ///
+ /// Behavior is undefined if the mutex is not locked by the current thread.
+ ///
+ /// May panic if used with more than one mutex.
+ #[inline]
+ pub unsafe fn wait_timeout(&self, mutex: &MovableMutex, dur: Duration) -> bool {
+ self.check.verify(mutex);
+ self.inner.wait_timeout(mutex.raw(), dur)
+ }
+}
diff --git a/library/std/src/sys_common/condvar/check.rs b/library/std/src/sys_common/condvar/check.rs
new file mode 100644
index 000000000..ce8f36704
--- /dev/null
+++ b/library/std/src/sys_common/condvar/check.rs
@@ -0,0 +1,57 @@
+use crate::ptr;
+use crate::sync::atomic::{AtomicPtr, Ordering};
+use crate::sys::locks as imp;
+use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+use crate::sys_common::mutex::MovableMutex;
+
+pub trait CondvarCheck {
+ type Check;
+}
+
+/// For boxed mutexes, a `Condvar` will check it's only ever used with the same
+/// mutex, based on its (stable) address.
+impl<T: LazyInit> CondvarCheck for LazyBox<T> {
+ type Check = SameMutexCheck;
+}
+
+pub struct SameMutexCheck {
+ addr: AtomicPtr<()>,
+}
+
+#[allow(dead_code)]
+impl SameMutexCheck {
+ pub const fn new() -> Self {
+ Self { addr: AtomicPtr::new(ptr::null_mut()) }
+ }
+ pub fn verify(&self, mutex: &MovableMutex) {
+ let addr = mutex.raw() as *const imp::Mutex as *const () as *mut _;
+ // Relaxed is okay here because we never read through `self.addr`, and only use it to
+ // compare addresses.
+ match self.addr.compare_exchange(
+ ptr::null_mut(),
+ addr,
+ Ordering::Relaxed,
+ Ordering::Relaxed,
+ ) {
+ Ok(_) => {} // Stored the address
+ Err(n) if n == addr => {} // Lost a race to store the same address
+ _ => panic!("attempted to use a condition variable with two mutexes"),
+ }
+ }
+}
+
+/// Unboxed mutexes may move, so `Condvar` can not require its address to stay
+/// constant.
+impl CondvarCheck for imp::Mutex {
+ type Check = NoCheck;
+}
+
+pub struct NoCheck;
+
+#[allow(dead_code)]
+impl NoCheck {
+ pub const fn new() -> Self {
+ Self
+ }
+ pub fn verify(&self, _: &MovableMutex) {}
+}
diff --git a/library/std/src/sys_common/fs.rs b/library/std/src/sys_common/fs.rs
new file mode 100644
index 000000000..617ac52e5
--- /dev/null
+++ b/library/std/src/sys_common/fs.rs
@@ -0,0 +1,51 @@
+#![allow(dead_code)] // not used on all platforms
+
+use crate::fs;
+use crate::io::{self, Error, ErrorKind};
+use crate::path::Path;
+
+pub(crate) const NOT_FILE_ERROR: Error = io::const_io_error!(
+ ErrorKind::InvalidInput,
+ "the source path is neither a regular file nor a symlink to a regular file",
+);
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+ let mut reader = fs::File::open(from)?;
+ let metadata = reader.metadata()?;
+
+ if !metadata.is_file() {
+ return Err(NOT_FILE_ERROR);
+ }
+
+ let mut writer = fs::File::create(to)?;
+ let perm = metadata.permissions();
+
+ let ret = io::copy(&mut reader, &mut writer)?;
+ writer.set_permissions(perm)?;
+ Ok(ret)
+}
+
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+ let filetype = fs::symlink_metadata(path)?.file_type();
+ if filetype.is_symlink() { fs::remove_file(path) } else { remove_dir_all_recursive(path) }
+}
+
+fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
+ for child in fs::read_dir(path)? {
+ let child = child?;
+ if child.file_type()?.is_dir() {
+ remove_dir_all_recursive(&child.path())?;
+ } else {
+ fs::remove_file(&child.path())?;
+ }
+ }
+ fs::remove_dir(path)
+}
+
+pub fn try_exists(path: &Path) -> io::Result<bool> {
+ match fs::metadata(path) {
+ Ok(_) => Ok(true),
+ Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false),
+ Err(error) => Err(error),
+ }
+}
diff --git a/library/std/src/sys_common/io.rs b/library/std/src/sys_common/io.rs
new file mode 100644
index 000000000..d1e9fed41
--- /dev/null
+++ b/library/std/src/sys_common/io.rs
@@ -0,0 +1,49 @@
+// Bare metal platforms usually have very small amounts of RAM
+// (in the order of hundreds of KB)
+pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 };
+
+#[cfg(test)]
+#[allow(dead_code)] // not used on emscripten
+pub mod test {
+ use crate::env;
+ use crate::fs;
+ use crate::path::{Path, PathBuf};
+ use crate::thread;
+ use rand::RngCore;
+
+ pub struct TempDir(PathBuf);
+
+ impl TempDir {
+ pub fn join(&self, path: &str) -> PathBuf {
+ let TempDir(ref p) = *self;
+ p.join(path)
+ }
+
+ pub fn path(&self) -> &Path {
+ let TempDir(ref p) = *self;
+ p
+ }
+ }
+
+ impl Drop for TempDir {
+ fn drop(&mut self) {
+ // Gee, seeing how we're testing the fs module I sure hope that we
+ // at least implement this correctly!
+ let TempDir(ref p) = *self;
+ let result = fs::remove_dir_all(p);
+ // Avoid panicking while panicking as this causes the process to
+ // immediately abort, without displaying test results.
+ if !thread::panicking() {
+ result.unwrap();
+ }
+ }
+ }
+
+ pub fn tmpdir() -> TempDir {
+ let p = env::temp_dir();
+ let mut r = rand::thread_rng();
+ let ret = p.join(&format!("rust-{}", r.next_u32()));
+ fs::create_dir(&ret).unwrap();
+ TempDir(ret)
+ }
+}
diff --git a/library/std/src/sys_common/lazy_box.rs b/library/std/src/sys_common/lazy_box.rs
new file mode 100644
index 000000000..63c3316bd
--- /dev/null
+++ b/library/std/src/sys_common/lazy_box.rs
@@ -0,0 +1,90 @@
+#![allow(dead_code)] // Only used on some platforms.
+
+// This is used to wrap pthread {Mutex, Condvar, RwLock} in.
+
+use crate::marker::PhantomData;
+use crate::ops::{Deref, DerefMut};
+use crate::ptr::null_mut;
+use crate::sync::atomic::{
+ AtomicPtr,
+ Ordering::{AcqRel, Acquire},
+};
+
+pub(crate) struct LazyBox<T: LazyInit> {
+ ptr: AtomicPtr<T>,
+ _phantom: PhantomData<T>,
+}
+
+pub(crate) trait LazyInit {
+ /// This is called before the box is allocated, to provide the value to
+ /// move into the new box.
+ ///
+ /// It might be called more than once per LazyBox, as multiple threads
+ /// might race to initialize it concurrently, each constructing and initializing
+ /// their own box. All but one of them will be passed to `cancel_init` right after.
+ fn init() -> Box<Self>;
+
+ /// Any surplus boxes from `init()` that lost the initialization race
+ /// are passed to this function for disposal.
+ ///
+ /// The default implementation calls destroy().
+ fn cancel_init(x: Box<Self>) {
+ Self::destroy(x);
+ }
+
+ /// This is called to destroy a used box.
+ ///
+ /// The default implementation just drops it.
+ fn destroy(_: Box<Self>) {}
+}
+
+impl<T: LazyInit> LazyBox<T> {
+ #[inline]
+ pub const fn new() -> Self {
+ Self { ptr: AtomicPtr::new(null_mut()), _phantom: PhantomData }
+ }
+
+ #[inline]
+ fn get_pointer(&self) -> *mut T {
+ let ptr = self.ptr.load(Acquire);
+ if ptr.is_null() { self.initialize() } else { ptr }
+ }
+
+ #[cold]
+ fn initialize(&self) -> *mut T {
+ let new_ptr = Box::into_raw(T::init());
+ match self.ptr.compare_exchange(null_mut(), new_ptr, AcqRel, Acquire) {
+ Ok(_) => new_ptr,
+ Err(ptr) => {
+ // Lost the race to another thread.
+ // Drop the box we created, and use the one from the other thread instead.
+ T::cancel_init(unsafe { Box::from_raw(new_ptr) });
+ ptr
+ }
+ }
+ }
+}
+
+impl<T: LazyInit> Deref for LazyBox<T> {
+ type Target = T;
+ #[inline]
+ fn deref(&self) -> &T {
+ unsafe { &*self.get_pointer() }
+ }
+}
+
+impl<T: LazyInit> DerefMut for LazyBox<T> {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut T {
+ unsafe { &mut *self.get_pointer() }
+ }
+}
+
+impl<T: LazyInit> Drop for LazyBox<T> {
+ fn drop(&mut self) {
+ let ptr = *self.ptr.get_mut();
+ if !ptr.is_null() {
+ T::destroy(unsafe { Box::from_raw(ptr) });
+ }
+ }
+}
diff --git a/library/std/src/sys_common/memchr.rs b/library/std/src/sys_common/memchr.rs
new file mode 100644
index 000000000..b219e8789
--- /dev/null
+++ b/library/std/src/sys_common/memchr.rs
@@ -0,0 +1,51 @@
+// Original implementation taken from rust-memchr.
+// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
+
+use crate::sys::memchr as sys;
+
+#[cfg(test)]
+mod tests;
+
+/// A safe interface to `memchr`.
+///
+/// Returns the index corresponding to the first occurrence of `needle` in
+/// `haystack`, or `None` if one is not found.
+///
+/// memchr reduces to super-optimized machine code at around an order of
+/// magnitude faster than `haystack.iter().position(|&b| b == needle)`.
+/// (See benchmarks.)
+///
+/// # Examples
+///
+/// This shows how to find the first position of a byte in a byte string.
+///
+/// ```ignore (cannot-doctest-private-modules)
+/// use memchr::memchr;
+///
+/// let haystack = b"the quick brown fox";
+/// assert_eq!(memchr(b'k', haystack), Some(8));
+/// ```
+#[inline]
+pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+ sys::memchr(needle, haystack)
+}
+
+/// A safe interface to `memrchr`.
+///
+/// Returns the index corresponding to the last occurrence of `needle` in
+/// `haystack`, or `None` if one is not found.
+///
+/// # Examples
+///
+/// This shows how to find the last position of a byte in a byte string.
+///
+/// ```ignore (cannot-doctest-private-modules)
+/// use memchr::memrchr;
+///
+/// let haystack = b"the quick brown fox";
+/// assert_eq!(memrchr(b'o', haystack), Some(17));
+/// ```
+#[inline]
+pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+ sys::memrchr(needle, haystack)
+}
diff --git a/library/std/src/sys_common/memchr/tests.rs b/library/std/src/sys_common/memchr/tests.rs
new file mode 100644
index 000000000..557d749c7
--- /dev/null
+++ b/library/std/src/sys_common/memchr/tests.rs
@@ -0,0 +1,86 @@
+// Original implementation taken from rust-memchr.
+// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
+
+// test the implementations for the current platform
+use super::{memchr, memrchr};
+
+#[test]
+fn matches_one() {
+ assert_eq!(Some(0), memchr(b'a', b"a"));
+}
+
+#[test]
+fn matches_begin() {
+ assert_eq!(Some(0), memchr(b'a', b"aaaa"));
+}
+
+#[test]
+fn matches_end() {
+ assert_eq!(Some(4), memchr(b'z', b"aaaaz"));
+}
+
+#[test]
+fn matches_nul() {
+ assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00"));
+}
+
+#[test]
+fn matches_past_nul() {
+ assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z"));
+}
+
+#[test]
+fn no_match_empty() {
+ assert_eq!(None, memchr(b'a', b""));
+}
+
+#[test]
+fn no_match() {
+ assert_eq!(None, memchr(b'a', b"xyz"));
+}
+
+#[test]
+fn matches_one_reversed() {
+ assert_eq!(Some(0), memrchr(b'a', b"a"));
+}
+
+#[test]
+fn matches_begin_reversed() {
+ assert_eq!(Some(3), memrchr(b'a', b"aaaa"));
+}
+
+#[test]
+fn matches_end_reversed() {
+ assert_eq!(Some(0), memrchr(b'z', b"zaaaa"));
+}
+
+#[test]
+fn matches_nul_reversed() {
+ assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00"));
+}
+
+#[test]
+fn matches_past_nul_reversed() {
+ assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa"));
+}
+
+#[test]
+fn no_match_empty_reversed() {
+ assert_eq!(None, memrchr(b'a', b""));
+}
+
+#[test]
+fn no_match_reversed() {
+ assert_eq!(None, memrchr(b'a', b"xyz"));
+}
+
+#[test]
+fn each_alignment() {
+ let mut data = [1u8; 64];
+ let needle = 2;
+ let pos = 40;
+ data[pos] = needle;
+ for start in 0..16 {
+ assert_eq!(Some(pos - start), memchr(needle, &data[start..]));
+ }
+}
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs
new file mode 100644
index 000000000..80f56bf75
--- /dev/null
+++ b/library/std/src/sys_common/mod.rs
@@ -0,0 +1,89 @@
+//! Platform-independent platform abstraction
+//!
+//! This is the platform-independent portion of the standard library's
+//! platform abstraction layer, whereas `std::sys` is the
+//! platform-specific portion.
+//!
+//! The relationship between `std::sys_common`, `std::sys` and the
+//! rest of `std` is complex, with dependencies going in all
+//! directions: `std` depending on `sys_common`, `sys_common`
+//! depending on `sys`, and `sys` depending on `sys_common` and `std`.
+//! This is because `sys_common` not only contains platform-independent code,
+//! but also code that is shared between the different platforms in `sys`.
+//! Ideally all that shared code should be moved to `sys::common`,
+//! and the dependencies between `std`, `sys_common` and `sys` all would form a dag.
+//! Progress on this is tracked in #84187.
+
+#![allow(missing_docs)]
+#![allow(missing_debug_implementations)]
+
+#[cfg(test)]
+mod tests;
+
+pub mod backtrace;
+pub mod condvar;
+pub mod fs;
+pub mod io;
+pub mod lazy_box;
+pub mod memchr;
+pub mod mutex;
+pub mod process;
+pub mod remutex;
+pub mod rwlock;
+pub mod thread;
+pub mod thread_info;
+pub mod thread_local_dtor;
+pub mod thread_local_key;
+pub mod thread_parker;
+pub mod wtf8;
+
+cfg_if::cfg_if! {
+ if #[cfg(any(target_os = "l4re",
+ target_os = "hermit",
+ feature = "restricted-std",
+ all(target_family = "wasm", not(target_os = "emscripten")),
+ all(target_vendor = "fortanix", target_env = "sgx")))] {
+ pub use crate::sys::net;
+ } else {
+ pub mod net;
+ }
+}
+
+// common error constructors
+
+/// A trait for viewing representations from std types
+#[doc(hidden)]
+pub trait AsInner<Inner: ?Sized> {
+ fn as_inner(&self) -> &Inner;
+}
+
+/// A trait for viewing representations from std types
+#[doc(hidden)]
+pub trait AsInnerMut<Inner: ?Sized> {
+ fn as_inner_mut(&mut self) -> &mut Inner;
+}
+
+/// A trait for extracting representations from std types
+#[doc(hidden)]
+pub trait IntoInner<Inner> {
+ fn into_inner(self) -> Inner;
+}
+
+/// A trait for creating std types from internal representations
+#[doc(hidden)]
+pub trait FromInner<Inner> {
+ fn from_inner(inner: Inner) -> Self;
+}
+
+// Computes (value*numer)/denom without overflow, as long as both
+// (numer*denom) and the overall result fit into i64 (which is the case
+// for our time conversions).
+#[allow(dead_code)] // not used on all platforms
+pub fn mul_div_u64(value: u64, numer: u64, denom: u64) -> u64 {
+ let q = value / denom;
+ let r = value % denom;
+ // Decompose value as (value/denom*denom + value%denom),
+ // substitute into (value*numer)/denom and simplify.
+ // r < denom, so (denom*numer) is the upper bound of (r*numer)
+ q * numer + r * numer / denom
+}
diff --git a/library/std/src/sys_common/mutex.rs b/library/std/src/sys_common/mutex.rs
new file mode 100644
index 000000000..48479f5bd
--- /dev/null
+++ b/library/std/src/sys_common/mutex.rs
@@ -0,0 +1,93 @@
+use crate::sys::locks as imp;
+
+/// An OS-based mutual exclusion lock, meant for use in static variables.
+///
+/// This mutex has a const constructor ([`StaticMutex::new`]), does not
+/// implement `Drop` to cleanup resources, and causes UB when used reentrantly.
+///
+/// This mutex does not implement poisoning.
+///
+/// This is a wrapper around `imp::Mutex` that does *not* call `init()` and
+/// `destroy()`.
+pub struct StaticMutex(imp::Mutex);
+
+unsafe impl Sync for StaticMutex {}
+
+impl StaticMutex {
+ /// Creates a new mutex for use.
+ #[inline]
+ pub const fn new() -> Self {
+ Self(imp::Mutex::new())
+ }
+
+ /// Calls raw_lock() and then returns an RAII guard to guarantee the mutex
+ /// will be unlocked.
+ ///
+ /// It is undefined behaviour to call this function while locked by the
+ /// same thread.
+ #[inline]
+ pub unsafe fn lock(&'static self) -> StaticMutexGuard {
+ self.0.lock();
+ StaticMutexGuard(&self.0)
+ }
+}
+
+#[must_use]
+pub struct StaticMutexGuard(&'static imp::Mutex);
+
+impl Drop for StaticMutexGuard {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe {
+ self.0.unlock();
+ }
+ }
+}
+
+/// An OS-based mutual exclusion lock.
+///
+/// This mutex cleans up its resources in its `Drop` implementation, may safely
+/// be moved (when not borrowed), and does not cause UB when used reentrantly.
+///
+/// This mutex does not implement poisoning.
+///
+/// This is either a wrapper around `LazyBox<imp::Mutex>` or `imp::Mutex`,
+/// depending on the platform. It is boxed on platforms where `imp::Mutex` may
+/// not be moved.
+pub struct MovableMutex(imp::MovableMutex);
+
+unsafe impl Sync for MovableMutex {}
+
+impl MovableMutex {
+ /// Creates a new mutex.
+ #[inline]
+ pub const fn new() -> Self {
+ Self(imp::MovableMutex::new())
+ }
+
+ pub(super) fn raw(&self) -> &imp::Mutex {
+ &self.0
+ }
+
+ /// Locks the mutex blocking the current thread until it is available.
+ #[inline]
+ pub fn raw_lock(&self) {
+ unsafe { self.0.lock() }
+ }
+
+ /// Attempts to lock the mutex without blocking, returning whether it was
+ /// successfully acquired or not.
+ #[inline]
+ pub fn try_lock(&self) -> bool {
+ unsafe { self.0.try_lock() }
+ }
+
+ /// Unlocks the mutex.
+ ///
+ /// Behavior is undefined if the current thread does not actually hold the
+ /// mutex.
+ #[inline]
+ pub unsafe fn raw_unlock(&self) {
+ self.0.unlock()
+ }
+}
diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs
new file mode 100644
index 000000000..33d336c43
--- /dev/null
+++ b/library/std/src/sys_common/net.rs
@@ -0,0 +1,737 @@
+#[cfg(test)]
+mod tests;
+
+use crate::cmp;
+use crate::ffi::CString;
+use crate::fmt;
+use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
+use crate::mem;
+use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+use crate::ptr;
+use crate::sys::net::netc as c;
+use crate::sys::net::{cvt, cvt_gai, cvt_r, init, wrlen_t, Socket};
+use crate::sys_common::{FromInner, IntoInner};
+use crate::time::Duration;
+
+use libc::{c_int, c_void};
+
+cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "dragonfly", target_os = "freebsd",
+ target_os = "ios", target_os = "macos", target_os = "watchos",
+ target_os = "openbsd", target_os = "netbsd", target_os = "illumos",
+ target_os = "solaris", target_os = "haiku", target_os = "l4re"))] {
+ use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP;
+ use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP;
+ } else {
+ use crate::sys::net::netc::IPV6_ADD_MEMBERSHIP;
+ use crate::sys::net::netc::IPV6_DROP_MEMBERSHIP;
+ }
+}
+
+cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "linux", target_os = "android",
+ target_os = "dragonfly", target_os = "freebsd",
+ target_os = "openbsd", target_os = "netbsd",
+ target_os = "haiku"))] {
+ use libc::MSG_NOSIGNAL;
+ } else {
+ const MSG_NOSIGNAL: c_int = 0x0;
+ }
+}
+
+cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "dragonfly", target_os = "freebsd",
+ target_os = "openbsd", target_os = "netbsd",
+ target_os = "solaris", target_os = "illumos"))] {
+ use libc::c_uchar;
+ type IpV4MultiCastType = c_uchar;
+ } else {
+ type IpV4MultiCastType = c_int;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// sockaddr and misc bindings
+////////////////////////////////////////////////////////////////////////////////
+
+pub fn setsockopt<T>(
+ sock: &Socket,
+ level: c_int,
+ option_name: c_int,
+ option_value: T,
+) -> io::Result<()> {
+ unsafe {
+ cvt(c::setsockopt(
+ sock.as_raw(),
+ level,
+ option_name,
+ &option_value as *const T as *const _,
+ mem::size_of::<T>() as c::socklen_t,
+ ))?;
+ Ok(())
+ }
+}
+
+pub fn getsockopt<T: Copy>(sock: &Socket, level: c_int, option_name: c_int) -> io::Result<T> {
+ unsafe {
+ let mut option_value: T = mem::zeroed();
+ let mut option_len = mem::size_of::<T>() as c::socklen_t;
+ cvt(c::getsockopt(
+ sock.as_raw(),
+ level,
+ option_name,
+ &mut option_value as *mut T as *mut _,
+ &mut option_len,
+ ))?;
+ Ok(option_value)
+ }
+}
+
+fn sockname<F>(f: F) -> io::Result<SocketAddr>
+where
+ F: FnOnce(*mut c::sockaddr, *mut c::socklen_t) -> c_int,
+{
+ unsafe {
+ let mut storage: c::sockaddr_storage = mem::zeroed();
+ let mut len = mem::size_of_val(&storage) as c::socklen_t;
+ cvt(f(&mut storage as *mut _ as *mut _, &mut len))?;
+ sockaddr_to_addr(&storage, len as usize)
+ }
+}
+
+pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, len: usize) -> io::Result<SocketAddr> {
+ match storage.ss_family as c_int {
+ c::AF_INET => {
+ assert!(len as usize >= mem::size_of::<c::sockaddr_in>());
+ Ok(SocketAddr::V4(FromInner::from_inner(unsafe {
+ *(storage as *const _ as *const c::sockaddr_in)
+ })))
+ }
+ c::AF_INET6 => {
+ assert!(len as usize >= mem::size_of::<c::sockaddr_in6>());
+ Ok(SocketAddr::V6(FromInner::from_inner(unsafe {
+ *(storage as *const _ as *const c::sockaddr_in6)
+ })))
+ }
+ _ => Err(io::const_io_error!(ErrorKind::InvalidInput, "invalid argument")),
+ }
+}
+
+#[cfg(target_os = "android")]
+fn to_ipv6mr_interface(value: u32) -> c_int {
+ value as c_int
+}
+
+#[cfg(not(target_os = "android"))]
+fn to_ipv6mr_interface(value: u32) -> libc::c_uint {
+ value as libc::c_uint
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// get_host_addresses
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct LookupHost {
+ original: *mut c::addrinfo,
+ cur: *mut c::addrinfo,
+ port: u16,
+}
+
+impl LookupHost {
+ pub fn port(&self) -> u16 {
+ self.port
+ }
+}
+
+impl Iterator for LookupHost {
+ type Item = SocketAddr;
+ fn next(&mut self) -> Option<SocketAddr> {
+ loop {
+ unsafe {
+ let cur = self.cur.as_ref()?;
+ self.cur = cur.ai_next;
+ match sockaddr_to_addr(mem::transmute(cur.ai_addr), cur.ai_addrlen as usize) {
+ Ok(addr) => return Some(addr),
+ Err(_) => continue,
+ }
+ }
+ }
+ }
+}
+
+unsafe impl Sync for LookupHost {}
+unsafe impl Send for LookupHost {}
+
+impl Drop for LookupHost {
+ fn drop(&mut self) {
+ unsafe { c::freeaddrinfo(self.original) }
+ }
+}
+
+impl TryFrom<&str> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(s: &str) -> io::Result<LookupHost> {
+ macro_rules! try_opt {
+ ($e:expr, $msg:expr) => {
+ match $e {
+ Some(r) => r,
+ None => return Err(io::const_io_error!(io::ErrorKind::InvalidInput, $msg)),
+ }
+ };
+ }
+
+ // split the string by ':' and convert the second part to u16
+ let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address");
+ let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value");
+ (host, port).try_into()
+ }
+}
+
+impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from((host, port): (&'a str, u16)) -> io::Result<LookupHost> {
+ init();
+
+ let c_host = CString::new(host)?;
+ let mut hints: c::addrinfo = unsafe { mem::zeroed() };
+ hints.ai_socktype = c::SOCK_STREAM;
+ let mut res = ptr::null_mut();
+ unsafe {
+ cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res))
+ .map(|_| LookupHost { original: res, cur: res, port })
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TCP streams
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct TcpStream {
+ inner: Socket,
+}
+
+impl TcpStream {
+ pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
+ let addr = addr?;
+
+ init();
+
+ let sock = Socket::new(addr, c::SOCK_STREAM)?;
+
+ let (addr, len) = addr.into_inner();
+ cvt_r(|| unsafe { c::connect(sock.as_raw(), addr.as_ptr(), len) })?;
+ Ok(TcpStream { inner: sock })
+ }
+
+ pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result<TcpStream> {
+ init();
+
+ let sock = Socket::new(addr, c::SOCK_STREAM)?;
+ sock.connect_timeout(addr, timeout)?;
+ Ok(TcpStream { inner: sock })
+ }
+
+ pub fn socket(&self) -> &Socket {
+ &self.inner
+ }
+
+ pub fn into_socket(self) -> Socket {
+ self.inner
+ }
+
+ pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
+ self.inner.set_timeout(dur, c::SO_RCVTIMEO)
+ }
+
+ pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
+ self.inner.set_timeout(dur, c::SO_SNDTIMEO)
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ self.inner.timeout(c::SO_RCVTIMEO)
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ self.inner.timeout(c::SO_SNDTIMEO)
+ }
+
+ pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.inner.peek(buf)
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.inner.read(buf)
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.inner.read_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ self.inner.is_read_vectored()
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ let len = cmp::min(buf.len(), <wrlen_t>::MAX as usize) as wrlen_t;
+ let ret = cvt(unsafe {
+ c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL)
+ })?;
+ Ok(ret as usize)
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.inner.write_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ self.inner.is_write_vectored()
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) })
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) })
+ }
+
+ pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+ self.inner.shutdown(how)
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpStream> {
+ self.inner.duplicate().map(|s| TcpStream { inner: s })
+ }
+
+ pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
+ self.inner.set_linger(linger)
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ self.inner.linger()
+ }
+
+ pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
+ self.inner.set_nodelay(nodelay)
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ self.inner.nodelay()
+ }
+
+ pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
+ setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int)
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?;
+ Ok(raw as u32)
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ self.inner.take_error()
+ }
+
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ self.inner.set_nonblocking(nonblocking)
+ }
+}
+
+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 {
+ let mut res = f.debug_struct("TcpStream");
+
+ if let Ok(addr) = self.socket_addr() {
+ res.field("addr", &addr);
+ }
+
+ if let Ok(peer) = self.peer_addr() {
+ res.field("peer", &peer);
+ }
+
+ let name = if cfg!(windows) { "socket" } else { "fd" };
+ res.field(name, &self.inner.as_raw()).finish()
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TCP listeners
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct TcpListener {
+ inner: Socket,
+}
+
+impl TcpListener {
+ pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
+ let addr = addr?;
+
+ init();
+
+ let sock = Socket::new(addr, c::SOCK_STREAM)?;
+
+ // On platforms with Berkeley-derived sockets, this allows to quickly
+ // rebind a socket, without needing to wait for the OS to clean up the
+ // previous one.
+ //
+ // On Windows, this allows rebinding sockets which are actively in use,
+ // which allows “socket hijacking”, so we explicitly don't set it here.
+ // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse
+ #[cfg(not(windows))]
+ setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?;
+
+ // Bind our new socket
+ let (addr, len) = addr.into_inner();
+ cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?;
+
+ cfg_if::cfg_if! {
+ if #[cfg(target_os = "horizon")] {
+ // The 3DS doesn't support a big connection backlog. Sometimes
+ // it allows up to about 37, but other times it doesn't even
+ // accept 32. There may be a global limitation causing this.
+ let backlog = 20;
+ } else {
+ // The default for all other platforms
+ let backlog = 128;
+ }
+ }
+
+ // Start listening
+ cvt(unsafe { c::listen(sock.as_raw(), backlog) })?;
+ Ok(TcpListener { inner: sock })
+ }
+
+ pub fn socket(&self) -> &Socket {
+ &self.inner
+ }
+
+ pub fn into_socket(self) -> Socket {
+ self.inner
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) })
+ }
+
+ pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+ let mut storage: c::sockaddr_storage = unsafe { mem::zeroed() };
+ let mut len = mem::size_of_val(&storage) as c::socklen_t;
+ let sock = self.inner.accept(&mut storage as *mut _ as *mut _, &mut len)?;
+ let addr = sockaddr_to_addr(&storage, len as usize)?;
+ Ok((TcpStream { inner: sock }, addr))
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpListener> {
+ self.inner.duplicate().map(|s| TcpListener { inner: s })
+ }
+
+ pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
+ setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int)
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?;
+ Ok(raw as u32)
+ }
+
+ pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
+ setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY, only_v6 as c_int)
+ }
+
+ pub fn only_v6(&self) -> io::Result<bool> {
+ let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY)?;
+ Ok(raw != 0)
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ self.inner.take_error()
+ }
+
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ self.inner.set_nonblocking(nonblocking)
+ }
+}
+
+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 {
+ let mut res = f.debug_struct("TcpListener");
+
+ if let Ok(addr) = self.socket_addr() {
+ res.field("addr", &addr);
+ }
+
+ let name = if cfg!(windows) { "socket" } else { "fd" };
+ res.field(name, &self.inner.as_raw()).finish()
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// UDP
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct UdpSocket {
+ inner: Socket,
+}
+
+impl UdpSocket {
+ pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+ let addr = addr?;
+
+ init();
+
+ let sock = Socket::new(addr, c::SOCK_DGRAM)?;
+ let (addr, len) = addr.into_inner();
+ cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?;
+ Ok(UdpSocket { inner: sock })
+ }
+
+ pub fn socket(&self) -> &Socket {
+ &self.inner
+ }
+
+ pub fn into_socket(self) -> Socket {
+ self.inner
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) })
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) })
+ }
+
+ pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ self.inner.recv_from(buf)
+ }
+
+ pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ self.inner.peek_from(buf)
+ }
+
+ pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result<usize> {
+ let len = cmp::min(buf.len(), <wrlen_t>::MAX as usize) as wrlen_t;
+ let (dst, dstlen) = dst.into_inner();
+ let ret = cvt(unsafe {
+ c::sendto(
+ self.inner.as_raw(),
+ buf.as_ptr() as *const c_void,
+ len,
+ MSG_NOSIGNAL,
+ dst.as_ptr(),
+ dstlen,
+ )
+ })?;
+ Ok(ret as usize)
+ }
+
+ pub fn duplicate(&self) -> io::Result<UdpSocket> {
+ self.inner.duplicate().map(|s| UdpSocket { inner: s })
+ }
+
+ pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
+ self.inner.set_timeout(dur, c::SO_RCVTIMEO)
+ }
+
+ pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
+ self.inner.set_timeout(dur, c::SO_SNDTIMEO)
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ self.inner.timeout(c::SO_RCVTIMEO)
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ self.inner.timeout(c::SO_SNDTIMEO)
+ }
+
+ pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> {
+ setsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST, broadcast as c_int)
+ }
+
+ pub fn broadcast(&self) -> io::Result<bool> {
+ let raw: c_int = getsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST)?;
+ Ok(raw != 0)
+ }
+
+ pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> {
+ setsockopt(
+ &self.inner,
+ c::IPPROTO_IP,
+ c::IP_MULTICAST_LOOP,
+ multicast_loop_v4 as IpV4MultiCastType,
+ )
+ }
+
+ pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+ let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP)?;
+ Ok(raw != 0)
+ }
+
+ pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> {
+ setsockopt(
+ &self.inner,
+ c::IPPROTO_IP,
+ c::IP_MULTICAST_TTL,
+ multicast_ttl_v4 as IpV4MultiCastType,
+ )
+ }
+
+ pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+ let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL)?;
+ Ok(raw as u32)
+ }
+
+ pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> {
+ setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP, multicast_loop_v6 as c_int)
+ }
+
+ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+ let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP)?;
+ Ok(raw != 0)
+ }
+
+ pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
+ let mreq = c::ip_mreq {
+ imr_multiaddr: multiaddr.into_inner(),
+ imr_interface: interface.into_inner(),
+ };
+ setsockopt(&self.inner, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq)
+ }
+
+ pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
+ let mreq = c::ipv6_mreq {
+ ipv6mr_multiaddr: multiaddr.into_inner(),
+ ipv6mr_interface: to_ipv6mr_interface(interface),
+ };
+ setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq)
+ }
+
+ pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
+ let mreq = c::ip_mreq {
+ imr_multiaddr: multiaddr.into_inner(),
+ imr_interface: interface.into_inner(),
+ };
+ setsockopt(&self.inner, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq)
+ }
+
+ pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
+ let mreq = c::ipv6_mreq {
+ ipv6mr_multiaddr: multiaddr.into_inner(),
+ ipv6mr_interface: to_ipv6mr_interface(interface),
+ };
+ setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq)
+ }
+
+ pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
+ setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int)
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?;
+ Ok(raw as u32)
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ self.inner.take_error()
+ }
+
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ self.inner.set_nonblocking(nonblocking)
+ }
+
+ pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.inner.read(buf)
+ }
+
+ pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.inner.peek(buf)
+ }
+
+ pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
+ let len = cmp::min(buf.len(), <wrlen_t>::MAX as usize) as wrlen_t;
+ let ret = cvt(unsafe {
+ c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL)
+ })?;
+ Ok(ret as usize)
+ }
+
+ pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> {
+ let (addr, len) = addr?.into_inner();
+ cvt_r(|| unsafe { c::connect(self.inner.as_raw(), addr.as_ptr(), len) }).map(drop)
+ }
+}
+
+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 {
+ let mut res = f.debug_struct("UdpSocket");
+
+ if let Ok(addr) = self.socket_addr() {
+ res.field("addr", &addr);
+ }
+
+ let name = if cfg!(windows) { "socket" } else { "fd" };
+ res.field(name, &self.inner.as_raw()).finish()
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Converting SocketAddr to libc representation
+////////////////////////////////////////////////////////////////////////////////
+
+/// A type with the same memory layout as `c::sockaddr`. Used in converting Rust level
+/// SocketAddr* types into their system representation. The benefit of this specific
+/// type over using `c::sockaddr_storage` is that this type is exactly as large as it
+/// needs to be and not a lot larger. And it can be initialized more cleanly from Rust.
+#[repr(C)]
+pub(crate) union SocketAddrCRepr {
+ v4: c::sockaddr_in,
+ v6: c::sockaddr_in6,
+}
+
+impl SocketAddrCRepr {
+ pub fn as_ptr(&self) -> *const c::sockaddr {
+ self as *const _ as *const c::sockaddr
+ }
+}
+
+impl<'a> IntoInner<(SocketAddrCRepr, c::socklen_t)> for &'a SocketAddr {
+ fn into_inner(self) -> (SocketAddrCRepr, c::socklen_t) {
+ match *self {
+ SocketAddr::V4(ref a) => {
+ let sockaddr = SocketAddrCRepr { v4: a.into_inner() };
+ (sockaddr, mem::size_of::<c::sockaddr_in>() as c::socklen_t)
+ }
+ SocketAddr::V6(ref a) => {
+ let sockaddr = SocketAddrCRepr { v6: a.into_inner() };
+ (sockaddr, mem::size_of::<c::sockaddr_in6>() as c::socklen_t)
+ }
+ }
+ }
+}
diff --git a/library/std/src/sys_common/net/tests.rs b/library/std/src/sys_common/net/tests.rs
new file mode 100644
index 000000000..ac75d9ebf
--- /dev/null
+++ b/library/std/src/sys_common/net/tests.rs
@@ -0,0 +1,19 @@
+use super::*;
+use crate::collections::HashMap;
+
+#[test]
+fn no_lookup_host_duplicates() {
+ let mut addrs = HashMap::new();
+ let lh = match LookupHost::try_from(("localhost", 0)) {
+ Ok(lh) => lh,
+ Err(e) => panic!("couldn't resolve `localhost': {e}"),
+ };
+ for sa in lh {
+ *addrs.entry(sa).or_insert(0) += 1;
+ }
+ assert_eq!(
+ addrs.iter().filter(|&(_, &v)| v > 1).collect::<Vec<_>>(),
+ vec![],
+ "There should be no duplicate localhost entries"
+ );
+}
diff --git a/library/std/src/sys_common/process.rs b/library/std/src/sys_common/process.rs
new file mode 100644
index 000000000..9f978789a
--- /dev/null
+++ b/library/std/src/sys_common/process.rs
@@ -0,0 +1,119 @@
+#![allow(dead_code)]
+#![unstable(feature = "process_internals", issue = "none")]
+
+use crate::collections::BTreeMap;
+use crate::env;
+use crate::ffi::{OsStr, OsString};
+use crate::sys::process::EnvKey;
+
+// Stores a set of changes to an environment
+#[derive(Clone, Debug)]
+pub struct CommandEnv {
+ clear: bool,
+ saw_path: bool,
+ vars: BTreeMap<EnvKey, Option<OsString>>,
+}
+
+impl Default for CommandEnv {
+ fn default() -> Self {
+ CommandEnv { clear: false, saw_path: false, vars: Default::default() }
+ }
+}
+
+impl CommandEnv {
+ // Capture the current environment with these changes applied
+ pub fn capture(&self) -> BTreeMap<EnvKey, OsString> {
+ let mut result = BTreeMap::<EnvKey, OsString>::new();
+ if !self.clear {
+ for (k, v) in env::vars_os() {
+ result.insert(k.into(), v);
+ }
+ }
+ for (k, maybe_v) in &self.vars {
+ if let &Some(ref v) = maybe_v {
+ result.insert(k.clone(), v.clone());
+ } else {
+ result.remove(k);
+ }
+ }
+ result
+ }
+
+ pub fn is_unchanged(&self) -> bool {
+ !self.clear && self.vars.is_empty()
+ }
+
+ pub fn capture_if_changed(&self) -> Option<BTreeMap<EnvKey, OsString>> {
+ if self.is_unchanged() { None } else { Some(self.capture()) }
+ }
+
+ // The following functions build up changes
+ pub fn set(&mut self, key: &OsStr, value: &OsStr) {
+ let key = EnvKey::from(key);
+ self.maybe_saw_path(&key);
+ self.vars.insert(key, Some(value.to_owned()));
+ }
+
+ pub fn remove(&mut self, key: &OsStr) {
+ let key = EnvKey::from(key);
+ self.maybe_saw_path(&key);
+ if self.clear {
+ self.vars.remove(&key);
+ } else {
+ self.vars.insert(key, None);
+ }
+ }
+
+ pub fn clear(&mut self) {
+ 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: &EnvKey) {
+ if !self.saw_path && key == "PATH" {
+ self.saw_path = true;
+ }
+ }
+
+ pub fn iter(&self) -> CommandEnvs<'_> {
+ let iter = self.vars.iter();
+ CommandEnvs { iter }
+ }
+}
+
+/// An iterator over the command environment variables.
+///
+/// This struct is created by
+/// [`Command::get_envs`][crate::process::Command::get_envs]. See its
+/// documentation for more.
+#[must_use = "iterators are lazy and do nothing unless consumed"]
+#[stable(feature = "command_access", since = "1.57.0")]
+#[derive(Debug)]
+pub struct CommandEnvs<'a> {
+ iter: crate::collections::btree_map::Iter<'a, EnvKey, Option<OsString>>,
+}
+
+#[stable(feature = "command_access", since = "1.57.0")]
+impl<'a> Iterator for CommandEnvs<'a> {
+ type Item = (&'a OsStr, Option<&'a OsStr>);
+ fn next(&mut self) -> Option<Self::Item> {
+ self.iter.next().map(|(key, value)| (key.as_ref(), value.as_deref()))
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+#[stable(feature = "command_access", since = "1.57.0")]
+impl<'a> ExactSizeIterator for CommandEnvs<'a> {
+ fn len(&self) -> usize {
+ self.iter.len()
+ }
+ fn is_empty(&self) -> bool {
+ self.iter.is_empty()
+ }
+}
diff --git a/library/std/src/sys_common/remutex.rs b/library/std/src/sys_common/remutex.rs
new file mode 100644
index 000000000..8921af311
--- /dev/null
+++ b/library/std/src/sys_common/remutex.rs
@@ -0,0 +1,200 @@
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
+use crate::cell::UnsafeCell;
+use crate::marker::PhantomPinned;
+use crate::ops::Deref;
+use crate::panic::{RefUnwindSafe, UnwindSafe};
+use crate::pin::Pin;
+use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed};
+use crate::sys::locks as sys;
+
+/// A re-entrant mutual exclusion
+///
+/// This mutex will block *other* threads waiting for the lock to become
+/// available. The thread which has already locked the mutex can lock it
+/// multiple times without blocking, preventing a common source of deadlocks.
+///
+/// This is used by stdout().lock() and friends.
+///
+/// ## Implementation details
+///
+/// The 'owner' field tracks which thread has locked the mutex.
+///
+/// We use current_thread_unique_ptr() as the thread identifier,
+/// which is just the address of a thread local variable.
+///
+/// If `owner` is set to the identifier of the current thread,
+/// we assume the mutex is already locked and instead of locking it again,
+/// we increment `lock_count`.
+///
+/// When unlocking, we decrement `lock_count`, and only unlock the mutex when
+/// it reaches zero.
+///
+/// `lock_count` is protected by the mutex and only accessed by the thread that has
+/// locked the mutex, so needs no synchronization.
+///
+/// `owner` can be checked by other threads that want to see if they already
+/// hold the lock, so needs to be atomic. If it compares equal, we're on the
+/// same thread that holds the mutex and memory access can use relaxed ordering
+/// since we're not dealing with multiple threads. If it compares unequal,
+/// synchronization is left to the mutex, making relaxed memory ordering for
+/// the `owner` field fine in all cases.
+pub struct ReentrantMutex<T> {
+ mutex: sys::Mutex,
+ owner: AtomicUsize,
+ lock_count: UnsafeCell<u32>,
+ data: T,
+ _pinned: PhantomPinned,
+}
+
+unsafe impl<T: Send> Send for ReentrantMutex<T> {}
+unsafe impl<T: Send> Sync for ReentrantMutex<T> {}
+
+impl<T> UnwindSafe for ReentrantMutex<T> {}
+impl<T> RefUnwindSafe for ReentrantMutex<T> {}
+
+/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
+/// dropped (falls out of scope), the lock will be unlocked.
+///
+/// The data protected by the mutex can be accessed through this guard via its
+/// Deref implementation.
+///
+/// # Mutability
+///
+/// Unlike `MutexGuard`, `ReentrantMutexGuard` does not implement `DerefMut`,
+/// 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 = "if unused the ReentrantMutex will immediately unlock"]
+pub struct ReentrantMutexGuard<'a, T: 'a> {
+ lock: Pin<&'a ReentrantMutex<T>>,
+}
+
+impl<T> !Send for ReentrantMutexGuard<'_, T> {}
+
+impl<T> ReentrantMutex<T> {
+ /// Creates a new reentrant mutex in an unlocked state.
+ ///
+ /// # Unsafety
+ ///
+ /// This function is unsafe because it is required that `init` is called
+ /// once this mutex is in its final resting place, and only then are the
+ /// lock/unlock methods safe.
+ pub const unsafe fn new(t: T) -> ReentrantMutex<T> {
+ ReentrantMutex {
+ mutex: sys::Mutex::new(),
+ owner: AtomicUsize::new(0),
+ lock_count: UnsafeCell::new(0),
+ data: t,
+ _pinned: PhantomPinned,
+ }
+ }
+
+ /// Initializes this mutex so it's ready for use.
+ ///
+ /// # Unsafety
+ ///
+ /// Unsafe to call more than once, and must be called after this will no
+ /// longer move in memory.
+ pub unsafe fn init(self: Pin<&mut Self>) {
+ self.get_unchecked_mut().mutex.init()
+ }
+
+ /// Acquires a mutex, blocking the current thread until it is able to do so.
+ ///
+ /// This function will block the caller until it is available to acquire the mutex.
+ /// Upon returning, the thread is the only thread with the mutex held. When the thread
+ /// calling this method already holds the lock, the call shall succeed without
+ /// blocking.
+ ///
+ /// # Errors
+ ///
+ /// If another user of this mutex panicked while holding the mutex, then
+ /// this call will return failure if the mutex would otherwise be
+ /// acquired.
+ pub fn lock(self: Pin<&Self>) -> ReentrantMutexGuard<'_, T> {
+ let this_thread = current_thread_unique_ptr();
+ // Safety: We only touch lock_count when we own the lock,
+ // and since self is pinned we can safely call the lock() on the mutex.
+ unsafe {
+ if self.owner.load(Relaxed) == this_thread {
+ self.increment_lock_count();
+ } else {
+ self.mutex.lock();
+ self.owner.store(this_thread, Relaxed);
+ debug_assert_eq!(*self.lock_count.get(), 0);
+ *self.lock_count.get() = 1;
+ }
+ }
+ ReentrantMutexGuard { lock: self }
+ }
+
+ /// Attempts to acquire this lock.
+ ///
+ /// If the lock could not be acquired at this time, then `Err` is returned.
+ /// Otherwise, an RAII guard is returned.
+ ///
+ /// This function does not block.
+ ///
+ /// # Errors
+ ///
+ /// If another user of this mutex panicked while holding the mutex, then
+ /// this call will return failure if the mutex would otherwise be
+ /// acquired.
+ pub fn try_lock(self: Pin<&Self>) -> Option<ReentrantMutexGuard<'_, T>> {
+ let this_thread = current_thread_unique_ptr();
+ // Safety: We only touch lock_count when we own the lock,
+ // and since self is pinned we can safely call the try_lock on the mutex.
+ unsafe {
+ if self.owner.load(Relaxed) == this_thread {
+ self.increment_lock_count();
+ Some(ReentrantMutexGuard { lock: self })
+ } else if self.mutex.try_lock() {
+ self.owner.store(this_thread, Relaxed);
+ debug_assert_eq!(*self.lock_count.get(), 0);
+ *self.lock_count.get() = 1;
+ Some(ReentrantMutexGuard { lock: self })
+ } else {
+ None
+ }
+ }
+ }
+
+ unsafe fn increment_lock_count(&self) {
+ *self.lock_count.get() = (*self.lock_count.get())
+ .checked_add(1)
+ .expect("lock count overflow in reentrant mutex");
+ }
+}
+
+impl<T> Deref for ReentrantMutexGuard<'_, T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &self.lock.data
+ }
+}
+
+impl<T> Drop for ReentrantMutexGuard<'_, T> {
+ #[inline]
+ fn drop(&mut self) {
+ // Safety: We own the lock, and the lock is pinned.
+ unsafe {
+ *self.lock.lock_count.get() -= 1;
+ if *self.lock.lock_count.get() == 0 {
+ self.lock.owner.store(0, Relaxed);
+ self.lock.mutex.unlock();
+ }
+ }
+ }
+}
+
+/// Get an address that is unique per running thread.
+///
+/// This can be used as a non-null usize-sized ID.
+pub fn current_thread_unique_ptr() -> usize {
+ // Use a non-drop type to make sure it's still available during thread destruction.
+ thread_local! { static X: u8 = const { 0 } }
+ X.with(|x| <*const _>::addr(x))
+}
diff --git a/library/std/src/sys_common/remutex/tests.rs b/library/std/src/sys_common/remutex/tests.rs
new file mode 100644
index 000000000..64873b850
--- /dev/null
+++ b/library/std/src/sys_common/remutex/tests.rs
@@ -0,0 +1,77 @@
+use crate::boxed::Box;
+use crate::cell::RefCell;
+use crate::pin::Pin;
+use crate::sync::Arc;
+use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
+use crate::thread;
+
+#[test]
+fn smoke() {
+ let m = unsafe {
+ let mut m = Box::pin(ReentrantMutex::new(()));
+ m.as_mut().init();
+ m
+ };
+ let m = m.as_ref();
+ {
+ let a = m.lock();
+ {
+ let b = m.lock();
+ {
+ let c = m.lock();
+ assert_eq!(*c, ());
+ }
+ assert_eq!(*b, ());
+ }
+ assert_eq!(*a, ());
+ }
+}
+
+#[test]
+fn is_mutex() {
+ let m = unsafe {
+ // FIXME: Simplify this if Arc gets an Arc::get_pin_mut.
+ let mut m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
+ Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init();
+ Pin::new_unchecked(m)
+ };
+ let m2 = m.clone();
+ let lock = m.as_ref().lock();
+ let child = thread::spawn(move || {
+ let lock = m2.as_ref().lock();
+ assert_eq!(*lock.borrow(), 4950);
+ });
+ for i in 0..100 {
+ let lock = m.as_ref().lock();
+ *lock.borrow_mut() += i;
+ }
+ drop(lock);
+ child.join().unwrap();
+}
+
+#[test]
+fn trylock_works() {
+ let m = unsafe {
+ // FIXME: Simplify this if Arc gets an Arc::get_pin_mut.
+ let mut m = Arc::new(ReentrantMutex::new(()));
+ Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init();
+ Pin::new_unchecked(m)
+ };
+ let m2 = m.clone();
+ let _lock = m.as_ref().try_lock();
+ let _lock2 = m.as_ref().try_lock();
+ thread::spawn(move || {
+ let lock = m2.as_ref().try_lock();
+ assert!(lock.is_none());
+ })
+ .join()
+ .unwrap();
+ let _lock3 = m.as_ref().try_lock();
+}
+
+pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);
+impl Drop for Answer<'_> {
+ fn drop(&mut self) {
+ *self.0.borrow_mut() = 42;
+ }
+}
diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs
new file mode 100644
index 000000000..ba56f3a8f
--- /dev/null
+++ b/library/std/src/sys_common/rwlock.rs
@@ -0,0 +1,130 @@
+use crate::sys::locks as imp;
+
+/// An OS-based reader-writer lock, meant for use in static variables.
+///
+/// This rwlock does not implement poisoning.
+///
+/// This rwlock has a const constructor ([`StaticRwLock::new`]), does not
+/// implement `Drop` to cleanup resources.
+pub struct StaticRwLock(imp::RwLock);
+
+impl StaticRwLock {
+ /// Creates a new rwlock for use.
+ #[inline]
+ pub const fn new() -> Self {
+ Self(imp::RwLock::new())
+ }
+
+ /// Acquires shared access to the underlying lock, blocking the current
+ /// thread to do so.
+ ///
+ /// The lock is automatically unlocked when the returned guard is dropped.
+ #[inline]
+ pub fn read(&'static self) -> StaticRwLockReadGuard {
+ unsafe { self.0.read() };
+ StaticRwLockReadGuard(&self.0)
+ }
+
+ /// Acquires write access to the underlying lock, blocking the current thread
+ /// to do so.
+ ///
+ /// The lock is automatically unlocked when the returned guard is dropped.
+ #[inline]
+ pub fn write(&'static self) -> StaticRwLockWriteGuard {
+ unsafe { self.0.write() };
+ StaticRwLockWriteGuard(&self.0)
+ }
+}
+
+#[must_use]
+pub struct StaticRwLockReadGuard(&'static imp::RwLock);
+
+impl Drop for StaticRwLockReadGuard {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe {
+ self.0.read_unlock();
+ }
+ }
+}
+
+#[must_use]
+pub struct StaticRwLockWriteGuard(&'static imp::RwLock);
+
+impl Drop for StaticRwLockWriteGuard {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe {
+ self.0.write_unlock();
+ }
+ }
+}
+
+/// An OS-based reader-writer lock.
+///
+/// This rwlock cleans up its resources in its `Drop` implementation and may
+/// safely be moved (when not borrowed).
+///
+/// This rwlock does not implement poisoning.
+///
+/// This is either a wrapper around `LazyBox<imp::RwLock>` or `imp::RwLock`,
+/// depending on the platform. It is boxed on platforms where `imp::RwLock` may
+/// not be moved.
+pub struct MovableRwLock(imp::MovableRwLock);
+
+impl MovableRwLock {
+ /// Creates a new reader-writer lock for use.
+ #[inline]
+ pub const fn new() -> Self {
+ Self(imp::MovableRwLock::new())
+ }
+
+ /// Acquires shared access to the underlying lock, blocking the current
+ /// thread to do so.
+ #[inline]
+ pub fn read(&self) {
+ unsafe { self.0.read() }
+ }
+
+ /// Attempts to acquire shared access to this lock, returning whether it
+ /// succeeded or not.
+ ///
+ /// This function does not block the current thread.
+ #[inline]
+ pub fn try_read(&self) -> bool {
+ unsafe { self.0.try_read() }
+ }
+
+ /// Acquires write access to the underlying lock, blocking the current thread
+ /// to do so.
+ #[inline]
+ pub fn write(&self) {
+ unsafe { self.0.write() }
+ }
+
+ /// Attempts to acquire exclusive access to this lock, returning whether it
+ /// succeeded or not.
+ ///
+ /// This function does not block the current thread.
+ #[inline]
+ pub fn try_write(&self) -> bool {
+ unsafe { self.0.try_write() }
+ }
+
+ /// Unlocks previously acquired shared access to this lock.
+ ///
+ /// Behavior is undefined if the current thread does not have shared access.
+ #[inline]
+ pub unsafe fn read_unlock(&self) {
+ self.0.read_unlock()
+ }
+
+ /// Unlocks previously acquired exclusive access to this lock.
+ ///
+ /// Behavior is undefined if the current thread does not currently have
+ /// exclusive access.
+ #[inline]
+ pub unsafe fn write_unlock(&self) {
+ self.0.write_unlock()
+ }
+}
diff --git a/library/std/src/sys_common/tests.rs b/library/std/src/sys_common/tests.rs
new file mode 100644
index 000000000..1b6446db5
--- /dev/null
+++ b/library/std/src/sys_common/tests.rs
@@ -0,0 +1,6 @@
+use super::mul_div_u64;
+
+#[test]
+fn test_muldiv() {
+ assert_eq!(mul_div_u64(1_000_000_000_001, 1_000_000_000, 1_000_000), 1_000_000_000_001_000);
+}
diff --git a/library/std/src/sys_common/thread.rs b/library/std/src/sys_common/thread.rs
new file mode 100644
index 000000000..76466b2b3
--- /dev/null
+++ b/library/std/src/sys_common/thread.rs
@@ -0,0 +1,18 @@
+use crate::env;
+use crate::sync::atomic::{self, Ordering};
+use crate::sys::thread as imp;
+
+pub fn min_stack() -> usize {
+ static MIN: atomic::AtomicUsize = atomic::AtomicUsize::new(0);
+ match MIN.load(Ordering::Relaxed) {
+ 0 => {}
+ n => return n - 1,
+ }
+ let amt = env::var("RUST_MIN_STACK").ok().and_then(|s| s.parse().ok());
+ let amt = amt.unwrap_or(imp::DEFAULT_MIN_STACK_SIZE);
+
+ // 0 is our sentinel value, so ensure that we'll never see 0 after
+ // initialization has run
+ MIN.store(amt + 1, Ordering::Relaxed);
+ amt
+}
diff --git a/library/std/src/sys_common/thread_info.rs b/library/std/src/sys_common/thread_info.rs
new file mode 100644
index 000000000..38c9e5000
--- /dev/null
+++ b/library/std/src/sys_common/thread_info.rs
@@ -0,0 +1,47 @@
+#![allow(dead_code)] // stack_guard isn't used right now on all platforms
+#![allow(unused_unsafe)] // thread_local with `const {}` triggers this liny
+
+use crate::cell::RefCell;
+use crate::sys::thread::guard::Guard;
+use crate::thread::Thread;
+
+struct ThreadInfo {
+ stack_guard: Option<Guard>,
+ thread: Thread,
+}
+
+thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = const { RefCell::new(None) } }
+
+impl ThreadInfo {
+ fn with<R, F>(f: F) -> Option<R>
+ where
+ F: FnOnce(&mut ThreadInfo) -> R,
+ {
+ THREAD_INFO
+ .try_with(move |thread_info| {
+ let mut thread_info = thread_info.borrow_mut();
+ let thread_info = thread_info.get_or_insert_with(|| ThreadInfo {
+ stack_guard: None,
+ thread: Thread::new(None),
+ });
+ f(thread_info)
+ })
+ .ok()
+ }
+}
+
+pub fn current_thread() -> Option<Thread> {
+ ThreadInfo::with(|info| info.thread.clone())
+}
+
+pub fn stack_guard() -> Option<Guard> {
+ ThreadInfo::with(|info| info.stack_guard.clone()).and_then(|o| o)
+}
+
+pub fn set(stack_guard: Option<Guard>, thread: Thread) {
+ THREAD_INFO.with(move |thread_info| {
+ let mut thread_info = thread_info.borrow_mut();
+ rtassert!(thread_info.is_none());
+ *thread_info = Some(ThreadInfo { stack_guard, thread });
+ });
+}
diff --git a/library/std/src/sys_common/thread_local_dtor.rs b/library/std/src/sys_common/thread_local_dtor.rs
new file mode 100644
index 000000000..1d13a7171
--- /dev/null
+++ b/library/std/src/sys_common/thread_local_dtor.rs
@@ -0,0 +1,49 @@
+//! Thread-local destructor
+//!
+//! Besides thread-local "keys" (pointer-sized non-addressable thread-local store
+//! with an associated destructor), many platforms also provide thread-local
+//! destructors that are not associated with any particular data. These are
+//! often more efficient.
+//!
+//! This module provides a fallback implementation for that interface, based
+//! on the less efficient thread-local "keys". Each platform provides
+//! a `thread_local_dtor` module which will either re-export the fallback,
+//! or implement something more efficient.
+
+#![unstable(feature = "thread_local_internals", issue = "none")]
+#![allow(dead_code)]
+
+use crate::ptr;
+use crate::sys_common::thread_local_key::StaticKey;
+
+pub unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern "C" 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.
+
+ static DTORS: StaticKey = StaticKey::new(Some(run_dtors));
+ type List = Vec<(*mut u8, unsafe extern "C" 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 "C" 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());
+ }
+ }
+}
diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs
new file mode 100644
index 000000000..70beebe86
--- /dev/null
+++ b/library/std/src/sys_common/thread_local_key.rs
@@ -0,0 +1,237 @@
+//! OS-based thread local storage
+//!
+//! This module provides an implementation of OS-based thread local storage,
+//! using the native OS-provided facilities (think `TlsAlloc` or
+//! `pthread_setspecific`). The interface of this differs from the other types
+//! of thread-local-storage provided in this crate in that OS-based TLS can only
+//! get/set pointer-sized data, possibly with an associated destructor.
+//!
+//! This module also provides two flavors of TLS. One is intended for static
+//! initialization, and does not contain a `Drop` implementation to deallocate
+//! the OS-TLS key. The other is a type which does implement `Drop` and hence
+//! has a safe interface.
+//!
+//! # Usage
+//!
+//! This module should likely not be used directly unless other primitives are
+//! being built on. Types such as `thread_local::spawn::Key` are likely much
+//! more useful in practice than this OS-based version which likely requires
+//! unsafe code to interoperate with.
+//!
+//! # Examples
+//!
+//! Using a dynamically allocated TLS key. Note that this key can be shared
+//! among many threads via an `Arc`.
+//!
+//! ```ignore (cannot-doctest-private-modules)
+//! let key = Key::new(None);
+//! assert!(key.get().is_null());
+//! key.set(1 as *mut u8);
+//! assert!(!key.get().is_null());
+//!
+//! drop(key); // deallocate this TLS slot.
+//! ```
+//!
+//! Sometimes a statically allocated key is either required or easier to work
+//! with, however.
+//!
+//! ```ignore (cannot-doctest-private-modules)
+//! static KEY: StaticKey = INIT;
+//!
+//! unsafe {
+//! assert!(KEY.get().is_null());
+//! KEY.set(1 as *mut u8);
+//! }
+//! ```
+
+#![allow(non_camel_case_types)]
+#![unstable(feature = "thread_local_internals", issue = "none")]
+#![allow(dead_code)]
+
+#[cfg(test)]
+mod tests;
+
+use crate::sync::atomic::{self, AtomicUsize, Ordering};
+use crate::sys::thread_local_key as imp;
+use crate::sys_common::mutex::StaticMutex;
+
+/// A type for TLS keys that are statically allocated.
+///
+/// This type is entirely `unsafe` to use as it does not protect against
+/// use-after-deallocation or use-during-deallocation.
+///
+/// The actual OS-TLS key is lazily allocated when this is used for the first
+/// time. The key is also deallocated when the Rust runtime exits or `destroy`
+/// is called, whichever comes first.
+///
+/// # Examples
+///
+/// ```ignore (cannot-doctest-private-modules)
+/// use tls::os::{StaticKey, INIT};
+///
+/// static KEY: StaticKey = INIT;
+///
+/// unsafe {
+/// assert!(KEY.get().is_null());
+/// KEY.set(1 as *mut u8);
+/// }
+/// ```
+pub struct StaticKey {
+ /// Inner static TLS key (internals).
+ key: AtomicUsize,
+ /// Destructor for the TLS value.
+ ///
+ /// See `Key::new` for information about when the destructor runs and how
+ /// it runs.
+ dtor: Option<unsafe extern "C" fn(*mut u8)>,
+}
+
+/// A type for a safely managed OS-based TLS slot.
+///
+/// This type allocates an OS TLS key when it is initialized and will deallocate
+/// the key when it falls out of scope. When compared with `StaticKey`, this
+/// type is entirely safe to use.
+///
+/// Implementations will likely, however, contain unsafe code as this type only
+/// operates on `*mut u8`, a raw pointer.
+///
+/// # Examples
+///
+/// ```ignore (cannot-doctest-private-modules)
+/// use tls::os::Key;
+///
+/// let key = Key::new(None);
+/// assert!(key.get().is_null());
+/// key.set(1 as *mut u8);
+/// assert!(!key.get().is_null());
+///
+/// drop(key); // deallocate this TLS slot.
+/// ```
+pub struct Key {
+ key: imp::Key,
+}
+
+/// Constant initialization value for static TLS keys.
+///
+/// This value specifies no destructor by default.
+pub const INIT: StaticKey = StaticKey::new(None);
+
+impl StaticKey {
+ #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
+ pub const fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> StaticKey {
+ StaticKey { key: atomic::AtomicUsize::new(0), dtor }
+ }
+
+ /// Gets the value associated with this TLS key
+ ///
+ /// This will lazily allocate a TLS key from the OS if one has not already
+ /// been allocated.
+ #[inline]
+ pub unsafe fn get(&self) -> *mut u8 {
+ imp::get(self.key())
+ }
+
+ /// Sets this TLS key to a new value.
+ ///
+ /// This will lazily allocate a TLS key from the OS if one has not already
+ /// been allocated.
+ #[inline]
+ pub unsafe fn set(&self, val: *mut u8) {
+ imp::set(self.key(), val)
+ }
+
+ #[inline]
+ unsafe fn key(&self) -> imp::Key {
+ match self.key.load(Ordering::Relaxed) {
+ 0 => self.lazy_init() as imp::Key,
+ n => n as imp::Key,
+ }
+ }
+
+ unsafe fn lazy_init(&self) -> usize {
+ // Currently the Windows implementation of TLS is pretty hairy, and
+ // it greatly simplifies creation if we just synchronize everything.
+ //
+ // Additionally a 0-index of a tls key hasn't been seen on windows, so
+ // we just simplify the whole branch.
+ if imp::requires_synchronized_create() {
+ // We never call `INIT_LOCK.init()`, so it is UB to attempt to
+ // acquire this mutex reentrantly!
+ static INIT_LOCK: StaticMutex = StaticMutex::new();
+ let _guard = INIT_LOCK.lock();
+ let mut key = self.key.load(Ordering::SeqCst);
+ if key == 0 {
+ key = imp::create(self.dtor) as usize;
+ self.key.store(key, Ordering::SeqCst);
+ }
+ rtassert!(key != 0);
+ return key;
+ }
+
+ // POSIX allows the key created here to be 0, but the compare_exchange
+ // below relies on using 0 as a sentinel value to check who won the
+ // race to set the shared TLS key. As far as I know, there is no
+ // guaranteed value that cannot be returned as a posix_key_create key,
+ // so there is no value we can initialize the inner key with to
+ // prove that it has not yet been set. As such, we'll continue using a
+ // value of 0, but with some gyrations to make sure we have a non-0
+ // value returned from the creation routine.
+ // FIXME: this is clearly a hack, and should be cleaned up.
+ let key1 = imp::create(self.dtor);
+ let key = if key1 != 0 {
+ key1
+ } else {
+ let key2 = imp::create(self.dtor);
+ imp::destroy(key1);
+ key2
+ };
+ rtassert!(key != 0);
+ match self.key.compare_exchange(0, key as usize, Ordering::SeqCst, Ordering::SeqCst) {
+ // The CAS succeeded, so we've created the actual key
+ Ok(_) => key as usize,
+ // If someone beat us to the punch, use their key instead
+ Err(n) => {
+ imp::destroy(key);
+ n
+ }
+ }
+ }
+}
+
+impl Key {
+ /// Creates a new managed OS TLS key.
+ ///
+ /// This key will be deallocated when the key falls out of scope.
+ ///
+ /// The argument provided is an optionally-specified destructor for the
+ /// value of this TLS key. When a thread exits and the value for this key
+ /// is non-null the destructor will be invoked. The TLS value will be reset
+ /// to null before the destructor is invoked.
+ ///
+ /// Note that the destructor will not be run when the `Key` goes out of
+ /// scope.
+ #[inline]
+ pub fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
+ Key { key: unsafe { imp::create(dtor) } }
+ }
+
+ /// See StaticKey::get
+ #[inline]
+ pub fn get(&self) -> *mut u8 {
+ unsafe { imp::get(self.key) }
+ }
+
+ /// See StaticKey::set
+ #[inline]
+ pub fn set(&self, val: *mut u8) {
+ unsafe { imp::set(self.key, val) }
+ }
+}
+
+impl Drop for Key {
+ fn drop(&mut self) {
+ // Right now Windows doesn't support TLS key destruction, but this also
+ // isn't used anywhere other than tests, so just leak the TLS key.
+ // unsafe { imp::destroy(self.key) }
+ }
+}
diff --git a/library/std/src/sys_common/thread_local_key/tests.rs b/library/std/src/sys_common/thread_local_key/tests.rs
new file mode 100644
index 000000000..968738a41
--- /dev/null
+++ b/library/std/src/sys_common/thread_local_key/tests.rs
@@ -0,0 +1,34 @@
+use super::{Key, StaticKey};
+
+fn assert_sync<T: Sync>() {}
+fn assert_send<T: Send>() {}
+
+#[test]
+fn smoke() {
+ assert_sync::<Key>();
+ assert_send::<Key>();
+
+ let k1 = Key::new(None);
+ let k2 = Key::new(None);
+ assert!(k1.get().is_null());
+ assert!(k2.get().is_null());
+ k1.set(1 as *mut _);
+ k2.set(2 as *mut _);
+ assert_eq!(k1.get() as usize, 1);
+ assert_eq!(k2.get() as usize, 2);
+}
+
+#[test]
+fn statik() {
+ static K1: StaticKey = StaticKey::new(None);
+ static K2: StaticKey = StaticKey::new(None);
+
+ unsafe {
+ assert!(K1.get().is_null());
+ assert!(K2.get().is_null());
+ K1.set(1 as *mut _);
+ K2.set(2 as *mut _);
+ assert_eq!(K1.get() as usize, 1);
+ assert_eq!(K2.get() as usize, 2);
+ }
+}
diff --git a/library/std/src/sys_common/thread_parker/futex.rs b/library/std/src/sys_common/thread_parker/futex.rs
new file mode 100644
index 000000000..d9e2f39e3
--- /dev/null
+++ b/library/std/src/sys_common/thread_parker/futex.rs
@@ -0,0 +1,97 @@
+use crate::pin::Pin;
+use crate::sync::atomic::AtomicU32;
+use crate::sync::atomic::Ordering::{Acquire, Release};
+use crate::sys::futex::{futex_wait, futex_wake};
+use crate::time::Duration;
+
+const PARKED: u32 = u32::MAX;
+const EMPTY: u32 = 0;
+const NOTIFIED: u32 = 1;
+
+pub struct Parker {
+ state: AtomicU32,
+}
+
+// Notes about memory ordering:
+//
+// Memory ordering is only relevant for the relative ordering of operations
+// between different variables. Even Ordering::Relaxed guarantees a
+// monotonic/consistent order when looking at just a single atomic variable.
+//
+// So, since this parker is just a single atomic variable, we only need to look
+// at the ordering guarantees we need to provide to the 'outside world'.
+//
+// The only memory ordering guarantee that parking and unparking provide, is
+// that things which happened before unpark() are visible on the thread
+// returning from park() afterwards. Otherwise, it was effectively unparked
+// before unpark() was called while still consuming the 'token'.
+//
+// In other words, unpark() needs to synchronize with the part of park() that
+// consumes the token and returns.
+//
+// This is done with a release-acquire synchronization, by using
+// Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using
+// Ordering::Acquire when checking for this state in park().
+impl Parker {
+ /// Construct the futex parker. The UNIX parker implementation
+ /// requires this to happen in-place.
+ pub unsafe fn new(parker: *mut Parker) {
+ parker.write(Self { state: AtomicU32::new(EMPTY) });
+ }
+
+ // Assumes this is only called by the thread that owns the Parker,
+ // which means that `self.state != PARKED`.
+ pub unsafe fn park(self: Pin<&Self>) {
+ // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
+ // first case.
+ if self.state.fetch_sub(1, Acquire) == NOTIFIED {
+ return;
+ }
+ loop {
+ // Wait for something to happen, assuming it's still set to PARKED.
+ futex_wait(&self.state, PARKED, None);
+ // Change NOTIFIED=>EMPTY and return in that case.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire).is_ok() {
+ return;
+ } else {
+ // Spurious wake up. We loop to try again.
+ }
+ }
+ }
+
+ // Assumes this is only called by the thread that owns the Parker,
+ // which means that `self.state != PARKED`. This implementation doesn't
+ // require `Pin`, but other implementations do.
+ pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) {
+ // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
+ // first case.
+ if self.state.fetch_sub(1, Acquire) == NOTIFIED {
+ return;
+ }
+ // Wait for something to happen, assuming it's still set to PARKED.
+ futex_wait(&self.state, PARKED, Some(timeout));
+ // This is not just a store, because we need to establish a
+ // release-acquire ordering with unpark().
+ if self.state.swap(EMPTY, Acquire) == NOTIFIED {
+ // Woke up because of unpark().
+ } else {
+ // Timeout or spurious wake up.
+ // We return either way, because we can't easily tell if it was the
+ // timeout or not.
+ }
+ }
+
+ // This implementation doesn't require `Pin`, but other implementations do.
+ #[inline]
+ pub fn unpark(self: Pin<&Self>) {
+ // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and
+ // wake the thread in the first case.
+ //
+ // Note that even NOTIFIED=>NOTIFIED results in a write. This is on
+ // purpose, to make sure every unpark() has a release-acquire ordering
+ // with park().
+ if self.state.swap(NOTIFIED, Release) == PARKED {
+ futex_wake(&self.state);
+ }
+ }
+}
diff --git a/library/std/src/sys_common/thread_parker/generic.rs b/library/std/src/sys_common/thread_parker/generic.rs
new file mode 100644
index 000000000..f3d8b34d3
--- /dev/null
+++ b/library/std/src/sys_common/thread_parker/generic.rs
@@ -0,0 +1,125 @@
+//! Parker implementation based on a Mutex and Condvar.
+
+use crate::pin::Pin;
+use crate::sync::atomic::AtomicUsize;
+use crate::sync::atomic::Ordering::SeqCst;
+use crate::sync::{Condvar, Mutex};
+use crate::time::Duration;
+
+const EMPTY: usize = 0;
+const PARKED: usize = 1;
+const NOTIFIED: usize = 2;
+
+pub struct Parker {
+ state: AtomicUsize,
+ lock: Mutex<()>,
+ cvar: Condvar,
+}
+
+impl Parker {
+ /// Construct the generic parker. The UNIX parker implementation
+ /// requires this to happen in-place.
+ pub unsafe fn new(parker: *mut Parker) {
+ parker.write(Parker {
+ state: AtomicUsize::new(EMPTY),
+ lock: Mutex::new(()),
+ cvar: Condvar::new(),
+ });
+ }
+
+ // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
+ pub unsafe fn park(self: Pin<&Self>) {
+ // If we were previously notified then we consume this notification and
+ // return quickly.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return;
+ }
+
+ // Otherwise we need to coordinate going to sleep
+ let mut m = self.lock.lock().unwrap();
+ match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => {
+ // We must read here, even though we know it will be `NOTIFIED`.
+ // This is because `unpark` may have been called again since we read
+ // `NOTIFIED` in the `compare_exchange` above. We must perform an
+ // acquire operation that synchronizes with that `unpark` to observe
+ // any writes it made before the call to unpark. To do that we must
+ // read from the write it made to `state`.
+ let old = self.state.swap(EMPTY, SeqCst);
+ assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+ return;
+ } // should consume this notification, so prohibit spurious wakeups in next park.
+ Err(_) => panic!("inconsistent park state"),
+ }
+ loop {
+ m = self.cvar.wait(m).unwrap();
+ match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) {
+ Ok(_) => return, // got a notification
+ Err(_) => {} // spurious wakeup, go back to sleep
+ }
+ }
+ }
+
+ // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
+ pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
+ // Like `park` above we have a fast path for an already-notified thread, and
+ // afterwards we start coordinating for a sleep.
+ // return quickly.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return;
+ }
+ let m = self.lock.lock().unwrap();
+ match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => {
+ // We must read again here, see `park`.
+ let old = self.state.swap(EMPTY, SeqCst);
+ assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+ return;
+ } // should consume this notification, so prohibit spurious wakeups in next park.
+ Err(_) => panic!("inconsistent park_timeout state"),
+ }
+
+ // Wait with a timeout, and if we spuriously wake up or otherwise wake up
+ // from a notification we just want to unconditionally set the state back to
+ // empty, either consuming a notification or un-flagging ourselves as
+ // parked.
+ let (_m, _result) = self.cvar.wait_timeout(m, dur).unwrap();
+ match self.state.swap(EMPTY, SeqCst) {
+ NOTIFIED => {} // got a notification, hurray!
+ PARKED => {} // no notification, alas
+ n => panic!("inconsistent park_timeout state: {n}"),
+ }
+ }
+
+ // This implementation doesn't require `Pin`, but other implementations do.
+ pub fn unpark(self: Pin<&Self>) {
+ // To ensure the unparked thread will observe any writes we made
+ // before this call, we must perform a release operation that `park`
+ // can synchronize with. To do that we must write `NOTIFIED` even if
+ // `state` is already `NOTIFIED`. That is why this must be a swap
+ // rather than a compare-and-swap that returns if it reads `NOTIFIED`
+ // on failure.
+ match self.state.swap(NOTIFIED, SeqCst) {
+ EMPTY => return, // no one was waiting
+ NOTIFIED => return, // already unparked
+ PARKED => {} // gotta go wake someone up
+ _ => panic!("inconsistent state in unpark"),
+ }
+
+ // There is a period between when the parked thread sets `state` to
+ // `PARKED` (or last checked `state` in the case of a spurious wake
+ // up) and when it actually waits on `cvar`. If we were to notify
+ // during this period it would be ignored and then when the parked
+ // thread went to sleep it would never wake up. Fortunately, it has
+ // `lock` locked at this stage so we can acquire `lock` to wait until
+ // it is ready to receive the notification.
+ //
+ // Releasing `lock` before the call to `notify_one` means that when the
+ // parked thread wakes it doesn't get woken only to have to wait for us
+ // to release `lock`.
+ drop(self.lock.lock().unwrap());
+ self.cvar.notify_one()
+ }
+}
diff --git a/library/std/src/sys_common/thread_parker/mod.rs b/library/std/src/sys_common/thread_parker/mod.rs
new file mode 100644
index 000000000..cbd7832eb
--- /dev/null
+++ b/library/std/src/sys_common/thread_parker/mod.rs
@@ -0,0 +1,22 @@
+cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ all(target_arch = "wasm32", target_feature = "atomics"),
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ target_os = "fuchsia",
+ ))] {
+ mod futex;
+ pub use futex::Parker;
+ } else if #[cfg(target_os = "solid_asp3")] {
+ mod wait_flag;
+ pub use wait_flag::Parker;
+ } else if #[cfg(any(windows, target_family = "unix"))] {
+ pub use crate::sys::thread_parker::Parker;
+ } else {
+ mod generic;
+ pub use generic::Parker;
+ }
+}
diff --git a/library/std/src/sys_common/thread_parker/wait_flag.rs b/library/std/src/sys_common/thread_parker/wait_flag.rs
new file mode 100644
index 000000000..6561c1866
--- /dev/null
+++ b/library/std/src/sys_common/thread_parker/wait_flag.rs
@@ -0,0 +1,102 @@
+//! A wait-flag-based thread parker.
+//!
+//! Some operating systems provide low-level parking primitives like wait counts,
+//! event flags or semaphores which are not susceptible to race conditions (meaning
+//! the wakeup can occur before the wait operation). To implement the `std` thread
+//! parker on top of these primitives, we only have to ensure that parking is fast
+//! when the thread token is available, the atomic ordering guarantees are maintained
+//! and spurious wakeups are minimized.
+//!
+//! To achieve this, this parker uses an atomic variable with three states: `EMPTY`,
+//! `PARKED` and `NOTIFIED`:
+//! * `EMPTY` means the token has not been made available, but the thread is not
+//! currently waiting on it.
+//! * `PARKED` means the token is not available and the thread is parked.
+//! * `NOTIFIED` means the token is available.
+//!
+//! `park` and `park_timeout` change the state from `EMPTY` to `PARKED` and from
+//! `NOTIFIED` to `EMPTY`. If the state was `NOTIFIED`, the thread was unparked and
+//! execution can continue without calling into the OS. If the state was `EMPTY`,
+//! the token is not available and the thread waits on the primitive (here called
+//! "wait flag").
+//!
+//! `unpark` changes the state to `NOTIFIED`. If the state was `PARKED`, the thread
+//! is or will be sleeping on the wait flag, so we raise it.
+
+use crate::pin::Pin;
+use crate::sync::atomic::AtomicI8;
+use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
+use crate::sys::wait_flag::WaitFlag;
+use crate::time::Duration;
+
+const EMPTY: i8 = 0;
+const PARKED: i8 = -1;
+const NOTIFIED: i8 = 1;
+
+pub struct Parker {
+ state: AtomicI8,
+ wait_flag: WaitFlag,
+}
+
+impl Parker {
+ /// Construct a parker for the current thread. The UNIX parker
+ /// implementation requires this to happen in-place.
+ pub unsafe fn new(parker: *mut Parker) {
+ parker.write(Parker { state: AtomicI8::new(EMPTY), wait_flag: WaitFlag::new() })
+ }
+
+ // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
+ pub unsafe fn park(self: Pin<&Self>) {
+ match self.state.fetch_sub(1, Acquire) {
+ // NOTIFIED => EMPTY
+ NOTIFIED => return,
+ // EMPTY => PARKED
+ EMPTY => (),
+ _ => panic!("inconsistent park state"),
+ }
+
+ // Avoid waking up from spurious wakeups (these are quite likely, see below).
+ loop {
+ self.wait_flag.wait();
+
+ match self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed) {
+ Ok(_) => return,
+ Err(PARKED) => (),
+ Err(_) => panic!("inconsistent park state"),
+ }
+ }
+ }
+
+ // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
+ pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
+ match self.state.fetch_sub(1, Acquire) {
+ NOTIFIED => return,
+ EMPTY => (),
+ _ => panic!("inconsistent park state"),
+ }
+
+ self.wait_flag.wait_timeout(dur);
+
+ // Either a wakeup or a timeout occurred. Wakeups may be spurious, as there can be
+ // a race condition when `unpark` is performed between receiving the timeout and
+ // resetting the state, resulting in the eventflag being set unnecessarily. `park`
+ // is protected against this by looping until the token is actually given, but
+ // here we cannot easily tell.
+
+ // Use `swap` to provide acquire ordering.
+ match self.state.swap(EMPTY, Acquire) {
+ NOTIFIED => (),
+ PARKED => (),
+ _ => panic!("inconsistent park state"),
+ }
+ }
+
+ // This implementation doesn't require `Pin`, but other implementations do.
+ pub fn unpark(self: Pin<&Self>) {
+ let state = self.state.swap(NOTIFIED, Release);
+
+ if state == PARKED {
+ self.wait_flag.raise();
+ }
+ }
+}
diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs
new file mode 100644
index 000000000..57fa49893
--- /dev/null
+++ b/library/std/src/sys_common/wtf8.rs
@@ -0,0 +1,926 @@
+//! Implementation of [the WTF-8 encoding](https://simonsapin.github.io/wtf-8/).
+//!
+//! This library uses Rust’s type system to maintain
+//! [well-formedness](https://simonsapin.github.io/wtf-8/#well-formed),
+//! like the `String` and `&str` types do for UTF-8.
+//!
+//! Since [WTF-8 must not be used
+//! for interchange](https://simonsapin.github.io/wtf-8/#intended-audience),
+//! this library deliberately does not provide access to the underlying bytes
+//! of WTF-8 strings,
+//! nor can it decode WTF-8 from arbitrary bytes.
+//! WTF-8 strings can be obtained from UTF-8, UTF-16, or code points.
+
+// this module is imported from @SimonSapin's repo and has tons of dead code on
+// unix (it's mostly used on windows), so don't worry about dead code here.
+#![allow(dead_code)]
+
+#[cfg(test)]
+mod tests;
+
+use core::str::next_code_point;
+
+use crate::borrow::Cow;
+use crate::char;
+use crate::collections::TryReserveError;
+use crate::fmt;
+use crate::hash::{Hash, Hasher};
+use crate::iter::FusedIterator;
+use crate::mem;
+use crate::ops;
+use crate::rc::Rc;
+use crate::slice;
+use crate::str;
+use crate::sync::Arc;
+use crate::sys_common::AsInner;
+
+const UTF8_REPLACEMENT_CHARACTER: &str = "\u{FFFD}";
+
+/// A Unicode code point: from U+0000 to U+10FFFF.
+///
+/// Compares with the `char` type,
+/// which represents a Unicode scalar value:
+/// a code point that is not a surrogate (U+D800 to U+DFFF).
+#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
+pub struct CodePoint {
+ value: u32,
+}
+
+/// Format the code point as `U+` followed by four to six hexadecimal digits.
+/// Example: `U+1F4A9`
+impl fmt::Debug for CodePoint {
+ #[inline]
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(formatter, "U+{:04X}", self.value)
+ }
+}
+
+impl CodePoint {
+ /// Unsafely creates a new `CodePoint` without checking the value.
+ ///
+ /// Only use when `value` is known to be less than or equal to 0x10FFFF.
+ #[inline]
+ pub unsafe fn from_u32_unchecked(value: u32) -> CodePoint {
+ CodePoint { value }
+ }
+
+ /// Creates a new `CodePoint` if the value is a valid code point.
+ ///
+ /// Returns `None` if `value` is above 0x10FFFF.
+ #[inline]
+ pub fn from_u32(value: u32) -> Option<CodePoint> {
+ match value {
+ 0..=0x10FFFF => Some(CodePoint { value }),
+ _ => None,
+ }
+ }
+
+ /// Creates a new `CodePoint` from a `char`.
+ ///
+ /// Since all Unicode scalar values are code points, this always succeeds.
+ #[inline]
+ pub fn from_char(value: char) -> CodePoint {
+ CodePoint { value: value as u32 }
+ }
+
+ /// Returns the numeric value of the code point.
+ #[inline]
+ pub fn to_u32(&self) -> u32 {
+ self.value
+ }
+
+ /// Optionally returns a Unicode scalar value for the code point.
+ ///
+ /// Returns `None` if the code point is a surrogate (from U+D800 to U+DFFF).
+ #[inline]
+ pub fn to_char(&self) -> Option<char> {
+ match self.value {
+ 0xD800..=0xDFFF => None,
+ _ => Some(unsafe { char::from_u32_unchecked(self.value) }),
+ }
+ }
+
+ /// Returns a Unicode scalar value for the code point.
+ ///
+ /// Returns `'\u{FFFD}'` (the replacement character “�”)
+ /// if the code point is a surrogate (from U+D800 to U+DFFF).
+ #[inline]
+ pub fn to_char_lossy(&self) -> char {
+ self.to_char().unwrap_or('\u{FFFD}')
+ }
+}
+
+/// An owned, growable string of well-formed WTF-8 data.
+///
+/// Similar to `String`, but can additionally contain surrogate code points
+/// if they’re not in a surrogate pair.
+#[derive(Eq, PartialEq, Ord, PartialOrd, Clone)]
+pub struct Wtf8Buf {
+ bytes: Vec<u8>,
+}
+
+impl ops::Deref for Wtf8Buf {
+ type Target = Wtf8;
+
+ fn deref(&self) -> &Wtf8 {
+ self.as_slice()
+ }
+}
+
+impl ops::DerefMut for Wtf8Buf {
+ fn deref_mut(&mut self) -> &mut Wtf8 {
+ self.as_mut_slice()
+ }
+}
+
+/// Format the string with double quotes,
+/// and surrogates as `\u` followed by four hexadecimal digits.
+/// 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<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&**self, formatter)
+ }
+}
+
+impl Wtf8Buf {
+ /// Creates a new, empty WTF-8 string.
+ #[inline]
+ pub fn new() -> Wtf8Buf {
+ Wtf8Buf { bytes: Vec::new() }
+ }
+
+ /// Creates a new, empty WTF-8 string with pre-allocated capacity for `capacity` bytes.
+ #[inline]
+ pub fn with_capacity(capacity: usize) -> Wtf8Buf {
+ Wtf8Buf { bytes: Vec::with_capacity(capacity) }
+ }
+
+ /// Creates a WTF-8 string from a UTF-8 `String`.
+ ///
+ /// This takes ownership of the `String` and does not copy.
+ ///
+ /// Since WTF-8 is a superset of UTF-8, this always succeeds.
+ #[inline]
+ pub fn from_string(string: String) -> Wtf8Buf {
+ Wtf8Buf { bytes: string.into_bytes() }
+ }
+
+ /// Creates a WTF-8 string from a UTF-8 `&str` slice.
+ ///
+ /// This copies the content of the slice.
+ ///
+ /// Since WTF-8 is a superset of UTF-8, this always succeeds.
+ #[inline]
+ pub fn from_str(str: &str) -> Wtf8Buf {
+ Wtf8Buf { bytes: <[_]>::to_vec(str.as_bytes()) }
+ }
+
+ pub fn clear(&mut self) {
+ self.bytes.clear()
+ }
+
+ /// Creates a WTF-8 string 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.
+ pub fn from_wide(v: &[u16]) -> Wtf8Buf {
+ let mut string = Wtf8Buf::with_capacity(v.len());
+ for item in char::decode_utf16(v.iter().cloned()) {
+ match item {
+ Ok(ch) => string.push_char(ch),
+ Err(surrogate) => {
+ let surrogate = surrogate.unpaired_surrogate();
+ // Surrogates are known to be in the code point range.
+ let code_point = unsafe { CodePoint::from_u32_unchecked(surrogate as u32) };
+ // Skip the WTF-8 concatenation check,
+ // surrogate pairs are already decoded by decode_utf16
+ string.push_code_point_unchecked(code_point)
+ }
+ }
+ }
+ string
+ }
+
+ /// Copied from String::push
+ /// This does **not** include the WTF-8 concatenation check.
+ fn push_code_point_unchecked(&mut self, code_point: CodePoint) {
+ let mut bytes = [0; 4];
+ let bytes = char::encode_utf8_raw(code_point.value, &mut bytes);
+ self.bytes.extend_from_slice(bytes)
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &Wtf8 {
+ unsafe { Wtf8::from_bytes_unchecked(&self.bytes) }
+ }
+
+ #[inline]
+ pub fn as_mut_slice(&mut self) -> &mut Wtf8 {
+ unsafe { Wtf8::from_mut_bytes_unchecked(&mut self.bytes) }
+ }
+
+ /// Reserves capacity for at least `additional` more bytes to be inserted
+ /// in the given `Wtf8Buf`.
+ /// The collection may reserve more space to avoid frequent reallocations.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the new capacity overflows `usize`.
+ #[inline]
+ pub fn reserve(&mut self, additional: usize) {
+ self.bytes.reserve(additional)
+ }
+
+ /// Tries to reserve capacity for at least `additional` more length units
+ /// in the given `Wtf8Buf`. The `Wtf8Buf` may reserve more space to avoid
+ /// frequent reallocations. After calling `try_reserve`, capacity will be
+ /// greater than or equal to `self.len() + additional`. Does nothing if
+ /// capacity is already sufficient.
+ ///
+ /// # Errors
+ ///
+ /// If the capacity overflows, or the allocator reports a failure, then an error
+ /// is returned.
+ #[inline]
+ pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
+ self.bytes.try_reserve(additional)
+ }
+
+ #[inline]
+ pub fn reserve_exact(&mut self, additional: usize) {
+ self.bytes.reserve_exact(additional)
+ }
+
+ /// Tries to reserve the minimum capacity for exactly `additional`
+ /// length units in the given `Wtf8Buf`. After calling
+ /// `try_reserve_exact`, capacity will be greater than or equal to
+ /// `self.len() + additional` if it returns `Ok(())`.
+ /// Does nothing if the capacity is already sufficient.
+ ///
+ /// Note that the allocator may give the `Wtf8Buf` more space than it
+ /// requests. Therefore, capacity can not be relied upon to be precisely
+ /// minimal. Prefer [`try_reserve`] if future insertions are expected.
+ ///
+ /// [`try_reserve`]: Wtf8Buf::try_reserve
+ ///
+ /// # Errors
+ ///
+ /// If the capacity overflows, or the allocator reports a failure, then an error
+ /// is returned.
+ #[inline]
+ pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
+ self.bytes.try_reserve_exact(additional)
+ }
+
+ #[inline]
+ pub fn shrink_to_fit(&mut self) {
+ self.bytes.shrink_to_fit()
+ }
+
+ #[inline]
+ pub fn shrink_to(&mut self, min_capacity: usize) {
+ self.bytes.shrink_to(min_capacity)
+ }
+
+ /// Returns the number of bytes that this string buffer can hold without reallocating.
+ #[inline]
+ pub fn capacity(&self) -> usize {
+ self.bytes.capacity()
+ }
+
+ /// Append a UTF-8 slice at the end of the string.
+ #[inline]
+ pub fn push_str(&mut self, other: &str) {
+ self.bytes.extend_from_slice(other.as_bytes())
+ }
+
+ /// Append a WTF-8 slice at the end of the string.
+ ///
+ /// This replaces newly paired surrogates at the boundary
+ /// with a supplementary code point,
+ /// like concatenating ill-formed UTF-16 strings effectively would.
+ #[inline]
+ pub fn push_wtf8(&mut self, other: &Wtf8) {
+ match ((&*self).final_lead_surrogate(), other.initial_trail_surrogate()) {
+ // Replace newly paired surrogates by a supplementary code point.
+ (Some(lead), Some(trail)) => {
+ let len_without_lead_surrogate = self.len() - 3;
+ self.bytes.truncate(len_without_lead_surrogate);
+ let other_without_trail_surrogate = &other.bytes[3..];
+ // 4 bytes for the supplementary code point
+ self.bytes.reserve(4 + other_without_trail_surrogate.len());
+ self.push_char(decode_surrogate_pair(lead, trail));
+ self.bytes.extend_from_slice(other_without_trail_surrogate);
+ }
+ _ => self.bytes.extend_from_slice(&other.bytes),
+ }
+ }
+
+ /// Append a Unicode scalar value at the end of the string.
+ #[inline]
+ pub fn push_char(&mut self, c: char) {
+ self.push_code_point_unchecked(CodePoint::from_char(c))
+ }
+
+ /// Append a code point at the end of the string.
+ ///
+ /// This replaces newly paired surrogates at the boundary
+ /// with a supplementary code point,
+ /// like concatenating ill-formed UTF-16 strings effectively would.
+ #[inline]
+ pub fn push(&mut self, code_point: CodePoint) {
+ if let trail @ 0xDC00..=0xDFFF = code_point.to_u32() {
+ if let Some(lead) = (&*self).final_lead_surrogate() {
+ let len_without_lead_surrogate = self.len() - 3;
+ self.bytes.truncate(len_without_lead_surrogate);
+ self.push_char(decode_surrogate_pair(lead, trail as u16));
+ return;
+ }
+ }
+
+ // No newly paired surrogates at the boundary.
+ self.push_code_point_unchecked(code_point)
+ }
+
+ /// Shortens a string to the specified length.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `new_len` > current length,
+ /// or if `new_len` is not a code point boundary.
+ #[inline]
+ pub fn truncate(&mut self, new_len: usize) {
+ assert!(is_code_point_boundary(self, new_len));
+ self.bytes.truncate(new_len)
+ }
+
+ /// Consumes the WTF-8 string and tries to convert it to UTF-8.
+ ///
+ /// This does not copy the data.
+ ///
+ /// If the contents are not well-formed UTF-8
+ /// (that is, if the string contains surrogates),
+ /// the original WTF-8 string is returned instead.
+ pub fn into_string(self) -> Result<String, Wtf8Buf> {
+ match self.next_surrogate(0) {
+ None => Ok(unsafe { String::from_utf8_unchecked(self.bytes) }),
+ Some(_) => Err(self),
+ }
+ }
+
+ /// Consumes the WTF-8 string and converts it lossily to UTF-8.
+ ///
+ /// This does not copy the data (but may overwrite parts of it in place).
+ ///
+ /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”)
+ pub fn into_string_lossy(mut self) -> String {
+ let mut pos = 0;
+ loop {
+ match self.next_surrogate(pos) {
+ Some((surrogate_pos, _)) => {
+ pos = surrogate_pos + 3;
+ self.bytes[surrogate_pos..pos]
+ .copy_from_slice(UTF8_REPLACEMENT_CHARACTER.as_bytes());
+ }
+ None => return unsafe { String::from_utf8_unchecked(self.bytes) },
+ }
+ }
+ }
+
+ /// Converts this `Wtf8Buf` into a boxed `Wtf8`.
+ #[inline]
+ pub fn into_box(self) -> Box<Wtf8> {
+ unsafe { mem::transmute(self.bytes.into_boxed_slice()) }
+ }
+
+ /// Converts a `Box<Wtf8>` into a `Wtf8Buf`.
+ pub fn from_box(boxed: Box<Wtf8>) -> Wtf8Buf {
+ let bytes: Box<[u8]> = unsafe { mem::transmute(boxed) };
+ Wtf8Buf { bytes: bytes.into_vec() }
+ }
+}
+
+/// Creates a new WTF-8 string from an iterator of code points.
+///
+/// This replaces surrogate code point pairs with supplementary code points,
+/// like concatenating ill-formed UTF-16 strings effectively would.
+impl FromIterator<CodePoint> for Wtf8Buf {
+ fn from_iter<T: IntoIterator<Item = CodePoint>>(iter: T) -> Wtf8Buf {
+ let mut string = Wtf8Buf::new();
+ string.extend(iter);
+ string
+ }
+}
+
+/// Append code points from an iterator to the string.
+///
+/// This replaces surrogate code point pairs with supplementary code points,
+/// like concatenating ill-formed UTF-16 strings effectively would.
+impl Extend<CodePoint> for Wtf8Buf {
+ fn extend<T: IntoIterator<Item = CodePoint>>(&mut self, iter: T) {
+ let iterator = iter.into_iter();
+ let (low, _high) = iterator.size_hint();
+ // Lower bound of one byte per code point (ASCII only)
+ self.bytes.reserve(low);
+ iterator.for_each(move |code_point| self.push(code_point));
+ }
+
+ #[inline]
+ fn extend_one(&mut self, code_point: CodePoint) {
+ self.push(code_point);
+ }
+
+ #[inline]
+ fn extend_reserve(&mut self, additional: usize) {
+ // Lower bound of one byte per code point (ASCII only)
+ self.bytes.reserve(additional);
+ }
+}
+
+/// A borrowed slice of well-formed WTF-8 data.
+///
+/// Similar to `&str`, but can additionally contain surrogate code points
+/// if they’re not in a surrogate pair.
+#[derive(Eq, Ord, PartialEq, PartialOrd)]
+pub struct Wtf8 {
+ bytes: [u8],
+}
+
+impl AsInner<[u8]> for Wtf8 {
+ fn as_inner(&self) -> &[u8] {
+ &self.bytes
+ }
+}
+
+/// Format the slice with double quotes,
+/// and surrogates as `\u` followed by four hexadecimal digits.
+/// Example: `"a\u{D800}"` for a slice with code points [U+0061, U+D800]
+impl fmt::Debug for Wtf8 {
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fn write_str_escaped(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result {
+ use crate::fmt::Write;
+ for c in s.chars().flat_map(|c| c.escape_debug()) {
+ f.write_char(c)?
+ }
+ Ok(())
+ }
+
+ formatter.write_str("\"")?;
+ let mut pos = 0;
+ while let Some((surrogate_pos, surrogate)) = self.next_surrogate(pos) {
+ write_str_escaped(formatter, unsafe {
+ str::from_utf8_unchecked(&self.bytes[pos..surrogate_pos])
+ })?;
+ write!(formatter, "\\u{{{:x}}}", surrogate)?;
+ pos = surrogate_pos + 3;
+ }
+ write_str_escaped(formatter, unsafe { str::from_utf8_unchecked(&self.bytes[pos..]) })?;
+ formatter.write_str("\"")
+ }
+}
+
+impl fmt::Display for Wtf8 {
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let wtf8_bytes = &self.bytes;
+ let mut pos = 0;
+ loop {
+ match self.next_surrogate(pos) {
+ Some((surrogate_pos, _)) => {
+ formatter.write_str(unsafe {
+ str::from_utf8_unchecked(&wtf8_bytes[pos..surrogate_pos])
+ })?;
+ formatter.write_str(UTF8_REPLACEMENT_CHARACTER)?;
+ pos = surrogate_pos + 3;
+ }
+ None => {
+ let s = unsafe { str::from_utf8_unchecked(&wtf8_bytes[pos..]) };
+ if pos == 0 { return s.fmt(formatter) } else { return formatter.write_str(s) }
+ }
+ }
+ }
+ }
+}
+
+impl Wtf8 {
+ /// Creates a WTF-8 slice from a UTF-8 `&str` slice.
+ ///
+ /// Since WTF-8 is a superset of UTF-8, this always succeeds.
+ #[inline]
+ pub fn from_str(value: &str) -> &Wtf8 {
+ unsafe { Wtf8::from_bytes_unchecked(value.as_bytes()) }
+ }
+
+ /// Creates a WTF-8 slice from a WTF-8 byte slice.
+ ///
+ /// Since the byte slice is not checked for valid WTF-8, this functions is
+ /// marked unsafe.
+ #[inline]
+ unsafe fn from_bytes_unchecked(value: &[u8]) -> &Wtf8 {
+ mem::transmute(value)
+ }
+
+ /// Creates a mutable WTF-8 slice from a mutable WTF-8 byte slice.
+ ///
+ /// Since the byte slice is not checked for valid WTF-8, this functions is
+ /// marked unsafe.
+ #[inline]
+ unsafe fn from_mut_bytes_unchecked(value: &mut [u8]) -> &mut Wtf8 {
+ mem::transmute(value)
+ }
+
+ /// Returns the length, in WTF-8 bytes.
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.bytes.len()
+ }
+
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.bytes.is_empty()
+ }
+
+ /// Returns the code point at `position` if it is in the ASCII range,
+ /// or `b'\xFF' otherwise.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `position` is beyond the end of the string.
+ #[inline]
+ pub fn ascii_byte_at(&self, position: usize) -> u8 {
+ match self.bytes[position] {
+ ascii_byte @ 0x00..=0x7F => ascii_byte,
+ _ => 0xFF,
+ }
+ }
+
+ /// Returns an iterator for the string’s code points.
+ #[inline]
+ pub fn code_points(&self) -> Wtf8CodePoints<'_> {
+ Wtf8CodePoints { bytes: self.bytes.iter() }
+ }
+
+ /// Tries to convert the string to UTF-8 and return a `&str` slice.
+ ///
+ /// Returns `None` if the string contains surrogates.
+ ///
+ /// This does not copy the data.
+ #[inline]
+ pub fn as_str(&self) -> Option<&str> {
+ // Well-formed WTF-8 is also well-formed UTF-8
+ // if and only if it contains no surrogate.
+ match self.next_surrogate(0) {
+ None => Some(unsafe { str::from_utf8_unchecked(&self.bytes) }),
+ Some(_) => None,
+ }
+ }
+
+ /// Lossily converts the string to UTF-8.
+ /// Returns a UTF-8 `&str` slice if the contents are well-formed in UTF-8.
+ ///
+ /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”).
+ ///
+ /// This only copies the data if necessary (if it contains any surrogate).
+ pub fn to_string_lossy(&self) -> Cow<'_, str> {
+ let surrogate_pos = match self.next_surrogate(0) {
+ None => return Cow::Borrowed(unsafe { str::from_utf8_unchecked(&self.bytes) }),
+ Some((pos, _)) => pos,
+ };
+ let wtf8_bytes = &self.bytes;
+ let mut utf8_bytes = Vec::with_capacity(self.len());
+ utf8_bytes.extend_from_slice(&wtf8_bytes[..surrogate_pos]);
+ utf8_bytes.extend_from_slice(UTF8_REPLACEMENT_CHARACTER.as_bytes());
+ let mut pos = surrogate_pos + 3;
+ loop {
+ match self.next_surrogate(pos) {
+ Some((surrogate_pos, _)) => {
+ utf8_bytes.extend_from_slice(&wtf8_bytes[pos..surrogate_pos]);
+ utf8_bytes.extend_from_slice(UTF8_REPLACEMENT_CHARACTER.as_bytes());
+ pos = surrogate_pos + 3;
+ }
+ None => {
+ utf8_bytes.extend_from_slice(&wtf8_bytes[pos..]);
+ return Cow::Owned(unsafe { String::from_utf8_unchecked(utf8_bytes) });
+ }
+ }
+ }
+ }
+
+ /// Converts the WTF-8 string to potentially ill-formed UTF-16
+ /// and return an iterator of 16-bit code units.
+ ///
+ /// This is lossless:
+ /// calling `Wtf8Buf::from_ill_formed_utf16` on the resulting code units
+ /// would always return the original WTF-8 string.
+ #[inline]
+ pub fn encode_wide(&self) -> EncodeWide<'_> {
+ EncodeWide { code_points: self.code_points(), extra: 0 }
+ }
+
+ #[inline]
+ fn next_surrogate(&self, mut pos: usize) -> Option<(usize, u16)> {
+ let mut iter = self.bytes[pos..].iter();
+ loop {
+ let b = *iter.next()?;
+ if b < 0x80 {
+ pos += 1;
+ } else if b < 0xE0 {
+ iter.next();
+ pos += 2;
+ } else if b == 0xED {
+ match (iter.next(), iter.next()) {
+ (Some(&b2), Some(&b3)) if b2 >= 0xA0 => {
+ return Some((pos, decode_surrogate(b2, b3)));
+ }
+ _ => pos += 3,
+ }
+ } else if b < 0xF0 {
+ iter.next();
+ iter.next();
+ pos += 3;
+ } else {
+ iter.next();
+ iter.next();
+ iter.next();
+ pos += 4;
+ }
+ }
+ }
+
+ #[inline]
+ fn final_lead_surrogate(&self) -> Option<u16> {
+ match self.bytes {
+ [.., 0xED, b2 @ 0xA0..=0xAF, b3] => Some(decode_surrogate(b2, b3)),
+ _ => None,
+ }
+ }
+
+ #[inline]
+ fn initial_trail_surrogate(&self) -> Option<u16> {
+ match self.bytes {
+ [0xED, b2 @ 0xB0..=0xBF, b3, ..] => Some(decode_surrogate(b2, b3)),
+ _ => None,
+ }
+ }
+
+ pub fn clone_into(&self, buf: &mut Wtf8Buf) {
+ self.bytes.clone_into(&mut buf.bytes)
+ }
+
+ /// Boxes this `Wtf8`.
+ #[inline]
+ pub fn into_box(&self) -> Box<Wtf8> {
+ let boxed: Box<[u8]> = self.bytes.into();
+ unsafe { mem::transmute(boxed) }
+ }
+
+ /// Creates a boxed, empty `Wtf8`.
+ pub fn empty_box() -> Box<Wtf8> {
+ let boxed: Box<[u8]> = Default::default();
+ unsafe { mem::transmute(boxed) }
+ }
+
+ #[inline]
+ pub fn into_arc(&self) -> Arc<Wtf8> {
+ let arc: Arc<[u8]> = Arc::from(&self.bytes);
+ unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Wtf8) }
+ }
+
+ #[inline]
+ pub fn into_rc(&self) -> Rc<Wtf8> {
+ let rc: Rc<[u8]> = Rc::from(&self.bytes);
+ unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Wtf8) }
+ }
+
+ #[inline]
+ pub fn make_ascii_lowercase(&mut self) {
+ self.bytes.make_ascii_lowercase()
+ }
+
+ #[inline]
+ pub fn make_ascii_uppercase(&mut self) {
+ self.bytes.make_ascii_uppercase()
+ }
+
+ #[inline]
+ pub fn to_ascii_lowercase(&self) -> Wtf8Buf {
+ Wtf8Buf { bytes: self.bytes.to_ascii_lowercase() }
+ }
+
+ #[inline]
+ pub fn to_ascii_uppercase(&self) -> Wtf8Buf {
+ Wtf8Buf { bytes: self.bytes.to_ascii_uppercase() }
+ }
+
+ #[inline]
+ pub fn is_ascii(&self) -> bool {
+ self.bytes.is_ascii()
+ }
+
+ #[inline]
+ pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
+ self.bytes.eq_ignore_ascii_case(&other.bytes)
+ }
+}
+
+/// Returns a slice of the given string for the byte range \[`begin`..`end`).
+///
+/// # Panics
+///
+/// Panics when `begin` and `end` do not point to code point boundaries,
+/// or point beyond the end of the string.
+impl ops::Index<ops::Range<usize>> for Wtf8 {
+ type Output = Wtf8;
+
+ #[inline]
+ fn index(&self, range: ops::Range<usize>) -> &Wtf8 {
+ // is_code_point_boundary checks that the index is in [0, .len()]
+ if range.start <= range.end
+ && is_code_point_boundary(self, range.start)
+ && is_code_point_boundary(self, range.end)
+ {
+ unsafe { slice_unchecked(self, range.start, range.end) }
+ } else {
+ slice_error_fail(self, range.start, range.end)
+ }
+ }
+}
+
+/// Returns a slice of the given string from byte `begin` to its end.
+///
+/// # Panics
+///
+/// Panics when `begin` is not at a code point boundary,
+/// or is beyond the end of the string.
+impl ops::Index<ops::RangeFrom<usize>> for Wtf8 {
+ type Output = Wtf8;
+
+ #[inline]
+ fn index(&self, range: ops::RangeFrom<usize>) -> &Wtf8 {
+ // is_code_point_boundary checks that the index is in [0, .len()]
+ if is_code_point_boundary(self, range.start) {
+ unsafe { slice_unchecked(self, range.start, self.len()) }
+ } else {
+ slice_error_fail(self, range.start, self.len())
+ }
+ }
+}
+
+/// Returns a slice of the given string from its beginning to byte `end`.
+///
+/// # Panics
+///
+/// Panics when `end` is not at a code point boundary,
+/// or is beyond the end of the string.
+impl ops::Index<ops::RangeTo<usize>> for Wtf8 {
+ type Output = Wtf8;
+
+ #[inline]
+ fn index(&self, range: ops::RangeTo<usize>) -> &Wtf8 {
+ // is_code_point_boundary checks that the index is in [0, .len()]
+ if is_code_point_boundary(self, range.end) {
+ unsafe { slice_unchecked(self, 0, range.end) }
+ } else {
+ slice_error_fail(self, 0, range.end)
+ }
+ }
+}
+
+impl ops::Index<ops::RangeFull> for Wtf8 {
+ type Output = Wtf8;
+
+ #[inline]
+ fn index(&self, _range: ops::RangeFull) -> &Wtf8 {
+ self
+ }
+}
+
+#[inline]
+fn decode_surrogate(second_byte: u8, third_byte: u8) -> u16 {
+ // The first byte is assumed to be 0xED
+ 0xD800 | (second_byte as u16 & 0x3F) << 6 | third_byte as u16 & 0x3F
+}
+
+#[inline]
+fn decode_surrogate_pair(lead: u16, trail: u16) -> char {
+ let code_point = 0x10000 + ((((lead - 0xD800) as u32) << 10) | (trail - 0xDC00) as u32);
+ unsafe { char::from_u32_unchecked(code_point) }
+}
+
+/// Copied from core::str::StrPrelude::is_char_boundary
+#[inline]
+pub fn is_code_point_boundary(slice: &Wtf8, index: usize) -> bool {
+ if index == slice.len() {
+ return true;
+ }
+ match slice.bytes.get(index) {
+ None => false,
+ Some(&b) => b < 128 || b >= 192,
+ }
+}
+
+/// Copied from core::str::raw::slice_unchecked
+#[inline]
+pub unsafe fn slice_unchecked(s: &Wtf8, begin: usize, end: usize) -> &Wtf8 {
+ // memory layout of a &[u8] and &Wtf8 are the same
+ Wtf8::from_bytes_unchecked(slice::from_raw_parts(s.bytes.as_ptr().add(begin), end - begin))
+}
+
+/// Copied from core::str::raw::slice_error_fail
+#[inline(never)]
+pub fn slice_error_fail(s: &Wtf8, begin: usize, end: usize) -> ! {
+ assert!(begin <= end);
+ panic!("index {begin} and/or {end} in `{s:?}` do not lie on character boundary");
+}
+
+/// Iterator for the code points of a WTF-8 string.
+///
+/// Created with the method `.code_points()`.
+#[derive(Clone)]
+pub struct Wtf8CodePoints<'a> {
+ bytes: slice::Iter<'a, u8>,
+}
+
+impl<'a> Iterator for Wtf8CodePoints<'a> {
+ type Item = CodePoint;
+
+ #[inline]
+ fn next(&mut self) -> Option<CodePoint> {
+ // SAFETY: `self.bytes` has been created from a WTF-8 string
+ unsafe { next_code_point(&mut self.bytes).map(|c| CodePoint { value: c }) }
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let len = self.bytes.len();
+ (len.saturating_add(3) / 4, Some(len))
+ }
+}
+
+/// Generates a wide character sequence for potentially ill-formed UTF-16.
+#[stable(feature = "rust1", since = "1.0.0")]
+#[derive(Clone)]
+pub struct EncodeWide<'a> {
+ code_points: Wtf8CodePoints<'a>,
+ extra: u16,
+}
+
+// Copied from libunicode/u_str.rs
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'a> Iterator for EncodeWide<'a> {
+ type Item = u16;
+
+ #[inline]
+ fn next(&mut self) -> Option<u16> {
+ if self.extra != 0 {
+ let tmp = self.extra;
+ self.extra = 0;
+ return Some(tmp);
+ }
+
+ let mut buf = [0; 2];
+ self.code_points.next().map(|code_point| {
+ let n = char::encode_utf16_raw(code_point.value, &mut buf).len();
+ if n == 2 {
+ self.extra = buf[1];
+ }
+ buf[0]
+ })
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let (low, high) = self.code_points.size_hint();
+ let ext = (self.extra != 0) as usize;
+ // every code point gets either one u16 or two u16,
+ // so this iterator is between 1 or 2 times as
+ // long as the underlying iterator.
+ (low + ext, high.and_then(|n| n.checked_mul(2)).and_then(|n| n.checked_add(ext)))
+ }
+}
+
+#[stable(feature = "encode_wide_fused_iterator", since = "1.62.0")]
+impl FusedIterator for EncodeWide<'_> {}
+
+impl Hash for CodePoint {
+ #[inline]
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.value.hash(state)
+ }
+}
+
+impl Hash for Wtf8Buf {
+ #[inline]
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ state.write(&self.bytes);
+ 0xfeu8.hash(state)
+ }
+}
+
+impl Hash for Wtf8 {
+ #[inline]
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ state.write(&self.bytes);
+ 0xfeu8.hash(state)
+ }
+}
diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs
new file mode 100644
index 000000000..931996791
--- /dev/null
+++ b/library/std/src/sys_common/wtf8/tests.rs
@@ -0,0 +1,409 @@
+use super::*;
+use crate::borrow::Cow;
+
+#[test]
+fn code_point_from_u32() {
+ assert!(CodePoint::from_u32(0).is_some());
+ assert!(CodePoint::from_u32(0xD800).is_some());
+ assert!(CodePoint::from_u32(0x10FFFF).is_some());
+ assert!(CodePoint::from_u32(0x110000).is_none());
+}
+
+#[test]
+fn code_point_to_u32() {
+ fn c(value: u32) -> CodePoint {
+ CodePoint::from_u32(value).unwrap()
+ }
+ assert_eq!(c(0).to_u32(), 0);
+ assert_eq!(c(0xD800).to_u32(), 0xD800);
+ assert_eq!(c(0x10FFFF).to_u32(), 0x10FFFF);
+}
+
+#[test]
+fn code_point_from_char() {
+ assert_eq!(CodePoint::from_char('a').to_u32(), 0x61);
+ assert_eq!(CodePoint::from_char('💩').to_u32(), 0x1F4A9);
+}
+
+#[test]
+fn code_point_to_string() {
+ assert_eq!(format!("{:?}", CodePoint::from_char('a')), "U+0061");
+ assert_eq!(format!("{:?}", CodePoint::from_char('💩')), "U+1F4A9");
+}
+
+#[test]
+fn code_point_to_char() {
+ fn c(value: u32) -> CodePoint {
+ CodePoint::from_u32(value).unwrap()
+ }
+ assert_eq!(c(0x61).to_char(), Some('a'));
+ assert_eq!(c(0x1F4A9).to_char(), Some('💩'));
+ assert_eq!(c(0xD800).to_char(), None);
+}
+
+#[test]
+fn code_point_to_char_lossy() {
+ fn c(value: u32) -> CodePoint {
+ CodePoint::from_u32(value).unwrap()
+ }
+ assert_eq!(c(0x61).to_char_lossy(), 'a');
+ assert_eq!(c(0x1F4A9).to_char_lossy(), '💩');
+ assert_eq!(c(0xD800).to_char_lossy(), '\u{FFFD}');
+}
+
+#[test]
+fn wtf8buf_new() {
+ assert_eq!(Wtf8Buf::new().bytes, b"");
+}
+
+#[test]
+fn wtf8buf_from_str() {
+ assert_eq!(Wtf8Buf::from_str("").bytes, b"");
+ assert_eq!(Wtf8Buf::from_str("aé 💩").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+}
+
+#[test]
+fn wtf8buf_from_string() {
+ assert_eq!(Wtf8Buf::from_string(String::from("")).bytes, b"");
+ assert_eq!(Wtf8Buf::from_string(String::from("aé 💩")).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+}
+
+#[test]
+fn wtf8buf_from_wide() {
+ assert_eq!(Wtf8Buf::from_wide(&[]).bytes, b"");
+ assert_eq!(
+ Wtf8Buf::from_wide(&[0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]).bytes,
+ b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9"
+ );
+}
+
+#[test]
+fn wtf8buf_push_str() {
+ let mut string = Wtf8Buf::new();
+ assert_eq!(string.bytes, b"");
+ string.push_str("aé 💩");
+ assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+}
+
+#[test]
+fn wtf8buf_push_char() {
+ let mut string = Wtf8Buf::from_str("aé ");
+ assert_eq!(string.bytes, b"a\xC3\xA9 ");
+ string.push_char('💩');
+ assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+}
+
+#[test]
+fn wtf8buf_push() {
+ let mut string = Wtf8Buf::from_str("aé ");
+ assert_eq!(string.bytes, b"a\xC3\xA9 ");
+ string.push(CodePoint::from_char('💩'));
+ assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+
+ fn c(value: u32) -> CodePoint {
+ CodePoint::from_u32(value).unwrap()
+ }
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0xD83D)); // lead
+ string.push(c(0xDCA9)); // trail
+ assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); // Magic!
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0xD83D)); // lead
+ string.push(c(0x20)); // not surrogate
+ string.push(c(0xDCA9)); // trail
+ assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0xD800)); // lead
+ string.push(c(0xDBFF)); // lead
+ assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF");
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0xD800)); // lead
+ string.push(c(0xE000)); // not surrogate
+ assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80");
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0xD7FF)); // not surrogate
+ string.push(c(0xDC00)); // trail
+ assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80");
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0x61)); // not surrogate, < 3 bytes
+ string.push(c(0xDC00)); // trail
+ assert_eq!(string.bytes, b"\x61\xED\xB0\x80");
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0xDC00)); // trail
+ assert_eq!(string.bytes, b"\xED\xB0\x80");
+}
+
+#[test]
+fn wtf8buf_push_wtf8() {
+ let mut string = Wtf8Buf::from_str("aé");
+ assert_eq!(string.bytes, b"a\xC3\xA9");
+ string.push_wtf8(Wtf8::from_str(" 💩"));
+ assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+
+ fn w(v: &[u8]) -> &Wtf8 {
+ unsafe { Wtf8::from_bytes_unchecked(v) }
+ }
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"\xED\xA0\xBD")); // lead
+ string.push_wtf8(w(b"\xED\xB2\xA9")); // trail
+ assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); // Magic!
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"\xED\xA0\xBD")); // lead
+ string.push_wtf8(w(b" ")); // not surrogate
+ string.push_wtf8(w(b"\xED\xB2\xA9")); // trail
+ assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"\xED\xA0\x80")); // lead
+ string.push_wtf8(w(b"\xED\xAF\xBF")); // lead
+ assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF");
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"\xED\xA0\x80")); // lead
+ string.push_wtf8(w(b"\xEE\x80\x80")); // not surrogate
+ assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80");
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"\xED\x9F\xBF")); // not surrogate
+ string.push_wtf8(w(b"\xED\xB0\x80")); // trail
+ assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80");
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"a")); // not surrogate, < 3 bytes
+ string.push_wtf8(w(b"\xED\xB0\x80")); // trail
+ assert_eq!(string.bytes, b"\x61\xED\xB0\x80");
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"\xED\xB0\x80")); // trail
+ assert_eq!(string.bytes, b"\xED\xB0\x80");
+}
+
+#[test]
+fn wtf8buf_truncate() {
+ let mut string = Wtf8Buf::from_str("aé");
+ string.truncate(1);
+ assert_eq!(string.bytes, b"a");
+}
+
+#[test]
+#[should_panic]
+fn wtf8buf_truncate_fail_code_point_boundary() {
+ let mut string = Wtf8Buf::from_str("aé");
+ string.truncate(2);
+}
+
+#[test]
+#[should_panic]
+fn wtf8buf_truncate_fail_longer() {
+ let mut string = Wtf8Buf::from_str("aé");
+ string.truncate(4);
+}
+
+#[test]
+fn wtf8buf_into_string() {
+ let mut string = Wtf8Buf::from_str("aé 💩");
+ assert_eq!(string.clone().into_string(), Ok(String::from("aé 💩")));
+ string.push(CodePoint::from_u32(0xD800).unwrap());
+ assert_eq!(string.clone().into_string(), Err(string));
+}
+
+#[test]
+fn wtf8buf_into_string_lossy() {
+ let mut string = Wtf8Buf::from_str("aé 💩");
+ assert_eq!(string.clone().into_string_lossy(), String::from("aé 💩"));
+ string.push(CodePoint::from_u32(0xD800).unwrap());
+ assert_eq!(string.clone().into_string_lossy(), String::from("aé 💩�"));
+}
+
+#[test]
+fn wtf8buf_from_iterator() {
+ fn f(values: &[u32]) -> Wtf8Buf {
+ values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::<Wtf8Buf>()
+ }
+ assert_eq!(f(&[0x61, 0xE9, 0x20, 0x1F4A9]).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+
+ assert_eq!(f(&[0xD83D, 0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic!
+ assert_eq!(f(&[0xD83D, 0x20, 0xDCA9]).bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
+ assert_eq!(f(&[0xD800, 0xDBFF]).bytes, b"\xED\xA0\x80\xED\xAF\xBF");
+ assert_eq!(f(&[0xD800, 0xE000]).bytes, b"\xED\xA0\x80\xEE\x80\x80");
+ assert_eq!(f(&[0xD7FF, 0xDC00]).bytes, b"\xED\x9F\xBF\xED\xB0\x80");
+ assert_eq!(f(&[0x61, 0xDC00]).bytes, b"\x61\xED\xB0\x80");
+ assert_eq!(f(&[0xDC00]).bytes, b"\xED\xB0\x80");
+}
+
+#[test]
+fn wtf8buf_extend() {
+ fn e(initial: &[u32], extended: &[u32]) -> Wtf8Buf {
+ fn c(value: &u32) -> CodePoint {
+ CodePoint::from_u32(*value).unwrap()
+ }
+ let mut string = initial.iter().map(c).collect::<Wtf8Buf>();
+ string.extend(extended.iter().map(c));
+ string
+ }
+
+ assert_eq!(e(&[0x61, 0xE9], &[0x20, 0x1F4A9]).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+
+ assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic!
+ assert_eq!(e(&[0xD83D, 0x20], &[0xDCA9]).bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
+ assert_eq!(e(&[0xD800], &[0xDBFF]).bytes, b"\xED\xA0\x80\xED\xAF\xBF");
+ assert_eq!(e(&[0xD800], &[0xE000]).bytes, b"\xED\xA0\x80\xEE\x80\x80");
+ assert_eq!(e(&[0xD7FF], &[0xDC00]).bytes, b"\xED\x9F\xBF\xED\xB0\x80");
+ assert_eq!(e(&[0x61], &[0xDC00]).bytes, b"\x61\xED\xB0\x80");
+ assert_eq!(e(&[], &[0xDC00]).bytes, b"\xED\xB0\x80");
+}
+
+#[test]
+fn wtf8buf_show() {
+ let mut string = Wtf8Buf::from_str("a\té \u{7f}💩\r");
+ string.push(CodePoint::from_u32(0xD800).unwrap());
+ assert_eq!(format!("{string:?}"), "\"a\\té \\u{7f}\u{1f4a9}\\r\\u{d800}\"");
+}
+
+#[test]
+fn wtf8buf_as_slice() {
+ assert_eq!(Wtf8Buf::from_str("aé").as_slice(), Wtf8::from_str("aé"));
+}
+
+#[test]
+fn wtf8buf_show_str() {
+ let text = "a\té 💩\r";
+ let string = Wtf8Buf::from_str(text);
+ assert_eq!(format!("{text:?}"), format!("{string:?}"));
+}
+
+#[test]
+fn wtf8_from_str() {
+ assert_eq!(&Wtf8::from_str("").bytes, b"");
+ assert_eq!(&Wtf8::from_str("aé 💩").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+}
+
+#[test]
+fn wtf8_len() {
+ assert_eq!(Wtf8::from_str("").len(), 0);
+ assert_eq!(Wtf8::from_str("aé 💩").len(), 8);
+}
+
+#[test]
+fn wtf8_slice() {
+ assert_eq!(&Wtf8::from_str("aé 💩")[1..4].bytes, b"\xC3\xA9 ");
+}
+
+#[test]
+#[should_panic]
+fn wtf8_slice_not_code_point_boundary() {
+ let _ = &Wtf8::from_str("aé 💩")[2..4];
+}
+
+#[test]
+fn wtf8_slice_from() {
+ assert_eq!(&Wtf8::from_str("aé 💩")[1..].bytes, b"\xC3\xA9 \xF0\x9F\x92\xA9");
+}
+
+#[test]
+#[should_panic]
+fn wtf8_slice_from_not_code_point_boundary() {
+ let _ = &Wtf8::from_str("aé 💩")[2..];
+}
+
+#[test]
+fn wtf8_slice_to() {
+ assert_eq!(&Wtf8::from_str("aé 💩")[..4].bytes, b"a\xC3\xA9 ");
+}
+
+#[test]
+#[should_panic]
+fn wtf8_slice_to_not_code_point_boundary() {
+ let _ = &Wtf8::from_str("aé 💩")[5..];
+}
+
+#[test]
+fn wtf8_ascii_byte_at() {
+ let slice = Wtf8::from_str("aé 💩");
+ assert_eq!(slice.ascii_byte_at(0), b'a');
+ assert_eq!(slice.ascii_byte_at(1), b'\xFF');
+ assert_eq!(slice.ascii_byte_at(2), b'\xFF');
+ assert_eq!(slice.ascii_byte_at(3), b' ');
+ assert_eq!(slice.ascii_byte_at(4), b'\xFF');
+}
+
+#[test]
+fn wtf8_code_points() {
+ fn c(value: u32) -> CodePoint {
+ CodePoint::from_u32(value).unwrap()
+ }
+ fn cp(string: &Wtf8Buf) -> Vec<Option<char>> {
+ string.code_points().map(|c| c.to_char()).collect::<Vec<_>>()
+ }
+ let mut string = Wtf8Buf::from_str("é ");
+ assert_eq!(cp(&string), [Some('é'), Some(' ')]);
+ string.push(c(0xD83D));
+ assert_eq!(cp(&string), [Some('é'), Some(' '), None]);
+ string.push(c(0xDCA9));
+ assert_eq!(cp(&string), [Some('é'), Some(' '), Some('💩')]);
+}
+
+#[test]
+fn wtf8_as_str() {
+ assert_eq!(Wtf8::from_str("").as_str(), Some(""));
+ assert_eq!(Wtf8::from_str("aé 💩").as_str(), Some("aé 💩"));
+ let mut string = Wtf8Buf::new();
+ string.push(CodePoint::from_u32(0xD800).unwrap());
+ assert_eq!(string.as_str(), None);
+}
+
+#[test]
+fn wtf8_to_string_lossy() {
+ assert_eq!(Wtf8::from_str("").to_string_lossy(), Cow::Borrowed(""));
+ assert_eq!(Wtf8::from_str("aé 💩").to_string_lossy(), Cow::Borrowed("aé 💩"));
+ let mut string = Wtf8Buf::from_str("aé 💩");
+ string.push(CodePoint::from_u32(0xD800).unwrap());
+ let expected: Cow<'_, str> = Cow::Owned(String::from("aé 💩�"));
+ assert_eq!(string.to_string_lossy(), expected);
+}
+
+#[test]
+fn wtf8_display() {
+ fn d(b: &[u8]) -> String {
+ (&unsafe { Wtf8::from_bytes_unchecked(b) }).to_string()
+ }
+
+ assert_eq!("", d("".as_bytes()));
+ assert_eq!("aé 💩", d("aé 💩".as_bytes()));
+
+ let mut string = Wtf8Buf::from_str("aé 💩");
+ string.push(CodePoint::from_u32(0xD800).unwrap());
+ assert_eq!("aé 💩�", d(string.as_inner()));
+}
+
+#[test]
+fn wtf8_encode_wide() {
+ let mut string = Wtf8Buf::from_str("aé ");
+ string.push(CodePoint::from_u32(0xD83D).unwrap());
+ string.push_char('💩');
+ assert_eq!(
+ string.encode_wide().collect::<Vec<_>>(),
+ vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]
+ );
+}
+
+#[test]
+fn wtf8_encode_wide_size_hint() {
+ let string = Wtf8Buf::from_str("\u{12345}");
+ let mut iter = string.encode_wide();
+ assert_eq!((1, Some(8)), iter.size_hint());
+ iter.next().unwrap();
+ assert_eq!((1, Some(1)), iter.size_hint());
+ iter.next().unwrap();
+ assert_eq!((0, Some(0)), iter.size_hint());
+ assert!(iter.next().is_none());
+}