From 99128b7e45f8b95d962da2e6ea584767f0c85455 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 11 Jan 2024 20:10:25 +0100 Subject: std: begin moving platform support modules into `pal` --- library/std/src/sys/common/alloc.rs | 58 - library/std/src/sys/common/mod.rs | 19 - library/std/src/sys/common/small_c_string.rs | 58 - library/std/src/sys/common/tests.rs | 66 - .../std/src/sys/common/thread_local/fast_local.rs | 245 -- library/std/src/sys/common/thread_local/mod.rs | 124 - .../std/src/sys/common/thread_local/os_local.rs | 185 - .../src/sys/common/thread_local/static_local.rs | 107 - library/std/src/sys/hermit/alloc.rs | 31 - library/std/src/sys/hermit/args.rs | 70 - library/std/src/sys/hermit/env.rs | 9 - library/std/src/sys/hermit/fd.rs | 101 - library/std/src/sys/hermit/fs.rs | 468 --- library/std/src/sys/hermit/futex.rs | 39 - library/std/src/sys/hermit/memchr.rs | 1 - library/std/src/sys/hermit/mod.rs | 207 - library/std/src/sys/hermit/net.rs | 372 -- library/std/src/sys/hermit/os.rs | 206 - library/std/src/sys/hermit/stdio.rs | 120 - library/std/src/sys/hermit/thread.rs | 112 - library/std/src/sys/hermit/thread_local_dtor.rs | 29 - library/std/src/sys/hermit/time.rs | 216 - library/std/src/sys/itron/abi.rs | 197 - library/std/src/sys/itron/condvar.rs | 292 -- library/std/src/sys/itron/error.rs | 164 - library/std/src/sys/itron/mutex.rs | 85 - library/std/src/sys/itron/spin.rs | 163 - library/std/src/sys/itron/task.rs | 44 - library/std/src/sys/itron/thread.rs | 368 -- library/std/src/sys/itron/thread_parking.rs | 37 - library/std/src/sys/itron/time.rs | 114 - library/std/src/sys/itron/time/tests.rs | 33 - library/std/src/sys/mod.rs | 132 +- library/std/src/sys/pal/common/alloc.rs | 58 + library/std/src/sys/pal/common/mod.rs | 19 + library/std/src/sys/pal/common/small_c_string.rs | 58 + library/std/src/sys/pal/common/tests.rs | 66 + .../src/sys/pal/common/thread_local/fast_local.rs | 245 ++ library/std/src/sys/pal/common/thread_local/mod.rs | 124 + .../src/sys/pal/common/thread_local/os_local.rs | 185 + .../sys/pal/common/thread_local/static_local.rs | 107 + library/std/src/sys/pal/hermit/alloc.rs | 31 + library/std/src/sys/pal/hermit/args.rs | 70 + library/std/src/sys/pal/hermit/env.rs | 9 + library/std/src/sys/pal/hermit/fd.rs | 101 + library/std/src/sys/pal/hermit/fs.rs | 468 +++ library/std/src/sys/pal/hermit/futex.rs | 39 + library/std/src/sys/pal/hermit/memchr.rs | 1 + library/std/src/sys/pal/hermit/mod.rs | 207 + library/std/src/sys/pal/hermit/net.rs | 372 ++ library/std/src/sys/pal/hermit/os.rs | 206 + library/std/src/sys/pal/hermit/stdio.rs | 120 + library/std/src/sys/pal/hermit/thread.rs | 112 + .../std/src/sys/pal/hermit/thread_local_dtor.rs | 29 + library/std/src/sys/pal/hermit/time.rs | 216 + library/std/src/sys/pal/itron/abi.rs | 197 + library/std/src/sys/pal/itron/condvar.rs | 292 ++ library/std/src/sys/pal/itron/error.rs | 164 + library/std/src/sys/pal/itron/mutex.rs | 85 + library/std/src/sys/pal/itron/spin.rs | 163 + library/std/src/sys/pal/itron/task.rs | 44 + library/std/src/sys/pal/itron/thread.rs | 368 ++ library/std/src/sys/pal/itron/thread_parking.rs | 37 + library/std/src/sys/pal/itron/time.rs | 114 + library/std/src/sys/pal/itron/time/tests.rs | 33 + library/std/src/sys/pal/mod.rs | 124 + library/std/src/sys/pal/personality/dwarf/eh.rs | 257 ++ library/std/src/sys/pal/personality/dwarf/mod.rs | 73 + library/std/src/sys/pal/personality/dwarf/tests.rs | 19 + library/std/src/sys/pal/personality/emcc.rs | 20 + library/std/src/sys/pal/personality/gcc.rs | 293 ++ library/std/src/sys/pal/personality/mod.rs | 47 + library/std/src/sys/pal/sgx/abi/entry.S | 376 ++ library/std/src/sys/pal/sgx/abi/mem.rs | 93 + library/std/src/sys/pal/sgx/abi/mod.rs | 108 + library/std/src/sys/pal/sgx/abi/panic.rs | 42 + library/std/src/sys/pal/sgx/abi/reloc.rs | 32 + library/std/src/sys/pal/sgx/abi/thread.rs | 17 + library/std/src/sys/pal/sgx/abi/tls/mod.rs | 133 + library/std/src/sys/pal/sgx/abi/tls/sync_bitset.rs | 85 + .../src/sys/pal/sgx/abi/tls/sync_bitset/tests.rs | 25 + library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs | 819 ++++ library/std/src/sys/pal/sgx/abi/usercalls/mod.rs | 329 ++ library/std/src/sys/pal/sgx/abi/usercalls/raw.rs | 269 ++ library/std/src/sys/pal/sgx/abi/usercalls/tests.rs | 56 + library/std/src/sys/pal/sgx/alloc.rs | 98 + library/std/src/sys/pal/sgx/args.rs | 59 + library/std/src/sys/pal/sgx/condvar.rs | 46 + library/std/src/sys/pal/sgx/env.rs | 9 + library/std/src/sys/pal/sgx/fd.rs | 89 + library/std/src/sys/pal/sgx/memchr.rs | 1 + library/std/src/sys/pal/sgx/mod.rs | 175 + library/std/src/sys/pal/sgx/mutex.rs | 59 + library/std/src/sys/pal/sgx/net.rs | 548 +++ library/std/src/sys/pal/sgx/os.rs | 187 + library/std/src/sys/pal/sgx/path.rs | 25 + library/std/src/sys/pal/sgx/rwlock.rs | 222 + library/std/src/sys/pal/sgx/rwlock/tests.rs | 21 + library/std/src/sys/pal/sgx/stdio.rs | 88 + library/std/src/sys/pal/sgx/thread.rs | 157 + library/std/src/sys/pal/sgx/thread_local_key.rs | 23 + library/std/src/sys/pal/sgx/thread_parking.rs | 23 + library/std/src/sys/pal/sgx/time.rs | 46 + library/std/src/sys/pal/sgx/waitqueue/mod.rs | 262 ++ .../std/src/sys/pal/sgx/waitqueue/spin_mutex.rs | 80 + .../src/sys/pal/sgx/waitqueue/spin_mutex/tests.rs | 23 + library/std/src/sys/pal/sgx/waitqueue/tests.rs | 20 + .../std/src/sys/pal/sgx/waitqueue/unsafe_list.rs | 156 + .../src/sys/pal/sgx/waitqueue/unsafe_list/tests.rs | 105 + library/std/src/sys/pal/solid/abi/fs.rs | 53 + library/std/src/sys/pal/solid/abi/mod.rs | 65 + library/std/src/sys/pal/solid/abi/sockets.rs | 277 ++ library/std/src/sys/pal/solid/alloc.rs | 32 + library/std/src/sys/pal/solid/env.rs | 9 + library/std/src/sys/pal/solid/error.rs | 55 + library/std/src/sys/pal/solid/fs.rs | 588 +++ library/std/src/sys/pal/solid/io.rs | 81 + library/std/src/sys/pal/solid/memchr.rs | 21 + library/std/src/sys/pal/solid/mod.rs | 97 + library/std/src/sys/pal/solid/net.rs | 435 ++ library/std/src/sys/pal/solid/os.rs | 228 + library/std/src/sys/pal/solid/path.rs | 25 + library/std/src/sys/pal/solid/rwlock.rs | 93 + library/std/src/sys/pal/solid/stdio.rs | 80 + library/std/src/sys/pal/solid/thread_local_dtor.rs | 43 + library/std/src/sys/pal/solid/thread_local_key.rs | 21 + library/std/src/sys/pal/solid/time.rs | 56 + library/std/src/sys/pal/teeos/alloc.rs | 57 + library/std/src/sys/pal/teeos/locks/condvar.rs | 100 + library/std/src/sys/pal/teeos/locks/mod.rs | 8 + library/std/src/sys/pal/teeos/locks/rwlock.rs | 44 + library/std/src/sys/pal/teeos/mod.rs | 167 + library/std/src/sys/pal/teeos/net.rs | 372 ++ library/std/src/sys/pal/teeos/os.rs | 134 + library/std/src/sys/pal/teeos/rand.rs | 21 + library/std/src/sys/pal/teeos/stdio.rs | 88 + library/std/src/sys/pal/teeos/thread.rs | 164 + library/std/src/sys/pal/teeos/thread_local_dtor.rs | 4 + library/std/src/sys/pal/uefi/alloc.rs | 49 + library/std/src/sys/pal/uefi/args.rs | 158 + library/std/src/sys/pal/uefi/env.rs | 9 + library/std/src/sys/pal/uefi/helpers.rs | 148 + library/std/src/sys/pal/uefi/mod.rs | 242 ++ library/std/src/sys/pal/uefi/os.rs | 237 ++ library/std/src/sys/pal/uefi/path.rs | 25 + library/std/src/sys/pal/uefi/stdio.rs | 162 + library/std/src/sys/pal/uefi/tests.rs | 21 + library/std/src/sys/pal/unix/alloc.rs | 106 + library/std/src/sys/pal/unix/android.rs | 81 + library/std/src/sys/pal/unix/args.rs | 282 ++ library/std/src/sys/pal/unix/cmath.rs | 37 + library/std/src/sys/pal/unix/env.rs | 263 ++ library/std/src/sys/pal/unix/fd.rs | 557 +++ library/std/src/sys/pal/unix/fd/tests.rs | 10 + library/std/src/sys/pal/unix/fs.rs | 2162 ++++++++++ library/std/src/sys/pal/unix/futex.rs | 301 ++ library/std/src/sys/pal/unix/io.rs | 82 + library/std/src/sys/pal/unix/kernel_copy.rs | 730 ++++ library/std/src/sys/pal/unix/kernel_copy/tests.rs | 312 ++ library/std/src/sys/pal/unix/l4re.rs | 560 +++ .../std/src/sys/pal/unix/locks/fuchsia_mutex.rs | 164 + .../std/src/sys/pal/unix/locks/futex_condvar.rs | 56 + library/std/src/sys/pal/unix/locks/futex_mutex.rs | 96 + library/std/src/sys/pal/unix/locks/futex_rwlock.rs | 320 ++ library/std/src/sys/pal/unix/locks/mod.rs | 31 + .../std/src/sys/pal/unix/locks/pthread_condvar.rs | 206 + .../std/src/sys/pal/unix/locks/pthread_mutex.rs | 131 + .../std/src/sys/pal/unix/locks/pthread_rwlock.rs | 195 + library/std/src/sys/pal/unix/memchr.rs | 40 + library/std/src/sys/pal/unix/mod.rs | 443 ++ library/std/src/sys/pal/unix/net.rs | 591 +++ library/std/src/sys/pal/unix/os.rs | 783 ++++ library/std/src/sys/pal/unix/os/tests.rs | 23 + library/std/src/sys/pal/unix/os_str.rs | 288 ++ library/std/src/sys/pal/unix/os_str/tests.rs | 17 + library/std/src/sys/pal/unix/path.rs | 63 + library/std/src/sys/pal/unix/pipe.rs | 167 + library/std/src/sys/pal/unix/process/mod.rs | 27 + .../std/src/sys/pal/unix/process/process_common.rs | 676 +++ .../sys/pal/unix/process/process_common/tests.rs | 194 + .../src/sys/pal/unix/process/process_fuchsia.rs | 330 ++ .../std/src/sys/pal/unix/process/process_unix.rs | 1141 +++++ .../src/sys/pal/unix/process/process_unix/tests.rs | 100 + .../sys/pal/unix/process/process_unsupported.rs | 74 + .../process/process_unsupported/wait_status.rs | 84 + .../process_unsupported/wait_status/tests.rs | 36 + .../src/sys/pal/unix/process/process_vxworks.rs | 264 ++ library/std/src/sys/pal/unix/process/zircon.rs | 309 ++ library/std/src/sys/pal/unix/rand.rs | 301 ++ library/std/src/sys/pal/unix/stack_overflow.rs | 223 + library/std/src/sys/pal/unix/stdio.rs | 99 + library/std/src/sys/pal/unix/thread.rs | 992 +++++ library/std/src/sys/pal/unix/thread_local_dtor.rs | 124 + library/std/src/sys/pal/unix/thread_local_key.rs | 29 + .../std/src/sys/pal/unix/thread_parking/darwin.rs | 130 + library/std/src/sys/pal/unix/thread_parking/mod.rs | 32 + .../std/src/sys/pal/unix/thread_parking/netbsd.rs | 52 + .../std/src/sys/pal/unix/thread_parking/pthread.rs | 284 ++ library/std/src/sys/pal/unix/time.rs | 330 ++ library/std/src/sys/pal/unix/weak.rs | 195 + library/std/src/sys/pal/unsupported/alloc.rs | 23 + library/std/src/sys/pal/unsupported/args.rs | 36 + library/std/src/sys/pal/unsupported/common.rs | 40 + library/std/src/sys/pal/unsupported/env.rs | 9 + library/std/src/sys/pal/unsupported/fs.rs | 324 ++ library/std/src/sys/pal/unsupported/io.rs | 51 + .../std/src/sys/pal/unsupported/locks/condvar.rs | 26 + library/std/src/sys/pal/unsupported/locks/mod.rs | 6 + library/std/src/sys/pal/unsupported/locks/mutex.rs | 32 + .../std/src/sys/pal/unsupported/locks/rwlock.rs | 65 + library/std/src/sys/pal/unsupported/mod.rs | 29 + library/std/src/sys/pal/unsupported/net.rs | 370 ++ library/std/src/sys/pal/unsupported/once.rs | 100 + library/std/src/sys/pal/unsupported/os.rs | 121 + library/std/src/sys/pal/unsupported/pipe.rs | 45 + library/std/src/sys/pal/unsupported/process.rs | 239 ++ library/std/src/sys/pal/unsupported/stdio.rs | 59 + library/std/src/sys/pal/unsupported/thread.rs | 46 + .../src/sys/pal/unsupported/thread_local_dtor.rs | 10 + .../src/sys/pal/unsupported/thread_local_key.rs | 21 + .../std/src/sys/pal/unsupported/thread_parking.rs | 11 + library/std/src/sys/pal/unsupported/time.rs | 45 + library/std/src/sys/pal/wasi/args.rs | 62 + library/std/src/sys/pal/wasi/env.rs | 9 + library/std/src/sys/pal/wasi/fd.rs | 332 ++ library/std/src/sys/pal/wasi/fs.rs | 810 ++++ library/std/src/sys/pal/wasi/io.rs | 79 + library/std/src/sys/pal/wasi/mod.rs | 198 + library/std/src/sys/pal/wasi/net.rs | 544 +++ library/std/src/sys/pal/wasi/os.rs | 301 ++ library/std/src/sys/pal/wasi/stdio.rs | 112 + library/std/src/sys/pal/wasi/thread.rs | 201 + library/std/src/sys/pal/wasi/time.rs | 65 + library/std/src/sys/pal/wasm/alloc.rs | 166 + library/std/src/sys/pal/wasm/atomics/futex.rs | 34 + library/std/src/sys/pal/wasm/atomics/thread.rs | 55 + library/std/src/sys/pal/wasm/env.rs | 9 + library/std/src/sys/pal/wasm/mod.rs | 81 + library/std/src/sys/pal/windows/alloc.rs | 247 ++ library/std/src/sys/pal/windows/alloc/tests.rs | 9 + library/std/src/sys/pal/windows/api.rs | 157 + library/std/src/sys/pal/windows/args.rs | 379 ++ library/std/src/sys/pal/windows/args/tests.rs | 91 + library/std/src/sys/pal/windows/c.rs | 480 +++ library/std/src/sys/pal/windows/c/windows_sys.lst | 2596 ++++++++++++ library/std/src/sys/pal/windows/c/windows_sys.rs | 4353 ++++++++++++++++++++ library/std/src/sys/pal/windows/cmath.rs | 96 + library/std/src/sys/pal/windows/compat.rs | 244 ++ library/std/src/sys/pal/windows/env.rs | 9 + library/std/src/sys/pal/windows/fs.rs | 1528 +++++++ library/std/src/sys/pal/windows/handle.rs | 338 ++ library/std/src/sys/pal/windows/handle/tests.rs | 22 + library/std/src/sys/pal/windows/io.rs | 161 + library/std/src/sys/pal/windows/locks/condvar.rs | 50 + library/std/src/sys/pal/windows/locks/mod.rs | 6 + library/std/src/sys/pal/windows/locks/mutex.rs | 54 + library/std/src/sys/pal/windows/locks/rwlock.rs | 40 + library/std/src/sys/pal/windows/memchr.rs | 5 + library/std/src/sys/pal/windows/mod.rs | 357 ++ library/std/src/sys/pal/windows/net.rs | 497 +++ library/std/src/sys/pal/windows/os.rs | 368 ++ library/std/src/sys/pal/windows/os/tests.rs | 13 + library/std/src/sys/pal/windows/os_str.rs | 245 ++ library/std/src/sys/pal/windows/path.rs | 344 ++ library/std/src/sys/pal/windows/path/tests.rs | 137 + library/std/src/sys/pal/windows/pipe.rs | 571 +++ library/std/src/sys/pal/windows/process.rs | 984 +++++ library/std/src/sys/pal/windows/process/tests.rs | 222 + library/std/src/sys/pal/windows/rand.rs | 42 + library/std/src/sys/pal/windows/stack_overflow.rs | 44 + .../std/src/sys/pal/windows/stack_overflow_uwp.rs | 11 + library/std/src/sys/pal/windows/stdio.rs | 462 +++ library/std/src/sys/pal/windows/stdio/tests.rs | 6 + library/std/src/sys/pal/windows/thread.rs | 137 + .../std/src/sys/pal/windows/thread_local_dtor.rs | 7 + .../std/src/sys/pal/windows/thread_local_key.rs | 341 ++ .../src/sys/pal/windows/thread_local_key/tests.rs | 57 + library/std/src/sys/pal/windows/thread_parking.rs | 253 ++ library/std/src/sys/pal/windows/time.rs | 262 ++ library/std/src/sys/pal/xous/alloc.rs | 62 + library/std/src/sys/pal/xous/locks/condvar.rs | 111 + library/std/src/sys/pal/xous/locks/mod.rs | 7 + library/std/src/sys/pal/xous/locks/mutex.rs | 116 + library/std/src/sys/pal/xous/locks/rwlock.rs | 72 + library/std/src/sys/pal/xous/mod.rs | 36 + library/std/src/sys/pal/xous/os.rs | 174 + library/std/src/sys/pal/xous/stdio.rs | 131 + library/std/src/sys/pal/xous/thread.rs | 144 + library/std/src/sys/pal/xous/thread_local_key.rs | 190 + library/std/src/sys/pal/xous/thread_parking.rs | 94 + library/std/src/sys/pal/xous/time.rs | 57 + library/std/src/sys/personality/dwarf/eh.rs | 257 -- library/std/src/sys/personality/dwarf/mod.rs | 73 - library/std/src/sys/personality/dwarf/tests.rs | 19 - library/std/src/sys/personality/emcc.rs | 20 - library/std/src/sys/personality/gcc.rs | 293 -- library/std/src/sys/personality/mod.rs | 47 - library/std/src/sys/sgx/abi/entry.S | 376 -- library/std/src/sys/sgx/abi/mem.rs | 93 - library/std/src/sys/sgx/abi/mod.rs | 108 - library/std/src/sys/sgx/abi/panic.rs | 42 - library/std/src/sys/sgx/abi/reloc.rs | 32 - library/std/src/sys/sgx/abi/thread.rs | 17 - library/std/src/sys/sgx/abi/tls/mod.rs | 133 - library/std/src/sys/sgx/abi/tls/sync_bitset.rs | 85 - .../std/src/sys/sgx/abi/tls/sync_bitset/tests.rs | 25 - library/std/src/sys/sgx/abi/usercalls/alloc.rs | 819 ---- library/std/src/sys/sgx/abi/usercalls/mod.rs | 329 -- library/std/src/sys/sgx/abi/usercalls/raw.rs | 269 -- library/std/src/sys/sgx/abi/usercalls/tests.rs | 56 - library/std/src/sys/sgx/alloc.rs | 98 - library/std/src/sys/sgx/args.rs | 59 - library/std/src/sys/sgx/condvar.rs | 46 - library/std/src/sys/sgx/env.rs | 9 - library/std/src/sys/sgx/fd.rs | 89 - library/std/src/sys/sgx/memchr.rs | 1 - library/std/src/sys/sgx/mod.rs | 175 - library/std/src/sys/sgx/mutex.rs | 59 - library/std/src/sys/sgx/net.rs | 548 --- library/std/src/sys/sgx/os.rs | 187 - library/std/src/sys/sgx/path.rs | 25 - library/std/src/sys/sgx/rwlock.rs | 222 - library/std/src/sys/sgx/rwlock/tests.rs | 21 - library/std/src/sys/sgx/stdio.rs | 88 - library/std/src/sys/sgx/thread.rs | 157 - library/std/src/sys/sgx/thread_local_key.rs | 23 - library/std/src/sys/sgx/thread_parking.rs | 23 - library/std/src/sys/sgx/time.rs | 46 - library/std/src/sys/sgx/waitqueue/mod.rs | 262 -- library/std/src/sys/sgx/waitqueue/spin_mutex.rs | 80 - .../std/src/sys/sgx/waitqueue/spin_mutex/tests.rs | 23 - library/std/src/sys/sgx/waitqueue/tests.rs | 20 - library/std/src/sys/sgx/waitqueue/unsafe_list.rs | 156 - .../std/src/sys/sgx/waitqueue/unsafe_list/tests.rs | 105 - library/std/src/sys/solid/abi/fs.rs | 53 - library/std/src/sys/solid/abi/mod.rs | 65 - library/std/src/sys/solid/abi/sockets.rs | 277 -- library/std/src/sys/solid/alloc.rs | 32 - library/std/src/sys/solid/env.rs | 9 - library/std/src/sys/solid/error.rs | 55 - library/std/src/sys/solid/fs.rs | 588 --- library/std/src/sys/solid/io.rs | 81 - library/std/src/sys/solid/memchr.rs | 21 - library/std/src/sys/solid/mod.rs | 97 - library/std/src/sys/solid/net.rs | 435 -- library/std/src/sys/solid/os.rs | 228 - library/std/src/sys/solid/path.rs | 25 - library/std/src/sys/solid/rwlock.rs | 93 - library/std/src/sys/solid/stdio.rs | 80 - library/std/src/sys/solid/thread_local_dtor.rs | 43 - library/std/src/sys/solid/thread_local_key.rs | 21 - library/std/src/sys/solid/time.rs | 56 - library/std/src/sys/teeos/alloc.rs | 57 - library/std/src/sys/teeos/locks/condvar.rs | 100 - library/std/src/sys/teeos/locks/mod.rs | 8 - library/std/src/sys/teeos/locks/rwlock.rs | 44 - library/std/src/sys/teeos/mod.rs | 167 - library/std/src/sys/teeos/net.rs | 372 -- library/std/src/sys/teeos/os.rs | 134 - library/std/src/sys/teeos/rand.rs | 21 - library/std/src/sys/teeos/stdio.rs | 88 - library/std/src/sys/teeos/thread.rs | 164 - library/std/src/sys/teeos/thread_local_dtor.rs | 4 - library/std/src/sys/uefi/alloc.rs | 49 - library/std/src/sys/uefi/args.rs | 158 - library/std/src/sys/uefi/env.rs | 9 - library/std/src/sys/uefi/helpers.rs | 148 - library/std/src/sys/uefi/mod.rs | 242 -- library/std/src/sys/uefi/os.rs | 237 -- library/std/src/sys/uefi/path.rs | 25 - library/std/src/sys/uefi/stdio.rs | 162 - library/std/src/sys/uefi/tests.rs | 21 - library/std/src/sys/unix/alloc.rs | 106 - library/std/src/sys/unix/android.rs | 81 - library/std/src/sys/unix/args.rs | 282 -- library/std/src/sys/unix/cmath.rs | 37 - library/std/src/sys/unix/env.rs | 263 -- library/std/src/sys/unix/fd.rs | 557 --- library/std/src/sys/unix/fd/tests.rs | 10 - library/std/src/sys/unix/fs.rs | 2162 ---------- library/std/src/sys/unix/futex.rs | 301 -- library/std/src/sys/unix/io.rs | 82 - library/std/src/sys/unix/kernel_copy.rs | 730 ---- library/std/src/sys/unix/kernel_copy/tests.rs | 312 -- library/std/src/sys/unix/l4re.rs | 560 --- library/std/src/sys/unix/locks/fuchsia_mutex.rs | 164 - library/std/src/sys/unix/locks/futex_condvar.rs | 56 - library/std/src/sys/unix/locks/futex_mutex.rs | 96 - library/std/src/sys/unix/locks/futex_rwlock.rs | 320 -- library/std/src/sys/unix/locks/mod.rs | 31 - library/std/src/sys/unix/locks/pthread_condvar.rs | 206 - library/std/src/sys/unix/locks/pthread_mutex.rs | 131 - library/std/src/sys/unix/locks/pthread_rwlock.rs | 195 - library/std/src/sys/unix/memchr.rs | 40 - library/std/src/sys/unix/mod.rs | 443 -- library/std/src/sys/unix/net.rs | 591 --- library/std/src/sys/unix/os.rs | 783 ---- library/std/src/sys/unix/os/tests.rs | 23 - library/std/src/sys/unix/os_str.rs | 288 -- library/std/src/sys/unix/os_str/tests.rs | 17 - library/std/src/sys/unix/path.rs | 63 - library/std/src/sys/unix/pipe.rs | 167 - library/std/src/sys/unix/process/mod.rs | 27 - library/std/src/sys/unix/process/process_common.rs | 676 --- .../src/sys/unix/process/process_common/tests.rs | 194 - .../std/src/sys/unix/process/process_fuchsia.rs | 330 -- library/std/src/sys/unix/process/process_unix.rs | 1141 ----- .../std/src/sys/unix/process/process_unix/tests.rs | 100 - .../src/sys/unix/process/process_unsupported.rs | 74 - .../process/process_unsupported/wait_status.rs | 84 - .../process_unsupported/wait_status/tests.rs | 36 - .../std/src/sys/unix/process/process_vxworks.rs | 264 -- library/std/src/sys/unix/process/zircon.rs | 309 -- library/std/src/sys/unix/rand.rs | 301 -- library/std/src/sys/unix/stack_overflow.rs | 223 - library/std/src/sys/unix/stdio.rs | 99 - library/std/src/sys/unix/thread.rs | 992 ----- library/std/src/sys/unix/thread_local_dtor.rs | 124 - library/std/src/sys/unix/thread_local_key.rs | 29 - library/std/src/sys/unix/thread_parking/darwin.rs | 130 - library/std/src/sys/unix/thread_parking/mod.rs | 32 - library/std/src/sys/unix/thread_parking/netbsd.rs | 52 - library/std/src/sys/unix/thread_parking/pthread.rs | 284 -- library/std/src/sys/unix/time.rs | 330 -- library/std/src/sys/unix/weak.rs | 195 - library/std/src/sys/unsupported/alloc.rs | 23 - library/std/src/sys/unsupported/args.rs | 36 - library/std/src/sys/unsupported/common.rs | 40 - library/std/src/sys/unsupported/env.rs | 9 - library/std/src/sys/unsupported/fs.rs | 324 -- library/std/src/sys/unsupported/io.rs | 51 - library/std/src/sys/unsupported/locks/condvar.rs | 26 - library/std/src/sys/unsupported/locks/mod.rs | 6 - library/std/src/sys/unsupported/locks/mutex.rs | 32 - library/std/src/sys/unsupported/locks/rwlock.rs | 65 - library/std/src/sys/unsupported/mod.rs | 29 - library/std/src/sys/unsupported/net.rs | 370 -- library/std/src/sys/unsupported/once.rs | 100 - library/std/src/sys/unsupported/os.rs | 121 - library/std/src/sys/unsupported/pipe.rs | 45 - library/std/src/sys/unsupported/process.rs | 239 -- library/std/src/sys/unsupported/stdio.rs | 59 - library/std/src/sys/unsupported/thread.rs | 46 - .../std/src/sys/unsupported/thread_local_dtor.rs | 10 - .../std/src/sys/unsupported/thread_local_key.rs | 21 - library/std/src/sys/unsupported/thread_parking.rs | 11 - library/std/src/sys/unsupported/time.rs | 45 - library/std/src/sys/wasi/args.rs | 62 - library/std/src/sys/wasi/env.rs | 9 - library/std/src/sys/wasi/fd.rs | 332 -- library/std/src/sys/wasi/fs.rs | 810 ---- library/std/src/sys/wasi/io.rs | 79 - library/std/src/sys/wasi/mod.rs | 198 - library/std/src/sys/wasi/net.rs | 544 --- library/std/src/sys/wasi/os.rs | 301 -- library/std/src/sys/wasi/stdio.rs | 112 - library/std/src/sys/wasi/thread.rs | 201 - library/std/src/sys/wasi/time.rs | 65 - library/std/src/sys/wasm/alloc.rs | 166 - library/std/src/sys/wasm/atomics/futex.rs | 34 - library/std/src/sys/wasm/atomics/thread.rs | 55 - library/std/src/sys/wasm/env.rs | 9 - library/std/src/sys/wasm/mod.rs | 81 - library/std/src/sys/windows/alloc.rs | 247 -- library/std/src/sys/windows/alloc/tests.rs | 9 - library/std/src/sys/windows/api.rs | 157 - library/std/src/sys/windows/args.rs | 379 -- library/std/src/sys/windows/args/tests.rs | 91 - library/std/src/sys/windows/c.rs | 480 --- library/std/src/sys/windows/c/windows_sys.lst | 2596 ------------ library/std/src/sys/windows/c/windows_sys.rs | 4353 -------------------- library/std/src/sys/windows/cmath.rs | 96 - library/std/src/sys/windows/compat.rs | 244 -- library/std/src/sys/windows/env.rs | 9 - library/std/src/sys/windows/fs.rs | 1528 ------- library/std/src/sys/windows/handle.rs | 338 -- library/std/src/sys/windows/handle/tests.rs | 22 - library/std/src/sys/windows/io.rs | 161 - library/std/src/sys/windows/locks/condvar.rs | 50 - library/std/src/sys/windows/locks/mod.rs | 6 - library/std/src/sys/windows/locks/mutex.rs | 54 - library/std/src/sys/windows/locks/rwlock.rs | 40 - library/std/src/sys/windows/memchr.rs | 5 - library/std/src/sys/windows/mod.rs | 357 -- library/std/src/sys/windows/net.rs | 497 --- library/std/src/sys/windows/os.rs | 368 -- library/std/src/sys/windows/os/tests.rs | 13 - library/std/src/sys/windows/os_str.rs | 245 -- library/std/src/sys/windows/path.rs | 344 -- library/std/src/sys/windows/path/tests.rs | 137 - library/std/src/sys/windows/pipe.rs | 571 --- library/std/src/sys/windows/process.rs | 984 ----- library/std/src/sys/windows/process/tests.rs | 222 - library/std/src/sys/windows/rand.rs | 42 - library/std/src/sys/windows/stack_overflow.rs | 44 - library/std/src/sys/windows/stack_overflow_uwp.rs | 11 - library/std/src/sys/windows/stdio.rs | 462 --- library/std/src/sys/windows/stdio/tests.rs | 6 - library/std/src/sys/windows/thread.rs | 137 - library/std/src/sys/windows/thread_local_dtor.rs | 7 - library/std/src/sys/windows/thread_local_key.rs | 341 -- .../std/src/sys/windows/thread_local_key/tests.rs | 57 - library/std/src/sys/windows/thread_parking.rs | 253 -- library/std/src/sys/windows/time.rs | 262 -- library/std/src/sys/xous/alloc.rs | 62 - library/std/src/sys/xous/locks/condvar.rs | 111 - library/std/src/sys/xous/locks/mod.rs | 7 - library/std/src/sys/xous/locks/mutex.rs | 116 - library/std/src/sys/xous/locks/rwlock.rs | 72 - library/std/src/sys/xous/mod.rs | 36 - library/std/src/sys/xous/os.rs | 174 - library/std/src/sys/xous/stdio.rs | 131 - library/std/src/sys/xous/thread.rs | 144 - library/std/src/sys/xous/thread_local_key.rs | 190 - library/std/src/sys/xous/thread_parking.rs | 94 - library/std/src/sys/xous/time.rs | 57 - 516 files changed, 51864 insertions(+), 51856 deletions(-) delete mode 100644 library/std/src/sys/common/alloc.rs delete mode 100644 library/std/src/sys/common/mod.rs delete mode 100644 library/std/src/sys/common/small_c_string.rs delete mode 100644 library/std/src/sys/common/tests.rs delete mode 100644 library/std/src/sys/common/thread_local/fast_local.rs delete mode 100644 library/std/src/sys/common/thread_local/mod.rs delete mode 100644 library/std/src/sys/common/thread_local/os_local.rs delete mode 100644 library/std/src/sys/common/thread_local/static_local.rs delete mode 100644 library/std/src/sys/hermit/alloc.rs delete mode 100644 library/std/src/sys/hermit/args.rs delete mode 100644 library/std/src/sys/hermit/env.rs delete mode 100644 library/std/src/sys/hermit/fd.rs delete mode 100644 library/std/src/sys/hermit/fs.rs delete mode 100644 library/std/src/sys/hermit/futex.rs delete mode 100644 library/std/src/sys/hermit/memchr.rs delete mode 100644 library/std/src/sys/hermit/mod.rs delete mode 100644 library/std/src/sys/hermit/net.rs delete mode 100644 library/std/src/sys/hermit/os.rs delete mode 100644 library/std/src/sys/hermit/stdio.rs delete mode 100644 library/std/src/sys/hermit/thread.rs delete mode 100644 library/std/src/sys/hermit/thread_local_dtor.rs delete mode 100644 library/std/src/sys/hermit/time.rs delete mode 100644 library/std/src/sys/itron/abi.rs delete mode 100644 library/std/src/sys/itron/condvar.rs delete mode 100644 library/std/src/sys/itron/error.rs delete mode 100644 library/std/src/sys/itron/mutex.rs delete mode 100644 library/std/src/sys/itron/spin.rs delete mode 100644 library/std/src/sys/itron/task.rs delete mode 100644 library/std/src/sys/itron/thread.rs delete mode 100644 library/std/src/sys/itron/thread_parking.rs delete mode 100644 library/std/src/sys/itron/time.rs delete mode 100644 library/std/src/sys/itron/time/tests.rs create mode 100644 library/std/src/sys/pal/common/alloc.rs create mode 100644 library/std/src/sys/pal/common/mod.rs create mode 100644 library/std/src/sys/pal/common/small_c_string.rs create mode 100644 library/std/src/sys/pal/common/tests.rs create mode 100644 library/std/src/sys/pal/common/thread_local/fast_local.rs create mode 100644 library/std/src/sys/pal/common/thread_local/mod.rs create mode 100644 library/std/src/sys/pal/common/thread_local/os_local.rs create mode 100644 library/std/src/sys/pal/common/thread_local/static_local.rs create mode 100644 library/std/src/sys/pal/hermit/alloc.rs create mode 100644 library/std/src/sys/pal/hermit/args.rs create mode 100644 library/std/src/sys/pal/hermit/env.rs create mode 100644 library/std/src/sys/pal/hermit/fd.rs create mode 100644 library/std/src/sys/pal/hermit/fs.rs create mode 100644 library/std/src/sys/pal/hermit/futex.rs create mode 100644 library/std/src/sys/pal/hermit/memchr.rs create mode 100644 library/std/src/sys/pal/hermit/mod.rs create mode 100644 library/std/src/sys/pal/hermit/net.rs create mode 100644 library/std/src/sys/pal/hermit/os.rs create mode 100644 library/std/src/sys/pal/hermit/stdio.rs create mode 100644 library/std/src/sys/pal/hermit/thread.rs create mode 100644 library/std/src/sys/pal/hermit/thread_local_dtor.rs create mode 100644 library/std/src/sys/pal/hermit/time.rs create mode 100644 library/std/src/sys/pal/itron/abi.rs create mode 100644 library/std/src/sys/pal/itron/condvar.rs create mode 100644 library/std/src/sys/pal/itron/error.rs create mode 100644 library/std/src/sys/pal/itron/mutex.rs create mode 100644 library/std/src/sys/pal/itron/spin.rs create mode 100644 library/std/src/sys/pal/itron/task.rs create mode 100644 library/std/src/sys/pal/itron/thread.rs create mode 100644 library/std/src/sys/pal/itron/thread_parking.rs create mode 100644 library/std/src/sys/pal/itron/time.rs create mode 100644 library/std/src/sys/pal/itron/time/tests.rs create mode 100644 library/std/src/sys/pal/mod.rs create mode 100644 library/std/src/sys/pal/personality/dwarf/eh.rs create mode 100644 library/std/src/sys/pal/personality/dwarf/mod.rs create mode 100644 library/std/src/sys/pal/personality/dwarf/tests.rs create mode 100644 library/std/src/sys/pal/personality/emcc.rs create mode 100644 library/std/src/sys/pal/personality/gcc.rs create mode 100644 library/std/src/sys/pal/personality/mod.rs create mode 100644 library/std/src/sys/pal/sgx/abi/entry.S create mode 100644 library/std/src/sys/pal/sgx/abi/mem.rs create mode 100644 library/std/src/sys/pal/sgx/abi/mod.rs create mode 100644 library/std/src/sys/pal/sgx/abi/panic.rs create mode 100644 library/std/src/sys/pal/sgx/abi/reloc.rs create mode 100644 library/std/src/sys/pal/sgx/abi/thread.rs create mode 100644 library/std/src/sys/pal/sgx/abi/tls/mod.rs create mode 100644 library/std/src/sys/pal/sgx/abi/tls/sync_bitset.rs create mode 100644 library/std/src/sys/pal/sgx/abi/tls/sync_bitset/tests.rs create mode 100644 library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs create mode 100644 library/std/src/sys/pal/sgx/abi/usercalls/mod.rs create mode 100644 library/std/src/sys/pal/sgx/abi/usercalls/raw.rs create mode 100644 library/std/src/sys/pal/sgx/abi/usercalls/tests.rs create mode 100644 library/std/src/sys/pal/sgx/alloc.rs create mode 100644 library/std/src/sys/pal/sgx/args.rs create mode 100644 library/std/src/sys/pal/sgx/condvar.rs create mode 100644 library/std/src/sys/pal/sgx/env.rs create mode 100644 library/std/src/sys/pal/sgx/fd.rs create mode 100644 library/std/src/sys/pal/sgx/memchr.rs create mode 100644 library/std/src/sys/pal/sgx/mod.rs create mode 100644 library/std/src/sys/pal/sgx/mutex.rs create mode 100644 library/std/src/sys/pal/sgx/net.rs create mode 100644 library/std/src/sys/pal/sgx/os.rs create mode 100644 library/std/src/sys/pal/sgx/path.rs create mode 100644 library/std/src/sys/pal/sgx/rwlock.rs create mode 100644 library/std/src/sys/pal/sgx/rwlock/tests.rs create mode 100644 library/std/src/sys/pal/sgx/stdio.rs create mode 100644 library/std/src/sys/pal/sgx/thread.rs create mode 100644 library/std/src/sys/pal/sgx/thread_local_key.rs create mode 100644 library/std/src/sys/pal/sgx/thread_parking.rs create mode 100644 library/std/src/sys/pal/sgx/time.rs create mode 100644 library/std/src/sys/pal/sgx/waitqueue/mod.rs create mode 100644 library/std/src/sys/pal/sgx/waitqueue/spin_mutex.rs create mode 100644 library/std/src/sys/pal/sgx/waitqueue/spin_mutex/tests.rs create mode 100644 library/std/src/sys/pal/sgx/waitqueue/tests.rs create mode 100644 library/std/src/sys/pal/sgx/waitqueue/unsafe_list.rs create mode 100644 library/std/src/sys/pal/sgx/waitqueue/unsafe_list/tests.rs create mode 100644 library/std/src/sys/pal/solid/abi/fs.rs create mode 100644 library/std/src/sys/pal/solid/abi/mod.rs create mode 100644 library/std/src/sys/pal/solid/abi/sockets.rs create mode 100644 library/std/src/sys/pal/solid/alloc.rs create mode 100644 library/std/src/sys/pal/solid/env.rs create mode 100644 library/std/src/sys/pal/solid/error.rs create mode 100644 library/std/src/sys/pal/solid/fs.rs create mode 100644 library/std/src/sys/pal/solid/io.rs create mode 100644 library/std/src/sys/pal/solid/memchr.rs create mode 100644 library/std/src/sys/pal/solid/mod.rs create mode 100644 library/std/src/sys/pal/solid/net.rs create mode 100644 library/std/src/sys/pal/solid/os.rs create mode 100644 library/std/src/sys/pal/solid/path.rs create mode 100644 library/std/src/sys/pal/solid/rwlock.rs create mode 100644 library/std/src/sys/pal/solid/stdio.rs create mode 100644 library/std/src/sys/pal/solid/thread_local_dtor.rs create mode 100644 library/std/src/sys/pal/solid/thread_local_key.rs create mode 100644 library/std/src/sys/pal/solid/time.rs create mode 100644 library/std/src/sys/pal/teeos/alloc.rs create mode 100644 library/std/src/sys/pal/teeos/locks/condvar.rs create mode 100644 library/std/src/sys/pal/teeos/locks/mod.rs create mode 100644 library/std/src/sys/pal/teeos/locks/rwlock.rs create mode 100644 library/std/src/sys/pal/teeos/mod.rs create mode 100644 library/std/src/sys/pal/teeos/net.rs create mode 100644 library/std/src/sys/pal/teeos/os.rs create mode 100644 library/std/src/sys/pal/teeos/rand.rs create mode 100644 library/std/src/sys/pal/teeos/stdio.rs create mode 100644 library/std/src/sys/pal/teeos/thread.rs create mode 100644 library/std/src/sys/pal/teeos/thread_local_dtor.rs create mode 100644 library/std/src/sys/pal/uefi/alloc.rs create mode 100644 library/std/src/sys/pal/uefi/args.rs create mode 100644 library/std/src/sys/pal/uefi/env.rs create mode 100644 library/std/src/sys/pal/uefi/helpers.rs create mode 100644 library/std/src/sys/pal/uefi/mod.rs create mode 100644 library/std/src/sys/pal/uefi/os.rs create mode 100644 library/std/src/sys/pal/uefi/path.rs create mode 100644 library/std/src/sys/pal/uefi/stdio.rs create mode 100644 library/std/src/sys/pal/uefi/tests.rs create mode 100644 library/std/src/sys/pal/unix/alloc.rs create mode 100644 library/std/src/sys/pal/unix/android.rs create mode 100644 library/std/src/sys/pal/unix/args.rs create mode 100644 library/std/src/sys/pal/unix/cmath.rs create mode 100644 library/std/src/sys/pal/unix/env.rs create mode 100644 library/std/src/sys/pal/unix/fd.rs create mode 100644 library/std/src/sys/pal/unix/fd/tests.rs create mode 100644 library/std/src/sys/pal/unix/fs.rs create mode 100644 library/std/src/sys/pal/unix/futex.rs create mode 100644 library/std/src/sys/pal/unix/io.rs create mode 100644 library/std/src/sys/pal/unix/kernel_copy.rs create mode 100644 library/std/src/sys/pal/unix/kernel_copy/tests.rs create mode 100644 library/std/src/sys/pal/unix/l4re.rs create mode 100644 library/std/src/sys/pal/unix/locks/fuchsia_mutex.rs create mode 100644 library/std/src/sys/pal/unix/locks/futex_condvar.rs create mode 100644 library/std/src/sys/pal/unix/locks/futex_mutex.rs create mode 100644 library/std/src/sys/pal/unix/locks/futex_rwlock.rs create mode 100644 library/std/src/sys/pal/unix/locks/mod.rs create mode 100644 library/std/src/sys/pal/unix/locks/pthread_condvar.rs create mode 100644 library/std/src/sys/pal/unix/locks/pthread_mutex.rs create mode 100644 library/std/src/sys/pal/unix/locks/pthread_rwlock.rs create mode 100644 library/std/src/sys/pal/unix/memchr.rs create mode 100644 library/std/src/sys/pal/unix/mod.rs create mode 100644 library/std/src/sys/pal/unix/net.rs create mode 100644 library/std/src/sys/pal/unix/os.rs create mode 100644 library/std/src/sys/pal/unix/os/tests.rs create mode 100644 library/std/src/sys/pal/unix/os_str.rs create mode 100644 library/std/src/sys/pal/unix/os_str/tests.rs create mode 100644 library/std/src/sys/pal/unix/path.rs create mode 100644 library/std/src/sys/pal/unix/pipe.rs create mode 100644 library/std/src/sys/pal/unix/process/mod.rs create mode 100644 library/std/src/sys/pal/unix/process/process_common.rs create mode 100644 library/std/src/sys/pal/unix/process/process_common/tests.rs create mode 100644 library/std/src/sys/pal/unix/process/process_fuchsia.rs create mode 100644 library/std/src/sys/pal/unix/process/process_unix.rs create mode 100644 library/std/src/sys/pal/unix/process/process_unix/tests.rs create mode 100644 library/std/src/sys/pal/unix/process/process_unsupported.rs create mode 100644 library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs create mode 100644 library/std/src/sys/pal/unix/process/process_unsupported/wait_status/tests.rs create mode 100644 library/std/src/sys/pal/unix/process/process_vxworks.rs create mode 100644 library/std/src/sys/pal/unix/process/zircon.rs create mode 100644 library/std/src/sys/pal/unix/rand.rs create mode 100644 library/std/src/sys/pal/unix/stack_overflow.rs create mode 100644 library/std/src/sys/pal/unix/stdio.rs create mode 100644 library/std/src/sys/pal/unix/thread.rs create mode 100644 library/std/src/sys/pal/unix/thread_local_dtor.rs create mode 100644 library/std/src/sys/pal/unix/thread_local_key.rs create mode 100644 library/std/src/sys/pal/unix/thread_parking/darwin.rs create mode 100644 library/std/src/sys/pal/unix/thread_parking/mod.rs create mode 100644 library/std/src/sys/pal/unix/thread_parking/netbsd.rs create mode 100644 library/std/src/sys/pal/unix/thread_parking/pthread.rs create mode 100644 library/std/src/sys/pal/unix/time.rs create mode 100644 library/std/src/sys/pal/unix/weak.rs create mode 100644 library/std/src/sys/pal/unsupported/alloc.rs create mode 100644 library/std/src/sys/pal/unsupported/args.rs create mode 100644 library/std/src/sys/pal/unsupported/common.rs create mode 100644 library/std/src/sys/pal/unsupported/env.rs create mode 100644 library/std/src/sys/pal/unsupported/fs.rs create mode 100644 library/std/src/sys/pal/unsupported/io.rs create mode 100644 library/std/src/sys/pal/unsupported/locks/condvar.rs create mode 100644 library/std/src/sys/pal/unsupported/locks/mod.rs create mode 100644 library/std/src/sys/pal/unsupported/locks/mutex.rs create mode 100644 library/std/src/sys/pal/unsupported/locks/rwlock.rs create mode 100644 library/std/src/sys/pal/unsupported/mod.rs create mode 100644 library/std/src/sys/pal/unsupported/net.rs create mode 100644 library/std/src/sys/pal/unsupported/once.rs create mode 100644 library/std/src/sys/pal/unsupported/os.rs create mode 100644 library/std/src/sys/pal/unsupported/pipe.rs create mode 100644 library/std/src/sys/pal/unsupported/process.rs create mode 100644 library/std/src/sys/pal/unsupported/stdio.rs create mode 100644 library/std/src/sys/pal/unsupported/thread.rs create mode 100644 library/std/src/sys/pal/unsupported/thread_local_dtor.rs create mode 100644 library/std/src/sys/pal/unsupported/thread_local_key.rs create mode 100644 library/std/src/sys/pal/unsupported/thread_parking.rs create mode 100644 library/std/src/sys/pal/unsupported/time.rs create mode 100644 library/std/src/sys/pal/wasi/args.rs create mode 100644 library/std/src/sys/pal/wasi/env.rs create mode 100644 library/std/src/sys/pal/wasi/fd.rs create mode 100644 library/std/src/sys/pal/wasi/fs.rs create mode 100644 library/std/src/sys/pal/wasi/io.rs create mode 100644 library/std/src/sys/pal/wasi/mod.rs create mode 100644 library/std/src/sys/pal/wasi/net.rs create mode 100644 library/std/src/sys/pal/wasi/os.rs create mode 100644 library/std/src/sys/pal/wasi/stdio.rs create mode 100644 library/std/src/sys/pal/wasi/thread.rs create mode 100644 library/std/src/sys/pal/wasi/time.rs create mode 100644 library/std/src/sys/pal/wasm/alloc.rs create mode 100644 library/std/src/sys/pal/wasm/atomics/futex.rs create mode 100644 library/std/src/sys/pal/wasm/atomics/thread.rs create mode 100644 library/std/src/sys/pal/wasm/env.rs create mode 100644 library/std/src/sys/pal/wasm/mod.rs create mode 100644 library/std/src/sys/pal/windows/alloc.rs create mode 100644 library/std/src/sys/pal/windows/alloc/tests.rs create mode 100644 library/std/src/sys/pal/windows/api.rs create mode 100644 library/std/src/sys/pal/windows/args.rs create mode 100644 library/std/src/sys/pal/windows/args/tests.rs create mode 100644 library/std/src/sys/pal/windows/c.rs create mode 100644 library/std/src/sys/pal/windows/c/windows_sys.lst create mode 100644 library/std/src/sys/pal/windows/c/windows_sys.rs create mode 100644 library/std/src/sys/pal/windows/cmath.rs create mode 100644 library/std/src/sys/pal/windows/compat.rs create mode 100644 library/std/src/sys/pal/windows/env.rs create mode 100644 library/std/src/sys/pal/windows/fs.rs create mode 100644 library/std/src/sys/pal/windows/handle.rs create mode 100644 library/std/src/sys/pal/windows/handle/tests.rs create mode 100644 library/std/src/sys/pal/windows/io.rs create mode 100644 library/std/src/sys/pal/windows/locks/condvar.rs create mode 100644 library/std/src/sys/pal/windows/locks/mod.rs create mode 100644 library/std/src/sys/pal/windows/locks/mutex.rs create mode 100644 library/std/src/sys/pal/windows/locks/rwlock.rs create mode 100644 library/std/src/sys/pal/windows/memchr.rs create mode 100644 library/std/src/sys/pal/windows/mod.rs create mode 100644 library/std/src/sys/pal/windows/net.rs create mode 100644 library/std/src/sys/pal/windows/os.rs create mode 100644 library/std/src/sys/pal/windows/os/tests.rs create mode 100644 library/std/src/sys/pal/windows/os_str.rs create mode 100644 library/std/src/sys/pal/windows/path.rs create mode 100644 library/std/src/sys/pal/windows/path/tests.rs create mode 100644 library/std/src/sys/pal/windows/pipe.rs create mode 100644 library/std/src/sys/pal/windows/process.rs create mode 100644 library/std/src/sys/pal/windows/process/tests.rs create mode 100644 library/std/src/sys/pal/windows/rand.rs create mode 100644 library/std/src/sys/pal/windows/stack_overflow.rs create mode 100644 library/std/src/sys/pal/windows/stack_overflow_uwp.rs create mode 100644 library/std/src/sys/pal/windows/stdio.rs create mode 100644 library/std/src/sys/pal/windows/stdio/tests.rs create mode 100644 library/std/src/sys/pal/windows/thread.rs create mode 100644 library/std/src/sys/pal/windows/thread_local_dtor.rs create mode 100644 library/std/src/sys/pal/windows/thread_local_key.rs create mode 100644 library/std/src/sys/pal/windows/thread_local_key/tests.rs create mode 100644 library/std/src/sys/pal/windows/thread_parking.rs create mode 100644 library/std/src/sys/pal/windows/time.rs create mode 100644 library/std/src/sys/pal/xous/alloc.rs create mode 100644 library/std/src/sys/pal/xous/locks/condvar.rs create mode 100644 library/std/src/sys/pal/xous/locks/mod.rs create mode 100644 library/std/src/sys/pal/xous/locks/mutex.rs create mode 100644 library/std/src/sys/pal/xous/locks/rwlock.rs create mode 100644 library/std/src/sys/pal/xous/mod.rs create mode 100644 library/std/src/sys/pal/xous/os.rs create mode 100644 library/std/src/sys/pal/xous/stdio.rs create mode 100644 library/std/src/sys/pal/xous/thread.rs create mode 100644 library/std/src/sys/pal/xous/thread_local_key.rs create mode 100644 library/std/src/sys/pal/xous/thread_parking.rs create mode 100644 library/std/src/sys/pal/xous/time.rs delete mode 100644 library/std/src/sys/personality/dwarf/eh.rs delete mode 100644 library/std/src/sys/personality/dwarf/mod.rs delete mode 100644 library/std/src/sys/personality/dwarf/tests.rs delete mode 100644 library/std/src/sys/personality/emcc.rs delete mode 100644 library/std/src/sys/personality/gcc.rs delete mode 100644 library/std/src/sys/personality/mod.rs delete mode 100644 library/std/src/sys/sgx/abi/entry.S delete mode 100644 library/std/src/sys/sgx/abi/mem.rs delete mode 100644 library/std/src/sys/sgx/abi/mod.rs delete mode 100644 library/std/src/sys/sgx/abi/panic.rs delete mode 100644 library/std/src/sys/sgx/abi/reloc.rs delete mode 100644 library/std/src/sys/sgx/abi/thread.rs delete mode 100644 library/std/src/sys/sgx/abi/tls/mod.rs delete mode 100644 library/std/src/sys/sgx/abi/tls/sync_bitset.rs delete mode 100644 library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs delete mode 100644 library/std/src/sys/sgx/abi/usercalls/alloc.rs delete mode 100644 library/std/src/sys/sgx/abi/usercalls/mod.rs delete mode 100644 library/std/src/sys/sgx/abi/usercalls/raw.rs delete mode 100644 library/std/src/sys/sgx/abi/usercalls/tests.rs delete mode 100644 library/std/src/sys/sgx/alloc.rs delete mode 100644 library/std/src/sys/sgx/args.rs delete mode 100644 library/std/src/sys/sgx/condvar.rs delete mode 100644 library/std/src/sys/sgx/env.rs delete mode 100644 library/std/src/sys/sgx/fd.rs delete mode 100644 library/std/src/sys/sgx/memchr.rs delete mode 100644 library/std/src/sys/sgx/mod.rs delete mode 100644 library/std/src/sys/sgx/mutex.rs delete mode 100644 library/std/src/sys/sgx/net.rs delete mode 100644 library/std/src/sys/sgx/os.rs delete mode 100644 library/std/src/sys/sgx/path.rs delete mode 100644 library/std/src/sys/sgx/rwlock.rs delete mode 100644 library/std/src/sys/sgx/rwlock/tests.rs delete mode 100644 library/std/src/sys/sgx/stdio.rs delete mode 100644 library/std/src/sys/sgx/thread.rs delete mode 100644 library/std/src/sys/sgx/thread_local_key.rs delete mode 100644 library/std/src/sys/sgx/thread_parking.rs delete mode 100644 library/std/src/sys/sgx/time.rs delete mode 100644 library/std/src/sys/sgx/waitqueue/mod.rs delete mode 100644 library/std/src/sys/sgx/waitqueue/spin_mutex.rs delete mode 100644 library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs delete mode 100644 library/std/src/sys/sgx/waitqueue/tests.rs delete mode 100644 library/std/src/sys/sgx/waitqueue/unsafe_list.rs delete mode 100644 library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs delete mode 100644 library/std/src/sys/solid/abi/fs.rs delete mode 100644 library/std/src/sys/solid/abi/mod.rs delete mode 100644 library/std/src/sys/solid/abi/sockets.rs delete mode 100644 library/std/src/sys/solid/alloc.rs delete mode 100644 library/std/src/sys/solid/env.rs delete mode 100644 library/std/src/sys/solid/error.rs delete mode 100644 library/std/src/sys/solid/fs.rs delete mode 100644 library/std/src/sys/solid/io.rs delete mode 100644 library/std/src/sys/solid/memchr.rs delete mode 100644 library/std/src/sys/solid/mod.rs delete mode 100644 library/std/src/sys/solid/net.rs delete mode 100644 library/std/src/sys/solid/os.rs delete mode 100644 library/std/src/sys/solid/path.rs delete mode 100644 library/std/src/sys/solid/rwlock.rs delete mode 100644 library/std/src/sys/solid/stdio.rs delete mode 100644 library/std/src/sys/solid/thread_local_dtor.rs delete mode 100644 library/std/src/sys/solid/thread_local_key.rs delete mode 100644 library/std/src/sys/solid/time.rs delete mode 100644 library/std/src/sys/teeos/alloc.rs delete mode 100644 library/std/src/sys/teeos/locks/condvar.rs delete mode 100644 library/std/src/sys/teeos/locks/mod.rs delete mode 100644 library/std/src/sys/teeos/locks/rwlock.rs delete mode 100644 library/std/src/sys/teeos/mod.rs delete mode 100644 library/std/src/sys/teeos/net.rs delete mode 100644 library/std/src/sys/teeos/os.rs delete mode 100644 library/std/src/sys/teeos/rand.rs delete mode 100644 library/std/src/sys/teeos/stdio.rs delete mode 100644 library/std/src/sys/teeos/thread.rs delete mode 100644 library/std/src/sys/teeos/thread_local_dtor.rs delete mode 100644 library/std/src/sys/uefi/alloc.rs delete mode 100644 library/std/src/sys/uefi/args.rs delete mode 100644 library/std/src/sys/uefi/env.rs delete mode 100644 library/std/src/sys/uefi/helpers.rs delete mode 100644 library/std/src/sys/uefi/mod.rs delete mode 100644 library/std/src/sys/uefi/os.rs delete mode 100644 library/std/src/sys/uefi/path.rs delete mode 100644 library/std/src/sys/uefi/stdio.rs delete mode 100644 library/std/src/sys/uefi/tests.rs delete mode 100644 library/std/src/sys/unix/alloc.rs delete mode 100644 library/std/src/sys/unix/android.rs delete mode 100644 library/std/src/sys/unix/args.rs delete mode 100644 library/std/src/sys/unix/cmath.rs delete mode 100644 library/std/src/sys/unix/env.rs delete mode 100644 library/std/src/sys/unix/fd.rs delete mode 100644 library/std/src/sys/unix/fd/tests.rs delete mode 100644 library/std/src/sys/unix/fs.rs delete mode 100644 library/std/src/sys/unix/futex.rs delete mode 100644 library/std/src/sys/unix/io.rs delete mode 100644 library/std/src/sys/unix/kernel_copy.rs delete mode 100644 library/std/src/sys/unix/kernel_copy/tests.rs delete mode 100644 library/std/src/sys/unix/l4re.rs delete mode 100644 library/std/src/sys/unix/locks/fuchsia_mutex.rs delete mode 100644 library/std/src/sys/unix/locks/futex_condvar.rs delete mode 100644 library/std/src/sys/unix/locks/futex_mutex.rs delete mode 100644 library/std/src/sys/unix/locks/futex_rwlock.rs delete mode 100644 library/std/src/sys/unix/locks/mod.rs delete mode 100644 library/std/src/sys/unix/locks/pthread_condvar.rs delete mode 100644 library/std/src/sys/unix/locks/pthread_mutex.rs delete mode 100644 library/std/src/sys/unix/locks/pthread_rwlock.rs delete mode 100644 library/std/src/sys/unix/memchr.rs delete mode 100644 library/std/src/sys/unix/mod.rs delete mode 100644 library/std/src/sys/unix/net.rs delete mode 100644 library/std/src/sys/unix/os.rs delete mode 100644 library/std/src/sys/unix/os/tests.rs delete mode 100644 library/std/src/sys/unix/os_str.rs delete mode 100644 library/std/src/sys/unix/os_str/tests.rs delete mode 100644 library/std/src/sys/unix/path.rs delete mode 100644 library/std/src/sys/unix/pipe.rs delete mode 100644 library/std/src/sys/unix/process/mod.rs delete mode 100644 library/std/src/sys/unix/process/process_common.rs delete mode 100644 library/std/src/sys/unix/process/process_common/tests.rs delete mode 100644 library/std/src/sys/unix/process/process_fuchsia.rs delete mode 100644 library/std/src/sys/unix/process/process_unix.rs delete mode 100644 library/std/src/sys/unix/process/process_unix/tests.rs delete mode 100644 library/std/src/sys/unix/process/process_unsupported.rs delete mode 100644 library/std/src/sys/unix/process/process_unsupported/wait_status.rs delete mode 100644 library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs delete mode 100644 library/std/src/sys/unix/process/process_vxworks.rs delete mode 100644 library/std/src/sys/unix/process/zircon.rs delete mode 100644 library/std/src/sys/unix/rand.rs delete mode 100644 library/std/src/sys/unix/stack_overflow.rs delete mode 100644 library/std/src/sys/unix/stdio.rs delete mode 100644 library/std/src/sys/unix/thread.rs delete mode 100644 library/std/src/sys/unix/thread_local_dtor.rs delete mode 100644 library/std/src/sys/unix/thread_local_key.rs delete mode 100644 library/std/src/sys/unix/thread_parking/darwin.rs delete mode 100644 library/std/src/sys/unix/thread_parking/mod.rs delete mode 100644 library/std/src/sys/unix/thread_parking/netbsd.rs delete mode 100644 library/std/src/sys/unix/thread_parking/pthread.rs delete mode 100644 library/std/src/sys/unix/time.rs delete mode 100644 library/std/src/sys/unix/weak.rs delete mode 100644 library/std/src/sys/unsupported/alloc.rs delete mode 100644 library/std/src/sys/unsupported/args.rs delete mode 100644 library/std/src/sys/unsupported/common.rs delete mode 100644 library/std/src/sys/unsupported/env.rs delete mode 100644 library/std/src/sys/unsupported/fs.rs delete mode 100644 library/std/src/sys/unsupported/io.rs delete mode 100644 library/std/src/sys/unsupported/locks/condvar.rs delete mode 100644 library/std/src/sys/unsupported/locks/mod.rs delete mode 100644 library/std/src/sys/unsupported/locks/mutex.rs delete mode 100644 library/std/src/sys/unsupported/locks/rwlock.rs delete mode 100644 library/std/src/sys/unsupported/mod.rs delete mode 100644 library/std/src/sys/unsupported/net.rs delete mode 100644 library/std/src/sys/unsupported/once.rs delete mode 100644 library/std/src/sys/unsupported/os.rs delete mode 100644 library/std/src/sys/unsupported/pipe.rs delete mode 100644 library/std/src/sys/unsupported/process.rs delete mode 100644 library/std/src/sys/unsupported/stdio.rs delete mode 100644 library/std/src/sys/unsupported/thread.rs delete mode 100644 library/std/src/sys/unsupported/thread_local_dtor.rs delete mode 100644 library/std/src/sys/unsupported/thread_local_key.rs delete mode 100644 library/std/src/sys/unsupported/thread_parking.rs delete mode 100644 library/std/src/sys/unsupported/time.rs delete mode 100644 library/std/src/sys/wasi/args.rs delete mode 100644 library/std/src/sys/wasi/env.rs delete mode 100644 library/std/src/sys/wasi/fd.rs delete mode 100644 library/std/src/sys/wasi/fs.rs delete mode 100644 library/std/src/sys/wasi/io.rs delete mode 100644 library/std/src/sys/wasi/mod.rs delete mode 100644 library/std/src/sys/wasi/net.rs delete mode 100644 library/std/src/sys/wasi/os.rs delete mode 100644 library/std/src/sys/wasi/stdio.rs delete mode 100644 library/std/src/sys/wasi/thread.rs delete mode 100644 library/std/src/sys/wasi/time.rs delete mode 100644 library/std/src/sys/wasm/alloc.rs delete mode 100644 library/std/src/sys/wasm/atomics/futex.rs delete mode 100644 library/std/src/sys/wasm/atomics/thread.rs delete mode 100644 library/std/src/sys/wasm/env.rs delete mode 100644 library/std/src/sys/wasm/mod.rs delete mode 100644 library/std/src/sys/windows/alloc.rs delete mode 100644 library/std/src/sys/windows/alloc/tests.rs delete mode 100644 library/std/src/sys/windows/api.rs delete mode 100644 library/std/src/sys/windows/args.rs delete mode 100644 library/std/src/sys/windows/args/tests.rs delete mode 100644 library/std/src/sys/windows/c.rs delete mode 100644 library/std/src/sys/windows/c/windows_sys.lst delete mode 100644 library/std/src/sys/windows/c/windows_sys.rs delete mode 100644 library/std/src/sys/windows/cmath.rs delete mode 100644 library/std/src/sys/windows/compat.rs delete mode 100644 library/std/src/sys/windows/env.rs delete mode 100644 library/std/src/sys/windows/fs.rs delete mode 100644 library/std/src/sys/windows/handle.rs delete mode 100644 library/std/src/sys/windows/handle/tests.rs delete mode 100644 library/std/src/sys/windows/io.rs delete mode 100644 library/std/src/sys/windows/locks/condvar.rs delete mode 100644 library/std/src/sys/windows/locks/mod.rs delete mode 100644 library/std/src/sys/windows/locks/mutex.rs delete mode 100644 library/std/src/sys/windows/locks/rwlock.rs delete mode 100644 library/std/src/sys/windows/memchr.rs delete mode 100644 library/std/src/sys/windows/mod.rs delete mode 100644 library/std/src/sys/windows/net.rs delete mode 100644 library/std/src/sys/windows/os.rs delete mode 100644 library/std/src/sys/windows/os/tests.rs delete mode 100644 library/std/src/sys/windows/os_str.rs delete mode 100644 library/std/src/sys/windows/path.rs delete mode 100644 library/std/src/sys/windows/path/tests.rs delete mode 100644 library/std/src/sys/windows/pipe.rs delete mode 100644 library/std/src/sys/windows/process.rs delete mode 100644 library/std/src/sys/windows/process/tests.rs delete mode 100644 library/std/src/sys/windows/rand.rs delete mode 100644 library/std/src/sys/windows/stack_overflow.rs delete mode 100644 library/std/src/sys/windows/stack_overflow_uwp.rs delete mode 100644 library/std/src/sys/windows/stdio.rs delete mode 100644 library/std/src/sys/windows/stdio/tests.rs delete mode 100644 library/std/src/sys/windows/thread.rs delete mode 100644 library/std/src/sys/windows/thread_local_dtor.rs delete mode 100644 library/std/src/sys/windows/thread_local_key.rs delete mode 100644 library/std/src/sys/windows/thread_local_key/tests.rs delete mode 100644 library/std/src/sys/windows/thread_parking.rs delete mode 100644 library/std/src/sys/windows/time.rs delete mode 100644 library/std/src/sys/xous/alloc.rs delete mode 100644 library/std/src/sys/xous/locks/condvar.rs delete mode 100644 library/std/src/sys/xous/locks/mod.rs delete mode 100644 library/std/src/sys/xous/locks/mutex.rs delete mode 100644 library/std/src/sys/xous/locks/rwlock.rs delete mode 100644 library/std/src/sys/xous/mod.rs delete mode 100644 library/std/src/sys/xous/os.rs delete mode 100644 library/std/src/sys/xous/stdio.rs delete mode 100644 library/std/src/sys/xous/thread.rs delete mode 100644 library/std/src/sys/xous/thread_local_key.rs delete mode 100644 library/std/src/sys/xous/thread_parking.rs delete mode 100644 library/std/src/sys/xous/time.rs (limited to 'library/std/src/sys') diff --git a/library/std/src/sys/common/alloc.rs b/library/std/src/sys/common/alloc.rs deleted file mode 100644 index b7357460f39..00000000000 --- a/library/std/src/sys/common/alloc.rs +++ /dev/null @@ -1,58 +0,0 @@ -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(any( - target_arch = "x86", - target_arch = "arm", - target_arch = "m68k", - target_arch = "csky", - target_arch = "mips", - target_arch = "mips32r6", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "sparc", - 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(any( - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "loongarch64", - target_arch = "mips64", - target_arch = "mips64r6", - 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(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 deleted file mode 100644 index b35c5d30b41..00000000000 --- a/library/std/src/sys/common/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -// 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; -pub mod small_c_string; -#[allow(unused_imports)] -pub mod thread_local; - -#[cfg(test)] -mod tests; diff --git a/library/std/src/sys/common/small_c_string.rs b/library/std/src/sys/common/small_c_string.rs deleted file mode 100644 index af9b18e372d..00000000000 --- a/library/std/src/sys/common/small_c_string.rs +++ /dev/null @@ -1,58 +0,0 @@ -use crate::ffi::{CStr, CString}; -use crate::mem::MaybeUninit; -use crate::path::Path; -use crate::slice; -use crate::{io, ptr}; - -// Make sure to stay under 4096 so the compiler doesn't insert a probe frame: -// https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html -#[cfg(not(target_os = "espidf"))] -const MAX_STACK_ALLOCATION: usize = 384; -#[cfg(target_os = "espidf")] -const MAX_STACK_ALLOCATION: usize = 32; - -const NUL_ERR: io::Error = - io::const_io_error!(io::ErrorKind::InvalidInput, "file name contained an unexpected NUL byte"); - -#[inline] -pub fn run_path_with_cstr(path: &Path, f: F) -> io::Result -where - F: FnOnce(&CStr) -> io::Result, -{ - run_with_cstr(path.as_os_str().as_encoded_bytes(), f) -} - -#[inline] -pub fn run_with_cstr(bytes: &[u8], f: F) -> io::Result -where - F: FnOnce(&CStr) -> io::Result, -{ - if bytes.len() >= MAX_STACK_ALLOCATION { - return run_with_cstr_allocating(bytes, f); - } - - let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit(); - let buf_ptr = buf.as_mut_ptr() as *mut u8; - - unsafe { - ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len()); - buf_ptr.add(bytes.len()).write(0); - } - - match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) { - Ok(s) => f(s), - Err(_) => Err(NUL_ERR), - } -} - -#[cold] -#[inline(never)] -fn run_with_cstr_allocating(bytes: &[u8], f: F) -> io::Result -where - F: FnOnce(&CStr) -> io::Result, -{ - match CString::new(bytes) { - Ok(s) => f(&s), - Err(_) => Err(NUL_ERR), - } -} diff --git a/library/std/src/sys/common/tests.rs b/library/std/src/sys/common/tests.rs deleted file mode 100644 index 32dc18ee1cf..00000000000 --- a/library/std/src/sys/common/tests.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::ffi::CString; -use crate::hint::black_box; -use crate::path::Path; -use crate::sys::common::small_c_string::run_path_with_cstr; -use core::iter::repeat; - -#[test] -fn stack_allocation_works() { - let path = Path::new("abc"); - let result = run_path_with_cstr(path, |p| { - assert_eq!(p, &*CString::new(path.as_os_str().as_encoded_bytes()).unwrap()); - Ok(42) - }); - assert_eq!(result.unwrap(), 42); -} - -#[test] -fn stack_allocation_fails() { - let path = Path::new("ab\0"); - assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err()); -} - -#[test] -fn heap_allocation_works() { - let path = repeat("a").take(384).collect::(); - let path = Path::new(&path); - let result = run_path_with_cstr(path, |p| { - assert_eq!(p, &*CString::new(path.as_os_str().as_encoded_bytes()).unwrap()); - Ok(42) - }); - assert_eq!(result.unwrap(), 42); -} - -#[test] -fn heap_allocation_fails() { - let mut path = repeat("a").take(384).collect::(); - path.push('\0'); - let path = Path::new(&path); - assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err()); -} - -#[bench] -fn bench_stack_path_alloc(b: &mut test::Bencher) { - let path = repeat("a").take(383).collect::(); - let p = Path::new(&path); - b.iter(|| { - run_path_with_cstr(p, |cstr| { - black_box(cstr); - Ok(()) - }) - .unwrap(); - }); -} - -#[bench] -fn bench_heap_path_alloc(b: &mut test::Bencher) { - let path = repeat("a").take(384).collect::(); - let p = Path::new(&path); - b.iter(|| { - run_path_with_cstr(p, |cstr| { - black_box(cstr); - Ok(()) - }) - .unwrap(); - }); -} diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs deleted file mode 100644 index 9206588be06..00000000000 --- a/library/std/src/sys/common/thread_local/fast_local.rs +++ /dev/null @@ -1,245 +0,0 @@ -use super::lazy::LazyKeyInner; -use crate::cell::Cell; -use crate::sys::thread_local_dtor::register_dtor; -use crate::{fmt, mem, panic}; - -#[doc(hidden)] -#[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)] -#[allow_internal_unsafe] -#[unstable(feature = "thread_local_internals", issue = "none")] -#[rustc_macro_transparency = "semitransparent"] -pub macro thread_local_inner { - // used to generate the `LocalKey` value for const-initialized thread locals - (@key $t:ty, const $init:expr) => {{ - #[inline] - #[deny(unsafe_op_in_unsafe_fn)] - // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint - #[cfg_attr(not(bootstrap), allow(static_mut_ref))] - unsafe fn __getit( - _init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - const INIT_EXPR: $t = $init; - // If the platform has support for `#[thread_local]`, use it. - #[thread_local] - static mut VAL: $t = INIT_EXPR; - - // If a dtor isn't needed we can do something "very raw" and - // just get going. - if !$crate::mem::needs_drop::<$t>() { - unsafe { - return $crate::option::Option::Some(&VAL) - } - } - - // 0 == dtor not registered - // 1 == dtor registered, dtor not run - // 2 == dtor registered and is running or has run - #[thread_local] - static STATE: $crate::cell::Cell<$crate::primitive::u8> = $crate::cell::Cell::new(0); - - // Safety: Performs `drop_in_place(ptr as *mut $t)`, and requires - // all that comes with it. - unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) { - $crate::thread::local_impl::abort_on_dtor_unwind(|| { - let old_state = STATE.replace(2); - $crate::debug_assert_eq!(old_state, 1); - // Safety: safety requirement is passed on to caller. - unsafe { $crate::ptr::drop_in_place(ptr.cast::<$t>()); } - }); - } - - unsafe { - match STATE.get() { - // 0 == we haven't registered a destructor, so do - // so now. - 0 => { - $crate::thread::local_impl::Key::<$t>::register_dtor( - $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8, - destroy, - ); - STATE.set(1); - $crate::option::Option::Some(&VAL) - } - // 1 == the destructor is registered and the value - // is valid, so return the pointer. - 1 => $crate::option::Option::Some(&VAL), - // otherwise the destructor has already run, so we - // can't give access. - _ => $crate::option::Option::None, - } - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) - } - }}, - - // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => { - { - #[inline] - fn __init() -> $t { $init } - - #[inline] - unsafe fn __getit( - init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - #[thread_local] - static __KEY: $crate::thread::local_impl::Key<$t> = - $crate::thread::local_impl::Key::<$t>::new(); - - unsafe { - __KEY.get(move || { - if let $crate::option::Option::Some(init) = init { - if let $crate::option::Option::Some(value) = init.take() { - return value; - } else if $crate::cfg!(debug_assertions) { - $crate::unreachable!("missing default value"); - } - } - __init() - }) - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) - } - } - }, - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { - $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); - }, -} - -#[derive(Copy, Clone)] -enum DtorState { - Unregistered, - Registered, - RunningOrHasRun, -} - -// This data structure has been carefully constructed so that the fast path -// only contains one branch on x86. That optimization is necessary to avoid -// duplicated tls lookups on OSX. -// -// LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 -pub struct Key { - // If `LazyKeyInner::get` returns `None`, that indicates either: - // * The value has never been initialized - // * The value is being recursively initialized - // * The value has already been destroyed or is being destroyed - // To determine which kind of `None`, check `dtor_state`. - // - // This is very optimizer friendly for the fast path - initialized but - // not yet dropped. - inner: LazyKeyInner, - - // Metadata to keep track of the state of the destructor. Remember that - // this variable is thread-local, not global. - dtor_state: Cell, -} - -impl fmt::Debug for Key { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Key").finish_non_exhaustive() - } -} -impl Key { - pub const fn new() -> Key { - Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } - } - - // note that this is just a publicly-callable function only for the - // const-initialized form of thread locals, basically a way to call the - // free `register_dtor` function defined elsewhere in std. - pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - unsafe { - register_dtor(a, dtor); - } - } - - pub unsafe fn get T>(&self, init: F) -> Option<&'static T> { - // SAFETY: See the definitions of `LazyKeyInner::get` and - // `try_initialize` for more information. - // - // The caller must ensure no mutable references are ever active to - // the inner cell or the inner T when this is called. - // The `try_initialize` is dependant on the passed `init` function - // for this. - unsafe { - match self.inner.get() { - Some(val) => Some(val), - None => self.try_initialize(init), - } - } - } - - // `try_initialize` is only called once per fast thread local variable, - // except in corner cases where thread_local dtors reference other - // thread_local's, or it is being recursively initialized. - // - // Macos: Inlining this function can cause two `tlv_get_addr` calls to - // be performed for every call to `Key::get`. - // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 - #[inline(never)] - unsafe fn try_initialize T>(&self, init: F) -> Option<&'static T> { - // SAFETY: See comment above (this function doc). - if !mem::needs_drop::() || unsafe { self.try_register_dtor() } { - // SAFETY: See comment above (this function doc). - Some(unsafe { self.inner.initialize(init) }) - } else { - None - } - } - - // `try_register_dtor` is only called once per fast thread local - // variable, except in corner cases where thread_local dtors reference - // other thread_local's, or it is being recursively initialized. - unsafe fn try_register_dtor(&self) -> bool { - match self.dtor_state.get() { - DtorState::Unregistered => { - // SAFETY: dtor registration happens before initialization. - // Passing `self` as a pointer while using `destroy_value` - // is safe because the function will build a pointer to a - // Key, which is the type of self and so find the correct - // size. - unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::) }; - self.dtor_state.set(DtorState::Registered); - true - } - DtorState::Registered => { - // recursively initialized - true - } - DtorState::RunningOrHasRun => false, - } - } -} - -unsafe extern "C" fn destroy_value(ptr: *mut u8) { - let ptr = ptr as *mut Key; - - // SAFETY: - // - // The pointer `ptr` has been built just above and comes from - // `try_register_dtor` where it is originally a Key coming from `self`, - // making it non-NUL and of the correct type. - // - // Right before we run the user destructor be sure to set the - // `Option` to `None`, and `dtor_state` to `RunningOrHasRun`. This - // causes future calls to `get` to run `try_initialize_drop` again, - // which will now fail, and return `None`. - // - // Wrap the call in a catch to ensure unwinding is caught in the event - // a panic takes place in a destructor. - if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe { - let value = (*ptr).inner.take(); - (*ptr).dtor_state.set(DtorState::RunningOrHasRun); - drop(value); - })) { - rtabort!("thread local panicked on drop"); - } -} diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs deleted file mode 100644 index 8b2c839f837..00000000000 --- a/library/std/src/sys/common/thread_local/mod.rs +++ /dev/null @@ -1,124 +0,0 @@ -#![unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")] - -// There are three thread-local implementations: "static", "fast", "OS". -// The "OS" thread local key type is accessed via platform-specific API calls and is slow, while the -// "fast" key type is accessed via code generated via LLVM, where TLS keys are set up by the linker. -// "static" is for single-threaded platforms where a global static is sufficient. - -cfg_if::cfg_if! { - if #[cfg(any(all(target_family = "wasm", not(target_feature = "atomics")), target_os = "uefi"))] { - #[doc(hidden)] - mod static_local; - #[doc(hidden)] - pub use static_local::{Key, thread_local_inner}; - } else if #[cfg(target_thread_local)] { - #[doc(hidden)] - mod fast_local; - #[doc(hidden)] - pub use fast_local::{Key, thread_local_inner}; - } else { - #[doc(hidden)] - mod os_local; - #[doc(hidden)] - pub use os_local::{Key, thread_local_inner}; - } -} - -mod lazy { - use crate::cell::UnsafeCell; - use crate::hint; - use crate::mem; - - pub struct LazyKeyInner { - inner: UnsafeCell>, - } - - impl LazyKeyInner { - pub const fn new() -> LazyKeyInner { - LazyKeyInner { inner: UnsafeCell::new(None) } - } - - pub unsafe fn get(&self) -> Option<&'static T> { - // SAFETY: The caller must ensure no reference is ever handed out to - // the inner cell nor mutable reference to the Option inside said - // cell. This make it safe to hand a reference, though the lifetime - // of 'static is itself unsafe, making the get method unsafe. - unsafe { (*self.inner.get()).as_ref() } - } - - /// The caller must ensure that no reference is active: this method - /// needs unique access. - pub unsafe fn initialize T>(&self, init: F) -> &'static T { - // Execute the initialization up front, *then* move it into our slot, - // just in case initialization fails. - let value = init(); - let ptr = self.inner.get(); - - // SAFETY: - // - // note that this can in theory just be `*ptr = Some(value)`, but due to - // the compiler will currently codegen that pattern with something like: - // - // ptr::drop_in_place(ptr) - // ptr::write(ptr, Some(value)) - // - // Due to this pattern it's possible for the destructor of the value in - // `ptr` (e.g., if this is being recursively initialized) to re-access - // TLS, in which case there will be a `&` and `&mut` pointer to the same - // value (an aliasing violation). To avoid setting the "I'm running a - // destructor" flag we just use `mem::replace` which should sequence the - // operations a little differently and make this safe to call. - // - // The precondition also ensures that we are the only one accessing - // `self` at the moment so replacing is fine. - unsafe { - let _ = mem::replace(&mut *ptr, Some(value)); - } - - // SAFETY: With the call to `mem::replace` it is guaranteed there is - // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked` - // will never be reached. - unsafe { - // After storing `Some` we want to get a reference to the contents of - // what we just stored. While we could use `unwrap` here and it should - // always work it empirically doesn't seem to always get optimized away, - // which means that using something like `try_with` can pull in - // panicking code and cause a large size bloat. - match *ptr { - Some(ref x) => x, - None => hint::unreachable_unchecked(), - } - } - } - - /// The other methods hand out references while taking &self. - /// As such, callers of this method must ensure no `&` and `&mut` are - /// available and used at the same time. - #[allow(unused)] - pub unsafe fn take(&mut self) -> Option { - // SAFETY: See doc comment for this method. - unsafe { (*self.inner.get()).take() } - } - } -} - -/// Run a callback in a scenario which must not unwind (such as a `extern "C" -/// fn` declared in a user crate). If the callback unwinds anyway, then -/// `rtabort` with a message about thread local panicking on drop. -#[inline] -pub fn abort_on_dtor_unwind(f: impl FnOnce()) { - // Using a guard like this is lower cost. - let guard = DtorUnwindGuard; - f(); - core::mem::forget(guard); - - struct DtorUnwindGuard; - impl Drop for DtorUnwindGuard { - #[inline] - fn drop(&mut self) { - // This is not terribly descriptive, but it doesn't need to be as we'll - // already have printed a panic message at this point. - rtabort!("thread local panicked on drop"); - } - } -} diff --git a/library/std/src/sys/common/thread_local/os_local.rs b/library/std/src/sys/common/thread_local/os_local.rs deleted file mode 100644 index 7cf29192122..00000000000 --- a/library/std/src/sys/common/thread_local/os_local.rs +++ /dev/null @@ -1,185 +0,0 @@ -use super::lazy::LazyKeyInner; -use crate::cell::Cell; -use crate::sys_common::thread_local_key::StaticKey as OsStaticKey; -use crate::{fmt, marker, panic, ptr}; - -#[doc(hidden)] -#[allow_internal_unstable(thread_local_internals)] -#[allow_internal_unsafe] -#[unstable(feature = "thread_local_internals", issue = "none")] -#[rustc_macro_transparency = "semitransparent"] -pub macro thread_local_inner { - // used to generate the `LocalKey` value for const-initialized thread locals - (@key $t:ty, const $init:expr) => {{ - #[inline] - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn __getit( - _init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - const INIT_EXPR: $t = $init; - - // On platforms without `#[thread_local]` we fall back to the - // same implementation as below for os thread locals. - #[inline] - const fn __init() -> $t { INIT_EXPR } - static __KEY: $crate::thread::local_impl::Key<$t> = - $crate::thread::local_impl::Key::new(); - unsafe { - __KEY.get(move || { - if let $crate::option::Option::Some(init) = _init { - if let $crate::option::Option::Some(value) = init.take() { - return value; - } else if $crate::cfg!(debug_assertions) { - $crate::unreachable!("missing initial value"); - } - } - __init() - }) - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) - } - }}, - - // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => { - { - #[inline] - fn __init() -> $t { $init } - - // `#[inline] does not work on windows-gnu due to linking errors around dllimports. - // See https://github.com/rust-lang/rust/issues/109797. - #[cfg_attr(not(windows), inline)] - unsafe fn __getit( - init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - static __KEY: $crate::thread::local_impl::Key<$t> = - $crate::thread::local_impl::Key::new(); - - unsafe { - __KEY.get(move || { - if let $crate::option::Option::Some(init) = init { - if let $crate::option::Option::Some(value) = init.take() { - return value; - } else if $crate::cfg!(debug_assertions) { - $crate::unreachable!("missing default value"); - } - } - __init() - }) - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) - } - } - }, - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { - $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); - }, -} - -/// Use a regular global static to store this key; the state provided will then be -/// thread-local. -pub struct Key { - // OS-TLS key that we'll use to key off. - os: OsStaticKey, - marker: marker::PhantomData>, -} - -impl fmt::Debug for Key { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Key").finish_non_exhaustive() - } -} - -unsafe impl Sync for Key {} - -struct Value { - inner: LazyKeyInner, - key: &'static Key, -} - -impl Key { - #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] - pub const fn new() -> Key { - Key { os: OsStaticKey::new(Some(destroy_value::)), marker: marker::PhantomData } - } - - /// It is a requirement for the caller to ensure that no mutable - /// reference is active when this method is called. - pub unsafe fn get(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> { - // SAFETY: See the documentation for this method. - let ptr = unsafe { self.os.get() as *mut Value }; - if ptr.addr() > 1 { - // SAFETY: the check ensured the pointer is safe (its destructor - // is not running) + it is coming from a trusted source (self). - if let Some(ref value) = unsafe { (*ptr).inner.get() } { - return Some(value); - } - } - // SAFETY: At this point we are sure we have no value and so - // initializing (or trying to) is safe. - unsafe { self.try_initialize(init) } - } - - // `try_initialize` is only called once per os thread local variable, - // except in corner cases where thread_local dtors reference other - // thread_local's, or it is being recursively initialized. - unsafe fn try_initialize(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> { - // SAFETY: No mutable references are ever handed out meaning getting - // the value is ok. - let ptr = unsafe { self.os.get() as *mut Value }; - if ptr.addr() == 1 { - // destructor is running - return None; - } - - let ptr = if ptr.is_null() { - // If the lookup returned null, we haven't initialized our own - // local copy, so do that now. - let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self })); - // SAFETY: At this point we are sure there is no value inside - // ptr so setting it will not affect anyone else. - unsafe { - self.os.set(ptr as *mut u8); - } - ptr - } else { - // recursive initialization - ptr - }; - - // SAFETY: ptr has been ensured as non-NUL just above an so can be - // dereferenced safely. - unsafe { Some((*ptr).inner.initialize(init)) } - } -} - -unsafe extern "C" fn destroy_value(ptr: *mut u8) { - // SAFETY: - // - // The OS TLS ensures that this key contains a null value when this - // destructor starts to run. We set it back to a sentinel value of 1 to - // ensure that any future calls to `get` for this thread will return - // `None`. - // - // Note that to prevent an infinite loop we reset it back to null right - // before we return from the destructor ourselves. - // - // Wrap the call in a catch to ensure unwinding is caught in the event - // a panic takes place in a destructor. - if let Err(_) = panic::catch_unwind(|| unsafe { - let ptr = Box::from_raw(ptr as *mut Value); - let key = ptr.key; - key.os.set(ptr::invalid_mut(1)); - drop(ptr); - key.os.set(ptr::null_mut()); - }) { - rtabort!("thread local panicked on drop"); - } -} diff --git a/library/std/src/sys/common/thread_local/static_local.rs b/library/std/src/sys/common/thread_local/static_local.rs deleted file mode 100644 index 51cba66fad7..00000000000 --- a/library/std/src/sys/common/thread_local/static_local.rs +++ /dev/null @@ -1,107 +0,0 @@ -use super::lazy::LazyKeyInner; -use crate::fmt; - -#[doc(hidden)] -#[allow_internal_unstable(thread_local_internals)] -#[allow_internal_unsafe] -#[unstable(feature = "thread_local_internals", issue = "none")] -#[rustc_macro_transparency = "semitransparent"] -pub macro thread_local_inner { - // used to generate the `LocalKey` value for const-initialized thread locals - (@key $t:ty, const $init:expr) => {{ - #[inline] // see comments below - #[deny(unsafe_op_in_unsafe_fn)] - // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint - #[cfg_attr(not(bootstrap), allow(static_mut_ref))] - unsafe fn __getit( - _init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - const INIT_EXPR: $t = $init; - - // wasm without atomics maps directly to `static mut`, and dtors - // aren't implemented because thread dtors aren't really a thing - // on wasm right now - // - // FIXME(#84224) this should come after the `target_thread_local` - // block. - static mut VAL: $t = INIT_EXPR; - unsafe { $crate::option::Option::Some(&VAL) } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) - } - }}, - - // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => { - { - #[inline] - fn __init() -> $t { $init } - #[inline] - unsafe fn __getit( - init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - static __KEY: $crate::thread::local_impl::Key<$t> = - $crate::thread::local_impl::Key::new(); - - unsafe { - __KEY.get(move || { - if let $crate::option::Option::Some(init) = init { - if let $crate::option::Option::Some(value) = init.take() { - return value; - } else if $crate::cfg!(debug_assertions) { - $crate::unreachable!("missing default value"); - } - } - __init() - }) - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) - } - } - }, - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { - $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); - }, -} - -/// On some targets like wasm there's no threads, so no need to generate -/// thread locals and we can instead just use plain statics! - -pub struct Key { - inner: LazyKeyInner, -} - -unsafe impl Sync for Key {} - -impl fmt::Debug for Key { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Key").finish_non_exhaustive() - } -} - -impl Key { - pub const fn new() -> Key { - Key { inner: LazyKeyInner::new() } - } - - pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> { - // SAFETY: The caller must ensure no reference is ever handed out to - // the inner cell nor mutable reference to the Option inside said - // cell. This make it safe to hand a reference, though the lifetime - // of 'static is itself unsafe, making the get method unsafe. - let value = unsafe { - match self.inner.get() { - Some(ref value) => value, - None => self.inner.initialize(init), - } - }; - - Some(value) - } -} diff --git a/library/std/src/sys/hermit/alloc.rs b/library/std/src/sys/hermit/alloc.rs deleted file mode 100644 index d153914e77e..00000000000 --- a/library/std/src/sys/hermit/alloc.rs +++ /dev/null @@ -1,31 +0,0 @@ -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 deleted file mode 100644 index 220a76e4b12..00000000000 --- a/library/std/src/sys/hermit/args.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::ffi::{c_char, CStr, OsString}; -use crate::fmt; -use crate::os::hermit::ffi::OsStringExt; -use crate::ptr; -use crate::sync::atomic::{ - AtomicIsize, AtomicPtr, - Ordering::{Acquire, Relaxed, Release}, -}; -use crate::vec; - -static ARGC: AtomicIsize = AtomicIsize::new(0); -static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut()); - -/// One-time global initialization. -pub unsafe fn init(argc: isize, argv: *const *const u8) { - ARGC.store(argc, Relaxed); - // Use release ordering here to broadcast writes by the OS. - ARGV.store(argv as *mut *const u8, Release); -} - -/// Returns the command line arguments -pub fn args() -> Args { - // Synchronize with the store above. - let argv = ARGV.load(Acquire); - // If argv has not been initialized yet, do not return any arguments. - let argc = if argv.is_null() { 0 } else { ARGC.load(Relaxed) }; - let args: Vec = (0..argc) - .map(|i| unsafe { - let cstr = CStr::from_ptr(*argv.offset(i) as *const c_char); - OsStringExt::from_vec(cstr.to_bytes().to_vec()) - }) - .collect(); - - Args { iter: args.into_iter() } -} - -pub struct Args { - iter: vec::IntoIter, -} - -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 { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - 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 { - self.iter.next_back() - } -} diff --git a/library/std/src/sys/hermit/env.rs b/library/std/src/sys/hermit/env.rs deleted file mode 100644 index 7a0fcb31ef2..00000000000 --- a/library/std/src/sys/hermit/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index ccde05aa1d7..00000000000 --- a/library/std/src/sys/hermit/fd.rs +++ /dev/null @@ -1,101 +0,0 @@ -#![unstable(reason = "not public", issue = "none", feature = "fd")] - -use crate::io::{self, Read}; -use crate::os::hermit::io::{FromRawFd, OwnedFd, RawFd}; -use crate::sys::cvt; -use crate::sys::hermit::abi; -use crate::sys::unsupported; -use crate::sys_common::{AsInner, FromInner, IntoInner}; - -use crate::os::hermit::io::*; - -#[derive(Debug)] -pub struct FileDesc { - fd: OwnedFd, -} - -impl FileDesc { - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let result = cvt(unsafe { abi::read(self.fd.as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?; - Ok(result as usize) - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - let mut me = self; - (&mut me).read_to_end(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let result = cvt(unsafe { abi::write(self.fd.as_raw_fd(), buf.as_ptr(), buf.len()) })?; - Ok(result as usize) - } - - pub fn duplicate(&self) -> io::Result { - self.duplicate_path(&[]) - } - pub fn duplicate_path(&self, _path: &[u8]) -> io::Result { - unsupported() - } - - pub fn nonblocking(&self) -> io::Result { - 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 { - (**self).read(buf) - } -} - -impl IntoInner for FileDesc { - fn into_inner(self) -> OwnedFd { - self.fd - } -} - -impl FromInner for FileDesc { - fn from_inner(owned_fd: OwnedFd) -> Self { - Self { fd: owned_fd } - } -} - -impl FromRawFd for FileDesc { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self { fd: FromRawFd::from_raw_fd(raw_fd) } - } -} - -impl AsInner for FileDesc { - #[inline] - fn as_inner(&self) -> &OwnedFd { - &self.fd - } -} - -impl AsFd for FileDesc { - fn as_fd(&self) -> BorrowedFd<'_> { - self.fd.as_fd() - } -} - -impl AsRawFd for FileDesc { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.fd.as_raw_fd() - } -} - -impl IntoRawFd for FileDesc { - fn into_raw_fd(self) -> RawFd { - self.fd.into_raw_fd() - } -} diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs deleted file mode 100644 index 6aa4ea7f5b4..00000000000 --- a/library/std/src/sys/hermit/fs.rs +++ /dev/null @@ -1,468 +0,0 @@ -use crate::ffi::{CStr, OsString}; -use crate::fmt; -use crate::hash::{Hash, Hasher}; -use crate::io::{self, Error, ErrorKind}; -use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::path::{Path, PathBuf}; -use crate::sys::common::small_c_string::run_path_with_cstr; -use crate::sys::cvt; -use crate::sys::hermit::abi::{ - self, 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; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; - -pub use crate::sys_common::fs::{copy, try_exists}; -//pub use crate::sys_common::fs::remove_dir_all; - -#[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, -} - -#[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 { - self.0 - } - - pub fn accessed(&self) -> io::Result { - self.0 - } - - pub fn created(&self) -> io::Result { - 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(&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; - - fn next(&mut self) -> Option> { - 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 { - self.0 - } - - pub fn file_type(&self) -> io::Result { - 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: 0o777, - } - } - - 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 { - 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 { - 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 { - run_path_with_cstr(path, |path| File::open_c(&path, opts)) - } - - pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result { - 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(unsafe { FileDesc::from_raw_fd(fd as i32) })) - } - - pub fn file_attr(&self) -> io::Result { - 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 { - self.0.read(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - crate::io::default_read_vectored(|buf| self.read(buf), bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - crate::io::default_read_buf(|buf| self.read(buf), cursor) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - crate::io::default_write_vectored(|buf| self.write(buf), bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - false - } - - #[inline] - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } - - pub fn seek(&self, _pos: SeekFrom) -> io::Result { - Err(Error::from_raw_os_error(22)) - } - - pub fn duplicate(&self) -> io::Result { - Err(Error::from_raw_os_error(22)) - } - - pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - Err(Error::from_raw_os_error(22)) - } - - pub fn set_times(&self, _times: FileTimes) -> 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() - } -} - -impl AsInner for File { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } -} - -impl AsInnerMut for File { - #[inline] - fn as_inner_mut(&mut self) -> &mut FileDesc { - &mut self.0 - } -} - -impl IntoInner for File { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -impl FromInner 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 { - #[inline] - 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)) - } -} - -pub fn readdir(_p: &Path) -> io::Result { - unsupported() -} - -pub fn unlink(path: &Path) -> io::Result<()> { - run_path_with_cstr(path, |path| cvt(unsafe { abi::unlink(path.as_ptr()) }).map(|_| ())) -} - -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 { - 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 { - unsupported() -} - -pub fn lstat(_p: &Path) -> io::Result { - unsupported() -} - -pub fn canonicalize(_p: &Path) -> io::Result { - unsupported() -} diff --git a/library/std/src/sys/hermit/futex.rs b/library/std/src/sys/hermit/futex.rs deleted file mode 100644 index 427d8ff6f2e..00000000000 --- a/library/std/src/sys/hermit/futex.rs +++ /dev/null @@ -1,39 +0,0 @@ -use super::abi; -use crate::ptr::null; -use crate::sync::atomic::AtomicU32; -use crate::time::Duration; - -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { - // Calculate the timeout as a relative timespec. - // - // Overflows are rounded up to an infinite timeout (None). - let timespec = timeout.and_then(|dur| { - Some(abi::timespec { - tv_sec: dur.as_secs().try_into().ok()?, - tv_nsec: dur.subsec_nanos().into(), - }) - }); - - let r = unsafe { - abi::futex_wait( - futex.as_ptr(), - expected, - timespec.as_ref().map_or(null(), |t| t as *const abi::timespec), - abi::FUTEX_RELATIVE_TIMEOUT, - ) - }; - - r != -abi::errno::ETIMEDOUT -} - -#[inline] -pub fn futex_wake(futex: &AtomicU32) -> bool { - unsafe { abi::futex_wake(futex.as_ptr(), 1) > 0 } -} - -#[inline] -pub fn futex_wake_all(futex: &AtomicU32) { - unsafe { - abi::futex_wake(futex.as_ptr(), i32::MAX); - } -} diff --git a/library/std/src/sys/hermit/memchr.rs b/library/std/src/sys/hermit/memchr.rs deleted file mode 100644 index 9967482197e..00000000000 --- a/library/std/src/sys/hermit/memchr.rs +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index abd7eb353f8..00000000000 --- a/library/std/src/sys/hermit/mod.rs +++ /dev/null @@ -1,207 +0,0 @@ -//! 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(missing_docs, nonstandard_style, unsafe_op_in_unsafe_fn)] - -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; -pub mod futex; -#[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; - -#[path = "../unix/locks"] -pub mod locks { - mod futex_condvar; - mod futex_mutex; - mod futex_rwlock; - pub(crate) use futex_condvar::Condvar; - pub(crate) use futex_mutex::Mutex; - pub(crate) use futex_rwlock::RwLock; -} - -use crate::io::ErrorKind; -use crate::os::hermit::abi; - -pub fn unsupported() -> crate::io::Result { - 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", - ) -} - -pub fn abort_internal() -> ! { - unsafe { - abi::abort(); - } -} - -pub fn hashmap_random_keys() -> (u64, u64) { - let mut buf = [0; 16]; - let mut slice = &mut buf[..]; - while !slice.is_empty() { - let res = cvt(unsafe { abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) }) - .expect("failed to generate random hashmap keys"); - slice = &mut slice[res as usize..]; - } - - let key1 = buf[..8].try_into().unwrap(); - let key2 = buf[8..].try_into().unwrap(); - (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) -} - -// 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, _sigpipe: u8) { - 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() {} - -#[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); -} - -#[inline] -pub(crate) fn is_interrupted(errno: i32) -> bool { - errno == abi::errno::EINTR -} - -pub fn decode_error_kind(errno: i32) -> ErrorKind { - match errno { - abi::errno::EACCES => ErrorKind::PermissionDenied, - abi::errno::EADDRINUSE => ErrorKind::AddrInUse, - abi::errno::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, - abi::errno::EAGAIN => ErrorKind::WouldBlock, - abi::errno::ECONNABORTED => ErrorKind::ConnectionAborted, - abi::errno::ECONNREFUSED => ErrorKind::ConnectionRefused, - abi::errno::ECONNRESET => ErrorKind::ConnectionReset, - abi::errno::EEXIST => ErrorKind::AlreadyExists, - abi::errno::EINTR => ErrorKind::Interrupted, - abi::errno::EINVAL => ErrorKind::InvalidInput, - abi::errno::ENOENT => ErrorKind::NotFound, - abi::errno::ENOTCONN => ErrorKind::NotConnected, - abi::errno::EPERM => ErrorKind::PermissionDenied, - abi::errno::EPIPE => ErrorKind::BrokenPipe, - abi::errno::ETIMEDOUT => ErrorKind::TimedOut, - _ => ErrorKind::Uncategorized, - } -} - -#[doc(hidden)] -pub trait IsNegative { - fn is_negative(&self) -> bool; - fn negate(&self) -> i32; -} - -macro_rules! impl_is_negative { - ($($t:ident)*) => ($(impl IsNegative for $t { - fn is_negative(&self) -> bool { - *self < 0 - } - - fn negate(&self) -> i32 { - i32::try_from(-(*self)).unwrap() - } - })*) -} - -impl IsNegative for i32 { - fn is_negative(&self) -> bool { - *self < 0 - } - - fn negate(&self) -> i32 { - -(*self) - } -} -impl_is_negative! { i8 i16 i64 isize } - -pub fn cvt(t: T) -> crate::io::Result { - if t.is_negative() { - let e = decode_error_kind(t.negate()); - Err(crate::io::Error::from(e)) - } else { - Ok(t) - } -} - -pub fn cvt_r(mut f: F) -> crate::io::Result -where - T: IsNegative, - F: FnMut() -> T, -{ - loop { - match cvt(f()) { - Err(ref e) if e.is_interrupted() => {} - other => return other, - } - } -} diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs deleted file mode 100644 index bd8b493d65a..00000000000 --- a/library/std/src/sys/hermit/net.rs +++ /dev/null @@ -1,372 +0,0 @@ -#![allow(dead_code)] - -use crate::cmp; -use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::mem; -use crate::net::{Shutdown, SocketAddr}; -use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; -use crate::sys::hermit::fd::FileDesc; -use crate::sys::time::Instant; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::Duration; - -use core::ffi::c_int; - -#[allow(unused_extern_crates)] -pub extern crate hermit_abi as netc; - -pub use crate::sys::{cvt, cvt_r}; - -pub type wrlen_t = usize; - -pub fn cvt_gai(err: i32) -> io::Result<()> { - if err == 0 { - return Ok(()); - } - - let detail = ""; - - Err(io::Error::new( - io::ErrorKind::Uncategorized, - &format!("failed to lookup address information: {detail}")[..], - )) -} - -pub fn init() {} - -#[derive(Debug)] -pub struct Socket(FileDesc); - -impl Socket { - pub fn new(addr: &SocketAddr, ty: i32) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: i32, ty: i32) -> io::Result { - let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; - Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) - } - - pub fn new_pair(_fam: i32, _ty: i32) -> io::Result<(Socket, Socket)> { - unimplemented!() - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; - Ok(()) - } - - 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.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(netc::errno::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::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 { netc::poll(&mut pollfd, 1, timeout) } { - -1 => { - let err = io::Error::last_os_error(); - if !err.is_interrupted() { - return Err(err); - } - } - 0 => {} - _ => { - // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look - // for POLLHUP rather than read readiness - if pollfd.revents & netc::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 netc::sockaddr, - len: *mut netc::socklen_t, - ) -> io::Result { - let fd = cvt(unsafe { netc::accept(self.0.as_raw_fd(), storage, len) })?; - Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) - } - - pub fn duplicate(&self) -> io::Result { - let fd = cvt(unsafe { netc::dup(self.0.as_raw_fd()) })?; - Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: i32) -> io::Result<()> { - let ret = cvt(unsafe { - netc::recv( - self.0.as_raw_fd(), - buf.as_mut().as_mut_ptr() as *mut u8, - buf.capacity(), - flags, - ) - })?; - unsafe { - buf.advance(ret as usize); - } - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let mut size: isize = 0; - - for i in bufs.iter_mut() { - let ret: isize = - cvt(unsafe { netc::read(self.0.as_raw_fd(), i.as_mut_ptr(), i.len()) })?; - - if ret != 0 { - size += ret; - } - } - - Ok(size.try_into().unwrap()) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> 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.as_raw_fd(), - buf.as_mut_ptr(), - 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, netc::MSG_PEEK) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let sz = cvt(unsafe { netc::write(self.0.as_raw_fd(), buf.as_ptr(), buf.len()) })?; - Ok(sz.try_into().unwrap()) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut size: isize = 0; - - for i in bufs.iter() { - size += cvt(unsafe { netc::write(self.0.as_raw_fd(), i.as_ptr(), i.len()) })?; - } - - Ok(size.try_into().unwrap()) - } - - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn set_timeout(&self, dur: Option, kind: i32) -> 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::time_t::MAX as u64 { - netc::time_t::MAX - } else { - dur.as_secs() as netc::time_t - }; - let mut timeout = netc::timeval { - tv_sec: secs, - tv_usec: dur.subsec_micros() as netc::suseconds_t, - }; - 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: i32) -> io::Result> { - 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_socket(self.as_raw_fd(), how) })?; - Ok(()) - } - - pub fn set_linger(&self, linger: Option) -> io::Result<()> { - let linger = netc::linger { - l_onoff: linger.is_some() as i32, - l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, - }; - - setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) - } - - pub fn linger(&self) -> io::Result> { - 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<()> { - let value: i32 = if nodelay { 1 } else { 0 }; - setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) - } - - pub fn nodelay(&self) -> io::Result { - let raw: i32 = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; - Ok(raw != 0) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking: i32 = if nonblocking { 1 } else { 0 }; - cvt(unsafe { - netc::ioctl( - self.as_raw_fd(), - netc::FIONBIO, - &mut nonblocking as *mut _ as *mut core::ffi::c_void, - ) - }) - .map(drop) - } - - pub fn take_error(&self) -> io::Result> { - unimplemented!() - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -impl FromInner 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 { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} diff --git a/library/std/src/sys/hermit/os.rs b/library/std/src/sys/hermit/os.rs deleted file mode 100644 index c79197a9ad1..00000000000 --- a/library/std/src/sys/hermit/os.rs +++ /dev/null @@ -1,206 +0,0 @@ -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::hermit::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 { - 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 { - self.0 - } -} - -#[derive(Debug)] -pub struct JoinPathsError; - -pub fn join_paths(_paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - 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 { - unsupported() -} - -static mut ENV: Option>> = 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)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -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) { - 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 { - 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 { - 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/stdio.rs b/library/std/src/sys/hermit/stdio.rs deleted file mode 100644 index 514de1df6f9..00000000000 --- a/library/std/src/sys/hermit/stdio.rs +++ /dev/null @@ -1,120 +0,0 @@ -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 { - self.read_vectored(&mut [IoSliceMut::new(data)]) - } - - fn read_vectored(&mut self, _data: &mut [IoSliceMut<'_>]) -> io::Result { - 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 { - 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 { - 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 { - 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 { - 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 { - Some(Stderr::new()) -} diff --git a/library/std/src/sys/hermit/thread.rs b/library/std/src/sys/hermit/thread.rs deleted file mode 100644 index 332151e40d0..00000000000 --- a/library/std/src/sys/hermit/thread.rs +++ /dev/null @@ -1,112 +0,0 @@ -#![allow(dead_code)] - -use crate::ffi::CStr; -use crate::io; -use crate::mem; -use crate::num::NonZeroUsize; -use crate::ptr; -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, - core_id: isize, - ) -> io::Result { - let p = Box::into_raw(Box::new(p)); - let tid = abi::spawn2( - thread_start, - p.expose_addr(), - 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(ptr::from_exposed_addr::>(main).cast_mut())(); - - // run all destructors - run_dtors(); - } - } - } - - pub unsafe fn new(stack: usize, p: Box) -> io::Result { - 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 { - unsafe { Ok(NonZeroUsize::new_unchecked(abi::get_processor_count())) } -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/hermit/thread_local_dtor.rs b/library/std/src/sys/hermit/thread_local_dtor.rs deleted file mode 100644 index 98adaf4bff1..00000000000 --- a/library/std/src/sys/hermit/thread_local_dtor.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![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::RefCell; - -#[thread_local] -static DTORS: RefCell> = RefCell::new(Vec::new()); - -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - match DTORS.try_borrow_mut() { - Ok(mut dtors) => dtors.push((t, dtor)), - Err(_) => rtabort!("global allocator may not use TLS"), - } -} - -// every thread call this function to run through all possible destructors -pub unsafe fn run_dtors() { - let mut list = DTORS.take(); - while !list.is_empty() { - for (ptr, dtor) in list { - dtor(ptr); - } - list = DTORS.take(); - } -} diff --git a/library/std/src/sys/hermit/time.rs b/library/std/src/sys/hermit/time.rs deleted file mode 100644 index 7d91460aba3..00000000000 --- a/library/std/src/sys/hermit/time.rs +++ /dev/null @@ -1,216 +0,0 @@ -#![allow(dead_code)] - -use crate::cmp::Ordering; -use crate::ops::{Add, AddAssign, Sub, SubAssign}; -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 { - 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 { - let mut secs = self.t.tv_sec.checked_add_unsigned(other.as_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 { - let mut secs = self.t.tv_sec.checked_sub_unsigned(other.as_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 { - 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(&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(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(time) - } - - #[stable(feature = "time2", since = "1.8.0")] - pub fn elapsed(&self) -> Duration { - Instant::now() - *self - } - - pub fn duration_since(&self, earlier: Instant) -> Duration { - self.checked_duration_since(earlier).unwrap_or_default() - } - - pub fn checked_duration_since(&self, earlier: Instant) -> Option { - self.checked_sub_instant(&earlier) - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option { - self.0.sub_timespec(&other.0).ok() - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_add_duration(other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_sub_duration(other)?)) - } - - pub fn checked_add(&self, duration: Duration) -> Option { - self.0.checked_add_duration(&duration).map(Instant) - } - - pub fn checked_sub(&self, duration: Duration) -> Option { - self.0.checked_sub_duration(&duration).map(Instant) - } -} - -impl Add for Instant { - type Output = Instant; - - /// # Panics - /// - /// This function may panic if the resulting point in time cannot be represented by the - /// underlying data structure. See [`Instant::checked_add`] for a version without panic. - fn add(self, other: Duration) -> Instant { - self.checked_add(other).expect("overflow when adding duration to instant") - } -} - -impl AddAssign for Instant { - fn add_assign(&mut self, other: Duration) { - *self = *self + other; - } -} - -impl Sub for Instant { - type Output = Instant; - - fn sub(self, other: Duration) -> Instant { - self.checked_sub(other).expect("overflow when subtracting duration from instant") - } -} - -impl SubAssign for Instant { - fn sub_assign(&mut self, other: Duration) { - *self = *self - other; - } -} - -impl Sub for Instant { - type Output = Duration; - - /// Returns the amount of time elapsed from another instant to this one, - /// or zero duration if that instant is later than this one. - /// - /// # Panics - /// - /// Previous rust versions panicked when `other` was later than `self`. Currently this - /// method saturates. Future versions may reintroduce the panic in some circumstances. - /// See [Monotonicity]. - /// - /// [Monotonicity]: Instant#monotonicity - fn sub(self, other: Instant) -> Duration { - self.duration_since(other) - } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct SystemTime(Timespec); - -pub const UNIX_EPOCH: SystemTime = SystemTime(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(time) - } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.0.sub_timespec(&other.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_add_duration(other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_sub_duration(other)?)) - } -} diff --git a/library/std/src/sys/itron/abi.rs b/library/std/src/sys/itron/abi.rs deleted file mode 100644 index 5eb14bb7e53..00000000000 --- a/library/std/src/sys/itron/abi.rs +++ /dev/null @@ -1,197 +0,0 @@ -//! 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; - -// 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 deleted file mode 100644 index 7a47cc6696a..00000000000 --- a/library/std/src/sys/itron/condvar.rs +++ /dev/null @@ -1,292 +0,0 @@ -//! 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, -} - -unsafe impl Send for Condvar {} -unsafe impl Sync for Condvar {} - -impl Condvar { - #[inline] - pub const fn new() -> Condvar { - Condvar { waiters: SpinMutex::new(waiter_queue::WaiterQueue::new()) } - } - - pub 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 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; - } - } - - 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) }); - - mutex.lock(); - success - } -} - -mod waiter_queue { - use super::*; - - pub struct WaiterQueue { - head: Option, - } - - #[derive(Copy, Clone)] - struct ListHead { - first: NonNull, - last: NonNull, - } - - 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>, - next: Option>, - } - - 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) { - 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) -> 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) -> bool { - unsafe { waiter.as_ref().task != 0 } - } - - #[inline] - pub fn pop_front(&mut self) -> Option { - 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 deleted file mode 100644 index fbc822d4eb6..00000000000 --- a/library/std/src/sys/itron/error.rs +++ /dev/null @@ -1,164 +0,0 @@ -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 { - 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 { - 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, - } -} - -#[inline] -pub fn is_interrupted(er: abi::ER) -> bool { - er == abi::E_RLWAI -} - -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 deleted file mode 100644 index 1f6cc419476..00000000000 --- a/library/std/src/sys/itron/mutex.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! 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<()>, -} - -/// Create a mutex object. This function never panics. -fn new_mtx() -> Result { - 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() } - } - - /// 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 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 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 { - 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 deleted file mode 100644 index 44d409444bc..00000000000 --- a/library/std/src/sys/itron/spin.rs +++ /dev/null @@ -1,163 +0,0 @@ -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 { - locked: AtomicBool, - data: UnsafeCell, -} - -impl SpinMutex { - #[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(&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 { - id: AtomicUsize, - spin: SpinMutex<()>, - extra: UnsafeCell>, -} - -const ID_UNINIT: usize = 0; - -impl SpinIdOnceCell { - #[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(&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(&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 Drop for SpinIdOnceCell { - #[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 deleted file mode 100644 index 94beb50a254..00000000000 --- a/library/std/src/sys/itron/task.rs +++ /dev/null @@ -1,44 +0,0 @@ -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 { - 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 { - 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 deleted file mode 100644 index ae0f718535b..00000000000 --- a/library/std/src/sys/itron/thread.rs +++ /dev/null @@ -1,368 +0,0 @@ -//! 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, - ptr::NonNull, - sync::atomic::{AtomicUsize, Ordering}, - sys::thread_local_dtor::run_dtors, - time::Duration, -}; - -pub struct Thread { - p_inner: NonNull, - - /// The ID of the underlying task. - task: abi::ID, -} - -// Safety: There's nothing in `Thread` that ties it to the original creator. It -// can be dropped by any threads. -unsafe impl Send for Thread {} -// Safety: `Thread` provides no methods that take `&self`. -unsafe impl Sync for Thread {} - -/// 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>>, - - /// A state machine. Each transition is annotated with `[...]` in the - /// source code. - /// - /// ```text - /// - ///

: parent, : child, (?): don't-care - /// - /// DETACHED (-1) --------------------> EXITED (?) - /// finish/exd_tsk - /// ^ - /// | - /// |

detach - /// | - /// - /// INIT (0) -----------------------> FINISHED (-1) - /// finish - /// | | - /// |

join/slp_tsk |

join/del_tsk - /// | |

detach/del_tsk - /// v v - /// - /// JOINING JOINED (?) - /// (parent_tid) - /// ^ - /// \ / - /// \ finish/wup_tsk /

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::(); - -impl Thread { - /// # Safety - /// - /// See `thread::Builder::spawn_unchecked` for safety requirements. - pub unsafe fn new(stack: usize, p: Box) -> io::Result { - let inner = Box::new(ThreadInner { - start: UnsafeCell::new(ManuallyDrop::new(p)), - lifecycle: AtomicUsize::new(LIFECYCLE_INIT), - }); - - unsafe extern "C" fn trampoline(exinf: isize) { - let p_inner: *mut ThreadInner = crate::ptr::from_exposed_addr_mut(exinf as usize); - // Safety: `ThreadInner` is alive at this point - let inner = unsafe { &*p_inner }; - - // 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::AcqRel); - - 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, `*p_inner`'s ownership has been moved to - // us, and we are responsible for dropping it. The acquire - // ordering ensures that the swap operation that wrote - // `LIFECYCLE_DETACHED` happens-before `Box::from_raw( - // p_inner)`. - // Safety: See above. - let _ = unsafe { Box::from_raw(p_inner) }; - - // 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. - // - // To make the task referred to by `parent_tid` visible, we - // must use the acquire ordering 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", - ); - } - } - } - - // Safety: `Box::into_raw` returns a non-null pointer - let p_inner = unsafe { NonNull::new_unchecked(Box::into_raw(inner)) }; - - let new_task = ItronError::err_if_negative(unsafe { - abi::acre_tsk(&abi::T_CTSK { - // Activate this task immediately - tskatr: abi::TA_ACT, - exinf: p_inner.as_ptr().expose_addr() 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 { p_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(self) { - // Safety: `ThreadInner` is alive at this point - let inner = unsafe { self.p_inner.as_ref() }; - // 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::AcqRel) { - LIFECYCLE_INIT => { - // [INIT → JOINING] - // The child task will transition the state to `JOIN_FINALIZE` - // and wake us up. - // - // To make the task referred to by `current_task` visible from - // the child task's point of view, we must use the release - // ordering in the above `swap` call. - 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 `*p_inner` will not be accessed hereafter - let _inner = unsafe { Box::from_raw(self.p_inner.as_ptr()) }; - - // Skip the destructor (because it would attempt to detach the thread) - crate::mem::forget(self); - } -} - -impl Drop for Thread { - fn drop(&mut self) { - // Safety: `ThreadInner` is alive at this point - let inner = unsafe { self.p_inner.as_ref() }; - - // Detach the thread. - match inner.lifecycle.swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::AcqRel) { - LIFECYCLE_INIT => { - // [INIT → DETACHED] - // When the time comes, the child will figure out that no - // one will ever join it. - // The ownership of `*p_inner` is moved to the child thread. - // The release ordering ensures that the above swap operation on - // `lifecycle` happens-before the child thread's - // `Box::from_raw(p_inner)`. - } - 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 `*p_inner`. - // Safety: The contents of `*p_inner` will not be accessed hereafter - let _ = unsafe { Box::from_raw(self.p_inner.as_ptr()) }; - } - _ => unsafe { hint::unreachable_unchecked() }, - } - } -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - 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 { - super::unsupported() -} diff --git a/library/std/src/sys/itron/thread_parking.rs b/library/std/src/sys/itron/thread_parking.rs deleted file mode 100644 index fe9934439d1..00000000000 --- a/library/std/src/sys/itron/thread_parking.rs +++ /dev/null @@ -1,37 +0,0 @@ -use super::abi; -use super::error::expect_success_aborting; -use super::time::with_tmos; -use crate::time::Duration; - -pub type ThreadId = abi::ID; - -pub use super::task::current_task_id_aborting as current; - -pub fn park(_hint: usize) { - match unsafe { abi::slp_tsk() } { - abi::E_OK | abi::E_RLWAI => {} - err => { - expect_success_aborting(err, &"slp_tsk"); - } - } -} - -pub fn park_timeout(dur: Duration, _hint: usize) { - match with_tmos(dur, |tmo| unsafe { abi::tslp_tsk(tmo) }) { - abi::E_OK | abi::E_RLWAI | abi::E_TMOUT => {} - err => { - expect_success_aborting(err, &"tslp_tsk"); - } - } -} - -pub fn unpark(id: ThreadId, _hint: usize) { - match unsafe { abi::wup_tsk(id) } { - // It is allowed to try to wake up a destroyed or unrelated task, so we ignore all - // errors that could result from that situation. - abi::E_OK | abi::E_NOEXS | abi::E_OBJ | abi::E_QOVR => {} - err => { - expect_success_aborting(err, &"wup_tsk"); - } - } -} diff --git a/library/std/src/sys/itron/time.rs b/library/std/src/sys/itron/time.rs deleted file mode 100644 index 427ea0d80e1..00000000000 --- a/library/std/src/sys/itron/time.rs +++ /dev/null @@ -1,114 +0,0 @@ -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 { - 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 { - // `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 { - // `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 { - // `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 { - // `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 deleted file mode 100644 index d14035d9da4..00000000000 --- a/library/std/src/sys/itron/time/tests.rs +++ /dev/null @@ -1,33 +0,0 @@ -use super::*; - -fn reltim2dur(t: u64) -> Duration { - Duration::from_micros(t) -} - -#[test] -fn test_dur2reltims() { - assert_eq!(dur2reltims(reltim2dur(0)).collect::>(), vec![]); - assert_eq!(dur2reltims(reltim2dur(42)).collect::>(), vec![42]); - assert_eq!( - dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), - vec![abi::TMAX_RELTIM] - ); - assert_eq!( - dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), - vec![abi::TMAX_RELTIM, 10000] - ); -} - -#[test] -fn test_dur2tmos() { - assert_eq!(dur2tmos(reltim2dur(0)).collect::>(), vec![0]); - assert_eq!(dur2tmos(reltim2dur(42)).collect::>(), vec![42]); - assert_eq!( - dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), - vec![abi::TMAX_RELTIM] - ); - assert_eq!( - dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), - vec![abi::TMAX_RELTIM, 10000] - ); -} diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 88420bd3612..0cd4528b69e 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -1,124 +1,8 @@ -//! 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)] - -pub mod common; -mod personality; - -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(target_os = "xous")] { - mod xous; - pub use self::xous::*; - } else if #[cfg(target_os = "uefi")] { - mod uefi; - pub use self::uefi::*; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { - mod sgx; - pub use self::sgx::*; - } else if #[cfg(target_os = "teeos")] { - mod teeos; - pub use self::teeos::*; - } else { - mod unsupported; - pub use self::unsupported::*; - } -} - -cfg_if::cfg_if! { - // Fuchsia components default to full backtrace. - if #[cfg(target_os = "fuchsia")] { - pub const FULL_BACKTRACE_DEFAULT: bool = true; - } else { - pub const FULL_BACKTRACE_DEFAULT: bool = false; - } -} - -#[cfg(not(test))] -cfg_if::cfg_if! { - if #[cfg(target_os = "android")] { - pub use self::android::log2f32; - pub use self::android::log2f64; - } else { - #[inline] - pub fn log2f32(n: f32) -> f32 { - unsafe { crate::intrinsics::log2f32(n) } - } - - #[inline] - pub fn log2f64(n: f64) -> f64 { - unsafe { crate::intrinsics::log2f64(n) } - } - } -} - -// Solaris/Illumos requires a wrapper around log, log2, and log10 functions -// because of their non-standard behavior (e.g., log(-n) returns -Inf instead -// of expected NaN). -#[cfg(not(test))] -#[cfg(any(target_os = "solaris", target_os = "illumos"))] -#[inline] -pub fn log_wrapper f64>(n: f64, log_fn: F) -> f64 { - if n.is_finite() { - if n > 0.0 { - log_fn(n) - } else if n == 0.0 { - f64::NEG_INFINITY // log(0) = -Inf - } else { - f64::NAN // log(-n) = NaN - } - } else if n.is_nan() { - n // log(NaN) = NaN - } else if n > 0.0 { - n // log(Inf) = Inf - } else { - f64::NAN // log(-Inf) = NaN - } -} - -#[cfg(not(test))] -#[cfg(not(any(target_os = "solaris", target_os = "illumos")))] -#[inline] -pub fn log_wrapper f64>(n: f64, log_fn: F) -> f64 { - log_fn(n) -} - -#[cfg(not(target_os = "uefi"))] -pub type RawOsError = i32; +/// The PAL (platform abstraction layer) contains platform-specific abstractions +/// for implementing the features in the other submodules, e.g. UNIX file +/// descriptors. +mod pal; + +// FIXME(117276): remove this, move feature implementations into individual +// submodules. +pub use pal::*; diff --git a/library/std/src/sys/pal/common/alloc.rs b/library/std/src/sys/pal/common/alloc.rs new file mode 100644 index 00000000000..b7357460f39 --- /dev/null +++ b/library/std/src/sys/pal/common/alloc.rs @@ -0,0 +1,58 @@ +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(any( + target_arch = "x86", + target_arch = "arm", + target_arch = "m68k", + target_arch = "csky", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "sparc", + 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(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "loongarch64", + target_arch = "mips64", + target_arch = "mips64r6", + 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(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/pal/common/mod.rs b/library/std/src/sys/pal/common/mod.rs new file mode 100644 index 00000000000..b35c5d30b41 --- /dev/null +++ b/library/std/src/sys/pal/common/mod.rs @@ -0,0 +1,19 @@ +// 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; +pub mod small_c_string; +#[allow(unused_imports)] +pub mod thread_local; + +#[cfg(test)] +mod tests; diff --git a/library/std/src/sys/pal/common/small_c_string.rs b/library/std/src/sys/pal/common/small_c_string.rs new file mode 100644 index 00000000000..af9b18e372d --- /dev/null +++ b/library/std/src/sys/pal/common/small_c_string.rs @@ -0,0 +1,58 @@ +use crate::ffi::{CStr, CString}; +use crate::mem::MaybeUninit; +use crate::path::Path; +use crate::slice; +use crate::{io, ptr}; + +// Make sure to stay under 4096 so the compiler doesn't insert a probe frame: +// https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html +#[cfg(not(target_os = "espidf"))] +const MAX_STACK_ALLOCATION: usize = 384; +#[cfg(target_os = "espidf")] +const MAX_STACK_ALLOCATION: usize = 32; + +const NUL_ERR: io::Error = + io::const_io_error!(io::ErrorKind::InvalidInput, "file name contained an unexpected NUL byte"); + +#[inline] +pub fn run_path_with_cstr(path: &Path, f: F) -> io::Result +where + F: FnOnce(&CStr) -> io::Result, +{ + run_with_cstr(path.as_os_str().as_encoded_bytes(), f) +} + +#[inline] +pub fn run_with_cstr(bytes: &[u8], f: F) -> io::Result +where + F: FnOnce(&CStr) -> io::Result, +{ + if bytes.len() >= MAX_STACK_ALLOCATION { + return run_with_cstr_allocating(bytes, f); + } + + let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit(); + let buf_ptr = buf.as_mut_ptr() as *mut u8; + + unsafe { + ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len()); + buf_ptr.add(bytes.len()).write(0); + } + + match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) { + Ok(s) => f(s), + Err(_) => Err(NUL_ERR), + } +} + +#[cold] +#[inline(never)] +fn run_with_cstr_allocating(bytes: &[u8], f: F) -> io::Result +where + F: FnOnce(&CStr) -> io::Result, +{ + match CString::new(bytes) { + Ok(s) => f(&s), + Err(_) => Err(NUL_ERR), + } +} diff --git a/library/std/src/sys/pal/common/tests.rs b/library/std/src/sys/pal/common/tests.rs new file mode 100644 index 00000000000..32dc18ee1cf --- /dev/null +++ b/library/std/src/sys/pal/common/tests.rs @@ -0,0 +1,66 @@ +use crate::ffi::CString; +use crate::hint::black_box; +use crate::path::Path; +use crate::sys::common::small_c_string::run_path_with_cstr; +use core::iter::repeat; + +#[test] +fn stack_allocation_works() { + let path = Path::new("abc"); + let result = run_path_with_cstr(path, |p| { + assert_eq!(p, &*CString::new(path.as_os_str().as_encoded_bytes()).unwrap()); + Ok(42) + }); + assert_eq!(result.unwrap(), 42); +} + +#[test] +fn stack_allocation_fails() { + let path = Path::new("ab\0"); + assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err()); +} + +#[test] +fn heap_allocation_works() { + let path = repeat("a").take(384).collect::(); + let path = Path::new(&path); + let result = run_path_with_cstr(path, |p| { + assert_eq!(p, &*CString::new(path.as_os_str().as_encoded_bytes()).unwrap()); + Ok(42) + }); + assert_eq!(result.unwrap(), 42); +} + +#[test] +fn heap_allocation_fails() { + let mut path = repeat("a").take(384).collect::(); + path.push('\0'); + let path = Path::new(&path); + assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err()); +} + +#[bench] +fn bench_stack_path_alloc(b: &mut test::Bencher) { + let path = repeat("a").take(383).collect::(); + let p = Path::new(&path); + b.iter(|| { + run_path_with_cstr(p, |cstr| { + black_box(cstr); + Ok(()) + }) + .unwrap(); + }); +} + +#[bench] +fn bench_heap_path_alloc(b: &mut test::Bencher) { + let path = repeat("a").take(384).collect::(); + let p = Path::new(&path); + b.iter(|| { + run_path_with_cstr(p, |cstr| { + black_box(cstr); + Ok(()) + }) + .unwrap(); + }); +} diff --git a/library/std/src/sys/pal/common/thread_local/fast_local.rs b/library/std/src/sys/pal/common/thread_local/fast_local.rs new file mode 100644 index 00000000000..9206588be06 --- /dev/null +++ b/library/std/src/sys/pal/common/thread_local/fast_local.rs @@ -0,0 +1,245 @@ +use super::lazy::LazyKeyInner; +use crate::cell::Cell; +use crate::sys::thread_local_dtor::register_dtor; +use crate::{fmt, mem, panic}; + +#[doc(hidden)] +#[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)] +#[allow_internal_unsafe] +#[unstable(feature = "thread_local_internals", issue = "none")] +#[rustc_macro_transparency = "semitransparent"] +pub macro thread_local_inner { + // used to generate the `LocalKey` value for const-initialized thread locals + (@key $t:ty, const $init:expr) => {{ + #[inline] + #[deny(unsafe_op_in_unsafe_fn)] + // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint + #[cfg_attr(not(bootstrap), allow(static_mut_ref))] + unsafe fn __getit( + _init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + const INIT_EXPR: $t = $init; + // If the platform has support for `#[thread_local]`, use it. + #[thread_local] + static mut VAL: $t = INIT_EXPR; + + // If a dtor isn't needed we can do something "very raw" and + // just get going. + if !$crate::mem::needs_drop::<$t>() { + unsafe { + return $crate::option::Option::Some(&VAL) + } + } + + // 0 == dtor not registered + // 1 == dtor registered, dtor not run + // 2 == dtor registered and is running or has run + #[thread_local] + static STATE: $crate::cell::Cell<$crate::primitive::u8> = $crate::cell::Cell::new(0); + + // Safety: Performs `drop_in_place(ptr as *mut $t)`, and requires + // all that comes with it. + unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) { + $crate::thread::local_impl::abort_on_dtor_unwind(|| { + let old_state = STATE.replace(2); + $crate::debug_assert_eq!(old_state, 1); + // Safety: safety requirement is passed on to caller. + unsafe { $crate::ptr::drop_in_place(ptr.cast::<$t>()); } + }); + } + + unsafe { + match STATE.get() { + // 0 == we haven't registered a destructor, so do + // so now. + 0 => { + $crate::thread::local_impl::Key::<$t>::register_dtor( + $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8, + destroy, + ); + STATE.set(1); + $crate::option::Option::Some(&VAL) + } + // 1 == the destructor is registered and the value + // is valid, so return the pointer. + 1 => $crate::option::Option::Some(&VAL), + // otherwise the destructor has already run, so we + // can't give access. + _ => $crate::option::Option::None, + } + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + }}, + + // used to generate the `LocalKey` value for `thread_local!` + (@key $t:ty, $init:expr) => { + { + #[inline] + fn __init() -> $t { $init } + + #[inline] + unsafe fn __getit( + init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + #[thread_local] + static __KEY: $crate::thread::local_impl::Key<$t> = + $crate::thread::local_impl::Key::<$t>::new(); + + unsafe { + __KEY.get(move || { + if let $crate::option::Option::Some(init) = init { + if let $crate::option::Option::Some(value) = init.take() { + return value; + } else if $crate::cfg!(debug_assertions) { + $crate::unreachable!("missing default value"); + } + } + __init() + }) + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + } + }, + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); + }, +} + +#[derive(Copy, Clone)] +enum DtorState { + Unregistered, + Registered, + RunningOrHasRun, +} + +// This data structure has been carefully constructed so that the fast path +// only contains one branch on x86. That optimization is necessary to avoid +// duplicated tls lookups on OSX. +// +// LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 +pub struct Key { + // If `LazyKeyInner::get` returns `None`, that indicates either: + // * The value has never been initialized + // * The value is being recursively initialized + // * The value has already been destroyed or is being destroyed + // To determine which kind of `None`, check `dtor_state`. + // + // This is very optimizer friendly for the fast path - initialized but + // not yet dropped. + inner: LazyKeyInner, + + // Metadata to keep track of the state of the destructor. Remember that + // this variable is thread-local, not global. + dtor_state: Cell, +} + +impl fmt::Debug for Key { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Key").finish_non_exhaustive() + } +} +impl Key { + pub const fn new() -> Key { + Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } + } + + // note that this is just a publicly-callable function only for the + // const-initialized form of thread locals, basically a way to call the + // free `register_dtor` function defined elsewhere in std. + pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + unsafe { + register_dtor(a, dtor); + } + } + + pub unsafe fn get T>(&self, init: F) -> Option<&'static T> { + // SAFETY: See the definitions of `LazyKeyInner::get` and + // `try_initialize` for more information. + // + // The caller must ensure no mutable references are ever active to + // the inner cell or the inner T when this is called. + // The `try_initialize` is dependant on the passed `init` function + // for this. + unsafe { + match self.inner.get() { + Some(val) => Some(val), + None => self.try_initialize(init), + } + } + } + + // `try_initialize` is only called once per fast thread local variable, + // except in corner cases where thread_local dtors reference other + // thread_local's, or it is being recursively initialized. + // + // Macos: Inlining this function can cause two `tlv_get_addr` calls to + // be performed for every call to `Key::get`. + // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 + #[inline(never)] + unsafe fn try_initialize T>(&self, init: F) -> Option<&'static T> { + // SAFETY: See comment above (this function doc). + if !mem::needs_drop::() || unsafe { self.try_register_dtor() } { + // SAFETY: See comment above (this function doc). + Some(unsafe { self.inner.initialize(init) }) + } else { + None + } + } + + // `try_register_dtor` is only called once per fast thread local + // variable, except in corner cases where thread_local dtors reference + // other thread_local's, or it is being recursively initialized. + unsafe fn try_register_dtor(&self) -> bool { + match self.dtor_state.get() { + DtorState::Unregistered => { + // SAFETY: dtor registration happens before initialization. + // Passing `self` as a pointer while using `destroy_value` + // is safe because the function will build a pointer to a + // Key, which is the type of self and so find the correct + // size. + unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::) }; + self.dtor_state.set(DtorState::Registered); + true + } + DtorState::Registered => { + // recursively initialized + true + } + DtorState::RunningOrHasRun => false, + } + } +} + +unsafe extern "C" fn destroy_value(ptr: *mut u8) { + let ptr = ptr as *mut Key; + + // SAFETY: + // + // The pointer `ptr` has been built just above and comes from + // `try_register_dtor` where it is originally a Key coming from `self`, + // making it non-NUL and of the correct type. + // + // Right before we run the user destructor be sure to set the + // `Option` to `None`, and `dtor_state` to `RunningOrHasRun`. This + // causes future calls to `get` to run `try_initialize_drop` again, + // which will now fail, and return `None`. + // + // Wrap the call in a catch to ensure unwinding is caught in the event + // a panic takes place in a destructor. + if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe { + let value = (*ptr).inner.take(); + (*ptr).dtor_state.set(DtorState::RunningOrHasRun); + drop(value); + })) { + rtabort!("thread local panicked on drop"); + } +} diff --git a/library/std/src/sys/pal/common/thread_local/mod.rs b/library/std/src/sys/pal/common/thread_local/mod.rs new file mode 100644 index 00000000000..8b2c839f837 --- /dev/null +++ b/library/std/src/sys/pal/common/thread_local/mod.rs @@ -0,0 +1,124 @@ +#![unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")] + +// There are three thread-local implementations: "static", "fast", "OS". +// The "OS" thread local key type is accessed via platform-specific API calls and is slow, while the +// "fast" key type is accessed via code generated via LLVM, where TLS keys are set up by the linker. +// "static" is for single-threaded platforms where a global static is sufficient. + +cfg_if::cfg_if! { + if #[cfg(any(all(target_family = "wasm", not(target_feature = "atomics")), target_os = "uefi"))] { + #[doc(hidden)] + mod static_local; + #[doc(hidden)] + pub use static_local::{Key, thread_local_inner}; + } else if #[cfg(target_thread_local)] { + #[doc(hidden)] + mod fast_local; + #[doc(hidden)] + pub use fast_local::{Key, thread_local_inner}; + } else { + #[doc(hidden)] + mod os_local; + #[doc(hidden)] + pub use os_local::{Key, thread_local_inner}; + } +} + +mod lazy { + use crate::cell::UnsafeCell; + use crate::hint; + use crate::mem; + + pub struct LazyKeyInner { + inner: UnsafeCell>, + } + + impl LazyKeyInner { + pub const fn new() -> LazyKeyInner { + LazyKeyInner { inner: UnsafeCell::new(None) } + } + + pub unsafe fn get(&self) -> Option<&'static T> { + // SAFETY: The caller must ensure no reference is ever handed out to + // the inner cell nor mutable reference to the Option inside said + // cell. This make it safe to hand a reference, though the lifetime + // of 'static is itself unsafe, making the get method unsafe. + unsafe { (*self.inner.get()).as_ref() } + } + + /// The caller must ensure that no reference is active: this method + /// needs unique access. + pub unsafe fn initialize T>(&self, init: F) -> &'static T { + // Execute the initialization up front, *then* move it into our slot, + // just in case initialization fails. + let value = init(); + let ptr = self.inner.get(); + + // SAFETY: + // + // note that this can in theory just be `*ptr = Some(value)`, but due to + // the compiler will currently codegen that pattern with something like: + // + // ptr::drop_in_place(ptr) + // ptr::write(ptr, Some(value)) + // + // Due to this pattern it's possible for the destructor of the value in + // `ptr` (e.g., if this is being recursively initialized) to re-access + // TLS, in which case there will be a `&` and `&mut` pointer to the same + // value (an aliasing violation). To avoid setting the "I'm running a + // destructor" flag we just use `mem::replace` which should sequence the + // operations a little differently and make this safe to call. + // + // The precondition also ensures that we are the only one accessing + // `self` at the moment so replacing is fine. + unsafe { + let _ = mem::replace(&mut *ptr, Some(value)); + } + + // SAFETY: With the call to `mem::replace` it is guaranteed there is + // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked` + // will never be reached. + unsafe { + // After storing `Some` we want to get a reference to the contents of + // what we just stored. While we could use `unwrap` here and it should + // always work it empirically doesn't seem to always get optimized away, + // which means that using something like `try_with` can pull in + // panicking code and cause a large size bloat. + match *ptr { + Some(ref x) => x, + None => hint::unreachable_unchecked(), + } + } + } + + /// The other methods hand out references while taking &self. + /// As such, callers of this method must ensure no `&` and `&mut` are + /// available and used at the same time. + #[allow(unused)] + pub unsafe fn take(&mut self) -> Option { + // SAFETY: See doc comment for this method. + unsafe { (*self.inner.get()).take() } + } + } +} + +/// Run a callback in a scenario which must not unwind (such as a `extern "C" +/// fn` declared in a user crate). If the callback unwinds anyway, then +/// `rtabort` with a message about thread local panicking on drop. +#[inline] +pub fn abort_on_dtor_unwind(f: impl FnOnce()) { + // Using a guard like this is lower cost. + let guard = DtorUnwindGuard; + f(); + core::mem::forget(guard); + + struct DtorUnwindGuard; + impl Drop for DtorUnwindGuard { + #[inline] + fn drop(&mut self) { + // This is not terribly descriptive, but it doesn't need to be as we'll + // already have printed a panic message at this point. + rtabort!("thread local panicked on drop"); + } + } +} diff --git a/library/std/src/sys/pal/common/thread_local/os_local.rs b/library/std/src/sys/pal/common/thread_local/os_local.rs new file mode 100644 index 00000000000..7cf29192122 --- /dev/null +++ b/library/std/src/sys/pal/common/thread_local/os_local.rs @@ -0,0 +1,185 @@ +use super::lazy::LazyKeyInner; +use crate::cell::Cell; +use crate::sys_common::thread_local_key::StaticKey as OsStaticKey; +use crate::{fmt, marker, panic, ptr}; + +#[doc(hidden)] +#[allow_internal_unstable(thread_local_internals)] +#[allow_internal_unsafe] +#[unstable(feature = "thread_local_internals", issue = "none")] +#[rustc_macro_transparency = "semitransparent"] +pub macro thread_local_inner { + // used to generate the `LocalKey` value for const-initialized thread locals + (@key $t:ty, const $init:expr) => {{ + #[inline] + #[deny(unsafe_op_in_unsafe_fn)] + unsafe fn __getit( + _init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + const INIT_EXPR: $t = $init; + + // On platforms without `#[thread_local]` we fall back to the + // same implementation as below for os thread locals. + #[inline] + const fn __init() -> $t { INIT_EXPR } + static __KEY: $crate::thread::local_impl::Key<$t> = + $crate::thread::local_impl::Key::new(); + unsafe { + __KEY.get(move || { + if let $crate::option::Option::Some(init) = _init { + if let $crate::option::Option::Some(value) = init.take() { + return value; + } else if $crate::cfg!(debug_assertions) { + $crate::unreachable!("missing initial value"); + } + } + __init() + }) + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + }}, + + // used to generate the `LocalKey` value for `thread_local!` + (@key $t:ty, $init:expr) => { + { + #[inline] + fn __init() -> $t { $init } + + // `#[inline] does not work on windows-gnu due to linking errors around dllimports. + // See https://github.com/rust-lang/rust/issues/109797. + #[cfg_attr(not(windows), inline)] + unsafe fn __getit( + init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + static __KEY: $crate::thread::local_impl::Key<$t> = + $crate::thread::local_impl::Key::new(); + + unsafe { + __KEY.get(move || { + if let $crate::option::Option::Some(init) = init { + if let $crate::option::Option::Some(value) = init.take() { + return value; + } else if $crate::cfg!(debug_assertions) { + $crate::unreachable!("missing default value"); + } + } + __init() + }) + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + } + }, + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); + }, +} + +/// Use a regular global static to store this key; the state provided will then be +/// thread-local. +pub struct Key { + // OS-TLS key that we'll use to key off. + os: OsStaticKey, + marker: marker::PhantomData>, +} + +impl fmt::Debug for Key { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Key").finish_non_exhaustive() + } +} + +unsafe impl Sync for Key {} + +struct Value { + inner: LazyKeyInner, + key: &'static Key, +} + +impl Key { + #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] + pub const fn new() -> Key { + Key { os: OsStaticKey::new(Some(destroy_value::)), marker: marker::PhantomData } + } + + /// It is a requirement for the caller to ensure that no mutable + /// reference is active when this method is called. + pub unsafe fn get(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> { + // SAFETY: See the documentation for this method. + let ptr = unsafe { self.os.get() as *mut Value }; + if ptr.addr() > 1 { + // SAFETY: the check ensured the pointer is safe (its destructor + // is not running) + it is coming from a trusted source (self). + if let Some(ref value) = unsafe { (*ptr).inner.get() } { + return Some(value); + } + } + // SAFETY: At this point we are sure we have no value and so + // initializing (or trying to) is safe. + unsafe { self.try_initialize(init) } + } + + // `try_initialize` is only called once per os thread local variable, + // except in corner cases where thread_local dtors reference other + // thread_local's, or it is being recursively initialized. + unsafe fn try_initialize(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> { + // SAFETY: No mutable references are ever handed out meaning getting + // the value is ok. + let ptr = unsafe { self.os.get() as *mut Value }; + if ptr.addr() == 1 { + // destructor is running + return None; + } + + let ptr = if ptr.is_null() { + // If the lookup returned null, we haven't initialized our own + // local copy, so do that now. + let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self })); + // SAFETY: At this point we are sure there is no value inside + // ptr so setting it will not affect anyone else. + unsafe { + self.os.set(ptr as *mut u8); + } + ptr + } else { + // recursive initialization + ptr + }; + + // SAFETY: ptr has been ensured as non-NUL just above an so can be + // dereferenced safely. + unsafe { Some((*ptr).inner.initialize(init)) } + } +} + +unsafe extern "C" fn destroy_value(ptr: *mut u8) { + // SAFETY: + // + // The OS TLS ensures that this key contains a null value when this + // destructor starts to run. We set it back to a sentinel value of 1 to + // ensure that any future calls to `get` for this thread will return + // `None`. + // + // Note that to prevent an infinite loop we reset it back to null right + // before we return from the destructor ourselves. + // + // Wrap the call in a catch to ensure unwinding is caught in the event + // a panic takes place in a destructor. + if let Err(_) = panic::catch_unwind(|| unsafe { + let ptr = Box::from_raw(ptr as *mut Value); + let key = ptr.key; + key.os.set(ptr::invalid_mut(1)); + drop(ptr); + key.os.set(ptr::null_mut()); + }) { + rtabort!("thread local panicked on drop"); + } +} diff --git a/library/std/src/sys/pal/common/thread_local/static_local.rs b/library/std/src/sys/pal/common/thread_local/static_local.rs new file mode 100644 index 00000000000..51cba66fad7 --- /dev/null +++ b/library/std/src/sys/pal/common/thread_local/static_local.rs @@ -0,0 +1,107 @@ +use super::lazy::LazyKeyInner; +use crate::fmt; + +#[doc(hidden)] +#[allow_internal_unstable(thread_local_internals)] +#[allow_internal_unsafe] +#[unstable(feature = "thread_local_internals", issue = "none")] +#[rustc_macro_transparency = "semitransparent"] +pub macro thread_local_inner { + // used to generate the `LocalKey` value for const-initialized thread locals + (@key $t:ty, const $init:expr) => {{ + #[inline] // see comments below + #[deny(unsafe_op_in_unsafe_fn)] + // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint + #[cfg_attr(not(bootstrap), allow(static_mut_ref))] + unsafe fn __getit( + _init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + const INIT_EXPR: $t = $init; + + // wasm without atomics maps directly to `static mut`, and dtors + // aren't implemented because thread dtors aren't really a thing + // on wasm right now + // + // FIXME(#84224) this should come after the `target_thread_local` + // block. + static mut VAL: $t = INIT_EXPR; + unsafe { $crate::option::Option::Some(&VAL) } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + }}, + + // used to generate the `LocalKey` value for `thread_local!` + (@key $t:ty, $init:expr) => { + { + #[inline] + fn __init() -> $t { $init } + #[inline] + unsafe fn __getit( + init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + static __KEY: $crate::thread::local_impl::Key<$t> = + $crate::thread::local_impl::Key::new(); + + unsafe { + __KEY.get(move || { + if let $crate::option::Option::Some(init) = init { + if let $crate::option::Option::Some(value) = init.take() { + return value; + } else if $crate::cfg!(debug_assertions) { + $crate::unreachable!("missing default value"); + } + } + __init() + }) + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + } + }, + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); + }, +} + +/// On some targets like wasm there's no threads, so no need to generate +/// thread locals and we can instead just use plain statics! + +pub struct Key { + inner: LazyKeyInner, +} + +unsafe impl Sync for Key {} + +impl fmt::Debug for Key { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Key").finish_non_exhaustive() + } +} + +impl Key { + pub const fn new() -> Key { + Key { inner: LazyKeyInner::new() } + } + + pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> { + // SAFETY: The caller must ensure no reference is ever handed out to + // the inner cell nor mutable reference to the Option inside said + // cell. This make it safe to hand a reference, though the lifetime + // of 'static is itself unsafe, making the get method unsafe. + let value = unsafe { + match self.inner.get() { + Some(ref value) => value, + None => self.inner.initialize(init), + } + }; + + Some(value) + } +} diff --git a/library/std/src/sys/pal/hermit/alloc.rs b/library/std/src/sys/pal/hermit/alloc.rs new file mode 100644 index 00000000000..d153914e77e --- /dev/null +++ b/library/std/src/sys/pal/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/pal/hermit/args.rs b/library/std/src/sys/pal/hermit/args.rs new file mode 100644 index 00000000000..220a76e4b12 --- /dev/null +++ b/library/std/src/sys/pal/hermit/args.rs @@ -0,0 +1,70 @@ +use crate::ffi::{c_char, CStr, OsString}; +use crate::fmt; +use crate::os::hermit::ffi::OsStringExt; +use crate::ptr; +use crate::sync::atomic::{ + AtomicIsize, AtomicPtr, + Ordering::{Acquire, Relaxed, Release}, +}; +use crate::vec; + +static ARGC: AtomicIsize = AtomicIsize::new(0); +static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut()); + +/// One-time global initialization. +pub unsafe fn init(argc: isize, argv: *const *const u8) { + ARGC.store(argc, Relaxed); + // Use release ordering here to broadcast writes by the OS. + ARGV.store(argv as *mut *const u8, Release); +} + +/// Returns the command line arguments +pub fn args() -> Args { + // Synchronize with the store above. + let argv = ARGV.load(Acquire); + // If argv has not been initialized yet, do not return any arguments. + let argc = if argv.is_null() { 0 } else { ARGC.load(Relaxed) }; + let args: Vec = (0..argc) + .map(|i| unsafe { + let cstr = CStr::from_ptr(*argv.offset(i) as *const c_char); + OsStringExt::from_vec(cstr.to_bytes().to_vec()) + }) + .collect(); + + Args { iter: args.into_iter() } +} + +pub struct Args { + iter: vec::IntoIter, +} + +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 { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + 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 { + self.iter.next_back() + } +} diff --git a/library/std/src/sys/pal/hermit/env.rs b/library/std/src/sys/pal/hermit/env.rs new file mode 100644 index 00000000000..7a0fcb31ef2 --- /dev/null +++ b/library/std/src/sys/pal/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/pal/hermit/fd.rs b/library/std/src/sys/pal/hermit/fd.rs new file mode 100644 index 00000000000..ccde05aa1d7 --- /dev/null +++ b/library/std/src/sys/pal/hermit/fd.rs @@ -0,0 +1,101 @@ +#![unstable(reason = "not public", issue = "none", feature = "fd")] + +use crate::io::{self, Read}; +use crate::os::hermit::io::{FromRawFd, OwnedFd, RawFd}; +use crate::sys::cvt; +use crate::sys::hermit::abi; +use crate::sys::unsupported; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +use crate::os::hermit::io::*; + +#[derive(Debug)] +pub struct FileDesc { + fd: OwnedFd, +} + +impl FileDesc { + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let result = cvt(unsafe { abi::read(self.fd.as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?; + Ok(result as usize) + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut me = self; + (&mut me).read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let result = cvt(unsafe { abi::write(self.fd.as_raw_fd(), buf.as_ptr(), buf.len()) })?; + Ok(result as usize) + } + + pub fn duplicate(&self) -> io::Result { + self.duplicate_path(&[]) + } + pub fn duplicate_path(&self, _path: &[u8]) -> io::Result { + unsupported() + } + + pub fn nonblocking(&self) -> io::Result { + 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 { + (**self).read(buf) + } +} + +impl IntoInner for FileDesc { + fn into_inner(self) -> OwnedFd { + self.fd + } +} + +impl FromInner for FileDesc { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self { fd: owned_fd } + } +} + +impl FromRawFd for FileDesc { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self { fd: FromRawFd::from_raw_fd(raw_fd) } + } +} + +impl AsInner for FileDesc { + #[inline] + fn as_inner(&self) -> &OwnedFd { + &self.fd + } +} + +impl AsFd for FileDesc { + fn as_fd(&self) -> BorrowedFd<'_> { + self.fd.as_fd() + } +} + +impl AsRawFd for FileDesc { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} + +impl IntoRawFd for FileDesc { + fn into_raw_fd(self) -> RawFd { + self.fd.into_raw_fd() + } +} diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs new file mode 100644 index 00000000000..6aa4ea7f5b4 --- /dev/null +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -0,0 +1,468 @@ +use crate::ffi::{CStr, OsString}; +use crate::fmt; +use crate::hash::{Hash, Hasher}; +use crate::io::{self, Error, ErrorKind}; +use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::path::{Path, PathBuf}; +use crate::sys::common::small_c_string::run_path_with_cstr; +use crate::sys::cvt; +use crate::sys::hermit::abi::{ + self, 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; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; + +pub use crate::sys_common::fs::{copy, try_exists}; +//pub use crate::sys_common::fs::remove_dir_all; + +#[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, +} + +#[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 { + self.0 + } + + pub fn accessed(&self) -> io::Result { + self.0 + } + + pub fn created(&self) -> io::Result { + 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(&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; + + fn next(&mut self) -> Option> { + 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 { + self.0 + } + + pub fn file_type(&self) -> io::Result { + 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: 0o777, + } + } + + 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 { + 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 { + 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 { + run_path_with_cstr(path, |path| File::open_c(&path, opts)) + } + + pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result { + 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(unsafe { FileDesc::from_raw_fd(fd as i32) })) + } + + pub fn file_attr(&self) -> io::Result { + 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 { + self.0.read(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + crate::io::default_read_vectored(|buf| self.read(buf), bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + crate::io::default_write_vectored(|buf| self.write(buf), bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + false + } + + #[inline] + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } + + pub fn seek(&self, _pos: SeekFrom) -> io::Result { + Err(Error::from_raw_os_error(22)) + } + + pub fn duplicate(&self) -> io::Result { + Err(Error::from_raw_os_error(22)) + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) + } + + pub fn set_times(&self, _times: FileTimes) -> 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() + } +} + +impl AsInner for File { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl AsInnerMut for File { + #[inline] + fn as_inner_mut(&mut self) -> &mut FileDesc { + &mut self.0 + } +} + +impl IntoInner for File { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner 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 { + #[inline] + 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)) + } +} + +pub fn readdir(_p: &Path) -> io::Result { + unsupported() +} + +pub fn unlink(path: &Path) -> io::Result<()> { + run_path_with_cstr(path, |path| cvt(unsafe { abi::unlink(path.as_ptr()) }).map(|_| ())) +} + +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 { + 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 { + unsupported() +} + +pub fn lstat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn canonicalize(_p: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/pal/hermit/futex.rs b/library/std/src/sys/pal/hermit/futex.rs new file mode 100644 index 00000000000..427d8ff6f2e --- /dev/null +++ b/library/std/src/sys/pal/hermit/futex.rs @@ -0,0 +1,39 @@ +use super::abi; +use crate::ptr::null; +use crate::sync::atomic::AtomicU32; +use crate::time::Duration; + +pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { + // Calculate the timeout as a relative timespec. + // + // Overflows are rounded up to an infinite timeout (None). + let timespec = timeout.and_then(|dur| { + Some(abi::timespec { + tv_sec: dur.as_secs().try_into().ok()?, + tv_nsec: dur.subsec_nanos().into(), + }) + }); + + let r = unsafe { + abi::futex_wait( + futex.as_ptr(), + expected, + timespec.as_ref().map_or(null(), |t| t as *const abi::timespec), + abi::FUTEX_RELATIVE_TIMEOUT, + ) + }; + + r != -abi::errno::ETIMEDOUT +} + +#[inline] +pub fn futex_wake(futex: &AtomicU32) -> bool { + unsafe { abi::futex_wake(futex.as_ptr(), 1) > 0 } +} + +#[inline] +pub fn futex_wake_all(futex: &AtomicU32) { + unsafe { + abi::futex_wake(futex.as_ptr(), i32::MAX); + } +} diff --git a/library/std/src/sys/pal/hermit/memchr.rs b/library/std/src/sys/pal/hermit/memchr.rs new file mode 100644 index 00000000000..9967482197e --- /dev/null +++ b/library/std/src/sys/pal/hermit/memchr.rs @@ -0,0 +1 @@ +pub use core::slice::memchr::{memchr, memrchr}; diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs new file mode 100644 index 00000000000..abd7eb353f8 --- /dev/null +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -0,0 +1,207 @@ +//! 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(missing_docs, nonstandard_style, unsafe_op_in_unsafe_fn)] + +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; +pub mod futex; +#[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; + +#[path = "../unix/locks"] +pub mod locks { + mod futex_condvar; + mod futex_mutex; + mod futex_rwlock; + pub(crate) use futex_condvar::Condvar; + pub(crate) use futex_mutex::Mutex; + pub(crate) use futex_rwlock::RwLock; +} + +use crate::io::ErrorKind; +use crate::os::hermit::abi; + +pub fn unsupported() -> crate::io::Result { + 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", + ) +} + +pub fn abort_internal() -> ! { + unsafe { + abi::abort(); + } +} + +pub fn hashmap_random_keys() -> (u64, u64) { + let mut buf = [0; 16]; + let mut slice = &mut buf[..]; + while !slice.is_empty() { + let res = cvt(unsafe { abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) }) + .expect("failed to generate random hashmap keys"); + slice = &mut slice[res as usize..]; + } + + let key1 = buf[..8].try_into().unwrap(); + let key2 = buf[8..].try_into().unwrap(); + (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) +} + +// 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, _sigpipe: u8) { + 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() {} + +#[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); +} + +#[inline] +pub(crate) fn is_interrupted(errno: i32) -> bool { + errno == abi::errno::EINTR +} + +pub fn decode_error_kind(errno: i32) -> ErrorKind { + match errno { + abi::errno::EACCES => ErrorKind::PermissionDenied, + abi::errno::EADDRINUSE => ErrorKind::AddrInUse, + abi::errno::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, + abi::errno::EAGAIN => ErrorKind::WouldBlock, + abi::errno::ECONNABORTED => ErrorKind::ConnectionAborted, + abi::errno::ECONNREFUSED => ErrorKind::ConnectionRefused, + abi::errno::ECONNRESET => ErrorKind::ConnectionReset, + abi::errno::EEXIST => ErrorKind::AlreadyExists, + abi::errno::EINTR => ErrorKind::Interrupted, + abi::errno::EINVAL => ErrorKind::InvalidInput, + abi::errno::ENOENT => ErrorKind::NotFound, + abi::errno::ENOTCONN => ErrorKind::NotConnected, + abi::errno::EPERM => ErrorKind::PermissionDenied, + abi::errno::EPIPE => ErrorKind::BrokenPipe, + abi::errno::ETIMEDOUT => ErrorKind::TimedOut, + _ => ErrorKind::Uncategorized, + } +} + +#[doc(hidden)] +pub trait IsNegative { + fn is_negative(&self) -> bool; + fn negate(&self) -> i32; +} + +macro_rules! impl_is_negative { + ($($t:ident)*) => ($(impl IsNegative for $t { + fn is_negative(&self) -> bool { + *self < 0 + } + + fn negate(&self) -> i32 { + i32::try_from(-(*self)).unwrap() + } + })*) +} + +impl IsNegative for i32 { + fn is_negative(&self) -> bool { + *self < 0 + } + + fn negate(&self) -> i32 { + -(*self) + } +} +impl_is_negative! { i8 i16 i64 isize } + +pub fn cvt(t: T) -> crate::io::Result { + if t.is_negative() { + let e = decode_error_kind(t.negate()); + Err(crate::io::Error::from(e)) + } else { + Ok(t) + } +} + +pub fn cvt_r(mut f: F) -> crate::io::Result +where + T: IsNegative, + F: FnMut() -> T, +{ + loop { + match cvt(f()) { + Err(ref e) if e.is_interrupted() => {} + other => return other, + } + } +} diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs new file mode 100644 index 00000000000..bd8b493d65a --- /dev/null +++ b/library/std/src/sys/pal/hermit/net.rs @@ -0,0 +1,372 @@ +#![allow(dead_code)] + +use crate::cmp; +use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::mem; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; +use crate::sys::hermit::fd::FileDesc; +use crate::sys::time::Instant; +use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; + +use core::ffi::c_int; + +#[allow(unused_extern_crates)] +pub extern crate hermit_abi as netc; + +pub use crate::sys::{cvt, cvt_r}; + +pub type wrlen_t = usize; + +pub fn cvt_gai(err: i32) -> io::Result<()> { + if err == 0 { + return Ok(()); + } + + let detail = ""; + + Err(io::Error::new( + io::ErrorKind::Uncategorized, + &format!("failed to lookup address information: {detail}")[..], + )) +} + +pub fn init() {} + +#[derive(Debug)] +pub struct Socket(FileDesc); + +impl Socket { + pub fn new(addr: &SocketAddr, ty: i32) -> io::Result { + let fam = match *addr { + SocketAddr::V4(..) => netc::AF_INET, + SocketAddr::V6(..) => netc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: i32, ty: i32) -> io::Result { + let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; + Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) + } + + pub fn new_pair(_fam: i32, _ty: i32) -> io::Result<(Socket, Socket)> { + unimplemented!() + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; + Ok(()) + } + + 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.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(netc::errno::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::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 { netc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if !err.is_interrupted() { + return Err(err); + } + } + 0 => {} + _ => { + // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look + // for POLLHUP rather than read readiness + if pollfd.revents & netc::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 netc::sockaddr, + len: *mut netc::socklen_t, + ) -> io::Result { + let fd = cvt(unsafe { netc::accept(self.0.as_raw_fd(), storage, len) })?; + Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) + } + + pub fn duplicate(&self) -> io::Result { + let fd = cvt(unsafe { netc::dup(self.0.as_raw_fd()) })?; + Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: i32) -> io::Result<()> { + let ret = cvt(unsafe { + netc::recv( + self.0.as_raw_fd(), + buf.as_mut().as_mut_ptr() as *mut u8, + buf.capacity(), + flags, + ) + })?; + unsafe { + buf.advance(ret as usize); + } + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let mut size: isize = 0; + + for i in bufs.iter_mut() { + let ret: isize = + cvt(unsafe { netc::read(self.0.as_raw_fd(), i.as_mut_ptr(), i.len()) })?; + + if ret != 0 { + size += ret; + } + } + + Ok(size.try_into().unwrap()) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> 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.as_raw_fd(), + buf.as_mut_ptr(), + 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, netc::MSG_PEEK) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let sz = cvt(unsafe { netc::write(self.0.as_raw_fd(), buf.as_ptr(), buf.len()) })?; + Ok(sz.try_into().unwrap()) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let mut size: isize = 0; + + for i in bufs.iter() { + size += cvt(unsafe { netc::write(self.0.as_raw_fd(), i.as_ptr(), i.len()) })?; + } + + Ok(size.try_into().unwrap()) + } + + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn set_timeout(&self, dur: Option, kind: i32) -> 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::time_t::MAX as u64 { + netc::time_t::MAX + } else { + dur.as_secs() as netc::time_t + }; + let mut timeout = netc::timeval { + tv_sec: secs, + tv_usec: dur.subsec_micros() as netc::suseconds_t, + }; + 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: i32) -> io::Result> { + 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_socket(self.as_raw_fd(), how) })?; + Ok(()) + } + + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + let linger = netc::linger { + l_onoff: linger.is_some() as i32, + l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, + }; + + setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) + } + + pub fn linger(&self) -> io::Result> { + 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<()> { + let value: i32 = if nodelay { 1 } else { 0 }; + setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) + } + + pub fn nodelay(&self) -> io::Result { + let raw: i32 = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking: i32 = if nonblocking { 1 } else { 0 }; + cvt(unsafe { + netc::ioctl( + self.as_raw_fd(), + netc::FIONBIO, + &mut nonblocking as *mut _ as *mut core::ffi::c_void, + ) + }) + .map(drop) + } + + pub fn take_error(&self) -> io::Result> { + unimplemented!() + } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner 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 { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs new file mode 100644 index 00000000000..c79197a9ad1 --- /dev/null +++ b/library/std/src/sys/pal/hermit/os.rs @@ -0,0 +1,206 @@ +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::hermit::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 { + 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 { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + 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 { + unsupported() +} + +static mut ENV: Option>> = 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)>, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { slice } = self; + f.debug_list() + .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { iter } = self; + EnvStrDebug { slice: iter.as_slice() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + f.debug_list().entries(iter.as_slice()).finish() + } +} + +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) { + 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 { + 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 { + None +} + +pub fn exit(code: i32) -> ! { + unsafe { + abi::exit(code); + } +} + +pub fn getpid() -> u32 { + unsafe { abi::getpid() } +} diff --git a/library/std/src/sys/pal/hermit/stdio.rs b/library/std/src/sys/pal/hermit/stdio.rs new file mode 100644 index 00000000000..514de1df6f9 --- /dev/null +++ b/library/std/src/sys/pal/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 { + self.read_vectored(&mut [IoSliceMut::new(data)]) + } + + fn read_vectored(&mut self, _data: &mut [IoSliceMut<'_>]) -> io::Result { + 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 { + 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 { + 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 { + 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 { + 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 { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs new file mode 100644 index 00000000000..332151e40d0 --- /dev/null +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -0,0 +1,112 @@ +#![allow(dead_code)] + +use crate::ffi::CStr; +use crate::io; +use crate::mem; +use crate::num::NonZeroUsize; +use crate::ptr; +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, + core_id: isize, + ) -> io::Result { + let p = Box::into_raw(Box::new(p)); + let tid = abi::spawn2( + thread_start, + p.expose_addr(), + 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(ptr::from_exposed_addr::>(main).cast_mut())(); + + // run all destructors + run_dtors(); + } + } + } + + pub unsafe fn new(stack: usize, p: Box) -> io::Result { + 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 { + unsafe { Ok(NonZeroUsize::new_unchecked(abi::get_processor_count())) } +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + None + } +} diff --git a/library/std/src/sys/pal/hermit/thread_local_dtor.rs b/library/std/src/sys/pal/hermit/thread_local_dtor.rs new file mode 100644 index 00000000000..98adaf4bff1 --- /dev/null +++ b/library/std/src/sys/pal/hermit/thread_local_dtor.rs @@ -0,0 +1,29 @@ +#![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::RefCell; + +#[thread_local] +static DTORS: RefCell> = RefCell::new(Vec::new()); + +pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + match DTORS.try_borrow_mut() { + Ok(mut dtors) => dtors.push((t, dtor)), + Err(_) => rtabort!("global allocator may not use TLS"), + } +} + +// every thread call this function to run through all possible destructors +pub unsafe fn run_dtors() { + let mut list = DTORS.take(); + while !list.is_empty() { + for (ptr, dtor) in list { + dtor(ptr); + } + list = DTORS.take(); + } +} diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs new file mode 100644 index 00000000000..7d91460aba3 --- /dev/null +++ b/library/std/src/sys/pal/hermit/time.rs @@ -0,0 +1,216 @@ +#![allow(dead_code)] + +use crate::cmp::Ordering; +use crate::ops::{Add, AddAssign, Sub, SubAssign}; +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 { + 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 { + let mut secs = self.t.tv_sec.checked_add_unsigned(other.as_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 { + let mut secs = self.t.tv_sec.checked_sub_unsigned(other.as_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 { + 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(&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(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(time) + } + + #[stable(feature = "time2", since = "1.8.0")] + pub fn elapsed(&self) -> Duration { + Instant::now() - *self + } + + pub fn duration_since(&self, earlier: Instant) -> Duration { + self.checked_duration_since(earlier).unwrap_or_default() + } + + pub fn checked_duration_since(&self, earlier: Instant) -> Option { + self.checked_sub_instant(&earlier) + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option { + self.0.sub_timespec(&other.0).ok() + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_add_duration(other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_sub_duration(other)?)) + } + + pub fn checked_add(&self, duration: Duration) -> Option { + self.0.checked_add_duration(&duration).map(Instant) + } + + pub fn checked_sub(&self, duration: Duration) -> Option { + self.0.checked_sub_duration(&duration).map(Instant) + } +} + +impl Add for Instant { + type Output = Instant; + + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. See [`Instant::checked_add`] for a version without panic. + fn add(self, other: Duration) -> Instant { + self.checked_add(other).expect("overflow when adding duration to instant") + } +} + +impl AddAssign for Instant { + fn add_assign(&mut self, other: Duration) { + *self = *self + other; + } +} + +impl Sub for Instant { + type Output = Instant; + + fn sub(self, other: Duration) -> Instant { + self.checked_sub(other).expect("overflow when subtracting duration from instant") + } +} + +impl SubAssign for Instant { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; + } +} + +impl Sub for Instant { + type Output = Duration; + + /// Returns the amount of time elapsed from another instant to this one, + /// or zero duration if that instant is later than this one. + /// + /// # Panics + /// + /// Previous rust versions panicked when `other` was later than `self`. Currently this + /// method saturates. Future versions may reintroduce the panic in some circumstances. + /// See [Monotonicity]. + /// + /// [Monotonicity]: Instant#monotonicity + fn sub(self, other: Instant) -> Duration { + self.duration_since(other) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct SystemTime(Timespec); + +pub const UNIX_EPOCH: SystemTime = SystemTime(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(time) + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.sub_timespec(&other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_add_duration(other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_sub_duration(other)?)) + } +} diff --git a/library/std/src/sys/pal/itron/abi.rs b/library/std/src/sys/pal/itron/abi.rs new file mode 100644 index 00000000000..5eb14bb7e53 --- /dev/null +++ b/library/std/src/sys/pal/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; + +// 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/pal/itron/condvar.rs b/library/std/src/sys/pal/itron/condvar.rs new file mode 100644 index 00000000000..7a47cc6696a --- /dev/null +++ b/library/std/src/sys/pal/itron/condvar.rs @@ -0,0 +1,292 @@ +//! 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, +} + +unsafe impl Send for Condvar {} +unsafe impl Sync for Condvar {} + +impl Condvar { + #[inline] + pub const fn new() -> Condvar { + Condvar { waiters: SpinMutex::new(waiter_queue::WaiterQueue::new()) } + } + + pub 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 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; + } + } + + 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) }); + + mutex.lock(); + success + } +} + +mod waiter_queue { + use super::*; + + pub struct WaiterQueue { + head: Option, + } + + #[derive(Copy, Clone)] + struct ListHead { + first: NonNull, + last: NonNull, + } + + 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>, + next: Option>, + } + + 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) { + 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) -> 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) -> bool { + unsafe { waiter.as_ref().task != 0 } + } + + #[inline] + pub fn pop_front(&mut self) -> Option { + 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/pal/itron/error.rs b/library/std/src/sys/pal/itron/error.rs new file mode 100644 index 00000000000..fbc822d4eb6 --- /dev/null +++ b/library/std/src/sys/pal/itron/error.rs @@ -0,0 +1,164 @@ +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 { + 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 { + 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, + } +} + +#[inline] +pub fn is_interrupted(er: abi::ER) -> bool { + er == abi::E_RLWAI +} + +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/pal/itron/mutex.rs b/library/std/src/sys/pal/itron/mutex.rs new file mode 100644 index 00000000000..1f6cc419476 --- /dev/null +++ b/library/std/src/sys/pal/itron/mutex.rs @@ -0,0 +1,85 @@ +//! 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<()>, +} + +/// Create a mutex object. This function never panics. +fn new_mtx() -> Result { + 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() } + } + + /// 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 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 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 { + x.lock(); + Self(x) + } +} + +impl Drop for MutexGuard<'_> { + #[inline] + fn drop(&mut self) { + unsafe { self.0.unlock() }; + } +} diff --git a/library/std/src/sys/pal/itron/spin.rs b/library/std/src/sys/pal/itron/spin.rs new file mode 100644 index 00000000000..44d409444bc --- /dev/null +++ b/library/std/src/sys/pal/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 { + locked: AtomicBool, + data: UnsafeCell, +} + +impl SpinMutex { + #[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(&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 { + id: AtomicUsize, + spin: SpinMutex<()>, + extra: UnsafeCell>, +} + +const ID_UNINIT: usize = 0; + +impl SpinIdOnceCell { + #[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(&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(&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 Drop for SpinIdOnceCell { + #[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/pal/itron/task.rs b/library/std/src/sys/pal/itron/task.rs new file mode 100644 index 00000000000..94beb50a254 --- /dev/null +++ b/library/std/src/sys/pal/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 { + 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 { + 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/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs new file mode 100644 index 00000000000..ae0f718535b --- /dev/null +++ b/library/std/src/sys/pal/itron/thread.rs @@ -0,0 +1,368 @@ +//! 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, + ptr::NonNull, + sync::atomic::{AtomicUsize, Ordering}, + sys::thread_local_dtor::run_dtors, + time::Duration, +}; + +pub struct Thread { + p_inner: NonNull, + + /// The ID of the underlying task. + task: abi::ID, +} + +// Safety: There's nothing in `Thread` that ties it to the original creator. It +// can be dropped by any threads. +unsafe impl Send for Thread {} +// Safety: `Thread` provides no methods that take `&self`. +unsafe impl Sync for Thread {} + +/// 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>>, + + /// A state machine. Each transition is annotated with `[...]` in the + /// source code. + /// + /// ```text + /// + ///

: parent, : child, (?): don't-care + /// + /// DETACHED (-1) --------------------> EXITED (?) + /// finish/exd_tsk + /// ^ + /// | + /// |

detach + /// | + /// + /// INIT (0) -----------------------> FINISHED (-1) + /// finish + /// | | + /// |

join/slp_tsk |

join/del_tsk + /// | |

detach/del_tsk + /// v v + /// + /// JOINING JOINED (?) + /// (parent_tid) + /// ^ + /// \ / + /// \ finish/wup_tsk /

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::(); + +impl Thread { + /// # Safety + /// + /// See `thread::Builder::spawn_unchecked` for safety requirements. + pub unsafe fn new(stack: usize, p: Box) -> io::Result { + let inner = Box::new(ThreadInner { + start: UnsafeCell::new(ManuallyDrop::new(p)), + lifecycle: AtomicUsize::new(LIFECYCLE_INIT), + }); + + unsafe extern "C" fn trampoline(exinf: isize) { + let p_inner: *mut ThreadInner = crate::ptr::from_exposed_addr_mut(exinf as usize); + // Safety: `ThreadInner` is alive at this point + let inner = unsafe { &*p_inner }; + + // 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::AcqRel); + + 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, `*p_inner`'s ownership has been moved to + // us, and we are responsible for dropping it. The acquire + // ordering ensures that the swap operation that wrote + // `LIFECYCLE_DETACHED` happens-before `Box::from_raw( + // p_inner)`. + // Safety: See above. + let _ = unsafe { Box::from_raw(p_inner) }; + + // 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. + // + // To make the task referred to by `parent_tid` visible, we + // must use the acquire ordering 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", + ); + } + } + } + + // Safety: `Box::into_raw` returns a non-null pointer + let p_inner = unsafe { NonNull::new_unchecked(Box::into_raw(inner)) }; + + let new_task = ItronError::err_if_negative(unsafe { + abi::acre_tsk(&abi::T_CTSK { + // Activate this task immediately + tskatr: abi::TA_ACT, + exinf: p_inner.as_ptr().expose_addr() 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 { p_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(self) { + // Safety: `ThreadInner` is alive at this point + let inner = unsafe { self.p_inner.as_ref() }; + // 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::AcqRel) { + LIFECYCLE_INIT => { + // [INIT → JOINING] + // The child task will transition the state to `JOIN_FINALIZE` + // and wake us up. + // + // To make the task referred to by `current_task` visible from + // the child task's point of view, we must use the release + // ordering in the above `swap` call. + 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 `*p_inner` will not be accessed hereafter + let _inner = unsafe { Box::from_raw(self.p_inner.as_ptr()) }; + + // Skip the destructor (because it would attempt to detach the thread) + crate::mem::forget(self); + } +} + +impl Drop for Thread { + fn drop(&mut self) { + // Safety: `ThreadInner` is alive at this point + let inner = unsafe { self.p_inner.as_ref() }; + + // Detach the thread. + match inner.lifecycle.swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::AcqRel) { + LIFECYCLE_INIT => { + // [INIT → DETACHED] + // When the time comes, the child will figure out that no + // one will ever join it. + // The ownership of `*p_inner` is moved to the child thread. + // The release ordering ensures that the above swap operation on + // `lifecycle` happens-before the child thread's + // `Box::from_raw(p_inner)`. + } + 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 `*p_inner`. + // Safety: The contents of `*p_inner` will not be accessed hereafter + let _ = unsafe { Box::from_raw(self.p_inner.as_ptr()) }; + } + _ => unsafe { hint::unreachable_unchecked() }, + } + } +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + 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 { + super::unsupported() +} diff --git a/library/std/src/sys/pal/itron/thread_parking.rs b/library/std/src/sys/pal/itron/thread_parking.rs new file mode 100644 index 00000000000..fe9934439d1 --- /dev/null +++ b/library/std/src/sys/pal/itron/thread_parking.rs @@ -0,0 +1,37 @@ +use super::abi; +use super::error::expect_success_aborting; +use super::time::with_tmos; +use crate::time::Duration; + +pub type ThreadId = abi::ID; + +pub use super::task::current_task_id_aborting as current; + +pub fn park(_hint: usize) { + match unsafe { abi::slp_tsk() } { + abi::E_OK | abi::E_RLWAI => {} + err => { + expect_success_aborting(err, &"slp_tsk"); + } + } +} + +pub fn park_timeout(dur: Duration, _hint: usize) { + match with_tmos(dur, |tmo| unsafe { abi::tslp_tsk(tmo) }) { + abi::E_OK | abi::E_RLWAI | abi::E_TMOUT => {} + err => { + expect_success_aborting(err, &"tslp_tsk"); + } + } +} + +pub fn unpark(id: ThreadId, _hint: usize) { + match unsafe { abi::wup_tsk(id) } { + // It is allowed to try to wake up a destroyed or unrelated task, so we ignore all + // errors that could result from that situation. + abi::E_OK | abi::E_NOEXS | abi::E_OBJ | abi::E_QOVR => {} + err => { + expect_success_aborting(err, &"wup_tsk"); + } + } +} diff --git a/library/std/src/sys/pal/itron/time.rs b/library/std/src/sys/pal/itron/time.rs new file mode 100644 index 00000000000..427ea0d80e1 --- /dev/null +++ b/library/std/src/sys/pal/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 { + 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 { + // `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 { + // `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 { + // `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 { + // `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/pal/itron/time/tests.rs b/library/std/src/sys/pal/itron/time/tests.rs new file mode 100644 index 00000000000..d14035d9da4 --- /dev/null +++ b/library/std/src/sys/pal/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![]); + assert_eq!(dur2reltims(reltim2dur(42)).collect::>(), vec![42]); + assert_eq!( + dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), + vec![abi::TMAX_RELTIM] + ); + assert_eq!( + dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), + vec![abi::TMAX_RELTIM, 10000] + ); +} + +#[test] +fn test_dur2tmos() { + assert_eq!(dur2tmos(reltim2dur(0)).collect::>(), vec![0]); + assert_eq!(dur2tmos(reltim2dur(42)).collect::>(), vec![42]); + assert_eq!( + dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), + vec![abi::TMAX_RELTIM] + ); + assert_eq!( + dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), + vec![abi::TMAX_RELTIM, 10000] + ); +} diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs new file mode 100644 index 00000000000..88420bd3612 --- /dev/null +++ b/library/std/src/sys/pal/mod.rs @@ -0,0 +1,124 @@ +//! 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)] + +pub mod common; +mod personality; + +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(target_os = "xous")] { + mod xous; + pub use self::xous::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use self::uefi::*; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod sgx; + pub use self::sgx::*; + } else if #[cfg(target_os = "teeos")] { + mod teeos; + pub use self::teeos::*; + } else { + mod unsupported; + pub use self::unsupported::*; + } +} + +cfg_if::cfg_if! { + // Fuchsia components default to full backtrace. + if #[cfg(target_os = "fuchsia")] { + pub const FULL_BACKTRACE_DEFAULT: bool = true; + } else { + pub const FULL_BACKTRACE_DEFAULT: bool = false; + } +} + +#[cfg(not(test))] +cfg_if::cfg_if! { + if #[cfg(target_os = "android")] { + pub use self::android::log2f32; + pub use self::android::log2f64; + } else { + #[inline] + pub fn log2f32(n: f32) -> f32 { + unsafe { crate::intrinsics::log2f32(n) } + } + + #[inline] + pub fn log2f64(n: f64) -> f64 { + unsafe { crate::intrinsics::log2f64(n) } + } + } +} + +// Solaris/Illumos requires a wrapper around log, log2, and log10 functions +// because of their non-standard behavior (e.g., log(-n) returns -Inf instead +// of expected NaN). +#[cfg(not(test))] +#[cfg(any(target_os = "solaris", target_os = "illumos"))] +#[inline] +pub fn log_wrapper f64>(n: f64, log_fn: F) -> f64 { + if n.is_finite() { + if n > 0.0 { + log_fn(n) + } else if n == 0.0 { + f64::NEG_INFINITY // log(0) = -Inf + } else { + f64::NAN // log(-n) = NaN + } + } else if n.is_nan() { + n // log(NaN) = NaN + } else if n > 0.0 { + n // log(Inf) = Inf + } else { + f64::NAN // log(-Inf) = NaN + } +} + +#[cfg(not(test))] +#[cfg(not(any(target_os = "solaris", target_os = "illumos")))] +#[inline] +pub fn log_wrapper f64>(n: f64, log_fn: F) -> f64 { + log_fn(n) +} + +#[cfg(not(target_os = "uefi"))] +pub type RawOsError = i32; diff --git a/library/std/src/sys/pal/personality/dwarf/eh.rs b/library/std/src/sys/pal/personality/dwarf/eh.rs new file mode 100644 index 00000000000..a78084de0fa --- /dev/null +++ b/library/std/src/sys/pal/personality/dwarf/eh.rs @@ -0,0 +1,257 @@ +//! Parsing of GCC-style Language-Specific Data Area (LSDA) +//! For details see: +//! * +//! * +//! * +//! * +//! * +//! +//! A reference implementation may be found in the GCC source tree +//! (`/libgcc/unwind-c.c` as of this writing). + +#![allow(non_upper_case_globals)] +#![allow(unused)] + +use super::DwarfReader; +use core::mem; +use core::ptr; + +pub const DW_EH_PE_omit: u8 = 0xFF; +pub const DW_EH_PE_absptr: u8 = 0x00; + +pub const DW_EH_PE_uleb128: u8 = 0x01; +pub const DW_EH_PE_udata2: u8 = 0x02; +pub const DW_EH_PE_udata4: u8 = 0x03; +pub const DW_EH_PE_udata8: u8 = 0x04; +pub const DW_EH_PE_sleb128: u8 = 0x09; +pub const DW_EH_PE_sdata2: u8 = 0x0A; +pub const DW_EH_PE_sdata4: u8 = 0x0B; +pub const DW_EH_PE_sdata8: u8 = 0x0C; + +pub const DW_EH_PE_pcrel: u8 = 0x10; +pub const DW_EH_PE_textrel: u8 = 0x20; +pub const DW_EH_PE_datarel: u8 = 0x30; +pub const DW_EH_PE_funcrel: u8 = 0x40; +pub const DW_EH_PE_aligned: u8 = 0x50; + +pub const DW_EH_PE_indirect: u8 = 0x80; + +#[derive(Copy, Clone)] +pub struct EHContext<'a> { + pub ip: *const u8, // Current instruction pointer + pub func_start: *const u8, // Pointer to the current function + pub get_text_start: &'a dyn Fn() -> *const u8, // Get pointer to the code section + pub get_data_start: &'a dyn Fn() -> *const u8, // Get pointer to the data section +} + +/// Landing pad. +type LPad = *const u8; +pub enum EHAction { + None, + Cleanup(LPad), + Catch(LPad), + Filter(LPad), + Terminate, +} + +pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm")); + +pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result { + if lsda.is_null() { + return Ok(EHAction::None); + } + + let func_start = context.func_start; + let mut reader = DwarfReader::new(lsda); + + let start_encoding = reader.read::(); + // base address for landing pad offsets + let lpad_base = if start_encoding != DW_EH_PE_omit { + read_encoded_pointer(&mut reader, context, start_encoding)? + } else { + func_start + }; + + let ttype_encoding = reader.read::(); + if ttype_encoding != DW_EH_PE_omit { + // Rust doesn't analyze exception types, so we don't care about the type table + reader.read_uleb128(); + } + + let call_site_encoding = reader.read::(); + let call_site_table_length = reader.read_uleb128(); + let action_table = reader.ptr.add(call_site_table_length as usize); + let ip = context.ip; + + if !USING_SJLJ_EXCEPTIONS { + // read the callsite table + while reader.ptr < action_table { + // these are offsets rather than pointers; + let cs_start = read_encoded_offset(&mut reader, call_site_encoding)?; + let cs_len = read_encoded_offset(&mut reader, call_site_encoding)?; + let cs_lpad = read_encoded_offset(&mut reader, call_site_encoding)?; + let cs_action_entry = reader.read_uleb128(); + // Callsite table is sorted by cs_start, so if we've passed the ip, we + // may stop searching. + if ip < func_start.wrapping_add(cs_start) { + break; + } + if ip < func_start.wrapping_add(cs_start + cs_len) { + if cs_lpad == 0 { + return Ok(EHAction::None); + } else { + let lpad = lpad_base.wrapping_add(cs_lpad); + return Ok(interpret_cs_action(action_table, cs_action_entry, lpad)); + } + } + } + // Ip is not present in the table. This indicates a nounwind call. + Ok(EHAction::Terminate) + } else { + // SjLj version: + // The "IP" is an index into the call-site table, with two exceptions: + // -1 means 'no-action', and 0 means 'terminate'. + match ip.addr() as isize { + -1 => return Ok(EHAction::None), + 0 => return Ok(EHAction::Terminate), + _ => (), + } + let mut idx = ip.addr(); + loop { + let cs_lpad = reader.read_uleb128(); + let cs_action_entry = reader.read_uleb128(); + idx -= 1; + if idx == 0 { + // Can never have null landing pad for sjlj -- that would have + // been indicated by a -1 call site index. + // FIXME(strict provenance) + let lpad = ptr::from_exposed_addr((cs_lpad + 1) as usize); + return Ok(interpret_cs_action(action_table, cs_action_entry, lpad)); + } + } + } +} + +unsafe fn interpret_cs_action( + action_table: *const u8, + cs_action_entry: u64, + lpad: LPad, +) -> EHAction { + if cs_action_entry == 0 { + // If cs_action_entry is 0 then this is a cleanup (Drop::drop). We run these + // for both Rust panics and foreign exceptions. + EHAction::Cleanup(lpad) + } else { + // If lpad != 0 and cs_action_entry != 0, we have to check ttype_index. + // If ttype_index == 0 under the condition, we take cleanup action. + let action_record = action_table.offset(cs_action_entry as isize - 1); + let mut action_reader = DwarfReader::new(action_record); + let ttype_index = action_reader.read_sleb128(); + if ttype_index == 0 { + EHAction::Cleanup(lpad) + } else if ttype_index > 0 { + // Stop unwinding Rust panics at catch_unwind. + EHAction::Catch(lpad) + } else { + EHAction::Filter(lpad) + } + } +} + +#[inline] +fn round_up(unrounded: usize, align: usize) -> Result { + if align.is_power_of_two() { Ok((unrounded + align - 1) & !(align - 1)) } else { Err(()) } +} + +/// Read a offset (`usize`) from `reader` whose encoding is described by `encoding`. +/// +/// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext]. +/// In addition the upper ("application") part must be zero. +/// +/// # Errors +/// Returns `Err` if `encoding` +/// * is not a valid DWARF Exception Header Encoding, +/// * is `DW_EH_PE_omit`, or +/// * has a non-zero application part. +/// +/// [LSB-dwarf-ext]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html +unsafe fn read_encoded_offset(reader: &mut DwarfReader, encoding: u8) -> Result { + if encoding == DW_EH_PE_omit || encoding & 0xF0 != 0 { + return Err(()); + } + let result = match encoding & 0x0F { + // despite the name, LLVM also uses absptr for offsets instead of pointers + DW_EH_PE_absptr => reader.read::(), + DW_EH_PE_uleb128 => reader.read_uleb128() as usize, + DW_EH_PE_udata2 => reader.read::() as usize, + DW_EH_PE_udata4 => reader.read::() as usize, + DW_EH_PE_udata8 => reader.read::() as usize, + DW_EH_PE_sleb128 => reader.read_sleb128() as usize, + DW_EH_PE_sdata2 => reader.read::() as usize, + DW_EH_PE_sdata4 => reader.read::() as usize, + DW_EH_PE_sdata8 => reader.read::() as usize, + _ => return Err(()), + }; + Ok(result) +} + +/// Read a pointer from `reader` whose encoding is described by `encoding`. +/// +/// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext]. +/// +/// # Errors +/// Returns `Err` if `encoding` +/// * is not a valid DWARF Exception Header Encoding, +/// * is `DW_EH_PE_omit`, or +/// * combines `DW_EH_PE_absptr` or `DW_EH_PE_aligned` application part with an integer encoding +/// (not `DW_EH_PE_absptr`) in the value format part. +/// +/// [LSB-dwarf-ext]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html +unsafe fn read_encoded_pointer( + reader: &mut DwarfReader, + context: &EHContext<'_>, + encoding: u8, +) -> Result<*const u8, ()> { + if encoding == DW_EH_PE_omit { + return Err(()); + } + + let base_ptr = match encoding & 0x70 { + DW_EH_PE_absptr => core::ptr::null(), + // relative to address of the encoded value, despite the name + DW_EH_PE_pcrel => reader.ptr, + DW_EH_PE_funcrel => { + if context.func_start.is_null() { + return Err(()); + } + context.func_start + } + DW_EH_PE_textrel => (*context.get_text_start)(), + DW_EH_PE_datarel => (*context.get_data_start)(), + // aligned means the value is aligned to the size of a pointer + DW_EH_PE_aligned => { + reader.ptr = + reader.ptr.with_addr(round_up(reader.ptr.addr(), mem::size_of::<*const u8>())?); + core::ptr::null() + } + _ => return Err(()), + }; + + let mut ptr = if base_ptr.is_null() { + // any value encoding other than absptr would be nonsensical here; + // there would be no source of pointer provenance + if encoding & 0x0F != DW_EH_PE_absptr { + return Err(()); + } + reader.read::<*const u8>() + } else { + let offset = read_encoded_offset(reader, encoding & 0x0F)?; + base_ptr.wrapping_add(offset) + }; + + if encoding & DW_EH_PE_indirect != 0 { + ptr = *(ptr.cast::<*const u8>()); + } + + Ok(ptr) +} diff --git a/library/std/src/sys/pal/personality/dwarf/mod.rs b/library/std/src/sys/pal/personality/dwarf/mod.rs new file mode 100644 index 00000000000..652fbe95a14 --- /dev/null +++ b/library/std/src/sys/pal/personality/dwarf/mod.rs @@ -0,0 +1,73 @@ +//! Utilities for parsing DWARF-encoded data streams. +//! See , +//! DWARF-4 standard, Section 7 - "Data Representation" + +// This module is used only by x86_64-pc-windows-gnu for now, but we +// are compiling it everywhere to avoid regressions. +#![allow(unused)] + +#[cfg(test)] +mod tests; + +pub mod eh; + +use core::mem; + +pub struct DwarfReader { + pub ptr: *const u8, +} + +#[repr(C, packed)] +struct Unaligned(T); + +impl DwarfReader { + pub fn new(ptr: *const u8) -> DwarfReader { + DwarfReader { ptr } + } + + // DWARF streams are packed, so e.g., a u32 would not necessarily be aligned + // on a 4-byte boundary. This may cause problems on platforms with strict + // alignment requirements. By wrapping data in a "packed" struct, we are + // telling the backend to generate "misalignment-safe" code. + pub unsafe fn read(&mut self) -> T { + let Unaligned(result) = *(self.ptr as *const Unaligned); + self.ptr = self.ptr.add(mem::size_of::()); + result + } + + // ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable + // Length Data". + pub unsafe fn read_uleb128(&mut self) -> u64 { + let mut shift: usize = 0; + let mut result: u64 = 0; + let mut byte: u8; + loop { + byte = self.read::(); + result |= ((byte & 0x7F) as u64) << shift; + shift += 7; + if byte & 0x80 == 0 { + break; + } + } + result + } + + pub unsafe fn read_sleb128(&mut self) -> i64 { + let mut shift: u32 = 0; + let mut result: u64 = 0; + let mut byte: u8; + loop { + byte = self.read::(); + result |= ((byte & 0x7F) as u64) << shift; + shift += 7; + if byte & 0x80 == 0 { + break; + } + } + // sign-extend + if shift < u64::BITS && (byte & 0x40) != 0 { + result |= (!0 as u64) << shift; + } + result as i64 + } +} diff --git a/library/std/src/sys/pal/personality/dwarf/tests.rs b/library/std/src/sys/pal/personality/dwarf/tests.rs new file mode 100644 index 00000000000..1644f37083a --- /dev/null +++ b/library/std/src/sys/pal/personality/dwarf/tests.rs @@ -0,0 +1,19 @@ +use super::*; + +#[test] +fn dwarf_reader() { + let encoded: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 0xE5, 0x8E, 0x26, 0x9B, 0xF1, 0x59, 0xFF, 0xFF]; + + let mut reader = DwarfReader::new(encoded.as_ptr()); + + unsafe { + assert!(reader.read::() == u8::to_be(1u8)); + assert!(reader.read::() == u16::to_be(0x0203)); + assert!(reader.read::() == u32::to_be(0x04050607)); + + assert!(reader.read_uleb128() == 624485); + assert!(reader.read_sleb128() == -624485); + + assert!(reader.read::() == i8::to_be(-1)); + } +} diff --git a/library/std/src/sys/pal/personality/emcc.rs b/library/std/src/sys/pal/personality/emcc.rs new file mode 100644 index 00000000000..cb52ae89b19 --- /dev/null +++ b/library/std/src/sys/pal/personality/emcc.rs @@ -0,0 +1,20 @@ +//! On Emscripten Rust panics are wrapped in C++ exceptions, so we just forward +//! to `__gxx_personality_v0` which is provided by Emscripten. + +use crate::ffi::c_int; +use unwind as uw; + +// This is required by the compiler to exist (e.g., it's a lang item), but it's +// never actually called by the compiler. Emscripten EH doesn't use a +// personality function at all, it instead uses __cxa_find_matching_catch. +// Wasm error handling would use __gxx_personality_wasm0. +#[lang = "eh_personality"] +unsafe extern "C" fn rust_eh_personality( + _version: c_int, + _actions: uw::_Unwind_Action, + _exception_class: uw::_Unwind_Exception_Class, + _exception_object: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context, +) -> uw::_Unwind_Reason_Code { + core::intrinsics::abort() +} diff --git a/library/std/src/sys/pal/personality/gcc.rs b/library/std/src/sys/pal/personality/gcc.rs new file mode 100644 index 00000000000..6f317131145 --- /dev/null +++ b/library/std/src/sys/pal/personality/gcc.rs @@ -0,0 +1,293 @@ +//! Implementation of panics backed by libgcc/libunwind (in some form). +//! +//! For background on exception handling and stack unwinding please see +//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and +//! documents linked from it. +//! These are also good reads: +//! * +//! * +//! * +//! +//! ## A brief summary +//! +//! Exception handling happens in two phases: a search phase and a cleanup +//! phase. +//! +//! In both phases the unwinder walks stack frames from top to bottom using +//! information from the stack frame unwind sections of the current process's +//! modules ("module" here refers to an OS module, i.e., an executable or a +//! dynamic library). +//! +//! For each stack frame, it invokes the associated "personality routine", whose +//! address is also stored in the unwind info section. +//! +//! In the search phase, the job of a personality routine is to examine +//! exception object being thrown, and to decide whether it should be caught at +//! that stack frame. Once the handler frame has been identified, cleanup phase +//! begins. +//! +//! In the cleanup phase, the unwinder invokes each personality routine again. +//! This time it decides which (if any) cleanup code needs to be run for +//! the current stack frame. If so, the control is transferred to a special +//! branch in the function body, the "landing pad", which invokes destructors, +//! frees memory, etc. At the end of the landing pad, control is transferred +//! back to the unwinder and unwinding resumes. +//! +//! Once stack has been unwound down to the handler frame level, unwinding stops +//! and the last personality routine transfers control to the catch block. + +use super::dwarf::eh::{self, EHAction, EHContext}; +use crate::ffi::c_int; +use unwind as uw; + +// Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister() +// and TargetLowering::getExceptionSelectorRegister() for each architecture, +// then mapped to DWARF register numbers via register definition tables +// (typically RegisterInfo.td, search for "DwarfRegNum"). +// See also https://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register. + +#[cfg(target_arch = "x86")] +const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX + +#[cfg(target_arch = "x86_64")] +const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX + +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 / X0, X1 + +#[cfg(target_arch = "m68k")] +const UNWIND_DATA_REG: (i32, i32) = (0, 1); // D0, D1 + +#[cfg(any( + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6" +))] +const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1 + +#[cfg(target_arch = "csky")] +const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 + +#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] +const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4 / X3, X4 + +#[cfg(target_arch = "s390x")] +const UNWIND_DATA_REG: (i32, i32) = (6, 7); // R6, R7 + +#[cfg(any(target_arch = "sparc", target_arch = "sparc64"))] +const UNWIND_DATA_REG: (i32, i32) = (24, 25); // I0, I1 + +#[cfg(target_arch = "hexagon")] +const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 + +#[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))] +const UNWIND_DATA_REG: (i32, i32) = (10, 11); // x10, x11 + +#[cfg(target_arch = "loongarch64")] +const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 + +// The following code is based on GCC's C and C++ personality routines. For reference, see: +// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc +// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c + +cfg_if::cfg_if! { + if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "netbsd")))] { + // ARM EHABI personality routine. + // https://web.archive.org/web/20190728160938/https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf + // + // iOS uses the default routine instead since it uses SjLj unwinding. + #[lang = "eh_personality"] + unsafe extern "C" fn rust_eh_personality( + state: uw::_Unwind_State, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context, + ) -> uw::_Unwind_Reason_Code { + let state = state as c_int; + let action = state & uw::_US_ACTION_MASK as c_int; + let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { + // Backtraces on ARM will call the personality routine with + // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases + // we want to continue unwinding the stack, otherwise all our backtraces + // would end at __rust_try + if state & uw::_US_FORCE_UNWIND as c_int != 0 { + return continue_unwind(exception_object, context); + } + true + } else if action == uw::_US_UNWIND_FRAME_STARTING as c_int { + false + } else if action == uw::_US_UNWIND_FRAME_RESUME as c_int { + return continue_unwind(exception_object, context); + } else { + return uw::_URC_FAILURE; + }; + + // The DWARF unwinder assumes that _Unwind_Context holds things like the function + // and LSDA pointers, however ARM EHABI places them into the exception object. + // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which + // take only the context pointer, GCC personality routines stash a pointer to + // exception_object in the context, using location reserved for ARM's + // "scratch register" (r12). + uw::_Unwind_SetGR(context, uw::UNWIND_POINTER_REG, exception_object as uw::_Unwind_Ptr); + // ...A more principled approach would be to provide the full definition of ARM's + // _Unwind_Context in our libunwind bindings and fetch the required data from there + // directly, bypassing DWARF compatibility functions. + + let eh_action = match find_eh_action(context) { + Ok(action) => action, + Err(_) => return uw::_URC_FAILURE, + }; + if search_phase { + match eh_action { + EHAction::None | EHAction::Cleanup(_) => { + return continue_unwind(exception_object, context); + } + EHAction::Catch(_) | EHAction::Filter(_) => { + // EHABI requires the personality routine to update the + // SP value in the barrier cache of the exception object. + (*exception_object).private[5] = + uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG); + return uw::_URC_HANDLER_FOUND; + } + EHAction::Terminate => return uw::_URC_FAILURE, + } + } else { + match eh_action { + EHAction::None => return continue_unwind(exception_object, context), + EHAction::Filter(_) if state & uw::_US_FORCE_UNWIND as c_int != 0 => return continue_unwind(exception_object, context), + EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => { + uw::_Unwind_SetGR( + context, + UNWIND_DATA_REG.0, + exception_object as uw::_Unwind_Ptr, + ); + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); + uw::_Unwind_SetIP(context, lpad); + return uw::_URC_INSTALL_CONTEXT; + } + EHAction::Terminate => return uw::_URC_FAILURE, + } + } + + // On ARM EHABI the personality routine is responsible for actually + // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). + unsafe fn continue_unwind( + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context, + ) -> uw::_Unwind_Reason_Code { + if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON { + uw::_URC_CONTINUE_UNWIND + } else { + uw::_URC_FAILURE + } + } + // defined in libgcc + extern "C" { + fn __gnu_unwind_frame( + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context, + ) -> uw::_Unwind_Reason_Code; + } + } + } else { + // Default personality routine, which is used directly on most targets + // and indirectly on Windows x86_64 via SEH. + unsafe extern "C" fn rust_eh_personality_impl( + version: c_int, + actions: uw::_Unwind_Action, + _exception_class: uw::_Unwind_Exception_Class, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context, + ) -> uw::_Unwind_Reason_Code { + if version != 1 { + return uw::_URC_FATAL_PHASE1_ERROR; + } + let eh_action = match find_eh_action(context) { + Ok(action) => action, + Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, + }; + if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { + match eh_action { + EHAction::None | EHAction::Cleanup(_) => uw::_URC_CONTINUE_UNWIND, + EHAction::Catch(_) | EHAction::Filter(_) => uw::_URC_HANDLER_FOUND, + EHAction::Terminate => uw::_URC_FATAL_PHASE1_ERROR, + } + } else { + match eh_action { + EHAction::None => uw::_URC_CONTINUE_UNWIND, + // Forced unwinding hits a terminate action. + EHAction::Filter(_) if actions as i32 & uw::_UA_FORCE_UNWIND as i32 != 0 => uw::_URC_CONTINUE_UNWIND, + EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => { + uw::_Unwind_SetGR( + context, + UNWIND_DATA_REG.0, + exception_object.cast(), + ); + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); + uw::_Unwind_SetIP(context, lpad); + uw::_URC_INSTALL_CONTEXT + } + EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR, + } + } + } + + cfg_if::cfg_if! { + if #[cfg(all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), target_env = "gnu"))] { + // On x86_64 MinGW targets, the unwinding mechanism is SEH however the unwind + // handler data (aka LSDA) uses GCC-compatible encoding. + #[lang = "eh_personality"] + #[allow(nonstandard_style)] + unsafe extern "C" fn rust_eh_personality( + exceptionRecord: *mut uw::EXCEPTION_RECORD, + establisherFrame: uw::LPVOID, + contextRecord: *mut uw::CONTEXT, + dispatcherContext: *mut uw::DISPATCHER_CONTEXT, + ) -> uw::EXCEPTION_DISPOSITION { + uw::_GCC_specific_handler( + exceptionRecord, + establisherFrame, + contextRecord, + dispatcherContext, + rust_eh_personality_impl, + ) + } + } else { + // The personality routine for most of our targets. + #[lang = "eh_personality"] + unsafe extern "C" fn rust_eh_personality( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context, + ) -> uw::_Unwind_Reason_Code { + rust_eh_personality_impl( + version, + actions, + exception_class, + exception_object, + context, + ) + } + } + } + } +} + +unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result { + let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; + let mut ip_before_instr: c_int = 0; + let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr); + let eh_context = EHContext { + // The return address points 1 byte past the call instruction, + // which could be in the next IP range in LSDA range table. + // + // `ip = -1` has special meaning, so use wrapping sub to allow for that + ip: if ip_before_instr != 0 { ip } else { ip.wrapping_sub(1) }, + func_start: uw::_Unwind_GetRegionStart(context), + get_text_start: &|| uw::_Unwind_GetTextRelBase(context), + get_data_start: &|| uw::_Unwind_GetDataRelBase(context), + }; + eh::find_eh_action(lsda, &eh_context) +} diff --git a/library/std/src/sys/pal/personality/mod.rs b/library/std/src/sys/pal/personality/mod.rs new file mode 100644 index 00000000000..d37b8ce6346 --- /dev/null +++ b/library/std/src/sys/pal/personality/mod.rs @@ -0,0 +1,47 @@ +//! This module contains the implementation of the `eh_personality` lang item. +//! +//! The actual implementation is heavily dependent on the target since Rust +//! tries to use the native stack unwinding mechanism whenever possible. +//! +//! This personality function is still required with `-C panic=abort` because +//! it is used to catch foreign exceptions from `extern "C-unwind"` and turn +//! them into aborts. +//! +//! Additionally, ARM EHABI uses the personality function when generating +//! backtraces. + +mod dwarf; + +#[cfg(not(any(test, doctest)))] +cfg_if::cfg_if! { + if #[cfg(target_os = "emscripten")] { + mod emcc; + } else if #[cfg(target_env = "msvc")] { + // This is required by the compiler to exist (e.g., it's a lang item), + // but it's never actually called by the compiler because + // _CxxFrameHandler3 is the personality function that is always used. + // Hence this is just an aborting stub. + #[lang = "eh_personality"] + fn rust_eh_personality() { + core::intrinsics::abort() + } + } else if #[cfg(any( + all(target_family = "windows", target_env = "gnu"), + target_os = "psp", + target_os = "xous", + target_os = "solid_asp3", + all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re")), + all(target_vendor = "fortanix", target_env = "sgx"), + ))] { + mod gcc; + } else { + // Targets that don't support unwinding. + // - family=wasm + // - os=none ("bare metal" targets) + // - os=uefi + // - os=espidf + // - os=hermit + // - nvptx64-nvidia-cuda + // - arch=avr + } +} diff --git a/library/std/src/sys/pal/sgx/abi/entry.S b/library/std/src/sys/pal/sgx/abi/entry.S new file mode 100644 index 00000000000..8a063b65dac --- /dev/null +++ b/library/std/src/sys/pal/sgx/abi/entry.S @@ -0,0 +1,376 @@ +/* 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 0x1fbf + +/* 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 enclave 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 enclave .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< !; + +// 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/pal/sgx/abi/mem.rs b/library/std/src/sys/pal/sgx/abi/mem.rs new file mode 100644 index 00000000000..18e6d5b3fa2 --- /dev/null +++ b/library/std/src/sys/pal/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(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(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/pal/sgx/abi/mod.rs b/library/std/src/sys/pal/sgx/abi/mod.rs new file mode 100644 index 00000000000..9508c387415 --- /dev/null +++ b/library/std/src/sys/pal/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/pal/sgx/abi/panic.rs b/library/std/src/sys/pal/sgx/abi/panic.rs new file mode 100644 index 00000000000..229b3b3291f --- /dev/null +++ b/library/std/src/sys/pal/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 { + 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 { + 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/pal/sgx/abi/reloc.rs b/library/std/src/sys/pal/sgx/abi/reloc.rs new file mode 100644 index 00000000000..02dff0ad29f --- /dev/null +++ b/library/std/src/sys/pal/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 { + 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::>(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/pal/sgx/abi/thread.rs b/library/std/src/sys/pal/sgx/abi/thread.rs new file mode 100644 index 00000000000..2b23e368cc3 --- /dev/null +++ b/library/std/src/sys/pal/sgx/abi/thread.rs @@ -0,0 +1,17 @@ +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() -> *mut u8; + } + let addr = unsafe { get_tcs_addr() }; + match Tcs::new(addr) { + Some(tcs) => tcs, + None => rtabort!("TCS must not be placed at address zero (this is a linker error)"), + } +} diff --git a/library/std/src/sys/pal/sgx/abi/tls/mod.rs b/library/std/src/sys/pal/sgx/abi/tls/mod.rs new file mode 100644 index 00000000000..09c4ab3d3e9 --- /dev/null +++ b/library/std/src/sys/pal/sgx/abi/tls/mod.rs @@ -0,0 +1,133 @@ +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>(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) { + // 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) -> 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); + unsafe { Self::current() }.data[index].set(ptr::null_mut()); + 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/pal/sgx/abi/tls/sync_bitset.rs b/library/std/src/sys/pal/sgx/abi/tls/sync_bitset.rs new file mode 100644 index 00000000000..4eeff8f6ef7 --- /dev/null +++ b/library/std/src/sys/pal/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 { + '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>>, + elem_idx: usize, +} + +impl<'a> Iterator for SyncBitsetIter<'a> { + type Item = usize; + + fn next(&mut self) -> Option { + 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/pal/sgx/abi/tls/sync_bitset/tests.rs b/library/std/src/sys/pal/sgx/abi/tls/sync_bitset/tests.rs new file mode 100644 index 00000000000..d7eb2e139d0 --- /dev/null +++ b/library/std/src/sys/pal/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::>(), 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/pal/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs new file mode 100644 index 00000000000..f99cea360f1 --- /dev/null +++ b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs @@ -0,0 +1,819 @@ +#![allow(unused)] + +use crate::arch::asm; +use crate::cell::UnsafeCell; +use crate::cmp; +use crate::convert::TryInto; +use crate::intrinsics; +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 UserSafeSized for FifoDescriptor {} +#[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 UserSafeSized for Cancel {} +#[unstable(feature = "sgx_platform", issue = "56975")] +unsafe impl 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::`. + 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 { + 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: *const u8| -> bool { p.is_aligned_to(Self::align_of()) }; + + 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 UserSafe for T { + fn align_of() -> usize { + mem::align_of::() + } + + unsafe fn from_raw_sized_unchecked(ptr: *mut u8, size: usize) -> *mut Self { + assert_eq!(size, mem::size_of::()); + ptr as _ + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +unsafe impl UserSafe for [T] { + fn align_of() -> usize { + mem::align_of::() + } + + /// # 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::(); + 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` 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`. 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(UnsafeCell); +/// An owned type in userspace memory. `User` is equivalent to `Box` 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(NonNull>); + +#[unstable(feature = "sgx_platform", issue = "56975")] +unsafe impl Send for User {} + +#[unstable(feature = "sgx_platform", issue = "56975")] +unsafe impl Send for User<[T]> {} + +trait NewUserRef { + unsafe fn new_userref(v: T) -> Self; +} + +impl NewUserRef<*mut T> for NonNull> { + unsafe fn new_userref(v: *mut T) -> Self { + // SAFETY: The caller has guaranteed the pointer is valid + unsafe { NonNull::new_unchecked(v as _) } + } +} + +impl NewUserRef> for NonNull> { + unsafe fn new_userref(v: NonNull) -> Self { + // SAFETY: The caller has guaranteed the pointer is valid + unsafe { NonNull::new_userref(v.as_ptr()) } + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl User +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` 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 User +where + T: UserSafe, +{ + /// Allocate space for `T` in user memory. + pub fn uninitialized() -> Self { + Self::new_uninit_bytes(mem::size_of::()) + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl 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::()) + } + + /// 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::())) + }) + } +} + +/// Divide the slice `(ptr, len)` into three parts, where the middle part is +/// aligned to `u64`. +/// +/// The return values `(prefix_len, mid_len, suffix_len)` add back up to `len`. +/// The return values are such that the memory region `(ptr + prefix_len, +/// mid_len)` is the largest possible region where `ptr + prefix_len` is aligned +/// to `u64` and `mid_len` is a multiple of the byte size of `u64`. This means +/// that `prefix_len` and `suffix_len` are guaranteed to be less than the byte +/// size of `u64`, and that `(ptr, prefix_len)` and `(ptr + prefix_len + +/// mid_len, suffix_len)` don't straddle an alignment boundary. +// Standard Rust functions such as `<[u8]>::align_to::` and +// `<*const u8>::align_offset` aren't _guaranteed_ to compute the largest +// possible middle region, and as such can't be used. +fn u64_align_to_guaranteed(ptr: *const u8, mut len: usize) -> (usize, usize, usize) { + const QWORD_SIZE: usize = mem::size_of::(); + + let offset = ptr as usize % QWORD_SIZE; + + let prefix_len = if intrinsics::unlikely(offset > 0) { QWORD_SIZE - offset } else { 0 }; + + len = match len.checked_sub(prefix_len) { + Some(remaining_len) => remaining_len, + None => return (len, 0, 0), + }; + + let suffix_len = len % QWORD_SIZE; + len -= suffix_len; + + (prefix_len, len, suffix_len) +} + +unsafe fn copy_quadwords(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) + ); + } +} + +/// 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) { + /// Like `ptr::copy(src, dst, len)`, except it uses the Intel-recommended + /// instruction sequence for unaligned writes. + unsafe fn write_bytewise_to_userspace(src: *const u8, dst: *mut u8, len: usize) { + if intrinsics::likely(len == 0) { + return; + } + + 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.add(off), + dst = in(reg) dst.add(off), + seg_sel = in(reg) &mut seg_sel, + options(nostack, att_syntax) + ); + } + } + } + + 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.addr().overflowing_add(len).1); + assert!(!dst.addr().overflowing_add(len).1); + + unsafe { + let (len1, len2, len3) = u64_align_to_guaranteed(dst, len); + let (src1, dst1) = (src, dst); + let (src2, dst2) = (src1.add(len1), dst1.add(len1)); + let (src3, dst3) = (src2.add(len2), dst2.add(len2)); + + write_bytewise_to_userspace(src1, dst1, len1); + copy_quadwords(src2, dst2, len2); + write_bytewise_to_userspace(src3, dst3, len3); + } +} + +/// Copies `len` bytes of data from userspace pointer `src` to enclave pointer `dst` +/// +/// This function mitigates AEPIC leak vulnerabilities by ensuring all reads from untrusted memory are 8-byte aligned +/// +/// # Panics +/// This function panics if: +/// +/// * The `src` pointer is null +/// * The `dst` pointer is null +/// * The `src` memory range is not in user memory +/// * The `dst` memory range is not in enclave memory +/// +/// # References +/// - https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00657.html +/// - https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/stale-data-read-from-xapic.html +pub(crate) unsafe fn copy_from_userspace(src: *const u8, dst: *mut u8, len: usize) { + /// Like `ptr::copy(src, dst, len)`, except it uses only u64-aligned reads. + /// + /// # Safety + /// The source memory region must not straddle an alignment boundary. + unsafe fn read_misaligned_from_userspace(src: *const u8, dst: *mut u8, len: usize) { + if intrinsics::likely(len == 0) { + return; + } + + unsafe { + let offset: usize; + let data: u64; + // doing a memory read that's potentially out of bounds for `src`, + // this isn't supported by Rust, so have to use assembly + asm!(" + movl {src:e}, {offset:e} + andl $7, {offset:e} + andq $-8, {src} + movq ({src}), {dst} + ", + src = inout(reg) src => _, + offset = out(reg) offset, + dst = out(reg) data, + options(nostack, att_syntax, readonly, pure) + ); + let data = data.to_le_bytes(); + ptr::copy_nonoverlapping(data.as_ptr().add(offset), dst, len); + } + } + + assert!(!src.is_null()); + assert!(!dst.is_null()); + assert!(is_user_range(src, len)); + assert!(is_enclave_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); + + unsafe { + let (len1, len2, len3) = u64_align_to_guaranteed(src, len); + let (src1, dst1) = (src, dst); + let (src2, dst2) = (src1.add(len1), dst1.add(len1)); + let (src3, dst3) = (src2.add(len2), dst2.add(len2)); + + read_misaligned_from_userspace(src1, dst1, len1); + copy_quadwords(src2, dst2, len2); + read_misaligned_from_userspace(src3, dst3, len3); + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl UserRef +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`. + /// + /// # 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())); + copy_from_userspace( + 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 UserRef +where + T: UserSafe, +{ + /// Copies the value from user memory into enclave memory. + pub fn to_enclave(&self) -> T { + unsafe { + let mut data = mem::MaybeUninit::uninit(); + copy_from_userspace(self.0.get() as _, data.as_mut_ptr() as _, mem::size_of::()); + data.assume_init() + } + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl 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::()).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`. + /// + /// # 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::()).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) { + 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 { + 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; + + #[inline] + fn next(&mut self) -> Option { + 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; + + #[inline] + fn next(&mut self) -> Option { + unsafe { self.0.next().map(|e| UserRef::from_mut_ptr(e)) } + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl Deref for User +where + T: UserSafe, +{ + type Target = UserRef; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.0.as_ptr() } + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl DerefMut for User +where + T: UserSafe, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.0.as_ptr() } + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl Drop for User +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, U> CoerceUnsized> for UserRef {} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl Index for UserRef<[T]> +where + [T]: UserSafe, + I: SliceIndex<[T]>, + I::Output: UserSafe, +{ + type Output = UserRef; + + #[inline] + fn index(&self, index: I) -> &UserRef { + 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 IndexMut for UserRef<[T]> +where + [T]: UserSafe, + I: SliceIndex<[T]>, + I::Output: UserSafe, +{ + #[inline] + fn index_mut(&mut self, index: I) -> &mut UserRef { + 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 { + /// 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 { + 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/pal/sgx/abi/usercalls/mod.rs b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs new file mode 100644 index 00000000000..e19e843267a --- /dev/null +++ b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs @@ -0,0 +1,329 @@ +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 { + 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> { + 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 { + 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, 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::::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 { + 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(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) -> 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) -> 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}") + } +} + +/// Translate the raw result of an SGX usercall. +#[unstable(feature = "sgx_platform", issue = "56975")] +pub trait FromSgxResult { + /// Return type + type Return; + + /// Translate the raw result of an SGX usercall. + fn from_sgx_result(self) -> IoResult; +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl FromSgxResult for (Result, T) { + type Return = T; + + fn from_sgx_result(self) -> IoResult { + if self.0 == RESULT_SUCCESS { + Ok(self.1) + } else { + Err(IoError::from_raw_os_error(check_os_error(self.0))) + } + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl FromSgxResult for Result { + type Return = (); + + fn from_sgx_result(self) -> IoResult { + if self == RESULT_SUCCESS { + Ok(()) + } else { + Err(IoError::from_raw_os_error(check_os_error(self))) + } + } +} diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs b/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs new file mode 100644 index 00000000000..10c1456d4fd --- /dev/null +++ b/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs @@ -0,0 +1,269 @@ +#![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) +} + +/// A value passed or returned in a CPU register. +#[unstable(feature = "sgx_platform", issue = "56975")] +pub type Register = u64; + +/// Translate a type from/to Register to be used as an argument. +#[unstable(feature = "sgx_platform", issue = "56975")] +pub trait RegisterArgument { + /// Translate a Register to Self. + fn from_register(_: Register) -> Self; + /// Translate self to a Register. + fn into_register(self) -> Register; +} + +/// Translate a pair of Registers to the raw usercall return value. +#[unstable(feature = "sgx_platform", issue = "56975")] +pub trait ReturnValue { + /// Translate a pair of Registers to the raw usercall return value. + 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) => { + #[unstable(feature = "sgx_platform", issue = "56975")] + 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) => { + #[unstable(feature = "sgx_platform", issue = "56975")] + 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) => { + #[unstable(feature = "sgx_platform", issue = "56975")] + 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!( *const T); +define_ra!( *mut T); + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl RegisterArgument for bool { + fn from_register(a: Register) -> bool { + if a != 0 { true } else { false } + } + fn into_register(self) -> Register { + self as _ + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl RegisterArgument for Option> { + fn from_register(a: Register) -> Option> { + NonNull::new(a as _) + } + fn into_register(self) -> Register { + self.map_or(0 as _, NonNull::as_ptr) as _ + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl ReturnValue for ! { + fn from_registers(call: &'static str, _regs: (Register, Register)) -> Self { + rtabort!("Usercall {call}: did not expect to be re-entered"); + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl ReturnValue for () { + fn from_registers(call: &'static str, usercall_retval: (Register, Register)) -> Self { + rtassert!(usercall_retval.0 == 0); + rtassert!(usercall_retval.1 == 0); + () + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl 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) + } +} + +#[unstable(feature = "sgx_platform", issue = "56975")] +impl 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/pal/sgx/abi/usercalls/tests.rs b/library/std/src/sys/pal/sgx/abi/usercalls/tests.rs new file mode 100644 index 00000000000..58b8eb215d7 --- /dev/null +++ b/library/std/src/sys/pal/sgx/abi/usercalls/tests.rs @@ -0,0 +1,56 @@ +use super::alloc::User; +use super::alloc::{copy_from_userspace, copy_to_userspace}; + +#[test] +fn test_copy_to_userspace_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().add(offset), size) }; + + // Verify copy + for byte in 0..size { + unsafe { + assert_eq!(*dst.as_ptr().add(offset + byte), src[byte as usize]); + } + } + } + } +} + +#[test] +fn test_copy_from_userspace_function() { + let mut dst = [0u8; 100]; + let mut src = User::<[u8]>::uninitialized(100); + + src.copy_from_enclave(&[0u8; 100]); + + for size in 0..48 { + // For all possible alignment + for offset in 0..8 { + // overwrite complete dst + dst = [0u8; 100]; + + // Copy src[0..size] to dst + offset + unsafe { copy_from_userspace(src.as_ptr().offset(offset), dst.as_mut_ptr(), size) }; + + // Verify copy + for byte in 0..size { + unsafe { + assert_eq!(dst[byte as usize], *src.as_ptr().offset(offset + byte as isize)); + } + } + } + } +} diff --git a/library/std/src/sys/pal/sgx/alloc.rs b/library/std/src/sys/pal/sgx/alloc.rs new file mode 100644 index 00000000000..4aea28cb83e --- /dev/null +++ b/library/std/src/sys/pal/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> = + 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/pal/sgx/args.rs b/library/std/src/sys/pal/sgx/args.rs new file mode 100644 index 00000000000..ef4176c4ac0 --- /dev/null +++ b/library/std/src/sys/pal/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; + +#[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::(); + 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 { + self.0.next().cloned() + } + fn size_hint(&self) -> (usize, Option) { + 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 { + self.0.next_back().cloned() + } +} diff --git a/library/std/src/sys/pal/sgx/condvar.rs b/library/std/src/sys/pal/sgx/condvar.rs new file mode 100644 index 00000000000..aa1174664ae --- /dev/null +++ b/library/std/src/sys/pal/sgx/condvar.rs @@ -0,0 +1,46 @@ +use crate::sys::locks::Mutex; +use crate::sys_common::lazy_box::{LazyBox, LazyInit}; +use crate::time::Duration; + +use super::waitqueue::{SpinMutex, WaitQueue, WaitVariable}; + +/// FIXME: `UnsafeList` is not movable. +struct AllocatedCondvar(SpinMutex>); + +pub struct Condvar { + inner: LazyBox, +} + +impl LazyInit for AllocatedCondvar { + fn init() -> Box { + Box::new(AllocatedCondvar(SpinMutex::new(WaitVariable::new(())))) + } +} + +impl Condvar { + pub const fn new() -> Condvar { + Condvar { inner: LazyBox::new() } + } + + #[inline] + pub fn notify_one(&self) { + let _ = WaitQueue::notify_one(self.inner.0.lock()); + } + + #[inline] + pub fn notify_all(&self) { + let _ = WaitQueue::notify_all(self.inner.0.lock()); + } + + pub unsafe fn wait(&self, mutex: &Mutex) { + let guard = self.inner.0.lock(); + WaitQueue::wait(guard, || unsafe { mutex.unlock() }); + mutex.lock() + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + let success = WaitQueue::wait_timeout(&self.inner.0, dur, || unsafe { mutex.unlock() }); + mutex.lock(); + success + } +} diff --git a/library/std/src/sys/pal/sgx/env.rs b/library/std/src/sys/pal/sgx/env.rs new file mode 100644 index 00000000000..8043b7c5213 --- /dev/null +++ b/library/std/src/sys/pal/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/pal/sgx/fd.rs b/library/std/src/sys/pal/sgx/fd.rs new file mode 100644 index 00000000000..b3686d0e283 --- /dev/null +++ b/library/std/src/sys/pal/sgx/fd.rs @@ -0,0 +1,89 @@ +use fortanix_sgx_abi::Fd; + +use super::abi::usercalls; +use crate::io::{self, BorrowedCursor, 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 { + usercalls::read(self.fd, &mut [IoSliceMut::new(buf)]) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|b| self.read(b), buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + usercalls::read(self.fd, bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + usercalls::write(self.fd, &[IoSlice::new(buf)]) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + 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 for FileDesc { + #[inline] + fn as_inner(&self) -> &Fd { + &self.fd + } +} + +impl IntoInner for FileDesc { + fn into_inner(self) -> Fd { + let fd = self.fd; + mem::forget(self); + fd + } +} + +impl FromInner 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/pal/sgx/memchr.rs b/library/std/src/sys/pal/sgx/memchr.rs new file mode 100644 index 00000000000..9967482197e --- /dev/null +++ b/library/std/src/sys/pal/sgx/memchr.rs @@ -0,0 +1 @@ +pub use core::slice::memchr::{memchr, memrchr}; diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs new file mode 100644 index 00000000000..09d3f7638ca --- /dev/null +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -0,0 +1,175 @@ +//! 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)] +#![allow(fuzzy_provenance_casts)] // FIXME: this entire module systematically confuses pointers and integers + +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 thread_parking; +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, _sigpipe: 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() -> crate::io::Result { + 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(v: T) -> crate::io::Result { + 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) + } +} + +#[inline] +pub fn is_interrupted(code: i32) -> bool { + use fortanix_sgx_abi::Error; + code == Error::Interrupted as _ +} + +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: Sized { + fn try_into_inner(self) -> Result; +} diff --git a/library/std/src/sys/pal/sgx/mutex.rs b/library/std/src/sys/pal/sgx/mutex.rs new file mode 100644 index 00000000000..0dbf020ebe0 --- /dev/null +++ b/library/std/src/sys/pal/sgx/mutex.rs @@ -0,0 +1,59 @@ +use super::waitqueue::{try_lock_or_false, SpinMutex, WaitQueue, WaitVariable}; +use crate::sys_common::lazy_box::{LazyBox, LazyInit}; + +/// FIXME: `UnsafeList` is not movable. +struct AllocatedMutex(SpinMutex>); + +pub struct Mutex { + inner: LazyBox, +} + +impl LazyInit for AllocatedMutex { + fn init() -> Box { + Box::new(AllocatedMutex(SpinMutex::new(WaitVariable::new(false)))) + } +} + +// Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28 +impl Mutex { + pub const fn new() -> Mutex { + Mutex { inner: LazyBox::new() } + } + + #[inline] + pub fn lock(&self) { + let mut guard = self.inner.0.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.0.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 fn try_lock(&self) -> bool { + let mut guard = try_lock_or_false!(self.inner.0); + 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/pal/sgx/net.rs b/library/std/src/sys/pal/sgx/net.rs new file mode 100644 index 00000000000..03620a08f2c --- /dev/null +++ b/library/std/src/sys/pal/sgx/net.rs @@ -0,0 +1,548 @@ +use crate::error; +use crate::fmt; +use crate::io::{self, BorrowedCursor, 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, + local_addr: Option, +} + +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 for Socket { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.inner + } +} + +impl TryIntoInner for Socket { + fn try_into_inner(self) -> Result { + let Socket { inner, local_addr } = self; + Arc::try_unwrap(inner).map_err(|inner| Socket { inner, local_addr }) + } +} + +impl FromInner<(FileDesc, Option)> for Socket { + fn from_inner((inner, local_addr): (FileDesc, Option)) -> Socket { + Socket { inner: Arc::new(inner), local_addr } + } +} + +#[derive(Clone)] +pub struct TcpStream { + inner: Socket, + peer_addr: Option, +} + +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 { + 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::()).is_some() { + Ok(e.into_inner().unwrap().downcast::().unwrap().host) + } else { + Err(e) + } + } + } +} + +fn addr_to_sockaddr(addr: &Option) -> io::Result { + 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 { + 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 { + 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) -> 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) -> 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> { + sgx_ineffective(None) + } + + pub fn write_timeout(&self) -> io::Result> { + sgx_ineffective(None) + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + Ok(0) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.inner.inner.read(buf) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.inner.inner.read_buf(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + 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 { + self.inner.inner.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + 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 { + addr_to_sockaddr(&self.peer_addr) + } + + pub fn socket_addr(&self) -> io::Result { + addr_to_sockaddr(&self.inner.local_addr) + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn duplicate(&self) -> io::Result { + Ok(self.clone()) + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn linger(&self) -> io::Result> { + sgx_ineffective(None) + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn nodelay(&self) -> io::Result { + sgx_ineffective(false) + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn ttl(&self) -> io::Result { + sgx_ineffective(DEFAULT_FAKE_TTL) + } + + pub fn take_error(&self) -> io::Result> { + Ok(None) + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } +} + +impl AsInner for TcpStream { + #[inline] + 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)> for TcpStream { + fn into_inner(self) -> (Socket, Option) { + (self.inner, self.peer_addr) + } +} + +impl FromInner<(Socket, Option)> for TcpStream { + fn from_inner((inner, peer_addr): (Socket, Option)) -> 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 { + 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 { + 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 { + Ok(self.clone()) + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn ttl(&self) -> io::Result { + sgx_ineffective(DEFAULT_FAKE_TTL) + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn only_v6(&self) -> io::Result { + sgx_ineffective(false) + } + + pub fn take_error(&self) -> io::Result> { + Ok(None) + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } +} + +impl AsInner for TcpListener { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl IntoInner for TcpListener { + fn into_inner(self) -> Socket { + self.inner + } +} + +impl FromInner for TcpListener { + fn from_inner(inner: Socket) -> TcpListener { + TcpListener { inner } + } +} + +pub struct UdpSocket(!); + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + 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 { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn broadcast(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v6(&self) -> io::Result { + 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 { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn send(&self, _: &[u8]) -> io::Result { + 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 { + 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 { + self.0 + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(v: &str) -> io::Result { + 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::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/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs new file mode 100644 index 00000000000..86f4c7d3d56 --- /dev/null +++ b/library/std/src/sys/pal/sgx/os.rs @@ -0,0 +1,187 @@ +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 { + 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 { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + 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 { + 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>; + +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 struct Env { + iter: vec::IntoIter<(OsString, OsString)>, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { slice } = self; + f.debug_list() + .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { iter } = self; + EnvStrDebug { slice: iter.as_slice() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + f.debug_list().entries(iter.as_slice()).finish() + } +} + +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) { + self.iter.size_hint() + } +} + +pub fn env() -> Env { + let clone_to_vec = |map: &HashMap| -> Vec<_> { + map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() + }; + + let iter = get_env_store() + .map(|env| clone_to_vec(&env.lock().unwrap())) + .unwrap_or_default() + .into_iter(); + Env { iter } +} + +pub fn getenv(k: &OsStr) -> Option { + 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 { + 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/pal/sgx/path.rs b/library/std/src/sys/pal/sgx/path.rs new file mode 100644 index 00000000000..c805c15e702 --- /dev/null +++ b/library/std/src/sys/pal/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> { + None +} + +pub const MAIN_SEP_STR: &str = "/"; +pub const MAIN_SEP: char = '/'; + +pub(crate) fn absolute(_path: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/pal/sgx/rwlock.rs b/library/std/src/sys/pal/sgx/rwlock.rs new file mode 100644 index 00000000000..d89de18ca5f --- /dev/null +++ b/library/std/src/sys/pal/sgx/rwlock.rs @@ -0,0 +1,222 @@ +#[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::alloc::Layout; + +struct AllocatedRwLock { + readers: SpinMutex>>, + writer: SpinMutex>, +} + +pub struct RwLock { + inner: LazyBox, +} + +impl LazyInit for AllocatedRwLock { + fn init() -> Box { + Box::new(AllocatedRwLock { + readers: SpinMutex::new(WaitVariable::new(None)), + writer: SpinMutex::new(WaitVariable::new(false)), + }) + } +} + +// Check at compile time that RwLock's size and alignment matches the C definition +// in libunwind (see also `test_c_rwlock_initializer` in `tests`). +const _: () = { + let rust = Layout::new::(); + let c = Layout::new::<*mut ()>(); + assert!(rust.size() == c.size()); + assert!(rust.align() == c.align()); +}; + +impl RwLock { + pub const fn new() -> RwLock { + RwLock { inner: LazyBox::new() } + } + + #[inline] + pub fn read(&self) { + let lock = &*self.inner; + let mut rguard = lock.readers.lock(); + let wguard = lock.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 lock = &*self.inner; + let mut rguard = try_lock_or_false!(lock.readers); + let wguard = try_lock_or_false!(lock.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 fn write(&self) { + let lock = &*self.inner; + let rguard = lock.readers.lock(); + let mut wguard = lock.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 fn try_write(&self) -> bool { + let lock = &*self.inner; + let rguard = try_lock_or_false!(lock.readers); + let mut wguard = try_lock_or_false!(lock.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>>, + wguard: SpinMutexGuard<'_, WaitVariable>, + ) { + *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 lock = &*self.inner; + let rguard = lock.readers.lock(); + let wguard = lock.writer.lock(); + unsafe { self.__read_unlock(rguard, wguard) }; + } + + #[inline] + unsafe fn __write_unlock( + &self, + rguard: SpinMutexGuard<'_, WaitVariable>>, + wguard: SpinMutexGuard<'_, WaitVariable>, + ) { + 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 lock = &*self.inner; + let rguard = lock.readers.lock(); + let wguard = lock.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 lock = &*self.inner; + let rguard = lock.readers.lock(); + let wguard = lock.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/pal/sgx/rwlock/tests.rs b/library/std/src/sys/pal/sgx/rwlock/tests.rs new file mode 100644 index 00000000000..5fd6670afd4 --- /dev/null +++ b/library/std/src/sys/pal/sgx/rwlock/tests.rs @@ -0,0 +1,21 @@ +use super::*; +use crate::ptr; + +// 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() { + const C_RWLOCK_INIT: *mut () = ptr::null_mut(); + + // 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::<_, *mut ()>(&RUST_RWLOCK_INIT), C_RWLOCK_INIT); + }; +} diff --git a/library/std/src/sys/pal/sgx/stdio.rs b/library/std/src/sys/pal/sgx/stdio.rs new file mode 100644 index 00000000000..2e680e740fd --- /dev/null +++ b/library/std/src/sys/pal/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 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 { + 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 { + 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 { + 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 { + 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/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs new file mode 100644 index 00000000000..7ac9d1d64b4 --- /dev/null +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -0,0 +1,157 @@ +#![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); + + impl Drop for JoinNotifier { + fn drop(&mut self) { + self.0.take().unwrap().notify(); + } + } + + pub(super) struct Task { + p: Box, + done: JoinNotifier, + } + + impl Task { + pub(super) fn new(p: Box) -> (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>> = None; + + pub(super) fn lock() -> MutexGuard<'static, Vec> { + 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 crate::pin::Pin; + use crate::sync::Arc; + use crate::sys_common::thread_parking::Parker; + + pub struct Notifier(Arc); + + 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) { + Pin::new(&*self.0).unpark() + } + } + + pub struct Waiter(Arc); + + 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) { + // SAFETY: + // This is only ever called on one thread. + unsafe { Pin::new(&*self.0).park() } + } + } + + pub fn new() -> (Notifier, Waiter) { + let inner = Arc::new(Parker::new()); + (Notifier(inner.clone()), Waiter(inner)) + } +} + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, p: Box) -> io::Result { + 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); + } + + /// SGX should protect in-enclave data from the outside (attacker), + /// so there should be no data leakage to the OS, + /// and therefore also no 1-1 mapping between SGX thread names and OS thread names. + /// + /// This is why the method is intentionally No-Op. + pub fn set_name(_name: &CStr) { + // Note that the internally visible SGX thread name is already provided + // by the platform-agnostic (target-agnostic) Rust thread code. + // This can be observed in the [`std::thread::tests::test_named_thread`] test, + // which succeeds as-is with the SGX target. + } + + pub fn sleep(dur: Duration) { + usercalls::wait_timeout(0, dur, || true); + } + + pub fn join(self) { + self.0.wait(); + } +} + +pub fn available_parallelism() -> io::Result { + unsupported() +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + None + } +} diff --git a/library/std/src/sys/pal/sgx/thread_local_key.rs b/library/std/src/sys/pal/sgx/thread_local_key.rs new file mode 100644 index 00000000000..c7a57d3a3d4 --- /dev/null +++ b/library/std/src/sys/pal/sgx/thread_local_key.rs @@ -0,0 +1,23 @@ +use super::abi::tls::{Key as AbiKey, Tls}; + +pub type Key = usize; + +#[inline] +pub unsafe fn create(dtor: Option) -> 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)) +} diff --git a/library/std/src/sys/pal/sgx/thread_parking.rs b/library/std/src/sys/pal/sgx/thread_parking.rs new file mode 100644 index 00000000000..0006cd4f1be --- /dev/null +++ b/library/std/src/sys/pal/sgx/thread_parking.rs @@ -0,0 +1,23 @@ +use super::abi::usercalls; +use crate::io::ErrorKind; +use crate::time::Duration; +use fortanix_sgx_abi::{EV_UNPARK, WAIT_INDEFINITE}; + +pub type ThreadId = fortanix_sgx_abi::Tcs; + +pub use super::abi::thread::current; + +pub fn park(_hint: usize) { + usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap(); +} + +pub fn park_timeout(dur: Duration, _hint: usize) { + let timeout = u128::min(dur.as_nanos(), WAIT_INDEFINITE as u128 - 1) as u64; + if let Err(e) = usercalls::wait(EV_UNPARK, timeout) { + assert!(matches!(e.kind(), ErrorKind::TimedOut | ErrorKind::WouldBlock)) + } +} + +pub fn unpark(tid: ThreadId, _hint: usize) { + let _ = usercalls::send(EV_UNPARK, Some(tid)); +} diff --git a/library/std/src/sys/pal/sgx/time.rs b/library/std/src/sys/pal/sgx/time.rs new file mode 100644 index 00000000000..db4cf2804bf --- /dev/null +++ b/library/std/src/sys/pal/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 { + self.0.checked_sub(other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + 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 { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} diff --git a/library/std/src/sys/pal/sgx/waitqueue/mod.rs b/library/std/src/sys/pal/sgx/waitqueue/mod.rs new file mode 100644 index 00000000000..25eca61d67b --- /dev/null +++ b/library/std/src/sys/pal/sgx/waitqueue/mod.rs @@ -0,0 +1,262 @@ +//! 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` 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::panic::{self, AssertUnwindSafe}; +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 { + queue: WaitQueue, + lock: T, +} + +impl WaitVariable { + 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>>, + 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>, +} +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(self, guard: U) { + drop(guard); + drop(self); + } +} + +impl<'a, T> Deref for WaitGuard<'a, T> { + type Target = SpinMutexGuard<'a, WaitVariable>; + + 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. When `before_wait` panics, + /// this function will abort. + pub fn wait(mut guard: SpinMutexGuard<'_, WaitVariable>, 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); + if let Err(_e) = panic::catch_unwind(AssertUnwindSafe(|| before_wait())) { + rtabort!("Panic before wait on wakeup event") + } + while !entry.lock().wake { + // `entry.wake` is only set in `notify_one` and `notify_all` functions. Both ensure + // the entry is removed from the queue _before_ setting this bool. There are no + // other references to `entry`. + // 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. + /// When `before_wait` panics, this function will abort. + pub fn wait_timeout( + lock: &SpinMutex>, + 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); + if let Err(_e) = panic::catch_unwind(AssertUnwindSafe(|| before_wait())) { + rtabort!("Panic before wait on wakeup event or timeout") + } + usercalls::wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake); + // acquire the wait queue's lock first to avoid deadlock + // and ensure no other function can simultaneously access the list + // (e.g., `notify_one` or `notify_all`) + 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( + mut guard: SpinMutexGuard<'_, WaitVariable>, + ) -> Result, SpinMutexGuard<'_, WaitVariable>> { + // SAFETY: lifetime of the pop() return value is limited to the map + // closure (The closure return value is 'static). The underlying + // stack frame won't be freed until after the lock on the queue is released + // (i.e., `guard` is dropped). + unsafe { + let tcs = guard.queue.inner.pop().map(|entry| -> Tcs { + let mut entry_guard = entry.lock(); + entry_guard.wake = true; + entry_guard.tcs + }); + + if let Some(tcs) = tcs { + 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( + mut guard: SpinMutexGuard<'_, WaitVariable>, + ) -> Result, SpinMutexGuard<'_, WaitVariable>> { + // SAFETY: lifetime of the pop() return values are limited to the + // while loop body. The underlying stack frames won't be freed until + // after the lock on the queue is released (i.e., `guard` is dropped). + 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/pal/sgx/waitqueue/spin_mutex.rs b/library/std/src/sys/pal/sgx/waitqueue/spin_mutex.rs new file mode 100644 index 00000000000..f6e851ccadd --- /dev/null +++ b/library/std/src/sys/pal/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 { + value: UnsafeCell, + lock: AtomicBool, +} + +unsafe impl Send for SpinMutex {} +unsafe impl Sync for SpinMutex {} + +pub struct SpinMutexGuard<'a, T: 'a> { + mutex: &'a SpinMutex, +} + +impl<'a, T> !Send for SpinMutexGuard<'a, T> {} +unsafe impl<'a, T: Sync> Sync for SpinMutexGuard<'a, T> {} + +impl SpinMutex { + 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> { + 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/pal/sgx/waitqueue/spin_mutex/tests.rs b/library/std/src/sys/pal/sgx/waitqueue/spin_mutex/tests.rs new file mode 100644 index 00000000000..4c5994bea61 --- /dev/null +++ b/library/std/src/sys/pal/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::::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/pal/sgx/waitqueue/tests.rs b/library/std/src/sys/pal/sgx/waitqueue/tests.rs new file mode 100644 index 00000000000..bf91fdd08ed --- /dev/null +++ b/library/std/src/sys/pal/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::>::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/pal/sgx/waitqueue/unsafe_list.rs b/library/std/src/sys/pal/sgx/waitqueue/unsafe_list.rs new file mode 100644 index 00000000000..c736cab576e --- /dev/null +++ b/library/std/src/sys/pal/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 { + next: NonNull>, + prev: NonNull>, + value: Option, +} + +impl UnsafeListEntry { + 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 { + head_tail: NonNull>, + head_tail_entry: Option>, +} + +impl UnsafeList { + 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) -> &'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) { + 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/pal/sgx/waitqueue/unsafe_list/tests.rs b/library/std/src/sys/pal/sgx/waitqueue/unsafe_list/tests.rs new file mode 100644 index 00000000000..c653dee17bc --- /dev/null +++ b/library/std/src/sys/pal/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(list: &mut UnsafeList) { + assert!(unsafe { list.pop() }.is_none(), "assertion failed: list is not empty"); +} + +#[test] +fn init_empty() { + unsafe { + assert_empty(&mut UnsafeList::::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/pal/solid/abi/fs.rs b/library/std/src/sys/pal/solid/abi/fs.rs new file mode 100644 index 00000000000..32800bd9a9d --- /dev/null +++ b/library/std/src/sys/pal/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/pal/solid/abi/mod.rs b/library/std/src/sys/pal/solid/abi/mod.rs new file mode 100644 index 00000000000..8440d572cfb --- /dev/null +++ b/library/std/src/sys/pal/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/pal/solid/abi/sockets.rs b/library/std/src/sys/pal/solid/abi/sockets.rs new file mode 100644 index 00000000000..eb06a6dd927 --- /dev/null +++ b/library/std/src/sys/pal/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/pal/solid/alloc.rs b/library/std/src/sys/pal/solid/alloc.rs new file mode 100644 index 00000000000..d013bd87610 --- /dev/null +++ b/library/std/src/sys/pal/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/pal/solid/env.rs b/library/std/src/sys/pal/solid/env.rs new file mode 100644 index 00000000000..6855c113b28 --- /dev/null +++ b/library/std/src/sys/pal/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/pal/solid/error.rs b/library/std/src/sys/pal/solid/error.rs new file mode 100644 index 00000000000..547b4f3a984 --- /dev/null +++ b/library/std/src/sys/pal/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/pal/solid/fs.rs b/library/std/src/sys/pal/solid/fs.rs new file mode 100644 index 00000000000..6c66b93a3e1 --- /dev/null +++ b/library/std/src/sys/pal/solid/fs.rs @@ -0,0 +1,588 @@ +use super::{abi, error}; +use crate::{ + ffi::{CStr, CString, OsStr, OsString}, + fmt, + io::{self, BorrowedCursor, IoSlice, IoSliceMut, 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, +} + +pub struct DirEntry { + entry: abi::dirent, + inner: Arc, +} + +#[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(Copy, Clone, Debug, Default)] +pub struct FileTimes {} + +#[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 { + Ok(SystemTime::from_time_t(self.stat.st_mtime)) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from_time_t(self.stat.st_atime)) + } + + pub fn created(&self) -> io::Result { + 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 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.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 { + 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; + + fn next(&mut self) -> Option> { + let entry = unsafe { + let mut out_entry = MaybeUninit::uninit(); + match error::SolidError::err_if_negative(abi::SOLID_FS_ReadDir( + self.inner.dirp, + out_entry.as_mut_ptr(), + )) { + Ok(_) => out_entry.assume_init(), + Err(e) if e.as_raw() == abi::SOLID_ERR_NOTFOUND => return None, + Err(e) => return Some(Err(e.as_io_error())), + } + }; + + (entry.d_name[0] != 0).then(|| Ok(DirEntry { entry, 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 { + lstat(&self.path()) + } + + pub fn file_type(&self) -> io::Result { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + unsafe { + let len = cursor.capacity(); + let mut out_num_bytes = MaybeUninit::uninit(); + error::SolidError::err_if_negative(abi::SOLID_FS_Read( + self.fd.raw(), + cursor.as_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 + cursor.advance(num_bytes_read); + + Ok(()) + } + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + 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 { + 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 { + 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 { + 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 { + unsupported() + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + unsupported() + } + + pub fn set_times(&self, _times: FileTimes) -> 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 { + // 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 { + // This target doesn't support symlinks + lstat(p) +} + +pub fn lstat(p: &Path) -> io::Result { + 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 { + unsupported() +} + +pub fn copy(from: &Path, to: &Path) -> io::Result { + 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/pal/solid/io.rs b/library/std/src/sys/pal/solid/io.rs new file mode 100644 index 00000000000..a862bb78702 --- /dev/null +++ b/library/std/src/sys/pal/solid/io.rs @@ -0,0 +1,81 @@ +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) } + } +} + +pub fn is_terminal(_: &T) -> bool { + false +} diff --git a/library/std/src/sys/pal/solid/memchr.rs b/library/std/src/sys/pal/solid/memchr.rs new file mode 100644 index 00000000000..452b7a3de1b --- /dev/null +++ b/library/std/src/sys/pal/solid/memchr.rs @@ -0,0 +1,21 @@ +pub fn memchr(needle: u8, haystack: &[u8]) -> Option { + 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 { + 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/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs new file mode 100644 index 00000000000..5af83653cf8 --- /dev/null +++ b/library/std/src/sys/pal/solid/mod.rs @@ -0,0 +1,97 @@ +#![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 mod thread_parking; + pub(super) mod time; + use super::unsupported; +} + +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 use self::itron::thread_parking; +pub mod time; + +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, _sigpipe: u8) {} + +// SAFETY: must be called only once during runtime cleanup. +pub unsafe fn cleanup() {} + +pub fn unsupported() -> crate::io::Result { + 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", + ) +} + +#[inline] +pub fn is_interrupted(code: i32) -> bool { + net::is_interrupted(code) +} + +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/pal/solid/net.rs b/library/std/src/sys/pal/solid/net.rs new file mode 100644 index 00000000000..a768e2406c8 --- /dev/null +++ b/library/std/src/sys/pal/solid/net.rs @@ -0,0 +1,435 @@ +use super::abi; +use crate::{ + cmp, + ffi::CStr, + io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}, + mem, + net::{Shutdown, SocketAddr}, + os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}, + ptr, str, + sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}, + sys_common::{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 +} + +#[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: T) -> io::Result { + 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(mut f: F) -> io::Result +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() +} + +#[inline] +pub fn is_interrupted(er: abi::ER) -> bool { + er == netc::SOLID_NET_ERR_BASE - libc::EINTR +} + +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(OwnedFd); + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + 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 { + unsafe { + let fd = cvt(netc::socket(fam, ty, 0))?; + Ok(Self::from_raw_fd(fd)) + } + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + cvt(unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; + Ok(()) + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = self.connect(addr); + 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.as_raw_fd()] }; + + let mut writefds = fds; + let mut errorfds = fds; + + let n = unsafe { + cvt(netc::select( + self.as_raw_fd() + 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 { + let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; + unsafe { Ok(Self::from_raw_fd(fd)) } + } + + pub fn duplicate(&self) -> io::Result { + Ok(Self(self.0.try_clone()?)) + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { + let ret = cvt(unsafe { + netc::recv(self.as_raw_fd(), buf.as_mut().as_mut_ptr().cast(), buf.capacity(), flags) + })?; + unsafe { + buf.advance(ret as usize); + } + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let ret = cvt(unsafe { + netc::readv( + self.as_raw_fd(), + bufs.as_ptr() as *const netc::iovec, + cmp::min(bufs.len(), max_iov()) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + 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.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) + } + + 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 { + let ret = cvt(unsafe { + netc::write( + self.as_raw_fd(), + buf.as_ptr() as *const c_void, + cmp::min(buf.len(), READ_LIMIT), + ) + })?; + Ok(ret as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let ret = cvt(unsafe { + netc::writev( + self.as_raw_fd(), + bufs.as_ptr() as *const netc::iovec, + cmp::min(bufs.len(), max_iov()) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn set_timeout(&self, dur: Option, 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> { + 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.as_raw_fd(), how) })?; + Ok(()) + } + + pub fn set_linger(&self, linger: Option) -> 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> { + 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 { + 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_raw_fd(), netc::FIONBIO, (&mut nonblocking) as *mut c_int as _) + }) + .map(drop) + } + + pub fn take_error(&self) -> io::Result> { + 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_raw_fd() + } +} + +impl FromInner for Socket { + #[inline] + fn from_inner(sock: OwnedFd) -> Socket { + Socket(sock) + } +} + +impl IntoInner for Socket { + #[inline] + fn into_inner(self) -> OwnedFd { + self.0 + } +} + +impl AsFd for Socket { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> c_int { + self.0.as_raw_fd() + } +} + +impl FromRawFd for Socket { + #[inline] + unsafe fn from_raw_fd(fd: c_int) -> Socket { + unsafe { Self(FromRawFd::from_raw_fd(fd)) } + } +} + +impl IntoRawFd for Socket { + #[inline] + fn into_raw_fd(self) -> c_int { + self.0.into_raw_fd() + } +} diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs new file mode 100644 index 00000000000..ff81544ba91 --- /dev/null +++ b/library/std/src/sys/pal/solid/os.rs @@ -0,0 +1,228 @@ +use super::unsupported; +use crate::error::Error as StdError; +use crate::ffi::{CStr, 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::sync::{PoisonError, RwLock}; +use crate::sys::common::small_c_string::run_with_cstr; +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 { + 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 { + *self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + 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 { + unsupported() +} + +static ENV_LOCK: RwLock<()> = RwLock::new(()); + +pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) +} + +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { slice } = self; + f.debug_list() + .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { iter } = self; + EnvStrDebug { slice: iter.as_slice() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + f.debug_list().entries(iter.as_slice()).finish() + } +} + +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) { + 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_read_lock(); + 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 { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), |k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() +} + +pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + run_with_cstr(k.as_bytes(), |k| { + run_with_cstr(v.as_bytes(), |v| { + let _guard = ENV_LOCK.write(); + cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) + }) + }) +} + +pub fn unsetenv(n: &OsStr) -> io::Result<()> { + run_with_cstr(n.as_bytes(), |nbuf| { + let _guard = ENV_LOCK.write(); + cvt_env(unsafe { 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 { + 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 { + 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/pal/solid/path.rs b/library/std/src/sys/pal/solid/path.rs new file mode 100644 index 00000000000..7045c9be25b --- /dev/null +++ b/library/std/src/sys/pal/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> { + None +} + +pub const MAIN_SEP_STR: &str = "\\"; +pub const MAIN_SEP: char = '\\'; + +pub(crate) fn absolute(_path: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/pal/solid/rwlock.rs b/library/std/src/sys/pal/solid/rwlock.rs new file mode 100644 index 00000000000..ecb4eb83b9b --- /dev/null +++ b/library/std/src/sys/pal/solid/rwlock.rs @@ -0,0 +1,93 @@ +//! 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<()>, +} + +// Safety: `num_readers` is protected by `mtx_num_readers` +unsafe impl Send for RwLock {} +unsafe impl Sync for RwLock {} + +fn new_rwl() -> Result { + 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 fn read(&self) { + let rwl = self.raw(); + expect_success(unsafe { abi::rwl_loc_rdl(rwl) }, &"rwl_loc_rdl"); + } + + #[inline] + pub 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 fn write(&self) { + let rwl = self.raw(); + expect_success(unsafe { abi::rwl_loc_wrl(rwl) }, &"rwl_loc_wrl"); + } + + #[inline] + pub 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/pal/solid/stdio.rs b/library/std/src/sys/pal/solid/stdio.rs new file mode 100644 index 00000000000..50f0176967b --- /dev/null +++ b/library/std/src/sys/pal/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 { + Ok(0) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + 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 { + 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 { + 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 { + Some(PanicOutput::new()) +} diff --git a/library/std/src/sys/pal/solid/thread_local_dtor.rs b/library/std/src/sys/pal/solid/thread_local_dtor.rs new file mode 100644 index 00000000000..26918a4fcb0 --- /dev/null +++ b/library/std/src/sys/pal/solid/thread_local_dtor.rs @@ -0,0 +1,43 @@ +#![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, RefCell}; + +#[thread_local] +static REGISTERED: Cell = Cell::new(false); + +#[thread_local] +static DTORS: RefCell> = RefCell::new(Vec::new()); + +pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + if !REGISTERED.get() { + let tid = task::current_task_id_aborting(); + // 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) }; + REGISTERED.set(true); + } + + match DTORS.try_borrow_mut() { + Ok(mut dtors) => dtors.push((t, dtor)), + Err(_) => rtabort!("global allocator may not use TLS"), + } +} + +pub unsafe fn run_dtors() { + let mut list = DTORS.take(); + while !list.is_empty() { + for (ptr, dtor) in list { + unsafe { dtor(ptr) }; + } + + list = DTORS.take(); + } +} + +unsafe extern "C" fn tls_dtor(_unused: *mut u8) { + unsafe { run_dtors() }; +} diff --git a/library/std/src/sys/pal/solid/thread_local_key.rs b/library/std/src/sys/pal/solid/thread_local_key.rs new file mode 100644 index 00000000000..b37bf999698 --- /dev/null +++ b/library/std/src/sys/pal/solid/thread_local_key.rs @@ -0,0 +1,21 @@ +pub type Key = usize; + +#[inline] +pub unsafe fn create(_dtor: Option) -> 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"); +} diff --git a/library/std/src/sys/pal/solid/time.rs b/library/std/src/sys/pal/solid/time.rs new file mode 100644 index 00000000000..f83f1644fe8 --- /dev/null +++ b/library/std/src/sys/pal/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 { + 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 { + Some(SystemTime(self.0.checked_add_unsigned(other.as_secs())?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_sub_unsigned(other.as_secs())?)) + } +} diff --git a/library/std/src/sys/pal/teeos/alloc.rs b/library/std/src/sys/pal/teeos/alloc.rs new file mode 100644 index 00000000000..e236819aa23 --- /dev/null +++ b/library/std/src/sys/pal/teeos/alloc.rs @@ -0,0 +1,57 @@ +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 and + // . + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + libc::malloc(layout.size()) as *mut u8 + } else { + 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) + } + } +} + +#[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::()); + 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/pal/teeos/locks/condvar.rs b/library/std/src/sys/pal/teeos/locks/condvar.rs new file mode 100644 index 00000000000..c08e8145b8c --- /dev/null +++ b/library/std/src/sys/pal/teeos/locks/condvar.rs @@ -0,0 +1,100 @@ +use crate::cell::UnsafeCell; +use crate::ptr; +use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed}; +use crate::sys::locks::mutex::{self, Mutex}; +use crate::sys::time::TIMESPEC_MAX; +use crate::sys_common::lazy_box::{LazyBox, LazyInit}; +use crate::time::Duration; + +extern "C" { + pub fn pthread_cond_timedwait( + cond: *mut libc::pthread_cond_t, + lock: *mut libc::pthread_mutex_t, + adstime: *const libc::timespec, + ) -> libc::c_int; +} + +struct AllocatedCondvar(UnsafeCell); + +pub struct Condvar { + inner: LazyBox, + mutex: AtomicPtr, +} + +#[inline] +fn raw(c: &Condvar) -> *mut libc::pthread_cond_t { + c.inner.0.get() +} + +unsafe impl Send for AllocatedCondvar {} +unsafe impl Sync for AllocatedCondvar {} + +impl LazyInit for AllocatedCondvar { + fn init() -> Box { + let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER))); + + let r = unsafe { libc::pthread_cond_init(condvar.0.get(), crate::ptr::null()) }; + assert_eq!(r, 0); + + condvar + } +} + +impl Drop for AllocatedCondvar { + #[inline] + fn drop(&mut self) { + let r = unsafe { libc::pthread_cond_destroy(self.0.get()) }; + debug_assert_eq!(r, 0); + } +} + +impl Condvar { + pub const fn new() -> Condvar { + Condvar { inner: LazyBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } + } + + #[inline] + fn verify(&self, mutex: *mut libc::pthread_mutex_t) { + match self.mutex.compare_exchange(ptr::null_mut(), mutex, Relaxed, Relaxed) { + Ok(_) => {} // Stored the address + Err(n) if n == mutex => {} // Lost a race to store the same address + _ => panic!("attempted to use a condition variable with two mutexes"), + } + } + + #[inline] + pub fn notify_one(&self) { + let r = unsafe { libc::pthread_cond_signal(raw(self)) }; + debug_assert_eq!(r, 0); + } + + #[inline] + pub fn notify_all(&self) { + let r = unsafe { libc::pthread_cond_broadcast(raw(self)) }; + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let mutex = mutex::raw(mutex); + self.verify(mutex); + let r = libc::pthread_cond_wait(raw(self), mutex); + debug_assert_eq!(r, 0); + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + use crate::sys::time::Timespec; + + let mutex = mutex::raw(mutex); + self.verify(mutex); + + let timeout = Timespec::now(libc::CLOCK_MONOTONIC) + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec()) + .unwrap_or(TIMESPEC_MAX); + + let r = pthread_cond_timedwait(raw(self), mutex, &timeout); + assert!(r == libc::ETIMEDOUT || r == 0); + r == 0 + } +} diff --git a/library/std/src/sys/pal/teeos/locks/mod.rs b/library/std/src/sys/pal/teeos/locks/mod.rs new file mode 100644 index 00000000000..c58e9c7fd45 --- /dev/null +++ b/library/std/src/sys/pal/teeos/locks/mod.rs @@ -0,0 +1,8 @@ +pub mod condvar; +#[path = "../../unix/locks/pthread_mutex.rs"] +pub mod mutex; +pub mod rwlock; + +pub(crate) use condvar::Condvar; +pub(crate) use mutex::Mutex; +pub(crate) use rwlock::RwLock; diff --git a/library/std/src/sys/pal/teeos/locks/rwlock.rs b/library/std/src/sys/pal/teeos/locks/rwlock.rs new file mode 100644 index 00000000000..27cdb88788f --- /dev/null +++ b/library/std/src/sys/pal/teeos/locks/rwlock.rs @@ -0,0 +1,44 @@ +use crate::sys::locks::mutex::Mutex; + +/// we do not supported rwlock, so use mutex to simulate rwlock. +/// it's useful because so many code in std will use rwlock. +pub struct RwLock { + inner: Mutex, +} + +impl RwLock { + #[inline] + pub const fn new() -> RwLock { + RwLock { inner: Mutex::new() } + } + + #[inline] + pub fn read(&self) { + unsafe { self.inner.lock() }; + } + + #[inline] + pub fn try_read(&self) -> bool { + unsafe { self.inner.try_lock() } + } + + #[inline] + pub fn write(&self) { + unsafe { self.inner.lock() }; + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + unsafe { self.inner.try_lock() } + } + + #[inline] + pub unsafe fn read_unlock(&self) { + unsafe { self.inner.unlock() }; + } + + #[inline] + pub unsafe fn write_unlock(&self) { + unsafe { self.inner.unlock() }; + } +} diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs new file mode 100644 index 00000000000..ed8c54b2c36 --- /dev/null +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -0,0 +1,167 @@ +//! System bindings for the Teeos platform +//! +//! This module contains the facade (aka platform-specific) implementations of +//! OS level functionality for Teeos. +#![allow(unsafe_op_in_unsafe_fn)] +#![allow(unused_variables)] +#![allow(dead_code)] + +pub use self::rand::hashmap_random_keys; + +pub mod alloc; +#[path = "../unsupported/args.rs"] +pub mod args; +#[path = "../unix/cmath.rs"] +pub mod cmath; +#[path = "../unsupported/env.rs"] +pub mod env; +pub mod locks; +//pub mod fd; +#[path = "../unsupported/fs.rs"] +pub mod fs; +#[path = "../unsupported/io.rs"] +pub mod io; +#[path = "../unix/memchr.rs"] +pub mod memchr; +pub mod net; +#[path = "../unsupported/once.rs"] +pub mod once; +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; +mod rand; +pub mod stdio; +pub mod thread; +pub mod thread_local_dtor; +#[path = "../unix/thread_local_key.rs"] +pub mod thread_local_key; +#[path = "../unsupported/thread_parking.rs"] +pub mod thread_parking; +#[allow(non_upper_case_globals)] +#[path = "../unix/time.rs"] +pub mod time; + +use crate::io::ErrorKind; + +pub fn abort_internal() -> ! { + unsafe { libc::abort() } +} + +// Trusted Applications are loaded as dynamic libraries on Teeos, +// so this should never be called. +pub fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {} + +// SAFETY: must be called only once during runtime cleanup. +// this is not guaranteed to run, for example when the program aborts. +pub unsafe fn cleanup() { + unimplemented!() + // We do NOT have stack overflow handler, because TEE OS will kill TA when it happens. + // So cleanup is commented + // stack_overflow::cleanup(); +} + +#[inline] +pub(crate) fn is_interrupted(errno: i32) -> bool { + errno == libc::EINTR +} + +// Note: code below is 1:1 copied from unix/mod.rs +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: T) -> crate::io::Result { + if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } +} + +pub fn cvt_r(mut f: F) -> crate::io::Result +where + T: IsMinusOne, + F: FnMut() -> T, +{ + loop { + match cvt(f()) { + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + other => return other, + } + } +} + +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)) } +} + +use crate::io as std_io; +pub fn unsupported() -> std_io::Result { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> std_io::Error { + std_io::Error::new(std_io::ErrorKind::Unsupported, "operation not supported on this platform") +} diff --git a/library/std/src/sys/pal/teeos/net.rs b/library/std/src/sys/pal/teeos/net.rs new file mode 100644 index 00000000000..0df681dbfa5 --- /dev/null +++ b/library/std/src/sys/pal/teeos/net.rs @@ -0,0 +1,372 @@ +use crate::fmt; +use crate::io::{self, BorrowedCursor, 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 { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn write(&self, _: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn linger(&self) -> io::Result> { + self.0 + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn nodelay(&self) -> io::Result { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + 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 { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn only_v6(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + 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 { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + 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 { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn broadcast(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v6(&self) -> io::Result { + 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 { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn send(&self, _: &[u8]) -> io::Result { + 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 { + self.0 + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &str) -> io::Result { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + 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 Socket = UdpSocket; diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs new file mode 100644 index 00000000000..e54a92f01f8 --- /dev/null +++ b/library/std/src/sys/pal/teeos/os.rs @@ -0,0 +1,134 @@ +//! Implementation of `std::os` functionality for teeos + +use core::marker::PhantomData; + +use crate::error::Error as StdError; +use crate::ffi::{OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::path; +use crate::path::PathBuf; + +use super::unsupported; + +pub fn errno() -> i32 { + unsafe { (*libc::__errno_location()) as i32 } +} + +// Hardcoded to return 4096, since `sysconf` is only implemented as a stub. +pub fn page_size() -> usize { + // unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; + 4096 +} + +// Everything below are stubs and copied from unsupported.rs + +pub fn error_string(_errno: i32) -> String { + "error string unimplemented".to_string() +} + +pub fn getcwd() -> io::Result { + 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 { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + 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 { + unsupported() +} + +pub struct Env(!); + +impl Env { + // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self(inner) = self; + match *inner {} + } +} + +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + match *inner {} + } +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + let Self(inner) = self; + match *inner {} + } +} + +pub fn env() -> Env { + panic!("not supported on this platform") +} + +pub fn getenv(_: &OsStr) -> Option { + None +} + +pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { + Err(io::Error::new(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +} + +pub fn unsetenv(_: &OsStr) -> io::Result<()> { + Err(io::Error::new(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 { + None +} + +pub fn exit(_code: i32) -> ! { + panic!("TA should not call `exit`") +} + +pub fn getpid() -> u32 { + panic!("no pids on this platform") +} diff --git a/library/std/src/sys/pal/teeos/rand.rs b/library/std/src/sys/pal/teeos/rand.rs new file mode 100644 index 00000000000..b45c3bb40e7 --- /dev/null +++ b/library/std/src/sys/pal/teeos/rand.rs @@ -0,0 +1,21 @@ +pub fn hashmap_random_keys() -> (u64, u64) { + const KEY_LEN: usize = core::mem::size_of::(); + + let mut v = [0u8; KEY_LEN * 2]; + imp::fill_bytes(&mut v); + + let key1 = v[0..KEY_LEN].try_into().unwrap(); + let key2 = v[KEY_LEN..].try_into().unwrap(); + + (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) +} + +mod imp { + extern "C" { + fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t); + } + + pub fn fill_bytes(v: &mut [u8]) { + unsafe { TEE_GenerateRandom(v.as_mut_ptr() as _, v.len() * crate::mem::size_of::()) } + } +} diff --git a/library/std/src/sys/pal/teeos/stdio.rs b/library/std/src/sys/pal/teeos/stdio.rs new file mode 100644 index 00000000000..9ca04f29273 --- /dev/null +++ b/library/std/src/sys/pal/teeos/stdio.rs @@ -0,0 +1,88 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::io; +use core::arch::asm; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +const KCALL_DEBUG_CMD_PUT_BYTES: i64 = 2; + +unsafe fn debug_call(cap_ref: u64, call_no: i64, arg1: u64, arg2: u64) -> i32 { + let ret: u64; + unsafe { + asm!( + "svc #99", + inout("x0") cap_ref => ret, + in("x1") call_no, + in("x2") arg1, + in("x3") arg2, + ); + } + + ret as i32 +} + +fn print_buf(s: &[u8]) -> io::Result { + // Corresponds to `HM_DEBUG_PUT_BYTES_LIMIT`. + const MAX_LEN: usize = 512; + let len = if s.len() > MAX_LEN { MAX_LEN } else { s.len() }; + let result = unsafe { debug_call(0, KCALL_DEBUG_CMD_PUT_BYTES, s.as_ptr() as u64, len as u64) }; + + if result == 0 { Ok(len) } else { Err(io::Error::from(io::ErrorKind::InvalidInput)) } +} + +impl Stdin { + pub const fn new() -> Stdin { + Stdin + } +} + +impl io::Read for Stdin { + fn read(&mut self, _buf: &mut [u8]) -> io::Result { + Ok(0) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + print_buf(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 { + print_buf(buf) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = 0; + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(libc::EBADF as i32) +} + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs new file mode 100644 index 00000000000..155f333f906 --- /dev/null +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -0,0 +1,164 @@ +use core::convert::TryInto; + +use crate::cmp; +use crate::ffi::CStr; +use crate::io; +use crate::mem; +use crate::num::NonZeroUsize; +use crate::ptr; +use crate::sys::os; +use crate::time::Duration; + +pub const DEFAULT_MIN_STACK_SIZE: usize = 8 * 1024; + +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 {} + +extern "C" { + pub fn TEE_Wait(timeout: u32) -> u32; +} + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box) -> io::Result { + let p = Box::into_raw(Box::new(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); + assert_eq!( + libc::pthread_attr_settee( + &mut attr, + libc::TEESMP_THREAD_ATTR_CA_INHERIT, + libc::TEESMP_THREAD_ATTR_TASK_ID_INHERIT, + libc::TEESMP_THREAD_ATTR_HAS_SHADOW, + ), + 0, + ); + + 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 { + // The new thread will start running earliest after the next yield. + // We add a yield here, so that the user does not have to. + Thread::yield_now(); + 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. + // this is not necessary in TEE. + //let _handler = stack_overflow::Handler::new(); + // Finally, let's run some code. + Box::from_raw(main as *mut Box)(); + } + ptr::null_mut() + } + } + + pub fn yield_now() { + let ret = unsafe { libc::sched_yield() }; + debug_assert_eq!(ret, 0); + } + + /// This does not do anything on teeos + pub fn set_name(_name: &CStr) { + // Both pthread_setname_np and prctl are not available to the TA, + // so we can't implement this currently. If the need arises please + // contact the teeos rustzone team. + } + + /// only main thread could wait for sometime in teeos + pub fn sleep(dur: Duration) { + let sleep_millis = dur.as_millis(); + let final_sleep: u32 = + if sleep_millis >= u32::MAX as u128 { u32::MAX } else { sleep_millis as u32 }; + unsafe { + let _ = TEE_Wait(final_sleep); + } + } + + /// must join, because no pthread_detach supported + 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) { + // we can not call detach, so just panic if thread spawn without join + panic!("thread must join, detach is not supported!"); + } +} + +// Note: Both `sched_getaffinity` and `sysconf` are available but not functional on +// teeos, so this function always returns an Error! +pub fn available_parallelism() -> io::Result { + Err(io::Error::new( + io::ErrorKind::NotFound, + "The number of hardware threads is not known for the target platform", + )) +} + +// stub +pub mod guard { + use crate::ops::Range; + pub type Guard = Range; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + None + } +} + +fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { + libc::PTHREAD_STACK_MIN.try_into().expect("Infallible") +} diff --git a/library/std/src/sys/pal/teeos/thread_local_dtor.rs b/library/std/src/sys/pal/teeos/thread_local_dtor.rs new file mode 100644 index 00000000000..5c6bc4d6750 --- /dev/null +++ b/library/std/src/sys/pal/teeos/thread_local_dtor.rs @@ -0,0 +1,4 @@ +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/pal/uefi/alloc.rs b/library/std/src/sys/pal/uefi/alloc.rs new file mode 100644 index 00000000000..ad3904d82f3 --- /dev/null +++ b/library/std/src/sys/pal/uefi/alloc.rs @@ -0,0 +1,49 @@ +//! Global Allocator for UEFI. +//! Uses [r-efi-alloc](https://crates.io/crates/r-efi-alloc) + +use r_efi::protocols::loaded_image; + +use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::sync::OnceLock; +use crate::sys::uefi::helpers; + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + static EFI_MEMORY_TYPE: OnceLock = OnceLock::new(); + + // Return null pointer if boot services are not available + if crate::os::uefi::env::boot_services().is_none() { + return crate::ptr::null_mut(); + } + + // If boot services is valid then SystemTable is not null. + let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); + + // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this + // will never fail. + let mem_type = EFI_MEMORY_TYPE.get_or_init(|| { + let protocol = helpers::image_handle_protocol::( + loaded_image::PROTOCOL_GUID, + ) + .unwrap(); + // Gives allocations the memory type that the data sections were loaded as. + unsafe { (*protocol.as_ptr()).image_data_type } + }); + + // The caller must ensure non-0 layout + unsafe { r_efi_alloc::raw::alloc(system_table, layout, *mem_type) } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // Do nothing if boot services are not available + if crate::os::uefi::env::boot_services().is_none() { + return; + } + + // If boot services is valid then SystemTable is not null. + let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); + // The caller must ensure non-0 layout + unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) } + } +} diff --git a/library/std/src/sys/pal/uefi/args.rs b/library/std/src/sys/pal/uefi/args.rs new file mode 100644 index 00000000000..4ff7be748e9 --- /dev/null +++ b/library/std/src/sys/pal/uefi/args.rs @@ -0,0 +1,158 @@ +use r_efi::protocols::loaded_image; + +use crate::env::current_exe; +use crate::ffi::OsString; +use crate::fmt; +use crate::iter::Iterator; +use crate::mem::size_of; +use crate::sys::uefi::helpers; +use crate::vec; + +pub struct Args { + parsed_args_list: vec::IntoIter, +} + +pub fn args() -> Args { + let lazy_current_exe = || Vec::from([current_exe().map(Into::into).unwrap_or_default()]); + + // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this + // will never fail. + let protocol = + helpers::image_handle_protocol::(loaded_image::PROTOCOL_GUID) + .unwrap(); + + let lp_size = unsafe { (*protocol.as_ptr()).load_options_size } as usize; + // Break if we are sure that it cannot be UTF-16 + if lp_size < size_of::() || lp_size % size_of::() != 0 { + return Args { parsed_args_list: lazy_current_exe().into_iter() }; + } + let lp_size = lp_size / size_of::(); + + let lp_cmd_line = unsafe { (*protocol.as_ptr()).load_options as *const u16 }; + if !lp_cmd_line.is_aligned() { + return Args { parsed_args_list: lazy_current_exe().into_iter() }; + } + let lp_cmd_line = unsafe { crate::slice::from_raw_parts(lp_cmd_line, lp_size) }; + + Args { + parsed_args_list: parse_lp_cmd_line(lp_cmd_line) + .unwrap_or_else(lazy_current_exe) + .into_iter(), + } +} + +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 { + self.parsed_args_list.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.parsed_args_list.size_hint() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.parsed_args_list.len() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + self.parsed_args_list.next_back() + } +} + +/// Implements the UEFI command-line argument parsing algorithm. +/// +/// This implementation is based on what is defined in Section 3.4 of +/// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_Spec_2_0.pdf) +/// +/// Return None in the following cases: +/// - Invalid UTF-16 (unpaired surrogate) +/// - Empty/improper arguments +fn parse_lp_cmd_line(code_units: &[u16]) -> Option> { + const QUOTE: char = '"'; + const SPACE: char = ' '; + const CARET: char = '^'; + const NULL: char = '\0'; + + let mut ret_val = Vec::new(); + let mut code_units_iter = char::decode_utf16(code_units.iter().cloned()).peekable(); + + // The executable name at the beginning is special. + let mut in_quotes = false; + let mut cur = String::new(); + while let Some(w) = code_units_iter.next() { + let w = w.ok()?; + match w { + // break on NULL + NULL => break, + // 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 if !in_quotes => break, + // In all other cases the code unit is taken literally. + _ => cur.push(w), + } + } + + // If exe name is missing, the cli args are invalid + if cur.is_empty() { + return None; + } + + ret_val.push(OsString::from(cur)); + // Skip whitespace. + while code_units_iter.next_if_eq(&Ok(SPACE)).is_some() {} + + // Parse the arguments according to these rules: + // * All code units are taken literally except space, quote and caret. + // * When not `in_quotes`, space separate arguments. Consecutive spaces are + // treated as a single separator. + // * A space `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 caret. + // * A caret can be escaped if preceded by caret. + let mut cur = String::new(); + let mut in_quotes = false; + while let Some(w) = code_units_iter.next() { + let w = w.ok()?; + match w { + // break on NULL + NULL => break, + // If not `in_quotes`, a space or tab ends the argument. + SPACE if !in_quotes => { + ret_val.push(OsString::from(&cur[..])); + cur.truncate(0); + + // Skip whitespace. + while code_units_iter.next_if_eq(&Ok(SPACE)).is_some() {} + } + // Caret can escape quotes or carets + CARET if in_quotes => { + if let Some(x) = code_units_iter.next() { + cur.push(x.ok()?); + } + } + // If quote then flip `in_quotes` + QUOTE => in_quotes = !in_quotes, + // Everything else is always taken literally. + _ => cur.push(w), + } + } + // Push the final argument, if any. + if !cur.is_empty() || in_quotes { + ret_val.push(OsString::from(cur)); + } + Some(ret_val) +} diff --git a/library/std/src/sys/pal/uefi/env.rs b/library/std/src/sys/pal/uefi/env.rs new file mode 100644 index 00000000000..c106d5fed3e --- /dev/null +++ b/library/std/src/sys/pal/uefi/env.rs @@ -0,0 +1,9 @@ +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = "uefi"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ""; + pub const DLL_EXTENSION: &str = ""; + pub const EXE_SUFFIX: &str = ".efi"; + pub const EXE_EXTENSION: &str = "efi"; +} diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs new file mode 100644 index 00000000000..9837cc89f2d --- /dev/null +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -0,0 +1,148 @@ +//! Contains most of the shared UEFI specific stuff. Some of this might be moved to `std::os::uefi` +//! if needed but no point in adding extra public API when there is not Std support for UEFI in the +//! first place +//! +//! Some Nomenclature +//! * Protocol: +//! - Protocols serve to enable communication between separately built modules, including drivers. +//! - Every protocol has a GUID associated with it. The GUID serves as the name for the protocol. +//! - Protocols are produced and consumed. +//! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) + +use r_efi::efi::{self, Guid}; + +use crate::mem::{size_of, MaybeUninit}; +use crate::os::uefi; +use crate::ptr::NonNull; +use crate::{ + io::{self, const_io_error}, + os::uefi::env::boot_services, +}; + +const BOOT_SERVICES_UNAVAILABLE: io::Error = + const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); + +/// Locate Handles with a particular Protocol GUID +/// Implemented using `EFI_BOOT_SERVICES.LocateHandles()` +/// +/// Returns an array of [Handles](r_efi::efi::Handle) that support a specified protocol. +pub(crate) fn locate_handles(mut guid: Guid) -> io::Result>> { + fn inner( + guid: &mut Guid, + boot_services: NonNull, + buf_size: &mut usize, + buf: *mut r_efi::efi::Handle, + ) -> io::Result<()> { + let r = unsafe { + ((*boot_services.as_ptr()).locate_handle)( + r_efi::efi::BY_PROTOCOL, + guid, + crate::ptr::null_mut(), + buf_size, + buf, + ) + }; + + if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } + + let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let mut buf_len = 0usize; + + // This should always fail since the size of buffer is 0. This call should update the buf_len + // variable with the required buffer length + match inner(&mut guid, boot_services, &mut buf_len, crate::ptr::null_mut()) { + Ok(()) => unreachable!(), + Err(e) => match e.kind() { + io::ErrorKind::FileTooLarge => {} + _ => return Err(e), + }, + } + + // The returned buf_len is in bytes + assert_eq!(buf_len % size_of::(), 0); + let num_of_handles = buf_len / size_of::(); + let mut buf: Vec = Vec::with_capacity(num_of_handles); + match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) { + Ok(()) => { + // This is safe because the call will succeed only if buf_len >= required length. + // Also, on success, the `buf_len` is updated with the size of bufferv (in bytes) written + unsafe { buf.set_len(num_of_handles) }; + Ok(buf.into_iter().filter_map(|x| NonNull::new(x)).collect()) + } + Err(e) => Err(e), + } +} + +/// Open Protocol on a handle. +/// Internally just a call to `EFI_BOOT_SERVICES.OpenProtocol()`. +/// +/// Queries a handle to determine if it supports a specified protocol. If the protocol is +/// supported by the handle, it opens the protocol on behalf of the calling agent. +pub(crate) fn open_protocol( + handle: NonNull, + mut protocol_guid: Guid, +) -> io::Result> { + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let system_handle = uefi::env::image_handle(); + let mut protocol: MaybeUninit<*mut T> = MaybeUninit::uninit(); + + let r = unsafe { + ((*boot_services.as_ptr()).open_protocol)( + handle.as_ptr(), + &mut protocol_guid, + protocol.as_mut_ptr().cast(), + system_handle.as_ptr(), + crate::ptr::null_mut(), + r_efi::system::OPEN_PROTOCOL_GET_PROTOCOL, + ) + }; + + if r.is_error() { + Err(crate::io::Error::from_raw_os_error(r.as_usize())) + } else { + NonNull::new(unsafe { protocol.assume_init() }) + .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + } +} + +pub(crate) fn create_event( + signal: u32, + tpl: efi::Tpl, + handler: Option, + context: *mut crate::ffi::c_void, +) -> io::Result> { + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let mut event: r_efi::efi::Event = crate::ptr::null_mut(); + let r = unsafe { + let create_event = (*boot_services.as_ptr()).create_event; + (create_event)(signal, tpl, handler, context, &mut event) + }; + if r.is_error() { + Err(crate::io::Error::from_raw_os_error(r.as_usize())) + } else { + NonNull::new(event).ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + } +} + +/// # SAFETY +/// - The supplied event must be valid +pub(crate) unsafe fn close_event(evt: NonNull) -> io::Result<()> { + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let r = unsafe { + let close_event = (*boot_services.as_ptr()).close_event; + (close_event)(evt.as_ptr()) + }; + + if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } +} + +/// Get the Protocol for current system handle. +/// Note: Some protocols need to be manually freed. It is the callers responsibility to do so. +pub(crate) fn image_handle_protocol(protocol_guid: Guid) -> Option> { + let system_handle = uefi::env::try_image_handle()?; + open_protocol(system_handle, protocol_guid).ok() +} diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs new file mode 100644 index 00000000000..4edc00e3ea0 --- /dev/null +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -0,0 +1,242 @@ +//! Platform-specific extensions to `std` for UEFI platforms. +//! +//! Provides access to platform-level information on UEFI platforms, and +//! exposes UEFI-specific functions that would otherwise be inappropriate as +//! part of the core `std` library. +//! +//! It exposes more ways to deal with platform-specific strings ([`OsStr`], +//! [`OsString`]), allows to set permissions more granularly, extract low-level +//! file descriptors from files and sockets, and has platform-specific helpers +//! for spawning processes. +//! +//! [`OsStr`]: crate::ffi::OsStr +//! [`OsString`]: crate::ffi::OsString + +pub mod alloc; +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/locks/mod.rs"] +pub mod locks; +#[path = "../unsupported/net.rs"] +pub mod net; +#[path = "../unsupported/once.rs"] +pub mod once; +pub mod os; +#[path = "../windows/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; +#[path = "../unsupported/thread.rs"] +pub mod thread; +#[path = "../unsupported/thread_local_key.rs"] +pub mod thread_local_key; +#[path = "../unsupported/thread_parking.rs"] +pub mod thread_parking; +#[path = "../unsupported/time.rs"] +pub mod time; + +mod helpers; + +#[cfg(test)] +mod tests; + +pub type RawOsError = usize; + +use crate::io as std_io; +use crate::os::uefi; +use crate::ptr::NonNull; +use crate::sync::atomic::{AtomicPtr, Ordering}; + +pub mod memchr { + pub use core::slice::memchr::{memchr, memrchr}; +} + +static EXIT_BOOT_SERVICE_EVENT: AtomicPtr = + AtomicPtr::new(crate::ptr::null_mut()); + +/// # SAFETY +/// - must be called only once during runtime initialization. +/// - argc must be 2. +/// - argv must be &[Handle, *mut SystemTable]. +pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { + assert_eq!(argc, 2); + let image_handle = unsafe { NonNull::new(*argv as *mut crate::ffi::c_void).unwrap() }; + let system_table = unsafe { NonNull::new(*argv.add(1) as *mut crate::ffi::c_void).unwrap() }; + unsafe { uefi::env::init_globals(image_handle, system_table) }; + + // Register exit boot services handler + match helpers::create_event( + r_efi::efi::EVT_SIGNAL_EXIT_BOOT_SERVICES, + r_efi::efi::TPL_NOTIFY, + Some(exit_boot_service_handler), + crate::ptr::null_mut(), + ) { + Ok(x) => { + if EXIT_BOOT_SERVICE_EVENT + .compare_exchange( + crate::ptr::null_mut(), + x.as_ptr(), + Ordering::Release, + Ordering::Acquire, + ) + .is_err() + { + abort_internal(); + }; + } + Err(_) => abort_internal(), + } +} + +/// # SAFETY +/// this is not guaranteed to run, for example when the program aborts. +/// - must be called only once during runtime cleanup. +pub unsafe fn cleanup() { + if let Some(exit_boot_service_event) = + NonNull::new(EXIT_BOOT_SERVICE_EVENT.swap(crate::ptr::null_mut(), Ordering::Acquire)) + { + let _ = unsafe { helpers::close_event(exit_boot_service_event) }; + } +} + +#[inline] +pub const fn unsupported() -> std_io::Result { + Err(unsupported_err()) +} + +#[inline] +pub const fn unsupported_err() -> std_io::Error { + std_io::const_io_error!(std_io::ErrorKind::Unsupported, "operation not supported on UEFI",) +} + +pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { + use crate::io::ErrorKind; + use r_efi::efi::Status; + + match r_efi::efi::Status::from_usize(code) { + Status::ALREADY_STARTED + | Status::COMPROMISED_DATA + | Status::CONNECTION_FIN + | Status::CRC_ERROR + | Status::DEVICE_ERROR + | Status::END_OF_MEDIA + | Status::HTTP_ERROR + | Status::ICMP_ERROR + | Status::INCOMPATIBLE_VERSION + | Status::LOAD_ERROR + | Status::MEDIA_CHANGED + | Status::NO_MAPPING + | Status::NO_MEDIA + | Status::NOT_STARTED + | Status::PROTOCOL_ERROR + | Status::PROTOCOL_UNREACHABLE + | Status::TFTP_ERROR + | Status::VOLUME_CORRUPTED => ErrorKind::Other, + Status::BAD_BUFFER_SIZE | Status::INVALID_LANGUAGE => ErrorKind::InvalidData, + Status::ABORTED => ErrorKind::ConnectionAborted, + Status::ACCESS_DENIED => ErrorKind::PermissionDenied, + Status::BUFFER_TOO_SMALL => ErrorKind::FileTooLarge, + Status::CONNECTION_REFUSED => ErrorKind::ConnectionRefused, + Status::CONNECTION_RESET => ErrorKind::ConnectionReset, + Status::END_OF_FILE => ErrorKind::UnexpectedEof, + Status::HOST_UNREACHABLE => ErrorKind::HostUnreachable, + Status::INVALID_PARAMETER => ErrorKind::InvalidInput, + Status::IP_ADDRESS_CONFLICT => ErrorKind::AddrInUse, + Status::NETWORK_UNREACHABLE => ErrorKind::NetworkUnreachable, + Status::NO_RESPONSE => ErrorKind::HostUnreachable, + Status::NOT_FOUND => ErrorKind::NotFound, + Status::NOT_READY => ErrorKind::ResourceBusy, + Status::OUT_OF_RESOURCES => ErrorKind::OutOfMemory, + Status::SECURITY_VIOLATION => ErrorKind::PermissionDenied, + Status::TIMEOUT => ErrorKind::TimedOut, + Status::UNSUPPORTED => ErrorKind::Unsupported, + Status::VOLUME_FULL => ErrorKind::StorageFull, + Status::WRITE_PROTECTED => ErrorKind::ReadOnlyFilesystem, + _ => ErrorKind::Uncategorized, + } +} + +pub fn abort_internal() -> ! { + if let Some(exit_boot_service_event) = + NonNull::new(EXIT_BOOT_SERVICE_EVENT.load(Ordering::Acquire)) + { + let _ = unsafe { helpers::close_event(exit_boot_service_event) }; + } + + if let (Some(boot_services), Some(handle)) = + (uefi::env::boot_services(), uefi::env::try_image_handle()) + { + let boot_services: NonNull = boot_services.cast(); + let _ = unsafe { + ((*boot_services.as_ptr()).exit)( + handle.as_ptr(), + r_efi::efi::Status::ABORTED, + 0, + crate::ptr::null_mut(), + ) + }; + } + + // In case SystemTable and ImageHandle cannot be reached, use `core::intrinsics::abort` + core::intrinsics::abort(); +} + +// 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] +pub extern "C" fn __rust_abort() { + abort_internal(); +} + +#[inline] +pub fn hashmap_random_keys() -> (u64, u64) { + get_random().unwrap() +} + +fn get_random() -> Option<(u64, u64)> { + use r_efi::protocols::rng; + + let mut buf = [0u8; 16]; + let handles = helpers::locate_handles(rng::PROTOCOL_GUID).ok()?; + for handle in handles { + if let Ok(protocol) = helpers::open_protocol::(handle, rng::PROTOCOL_GUID) { + let r = unsafe { + ((*protocol.as_ptr()).get_rng)( + protocol.as_ptr(), + crate::ptr::null_mut(), + buf.len(), + buf.as_mut_ptr(), + ) + }; + if r.is_error() { + continue; + } else { + return Some(( + u64::from_le_bytes(buf[..8].try_into().ok()?), + u64::from_le_bytes(buf[8..].try_into().ok()?), + )); + } + } + } + None +} + +/// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled +extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { + uefi::env::disable_boot_services(); +} + +pub fn is_interrupted(_code: RawOsError) -> bool { + false +} diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs new file mode 100644 index 00000000000..e6693db68e6 --- /dev/null +++ b/library/std/src/sys/pal/uefi/os.rs @@ -0,0 +1,237 @@ +use super::{unsupported, RawOsError}; +use crate::error::Error as StdError; +use crate::ffi::{OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::marker::PhantomData; +use crate::os::uefi; +use crate::path::{self, PathBuf}; +use crate::ptr::NonNull; +use r_efi::efi::Status; + +pub fn errno() -> RawOsError { + 0 +} + +pub fn error_string(errno: RawOsError) -> String { + // Keep the List in Alphabetical Order + // The Messages are taken from UEFI Specification Appendix D - Status Codes + match r_efi::efi::Status::from_usize(errno) { + Status::ABORTED => "The operation was aborted.".to_owned(), + Status::ACCESS_DENIED => "Access was denied.".to_owned(), + Status::ALREADY_STARTED => "The protocol has already been started.".to_owned(), + Status::BAD_BUFFER_SIZE => "The buffer was not the proper size for the request.".to_owned(), + Status::BUFFER_TOO_SMALL => { + "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.".to_owned() + } + Status::COMPROMISED_DATA => { + "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.".to_owned() + } + Status::CONNECTION_FIN => { + "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance.".to_owned() + } + Status::CONNECTION_REFUSED => { + "The receiving or transmission operation fails because this connection is refused.".to_owned() + } + Status::CONNECTION_RESET => { + "The connect fails because the connection is reset either by instance itself or the communication peer.".to_owned() + } + Status::CRC_ERROR => "A CRC error was detected.".to_owned(), + Status::DEVICE_ERROR => "The physical device reported an error while attempting the operation.".to_owned() + , + Status::END_OF_FILE => { + "The end of the file was reached.".to_owned() + } + Status::END_OF_MEDIA => { + "Beginning or end of media was reached".to_owned() + } + Status::HOST_UNREACHABLE => { + "The remote host is not reachable.".to_owned() + } + Status::HTTP_ERROR => { + "A HTTP error occurred during the network operation.".to_owned() + } + Status::ICMP_ERROR => { + "An ICMP error occurred during the network operation.".to_owned() + } + Status::INCOMPATIBLE_VERSION => { + "The function encountered an internal version that was incompatible with a version requested by the caller.".to_owned() + } + Status::INVALID_LANGUAGE => { + "The language specified was invalid.".to_owned() + } + Status::INVALID_PARAMETER => { + "A parameter was incorrect.".to_owned() + } + Status::IP_ADDRESS_CONFLICT => { + "There is an address conflict address allocation".to_owned() + } + Status::LOAD_ERROR => { + "The image failed to load.".to_owned() + } + Status::MEDIA_CHANGED => { + "The medium in the device has changed since the last access.".to_owned() + } + Status::NETWORK_UNREACHABLE => { + "The network containing the remote host is not reachable.".to_owned() + } + Status::NO_MAPPING => { + "A mapping to a device does not exist.".to_owned() + } + Status::NO_MEDIA => { + "The device does not contain any medium to perform the operation.".to_owned() + } + Status::NO_RESPONSE => { + "The server was not found or did not respond to the request.".to_owned() + } + Status::NOT_FOUND => "The item was not found.".to_owned(), + Status::NOT_READY => { + "There is no data pending upon return.".to_owned() + } + Status::NOT_STARTED => { + "The protocol has not been started.".to_owned() + } + Status::OUT_OF_RESOURCES => { + "A resource has run out.".to_owned() + } + Status::PROTOCOL_ERROR => { + "A protocol error occurred during the network operation.".to_owned() + } + Status::PROTOCOL_UNREACHABLE => { + "An ICMP protocol unreachable error is received.".to_owned() + } + Status::SECURITY_VIOLATION => { + "The function was not performed due to a security violation.".to_owned() + } + Status::TFTP_ERROR => { + "A TFTP error occurred during the network operation.".to_owned() + } + Status::TIMEOUT => "The timeout time expired.".to_owned(), + Status::UNSUPPORTED => { + "The operation is not supported.".to_owned() + } + Status::VOLUME_FULL => { + "There is no more space on the file system.".to_owned() + } + Status::VOLUME_CORRUPTED => { + "An inconstancy was detected on the file system causing the operating to fail.".to_owned() + } + Status::WRITE_PROTECTED => { + "The device cannot be written to.".to_owned() + } + _ => format!("Status: {}", errno), + } +} + +pub fn getcwd() -> io::Result { + 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 { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + 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 {} + +pub fn current_exe() -> io::Result { + unsupported() +} + +pub struct Env(!); + +impl Env { + // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self(inner) = self; + match *inner {} + } +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.0 + } +} + +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + match *inner {} + } +} + +pub fn env() -> Env { + panic!("not supported on this platform") +} + +pub fn getenv(_: &OsStr) -> Option { + 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 { + None +} + +pub fn exit(code: i32) -> ! { + if let (Some(boot_services), Some(handle)) = + (uefi::env::boot_services(), uefi::env::try_image_handle()) + { + let boot_services: NonNull = boot_services.cast(); + let _ = unsafe { + ((*boot_services.as_ptr()).exit)( + handle.as_ptr(), + Status::from_usize(code as usize), + 0, + crate::ptr::null_mut(), + ) + }; + } + crate::intrinsics::abort() +} + +pub fn getpid() -> u32 { + panic!("no pids on this platform") +} diff --git a/library/std/src/sys/pal/uefi/path.rs b/library/std/src/sys/pal/uefi/path.rs new file mode 100644 index 00000000000..106682eee56 --- /dev/null +++ b/library/std/src/sys/pal/uefi/path.rs @@ -0,0 +1,25 @@ +use super::unsupported; +use crate::ffi::OsStr; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; + +pub const MAIN_SEP_STR: &str = "\\"; +pub const MAIN_SEP: char = '\\'; + +#[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(_p: &OsStr) -> Option> { + None +} + +pub(crate) fn absolute(_path: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/pal/uefi/stdio.rs b/library/std/src/sys/pal/uefi/stdio.rs new file mode 100644 index 00000000000..a533d8a0575 --- /dev/null +++ b/library/std/src/sys/pal/uefi/stdio.rs @@ -0,0 +1,162 @@ +use crate::io; +use crate::iter::Iterator; +use crate::mem::MaybeUninit; +use crate::os::uefi; +use crate::ptr::NonNull; + +const MAX_BUFFER_SIZE: usize = 8192; + +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 { + let st: NonNull = uefi::env::system_table().cast(); + let stdin = unsafe { (*st.as_ptr()).con_in }; + + // Try reading any pending data + let inp = match read_key_stroke(stdin) { + Ok(x) => x, + Err(e) if e == r_efi::efi::Status::NOT_READY => { + // Wait for keypress for new data + wait_stdin(stdin)?; + read_key_stroke(stdin).map_err(|x| io::Error::from_raw_os_error(x.as_usize()))? + } + Err(e) => { + return Err(io::Error::from_raw_os_error(e.as_usize())); + } + }; + + // Check if the key is printiable character + if inp.scan_code != 0x00 { + return Err(io::const_io_error!(io::ErrorKind::Interrupted, "Special Key Press")); + } + + // SAFETY: Iterator will have only 1 character since we are reading only 1 Key + // SAFETY: This character will always be UCS-2 and thus no surrogates. + let ch: char = char::decode_utf16([inp.unicode_char]).next().unwrap().unwrap(); + if ch.len_utf8() > buf.len() { + return Ok(0); + } + + ch.encode_utf8(buf); + + Ok(ch.len_utf8()) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + let st: NonNull = uefi::env::system_table().cast(); + let stdout = unsafe { (*st.as_ptr()).con_out }; + + write(stdout, 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 { + let st: NonNull = uefi::env::system_table().cast(); + let stderr = unsafe { (*st.as_ptr()).std_err }; + + write(stderr, buf) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +// UCS-2 character should occupy 3 bytes at most in UTF-8 +pub const STDIN_BUF_SIZE: usize = 3; + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} + +pub fn panic_output() -> Option { + uefi::env::try_system_table().map(|_| Stderr::new()) +} + +fn write( + protocol: *mut r_efi::protocols::simple_text_output::Protocol, + buf: &[u8], +) -> io::Result { + let mut utf16 = [0; MAX_BUFFER_SIZE / 2]; + + // Get valid UTF-8 buffer + let utf8 = match crate::str::from_utf8(buf) { + Ok(x) => x, + Err(e) => unsafe { crate::str::from_utf8_unchecked(&buf[..e.valid_up_to()]) }, + }; + // Clip UTF-8 buffer to max UTF-16 buffer we support + let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len() - 1)]; + + for (i, ch) in utf8.encode_utf16().enumerate() { + utf16[i] = ch; + } + + unsafe { simple_text_output(protocol, &mut utf16) }?; + + Ok(utf8.len()) +} + +unsafe fn simple_text_output( + protocol: *mut r_efi::protocols::simple_text_output::Protocol, + buf: &mut [u16], +) -> io::Result<()> { + let res = unsafe { ((*protocol).output_string)(protocol, buf.as_mut_ptr()) }; + if res.is_error() { Err(io::Error::from_raw_os_error(res.as_usize())) } else { Ok(()) } +} + +fn wait_stdin(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> { + let boot_services: NonNull = + uefi::env::boot_services().unwrap().cast(); + let wait_for_event = unsafe { (*boot_services.as_ptr()).wait_for_event }; + let wait_for_key_event = unsafe { (*stdin).wait_for_key }; + + let r = { + let mut x: usize = 0; + (wait_for_event)(1, [wait_for_key_event].as_mut_ptr(), &mut x) + }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } +} + +fn read_key_stroke( + stdin: *mut r_efi::protocols::simple_text_input::Protocol, +) -> Result { + let mut input_key: MaybeUninit = + MaybeUninit::uninit(); + + let r = unsafe { ((*stdin).read_key_stroke)(stdin, input_key.as_mut_ptr()) }; + + if r.is_error() { + Err(r) + } else { + let input_key = unsafe { input_key.assume_init() }; + Ok(input_key) + } +} diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs new file mode 100644 index 00000000000..8806eda3ac0 --- /dev/null +++ b/library/std/src/sys/pal/uefi/tests.rs @@ -0,0 +1,21 @@ +use super::alloc::*; + +#[test] +fn align() { + // UEFI ABI specifies that allocation alignment minimum is always 8. So this can be + // statically verified. + assert_eq!(POOL_ALIGNMENT, 8); + + // Loop over allocation-request sizes from 0-256 and alignments from 1-128, and verify + // that in case of overalignment there is at least space for one additional pointer to + // store in the allocation. + for i in 0..256 { + for j in &[1, 2, 4, 8, 16, 32, 64, 128] { + if *j <= 8 { + assert_eq!(align_size(i, *j), i); + } else { + assert!(align_size(i, *j) > i + std::mem::size_of::<*mut ()>()); + } + } + } +} diff --git a/library/std/src/sys/pal/unix/alloc.rs b/library/std/src/sys/pal/unix/alloc.rs new file mode 100644 index 00000000000..af0089978ec --- /dev/null +++ b/library/std/src/sys/pal/unix/alloc.rs @@ -0,0 +1,106 @@ +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 and + // . + 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", + target_os = "vita", + ))] { + #[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 { + // C11 aligned_alloc requires that the size be a multiple of the alignment. + // Layout already checks that the size rounded up doesn't overflow isize::MAX. + let align = layout.align(); + let size = layout.size().next_multiple_of(align); + libc::aligned_alloc(align, 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::()); + 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/pal/unix/android.rs b/library/std/src/sys/pal/unix/android.rs new file mode 100644 index 00000000000..0f704994f55 --- /dev/null +++ b/library/std/src/sys/pal/unix/android.rs @@ -0,0 +1,81 @@ +//! Android ABI-compatibility module +//! +//! The ABI of Android has changed quite a bit over time, and std 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/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs new file mode 100644 index 00000000000..9f7dcc0416e --- /dev/null +++ b/library/std/src/sys/pal/unix/args.rs @@ -0,0 +1,282 @@ +//! 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, +} + +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 { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + 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 { + 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", + target_os = "aix", + target_os = "nto", + target_os = "hurd", +))] +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 { + 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) }; + let mut args = Vec::with_capacity(argc as usize); + for i in 0..argc { + let ptr = *argv.offset(i) as *const libc::c_char; + + // Some C commandline parsers (e.g. GLib and Qt) are replacing already + // handled arguments in `argv` with `NULL` and move them to the end. That + // means that `argc` might be bigger than the actual number of non-`NULL` + // pointers in `argv` at this point. + // + // To handle this we simply stop iterating at the first `NULL` argument. + // + // `argv` is also guaranteed to be `NULL`-terminated so any non-`NULL` arguments + // after the first `NULL` can safely be ignored. + if ptr.is_null() { + break; + } + + let cstr = CStr::from_ptr(ptr); + args.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); + } + + args + } + } +} + +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", 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::>() + }; + 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 = "tvos", 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(c"processInfo".as_ptr() as *const libc::c_uchar); + let arguments_sel = sel_registerName(c"arguments".as_ptr() as *const libc::c_uchar); + let utf8_sel = sel_registerName(c"UTF8String".as_ptr() as *const libc::c_uchar); + let count_sel = sel_registerName(c"count".as_ptr() as *const libc::c_uchar); + let object_at_sel = + sel_registerName(c"objectAtIndex:".as_ptr() as *const libc::c_uchar); + + let klass = objc_getClass(c"NSProcessInfo".as_ptr() as *const libc::c_uchar); + 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(any(target_os = "espidf", target_os = "vita"))] +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/pal/unix/cmath.rs b/library/std/src/sys/pal/unix/cmath.rs new file mode 100644 index 00000000000..5346d229116 --- /dev/null +++ b/library/std/src/sys/pal/unix/cmath.rs @@ -0,0 +1,37 @@ +#![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; + pub fn tgamma(n: f64) -> f64; + pub fn tgammaf(n: f32) -> f32; + pub fn lgamma_r(n: f64, s: &mut i32) -> f64; + pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; +} diff --git a/library/std/src/sys/pal/unix/env.rs b/library/std/src/sys/pal/unix/env.rs new file mode 100644 index 00000000000..3d4ba509829 --- /dev/null +++ b/library/std/src/sys/pal/unix/env.rs @@ -0,0 +1,263 @@ +#[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 = "tvos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "tvos"; + 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(target_os = "hurd")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "hurd"; + 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 = "vita")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "vita"; + 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 = "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 = "nto")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "nto"; + 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 = ""; +} + +#[cfg(target_os = "aix")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "aix"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".a"; + pub const DLL_EXTENSION: &str = "a"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs new file mode 100644 index 00000000000..bf1fb3123c4 --- /dev/null +++ b/library/std/src/sys/pal/unix/fd.rs @@ -0,0 +1,557 @@ +#![unstable(reason = "not public", issue = "none", feature = "fd")] + +#[cfg(test)] +mod tests; + +use crate::cmp; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; +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", + target_os = "hurd", +))] +use libc::off64_t; +#[cfg(not(any( + target_os = "linux", + target_os = "emscripten", + target_os = "l4re", + target_os = "android", + target_os = "hurd", +)))] +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 = "tvos", + 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", + target_os = "nto", +))] +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 = "tvos", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "nto", + target_os = "openbsd", + target_os = "horizon", + target_os = "vita", + 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 { + 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", target_os = "vita")))] + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let ret = cvt(unsafe { + libc::readv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec 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", target_os = "vita"))] + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + 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", target_os = "vita"))) + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut me = self; + (&mut me).read_to_end(buf) + } + + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + #[cfg(not(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd" + )))] + use libc::pread as pread64; + #[cfg(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd" + ))] + 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, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + let ret = cvt(unsafe { + libc::read( + self.as_raw_fd(), + cursor.as_mut().as_mut_ptr() as *mut libc::c_void, + cmp::min(cursor.capacity(), READ_LIMIT), + ) + })?; + + // Safety: `ret` bytes were written to the initialized portion of the buffer + unsafe { + cursor.advance(ret as usize); + } + Ok(()) + } + + #[cfg(any( + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + ))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + let ret = cvt(unsafe { + libc::preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + #[cfg(not(any( + target_os = "android", + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_os = "ios", + target_os = "tvos", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + )))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + io::default_read_vectored(|b| self.read_at(b, offset), bufs) + } + + // We support some old Android versions that do not have `preadv` in libc, + // so we use weak linkage and fallback to a direct syscall if not available. + // + // On 32-bit targets, we don't want to deal with weird ABI issues around + // passing 64-bits parameters to syscalls, so we fallback to the default + // implementation if `preadv` is not available. + #[cfg(all(target_os = "android", target_pointer_width = "64"))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + super::weak::syscall! { + fn preadv( + fd: libc::c_int, + iovec: *const libc::iovec, + n_iovec: libc::c_int, + offset: off64_t + ) -> isize + } + + let ret = cvt(unsafe { + preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + // We support old MacOS and iOS versions that do not have `preadv`. There is + // no `syscall` possible in these platform. + #[cfg(any( + all(target_os = "android", target_pointer_width = "32"), + target_os = "ios", + target_os = "tvos", + target_os = "macos", + ))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); + + match preadv64.get() { + Some(preadv) => { + let ret = cvt(unsafe { + preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_read_vectored(|b| self.read_at(b, offset), bufs), + } + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + 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", target_os = "vita")))] + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + 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", target_os = "vita"))] + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + 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", target_os = "vita"))) + } + + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + #[cfg(not(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd" + )))] + use libc::pwrite as pwrite64; + #[cfg(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd" + ))] + 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(any( + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + ))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + let ret = cvt(unsafe { + libc::pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + #[cfg(not(any( + target_os = "android", + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_os = "ios", + target_os = "tvos", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + )))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + io::default_write_vectored(|b| self.write_at(b, offset), bufs) + } + + // We support some old Android versions that do not have `pwritev` in libc, + // so we use weak linkage and fallback to a direct syscall if not available. + // + // On 32-bit targets, we don't want to deal with weird ABI issues around + // passing 64-bits parameters to syscalls, so we fallback to the default + // implementation if `pwritev` is not available. + #[cfg(all(target_os = "android", target_pointer_width = "64"))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + super::weak::syscall! { + fn pwritev( + fd: libc::c_int, + iovec: *const libc::iovec, + n_iovec: libc::c_int, + offset: off64_t + ) -> isize + } + + let ret = cvt(unsafe { + pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + // We support old MacOS and iOS versions that do not have `pwritev`. There is + // no `syscall` possible in these platform. + #[cfg(any( + all(target_os = "android", target_pointer_width = "32"), + target_os = "ios", + target_os = "tvos", + target_os = "macos", + ))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); + + match pwritev64.get() { + Some(pwritev) => { + let ret = cvt(unsafe { + pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_write_vectored(|b| self.write_at(b, offset), bufs), + } + } + + #[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", + target_os = "nto", + )))] + 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 = "vita")) + ), + 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", + target_os = "nto", + ))] + 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", target_os = "vita"))] + pub fn set_cloexec(&self) -> io::Result<()> { + // FD_CLOEXEC is not supported in ESP-IDF, Horizon OS and Vita but there's no need to, + // because none of them 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 { + Ok(Self(self.0.try_clone()?)) + } +} + +impl<'a> Read for &'a FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } + + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf(cursor) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + (**self).read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + (**self).is_read_vectored() + } +} + +impl AsInner for FileDesc { + #[inline] + fn as_inner(&self) -> &OwnedFd { + &self.0 + } +} + +impl IntoInner for FileDesc { + fn into_inner(self) -> OwnedFd { + self.0 + } +} + +impl FromInner 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 { + #[inline] + 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/pal/unix/fd/tests.rs b/library/std/src/sys/pal/unix/fd/tests.rs new file mode 100644 index 00000000000..5d17e46786c --- /dev/null +++ b/library/std/src/sys/pal/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::>(); + assert!(stdout.write_vectored(&bufs).is_ok()); +} diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs new file mode 100644 index 00000000000..72e7b1b1fc3 --- /dev/null +++ b/library/std/src/sys/pal/unix/fs.rs @@ -0,0 +1,2162 @@ +// miri has some special hacks here that make things unused. +#![cfg_attr(miri, allow(unused))] + +use crate::os::unix::prelude::*; + +use crate::ffi::{CStr, OsStr, OsString}; +use crate::fmt; +use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, 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::common::small_c_string::run_path_with_cstr; +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 = "tvos", + target_os = "watchos", +))] +use crate::sys::weak::syscall; +#[cfg(any(target_os = "android", target_os = "macos", target_os = "solaris"))] +use crate::sys::weak::weak; + +use libc::{c_int, mode_t}; + +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "solaris", + all(target_os = "linux", target_env = "gnu") +))] +use libc::c_char; +#[cfg(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "emscripten", + target_os = "android", + target_os = "hurd" +))] +use libc::dirfd; +#[cfg(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "emscripten", + target_os = "hurd" +))] +use libc::fstatat64; +#[cfg(any( + target_os = "android", + target_os = "solaris", + target_os = "fuchsia", + target_os = "redox", + target_os = "illumos", + target_os = "aix", + target_os = "nto", + target_os = "vita", + all(target_os = "linux", target_env = "musl"), +))] +use libc::readdir as readdir64; +#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] +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", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", +)))] +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( + all(target_os = "linux", not(target_env = "musl")), + target_os = "emscripten", + target_os = "l4re", + target_os = "android", + target_os = "hurd", +)))] +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( + all(target_os = "linux", not(target_env = "musl")), + target_os = "emscripten", + target_os = "l4re", + target_os = "hurd" +))] +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, + } + + #[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> { + use crate::sync::atomic::{AtomicU8, Ordering}; + + // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`. + // We check for it on first failure and remember availability to avoid having to + // do it again. + #[repr(u8)] + enum STATX_STATE{ Unknown = 0, Present, Unavailable } + static STATX_SAVED_STATE: AtomicU8 = AtomicU8::new(STATX_STATE::Unknown as u8); + + syscall! { + fn statx( + fd: c_int, + pathname: *const c_char, + flags: c_int, + mask: libc::c_uint, + statxbuf: *mut libc::statx + ) -> c_int + } + + if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Unavailable as u8 { + return None; + } + + let mut buf: libc::statx = mem::zeroed(); + if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) { + if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Present as u8 { + return Some(Err(err)); + } + + // Availability not checked yet. + // + // First try the cheap way. + if err.raw_os_error() == Some(libc::ENOSYS) { + STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed); + return None; + } + + // Error other than `ENOSYS` is not a good enough indicator -- it is + // known that `EPERM` can be returned as a result of using seccomp to + // block the syscall. + // Availability is checked by performing a call which expects `EFAULT` + // if the syscall is usable. + // See: https://github.com/rust-lang/rust/issues/65662 + // FIXME this can probably just do the call if `EPERM` was received, but + // previous iteration of the code checked it for all errors and for now + // this is retained. + // FIXME what about transient conditions like `ENOMEM`? + let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut())) + .err() + .and_then(|e| e.raw_os_error()); + if err2 == Some(libc::EFAULT) { + STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed); + return Some(Err(err)); + } else { + STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed); + return None; + } + } + + // 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, + end_of_stream: bool, +} + +impl ReadDir { + fn new(inner: InnerReadDir) -> Self { + Self { inner: Arc::new(inner), end_of_stream: false } + } +} + +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", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", +))] +pub struct DirEntry { + dir: Arc, + 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: crate::ffi::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", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", +))] +struct dirent64_min { + d_ino: u64, + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "aix", + target_os = "nto", + target_os = "vita", + )))] + d_type: u8, +} + +#[cfg(not(any( + target_os = "android", + target_os = "linux", + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", +)))] +pub struct DirEntry { + dir: Arc, + // 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, Debug, Default)] +pub struct FileTimes { + accessed: Option, + modified: Option, + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] + created: Option, +} + +#[derive(Copy, Clone, Eq, Debug)] +pub struct FileType { + mode: mode_t, +} + +impl PartialEq for FileType { + fn eq(&self, other: &Self) -> bool { + self.masked() == other.masked() + } +} + +impl core::hash::Hash for FileType { + fn hash(&self, state: &mut H) { + self.masked().hash(state); + } +} + +#[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 { + Ok(SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64)) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64)) + } + + pub fn created(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64)) + } +} + +#[cfg(target_os = "aix")] +impl FileAttr { + pub fn modified(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64)) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64)) + } + + pub fn created(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64)) + } +} + +#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix")))] +impl FileAttr { + #[cfg(not(any( + target_os = "vxworks", + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "hurd", + )))] + pub fn modified(&self) -> io::Result { + #[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", target_os = "vita"))] + pub fn modified(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_mtime as i64, 0)) + } + + #[cfg(any(target_os = "horizon", target_os = "hurd"))] + pub fn modified(&self) -> io::Result { + Ok(SystemTime::from(self.stat.st_mtim)) + } + + #[cfg(not(any( + target_os = "vxworks", + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "hurd", + )))] + pub fn accessed(&self) -> io::Result { + #[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", target_os = "vita"))] + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_atime as i64, 0)) + } + + #[cfg(any(target_os = "horizon", target_os = "hurd"))] + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from(self.stat.st_atim)) + } + + #[cfg(any( + target_os = "freebsd", + target_os = "openbsd", + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + ))] + pub fn created(&self) -> io::Result { + 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 = "tvos", + target_os = "watchos", + target_os = "vita", + )))] + pub fn created(&self) -> io::Result { + 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", + )) + } + + #[cfg(target_os = "vita")] + pub fn created(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_ctime as i64, 0)) + } +} + +#[cfg(target_os = "nto")] +impl FileAttr { + pub fn modified(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec)) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec)) + } + + pub fn created(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec)) + } +} + +impl AsInner for FileAttr { + #[inline] + 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 ` + self.mode &= !0o222; + } else { + // add write permission for all classes; equivalent to `chmod a+w ` + self.mode |= 0o222; + } + } + pub fn mode(&self) -> u32 { + self.mode as u32 + } +} + +impl FileTimes { + pub fn set_accessed(&mut self, t: SystemTime) { + self.accessed = Some(t); + } + + pub fn set_modified(&mut self, t: SystemTime) { + self.modified = Some(t); + } + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] + pub fn set_created(&mut self, t: SystemTime) { + self.created = Some(t); + } +} + +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.masked() == mode + } + + fn masked(&self) -> mode_t { + self.mode & libc::S_IFMT + } +} + +impl FromInner 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; + + #[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "solaris", + target_os = "fuchsia", + target_os = "redox", + target_os = "illumos", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", + ))] + fn next(&mut self) -> Option> { + if self.end_of_stream { + return None; + } + + 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() { + // We either encountered an error, or reached the end. Either way, + // the next call to next() should return None. + self.end_of_stream = true; + + // To distinguish between errors and end-of-directory, 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))), + }; + } + + // The dirent64 struct is a weird imaginary thing that isn't ever supposed + // to be worked with by value. Its trailing d_name field is declared + // variously as [c_char; 256] or [c_char; 1] on different systems but + // either way that size is meaningless; only the offset of d_name is + // meaningful. The dirent64 pointers that libc returns from readdir64 are + // allowed to point to allocations smaller _or_ LARGER than implied by the + // definition of the struct. + // + // As such, we need to be even more careful with dirent64 than if its + // contents were "simply" partially initialized data. + // + // Like for uninitialized contents, converting entry_ptr to `&dirent64` + // would not be legal. However, unique to dirent64 is that we don't even + // get to use `addr_of!((*entry_ptr).d_name)` because that operation + // requires the full extent of *entry_ptr to be in bounds of the same + // allocation, which is not necessarily the case here. + // + // Instead we must access fields individually through their offsets. + macro_rules! offset_ptr { + ($entry_ptr:expr, $field:ident) => {{ + const OFFSET: isize = mem::offset_of!(dirent64, $field) as isize; + if true { + // Cast to the same type determined by the else branch. + $entry_ptr.byte_offset(OFFSET).cast::<_>() + } else { + #[allow(deref_nullptr)] + { + ptr::addr_of!((*ptr::null::()).$field) + } + } + }}; + } + + // d_name is guaranteed to be null-terminated. + let name = CStr::from_ptr(offset_ptr!(entry_ptr, d_name).cast()); + let name_bytes = name.to_bytes(); + if name_bytes == b"." || name_bytes == b".." { + continue; + } + + #[cfg(not(target_os = "vita"))] + let entry = dirent64_min { + d_ino: *offset_ptr!(entry_ptr, d_ino) as u64, + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "aix", + target_os = "nto", + )))] + d_type: *offset_ptr!(entry_ptr, d_type) as u8, + }; + + #[cfg(target_os = "vita")] + let entry = dirent64_min { d_ino: 0u64 }; + + return Some(Ok(DirEntry { + entry, + name: name.to_owned(), + dir: Arc::clone(&self.inner), + })); + } + } + } + + #[cfg(not(any( + target_os = "android", + target_os = "linux", + target_os = "solaris", + target_os = "fuchsia", + target_os = "redox", + target_os = "illumos", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", + )))] + fn next(&mut self) -> Option> { + 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) }; + assert!( + r == 0 || crate::io::Error::last_os_error().is_interrupted(), + "unexpected error during closedir: {:?}", + crate::io::Error::last_os_error() + ); + } +} + +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(all( + any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "emscripten", + target_os = "android", + target_os = "hurd" + ), + not(miri) + ))] + pub fn metadata(&self) -> io::Result { + 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(any( + not(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "emscripten", + target_os = "android", + target_os = "hurd", + )), + miri + ))] + pub fn metadata(&self) -> io::Result { + lstat(&self.path()) + } + + #[cfg(any( + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "vxworks", + target_os = "aix", + target_os = "nto", + target_os = "vita", + ))] + pub fn file_type(&self) -> io::Result { + self.metadata().map(|m| m.file_type()) + } + + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "vxworks", + target_os = "aix", + target_os = "nto", + target_os = "vita", + )))] + pub fn file_type(&self) -> io::Result { + 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 = "tvos", + 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", + target_os = "vita", + target_os = "aix", + target_os = "nto", + target_os = "hurd", + ))] + 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 = "tvos", + 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 = "tvos", + 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", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", + )))] + 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", + target_os = "aix", + target_os = "nto", + target_os = "vita", + target_os = "hurd", + ))] + 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 { + 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 { + 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 { + run_path_with_cstr(path, |path| File::open_c(path, opts)) + } + + pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result { + 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 { + let fd = self.as_raw_fd(); + + cfg_has_statx! { + if let Some(ret) = unsafe { try_statx( + fd, + c"".as_ptr() 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 = "tvos", + 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 = "tvos", + 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 = "tvos", + 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", + target_os = "nto", + target_os = "hurd", + ))] + 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 = "tvos", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "watchos", + target_os = "nto", + target_os = "hurd", + )))] + 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 { + self.0.read(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + 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 { + self.0.read_at(buf, offset) + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(cursor) + } + + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + self.0.read_vectored_at(bufs, offset) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + 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 { + self.0.write_at(buf, offset) + } + + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + self.0.write_vectored_at(bufs, offset) + } + + #[inline] + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + 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 { + 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(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))] + let to_timespec = |time: Option| match time { + Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), + Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time" + )), + Some(_) => Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "timestamp is too small to set as a file time" + )), + None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), + }; + cfg_if::cfg_if! { + if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon"))] { + // Redox doesn't appear to support `UTIME_OMIT`. + // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore + // the same as for Redox. + let _ = times; + Err(io::const_io_error!( + io::ErrorKind::Unsupported, + "setting file times not supported", + )) + } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] { + let mut buf = [mem::MaybeUninit::::uninit(); 3]; + let mut num_times = 0; + let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; + attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; + if times.created.is_some() { + buf[num_times].write(to_timespec(times.created)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_CRTIME; + } + if times.modified.is_some() { + buf[num_times].write(to_timespec(times.modified)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_MODTIME; + } + if times.accessed.is_some() { + buf[num_times].write(to_timespec(times.accessed)?); + num_times += 1; + attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; + } + cvt(unsafe { libc::fsetattrlist( + self.as_raw_fd(), + (&attrlist as *const libc::attrlist).cast::().cast_mut(), + buf.as_ptr().cast::().cast_mut(), + num_times * mem::size_of::(), + 0 + ) })?; + Ok(()) + } else if #[cfg(target_os = "android")] { + let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + // futimens requires 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.as_ptr()), + None => return Err(io::const_io_error!( + io::ErrorKind::Unsupported, + "setting file times requires Android API level >= 19", + )), + } + })?; + Ok(()) + } else { + #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))] + { + use crate::sys::{time::__timespec64, weak::weak}; + + // Added in glibc 2.34 + weak!(fn __futimens64(libc::c_int, *const __timespec64) -> libc::c_int); + + if let Some(futimens64) = __futimens64.get() { + let to_timespec = |time: Option| time.map(|time| time.t.to_timespec64()) + .unwrap_or(__timespec64::new(0, libc::UTIME_OMIT as _)); + let times = [to_timespec(times.accessed), to_timespec(times.modified)]; + cvt(unsafe { futimens64(self.as_raw_fd(), times.as_ptr()) })?; + return Ok(()); + } + } + let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) })?; + Ok(()) + } + } + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { mode: 0o777 } + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + run_path_with_cstr(p, |p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ())) + } + + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as mode_t; + } +} + +impl AsInner for File { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl AsInnerMut for File { + #[inline] + fn as_inner_mut(&mut self) -> &mut FileDesc { + &mut self.0 + } +} + +impl IntoInner for File { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for File { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for File { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for File { + #[inline] + 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", + target_os = "illumos", + target_os = "solaris" + ))] + fn get_path(fd: c_int) -> Option { + 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 { + // 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 { + let info = Box::::new_zeroed(); + let mut info = unsafe { info.assume_init() }; + info.kf_structsize = mem::size_of::() 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 { + 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", + target_os = "illumos", + target_os = "solaris" + )))] + fn get_path(_fd: c_int) -> Option { + // FIXME(#24570): implement this for other Unix platforms + None + } + + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "freebsd", + target_os = "hurd", + target_os = "netbsd", + target_os = "openbsd", + 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 = "freebsd", + target_os = "hurd", + target_os = "netbsd", + target_os = "openbsd", + 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(path: &Path) -> io::Result { + let ptr = run_path_with_cstr(path, |p| unsafe { Ok(libc::opendir(p.as_ptr())) })?; + if ptr.is_null() { + Err(Error::last_os_error()) + } else { + let root = path.to_path_buf(); + let inner = InnerReadDir { dirp: Dir(ptr), root }; + Ok(ReadDir::new(inner)) + } +} + +pub fn unlink(p: &Path) -> io::Result<()> { + run_path_with_cstr(p, |p| cvt(unsafe { libc::unlink(p.as_ptr()) }).map(|_| ())) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + run_path_with_cstr(old, |old| { + run_path_with_cstr(new, |new| { + cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ()) + }) + }) +} + +pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + run_path_with_cstr(p, |p| cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ())) +} + +pub fn rmdir(p: &Path) -> io::Result<()> { + run_path_with_cstr(p, |p| cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ())) +} + +pub fn readlink(p: &Path) -> io::Result { + run_path_with_cstr(p, |c_path| { + 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<()> { + run_path_with_cstr(original, |original| { + run_path_with_cstr(link, |link| { + cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ()) + }) + }) +} + +pub fn link(original: &Path, link: &Path) -> io::Result<()> { + run_path_with_cstr(original, |original| { + run_path_with_cstr(link, |link| { + cfg_if::cfg_if! { + if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { + // 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(any(target_os = "macos", target_os = "solaris"))] { + // MacOS (<=10.9) and Solaris 10 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 { + run_path_with_cstr(p, |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 { + run_path_with_cstr(p, |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 { + let r = run_path_with_cstr(p, |path| unsafe { + Ok(libc::realpath(path.as_ptr(), ptr::null_mut())) + })?; + if r.is_null() { + return Err(io::Error::last_os_error()); + } + Ok(PathBuf::from(OsString::from_vec(unsafe { + let buf = CStr::from_ptr(r).to_bytes().to_vec(); + libc::free(r as *mut _); + 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()?; + // fchmod is broken on vita + #[cfg(not(target_os = "vita"))] + 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 = "tvos", + target_os = "watchos", +)))] +pub fn copy(from: &Path, to: &Path) -> io::Result { + 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 { + 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 = "tvos", target_os = "watchos"))] +pub fn copy(from: &Path, to: &Path) -> io::Result { + 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 clonefile_result = run_path_with_cstr(to, |to| { + 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<()> { + run_path_with_cstr(path, |path| { + cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) + .map(|_| ()) + }) +} + +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<()> { + run_path_with_cstr(path, |path| { + cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) + .map(|_| ()) + }) +} + +#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] +pub fn chroot(dir: &Path) -> io::Result<()> { + run_path_with_cstr(dir, |dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ())) +} + +pub use remove_dir_impl::remove_dir_all; + +// Fallback for REDOX, ESP-ID, Horizon, Vita and Miri +#[cfg(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nto", + 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", + target_os = "vita", + target_os = "nto", + miri +)))] +mod remove_dir_impl { + use super::{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::sys::common::small_c_string::run_path_with_cstr; + use crate::sys::{cvt, cvt_r}; + + #[cfg(not(any( + all(target_os = "linux", target_env = "gnu"), + all(target_os = "macos", not(target_arch = "aarch64")) + )))] + use libc::{fdopendir, openat, unlinkat}; + #[cfg(all(target_os = "linux", target_env = "gnu"))] + use libc::{fdopendir, openat64 as 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 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, p: &CStr) -> io::Result { + 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 `DirEntry`s. + let dummy_root = PathBuf::new(); + let inner = InnerReadDir { dirp, root: dummy_root }; + Ok((ReadDir::new(inner), new_parent_fd)) + } + + #[cfg(any( + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "vxworks", + target_os = "aix", + ))] + fn is_dir(_ent: &DirEntry) -> Option { + None + } + + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "vxworks", + target_os = "aix", + )))] + fn is_dir(ent: &DirEntry) -> Option { + match ent.entry.d_type { + libc::DT_UNKNOWN => None, + libc::DT_DIR => Some(true), + _ => Some(false), + } + } + + fn remove_dir_all_recursive(parent_fd: Option, 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 { + run_path_with_cstr(p, |p| remove_dir_all_recursive(None, &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/pal/unix/futex.rs b/library/std/src/sys/pal/unix/futex.rs new file mode 100644 index 00000000000..d310be6c7a1 --- /dev/null +++ b/library/std/src/sys/pal/unix/futex.rs @@ -0,0 +1,301 @@ +#![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) -> 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::(), // 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) -> 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) -> 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) -> 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) -> bool { + // 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/pal/unix/io.rs b/library/std/src/sys/pal/unix/io.rs new file mode 100644 index 00000000000..29c340dd349 --- /dev/null +++ b/library/std/src/sys/pal/unix/io.rs @@ -0,0 +1,82 @@ +use crate::marker::PhantomData; +use crate::os::fd::{AsFd, AsRawFd}; +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) } + } +} + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + unsafe { libc::isatty(fd.as_raw_fd()) != 0 } +} diff --git a/library/std/src/sys/pal/unix/kernel_copy.rs b/library/std/src/sys/pal/unix/kernel_copy.rs new file mode 100644 index 00000000000..18acd5ecccd --- /dev/null +++ b/library/std/src/sys/pal/unix/kernel_copy.rs @@ -0,0 +1,730 @@ +//! 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 it attempts +//! to use them one after another (guided by hints) to figure out which one works 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; +#[cfg(not(any(all(target_os = "linux", target_env = "gnu"), target_os = "hurd")))] +use libc::sendfile as sendfile64; +#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "hurd"))] +use libc::sendfile64; +use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV}; + +#[cfg(test)] +mod tests; + +pub(crate) fn copy_spec( + read: &mut R, + write: &mut W, +) -> Result { + 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 { + Metadata(Metadata), + Socket, + Pipe, + /// We don't have any metadata because the stat syscall failed + NoneObtained, +} + +#[derive(PartialEq)] +enum FdHandle { + Input, + Output, +} + +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, f: FdHandle) -> 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 f == FdHandle::Input && meta.is_file() && meta.len() > 0 => { + true + } + FdMeta::Metadata(meta) if f == FdHandle::Output && meta.is_file() => true, + _ => false, + } + } +} + +/// Returns true either if changes made to the source after a sendfile/splice call won't become +/// visible in the sink or the source has explicitly opted into such behavior (e.g. by splicing +/// a file into a pipe, the pipe being the source in this case). +/// +/// This will prevent File -> Pipe and File -> Socket splicing/sendfile optimizations to uphold +/// the Read/Write API semantics of io::copy. +/// +/// Note: This is not 100% airtight, the caller can use the RawFd conversion methods to turn a +/// regular file into a TcpSocket which will be treated as a socket here without checking. +fn safe_kernel_copy(source: &FdMeta, sink: &FdMeta) -> bool { + match (source, sink) { + // Data arriving from a socket is safe because the sender can't modify the socket buffer. + // Data arriving from a pipe is safe(-ish) because either the sender *copied* + // the bytes into the pipe OR explicitly performed an operation that enables zero-copy, + // thus promising not to modify the data later. + (FdMeta::Socket, _) => true, + (FdMeta::Pipe, _) => true, + (FdMeta::Metadata(meta), _) + if meta.file_type().is_fifo() || meta.file_type().is_socket() => + { + true + } + // Data going into non-pipes/non-sockets is safe because the "later changes may become visible" issue + // only happens for pages sitting in send buffers or pipes. + (_, FdMeta::Metadata(meta)) + if !meta.file_type().is_fifo() && !meta.file_type().is_socket() => + { + true + } + _ => false, + } +} + +struct CopyParams(FdMeta, Option); + +struct Copier<'a, 'b, R: Read + ?Sized, W: Write + ?Sized> { + read: &'a mut R, + write: &'b mut W, +} + +trait SpecCopy { + fn copy(self) -> Result; +} + +impl SpecCopy for Copier<'_, '_, R, W> { + default fn copy(self) -> Result { + generic_copy(self.read, self.write) + } +} + +impl SpecCopy for Copier<'_, '_, R, W> { + fn copy(self) -> Result { + 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 { + 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(FdHandle::Input) + && output_meta.copy_file_range_candidate(FdHandle::Output) + { + 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() && safe_kernel_copy(&input_meta, &output_meta) + { + 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()) + && safe_kernel_copy(&input_meta, &output_meta) + { + 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(&mut self, _writer: &mut W, _limit: u64) -> Result { + 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>>` 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 CopyRead for &mut T +where + T: CopyRead, +{ + fn drain_to(&mut self, writer: &mut W, limit: u64) -> Result { + (**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 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(fd_to_meta(self), Some(self.as_raw_fd())) + } +} + +impl CopyWrite for &File { + fn properties(&self) -> CopyParams { + CopyParams(fd_to_meta(*self), 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(&mut self, writer: &mut W, outer_limit: u64) -> Result { + 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(fd_to_meta(self), Some(self.as_raw_fd())) + } +} + +impl CopyWrite for StderrLock<'_> { + fn properties(&self) -> CopyParams { + CopyParams(fd_to_meta(self), Some(self.as_raw_fd())) + } +} + +impl CopyRead for Take { + fn drain_to(&mut self, writer: &mut W, outer_limit: u64) -> Result { + 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 CopyRead for BufReader { + fn drain_to(&mut self, writer: &mut W, outer_limit: u64) -> Result { + 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 CopyWrite for BufWriter { + fn properties(&self) -> CopyParams { + self.get_ref().properties() + } +} + +fn fd_to_meta(fd: &T) -> FdMeta { + let fd = fd.as_raw_fd(); + let file: ManuallyDrop = 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 { sendfile64(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/pal/unix/kernel_copy/tests.rs b/library/std/src/sys/pal/unix/kernel_copy/tests.rs new file mode 100644 index 00000000000..a524270e3fb --- /dev/null +++ b/library/std/src/sys/pal/unix/kernel_copy/tests.rs @@ -0,0 +1,312 @@ +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(()) +} + +#[test] +fn dont_splice_pipes_from_files() -> Result<()> { + // splicing to a pipe and then modifying the source could lead to changes + // becoming visible in an unexpected order. + + use crate::io::SeekFrom; + use crate::os::unix::fs::FileExt; + use crate::process::{ChildStdin, ChildStdout}; + use crate::sys_common::FromInner; + + let (read_end, write_end) = crate::sys::pipe::anon_pipe()?; + + let mut read_end = ChildStdout::from_inner(read_end); + let mut write_end = ChildStdin::from_inner(write_end); + + let tmp_path = tmpdir(); + let file = tmp_path.join("to_be_modified"); + let mut file = + crate::fs::OpenOptions::new().create_new(true).read(true).write(true).open(file)?; + + const SZ: usize = libc::PIPE_BUF as usize; + + // put data in page cache + let mut buf: [u8; SZ] = [0x01; SZ]; + file.write_all(&buf).unwrap(); + + // copy page into pipe + file.seek(SeekFrom::Start(0)).unwrap(); + assert!(io::copy(&mut file, &mut write_end).unwrap() == SZ as u64); + + // modify file + buf[0] = 0x02; + file.write_at(&buf, 0).unwrap(); + + // read from pipe + read_end.read_exact(buf.as_mut_slice()).unwrap(); + + assert_eq!(buf[0], 0x01, "data in pipe should reflect the original, not later modifications"); + + 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/pal/unix/l4re.rs b/library/std/src/sys/pal/unix/l4re.rs new file mode 100644 index 00000000000..fe9559f2a56 --- /dev/null +++ b/library/std/src/sys/pal/unix/l4re.rs @@ -0,0 +1,560 @@ +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, BorrowedCursor, 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 { + unimpl!(); + } + + pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result { + 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 { + unimpl!(); + } + + pub fn duplicate(&self) -> io::Result { + unimpl!(); + } + + pub fn read(&self, _: &mut [u8]) -> io::Result { + unimpl!(); + } + + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { + unimpl!(); + } + + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + 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 { + unimpl!(); + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { + unimpl!(); + } + + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn set_timeout(&self, _: Option, _: libc::c_int) -> io::Result<()> { + unimpl!(); + } + + pub fn timeout(&self, _: libc::c_int) -> io::Result> { + unimpl!(); + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + unimpl!(); + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn linger(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn nodelay(&self) -> io::Result { + unimpl!(); + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn take_error(&self) -> io::Result> { + 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 for Socket { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } + } + + impl FromInner for Socket { + fn from_inner(file_desc: FileDesc) -> Socket { + Socket(file_desc) + } + } + + impl IntoInner 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 { + #[inline] + 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 { + unimpl!(); + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unimpl!(); + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn read_timeout(&self) -> io::Result> { + unimpl!(); + } + + pub fn write_timeout(&self) -> io::Result> { + unimpl!(); + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + unimpl!(); + } + + pub fn read(&self, _: &mut [u8]) -> io::Result { + unimpl!(); + } + + pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { + unimpl!(); + } + + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { + unimpl!(); + } + + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn write(&self, _: &[u8]) -> io::Result { + unimpl!(); + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { + unimpl!(); + } + + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn peer_addr(&self) -> io::Result { + unimpl!(); + } + + pub fn socket_addr(&self) -> io::Result { + unimpl!(); + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + unimpl!(); + } + + pub fn duplicate(&self) -> io::Result { + unimpl!(); + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn linger(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn nodelay(&self) -> io::Result { + unimpl!(); + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unimpl!(); + } + + pub fn ttl(&self) -> io::Result { + unimpl!(); + } + + pub fn take_error(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + } + + impl FromInner for TcpStream { + fn from_inner(socket: Socket) -> TcpStream { + TcpStream { inner: socket } + } + } + + impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "No networking support available on L4Re") + } + } + + pub struct TcpListener { + inner: Socket, + } + + impl TcpListener { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unimpl!(); + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn socket_addr(&self) -> io::Result { + unimpl!(); + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + unimpl!(); + } + + pub fn duplicate(&self) -> io::Result { + unimpl!(); + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unimpl!(); + } + + pub fn ttl(&self) -> io::Result { + unimpl!(); + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn only_v6(&self) -> io::Result { + unimpl!(); + } + + pub fn take_error(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + } + + impl FromInner for TcpListener { + fn from_inner(socket: Socket) -> TcpListener { + TcpListener { inner: socket } + } + } + + impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "No networking support available on L4Re.") + } + } + + pub struct UdpSocket { + inner: Socket, + } + + impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unimpl!(); + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn peer_addr(&self) -> io::Result { + unimpl!(); + } + + pub fn socket_addr(&self) -> io::Result { + 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 { + unimpl!(); + } + + pub fn duplicate(&self) -> io::Result { + unimpl!(); + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn read_timeout(&self) -> io::Result> { + unimpl!(); + } + + pub fn write_timeout(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn broadcast(&self) -> io::Result { + unimpl!(); + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn multicast_loop_v4(&self) -> io::Result { + unimpl!(); + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + unimpl!(); + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + unimpl!(); + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn multicast_loop_v6(&self) -> io::Result { + 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 { + unimpl!(); + } + + pub fn take_error(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + unimpl!(); + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + unimpl!(); + } + + pub fn send(&self, _: &[u8]) -> io::Result { + unimpl!(); + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + unimpl!(); + } + } + + impl FromInner for UdpSocket { + fn from_inner(socket: Socket) -> UdpSocket { + UdpSocket { inner: socket } + } + } + + impl fmt::Debug for UdpSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + 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 { + 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 { + unimpl!(); + } + } + + impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + unimpl!(); + } + } +} diff --git a/library/std/src/sys/pal/unix/locks/fuchsia_mutex.rs b/library/std/src/sys/pal/unix/locks/fuchsia_mutex.rs new file mode 100644 index 00000000000..5d89e5a13fd --- /dev/null +++ b/library/std/src/sys/pal/unix/locks/fuchsia_mutex.rs @@ -0,0 +1,164 @@ +//! 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 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 fn try_lock(&self) -> bool { + let thread_self = unsafe { zx_thread_self() }; + self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed).is_ok() + } + + #[inline] + pub fn lock(&self) { + let thread_self = unsafe { zx_thread_self() }; + if let Err(state) = + self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed) + { + unsafe { + self.lock_contested(state, thread_self); + } + } + } + + /// # Safety + /// `thread_self` must be the handle for the current thread. + #[cold] + unsafe 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 occurred, 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/pal/unix/locks/futex_condvar.rs b/library/std/src/sys/pal/unix/locks/futex_condvar.rs new file mode 100644 index 00000000000..4bd65dd25c2 --- /dev/null +++ b/library/std/src/sys/pal/unix/locks/futex_condvar.rs @@ -0,0 +1,56 @@ +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 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 fn notify_one(&self) { + self.futex.fetch_add(1, Relaxed); + futex_wake(&self.futex); + } + + pub 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) -> 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/pal/unix/locks/futex_mutex.rs b/library/std/src/sys/pal/unix/locks/futex_mutex.rs new file mode 100644 index 00000000000..c01229586c3 --- /dev/null +++ b/library/std/src/sys/pal/unix/locks/futex_mutex.rs @@ -0,0 +1,96 @@ +use crate::sync::atomic::{ + AtomicU32, + Ordering::{Acquire, Relaxed, Release}, +}; +use crate::sys::futex::{futex_wait, futex_wake}; + +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 fn try_lock(&self) -> bool { + self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_ok() + } + + #[inline] + pub 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 successfully 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/pal/unix/locks/futex_rwlock.rs b/library/std/src/sys/pal/unix/locks/futex_rwlock.rs new file mode 100644 index 00000000000..aa0de900238 --- /dev/null +++ b/library/std/src/sys/pal/unix/locks/futex_rwlock.rs @@ -0,0 +1,320 @@ +use crate::sync::atomic::{ + AtomicU32, + Ordering::{Acquire, Relaxed, Release}, +}; +use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; + +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 necessary. + 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 fn try_read(&self) -> bool { + self.state + .fetch_update(Acquire, Relaxed, |s| is_read_lockable(s).then(|| s + READ_LOCKED)) + .is_ok() + } + + #[inline] + pub 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 fn try_write(&self) -> bool { + self.state + .fetch_update(Acquire, Relaxed, |s| is_unlocked(s).then(|| s + WRITE_LOCKED)) + .is_ok() + } + + #[inline] + pub 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/pal/unix/locks/mod.rs b/library/std/src/sys/pal/unix/locks/mod.rs new file mode 100644 index 00000000000..b2e0e49ad73 --- /dev/null +++ b/library/std/src/sys/pal/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; + pub(crate) use futex_rwlock::RwLock; + pub(crate) use futex_condvar::Condvar; + } else if #[cfg(target_os = "fuchsia")] { + mod fuchsia_mutex; + mod futex_rwlock; + mod futex_condvar; + pub(crate) use fuchsia_mutex::Mutex; + pub(crate) use futex_rwlock::RwLock; + pub(crate) use futex_condvar::Condvar; + } else { + mod pthread_mutex; + mod pthread_rwlock; + mod pthread_condvar; + pub(crate) use pthread_mutex::Mutex; + pub(crate) use pthread_rwlock::RwLock; + pub(crate) use pthread_condvar::Condvar; + } +} diff --git a/library/std/src/sys/pal/unix/locks/pthread_condvar.rs b/library/std/src/sys/pal/unix/locks/pthread_condvar.rs new file mode 100644 index 00000000000..2dc1b0c601e --- /dev/null +++ b/library/std/src/sys/pal/unix/locks/pthread_condvar.rs @@ -0,0 +1,206 @@ +use crate::cell::UnsafeCell; +use crate::ptr; +use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed}; +use crate::sys::locks::{pthread_mutex, Mutex}; +#[cfg(not(target_os = "nto"))] +use crate::sys::time::TIMESPEC_MAX; +#[cfg(target_os = "nto")] +use crate::sys::time::TIMESPEC_MAX_CAPPED; +use crate::sys_common::lazy_box::{LazyBox, LazyInit}; +use crate::time::Duration; + +struct AllocatedCondvar(UnsafeCell); + +pub struct Condvar { + inner: LazyBox, + mutex: AtomicPtr, +} + +#[inline] +fn raw(c: &Condvar) -> *mut libc::pthread_cond_t { + c.inner.0.get() +} + +unsafe impl Send for AllocatedCondvar {} +unsafe impl Sync for AllocatedCondvar {} + +impl LazyInit for AllocatedCondvar { + fn init() -> Box { + let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER))); + + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "l4re", + target_os = "android", + target_os = "redox" + ))] { + // `pthread_condattr_setclock` is unfortunately not supported on these platforms. + } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { + // 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). + let r = unsafe { libc::pthread_cond_init(condvar.0.get(), crate::ptr::null()) }; + assert_eq!(r, 0); + } else { + use crate::mem::MaybeUninit; + let mut attr = MaybeUninit::::uninit(); + let r = unsafe { libc::pthread_condattr_init(attr.as_mut_ptr()) }; + assert_eq!(r, 0); + let r = unsafe { libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC) }; + assert_eq!(r, 0); + let r = unsafe { libc::pthread_cond_init(condvar.0.get(), attr.as_ptr()) }; + assert_eq!(r, 0); + let r = unsafe { libc::pthread_condattr_destroy(attr.as_mut_ptr()) }; + assert_eq!(r, 0); + } + } + + condvar + } +} + +impl Drop for AllocatedCondvar { + #[inline] + fn drop(&mut self) { + let r = unsafe { libc::pthread_cond_destroy(self.0.get()) }; + if cfg!(target_os = "dragonfly") { + // 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); + } else { + debug_assert_eq!(r, 0); + } + } +} + +impl Condvar { + pub const fn new() -> Condvar { + Condvar { inner: LazyBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } + } + + #[inline] + fn verify(&self, mutex: *mut libc::pthread_mutex_t) { + // Relaxed is okay here because we never read through `self.addr`, and only use it to + // compare addresses. + match self.mutex.compare_exchange(ptr::null_mut(), mutex, Relaxed, Relaxed) { + Ok(_) => {} // Stored the address + Err(n) if n == mutex => {} // Lost a race to store the same address + _ => panic!("attempted to use a condition variable with two mutexes"), + } + } + + #[inline] + pub fn notify_one(&self) { + let r = unsafe { libc::pthread_cond_signal(raw(self)) }; + debug_assert_eq!(r, 0); + } + + #[inline] + pub fn notify_all(&self) { + let r = unsafe { libc::pthread_cond_broadcast(raw(self)) }; + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let mutex = pthread_mutex::raw(mutex); + self.verify(mutex); + let r = libc::pthread_cond_wait(raw(self), 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 = "tvos", + 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::sys::time::Timespec; + + let mutex = pthread_mutex::raw(mutex); + self.verify(mutex); + + #[cfg(not(target_os = "nto"))] + let timeout = Timespec::now(libc::CLOCK_MONOTONIC) + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec()) + .unwrap_or(TIMESPEC_MAX); + + #[cfg(target_os = "nto")] + let timeout = Timespec::now(libc::CLOCK_MONOTONIC) + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec_capped()) + .unwrap_or(TIMESPEC_MAX_CAPPED); + + let r = libc::pthread_cond_timedwait(raw(self), 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 = "tvos", + 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::sys::time::SystemTime; + use crate::time::Instant; + + let mutex = pthread_mutex::raw(mutex); + self.verify(mutex); + + // 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 returns 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. + let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400)); + + // pthread_cond_timedwait uses system time, but we want to report timeout + // based on stable time. + let now = Instant::now(); + + let timeout = SystemTime::now() + .t + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec()) + .unwrap_or(TIMESPEC_MAX); + + let r = libc::pthread_cond_timedwait(raw(self), 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 + now.elapsed() < dur + } +} diff --git a/library/std/src/sys/pal/unix/locks/pthread_mutex.rs b/library/std/src/sys/pal/unix/locks/pthread_mutex.rs new file mode 100644 index 00000000000..8a78bc1fd73 --- /dev/null +++ b/library/std/src/sys/pal/unix/locks/pthread_mutex.rs @@ -0,0 +1,131 @@ +use crate::cell::UnsafeCell; +use crate::mem::{forget, MaybeUninit}; +use crate::sys::cvt_nz; +use crate::sys_common::lazy_box::{LazyBox, LazyInit}; + +struct AllocatedMutex(UnsafeCell); + +pub struct Mutex { + inner: LazyBox, +} + +#[inline] +pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t { + m.inner.0.get() +} + +unsafe impl Send for AllocatedMutex {} +unsafe impl Sync for AllocatedMutex {} + +impl LazyInit for AllocatedMutex { + fn init() -> Box { + let mutex = Box::new(AllocatedMutex(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))); + + // 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. + unsafe { + let mut attr = MaybeUninit::::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(mutex.0.get(), attr.0.as_ptr())).unwrap(); + } + + mutex + } + + fn destroy(mutex: Box) { + // We're not allowed to pthread_mutex_destroy a locked mutex, + // so check first if it's unlocked. + if unsafe { libc::pthread_mutex_trylock(mutex.0.get()) == 0 } { + unsafe { libc::pthread_mutex_unlock(mutex.0.get()) }; + 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) { + // In this case, we can just drop it without any checks, + // since it cannot have been locked yet. + } +} + +impl Drop for AllocatedMutex { + #[inline] + fn drop(&mut self) { + let r = unsafe { libc::pthread_mutex_destroy(self.0.get()) }; + if cfg!(target_os = "dragonfly") { + // 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); + } else { + debug_assert_eq!(r, 0); + } + } +} + +impl Mutex { + #[inline] + pub const fn new() -> Mutex { + Mutex { inner: LazyBox::new() } + } + + #[inline] + pub unsafe fn lock(&self) { + let r = libc::pthread_mutex_lock(raw(self)); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn unlock(&self) { + let r = libc::pthread_mutex_unlock(raw(self)); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + libc::pthread_mutex_trylock(raw(self)) == 0 + } +} + +pub(super) struct PthreadMutexAttr<'a>(pub &'a mut MaybeUninit); + +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/pal/unix/locks/pthread_rwlock.rs b/library/std/src/sys/pal/unix/locks/pthread_rwlock.rs new file mode 100644 index 00000000000..04662be9d82 --- /dev/null +++ b/library/std/src/sys/pal/unix/locks/pthread_rwlock.rs @@ -0,0 +1,195 @@ +use crate::cell::UnsafeCell; +use crate::mem::forget; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sys_common::lazy_box::{LazyBox, LazyInit}; + +struct AllocatedRwLock { + inner: UnsafeCell, + write_locked: UnsafeCell, // guarded by the `inner` RwLock + num_readers: AtomicUsize, +} + +unsafe impl Send for AllocatedRwLock {} +unsafe impl Sync for AllocatedRwLock {} + +pub struct RwLock { + inner: LazyBox, +} + +impl LazyInit for AllocatedRwLock { + fn init() -> Box { + Box::new(AllocatedRwLock { + inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER), + write_locked: UnsafeCell::new(false), + num_readers: AtomicUsize::new(0), + }) + } + + fn destroy(mut rwlock: Box) { + // 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) { + // In this case, we can just drop it without any checks, + // since it cannot have been locked yet. + } +} + +impl AllocatedRwLock { + #[inline] + unsafe fn raw_unlock(&self) { + let r = libc::pthread_rwlock_unlock(self.inner.get()); + debug_assert_eq!(r, 0); + } +} + +impl Drop for AllocatedRwLock { + fn drop(&mut self) { + let r = unsafe { 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 RwLock { + #[inline] + pub const fn new() -> RwLock { + RwLock { inner: LazyBox::new() } + } + + #[inline] + pub fn read(&self) { + let lock = &*self.inner; + let r = unsafe { libc::pthread_rwlock_rdlock(lock.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 && unsafe { *lock.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. + unsafe { + lock.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); + lock.num_readers.fetch_add(1, Ordering::Relaxed); + } + } + + #[inline] + pub fn try_read(&self) -> bool { + let lock = &*self.inner; + let r = unsafe { libc::pthread_rwlock_tryrdlock(lock.inner.get()) }; + if r == 0 { + if unsafe { *lock.write_locked.get() } { + // `pthread_rwlock_tryrdlock` succeeded when it should not have. + unsafe { + lock.raw_unlock(); + } + false + } else { + lock.num_readers.fetch_add(1, Ordering::Relaxed); + true + } + } else { + false + } + } + + #[inline] + pub fn write(&self) { + let lock = &*self.inner; + let r = unsafe { libc::pthread_rwlock_wrlock(lock.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 && unsafe { *lock.write_locked.get() }) + || lock.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. + unsafe { + lock.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); + } + + unsafe { + *lock.write_locked.get() = true; + } + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + let lock = &*self.inner; + let r = libc::pthread_rwlock_trywrlock(lock.inner.get()); + if r == 0 { + if *lock.write_locked.get() || lock.num_readers.load(Ordering::Relaxed) != 0 { + // `pthread_rwlock_trywrlock` succeeded when it should not have. + lock.raw_unlock(); + false + } else { + *lock.write_locked.get() = true; + true + } + } else { + false + } + } + + #[inline] + pub unsafe fn read_unlock(&self) { + let lock = &*self.inner; + debug_assert!(!*lock.write_locked.get()); + lock.num_readers.fetch_sub(1, Ordering::Relaxed); + lock.raw_unlock(); + } + + #[inline] + pub unsafe fn write_unlock(&self) { + let lock = &*self.inner; + debug_assert_eq!(lock.num_readers.load(Ordering::Relaxed), 0); + debug_assert!(*lock.write_locked.get()); + *lock.write_locked.get() = false; + lock.raw_unlock(); + } +} diff --git a/library/std/src/sys/pal/unix/memchr.rs b/library/std/src/sys/pal/unix/memchr.rs new file mode 100644 index 00000000000..73ba604eccb --- /dev/null +++ b/library/std/src/sys/pal/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 { + 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 { + #[cfg(target_os = "linux")] + fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option { + // 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 { + core::slice::memchr::memrchr(needle, haystack) + } + + memrchr_specific(needle, haystack) +} diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs new file mode 100644 index 00000000000..b5da5f870ec --- /dev/null +++ b/library/std/src/sys/pal/unix/mod.rs @@ -0,0 +1,443 @@ +#![allow(missing_docs, nonstandard_style)] + +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_parking; +pub mod time; + +#[cfg(target_os = "espidf")] +pub fn init(argc: isize, argv: *const *const u8, _sigpipe: 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. +// See `fn init()` in `library/std/src/rt.rs` for docs on `sigpipe`. +pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: 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. Add `#[unix_sigpipe = "..."]` above `fn main()` to + // alter this behavior. + reset_sigpipe(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(&c"main"); + } + + 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 = "tvos", + target_os = "watchos", + target_os = "redox", + target_os = "l4re", + target_os = "horizon", + target_os = "vita", + )))] + 'poll: { + use crate::sys::os::errno; + #[cfg(not(all(target_os = "linux", target_env = "gnu")))] + use libc::open as open64; + #[cfg(all(target_os = "linux", target_env = "gnu"))] + use libc::open64; + 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, + #[cfg(target_vendor = "unikraft")] + libc::ENOSYS => { + // Not all configurations of Unikraft enable `LIBPOSIX_EVENT`. + break 'poll; + } + 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 open64(c"/dev/null".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", + target_os = "vita", + )))] + { + use crate::sys::os::errno; + #[cfg(not(all(target_os = "linux", target_env = "gnu")))] + use libc::open as open64; + #[cfg(all(target_os = "linux", target_env = "gnu"))] + use libc::open64; + for fd in 0..3 { + if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF { + if open64(c"/dev/null".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(#[allow(unused_variables)] sigpipe: u8) { + #[cfg(not(any( + target_os = "emscripten", + target_os = "fuchsia", + target_os = "horizon", + // Unikraft's `signal` implementation is currently broken: + // https://github.com/unikraft/lib-musl/issues/57 + target_vendor = "unikraft", + )))] + { + // We don't want to add this as a public type to std, nor do we + // want to `include!` a file from the compiler (which would break + // Miri and xargo for example), so we choose to duplicate these + // constants from `compiler/rustc_session/src/config/sigpipe.rs`. + // See the other file for docs. NOTE: Make sure to keep them in + // sync! + mod sigpipe { + pub const DEFAULT: u8 = 0; + pub const INHERIT: u8 = 1; + pub const SIG_IGN: u8 = 2; + pub const SIG_DFL: u8 = 3; + } + + let (sigpipe_attr_specified, handler) = match sigpipe { + sigpipe::DEFAULT => (false, Some(libc::SIG_IGN)), + sigpipe::INHERIT => (true, None), + sigpipe::SIG_IGN => (true, Some(libc::SIG_IGN)), + sigpipe::SIG_DFL => (true, Some(libc::SIG_DFL)), + _ => unreachable!(), + }; + if sigpipe_attr_specified { + UNIX_SIGPIPE_ATTR_SPECIFIED.store(true, crate::sync::atomic::Ordering::Relaxed); + } + if let Some(handler) = handler { + rtassert!(signal(libc::SIGPIPE, handler) != libc::SIG_ERR); + #[cfg(target_os = "hurd")] + { + rtassert!(signal(libc::SIGLOST, handler) != libc::SIG_ERR); + } + } + } + } +} + +// This is set (up to once) in reset_sigpipe. +#[cfg(not(any( + target_os = "espidf", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "horizon", +)))] +static UNIX_SIGPIPE_ATTR_SPECIFIED: crate::sync::atomic::AtomicBool = + crate::sync::atomic::AtomicBool::new(false); + +#[cfg(not(any( + target_os = "espidf", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "horizon", +)))] +pub(crate) fn unix_sigpipe_attr_specified() -> bool { + UNIX_SIGPIPE_ATTR_SPECIFIED.load(crate::sync::atomic::Ordering::Relaxed) +} + +// 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; +#[allow(unused_imports)] +#[cfg(not(target_os = "android"))] +pub use libc::signal; + +#[inline] +pub(crate) fn is_interrupted(errno: i32) -> bool { + errno == libc::EINTR +} + +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, + #[cfg(not(target_os = "aix"))] + 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: T) -> crate::io::Result { + if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } +} + +pub fn cvt_r(mut f: F) -> crate::io::Result +where + T: IsMinusOne, + F: FnMut() -> T, +{ + loop { + match cvt(f()) { + Err(ref e) if e.is_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", kind = "static", modifiers = "-bundle", + cfg(target_feature = "crt-static"))] + #[link(name = "dl", cfg(not(target_feature = "crt-static")))] + #[link(name = "log", cfg(not(target_feature = "crt-static")))] + 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")] + extern "C" {} + } else if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] { + #[link(name = "System")] + #[link(name = "objc")] + #[link(name = "Foundation", kind = "framework")] + 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" {} + } else if #[cfg(target_os = "vita")] { + #[link(name = "pthread", kind = "static", modifiers = "-bundle")] + extern "C" {} + } +} + +#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] +mod unsupported { + use crate::io; + + pub fn unsupported() -> io::Result { + 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/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs new file mode 100644 index 00000000000..ec861f9cb86 --- /dev/null +++ b/library/std/src/sys/pal/unix/net.rs @@ -0,0 +1,591 @@ +use crate::cmp; +use crate::ffi::CStr; +use crate::io::{self, BorrowedBuf, BorrowedCursor, 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::unix::IsMinusOne; +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 { + 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 { + unsafe { + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "nto", + ))] { + // 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 = "hurd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "nto", + ))] { + // 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(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + loop { + let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) }; + if result.is_minus_one() { + let err = crate::sys::os::errno(); + match err { + libc::EINTR => continue, + libc::EISCONN => return Ok(()), + _ => return Err(io::Error::from_raw_os_error(err)), + } + } + return Ok(()); + } + } + + 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.is_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 { + // 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 = "hurd", + 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 { + self.0.duplicate().map(Socket) + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { + let ret = cvt(unsafe { + libc::recv( + self.as_raw_fd(), + buf.as_mut().as_mut_ptr() as *mut c_void, + buf.capacity(), + flags, + ) + })?; + unsafe { + buf.advance(ret as usize); + } + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + 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 { + 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 { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + 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 { + let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?; + Ok(n as usize) + } + + pub fn set_timeout(&self, dur: Option, 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> { + 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) -> 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> { + 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 { + 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_quickack(&self, quickack: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) + } + + #[cfg(any(target_os = "android", target_os = "linux",))] + pub fn quickack(&self) -> io::Result { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)?; + 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 { + 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 { + let passcred: libc::c_int = getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)?; + Ok(passcred != 0) + } + + #[cfg(target_os = "freebsd")] + pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { + setsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT, passcred as libc::c_int) + } + + #[cfg(target_os = "freebsd")] + pub fn passcred(&self) -> io::Result { + let passcred: libc::c_int = getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?; + Ok(passcred != 0) + } + + #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "vita")))] + 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(target_os = "vita")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let option = nonblocking as libc::c_int; + setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) + } + + #[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) + } + + #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] + pub fn set_mark(&self, mark: u32) -> io::Result<()> { + #[cfg(target_os = "linux")] + let option = libc::SO_MARK; + #[cfg(target_os = "freebsd")] + let option = libc::SO_USER_COOKIE; + #[cfg(target_os = "openbsd")] + let option = libc::SO_RTABLE; + setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) + } + + pub fn take_error(&self) -> io::Result> { + 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 for Socket { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner 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 { + #[inline] + 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 std 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/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs new file mode 100644 index 00000000000..881b3a25c51 --- /dev/null +++ b/library/std/src/sys/pal/unix/os.rs @@ -0,0 +1,783 @@ +//! 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::sync::{PoisonError, RwLock}; +use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; +use crate::sys::cvt; +use crate::sys::fd; +use crate::sys::memchr; +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", + target_os = "hurd", + ), + 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(target_os = "nto", link_name = "__get_errno_ptr")] + #[cfg_attr( + any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "freebsd", + target_os = "watchos" + ), + link_name = "__error" + )] + #[cfg_attr(target_os = "haiku", link_name = "_errnop")] + #[cfg_attr(target_os = "aix", link_name = "_Errno")] + 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( + all( + any(target_os = "linux", target_os = "hurd", target_env = "newlib"), + not(target_env = "ohos") + ), + 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 _; + // We can't always expect a UTF-8 environment. When we don't get that luxury, + // it's better to give a low-quality error message than none at all. + String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into() + } +} + +#[cfg(target_os = "espidf")] +pub fn getcwd() -> io::Result { + Ok(PathBuf::from("/")) +} + +#[cfg(not(target_os = "espidf"))] +pub fn getcwd() -> io::Result { + 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 result = run_path_with_cstr(p, |p| unsafe { Ok(libc::chdir(p.as_ptr())) })?; + if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) } +} + +pub struct SplitPaths<'a> { + iter: iter::Map bool>, fn(&'a [u8]) -> PathBuf>, +} + +pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { + fn bytes_to_path(b: &[u8]) -> PathBuf { + PathBuf::from(::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 { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + 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(target_os = "aix")] +pub fn current_exe() -> io::Result { + 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::NotFound, + "an executable path was not found because no arguments were provided through argv" + ))?; + let path = PathBuf::from(exe_path); + if path.is_absolute() { + return path.canonicalize(); + } + // Search PWD to infer current_exe. + if let Some(pstr) = path.to_str() + && pstr.contains("/") + { + return getcwd().map(|cwd| cwd.join(path))?.canonicalize(); + } + // Search PATH to infer current_exe. + if let Some(p) = getenv(OsStr::from_bytes("PATH".as_bytes())) { + for search_path in split_paths(&p) { + let pb = search_path.join(&path); + if pb.is_file() + && let Ok(metadata) = crate::fs::metadata(&pb) + && metadata.permissions().mode() & 0o111 != 0 + { + return pb.canonicalize(); + } + } + } + Err(io::const_io_error!(ErrorKind::NotFound, "an executable path was not found")) +} + +#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] +pub fn current_exe() -> io::Result { + 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 = 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 { + fn sysctl() -> io::Result { + 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 = 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 { + 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 { + 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 = "hurd", + target_os = "android", + target_os = "emscripten" +))] +pub fn current_exe() -> io::Result { + 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(target_os = "nto")] +pub fn current_exe() -> io::Result { + let mut e = crate::fs::read("/proc/self/exefile")?; + // Current versions of QNX Neutrino provide a null-terminated path. + // Ensure the trailing null byte is not returned here. + if let Some(0) = e.last() { + e.pop(); + } + Ok(PathBuf::from(OsString::from_vec(e))) +} + +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] +pub fn current_exe() -> io::Result { + 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 = 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 { + 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(::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 { + unsafe { + let mut info: mem::MaybeUninit = 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::(), + ); + 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 { + crate::fs::read_to_string("sys:exe").map(PathBuf::from) +} + +#[cfg(target_os = "l4re")] +pub fn current_exe() -> io::Result { + use crate::io::ErrorKind; + Err(io::const_io_error!(ErrorKind::Unsupported, "Not yet implemented!")) +} + +#[cfg(target_os = "vxworks")] +pub fn current_exe() -> io::Result { + #[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", target_os = "vita"))] +pub fn current_exe() -> io::Result { + super::unsupported::unsupported() +} + +#[cfg(target_os = "fuchsia")] +pub fn current_exe() -> io::Result { + 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)>, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { slice } = self; + f.debug_list() + .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { iter } = self; + EnvStrDebug { slice: iter.as_slice() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + f.debug_list().entries(iter.as_slice()).finish() + } +} + +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) { + 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: RwLock<()> = RwLock::new(()); + +pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) +} + +/// 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 { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), |k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() +} + +pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + run_with_cstr(k.as_bytes(), |k| { + run_with_cstr(v.as_bytes(), |v| { + let _guard = ENV_LOCK.write(); + cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) + }) + }) +} + +pub fn unsetenv(n: &OsStr) -> io::Result<()> { + run_with_cstr(n.as_bytes(), |nbuf| { + let _guard = ENV_LOCK.write(); + cvt(unsafe { 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 { + return crate::env::var_os("HOME").or_else(|| unsafe { fallback() }).map(PathBuf::from); + + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "emscripten", + target_os = "redox", + target_os = "vxworks", + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + ))] + unsafe fn fallback() -> Option { + None + } + #[cfg(not(any( + target_os = "android", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "emscripten", + target_os = "redox", + target_os = "vxworks", + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + )))] + unsafe fn fallback() -> Option { + 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::).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/pal/unix/os/tests.rs b/library/std/src/sys/pal/unix/os/tests.rs new file mode 100644 index 00000000000..efc29955b05 --- /dev/null +++ b/library/std/src/sys/pal/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/pal/unix/os_str.rs b/library/std/src/sys/pal/unix/os_str.rs new file mode 100644 index 00000000000..7bd2f656a24 --- /dev/null +++ b/library/std/src/sys/pal/unix/os_str.rs @@ -0,0 +1,288 @@ +//! The underlying OsString/OsStr implementation on Unix and many other +//! systems: just a `Vec`/`[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::Utf8Chunks; + +#[cfg(test)] +#[path = "../unix/os_str/tests.rs"] +mod tests; + +#[derive(Hash)] +#[repr(transparent)] +pub struct Buf { + pub inner: Vec, +} + +#[repr(transparent)] +pub struct Slice { + pub inner: [u8], +} + +impl fmt::Debug for Slice { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&Utf8Chunks::new(&self.inner).debug(), f) + } +} + +impl fmt::Display for Slice { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // If we're the empty string then our iterator won't actually yield + // anything, so perform the formatting manually + if self.inner.is_empty() { + return "".fmt(f); + } + + for chunk in Utf8Chunks::new(&self.inner) { + let valid = chunk.valid(); + // If we successfully decoded the whole chunk as a valid string then + // we can return a direct formatting of the string which will also + // respect various formatting flags if possible. + if chunk.invalid().is_empty() { + return valid.fmt(f); + } + + f.write_str(valid)?; + f.write_char(char::REPLACEMENT_CHARACTER)?; + } + Ok(()) + } +} + +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> for Buf { + fn into_inner(self) -> Vec { + self.inner + } +} + +impl AsInner<[u8]> for Buf { + #[inline] + fn as_inner(&self) -> &[u8] { + &self.inner + } +} + +impl Buf { + #[inline] + pub fn into_encoded_bytes(self) -> Vec { + self.inner + } + + #[inline] + pub unsafe fn from_encoded_bytes_unchecked(s: Vec) -> Self { + Self { inner: s } + } + + 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::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 { + unsafe { mem::transmute(self.inner.into_boxed_slice()) } + } + + #[inline] + pub fn from_box(boxed: Box) -> Buf { + let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; + Buf { inner: inner.into_vec() } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc { + self.as_slice().into_rc() + } +} + +impl Slice { + #[inline] + pub fn as_encoded_bytes(&self) -> &[u8] { + &self.inner + } + + #[inline] + pub unsafe fn from_encoded_bytes_unchecked(s: &[u8]) -> &Slice { + unsafe { mem::transmute(s) } + } + + #[inline] + pub fn from_str(s: &str) -> &Slice { + unsafe { Slice::from_encoded_bytes_unchecked(s.as_bytes()) } + } + + pub fn to_str(&self) -> Result<&str, crate::str::Utf8Error> { + str::from_utf8(&self.inner) + } + + 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 { + let boxed: Box<[u8]> = self.inner.into(); + unsafe { mem::transmute(boxed) } + } + + pub fn empty_box() -> Box { + let boxed: Box<[u8]> = Default::default(); + unsafe { mem::transmute(boxed) } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + 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 { + 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/pal/unix/os_str/tests.rs b/library/std/src/sys/pal/unix/os_str/tests.rs new file mode 100644 index 00000000000..e2a99045e41 --- /dev/null +++ b/library/std/src/sys/pal/unix/os_str/tests.rs @@ -0,0 +1,17 @@ +use super::*; + +#[test] +fn slice_debug_output() { + let input = unsafe { Slice::from_encoded_bytes_unchecked(b"\xF0hello,\tworld") }; + let expected = r#""\xF0hello,\tworld""#; + let output = format!("{input:?}"); + + assert_eq!(output, expected); +} + +#[test] +fn display() { + assert_eq!("Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye", unsafe { + Slice::from_encoded_bytes_unchecked(b"Hello\xC0\x80 There\xE6\x83 Goodbye").to_string() + },); +} diff --git a/library/std/src/sys/pal/unix/path.rs b/library/std/src/sys/pal/unix/path.rs new file mode 100644 index 00000000000..837f68d3eaf --- /dev/null +++ b/library/std/src/sys/pal/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> { + 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 { + // 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().as_encoded_bytes(); + + let mut normalized = if path.is_absolute() { + // "If a pathname begins with two successive characters, the + // first component following the leading characters may be + // interpreted in an implementation-defined manner, although more than + // two leading characters shall be treated as a single + // 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- character and contains one or more trailing + // characters". + // A trailing 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/pal/unix/pipe.rs b/library/std/src/sys/pal/unix/pipe.rs new file mode 100644 index 00000000000..33db24e77e4 --- /dev/null +++ b/library/std/src/sys/pal/unix/pipe.rs @@ -0,0 +1,167 @@ +use crate::io::{self, BorrowedCursor, 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::{FromInner, 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 = "hurd", + 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 { + self.0.read(buf) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +impl IntoInner for AnonPipe { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> 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) -> Result { + 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 { + #[inline] + 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)) + } +} + +impl FromInner for AnonPipe { + fn from_inner(fd: FileDesc) -> Self { + Self(fd) + } +} diff --git a/library/std/src/sys/pal/unix/process/mod.rs b/library/std/src/sys/pal/unix/process/mod.rs new file mode 100644 index 00000000000..074f0a105e3 --- /dev/null +++ b/library/std/src/sys/pal/unix/process/mod.rs @@ -0,0 +1,27 @@ +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; + +#[cfg_attr(any(target_os = "espidf", target_os = "horizon"), allow(unused))] +mod process_common; + +#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] +mod process_unsupported; + +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", target_os = "vita"))] { + mod process_inner { + pub use super::process_unsupported::*; + } + } else { + #[path = "process_unix.rs"] + mod process_inner; + } +} diff --git a/library/std/src/sys/pal/unix/process/process_common.rs b/library/std/src/sys/pal/unix/process/process_common.rs new file mode 100644 index 00000000000..c5f04fb8b3b --- /dev/null +++ b/library/std/src/sys/pal/unix/process/process_common.rs @@ -0,0 +1,676 @@ +#[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::{FromInner, 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: &CStr = c"null:"; + } else if #[cfg(target_os = "vxworks")] { + const DEV_NULL: &CStr = c"/null"; + } else { + const DEV_NULL: &CStr = c"/dev/null"; + } +} + +// 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")] { + #[allow(dead_code)] + 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::{ + mem::{align_of, size_of}, + slice, + }; + use libc::{c_ulong, sigset_t}; + + // The implementations from bionic (android libc) type pun `sigset_t` as an + // array of `c_ulong`. This works, but lets add a smoke check to make sure + // that doesn't change. + const _: () = assert!( + align_of::() == align_of::() + && (size_of::() % size_of::()) == 0 + ); + + let bit = (signum - 1) as usize; + if set.is_null() || bit >= (8 * size_of::()) { + crate::sys::unix::os::set_errno(libc::EINVAL); + return -1; + } + let raw = slice::from_raw_parts_mut( + set as *mut c_ulong, + size_of::() / size_of::(), + ); + const LONG_BIT: usize = size_of::() * 8; + raw[bit / LONG_BIT] |= 1 << (bit % LONG_BIT); + return 0; + } + } else { + #[allow(unused_imports)] + pub use libc::{sigemptyset, sigaddset}; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + program: CString, + args: Vec, + /// 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, + + program_kind: ProgramKind, + cwd: Option, + uid: Option, + gid: Option, + saw_nul: bool, + closures: Vec io::Result<()> + Send + Sync>>, + groups: Option>, + stdin: Option, + stdout: Option, + stderr: Option, + #[cfg(target_os = "linux")] + create_pidfd: bool, + pgroup: Option, +} + +// 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, + pub stdout: Option, + pub stderr: Option, +} + +// 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, +} + +#[derive(Debug)] +pub enum Stdio { + Inherit, + Null, + MakePipe, + Fd(FileDesc), + StaticFd(BorrowedFd<'static>), +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ProgramKind { + /// A program that would be looked up on the PATH (e.g. `ls`) + PathLookup, + /// A relative path (e.g. `my-dir/foo`, `../foo`, `./foo`) + Relative, + /// An absolute path. + Absolute, +} + +impl ProgramKind { + fn new(program: &OsStr) -> Self { + if program.as_encoded_bytes().starts_with(b"/") { + Self::Absolute + } else if program.as_encoded_bytes().contains(&b'/') { + // If the program has more than one component in it, it is a relative path. + Self::Relative + } else { + Self::PathLookup + } + } +} + +impl Command { + #[cfg(not(target_os = "linux"))] + pub fn new(program: &OsStr) -> Command { + let mut saw_nul = false; + let program_kind = ProgramKind::new(program.as_ref()); + let program = os2c(program, &mut saw_nul); + Command { + argv: Argv(vec![program.as_ptr(), ptr::null()]), + args: vec![program.clone()], + program, + program_kind, + 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_kind = ProgramKind::new(program.as_ref()); + let program = os2c(program, &mut saw_nul); + Command { + argv: Argv(vec![program.as_ptr(), ptr::null()]), + args: vec![program.clone()], + program, + program_kind, + 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()) + } + + #[allow(dead_code)] + pub fn get_program_kind(&self) -> ProgramKind { + self.program_kind + } + + 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 { + &self.cwd + } + #[allow(dead_code)] + pub fn get_uid(&self) -> Option { + self.uid + } + #[allow(dead_code)] + pub fn get_gid(&self) -> Option { + 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 { + self.pgroup + } + + pub fn get_closures(&mut self) -> &mut Vec io::Result<()> + Send + Sync>> { + &mut self.closures + } + + pub unsafe fn pre_exec(&mut self, f: Box 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 { + 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("").unwrap() + }) +} + +// Helper type to manage ownership of the strings within a C-style array. +pub struct CStringArray { + items: Vec, + 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, 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)> { + 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::StaticFd(fd) => { + let fd = FileDesc::from_inner(fd.try_clone_to_owned()?); + Ok((ChildStdio::Owned(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 fd = File::open_c(DEV_NULL, &opts)?; + Ok((ChildStdio::Owned(fd.into_inner()), None)) + } + + #[cfg(target_os = "fuchsia")] + Stdio::Null => Ok((ChildStdio::Null, None)), + } + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + Stdio::Fd(pipe.into_inner()) + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + Stdio::Fd(file.into_inner()) + } +} + +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + // This ought really to be is Stdio::StaticFd(input_argument.as_fd()). + // But AsFd::as_fd takes its argument by reference, and yields + // a bounded lifetime, so it's no use here. There is no AsStaticFd. + // + // Additionally AsFd is only implemented for the *locked* versions. + // We don't want to lock them here. (The implications of not locking + // are the same as those for process::Stdio::inherit().) + // + // Arguably the hypothetical AsStaticFd and AsFd<'static> + // should be implemented for io::Stdout, not just for StdoutLocked. + Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) }) + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) }) + } +} + +impl ChildStdio { + pub fn fd(&self) -> Option { + 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 { + // show all attributes but `self.closures` which does not implement `Debug` + // and `self.argv` which is not useful for debugging + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + let mut debug_command = f.debug_struct("Command"); + debug_command.field("program", &self.program).field("args", &self.args); + if !self.env.is_unchanged() { + debug_command.field("env", &self.env); + } + + if self.cwd.is_some() { + debug_command.field("cwd", &self.cwd); + } + if self.uid.is_some() { + debug_command.field("uid", &self.uid); + } + if self.gid.is_some() { + debug_command.field("gid", &self.gid); + } + + if self.groups.is_some() { + debug_command.field("groups", &self.groups); + } + + if self.stdin.is_some() { + debug_command.field("stdin", &self.stdin); + } + if self.stdout.is_some() { + debug_command.field("stdout", &self.stdout); + } + if self.stderr.is_some() { + debug_command.field("stderr", &self.stderr); + } + if self.pgroup.is_some() { + debug_command.field("pgroup", &self.pgroup); + } + + #[cfg(target_os = "linux")] + { + debug_command.field("create_pidfd", &self.create_pidfd); + } + + debug_command.finish() + } else { + if let Some(ref cwd) = self.cwd { + write!(f, "cd {cwd:?} && ")?; + } + if self.env.does_clear() { + write!(f, "env -i ")?; + // Altered env vars will be printed next, that should exactly work as expected. + } else { + // Removed env vars need the command to be wrapped in `env`. + let mut any_removed = false; + for (key, value_opt) in self.get_envs() { + if value_opt.is_none() { + if !any_removed { + write!(f, "env ")?; + any_removed = true; + } + write!(f, "-u {} ", key.to_string_lossy())?; + } + } + } + // Altered env vars can just be added in front of the program. + for (key, value_opt) in self.get_envs() { + if let Some(value) = value_opt { + write!(f, "{}={value:?} ", key.to_string_lossy())?; + } + } + 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 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) { + 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/pal/unix/process/process_common/tests.rs b/library/std/src/sys/pal/unix/process/process_common/tests.rs new file mode 100644 index 00000000000..4e41efc9096 --- /dev/null +++ b/library/std/src/sys/pal/unix/process/process_common/tests.rs @@ -0,0 +1,194 @@ +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() { + // Test to make sure that a signal mask *does* get inherited. + fn test_inner(mut cmd: Command) { + unsafe { + let mut set = mem::MaybeUninit::::uninit(); + let mut old_set = mem::MaybeUninit::::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); + + // Exactly 5 bytes should be read. + let mut buf = [0; 5]; + let ret = t!(stdout_read.read(&mut buf)); + assert_eq!(ret, 5); + assert_eq!(&buf, b"Hello"); + + t!(cat.wait()); + } + } + + // A plain `Command::new` uses the posix_spawn path on many platforms. + let cmd = Command::new(OsStr::new("cat")); + test_inner(cmd); + + // Specifying `pre_exec` forces the fork/exec path. + let mut cmd = Command::new(OsStr::new("cat")); + unsafe { cmd.pre_exec(Box::new(|| Ok(()))) }; + test_inner(cmd); +} + +#[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()); + } +} + +#[test] +fn test_program_kind() { + let vectors = &[ + ("foo", ProgramKind::PathLookup), + ("foo.out", ProgramKind::PathLookup), + ("./foo", ProgramKind::Relative), + ("../foo", ProgramKind::Relative), + ("dir/foo", ProgramKind::Relative), + // Note that paths on Unix can't contain / in them, so this is actually the directory "fo\\" + // followed by the file "o". + ("fo\\/o", ProgramKind::Relative), + ("/foo", ProgramKind::Absolute), + ("/dir/../foo", ProgramKind::Absolute), + ]; + + for (program, expected_kind) in vectors { + assert_eq!( + ProgramKind::new(program.as_ref()), + *expected_kind, + "actual != expected program kind for input {program}", + ); + } +} + +// Test that Rust std handles wait status values (`ExitStatus`) the way that Unix does, +// at least for the values which represent a Unix exit status (`ExitCode`). +// Should work on every #[cfg(unix)] platform. However: +#[cfg(not(any( + // Fuchsia is not Unix and has totally broken std::os::unix. + // https://github.com/rust-lang/rust/issues/58590#issuecomment-836535609 + target_os = "fuchsia", +)))] +#[test] +fn unix_exit_statuses() { + use crate::num::NonZeroI32; + use crate::os::unix::process::ExitStatusExt; + use crate::process::*; + + for exit_code in 0..=0xff { + // FIXME impl From for ExitStatus and then test that here too; + // the two ExitStatus values should be the same + let raw_wait_status = exit_code << 8; + let exit_status = ExitStatus::from_raw(raw_wait_status); + + assert_eq!(exit_status.code(), Some(exit_code)); + + if let Ok(nz) = NonZeroI32::try_from(exit_code) { + assert!(!exit_status.success()); + let es_error = exit_status.exit_ok().unwrap_err(); + assert_eq!(es_error.code().unwrap(), i32::from(nz)); + } else { + assert!(exit_status.success()); + assert_eq!(exit_status.exit_ok(), Ok(())); + } + } +} diff --git a/library/std/src/sys/pal/unix/process/process_fuchsia.rs b/library/std/src/sys/pal/unix/process/process_fuchsia.rs new file mode 100644 index 00000000000..9931c2af2f1 --- /dev/null +++ b/library/std/src/sys/pal/unix/process/process_fuchsia.rs @@ -0,0 +1,330 @@ +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 output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { + let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; + crate::sys_common::process::wait_with_output(proc, pipes) + } + + 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 { + 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 { + 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 { + 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::(), + &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> { + 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::(), + &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, Default)] +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 { + // FIXME: support extracting return code as an i64 + self.0.try_into().ok() + } + + pub fn signal(&self) -> Option { + 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 { + 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 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 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 for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option { + // 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/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs new file mode 100644 index 00000000000..ee86a5f88dd --- /dev/null +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -0,0 +1,1141 @@ +use crate::fmt; +use crate::io::{self, Error, ErrorKind}; +use crate::mem; +use crate::num::NonZeroI32; +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::os::unix::io::AsRawFd; + +#[cfg(any( + target_os = "macos", + target_os = "watchos", + target_os = "tvos", + target_os = "freebsd", + all(target_os = "linux", target_env = "gnu"), + all(target_os = "linux", target_env = "musl"), + target_os = "nto", +))] +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", + target_os = "tvos", + target_os = "watchos", +)))] +use libc::{gid_t, uid_t}; + +cfg_if::cfg_if! { + if #[cfg(all(target_os = "nto", target_env = "nto71"))] { + use crate::thread; + use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; + use crate::time::Duration; + use crate::sync::LazyLock; + // Get smallest amount of time we can sleep. + // Return a common value if it cannot be determined. + fn get_clock_resolution() -> Duration { + static MIN_DELAY: LazyLock Duration> = LazyLock::new(|| { + let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0 + { + Duration::from_nanos(mindelay.tv_nsec as u64) + } else { + Duration::from_millis(1) + } + }); + *MIN_DELAY + } + // Arbitrary minimum sleep duration for retrying fork/spawn + const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1); + // Maximum duration of sleeping before giving up and returning an error + const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// 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)); + } + + #[cfg(target_os = "linux")] + let (input, output) = sys::net::Socket::new_pair(libc::AF_UNIX, libc::SOCK_SEQPACKET)?; + + #[cfg(not(target_os = "linux"))] + 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 immediately. + // The child calls `mem::forget` to leak the lock, which is crucial because + // releasing a lock is not async-signal-safe. + let env_lock = sys::os::env_read_lock(); + let pid = unsafe { self.do_fork()? }; + + if pid == 0 { + crate::panic::always_abort(); + mem::forget(env_lock); // avoid non-async-signal-safe unlocking + drop(input); + #[cfg(target_os = "linux")] + if self.get_create_pidfd() { + self.send_pidfd(&output); + } + 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); + + #[cfg(target_os = "linux")] + let pidfd = if self.get_create_pidfd() { self.recv_pidfd(&input) } else { -1 }; + + #[cfg(not(target_os = "linux"))] + let pidfd = -1; + + // 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.is_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 + // similarly SOCK_SEQPACKET messages should arrive whole + assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); + panic!("short read on the CLOEXEC pipe") + } + } + } + } + + pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { + let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; + crate::sys_common::process::wait_with_output(proc, pipes) + } + + // WatchOS and TVOS headers mark the `fork`/`exec*` functions with + // `__WATCHOS_PROHIBITED __TVOS_PROHIBITED`, and indicate that the + // `posix_spawn*` functions should be used instead. It isn't entirely clear + // what `PROHIBITED` means here (e.g. if calls to these functions are + // allowed to exist in dead code), but it sounds bad, so we go out of our + // way to avoid that all-together. + #[cfg(any(target_os = "tvos", target_os = "watchos"))] + const ERR_APPLE_TV_WATCH_NO_FORK_EXEC: Error = io::const_io_error!( + ErrorKind::Unsupported, + "`fork`+`exec`-based process spawning is not supported on this target", + ); + + #[cfg(any(target_os = "tvos", target_os = "watchos"))] + unsafe fn do_fork(&mut self) -> Result { + return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); + } + + // Attempts to fork the process. If successful, returns Ok((0, -1)) + // in the child, and Ok((child_pid, -1)) in the parent. + #[cfg(not(any( + target_os = "watchos", + target_os = "tvos", + all(target_os = "nto", target_env = "nto71"), + )))] + unsafe fn do_fork(&mut self) -> Result { + cvt(libc::fork()) + } + + // On QNX Neutrino, fork can fail with EBADF in case "another thread might have opened + // or closed a file descriptor while the fork() was occurring". + // Documentation says "... or try calling fork() again". This is what we do here. + // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html + #[cfg(all(target_os = "nto", target_env = "nto71"))] + unsafe fn do_fork(&mut self) -> Result { + use crate::sys::os::errno; + + let mut delay = MIN_FORKSPAWN_SLEEP; + + loop { + let r = libc::fork(); + if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF { + if delay < get_clock_resolution() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else if delay < MAX_FORKSPAWN_SLEEP { + thread::sleep(delay); + } else { + return Err(io::const_io_error!( + ErrorKind::WouldBlock, + "forking returned EBADF too often", + )); + } + delay *= 2; + continue; + } else { + return cvt(r); + } + } + } + + 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) + #[cfg(not(any(target_os = "tvos", target_os = "watchos")))] + unsafe fn do_exec( + &mut self, + stdio: ChildPipes, + maybe_envp: Option<&CStringArray>, + ) -> Result { + 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, crate::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"))] + { + // Inherit the signal mask from the parent rather than resetting it (i.e. do not call + // pthread_sigmask). + + // If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL. + // If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility. + // + // #[unix_sigpipe] is an opportunity to change the default here. + if !crate::sys::unix_sigpipe_attr_specified() { + #[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, crate::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()); + } + } + #[cfg(target_os = "hurd")] + { + let ret = sys::signal(libc::SIGLOST, 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(any(target_os = "tvos", target_os = "watchos"))] + unsafe fn do_exec( + &mut self, + _stdio: ChildPipes, + _maybe_envp: Option<&CStringArray>, + ) -> Result { + return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); + } + + #[cfg(not(any( + target_os = "macos", + target_os = "tvos", + target_os = "watchos", + target_os = "freebsd", + all(target_os = "linux", target_env = "gnu"), + all(target_os = "linux", target_env = "musl"), + target_os = "nto", + )))] + fn posix_spawn( + &mut self, + _: &ChildPipes, + _: Option<&CStringArray>, + ) -> io::Result> { + Ok(None) + } + + // Only support platforms for which posix_spawn() can return ENOENT + // directly. + #[cfg(any( + target_os = "macos", + // FIXME: `target_os = "ios"`? + target_os = "tvos", + target_os = "watchos", + target_os = "freebsd", + all(target_os = "linux", target_env = "gnu"), + all(target_os = "linux", target_env = "musl"), + target_os = "nto", + ))] + fn posix_spawn( + &mut self, + stdio: &ChildPipes, + envp: Option<&CStringArray>, + ) -> io::Result> { + use crate::mem::MaybeUninit; + use crate::sys::{self, cvt_nz, unix_sigpipe_attr_specified}; + + 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); + } + } + + // On QNX Neutrino, posix_spawnp can fail with EBADF in case "another thread might have opened + // or closed a file descriptor while the posix_spawn() was occurring". + // Documentation says "... or try calling posix_spawn() again". This is what we do here. + // See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html + #[cfg(all(target_os = "nto", target_env = "nto71"))] + unsafe fn retrying_libc_posix_spawnp( + pid: *mut pid_t, + file: *const c_char, + file_actions: *const posix_spawn_file_actions_t, + attrp: *const posix_spawnattr_t, + argv: *const *mut c_char, + envp: *const *mut c_char, + ) -> io::Result { + let mut delay = MIN_FORKSPAWN_SLEEP; + loop { + match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) { + libc::EBADF => { + if delay < get_clock_resolution() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else if delay < MAX_FORKSPAWN_SLEEP { + thread::sleep(delay); + } else { + return Err(io::const_io_error!( + ErrorKind::WouldBlock, + "posix_spawnp returned EBADF too often", + )); + } + delay *= 2; + continue; + } + r => { + return Ok(r); + } + } + } + } + + // 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!(any(target_os = "macos", target_os = "tvos", target_os = "watchos")) { + // 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. + if self.get_program_kind() == ProgramKind::Relative { + 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); + + 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); + + 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))?; + } + + // Inherit the signal mask from this process rather than resetting it (i.e. do not call + // posix_spawnattr_setsigmask). + + // If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL. + // If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility. + // + // #[unix_sigpipe] is an opportunity to change the default here. + if !unix_sigpipe_attr_specified() { + let mut default_set = MaybeUninit::::uninit(); + cvt(sigemptyset(default_set.as_mut_ptr()))?; + cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGPIPE))?; + #[cfg(target_os = "hurd")] + { + cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGLOST))?; + } + cvt_nz(libc::posix_spawnattr_setsigdefault( + attrs.0.as_mut_ptr(), + default_set.as_ptr(), + ))?; + flags |= libc::POSIX_SPAWN_SETSIGDEF; + } + + 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 _); + + #[cfg(not(target_os = "nto"))] + let spawn_fn = libc::posix_spawnp; + #[cfg(target_os = "nto")] + let spawn_fn = retrying_libc_posix_spawnp; + + let spawn_res = spawn_fn( + &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 _, + ); + + #[cfg(target_os = "nto")] + let spawn_res = spawn_res?; + + cvt_nz(spawn_res)?; + Ok(Some(p)) + } + } + + #[cfg(target_os = "linux")] + fn send_pidfd(&self, sock: &crate::sys::net::Socket) { + use crate::io::IoSlice; + use crate::os::fd::RawFd; + use crate::sys::cvt_r; + use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; + + unsafe { + let child_pid = libc::getpid(); + // pidfd_open sets CLOEXEC by default + let pidfd = libc::syscall(libc::SYS_pidfd_open, child_pid, 0); + + let fds: [c_int; 1] = [pidfd as RawFd]; + + const SCM_MSG_LEN: usize = mem::size_of::<[c_int; 1]>(); + + #[repr(C)] + union Cmsg { + buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }], + _align: libc::cmsghdr, + } + + let mut cmsg: Cmsg = mem::zeroed(); + + // 0-length message to send through the socket so we can pass along the fd + let mut iov = [IoSlice::new(b"")]; + let mut msg: libc::msghdr = mem::zeroed(); + + msg.msg_iov = &mut iov as *mut _ as *mut _; + msg.msg_iovlen = 1; + + // only attach cmsg if we successfully acquired the pidfd + if pidfd >= 0 { + msg.msg_controllen = mem::size_of_val(&cmsg.buf) as _; + msg.msg_control = &mut cmsg.buf as *mut _ as *mut _; + + let hdr = CMSG_FIRSTHDR(&mut msg as *mut _ as *mut _); + (*hdr).cmsg_level = SOL_SOCKET; + (*hdr).cmsg_type = SCM_RIGHTS; + (*hdr).cmsg_len = CMSG_LEN(SCM_MSG_LEN as _) as _; + let data = CMSG_DATA(hdr); + crate::ptr::copy_nonoverlapping( + fds.as_ptr().cast::(), + data as *mut _, + SCM_MSG_LEN, + ); + } + + // we send the 0-length message even if we failed to acquire the pidfd + // so we get a consistent SEQPACKET order + match cvt_r(|| libc::sendmsg(sock.as_raw(), &msg, 0)) { + Ok(0) => {} + other => rtabort!("failed to communicate with parent process. {:?}", other), + } + } + } + + #[cfg(target_os = "linux")] + fn recv_pidfd(&self, sock: &crate::sys::net::Socket) -> pid_t { + use crate::io::IoSliceMut; + use crate::sys::cvt_r; + + use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; + + unsafe { + const SCM_MSG_LEN: usize = mem::size_of::<[c_int; 1]>(); + + #[repr(C)] + union Cmsg { + _buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }], + _align: libc::cmsghdr, + } + let mut cmsg: Cmsg = mem::zeroed(); + // 0-length read to get the fd + let mut iov = [IoSliceMut::new(&mut [])]; + + let mut msg: libc::msghdr = mem::zeroed(); + + msg.msg_iov = &mut iov as *mut _ as *mut _; + msg.msg_iovlen = 1; + msg.msg_controllen = mem::size_of::() as _; + msg.msg_control = &mut cmsg as *mut _ as *mut _; + + match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, libc::MSG_CMSG_CLOEXEC)) { + Err(_) => return -1, + Ok(_) => {} + } + + let hdr = CMSG_FIRSTHDR(&mut msg as *mut _ as *mut _); + if hdr.is_null() + || (*hdr).cmsg_level != SOL_SOCKET + || (*hdr).cmsg_type != SCM_RIGHTS + || (*hdr).cmsg_len != CMSG_LEN(SCM_MSG_LEN as _) as _ + { + return -1; + } + let data = CMSG_DATA(hdr); + + let mut fds = [-1 as c_int]; + + crate::ptr::copy_nonoverlapping( + data as *const _, + fds.as_mut_ptr().cast::(), + SCM_MSG_LEN, + ); + + fds[0] + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +/// The unique ID of the process (this should never be negative). +pub struct Process { + pid: pid_t, + status: Option, + // 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 `pidfd_open` syscall was not available). + #[cfg(target_os = "linux")] + pidfd: Option, +} + +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 return Ok because the process has exited already. + if self.status.is_some() { + return Ok(()); + } + #[cfg(target_os = "linux")] + if let Some(pid_fd) = self.pidfd.as_ref() { + // pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too + return cvt(unsafe { + libc::syscall( + libc::SYS_pidfd_send_signal, + pid_fd.as_raw_fd(), + libc::SIGKILL, + crate::ptr::null::<()>(), + 0, + ) + }) + .map(drop); + } + cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) + } + + pub fn wait(&mut self) -> io::Result { + use crate::sys::cvt_r; + if let Some(status) = self.status { + return Ok(status); + } + #[cfg(target_os = "linux")] + if let Some(pid_fd) = self.pidfd.as_ref() { + let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; + + cvt_r(|| unsafe { + libc::waitid(libc::P_PIDFD, pid_fd.as_raw_fd() as u32, &mut siginfo, libc::WEXITED) + })?; + let status = ExitStatus::from_waitid_siginfo(siginfo); + self.status = Some(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> { + if let Some(status) = self.status { + return Ok(Some(status)); + } + #[cfg(target_os = "linux")] + if let Some(pid_fd) = self.pidfd.as_ref() { + let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; + + cvt(unsafe { + libc::waitid( + libc::P_PIDFD, + pid_fd.as_raw_fd() as u32, + &mut siginfo, + libc::WEXITED | libc::WNOHANG, + ) + })?; + if unsafe { siginfo.si_pid() } == 0 { + return Ok(None); + } + let status = ExitStatus::from_waitid_siginfo(siginfo); + self.status = Some(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, Default)] +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) + } + + #[cfg(target_os = "linux")] + pub fn from_waitid_siginfo(siginfo: libc::siginfo_t) -> ExitStatus { + let status = unsafe { siginfo.si_status() }; + + match siginfo.si_code { + libc::CLD_EXITED => ExitStatus((status & 0xff) << 8), + libc::CLD_KILLED => ExitStatus(status), + libc::CLD_DUMPED => ExitStatus(status | 0x80), + libc::CLD_CONTINUED => ExitStatus(0xffff), + libc::CLD_STOPPED | libc::CLD_TRAPPED => ExitStatus(((status & 0xff) << 8) | 0x7f), + _ => unreachable!("waitid() should only return the above codes"), + } + } + + 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 + // process_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 { + self.exited().then(|| libc::WEXITSTATUS(self.0)) + } + + pub fn signal(&self) -> Option { + 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 { + 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 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)", + #[cfg(not(target_os = "l4re"))] + libc::SIGBUS => " (SIGBUS)", + libc::SIGFPE => " (SIGFPE)", + libc::SIGKILL => " (SIGKILL)", + #[cfg(not(target_os = "l4re"))] + libc::SIGUSR1 => " (SIGUSR1)", + libc::SIGSEGV => " (SIGSEGV)", + #[cfg(not(target_os = "l4re"))] + libc::SIGUSR2 => " (SIGUSR2)", + libc::SIGPIPE => " (SIGPIPE)", + libc::SIGALRM => " (SIGALRM)", + libc::SIGTERM => " (SIGTERM)", + #[cfg(not(target_os = "l4re"))] + libc::SIGCHLD => " (SIGCHLD)", + #[cfg(not(target_os = "l4re"))] + libc::SIGCONT => " (SIGCONT)", + #[cfg(not(target_os = "l4re"))] + libc::SIGSTOP => " (SIGSTOP)", + #[cfg(not(target_os = "l4re"))] + libc::SIGTSTP => " (SIGTSTP)", + #[cfg(not(target_os = "l4re"))] + libc::SIGTTIN => " (SIGTTIN)", + #[cfg(not(target_os = "l4re"))] + libc::SIGTTOU => " (SIGTTOU)", + #[cfg(not(target_os = "l4re"))] + libc::SIGURG => " (SIGURG)", + #[cfg(not(target_os = "l4re"))] + libc::SIGXCPU => " (SIGXCPU)", + #[cfg(not(target_os = "l4re"))] + libc::SIGXFSZ => " (SIGXFSZ)", + #[cfg(not(target_os = "l4re"))] + libc::SIGVTALRM => " (SIGVTALRM)", + #[cfg(not(target_os = "l4re"))] + libc::SIGPROF => " (SIGPROF)", + #[cfg(not(target_os = "l4re"))] + libc::SIGWINCH => " (SIGWINCH)", + #[cfg(not(any(target_os = "haiku", target_os = "l4re")))] + libc::SIGIO => " (SIGIO)", + #[cfg(target_os = "haiku")] + libc::SIGPOLL => " (SIGPOLL)", + #[cfg(not(target_os = "l4re"))] + 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(any(target_os = "linux", target_os = "nto"))] + 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", + target_os = "nto", + ))] + 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)", + #[cfg(target_os = "hurd")] + libc::SIGLOST => " (SIGLOST)", + _ => "", + } +} + +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 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 { + 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::Uncategorized, "No pidfd was created.")) + } + + fn take_pidfd(&mut self) -> io::Result { + self.handle + .pidfd + .take() + .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created.")) + } +} + +#[cfg(test)] +#[path = "process_unix/tests.rs"] +mod tests; + +// See [`process_unsupported_wait_status::compare_with_linux`]; +#[cfg(all(test, target_os = "linux"))] +#[path = "process_unsupported/wait_status.rs"] +mod process_unsupported_wait_status; diff --git a/library/std/src/sys/pal/unix/process/process_unix/tests.rs b/library/std/src/sys/pal/unix/process/process_unix/tests.rs new file mode 100644 index 00000000000..6e952ed7c42 --- /dev/null +++ b/library/std/src/sys/pal/unix/process/process_unix/tests.rs @@ -0,0 +1,100 @@ +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 tests/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!("{}", ::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 + ); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_command_pidfd() { + use crate::assert_matches::assert_matches; + use crate::os::fd::{AsRawFd, RawFd}; + use crate::os::linux::process::{ChildExt, CommandExt}; + use crate::process::Command; + + let our_pid = crate::process::id(); + let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) }; + let pidfd_open_available = if pidfd >= 0 { + unsafe { libc::close(pidfd as RawFd) }; + true + } else { + false + }; + + // always exercise creation attempts + let mut child = Command::new("false").create_pidfd(true).spawn().unwrap(); + + // but only check if we know that the kernel supports pidfds + if pidfd_open_available { + assert!(child.pidfd().is_ok()); + } + if let Ok(pidfd) = child.pidfd() { + let flags = super::cvt(unsafe { libc::fcntl(pidfd.as_raw_fd(), libc::F_GETFD) }).unwrap(); + assert!(flags & libc::FD_CLOEXEC != 0); + } + let status = child.wait().expect("error waiting on pidfd"); + assert_eq!(status.code(), Some(1)); + + let mut child = Command::new("sleep").arg("1000").create_pidfd(true).spawn().unwrap(); + assert_matches!(child.try_wait(), Ok(None)); + child.kill().expect("failed to kill child"); + let status = child.wait().expect("error waiting on pidfd"); + assert_eq!(status.signal(), Some(libc::SIGKILL)); +} diff --git a/library/std/src/sys/pal/unix/process/process_unsupported.rs b/library/std/src/sys/pal/unix/process/process_unsupported.rs new file mode 100644 index 00000000000..2fbb3192265 --- /dev/null +++ b/library/std/src/sys/pal/unix/process/process_unsupported.rs @@ -0,0 +1,74 @@ +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 output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { + 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 { + unsupported() + } + + pub fn try_wait(&mut self) -> io::Result> { + unsupported() + } +} + +mod wait_status; +pub use wait_status::ExitStatus; + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatusError(NonZero_c_int); + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus::from(c_int::from(self.0)) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option { + ExitStatus::from(c_int::from(self.0)).code().map(|st| st.try_into().unwrap()) + } +} diff --git a/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs b/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs new file mode 100644 index 00000000000..72b7ae18cff --- /dev/null +++ b/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs @@ -0,0 +1,84 @@ +//! Emulated wait status for non-Unix #[cfg(unix) platforms +//! +//! Separate module to facilitate testing against a real Unix implementation. +use core::ffi::NonZero_c_int; + +use crate::ffi::c_int; +use crate::fmt; + +use super::ExitStatusError; + +/// Emulated wait status for use by `process_unsupported.rs` +/// +/// Uses the "traditional unix" encoding. For use on platfors which are `#[cfg(unix)]` +/// but do not actually support subprocesses at all. +/// +/// These platforms aren't Unix, but are simply pretending to be for porting convenience. +/// So, we provide a faithful pretence here. +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] +pub struct ExitStatus { + wait_status: c_int, +} + +/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it +impl From for ExitStatus { + fn from(wait_status: c_int) -> ExitStatus { + ExitStatus { wait_status } + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "emulated wait status: {}", self.wait_status) + } +} + +impl ExitStatus { + pub fn code(&self) -> Option { + // Linux and FreeBSD both agree that values linux 0x80 + // count as "WIFEXITED" even though this is quite mad. + // Likewise the macros disregard all the high bits, so are happy to declare + // out-of-range values to be WIFEXITED, WIFSTOPPED, etc. + let w = self.wait_status; + if (w & 0x7f) == 0 { Some((w & 0xff00) >> 8) } else { None } + } + + #[allow(unused)] + 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 + // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. + match NonZero_c_int::try_from(self.wait_status) { + /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), + /* was zero, couldn't convert */ Err(_) => Ok(()), + } + } + + pub fn signal(&self) -> Option { + let signal = self.wait_status & 0x007f; + if signal > 0 && signal < 0x7f { Some(signal) } else { None } + } + + pub fn core_dumped(&self) -> bool { + self.signal().is_some() && (self.wait_status & 0x80) != 0 + } + + pub fn stopped_signal(&self) -> Option { + let w = self.wait_status; + if (w & 0xff) == 0x7f { Some((w & 0xff00) >> 8) } else { None } + } + + pub fn continued(&self) -> bool { + self.wait_status == 0xffff + } + + pub fn into_raw(&self) -> c_int { + self.wait_status + } +} + +#[cfg(test)] +#[path = "wait_status/tests.rs"] // needed because of strange layout of process_unsupported +mod tests; diff --git a/library/std/src/sys/pal/unix/process/process_unsupported/wait_status/tests.rs b/library/std/src/sys/pal/unix/process/process_unsupported/wait_status/tests.rs new file mode 100644 index 00000000000..5132eab10a1 --- /dev/null +++ b/library/std/src/sys/pal/unix/process/process_unsupported/wait_status/tests.rs @@ -0,0 +1,36 @@ +// Note that tests in this file are run on Linux as well as on platforms using process_unsupported + +// Test that our emulation exactly matches Linux +// +// This test runs *on Linux* but it tests +// the implementation used on non-Unix `#[cfg(unix)]` platforms. +// +// I.e. we're using Linux as a proxy for "trad unix". +#[cfg(target_os = "linux")] +#[test] +fn compare_with_linux() { + use super::ExitStatus as Emulated; + use crate::os::unix::process::ExitStatusExt as _; + use crate::process::ExitStatus as Real; + + // Check that we handle out-of-range values similarly, too. + for wstatus in -0xf_ffff..0xf_ffff { + let emulated = Emulated::from(wstatus); + let real = Real::from_raw(wstatus); + + macro_rules! compare { { $method:ident } => { + assert_eq!( + emulated.$method(), + real.$method(), + "{wstatus:#x}.{}()", + stringify!($method), + ); + } } + compare!(code); + compare!(signal); + compare!(core_dumped); + compare!(stopped_signal); + compare!(continued); + compare!(into_raw); + } +} diff --git a/library/std/src/sys/pal/unix/process/process_vxworks.rs b/library/std/src/sys/pal/unix/process/process_vxworks.rs new file mode 100644 index 00000000000..1ff2b2fb383 --- /dev/null +++ b/library/std/src/sys/pal/unix/process/process_vxworks.rs @@ -0,0 +1,264 @@ +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 output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { + let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; + crate::sys_common::process::wait_with_output(proc, pipes) + } + + 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, +} + +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 return Ok because the process has exited already. + if self.status.is_some() { + Ok(()) + } else { + cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) + } + } + + pub fn wait(&mut self) -> io::Result { + 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> { + 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, Default)] +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 + // process_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 { + if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None } + } + + pub fn signal(&self) -> Option { + 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 { + 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 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 for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option { + ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) + } +} diff --git a/library/std/src/sys/pal/unix/process/zircon.rs b/library/std/src/sys/pal/unix/process/zircon.rs new file mode 100644 index 00000000000..2e596486f9c --- /dev/null +++ b/library/std/src/sys/pal/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) -> io::Result +where + T: TryInto + 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/pal/unix/rand.rs b/library/std/src/sys/pal/unix/rand.rs new file mode 100644 index 00000000000..cf0fe0f47c5 --- /dev/null +++ b/library/std/src/sys/pal/unix/rand.rs @@ -0,0 +1,301 @@ +pub fn hashmap_random_keys() -> (u64, u64) { + const KEY_LEN: usize = core::mem::size_of::(); + + let mut v = [0u8; KEY_LEN * 2]; + imp::fill_bytes(&mut v); + + let key1 = v[0..KEY_LEN].try_into().unwrap(); + let key2 = v[KEY_LEN..].try_into().unwrap(); + + (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) +} + +#[cfg(all( + unix, + not(target_os = "macos"), + not(target_os = "ios"), + not(target_os = "tvos"), + not(target_os = "watchos"), + not(target_os = "openbsd"), + not(target_os = "netbsd"), + not(target_os = "fuchsia"), + not(target_os = "redox"), + not(target_os = "vxworks"), + not(target_os = "emscripten"), + not(target_os = "vita"), +))] +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", target_os = "freebsd"))] + 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", + target_os = "freebsd" + )))] + fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool { + false + } + + #[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "espidf", + target_os = "horizon", + target_os = "freebsd" + ))] + 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_vendor = "apple")] +mod imp { + use crate::io; + use libc::{c_int, c_void, size_t}; + + #[inline(always)] + fn random_failure() -> ! { + panic!("unexpected random generation error: {}", io::Error::last_os_error()); + } + + #[cfg(target_os = "macos")] + fn getentropy_fill_bytes(v: &mut [u8]) { + extern "C" { + fn getentropy(bytes: *mut c_void, count: size_t) -> c_int; + } + + // getentropy(2) permits a maximum buffer size of 256 bytes + for s in v.chunks_mut(256) { + let ret = unsafe { getentropy(s.as_mut_ptr().cast(), s.len()) }; + if ret == -1 { + random_failure() + } + } + } + + #[cfg(not(target_os = "macos"))] + fn ccrandom_fill_bytes(v: &mut [u8]) { + extern "C" { + fn CCRandomGenerateBytes(bytes: *mut c_void, count: size_t) -> c_int; + } + + let ret = unsafe { CCRandomGenerateBytes(v.as_mut_ptr().cast(), v.len()) }; + if ret == -1 { + random_failure() + } + } + + pub fn fill_bytes(v: &mut [u8]) { + // All supported versions of macOS (10.12+) support getentropy. + // + // `getentropy` is measurably faster (via Divan) then the other alternatives so its preferred + // when usable. + #[cfg(target_os = "macos")] + getentropy_fill_bytes(v); + + // On Apple platforms, `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply + // call into `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes` + // manages a CSPRNG which is seeded from the kernel's CSPRNG and which runs on + // its own thread accessed via GCD. This seems needlessly heavyweight for our purposes + // so we only use it on non-Mac OSes where the better entrypoints are blocked. + // + // `CCRandomGenerateBytes` is used instead of `SecRandomCopyBytes` because the former is accessible + // via `libSystem` (libc) while the other needs to link to `Security.framework`. + // + // Note that while `getentropy` has a available attribute in the macOS headers, the lack + // of a header in the iOS (and others) SDK means that its can cause app store rejections. + // Just use `CCRandomGenerateBytes` instead. + #[cfg(not(target_os = "macos"))] + ccrandom_fill_bytes(v); + } +} + +#[cfg(any(target_os = "openbsd", target_os = "emscripten", target_os = "vita"))] +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()); + } + } + } +} + +// FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification. +#[cfg(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/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs new file mode 100644 index 00000000000..3dbab4cc486 --- /dev/null +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -0,0 +1,223 @@ +#![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 = "hurd", + 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; + #[cfg(not(all(target_os = "linux", target_env = "gnu")))] + use libc::{mmap as mmap64, munmap}; + #[cfg(all(target_os = "linux", target_env = "gnu"))] + use libc::{mmap64, 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("") + ); + 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 = 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", + target_os = "dragonfly", + ))] + let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK; + #[cfg(not(any( + target_os = "openbsd", + target_os = "netbsd", + target_os = "linux", + target_os = "dragonfly", + )))] + let flags = MAP_PRIVATE | MAP_ANON; + let stackp = + mmap64(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 = "hurd", + 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/pal/unix/stdio.rs b/library/std/src/sys/pal/unix/stdio.rs new file mode 100644 index 00000000000..97e75f1b5b6 --- /dev/null +++ b/library/std/src/sys/pal/unix/stdio.rs @@ -0,0 +1,99 @@ +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::mem::ManuallyDrop; +use crate::os::unix::io::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 { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read(buf) } + } + + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_buf(buf) } + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + 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 { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write(buf) } + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write_vectored(bufs) + } + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + 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 { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write(buf) } + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write_vectored(bufs) + } + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + 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 { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs new file mode 100644 index 00000000000..7e4a01a5ecd --- /dev/null +++ b/library/std/src/sys/pal/unix/thread.rs @@ -0,0 +1,992 @@ +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", target_os = "nto"))] +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) -> io::Result { + let p = Box::into_raw(Box::new(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)(); + } + ptr::null_mut() + } + } + + pub fn yield_now() { + let ret = unsafe { libc::sched_yield() }; + debug_assert_eq!(ret, 0); + } + + #[cfg(target_os = "android")] + pub fn set_name(name: &CStr) { + const PR_SET_NAME: libc::c_int = 15; + 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(target_os = "linux")] + pub fn set_name(name: &CStr) { + const TASK_COMM_LEN: usize = 16; + + unsafe { + // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20. + let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); + let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, 0); + } + } + + #[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 = "tvos", target_os = "watchos"))] + pub fn set_name(name: &CStr) { + unsafe { + let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name); + let res = libc::pthread_setname_np(name.as_ptr()); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, 0); + } + } + + #[cfg(target_os = "netbsd")] + pub fn set_name(name: &CStr) { + unsafe { + let res = libc::pthread_setname_np( + libc::pthread_self(), + c"%s".as_ptr(), + name.as_ptr() as *mut libc::c_void, + ); + debug_assert_eq!(res, 0); + } + } + + #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] + 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() { + #[cfg(target_os = "nto")] + let name = truncate_cstr::<{ libc::_NTO_THREAD_NAME_MAX as usize }>(name); + + let res = unsafe { f(libc::pthread_self(), name.as_ptr()) }; + debug_assert_eq!(res, 0); + } + } + + #[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()); + let res = libc::rename_thread(thread_self, name.as_ptr()); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, libc::B_OK); + } + } + + #[cfg(any( + target_env = "newlib", + target_os = "l4re", + target_os = "emscripten", + target_os = "redox", + target_os = "vxworks", + target_os = "hurd", + target_os = "aix", + ))] + 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); + } +} + +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "nto", +))] +fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { + let mut result = [0; MAX_WITH_NUL]; + for (src, dst) in cstr.to_bytes().iter().zip(&mut result[..MAX_WITH_NUL - 1]) { + *dst = *src as libc::c_char; + } + result +} + +pub fn available_parallelism() -> io::Result { + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "hurd", + target_os = "ios", + target_os = "tvos", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "illumos", + target_os = "aix", + ))] { + #[allow(unused_assignments)] + #[allow(unused_mut)] + let mut quota = usize::MAX; + + #[cfg(any(target_os = "android", target_os = "linux"))] + { + quota = cgroups::quota().max(1); + let mut set: libc::cpu_set_t = unsafe { mem::zeroed() }; + unsafe { + if libc::sched_getaffinity(0, mem::size_of::(), &mut set) == 0 { + let count = libc::CPU_COUNT(&set) as usize; + let count = count.min(quota); + + // According to sched_getaffinity's API it should always be non-zero, but + // some old MIPS kernels were buggy and zero-initialized the mask if + // none was explicitly set. + // In that case we use the sysconf fallback. + if let Some(count) = NonZeroUsize::new(count) { + return Ok(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 => { + let count = cpus as usize; + // Cover the unusual situation where we were able to get the quota but not the affinity mask + let count = count.min(quota); + Ok(unsafe { NonZeroUsize::new_unchecked(count) }) + } + } + } else if #[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd", + target_os = "netbsd", + ))] { + use crate::ptr; + + #[cfg(target_os = "freebsd")] + { + let mut set: libc::cpuset_t = unsafe { mem::zeroed() }; + unsafe { + if libc::cpuset_getaffinity( + libc::CPU_LEVEL_WHICH, + libc::CPU_WHICH_PID, + -1, + mem::size_of::(), + &mut set, + ) == 0 { + let count = libc::CPU_COUNT(&set) as usize; + if count > 0 { + return Ok(NonZeroUsize::new_unchecked(count)); + } + } + } + } + + #[cfg(target_os = "netbsd")] + { + unsafe { + let set = libc::_cpuset_create(); + if !set.is_null() { + let mut count: usize = 0; + if libc::pthread_getaffinity_np(libc::pthread_self(), libc::_cpuset_size(set), set) == 0 { + for i in 0..u64::MAX { + match libc::_cpuset_isset(i, set) { + -1 => break, + 0 => continue, + _ => count = count + 1, + } + } + } + libc::_cpuset_destroy(set); + if let Some(count) = NonZeroUsize::new(count) { + return Ok(count); + } + } + } + } + + 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 = "nto")] { + unsafe { + use libc::_syspage_ptr; + if _syspage_ptr.is_null() { + Err(io::const_io_error!(io::ErrorKind::NotFound, "No syspage available")) + } else { + let cpus = (*_syspage_ptr).num_cpu; + NonZeroUsize::new(cpus as usize) + .ok_or(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")) + } + } + } 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::(), period.parse::()) { + (Ok(limit), Ok(period)) if period > 0 => { + 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::().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)) if period > 0 => 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 = "hurd"), + 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; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + None + } +} + +#[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "hurd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris" +))] +#[cfg_attr(test, allow(dead_code))] +pub mod guard { + #[cfg(not(all(target_os = "linux", target_env = "gnu")))] + use libc::{mmap as mmap64, mprotect}; + #[cfg(all(target_os = "linux", target_env = "gnu"))] + use libc::{mmap64, 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; + + #[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 = "hurd", + 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 { + 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 if cfg!(target_os = "openbsd") { + // OpenBSD stack already includes a guard page, and stack is + // immutable. + // + // 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 { + // 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 = mmap64( + 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 { + 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 = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "l4re" + ))] + pub unsafe fn current() -> Option { + 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::(); + 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", target_os = "hurd")) { + 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/pal/unix/thread_local_dtor.rs b/library/std/src/sys/pal/unix/thread_local_dtor.rs new file mode 100644 index 00000000000..58f7ab84101 --- /dev/null +++ b/library/std/src/sys/pal/unix/thread_local_dtor.rs @@ -0,0 +1,124 @@ +#![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_attr(bootstrap, allow(unexpected_cfgs))] +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "fuchsia", + target_os = "redox", + target_os = "hurd" +))] +// FIXME: The Rust compiler currently omits weakly function definitions (i.e., +// __cxa_thread_atexit_impl) and its metadata from LLVM IR. +#[no_sanitize(cfi, kcfi)] +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; + + /// This is necessary because the __cxa_thread_atexit_impl implementation + /// std links to by default may be a C or C++ implementation that was not + /// compiled using the Clang integer normalization option. + #[cfg(sanitizer_cfi_normalize_integers)] + use core::ffi::c_int; + #[cfg(not(sanitizer_cfi_normalize_integers))] + #[cfi_encoding = "i"] + #[repr(transparent)] + pub struct c_int(pub libc::c_int); + + extern "C" { + #[linkage = "extern_weak"] + static __dso_handle: *mut u8; + #[linkage = "extern_weak"] + static __cxa_thread_atexit_impl: Option< + extern "C" fn( + unsafe extern "C" fn(*mut libc::c_void), + *mut libc::c_void, + *mut libc::c_void, + ) -> c_int, + >; + } + + if let Some(f) = __cxa_thread_atexit_impl { + unsafe { + f( + mem::transmute::< + unsafe extern "C" fn(*mut u8), + unsafe extern "C" fn(*mut libc::c_void), + >(dtor), + t.cast(), + &__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(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] +pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + use crate::cell::{Cell, RefCell}; + use crate::ptr; + + #[thread_local] + static REGISTERED: Cell = Cell::new(false); + + #[thread_local] + static DTORS: RefCell> = RefCell::new(Vec::new()); + + if !REGISTERED.get() { + _tlv_atexit(run_dtors, ptr::null_mut()); + REGISTERED.set(true); + } + + extern "C" { + fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); + } + + match DTORS.try_borrow_mut() { + Ok(mut dtors) => dtors.push((t, dtor)), + Err(_) => rtabort!("global allocator may not use TLS"), + } + + unsafe extern "C" fn run_dtors(_: *mut u8) { + let mut list = DTORS.take(); + while !list.is_empty() { + for (ptr, dtor) in list { + dtor(ptr); + } + list = DTORS.take(); + } + } +} + +#[cfg(any( + target_os = "vxworks", + target_os = "horizon", + target_os = "emscripten", + target_os = "aix" +))] +#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten) +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/pal/unix/thread_local_key.rs b/library/std/src/sys/pal/unix/thread_local_key.rs new file mode 100644 index 00000000000..2b2d079ee4d --- /dev/null +++ b/library/std/src/sys/pal/unix/thread_local_key.rs @@ -0,0 +1,29 @@ +#![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) -> 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); +} diff --git a/library/std/src/sys/pal/unix/thread_parking/darwin.rs b/library/std/src/sys/pal/unix/thread_parking/darwin.rs new file mode 100644 index 00000000000..8231f3cba2d --- /dev/null +++ b/library/std/src/sys/pal/unix/thread_parking/darwin.rs @@ -0,0 +1,130 @@ +//! Thread parking for Darwin-based systems. +//! +//! Darwin actually has futex syscalls (`__ulock_wait`/`__ulock_wake`), but they +//! cannot be used in `std` because they are non-public (their use will lead to +//! rejection from the App Store). +//! +//! Therefore, we need to look for other synchronization primitives. Luckily, Darwin +//! supports semaphores, which allow us to implement the behaviour we need with +//! only one primitive (as opposed to a mutex-condvar pair). We use the semaphore +//! provided by libdispatch, as the underlying Mach semaphore is only dubiously +//! public. + +use crate::pin::Pin; +use crate::sync::atomic::{ + AtomicI8, + Ordering::{Acquire, Release}, +}; +use crate::time::Duration; + +type dispatch_semaphore_t = *mut crate::ffi::c_void; +type dispatch_time_t = u64; + +const DISPATCH_TIME_NOW: dispatch_time_t = 0; +const DISPATCH_TIME_FOREVER: dispatch_time_t = !0; + +// Contained in libSystem.dylib, which is linked by default. +extern "C" { + fn dispatch_time(when: dispatch_time_t, delta: i64) -> dispatch_time_t; + fn dispatch_semaphore_create(val: isize) -> dispatch_semaphore_t; + fn dispatch_semaphore_wait(dsema: dispatch_semaphore_t, timeout: dispatch_time_t) -> isize; + fn dispatch_semaphore_signal(dsema: dispatch_semaphore_t) -> isize; + fn dispatch_release(object: *mut crate::ffi::c_void); +} + +const EMPTY: i8 = 0; +const NOTIFIED: i8 = 1; +const PARKED: i8 = -1; + +pub struct Parker { + semaphore: dispatch_semaphore_t, + state: AtomicI8, +} + +unsafe impl Sync for Parker {} +unsafe impl Send for Parker {} + +impl Parker { + pub unsafe fn new_in_place(parker: *mut Parker) { + let semaphore = dispatch_semaphore_create(0); + assert!( + !semaphore.is_null(), + "failed to create dispatch semaphore for thread synchronization" + ); + parker.write(Parker { semaphore, state: AtomicI8::new(EMPTY) }) + } + + // Does not need `Pin`, but other implementation do. + pub unsafe fn park(self: Pin<&Self>) { + // The semaphore counter must be zero at this point, because unparking + // threads will not actually increase it until we signalled that we + // are waiting. + + // Change NOTIFIED to EMPTY and EMPTY to PARKED. + if self.state.fetch_sub(1, Acquire) == NOTIFIED { + return; + } + + // Another thread may increase the semaphore counter from this point on. + // If it is faster than us, we will decrement it again immediately below. + // If we are faster, we wait. + + // Ensure that the semaphore counter has actually been decremented, even + // if the call timed out for some reason. + while dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER) != 0 {} + + // At this point, the semaphore counter is zero again. + + // We were definitely woken up, so we don't need to check the state. + // Still, we need to reset the state using a swap to observe the state + // change with acquire ordering. + self.state.swap(EMPTY, Acquire); + } + + // Does not need `Pin`, but other implementation do. + pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) { + if self.state.fetch_sub(1, Acquire) == NOTIFIED { + return; + } + + let nanos = dur.as_nanos().try_into().unwrap_or(i64::MAX); + let timeout = dispatch_time(DISPATCH_TIME_NOW, nanos); + + let timeout = dispatch_semaphore_wait(self.semaphore, timeout) != 0; + + let state = self.state.swap(EMPTY, Acquire); + if state == NOTIFIED && timeout { + // If the state was NOTIFIED but semaphore_wait returned without + // decrementing the count because of a timeout, it means another + // thread is about to call semaphore_signal. We must wait for that + // to happen to ensure the semaphore count is reset. + while dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER) != 0 {} + } else { + // Either a timeout occurred and we reset the state before any thread + // tried to wake us up, or we were woken up and reset the state, + // making sure to observe the state change with acquire ordering. + // Either way, the semaphore counter is now zero again. + } + } + + // Does not need `Pin`, but other implementation do. + pub fn unpark(self: Pin<&Self>) { + let state = self.state.swap(NOTIFIED, Release); + if state == PARKED { + unsafe { + dispatch_semaphore_signal(self.semaphore); + } + } + } +} + +impl Drop for Parker { + fn drop(&mut self) { + // SAFETY: + // We always ensure that the semaphore count is reset, so this will + // never cause an exception. + unsafe { + dispatch_release(self.semaphore); + } + } +} diff --git a/library/std/src/sys/pal/unix/thread_parking/mod.rs b/library/std/src/sys/pal/unix/thread_parking/mod.rs new file mode 100644 index 00000000000..185333c072f --- /dev/null +++ b/library/std/src/sys/pal/unix/thread_parking/mod.rs @@ -0,0 +1,32 @@ +//! Thread parking on systems without futex support. + +#![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", +)))] + +cfg_if::cfg_if! { + if #[cfg(all( + any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos", + ), + not(miri), + ))] { + mod darwin; + pub use darwin::Parker; + } else if #[cfg(target_os = "netbsd")] { + mod netbsd; + pub use netbsd::{current, park, park_timeout, unpark, ThreadId}; + } else { + mod pthread; + pub use pthread::Parker; + } +} diff --git a/library/std/src/sys/pal/unix/thread_parking/netbsd.rs b/library/std/src/sys/pal/unix/thread_parking/netbsd.rs new file mode 100644 index 00000000000..3be08122138 --- /dev/null +++ b/library/std/src/sys/pal/unix/thread_parking/netbsd.rs @@ -0,0 +1,52 @@ +use crate::ffi::{c_int, c_void}; +use crate::ptr; +use crate::time::Duration; +use libc::{_lwp_self, clockid_t, lwpid_t, time_t, timespec, CLOCK_MONOTONIC}; + +extern "C" { + fn ___lwp_park60( + clock_id: clockid_t, + flags: c_int, + ts: *mut timespec, + unpark: lwpid_t, + hint: *const c_void, + unparkhint: *const c_void, + ) -> c_int; + fn _lwp_unpark(lwp: lwpid_t, hint: *const c_void) -> c_int; +} + +pub type ThreadId = lwpid_t; + +#[inline] +pub fn current() -> ThreadId { + unsafe { _lwp_self() } +} + +#[inline] +pub fn park(hint: usize) { + unsafe { + ___lwp_park60(0, 0, ptr::null_mut(), 0, ptr::invalid(hint), ptr::null()); + } +} + +pub fn park_timeout(dur: Duration, hint: usize) { + let mut timeout = timespec { + // Saturate so that the operation will definitely time out + // (even if it is after the heat death of the universe). + tv_sec: dur.as_secs().try_into().ok().unwrap_or(time_t::MAX), + tv_nsec: dur.subsec_nanos().into(), + }; + + // Timeout needs to be mutable since it is modified on NetBSD 9.0 and + // above. + unsafe { + ___lwp_park60(CLOCK_MONOTONIC, 0, &mut timeout, 0, ptr::invalid(hint), ptr::null()); + } +} + +#[inline] +pub fn unpark(tid: ThreadId, hint: usize) { + unsafe { + _lwp_unpark(tid, ptr::invalid(hint)); + } +} diff --git a/library/std/src/sys/pal/unix/thread_parking/pthread.rs b/library/std/src/sys/pal/unix/thread_parking/pthread.rs new file mode 100644 index 00000000000..ae805d84399 --- /dev/null +++ b/library/std/src/sys/pal/unix/thread_parking/pthread.rs @@ -0,0 +1,284 @@ +//! Thread parking without `futex` using the `pthread` synchronization primitives. + +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; +#[cfg(not(target_os = "nto"))] +use crate::sys::time::TIMESPEC_MAX; +#[cfg(target_os = "nto")] +use crate::sys::time::TIMESPEC_MAX_CAPPED; +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); +} + +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 = "tvos", + target_os = "watchos", + target_os = "espidf", + target_os = "horizon", + ))] + let (now, dur) = { + use crate::cmp::min; + use crate::sys::time::SystemTime; + + // 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 = "tvos", + target_os = "watchos", + target_os = "espidf", + target_os = "horizon", + )))] + let (now, dur) = { + use crate::sys::time::Timespec; + + (Timespec::now(libc::CLOCK_MONOTONIC), dur) + }; + + #[cfg(not(target_os = "nto"))] + let timeout = + now.checked_add_duration(&dur).and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX); + #[cfg(target_os = "nto")] + let timeout = now + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec_capped()) + .unwrap_or(TIMESPEC_MAX_CAPPED); + let r = libc::pthread_cond_timedwait(cond, lock, &timeout); + debug_assert!(r == libc::ETIMEDOUT || r == 0); +} + +pub struct Parker { + state: AtomicUsize, + lock: UnsafeCell, + cvar: UnsafeCell, + // 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_in_place(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 = "tvos", + target_os = "watchos", + target_os = "l4re", + target_os = "android", + target_os = "redox", + target_os = "vita", + ))] { + 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::::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/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs new file mode 100644 index 00000000000..f62eb828ee5 --- /dev/null +++ b/library/std/src/sys/pal/unix/time.rs @@ -0,0 +1,330 @@ +use crate::fmt; +use crate::time::Duration; + +const NSEC_PER_SEC: u64 = 1_000_000_000; +pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; +#[allow(dead_code)] // Used for pthread condvar timeouts +pub const TIMESPEC_MAX: libc::timespec = + libc::timespec { tv_sec: ::MAX, tv_nsec: 1_000_000_000 - 1 }; + +// This additional constant is only used when calling +// `libc::pthread_cond_timedwait`. +#[cfg(target_os = "nto")] +pub(super) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec { + tv_sec: (u64::MAX / NSEC_PER_SEC) as i64, + tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64, +}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +#[rustc_layout_scalar_valid_range_end(999_999_999)] +struct Nanoseconds(u32); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SystemTime { + pub(crate) t: Timespec, +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct Timespec { + tv_sec: i64, + tv_nsec: Nanoseconds, +} + +impl SystemTime { + #[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))] + pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + SystemTime { t: Timespec::new(tv_sec, tv_nsec) } + } + + pub fn now() -> SystemTime { + SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) } + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.t.sub_timespec(&other.t) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) + } +} + +impl From 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.0) + .finish() + } +} + +impl Timespec { + pub const fn zero() -> Timespec { + Timespec::new(0, 0) + } + + const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + // On Apple OS, dates before epoch are represented differently than on other + // Unix platforms: e.g. 1/10th of a second before epoch is represented as `seconds=-1` + // and `nanoseconds=100_000_000` on other platforms, but is `seconds=0` and + // `nanoseconds=-900_000_000` on Apple OS. + // + // To compensate, we first detect this special case by checking if both + // seconds and nanoseconds are in range, and then correct the value for seconds + // and nanoseconds to match the common unix representation. + // + // Please note that Apple OS nonetheless accepts the standard unix format when + // setting file times, which makes this compensation round-trippable and generally + // transparent. + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos" + ))] + let (tv_sec, tv_nsec) = + if (tv_sec <= 0 && tv_sec > i64::MIN) && (tv_nsec < 0 && tv_nsec > -1_000_000_000) { + (tv_sec - 1, tv_nsec + 1_000_000_000) + } else { + (tv_sec, tv_nsec) + }; + assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); + // SAFETY: The assert above checks tv_nsec is within the valid range + Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } + } + + pub fn now(clock: libc::clockid_t) -> Timespec { + use crate::mem::MaybeUninit; + use crate::sys::cvt; + + // Try to use 64-bit time in preparation for Y2038. + #[cfg(all( + target_os = "linux", + target_env = "gnu", + target_pointer_width = "32", + not(target_arch = "riscv32") + ))] + { + 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); + + if let Some(clock_gettime64) = __clock_gettime64.get() { + let mut t = MaybeUninit::uninit(); + cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); + return Timespec::from(unsafe { t.assume_init() }); + } + } + + let mut t = MaybeUninit::uninit(); + cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap(); + Timespec::from(unsafe { t.assume_init() }) + } + + pub fn sub_timespec(&self, other: &Timespec) -> Result { + 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.0 >= other.tv_nsec.0 { + ((self.tv_sec - other.tv_sec) as u64, self.tv_nsec.0 - other.tv_nsec.0) + } else { + ( + (self.tv_sec - other.tv_sec - 1) as u64, + self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0, + ) + }; + + 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 { + let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?; + + // Nano calculations can't overflow because nanos are <1B which fit + // in a u32. + let mut nsec = other.subsec_nanos() + self.tv_nsec.0; + if nsec >= NSEC_PER_SEC as u32 { + nsec -= NSEC_PER_SEC as u32; + secs = secs.checked_add(1)?; + } + Some(Timespec::new(secs, nsec.into())) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?; + + // Similar to above, nanos can't overflow. + let mut nsec = self.tv_nsec.0 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.into())) + } + + #[allow(dead_code)] + pub fn to_timespec(&self) -> Option { + Some(libc::timespec { + tv_sec: self.tv_sec.try_into().ok()?, + tv_nsec: self.tv_nsec.0.try_into().ok()?, + }) + } + + // On QNX Neutrino, the maximum timespec for e.g. pthread_cond_timedwait + // is 2^64 nanoseconds + #[cfg(target_os = "nto")] + pub(super) fn to_timespec_capped(&self) -> Option { + // Check if timeout in nanoseconds would fit into an u64 + if (self.tv_nsec.0 as u64) + .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?) + .is_none() + { + return None; + } + self.to_timespec() + } + + #[cfg(all( + target_os = "linux", + target_env = "gnu", + target_pointer_width = "32", + not(target_arch = "riscv32") + ))] + pub fn to_timespec64(&self) -> __timespec64 { + __timespec64::new(self.tv_sec, self.tv_nsec.0 as _) + } +} + +impl From for Timespec { + fn from(t: libc::timespec) -> Timespec { + Timespec::new(t.tv_sec as i64, t.tv_nsec as i64) + } +} + +#[cfg(all( + target_os = "linux", + target_env = "gnu", + target_pointer_width = "32", + not(target_arch = "riscv32") +))] +#[repr(C)] +pub(crate) struct __timespec64 { + pub(crate) tv_sec: i64, + #[cfg(target_endian = "big")] + _padding: i32, + pub(crate) tv_nsec: i32, + #[cfg(target_endian = "little")] + _padding: i32, +} + +#[cfg(all( + target_os = "linux", + target_env = "gnu", + target_pointer_width = "32", + not(target_arch = "riscv32") +))] +impl __timespec64 { + pub(crate) fn new(tv_sec: i64, tv_nsec: i32) -> Self { + Self { tv_sec, tv_nsec, _padding: 0 } + } +} + +#[cfg(all( + target_os = "linux", + target_env = "gnu", + target_pointer_width = "32", + not(target_arch = "riscv32") +))] +impl From<__timespec64> for Timespec { + fn from(t: __timespec64) -> Timespec { + Timespec::new(t.tv_sec, t.tv_nsec.into()) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Instant { + t: Timespec, +} + +impl Instant { + pub fn now() -> Instant { + // https://www.manpagez.com/man/3/clock_gettime/ + // + // CLOCK_UPTIME_RAW clock that increments monotonically, in the same man- + // ner as CLOCK_MONOTONIC_RAW, but that does not incre- + // ment while the system is asleep. The returned value + // is identical to the result of mach_absolute_time() + // after the appropriate mach_timebase conversion is + // applied. + // + // Instant on macos was historically implemented using mach_absolute_time; + // we preserve this value domain out of an abundance of caution. + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos" + ))] + const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW; + #[cfg(not(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos" + )))] + const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC; + Instant { t: Timespec::now(clock_id) } + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option { + self.t.sub_timespec(&other.t).ok() + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(Instant { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + 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.0) + .finish() + } +} diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs new file mode 100644 index 00000000000..61088ff16ed --- /dev/null +++ b/library/std/src/sys/pal/unix/weak.rs @@ -0,0 +1,195 @@ +//! Support for "weak linkage" to symbols on Unix +//! +//! Some I/O operations we do in std 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", target_os = "tvos")))] +pub(crate) macro weak { + (fn $name:ident($($t:ty),*) -> $ret:ty) => ( + let ref $name: ExternWeak $ret> = { + extern "C" { + #[linkage = "extern_weak"] + static $name: Option $ret>; + } + #[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", target_os = "tvos"))] +pub(crate) use self::dlsym as weak; + +pub(crate) struct ExternWeak { + weak_ptr: Option, +} + +impl ExternWeak { + #[inline] + pub(crate) fn new(weak_ptr: Option) -> Self { + ExternWeak { weak_ptr } + } + + #[inline] + pub(crate) fn get(&self) -> Option { + 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 $ret> = + DlsymWeak::new(concat!($sym, '\0')); + let $name = &DLSYM; + ) +} +pub(crate) struct DlsymWeak { + name: &'static str, + func: AtomicPtr, + _marker: PhantomData, +} + +impl DlsymWeak { + 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 { + 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 { + assert_eq!(mem::size_of::(), 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/pal/unsupported/alloc.rs b/library/std/src/sys/pal/unsupported/alloc.rs new file mode 100644 index 00000000000..d715ae45401 --- /dev/null +++ b/library/std/src/sys/pal/unsupported/alloc.rs @@ -0,0 +1,23 @@ +use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::ptr::null_mut; + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { + null_mut() + } + + #[inline] + unsafe fn alloc_zeroed(&self, _layout: Layout) -> *mut u8 { + null_mut() + } + + #[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 { + null_mut() + } +} diff --git a/library/std/src/sys/pal/unsupported/args.rs b/library/std/src/sys/pal/unsupported/args.rs new file mode 100644 index 00000000000..a2d75a61976 --- /dev/null +++ b/library/std/src/sys/pal/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 { + None + } + fn size_hint(&self) -> (usize, Option) { + (0, Some(0)) + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + 0 + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + None + } +} diff --git a/library/std/src/sys/pal/unsupported/common.rs b/library/std/src/sys/pal/unsupported/common.rs new file mode 100644 index 00000000000..5c379992b20 --- /dev/null +++ b/library/std/src/sys/pal/unsupported/common.rs @@ -0,0 +1,40 @@ +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, _sigpipe: 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() -> std_io::Result { + 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 is_interrupted(_code: i32) -> bool { + false +} + +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/pal/unsupported/env.rs b/library/std/src/sys/pal/unsupported/env.rs new file mode 100644 index 00000000000..d2efec506c5 --- /dev/null +++ b/library/std/src/sys/pal/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/pal/unsupported/fs.rs b/library/std/src/sys/pal/unsupported/fs.rs new file mode 100644 index 00000000000..6ac1b5d2bcf --- /dev/null +++ b/library/std/src/sys/pal/unsupported/fs.rs @@ -0,0 +1,324 @@ +use crate::ffi::OsString; +use crate::fmt; +use crate::hash::{Hash, Hasher}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, 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 { + self.0 + } + + pub fn accessed(&self) -> io::Result { + self.0 + } + + pub fn created(&self) -> io::Result { + 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(&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; + + fn next(&mut self) -> Option> { + 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 { + self.0 + } + + pub fn file_type(&self) -> io::Result { + 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 { + unsupported() + } + + pub fn file_attr(&self) -> io::Result { + 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 { + self.0 + } + + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { + 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 { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + 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 { + 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 { + unsupported() +} + +pub fn readlink(_p: &Path) -> io::Result { + 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 { + unsupported() +} + +pub fn lstat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn canonicalize(_p: &Path) -> io::Result { + unsupported() +} + +pub fn copy(_from: &Path, _to: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/pal/unsupported/io.rs b/library/std/src/sys/pal/unsupported/io.rs new file mode 100644 index 00000000000..6372fca74e0 --- /dev/null +++ b/library/std/src/sys/pal/unsupported/io.rs @@ -0,0 +1,51 @@ +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::take(&mut self.0); + 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 + } +} + +pub fn is_terminal(_: &T) -> bool { + false +} diff --git a/library/std/src/sys/pal/unsupported/locks/condvar.rs b/library/std/src/sys/pal/unsupported/locks/condvar.rs new file mode 100644 index 00000000000..3f0943b50ee --- /dev/null +++ b/library/std/src/sys/pal/unsupported/locks/condvar.rs @@ -0,0 +1,26 @@ +use crate::sys::locks::Mutex; +use crate::time::Duration; + +pub struct Condvar {} + +impl Condvar { + #[inline] + #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] + pub const fn new() -> Condvar { + Condvar {} + } + + #[inline] + pub fn notify_one(&self) {} + + #[inline] + pub 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/pal/unsupported/locks/mod.rs b/library/std/src/sys/pal/unsupported/locks/mod.rs new file mode 100644 index 00000000000..0e0f9eccb21 --- /dev/null +++ b/library/std/src/sys/pal/unsupported/locks/mod.rs @@ -0,0 +1,6 @@ +mod condvar; +mod mutex; +mod rwlock; +pub use condvar::Condvar; +pub use mutex::Mutex; +pub use rwlock::RwLock; diff --git a/library/std/src/sys/pal/unsupported/locks/mutex.rs b/library/std/src/sys/pal/unsupported/locks/mutex.rs new file mode 100644 index 00000000000..4a13c55fb8b --- /dev/null +++ b/library/std/src/sys/pal/unsupported/locks/mutex.rs @@ -0,0 +1,32 @@ +use crate::cell::Cell; + +pub struct Mutex { + // This platform has no threads, so we can use a Cell here. + locked: Cell, +} + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} // no threads on this platform + +impl Mutex { + #[inline] + #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] + pub const fn new() -> Mutex { + Mutex { locked: Cell::new(false) } + } + + #[inline] + pub 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 fn try_lock(&self) -> bool { + self.locked.replace(true) == false + } +} diff --git a/library/std/src/sys/pal/unsupported/locks/rwlock.rs b/library/std/src/sys/pal/unsupported/locks/rwlock.rs new file mode 100644 index 00000000000..789ef9b29e5 --- /dev/null +++ b/library/std/src/sys/pal/unsupported/locks/rwlock.rs @@ -0,0 +1,65 @@ +use crate::cell::Cell; + +pub struct RwLock { + // This platform has no threads, so we can use a Cell here. + mode: Cell, +} + +unsafe impl Send for RwLock {} +unsafe impl Sync for RwLock {} // no threads on this platform + +impl RwLock { + #[inline] + #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] + pub const fn new() -> RwLock { + RwLock { mode: Cell::new(0) } + } + + #[inline] + pub fn read(&self) { + let m = self.mode.get(); + if m >= 0 { + self.mode.set(m + 1); + } else { + rtabort!("rwlock locked for writing"); + } + } + + #[inline] + pub fn try_read(&self) -> bool { + let m = self.mode.get(); + if m >= 0 { + self.mode.set(m + 1); + true + } else { + false + } + } + + #[inline] + pub fn write(&self) { + if self.mode.replace(-1) != 0 { + rtabort!("rwlock locked for reading") + } + } + + #[inline] + pub 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/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs new file mode 100644 index 00000000000..e1a38de6471 --- /dev/null +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -0,0 +1,29 @@ +#![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 once; +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 thread_parking; +pub mod time; + +mod common; +pub use common::*; diff --git a/library/std/src/sys/pal/unsupported/net.rs b/library/std/src/sys/pal/unsupported/net.rs new file mode 100644 index 00000000000..bbc52703f96 --- /dev/null +++ b/library/std/src/sys/pal/unsupported/net.rs @@ -0,0 +1,370 @@ +use crate::fmt; +use crate::io::{self, BorrowedCursor, 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 { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn write(&self, _: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn linger(&self) -> io::Result> { + self.0 + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn nodelay(&self) -> io::Result { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + 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 { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn only_v6(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + 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 { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + 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 { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn broadcast(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v6(&self) -> io::Result { + 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 { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn send(&self, _: &[u8]) -> io::Result { + 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 { + self.0 + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &str) -> io::Result { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + 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/pal/unsupported/once.rs b/library/std/src/sys/pal/unsupported/once.rs new file mode 100644 index 00000000000..11fde1888ba --- /dev/null +++ b/library/std/src/sys/pal/unsupported/once.rs @@ -0,0 +1,100 @@ +use crate::cell::Cell; +use crate::sync as public; +use crate::sync::once::ExclusiveState; + +pub struct Once { + state: Cell, +} + +pub struct OnceState { + poisoned: bool, + set_state_to: Cell, +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum State { + Incomplete, + Poisoned, + Running, + Complete, +} + +struct CompletionGuard<'a> { + state: &'a Cell, + set_state_on_drop_to: State, +} + +impl<'a> Drop for CompletionGuard<'a> { + fn drop(&mut self) { + self.state.set(self.set_state_on_drop_to); + } +} + +// Safety: threads are not supported on this platform. +unsafe impl Sync for Once {} + +impl Once { + #[inline] + #[rustc_const_stable(feature = "const_once_new", since = "1.32.0")] + pub const fn new() -> Once { + Once { state: Cell::new(State::Incomplete) } + } + + #[inline] + pub fn is_completed(&self) -> bool { + self.state.get() == State::Complete + } + + #[inline] + pub(crate) fn state(&mut self) -> ExclusiveState { + match self.state.get() { + State::Incomplete => ExclusiveState::Incomplete, + State::Poisoned => ExclusiveState::Poisoned, + State::Complete => ExclusiveState::Complete, + _ => unreachable!("invalid Once state"), + } + } + + #[cold] + #[track_caller] + pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) { + let state = self.state.get(); + match state { + State::Poisoned if !ignore_poisoning => { + // Panic to propagate the poison. + panic!("Once instance has previously been poisoned"); + } + State::Incomplete | State::Poisoned => { + self.state.set(State::Running); + // `guard` will set the new state on drop. + let mut guard = + CompletionGuard { state: &self.state, set_state_on_drop_to: State::Poisoned }; + // Run the function, letting it know if we're poisoned or not. + let f_state = public::OnceState { + inner: OnceState { + poisoned: state == State::Poisoned, + set_state_to: Cell::new(State::Complete), + }, + }; + f(&f_state); + guard.set_state_on_drop_to = f_state.inner.set_state_to.get(); + } + State::Running => { + panic!("one-time initialization may not be performed recursively"); + } + State::Complete => {} + } + } +} + +impl OnceState { + #[inline] + pub fn is_poisoned(&self) -> bool { + self.poisoned + } + + #[inline] + pub fn poison(&self) { + self.set_state_to.set(State::Poisoned) + } +} diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs new file mode 100644 index 00000000000..248b34829f2 --- /dev/null +++ b/library/std/src/sys/pal/unsupported/os.rs @@ -0,0 +1,121 @@ +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 { + 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 { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + 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 { + unsupported() +} + +pub struct Env(!); + +impl Env { + // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self(inner) = self; + match *inner {} + } +} + +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + match *inner {} + } +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + let Self(inner) = self; + match *inner {} + } +} + +pub fn env() -> Env { + panic!("not supported on this platform") +} + +pub fn getenv(_: &OsStr) -> Option { + 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 { + 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/pal/unsupported/pipe.rs b/library/std/src/sys/pal/unsupported/pipe.rs new file mode 100644 index 00000000000..d7d8f297ae5 --- /dev/null +++ b/library/std/src/sys/pal/unsupported/pipe.rs @@ -0,0 +1,45 @@ +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; + +pub struct AnonPipe(!); + +impl AnonPipe { + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn read_to_end(&self, _buf: &mut Vec) -> io::Result { + self.0 + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { + 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, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { + match p1.0 {} +} diff --git a/library/std/src/sys/pal/unsupported/process.rs b/library/std/src/sys/pal/unsupported/process.rs new file mode 100644 index 00000000000..a639afcc674 --- /dev/null +++ b/library/std/src/sys/pal/unsupported/process.rs @@ -0,0 +1,239 @@ +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, + pub stdout: Option, + pub stderr: Option, +} + +// FIXME: This should be a unit struct, so we can always construct it +// The value here should be never used, since we cannot spawn processes. +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() + } + + pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { + unsupported() + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +impl From for Stdio { + fn from(_file: File) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +impl fmt::Debug for Command { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + Ok(()) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] +#[non_exhaustive] +pub struct ExitStatus(); + +impl ExitStatus { + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + Ok(()) + } + + pub fn code(&self) -> Option { + Some(0) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "") + } +} + +pub struct ExitStatusError(!); + +impl Clone for ExitStatusError { + fn clone(&self) -> ExitStatusError { + self.0 + } +} + +impl Copy for ExitStatusError {} + +impl PartialEq for ExitStatusError { + fn eq(&self, _other: &ExitStatusError) -> bool { + self.0 + } +} + +impl Eq for ExitStatusError {} + +impl fmt::Debug for ExitStatusError { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + self.0 + } +} + +impl ExitStatusError { + pub fn code(self) -> Option { + self.0 + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(bool); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(false); + pub const FAILURE: ExitCode = ExitCode(true); + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + +impl From 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 { + self.0 + } + + pub fn try_wait(&mut self) -> io::Result> { + 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 + } + fn size_hint(&self) -> (usize, Option) { + (0, Some(0)) + } +} + +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/pal/unsupported/stdio.rs b/library/std/src/sys/pal/unsupported/stdio.rs new file mode 100644 index 00000000000..b5e3f5be988 --- /dev/null +++ b/library/std/src/sys/pal/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 { + Ok(0) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + 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 { + 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> { + None +} diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs new file mode 100644 index 00000000000..a8db251de20 --- /dev/null +++ b/library/std/src/sys/pal/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) -> io::Result { + 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 { + unsupported() +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + None + } +} diff --git a/library/std/src/sys/pal/unsupported/thread_local_dtor.rs b/library/std/src/sys/pal/unsupported/thread_local_dtor.rs new file mode 100644 index 00000000000..84660ea5881 --- /dev/null +++ b/library/std/src/sys/pal/unsupported/thread_local_dtor.rs @@ -0,0 +1,10 @@ +#![unstable(feature = "thread_local_internals", issue = "none")] + +#[cfg_attr(target_family = "wasm", allow(unused))] // unused on wasm32-unknown-unknown +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/pal/unsupported/thread_local_key.rs b/library/std/src/sys/pal/unsupported/thread_local_key.rs new file mode 100644 index 00000000000..b6e5e4cd2e1 --- /dev/null +++ b/library/std/src/sys/pal/unsupported/thread_local_key.rs @@ -0,0 +1,21 @@ +pub type Key = usize; + +#[inline] +pub unsafe fn create(_dtor: Option) -> 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"); +} diff --git a/library/std/src/sys/pal/unsupported/thread_parking.rs b/library/std/src/sys/pal/unsupported/thread_parking.rs new file mode 100644 index 00000000000..197078bb186 --- /dev/null +++ b/library/std/src/sys/pal/unsupported/thread_parking.rs @@ -0,0 +1,11 @@ +use crate::pin::Pin; +use crate::time::Duration; + +pub struct Parker {} + +impl Parker { + pub unsafe fn new_in_place(_parker: *mut Parker) {} + pub unsafe fn park(self: Pin<&Self>) {} + pub unsafe fn park_timeout(self: Pin<&Self>, _dur: Duration) {} + pub fn unpark(self: Pin<&Self>) {} +} diff --git a/library/std/src/sys/pal/unsupported/time.rs b/library/std/src/sys/pal/unsupported/time.rs new file mode 100644 index 00000000000..6d67b538a96 --- /dev/null +++ b/library/std/src/sys/pal/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 { + self.0.checked_sub(other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + 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 { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} diff --git a/library/std/src/sys/pal/wasi/args.rs b/library/std/src/sys/pal/wasi/args.rs new file mode 100644 index 00000000000..c42c310e3a2 --- /dev/null +++ b/library/std/src/sys/pal/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, +} + +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> { + 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 { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + 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 { + self.iter.next_back() + } +} diff --git a/library/std/src/sys/pal/wasi/env.rs b/library/std/src/sys/pal/wasi/env.rs new file mode 100644 index 00000000000..730e356d7fe --- /dev/null +++ b/library/std/src/sys/pal/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/pal/wasi/fd.rs b/library/std/src/sys/pal/wasi/fd.rs new file mode 100644 index 00000000000..d7295a799da --- /dev/null +++ b/library/std/src/sys/pal/wasi/fd.rs @@ -0,0 +1,332 @@ +#![deny(unsafe_op_in_unsafe_fn)] +#![allow(dead_code)] + +use super::err2io; +use crate::io::{self, BorrowedCursor, 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::>(), mem::size_of::()); + assert_eq!(mem::align_of::>(), mem::align_of::()); + // SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout. + // We decorate our `IoSliceMut` with `repr(transparent)` (see `io.rs`), and + // `crate::io::IoSliceMut` is a `repr(transparent)` wrapper around our type, so this is + // guaranteed. + unsafe { mem::transmute(a) } +} + +fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] { + assert_eq!(mem::size_of::>(), mem::size_of::()); + assert_eq!(mem::align_of::>(), mem::align_of::()); + // SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout. + // We decorate our `IoSlice` with `repr(transparent)` (see `io.rs`), and + // `crate::io::IoSlice` is a `repr(transparent)` wrapper around our type, so this is + // guaranteed. + 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 { + 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 { + 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 { + unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) } + } + + pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { + unsafe { + let bufs = [wasi::Iovec { + buf: buf.as_mut().as_mut_ptr() as *mut u8, + buf_len: buf.capacity(), + }]; + match wasi::fd_read(self.as_raw_fd() as wasi::Fd, &bufs) { + Ok(n) => { + buf.advance(n); + Ok(()) + } + Err(e) => Err(err2io(e)), + } + } + } + + pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result { + unsafe { wasi::fd_write(self.as_raw_fd() as wasi::Fd, ciovec(bufs)).map_err(err2io) } + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + 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 { + 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(crate) 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 { + 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 { + 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 { + 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(crate) fn filestat_get(&self) -> io::Result { + 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(crate) fn path_filestat_get( + &self, + flags: wasi::Lookupflags, + path: &str, + ) -> io::Result { + 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 { + 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 { + 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 for WasiFd { + #[inline] + fn as_inner(&self) -> &OwnedFd { + &self.fd + } +} + +impl AsInnerMut for WasiFd { + #[inline] + fn as_inner_mut(&mut self) -> &mut OwnedFd { + &mut self.fd + } +} + +impl IntoInner for WasiFd { + fn into_inner(self) -> OwnedFd { + self.fd + } +} + +impl FromInner 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 { + #[inline] + 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/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs new file mode 100644 index 00000000000..e8238665452 --- /dev/null +++ b/library/std/src/sys/pal/wasi/fs.rs @@ -0,0 +1,810 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use super::fd::WasiFd; +use crate::ffi::{CStr, OsStr, OsString}; +use crate::fmt; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, 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::common::small_c_string::run_path_with_cstr; +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, + cookie: Option, + buf: Vec, + offset: usize, + cap: usize, +} + +struct ReadDirInner { + root: PathBuf, + dir: File, +} + +pub struct DirEntry { + meta: wasi::Dirent, + name: Vec, + inner: Arc, +} + +#[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, + rights_inheriting: Option, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + readonly: bool, +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes { + accessed: Option, + modified: Option, +} + +#[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 { + Ok(SystemTime::from_wasi_timestamp(self.meta.mtim)) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from_wasi_timestamp(self.meta.atim)) + } + + pub fn created(&self) -> io::Result { + Ok(SystemTime::from_wasi_timestamp(self.meta.ctim)) + } + + pub(crate) 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); + } + + pub fn set_modified(&mut self, t: SystemTime) { + self.modified = Some(t); + } +} + +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(crate) 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; + + fn next(&mut self) -> Option> { + 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::(); + 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 { + metadata_at(&self.inner.dir.fd, 0, OsStr::from_bytes(&self.name).as_ref()) + } + + pub fn file_type(&self) -> io::Result { + 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 { + let (dir, file) = open_parent(path)?; + open_at(&dir, &file, opts) + } + + pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result { + open_at(&self.fd, path, opts) + } + + pub fn file_attr(&self) -> io::Result { + self.fd.filestat_get().map(|meta| FileAttr { meta }) + } + + pub fn metadata_at(&self, flags: wasi::Lookupflags, path: &Path) -> io::Result { + 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 { + self.read_vectored(&mut [IoSliceMut::new(buf)]) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.fd.read(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.fd.read_buf(cursor) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.write_vectored(&[IoSlice::new(buf)]) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + 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 { + self.fd.seek(pos) + } + + pub fn duplicate(&self) -> io::Result { + // 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<()> { + let to_timestamp = |time: Option| match time { + Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), + Some(_) => Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time" + )), + None => Ok(0), + }; + self.fd.filestat_set_times( + to_timestamp(times.accessed)?, + to_timestamp(times.modified)?, + 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 { + read_link(&self.fd, file) + } +} + +impl AsInner for File { + #[inline] + fn as_inner(&self) -> &WasiFd { + &self.fd + } +} + +impl IntoInner for File { + fn into_inner(self) -> WasiFd { + self.fd + } +} + +impl FromInner 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 { + #[inline] + 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 { + 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 { + let (dir, file) = open_parent(p)?; + read_link(&dir, &file) +} + +fn read_link(fd: &WasiFd, file: &Path) -> io::Result { + // 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 { + let (dir, file) = open_parent(p)?; + metadata_at(&dir, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, &file) +} + +pub fn lstat(p: &Path) -> io::Result { + let (dir, file) = open_parent(p)?; + metadata_at(&dir, 0, &file) +} + +fn metadata_at(fd: &WasiFd, flags: wasi::Lookupflags, path: &Path) -> io::Result { + let meta = fd.path_filestat_get(flags, osstr2str(path.as_ref())?)?; + Ok(FileAttr { meta }) +} + +pub fn canonicalize(_p: &Path) -> io::Result { + // 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 { + 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`. 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, PathBuf)> { + run_path_with_cstr(p, |p| { + let mut buf = Vec::::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 { + 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/pal/wasi/io.rs b/library/std/src/sys/pal/wasi/io.rs new file mode 100644 index 00000000000..2cd45df88fa --- /dev/null +++ b/library/std/src/sys/pal/wasi/io.rs @@ -0,0 +1,79 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::marker::PhantomData; +use crate::os::fd::{AsFd, AsRawFd}; +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) } + } +} + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + unsafe { libc::isatty(fd.as_raw_fd()) != 0 } +} diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs new file mode 100644 index 00000000000..5919cc506d9 --- /dev/null +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -0,0 +1,198 @@ +//! 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; +#[allow(unused)] +#[path = "../wasm/atomics/futex.rs"] +pub mod futex; +pub mod io; + +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; + +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; + pub(crate) use futex_mutex::Mutex; + pub(crate) use futex_rwlock::RwLock; + } + } else { + #[path = "../unsupported/locks/mod.rs"] + pub mod locks; + #[path = "../unsupported/once.rs"] + pub mod once; + #[path = "../unsupported/thread_parking.rs"] + pub mod thread_parking; + } +} + +#[path = "../unsupported/common.rs"] +#[deny(unsafe_op_in_unsafe_fn)] +#[allow(unused)] +mod common; +pub use common::*; + +#[inline] +pub fn is_interrupted(errno: i32) -> bool { + errno == wasi::ERRNO_INTR.raw().into() +} + +pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { + use std_io::ErrorKind; + + let Ok(errno) = u16::try_from(errno) else { + return ErrorKind::Uncategorized; + }; + + macro_rules! match_errno { + ($($($errno:ident)|+ => $errkind:ident),*, _ => $wildcard:ident $(,)?) => { + match errno { + $(e if $(e == ::wasi::$errno.raw())||+ => ErrorKind::$errkind),*, + _ => ErrorKind::$wildcard, + } + }; + } + + match_errno! { + ERRNO_2BIG => ArgumentListTooLong, + ERRNO_ACCES => PermissionDenied, + ERRNO_ADDRINUSE => AddrInUse, + ERRNO_ADDRNOTAVAIL => AddrNotAvailable, + ERRNO_AFNOSUPPORT => Unsupported, + ERRNO_AGAIN => WouldBlock, + // ALREADY => "connection already in progress", + // BADF => "bad file descriptor", + // BADMSG => "bad message", + ERRNO_BUSY => ResourceBusy, + // CANCELED => "operation canceled", + // CHILD => "no child processes", + ERRNO_CONNABORTED => ConnectionAborted, + ERRNO_CONNREFUSED => ConnectionRefused, + ERRNO_CONNRESET => ConnectionReset, + ERRNO_DEADLK => Deadlock, + // DESTADDRREQ => "destination address required", + ERRNO_DOM => InvalidInput, + // DQUOT => /* reserved */, + ERRNO_EXIST => AlreadyExists, + // FAULT => "bad address", + ERRNO_FBIG => FileTooLarge, + ERRNO_HOSTUNREACH => HostUnreachable, + // IDRM => "identifier removed", + // ILSEQ => "illegal byte sequence", + // INPROGRESS => "operation in progress", + ERRNO_INTR => Interrupted, + ERRNO_INVAL => InvalidInput, + ERRNO_IO => Uncategorized, + // ISCONN => "socket is connected", + ERRNO_ISDIR => IsADirectory, + ERRNO_LOOP => FilesystemLoop, + // MFILE => "file descriptor value too large", + ERRNO_MLINK => TooManyLinks, + // MSGSIZE => "message too large", + // MULTIHOP => /* reserved */, + ERRNO_NAMETOOLONG => InvalidFilename, + ERRNO_NETDOWN => NetworkDown, + // NETRESET => "connection aborted by network", + ERRNO_NETUNREACH => NetworkUnreachable, + // NFILE => "too many files open in system", + // NOBUFS => "no buffer space available", + ERRNO_NODEV => NotFound, + ERRNO_NOENT => NotFound, + // NOEXEC => "executable file format error", + // NOLCK => "no locks available", + // NOLINK => /* reserved */, + ERRNO_NOMEM => OutOfMemory, + // NOMSG => "no message of the desired type", + // NOPROTOOPT => "protocol not available", + ERRNO_NOSPC => StorageFull, + ERRNO_NOSYS => Unsupported, + ERRNO_NOTCONN => NotConnected, + ERRNO_NOTDIR => NotADirectory, + ERRNO_NOTEMPTY => DirectoryNotEmpty, + // NOTRECOVERABLE => "state not recoverable", + // NOTSOCK => "not a socket", + ERRNO_NOTSUP => Unsupported, + // NOTTY => "inappropriate I/O control operation", + ERRNO_NXIO => NotFound, + // OVERFLOW => "value too large to be stored in data type", + // OWNERDEAD => "previous owner died", + ERRNO_PERM => PermissionDenied, + ERRNO_PIPE => BrokenPipe, + // PROTO => "protocol error", + ERRNO_PROTONOSUPPORT => Unsupported, + // PROTOTYPE => "protocol wrong type for socket", + // RANGE => "result too large", + ERRNO_ROFS => ReadOnlyFilesystem, + ERRNO_SPIPE => NotSeekable, + ERRNO_SRCH => NotFound, + // STALE => /* reserved */, + ERRNO_TIMEDOUT => TimedOut, + ERRNO_TXTBSY => ResourceBusy, + ERRNO_XDEV => CrossesDevices, + ERRNO_NOTCAPABLE => PermissionDenied, + _ => 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; +} + +#[inline] +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/pal/wasi/net.rs b/library/std/src/sys/pal/wasi/net.rs new file mode 100644 index 00000000000..2239880ffbe --- /dev/null +++ b/library/std/src/sys/pal/wasi/net.rs @@ -0,0 +1,544 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use super::err2io; +use super::fd::WasiFd; +use crate::fmt; +use crate::io::{self, BorrowedCursor, 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 for Socket { + #[inline] + fn as_inner(&self) -> &WasiFd { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> WasiFd { + self.0 + } +} + +impl FromInner 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 { + #[inline] + 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 { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn read_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn write_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + unsupported() + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.read_vectored(&mut [IoSliceMut::new(buf)]) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.socket().as_inner().read_buf(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.socket().as_inner().read(bufs) + } + + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.write_vectored(&[IoSlice::new(buf)]) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.socket().as_inner().write(bufs) + } + + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn peer_addr(&self) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + unsupported() + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let wasi_how = match how { + Shutdown::Read => wasi::SDFLAGS_RD, + Shutdown::Write => wasi::SDFLAGS_WR, + Shutdown::Both => wasi::SDFLAGS_RD | wasi::SDFLAGS_WR, + }; + + unsafe { wasi::sock_shutdown(self.socket().as_raw_fd() as _, wasi_how).map_err(err2io) } + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn linger(&self) -> io::Result> { + unsupported() + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn nodelay(&self) -> io::Result { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result { + unsupported() + } + + pub fn take_error(&self) -> io::Result> { + 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) + } + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } +} + +impl FromInner for TcpStream { + fn from_inner(socket: Socket) -> TcpStream { + TcpStream { inner: socket } + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + 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 { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + 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 { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result { + unsupported() + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn only_v6(&self) -> io::Result { + unsupported() + } + + pub fn take_error(&self) -> io::Result> { + 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) + } + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } +} + +impl AsInner for TcpListener { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl IntoInner for TcpListener { + fn into_inner(self) -> Socket { + self.inner + } +} + +impl FromInner 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 { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + 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 { + unsupported() + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn read_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn write_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn broadcast(&self) -> io::Result { + unsupported() + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn multicast_loop_v4(&self) -> io::Result { + unsupported() + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + unsupported() + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn multicast_loop_v6(&self) -> io::Result { + 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 { + unsupported() + } + + pub fn take_error(&self) -> io::Result> { + unsupported() + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + unsupported() + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + unsupported() + } + + pub fn send(&self, _: &[u8]) -> io::Result { + unsupported() + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + unsupported() + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } +} + +impl AsInner for UdpSocket { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl IntoInner for UdpSocket { + fn into_inner(self) -> Socket { + self.inner + } +} + +impl FromInner 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 { + self.0 + } +} + +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + 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/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs new file mode 100644 index 00000000000..d53bddd8e9d --- /dev/null +++ b/library/std/src/sys/pal/wasi/os.rs @@ -0,0 +1,301 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use crate::error::Error as StdError; +use crate::ffi::{CStr, OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::marker::PhantomData; +use crate::ops::Drop; +use crate::os::wasi::prelude::*; +use crate::path::{self, PathBuf}; +use crate::str; +use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; +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; + pub fn __wasilibc_get_environ() -> *mut *mut c_char; + } +} + +cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + // Access to the environment must be protected by a lock in multi-threaded scenarios. + use crate::sync::{PoisonError, RwLock}; + static ENV_LOCK: RwLock<()> = RwLock::new(()); + pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) + } + pub fn env_write_lock() -> impl Drop { + ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner) + } + } else { + // No need for a lock if we are single-threaded. + pub fn env_read_lock() -> impl Drop { + Box::new(()) + } + pub fn env_write_lock() -> impl Drop { + Box::new(()) + } + } +} + +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 { + 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 result = run_path_with_cstr(p, |p| unsafe { Ok(libc::chdir(p.as_ptr())) })?; + match result == (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 { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + 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 { + unsupported() +} + +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { slice } = self; + f.debug_list() + .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { iter } = self; + EnvStrDebug { slice: iter.as_slice() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + f.debug_list().entries(iter.as_slice()).finish() + } +} + +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) { + self.iter.size_hint() + } +} + +pub fn env() -> Env { + unsafe { + let _guard = env_read_lock(); + + // Use `__wasilibc_get_environ` instead of `environ` here so that we + // don't require wasi-libc to eagerly initialize the environment + // variables. + let mut environ = libc::__wasilibc_get_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 { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), |k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() +} + +pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + run_with_cstr(k.as_bytes(), |k| { + run_with_cstr(v.as_bytes(), |v| unsafe { + let _guard = env_write_lock(); + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) + }) + }) +} + +pub fn unsetenv(n: &OsStr) -> io::Result<()> { + run_with_cstr(n.as_bytes(), |nbuf| unsafe { + let _guard = env_write_lock(); + cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) + }) +} + +#[allow(dead_code)] +pub fn page_size() -> usize { + unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } +} + +pub fn temp_dir() -> PathBuf { + panic!("no filesystem on wasm") +} + +pub fn home_dir() -> Option { + 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: T) -> io::Result { + if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) } +} diff --git a/library/std/src/sys/pal/wasi/stdio.rs b/library/std/src/sys/pal/wasi/stdio.rs new file mode 100644 index 00000000000..4cc0e4ed5a4 --- /dev/null +++ b/library/std/src/sys/pal/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 { + self.read_vectored(&mut [IoSliceMut::new(data)]) + } + + fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result { + 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 { + self.write_vectored(&[IoSlice::new(data)]) + } + + fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { + 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 { + self.write_vectored(&[IoSlice::new(data)]) + } + + fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { + 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 { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs new file mode 100644 index 00000000000..a0eefa8811a --- /dev/null +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -0,0 +1,201 @@ +use crate::ffi::CStr; +use crate::io; +use crate::mem; +use crate::num::NonZeroUsize; +use crate::sys::unsupported; +use crate::time::Duration; + +cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + use crate::cmp; + use crate::ptr; + use crate::sys::os; + // Add a few symbols not in upstream `libc` just yet. + mod libc { + pub use crate::ffi; + pub use crate::mem; + pub use libc::*; + + // defined in wasi-libc + // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108 + #[repr(C)] + union pthread_attr_union { + __i: [ffi::c_int; if mem::size_of::() == 8 { 14 } else { 9 }], + __vi: [ffi::c_int; if mem::size_of::() == 8 { 14 } else { 9 }], + __s: [ffi::c_ulong; if mem::size_of::() == 8 { 7 } else { 9 }], + } + + #[repr(C)] + pub struct pthread_attr_t { + __u: pthread_attr_union, + } + + #[allow(non_camel_case_types)] + pub type pthread_t = *mut ffi::c_void; + + extern "C" { + pub fn pthread_create( + native: *mut pthread_t, + attr: *const pthread_attr_t, + f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void, + value: *mut ffi::c_void, + ) -> ffi::c_int; + pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int; + pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int; + pub fn pthread_attr_setstacksize( + attr: *mut pthread_attr_t, + stack_size: libc::size_t, + ) -> ffi::c_int; + pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int; + pub fn pthread_detach(thread: pthread_t) -> ffi::c_int; + } + } + + pub struct Thread { + id: libc::pthread_t, + } + + impl Drop for Thread { + fn drop(&mut self) { + let ret = unsafe { libc::pthread_detach(self.id) }; + debug_assert_eq!(ret, 0); + } + } + } else { + pub struct Thread(!); + } +} + +pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + pub unsafe fn new(stack: usize, p: Box) -> io::Result { + let p = Box::into_raw(Box::new(p)); + let mut native: libc::pthread_t = mem::zeroed(); + let mut attr: libc::pthread_attr_t = mem::zeroed(); + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + + let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE); + + 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 { + // Finally, let's run some code. + Box::from_raw(main as *mut Box)(); + } + ptr::null_mut() + } + } + } else { + pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + 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) { + cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + unsafe { + let ret = libc::pthread_join(self.id, ptr::null_mut()); + mem::forget(self); + if ret != 0 { + rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); + } + } + } else { + self.0 + } + } + } +} + +pub fn available_parallelism() -> io::Result { + unsupported() +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + None + } +} diff --git a/library/std/src/sys/pal/wasi/time.rs b/library/std/src/sys/pal/wasi/time.rs new file mode 100644 index 00000000000..016b06efbdc --- /dev/null +++ b/library/std/src/sys/pal/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 { + self.0.checked_sub(other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + 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(&self) -> Option { + self.0.as_nanos().try_into().ok() + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} diff --git a/library/std/src/sys/pal/wasm/alloc.rs b/library/std/src/sys/pal/wasm/alloc.rs new file mode 100644 index 00000000000..6dceb1689a8 --- /dev/null +++ b/library/std/src/sys/pal/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/pal/wasm/atomics/futex.rs b/library/std/src/sys/pal/wasm/atomics/futex.rs new file mode 100644 index 00000000000..f4fbe9f4855 --- /dev/null +++ b/library/std/src/sys/pal/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) -> 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/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs new file mode 100644 index 00000000000..714b7049227 --- /dev/null +++ b/library/std/src/sys/pal/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) -> io::Result { + 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 { + unsupported() +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + None + } +} diff --git a/library/std/src/sys/pal/wasm/env.rs b/library/std/src/sys/pal/wasm/env.rs new file mode 100644 index 00000000000..730e356d7fe --- /dev/null +++ b/library/std/src/sys/pal/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/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs new file mode 100644 index 00000000000..6c05b56e1bf --- /dev/null +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -0,0 +1,81 @@ +//! 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; + pub(crate) use futex_mutex::Mutex; + pub(crate) use futex_rwlock::RwLock; + } + #[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/once.rs"] + pub mod once; + #[path = "../unsupported/thread.rs"] + pub mod thread; + #[path = "../unsupported/thread_parking.rs"] + pub mod thread_parking; + } +} + +#[path = "../unsupported/common.rs"] +#[deny(unsafe_op_in_unsafe_fn)] +mod common; +pub use common::*; diff --git a/library/std/src/sys/pal/windows/alloc.rs b/library/std/src/sys/pal/windows/alloc.rs new file mode 100644 index 00000000000..d53ea16005f --- /dev/null +++ b/library/std/src/sys/pal/windows/alloc.rs @@ -0,0 +1,247 @@ +#![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; + +#[link(name = "kernel32")] +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 = 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).sub(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).sub(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/pal/windows/alloc/tests.rs b/library/std/src/sys/pal/windows/alloc/tests.rs new file mode 100644 index 00000000000..674a3e1d92d --- /dev/null +++ b/library/std/src/sys/pal/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::

() <= MIN_ALIGN); + assert!(mem::align_of::
() <= MIN_ALIGN); +} diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs new file mode 100644 index 00000000000..a7ea59e85f7 --- /dev/null +++ b/library/std/src/sys/pal/windows/api.rs @@ -0,0 +1,157 @@ +//! # Safe(r) wrappers around Windows API functions. +//! +//! This module contains fairly thin wrappers around Windows API functions, +//! aimed at centralising safety instead of having unsafe blocks spread +//! throughout higher level code. This makes it much easier to audit FFI safety. +//! +//! Not all functions can be made completely safe without more context but in +//! such cases we should still endeavour to reduce the caller's burden of safety +//! as much as possible. +//! +//! ## Guidelines for wrappers +//! +//! Items here should be named similarly to their raw Windows API name, except +//! that they follow Rust's case conventions. E.g. function names are +//! lower_snake_case. The idea here is that it should be easy for a Windows +//! C/C++ programmer to identify the underlying function that's being wrapped +//! while not looking too out of place in Rust code. +//! +//! Every use of an `unsafe` block must have a related SAFETY comment, even if +//! it's trivially safe (for example, see `get_last_error`). Public unsafe +//! functions must document what the caller has to do to call them safely. +//! +//! Avoid unchecked `as` casts. For integers, either assert that the integer +//! is in range or use `try_into` instead. For pointers, prefer to use +//! `ptr.cast::()` when possible. +//! +//! This module must only depend on core and not on std types as the eventual +//! hope is to have std depend on sys and not the other way around. +//! However, some amount of glue code may currently be necessary so such code +//! should go in sys/windows/mod.rs rather than here. See `IoResult` as an example. + +use core::ffi::c_void; +use core::ptr::addr_of; + +use super::c; + +/// Helper method for getting the size of `T` as a u32. +/// Errors at compile time if the size would overflow. +/// +/// While a type larger than u32::MAX is unlikely, it is possible if only because of a bug. +/// However, one key motivation for this function is to avoid the temptation to +/// use frequent `as` casts. This is risky because they are too powerful. +/// For example, the following will compile today: +/// +/// `std::mem::size_of:: as u32` +/// +/// Note that `size_of` is never actually called, instead a function pointer is +/// converted to a `u32`. Clippy would warn about this but, alas, it's not run +/// on the standard library. +const fn win32_size_of() -> u32 { + // Const assert that the size does not exceed u32::MAX. + // Uses a trait to workaround restriction on using generic types in inner items. + trait Win32SizeOf: Sized { + const WIN32_SIZE_OF: u32 = { + let size = core::mem::size_of::(); + assert!(size <= u32::MAX as usize); + size as u32 + }; + } + impl Win32SizeOf for T {} + + T::WIN32_SIZE_OF +} + +/// The `SetFileInformationByHandle` function takes a generic parameter by +/// making the user specify the type (class), a pointer to the data and its +/// size. This trait allows attaching that information to a Rust type so that +/// [`set_file_information_by_handle`] can be called safely. +/// +/// This trait is designed so that it can support variable sized types. +/// However, currently Rust's std only uses fixed sized structures. +/// +/// # Safety +/// +/// * `as_ptr` must return a pointer to memory that is readable up to `size` bytes. +/// * `CLASS` must accurately reflect the type pointed to by `as_ptr`. E.g. +/// the `FILE_BASIC_INFO` structure has the class `FileBasicInfo`. +pub unsafe trait SetFileInformation { + /// The type of information to set. + const CLASS: i32; + /// A pointer to the file information to set. + fn as_ptr(&self) -> *const c_void; + /// The size of the type pointed to by `as_ptr`. + fn size(&self) -> u32; +} +/// Helper trait for implementing `SetFileInformation` for statically sized types. +unsafe trait SizedSetFileInformation: Sized { + const CLASS: i32; +} +unsafe impl SetFileInformation for T { + const CLASS: i32 = T::CLASS; + fn as_ptr(&self) -> *const c_void { + addr_of!(*self).cast::() + } + fn size(&self) -> u32 { + win32_size_of::() + } +} + +// SAFETY: FILE_BASIC_INFO, FILE_END_OF_FILE_INFO, FILE_ALLOCATION_INFO, +// FILE_DISPOSITION_INFO, FILE_DISPOSITION_INFO_EX and FILE_IO_PRIORITY_HINT_INFO +// are all plain `repr(C)` structs that only contain primitive types. +// The given information classes correctly match with the struct. +unsafe impl SizedSetFileInformation for c::FILE_BASIC_INFO { + const CLASS: i32 = c::FileBasicInfo; +} +unsafe impl SizedSetFileInformation for c::FILE_END_OF_FILE_INFO { + const CLASS: i32 = c::FileEndOfFileInfo; +} +unsafe impl SizedSetFileInformation for c::FILE_ALLOCATION_INFO { + const CLASS: i32 = c::FileAllocationInfo; +} +unsafe impl SizedSetFileInformation for c::FILE_DISPOSITION_INFO { + const CLASS: i32 = c::FileDispositionInfo; +} +unsafe impl SizedSetFileInformation for c::FILE_DISPOSITION_INFO_EX { + const CLASS: i32 = c::FileDispositionInfoEx; +} +unsafe impl SizedSetFileInformation for c::FILE_IO_PRIORITY_HINT_INFO { + const CLASS: i32 = c::FileIoPriorityHintInfo; +} + +#[inline] +pub fn set_file_information_by_handle( + handle: c::HANDLE, + info: &T, +) -> Result<(), WinError> { + unsafe fn set_info( + handle: c::HANDLE, + class: i32, + info: *const c_void, + size: u32, + ) -> Result<(), WinError> { + let result = c::SetFileInformationByHandle(handle, class, info, size); + (result != 0).then_some(()).ok_or_else(get_last_error) + } + // SAFETY: The `SetFileInformation` trait ensures that this is safe. + unsafe { set_info(handle, T::CLASS, info.as_ptr(), info.size()) } +} + +/// Gets the error from the last function. +/// This must be called immediately after the function that sets the error to +/// avoid the risk of another function overwriting it. +pub fn get_last_error() -> WinError { + // SAFETY: This just returns a thread-local u32 and has no other effects. + unsafe { WinError { code: c::GetLastError() } } +} + +/// An error code as returned by [`get_last_error`]. +/// +/// This is usually a 16-bit Win32 error code but may be a 32-bit HRESULT or NTSTATUS. +/// Check the documentation of the Windows API function being called for expected errors. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct WinError { + pub code: u32, +} diff --git a/library/std/src/sys/pal/windows/args.rs b/library/std/src/sys/pal/windows/args.rs new file mode 100644 index 00000000000..ee7dba6e5b3 --- /dev/null +++ b/library/std/src/sys/pal/windows/args.rs @@ -0,0 +1,379 @@ +//! 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::num::NonZeroU16; +use crate::os::windows::prelude::*; +use crate::path::{Path, PathBuf}; +use crate::sys::path::get_long_path; +use crate::sys::process::ensure_no_nuls; +use crate::sys::windows::os::current_exe; +use crate::sys::{c, to_u16s}; +use crate::sys_common::wstr::WStrUnits; +use crate::vec; + +use crate::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 +/// +/// +/// A more in-depth explanation is here: +/// +/// +/// 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 +/// . +/// +/// 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 +/// . +fn parse_lp_cmd_line<'a, F: Fn() -> OsString>( + lp_cmd_line: Option>, + exe_name: F, +) -> Vec { + 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, +} + +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 { + self.parsed_args_list.next() + } + fn size_hint(&self) -> (usize, Option) { + self.parsed_args_list.size_hint() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + self.parsed_args_list.next_back() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.parsed_args_list.len() + } +} + +#[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, 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.as_encoded_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> { + // 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 = "cmd.exe /d /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); + // Make sure to always quote special command prompt characters, including: + // * Characters `cmd /?` says require quotes. + // * `%` for environment variables, as in `%TMP%`. + // * `|<>` pipe/redirect characters. + const SPECIAL: &[u8] = b"\t &()[]{}^=;!'+,`~%|<>"; + let force_quotes = match arg { + Arg::Regular(arg) if !force_quotes => { + arg.as_encoded_bytes().iter().any(|c| SPECIAL.contains(c)) + } + _ => force_quotes, + }; + 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(path: &Path) -> io::Result> { + from_wide_to_user_path(to_u16s(path)?) +} +pub(crate) fn from_wide_to_user_path(mut path: Vec) -> io::Result> { + 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] { + let mut path: Vec = full_path.into(); + path.push(0); + path + } 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] { + let mut path: Vec = full_path.into(); + path.push(0); + path + } else { + // Restore the 'C' in "UNC". + path[6] = b'C' as u16; + path + } + }, + ) + }, + // For everything else, leave the path unchanged. + _ => get_long_path(path, false), + } +} diff --git a/library/std/src/sys/pal/windows/args/tests.rs b/library/std/src/sys/pal/windows/args/tests.rs new file mode 100644 index 00000000000..82c32d08c5e --- /dev/null +++ b/library/std/src/sys/pal/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 = 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 = 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/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs new file mode 100644 index 00000000000..d55d9bace81 --- /dev/null +++ b/library/std/src/sys/pal/windows/c.rs @@ -0,0 +1,480 @@ +//! 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")] +#![allow(clippy::style)] + +use crate::ffi::CStr; +use crate::mem; +pub use crate::os::raw::c_int; +use crate::os::raw::{c_char, c_long, c_longlong, c_uint, c_ulong, c_ushort, c_void}; +use crate::os::windows::io::{AsRawHandle, BorrowedHandle}; +use crate::ptr; +use core::ffi::NonZero_c_ulong; + +mod windows_sys; +pub use windows_sys::*; + +pub type DWORD = c_ulong; +pub type NonZeroDWORD = NonZero_c_ulong; +pub type LARGE_INTEGER = c_longlong; +#[cfg_attr(target_vendor = "uwp", allow(unused))] +pub type LONG = c_long; +pub type UINT = c_uint; +pub type WCHAR = u16; +pub type USHORT = c_ushort; +pub type SIZE_T = usize; +pub type WORD = u16; +pub type CHAR = c_char; +pub type ULONG = c_ulong; +pub type ACCESS_MASK = DWORD; + +pub type LPCVOID = *const c_void; +pub type LPHANDLE = *mut HANDLE; +pub type LPOVERLAPPED = *mut OVERLAPPED; +pub type LPSECURITY_ATTRIBUTES = *mut SECURITY_ATTRIBUTES; +pub type LPVOID = *mut c_void; +pub type LPWCH = *mut WCHAR; +pub type LPWSTR = *mut WCHAR; + +pub type PLARGE_INTEGER = *mut c_longlong; +pub type PSRWLOCK = *mut SRWLOCK; + +pub type socklen_t = c_int; +pub type ADDRESS_FAMILY = USHORT; +pub use FD_SET as fd_set; +pub use LINGER as linger; +pub use TIMEVAL as timeval; + +pub const INVALID_HANDLE_VALUE: HANDLE = ::core::ptr::invalid_mut(-1i32 as _); + +// https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure?view=msvc-170 +pub const EXIT_SUCCESS: u32 = 0; +pub const EXIT_FAILURE: u32 = 1; + +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 INIT_ONCE_STATIC_INIT: INIT_ONCE = INIT_ONCE { Ptr: ptr::null_mut() }; + +// Some windows_sys types have different signs than the types we use. +pub const OBJ_DONT_REPARSE: u32 = windows_sys::OBJ_DONT_REPARSE as u32; +pub const FRS_ERR_SYSVOL_POPULATE_TIMEOUT: u32 = + windows_sys::FRS_ERR_SYSVOL_POPULATE_TIMEOUT as u32; +pub const AF_INET: c_int = windows_sys::AF_INET as c_int; +pub const AF_INET6: c_int = windows_sys::AF_INET6 as c_int; + +#[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, +} + +// 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 +} + +impl UNICODE_STRING { + pub fn from_ref(slice: &[u16]) -> Self { + let len = mem::size_of_val(slice); + Self { Length: len as _, MaximumLength: len as _, Buffer: slice.as_ptr() as _ } + } +} + +impl Default for OBJECT_ATTRIBUTES { + fn default() -> Self { + Self { + Length: mem::size_of::() as _, + RootDirectory: ptr::null_mut(), + ObjectName: ptr::null_mut(), + Attributes: 0, + SecurityDescriptor: ptr::null_mut(), + SecurityQualityOfService: ptr::null_mut(), + } + } +} + +impl IO_STATUS_BLOCK { + pub const PENDING: Self = + IO_STATUS_BLOCK { Anonymous: IO_STATUS_BLOCK_0 { Status: STATUS_PENDING }, Information: 0 }; + pub fn status(&self) -> NTSTATUS { + // SAFETY: If `self.Anonymous.Status` was set then this is obviously safe. + // If `self.Anonymous.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.Anonymous.Status } + } +} + +/// NB: Use carefully! In general using this as a reference is likely to get the +/// provenance wrong for the `rest` field! +#[repr(C)] +pub struct REPARSE_DATA_BUFFER { + pub ReparseTag: c_uint, + pub ReparseDataLength: c_ushort, + pub Reserved: c_ushort, + pub rest: (), +} + +/// NB: Use carefully! In general using this as a reference is likely to get the +/// provenance wrong for the `PathBuffer` field! +#[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, +} +#[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 SOCKADDR_STORAGE_LH { + pub ss_family: ADDRESS_FAMILY, + pub __ss_pad1: [CHAR; 6], + pub __ss_align: i64, + pub __ss_pad2: [CHAR; 112], +} + +#[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], +} + +// Desktop specific functions & types +cfg_if::cfg_if! { +if #[cfg(not(target_vendor = "uwp"))] { + pub const EXCEPTION_CONTINUE_SEARCH: i32 = 0; +} +} + +pub unsafe extern "system" fn WriteFileEx( + hFile: BorrowedHandle<'_>, + lpBuffer: *mut ::core::ffi::c_void, + nNumberOfBytesToWrite: u32, + lpOverlapped: *mut OVERLAPPED, + lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, +) -> BOOL { + windows_sys::WriteFileEx( + hFile.as_raw_handle(), + lpBuffer.cast::(), + nNumberOfBytesToWrite, + lpOverlapped, + lpCompletionRoutine, + ) +} + +pub unsafe extern "system" fn ReadFileEx( + hFile: BorrowedHandle<'_>, + lpBuffer: *mut ::core::ffi::c_void, + nNumberOfBytesToRead: u32, + lpOverlapped: *mut OVERLAPPED, + lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, +) -> BOOL { + windows_sys::ReadFileEx( + hFile.as_raw_handle(), + lpBuffer.cast::(), + nNumberOfBytesToRead, + lpOverlapped, + lpCompletionRoutine, + ) +} + +// POSIX compatibility shims. +pub unsafe fn recv(socket: SOCKET, buf: *mut c_void, len: c_int, flags: c_int) -> c_int { + windows_sys::recv(socket, buf.cast::(), len, flags) +} +pub unsafe fn send(socket: SOCKET, buf: *const c_void, len: c_int, flags: c_int) -> c_int { + windows_sys::send(socket, buf.cast::(), len, flags) +} +pub unsafe fn recvfrom( + socket: SOCKET, + buf: *mut c_void, + len: c_int, + flags: c_int, + addr: *mut SOCKADDR, + addrlen: *mut c_int, +) -> c_int { + windows_sys::recvfrom(socket, buf.cast::(), len, flags, addr, addrlen) +} +pub unsafe fn sendto( + socket: SOCKET, + buf: *const c_void, + len: c_int, + flags: c_int, + addr: *const SOCKADDR, + addrlen: c_int, +) -> c_int { + windows_sys::sendto(socket, buf.cast::(), len, flags, addr, addrlen) +} +pub unsafe fn getaddrinfo( + node: *const c_char, + service: *const c_char, + hints: *const ADDRINFOA, + res: *mut *mut ADDRINFOA, +) -> c_int { + windows_sys::getaddrinfo(node.cast::(), service.cast::(), hints, res) +} + +cfg_if::cfg_if! { +if #[cfg(not(target_vendor = "uwp"))] { +pub unsafe fn NtReadFile( + filehandle: BorrowedHandle<'_>, + event: HANDLE, + apcroutine: PIO_APC_ROUTINE, + apccontext: *mut c_void, + iostatusblock: &mut IO_STATUS_BLOCK, + buffer: *mut crate::mem::MaybeUninit, + length: ULONG, + byteoffset: Option<&LARGE_INTEGER>, + key: Option<&ULONG>, +) -> NTSTATUS { + windows_sys::NtReadFile( + filehandle.as_raw_handle(), + event, + apcroutine, + apccontext, + iostatusblock, + buffer.cast::(), + length, + byteoffset.map(|o| o as *const i64).unwrap_or(ptr::null()), + key.map(|k| k as *const u32).unwrap_or(ptr::null()), + ) +} +pub unsafe fn NtWriteFile( + filehandle: BorrowedHandle<'_>, + event: HANDLE, + apcroutine: PIO_APC_ROUTINE, + apccontext: *mut c_void, + iostatusblock: &mut IO_STATUS_BLOCK, + buffer: *const u8, + length: ULONG, + byteoffset: Option<&LARGE_INTEGER>, + key: Option<&ULONG>, +) -> NTSTATUS { + windows_sys::NtWriteFile( + filehandle.as_raw_handle(), + event, + apcroutine, + apccontext, + iostatusblock, + buffer.cast::(), + length, + byteoffset.map(|o| o as *const i64).unwrap_or(ptr::null()), + key.map(|k| k as *const u32).unwrap_or(ptr::null()), + ) +} +} +} + +// 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 = c"kernel32"; + + // >= Win10 1607 + // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription + pub fn SetThreadDescription(hthread: HANDLE, lpthreaddescription: PCWSTR) -> 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: *mut FILETIME) -> () { + GetSystemTimeAsFileTime(lpsystemtimeasfiletime) + } + + // >= Win11 / Server 2022 + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2a + pub fn GetTempPath2W(bufferlength: u32, buffer: PWSTR) -> u32 { + GetTempPathW(bufferlength, buffer) + } +} + +compat_fn_optional! { + crate::sys::compat::load_synch_functions(); + pub fn WaitOnAddress( + address: *const ::core::ffi::c_void, + compareaddress: *const ::core::ffi::c_void, + addresssize: usize, + dwmilliseconds: u32 + ) -> BOOL; + pub fn WakeByAddressSingle(address: *const ::core::ffi::c_void); +} + +compat_fn_with_fallback! { + pub static NTDLL: &CStr = c"ntdll"; + + 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") + } + + // These functions are available on UWP when lazily loaded. They will fail WACK if loaded statically. + #[cfg(target_vendor = "uwp")] + pub fn NtCreateFile( + filehandle: *mut HANDLE, + desiredaccess: FILE_ACCESS_RIGHTS, + objectattributes: *const OBJECT_ATTRIBUTES, + iostatusblock: *mut IO_STATUS_BLOCK, + allocationsize: *const i64, + fileattributes: FILE_FLAGS_AND_ATTRIBUTES, + shareaccess: FILE_SHARE_MODE, + createdisposition: NTCREATEFILE_CREATE_DISPOSITION, + createoptions: NTCREATEFILE_CREATE_OPTIONS, + eabuffer: *const ::core::ffi::c_void, + ealength: u32 + ) -> NTSTATUS { + STATUS_NOT_IMPLEMENTED + } + #[cfg(target_vendor = "uwp")] + pub fn NtReadFile( + filehandle: BorrowedHandle<'_>, + event: HANDLE, + apcroutine: PIO_APC_ROUTINE, + apccontext: *mut c_void, + iostatusblock: &mut IO_STATUS_BLOCK, + buffer: *mut crate::mem::MaybeUninit, + length: ULONG, + byteoffset: Option<&LARGE_INTEGER>, + key: Option<&ULONG> + ) -> NTSTATUS { + STATUS_NOT_IMPLEMENTED + } + #[cfg(target_vendor = "uwp")] + pub fn NtWriteFile( + filehandle: BorrowedHandle<'_>, + event: HANDLE, + apcroutine: PIO_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 + } + #[cfg(target_vendor = "uwp")] + pub fn RtlNtStatusToDosError(Status: NTSTATUS) -> u32 { + Status as u32 + } +} + +// # Arm32 shim +// +// AddVectoredExceptionHandler and WSAStartup use platform-specific types. +// However, Microsoft no longer supports thumbv7a so definitions for those targets +// are not included in the win32 metadata. We work around that by defining them here. +// +// Where possible, these definitions should be kept in sync with https://docs.rs/windows-sys +cfg_if::cfg_if! { +if #[cfg(not(target_vendor = "uwp"))] { + #[link(name = "kernel32")] + extern "system" { + pub fn AddVectoredExceptionHandler( + first: u32, + handler: PVECTORED_EXCEPTION_HANDLER, + ) -> *mut c_void; + } + pub type PVECTORED_EXCEPTION_HANDLER = Option< + unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32, + >; + #[repr(C)] + pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: *mut CONTEXT, + } + #[cfg(target_arch = "arm")] + pub enum CONTEXT {} +}} + +#[link(name = "ws2_32")] +extern "system" { + pub fn WSAStartup(wversionrequested: u16, lpwsadata: *mut WSADATA) -> i32; +} +#[cfg(target_arch = "arm")] +#[repr(C)] +pub struct WSADATA { + pub wVersion: u16, + pub wHighVersion: u16, + pub szDescription: [u8; 257], + pub szSystemStatus: [u8; 129], + pub iMaxSockets: u16, + pub iMaxUdpDg: u16, + pub lpVendorInfo: PSTR, +} diff --git a/library/std/src/sys/pal/windows/c/windows_sys.lst b/library/std/src/sys/pal/windows/c/windows_sys.lst new file mode 100644 index 00000000000..f91e1054a04 --- /dev/null +++ b/library/std/src/sys/pal/windows/c/windows_sys.lst @@ -0,0 +1,2596 @@ +--out windows_sys.rs +--config flatten std +--filter +// tidy-alphabetical-start +!Windows.Win32.Foundation.INVALID_HANDLE_VALUE +Windows.Wdk.Storage.FileSystem.FILE_COMPLETE_IF_OPLOCKED +Windows.Wdk.Storage.FileSystem.FILE_CONTAINS_EXTENDED_CREATE_INFORMATION +Windows.Wdk.Storage.FileSystem.FILE_CREATE +Windows.Wdk.Storage.FileSystem.FILE_CREATE_TREE_CONNECTION +Windows.Wdk.Storage.FileSystem.FILE_DELETE_ON_CLOSE +Windows.Wdk.Storage.FileSystem.FILE_DIRECTORY_FILE +Windows.Wdk.Storage.FileSystem.FILE_DISALLOW_EXCLUSIVE +Windows.Wdk.Storage.FileSystem.FILE_NO_COMPRESSION +Windows.Wdk.Storage.FileSystem.FILE_NO_EA_KNOWLEDGE +Windows.Wdk.Storage.FileSystem.FILE_NO_INTERMEDIATE_BUFFERING +Windows.Wdk.Storage.FileSystem.FILE_NON_DIRECTORY_FILE +Windows.Wdk.Storage.FileSystem.FILE_OPEN +Windows.Wdk.Storage.FileSystem.FILE_OPEN_BY_FILE_ID +Windows.Wdk.Storage.FileSystem.FILE_OPEN_FOR_BACKUP_INTENT +Windows.Wdk.Storage.FileSystem.FILE_OPEN_FOR_FREE_SPACE_QUERY +Windows.Wdk.Storage.FileSystem.FILE_OPEN_IF +Windows.Wdk.Storage.FileSystem.FILE_OPEN_NO_RECALL +Windows.Wdk.Storage.FileSystem.FILE_OPEN_REPARSE_POINT +Windows.Wdk.Storage.FileSystem.FILE_OPEN_REQUIRING_OPLOCK +Windows.Wdk.Storage.FileSystem.FILE_OVERWRITE +Windows.Wdk.Storage.FileSystem.FILE_OVERWRITE_IF +Windows.Wdk.Storage.FileSystem.FILE_RANDOM_ACCESS +Windows.Wdk.Storage.FileSystem.FILE_RESERVE_OPFILTER +Windows.Wdk.Storage.FileSystem.FILE_SEQUENTIAL_ONLY +Windows.Wdk.Storage.FileSystem.FILE_SESSION_AWARE +Windows.Wdk.Storage.FileSystem.FILE_SUPERSEDE +Windows.Wdk.Storage.FileSystem.FILE_SYNCHRONOUS_IO_ALERT +Windows.Wdk.Storage.FileSystem.FILE_SYNCHRONOUS_IO_NONALERT +Windows.Wdk.Storage.FileSystem.FILE_WRITE_THROUGH +Windows.Wdk.Storage.FileSystem.NtCreateFile +Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_DISPOSITION +Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_OPTIONS +Windows.Wdk.Storage.FileSystem.NtReadFile +Windows.Wdk.Storage.FileSystem.NtWriteFile +Windows.Wdk.Storage.FileSystem.SYMLINK_FLAG_RELATIVE +Windows.Win32.Foundation.BOOL +Windows.Win32.Foundation.BOOLEAN +Windows.Win32.Foundation.CloseHandle +Windows.Win32.Foundation.DNS_ERROR_ADDRESS_REQUIRED +Windows.Win32.Foundation.DNS_ERROR_ALIAS_LOOP +Windows.Win32.Foundation.DNS_ERROR_AUTOZONE_ALREADY_EXISTS +Windows.Win32.Foundation.DNS_ERROR_AXFR +Windows.Win32.Foundation.DNS_ERROR_BACKGROUND_LOADING +Windows.Win32.Foundation.DNS_ERROR_BAD_KEYMASTER +Windows.Win32.Foundation.DNS_ERROR_BAD_PACKET +Windows.Win32.Foundation.DNS_ERROR_CANNOT_FIND_ROOT_HINTS +Windows.Win32.Foundation.DNS_ERROR_CLIENT_SUBNET_ALREADY_EXISTS +Windows.Win32.Foundation.DNS_ERROR_CLIENT_SUBNET_DOES_NOT_EXIST +Windows.Win32.Foundation.DNS_ERROR_CLIENT_SUBNET_IS_ACCESSED +Windows.Win32.Foundation.DNS_ERROR_CNAME_COLLISION +Windows.Win32.Foundation.DNS_ERROR_CNAME_LOOP +Windows.Win32.Foundation.DNS_ERROR_DATAFILE_OPEN_FAILURE +Windows.Win32.Foundation.DNS_ERROR_DATAFILE_PARSING +Windows.Win32.Foundation.DNS_ERROR_DEFAULT_SCOPE +Windows.Win32.Foundation.DNS_ERROR_DEFAULT_VIRTUALIZATION_INSTANCE +Windows.Win32.Foundation.DNS_ERROR_DEFAULT_ZONESCOPE +Windows.Win32.Foundation.DNS_ERROR_DELEGATION_REQUIRED +Windows.Win32.Foundation.DNS_ERROR_DNAME_COLLISION +Windows.Win32.Foundation.DNS_ERROR_DNSSEC_IS_DISABLED +Windows.Win32.Foundation.DNS_ERROR_DP_ALREADY_ENLISTED +Windows.Win32.Foundation.DNS_ERROR_DP_ALREADY_EXISTS +Windows.Win32.Foundation.DNS_ERROR_DP_DOES_NOT_EXIST +Windows.Win32.Foundation.DNS_ERROR_DP_FSMO_ERROR +Windows.Win32.Foundation.DNS_ERROR_DP_NOT_AVAILABLE +Windows.Win32.Foundation.DNS_ERROR_DP_NOT_ENLISTED +Windows.Win32.Foundation.DNS_ERROR_DS_UNAVAILABLE +Windows.Win32.Foundation.DNS_ERROR_DS_ZONE_ALREADY_EXISTS +Windows.Win32.Foundation.DNS_ERROR_DWORD_VALUE_TOO_LARGE +Windows.Win32.Foundation.DNS_ERROR_DWORD_VALUE_TOO_SMALL +Windows.Win32.Foundation.DNS_ERROR_FILE_WRITEBACK_FAILED +Windows.Win32.Foundation.DNS_ERROR_FORWARDER_ALREADY_EXISTS +Windows.Win32.Foundation.DNS_ERROR_INCONSISTENT_ROOT_HINTS +Windows.Win32.Foundation.DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME +Windows.Win32.Foundation.DNS_ERROR_INVALID_CLIENT_SUBNET_NAME +Windows.Win32.Foundation.DNS_ERROR_INVALID_DATA +Windows.Win32.Foundation.DNS_ERROR_INVALID_DATAFILE_NAME +Windows.Win32.Foundation.DNS_ERROR_INVALID_INITIAL_ROLLOVER_OFFSET +Windows.Win32.Foundation.DNS_ERROR_INVALID_IP_ADDRESS +Windows.Win32.Foundation.DNS_ERROR_INVALID_KEY_SIZE +Windows.Win32.Foundation.DNS_ERROR_INVALID_NAME +Windows.Win32.Foundation.DNS_ERROR_INVALID_NAME_CHAR +Windows.Win32.Foundation.DNS_ERROR_INVALID_NSEC3_ITERATION_COUNT +Windows.Win32.Foundation.DNS_ERROR_INVALID_POLICY_TABLE +Windows.Win32.Foundation.DNS_ERROR_INVALID_PROPERTY +Windows.Win32.Foundation.DNS_ERROR_INVALID_ROLLOVER_PERIOD +Windows.Win32.Foundation.DNS_ERROR_INVALID_SCOPE_NAME +Windows.Win32.Foundation.DNS_ERROR_INVALID_SCOPE_OPERATION +Windows.Win32.Foundation.DNS_ERROR_INVALID_SIGNATURE_VALIDITY_PERIOD +Windows.Win32.Foundation.DNS_ERROR_INVALID_TYPE +Windows.Win32.Foundation.DNS_ERROR_INVALID_XML +Windows.Win32.Foundation.DNS_ERROR_INVALID_ZONE_OPERATION +Windows.Win32.Foundation.DNS_ERROR_INVALID_ZONE_TYPE +Windows.Win32.Foundation.DNS_ERROR_INVALID_ZONESCOPE_NAME +Windows.Win32.Foundation.DNS_ERROR_KEYMASTER_REQUIRED +Windows.Win32.Foundation.DNS_ERROR_KSP_DOES_NOT_SUPPORT_PROTECTION +Windows.Win32.Foundation.DNS_ERROR_KSP_NOT_ACCESSIBLE +Windows.Win32.Foundation.DNS_ERROR_LOAD_ZONESCOPE_FAILED +Windows.Win32.Foundation.DNS_ERROR_NAME_DOES_NOT_EXIST +Windows.Win32.Foundation.DNS_ERROR_NAME_NOT_IN_ZONE +Windows.Win32.Foundation.DNS_ERROR_NBSTAT_INIT_FAILED +Windows.Win32.Foundation.DNS_ERROR_NEED_SECONDARY_ADDRESSES +Windows.Win32.Foundation.DNS_ERROR_NEED_WINS_SERVERS +Windows.Win32.Foundation.DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE +Windows.Win32.Foundation.DNS_ERROR_NO_CREATE_CACHE_DATA +Windows.Win32.Foundation.DNS_ERROR_NO_DNS_SERVERS +Windows.Win32.Foundation.DNS_ERROR_NO_MEMORY +Windows.Win32.Foundation.DNS_ERROR_NO_PACKET +Windows.Win32.Foundation.DNS_ERROR_NO_TCPIP +Windows.Win32.Foundation.DNS_ERROR_NO_VALID_TRUST_ANCHORS +Windows.Win32.Foundation.DNS_ERROR_NO_ZONE_INFO +Windows.Win32.Foundation.DNS_ERROR_NODE_CREATION_FAILED +Windows.Win32.Foundation.DNS_ERROR_NODE_IS_CNAME +Windows.Win32.Foundation.DNS_ERROR_NODE_IS_DNAME +Windows.Win32.Foundation.DNS_ERROR_NON_RFC_NAME +Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_ACTIVE_SKD +Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_RODC +Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER +Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_SIGNED_ZONE +Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_UNSIGNED_ZONE +Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_ZSK +Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION +Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_UNDER_DNAME +Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_WITH_ZONESCOPES +Windows.Win32.Foundation.DNS_ERROR_NOT_ENOUGH_SIGNING_KEY_DESCRIPTORS +Windows.Win32.Foundation.DNS_ERROR_NOT_UNIQUE +Windows.Win32.Foundation.DNS_ERROR_NSEC3_INCOMPATIBLE_WITH_RSA_SHA1 +Windows.Win32.Foundation.DNS_ERROR_NSEC3_NAME_COLLISION +Windows.Win32.Foundation.DNS_ERROR_NSEC_INCOMPATIBLE_WITH_NSEC3_RSA_SHA1 +Windows.Win32.Foundation.DNS_ERROR_NUMERIC_NAME +Windows.Win32.Foundation.DNS_ERROR_POLICY_ALREADY_EXISTS +Windows.Win32.Foundation.DNS_ERROR_POLICY_DOES_NOT_EXIST +Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA +Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_CLIENT_SUBNET +Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_FQDN +Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_INTERFACE +Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_NETWORK_PROTOCOL +Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_QUERY_TYPE +Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_TIME_OF_DAY +Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_TRANSPORT_PROTOCOL +Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_NAME +Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_SETTINGS +Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_WEIGHT +Windows.Win32.Foundation.DNS_ERROR_POLICY_LOCKED +Windows.Win32.Foundation.DNS_ERROR_POLICY_MISSING_CRITERIA +Windows.Win32.Foundation.DNS_ERROR_POLICY_PROCESSING_ORDER_INVALID +Windows.Win32.Foundation.DNS_ERROR_POLICY_SCOPE_MISSING +Windows.Win32.Foundation.DNS_ERROR_POLICY_SCOPE_NOT_ALLOWED +Windows.Win32.Foundation.DNS_ERROR_PRIMARY_REQUIRES_DATAFILE +Windows.Win32.Foundation.DNS_ERROR_RCODE +Windows.Win32.Foundation.DNS_ERROR_RCODE_BADKEY +Windows.Win32.Foundation.DNS_ERROR_RCODE_BADSIG +Windows.Win32.Foundation.DNS_ERROR_RCODE_BADTIME +Windows.Win32.Foundation.DNS_ERROR_RCODE_FORMAT_ERROR +Windows.Win32.Foundation.DNS_ERROR_RCODE_NAME_ERROR +Windows.Win32.Foundation.DNS_ERROR_RCODE_NOT_IMPLEMENTED +Windows.Win32.Foundation.DNS_ERROR_RCODE_NOTAUTH +Windows.Win32.Foundation.DNS_ERROR_RCODE_NOTZONE +Windows.Win32.Foundation.DNS_ERROR_RCODE_NXRRSET +Windows.Win32.Foundation.DNS_ERROR_RCODE_REFUSED +Windows.Win32.Foundation.DNS_ERROR_RCODE_SERVER_FAILURE +Windows.Win32.Foundation.DNS_ERROR_RCODE_YXDOMAIN +Windows.Win32.Foundation.DNS_ERROR_RCODE_YXRRSET +Windows.Win32.Foundation.DNS_ERROR_RECORD_ALREADY_EXISTS +Windows.Win32.Foundation.DNS_ERROR_RECORD_DOES_NOT_EXIST +Windows.Win32.Foundation.DNS_ERROR_RECORD_FORMAT +Windows.Win32.Foundation.DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT +Windows.Win32.Foundation.DNS_ERROR_RECORD_TIMED_OUT +Windows.Win32.Foundation.DNS_ERROR_ROLLOVER_ALREADY_QUEUED +Windows.Win32.Foundation.DNS_ERROR_ROLLOVER_IN_PROGRESS +Windows.Win32.Foundation.DNS_ERROR_ROLLOVER_NOT_POKEABLE +Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_IPV4_PREFIX +Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_IPV6_PREFIX +Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_LEAK_RATE +Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_TC_RATE +Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_WINDOW_SIZE +Windows.Win32.Foundation.DNS_ERROR_RRL_LEAK_RATE_LESSTHAN_TC_RATE +Windows.Win32.Foundation.DNS_ERROR_RRL_NOT_ENABLED +Windows.Win32.Foundation.DNS_ERROR_SCOPE_ALREADY_EXISTS +Windows.Win32.Foundation.DNS_ERROR_SCOPE_DOES_NOT_EXIST +Windows.Win32.Foundation.DNS_ERROR_SCOPE_LOCKED +Windows.Win32.Foundation.DNS_ERROR_SECONDARY_DATA +Windows.Win32.Foundation.DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP +Windows.Win32.Foundation.DNS_ERROR_SERVERSCOPE_IS_REFERENCED +Windows.Win32.Foundation.DNS_ERROR_SIGNING_KEY_NOT_ACCESSIBLE +Windows.Win32.Foundation.DNS_ERROR_SOA_DELETE_INVALID +Windows.Win32.Foundation.DNS_ERROR_STANDBY_KEY_NOT_PRESENT +Windows.Win32.Foundation.DNS_ERROR_SUBNET_ALREADY_EXISTS +Windows.Win32.Foundation.DNS_ERROR_SUBNET_DOES_NOT_EXIST +Windows.Win32.Foundation.DNS_ERROR_TOO_MANY_SKDS +Windows.Win32.Foundation.DNS_ERROR_TRY_AGAIN_LATER +Windows.Win32.Foundation.DNS_ERROR_UNEXPECTED_CNG_ERROR +Windows.Win32.Foundation.DNS_ERROR_UNEXPECTED_DATA_PROTECTION_ERROR +Windows.Win32.Foundation.DNS_ERROR_UNKNOWN_RECORD_TYPE +Windows.Win32.Foundation.DNS_ERROR_UNKNOWN_SIGNING_PARAMETER_VERSION +Windows.Win32.Foundation.DNS_ERROR_UNSECURE_PACKET +Windows.Win32.Foundation.DNS_ERROR_UNSUPPORTED_ALGORITHM +Windows.Win32.Foundation.DNS_ERROR_VIRTUALIZATION_INSTANCE_ALREADY_EXISTS +Windows.Win32.Foundation.DNS_ERROR_VIRTUALIZATION_INSTANCE_DOES_NOT_EXIST +Windows.Win32.Foundation.DNS_ERROR_VIRTUALIZATION_TREE_LOCKED +Windows.Win32.Foundation.DNS_ERROR_WINS_INIT_FAILED +Windows.Win32.Foundation.DNS_ERROR_ZONE_ALREADY_EXISTS +Windows.Win32.Foundation.DNS_ERROR_ZONE_CONFIGURATION_ERROR +Windows.Win32.Foundation.DNS_ERROR_ZONE_CREATION_FAILED +Windows.Win32.Foundation.DNS_ERROR_ZONE_DOES_NOT_EXIST +Windows.Win32.Foundation.DNS_ERROR_ZONE_HAS_NO_NS_RECORDS +Windows.Win32.Foundation.DNS_ERROR_ZONE_HAS_NO_SOA_RECORD +Windows.Win32.Foundation.DNS_ERROR_ZONE_IS_SHUTDOWN +Windows.Win32.Foundation.DNS_ERROR_ZONE_LOCKED +Windows.Win32.Foundation.DNS_ERROR_ZONE_LOCKED_FOR_SIGNING +Windows.Win32.Foundation.DNS_ERROR_ZONE_NOT_SECONDARY +Windows.Win32.Foundation.DNS_ERROR_ZONE_REQUIRES_MASTER_IP +Windows.Win32.Foundation.DNS_ERROR_ZONESCOPE_ALREADY_EXISTS +Windows.Win32.Foundation.DNS_ERROR_ZONESCOPE_DOES_NOT_EXIST +Windows.Win32.Foundation.DNS_ERROR_ZONESCOPE_FILE_WRITEBACK_FAILED +Windows.Win32.Foundation.DNS_ERROR_ZONESCOPE_IS_REFERENCED +Windows.Win32.Foundation.DUPLICATE_CLOSE_SOURCE +Windows.Win32.Foundation.DUPLICATE_HANDLE_OPTIONS +Windows.Win32.Foundation.DUPLICATE_SAME_ACCESS +Windows.Win32.Foundation.DuplicateHandle +Windows.Win32.Foundation.E_NOTIMPL +Windows.Win32.Foundation.ERROR_ABANDON_HIBERFILE +Windows.Win32.Foundation.ERROR_ABANDONED_WAIT_0 +Windows.Win32.Foundation.ERROR_ABANDONED_WAIT_63 +Windows.Win32.Foundation.ERROR_ABIOS_ERROR +Windows.Win32.Foundation.ERROR_ACCESS_AUDIT_BY_POLICY +Windows.Win32.Foundation.ERROR_ACCESS_DENIED +Windows.Win32.Foundation.ERROR_ACCESS_DENIED_APPDATA +Windows.Win32.Foundation.ERROR_ACCESS_DISABLED_BY_POLICY +Windows.Win32.Foundation.ERROR_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY +Windows.Win32.Foundation.ERROR_ACCESS_DISABLED_WEBBLADE +Windows.Win32.Foundation.ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER +Windows.Win32.Foundation.ERROR_ACCOUNT_DISABLED +Windows.Win32.Foundation.ERROR_ACCOUNT_EXPIRED +Windows.Win32.Foundation.ERROR_ACCOUNT_LOCKED_OUT +Windows.Win32.Foundation.ERROR_ACCOUNT_RESTRICTION +Windows.Win32.Foundation.ERROR_ACPI_ERROR +Windows.Win32.Foundation.ERROR_ACTIVE_CONNECTIONS +Windows.Win32.Foundation.ERROR_ADAP_HDW_ERR +Windows.Win32.Foundation.ERROR_ADDRESS_ALREADY_ASSOCIATED +Windows.Win32.Foundation.ERROR_ADDRESS_NOT_ASSOCIATED +Windows.Win32.Foundation.ERROR_ALERTED +Windows.Win32.Foundation.ERROR_ALIAS_EXISTS +Windows.Win32.Foundation.ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED +Windows.Win32.Foundation.ERROR_ALLOCATE_BUCKET +Windows.Win32.Foundation.ERROR_ALLOTTED_SPACE_EXCEEDED +Windows.Win32.Foundation.ERROR_ALREADY_ASSIGNED +Windows.Win32.Foundation.ERROR_ALREADY_EXISTS +Windows.Win32.Foundation.ERROR_ALREADY_FIBER +Windows.Win32.Foundation.ERROR_ALREADY_HAS_STREAM_ID +Windows.Win32.Foundation.ERROR_ALREADY_INITIALIZED +Windows.Win32.Foundation.ERROR_ALREADY_REGISTERED +Windows.Win32.Foundation.ERROR_ALREADY_RUNNING_LKG +Windows.Win32.Foundation.ERROR_ALREADY_THREAD +Windows.Win32.Foundation.ERROR_ALREADY_WAITING +Windows.Win32.Foundation.ERROR_ALREADY_WIN32 +Windows.Win32.Foundation.ERROR_API_UNAVAILABLE +Windows.Win32.Foundation.ERROR_APP_HANG +Windows.Win32.Foundation.ERROR_APP_INIT_FAILURE +Windows.Win32.Foundation.ERROR_APP_WRONG_OS +Windows.Win32.Foundation.ERROR_APPCONTAINER_REQUIRED +Windows.Win32.Foundation.ERROR_APPEXEC_APP_COMPAT_BLOCK +Windows.Win32.Foundation.ERROR_APPEXEC_CALLER_WAIT_TIMEOUT +Windows.Win32.Foundation.ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_LICENSING +Windows.Win32.Foundation.ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_RESOURCES +Windows.Win32.Foundation.ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_TERMINATION +Windows.Win32.Foundation.ERROR_APPEXEC_CONDITION_NOT_SATISFIED +Windows.Win32.Foundation.ERROR_APPEXEC_HANDLE_INVALIDATED +Windows.Win32.Foundation.ERROR_APPEXEC_HOST_ID_MISMATCH +Windows.Win32.Foundation.ERROR_APPEXEC_INVALID_HOST_GENERATION +Windows.Win32.Foundation.ERROR_APPEXEC_INVALID_HOST_STATE +Windows.Win32.Foundation.ERROR_APPEXEC_NO_DONOR +Windows.Win32.Foundation.ERROR_APPEXEC_UNEXPECTED_PROCESS_REGISTRATION +Windows.Win32.Foundation.ERROR_APPEXEC_UNKNOWN_USER +Windows.Win32.Foundation.ERROR_APPHELP_BLOCK +Windows.Win32.Foundation.ERROR_APPX_FILE_NOT_ENCRYPTED +Windows.Win32.Foundation.ERROR_ARBITRATION_UNHANDLED +Windows.Win32.Foundation.ERROR_ARENA_TRASHED +Windows.Win32.Foundation.ERROR_ARITHMETIC_OVERFLOW +Windows.Win32.Foundation.ERROR_ASSERTION_FAILURE +Windows.Win32.Foundation.ERROR_ATOMIC_LOCKS_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_AUDIT_FAILED +Windows.Win32.Foundation.ERROR_AUTHENTICATION_FIREWALL_FAILED +Windows.Win32.Foundation.ERROR_AUTHIP_FAILURE +Windows.Win32.Foundation.ERROR_AUTODATASEG_EXCEEDS_64k +Windows.Win32.Foundation.ERROR_BACKUP_CONTROLLER +Windows.Win32.Foundation.ERROR_BAD_ACCESSOR_FLAGS +Windows.Win32.Foundation.ERROR_BAD_ARGUMENTS +Windows.Win32.Foundation.ERROR_BAD_COMMAND +Windows.Win32.Foundation.ERROR_BAD_COMPRESSION_BUFFER +Windows.Win32.Foundation.ERROR_BAD_CONFIGURATION +Windows.Win32.Foundation.ERROR_BAD_CURRENT_DIRECTORY +Windows.Win32.Foundation.ERROR_BAD_DESCRIPTOR_FORMAT +Windows.Win32.Foundation.ERROR_BAD_DEV_TYPE +Windows.Win32.Foundation.ERROR_BAD_DEVICE +Windows.Win32.Foundation.ERROR_BAD_DEVICE_PATH +Windows.Win32.Foundation.ERROR_BAD_DLL_ENTRYPOINT +Windows.Win32.Foundation.ERROR_BAD_DRIVER_LEVEL +Windows.Win32.Foundation.ERROR_BAD_ENVIRONMENT +Windows.Win32.Foundation.ERROR_BAD_EXE_FORMAT +Windows.Win32.Foundation.ERROR_BAD_FILE_TYPE +Windows.Win32.Foundation.ERROR_BAD_FORMAT +Windows.Win32.Foundation.ERROR_BAD_FUNCTION_TABLE +Windows.Win32.Foundation.ERROR_BAD_IMPERSONATION_LEVEL +Windows.Win32.Foundation.ERROR_BAD_INHERITANCE_ACL +Windows.Win32.Foundation.ERROR_BAD_LENGTH +Windows.Win32.Foundation.ERROR_BAD_LOGON_SESSION_STATE +Windows.Win32.Foundation.ERROR_BAD_MCFG_TABLE +Windows.Win32.Foundation.ERROR_BAD_NET_NAME +Windows.Win32.Foundation.ERROR_BAD_NET_RESP +Windows.Win32.Foundation.ERROR_BAD_NETPATH +Windows.Win32.Foundation.ERROR_BAD_PATHNAME +Windows.Win32.Foundation.ERROR_BAD_PIPE +Windows.Win32.Foundation.ERROR_BAD_PROFILE +Windows.Win32.Foundation.ERROR_BAD_PROVIDER +Windows.Win32.Foundation.ERROR_BAD_QUERY_SYNTAX +Windows.Win32.Foundation.ERROR_BAD_RECOVERY_POLICY +Windows.Win32.Foundation.ERROR_BAD_REM_ADAP +Windows.Win32.Foundation.ERROR_BAD_SERVICE_ENTRYPOINT +Windows.Win32.Foundation.ERROR_BAD_STACK +Windows.Win32.Foundation.ERROR_BAD_THREADID_ADDR +Windows.Win32.Foundation.ERROR_BAD_TOKEN_TYPE +Windows.Win32.Foundation.ERROR_BAD_UNIT +Windows.Win32.Foundation.ERROR_BAD_USER_PROFILE +Windows.Win32.Foundation.ERROR_BAD_USERNAME +Windows.Win32.Foundation.ERROR_BAD_VALIDATION_CLASS +Windows.Win32.Foundation.ERROR_BADDB +Windows.Win32.Foundation.ERROR_BADKEY +Windows.Win32.Foundation.ERROR_BADSTARTPOSITION +Windows.Win32.Foundation.ERROR_BEGINNING_OF_MEDIA +Windows.Win32.Foundation.ERROR_BEYOND_VDL +Windows.Win32.Foundation.ERROR_BIOS_FAILED_TO_CONNECT_INTERRUPT +Windows.Win32.Foundation.ERROR_BLOCK_SHARED +Windows.Win32.Foundation.ERROR_BLOCK_SOURCE_WEAK_REFERENCE_INVALID +Windows.Win32.Foundation.ERROR_BLOCK_TARGET_WEAK_REFERENCE_INVALID +Windows.Win32.Foundation.ERROR_BLOCK_TOO_MANY_REFERENCES +Windows.Win32.Foundation.ERROR_BLOCK_WEAK_REFERENCE_INVALID +Windows.Win32.Foundation.ERROR_BLOCKED_BY_PARENTAL_CONTROLS +Windows.Win32.Foundation.ERROR_BOOT_ALREADY_ACCEPTED +Windows.Win32.Foundation.ERROR_BROKEN_PIPE +Windows.Win32.Foundation.ERROR_BUFFER_ALL_ZEROS +Windows.Win32.Foundation.ERROR_BUFFER_OVERFLOW +Windows.Win32.Foundation.ERROR_BUS_RESET +Windows.Win32.Foundation.ERROR_BUSY +Windows.Win32.Foundation.ERROR_BUSY_DRIVE +Windows.Win32.Foundation.ERROR_BYPASSIO_FLT_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_CACHE_PAGE_LOCKED +Windows.Win32.Foundation.ERROR_CALL_NOT_IMPLEMENTED +Windows.Win32.Foundation.ERROR_CALLBACK_INVOKE_INLINE +Windows.Win32.Foundation.ERROR_CALLBACK_POP_STACK +Windows.Win32.Foundation.ERROR_CALLBACK_SUPPLIED_INVALID_DATA +Windows.Win32.Foundation.ERROR_CAN_NOT_COMPLETE +Windows.Win32.Foundation.ERROR_CANCEL_VIOLATION +Windows.Win32.Foundation.ERROR_CANCELLED +Windows.Win32.Foundation.ERROR_CANNOT_BREAK_OPLOCK +Windows.Win32.Foundation.ERROR_CANNOT_COPY +Windows.Win32.Foundation.ERROR_CANNOT_DETECT_DRIVER_FAILURE +Windows.Win32.Foundation.ERROR_CANNOT_DETECT_PROCESS_ABORT +Windows.Win32.Foundation.ERROR_CANNOT_FIND_WND_CLASS +Windows.Win32.Foundation.ERROR_CANNOT_GRANT_REQUESTED_OPLOCK +Windows.Win32.Foundation.ERROR_CANNOT_IMPERSONATE +Windows.Win32.Foundation.ERROR_CANNOT_LOAD_REGISTRY_FILE +Windows.Win32.Foundation.ERROR_CANNOT_MAKE +Windows.Win32.Foundation.ERROR_CANNOT_OPEN_PROFILE +Windows.Win32.Foundation.ERROR_CANT_ACCESS_DOMAIN_INFO +Windows.Win32.Foundation.ERROR_CANT_ACCESS_FILE +Windows.Win32.Foundation.ERROR_CANT_CLEAR_ENCRYPTION_FLAG +Windows.Win32.Foundation.ERROR_CANT_DISABLE_MANDATORY +Windows.Win32.Foundation.ERROR_CANT_ENABLE_DENY_ONLY +Windows.Win32.Foundation.ERROR_CANT_OPEN_ANONYMOUS +Windows.Win32.Foundation.ERROR_CANT_RESOLVE_FILENAME +Windows.Win32.Foundation.ERROR_CANT_TERMINATE_SELF +Windows.Win32.Foundation.ERROR_CANT_WAIT +Windows.Win32.Foundation.ERROR_CANTFETCHBACKWARDS +Windows.Win32.Foundation.ERROR_CANTOPEN +Windows.Win32.Foundation.ERROR_CANTREAD +Windows.Win32.Foundation.ERROR_CANTSCROLLBACKWARDS +Windows.Win32.Foundation.ERROR_CANTWRITE +Windows.Win32.Foundation.ERROR_CAPAUTHZ_CHANGE_TYPE +Windows.Win32.Foundation.ERROR_CAPAUTHZ_DB_CORRUPTED +Windows.Win32.Foundation.ERROR_CAPAUTHZ_NO_POLICY +Windows.Win32.Foundation.ERROR_CAPAUTHZ_NOT_AUTHORIZED +Windows.Win32.Foundation.ERROR_CAPAUTHZ_NOT_DEVUNLOCKED +Windows.Win32.Foundation.ERROR_CAPAUTHZ_NOT_PROVISIONED +Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_DEV_MODE_REQUIRED +Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_INVALID_CATALOG +Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_NO_AUTH_ENTITY +Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_NO_CAPABILITY_MATCH +Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_PARSE_ERROR +Windows.Win32.Foundation.ERROR_CARDBUS_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_CASE_DIFFERING_NAMES_IN_DIR +Windows.Win32.Foundation.ERROR_CASE_SENSITIVE_PATH +Windows.Win32.Foundation.ERROR_CERTIFICATE_VALIDATION_PREFERENCE_CONFLICT +Windows.Win32.Foundation.ERROR_CHECKING_FILE_SYSTEM +Windows.Win32.Foundation.ERROR_CHECKOUT_REQUIRED +Windows.Win32.Foundation.ERROR_CHILD_MUST_BE_VOLATILE +Windows.Win32.Foundation.ERROR_CHILD_NOT_COMPLETE +Windows.Win32.Foundation.ERROR_CHILD_PROCESS_BLOCKED +Windows.Win32.Foundation.ERROR_CHILD_WINDOW_MENU +Windows.Win32.Foundation.ERROR_CIMFS_IMAGE_CORRUPT +Windows.Win32.Foundation.ERROR_CIMFS_IMAGE_VERSION_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_CIRCULAR_DEPENDENCY +Windows.Win32.Foundation.ERROR_CLASS_ALREADY_EXISTS +Windows.Win32.Foundation.ERROR_CLASS_DOES_NOT_EXIST +Windows.Win32.Foundation.ERROR_CLASS_HAS_WINDOWS +Windows.Win32.Foundation.ERROR_CLIENT_SERVER_PARAMETERS_INVALID +Windows.Win32.Foundation.ERROR_CLIPBOARD_NOT_OPEN +Windows.Win32.Foundation.ERROR_CLOUD_FILE_ACCESS_DENIED +Windows.Win32.Foundation.ERROR_CLOUD_FILE_ALREADY_CONNECTED +Windows.Win32.Foundation.ERROR_CLOUD_FILE_AUTHENTICATION_FAILED +Windows.Win32.Foundation.ERROR_CLOUD_FILE_CONNECTED_PROVIDER_ONLY +Windows.Win32.Foundation.ERROR_CLOUD_FILE_DEHYDRATION_DISALLOWED +Windows.Win32.Foundation.ERROR_CLOUD_FILE_IN_USE +Windows.Win32.Foundation.ERROR_CLOUD_FILE_INCOMPATIBLE_HARDLINKS +Windows.Win32.Foundation.ERROR_CLOUD_FILE_INSUFFICIENT_RESOURCES +Windows.Win32.Foundation.ERROR_CLOUD_FILE_INVALID_REQUEST +Windows.Win32.Foundation.ERROR_CLOUD_FILE_METADATA_CORRUPT +Windows.Win32.Foundation.ERROR_CLOUD_FILE_METADATA_TOO_LARGE +Windows.Win32.Foundation.ERROR_CLOUD_FILE_NETWORK_UNAVAILABLE +Windows.Win32.Foundation.ERROR_CLOUD_FILE_NOT_IN_SYNC +Windows.Win32.Foundation.ERROR_CLOUD_FILE_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_CLOUD_FILE_NOT_UNDER_SYNC_ROOT +Windows.Win32.Foundation.ERROR_CLOUD_FILE_PINNED +Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_BLOB_CHECKSUM_MISMATCH +Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_BLOB_TOO_LARGE +Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_CORRUPT +Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_LOCK_CONFLICT +Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_VERSION_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROVIDER_NOT_RUNNING +Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROVIDER_TERMINATED +Windows.Win32.Foundation.ERROR_CLOUD_FILE_READ_ONLY_VOLUME +Windows.Win32.Foundation.ERROR_CLOUD_FILE_REQUEST_ABORTED +Windows.Win32.Foundation.ERROR_CLOUD_FILE_REQUEST_CANCELED +Windows.Win32.Foundation.ERROR_CLOUD_FILE_REQUEST_TIMEOUT +Windows.Win32.Foundation.ERROR_CLOUD_FILE_SYNC_ROOT_METADATA_CORRUPT +Windows.Win32.Foundation.ERROR_CLOUD_FILE_TOO_MANY_PROPERTY_BLOBS +Windows.Win32.Foundation.ERROR_CLOUD_FILE_UNSUCCESSFUL +Windows.Win32.Foundation.ERROR_CLOUD_FILE_US_MESSAGE_TIMEOUT +Windows.Win32.Foundation.ERROR_CLOUD_FILE_VALIDATION_FAILED +Windows.Win32.Foundation.ERROR_COMMITMENT_LIMIT +Windows.Win32.Foundation.ERROR_COMMITMENT_MINIMUM +Windows.Win32.Foundation.ERROR_COMPRESSED_FILE_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_COMPRESSION_DISABLED +Windows.Win32.Foundation.ERROR_COMPRESSION_NOT_BENEFICIAL +Windows.Win32.Foundation.ERROR_CONNECTED_OTHER_PASSWORD +Windows.Win32.Foundation.ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT +Windows.Win32.Foundation.ERROR_CONNECTION_ABORTED +Windows.Win32.Foundation.ERROR_CONNECTION_ACTIVE +Windows.Win32.Foundation.ERROR_CONNECTION_COUNT_LIMIT +Windows.Win32.Foundation.ERROR_CONNECTION_INVALID +Windows.Win32.Foundation.ERROR_CONNECTION_REFUSED +Windows.Win32.Foundation.ERROR_CONNECTION_UNAVAIL +Windows.Win32.Foundation.ERROR_CONTAINER_ASSIGNED +Windows.Win32.Foundation.ERROR_CONTENT_BLOCKED +Windows.Win32.Foundation.ERROR_CONTEXT_EXPIRED +Windows.Win32.Foundation.ERROR_CONTINUE +Windows.Win32.Foundation.ERROR_CONTROL_C_EXIT +Windows.Win32.Foundation.ERROR_CONTROL_ID_NOT_FOUND +Windows.Win32.Foundation.ERROR_CONVERT_TO_LARGE +Windows.Win32.Foundation.ERROR_CORRUPT_LOG_CLEARED +Windows.Win32.Foundation.ERROR_CORRUPT_LOG_CORRUPTED +Windows.Win32.Foundation.ERROR_CORRUPT_LOG_DELETED_FULL +Windows.Win32.Foundation.ERROR_CORRUPT_LOG_OVERFULL +Windows.Win32.Foundation.ERROR_CORRUPT_LOG_UNAVAILABLE +Windows.Win32.Foundation.ERROR_CORRUPT_SYSTEM_FILE +Windows.Win32.Foundation.ERROR_COULD_NOT_INTERPRET +Windows.Win32.Foundation.ERROR_COUNTER_TIMEOUT +Windows.Win32.Foundation.ERROR_CPU_SET_INVALID +Windows.Win32.Foundation.ERROR_CRASH_DUMP +Windows.Win32.Foundation.ERROR_CRC +Windows.Win32.Foundation.ERROR_CREATE_FAILED +Windows.Win32.Foundation.ERROR_CROSS_PARTITION_VIOLATION +Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE +Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_FILE_NOT_CSE +Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_INVALID_SERVER_RESPONSE +Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_NEW_ENCRYPTED_FILE +Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_UNSUPPORTED_SERVER +Windows.Win32.Foundation.ERROR_CSCSHARE_OFFLINE +Windows.Win32.Foundation.ERROR_CTX_CLIENT_QUERY_TIMEOUT +Windows.Win32.Foundation.ERROR_CTX_MODEM_RESPONSE_TIMEOUT +Windows.Win32.Foundation.ERROR_CURRENT_DIRECTORY +Windows.Win32.Foundation.ERROR_CURRENT_DOMAIN_NOT_ALLOWED +Windows.Win32.Foundation.ERROR_DATA_CHECKSUM_ERROR +Windows.Win32.Foundation.ERROR_DATA_NOT_ACCEPTED +Windows.Win32.Foundation.ERROR_DATABASE_DOES_NOT_EXIST +Windows.Win32.Foundation.ERROR_DATATYPE_MISMATCH +Windows.Win32.Foundation.ERROR_DAX_MAPPING_EXISTS +Windows.Win32.Foundation.ERROR_DBG_COMMAND_EXCEPTION +Windows.Win32.Foundation.ERROR_DBG_CONTINUE +Windows.Win32.Foundation.ERROR_DBG_CONTROL_BREAK +Windows.Win32.Foundation.ERROR_DBG_CONTROL_C +Windows.Win32.Foundation.ERROR_DBG_EXCEPTION_HANDLED +Windows.Win32.Foundation.ERROR_DBG_EXCEPTION_NOT_HANDLED +Windows.Win32.Foundation.ERROR_DBG_PRINTEXCEPTION_C +Windows.Win32.Foundation.ERROR_DBG_REPLY_LATER +Windows.Win32.Foundation.ERROR_DBG_RIPEXCEPTION +Windows.Win32.Foundation.ERROR_DBG_TERMINATE_PROCESS +Windows.Win32.Foundation.ERROR_DBG_TERMINATE_THREAD +Windows.Win32.Foundation.ERROR_DBG_UNABLE_TO_PROVIDE_HANDLE +Windows.Win32.Foundation.ERROR_DC_NOT_FOUND +Windows.Win32.Foundation.ERROR_DDE_FAIL +Windows.Win32.Foundation.ERROR_DEBUG_ATTACH_FAILED +Windows.Win32.Foundation.ERROR_DEBUGGER_INACTIVE +Windows.Win32.Foundation.ERROR_DECRYPTION_FAILED +Windows.Win32.Foundation.ERROR_DELAY_LOAD_FAILED +Windows.Win32.Foundation.ERROR_DELETE_PENDING +Windows.Win32.Foundation.ERROR_DEPENDENT_SERVICES_RUNNING +Windows.Win32.Foundation.ERROR_DESTINATION_ELEMENT_FULL +Windows.Win32.Foundation.ERROR_DESTROY_OBJECT_OF_OTHER_THREAD +Windows.Win32.Foundation.ERROR_DEV_NOT_EXIST +Windows.Win32.Foundation.ERROR_DEVICE_ALREADY_ATTACHED +Windows.Win32.Foundation.ERROR_DEVICE_ALREADY_REMEMBERED +Windows.Win32.Foundation.ERROR_DEVICE_DOOR_OPEN +Windows.Win32.Foundation.ERROR_DEVICE_ENUMERATION_ERROR +Windows.Win32.Foundation.ERROR_DEVICE_FEATURE_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_DEVICE_HARDWARE_ERROR +Windows.Win32.Foundation.ERROR_DEVICE_HINT_NAME_BUFFER_TOO_SMALL +Windows.Win32.Foundation.ERROR_DEVICE_IN_MAINTENANCE +Windows.Win32.Foundation.ERROR_DEVICE_IN_USE +Windows.Win32.Foundation.ERROR_DEVICE_NO_RESOURCES +Windows.Win32.Foundation.ERROR_DEVICE_NOT_CONNECTED +Windows.Win32.Foundation.ERROR_DEVICE_NOT_PARTITIONED +Windows.Win32.Foundation.ERROR_DEVICE_REINITIALIZATION_NEEDED +Windows.Win32.Foundation.ERROR_DEVICE_REMOVED +Windows.Win32.Foundation.ERROR_DEVICE_REQUIRES_CLEANING +Windows.Win32.Foundation.ERROR_DEVICE_RESET_REQUIRED +Windows.Win32.Foundation.ERROR_DEVICE_SUPPORT_IN_PROGRESS +Windows.Win32.Foundation.ERROR_DEVICE_UNREACHABLE +Windows.Win32.Foundation.ERROR_DHCP_ADDRESS_CONFLICT +Windows.Win32.Foundation.ERROR_DIFFERENT_SERVICE_ACCOUNT +Windows.Win32.Foundation.ERROR_DIR_EFS_DISALLOWED +Windows.Win32.Foundation.ERROR_DIR_NOT_EMPTY +Windows.Win32.Foundation.ERROR_DIR_NOT_ROOT +Windows.Win32.Foundation.ERROR_DIRECT_ACCESS_HANDLE +Windows.Win32.Foundation.ERROR_DIRECTORY +Windows.Win32.Foundation.ERROR_DIRECTORY_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_DISCARDED +Windows.Win32.Foundation.ERROR_DISK_CHANGE +Windows.Win32.Foundation.ERROR_DISK_CORRUPT +Windows.Win32.Foundation.ERROR_DISK_FULL +Windows.Win32.Foundation.ERROR_DISK_OPERATION_FAILED +Windows.Win32.Foundation.ERROR_DISK_QUOTA_EXCEEDED +Windows.Win32.Foundation.ERROR_DISK_RECALIBRATE_FAILED +Windows.Win32.Foundation.ERROR_DISK_REPAIR_DISABLED +Windows.Win32.Foundation.ERROR_DISK_REPAIR_REDIRECTED +Windows.Win32.Foundation.ERROR_DISK_REPAIR_UNSUCCESSFUL +Windows.Win32.Foundation.ERROR_DISK_RESET_FAILED +Windows.Win32.Foundation.ERROR_DISK_RESOURCES_EXHAUSTED +Windows.Win32.Foundation.ERROR_DISK_TOO_FRAGMENTED +Windows.Win32.Foundation.ERROR_DLL_INIT_FAILED +Windows.Win32.Foundation.ERROR_DLL_INIT_FAILED_LOGOFF +Windows.Win32.Foundation.ERROR_DLL_MIGHT_BE_INCOMPATIBLE +Windows.Win32.Foundation.ERROR_DLL_MIGHT_BE_INSECURE +Windows.Win32.Foundation.ERROR_DLL_NOT_FOUND +Windows.Win32.Foundation.ERROR_DLP_POLICY_DENIES_OPERATION +Windows.Win32.Foundation.ERROR_DLP_POLICY_SILENTLY_FAIL +Windows.Win32.Foundation.ERROR_DLP_POLICY_WARNS_AGAINST_OPERATION +Windows.Win32.Foundation.ERROR_DOMAIN_CONTROLLER_EXISTS +Windows.Win32.Foundation.ERROR_DOMAIN_CONTROLLER_NOT_FOUND +Windows.Win32.Foundation.ERROR_DOMAIN_CTRLR_CONFIG_ERROR +Windows.Win32.Foundation.ERROR_DOMAIN_EXISTS +Windows.Win32.Foundation.ERROR_DOMAIN_LIMIT_EXCEEDED +Windows.Win32.Foundation.ERROR_DOMAIN_SID_SAME_AS_LOCAL_WORKSTATION +Windows.Win32.Foundation.ERROR_DOMAIN_TRUST_INCONSISTENT +Windows.Win32.Foundation.ERROR_DOWNGRADE_DETECTED +Windows.Win32.Foundation.ERROR_DPL_NOT_SUPPORTED_FOR_USER +Windows.Win32.Foundation.ERROR_DRIVE_LOCKED +Windows.Win32.Foundation.ERROR_DRIVER_BLOCKED +Windows.Win32.Foundation.ERROR_DRIVER_CANCEL_TIMEOUT +Windows.Win32.Foundation.ERROR_DRIVER_DATABASE_ERROR +Windows.Win32.Foundation.ERROR_DRIVER_FAILED_PRIOR_UNLOAD +Windows.Win32.Foundation.ERROR_DRIVER_FAILED_SLEEP +Windows.Win32.Foundation.ERROR_DRIVER_PROCESS_TERMINATED +Windows.Win32.Foundation.ERROR_DRIVERS_LEAKING_LOCKED_PAGES +Windows.Win32.Foundation.ERROR_DS_ADD_REPLICA_INHIBITED +Windows.Win32.Foundation.ERROR_DS_ADMIN_LIMIT_EXCEEDED +Windows.Win32.Foundation.ERROR_DS_AFFECTS_MULTIPLE_DSAS +Windows.Win32.Foundation.ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER +Windows.Win32.Foundation.ERROR_DS_ALIAS_DEREF_PROBLEM +Windows.Win32.Foundation.ERROR_DS_ALIAS_POINTS_TO_ALIAS +Windows.Win32.Foundation.ERROR_DS_ALIAS_PROBLEM +Windows.Win32.Foundation.ERROR_DS_ALIASED_OBJ_MISSING +Windows.Win32.Foundation.ERROR_DS_ATT_ALREADY_EXISTS +Windows.Win32.Foundation.ERROR_DS_ATT_IS_NOT_ON_OBJ +Windows.Win32.Foundation.ERROR_DS_ATT_NOT_DEF_FOR_CLASS +Windows.Win32.Foundation.ERROR_DS_ATT_NOT_DEF_IN_SCHEMA +Windows.Win32.Foundation.ERROR_DS_ATT_SCHEMA_REQ_ID +Windows.Win32.Foundation.ERROR_DS_ATT_SCHEMA_REQ_SYNTAX +Windows.Win32.Foundation.ERROR_DS_ATT_VAL_ALREADY_EXISTS +Windows.Win32.Foundation.ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS +Windows.Win32.Foundation.ERROR_DS_ATTRIBUTE_OWNED_BY_SAM +Windows.Win32.Foundation.ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED +Windows.Win32.Foundation.ERROR_DS_AUDIT_FAILURE +Windows.Win32.Foundation.ERROR_DS_AUTH_METHOD_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_DS_AUTH_UNKNOWN +Windows.Win32.Foundation.ERROR_DS_AUTHORIZATION_FAILED +Windows.Win32.Foundation.ERROR_DS_AUX_CLS_TEST_FAIL +Windows.Win32.Foundation.ERROR_DS_BACKLINK_WITHOUT_LINK +Windows.Win32.Foundation.ERROR_DS_BAD_ATT_SCHEMA_SYNTAX +Windows.Win32.Foundation.ERROR_DS_BAD_HIERARCHY_FILE +Windows.Win32.Foundation.ERROR_DS_BAD_INSTANCE_TYPE +Windows.Win32.Foundation.ERROR_DS_BAD_NAME_SYNTAX +Windows.Win32.Foundation.ERROR_DS_BAD_RDN_ATT_ID_SYNTAX +Windows.Win32.Foundation.ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED +Windows.Win32.Foundation.ERROR_DS_BUSY +Windows.Win32.Foundation.ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD +Windows.Win32.Foundation.ERROR_DS_CANT_ADD_ATT_VALUES +Windows.Win32.Foundation.ERROR_DS_CANT_ADD_SYSTEM_ONLY +Windows.Win32.Foundation.ERROR_DS_CANT_ADD_TO_GC +Windows.Win32.Foundation.ERROR_DS_CANT_CACHE_ATT +Windows.Win32.Foundation.ERROR_DS_CANT_CACHE_CLASS +Windows.Win32.Foundation.ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC +Windows.Win32.Foundation.ERROR_DS_CANT_CREATE_UNDER_SCHEMA +Windows.Win32.Foundation.ERROR_DS_CANT_DEL_MASTER_CROSSREF +Windows.Win32.Foundation.ERROR_DS_CANT_DELETE +Windows.Win32.Foundation.ERROR_DS_CANT_DELETE_DSA_OBJ +Windows.Win32.Foundation.ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC +Windows.Win32.Foundation.ERROR_DS_CANT_DEREF_ALIAS +Windows.Win32.Foundation.ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN +Windows.Win32.Foundation.ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF +Windows.Win32.Foundation.ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN +Windows.Win32.Foundation.ERROR_DS_CANT_FIND_DSA_OBJ +Windows.Win32.Foundation.ERROR_DS_CANT_FIND_EXPECTED_NC +Windows.Win32.Foundation.ERROR_DS_CANT_FIND_NC_IN_CACHE +Windows.Win32.Foundation.ERROR_DS_CANT_MIX_MASTER_AND_REPS +Windows.Win32.Foundation.ERROR_DS_CANT_MOD_OBJ_CLASS +Windows.Win32.Foundation.ERROR_DS_CANT_MOD_PRIMARYGROUPID +Windows.Win32.Foundation.ERROR_DS_CANT_MOD_SYSTEM_ONLY +Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_ACCOUNT_GROUP +Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_APP_BASIC_GROUP +Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_APP_QUERY_GROUP +Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_DELETED_OBJECT +Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_RESOURCE_GROUP +Windows.Win32.Foundation.ERROR_DS_CANT_ON_NON_LEAF +Windows.Win32.Foundation.ERROR_DS_CANT_ON_RDN +Windows.Win32.Foundation.ERROR_DS_CANT_REM_MISSING_ATT +Windows.Win32.Foundation.ERROR_DS_CANT_REM_MISSING_ATT_VAL +Windows.Win32.Foundation.ERROR_DS_CANT_REMOVE_ATT_CACHE +Windows.Win32.Foundation.ERROR_DS_CANT_REMOVE_CLASS_CACHE +Windows.Win32.Foundation.ERROR_DS_CANT_REPLACE_HIDDEN_REC +Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_ATTS +Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_CHILD +Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_DN +Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_INSTANCE +Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_SD +Windows.Win32.Foundation.ERROR_DS_CANT_START +Windows.Win32.Foundation.ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ +Windows.Win32.Foundation.ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS +Windows.Win32.Foundation.ERROR_DS_CHILDREN_EXIST +Windows.Win32.Foundation.ERROR_DS_CLASS_MUST_BE_CONCRETE +Windows.Win32.Foundation.ERROR_DS_CLASS_NOT_DSA +Windows.Win32.Foundation.ERROR_DS_CLIENT_LOOP +Windows.Win32.Foundation.ERROR_DS_CODE_INCONSISTENCY +Windows.Win32.Foundation.ERROR_DS_COMPARE_FALSE +Windows.Win32.Foundation.ERROR_DS_COMPARE_TRUE +Windows.Win32.Foundation.ERROR_DS_CONFIDENTIALITY_REQUIRED +Windows.Win32.Foundation.ERROR_DS_CONFIG_PARAM_MISSING +Windows.Win32.Foundation.ERROR_DS_CONSTRAINT_VIOLATION +Windows.Win32.Foundation.ERROR_DS_CONSTRUCTED_ATT_MOD +Windows.Win32.Foundation.ERROR_DS_CONTROL_NOT_FOUND +Windows.Win32.Foundation.ERROR_DS_COULDNT_CONTACT_FSMO +Windows.Win32.Foundation.ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE +Windows.Win32.Foundation.ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE +Windows.Win32.Foundation.ERROR_DS_COULDNT_UPDATE_SPNS +Windows.Win32.Foundation.ERROR_DS_COUNTING_AB_INDICES_FAILED +Windows.Win32.Foundation.ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE +Windows.Win32.Foundation.ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2 +Windows.Win32.Foundation.ERROR_DS_CROSS_DOM_MOVE_ERROR +Windows.Win32.Foundation.ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD +Windows.Win32.Foundation.ERROR_DS_CROSS_NC_DN_RENAME +Windows.Win32.Foundation.ERROR_DS_CROSS_REF_BUSY +Windows.Win32.Foundation.ERROR_DS_CROSS_REF_EXISTS +Windows.Win32.Foundation.ERROR_DS_DATABASE_ERROR +Windows.Win32.Foundation.ERROR_DS_DECODING_ERROR +Windows.Win32.Foundation.ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED +Windows.Win32.Foundation.ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST +Windows.Win32.Foundation.ERROR_DS_DIFFERENT_REPL_EPOCHS +Windows.Win32.Foundation.ERROR_DS_DISALLOWED_IN_SYSTEM_CONTAINER +Windows.Win32.Foundation.ERROR_DS_DISALLOWED_NC_REDIRECT +Windows.Win32.Foundation.ERROR_DS_DNS_LOOKUP_FAILURE +Windows.Win32.Foundation.ERROR_DS_DOMAIN_NAME_EXISTS_IN_FOREST +Windows.Win32.Foundation.ERROR_DS_DOMAIN_RENAME_IN_PROGRESS +Windows.Win32.Foundation.ERROR_DS_DOMAIN_VERSION_TOO_HIGH +Windows.Win32.Foundation.ERROR_DS_DOMAIN_VERSION_TOO_LOW +Windows.Win32.Foundation.ERROR_DS_DRA_ABANDON_SYNC +Windows.Win32.Foundation.ERROR_DS_DRA_ACCESS_DENIED +Windows.Win32.Foundation.ERROR_DS_DRA_BAD_DN +Windows.Win32.Foundation.ERROR_DS_DRA_BAD_INSTANCE_TYPE +Windows.Win32.Foundation.ERROR_DS_DRA_BAD_NC +Windows.Win32.Foundation.ERROR_DS_DRA_BUSY +Windows.Win32.Foundation.ERROR_DS_DRA_CONNECTION_FAILED +Windows.Win32.Foundation.ERROR_DS_DRA_CORRUPT_UTD_VECTOR +Windows.Win32.Foundation.ERROR_DS_DRA_DB_ERROR +Windows.Win32.Foundation.ERROR_DS_DRA_DN_EXISTS +Windows.Win32.Foundation.ERROR_DS_DRA_EARLIER_SCHEMA_CONFLICT +Windows.Win32.Foundation.ERROR_DS_DRA_EXTN_CONNECTION_FAILED +Windows.Win32.Foundation.ERROR_DS_DRA_GENERIC +Windows.Win32.Foundation.ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET +Windows.Win32.Foundation.ERROR_DS_DRA_INCONSISTENT_DIT +Windows.Win32.Foundation.ERROR_DS_DRA_INTERNAL_ERROR +Windows.Win32.Foundation.ERROR_DS_DRA_INVALID_PARAMETER +Windows.Win32.Foundation.ERROR_DS_DRA_MAIL_PROBLEM +Windows.Win32.Foundation.ERROR_DS_DRA_MISSING_KRBTGT_SECRET +Windows.Win32.Foundation.ERROR_DS_DRA_MISSING_PARENT +Windows.Win32.Foundation.ERROR_DS_DRA_NAME_COLLISION +Windows.Win32.Foundation.ERROR_DS_DRA_NO_REPLICA +Windows.Win32.Foundation.ERROR_DS_DRA_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_DS_DRA_OBJ_IS_REP_SOURCE +Windows.Win32.Foundation.ERROR_DS_DRA_OBJ_NC_MISMATCH +Windows.Win32.Foundation.ERROR_DS_DRA_OUT_OF_MEM +Windows.Win32.Foundation.ERROR_DS_DRA_OUT_SCHEDULE_WINDOW +Windows.Win32.Foundation.ERROR_DS_DRA_PREEMPTED +Windows.Win32.Foundation.ERROR_DS_DRA_RECYCLED_TARGET +Windows.Win32.Foundation.ERROR_DS_DRA_REF_ALREADY_EXISTS +Windows.Win32.Foundation.ERROR_DS_DRA_REF_NOT_FOUND +Windows.Win32.Foundation.ERROR_DS_DRA_REPL_PENDING +Windows.Win32.Foundation.ERROR_DS_DRA_RPC_CANCELLED +Windows.Win32.Foundation.ERROR_DS_DRA_SCHEMA_CONFLICT +Windows.Win32.Foundation.ERROR_DS_DRA_SCHEMA_INFO_SHIP +Windows.Win32.Foundation.ERROR_DS_DRA_SCHEMA_MISMATCH +Windows.Win32.Foundation.ERROR_DS_DRA_SECRETS_DENIED +Windows.Win32.Foundation.ERROR_DS_DRA_SHUTDOWN +Windows.Win32.Foundation.ERROR_DS_DRA_SINK_DISABLED +Windows.Win32.Foundation.ERROR_DS_DRA_SOURCE_DISABLED +Windows.Win32.Foundation.ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA +Windows.Win32.Foundation.ERROR_DS_DRA_SOURCE_REINSTALLED +Windows.Win32.Foundation.ERROR_DS_DRS_EXTENSIONS_CHANGED +Windows.Win32.Foundation.ERROR_DS_DS_REQUIRED +Windows.Win32.Foundation.ERROR_DS_DSA_MUST_BE_INT_MASTER +Windows.Win32.Foundation.ERROR_DS_DST_DOMAIN_NOT_NATIVE +Windows.Win32.Foundation.ERROR_DS_DST_NC_MISMATCH +Windows.Win32.Foundation.ERROR_DS_DUP_LDAP_DISPLAY_NAME +Windows.Win32.Foundation.ERROR_DS_DUP_LINK_ID +Windows.Win32.Foundation.ERROR_DS_DUP_MAPI_ID +Windows.Win32.Foundation.ERROR_DS_DUP_MSDS_INTID +Windows.Win32.Foundation.ERROR_DS_DUP_OID +Windows.Win32.Foundation.ERROR_DS_DUP_RDN +Windows.Win32.Foundation.ERROR_DS_DUP_SCHEMA_ID_GUID +Windows.Win32.Foundation.ERROR_DS_DUPLICATE_ID_FOUND +Windows.Win32.Foundation.ERROR_DS_ENCODING_ERROR +Windows.Win32.Foundation.ERROR_DS_EPOCH_MISMATCH +Windows.Win32.Foundation.ERROR_DS_EXISTING_AD_CHILD_NC +Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_AUX_CLS +Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_MAY_HAVE +Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_MUST_HAVE +Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_POSS_SUP +Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_RDNATTID +Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_SUB_CLS +Windows.Win32.Foundation.ERROR_DS_FILTER_UNKNOWN +Windows.Win32.Foundation.ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS +Windows.Win32.Foundation.ERROR_DS_FLAT_NAME_EXISTS_IN_FOREST +Windows.Win32.Foundation.ERROR_DS_FOREST_VERSION_TOO_HIGH +Windows.Win32.Foundation.ERROR_DS_FOREST_VERSION_TOO_LOW +Windows.Win32.Foundation.ERROR_DS_GC_NOT_AVAILABLE +Windows.Win32.Foundation.ERROR_DS_GC_REQUIRED +Windows.Win32.Foundation.ERROR_DS_GCVERIFY_ERROR +Windows.Win32.Foundation.ERROR_DS_GENERIC_ERROR +Windows.Win32.Foundation.ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER +Windows.Win32.Foundation.ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER +Windows.Win32.Foundation.ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER +Windows.Win32.Foundation.ERROR_DS_GOVERNSID_MISSING +Windows.Win32.Foundation.ERROR_DS_GROUP_CONVERSION_ERROR +Windows.Win32.Foundation.ERROR_DS_HAVE_PRIMARY_MEMBERS +Windows.Win32.Foundation.ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED +Windows.Win32.Foundation.ERROR_DS_HIERARCHY_TABLE_TOO_DEEP +Windows.Win32.Foundation.ERROR_DS_HIGH_ADLDS_FFL +Windows.Win32.Foundation.ERROR_DS_HIGH_DSA_VERSION +Windows.Win32.Foundation.ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD +Windows.Win32.Foundation.ERROR_DS_ILLEGAL_MOD_OPERATION +Windows.Win32.Foundation.ERROR_DS_ILLEGAL_SUPERIOR +Windows.Win32.Foundation.ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION +Windows.Win32.Foundation.ERROR_DS_INAPPROPRIATE_AUTH +Windows.Win32.Foundation.ERROR_DS_INAPPROPRIATE_MATCHING +Windows.Win32.Foundation.ERROR_DS_INCOMPATIBLE_CONTROLS_USED +Windows.Win32.Foundation.ERROR_DS_INCOMPATIBLE_VERSION +Windows.Win32.Foundation.ERROR_DS_INCORRECT_ROLE_OWNER +Windows.Win32.Foundation.ERROR_DS_INIT_FAILURE +Windows.Win32.Foundation.ERROR_DS_INIT_FAILURE_CONSOLE +Windows.Win32.Foundation.ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE +Windows.Win32.Foundation.ERROR_DS_INSTALL_NO_SRC_SCH_VERSION +Windows.Win32.Foundation.ERROR_DS_INSTALL_SCHEMA_MISMATCH +Windows.Win32.Foundation.ERROR_DS_INSUFF_ACCESS_RIGHTS +Windows.Win32.Foundation.ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT +Windows.Win32.Foundation.ERROR_DS_INTERNAL_FAILURE +Windows.Win32.Foundation.ERROR_DS_INVALID_ATTRIBUTE_SYNTAX +Windows.Win32.Foundation.ERROR_DS_INVALID_DMD +Windows.Win32.Foundation.ERROR_DS_INVALID_DN_SYNTAX +Windows.Win32.Foundation.ERROR_DS_INVALID_GROUP_TYPE +Windows.Win32.Foundation.ERROR_DS_INVALID_LDAP_DISPLAY_NAME +Windows.Win32.Foundation.ERROR_DS_INVALID_NAME_FOR_SPN +Windows.Win32.Foundation.ERROR_DS_INVALID_ROLE_OWNER +Windows.Win32.Foundation.ERROR_DS_INVALID_SCRIPT +Windows.Win32.Foundation.ERROR_DS_INVALID_SEARCH_FLAG +Windows.Win32.Foundation.ERROR_DS_INVALID_SEARCH_FLAG_SUBTREE +Windows.Win32.Foundation.ERROR_DS_INVALID_SEARCH_FLAG_TUPLE +Windows.Win32.Foundation.ERROR_DS_IS_LEAF +Windows.Win32.Foundation.ERROR_DS_KEY_NOT_UNIQUE +Windows.Win32.Foundation.ERROR_DS_LDAP_SEND_QUEUE_FULL +Windows.Win32.Foundation.ERROR_DS_LINK_ID_NOT_AVAILABLE +Windows.Win32.Foundation.ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER +Windows.Win32.Foundation.ERROR_DS_LOCAL_ERROR +Windows.Win32.Foundation.ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY +Windows.Win32.Foundation.ERROR_DS_LOOP_DETECT +Windows.Win32.Foundation.ERROR_DS_LOW_ADLDS_FFL +Windows.Win32.Foundation.ERROR_DS_LOW_DSA_VERSION +Windows.Win32.Foundation.ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4 +Windows.Win32.Foundation.ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED +Windows.Win32.Foundation.ERROR_DS_MAPI_ID_NOT_AVAILABLE +Windows.Win32.Foundation.ERROR_DS_MASTERDSA_REQUIRED +Windows.Win32.Foundation.ERROR_DS_MAX_OBJ_SIZE_EXCEEDED +Windows.Win32.Foundation.ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY +Windows.Win32.Foundation.ERROR_DS_MISSING_EXPECTED_ATT +Windows.Win32.Foundation.ERROR_DS_MISSING_FOREST_TRUST +Windows.Win32.Foundation.ERROR_DS_MISSING_FSMO_SETTINGS +Windows.Win32.Foundation.ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER +Windows.Win32.Foundation.ERROR_DS_MISSING_REQUIRED_ATT +Windows.Win32.Foundation.ERROR_DS_MISSING_SUPREF +Windows.Win32.Foundation.ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG +Windows.Win32.Foundation.ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE +Windows.Win32.Foundation.ERROR_DS_MODIFYDN_WRONG_GRANDPARENT +Windows.Win32.Foundation.ERROR_DS_MUST_BE_RUN_ON_DST_DC +Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_DOMAIN_ONLY +Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_NO_MAPPING +Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING +Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_NOT_FOUND +Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_NOT_UNIQUE +Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_RESOLVING +Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_TRUST_REFERRAL +Windows.Win32.Foundation.ERROR_DS_NAME_NOT_UNIQUE +Windows.Win32.Foundation.ERROR_DS_NAME_REFERENCE_INVALID +Windows.Win32.Foundation.ERROR_DS_NAME_TOO_LONG +Windows.Win32.Foundation.ERROR_DS_NAME_TOO_MANY_PARTS +Windows.Win32.Foundation.ERROR_DS_NAME_TYPE_UNKNOWN +Windows.Win32.Foundation.ERROR_DS_NAME_UNPARSEABLE +Windows.Win32.Foundation.ERROR_DS_NAME_VALUE_TOO_LONG +Windows.Win32.Foundation.ERROR_DS_NAMING_MASTER_GC +Windows.Win32.Foundation.ERROR_DS_NAMING_VIOLATION +Windows.Win32.Foundation.ERROR_DS_NC_MUST_HAVE_NC_PARENT +Windows.Win32.Foundation.ERROR_DS_NC_STILL_HAS_DSAS +Windows.Win32.Foundation.ERROR_DS_NCNAME_MISSING_CR_REF +Windows.Win32.Foundation.ERROR_DS_NCNAME_MUST_BE_NC +Windows.Win32.Foundation.ERROR_DS_NO_ATTRIBUTE_OR_VALUE +Windows.Win32.Foundation.ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN +Windows.Win32.Foundation.ERROR_DS_NO_CHAINED_EVAL +Windows.Win32.Foundation.ERROR_DS_NO_CHAINING +Windows.Win32.Foundation.ERROR_DS_NO_CHECKPOINT_WITH_PDC +Windows.Win32.Foundation.ERROR_DS_NO_CROSSREF_FOR_NC +Windows.Win32.Foundation.ERROR_DS_NO_DELETED_NAME +Windows.Win32.Foundation.ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS +Windows.Win32.Foundation.ERROR_DS_NO_MORE_RIDS +Windows.Win32.Foundation.ERROR_DS_NO_MSDS_INTID +Windows.Win32.Foundation.ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN +Windows.Win32.Foundation.ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN +Windows.Win32.Foundation.ERROR_DS_NO_NTDSA_OBJECT +Windows.Win32.Foundation.ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC +Windows.Win32.Foundation.ERROR_DS_NO_PARENT_OBJECT +Windows.Win32.Foundation.ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION +Windows.Win32.Foundation.ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA +Windows.Win32.Foundation.ERROR_DS_NO_REF_DOMAIN +Windows.Win32.Foundation.ERROR_DS_NO_REQUESTED_ATTS_FOUND +Windows.Win32.Foundation.ERROR_DS_NO_RESULTS_RETURNED +Windows.Win32.Foundation.ERROR_DS_NO_RIDS_ALLOCATED +Windows.Win32.Foundation.ERROR_DS_NO_SERVER_OBJECT +Windows.Win32.Foundation.ERROR_DS_NO_SUCH_OBJECT +Windows.Win32.Foundation.ERROR_DS_NO_TREE_DELETE_ABOVE_NC +Windows.Win32.Foundation.ERROR_DS_NON_ASQ_SEARCH +Windows.Win32.Foundation.ERROR_DS_NON_BASE_SEARCH +Windows.Win32.Foundation.ERROR_DS_NONEXISTENT_MAY_HAVE +Windows.Win32.Foundation.ERROR_DS_NONEXISTENT_MUST_HAVE +Windows.Win32.Foundation.ERROR_DS_NONEXISTENT_POSS_SUP +Windows.Win32.Foundation.ERROR_DS_NONSAFE_SCHEMA_CHANGE +Windows.Win32.Foundation.ERROR_DS_NOT_AN_OBJECT +Windows.Win32.Foundation.ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC +Windows.Win32.Foundation.ERROR_DS_NOT_CLOSEST +Windows.Win32.Foundation.ERROR_DS_NOT_INSTALLED +Windows.Win32.Foundation.ERROR_DS_NOT_ON_BACKLINK +Windows.Win32.Foundation.ERROR_DS_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_DS_NOT_SUPPORTED_SORT_ORDER +Windows.Win32.Foundation.ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX +Windows.Win32.Foundation.ERROR_DS_NTDSCRIPT_PROCESS_ERROR +Windows.Win32.Foundation.ERROR_DS_NTDSCRIPT_SYNTAX_ERROR +Windows.Win32.Foundation.ERROR_DS_OBJ_CLASS_NOT_DEFINED +Windows.Win32.Foundation.ERROR_DS_OBJ_CLASS_NOT_SUBCLASS +Windows.Win32.Foundation.ERROR_DS_OBJ_CLASS_VIOLATION +Windows.Win32.Foundation.ERROR_DS_OBJ_GUID_EXISTS +Windows.Win32.Foundation.ERROR_DS_OBJ_NOT_FOUND +Windows.Win32.Foundation.ERROR_DS_OBJ_STRING_NAME_EXISTS +Windows.Win32.Foundation.ERROR_DS_OBJ_TOO_LARGE +Windows.Win32.Foundation.ERROR_DS_OBJECT_BEING_REMOVED +Windows.Win32.Foundation.ERROR_DS_OBJECT_CLASS_REQUIRED +Windows.Win32.Foundation.ERROR_DS_OBJECT_RESULTS_TOO_LARGE +Windows.Win32.Foundation.ERROR_DS_OFFSET_RANGE_ERROR +Windows.Win32.Foundation.ERROR_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS +Windows.Win32.Foundation.ERROR_DS_OID_NOT_FOUND +Windows.Win32.Foundation.ERROR_DS_OPERATIONS_ERROR +Windows.Win32.Foundation.ERROR_DS_OUT_OF_SCOPE +Windows.Win32.Foundation.ERROR_DS_OUT_OF_VERSION_STORE +Windows.Win32.Foundation.ERROR_DS_PARAM_ERROR +Windows.Win32.Foundation.ERROR_DS_PARENT_IS_AN_ALIAS +Windows.Win32.Foundation.ERROR_DS_PDC_OPERATION_IN_PROGRESS +Windows.Win32.Foundation.ERROR_DS_PER_ATTRIBUTE_AUTHZ_FAILED_DURING_ADD +Windows.Win32.Foundation.ERROR_DS_POLICY_NOT_KNOWN +Windows.Win32.Foundation.ERROR_DS_PROTOCOL_ERROR +Windows.Win32.Foundation.ERROR_DS_RANGE_CONSTRAINT +Windows.Win32.Foundation.ERROR_DS_RDN_DOESNT_MATCH_SCHEMA +Windows.Win32.Foundation.ERROR_DS_RECALCSCHEMA_FAILED +Windows.Win32.Foundation.ERROR_DS_REFERRAL +Windows.Win32.Foundation.ERROR_DS_REFERRAL_LIMIT_EXCEEDED +Windows.Win32.Foundation.ERROR_DS_REFUSING_FSMO_ROLES +Windows.Win32.Foundation.ERROR_DS_REMOTE_CROSSREF_OP_FAILED +Windows.Win32.Foundation.ERROR_DS_REPL_LIFETIME_EXCEEDED +Windows.Win32.Foundation.ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR +Windows.Win32.Foundation.ERROR_DS_REPLICATOR_ONLY +Windows.Win32.Foundation.ERROR_DS_RESERVED_LINK_ID +Windows.Win32.Foundation.ERROR_DS_RESERVED_MAPI_ID +Windows.Win32.Foundation.ERROR_DS_RIDMGR_DISABLED +Windows.Win32.Foundation.ERROR_DS_RIDMGR_INIT_ERROR +Windows.Win32.Foundation.ERROR_DS_ROLE_NOT_VERIFIED +Windows.Win32.Foundation.ERROR_DS_ROOT_CANT_BE_SUBREF +Windows.Win32.Foundation.ERROR_DS_ROOT_MUST_BE_NC +Windows.Win32.Foundation.ERROR_DS_ROOT_REQUIRES_CLASS_TOP +Windows.Win32.Foundation.ERROR_DS_SAM_INIT_FAILURE +Windows.Win32.Foundation.ERROR_DS_SAM_INIT_FAILURE_CONSOLE +Windows.Win32.Foundation.ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY +Windows.Win32.Foundation.ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD +Windows.Win32.Foundation.ERROR_DS_SCHEMA_ALLOC_FAILED +Windows.Win32.Foundation.ERROR_DS_SCHEMA_NOT_LOADED +Windows.Win32.Foundation.ERROR_DS_SCHEMA_UPDATE_DISALLOWED +Windows.Win32.Foundation.ERROR_DS_SEC_DESC_INVALID +Windows.Win32.Foundation.ERROR_DS_SEC_DESC_TOO_SHORT +Windows.Win32.Foundation.ERROR_DS_SECURITY_CHECKING_ERROR +Windows.Win32.Foundation.ERROR_DS_SECURITY_ILLEGAL_MODIFY +Windows.Win32.Foundation.ERROR_DS_SEMANTIC_ATT_TEST +Windows.Win32.Foundation.ERROR_DS_SENSITIVE_GROUP_VIOLATION +Windows.Win32.Foundation.ERROR_DS_SERVER_DOWN +Windows.Win32.Foundation.ERROR_DS_SHUTTING_DOWN +Windows.Win32.Foundation.ERROR_DS_SINGLE_USER_MODE_FAILED +Windows.Win32.Foundation.ERROR_DS_SINGLE_VALUE_CONSTRAINT +Windows.Win32.Foundation.ERROR_DS_SIZELIMIT_EXCEEDED +Windows.Win32.Foundation.ERROR_DS_SORT_CONTROL_MISSING +Windows.Win32.Foundation.ERROR_DS_SOURCE_AUDITING_NOT_ENABLED +Windows.Win32.Foundation.ERROR_DS_SOURCE_DOMAIN_IN_FOREST +Windows.Win32.Foundation.ERROR_DS_SPN_VALUE_NOT_UNIQUE_IN_FOREST +Windows.Win32.Foundation.ERROR_DS_SRC_AND_DST_NC_IDENTICAL +Windows.Win32.Foundation.ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH +Windows.Win32.Foundation.ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER +Windows.Win32.Foundation.ERROR_DS_SRC_GUID_MISMATCH +Windows.Win32.Foundation.ERROR_DS_SRC_NAME_MISMATCH +Windows.Win32.Foundation.ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER +Windows.Win32.Foundation.ERROR_DS_SRC_SID_EXISTS_IN_FOREST +Windows.Win32.Foundation.ERROR_DS_STRING_SD_CONVERSION_FAILED +Windows.Win32.Foundation.ERROR_DS_STRONG_AUTH_REQUIRED +Windows.Win32.Foundation.ERROR_DS_SUB_CLS_TEST_FAIL +Windows.Win32.Foundation.ERROR_DS_SUBREF_MUST_HAVE_PARENT +Windows.Win32.Foundation.ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD +Windows.Win32.Foundation.ERROR_DS_SYNTAX_MISMATCH +Windows.Win32.Foundation.ERROR_DS_THREAD_LIMIT_EXCEEDED +Windows.Win32.Foundation.ERROR_DS_TIMELIMIT_EXCEEDED +Windows.Win32.Foundation.ERROR_DS_TREE_DELETE_NOT_FINISHED +Windows.Win32.Foundation.ERROR_DS_UNABLE_TO_SURRENDER_ROLES +Windows.Win32.Foundation.ERROR_DS_UNAVAILABLE +Windows.Win32.Foundation.ERROR_DS_UNAVAILABLE_CRIT_EXTENSION +Windows.Win32.Foundation.ERROR_DS_UNDELETE_SAM_VALIDATION_FAILED +Windows.Win32.Foundation.ERROR_DS_UNICODEPWD_NOT_IN_QUOTES +Windows.Win32.Foundation.ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER +Windows.Win32.Foundation.ERROR_DS_UNKNOWN_ERROR +Windows.Win32.Foundation.ERROR_DS_UNKNOWN_OPERATION +Windows.Win32.Foundation.ERROR_DS_UNWILLING_TO_PERFORM +Windows.Win32.Foundation.ERROR_DS_UPN_VALUE_NOT_UNIQUE_IN_FOREST +Windows.Win32.Foundation.ERROR_DS_USER_BUFFER_TO_SMALL +Windows.Win32.Foundation.ERROR_DS_VALUE_KEY_NOT_UNIQUE +Windows.Win32.Foundation.ERROR_DS_VERSION_CHECK_FAILURE +Windows.Win32.Foundation.ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL +Windows.Win32.Foundation.ERROR_DS_WRONG_LINKED_ATT_SYNTAX +Windows.Win32.Foundation.ERROR_DS_WRONG_OM_OBJ_CLASS +Windows.Win32.Foundation.ERROR_DUP_DOMAINNAME +Windows.Win32.Foundation.ERROR_DUP_NAME +Windows.Win32.Foundation.ERROR_DUPLICATE_PRIVILEGES +Windows.Win32.Foundation.ERROR_DUPLICATE_SERVICE_NAME +Windows.Win32.Foundation.ERROR_DYNAMIC_CODE_BLOCKED +Windows.Win32.Foundation.ERROR_DYNLINK_FROM_INVALID_RING +Windows.Win32.Foundation.ERROR_EA_ACCESS_DENIED +Windows.Win32.Foundation.ERROR_EA_FILE_CORRUPT +Windows.Win32.Foundation.ERROR_EA_LIST_INCONSISTENT +Windows.Win32.Foundation.ERROR_EA_TABLE_FULL +Windows.Win32.Foundation.ERROR_EAS_DIDNT_FIT +Windows.Win32.Foundation.ERROR_EAS_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_EDP_DPL_POLICY_CANT_BE_SATISFIED +Windows.Win32.Foundation.ERROR_EDP_POLICY_DENIES_OPERATION +Windows.Win32.Foundation.ERROR_EFS_ALG_BLOB_TOO_BIG +Windows.Win32.Foundation.ERROR_EFS_DISABLED +Windows.Win32.Foundation.ERROR_EFS_SERVER_NOT_TRUSTED +Windows.Win32.Foundation.ERROR_EFS_VERSION_NOT_SUPPORT +Windows.Win32.Foundation.ERROR_ELEVATION_REQUIRED +Windows.Win32.Foundation.ERROR_ENCLAVE_FAILURE +Windows.Win32.Foundation.ERROR_ENCLAVE_NOT_TERMINATED +Windows.Win32.Foundation.ERROR_ENCLAVE_VIOLATION +Windows.Win32.Foundation.ERROR_ENCRYPTED_FILE_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_ENCRYPTED_IO_NOT_POSSIBLE +Windows.Win32.Foundation.ERROR_ENCRYPTING_METADATA_DISALLOWED +Windows.Win32.Foundation.ERROR_ENCRYPTION_DISABLED +Windows.Win32.Foundation.ERROR_ENCRYPTION_FAILED +Windows.Win32.Foundation.ERROR_ENCRYPTION_POLICY_DENIES_OPERATION +Windows.Win32.Foundation.ERROR_END_OF_MEDIA +Windows.Win32.Foundation.ERROR_ENVVAR_NOT_FOUND +Windows.Win32.Foundation.ERROR_EOM_OVERFLOW +Windows.Win32.Foundation.ERROR_ERRORS_ENCOUNTERED +Windows.Win32.Foundation.ERROR_EVALUATION_EXPIRATION +Windows.Win32.Foundation.ERROR_EVENT_DONE +Windows.Win32.Foundation.ERROR_EVENT_PENDING +Windows.Win32.Foundation.ERROR_EVENTLOG_CANT_START +Windows.Win32.Foundation.ERROR_EVENTLOG_FILE_CHANGED +Windows.Win32.Foundation.ERROR_EVENTLOG_FILE_CORRUPT +Windows.Win32.Foundation.ERROR_EXCEPTION_IN_SERVICE +Windows.Win32.Foundation.ERROR_EXCL_SEM_ALREADY_OWNED +Windows.Win32.Foundation.ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY +Windows.Win32.Foundation.ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY +Windows.Win32.Foundation.ERROR_EXE_MACHINE_TYPE_MISMATCH +Windows.Win32.Foundation.ERROR_EXE_MARKED_INVALID +Windows.Win32.Foundation.ERROR_EXTENDED_ERROR +Windows.Win32.Foundation.ERROR_EXTERNAL_BACKING_PROVIDER_UNKNOWN +Windows.Win32.Foundation.ERROR_EXTERNAL_SYSKEY_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_EXTRANEOUS_INFORMATION +Windows.Win32.Foundation.ERROR_FAIL_FAST_EXCEPTION +Windows.Win32.Foundation.ERROR_FAIL_I24 +Windows.Win32.Foundation.ERROR_FAIL_NOACTION_REBOOT +Windows.Win32.Foundation.ERROR_FAIL_RESTART +Windows.Win32.Foundation.ERROR_FAIL_SHUTDOWN +Windows.Win32.Foundation.ERROR_FAILED_DRIVER_ENTRY +Windows.Win32.Foundation.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT +Windows.Win32.Foundation.ERROR_FATAL_APP_EXIT +Windows.Win32.Foundation.ERROR_FILE_CHECKED_OUT +Windows.Win32.Foundation.ERROR_FILE_CORRUPT +Windows.Win32.Foundation.ERROR_FILE_ENCRYPTED +Windows.Win32.Foundation.ERROR_FILE_EXISTS +Windows.Win32.Foundation.ERROR_FILE_HANDLE_REVOKED +Windows.Win32.Foundation.ERROR_FILE_INVALID +Windows.Win32.Foundation.ERROR_FILE_LEVEL_TRIM_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_FILE_METADATA_OPTIMIZATION_IN_PROGRESS +Windows.Win32.Foundation.ERROR_FILE_NOT_ENCRYPTED +Windows.Win32.Foundation.ERROR_FILE_NOT_FOUND +Windows.Win32.Foundation.ERROR_FILE_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_FILE_OFFLINE +Windows.Win32.Foundation.ERROR_FILE_PROTECTED_UNDER_DPL +Windows.Win32.Foundation.ERROR_FILE_READ_ONLY +Windows.Win32.Foundation.ERROR_FILE_SNAP_IN_PROGRESS +Windows.Win32.Foundation.ERROR_FILE_SNAP_INVALID_PARAMETER +Windows.Win32.Foundation.ERROR_FILE_SNAP_IO_NOT_COORDINATED +Windows.Win32.Foundation.ERROR_FILE_SNAP_MODIFY_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_FILE_SNAP_UNEXPECTED_ERROR +Windows.Win32.Foundation.ERROR_FILE_SNAP_USER_SECTION_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_FILE_SYSTEM_LIMITATION +Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_BUSY +Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_INVALID_OPERATION +Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_METADATA_CORRUPT +Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_PROVIDER_UNKNOWN +Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_UNAVAILABLE +Windows.Win32.Foundation.ERROR_FILE_TOO_LARGE +Windows.Win32.Foundation.ERROR_FILEMARK_DETECTED +Windows.Win32.Foundation.ERROR_FILENAME_EXCED_RANGE +Windows.Win32.Foundation.ERROR_FIRMWARE_UPDATED +Windows.Win32.Foundation.ERROR_FLOAT_MULTIPLE_FAULTS +Windows.Win32.Foundation.ERROR_FLOAT_MULTIPLE_TRAPS +Windows.Win32.Foundation.ERROR_FLOPPY_BAD_REGISTERS +Windows.Win32.Foundation.ERROR_FLOPPY_ID_MARK_NOT_FOUND +Windows.Win32.Foundation.ERROR_FLOPPY_UNKNOWN_ERROR +Windows.Win32.Foundation.ERROR_FLOPPY_VOLUME +Windows.Win32.Foundation.ERROR_FLOPPY_WRONG_CYLINDER +Windows.Win32.Foundation.ERROR_FORMS_AUTH_REQUIRED +Windows.Win32.Foundation.ERROR_FOUND_OUT_OF_SCOPE +Windows.Win32.Foundation.ERROR_FS_DRIVER_REQUIRED +Windows.Win32.Foundation.ERROR_FS_METADATA_INCONSISTENT +Windows.Win32.Foundation.ERROR_FSFILTER_OP_COMPLETED_SUCCESSFULLY +Windows.Win32.Foundation.ERROR_FT_DI_SCAN_REQUIRED +Windows.Win32.Foundation.ERROR_FT_READ_FAILURE +Windows.Win32.Foundation.ERROR_FT_READ_FROM_COPY_FAILURE +Windows.Win32.Foundation.ERROR_FT_READ_RECOVERY_FROM_BACKUP +Windows.Win32.Foundation.ERROR_FT_WRITE_FAILURE +Windows.Win32.Foundation.ERROR_FT_WRITE_RECOVERY +Windows.Win32.Foundation.ERROR_FULLSCREEN_MODE +Windows.Win32.Foundation.ERROR_FUNCTION_FAILED +Windows.Win32.Foundation.ERROR_FUNCTION_NOT_CALLED +Windows.Win32.Foundation.ERROR_GDI_HANDLE_LEAK +Windows.Win32.Foundation.ERROR_GEN_FAILURE +Windows.Win32.Foundation.ERROR_GENERIC_NOT_MAPPED +Windows.Win32.Foundation.ERROR_GLOBAL_ONLY_HOOK +Windows.Win32.Foundation.ERROR_GRACEFUL_DISCONNECT +Windows.Win32.Foundation.ERROR_GROUP_EXISTS +Windows.Win32.Foundation.ERROR_GUID_SUBSTITUTION_MADE +Windows.Win32.Foundation.ERROR_HANDLE_DISK_FULL +Windows.Win32.Foundation.ERROR_HANDLE_EOF +Windows.Win32.Foundation.ERROR_HANDLE_REVOKED +Windows.Win32.Foundation.ERROR_HANDLES_CLOSED +Windows.Win32.Foundation.ERROR_HAS_SYSTEM_CRITICAL_FILES +Windows.Win32.Foundation.ERROR_HIBERNATED +Windows.Win32.Foundation.ERROR_HIBERNATION_FAILURE +Windows.Win32.Foundation.ERROR_HOOK_NEEDS_HMOD +Windows.Win32.Foundation.ERROR_HOOK_NOT_INSTALLED +Windows.Win32.Foundation.ERROR_HOOK_TYPE_NOT_ALLOWED +Windows.Win32.Foundation.ERROR_HOST_DOWN +Windows.Win32.Foundation.ERROR_HOST_UNREACHABLE +Windows.Win32.Foundation.ERROR_HOTKEY_ALREADY_REGISTERED +Windows.Win32.Foundation.ERROR_HOTKEY_NOT_REGISTERED +Windows.Win32.Foundation.ERROR_HWNDS_HAVE_DIFF_PARENT +Windows.Win32.Foundation.ERROR_ILL_FORMED_PASSWORD +Windows.Win32.Foundation.ERROR_ILLEGAL_CHARACTER +Windows.Win32.Foundation.ERROR_ILLEGAL_DLL_RELOCATION +Windows.Win32.Foundation.ERROR_ILLEGAL_ELEMENT_ADDRESS +Windows.Win32.Foundation.ERROR_ILLEGAL_FLOAT_CONTEXT +Windows.Win32.Foundation.ERROR_IMAGE_AT_DIFFERENT_BASE +Windows.Win32.Foundation.ERROR_IMAGE_MACHINE_TYPE_MISMATCH +Windows.Win32.Foundation.ERROR_IMAGE_MACHINE_TYPE_MISMATCH_EXE +Windows.Win32.Foundation.ERROR_IMAGE_NOT_AT_BASE +Windows.Win32.Foundation.ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT +Windows.Win32.Foundation.ERROR_IMPLEMENTATION_LIMIT +Windows.Win32.Foundation.ERROR_INCOMPATIBLE_SERVICE_PRIVILEGE +Windows.Win32.Foundation.ERROR_INCOMPATIBLE_SERVICE_SID_TYPE +Windows.Win32.Foundation.ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING +Windows.Win32.Foundation.ERROR_INCORRECT_ACCOUNT_TYPE +Windows.Win32.Foundation.ERROR_INCORRECT_ADDRESS +Windows.Win32.Foundation.ERROR_INCORRECT_SIZE +Windows.Win32.Foundation.ERROR_INDEX_ABSENT +Windows.Win32.Foundation.ERROR_INDEX_OUT_OF_BOUNDS +Windows.Win32.Foundation.ERROR_INFLOOP_IN_RELOC_CHAIN +Windows.Win32.Foundation.ERROR_INSTALL_ALREADY_RUNNING +Windows.Win32.Foundation.ERROR_INSTALL_FAILURE +Windows.Win32.Foundation.ERROR_INSTALL_LANGUAGE_UNSUPPORTED +Windows.Win32.Foundation.ERROR_INSTALL_LOG_FAILURE +Windows.Win32.Foundation.ERROR_INSTALL_NOTUSED +Windows.Win32.Foundation.ERROR_INSTALL_PACKAGE_INVALID +Windows.Win32.Foundation.ERROR_INSTALL_PACKAGE_OPEN_FAILED +Windows.Win32.Foundation.ERROR_INSTALL_PACKAGE_REJECTED +Windows.Win32.Foundation.ERROR_INSTALL_PACKAGE_VERSION +Windows.Win32.Foundation.ERROR_INSTALL_PLATFORM_UNSUPPORTED +Windows.Win32.Foundation.ERROR_INSTALL_REJECTED +Windows.Win32.Foundation.ERROR_INSTALL_REMOTE_DISALLOWED +Windows.Win32.Foundation.ERROR_INSTALL_REMOTE_PROHIBITED +Windows.Win32.Foundation.ERROR_INSTALL_SERVICE_FAILURE +Windows.Win32.Foundation.ERROR_INSTALL_SERVICE_SAFEBOOT +Windows.Win32.Foundation.ERROR_INSTALL_SOURCE_ABSENT +Windows.Win32.Foundation.ERROR_INSTALL_SUSPEND +Windows.Win32.Foundation.ERROR_INSTALL_TEMP_UNWRITABLE +Windows.Win32.Foundation.ERROR_INSTALL_TRANSFORM_FAILURE +Windows.Win32.Foundation.ERROR_INSTALL_TRANSFORM_REJECTED +Windows.Win32.Foundation.ERROR_INSTALL_UI_FAILURE +Windows.Win32.Foundation.ERROR_INSTALL_USEREXIT +Windows.Win32.Foundation.ERROR_INSTRUCTION_MISALIGNMENT +Windows.Win32.Foundation.ERROR_INSUFFICIENT_BUFFER +Windows.Win32.Foundation.ERROR_INSUFFICIENT_LOGON_INFO +Windows.Win32.Foundation.ERROR_INSUFFICIENT_POWER +Windows.Win32.Foundation.ERROR_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE +Windows.Win32.Foundation.ERROR_INSUFFICIENT_VIRTUAL_ADDR_RESOURCES +Windows.Win32.Foundation.ERROR_INTERMIXED_KERNEL_EA_OPERATION +Windows.Win32.Foundation.ERROR_INTERNAL_DB_CORRUPTION +Windows.Win32.Foundation.ERROR_INTERNAL_DB_ERROR +Windows.Win32.Foundation.ERROR_INTERNAL_ERROR +Windows.Win32.Foundation.ERROR_INTERRUPT_STILL_CONNECTED +Windows.Win32.Foundation.ERROR_INTERRUPT_VECTOR_ALREADY_CONNECTED +Windows.Win32.Foundation.ERROR_INVALID_ACCEL_HANDLE +Windows.Win32.Foundation.ERROR_INVALID_ACCESS +Windows.Win32.Foundation.ERROR_INVALID_ACCOUNT_NAME +Windows.Win32.Foundation.ERROR_INVALID_ACE_CONDITION +Windows.Win32.Foundation.ERROR_INVALID_ACL +Windows.Win32.Foundation.ERROR_INVALID_ADDRESS +Windows.Win32.Foundation.ERROR_INVALID_AT_INTERRUPT_TIME +Windows.Win32.Foundation.ERROR_INVALID_BLOCK +Windows.Win32.Foundation.ERROR_INVALID_BLOCK_LENGTH +Windows.Win32.Foundation.ERROR_INVALID_CAP +Windows.Win32.Foundation.ERROR_INVALID_CATEGORY +Windows.Win32.Foundation.ERROR_INVALID_COMBOBOX_MESSAGE +Windows.Win32.Foundation.ERROR_INVALID_COMMAND_LINE +Windows.Win32.Foundation.ERROR_INVALID_COMPUTERNAME +Windows.Win32.Foundation.ERROR_INVALID_CRUNTIME_PARAMETER +Windows.Win32.Foundation.ERROR_INVALID_CURSOR_HANDLE +Windows.Win32.Foundation.ERROR_INVALID_DATA +Windows.Win32.Foundation.ERROR_INVALID_DATATYPE +Windows.Win32.Foundation.ERROR_INVALID_DEVICE_OBJECT_PARAMETER +Windows.Win32.Foundation.ERROR_INVALID_DLL +Windows.Win32.Foundation.ERROR_INVALID_DOMAIN_ROLE +Windows.Win32.Foundation.ERROR_INVALID_DOMAIN_STATE +Windows.Win32.Foundation.ERROR_INVALID_DOMAINNAME +Windows.Win32.Foundation.ERROR_INVALID_DRIVE +Windows.Win32.Foundation.ERROR_INVALID_DWP_HANDLE +Windows.Win32.Foundation.ERROR_INVALID_EA_HANDLE +Windows.Win32.Foundation.ERROR_INVALID_EA_NAME +Windows.Win32.Foundation.ERROR_INVALID_EDIT_HEIGHT +Windows.Win32.Foundation.ERROR_INVALID_ENVIRONMENT +Windows.Win32.Foundation.ERROR_INVALID_EVENT_COUNT +Windows.Win32.Foundation.ERROR_INVALID_EVENTNAME +Windows.Win32.Foundation.ERROR_INVALID_EXCEPTION_HANDLER +Windows.Win32.Foundation.ERROR_INVALID_EXE_SIGNATURE +Windows.Win32.Foundation.ERROR_INVALID_FIELD +Windows.Win32.Foundation.ERROR_INVALID_FIELD_IN_PARAMETER_LIST +Windows.Win32.Foundation.ERROR_INVALID_FILTER_PROC +Windows.Win32.Foundation.ERROR_INVALID_FLAG_NUMBER +Windows.Win32.Foundation.ERROR_INVALID_FLAGS +Windows.Win32.Foundation.ERROR_INVALID_FORM_NAME +Windows.Win32.Foundation.ERROR_INVALID_FORM_SIZE +Windows.Win32.Foundation.ERROR_INVALID_FUNCTION +Windows.Win32.Foundation.ERROR_INVALID_GROUP_ATTRIBUTES +Windows.Win32.Foundation.ERROR_INVALID_GROUPNAME +Windows.Win32.Foundation.ERROR_INVALID_GW_COMMAND +Windows.Win32.Foundation.ERROR_INVALID_HANDLE +Windows.Win32.Foundation.ERROR_INVALID_HANDLE_STATE +Windows.Win32.Foundation.ERROR_INVALID_HOOK_FILTER +Windows.Win32.Foundation.ERROR_INVALID_HOOK_HANDLE +Windows.Win32.Foundation.ERROR_INVALID_HW_PROFILE +Windows.Win32.Foundation.ERROR_INVALID_ICON_HANDLE +Windows.Win32.Foundation.ERROR_INVALID_ID_AUTHORITY +Windows.Win32.Foundation.ERROR_INVALID_IMAGE_HASH +Windows.Win32.Foundation.ERROR_INVALID_IMPORT_OF_NON_DLL +Windows.Win32.Foundation.ERROR_INVALID_INDEX +Windows.Win32.Foundation.ERROR_INVALID_KERNEL_INFO_VERSION +Windows.Win32.Foundation.ERROR_INVALID_KEYBOARD_HANDLE +Windows.Win32.Foundation.ERROR_INVALID_LABEL +Windows.Win32.Foundation.ERROR_INVALID_LB_MESSAGE +Windows.Win32.Foundation.ERROR_INVALID_LDT_DESCRIPTOR +Windows.Win32.Foundation.ERROR_INVALID_LDT_OFFSET +Windows.Win32.Foundation.ERROR_INVALID_LDT_SIZE +Windows.Win32.Foundation.ERROR_INVALID_LEVEL +Windows.Win32.Foundation.ERROR_INVALID_LIST_FORMAT +Windows.Win32.Foundation.ERROR_INVALID_LOCK_RANGE +Windows.Win32.Foundation.ERROR_INVALID_LOGON_HOURS +Windows.Win32.Foundation.ERROR_INVALID_LOGON_TYPE +Windows.Win32.Foundation.ERROR_INVALID_MEMBER +Windows.Win32.Foundation.ERROR_INVALID_MENU_HANDLE +Windows.Win32.Foundation.ERROR_INVALID_MESSAGE +Windows.Win32.Foundation.ERROR_INVALID_MESSAGEDEST +Windows.Win32.Foundation.ERROR_INVALID_MESSAGENAME +Windows.Win32.Foundation.ERROR_INVALID_MINALLOCSIZE +Windows.Win32.Foundation.ERROR_INVALID_MODULETYPE +Windows.Win32.Foundation.ERROR_INVALID_MONITOR_HANDLE +Windows.Win32.Foundation.ERROR_INVALID_MSGBOX_STYLE +Windows.Win32.Foundation.ERROR_INVALID_NAME +Windows.Win32.Foundation.ERROR_INVALID_NETNAME +Windows.Win32.Foundation.ERROR_INVALID_OPLOCK_PROTOCOL +Windows.Win32.Foundation.ERROR_INVALID_ORDINAL +Windows.Win32.Foundation.ERROR_INVALID_OWNER +Windows.Win32.Foundation.ERROR_INVALID_PACKAGE_SID_LENGTH +Windows.Win32.Foundation.ERROR_INVALID_PARAMETER +Windows.Win32.Foundation.ERROR_INVALID_PASSWORD +Windows.Win32.Foundation.ERROR_INVALID_PASSWORDNAME +Windows.Win32.Foundation.ERROR_INVALID_PATCH_XML +Windows.Win32.Foundation.ERROR_INVALID_PEP_INFO_VERSION +Windows.Win32.Foundation.ERROR_INVALID_PLUGPLAY_DEVICE_PATH +Windows.Win32.Foundation.ERROR_INVALID_PORT_ATTRIBUTES +Windows.Win32.Foundation.ERROR_INVALID_PRIMARY_GROUP +Windows.Win32.Foundation.ERROR_INVALID_PRINTER_COMMAND +Windows.Win32.Foundation.ERROR_INVALID_PRINTER_NAME +Windows.Win32.Foundation.ERROR_INVALID_PRINTER_STATE +Windows.Win32.Foundation.ERROR_INVALID_PRIORITY +Windows.Win32.Foundation.ERROR_INVALID_QUOTA_LOWER +Windows.Win32.Foundation.ERROR_INVALID_REPARSE_DATA +Windows.Win32.Foundation.ERROR_INVALID_SCROLLBAR_RANGE +Windows.Win32.Foundation.ERROR_INVALID_SECURITY_DESCR +Windows.Win32.Foundation.ERROR_INVALID_SEGDPL +Windows.Win32.Foundation.ERROR_INVALID_SEGMENT_NUMBER +Windows.Win32.Foundation.ERROR_INVALID_SEPARATOR_FILE +Windows.Win32.Foundation.ERROR_INVALID_SERVER_STATE +Windows.Win32.Foundation.ERROR_INVALID_SERVICE_ACCOUNT +Windows.Win32.Foundation.ERROR_INVALID_SERVICE_CONTROL +Windows.Win32.Foundation.ERROR_INVALID_SERVICE_LOCK +Windows.Win32.Foundation.ERROR_INVALID_SERVICENAME +Windows.Win32.Foundation.ERROR_INVALID_SHARENAME +Windows.Win32.Foundation.ERROR_INVALID_SHOWWIN_COMMAND +Windows.Win32.Foundation.ERROR_INVALID_SID +Windows.Win32.Foundation.ERROR_INVALID_SIGNAL_NUMBER +Windows.Win32.Foundation.ERROR_INVALID_SPI_VALUE +Windows.Win32.Foundation.ERROR_INVALID_STACKSEG +Windows.Win32.Foundation.ERROR_INVALID_STARTING_CODESEG +Windows.Win32.Foundation.ERROR_INVALID_SUB_AUTHORITY +Windows.Win32.Foundation.ERROR_INVALID_TABLE +Windows.Win32.Foundation.ERROR_INVALID_TARGET_HANDLE +Windows.Win32.Foundation.ERROR_INVALID_TASK_INDEX +Windows.Win32.Foundation.ERROR_INVALID_TASK_NAME +Windows.Win32.Foundation.ERROR_INVALID_THREAD_ID +Windows.Win32.Foundation.ERROR_INVALID_TIME +Windows.Win32.Foundation.ERROR_INVALID_TOKEN +Windows.Win32.Foundation.ERROR_INVALID_UNWIND_TARGET +Windows.Win32.Foundation.ERROR_INVALID_USER_BUFFER +Windows.Win32.Foundation.ERROR_INVALID_USER_PRINCIPAL_NAME +Windows.Win32.Foundation.ERROR_INVALID_VARIANT +Windows.Win32.Foundation.ERROR_INVALID_VERIFY_SWITCH +Windows.Win32.Foundation.ERROR_INVALID_WINDOW_HANDLE +Windows.Win32.Foundation.ERROR_INVALID_WORKSTATION +Windows.Win32.Foundation.ERROR_IO_DEVICE +Windows.Win32.Foundation.ERROR_IO_INCOMPLETE +Windows.Win32.Foundation.ERROR_IO_PENDING +Windows.Win32.Foundation.ERROR_IO_PRIVILEGE_FAILED +Windows.Win32.Foundation.ERROR_IO_REISSUE_AS_CACHED +Windows.Win32.Foundation.ERROR_IOPL_NOT_ENABLED +Windows.Win32.Foundation.ERROR_IP_ADDRESS_CONFLICT1 +Windows.Win32.Foundation.ERROR_IP_ADDRESS_CONFLICT2 +Windows.Win32.Foundation.ERROR_IPSEC_IKE_TIMED_OUT +Windows.Win32.Foundation.ERROR_IRQ_BUSY +Windows.Win32.Foundation.ERROR_IS_JOIN_PATH +Windows.Win32.Foundation.ERROR_IS_JOIN_TARGET +Windows.Win32.Foundation.ERROR_IS_JOINED +Windows.Win32.Foundation.ERROR_IS_SUBST_PATH +Windows.Win32.Foundation.ERROR_IS_SUBST_TARGET +Windows.Win32.Foundation.ERROR_IS_SUBSTED +Windows.Win32.Foundation.ERROR_ITERATED_DATA_EXCEEDS_64k +Windows.Win32.Foundation.ERROR_JOB_NO_CONTAINER +Windows.Win32.Foundation.ERROR_JOIN_TO_JOIN +Windows.Win32.Foundation.ERROR_JOIN_TO_SUBST +Windows.Win32.Foundation.ERROR_JOURNAL_DELETE_IN_PROGRESS +Windows.Win32.Foundation.ERROR_JOURNAL_ENTRY_DELETED +Windows.Win32.Foundation.ERROR_JOURNAL_HOOK_SET +Windows.Win32.Foundation.ERROR_JOURNAL_NOT_ACTIVE +Windows.Win32.Foundation.ERROR_KERNEL_APC +Windows.Win32.Foundation.ERROR_KEY_DELETED +Windows.Win32.Foundation.ERROR_KEY_HAS_CHILDREN +Windows.Win32.Foundation.ERROR_KM_DRIVER_BLOCKED +Windows.Win32.Foundation.ERROR_LABEL_TOO_LONG +Windows.Win32.Foundation.ERROR_LAST_ADMIN +Windows.Win32.Foundation.ERROR_LB_WITHOUT_TABSTOPS +Windows.Win32.Foundation.ERROR_LICENSE_QUOTA_EXCEEDED +Windows.Win32.Foundation.ERROR_LINUX_SUBSYSTEM_NOT_PRESENT +Windows.Win32.Foundation.ERROR_LINUX_SUBSYSTEM_UPDATE_REQUIRED +Windows.Win32.Foundation.ERROR_LISTBOX_ID_NOT_FOUND +Windows.Win32.Foundation.ERROR_LM_CROSS_ENCRYPTION_REQUIRED +Windows.Win32.Foundation.ERROR_LOCAL_POLICY_MODIFICATION_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_LOCAL_USER_SESSION_KEY +Windows.Win32.Foundation.ERROR_LOCK_FAILED +Windows.Win32.Foundation.ERROR_LOCK_VIOLATION +Windows.Win32.Foundation.ERROR_LOCKED +Windows.Win32.Foundation.ERROR_LOG_FILE_FULL +Windows.Win32.Foundation.ERROR_LOG_HARD_ERROR +Windows.Win32.Foundation.ERROR_LOGIN_TIME_RESTRICTION +Windows.Win32.Foundation.ERROR_LOGIN_WKSTA_RESTRICTION +Windows.Win32.Foundation.ERROR_LOGON_FAILURE +Windows.Win32.Foundation.ERROR_LOGON_NOT_GRANTED +Windows.Win32.Foundation.ERROR_LOGON_SERVER_CONFLICT +Windows.Win32.Foundation.ERROR_LOGON_SESSION_COLLISION +Windows.Win32.Foundation.ERROR_LOGON_SESSION_EXISTS +Windows.Win32.Foundation.ERROR_LOGON_TYPE_NOT_GRANTED +Windows.Win32.Foundation.ERROR_LONGJUMP +Windows.Win32.Foundation.ERROR_LOST_MODE_LOGON_RESTRICTION +Windows.Win32.Foundation.ERROR_LOST_WRITEBEHIND_DATA +Windows.Win32.Foundation.ERROR_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR +Windows.Win32.Foundation.ERROR_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED +Windows.Win32.Foundation.ERROR_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR +Windows.Win32.Foundation.ERROR_LUIDS_EXHAUSTED +Windows.Win32.Foundation.ERROR_MACHINE_LOCKED +Windows.Win32.Foundation.ERROR_MAGAZINE_NOT_PRESENT +Windows.Win32.Foundation.ERROR_MAPPED_ALIGNMENT +Windows.Win32.Foundation.ERROR_MARKED_TO_DISALLOW_WRITES +Windows.Win32.Foundation.ERROR_MARSHALL_OVERFLOW +Windows.Win32.Foundation.ERROR_MAX_SESSIONS_REACHED +Windows.Win32.Foundation.ERROR_MAX_THRDS_REACHED +Windows.Win32.Foundation.ERROR_MCA_EXCEPTION +Windows.Win32.Foundation.ERROR_MCA_OCCURED +Windows.Win32.Foundation.ERROR_MEDIA_CHANGED +Windows.Win32.Foundation.ERROR_MEDIA_CHECK +Windows.Win32.Foundation.ERROR_MEMBER_IN_ALIAS +Windows.Win32.Foundation.ERROR_MEMBER_IN_GROUP +Windows.Win32.Foundation.ERROR_MEMBER_NOT_IN_ALIAS +Windows.Win32.Foundation.ERROR_MEMBER_NOT_IN_GROUP +Windows.Win32.Foundation.ERROR_MEMBERS_PRIMARY_GROUP +Windows.Win32.Foundation.ERROR_MEMORY_HARDWARE +Windows.Win32.Foundation.ERROR_MENU_ITEM_NOT_FOUND +Windows.Win32.Foundation.ERROR_MESSAGE_SYNC_ONLY +Windows.Win32.Foundation.ERROR_META_EXPANSION_TOO_LONG +Windows.Win32.Foundation.ERROR_MISSING_SYSTEMFILE +Windows.Win32.Foundation.ERROR_MOD_NOT_FOUND +Windows.Win32.Foundation.ERROR_MORE_DATA +Windows.Win32.Foundation.ERROR_MORE_WRITES +Windows.Win32.Foundation.ERROR_MOUNT_POINT_NOT_RESOLVED +Windows.Win32.Foundation.ERROR_MP_PROCESSOR_MISMATCH +Windows.Win32.Foundation.ERROR_MR_MID_NOT_FOUND +Windows.Win32.Foundation.ERROR_MULTIPLE_FAULT_VIOLATION +Windows.Win32.Foundation.ERROR_MUTANT_LIMIT_EXCEEDED +Windows.Win32.Foundation.ERROR_MUTUAL_AUTH_FAILED +Windows.Win32.Foundation.ERROR_NEGATIVE_SEEK +Windows.Win32.Foundation.ERROR_NESTING_NOT_ALLOWED +Windows.Win32.Foundation.ERROR_NET_OPEN_FAILED +Windows.Win32.Foundation.ERROR_NET_WRITE_FAULT +Windows.Win32.Foundation.ERROR_NETLOGON_NOT_STARTED +Windows.Win32.Foundation.ERROR_NETNAME_DELETED +Windows.Win32.Foundation.ERROR_NETWORK_ACCESS_DENIED +Windows.Win32.Foundation.ERROR_NETWORK_ACCESS_DENIED_EDP +Windows.Win32.Foundation.ERROR_NETWORK_BUSY +Windows.Win32.Foundation.ERROR_NETWORK_UNREACHABLE +Windows.Win32.Foundation.ERROR_NO_ACE_CONDITION +Windows.Win32.Foundation.ERROR_NO_ASSOCIATION +Windows.Win32.Foundation.ERROR_NO_BYPASSIO_DRIVER_SUPPORT +Windows.Win32.Foundation.ERROR_NO_CALLBACK_ACTIVE +Windows.Win32.Foundation.ERROR_NO_DATA +Windows.Win32.Foundation.ERROR_NO_DATA_DETECTED +Windows.Win32.Foundation.ERROR_NO_EFS +Windows.Win32.Foundation.ERROR_NO_EVENT_PAIR +Windows.Win32.Foundation.ERROR_NO_GUID_TRANSLATION +Windows.Win32.Foundation.ERROR_NO_IMPERSONATION_TOKEN +Windows.Win32.Foundation.ERROR_NO_INHERITANCE +Windows.Win32.Foundation.ERROR_NO_LOG_SPACE +Windows.Win32.Foundation.ERROR_NO_LOGON_SERVERS +Windows.Win32.Foundation.ERROR_NO_MATCH +Windows.Win32.Foundation.ERROR_NO_MEDIA_IN_DRIVE +Windows.Win32.Foundation.ERROR_NO_MORE_DEVICES +Windows.Win32.Foundation.ERROR_NO_MORE_FILES +Windows.Win32.Foundation.ERROR_NO_MORE_ITEMS +Windows.Win32.Foundation.ERROR_NO_MORE_MATCHES +Windows.Win32.Foundation.ERROR_NO_MORE_SEARCH_HANDLES +Windows.Win32.Foundation.ERROR_NO_MORE_USER_HANDLES +Windows.Win32.Foundation.ERROR_NO_NET_OR_BAD_PATH +Windows.Win32.Foundation.ERROR_NO_NETWORK +Windows.Win32.Foundation.ERROR_NO_NVRAM_RESOURCES +Windows.Win32.Foundation.ERROR_NO_PAGEFILE +Windows.Win32.Foundation.ERROR_NO_PHYSICALLY_ALIGNED_FREE_SPACE_FOUND +Windows.Win32.Foundation.ERROR_NO_PROC_SLOTS +Windows.Win32.Foundation.ERROR_NO_PROMOTION_ACTIVE +Windows.Win32.Foundation.ERROR_NO_QUOTAS_FOR_ACCOUNT +Windows.Win32.Foundation.ERROR_NO_RANGES_PROCESSED +Windows.Win32.Foundation.ERROR_NO_RECOVERY_POLICY +Windows.Win32.Foundation.ERROR_NO_RECOVERY_PROGRAM +Windows.Win32.Foundation.ERROR_NO_SCROLLBARS +Windows.Win32.Foundation.ERROR_NO_SECRETS +Windows.Win32.Foundation.ERROR_NO_SECURITY_ON_OBJECT +Windows.Win32.Foundation.ERROR_NO_SHUTDOWN_IN_PROGRESS +Windows.Win32.Foundation.ERROR_NO_SIGNAL_SENT +Windows.Win32.Foundation.ERROR_NO_SITE_SETTINGS_OBJECT +Windows.Win32.Foundation.ERROR_NO_SITENAME +Windows.Win32.Foundation.ERROR_NO_SPOOL_SPACE +Windows.Win32.Foundation.ERROR_NO_SUCH_ALIAS +Windows.Win32.Foundation.ERROR_NO_SUCH_DEVICE +Windows.Win32.Foundation.ERROR_NO_SUCH_DOMAIN +Windows.Win32.Foundation.ERROR_NO_SUCH_GROUP +Windows.Win32.Foundation.ERROR_NO_SUCH_LOGON_SESSION +Windows.Win32.Foundation.ERROR_NO_SUCH_MEMBER +Windows.Win32.Foundation.ERROR_NO_SUCH_PACKAGE +Windows.Win32.Foundation.ERROR_NO_SUCH_PRIVILEGE +Windows.Win32.Foundation.ERROR_NO_SUCH_SITE +Windows.Win32.Foundation.ERROR_NO_SUCH_USER +Windows.Win32.Foundation.ERROR_NO_SYSTEM_MENU +Windows.Win32.Foundation.ERROR_NO_SYSTEM_RESOURCES +Windows.Win32.Foundation.ERROR_NO_TASK_QUEUE +Windows.Win32.Foundation.ERROR_NO_TOKEN +Windows.Win32.Foundation.ERROR_NO_TRACKING_SERVICE +Windows.Win32.Foundation.ERROR_NO_TRUST_LSA_SECRET +Windows.Win32.Foundation.ERROR_NO_TRUST_SAM_ACCOUNT +Windows.Win32.Foundation.ERROR_NO_UNICODE_TRANSLATION +Windows.Win32.Foundation.ERROR_NO_USER_KEYS +Windows.Win32.Foundation.ERROR_NO_USER_SESSION_KEY +Windows.Win32.Foundation.ERROR_NO_VOLUME_ID +Windows.Win32.Foundation.ERROR_NO_VOLUME_LABEL +Windows.Win32.Foundation.ERROR_NO_WILDCARD_CHARACTERS +Windows.Win32.Foundation.ERROR_NO_WORK_DONE +Windows.Win32.Foundation.ERROR_NO_WRITABLE_DC_FOUND +Windows.Win32.Foundation.ERROR_NO_YIELD_PERFORMED +Windows.Win32.Foundation.ERROR_NOACCESS +Windows.Win32.Foundation.ERROR_NOINTERFACE +Windows.Win32.Foundation.ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT +Windows.Win32.Foundation.ERROR_NOLOGON_SERVER_TRUST_ACCOUNT +Windows.Win32.Foundation.ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT +Windows.Win32.Foundation.ERROR_NON_ACCOUNT_SID +Windows.Win32.Foundation.ERROR_NON_DOMAIN_SID +Windows.Win32.Foundation.ERROR_NON_MDICHILD_WINDOW +Windows.Win32.Foundation.ERROR_NONE_MAPPED +Windows.Win32.Foundation.ERROR_NONPAGED_SYSTEM_RESOURCES +Windows.Win32.Foundation.ERROR_NOT_A_CLOUD_FILE +Windows.Win32.Foundation.ERROR_NOT_A_CLOUD_SYNC_ROOT +Windows.Win32.Foundation.ERROR_NOT_A_DAX_VOLUME +Windows.Win32.Foundation.ERROR_NOT_A_REPARSE_POINT +Windows.Win32.Foundation.ERROR_NOT_ALL_ASSIGNED +Windows.Win32.Foundation.ERROR_NOT_ALLOWED_ON_SYSTEM_FILE +Windows.Win32.Foundation.ERROR_NOT_APPCONTAINER +Windows.Win32.Foundation.ERROR_NOT_AUTHENTICATED +Windows.Win32.Foundation.ERROR_NOT_CAPABLE +Windows.Win32.Foundation.ERROR_NOT_CHILD_WINDOW +Windows.Win32.Foundation.ERROR_NOT_CONNECTED +Windows.Win32.Foundation.ERROR_NOT_CONTAINER +Windows.Win32.Foundation.ERROR_NOT_DAX_MAPPABLE +Windows.Win32.Foundation.ERROR_NOT_DOS_DISK +Windows.Win32.Foundation.ERROR_NOT_ENOUGH_MEMORY +Windows.Win32.Foundation.ERROR_NOT_ENOUGH_QUOTA +Windows.Win32.Foundation.ERROR_NOT_ENOUGH_SERVER_MEMORY +Windows.Win32.Foundation.ERROR_NOT_EXPORT_FORMAT +Windows.Win32.Foundation.ERROR_NOT_FOUND +Windows.Win32.Foundation.ERROR_NOT_GUI_PROCESS +Windows.Win32.Foundation.ERROR_NOT_JOINED +Windows.Win32.Foundation.ERROR_NOT_LOCKED +Windows.Win32.Foundation.ERROR_NOT_LOGGED_ON +Windows.Win32.Foundation.ERROR_NOT_LOGON_PROCESS +Windows.Win32.Foundation.ERROR_NOT_OWNER +Windows.Win32.Foundation.ERROR_NOT_READ_FROM_COPY +Windows.Win32.Foundation.ERROR_NOT_READY +Windows.Win32.Foundation.ERROR_NOT_REDUNDANT_STORAGE +Windows.Win32.Foundation.ERROR_NOT_REGISTRY_FILE +Windows.Win32.Foundation.ERROR_NOT_SAFE_MODE_DRIVER +Windows.Win32.Foundation.ERROR_NOT_SAFEBOOT_SERVICE +Windows.Win32.Foundation.ERROR_NOT_SAME_DEVICE +Windows.Win32.Foundation.ERROR_NOT_SAME_OBJECT +Windows.Win32.Foundation.ERROR_NOT_SUBSTED +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_IN_APPCONTAINER +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_ON_DAX +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_ON_SBS +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_AUDITING +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_BTT +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_BYPASSIO +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_CACHED_HANDLE +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_COMPRESSION +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_DEDUPLICATION +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_ENCRYPTION +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_MONITORING +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_REPLICATION +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_SNAPSHOT +Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_VIRTUALIZATION +Windows.Win32.Foundation.ERROR_NOT_TINY_STREAM +Windows.Win32.Foundation.ERROR_NOTHING_TO_TERMINATE +Windows.Win32.Foundation.ERROR_NOTIFICATION_GUID_ALREADY_DEFINED +Windows.Win32.Foundation.ERROR_NOTIFY_CLEANUP +Windows.Win32.Foundation.ERROR_NOTIFY_ENUM_DIR +Windows.Win32.Foundation.ERROR_NT_CROSS_ENCRYPTION_REQUIRED +Windows.Win32.Foundation.ERROR_NTLM_BLOCKED +Windows.Win32.Foundation.ERROR_NULL_LM_PASSWORD +Windows.Win32.Foundation.ERROR_OBJECT_IS_IMMUTABLE +Windows.Win32.Foundation.ERROR_OBJECT_NAME_EXISTS +Windows.Win32.Foundation.ERROR_OBJECT_NOT_EXTERNALLY_BACKED +Windows.Win32.Foundation.ERROR_OFFLOAD_READ_FILE_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_OFFLOAD_READ_FLT_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_OFFLOAD_WRITE_FILE_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_OFFLOAD_WRITE_FLT_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_OFFSET_ALIGNMENT_VIOLATION +Windows.Win32.Foundation.ERROR_OLD_WIN_VERSION +Windows.Win32.Foundation.ERROR_ONLY_IF_CONNECTED +Windows.Win32.Foundation.ERROR_OPEN_FAILED +Windows.Win32.Foundation.ERROR_OPEN_FILES +Windows.Win32.Foundation.ERROR_OPERATION_ABORTED +Windows.Win32.Foundation.ERROR_OPERATION_IN_PROGRESS +Windows.Win32.Foundation.ERROR_OPLOCK_BREAK_IN_PROGRESS +Windows.Win32.Foundation.ERROR_OPLOCK_HANDLE_CLOSED +Windows.Win32.Foundation.ERROR_OPLOCK_NOT_GRANTED +Windows.Win32.Foundation.ERROR_OPLOCK_SWITCHED_TO_NEW_HANDLE +Windows.Win32.Foundation.ERROR_ORPHAN_NAME_EXHAUSTED +Windows.Win32.Foundation.ERROR_OUT_OF_PAPER +Windows.Win32.Foundation.ERROR_OUT_OF_STRUCTURES +Windows.Win32.Foundation.ERROR_OUTOFMEMORY +Windows.Win32.Foundation.ERROR_OVERRIDE_NOCHANGES +Windows.Win32.Foundation.ERROR_PAGE_FAULT_COPY_ON_WRITE +Windows.Win32.Foundation.ERROR_PAGE_FAULT_DEMAND_ZERO +Windows.Win32.Foundation.ERROR_PAGE_FAULT_GUARD_PAGE +Windows.Win32.Foundation.ERROR_PAGE_FAULT_PAGING_FILE +Windows.Win32.Foundation.ERROR_PAGE_FAULT_TRANSITION +Windows.Win32.Foundation.ERROR_PAGED_SYSTEM_RESOURCES +Windows.Win32.Foundation.ERROR_PAGEFILE_CREATE_FAILED +Windows.Win32.Foundation.ERROR_PAGEFILE_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_PAGEFILE_QUOTA +Windows.Win32.Foundation.ERROR_PAGEFILE_QUOTA_EXCEEDED +Windows.Win32.Foundation.ERROR_PARAMETER_QUOTA_EXCEEDED +Windows.Win32.Foundation.ERROR_PARTIAL_COPY +Windows.Win32.Foundation.ERROR_PARTITION_FAILURE +Windows.Win32.Foundation.ERROR_PARTITION_TERMINATING +Windows.Win32.Foundation.ERROR_PASSWORD_CHANGE_REQUIRED +Windows.Win32.Foundation.ERROR_PASSWORD_EXPIRED +Windows.Win32.Foundation.ERROR_PASSWORD_MUST_CHANGE +Windows.Win32.Foundation.ERROR_PASSWORD_RESTRICTION +Windows.Win32.Foundation.ERROR_PATCH_MANAGED_ADVERTISED_PRODUCT +Windows.Win32.Foundation.ERROR_PATCH_NO_SEQUENCE +Windows.Win32.Foundation.ERROR_PATCH_PACKAGE_INVALID +Windows.Win32.Foundation.ERROR_PATCH_PACKAGE_OPEN_FAILED +Windows.Win32.Foundation.ERROR_PATCH_PACKAGE_REJECTED +Windows.Win32.Foundation.ERROR_PATCH_PACKAGE_UNSUPPORTED +Windows.Win32.Foundation.ERROR_PATCH_REMOVAL_DISALLOWED +Windows.Win32.Foundation.ERROR_PATCH_REMOVAL_UNSUPPORTED +Windows.Win32.Foundation.ERROR_PATCH_TARGET_NOT_FOUND +Windows.Win32.Foundation.ERROR_PATH_BUSY +Windows.Win32.Foundation.ERROR_PATH_NOT_FOUND +Windows.Win32.Foundation.ERROR_PER_USER_TRUST_QUOTA_EXCEEDED +Windows.Win32.Foundation.ERROR_PIPE_BUSY +Windows.Win32.Foundation.ERROR_PIPE_CONNECTED +Windows.Win32.Foundation.ERROR_PIPE_LISTENING +Windows.Win32.Foundation.ERROR_PIPE_LOCAL +Windows.Win32.Foundation.ERROR_PIPE_NOT_CONNECTED +Windows.Win32.Foundation.ERROR_PKINIT_FAILURE +Windows.Win32.Foundation.ERROR_PLUGPLAY_QUERY_VETOED +Windows.Win32.Foundation.ERROR_PNP_BAD_MPS_TABLE +Windows.Win32.Foundation.ERROR_PNP_INVALID_ID +Windows.Win32.Foundation.ERROR_PNP_IRQ_TRANSLATION_FAILED +Windows.Win32.Foundation.ERROR_PNP_QUERY_REMOVE_DEVICE_TIMEOUT +Windows.Win32.Foundation.ERROR_PNP_QUERY_REMOVE_RELATED_DEVICE_TIMEOUT +Windows.Win32.Foundation.ERROR_PNP_QUERY_REMOVE_UNRELATED_DEVICE_TIMEOUT +Windows.Win32.Foundation.ERROR_PNP_REBOOT_REQUIRED +Windows.Win32.Foundation.ERROR_PNP_RESTART_ENUMERATION +Windows.Win32.Foundation.ERROR_PNP_TRANSLATION_FAILED +Windows.Win32.Foundation.ERROR_POINT_NOT_FOUND +Windows.Win32.Foundation.ERROR_POLICY_OBJECT_NOT_FOUND +Windows.Win32.Foundation.ERROR_POLICY_ONLY_IN_DS +Windows.Win32.Foundation.ERROR_POPUP_ALREADY_ACTIVE +Windows.Win32.Foundation.ERROR_PORT_MESSAGE_TOO_LONG +Windows.Win32.Foundation.ERROR_PORT_NOT_SET +Windows.Win32.Foundation.ERROR_PORT_UNREACHABLE +Windows.Win32.Foundation.ERROR_POSSIBLE_DEADLOCK +Windows.Win32.Foundation.ERROR_POTENTIAL_FILE_FOUND +Windows.Win32.Foundation.ERROR_PREDEFINED_HANDLE +Windows.Win32.Foundation.ERROR_PRIMARY_TRANSPORT_CONNECT_FAILED +Windows.Win32.Foundation.ERROR_PRINT_CANCELLED +Windows.Win32.Foundation.ERROR_PRINTER_ALREADY_EXISTS +Windows.Win32.Foundation.ERROR_PRINTER_DELETED +Windows.Win32.Foundation.ERROR_PRINTER_DRIVER_ALREADY_INSTALLED +Windows.Win32.Foundation.ERROR_PRINTQ_FULL +Windows.Win32.Foundation.ERROR_PRIVATE_DIALOG_INDEX +Windows.Win32.Foundation.ERROR_PRIVILEGE_NOT_HELD +Windows.Win32.Foundation.ERROR_PROC_NOT_FOUND +Windows.Win32.Foundation.ERROR_PROCESS_ABORTED +Windows.Win32.Foundation.ERROR_PROCESS_IN_JOB +Windows.Win32.Foundation.ERROR_PROCESS_IS_PROTECTED +Windows.Win32.Foundation.ERROR_PROCESS_MODE_ALREADY_BACKGROUND +Windows.Win32.Foundation.ERROR_PROCESS_MODE_NOT_BACKGROUND +Windows.Win32.Foundation.ERROR_PROCESS_NOT_IN_JOB +Windows.Win32.Foundation.ERROR_PRODUCT_UNINSTALLED +Windows.Win32.Foundation.ERROR_PRODUCT_VERSION +Windows.Win32.Foundation.ERROR_PROFILING_AT_LIMIT +Windows.Win32.Foundation.ERROR_PROFILING_NOT_STARTED +Windows.Win32.Foundation.ERROR_PROFILING_NOT_STOPPED +Windows.Win32.Foundation.ERROR_PROMOTION_ACTIVE +Windows.Win32.Foundation.ERROR_PROTOCOL_UNREACHABLE +Windows.Win32.Foundation.ERROR_PWD_HISTORY_CONFLICT +Windows.Win32.Foundation.ERROR_PWD_TOO_LONG +Windows.Win32.Foundation.ERROR_PWD_TOO_RECENT +Windows.Win32.Foundation.ERROR_PWD_TOO_SHORT +Windows.Win32.Foundation.ERROR_QUOTA_ACTIVITY +Windows.Win32.Foundation.ERROR_QUOTA_LIST_INCONSISTENT +Windows.Win32.Foundation.ERROR_RANGE_LIST_CONFLICT +Windows.Win32.Foundation.ERROR_RANGE_NOT_FOUND +Windows.Win32.Foundation.ERROR_READ_FAULT +Windows.Win32.Foundation.ERROR_RECEIVE_EXPEDITED +Windows.Win32.Foundation.ERROR_RECEIVE_PARTIAL +Windows.Win32.Foundation.ERROR_RECEIVE_PARTIAL_EXPEDITED +Windows.Win32.Foundation.ERROR_RECOVERY_FAILURE +Windows.Win32.Foundation.ERROR_REDIR_PAUSED +Windows.Win32.Foundation.ERROR_REDIRECTOR_HAS_OPEN_HANDLES +Windows.Win32.Foundation.ERROR_REG_NAT_CONSUMPTION +Windows.Win32.Foundation.ERROR_REGISTRY_CORRUPT +Windows.Win32.Foundation.ERROR_REGISTRY_HIVE_RECOVERED +Windows.Win32.Foundation.ERROR_REGISTRY_IO_FAILED +Windows.Win32.Foundation.ERROR_REGISTRY_QUOTA_LIMIT +Windows.Win32.Foundation.ERROR_REGISTRY_RECOVERED +Windows.Win32.Foundation.ERROR_RELOC_CHAIN_XEEDS_SEGLIM +Windows.Win32.Foundation.ERROR_REM_NOT_LIST +Windows.Win32.Foundation.ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED +Windows.Win32.Foundation.ERROR_REMOTE_SESSION_LIMIT_EXCEEDED +Windows.Win32.Foundation.ERROR_REMOTE_STORAGE_MEDIA_ERROR +Windows.Win32.Foundation.ERROR_REMOTE_STORAGE_NOT_ACTIVE +Windows.Win32.Foundation.ERROR_REPARSE +Windows.Win32.Foundation.ERROR_REPARSE_ATTRIBUTE_CONFLICT +Windows.Win32.Foundation.ERROR_REPARSE_OBJECT +Windows.Win32.Foundation.ERROR_REPARSE_POINT_ENCOUNTERED +Windows.Win32.Foundation.ERROR_REPARSE_TAG_INVALID +Windows.Win32.Foundation.ERROR_REPARSE_TAG_MISMATCH +Windows.Win32.Foundation.ERROR_REPLY_MESSAGE_MISMATCH +Windows.Win32.Foundation.ERROR_REQ_NOT_ACCEP +Windows.Win32.Foundation.ERROR_REQUEST_ABORTED +Windows.Win32.Foundation.ERROR_REQUEST_OUT_OF_SEQUENCE +Windows.Win32.Foundation.ERROR_REQUEST_PAUSED +Windows.Win32.Foundation.ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION +Windows.Win32.Foundation.ERROR_RESIDENT_FILE_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_RESOURCE_CALL_TIMED_OUT +Windows.Win32.Foundation.ERROR_RESOURCE_DATA_NOT_FOUND +Windows.Win32.Foundation.ERROR_RESOURCE_LANG_NOT_FOUND +Windows.Win32.Foundation.ERROR_RESOURCE_NAME_NOT_FOUND +Windows.Win32.Foundation.ERROR_RESOURCE_REQUIREMENTS_CHANGED +Windows.Win32.Foundation.ERROR_RESOURCE_TYPE_NOT_FOUND +Windows.Win32.Foundation.ERROR_RESTART_APPLICATION +Windows.Win32.Foundation.ERROR_RESUME_HIBERNATION +Windows.Win32.Foundation.ERROR_RETRY +Windows.Win32.Foundation.ERROR_RETURN_ADDRESS_HIJACK_ATTEMPT +Windows.Win32.Foundation.ERROR_REVISION_MISMATCH +Windows.Win32.Foundation.ERROR_RING2_STACK_IN_USE +Windows.Win32.Foundation.ERROR_RING2SEG_MUST_BE_MOVABLE +Windows.Win32.Foundation.ERROR_RMODE_APP +Windows.Win32.Foundation.ERROR_ROWSNOTRELEASED +Windows.Win32.Foundation.ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT +Windows.Win32.Foundation.ERROR_RUNLEVEL_SWITCH_TIMEOUT +Windows.Win32.Foundation.ERROR_RWRAW_ENCRYPTED_FILE_NOT_ENCRYPTED +Windows.Win32.Foundation.ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_FILEOFFSET +Windows.Win32.Foundation.ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_FILERANGE +Windows.Win32.Foundation.ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_PARAMETER +Windows.Win32.Foundation.ERROR_RXACT_COMMIT_FAILURE +Windows.Win32.Foundation.ERROR_RXACT_COMMIT_NECESSARY +Windows.Win32.Foundation.ERROR_RXACT_COMMITTED +Windows.Win32.Foundation.ERROR_RXACT_INVALID_STATE +Windows.Win32.Foundation.ERROR_RXACT_STATE_CREATED +Windows.Win32.Foundation.ERROR_SAM_INIT_FAILURE +Windows.Win32.Foundation.ERROR_SAME_DRIVE +Windows.Win32.Foundation.ERROR_SCOPE_NOT_FOUND +Windows.Win32.Foundation.ERROR_SCREEN_ALREADY_LOCKED +Windows.Win32.Foundation.ERROR_SCRUB_DATA_DISABLED +Windows.Win32.Foundation.ERROR_SECRET_TOO_LONG +Windows.Win32.Foundation.ERROR_SECTION_DIRECT_MAP_ONLY +Windows.Win32.Foundation.ERROR_SECTOR_NOT_FOUND +Windows.Win32.Foundation.ERROR_SECURITY_DENIES_OPERATION +Windows.Win32.Foundation.ERROR_SECURITY_STREAM_IS_INCONSISTENT +Windows.Win32.Foundation.ERROR_SEEK +Windows.Win32.Foundation.ERROR_SEEK_ON_DEVICE +Windows.Win32.Foundation.ERROR_SEGMENT_NOTIFICATION +Windows.Win32.Foundation.ERROR_SEM_IS_SET +Windows.Win32.Foundation.ERROR_SEM_NOT_FOUND +Windows.Win32.Foundation.ERROR_SEM_OWNER_DIED +Windows.Win32.Foundation.ERROR_SEM_TIMEOUT +Windows.Win32.Foundation.ERROR_SEM_USER_LIMIT +Windows.Win32.Foundation.ERROR_SERIAL_NO_DEVICE +Windows.Win32.Foundation.ERROR_SERVER_DISABLED +Windows.Win32.Foundation.ERROR_SERVER_HAS_OPEN_HANDLES +Windows.Win32.Foundation.ERROR_SERVER_NOT_DISABLED +Windows.Win32.Foundation.ERROR_SERVER_SHUTDOWN_IN_PROGRESS +Windows.Win32.Foundation.ERROR_SERVER_SID_MISMATCH +Windows.Win32.Foundation.ERROR_SERVER_TRANSPORT_CONFLICT +Windows.Win32.Foundation.ERROR_SERVICE_ALREADY_RUNNING +Windows.Win32.Foundation.ERROR_SERVICE_CANNOT_ACCEPT_CTRL +Windows.Win32.Foundation.ERROR_SERVICE_DATABASE_LOCKED +Windows.Win32.Foundation.ERROR_SERVICE_DEPENDENCY_DELETED +Windows.Win32.Foundation.ERROR_SERVICE_DEPENDENCY_FAIL +Windows.Win32.Foundation.ERROR_SERVICE_DISABLED +Windows.Win32.Foundation.ERROR_SERVICE_DOES_NOT_EXIST +Windows.Win32.Foundation.ERROR_SERVICE_EXISTS +Windows.Win32.Foundation.ERROR_SERVICE_LOGON_FAILED +Windows.Win32.Foundation.ERROR_SERVICE_MARKED_FOR_DELETE +Windows.Win32.Foundation.ERROR_SERVICE_NEVER_STARTED +Windows.Win32.Foundation.ERROR_SERVICE_NO_THREAD +Windows.Win32.Foundation.ERROR_SERVICE_NOT_ACTIVE +Windows.Win32.Foundation.ERROR_SERVICE_NOT_FOUND +Windows.Win32.Foundation.ERROR_SERVICE_NOT_IN_EXE +Windows.Win32.Foundation.ERROR_SERVICE_NOTIFICATION +Windows.Win32.Foundation.ERROR_SERVICE_NOTIFY_CLIENT_LAGGING +Windows.Win32.Foundation.ERROR_SERVICE_REQUEST_TIMEOUT +Windows.Win32.Foundation.ERROR_SERVICE_SPECIFIC_ERROR +Windows.Win32.Foundation.ERROR_SERVICE_START_HANG +Windows.Win32.Foundation.ERROR_SESSION_CREDENTIAL_CONFLICT +Windows.Win32.Foundation.ERROR_SESSION_KEY_TOO_SHORT +Windows.Win32.Foundation.ERROR_SET_CONTEXT_DENIED +Windows.Win32.Foundation.ERROR_SET_NOT_FOUND +Windows.Win32.Foundation.ERROR_SET_POWER_STATE_FAILED +Windows.Win32.Foundation.ERROR_SET_POWER_STATE_VETOED +Windows.Win32.Foundation.ERROR_SETCOUNT_ON_BAD_LB +Windows.Win32.Foundation.ERROR_SETMARK_DETECTED +Windows.Win32.Foundation.ERROR_SHARED_POLICY +Windows.Win32.Foundation.ERROR_SHARING_BUFFER_EXCEEDED +Windows.Win32.Foundation.ERROR_SHARING_PAUSED +Windows.Win32.Foundation.ERROR_SHARING_VIOLATION +Windows.Win32.Foundation.ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME +Windows.Win32.Foundation.ERROR_SHUTDOWN_DISKS_NOT_IN_MAINTENANCE_MODE +Windows.Win32.Foundation.ERROR_SHUTDOWN_IN_PROGRESS +Windows.Win32.Foundation.ERROR_SHUTDOWN_IS_SCHEDULED +Windows.Win32.Foundation.ERROR_SHUTDOWN_USERS_LOGGED_ON +Windows.Win32.Foundation.ERROR_SIGNAL_PENDING +Windows.Win32.Foundation.ERROR_SIGNAL_REFUSED +Windows.Win32.Foundation.ERROR_SINGLE_INSTANCE_APP +Windows.Win32.Foundation.ERROR_SMARTCARD_SUBSYSTEM_FAILURE +Windows.Win32.Foundation.ERROR_SMB1_NOT_AVAILABLE +Windows.Win32.Foundation.ERROR_SMB_GUEST_LOGON_BLOCKED +Windows.Win32.Foundation.ERROR_SMR_GARBAGE_COLLECTION_REQUIRED +Windows.Win32.Foundation.ERROR_SOME_NOT_MAPPED +Windows.Win32.Foundation.ERROR_SOURCE_ELEMENT_EMPTY +Windows.Win32.Foundation.ERROR_SPARSE_FILE_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_SPECIAL_ACCOUNT +Windows.Win32.Foundation.ERROR_SPECIAL_GROUP +Windows.Win32.Foundation.ERROR_SPECIAL_USER +Windows.Win32.Foundation.ERROR_SRC_SRV_DLL_LOAD_FAILED +Windows.Win32.Foundation.ERROR_STACK_BUFFER_OVERRUN +Windows.Win32.Foundation.ERROR_STACK_OVERFLOW +Windows.Win32.Foundation.ERROR_STACK_OVERFLOW_READ +Windows.Win32.Foundation.ERROR_STOPPED_ON_SYMLINK +Windows.Win32.Foundation.ERROR_STORAGE_LOST_DATA_PERSISTENCE +Windows.Win32.Foundation.ERROR_STORAGE_RESERVE_ALREADY_EXISTS +Windows.Win32.Foundation.ERROR_STORAGE_RESERVE_DOES_NOT_EXIST +Windows.Win32.Foundation.ERROR_STORAGE_RESERVE_ID_INVALID +Windows.Win32.Foundation.ERROR_STORAGE_RESERVE_NOT_EMPTY +Windows.Win32.Foundation.ERROR_STORAGE_STACK_ACCESS_DENIED +Windows.Win32.Foundation.ERROR_STORAGE_TOPOLOGY_ID_MISMATCH +Windows.Win32.Foundation.ERROR_STRICT_CFG_VIOLATION +Windows.Win32.Foundation.ERROR_SUBST_TO_JOIN +Windows.Win32.Foundation.ERROR_SUBST_TO_SUBST +Windows.Win32.Foundation.ERROR_SUCCESS +Windows.Win32.Foundation.ERROR_SUCCESS_REBOOT_INITIATED +Windows.Win32.Foundation.ERROR_SWAPERROR +Windows.Win32.Foundation.ERROR_SYMLINK_CLASS_DISABLED +Windows.Win32.Foundation.ERROR_SYMLINK_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED +Windows.Win32.Foundation.ERROR_SYNCHRONIZATION_REQUIRED +Windows.Win32.Foundation.ERROR_SYSTEM_HIVE_TOO_LARGE +Windows.Win32.Foundation.ERROR_SYSTEM_IMAGE_BAD_SIGNATURE +Windows.Win32.Foundation.ERROR_SYSTEM_POWERSTATE_COMPLEX_TRANSITION +Windows.Win32.Foundation.ERROR_SYSTEM_POWERSTATE_TRANSITION +Windows.Win32.Foundation.ERROR_SYSTEM_PROCESS_TERMINATED +Windows.Win32.Foundation.ERROR_SYSTEM_SHUTDOWN +Windows.Win32.Foundation.ERROR_SYSTEM_TRACE +Windows.Win32.Foundation.ERROR_THREAD_1_INACTIVE +Windows.Win32.Foundation.ERROR_THREAD_ALREADY_IN_TASK +Windows.Win32.Foundation.ERROR_THREAD_MODE_ALREADY_BACKGROUND +Windows.Win32.Foundation.ERROR_THREAD_MODE_NOT_BACKGROUND +Windows.Win32.Foundation.ERROR_THREAD_NOT_IN_PROCESS +Windows.Win32.Foundation.ERROR_THREAD_WAS_SUSPENDED +Windows.Win32.Foundation.ERROR_TIME_SENSITIVE_THREAD +Windows.Win32.Foundation.ERROR_TIME_SKEW +Windows.Win32.Foundation.ERROR_TIMEOUT +Windows.Win32.Foundation.ERROR_TIMER_NOT_CANCELED +Windows.Win32.Foundation.ERROR_TIMER_RESOLUTION_NOT_SET +Windows.Win32.Foundation.ERROR_TIMER_RESUME_IGNORED +Windows.Win32.Foundation.ERROR_TLW_WITH_WSCHILD +Windows.Win32.Foundation.ERROR_TOKEN_ALREADY_IN_USE +Windows.Win32.Foundation.ERROR_TOO_MANY_CMDS +Windows.Win32.Foundation.ERROR_TOO_MANY_CONTEXT_IDS +Windows.Win32.Foundation.ERROR_TOO_MANY_DESCRIPTORS +Windows.Win32.Foundation.ERROR_TOO_MANY_LINKS +Windows.Win32.Foundation.ERROR_TOO_MANY_LUIDS_REQUESTED +Windows.Win32.Foundation.ERROR_TOO_MANY_MODULES +Windows.Win32.Foundation.ERROR_TOO_MANY_MUXWAITERS +Windows.Win32.Foundation.ERROR_TOO_MANY_NAMES +Windows.Win32.Foundation.ERROR_TOO_MANY_OPEN_FILES +Windows.Win32.Foundation.ERROR_TOO_MANY_POSTS +Windows.Win32.Foundation.ERROR_TOO_MANY_SECRETS +Windows.Win32.Foundation.ERROR_TOO_MANY_SEM_REQUESTS +Windows.Win32.Foundation.ERROR_TOO_MANY_SEMAPHORES +Windows.Win32.Foundation.ERROR_TOO_MANY_SESS +Windows.Win32.Foundation.ERROR_TOO_MANY_SIDS +Windows.Win32.Foundation.ERROR_TOO_MANY_TCBS +Windows.Win32.Foundation.ERROR_TOO_MANY_THREADS +Windows.Win32.Foundation.ERROR_TRANSLATION_COMPLETE +Windows.Win32.Foundation.ERROR_TRUST_FAILURE +Windows.Win32.Foundation.ERROR_TRUSTED_DOMAIN_FAILURE +Windows.Win32.Foundation.ERROR_TRUSTED_RELATIONSHIP_FAILURE +Windows.Win32.Foundation.ERROR_UNABLE_TO_LOCK_MEDIA +Windows.Win32.Foundation.ERROR_UNABLE_TO_MOVE_REPLACEMENT +Windows.Win32.Foundation.ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 +Windows.Win32.Foundation.ERROR_UNABLE_TO_REMOVE_REPLACED +Windows.Win32.Foundation.ERROR_UNABLE_TO_UNLOAD_MEDIA +Windows.Win32.Foundation.ERROR_UNDEFINED_CHARACTER +Windows.Win32.Foundation.ERROR_UNDEFINED_SCOPE +Windows.Win32.Foundation.ERROR_UNEXP_NET_ERR +Windows.Win32.Foundation.ERROR_UNEXPECTED_MM_CREATE_ERR +Windows.Win32.Foundation.ERROR_UNEXPECTED_MM_EXTEND_ERR +Windows.Win32.Foundation.ERROR_UNEXPECTED_MM_MAP_ERROR +Windows.Win32.Foundation.ERROR_UNEXPECTED_NTCACHEMANAGER_ERROR +Windows.Win32.Foundation.ERROR_UNHANDLED_EXCEPTION +Windows.Win32.Foundation.ERROR_UNIDENTIFIED_ERROR +Windows.Win32.Foundation.ERROR_UNKNOWN_COMPONENT +Windows.Win32.Foundation.ERROR_UNKNOWN_FEATURE +Windows.Win32.Foundation.ERROR_UNKNOWN_PATCH +Windows.Win32.Foundation.ERROR_UNKNOWN_PORT +Windows.Win32.Foundation.ERROR_UNKNOWN_PRINTER_DRIVER +Windows.Win32.Foundation.ERROR_UNKNOWN_PRINTPROCESSOR +Windows.Win32.Foundation.ERROR_UNKNOWN_PRODUCT +Windows.Win32.Foundation.ERROR_UNKNOWN_PROPERTY +Windows.Win32.Foundation.ERROR_UNKNOWN_REVISION +Windows.Win32.Foundation.ERROR_UNRECOGNIZED_MEDIA +Windows.Win32.Foundation.ERROR_UNRECOGNIZED_VOLUME +Windows.Win32.Foundation.ERROR_UNSATISFIED_DEPENDENCIES +Windows.Win32.Foundation.ERROR_UNSUPPORTED_COMPRESSION +Windows.Win32.Foundation.ERROR_UNSUPPORTED_TYPE +Windows.Win32.Foundation.ERROR_UNTRUSTED_MOUNT_POINT +Windows.Win32.Foundation.ERROR_UNWIND +Windows.Win32.Foundation.ERROR_UNWIND_CONSOLIDATE +Windows.Win32.Foundation.ERROR_USER_APC +Windows.Win32.Foundation.ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED +Windows.Win32.Foundation.ERROR_USER_EXISTS +Windows.Win32.Foundation.ERROR_USER_MAPPED_FILE +Windows.Win32.Foundation.ERROR_USER_PROFILE_LOAD +Windows.Win32.Foundation.ERROR_VALIDATE_CONTINUE +Windows.Win32.Foundation.ERROR_VC_DISCONNECTED +Windows.Win32.Foundation.ERROR_VDM_DISALLOWED +Windows.Win32.Foundation.ERROR_VDM_HARD_ERROR +Windows.Win32.Foundation.ERROR_VERIFIER_STOP +Windows.Win32.Foundation.ERROR_VERSION_PARSE_ERROR +Windows.Win32.Foundation.ERROR_VIRUS_DELETED +Windows.Win32.Foundation.ERROR_VIRUS_INFECTED +Windows.Win32.Foundation.ERROR_VOLSNAP_HIBERNATE_READY +Windows.Win32.Foundation.ERROR_VOLSNAP_PREPARE_HIBERNATE +Windows.Win32.Foundation.ERROR_VOLUME_MOUNTED +Windows.Win32.Foundation.ERROR_VOLUME_NOT_CLUSTER_ALIGNED +Windows.Win32.Foundation.ERROR_VOLUME_NOT_SIS_ENABLED +Windows.Win32.Foundation.ERROR_VOLUME_NOT_SUPPORT_EFS +Windows.Win32.Foundation.ERROR_VOLUME_NOT_SUPPORTED +Windows.Win32.Foundation.ERROR_VOLUME_WRITE_ACCESS_DENIED +Windows.Win32.Foundation.ERROR_WAIT_1 +Windows.Win32.Foundation.ERROR_WAIT_2 +Windows.Win32.Foundation.ERROR_WAIT_3 +Windows.Win32.Foundation.ERROR_WAIT_63 +Windows.Win32.Foundation.ERROR_WAIT_FOR_OPLOCK +Windows.Win32.Foundation.ERROR_WAIT_NO_CHILDREN +Windows.Win32.Foundation.ERROR_WAKE_SYSTEM +Windows.Win32.Foundation.ERROR_WAKE_SYSTEM_DEBUGGER +Windows.Win32.Foundation.ERROR_WAS_LOCKED +Windows.Win32.Foundation.ERROR_WAS_UNLOCKED +Windows.Win32.Foundation.ERROR_WEAK_WHFBKEY_BLOCKED +Windows.Win32.Foundation.ERROR_WINDOW_NOT_COMBOBOX +Windows.Win32.Foundation.ERROR_WINDOW_NOT_DIALOG +Windows.Win32.Foundation.ERROR_WINDOW_OF_OTHER_THREAD +Windows.Win32.Foundation.ERROR_WIP_ENCRYPTION_FAILED +Windows.Win32.Foundation.ERROR_WOF_FILE_RESOURCE_TABLE_CORRUPT +Windows.Win32.Foundation.ERROR_WOF_WIM_HEADER_CORRUPT +Windows.Win32.Foundation.ERROR_WOF_WIM_RESOURCE_TABLE_CORRUPT +Windows.Win32.Foundation.ERROR_WORKING_SET_QUOTA +Windows.Win32.Foundation.ERROR_WOW_ASSERTION +Windows.Win32.Foundation.ERROR_WRITE_FAULT +Windows.Win32.Foundation.ERROR_WRITE_PROTECT +Windows.Win32.Foundation.ERROR_WRONG_COMPARTMENT +Windows.Win32.Foundation.ERROR_WRONG_DISK +Windows.Win32.Foundation.ERROR_WRONG_EFS +Windows.Win32.Foundation.ERROR_WRONG_PASSWORD +Windows.Win32.Foundation.ERROR_WRONG_TARGET_NAME +Windows.Win32.Foundation.ERROR_WX86_ERROR +Windows.Win32.Foundation.ERROR_WX86_WARNING +Windows.Win32.Foundation.ERROR_XML_PARSE_ERROR +Windows.Win32.Foundation.ERROR_XMLDSIG_ERROR +Windows.Win32.Foundation.EXCEPTION_STACK_OVERFLOW +Windows.Win32.Foundation.FALSE +Windows.Win32.Foundation.FARPROC +Windows.Win32.Foundation.FILETIME +Windows.Win32.Foundation.FRS_ERR_SYSVOL_POPULATE_TIMEOUT +Windows.Win32.Foundation.GENERIC_ACCESS_RIGHTS +Windows.Win32.Foundation.GENERIC_ALL +Windows.Win32.Foundation.GENERIC_EXECUTE +Windows.Win32.Foundation.GENERIC_READ +Windows.Win32.Foundation.GENERIC_WRITE +Windows.Win32.Foundation.GetLastError +Windows.Win32.Foundation.HANDLE +Windows.Win32.Foundation.HANDLE_FLAG_INHERIT +Windows.Win32.Foundation.HANDLE_FLAG_PROTECT_FROM_CLOSE +Windows.Win32.Foundation.HANDLE_FLAGS +Windows.Win32.Foundation.HMODULE +Windows.Win32.Foundation.MAX_PATH +Windows.Win32.Foundation.NO_ERROR +Windows.Win32.Foundation.NTSTATUS +Windows.Win32.Foundation.RtlNtStatusToDosError +Windows.Win32.Foundation.SetHandleInformation +Windows.Win32.Foundation.SetLastError +Windows.Win32.Foundation.STATUS_DELETE_PENDING +Windows.Win32.Foundation.STATUS_END_OF_FILE +Windows.Win32.Foundation.STATUS_INVALID_PARAMETER +Windows.Win32.Foundation.STATUS_NOT_IMPLEMENTED +Windows.Win32.Foundation.STATUS_PENDING +Windows.Win32.Foundation.STATUS_SUCCESS +Windows.Win32.Foundation.TRUE +Windows.Win32.Foundation.UNICODE_STRING +Windows.Win32.Foundation.WAIT_ABANDONED +Windows.Win32.Foundation.WAIT_ABANDONED_0 +Windows.Win32.Foundation.WAIT_FAILED +Windows.Win32.Foundation.WAIT_IO_COMPLETION +Windows.Win32.Foundation.WAIT_OBJECT_0 +Windows.Win32.Foundation.WAIT_TIMEOUT +Windows.Win32.Foundation.WIN32_ERROR +Windows.Win32.Globalization.COMPARESTRING_RESULT +Windows.Win32.Globalization.CompareStringOrdinal +Windows.Win32.Globalization.CP_UTF8 +Windows.Win32.Globalization.CSTR_EQUAL +Windows.Win32.Globalization.CSTR_GREATER_THAN +Windows.Win32.Globalization.CSTR_LESS_THAN +Windows.Win32.Globalization.MB_COMPOSITE +Windows.Win32.Globalization.MB_ERR_INVALID_CHARS +Windows.Win32.Globalization.MB_PRECOMPOSED +Windows.Win32.Globalization.MB_USEGLYPHCHARS +Windows.Win32.Globalization.MULTI_BYTE_TO_WIDE_CHAR_FLAGS +Windows.Win32.Globalization.MultiByteToWideChar +Windows.Win32.Globalization.WC_ERR_INVALID_CHARS +Windows.Win32.Globalization.WideCharToMultiByte +Windows.Win32.Networking.WinSock.accept +Windows.Win32.Networking.WinSock.ADDRESS_FAMILY +Windows.Win32.Networking.WinSock.ADDRINFOA +Windows.Win32.Networking.WinSock.AF_INET +Windows.Win32.Networking.WinSock.AF_INET6 +Windows.Win32.Networking.WinSock.AF_UNIX +Windows.Win32.Networking.WinSock.AF_UNSPEC +Windows.Win32.Networking.WinSock.bind +Windows.Win32.Networking.WinSock.closesocket +Windows.Win32.Networking.WinSock.connect +Windows.Win32.Networking.WinSock.FD_SET +Windows.Win32.Networking.WinSock.FIONBIO +Windows.Win32.Networking.WinSock.freeaddrinfo +Windows.Win32.Networking.WinSock.getaddrinfo +Windows.Win32.Networking.WinSock.getpeername +Windows.Win32.Networking.WinSock.getsockname +Windows.Win32.Networking.WinSock.getsockopt +Windows.Win32.Networking.WinSock.IN6_ADDR +Windows.Win32.Networking.WinSock.IN_ADDR +Windows.Win32.Networking.WinSock.INVALID_SOCKET +Windows.Win32.Networking.WinSock.ioctlsocket +Windows.Win32.Networking.WinSock.IP_ADD_MEMBERSHIP +Windows.Win32.Networking.WinSock.IP_DROP_MEMBERSHIP +Windows.Win32.Networking.WinSock.IP_MREQ +Windows.Win32.Networking.WinSock.IP_MULTICAST_LOOP +Windows.Win32.Networking.WinSock.IP_MULTICAST_TTL +Windows.Win32.Networking.WinSock.IP_TTL +Windows.Win32.Networking.WinSock.IPPROTO +Windows.Win32.Networking.WinSock.IPPROTO_AH +Windows.Win32.Networking.WinSock.IPPROTO_CBT +Windows.Win32.Networking.WinSock.IPPROTO_DSTOPTS +Windows.Win32.Networking.WinSock.IPPROTO_EGP +Windows.Win32.Networking.WinSock.IPPROTO_ESP +Windows.Win32.Networking.WinSock.IPPROTO_FRAGMENT +Windows.Win32.Networking.WinSock.IPPROTO_GGP +Windows.Win32.Networking.WinSock.IPPROTO_HOPOPTS +Windows.Win32.Networking.WinSock.IPPROTO_ICLFXBM +Windows.Win32.Networking.WinSock.IPPROTO_ICMP +Windows.Win32.Networking.WinSock.IPPROTO_ICMPV6 +Windows.Win32.Networking.WinSock.IPPROTO_IDP +Windows.Win32.Networking.WinSock.IPPROTO_IGMP +Windows.Win32.Networking.WinSock.IPPROTO_IGP +Windows.Win32.Networking.WinSock.IPPROTO_IP +Windows.Win32.Networking.WinSock.IPPROTO_IPV4 +Windows.Win32.Networking.WinSock.IPPROTO_IPV6 +Windows.Win32.Networking.WinSock.IPPROTO_L2TP +Windows.Win32.Networking.WinSock.IPPROTO_MAX +Windows.Win32.Networking.WinSock.IPPROTO_ND +Windows.Win32.Networking.WinSock.IPPROTO_NONE +Windows.Win32.Networking.WinSock.IPPROTO_PGM +Windows.Win32.Networking.WinSock.IPPROTO_PIM +Windows.Win32.Networking.WinSock.IPPROTO_PUP +Windows.Win32.Networking.WinSock.IPPROTO_RAW +Windows.Win32.Networking.WinSock.IPPROTO_RDP +Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_IPSEC +Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_IPSECOFFLOAD +Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_MAX +Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_RAW +Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_WNV +Windows.Win32.Networking.WinSock.IPPROTO_RM +Windows.Win32.Networking.WinSock.IPPROTO_ROUTING +Windows.Win32.Networking.WinSock.IPPROTO_SCTP +Windows.Win32.Networking.WinSock.IPPROTO_ST +Windows.Win32.Networking.WinSock.IPPROTO_TCP +Windows.Win32.Networking.WinSock.IPPROTO_UDP +Windows.Win32.Networking.WinSock.IPV6_ADD_MEMBERSHIP +Windows.Win32.Networking.WinSock.IPV6_DROP_MEMBERSHIP +Windows.Win32.Networking.WinSock.IPV6_MREQ +Windows.Win32.Networking.WinSock.IPV6_MULTICAST_LOOP +Windows.Win32.Networking.WinSock.IPV6_V6ONLY +Windows.Win32.Networking.WinSock.LINGER +Windows.Win32.Networking.WinSock.listen +Windows.Win32.Networking.WinSock.LPWSAOVERLAPPED_COMPLETION_ROUTINE +Windows.Win32.Networking.WinSock.MSG_DONTROUTE +Windows.Win32.Networking.WinSock.MSG_OOB +Windows.Win32.Networking.WinSock.MSG_PEEK +Windows.Win32.Networking.WinSock.MSG_PUSH_IMMEDIATE +Windows.Win32.Networking.WinSock.MSG_WAITALL +Windows.Win32.Networking.WinSock.recv +Windows.Win32.Networking.WinSock.recvfrom +Windows.Win32.Networking.WinSock.SD_BOTH +Windows.Win32.Networking.WinSock.SD_RECEIVE +Windows.Win32.Networking.WinSock.SD_SEND +Windows.Win32.Networking.WinSock.select +Windows.Win32.Networking.WinSock.send +Windows.Win32.Networking.WinSock.SEND_RECV_FLAGS +Windows.Win32.Networking.WinSock.sendto +Windows.Win32.Networking.WinSock.setsockopt +Windows.Win32.Networking.WinSock.shutdown +Windows.Win32.Networking.WinSock.SO_BROADCAST +Windows.Win32.Networking.WinSock.SO_ERROR +Windows.Win32.Networking.WinSock.SO_LINGER +Windows.Win32.Networking.WinSock.SO_RCVTIMEO +Windows.Win32.Networking.WinSock.SO_SNDTIMEO +Windows.Win32.Networking.WinSock.SOCK_DGRAM +Windows.Win32.Networking.WinSock.SOCK_RAW +Windows.Win32.Networking.WinSock.SOCK_RDM +Windows.Win32.Networking.WinSock.SOCK_SEQPACKET +Windows.Win32.Networking.WinSock.SOCK_STREAM +Windows.Win32.Networking.WinSock.SOCKADDR +Windows.Win32.Networking.WinSock.SOCKADDR_UN +Windows.Win32.Networking.WinSock.SOCKET +Windows.Win32.Networking.WinSock.SOCKET_ERROR +Windows.Win32.Networking.WinSock.SOL_SOCKET +Windows.Win32.Networking.WinSock.TCP_NODELAY +Windows.Win32.Networking.WinSock.TIMEVAL +Windows.Win32.Networking.WinSock.WINSOCK_SHUTDOWN_HOW +Windows.Win32.Networking.WinSock.WINSOCK_SOCKET_TYPE +Windows.Win32.Networking.WinSock.WSA_E_CANCELLED +Windows.Win32.Networking.WinSock.WSA_E_NO_MORE +Windows.Win32.Networking.WinSock.WSA_ERROR +Windows.Win32.Networking.WinSock.WSA_FLAG_NO_HANDLE_INHERIT +Windows.Win32.Networking.WinSock.WSA_FLAG_OVERLAPPED +Windows.Win32.Networking.WinSock.WSA_INVALID_HANDLE +Windows.Win32.Networking.WinSock.WSA_INVALID_PARAMETER +Windows.Win32.Networking.WinSock.WSA_IO_INCOMPLETE +Windows.Win32.Networking.WinSock.WSA_IO_PENDING +Windows.Win32.Networking.WinSock.WSA_IPSEC_NAME_POLICY_ERROR +Windows.Win32.Networking.WinSock.WSA_NOT_ENOUGH_MEMORY +Windows.Win32.Networking.WinSock.WSA_OPERATION_ABORTED +Windows.Win32.Networking.WinSock.WSA_QOS_ADMISSION_FAILURE +Windows.Win32.Networking.WinSock.WSA_QOS_BAD_OBJECT +Windows.Win32.Networking.WinSock.WSA_QOS_BAD_STYLE +Windows.Win32.Networking.WinSock.WSA_QOS_EFILTERCOUNT +Windows.Win32.Networking.WinSock.WSA_QOS_EFILTERSTYLE +Windows.Win32.Networking.WinSock.WSA_QOS_EFILTERTYPE +Windows.Win32.Networking.WinSock.WSA_QOS_EFLOWCOUNT +Windows.Win32.Networking.WinSock.WSA_QOS_EFLOWDESC +Windows.Win32.Networking.WinSock.WSA_QOS_EFLOWSPEC +Windows.Win32.Networking.WinSock.WSA_QOS_EOBJLENGTH +Windows.Win32.Networking.WinSock.WSA_QOS_EPOLICYOBJ +Windows.Win32.Networking.WinSock.WSA_QOS_EPROVSPECBUF +Windows.Win32.Networking.WinSock.WSA_QOS_EPSFILTERSPEC +Windows.Win32.Networking.WinSock.WSA_QOS_EPSFLOWSPEC +Windows.Win32.Networking.WinSock.WSA_QOS_ESDMODEOBJ +Windows.Win32.Networking.WinSock.WSA_QOS_ESERVICETYPE +Windows.Win32.Networking.WinSock.WSA_QOS_ESHAPERATEOBJ +Windows.Win32.Networking.WinSock.WSA_QOS_EUNKOWNPSOBJ +Windows.Win32.Networking.WinSock.WSA_QOS_GENERIC_ERROR +Windows.Win32.Networking.WinSock.WSA_QOS_NO_RECEIVERS +Windows.Win32.Networking.WinSock.WSA_QOS_NO_SENDERS +Windows.Win32.Networking.WinSock.WSA_QOS_POLICY_FAILURE +Windows.Win32.Networking.WinSock.WSA_QOS_RECEIVERS +Windows.Win32.Networking.WinSock.WSA_QOS_REQUEST_CONFIRMED +Windows.Win32.Networking.WinSock.WSA_QOS_RESERVED_PETYPE +Windows.Win32.Networking.WinSock.WSA_QOS_SENDERS +Windows.Win32.Networking.WinSock.WSA_QOS_TRAFFIC_CTRL_ERROR +Windows.Win32.Networking.WinSock.WSA_SECURE_HOST_NOT_FOUND +Windows.Win32.Networking.WinSock.WSA_WAIT_EVENT_0 +Windows.Win32.Networking.WinSock.WSA_WAIT_IO_COMPLETION +Windows.Win32.Networking.WinSock.WSABASEERR +Windows.Win32.Networking.WinSock.WSABUF +Windows.Win32.Networking.WinSock.WSACleanup +Windows.Win32.Networking.WinSock.WSADATA +Windows.Win32.Networking.WinSock.WSADuplicateSocketW +Windows.Win32.Networking.WinSock.WSAEACCES +Windows.Win32.Networking.WinSock.WSAEADDRINUSE +Windows.Win32.Networking.WinSock.WSAEADDRNOTAVAIL +Windows.Win32.Networking.WinSock.WSAEAFNOSUPPORT +Windows.Win32.Networking.WinSock.WSAEALREADY +Windows.Win32.Networking.WinSock.WSAEBADF +Windows.Win32.Networking.WinSock.WSAECANCELLED +Windows.Win32.Networking.WinSock.WSAECONNABORTED +Windows.Win32.Networking.WinSock.WSAECONNREFUSED +Windows.Win32.Networking.WinSock.WSAECONNRESET +Windows.Win32.Networking.WinSock.WSAEDESTADDRREQ +Windows.Win32.Networking.WinSock.WSAEDISCON +Windows.Win32.Networking.WinSock.WSAEDQUOT +Windows.Win32.Networking.WinSock.WSAEFAULT +Windows.Win32.Networking.WinSock.WSAEHOSTDOWN +Windows.Win32.Networking.WinSock.WSAEHOSTUNREACH +Windows.Win32.Networking.WinSock.WSAEINPROGRESS +Windows.Win32.Networking.WinSock.WSAEINTR +Windows.Win32.Networking.WinSock.WSAEINVAL +Windows.Win32.Networking.WinSock.WSAEINVALIDPROCTABLE +Windows.Win32.Networking.WinSock.WSAEINVALIDPROVIDER +Windows.Win32.Networking.WinSock.WSAEISCONN +Windows.Win32.Networking.WinSock.WSAELOOP +Windows.Win32.Networking.WinSock.WSAEMFILE +Windows.Win32.Networking.WinSock.WSAEMSGSIZE +Windows.Win32.Networking.WinSock.WSAENAMETOOLONG +Windows.Win32.Networking.WinSock.WSAENETDOWN +Windows.Win32.Networking.WinSock.WSAENETRESET +Windows.Win32.Networking.WinSock.WSAENETUNREACH +Windows.Win32.Networking.WinSock.WSAENOBUFS +Windows.Win32.Networking.WinSock.WSAENOMORE +Windows.Win32.Networking.WinSock.WSAENOPROTOOPT +Windows.Win32.Networking.WinSock.WSAENOTCONN +Windows.Win32.Networking.WinSock.WSAENOTEMPTY +Windows.Win32.Networking.WinSock.WSAENOTSOCK +Windows.Win32.Networking.WinSock.WSAEOPNOTSUPP +Windows.Win32.Networking.WinSock.WSAEPFNOSUPPORT +Windows.Win32.Networking.WinSock.WSAEPROCLIM +Windows.Win32.Networking.WinSock.WSAEPROTONOSUPPORT +Windows.Win32.Networking.WinSock.WSAEPROTOTYPE +Windows.Win32.Networking.WinSock.WSAEPROVIDERFAILEDINIT +Windows.Win32.Networking.WinSock.WSAEREFUSED +Windows.Win32.Networking.WinSock.WSAEREMOTE +Windows.Win32.Networking.WinSock.WSAESHUTDOWN +Windows.Win32.Networking.WinSock.WSAESOCKTNOSUPPORT +Windows.Win32.Networking.WinSock.WSAESTALE +Windows.Win32.Networking.WinSock.WSAETIMEDOUT +Windows.Win32.Networking.WinSock.WSAETOOMANYREFS +Windows.Win32.Networking.WinSock.WSAEUSERS +Windows.Win32.Networking.WinSock.WSAEWOULDBLOCK +Windows.Win32.Networking.WinSock.WSAGetLastError +Windows.Win32.Networking.WinSock.WSAHOST_NOT_FOUND +Windows.Win32.Networking.WinSock.WSANO_DATA +Windows.Win32.Networking.WinSock.WSANO_RECOVERY +Windows.Win32.Networking.WinSock.WSANOTINITIALISED +Windows.Win32.Networking.WinSock.WSAPROTOCOL_INFOW +Windows.Win32.Networking.WinSock.WSAPROTOCOLCHAIN +Windows.Win32.Networking.WinSock.WSARecv +Windows.Win32.Networking.WinSock.WSASend +Windows.Win32.Networking.WinSock.WSASERVICE_NOT_FOUND +Windows.Win32.Networking.WinSock.WSASocketW +Windows.Win32.Networking.WinSock.WSASYSCALLFAILURE +Windows.Win32.Networking.WinSock.WSASYSNOTREADY +Windows.Win32.Networking.WinSock.WSATRY_AGAIN +Windows.Win32.Networking.WinSock.WSATYPE_NOT_FOUND +Windows.Win32.Networking.WinSock.WSAVERNOTSUPPORTED +Windows.Win32.Security.Authentication.Identity.RtlGenRandom +Windows.Win32.Security.Cryptography.BCRYPT_ALG_HANDLE +Windows.Win32.Security.Cryptography.BCRYPT_USE_SYSTEM_PREFERRED_RNG +Windows.Win32.Security.Cryptography.BCryptGenRandom +Windows.Win32.Security.Cryptography.BCRYPTGENRANDOM_FLAGS +Windows.Win32.Security.SECURITY_ATTRIBUTES +Windows.Win32.Security.TOKEN_ACCESS_MASK +Windows.Win32.Security.TOKEN_ACCESS_PSEUDO_HANDLE +Windows.Win32.Security.TOKEN_ACCESS_PSEUDO_HANDLE_WIN8 +Windows.Win32.Security.TOKEN_ACCESS_SYSTEM_SECURITY +Windows.Win32.Security.TOKEN_ADJUST_DEFAULT +Windows.Win32.Security.TOKEN_ADJUST_GROUPS +Windows.Win32.Security.TOKEN_ADJUST_PRIVILEGES +Windows.Win32.Security.TOKEN_ADJUST_SESSIONID +Windows.Win32.Security.TOKEN_ALL_ACCESS +Windows.Win32.Security.TOKEN_ASSIGN_PRIMARY +Windows.Win32.Security.TOKEN_DELETE +Windows.Win32.Security.TOKEN_DUPLICATE +Windows.Win32.Security.TOKEN_EXECUTE +Windows.Win32.Security.TOKEN_IMPERSONATE +Windows.Win32.Security.TOKEN_QUERY +Windows.Win32.Security.TOKEN_QUERY_SOURCE +Windows.Win32.Security.TOKEN_READ +Windows.Win32.Security.TOKEN_READ_CONTROL +Windows.Win32.Security.TOKEN_TRUST_CONSTRAINT_MASK +Windows.Win32.Security.TOKEN_WRITE +Windows.Win32.Security.TOKEN_WRITE_DAC +Windows.Win32.Security.TOKEN_WRITE_OWNER +Windows.Win32.Storage.FileSystem.BY_HANDLE_FILE_INFORMATION +Windows.Win32.Storage.FileSystem.CALLBACK_CHUNK_FINISHED +Windows.Win32.Storage.FileSystem.CALLBACK_STREAM_SWITCH +Windows.Win32.Storage.FileSystem.CopyFileExW +Windows.Win32.Storage.FileSystem.CREATE_ALWAYS +Windows.Win32.Storage.FileSystem.CREATE_NEW +Windows.Win32.Storage.FileSystem.CreateDirectoryW +Windows.Win32.Storage.FileSystem.CreateFileW +Windows.Win32.Storage.FileSystem.CreateHardLinkW +Windows.Win32.Storage.FileSystem.CreateSymbolicLinkW +Windows.Win32.Storage.FileSystem.DELETE +Windows.Win32.Storage.FileSystem.DeleteFileW +Windows.Win32.Storage.FileSystem.FILE_ACCESS_RIGHTS +Windows.Win32.Storage.FileSystem.FILE_ADD_FILE +Windows.Win32.Storage.FileSystem.FILE_ADD_SUBDIRECTORY +Windows.Win32.Storage.FileSystem.FILE_ALL_ACCESS +Windows.Win32.Storage.FileSystem.FILE_ALLOCATION_INFO +Windows.Win32.Storage.FileSystem.FILE_APPEND_DATA +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_ARCHIVE +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_COMPRESSED +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_DEVICE +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_DIRECTORY +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_EA +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_ENCRYPTED +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_HIDDEN +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_INTEGRITY_STREAM +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_NO_SCRUB_DATA +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_NORMAL +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_NOT_CONTENT_INDEXED +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_OFFLINE +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_PINNED +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_READONLY +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_RECALL_ON_OPEN +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_REPARSE_POINT +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_SPARSE_FILE +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_SYSTEM +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_TAG_INFO +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_TEMPORARY +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_UNPINNED +Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_VIRTUAL +Windows.Win32.Storage.FileSystem.FILE_BASIC_INFO +Windows.Win32.Storage.FileSystem.FILE_BEGIN +Windows.Win32.Storage.FileSystem.FILE_CREATE_PIPE_INSTANCE +Windows.Win32.Storage.FileSystem.FILE_CREATION_DISPOSITION +Windows.Win32.Storage.FileSystem.FILE_CURRENT +Windows.Win32.Storage.FileSystem.FILE_DELETE_CHILD +Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_DELETE +Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_DO_NOT_DELETE +Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_FORCE_IMAGE_SECTION_CHECK +Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE +Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_ON_CLOSE +Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_POSIX_SEMANTICS +Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_INFO +Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_INFO_EX +Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_INFO_EX_FLAGS +Windows.Win32.Storage.FileSystem.FILE_END +Windows.Win32.Storage.FileSystem.FILE_END_OF_FILE_INFO +Windows.Win32.Storage.FileSystem.FILE_EXECUTE +Windows.Win32.Storage.FileSystem.FILE_FLAG_BACKUP_SEMANTICS +Windows.Win32.Storage.FileSystem.FILE_FLAG_DELETE_ON_CLOSE +Windows.Win32.Storage.FileSystem.FILE_FLAG_FIRST_PIPE_INSTANCE +Windows.Win32.Storage.FileSystem.FILE_FLAG_NO_BUFFERING +Windows.Win32.Storage.FileSystem.FILE_FLAG_OPEN_NO_RECALL +Windows.Win32.Storage.FileSystem.FILE_FLAG_OPEN_REPARSE_POINT +Windows.Win32.Storage.FileSystem.FILE_FLAG_OVERLAPPED +Windows.Win32.Storage.FileSystem.FILE_FLAG_POSIX_SEMANTICS +Windows.Win32.Storage.FileSystem.FILE_FLAG_RANDOM_ACCESS +Windows.Win32.Storage.FileSystem.FILE_FLAG_SEQUENTIAL_SCAN +Windows.Win32.Storage.FileSystem.FILE_FLAG_SESSION_AWARE +Windows.Win32.Storage.FileSystem.FILE_FLAG_WRITE_THROUGH +Windows.Win32.Storage.FileSystem.FILE_FLAGS_AND_ATTRIBUTES +Windows.Win32.Storage.FileSystem.FILE_GENERIC_EXECUTE +Windows.Win32.Storage.FileSystem.FILE_GENERIC_READ +Windows.Win32.Storage.FileSystem.FILE_GENERIC_WRITE +Windows.Win32.Storage.FileSystem.FILE_ID_BOTH_DIR_INFO +Windows.Win32.Storage.FileSystem.FILE_INFO_BY_HANDLE_CLASS +Windows.Win32.Storage.FileSystem.FILE_IO_PRIORITY_HINT_INFO +Windows.Win32.Storage.FileSystem.FILE_LIST_DIRECTORY +Windows.Win32.Storage.FileSystem.FILE_NAME_NORMALIZED +Windows.Win32.Storage.FileSystem.FILE_NAME_OPENED +Windows.Win32.Storage.FileSystem.FILE_READ_ATTRIBUTES +Windows.Win32.Storage.FileSystem.FILE_READ_DATA +Windows.Win32.Storage.FileSystem.FILE_READ_EA +Windows.Win32.Storage.FileSystem.FILE_SHARE_DELETE +Windows.Win32.Storage.FileSystem.FILE_SHARE_MODE +Windows.Win32.Storage.FileSystem.FILE_SHARE_NONE +Windows.Win32.Storage.FileSystem.FILE_SHARE_READ +Windows.Win32.Storage.FileSystem.FILE_SHARE_WRITE +Windows.Win32.Storage.FileSystem.FILE_STANDARD_INFO +Windows.Win32.Storage.FileSystem.FILE_TRAVERSE +Windows.Win32.Storage.FileSystem.FILE_TYPE +Windows.Win32.Storage.FileSystem.FILE_TYPE_CHAR +Windows.Win32.Storage.FileSystem.FILE_TYPE_DISK +Windows.Win32.Storage.FileSystem.FILE_TYPE_PIPE +Windows.Win32.Storage.FileSystem.FILE_TYPE_REMOTE +Windows.Win32.Storage.FileSystem.FILE_TYPE_UNKNOWN +Windows.Win32.Storage.FileSystem.FILE_WRITE_ATTRIBUTES +Windows.Win32.Storage.FileSystem.FILE_WRITE_DATA +Windows.Win32.Storage.FileSystem.FILE_WRITE_EA +Windows.Win32.Storage.FileSystem.FileAlignmentInfo +Windows.Win32.Storage.FileSystem.FileAllocationInfo +Windows.Win32.Storage.FileSystem.FileAttributeTagInfo +Windows.Win32.Storage.FileSystem.FileBasicInfo +Windows.Win32.Storage.FileSystem.FileCaseSensitiveInfo +Windows.Win32.Storage.FileSystem.FileCompressionInfo +Windows.Win32.Storage.FileSystem.FileDispositionInfo +Windows.Win32.Storage.FileSystem.FileDispositionInfoEx +Windows.Win32.Storage.FileSystem.FileEndOfFileInfo +Windows.Win32.Storage.FileSystem.FileFullDirectoryInfo +Windows.Win32.Storage.FileSystem.FileFullDirectoryRestartInfo +Windows.Win32.Storage.FileSystem.FileIdBothDirectoryInfo +Windows.Win32.Storage.FileSystem.FileIdBothDirectoryRestartInfo +Windows.Win32.Storage.FileSystem.FileIdExtdDirectoryInfo +Windows.Win32.Storage.FileSystem.FileIdExtdDirectoryRestartInfo +Windows.Win32.Storage.FileSystem.FileIdInfo +Windows.Win32.Storage.FileSystem.FileIoPriorityHintInfo +Windows.Win32.Storage.FileSystem.FileNameInfo +Windows.Win32.Storage.FileSystem.FileNormalizedNameInfo +Windows.Win32.Storage.FileSystem.FileRemoteProtocolInfo +Windows.Win32.Storage.FileSystem.FileRenameInfo +Windows.Win32.Storage.FileSystem.FileRenameInfoEx +Windows.Win32.Storage.FileSystem.FileStandardInfo +Windows.Win32.Storage.FileSystem.FileStorageInfo +Windows.Win32.Storage.FileSystem.FileStreamInfo +Windows.Win32.Storage.FileSystem.FindClose +Windows.Win32.Storage.FileSystem.FindFirstFileW +Windows.Win32.Storage.FileSystem.FindNextFileW +Windows.Win32.Storage.FileSystem.FlushFileBuffers +Windows.Win32.Storage.FileSystem.GetFileAttributesW +Windows.Win32.Storage.FileSystem.GetFileInformationByHandle +Windows.Win32.Storage.FileSystem.GetFileInformationByHandleEx +Windows.Win32.Storage.FileSystem.GetFileType +Windows.Win32.Storage.FileSystem.GETFINALPATHNAMEBYHANDLE_FLAGS +Windows.Win32.Storage.FileSystem.GetFinalPathNameByHandleW +Windows.Win32.Storage.FileSystem.GetFullPathNameW +Windows.Win32.Storage.FileSystem.GetTempPathW +Windows.Win32.Storage.FileSystem.INVALID_FILE_ATTRIBUTES +Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE +Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE_CALLBACK_REASON +Windows.Win32.Storage.FileSystem.MAXIMUM_REPARSE_DATA_BUFFER_SIZE +Windows.Win32.Storage.FileSystem.MaximumFileInfoByHandleClass +Windows.Win32.Storage.FileSystem.MOVE_FILE_FLAGS +Windows.Win32.Storage.FileSystem.MOVEFILE_COPY_ALLOWED +Windows.Win32.Storage.FileSystem.MOVEFILE_CREATE_HARDLINK +Windows.Win32.Storage.FileSystem.MOVEFILE_DELAY_UNTIL_REBOOT +Windows.Win32.Storage.FileSystem.MOVEFILE_FAIL_IF_NOT_TRACKABLE +Windows.Win32.Storage.FileSystem.MOVEFILE_REPLACE_EXISTING +Windows.Win32.Storage.FileSystem.MOVEFILE_WRITE_THROUGH +Windows.Win32.Storage.FileSystem.MoveFileExW +Windows.Win32.Storage.FileSystem.OPEN_ALWAYS +Windows.Win32.Storage.FileSystem.OPEN_EXISTING +Windows.Win32.Storage.FileSystem.PIPE_ACCESS_DUPLEX +Windows.Win32.Storage.FileSystem.PIPE_ACCESS_INBOUND +Windows.Win32.Storage.FileSystem.PIPE_ACCESS_OUTBOUND +Windows.Win32.Storage.FileSystem.READ_CONTROL +Windows.Win32.Storage.FileSystem.ReadFile +Windows.Win32.Storage.FileSystem.ReadFileEx +Windows.Win32.Storage.FileSystem.RemoveDirectoryW +Windows.Win32.Storage.FileSystem.SECURITY_ANONYMOUS +Windows.Win32.Storage.FileSystem.SECURITY_CONTEXT_TRACKING +Windows.Win32.Storage.FileSystem.SECURITY_DELEGATION +Windows.Win32.Storage.FileSystem.SECURITY_EFFECTIVE_ONLY +Windows.Win32.Storage.FileSystem.SECURITY_IDENTIFICATION +Windows.Win32.Storage.FileSystem.SECURITY_IMPERSONATION +Windows.Win32.Storage.FileSystem.SECURITY_SQOS_PRESENT +Windows.Win32.Storage.FileSystem.SECURITY_VALID_SQOS_FLAGS +Windows.Win32.Storage.FileSystem.SET_FILE_POINTER_MOVE_METHOD +Windows.Win32.Storage.FileSystem.SetFileAttributesW +Windows.Win32.Storage.FileSystem.SetFileInformationByHandle +Windows.Win32.Storage.FileSystem.SetFilePointerEx +Windows.Win32.Storage.FileSystem.SetFileTime +Windows.Win32.Storage.FileSystem.SPECIFIC_RIGHTS_ALL +Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_ALL +Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_EXECUTE +Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_READ +Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_REQUIRED +Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_WRITE +Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE +Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAG_DIRECTORY +Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAGS +Windows.Win32.Storage.FileSystem.SYNCHRONIZE +Windows.Win32.Storage.FileSystem.TRUNCATE_EXISTING +Windows.Win32.Storage.FileSystem.VOLUME_NAME_DOS +Windows.Win32.Storage.FileSystem.VOLUME_NAME_GUID +Windows.Win32.Storage.FileSystem.VOLUME_NAME_NONE +Windows.Win32.Storage.FileSystem.WIN32_FIND_DATAW +Windows.Win32.Storage.FileSystem.WRITE_DAC +Windows.Win32.Storage.FileSystem.WRITE_OWNER +Windows.Win32.Storage.FileSystem.WriteFileEx +Windows.Win32.System.Console.CONSOLE_MODE +Windows.Win32.System.Console.CONSOLE_READCONSOLE_CONTROL +Windows.Win32.System.Console.DISABLE_NEWLINE_AUTO_RETURN +Windows.Win32.System.Console.ENABLE_AUTO_POSITION +Windows.Win32.System.Console.ENABLE_ECHO_INPUT +Windows.Win32.System.Console.ENABLE_EXTENDED_FLAGS +Windows.Win32.System.Console.ENABLE_INSERT_MODE +Windows.Win32.System.Console.ENABLE_LINE_INPUT +Windows.Win32.System.Console.ENABLE_LVB_GRID_WORLDWIDE +Windows.Win32.System.Console.ENABLE_MOUSE_INPUT +Windows.Win32.System.Console.ENABLE_PROCESSED_INPUT +Windows.Win32.System.Console.ENABLE_PROCESSED_OUTPUT +Windows.Win32.System.Console.ENABLE_QUICK_EDIT_MODE +Windows.Win32.System.Console.ENABLE_VIRTUAL_TERMINAL_INPUT +Windows.Win32.System.Console.ENABLE_VIRTUAL_TERMINAL_PROCESSING +Windows.Win32.System.Console.ENABLE_WINDOW_INPUT +Windows.Win32.System.Console.ENABLE_WRAP_AT_EOL_OUTPUT +Windows.Win32.System.Console.GetConsoleMode +Windows.Win32.System.Console.GetStdHandle +Windows.Win32.System.Console.ReadConsoleW +Windows.Win32.System.Console.STD_ERROR_HANDLE +Windows.Win32.System.Console.STD_HANDLE +Windows.Win32.System.Console.STD_INPUT_HANDLE +Windows.Win32.System.Console.STD_OUTPUT_HANDLE +Windows.Win32.System.Console.WriteConsoleW +Windows.Win32.System.Diagnostics.Debug.ARM64_NT_NEON128 +Windows.Win32.System.Diagnostics.Debug.CONTEXT +Windows.Win32.System.Diagnostics.Debug.EXCEPTION_RECORD +Windows.Win32.System.Diagnostics.Debug.FACILITY_CODE +Windows.Win32.System.Diagnostics.Debug.FACILITY_NT_BIT +Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_ALLOCATE_BUFFER +Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_ARGUMENT_ARRAY +Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_HMODULE +Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_STRING +Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_SYSTEM +Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_IGNORE_INSERTS +Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_OPTIONS +Windows.Win32.System.Diagnostics.Debug.FormatMessageW +Windows.Win32.System.Diagnostics.Debug.M128A +Windows.Win32.System.Diagnostics.Debug.XSAVE_FORMAT +Windows.Win32.System.Environment.FreeEnvironmentStringsW +Windows.Win32.System.Environment.GetCommandLineW +Windows.Win32.System.Environment.GetCurrentDirectoryW +Windows.Win32.System.Environment.GetEnvironmentStringsW +Windows.Win32.System.Environment.GetEnvironmentVariableW +Windows.Win32.System.Environment.SetCurrentDirectoryW +Windows.Win32.System.Environment.SetEnvironmentVariableW +Windows.Win32.System.IO.CancelIo +Windows.Win32.System.IO.DeviceIoControl +Windows.Win32.System.IO.GetOverlappedResult +Windows.Win32.System.IO.LPOVERLAPPED_COMPLETION_ROUTINE +Windows.Win32.System.IO.OVERLAPPED +Windows.Win32.System.Ioctl.FSCTL_GET_REPARSE_POINT +Windows.Win32.System.Ioctl.FSCTL_SET_REPARSE_POINT +Windows.Win32.System.Kernel.EXCEPTION_DISPOSITION +Windows.Win32.System.Kernel.ExceptionCollidedUnwind +Windows.Win32.System.Kernel.ExceptionContinueExecution +Windows.Win32.System.Kernel.ExceptionContinueSearch +Windows.Win32.System.Kernel.ExceptionNestedException +Windows.Win32.System.Kernel.FLOATING_SAVE_AREA +Windows.Win32.System.Kernel.OBJ_DONT_REPARSE +Windows.Win32.System.LibraryLoader.GetModuleFileNameW +Windows.Win32.System.LibraryLoader.GetModuleHandleA +Windows.Win32.System.LibraryLoader.GetModuleHandleW +Windows.Win32.System.LibraryLoader.GetProcAddress +Windows.Win32.System.Performance.QueryPerformanceCounter +Windows.Win32.System.Performance.QueryPerformanceFrequency +Windows.Win32.System.Pipes.CreateNamedPipeW +Windows.Win32.System.Pipes.NAMED_PIPE_MODE +Windows.Win32.System.Pipes.PIPE_ACCEPT_REMOTE_CLIENTS +Windows.Win32.System.Pipes.PIPE_CLIENT_END +Windows.Win32.System.Pipes.PIPE_NOWAIT +Windows.Win32.System.Pipes.PIPE_READMODE_BYTE +Windows.Win32.System.Pipes.PIPE_READMODE_MESSAGE +Windows.Win32.System.Pipes.PIPE_REJECT_REMOTE_CLIENTS +Windows.Win32.System.Pipes.PIPE_SERVER_END +Windows.Win32.System.Pipes.PIPE_TYPE_BYTE +Windows.Win32.System.Pipes.PIPE_TYPE_MESSAGE +Windows.Win32.System.Pipes.PIPE_WAIT +Windows.Win32.System.SystemInformation.GetSystemDirectoryW +Windows.Win32.System.SystemInformation.GetSystemInfo +Windows.Win32.System.SystemInformation.GetSystemTimeAsFileTime +Windows.Win32.System.SystemInformation.GetWindowsDirectoryW +Windows.Win32.System.SystemInformation.PROCESSOR_ARCHITECTURE +Windows.Win32.System.SystemInformation.SYSTEM_INFO +Windows.Win32.System.SystemServices.DLL_PROCESS_DETACH +Windows.Win32.System.SystemServices.DLL_THREAD_DETACH +Windows.Win32.System.SystemServices.EXCEPTION_MAXIMUM_PARAMETERS +Windows.Win32.System.SystemServices.IO_REPARSE_TAG_MOUNT_POINT +Windows.Win32.System.SystemServices.IO_REPARSE_TAG_SYMLINK +Windows.Win32.System.Threading.ABOVE_NORMAL_PRIORITY_CLASS +Windows.Win32.System.Threading.AcquireSRWLockExclusive +Windows.Win32.System.Threading.AcquireSRWLockShared +Windows.Win32.System.Threading.ALL_PROCESSOR_GROUPS +Windows.Win32.System.Threading.BELOW_NORMAL_PRIORITY_CLASS +Windows.Win32.System.Threading.CREATE_BREAKAWAY_FROM_JOB +Windows.Win32.System.Threading.CREATE_DEFAULT_ERROR_MODE +Windows.Win32.System.Threading.CREATE_FORCEDOS +Windows.Win32.System.Threading.CREATE_IGNORE_SYSTEM_DEFAULT +Windows.Win32.System.Threading.CREATE_NEW_CONSOLE +Windows.Win32.System.Threading.CREATE_NEW_PROCESS_GROUP +Windows.Win32.System.Threading.CREATE_NO_WINDOW +Windows.Win32.System.Threading.CREATE_PRESERVE_CODE_AUTHZ_LEVEL +Windows.Win32.System.Threading.CREATE_PROTECTED_PROCESS +Windows.Win32.System.Threading.CREATE_SECURE_PROCESS +Windows.Win32.System.Threading.CREATE_SEPARATE_WOW_VDM +Windows.Win32.System.Threading.CREATE_SHARED_WOW_VDM +Windows.Win32.System.Threading.CREATE_SUSPENDED +Windows.Win32.System.Threading.CREATE_UNICODE_ENVIRONMENT +Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_HIGH_RESOLUTION +Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_MANUAL_RESET +Windows.Win32.System.Threading.CreateEventW +Windows.Win32.System.Threading.CreateProcessW +Windows.Win32.System.Threading.CreateThread +Windows.Win32.System.Threading.CreateWaitableTimerExW +Windows.Win32.System.Threading.DEBUG_ONLY_THIS_PROCESS +Windows.Win32.System.Threading.DEBUG_PROCESS +Windows.Win32.System.Threading.DeleteProcThreadAttributeList +Windows.Win32.System.Threading.DETACHED_PROCESS +Windows.Win32.System.Threading.ExitProcess +Windows.Win32.System.Threading.EXTENDED_STARTUPINFO_PRESENT +Windows.Win32.System.Threading.GetActiveProcessorCount +Windows.Win32.System.Threading.GetCurrentProcess +Windows.Win32.System.Threading.GetCurrentProcessId +Windows.Win32.System.Threading.GetCurrentThread +Windows.Win32.System.Threading.GetExitCodeProcess +Windows.Win32.System.Threading.GetProcessId +Windows.Win32.System.Threading.HIGH_PRIORITY_CLASS +Windows.Win32.System.Threading.IDLE_PRIORITY_CLASS +Windows.Win32.System.Threading.INFINITE +Windows.Win32.System.Threading.INHERIT_CALLER_PRIORITY +Windows.Win32.System.Threading.INHERIT_PARENT_AFFINITY +Windows.Win32.System.Threading.INIT_ONCE_INIT_FAILED +Windows.Win32.System.Threading.InitializeProcThreadAttributeList +Windows.Win32.System.Threading.InitOnceBeginInitialize +Windows.Win32.System.Threading.InitOnceComplete +Windows.Win32.System.Threading.LPPROC_THREAD_ATTRIBUTE_LIST +Windows.Win32.System.Threading.LPTHREAD_START_ROUTINE +Windows.Win32.System.Threading.NORMAL_PRIORITY_CLASS +Windows.Win32.System.Threading.OpenProcessToken +Windows.Win32.System.Threading.PROCESS_CREATION_FLAGS +Windows.Win32.System.Threading.PROCESS_INFORMATION +Windows.Win32.System.Threading.PROCESS_MODE_BACKGROUND_BEGIN +Windows.Win32.System.Threading.PROCESS_MODE_BACKGROUND_END +Windows.Win32.System.Threading.PROFILE_KERNEL +Windows.Win32.System.Threading.PROFILE_SERVER +Windows.Win32.System.Threading.PROFILE_USER +Windows.Win32.System.Threading.REALTIME_PRIORITY_CLASS +Windows.Win32.System.Threading.ReleaseSRWLockExclusive +Windows.Win32.System.Threading.ReleaseSRWLockShared +Windows.Win32.System.Threading.SetThreadStackGuarantee +Windows.Win32.System.Threading.SetWaitableTimer +Windows.Win32.System.Threading.Sleep +Windows.Win32.System.Threading.SleepConditionVariableSRW +Windows.Win32.System.Threading.SleepEx +Windows.Win32.System.Threading.STACK_SIZE_PARAM_IS_A_RESERVATION +Windows.Win32.System.Threading.STARTF_FORCEOFFFEEDBACK +Windows.Win32.System.Threading.STARTF_FORCEONFEEDBACK +Windows.Win32.System.Threading.STARTF_PREVENTPINNING +Windows.Win32.System.Threading.STARTF_RUNFULLSCREEN +Windows.Win32.System.Threading.STARTF_TITLEISAPPID +Windows.Win32.System.Threading.STARTF_TITLEISLINKNAME +Windows.Win32.System.Threading.STARTF_UNTRUSTEDSOURCE +Windows.Win32.System.Threading.STARTF_USECOUNTCHARS +Windows.Win32.System.Threading.STARTF_USEFILLATTRIBUTE +Windows.Win32.System.Threading.STARTF_USEHOTKEY +Windows.Win32.System.Threading.STARTF_USEPOSITION +Windows.Win32.System.Threading.STARTF_USESHOWWINDOW +Windows.Win32.System.Threading.STARTF_USESIZE +Windows.Win32.System.Threading.STARTF_USESTDHANDLES +Windows.Win32.System.Threading.STARTUPINFOEXW +Windows.Win32.System.Threading.STARTUPINFOW +Windows.Win32.System.Threading.STARTUPINFOW_FLAGS +Windows.Win32.System.Threading.SwitchToThread +Windows.Win32.System.Threading.TerminateProcess +Windows.Win32.System.Threading.THREAD_CREATE_RUN_IMMEDIATELY +Windows.Win32.System.Threading.THREAD_CREATE_SUSPENDED +Windows.Win32.System.Threading.THREAD_CREATION_FLAGS +Windows.Win32.System.Threading.TIMER_ALL_ACCESS +Windows.Win32.System.Threading.TIMER_MODIFY_STATE +Windows.Win32.System.Threading.TLS_OUT_OF_INDEXES +Windows.Win32.System.Threading.TlsAlloc +Windows.Win32.System.Threading.TlsFree +Windows.Win32.System.Threading.TlsGetValue +Windows.Win32.System.Threading.TlsSetValue +Windows.Win32.System.Threading.TryAcquireSRWLockExclusive +Windows.Win32.System.Threading.TryAcquireSRWLockShared +Windows.Win32.System.Threading.UpdateProcThreadAttribute +Windows.Win32.System.Threading.WaitForMultipleObjects +Windows.Win32.System.Threading.WaitForSingleObject +Windows.Win32.System.Threading.WakeAllConditionVariable +Windows.Win32.System.Threading.WakeConditionVariable +Windows.Win32.System.WindowsProgramming.PROGRESS_CONTINUE +Windows.Win32.UI.Shell.GetUserProfileDirectoryW +// tidy-alphabetical-end + diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs new file mode 100644 index 00000000000..b38b70c8983 --- /dev/null +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -0,0 +1,4353 @@ +// This file is autogenerated. +// +// To add bindings, edit windows_sys.lst then use `./x run generate-windows-sys` to +// regenerate the bindings. +// +// ignore-tidy-filelength +// Bindings generated by `windows-bindgen` 0.52.0 + +#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] +#[link(name = "advapi32")] +extern "system" { + pub fn OpenProcessToken( + processhandle: HANDLE, + desiredaccess: TOKEN_ACCESS_MASK, + tokenhandle: *mut HANDLE, + ) -> BOOL; +} +#[link(name = "advapi32")] +extern "system" { + #[link_name = "SystemFunction036"] + pub fn RtlGenRandom(randombuffer: *mut ::core::ffi::c_void, randombufferlength: u32) + -> BOOLEAN; +} +#[link(name = "bcrypt")] +extern "system" { + pub fn BCryptGenRandom( + halgorithm: BCRYPT_ALG_HANDLE, + pbbuffer: *mut u8, + cbbuffer: u32, + dwflags: BCRYPTGENRANDOM_FLAGS, + ) -> NTSTATUS; +} +#[link(name = "kernel32")] +extern "system" { + pub fn AcquireSRWLockExclusive(srwlock: *mut SRWLOCK) -> (); +} +#[link(name = "kernel32")] +extern "system" { + pub fn AcquireSRWLockShared(srwlock: *mut SRWLOCK) -> (); +} +#[link(name = "kernel32")] +extern "system" { + pub fn CancelIo(hfile: HANDLE) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn CloseHandle(hobject: HANDLE) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn CompareStringOrdinal( + lpstring1: PCWSTR, + cchcount1: i32, + lpstring2: PCWSTR, + cchcount2: i32, + bignorecase: BOOL, + ) -> COMPARESTRING_RESULT; +} +#[link(name = "kernel32")] +extern "system" { + pub fn CopyFileExW( + lpexistingfilename: PCWSTR, + lpnewfilename: PCWSTR, + lpprogressroutine: LPPROGRESS_ROUTINE, + lpdata: *const ::core::ffi::c_void, + pbcancel: *mut BOOL, + dwcopyflags: u32, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn CreateDirectoryW( + lppathname: PCWSTR, + lpsecurityattributes: *const SECURITY_ATTRIBUTES, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn CreateEventW( + lpeventattributes: *const SECURITY_ATTRIBUTES, + bmanualreset: BOOL, + binitialstate: BOOL, + lpname: PCWSTR, + ) -> HANDLE; +} +#[link(name = "kernel32")] +extern "system" { + pub fn CreateFileW( + lpfilename: PCWSTR, + dwdesiredaccess: u32, + dwsharemode: FILE_SHARE_MODE, + lpsecurityattributes: *const SECURITY_ATTRIBUTES, + dwcreationdisposition: FILE_CREATION_DISPOSITION, + dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, + htemplatefile: HANDLE, + ) -> HANDLE; +} +#[link(name = "kernel32")] +extern "system" { + pub fn CreateHardLinkW( + lpfilename: PCWSTR, + lpexistingfilename: PCWSTR, + lpsecurityattributes: *const SECURITY_ATTRIBUTES, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn CreateNamedPipeW( + lpname: PCWSTR, + dwopenmode: FILE_FLAGS_AND_ATTRIBUTES, + dwpipemode: NAMED_PIPE_MODE, + nmaxinstances: u32, + noutbuffersize: u32, + ninbuffersize: u32, + ndefaulttimeout: u32, + lpsecurityattributes: *const SECURITY_ATTRIBUTES, + ) -> HANDLE; +} +#[link(name = "kernel32")] +extern "system" { + pub fn CreateProcessW( + lpapplicationname: PCWSTR, + lpcommandline: PWSTR, + lpprocessattributes: *const SECURITY_ATTRIBUTES, + lpthreadattributes: *const SECURITY_ATTRIBUTES, + binherithandles: BOOL, + dwcreationflags: PROCESS_CREATION_FLAGS, + lpenvironment: *const ::core::ffi::c_void, + lpcurrentdirectory: PCWSTR, + lpstartupinfo: *const STARTUPINFOW, + lpprocessinformation: *mut PROCESS_INFORMATION, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn CreateSymbolicLinkW( + lpsymlinkfilename: PCWSTR, + lptargetfilename: PCWSTR, + dwflags: SYMBOLIC_LINK_FLAGS, + ) -> BOOLEAN; +} +#[link(name = "kernel32")] +extern "system" { + pub fn CreateThread( + lpthreadattributes: *const SECURITY_ATTRIBUTES, + dwstacksize: usize, + lpstartaddress: LPTHREAD_START_ROUTINE, + lpparameter: *const ::core::ffi::c_void, + dwcreationflags: THREAD_CREATION_FLAGS, + lpthreadid: *mut u32, + ) -> HANDLE; +} +#[link(name = "kernel32")] +extern "system" { + pub fn CreateWaitableTimerExW( + lptimerattributes: *const SECURITY_ATTRIBUTES, + lptimername: PCWSTR, + dwflags: u32, + dwdesiredaccess: u32, + ) -> HANDLE; +} +#[link(name = "kernel32")] +extern "system" { + pub fn DeleteFileW(lpfilename: PCWSTR) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn DeleteProcThreadAttributeList(lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST) -> (); +} +#[link(name = "kernel32")] +extern "system" { + pub fn DeviceIoControl( + hdevice: HANDLE, + dwiocontrolcode: u32, + lpinbuffer: *const ::core::ffi::c_void, + ninbuffersize: u32, + lpoutbuffer: *mut ::core::ffi::c_void, + noutbuffersize: u32, + lpbytesreturned: *mut u32, + lpoverlapped: *mut OVERLAPPED, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn DuplicateHandle( + hsourceprocesshandle: HANDLE, + hsourcehandle: HANDLE, + htargetprocesshandle: HANDLE, + lptargethandle: *mut HANDLE, + dwdesiredaccess: u32, + binherithandle: BOOL, + dwoptions: DUPLICATE_HANDLE_OPTIONS, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn ExitProcess(uexitcode: u32) -> !; +} +#[link(name = "kernel32")] +extern "system" { + pub fn FindClose(hfindfile: HANDLE) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn FindFirstFileW(lpfilename: PCWSTR, lpfindfiledata: *mut WIN32_FIND_DATAW) -> HANDLE; +} +#[link(name = "kernel32")] +extern "system" { + pub fn FindNextFileW(hfindfile: HANDLE, lpfindfiledata: *mut WIN32_FIND_DATAW) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn FlushFileBuffers(hfile: HANDLE) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn FormatMessageW( + dwflags: FORMAT_MESSAGE_OPTIONS, + lpsource: *const ::core::ffi::c_void, + dwmessageid: u32, + dwlanguageid: u32, + lpbuffer: PWSTR, + nsize: u32, + arguments: *const *const i8, + ) -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn FreeEnvironmentStringsW(penv: PCWSTR) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetActiveProcessorCount(groupnumber: u16) -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetCommandLineW() -> PCWSTR; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetConsoleMode(hconsolehandle: HANDLE, lpmode: *mut CONSOLE_MODE) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetCurrentDirectoryW(nbufferlength: u32, lpbuffer: PWSTR) -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetCurrentProcess() -> HANDLE; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetCurrentProcessId() -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetCurrentThread() -> HANDLE; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetEnvironmentStringsW() -> PWSTR; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetEnvironmentVariableW(lpname: PCWSTR, lpbuffer: PWSTR, nsize: u32) -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetExitCodeProcess(hprocess: HANDLE, lpexitcode: *mut u32) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetFileAttributesW(lpfilename: PCWSTR) -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetFileInformationByHandle( + hfile: HANDLE, + lpfileinformation: *mut BY_HANDLE_FILE_INFORMATION, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetFileInformationByHandleEx( + hfile: HANDLE, + fileinformationclass: FILE_INFO_BY_HANDLE_CLASS, + lpfileinformation: *mut ::core::ffi::c_void, + dwbuffersize: u32, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetFileType(hfile: HANDLE) -> FILE_TYPE; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetFinalPathNameByHandleW( + hfile: HANDLE, + lpszfilepath: PWSTR, + cchfilepath: u32, + dwflags: GETFINALPATHNAMEBYHANDLE_FLAGS, + ) -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetFullPathNameW( + lpfilename: PCWSTR, + nbufferlength: u32, + lpbuffer: PWSTR, + lpfilepart: *mut PWSTR, + ) -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetLastError() -> WIN32_ERROR; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetModuleFileNameW(hmodule: HMODULE, lpfilename: PWSTR, nsize: u32) -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetModuleHandleA(lpmodulename: PCSTR) -> HMODULE; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetModuleHandleW(lpmodulename: PCWSTR) -> HMODULE; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetOverlappedResult( + hfile: HANDLE, + lpoverlapped: *const OVERLAPPED, + lpnumberofbytestransferred: *mut u32, + bwait: BOOL, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetProcAddress(hmodule: HMODULE, lpprocname: PCSTR) -> FARPROC; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetProcessId(process: HANDLE) -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetStdHandle(nstdhandle: STD_HANDLE) -> HANDLE; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetSystemDirectoryW(lpbuffer: PWSTR, usize: u32) -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetSystemInfo(lpsysteminfo: *mut SYSTEM_INFO) -> (); +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetSystemTimeAsFileTime(lpsystemtimeasfiletime: *mut FILETIME) -> (); +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetTempPathW(nbufferlength: u32, lpbuffer: PWSTR) -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetWindowsDirectoryW(lpbuffer: PWSTR, usize: u32) -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn InitOnceBeginInitialize( + lpinitonce: *mut INIT_ONCE, + dwflags: u32, + fpending: *mut BOOL, + lpcontext: *mut *mut ::core::ffi::c_void, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn InitOnceComplete( + lpinitonce: *mut INIT_ONCE, + dwflags: u32, + lpcontext: *const ::core::ffi::c_void, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn InitializeProcThreadAttributeList( + lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST, + dwattributecount: u32, + dwflags: u32, + lpsize: *mut usize, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn MoveFileExW( + lpexistingfilename: PCWSTR, + lpnewfilename: PCWSTR, + dwflags: MOVE_FILE_FLAGS, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn MultiByteToWideChar( + codepage: u32, + dwflags: MULTI_BYTE_TO_WIDE_CHAR_FLAGS, + lpmultibytestr: PCSTR, + cbmultibyte: i32, + lpwidecharstr: PWSTR, + cchwidechar: i32, + ) -> i32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn QueryPerformanceCounter(lpperformancecount: *mut i64) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn QueryPerformanceFrequency(lpfrequency: *mut i64) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn ReadConsoleW( + hconsoleinput: HANDLE, + lpbuffer: *mut ::core::ffi::c_void, + nnumberofcharstoread: u32, + lpnumberofcharsread: *mut u32, + pinputcontrol: *const CONSOLE_READCONSOLE_CONTROL, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn ReadFile( + hfile: HANDLE, + lpbuffer: *mut u8, + nnumberofbytestoread: u32, + lpnumberofbytesread: *mut u32, + lpoverlapped: *mut OVERLAPPED, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn ReadFileEx( + hfile: HANDLE, + lpbuffer: *mut u8, + nnumberofbytestoread: u32, + lpoverlapped: *mut OVERLAPPED, + lpcompletionroutine: LPOVERLAPPED_COMPLETION_ROUTINE, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn ReleaseSRWLockExclusive(srwlock: *mut SRWLOCK) -> (); +} +#[link(name = "kernel32")] +extern "system" { + pub fn ReleaseSRWLockShared(srwlock: *mut SRWLOCK) -> (); +} +#[link(name = "kernel32")] +extern "system" { + pub fn RemoveDirectoryW(lppathname: PCWSTR) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn SetCurrentDirectoryW(lppathname: PCWSTR) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn SetEnvironmentVariableW(lpname: PCWSTR, lpvalue: PCWSTR) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn SetFileAttributesW( + lpfilename: PCWSTR, + dwfileattributes: FILE_FLAGS_AND_ATTRIBUTES, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn SetFileInformationByHandle( + hfile: HANDLE, + fileinformationclass: FILE_INFO_BY_HANDLE_CLASS, + lpfileinformation: *const ::core::ffi::c_void, + dwbuffersize: u32, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn SetFilePointerEx( + hfile: HANDLE, + lidistancetomove: i64, + lpnewfilepointer: *mut i64, + dwmovemethod: SET_FILE_POINTER_MOVE_METHOD, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn SetFileTime( + hfile: HANDLE, + lpcreationtime: *const FILETIME, + lplastaccesstime: *const FILETIME, + lplastwritetime: *const FILETIME, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn SetHandleInformation(hobject: HANDLE, dwmask: u32, dwflags: HANDLE_FLAGS) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn SetLastError(dwerrcode: WIN32_ERROR) -> (); +} +#[link(name = "kernel32")] +extern "system" { + pub fn SetThreadStackGuarantee(stacksizeinbytes: *mut u32) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn SetWaitableTimer( + htimer: HANDLE, + lpduetime: *const i64, + lperiod: i32, + pfncompletionroutine: PTIMERAPCROUTINE, + lpargtocompletionroutine: *const ::core::ffi::c_void, + fresume: BOOL, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn Sleep(dwmilliseconds: u32) -> (); +} +#[link(name = "kernel32")] +extern "system" { + pub fn SleepConditionVariableSRW( + conditionvariable: *mut CONDITION_VARIABLE, + srwlock: *mut SRWLOCK, + dwmilliseconds: u32, + flags: u32, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn SleepEx(dwmilliseconds: u32, balertable: BOOL) -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn SwitchToThread() -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn TerminateProcess(hprocess: HANDLE, uexitcode: u32) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn TlsAlloc() -> u32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn TlsFree(dwtlsindex: u32) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn TlsGetValue(dwtlsindex: u32) -> *mut ::core::ffi::c_void; +} +#[link(name = "kernel32")] +extern "system" { + pub fn TlsSetValue(dwtlsindex: u32, lptlsvalue: *const ::core::ffi::c_void) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn TryAcquireSRWLockExclusive(srwlock: *mut SRWLOCK) -> BOOLEAN; +} +#[link(name = "kernel32")] +extern "system" { + pub fn TryAcquireSRWLockShared(srwlock: *mut SRWLOCK) -> BOOLEAN; +} +#[link(name = "kernel32")] +extern "system" { + pub fn UpdateProcThreadAttribute( + lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST, + dwflags: u32, + attribute: usize, + lpvalue: *const ::core::ffi::c_void, + cbsize: usize, + lppreviousvalue: *mut ::core::ffi::c_void, + lpreturnsize: *const usize, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn WaitForMultipleObjects( + ncount: u32, + lphandles: *const HANDLE, + bwaitall: BOOL, + dwmilliseconds: u32, + ) -> WAIT_EVENT; +} +#[link(name = "kernel32")] +extern "system" { + pub fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> WAIT_EVENT; +} +#[link(name = "kernel32")] +extern "system" { + pub fn WakeAllConditionVariable(conditionvariable: *mut CONDITION_VARIABLE) -> (); +} +#[link(name = "kernel32")] +extern "system" { + pub fn WakeConditionVariable(conditionvariable: *mut CONDITION_VARIABLE) -> (); +} +#[link(name = "kernel32")] +extern "system" { + pub fn WideCharToMultiByte( + codepage: u32, + dwflags: u32, + lpwidecharstr: PCWSTR, + cchwidechar: i32, + lpmultibytestr: PSTR, + cbmultibyte: i32, + lpdefaultchar: PCSTR, + lpuseddefaultchar: *mut BOOL, + ) -> i32; +} +#[link(name = "kernel32")] +extern "system" { + pub fn WriteConsoleW( + hconsoleoutput: HANDLE, + lpbuffer: *const ::core::ffi::c_void, + nnumberofcharstowrite: u32, + lpnumberofcharswritten: *mut u32, + lpreserved: *const ::core::ffi::c_void, + ) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn WriteFileEx( + hfile: HANDLE, + lpbuffer: *const u8, + nnumberofbytestowrite: u32, + lpoverlapped: *mut OVERLAPPED, + lpcompletionroutine: LPOVERLAPPED_COMPLETION_ROUTINE, + ) -> BOOL; +} +#[link(name = "ntdll")] +extern "system" { + pub fn NtCreateFile( + filehandle: *mut HANDLE, + desiredaccess: FILE_ACCESS_RIGHTS, + objectattributes: *const OBJECT_ATTRIBUTES, + iostatusblock: *mut IO_STATUS_BLOCK, + allocationsize: *const i64, + fileattributes: FILE_FLAGS_AND_ATTRIBUTES, + shareaccess: FILE_SHARE_MODE, + createdisposition: NTCREATEFILE_CREATE_DISPOSITION, + createoptions: NTCREATEFILE_CREATE_OPTIONS, + eabuffer: *const ::core::ffi::c_void, + ealength: u32, + ) -> NTSTATUS; +} +#[link(name = "ntdll")] +extern "system" { + pub fn NtReadFile( + filehandle: HANDLE, + event: HANDLE, + apcroutine: PIO_APC_ROUTINE, + apccontext: *const ::core::ffi::c_void, + iostatusblock: *mut IO_STATUS_BLOCK, + buffer: *mut ::core::ffi::c_void, + length: u32, + byteoffset: *const i64, + key: *const u32, + ) -> NTSTATUS; +} +#[link(name = "ntdll")] +extern "system" { + pub fn NtWriteFile( + filehandle: HANDLE, + event: HANDLE, + apcroutine: PIO_APC_ROUTINE, + apccontext: *const ::core::ffi::c_void, + iostatusblock: *mut IO_STATUS_BLOCK, + buffer: *const ::core::ffi::c_void, + length: u32, + byteoffset: *const i64, + key: *const u32, + ) -> NTSTATUS; +} +#[link(name = "ntdll")] +extern "system" { + pub fn RtlNtStatusToDosError(status: NTSTATUS) -> u32; +} +#[link(name = "userenv")] +extern "system" { + pub fn GetUserProfileDirectoryW( + htoken: HANDLE, + lpprofiledir: PWSTR, + lpcchsize: *mut u32, + ) -> BOOL; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn WSACleanup() -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn WSADuplicateSocketW( + s: SOCKET, + dwprocessid: u32, + lpprotocolinfo: *mut WSAPROTOCOL_INFOW, + ) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn WSAGetLastError() -> WSA_ERROR; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn WSARecv( + s: SOCKET, + lpbuffers: *const WSABUF, + dwbuffercount: u32, + lpnumberofbytesrecvd: *mut u32, + lpflags: *mut u32, + lpoverlapped: *mut OVERLAPPED, + lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE, + ) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn WSASend( + s: SOCKET, + lpbuffers: *const WSABUF, + dwbuffercount: u32, + lpnumberofbytessent: *mut u32, + dwflags: u32, + lpoverlapped: *mut OVERLAPPED, + lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE, + ) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn WSASocketW( + af: i32, + r#type: i32, + protocol: i32, + lpprotocolinfo: *const WSAPROTOCOL_INFOW, + g: u32, + dwflags: u32, + ) -> SOCKET; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn accept(s: SOCKET, addr: *mut SOCKADDR, addrlen: *mut i32) -> SOCKET; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn bind(s: SOCKET, name: *const SOCKADDR, namelen: i32) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn closesocket(s: SOCKET) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn connect(s: SOCKET, name: *const SOCKADDR, namelen: i32) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn freeaddrinfo(paddrinfo: *const ADDRINFOA) -> (); +} +#[link(name = "ws2_32")] +extern "system" { + pub fn getaddrinfo( + pnodename: PCSTR, + pservicename: PCSTR, + phints: *const ADDRINFOA, + ppresult: *mut *mut ADDRINFOA, + ) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn getpeername(s: SOCKET, name: *mut SOCKADDR, namelen: *mut i32) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn getsockname(s: SOCKET, name: *mut SOCKADDR, namelen: *mut i32) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn getsockopt(s: SOCKET, level: i32, optname: i32, optval: PSTR, optlen: *mut i32) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn ioctlsocket(s: SOCKET, cmd: i32, argp: *mut u32) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn listen(s: SOCKET, backlog: i32) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn recv(s: SOCKET, buf: PSTR, len: i32, flags: SEND_RECV_FLAGS) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn recvfrom( + s: SOCKET, + buf: PSTR, + len: i32, + flags: i32, + from: *mut SOCKADDR, + fromlen: *mut i32, + ) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn select( + nfds: i32, + readfds: *mut FD_SET, + writefds: *mut FD_SET, + exceptfds: *mut FD_SET, + timeout: *const TIMEVAL, + ) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn send(s: SOCKET, buf: PCSTR, len: i32, flags: SEND_RECV_FLAGS) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn sendto( + s: SOCKET, + buf: PCSTR, + len: i32, + flags: i32, + to: *const SOCKADDR, + tolen: i32, + ) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn setsockopt(s: SOCKET, level: i32, optname: i32, optval: PCSTR, optlen: i32) -> i32; +} +#[link(name = "ws2_32")] +extern "system" { + pub fn shutdown(s: SOCKET, how: WINSOCK_SHUTDOWN_HOW) -> i32; +} +pub const ABOVE_NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 32768u32; +pub type ADDRESS_FAMILY = u16; +#[repr(C)] +pub struct ADDRINFOA { + pub ai_flags: i32, + pub ai_family: i32, + pub ai_socktype: i32, + pub ai_protocol: i32, + pub ai_addrlen: usize, + pub ai_canonname: PSTR, + pub ai_addr: *mut SOCKADDR, + pub ai_next: *mut ADDRINFOA, +} +impl ::core::marker::Copy for ADDRINFOA {} +impl ::core::clone::Clone for ADDRINFOA { + fn clone(&self) -> Self { + *self + } +} +pub const AF_INET: ADDRESS_FAMILY = 2u16; +pub const AF_INET6: ADDRESS_FAMILY = 23u16; +pub const AF_UNIX: u16 = 1u16; +pub const AF_UNSPEC: ADDRESS_FAMILY = 0u16; +pub const ALL_PROCESSOR_GROUPS: u16 = 65535u16; +#[repr(C)] +pub union ARM64_NT_NEON128 { + pub Anonymous: ARM64_NT_NEON128_0, + pub D: [f64; 2], + pub S: [f32; 4], + pub H: [u16; 8], + pub B: [u8; 16], +} +impl ::core::marker::Copy for ARM64_NT_NEON128 {} +impl ::core::clone::Clone for ARM64_NT_NEON128 { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub struct ARM64_NT_NEON128_0 { + pub Low: u64, + pub High: i64, +} +impl ::core::marker::Copy for ARM64_NT_NEON128_0 {} +impl ::core::clone::Clone for ARM64_NT_NEON128_0 { + fn clone(&self) -> Self { + *self + } +} +pub type BCRYPTGENRANDOM_FLAGS = u32; +pub type BCRYPT_ALG_HANDLE = *mut ::core::ffi::c_void; +pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: BCRYPTGENRANDOM_FLAGS = 2u32; +pub const BELOW_NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 16384u32; +pub type BOOL = i32; +pub type BOOLEAN = u8; +#[repr(C)] +pub struct BY_HANDLE_FILE_INFORMATION { + pub dwFileAttributes: u32, + pub ftCreationTime: FILETIME, + pub ftLastAccessTime: FILETIME, + pub ftLastWriteTime: FILETIME, + pub dwVolumeSerialNumber: u32, + pub nFileSizeHigh: u32, + pub nFileSizeLow: u32, + pub nNumberOfLinks: u32, + pub nFileIndexHigh: u32, + pub nFileIndexLow: u32, +} +impl ::core::marker::Copy for BY_HANDLE_FILE_INFORMATION {} +impl ::core::clone::Clone for BY_HANDLE_FILE_INFORMATION { + fn clone(&self) -> Self { + *self + } +} +pub const CALLBACK_CHUNK_FINISHED: LPPROGRESS_ROUTINE_CALLBACK_REASON = 0u32; +pub const CALLBACK_STREAM_SWITCH: LPPROGRESS_ROUTINE_CALLBACK_REASON = 1u32; +pub type COMPARESTRING_RESULT = i32; +#[repr(C)] +pub struct CONDITION_VARIABLE { + pub Ptr: *mut ::core::ffi::c_void, +} +impl ::core::marker::Copy for CONDITION_VARIABLE {} +impl ::core::clone::Clone for CONDITION_VARIABLE { + fn clone(&self) -> Self { + *self + } +} +pub type CONSOLE_MODE = u32; +#[repr(C)] +pub struct CONSOLE_READCONSOLE_CONTROL { + pub nLength: u32, + pub nInitialChars: u32, + pub dwCtrlWakeupMask: u32, + pub dwControlKeyState: u32, +} +impl ::core::marker::Copy for CONSOLE_READCONSOLE_CONTROL {} +impl ::core::clone::Clone for CONSOLE_READCONSOLE_CONTROL { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +#[cfg(target_arch = "aarch64")] +pub struct CONTEXT { + pub ContextFlags: CONTEXT_FLAGS, + pub Cpsr: u32, + pub Anonymous: CONTEXT_0, + pub Sp: u64, + pub Pc: u64, + pub V: [ARM64_NT_NEON128; 32], + pub Fpcr: u32, + pub Fpsr: u32, + pub Bcr: [u32; 8], + pub Bvr: [u64; 8], + pub Wcr: [u32; 2], + pub Wvr: [u64; 2], +} +#[cfg(target_arch = "aarch64")] +impl ::core::marker::Copy for CONTEXT {} +#[cfg(target_arch = "aarch64")] +impl ::core::clone::Clone for CONTEXT { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +#[cfg(target_arch = "aarch64")] +pub union CONTEXT_0 { + pub Anonymous: CONTEXT_0_0, + pub X: [u64; 31], +} +#[cfg(target_arch = "aarch64")] +impl ::core::marker::Copy for CONTEXT_0 {} +#[cfg(target_arch = "aarch64")] +impl ::core::clone::Clone for CONTEXT_0 { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +#[cfg(target_arch = "aarch64")] +pub struct CONTEXT_0_0 { + pub X0: u64, + pub X1: u64, + pub X2: u64, + pub X3: u64, + pub X4: u64, + pub X5: u64, + pub X6: u64, + pub X7: u64, + pub X8: u64, + pub X9: u64, + pub X10: u64, + pub X11: u64, + pub X12: u64, + pub X13: u64, + pub X14: u64, + pub X15: u64, + pub X16: u64, + pub X17: u64, + pub X18: u64, + pub X19: u64, + pub X20: u64, + pub X21: u64, + pub X22: u64, + pub X23: u64, + pub X24: u64, + pub X25: u64, + pub X26: u64, + pub X27: u64, + pub X28: u64, + pub Fp: u64, + pub Lr: u64, +} +#[cfg(target_arch = "aarch64")] +impl ::core::marker::Copy for CONTEXT_0_0 {} +#[cfg(target_arch = "aarch64")] +impl ::core::clone::Clone for CONTEXT_0_0 { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +#[cfg(target_arch = "x86_64")] +pub struct CONTEXT { + pub P1Home: u64, + pub P2Home: u64, + pub P3Home: u64, + pub P4Home: u64, + pub P5Home: u64, + pub P6Home: u64, + pub ContextFlags: CONTEXT_FLAGS, + pub MxCsr: u32, + pub SegCs: u16, + pub SegDs: u16, + pub SegEs: u16, + pub SegFs: u16, + pub SegGs: u16, + pub SegSs: u16, + pub EFlags: u32, + pub Dr0: u64, + pub Dr1: u64, + pub Dr2: u64, + pub Dr3: u64, + pub Dr6: u64, + pub Dr7: u64, + pub Rax: u64, + pub Rcx: u64, + pub Rdx: u64, + pub Rbx: u64, + pub Rsp: u64, + pub Rbp: u64, + pub Rsi: u64, + pub Rdi: u64, + pub R8: u64, + pub R9: u64, + pub R10: u64, + pub R11: u64, + pub R12: u64, + pub R13: u64, + pub R14: u64, + pub R15: u64, + pub Rip: u64, + pub Anonymous: CONTEXT_0, + pub VectorRegister: [M128A; 26], + pub VectorControl: u64, + pub DebugControl: u64, + pub LastBranchToRip: u64, + pub LastBranchFromRip: u64, + pub LastExceptionToRip: u64, + pub LastExceptionFromRip: u64, +} +#[cfg(target_arch = "x86_64")] +impl ::core::marker::Copy for CONTEXT {} +#[cfg(target_arch = "x86_64")] +impl ::core::clone::Clone for CONTEXT { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +#[cfg(target_arch = "x86_64")] +pub union CONTEXT_0 { + pub FltSave: XSAVE_FORMAT, + pub Anonymous: CONTEXT_0_0, +} +#[cfg(target_arch = "x86_64")] +impl ::core::marker::Copy for CONTEXT_0 {} +#[cfg(target_arch = "x86_64")] +impl ::core::clone::Clone for CONTEXT_0 { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +#[cfg(target_arch = "x86_64")] +pub struct CONTEXT_0_0 { + pub Header: [M128A; 2], + pub Legacy: [M128A; 8], + pub Xmm0: M128A, + pub Xmm1: M128A, + pub Xmm2: M128A, + pub Xmm3: M128A, + pub Xmm4: M128A, + pub Xmm5: M128A, + pub Xmm6: M128A, + pub Xmm7: M128A, + pub Xmm8: M128A, + pub Xmm9: M128A, + pub Xmm10: M128A, + pub Xmm11: M128A, + pub Xmm12: M128A, + pub Xmm13: M128A, + pub Xmm14: M128A, + pub Xmm15: M128A, +} +#[cfg(target_arch = "x86_64")] +impl ::core::marker::Copy for CONTEXT_0_0 {} +#[cfg(target_arch = "x86_64")] +impl ::core::clone::Clone for CONTEXT_0_0 { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +#[cfg(target_arch = "x86")] +pub struct CONTEXT { + pub ContextFlags: CONTEXT_FLAGS, + pub Dr0: u32, + pub Dr1: u32, + pub Dr2: u32, + pub Dr3: u32, + pub Dr6: u32, + pub Dr7: u32, + pub FloatSave: FLOATING_SAVE_AREA, + pub SegGs: u32, + pub SegFs: u32, + pub SegEs: u32, + pub SegDs: u32, + pub Edi: u32, + pub Esi: u32, + pub Ebx: u32, + pub Edx: u32, + pub Ecx: u32, + pub Eax: u32, + pub Ebp: u32, + pub Eip: u32, + pub SegCs: u32, + pub EFlags: u32, + pub Esp: u32, + pub SegSs: u32, + pub ExtendedRegisters: [u8; 512], +} +#[cfg(target_arch = "x86")] +impl ::core::marker::Copy for CONTEXT {} +#[cfg(target_arch = "x86")] +impl ::core::clone::Clone for CONTEXT { + fn clone(&self) -> Self { + *self + } +} +pub type CONTEXT_FLAGS = u32; +pub const CP_UTF8: u32 = 65001u32; +pub const CREATE_ALWAYS: FILE_CREATION_DISPOSITION = 2u32; +pub const CREATE_BREAKAWAY_FROM_JOB: PROCESS_CREATION_FLAGS = 16777216u32; +pub const CREATE_DEFAULT_ERROR_MODE: PROCESS_CREATION_FLAGS = 67108864u32; +pub const CREATE_FORCEDOS: PROCESS_CREATION_FLAGS = 8192u32; +pub const CREATE_IGNORE_SYSTEM_DEFAULT: PROCESS_CREATION_FLAGS = 2147483648u32; +pub const CREATE_NEW: FILE_CREATION_DISPOSITION = 1u32; +pub const CREATE_NEW_CONSOLE: PROCESS_CREATION_FLAGS = 16u32; +pub const CREATE_NEW_PROCESS_GROUP: PROCESS_CREATION_FLAGS = 512u32; +pub const CREATE_NO_WINDOW: PROCESS_CREATION_FLAGS = 134217728u32; +pub const CREATE_PRESERVE_CODE_AUTHZ_LEVEL: PROCESS_CREATION_FLAGS = 33554432u32; +pub const CREATE_PROTECTED_PROCESS: PROCESS_CREATION_FLAGS = 262144u32; +pub const CREATE_SECURE_PROCESS: PROCESS_CREATION_FLAGS = 4194304u32; +pub const CREATE_SEPARATE_WOW_VDM: PROCESS_CREATION_FLAGS = 2048u32; +pub const CREATE_SHARED_WOW_VDM: PROCESS_CREATION_FLAGS = 4096u32; +pub const CREATE_SUSPENDED: PROCESS_CREATION_FLAGS = 4u32; +pub const CREATE_UNICODE_ENVIRONMENT: PROCESS_CREATION_FLAGS = 1024u32; +pub const CREATE_WAITABLE_TIMER_HIGH_RESOLUTION: u32 = 2u32; +pub const CREATE_WAITABLE_TIMER_MANUAL_RESET: u32 = 1u32; +pub const CSTR_EQUAL: COMPARESTRING_RESULT = 2i32; +pub const CSTR_GREATER_THAN: COMPARESTRING_RESULT = 3i32; +pub const CSTR_LESS_THAN: COMPARESTRING_RESULT = 1i32; +pub const DEBUG_ONLY_THIS_PROCESS: PROCESS_CREATION_FLAGS = 2u32; +pub const DEBUG_PROCESS: PROCESS_CREATION_FLAGS = 1u32; +pub const DELETE: FILE_ACCESS_RIGHTS = 65536u32; +pub const DETACHED_PROCESS: PROCESS_CREATION_FLAGS = 8u32; +pub const DISABLE_NEWLINE_AUTO_RETURN: CONSOLE_MODE = 8u32; +pub const DLL_PROCESS_DETACH: u32 = 0u32; +pub const DLL_THREAD_DETACH: u32 = 3u32; +pub const DNS_ERROR_ADDRESS_REQUIRED: WIN32_ERROR = 9573u32; +pub const DNS_ERROR_ALIAS_LOOP: WIN32_ERROR = 9722u32; +pub const DNS_ERROR_AUTOZONE_ALREADY_EXISTS: WIN32_ERROR = 9610u32; +pub const DNS_ERROR_AXFR: WIN32_ERROR = 9752u32; +pub const DNS_ERROR_BACKGROUND_LOADING: WIN32_ERROR = 9568u32; +pub const DNS_ERROR_BAD_KEYMASTER: WIN32_ERROR = 9122u32; +pub const DNS_ERROR_BAD_PACKET: WIN32_ERROR = 9502u32; +pub const DNS_ERROR_CANNOT_FIND_ROOT_HINTS: WIN32_ERROR = 9564u32; +pub const DNS_ERROR_CLIENT_SUBNET_ALREADY_EXISTS: WIN32_ERROR = 9977u32; +pub const DNS_ERROR_CLIENT_SUBNET_DOES_NOT_EXIST: WIN32_ERROR = 9976u32; +pub const DNS_ERROR_CLIENT_SUBNET_IS_ACCESSED: WIN32_ERROR = 9975u32; +pub const DNS_ERROR_CNAME_COLLISION: WIN32_ERROR = 9709u32; +pub const DNS_ERROR_CNAME_LOOP: WIN32_ERROR = 9707u32; +pub const DNS_ERROR_DATAFILE_OPEN_FAILURE: WIN32_ERROR = 9653u32; +pub const DNS_ERROR_DATAFILE_PARSING: WIN32_ERROR = 9655u32; +pub const DNS_ERROR_DEFAULT_SCOPE: WIN32_ERROR = 9960u32; +pub const DNS_ERROR_DEFAULT_VIRTUALIZATION_INSTANCE: WIN32_ERROR = 9925u32; +pub const DNS_ERROR_DEFAULT_ZONESCOPE: WIN32_ERROR = 9953u32; +pub const DNS_ERROR_DELEGATION_REQUIRED: WIN32_ERROR = 9571u32; +pub const DNS_ERROR_DNAME_COLLISION: WIN32_ERROR = 9721u32; +pub const DNS_ERROR_DNSSEC_IS_DISABLED: WIN32_ERROR = 9125u32; +pub const DNS_ERROR_DP_ALREADY_ENLISTED: WIN32_ERROR = 9904u32; +pub const DNS_ERROR_DP_ALREADY_EXISTS: WIN32_ERROR = 9902u32; +pub const DNS_ERROR_DP_DOES_NOT_EXIST: WIN32_ERROR = 9901u32; +pub const DNS_ERROR_DP_FSMO_ERROR: WIN32_ERROR = 9906u32; +pub const DNS_ERROR_DP_NOT_AVAILABLE: WIN32_ERROR = 9905u32; +pub const DNS_ERROR_DP_NOT_ENLISTED: WIN32_ERROR = 9903u32; +pub const DNS_ERROR_DS_UNAVAILABLE: WIN32_ERROR = 9717u32; +pub const DNS_ERROR_DS_ZONE_ALREADY_EXISTS: WIN32_ERROR = 9718u32; +pub const DNS_ERROR_DWORD_VALUE_TOO_LARGE: WIN32_ERROR = 9567u32; +pub const DNS_ERROR_DWORD_VALUE_TOO_SMALL: WIN32_ERROR = 9566u32; +pub const DNS_ERROR_FILE_WRITEBACK_FAILED: WIN32_ERROR = 9654u32; +pub const DNS_ERROR_FORWARDER_ALREADY_EXISTS: WIN32_ERROR = 9619u32; +pub const DNS_ERROR_INCONSISTENT_ROOT_HINTS: WIN32_ERROR = 9565u32; +pub const DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME: WIN32_ERROR = 9924u32; +pub const DNS_ERROR_INVALID_CLIENT_SUBNET_NAME: WIN32_ERROR = 9984u32; +pub const DNS_ERROR_INVALID_DATA: WIN32_ERROR = 13u32; +pub const DNS_ERROR_INVALID_DATAFILE_NAME: WIN32_ERROR = 9652u32; +pub const DNS_ERROR_INVALID_INITIAL_ROLLOVER_OFFSET: WIN32_ERROR = 9115u32; +pub const DNS_ERROR_INVALID_IP_ADDRESS: WIN32_ERROR = 9552u32; +pub const DNS_ERROR_INVALID_KEY_SIZE: WIN32_ERROR = 9106u32; +pub const DNS_ERROR_INVALID_NAME: WIN32_ERROR = 123u32; +pub const DNS_ERROR_INVALID_NAME_CHAR: WIN32_ERROR = 9560u32; +pub const DNS_ERROR_INVALID_NSEC3_ITERATION_COUNT: WIN32_ERROR = 9124u32; +pub const DNS_ERROR_INVALID_POLICY_TABLE: WIN32_ERROR = 9572u32; +pub const DNS_ERROR_INVALID_PROPERTY: WIN32_ERROR = 9553u32; +pub const DNS_ERROR_INVALID_ROLLOVER_PERIOD: WIN32_ERROR = 9114u32; +pub const DNS_ERROR_INVALID_SCOPE_NAME: WIN32_ERROR = 9958u32; +pub const DNS_ERROR_INVALID_SCOPE_OPERATION: WIN32_ERROR = 9961u32; +pub const DNS_ERROR_INVALID_SIGNATURE_VALIDITY_PERIOD: WIN32_ERROR = 9123u32; +pub const DNS_ERROR_INVALID_TYPE: WIN32_ERROR = 9551u32; +pub const DNS_ERROR_INVALID_XML: WIN32_ERROR = 9126u32; +pub const DNS_ERROR_INVALID_ZONESCOPE_NAME: WIN32_ERROR = 9954u32; +pub const DNS_ERROR_INVALID_ZONE_OPERATION: WIN32_ERROR = 9603u32; +pub const DNS_ERROR_INVALID_ZONE_TYPE: WIN32_ERROR = 9611u32; +pub const DNS_ERROR_KEYMASTER_REQUIRED: WIN32_ERROR = 9101u32; +pub const DNS_ERROR_KSP_DOES_NOT_SUPPORT_PROTECTION: WIN32_ERROR = 9108u32; +pub const DNS_ERROR_KSP_NOT_ACCESSIBLE: WIN32_ERROR = 9112u32; +pub const DNS_ERROR_LOAD_ZONESCOPE_FAILED: WIN32_ERROR = 9956u32; +pub const DNS_ERROR_NAME_DOES_NOT_EXIST: WIN32_ERROR = 9714u32; +pub const DNS_ERROR_NAME_NOT_IN_ZONE: WIN32_ERROR = 9706u32; +pub const DNS_ERROR_NBSTAT_INIT_FAILED: WIN32_ERROR = 9617u32; +pub const DNS_ERROR_NEED_SECONDARY_ADDRESSES: WIN32_ERROR = 9614u32; +pub const DNS_ERROR_NEED_WINS_SERVERS: WIN32_ERROR = 9616u32; +pub const DNS_ERROR_NODE_CREATION_FAILED: WIN32_ERROR = 9703u32; +pub const DNS_ERROR_NODE_IS_CNAME: WIN32_ERROR = 9708u32; +pub const DNS_ERROR_NODE_IS_DNAME: WIN32_ERROR = 9720u32; +pub const DNS_ERROR_NON_RFC_NAME: WIN32_ERROR = 9556u32; +pub const DNS_ERROR_NOT_ALLOWED_ON_ACTIVE_SKD: WIN32_ERROR = 9119u32; +pub const DNS_ERROR_NOT_ALLOWED_ON_RODC: WIN32_ERROR = 9569u32; +pub const DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER: WIN32_ERROR = 9562u32; +pub const DNS_ERROR_NOT_ALLOWED_ON_SIGNED_ZONE: WIN32_ERROR = 9102u32; +pub const DNS_ERROR_NOT_ALLOWED_ON_UNSIGNED_ZONE: WIN32_ERROR = 9121u32; +pub const DNS_ERROR_NOT_ALLOWED_ON_ZSK: WIN32_ERROR = 9118u32; +pub const DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION: WIN32_ERROR = 9563u32; +pub const DNS_ERROR_NOT_ALLOWED_UNDER_DNAME: WIN32_ERROR = 9570u32; +pub const DNS_ERROR_NOT_ALLOWED_WITH_ZONESCOPES: WIN32_ERROR = 9955u32; +pub const DNS_ERROR_NOT_ENOUGH_SIGNING_KEY_DESCRIPTORS: WIN32_ERROR = 9104u32; +pub const DNS_ERROR_NOT_UNIQUE: WIN32_ERROR = 9555u32; +pub const DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE: WIN32_ERROR = 9719u32; +pub const DNS_ERROR_NO_CREATE_CACHE_DATA: WIN32_ERROR = 9713u32; +pub const DNS_ERROR_NO_DNS_SERVERS: WIN32_ERROR = 9852u32; +pub const DNS_ERROR_NO_MEMORY: WIN32_ERROR = 14u32; +pub const DNS_ERROR_NO_PACKET: WIN32_ERROR = 9503u32; +pub const DNS_ERROR_NO_TCPIP: WIN32_ERROR = 9851u32; +pub const DNS_ERROR_NO_VALID_TRUST_ANCHORS: WIN32_ERROR = 9127u32; +pub const DNS_ERROR_NO_ZONE_INFO: WIN32_ERROR = 9602u32; +pub const DNS_ERROR_NSEC3_INCOMPATIBLE_WITH_RSA_SHA1: WIN32_ERROR = 9103u32; +pub const DNS_ERROR_NSEC3_NAME_COLLISION: WIN32_ERROR = 9129u32; +pub const DNS_ERROR_NSEC_INCOMPATIBLE_WITH_NSEC3_RSA_SHA1: WIN32_ERROR = 9130u32; +pub const DNS_ERROR_NUMERIC_NAME: WIN32_ERROR = 9561u32; +pub const DNS_ERROR_POLICY_ALREADY_EXISTS: WIN32_ERROR = 9971u32; +pub const DNS_ERROR_POLICY_DOES_NOT_EXIST: WIN32_ERROR = 9972u32; +pub const DNS_ERROR_POLICY_INVALID_CRITERIA: WIN32_ERROR = 9973u32; +pub const DNS_ERROR_POLICY_INVALID_CRITERIA_CLIENT_SUBNET: WIN32_ERROR = 9990u32; +pub const DNS_ERROR_POLICY_INVALID_CRITERIA_FQDN: WIN32_ERROR = 9994u32; +pub const DNS_ERROR_POLICY_INVALID_CRITERIA_INTERFACE: WIN32_ERROR = 9993u32; +pub const DNS_ERROR_POLICY_INVALID_CRITERIA_NETWORK_PROTOCOL: WIN32_ERROR = 9992u32; +pub const DNS_ERROR_POLICY_INVALID_CRITERIA_QUERY_TYPE: WIN32_ERROR = 9995u32; +pub const DNS_ERROR_POLICY_INVALID_CRITERIA_TIME_OF_DAY: WIN32_ERROR = 9996u32; +pub const DNS_ERROR_POLICY_INVALID_CRITERIA_TRANSPORT_PROTOCOL: WIN32_ERROR = 9991u32; +pub const DNS_ERROR_POLICY_INVALID_NAME: WIN32_ERROR = 9982u32; +pub const DNS_ERROR_POLICY_INVALID_SETTINGS: WIN32_ERROR = 9974u32; +pub const DNS_ERROR_POLICY_INVALID_WEIGHT: WIN32_ERROR = 9981u32; +pub const DNS_ERROR_POLICY_LOCKED: WIN32_ERROR = 9980u32; +pub const DNS_ERROR_POLICY_MISSING_CRITERIA: WIN32_ERROR = 9983u32; +pub const DNS_ERROR_POLICY_PROCESSING_ORDER_INVALID: WIN32_ERROR = 9985u32; +pub const DNS_ERROR_POLICY_SCOPE_MISSING: WIN32_ERROR = 9986u32; +pub const DNS_ERROR_POLICY_SCOPE_NOT_ALLOWED: WIN32_ERROR = 9987u32; +pub const DNS_ERROR_PRIMARY_REQUIRES_DATAFILE: WIN32_ERROR = 9651u32; +pub const DNS_ERROR_RCODE: WIN32_ERROR = 9504u32; +pub const DNS_ERROR_RCODE_BADKEY: WIN32_ERROR = 9017u32; +pub const DNS_ERROR_RCODE_BADSIG: WIN32_ERROR = 9016u32; +pub const DNS_ERROR_RCODE_BADTIME: WIN32_ERROR = 9018u32; +pub const DNS_ERROR_RCODE_FORMAT_ERROR: WIN32_ERROR = 9001u32; +pub const DNS_ERROR_RCODE_NAME_ERROR: WIN32_ERROR = 9003u32; +pub const DNS_ERROR_RCODE_NOTAUTH: WIN32_ERROR = 9009u32; +pub const DNS_ERROR_RCODE_NOTZONE: WIN32_ERROR = 9010u32; +pub const DNS_ERROR_RCODE_NOT_IMPLEMENTED: WIN32_ERROR = 9004u32; +pub const DNS_ERROR_RCODE_NXRRSET: WIN32_ERROR = 9008u32; +pub const DNS_ERROR_RCODE_REFUSED: WIN32_ERROR = 9005u32; +pub const DNS_ERROR_RCODE_SERVER_FAILURE: WIN32_ERROR = 9002u32; +pub const DNS_ERROR_RCODE_YXDOMAIN: WIN32_ERROR = 9006u32; +pub const DNS_ERROR_RCODE_YXRRSET: WIN32_ERROR = 9007u32; +pub const DNS_ERROR_RECORD_ALREADY_EXISTS: WIN32_ERROR = 9711u32; +pub const DNS_ERROR_RECORD_DOES_NOT_EXIST: WIN32_ERROR = 9701u32; +pub const DNS_ERROR_RECORD_FORMAT: WIN32_ERROR = 9702u32; +pub const DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT: WIN32_ERROR = 9710u32; +pub const DNS_ERROR_RECORD_TIMED_OUT: WIN32_ERROR = 9705u32; +pub const DNS_ERROR_ROLLOVER_ALREADY_QUEUED: WIN32_ERROR = 9120u32; +pub const DNS_ERROR_ROLLOVER_IN_PROGRESS: WIN32_ERROR = 9116u32; +pub const DNS_ERROR_ROLLOVER_NOT_POKEABLE: WIN32_ERROR = 9128u32; +pub const DNS_ERROR_RRL_INVALID_IPV4_PREFIX: WIN32_ERROR = 9913u32; +pub const DNS_ERROR_RRL_INVALID_IPV6_PREFIX: WIN32_ERROR = 9914u32; +pub const DNS_ERROR_RRL_INVALID_LEAK_RATE: WIN32_ERROR = 9916u32; +pub const DNS_ERROR_RRL_INVALID_TC_RATE: WIN32_ERROR = 9915u32; +pub const DNS_ERROR_RRL_INVALID_WINDOW_SIZE: WIN32_ERROR = 9912u32; +pub const DNS_ERROR_RRL_LEAK_RATE_LESSTHAN_TC_RATE: WIN32_ERROR = 9917u32; +pub const DNS_ERROR_RRL_NOT_ENABLED: WIN32_ERROR = 9911u32; +pub const DNS_ERROR_SCOPE_ALREADY_EXISTS: WIN32_ERROR = 9963u32; +pub const DNS_ERROR_SCOPE_DOES_NOT_EXIST: WIN32_ERROR = 9959u32; +pub const DNS_ERROR_SCOPE_LOCKED: WIN32_ERROR = 9962u32; +pub const DNS_ERROR_SECONDARY_DATA: WIN32_ERROR = 9712u32; +pub const DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP: WIN32_ERROR = 9612u32; +pub const DNS_ERROR_SERVERSCOPE_IS_REFERENCED: WIN32_ERROR = 9988u32; +pub const DNS_ERROR_SIGNING_KEY_NOT_ACCESSIBLE: WIN32_ERROR = 9107u32; +pub const DNS_ERROR_SOA_DELETE_INVALID: WIN32_ERROR = 9618u32; +pub const DNS_ERROR_STANDBY_KEY_NOT_PRESENT: WIN32_ERROR = 9117u32; +pub const DNS_ERROR_SUBNET_ALREADY_EXISTS: WIN32_ERROR = 9979u32; +pub const DNS_ERROR_SUBNET_DOES_NOT_EXIST: WIN32_ERROR = 9978u32; +pub const DNS_ERROR_TOO_MANY_SKDS: WIN32_ERROR = 9113u32; +pub const DNS_ERROR_TRY_AGAIN_LATER: WIN32_ERROR = 9554u32; +pub const DNS_ERROR_UNEXPECTED_CNG_ERROR: WIN32_ERROR = 9110u32; +pub const DNS_ERROR_UNEXPECTED_DATA_PROTECTION_ERROR: WIN32_ERROR = 9109u32; +pub const DNS_ERROR_UNKNOWN_RECORD_TYPE: WIN32_ERROR = 9704u32; +pub const DNS_ERROR_UNKNOWN_SIGNING_PARAMETER_VERSION: WIN32_ERROR = 9111u32; +pub const DNS_ERROR_UNSECURE_PACKET: WIN32_ERROR = 9505u32; +pub const DNS_ERROR_UNSUPPORTED_ALGORITHM: WIN32_ERROR = 9105u32; +pub const DNS_ERROR_VIRTUALIZATION_INSTANCE_ALREADY_EXISTS: WIN32_ERROR = 9921u32; +pub const DNS_ERROR_VIRTUALIZATION_INSTANCE_DOES_NOT_EXIST: WIN32_ERROR = 9922u32; +pub const DNS_ERROR_VIRTUALIZATION_TREE_LOCKED: WIN32_ERROR = 9923u32; +pub const DNS_ERROR_WINS_INIT_FAILED: WIN32_ERROR = 9615u32; +pub const DNS_ERROR_ZONESCOPE_ALREADY_EXISTS: WIN32_ERROR = 9951u32; +pub const DNS_ERROR_ZONESCOPE_DOES_NOT_EXIST: WIN32_ERROR = 9952u32; +pub const DNS_ERROR_ZONESCOPE_FILE_WRITEBACK_FAILED: WIN32_ERROR = 9957u32; +pub const DNS_ERROR_ZONESCOPE_IS_REFERENCED: WIN32_ERROR = 9989u32; +pub const DNS_ERROR_ZONE_ALREADY_EXISTS: WIN32_ERROR = 9609u32; +pub const DNS_ERROR_ZONE_CONFIGURATION_ERROR: WIN32_ERROR = 9604u32; +pub const DNS_ERROR_ZONE_CREATION_FAILED: WIN32_ERROR = 9608u32; +pub const DNS_ERROR_ZONE_DOES_NOT_EXIST: WIN32_ERROR = 9601u32; +pub const DNS_ERROR_ZONE_HAS_NO_NS_RECORDS: WIN32_ERROR = 9606u32; +pub const DNS_ERROR_ZONE_HAS_NO_SOA_RECORD: WIN32_ERROR = 9605u32; +pub const DNS_ERROR_ZONE_IS_SHUTDOWN: WIN32_ERROR = 9621u32; +pub const DNS_ERROR_ZONE_LOCKED: WIN32_ERROR = 9607u32; +pub const DNS_ERROR_ZONE_LOCKED_FOR_SIGNING: WIN32_ERROR = 9622u32; +pub const DNS_ERROR_ZONE_NOT_SECONDARY: WIN32_ERROR = 9613u32; +pub const DNS_ERROR_ZONE_REQUIRES_MASTER_IP: WIN32_ERROR = 9620u32; +pub const DUPLICATE_CLOSE_SOURCE: DUPLICATE_HANDLE_OPTIONS = 1u32; +pub type DUPLICATE_HANDLE_OPTIONS = u32; +pub const DUPLICATE_SAME_ACCESS: DUPLICATE_HANDLE_OPTIONS = 2u32; +pub const ENABLE_AUTO_POSITION: CONSOLE_MODE = 256u32; +pub const ENABLE_ECHO_INPUT: CONSOLE_MODE = 4u32; +pub const ENABLE_EXTENDED_FLAGS: CONSOLE_MODE = 128u32; +pub const ENABLE_INSERT_MODE: CONSOLE_MODE = 32u32; +pub const ENABLE_LINE_INPUT: CONSOLE_MODE = 2u32; +pub const ENABLE_LVB_GRID_WORLDWIDE: CONSOLE_MODE = 16u32; +pub const ENABLE_MOUSE_INPUT: CONSOLE_MODE = 16u32; +pub const ENABLE_PROCESSED_INPUT: CONSOLE_MODE = 1u32; +pub const ENABLE_PROCESSED_OUTPUT: CONSOLE_MODE = 1u32; +pub const ENABLE_QUICK_EDIT_MODE: CONSOLE_MODE = 64u32; +pub const ENABLE_VIRTUAL_TERMINAL_INPUT: CONSOLE_MODE = 512u32; +pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING: CONSOLE_MODE = 4u32; +pub const ENABLE_WINDOW_INPUT: CONSOLE_MODE = 8u32; +pub const ENABLE_WRAP_AT_EOL_OUTPUT: CONSOLE_MODE = 2u32; +pub const ERROR_ABANDONED_WAIT_0: WIN32_ERROR = 735u32; +pub const ERROR_ABANDONED_WAIT_63: WIN32_ERROR = 736u32; +pub const ERROR_ABANDON_HIBERFILE: WIN32_ERROR = 787u32; +pub const ERROR_ABIOS_ERROR: WIN32_ERROR = 538u32; +pub const ERROR_ACCESS_AUDIT_BY_POLICY: WIN32_ERROR = 785u32; +pub const ERROR_ACCESS_DENIED: WIN32_ERROR = 5u32; +pub const ERROR_ACCESS_DENIED_APPDATA: WIN32_ERROR = 502u32; +pub const ERROR_ACCESS_DISABLED_BY_POLICY: WIN32_ERROR = 1260u32; +pub const ERROR_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY: WIN32_ERROR = 786u32; +pub const ERROR_ACCESS_DISABLED_WEBBLADE: WIN32_ERROR = 1277u32; +pub const ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER: WIN32_ERROR = 1278u32; +pub const ERROR_ACCOUNT_DISABLED: WIN32_ERROR = 1331u32; +pub const ERROR_ACCOUNT_EXPIRED: WIN32_ERROR = 1793u32; +pub const ERROR_ACCOUNT_LOCKED_OUT: WIN32_ERROR = 1909u32; +pub const ERROR_ACCOUNT_RESTRICTION: WIN32_ERROR = 1327u32; +pub const ERROR_ACPI_ERROR: WIN32_ERROR = 669u32; +pub const ERROR_ACTIVE_CONNECTIONS: WIN32_ERROR = 2402u32; +pub const ERROR_ADAP_HDW_ERR: WIN32_ERROR = 57u32; +pub const ERROR_ADDRESS_ALREADY_ASSOCIATED: WIN32_ERROR = 1227u32; +pub const ERROR_ADDRESS_NOT_ASSOCIATED: WIN32_ERROR = 1228u32; +pub const ERROR_ALERTED: WIN32_ERROR = 739u32; +pub const ERROR_ALIAS_EXISTS: WIN32_ERROR = 1379u32; +pub const ERROR_ALLOCATE_BUCKET: WIN32_ERROR = 602u32; +pub const ERROR_ALLOTTED_SPACE_EXCEEDED: WIN32_ERROR = 1344u32; +pub const ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED: WIN32_ERROR = 1933u32; +pub const ERROR_ALREADY_ASSIGNED: WIN32_ERROR = 85u32; +pub const ERROR_ALREADY_EXISTS: WIN32_ERROR = 183u32; +pub const ERROR_ALREADY_FIBER: WIN32_ERROR = 1280u32; +pub const ERROR_ALREADY_HAS_STREAM_ID: WIN32_ERROR = 4444u32; +pub const ERROR_ALREADY_INITIALIZED: WIN32_ERROR = 1247u32; +pub const ERROR_ALREADY_REGISTERED: WIN32_ERROR = 1242u32; +pub const ERROR_ALREADY_RUNNING_LKG: WIN32_ERROR = 1074u32; +pub const ERROR_ALREADY_THREAD: WIN32_ERROR = 1281u32; +pub const ERROR_ALREADY_WAITING: WIN32_ERROR = 1904u32; +pub const ERROR_ALREADY_WIN32: WIN32_ERROR = 719u32; +pub const ERROR_API_UNAVAILABLE: WIN32_ERROR = 15841u32; +pub const ERROR_APPCONTAINER_REQUIRED: WIN32_ERROR = 4251u32; +pub const ERROR_APPEXEC_APP_COMPAT_BLOCK: WIN32_ERROR = 3068u32; +pub const ERROR_APPEXEC_CALLER_WAIT_TIMEOUT: WIN32_ERROR = 3069u32; +pub const ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_LICENSING: WIN32_ERROR = 3071u32; +pub const ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_RESOURCES: WIN32_ERROR = 3072u32; +pub const ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_TERMINATION: WIN32_ERROR = 3070u32; +pub const ERROR_APPEXEC_CONDITION_NOT_SATISFIED: WIN32_ERROR = 3060u32; +pub const ERROR_APPEXEC_HANDLE_INVALIDATED: WIN32_ERROR = 3061u32; +pub const ERROR_APPEXEC_HOST_ID_MISMATCH: WIN32_ERROR = 3066u32; +pub const ERROR_APPEXEC_INVALID_HOST_GENERATION: WIN32_ERROR = 3062u32; +pub const ERROR_APPEXEC_INVALID_HOST_STATE: WIN32_ERROR = 3064u32; +pub const ERROR_APPEXEC_NO_DONOR: WIN32_ERROR = 3065u32; +pub const ERROR_APPEXEC_UNEXPECTED_PROCESS_REGISTRATION: WIN32_ERROR = 3063u32; +pub const ERROR_APPEXEC_UNKNOWN_USER: WIN32_ERROR = 3067u32; +pub const ERROR_APPHELP_BLOCK: WIN32_ERROR = 1259u32; +pub const ERROR_APPX_FILE_NOT_ENCRYPTED: WIN32_ERROR = 409u32; +pub const ERROR_APP_HANG: WIN32_ERROR = 1298u32; +pub const ERROR_APP_INIT_FAILURE: WIN32_ERROR = 575u32; +pub const ERROR_APP_WRONG_OS: WIN32_ERROR = 1151u32; +pub const ERROR_ARBITRATION_UNHANDLED: WIN32_ERROR = 723u32; +pub const ERROR_ARENA_TRASHED: WIN32_ERROR = 7u32; +pub const ERROR_ARITHMETIC_OVERFLOW: WIN32_ERROR = 534u32; +pub const ERROR_ASSERTION_FAILURE: WIN32_ERROR = 668u32; +pub const ERROR_ATOMIC_LOCKS_NOT_SUPPORTED: WIN32_ERROR = 174u32; +pub const ERROR_AUDIT_FAILED: WIN32_ERROR = 606u32; +pub const ERROR_AUTHENTICATION_FIREWALL_FAILED: WIN32_ERROR = 1935u32; +pub const ERROR_AUTHIP_FAILURE: WIN32_ERROR = 1469u32; +pub const ERROR_AUTODATASEG_EXCEEDS_64k: WIN32_ERROR = 199u32; +pub const ERROR_BACKUP_CONTROLLER: WIN32_ERROR = 586u32; +pub const ERROR_BADDB: WIN32_ERROR = 1009u32; +pub const ERROR_BADKEY: WIN32_ERROR = 1010u32; +pub const ERROR_BADSTARTPOSITION: WIN32_ERROR = 778u32; +pub const ERROR_BAD_ACCESSOR_FLAGS: WIN32_ERROR = 773u32; +pub const ERROR_BAD_ARGUMENTS: WIN32_ERROR = 160u32; +pub const ERROR_BAD_COMMAND: WIN32_ERROR = 22u32; +pub const ERROR_BAD_COMPRESSION_BUFFER: WIN32_ERROR = 605u32; +pub const ERROR_BAD_CONFIGURATION: WIN32_ERROR = 1610u32; +pub const ERROR_BAD_CURRENT_DIRECTORY: WIN32_ERROR = 703u32; +pub const ERROR_BAD_DESCRIPTOR_FORMAT: WIN32_ERROR = 1361u32; +pub const ERROR_BAD_DEVICE: WIN32_ERROR = 1200u32; +pub const ERROR_BAD_DEVICE_PATH: WIN32_ERROR = 330u32; +pub const ERROR_BAD_DEV_TYPE: WIN32_ERROR = 66u32; +pub const ERROR_BAD_DLL_ENTRYPOINT: WIN32_ERROR = 609u32; +pub const ERROR_BAD_DRIVER_LEVEL: WIN32_ERROR = 119u32; +pub const ERROR_BAD_ENVIRONMENT: WIN32_ERROR = 10u32; +pub const ERROR_BAD_EXE_FORMAT: WIN32_ERROR = 193u32; +pub const ERROR_BAD_FILE_TYPE: WIN32_ERROR = 222u32; +pub const ERROR_BAD_FORMAT: WIN32_ERROR = 11u32; +pub const ERROR_BAD_FUNCTION_TABLE: WIN32_ERROR = 559u32; +pub const ERROR_BAD_IMPERSONATION_LEVEL: WIN32_ERROR = 1346u32; +pub const ERROR_BAD_INHERITANCE_ACL: WIN32_ERROR = 1340u32; +pub const ERROR_BAD_LENGTH: WIN32_ERROR = 24u32; +pub const ERROR_BAD_LOGON_SESSION_STATE: WIN32_ERROR = 1365u32; +pub const ERROR_BAD_MCFG_TABLE: WIN32_ERROR = 791u32; +pub const ERROR_BAD_NETPATH: WIN32_ERROR = 53u32; +pub const ERROR_BAD_NET_NAME: WIN32_ERROR = 67u32; +pub const ERROR_BAD_NET_RESP: WIN32_ERROR = 58u32; +pub const ERROR_BAD_PATHNAME: WIN32_ERROR = 161u32; +pub const ERROR_BAD_PIPE: WIN32_ERROR = 230u32; +pub const ERROR_BAD_PROFILE: WIN32_ERROR = 1206u32; +pub const ERROR_BAD_PROVIDER: WIN32_ERROR = 1204u32; +pub const ERROR_BAD_QUERY_SYNTAX: WIN32_ERROR = 1615u32; +pub const ERROR_BAD_RECOVERY_POLICY: WIN32_ERROR = 6012u32; +pub const ERROR_BAD_REM_ADAP: WIN32_ERROR = 60u32; +pub const ERROR_BAD_SERVICE_ENTRYPOINT: WIN32_ERROR = 610u32; +pub const ERROR_BAD_STACK: WIN32_ERROR = 543u32; +pub const ERROR_BAD_THREADID_ADDR: WIN32_ERROR = 159u32; +pub const ERROR_BAD_TOKEN_TYPE: WIN32_ERROR = 1349u32; +pub const ERROR_BAD_UNIT: WIN32_ERROR = 20u32; +pub const ERROR_BAD_USERNAME: WIN32_ERROR = 2202u32; +pub const ERROR_BAD_USER_PROFILE: WIN32_ERROR = 1253u32; +pub const ERROR_BAD_VALIDATION_CLASS: WIN32_ERROR = 1348u32; +pub const ERROR_BEGINNING_OF_MEDIA: WIN32_ERROR = 1102u32; +pub const ERROR_BEYOND_VDL: WIN32_ERROR = 1289u32; +pub const ERROR_BIOS_FAILED_TO_CONNECT_INTERRUPT: WIN32_ERROR = 585u32; +pub const ERROR_BLOCKED_BY_PARENTAL_CONTROLS: WIN32_ERROR = 346u32; +pub const ERROR_BLOCK_SHARED: WIN32_ERROR = 514u32; +pub const ERROR_BLOCK_SOURCE_WEAK_REFERENCE_INVALID: WIN32_ERROR = 512u32; +pub const ERROR_BLOCK_TARGET_WEAK_REFERENCE_INVALID: WIN32_ERROR = 513u32; +pub const ERROR_BLOCK_TOO_MANY_REFERENCES: WIN32_ERROR = 347u32; +pub const ERROR_BLOCK_WEAK_REFERENCE_INVALID: WIN32_ERROR = 511u32; +pub const ERROR_BOOT_ALREADY_ACCEPTED: WIN32_ERROR = 1076u32; +pub const ERROR_BROKEN_PIPE: WIN32_ERROR = 109u32; +pub const ERROR_BUFFER_ALL_ZEROS: WIN32_ERROR = 754u32; +pub const ERROR_BUFFER_OVERFLOW: WIN32_ERROR = 111u32; +pub const ERROR_BUSY: WIN32_ERROR = 170u32; +pub const ERROR_BUSY_DRIVE: WIN32_ERROR = 142u32; +pub const ERROR_BUS_RESET: WIN32_ERROR = 1111u32; +pub const ERROR_BYPASSIO_FLT_NOT_SUPPORTED: WIN32_ERROR = 506u32; +pub const ERROR_CACHE_PAGE_LOCKED: WIN32_ERROR = 752u32; +pub const ERROR_CALLBACK_INVOKE_INLINE: WIN32_ERROR = 812u32; +pub const ERROR_CALLBACK_POP_STACK: WIN32_ERROR = 768u32; +pub const ERROR_CALLBACK_SUPPLIED_INVALID_DATA: WIN32_ERROR = 1273u32; +pub const ERROR_CALL_NOT_IMPLEMENTED: WIN32_ERROR = 120u32; +pub const ERROR_CANCELLED: WIN32_ERROR = 1223u32; +pub const ERROR_CANCEL_VIOLATION: WIN32_ERROR = 173u32; +pub const ERROR_CANNOT_BREAK_OPLOCK: WIN32_ERROR = 802u32; +pub const ERROR_CANNOT_COPY: WIN32_ERROR = 266u32; +pub const ERROR_CANNOT_DETECT_DRIVER_FAILURE: WIN32_ERROR = 1080u32; +pub const ERROR_CANNOT_DETECT_PROCESS_ABORT: WIN32_ERROR = 1081u32; +pub const ERROR_CANNOT_FIND_WND_CLASS: WIN32_ERROR = 1407u32; +pub const ERROR_CANNOT_GRANT_REQUESTED_OPLOCK: WIN32_ERROR = 801u32; +pub const ERROR_CANNOT_IMPERSONATE: WIN32_ERROR = 1368u32; +pub const ERROR_CANNOT_LOAD_REGISTRY_FILE: WIN32_ERROR = 589u32; +pub const ERROR_CANNOT_MAKE: WIN32_ERROR = 82u32; +pub const ERROR_CANNOT_OPEN_PROFILE: WIN32_ERROR = 1205u32; +pub const ERROR_CANTFETCHBACKWARDS: WIN32_ERROR = 770u32; +pub const ERROR_CANTOPEN: WIN32_ERROR = 1011u32; +pub const ERROR_CANTREAD: WIN32_ERROR = 1012u32; +pub const ERROR_CANTSCROLLBACKWARDS: WIN32_ERROR = 771u32; +pub const ERROR_CANTWRITE: WIN32_ERROR = 1013u32; +pub const ERROR_CANT_ACCESS_DOMAIN_INFO: WIN32_ERROR = 1351u32; +pub const ERROR_CANT_ACCESS_FILE: WIN32_ERROR = 1920u32; +pub const ERROR_CANT_CLEAR_ENCRYPTION_FLAG: WIN32_ERROR = 432u32; +pub const ERROR_CANT_DISABLE_MANDATORY: WIN32_ERROR = 1310u32; +pub const ERROR_CANT_ENABLE_DENY_ONLY: WIN32_ERROR = 629u32; +pub const ERROR_CANT_OPEN_ANONYMOUS: WIN32_ERROR = 1347u32; +pub const ERROR_CANT_RESOLVE_FILENAME: WIN32_ERROR = 1921u32; +pub const ERROR_CANT_TERMINATE_SELF: WIN32_ERROR = 555u32; +pub const ERROR_CANT_WAIT: WIN32_ERROR = 554u32; +pub const ERROR_CAN_NOT_COMPLETE: WIN32_ERROR = 1003u32; +pub const ERROR_CAPAUTHZ_CHANGE_TYPE: WIN32_ERROR = 451u32; +pub const ERROR_CAPAUTHZ_DB_CORRUPTED: WIN32_ERROR = 455u32; +pub const ERROR_CAPAUTHZ_NOT_AUTHORIZED: WIN32_ERROR = 453u32; +pub const ERROR_CAPAUTHZ_NOT_DEVUNLOCKED: WIN32_ERROR = 450u32; +pub const ERROR_CAPAUTHZ_NOT_PROVISIONED: WIN32_ERROR = 452u32; +pub const ERROR_CAPAUTHZ_NO_POLICY: WIN32_ERROR = 454u32; +pub const ERROR_CAPAUTHZ_SCCD_DEV_MODE_REQUIRED: WIN32_ERROR = 459u32; +pub const ERROR_CAPAUTHZ_SCCD_INVALID_CATALOG: WIN32_ERROR = 456u32; +pub const ERROR_CAPAUTHZ_SCCD_NO_AUTH_ENTITY: WIN32_ERROR = 457u32; +pub const ERROR_CAPAUTHZ_SCCD_NO_CAPABILITY_MATCH: WIN32_ERROR = 460u32; +pub const ERROR_CAPAUTHZ_SCCD_PARSE_ERROR: WIN32_ERROR = 458u32; +pub const ERROR_CARDBUS_NOT_SUPPORTED: WIN32_ERROR = 724u32; +pub const ERROR_CASE_DIFFERING_NAMES_IN_DIR: WIN32_ERROR = 424u32; +pub const ERROR_CASE_SENSITIVE_PATH: WIN32_ERROR = 442u32; +pub const ERROR_CERTIFICATE_VALIDATION_PREFERENCE_CONFLICT: WIN32_ERROR = 817u32; +pub const ERROR_CHECKING_FILE_SYSTEM: WIN32_ERROR = 712u32; +pub const ERROR_CHECKOUT_REQUIRED: WIN32_ERROR = 221u32; +pub const ERROR_CHILD_MUST_BE_VOLATILE: WIN32_ERROR = 1021u32; +pub const ERROR_CHILD_NOT_COMPLETE: WIN32_ERROR = 129u32; +pub const ERROR_CHILD_PROCESS_BLOCKED: WIN32_ERROR = 367u32; +pub const ERROR_CHILD_WINDOW_MENU: WIN32_ERROR = 1436u32; +pub const ERROR_CIMFS_IMAGE_CORRUPT: WIN32_ERROR = 470u32; +pub const ERROR_CIMFS_IMAGE_VERSION_NOT_SUPPORTED: WIN32_ERROR = 471u32; +pub const ERROR_CIRCULAR_DEPENDENCY: WIN32_ERROR = 1059u32; +pub const ERROR_CLASS_ALREADY_EXISTS: WIN32_ERROR = 1410u32; +pub const ERROR_CLASS_DOES_NOT_EXIST: WIN32_ERROR = 1411u32; +pub const ERROR_CLASS_HAS_WINDOWS: WIN32_ERROR = 1412u32; +pub const ERROR_CLIENT_SERVER_PARAMETERS_INVALID: WIN32_ERROR = 597u32; +pub const ERROR_CLIPBOARD_NOT_OPEN: WIN32_ERROR = 1418u32; +pub const ERROR_CLOUD_FILE_ACCESS_DENIED: WIN32_ERROR = 395u32; +pub const ERROR_CLOUD_FILE_ALREADY_CONNECTED: WIN32_ERROR = 378u32; +pub const ERROR_CLOUD_FILE_AUTHENTICATION_FAILED: WIN32_ERROR = 386u32; +pub const ERROR_CLOUD_FILE_CONNECTED_PROVIDER_ONLY: WIN32_ERROR = 382u32; +pub const ERROR_CLOUD_FILE_DEHYDRATION_DISALLOWED: WIN32_ERROR = 434u32; +pub const ERROR_CLOUD_FILE_INCOMPATIBLE_HARDLINKS: WIN32_ERROR = 396u32; +pub const ERROR_CLOUD_FILE_INSUFFICIENT_RESOURCES: WIN32_ERROR = 387u32; +pub const ERROR_CLOUD_FILE_INVALID_REQUEST: WIN32_ERROR = 380u32; +pub const ERROR_CLOUD_FILE_IN_USE: WIN32_ERROR = 391u32; +pub const ERROR_CLOUD_FILE_METADATA_CORRUPT: WIN32_ERROR = 363u32; +pub const ERROR_CLOUD_FILE_METADATA_TOO_LARGE: WIN32_ERROR = 364u32; +pub const ERROR_CLOUD_FILE_NETWORK_UNAVAILABLE: WIN32_ERROR = 388u32; +pub const ERROR_CLOUD_FILE_NOT_IN_SYNC: WIN32_ERROR = 377u32; +pub const ERROR_CLOUD_FILE_NOT_SUPPORTED: WIN32_ERROR = 379u32; +pub const ERROR_CLOUD_FILE_NOT_UNDER_SYNC_ROOT: WIN32_ERROR = 390u32; +pub const ERROR_CLOUD_FILE_PINNED: WIN32_ERROR = 392u32; +pub const ERROR_CLOUD_FILE_PROPERTY_BLOB_CHECKSUM_MISMATCH: WIN32_ERROR = 366u32; +pub const ERROR_CLOUD_FILE_PROPERTY_BLOB_TOO_LARGE: WIN32_ERROR = 365u32; +pub const ERROR_CLOUD_FILE_PROPERTY_CORRUPT: WIN32_ERROR = 394u32; +pub const ERROR_CLOUD_FILE_PROPERTY_LOCK_CONFLICT: WIN32_ERROR = 397u32; +pub const ERROR_CLOUD_FILE_PROPERTY_VERSION_NOT_SUPPORTED: WIN32_ERROR = 375u32; +pub const ERROR_CLOUD_FILE_PROVIDER_NOT_RUNNING: WIN32_ERROR = 362u32; +pub const ERROR_CLOUD_FILE_PROVIDER_TERMINATED: WIN32_ERROR = 404u32; +pub const ERROR_CLOUD_FILE_READ_ONLY_VOLUME: WIN32_ERROR = 381u32; +pub const ERROR_CLOUD_FILE_REQUEST_ABORTED: WIN32_ERROR = 393u32; +pub const ERROR_CLOUD_FILE_REQUEST_CANCELED: WIN32_ERROR = 398u32; +pub const ERROR_CLOUD_FILE_REQUEST_TIMEOUT: WIN32_ERROR = 426u32; +pub const ERROR_CLOUD_FILE_SYNC_ROOT_METADATA_CORRUPT: WIN32_ERROR = 358u32; +pub const ERROR_CLOUD_FILE_TOO_MANY_PROPERTY_BLOBS: WIN32_ERROR = 374u32; +pub const ERROR_CLOUD_FILE_UNSUCCESSFUL: WIN32_ERROR = 389u32; +pub const ERROR_CLOUD_FILE_US_MESSAGE_TIMEOUT: WIN32_ERROR = 475u32; +pub const ERROR_CLOUD_FILE_VALIDATION_FAILED: WIN32_ERROR = 383u32; +pub const ERROR_COMMITMENT_LIMIT: WIN32_ERROR = 1455u32; +pub const ERROR_COMMITMENT_MINIMUM: WIN32_ERROR = 635u32; +pub const ERROR_COMPRESSED_FILE_NOT_SUPPORTED: WIN32_ERROR = 335u32; +pub const ERROR_COMPRESSION_DISABLED: WIN32_ERROR = 769u32; +pub const ERROR_COMPRESSION_NOT_BENEFICIAL: WIN32_ERROR = 344u32; +pub const ERROR_CONNECTED_OTHER_PASSWORD: WIN32_ERROR = 2108u32; +pub const ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT: WIN32_ERROR = 2109u32; +pub const ERROR_CONNECTION_ABORTED: WIN32_ERROR = 1236u32; +pub const ERROR_CONNECTION_ACTIVE: WIN32_ERROR = 1230u32; +pub const ERROR_CONNECTION_COUNT_LIMIT: WIN32_ERROR = 1238u32; +pub const ERROR_CONNECTION_INVALID: WIN32_ERROR = 1229u32; +pub const ERROR_CONNECTION_REFUSED: WIN32_ERROR = 1225u32; +pub const ERROR_CONNECTION_UNAVAIL: WIN32_ERROR = 1201u32; +pub const ERROR_CONTAINER_ASSIGNED: WIN32_ERROR = 1504u32; +pub const ERROR_CONTENT_BLOCKED: WIN32_ERROR = 1296u32; +pub const ERROR_CONTEXT_EXPIRED: WIN32_ERROR = 1931u32; +pub const ERROR_CONTINUE: WIN32_ERROR = 1246u32; +pub const ERROR_CONTROL_C_EXIT: WIN32_ERROR = 572u32; +pub const ERROR_CONTROL_ID_NOT_FOUND: WIN32_ERROR = 1421u32; +pub const ERROR_CONVERT_TO_LARGE: WIN32_ERROR = 600u32; +pub const ERROR_CORRUPT_LOG_CLEARED: WIN32_ERROR = 798u32; +pub const ERROR_CORRUPT_LOG_CORRUPTED: WIN32_ERROR = 795u32; +pub const ERROR_CORRUPT_LOG_DELETED_FULL: WIN32_ERROR = 797u32; +pub const ERROR_CORRUPT_LOG_OVERFULL: WIN32_ERROR = 794u32; +pub const ERROR_CORRUPT_LOG_UNAVAILABLE: WIN32_ERROR = 796u32; +pub const ERROR_CORRUPT_SYSTEM_FILE: WIN32_ERROR = 634u32; +pub const ERROR_COULD_NOT_INTERPRET: WIN32_ERROR = 552u32; +pub const ERROR_COUNTER_TIMEOUT: WIN32_ERROR = 1121u32; +pub const ERROR_CPU_SET_INVALID: WIN32_ERROR = 813u32; +pub const ERROR_CRASH_DUMP: WIN32_ERROR = 753u32; +pub const ERROR_CRC: WIN32_ERROR = 23u32; +pub const ERROR_CREATE_FAILED: WIN32_ERROR = 1631u32; +pub const ERROR_CROSS_PARTITION_VIOLATION: WIN32_ERROR = 1661u32; +pub const ERROR_CSCSHARE_OFFLINE: WIN32_ERROR = 1262u32; +pub const ERROR_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE: WIN32_ERROR = 6019u32; +pub const ERROR_CS_ENCRYPTION_FILE_NOT_CSE: WIN32_ERROR = 6021u32; +pub const ERROR_CS_ENCRYPTION_INVALID_SERVER_RESPONSE: WIN32_ERROR = 6017u32; +pub const ERROR_CS_ENCRYPTION_NEW_ENCRYPTED_FILE: WIN32_ERROR = 6020u32; +pub const ERROR_CS_ENCRYPTION_UNSUPPORTED_SERVER: WIN32_ERROR = 6018u32; +pub const ERROR_CTX_CLIENT_QUERY_TIMEOUT: WIN32_ERROR = 7040u32; +pub const ERROR_CTX_MODEM_RESPONSE_TIMEOUT: WIN32_ERROR = 7012u32; +pub const ERROR_CURRENT_DIRECTORY: WIN32_ERROR = 16u32; +pub const ERROR_CURRENT_DOMAIN_NOT_ALLOWED: WIN32_ERROR = 1399u32; +pub const ERROR_DATABASE_DOES_NOT_EXIST: WIN32_ERROR = 1065u32; +pub const ERROR_DATATYPE_MISMATCH: WIN32_ERROR = 1629u32; +pub const ERROR_DATA_CHECKSUM_ERROR: WIN32_ERROR = 323u32; +pub const ERROR_DATA_NOT_ACCEPTED: WIN32_ERROR = 592u32; +pub const ERROR_DAX_MAPPING_EXISTS: WIN32_ERROR = 361u32; +pub const ERROR_DBG_COMMAND_EXCEPTION: WIN32_ERROR = 697u32; +pub const ERROR_DBG_CONTINUE: WIN32_ERROR = 767u32; +pub const ERROR_DBG_CONTROL_BREAK: WIN32_ERROR = 696u32; +pub const ERROR_DBG_CONTROL_C: WIN32_ERROR = 693u32; +pub const ERROR_DBG_EXCEPTION_HANDLED: WIN32_ERROR = 766u32; +pub const ERROR_DBG_EXCEPTION_NOT_HANDLED: WIN32_ERROR = 688u32; +pub const ERROR_DBG_PRINTEXCEPTION_C: WIN32_ERROR = 694u32; +pub const ERROR_DBG_REPLY_LATER: WIN32_ERROR = 689u32; +pub const ERROR_DBG_RIPEXCEPTION: WIN32_ERROR = 695u32; +pub const ERROR_DBG_TERMINATE_PROCESS: WIN32_ERROR = 692u32; +pub const ERROR_DBG_TERMINATE_THREAD: WIN32_ERROR = 691u32; +pub const ERROR_DBG_UNABLE_TO_PROVIDE_HANDLE: WIN32_ERROR = 690u32; +pub const ERROR_DC_NOT_FOUND: WIN32_ERROR = 1425u32; +pub const ERROR_DDE_FAIL: WIN32_ERROR = 1156u32; +pub const ERROR_DEBUGGER_INACTIVE: WIN32_ERROR = 1284u32; +pub const ERROR_DEBUG_ATTACH_FAILED: WIN32_ERROR = 590u32; +pub const ERROR_DECRYPTION_FAILED: WIN32_ERROR = 6001u32; +pub const ERROR_DELAY_LOAD_FAILED: WIN32_ERROR = 1285u32; +pub const ERROR_DELETE_PENDING: WIN32_ERROR = 303u32; +pub const ERROR_DEPENDENT_SERVICES_RUNNING: WIN32_ERROR = 1051u32; +pub const ERROR_DESTINATION_ELEMENT_FULL: WIN32_ERROR = 1161u32; +pub const ERROR_DESTROY_OBJECT_OF_OTHER_THREAD: WIN32_ERROR = 1435u32; +pub const ERROR_DEVICE_ALREADY_ATTACHED: WIN32_ERROR = 548u32; +pub const ERROR_DEVICE_ALREADY_REMEMBERED: WIN32_ERROR = 1202u32; +pub const ERROR_DEVICE_DOOR_OPEN: WIN32_ERROR = 1166u32; +pub const ERROR_DEVICE_ENUMERATION_ERROR: WIN32_ERROR = 648u32; +pub const ERROR_DEVICE_FEATURE_NOT_SUPPORTED: WIN32_ERROR = 316u32; +pub const ERROR_DEVICE_HARDWARE_ERROR: WIN32_ERROR = 483u32; +pub const ERROR_DEVICE_HINT_NAME_BUFFER_TOO_SMALL: WIN32_ERROR = 355u32; +pub const ERROR_DEVICE_IN_MAINTENANCE: WIN32_ERROR = 359u32; +pub const ERROR_DEVICE_IN_USE: WIN32_ERROR = 2404u32; +pub const ERROR_DEVICE_NOT_CONNECTED: WIN32_ERROR = 1167u32; +pub const ERROR_DEVICE_NOT_PARTITIONED: WIN32_ERROR = 1107u32; +pub const ERROR_DEVICE_NO_RESOURCES: WIN32_ERROR = 322u32; +pub const ERROR_DEVICE_REINITIALIZATION_NEEDED: WIN32_ERROR = 1164u32; +pub const ERROR_DEVICE_REMOVED: WIN32_ERROR = 1617u32; +pub const ERROR_DEVICE_REQUIRES_CLEANING: WIN32_ERROR = 1165u32; +pub const ERROR_DEVICE_RESET_REQUIRED: WIN32_ERROR = 507u32; +pub const ERROR_DEVICE_SUPPORT_IN_PROGRESS: WIN32_ERROR = 171u32; +pub const ERROR_DEVICE_UNREACHABLE: WIN32_ERROR = 321u32; +pub const ERROR_DEV_NOT_EXIST: WIN32_ERROR = 55u32; +pub const ERROR_DHCP_ADDRESS_CONFLICT: WIN32_ERROR = 4100u32; +pub const ERROR_DIFFERENT_SERVICE_ACCOUNT: WIN32_ERROR = 1079u32; +pub const ERROR_DIRECTORY: WIN32_ERROR = 267u32; +pub const ERROR_DIRECTORY_NOT_SUPPORTED: WIN32_ERROR = 336u32; +pub const ERROR_DIRECT_ACCESS_HANDLE: WIN32_ERROR = 130u32; +pub const ERROR_DIR_EFS_DISALLOWED: WIN32_ERROR = 6010u32; +pub const ERROR_DIR_NOT_EMPTY: WIN32_ERROR = 145u32; +pub const ERROR_DIR_NOT_ROOT: WIN32_ERROR = 144u32; +pub const ERROR_DISCARDED: WIN32_ERROR = 157u32; +pub const ERROR_DISK_CHANGE: WIN32_ERROR = 107u32; +pub const ERROR_DISK_CORRUPT: WIN32_ERROR = 1393u32; +pub const ERROR_DISK_FULL: WIN32_ERROR = 112u32; +pub const ERROR_DISK_OPERATION_FAILED: WIN32_ERROR = 1127u32; +pub const ERROR_DISK_QUOTA_EXCEEDED: WIN32_ERROR = 1295u32; +pub const ERROR_DISK_RECALIBRATE_FAILED: WIN32_ERROR = 1126u32; +pub const ERROR_DISK_REPAIR_DISABLED: WIN32_ERROR = 780u32; +pub const ERROR_DISK_REPAIR_REDIRECTED: WIN32_ERROR = 792u32; +pub const ERROR_DISK_REPAIR_UNSUCCESSFUL: WIN32_ERROR = 793u32; +pub const ERROR_DISK_RESET_FAILED: WIN32_ERROR = 1128u32; +pub const ERROR_DISK_RESOURCES_EXHAUSTED: WIN32_ERROR = 314u32; +pub const ERROR_DISK_TOO_FRAGMENTED: WIN32_ERROR = 302u32; +pub const ERROR_DLL_INIT_FAILED: WIN32_ERROR = 1114u32; +pub const ERROR_DLL_INIT_FAILED_LOGOFF: WIN32_ERROR = 624u32; +pub const ERROR_DLL_MIGHT_BE_INCOMPATIBLE: WIN32_ERROR = 687u32; +pub const ERROR_DLL_MIGHT_BE_INSECURE: WIN32_ERROR = 686u32; +pub const ERROR_DLL_NOT_FOUND: WIN32_ERROR = 1157u32; +pub const ERROR_DLP_POLICY_DENIES_OPERATION: WIN32_ERROR = 446u32; +pub const ERROR_DLP_POLICY_SILENTLY_FAIL: WIN32_ERROR = 449u32; +pub const ERROR_DLP_POLICY_WARNS_AGAINST_OPERATION: WIN32_ERROR = 445u32; +pub const ERROR_DOMAIN_CONTROLLER_EXISTS: WIN32_ERROR = 1250u32; +pub const ERROR_DOMAIN_CONTROLLER_NOT_FOUND: WIN32_ERROR = 1908u32; +pub const ERROR_DOMAIN_CTRLR_CONFIG_ERROR: WIN32_ERROR = 581u32; +pub const ERROR_DOMAIN_EXISTS: WIN32_ERROR = 1356u32; +pub const ERROR_DOMAIN_LIMIT_EXCEEDED: WIN32_ERROR = 1357u32; +pub const ERROR_DOMAIN_SID_SAME_AS_LOCAL_WORKSTATION: WIN32_ERROR = 8644u32; +pub const ERROR_DOMAIN_TRUST_INCONSISTENT: WIN32_ERROR = 1810u32; +pub const ERROR_DOWNGRADE_DETECTED: WIN32_ERROR = 1265u32; +pub const ERROR_DPL_NOT_SUPPORTED_FOR_USER: WIN32_ERROR = 423u32; +pub const ERROR_DRIVERS_LEAKING_LOCKED_PAGES: WIN32_ERROR = 729u32; +pub const ERROR_DRIVER_BLOCKED: WIN32_ERROR = 1275u32; +pub const ERROR_DRIVER_CANCEL_TIMEOUT: WIN32_ERROR = 594u32; +pub const ERROR_DRIVER_DATABASE_ERROR: WIN32_ERROR = 652u32; +pub const ERROR_DRIVER_FAILED_PRIOR_UNLOAD: WIN32_ERROR = 654u32; +pub const ERROR_DRIVER_FAILED_SLEEP: WIN32_ERROR = 633u32; +pub const ERROR_DRIVER_PROCESS_TERMINATED: WIN32_ERROR = 1291u32; +pub const ERROR_DRIVE_LOCKED: WIN32_ERROR = 108u32; +pub const ERROR_DS_ADD_REPLICA_INHIBITED: WIN32_ERROR = 8302u32; +pub const ERROR_DS_ADMIN_LIMIT_EXCEEDED: WIN32_ERROR = 8228u32; +pub const ERROR_DS_AFFECTS_MULTIPLE_DSAS: WIN32_ERROR = 8249u32; +pub const ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER: WIN32_ERROR = 8578u32; +pub const ERROR_DS_ALIASED_OBJ_MISSING: WIN32_ERROR = 8334u32; +pub const ERROR_DS_ALIAS_DEREF_PROBLEM: WIN32_ERROR = 8244u32; +pub const ERROR_DS_ALIAS_POINTS_TO_ALIAS: WIN32_ERROR = 8336u32; +pub const ERROR_DS_ALIAS_PROBLEM: WIN32_ERROR = 8241u32; +pub const ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS: WIN32_ERROR = 8205u32; +pub const ERROR_DS_ATTRIBUTE_OWNED_BY_SAM: WIN32_ERROR = 8346u32; +pub const ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED: WIN32_ERROR = 8204u32; +pub const ERROR_DS_ATT_ALREADY_EXISTS: WIN32_ERROR = 8318u32; +pub const ERROR_DS_ATT_IS_NOT_ON_OBJ: WIN32_ERROR = 8310u32; +pub const ERROR_DS_ATT_NOT_DEF_FOR_CLASS: WIN32_ERROR = 8317u32; +pub const ERROR_DS_ATT_NOT_DEF_IN_SCHEMA: WIN32_ERROR = 8303u32; +pub const ERROR_DS_ATT_SCHEMA_REQ_ID: WIN32_ERROR = 8399u32; +pub const ERROR_DS_ATT_SCHEMA_REQ_SYNTAX: WIN32_ERROR = 8416u32; +pub const ERROR_DS_ATT_VAL_ALREADY_EXISTS: WIN32_ERROR = 8323u32; +pub const ERROR_DS_AUDIT_FAILURE: WIN32_ERROR = 8625u32; +pub const ERROR_DS_AUTHORIZATION_FAILED: WIN32_ERROR = 8599u32; +pub const ERROR_DS_AUTH_METHOD_NOT_SUPPORTED: WIN32_ERROR = 8231u32; +pub const ERROR_DS_AUTH_UNKNOWN: WIN32_ERROR = 8234u32; +pub const ERROR_DS_AUX_CLS_TEST_FAIL: WIN32_ERROR = 8389u32; +pub const ERROR_DS_BACKLINK_WITHOUT_LINK: WIN32_ERROR = 8482u32; +pub const ERROR_DS_BAD_ATT_SCHEMA_SYNTAX: WIN32_ERROR = 8400u32; +pub const ERROR_DS_BAD_HIERARCHY_FILE: WIN32_ERROR = 8425u32; +pub const ERROR_DS_BAD_INSTANCE_TYPE: WIN32_ERROR = 8313u32; +pub const ERROR_DS_BAD_NAME_SYNTAX: WIN32_ERROR = 8335u32; +pub const ERROR_DS_BAD_RDN_ATT_ID_SYNTAX: WIN32_ERROR = 8392u32; +pub const ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED: WIN32_ERROR = 8426u32; +pub const ERROR_DS_BUSY: WIN32_ERROR = 8206u32; +pub const ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD: WIN32_ERROR = 8585u32; +pub const ERROR_DS_CANT_ADD_ATT_VALUES: WIN32_ERROR = 8320u32; +pub const ERROR_DS_CANT_ADD_SYSTEM_ONLY: WIN32_ERROR = 8358u32; +pub const ERROR_DS_CANT_ADD_TO_GC: WIN32_ERROR = 8550u32; +pub const ERROR_DS_CANT_CACHE_ATT: WIN32_ERROR = 8401u32; +pub const ERROR_DS_CANT_CACHE_CLASS: WIN32_ERROR = 8402u32; +pub const ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC: WIN32_ERROR = 8553u32; +pub const ERROR_DS_CANT_CREATE_UNDER_SCHEMA: WIN32_ERROR = 8510u32; +pub const ERROR_DS_CANT_DELETE: WIN32_ERROR = 8398u32; +pub const ERROR_DS_CANT_DELETE_DSA_OBJ: WIN32_ERROR = 8340u32; +pub const ERROR_DS_CANT_DEL_MASTER_CROSSREF: WIN32_ERROR = 8375u32; +pub const ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC: WIN32_ERROR = 8604u32; +pub const ERROR_DS_CANT_DEREF_ALIAS: WIN32_ERROR = 8337u32; +pub const ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN: WIN32_ERROR = 8603u32; +pub const ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF: WIN32_ERROR = 8589u32; +pub const ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN: WIN32_ERROR = 8537u32; +pub const ERROR_DS_CANT_FIND_DSA_OBJ: WIN32_ERROR = 8419u32; +pub const ERROR_DS_CANT_FIND_EXPECTED_NC: WIN32_ERROR = 8420u32; +pub const ERROR_DS_CANT_FIND_NC_IN_CACHE: WIN32_ERROR = 8421u32; +pub const ERROR_DS_CANT_MIX_MASTER_AND_REPS: WIN32_ERROR = 8331u32; +pub const ERROR_DS_CANT_MOD_OBJ_CLASS: WIN32_ERROR = 8215u32; +pub const ERROR_DS_CANT_MOD_PRIMARYGROUPID: WIN32_ERROR = 8506u32; +pub const ERROR_DS_CANT_MOD_SYSTEM_ONLY: WIN32_ERROR = 8369u32; +pub const ERROR_DS_CANT_MOVE_ACCOUNT_GROUP: WIN32_ERROR = 8498u32; +pub const ERROR_DS_CANT_MOVE_APP_BASIC_GROUP: WIN32_ERROR = 8608u32; +pub const ERROR_DS_CANT_MOVE_APP_QUERY_GROUP: WIN32_ERROR = 8609u32; +pub const ERROR_DS_CANT_MOVE_DELETED_OBJECT: WIN32_ERROR = 8489u32; +pub const ERROR_DS_CANT_MOVE_RESOURCE_GROUP: WIN32_ERROR = 8499u32; +pub const ERROR_DS_CANT_ON_NON_LEAF: WIN32_ERROR = 8213u32; +pub const ERROR_DS_CANT_ON_RDN: WIN32_ERROR = 8214u32; +pub const ERROR_DS_CANT_REMOVE_ATT_CACHE: WIN32_ERROR = 8403u32; +pub const ERROR_DS_CANT_REMOVE_CLASS_CACHE: WIN32_ERROR = 8404u32; +pub const ERROR_DS_CANT_REM_MISSING_ATT: WIN32_ERROR = 8324u32; +pub const ERROR_DS_CANT_REM_MISSING_ATT_VAL: WIN32_ERROR = 8325u32; +pub const ERROR_DS_CANT_REPLACE_HIDDEN_REC: WIN32_ERROR = 8424u32; +pub const ERROR_DS_CANT_RETRIEVE_ATTS: WIN32_ERROR = 8481u32; +pub const ERROR_DS_CANT_RETRIEVE_CHILD: WIN32_ERROR = 8422u32; +pub const ERROR_DS_CANT_RETRIEVE_DN: WIN32_ERROR = 8405u32; +pub const ERROR_DS_CANT_RETRIEVE_INSTANCE: WIN32_ERROR = 8407u32; +pub const ERROR_DS_CANT_RETRIEVE_SD: WIN32_ERROR = 8526u32; +pub const ERROR_DS_CANT_START: WIN32_ERROR = 8531u32; +pub const ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ: WIN32_ERROR = 8560u32; +pub const ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS: WIN32_ERROR = 8493u32; +pub const ERROR_DS_CHILDREN_EXIST: WIN32_ERROR = 8332u32; +pub const ERROR_DS_CLASS_MUST_BE_CONCRETE: WIN32_ERROR = 8359u32; +pub const ERROR_DS_CLASS_NOT_DSA: WIN32_ERROR = 8343u32; +pub const ERROR_DS_CLIENT_LOOP: WIN32_ERROR = 8259u32; +pub const ERROR_DS_CODE_INCONSISTENCY: WIN32_ERROR = 8408u32; +pub const ERROR_DS_COMPARE_FALSE: WIN32_ERROR = 8229u32; +pub const ERROR_DS_COMPARE_TRUE: WIN32_ERROR = 8230u32; +pub const ERROR_DS_CONFIDENTIALITY_REQUIRED: WIN32_ERROR = 8237u32; +pub const ERROR_DS_CONFIG_PARAM_MISSING: WIN32_ERROR = 8427u32; +pub const ERROR_DS_CONSTRAINT_VIOLATION: WIN32_ERROR = 8239u32; +pub const ERROR_DS_CONSTRUCTED_ATT_MOD: WIN32_ERROR = 8475u32; +pub const ERROR_DS_CONTROL_NOT_FOUND: WIN32_ERROR = 8258u32; +pub const ERROR_DS_COULDNT_CONTACT_FSMO: WIN32_ERROR = 8367u32; +pub const ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE: WIN32_ERROR = 8503u32; +pub const ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE: WIN32_ERROR = 8502u32; +pub const ERROR_DS_COULDNT_UPDATE_SPNS: WIN32_ERROR = 8525u32; +pub const ERROR_DS_COUNTING_AB_INDICES_FAILED: WIN32_ERROR = 8428u32; +pub const ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD: WIN32_ERROR = 8491u32; +pub const ERROR_DS_CROSS_DOM_MOVE_ERROR: WIN32_ERROR = 8216u32; +pub const ERROR_DS_CROSS_NC_DN_RENAME: WIN32_ERROR = 8368u32; +pub const ERROR_DS_CROSS_REF_BUSY: WIN32_ERROR = 8602u32; +pub const ERROR_DS_CROSS_REF_EXISTS: WIN32_ERROR = 8374u32; +pub const ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE: WIN32_ERROR = 8495u32; +pub const ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2: WIN32_ERROR = 8586u32; +pub const ERROR_DS_DATABASE_ERROR: WIN32_ERROR = 8409u32; +pub const ERROR_DS_DECODING_ERROR: WIN32_ERROR = 8253u32; +pub const ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED: WIN32_ERROR = 8536u32; +pub const ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST: WIN32_ERROR = 8535u32; +pub const ERROR_DS_DIFFERENT_REPL_EPOCHS: WIN32_ERROR = 8593u32; +pub const ERROR_DS_DISALLOWED_IN_SYSTEM_CONTAINER: WIN32_ERROR = 8615u32; +pub const ERROR_DS_DISALLOWED_NC_REDIRECT: WIN32_ERROR = 8640u32; +pub const ERROR_DS_DNS_LOOKUP_FAILURE: WIN32_ERROR = 8524u32; +pub const ERROR_DS_DOMAIN_NAME_EXISTS_IN_FOREST: WIN32_ERROR = 8634u32; +pub const ERROR_DS_DOMAIN_RENAME_IN_PROGRESS: WIN32_ERROR = 8612u32; +pub const ERROR_DS_DOMAIN_VERSION_TOO_HIGH: WIN32_ERROR = 8564u32; +pub const ERROR_DS_DOMAIN_VERSION_TOO_LOW: WIN32_ERROR = 8566u32; +pub const ERROR_DS_DRA_ABANDON_SYNC: WIN32_ERROR = 8462u32; +pub const ERROR_DS_DRA_ACCESS_DENIED: WIN32_ERROR = 8453u32; +pub const ERROR_DS_DRA_BAD_DN: WIN32_ERROR = 8439u32; +pub const ERROR_DS_DRA_BAD_INSTANCE_TYPE: WIN32_ERROR = 8445u32; +pub const ERROR_DS_DRA_BAD_NC: WIN32_ERROR = 8440u32; +pub const ERROR_DS_DRA_BUSY: WIN32_ERROR = 8438u32; +pub const ERROR_DS_DRA_CONNECTION_FAILED: WIN32_ERROR = 8444u32; +pub const ERROR_DS_DRA_CORRUPT_UTD_VECTOR: WIN32_ERROR = 8629u32; +pub const ERROR_DS_DRA_DB_ERROR: WIN32_ERROR = 8451u32; +pub const ERROR_DS_DRA_DN_EXISTS: WIN32_ERROR = 8441u32; +pub const ERROR_DS_DRA_EARLIER_SCHEMA_CONFLICT: WIN32_ERROR = 8544u32; +pub const ERROR_DS_DRA_EXTN_CONNECTION_FAILED: WIN32_ERROR = 8466u32; +pub const ERROR_DS_DRA_GENERIC: WIN32_ERROR = 8436u32; +pub const ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET: WIN32_ERROR = 8464u32; +pub const ERROR_DS_DRA_INCONSISTENT_DIT: WIN32_ERROR = 8443u32; +pub const ERROR_DS_DRA_INTERNAL_ERROR: WIN32_ERROR = 8442u32; +pub const ERROR_DS_DRA_INVALID_PARAMETER: WIN32_ERROR = 8437u32; +pub const ERROR_DS_DRA_MAIL_PROBLEM: WIN32_ERROR = 8447u32; +pub const ERROR_DS_DRA_MISSING_KRBTGT_SECRET: WIN32_ERROR = 8633u32; +pub const ERROR_DS_DRA_MISSING_PARENT: WIN32_ERROR = 8460u32; +pub const ERROR_DS_DRA_NAME_COLLISION: WIN32_ERROR = 8458u32; +pub const ERROR_DS_DRA_NOT_SUPPORTED: WIN32_ERROR = 8454u32; +pub const ERROR_DS_DRA_NO_REPLICA: WIN32_ERROR = 8452u32; +pub const ERROR_DS_DRA_OBJ_IS_REP_SOURCE: WIN32_ERROR = 8450u32; +pub const ERROR_DS_DRA_OBJ_NC_MISMATCH: WIN32_ERROR = 8545u32; +pub const ERROR_DS_DRA_OUT_OF_MEM: WIN32_ERROR = 8446u32; +pub const ERROR_DS_DRA_OUT_SCHEDULE_WINDOW: WIN32_ERROR = 8617u32; +pub const ERROR_DS_DRA_PREEMPTED: WIN32_ERROR = 8461u32; +pub const ERROR_DS_DRA_RECYCLED_TARGET: WIN32_ERROR = 8639u32; +pub const ERROR_DS_DRA_REF_ALREADY_EXISTS: WIN32_ERROR = 8448u32; +pub const ERROR_DS_DRA_REF_NOT_FOUND: WIN32_ERROR = 8449u32; +pub const ERROR_DS_DRA_REPL_PENDING: WIN32_ERROR = 8477u32; +pub const ERROR_DS_DRA_RPC_CANCELLED: WIN32_ERROR = 8455u32; +pub const ERROR_DS_DRA_SCHEMA_CONFLICT: WIN32_ERROR = 8543u32; +pub const ERROR_DS_DRA_SCHEMA_INFO_SHIP: WIN32_ERROR = 8542u32; +pub const ERROR_DS_DRA_SCHEMA_MISMATCH: WIN32_ERROR = 8418u32; +pub const ERROR_DS_DRA_SECRETS_DENIED: WIN32_ERROR = 8630u32; +pub const ERROR_DS_DRA_SHUTDOWN: WIN32_ERROR = 8463u32; +pub const ERROR_DS_DRA_SINK_DISABLED: WIN32_ERROR = 8457u32; +pub const ERROR_DS_DRA_SOURCE_DISABLED: WIN32_ERROR = 8456u32; +pub const ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA: WIN32_ERROR = 8465u32; +pub const ERROR_DS_DRA_SOURCE_REINSTALLED: WIN32_ERROR = 8459u32; +pub const ERROR_DS_DRS_EXTENSIONS_CHANGED: WIN32_ERROR = 8594u32; +pub const ERROR_DS_DSA_MUST_BE_INT_MASTER: WIN32_ERROR = 8342u32; +pub const ERROR_DS_DST_DOMAIN_NOT_NATIVE: WIN32_ERROR = 8496u32; +pub const ERROR_DS_DST_NC_MISMATCH: WIN32_ERROR = 8486u32; +pub const ERROR_DS_DS_REQUIRED: WIN32_ERROR = 8478u32; +pub const ERROR_DS_DUPLICATE_ID_FOUND: WIN32_ERROR = 8605u32; +pub const ERROR_DS_DUP_LDAP_DISPLAY_NAME: WIN32_ERROR = 8382u32; +pub const ERROR_DS_DUP_LINK_ID: WIN32_ERROR = 8468u32; +pub const ERROR_DS_DUP_MAPI_ID: WIN32_ERROR = 8380u32; +pub const ERROR_DS_DUP_MSDS_INTID: WIN32_ERROR = 8597u32; +pub const ERROR_DS_DUP_OID: WIN32_ERROR = 8379u32; +pub const ERROR_DS_DUP_RDN: WIN32_ERROR = 8378u32; +pub const ERROR_DS_DUP_SCHEMA_ID_GUID: WIN32_ERROR = 8381u32; +pub const ERROR_DS_ENCODING_ERROR: WIN32_ERROR = 8252u32; +pub const ERROR_DS_EPOCH_MISMATCH: WIN32_ERROR = 8483u32; +pub const ERROR_DS_EXISTING_AD_CHILD_NC: WIN32_ERROR = 8613u32; +pub const ERROR_DS_EXISTS_IN_AUX_CLS: WIN32_ERROR = 8393u32; +pub const ERROR_DS_EXISTS_IN_MAY_HAVE: WIN32_ERROR = 8386u32; +pub const ERROR_DS_EXISTS_IN_MUST_HAVE: WIN32_ERROR = 8385u32; +pub const ERROR_DS_EXISTS_IN_POSS_SUP: WIN32_ERROR = 8395u32; +pub const ERROR_DS_EXISTS_IN_RDNATTID: WIN32_ERROR = 8598u32; +pub const ERROR_DS_EXISTS_IN_SUB_CLS: WIN32_ERROR = 8394u32; +pub const ERROR_DS_FILTER_UNKNOWN: WIN32_ERROR = 8254u32; +pub const ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS: WIN32_ERROR = 8555u32; +pub const ERROR_DS_FLAT_NAME_EXISTS_IN_FOREST: WIN32_ERROR = 8635u32; +pub const ERROR_DS_FOREST_VERSION_TOO_HIGH: WIN32_ERROR = 8563u32; +pub const ERROR_DS_FOREST_VERSION_TOO_LOW: WIN32_ERROR = 8565u32; +pub const ERROR_DS_GCVERIFY_ERROR: WIN32_ERROR = 8417u32; +pub const ERROR_DS_GC_NOT_AVAILABLE: WIN32_ERROR = 8217u32; +pub const ERROR_DS_GC_REQUIRED: WIN32_ERROR = 8547u32; +pub const ERROR_DS_GENERIC_ERROR: WIN32_ERROR = 8341u32; +pub const ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER: WIN32_ERROR = 8519u32; +pub const ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER: WIN32_ERROR = 8516u32; +pub const ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER: WIN32_ERROR = 8517u32; +pub const ERROR_DS_GOVERNSID_MISSING: WIN32_ERROR = 8410u32; +pub const ERROR_DS_GROUP_CONVERSION_ERROR: WIN32_ERROR = 8607u32; +pub const ERROR_DS_HAVE_PRIMARY_MEMBERS: WIN32_ERROR = 8521u32; +pub const ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED: WIN32_ERROR = 8429u32; +pub const ERROR_DS_HIERARCHY_TABLE_TOO_DEEP: WIN32_ERROR = 8628u32; +pub const ERROR_DS_HIGH_ADLDS_FFL: WIN32_ERROR = 8641u32; +pub const ERROR_DS_HIGH_DSA_VERSION: WIN32_ERROR = 8642u32; +pub const ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD: WIN32_ERROR = 8507u32; +pub const ERROR_DS_ILLEGAL_MOD_OPERATION: WIN32_ERROR = 8311u32; +pub const ERROR_DS_ILLEGAL_SUPERIOR: WIN32_ERROR = 8345u32; +pub const ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION: WIN32_ERROR = 8492u32; +pub const ERROR_DS_INAPPROPRIATE_AUTH: WIN32_ERROR = 8233u32; +pub const ERROR_DS_INAPPROPRIATE_MATCHING: WIN32_ERROR = 8238u32; +pub const ERROR_DS_INCOMPATIBLE_CONTROLS_USED: WIN32_ERROR = 8574u32; +pub const ERROR_DS_INCOMPATIBLE_VERSION: WIN32_ERROR = 8567u32; +pub const ERROR_DS_INCORRECT_ROLE_OWNER: WIN32_ERROR = 8210u32; +pub const ERROR_DS_INIT_FAILURE: WIN32_ERROR = 8532u32; +pub const ERROR_DS_INIT_FAILURE_CONSOLE: WIN32_ERROR = 8561u32; +pub const ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE: WIN32_ERROR = 8512u32; +pub const ERROR_DS_INSTALL_NO_SRC_SCH_VERSION: WIN32_ERROR = 8511u32; +pub const ERROR_DS_INSTALL_SCHEMA_MISMATCH: WIN32_ERROR = 8467u32; +pub const ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT: WIN32_ERROR = 8606u32; +pub const ERROR_DS_INSUFF_ACCESS_RIGHTS: WIN32_ERROR = 8344u32; +pub const ERROR_DS_INTERNAL_FAILURE: WIN32_ERROR = 8430u32; +pub const ERROR_DS_INVALID_ATTRIBUTE_SYNTAX: WIN32_ERROR = 8203u32; +pub const ERROR_DS_INVALID_DMD: WIN32_ERROR = 8360u32; +pub const ERROR_DS_INVALID_DN_SYNTAX: WIN32_ERROR = 8242u32; +pub const ERROR_DS_INVALID_GROUP_TYPE: WIN32_ERROR = 8513u32; +pub const ERROR_DS_INVALID_LDAP_DISPLAY_NAME: WIN32_ERROR = 8479u32; +pub const ERROR_DS_INVALID_NAME_FOR_SPN: WIN32_ERROR = 8554u32; +pub const ERROR_DS_INVALID_ROLE_OWNER: WIN32_ERROR = 8366u32; +pub const ERROR_DS_INVALID_SCRIPT: WIN32_ERROR = 8600u32; +pub const ERROR_DS_INVALID_SEARCH_FLAG: WIN32_ERROR = 8500u32; +pub const ERROR_DS_INVALID_SEARCH_FLAG_SUBTREE: WIN32_ERROR = 8626u32; +pub const ERROR_DS_INVALID_SEARCH_FLAG_TUPLE: WIN32_ERROR = 8627u32; +pub const ERROR_DS_IS_LEAF: WIN32_ERROR = 8243u32; +pub const ERROR_DS_KEY_NOT_UNIQUE: WIN32_ERROR = 8527u32; +pub const ERROR_DS_LDAP_SEND_QUEUE_FULL: WIN32_ERROR = 8616u32; +pub const ERROR_DS_LINK_ID_NOT_AVAILABLE: WIN32_ERROR = 8577u32; +pub const ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER: WIN32_ERROR = 8520u32; +pub const ERROR_DS_LOCAL_ERROR: WIN32_ERROR = 8251u32; +pub const ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY: WIN32_ERROR = 8548u32; +pub const ERROR_DS_LOOP_DETECT: WIN32_ERROR = 8246u32; +pub const ERROR_DS_LOW_ADLDS_FFL: WIN32_ERROR = 8643u32; +pub const ERROR_DS_LOW_DSA_VERSION: WIN32_ERROR = 8568u32; +pub const ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4: WIN32_ERROR = 8572u32; +pub const ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED: WIN32_ERROR = 8557u32; +pub const ERROR_DS_MAPI_ID_NOT_AVAILABLE: WIN32_ERROR = 8632u32; +pub const ERROR_DS_MASTERDSA_REQUIRED: WIN32_ERROR = 8314u32; +pub const ERROR_DS_MAX_OBJ_SIZE_EXCEEDED: WIN32_ERROR = 8304u32; +pub const ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY: WIN32_ERROR = 8201u32; +pub const ERROR_DS_MISSING_EXPECTED_ATT: WIN32_ERROR = 8411u32; +pub const ERROR_DS_MISSING_FOREST_TRUST: WIN32_ERROR = 8649u32; +pub const ERROR_DS_MISSING_FSMO_SETTINGS: WIN32_ERROR = 8434u32; +pub const ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER: WIN32_ERROR = 8497u32; +pub const ERROR_DS_MISSING_REQUIRED_ATT: WIN32_ERROR = 8316u32; +pub const ERROR_DS_MISSING_SUPREF: WIN32_ERROR = 8406u32; +pub const ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG: WIN32_ERROR = 8581u32; +pub const ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE: WIN32_ERROR = 8579u32; +pub const ERROR_DS_MODIFYDN_WRONG_GRANDPARENT: WIN32_ERROR = 8582u32; +pub const ERROR_DS_MUST_BE_RUN_ON_DST_DC: WIN32_ERROR = 8558u32; +pub const ERROR_DS_NAME_ERROR_DOMAIN_ONLY: WIN32_ERROR = 8473u32; +pub const ERROR_DS_NAME_ERROR_NOT_FOUND: WIN32_ERROR = 8470u32; +pub const ERROR_DS_NAME_ERROR_NOT_UNIQUE: WIN32_ERROR = 8471u32; +pub const ERROR_DS_NAME_ERROR_NO_MAPPING: WIN32_ERROR = 8472u32; +pub const ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING: WIN32_ERROR = 8474u32; +pub const ERROR_DS_NAME_ERROR_RESOLVING: WIN32_ERROR = 8469u32; +pub const ERROR_DS_NAME_ERROR_TRUST_REFERRAL: WIN32_ERROR = 8583u32; +pub const ERROR_DS_NAME_NOT_UNIQUE: WIN32_ERROR = 8571u32; +pub const ERROR_DS_NAME_REFERENCE_INVALID: WIN32_ERROR = 8373u32; +pub const ERROR_DS_NAME_TOO_LONG: WIN32_ERROR = 8348u32; +pub const ERROR_DS_NAME_TOO_MANY_PARTS: WIN32_ERROR = 8347u32; +pub const ERROR_DS_NAME_TYPE_UNKNOWN: WIN32_ERROR = 8351u32; +pub const ERROR_DS_NAME_UNPARSEABLE: WIN32_ERROR = 8350u32; +pub const ERROR_DS_NAME_VALUE_TOO_LONG: WIN32_ERROR = 8349u32; +pub const ERROR_DS_NAMING_MASTER_GC: WIN32_ERROR = 8523u32; +pub const ERROR_DS_NAMING_VIOLATION: WIN32_ERROR = 8247u32; +pub const ERROR_DS_NCNAME_MISSING_CR_REF: WIN32_ERROR = 8412u32; +pub const ERROR_DS_NCNAME_MUST_BE_NC: WIN32_ERROR = 8357u32; +pub const ERROR_DS_NC_MUST_HAVE_NC_PARENT: WIN32_ERROR = 8494u32; +pub const ERROR_DS_NC_STILL_HAS_DSAS: WIN32_ERROR = 8546u32; +pub const ERROR_DS_NONEXISTENT_MAY_HAVE: WIN32_ERROR = 8387u32; +pub const ERROR_DS_NONEXISTENT_MUST_HAVE: WIN32_ERROR = 8388u32; +pub const ERROR_DS_NONEXISTENT_POSS_SUP: WIN32_ERROR = 8390u32; +pub const ERROR_DS_NONSAFE_SCHEMA_CHANGE: WIN32_ERROR = 8508u32; +pub const ERROR_DS_NON_ASQ_SEARCH: WIN32_ERROR = 8624u32; +pub const ERROR_DS_NON_BASE_SEARCH: WIN32_ERROR = 8480u32; +pub const ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX: WIN32_ERROR = 8377u32; +pub const ERROR_DS_NOT_AN_OBJECT: WIN32_ERROR = 8352u32; +pub const ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC: WIN32_ERROR = 8487u32; +pub const ERROR_DS_NOT_CLOSEST: WIN32_ERROR = 8588u32; +pub const ERROR_DS_NOT_INSTALLED: WIN32_ERROR = 8200u32; +pub const ERROR_DS_NOT_ON_BACKLINK: WIN32_ERROR = 8362u32; +pub const ERROR_DS_NOT_SUPPORTED: WIN32_ERROR = 8256u32; +pub const ERROR_DS_NOT_SUPPORTED_SORT_ORDER: WIN32_ERROR = 8570u32; +pub const ERROR_DS_NO_ATTRIBUTE_OR_VALUE: WIN32_ERROR = 8202u32; +pub const ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN: WIN32_ERROR = 8569u32; +pub const ERROR_DS_NO_CHAINED_EVAL: WIN32_ERROR = 8328u32; +pub const ERROR_DS_NO_CHAINING: WIN32_ERROR = 8327u32; +pub const ERROR_DS_NO_CHECKPOINT_WITH_PDC: WIN32_ERROR = 8551u32; +pub const ERROR_DS_NO_CROSSREF_FOR_NC: WIN32_ERROR = 8363u32; +pub const ERROR_DS_NO_DELETED_NAME: WIN32_ERROR = 8355u32; +pub const ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS: WIN32_ERROR = 8549u32; +pub const ERROR_DS_NO_MORE_RIDS: WIN32_ERROR = 8209u32; +pub const ERROR_DS_NO_MSDS_INTID: WIN32_ERROR = 8596u32; +pub const ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN: WIN32_ERROR = 8514u32; +pub const ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN: WIN32_ERROR = 8515u32; +pub const ERROR_DS_NO_NTDSA_OBJECT: WIN32_ERROR = 8623u32; +pub const ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC: WIN32_ERROR = 8580u32; +pub const ERROR_DS_NO_PARENT_OBJECT: WIN32_ERROR = 8329u32; +pub const ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION: WIN32_ERROR = 8533u32; +pub const ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA: WIN32_ERROR = 8306u32; +pub const ERROR_DS_NO_REF_DOMAIN: WIN32_ERROR = 8575u32; +pub const ERROR_DS_NO_REQUESTED_ATTS_FOUND: WIN32_ERROR = 8308u32; +pub const ERROR_DS_NO_RESULTS_RETURNED: WIN32_ERROR = 8257u32; +pub const ERROR_DS_NO_RIDS_ALLOCATED: WIN32_ERROR = 8208u32; +pub const ERROR_DS_NO_SERVER_OBJECT: WIN32_ERROR = 8622u32; +pub const ERROR_DS_NO_SUCH_OBJECT: WIN32_ERROR = 8240u32; +pub const ERROR_DS_NO_TREE_DELETE_ABOVE_NC: WIN32_ERROR = 8501u32; +pub const ERROR_DS_NTDSCRIPT_PROCESS_ERROR: WIN32_ERROR = 8592u32; +pub const ERROR_DS_NTDSCRIPT_SYNTAX_ERROR: WIN32_ERROR = 8591u32; +pub const ERROR_DS_OBJECT_BEING_REMOVED: WIN32_ERROR = 8339u32; +pub const ERROR_DS_OBJECT_CLASS_REQUIRED: WIN32_ERROR = 8315u32; +pub const ERROR_DS_OBJECT_RESULTS_TOO_LARGE: WIN32_ERROR = 8248u32; +pub const ERROR_DS_OBJ_CLASS_NOT_DEFINED: WIN32_ERROR = 8371u32; +pub const ERROR_DS_OBJ_CLASS_NOT_SUBCLASS: WIN32_ERROR = 8372u32; +pub const ERROR_DS_OBJ_CLASS_VIOLATION: WIN32_ERROR = 8212u32; +pub const ERROR_DS_OBJ_GUID_EXISTS: WIN32_ERROR = 8361u32; +pub const ERROR_DS_OBJ_NOT_FOUND: WIN32_ERROR = 8333u32; +pub const ERROR_DS_OBJ_STRING_NAME_EXISTS: WIN32_ERROR = 8305u32; +pub const ERROR_DS_OBJ_TOO_LARGE: WIN32_ERROR = 8312u32; +pub const ERROR_DS_OFFSET_RANGE_ERROR: WIN32_ERROR = 8262u32; +pub const ERROR_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS: WIN32_ERROR = 8637u32; +pub const ERROR_DS_OID_NOT_FOUND: WIN32_ERROR = 8638u32; +pub const ERROR_DS_OPERATIONS_ERROR: WIN32_ERROR = 8224u32; +pub const ERROR_DS_OUT_OF_SCOPE: WIN32_ERROR = 8338u32; +pub const ERROR_DS_OUT_OF_VERSION_STORE: WIN32_ERROR = 8573u32; +pub const ERROR_DS_PARAM_ERROR: WIN32_ERROR = 8255u32; +pub const ERROR_DS_PARENT_IS_AN_ALIAS: WIN32_ERROR = 8330u32; +pub const ERROR_DS_PDC_OPERATION_IN_PROGRESS: WIN32_ERROR = 8490u32; +pub const ERROR_DS_PER_ATTRIBUTE_AUTHZ_FAILED_DURING_ADD: WIN32_ERROR = 8652u32; +pub const ERROR_DS_POLICY_NOT_KNOWN: WIN32_ERROR = 8618u32; +pub const ERROR_DS_PROTOCOL_ERROR: WIN32_ERROR = 8225u32; +pub const ERROR_DS_RANGE_CONSTRAINT: WIN32_ERROR = 8322u32; +pub const ERROR_DS_RDN_DOESNT_MATCH_SCHEMA: WIN32_ERROR = 8307u32; +pub const ERROR_DS_RECALCSCHEMA_FAILED: WIN32_ERROR = 8396u32; +pub const ERROR_DS_REFERRAL: WIN32_ERROR = 8235u32; +pub const ERROR_DS_REFERRAL_LIMIT_EXCEEDED: WIN32_ERROR = 8260u32; +pub const ERROR_DS_REFUSING_FSMO_ROLES: WIN32_ERROR = 8433u32; +pub const ERROR_DS_REMOTE_CROSSREF_OP_FAILED: WIN32_ERROR = 8601u32; +pub const ERROR_DS_REPLICATOR_ONLY: WIN32_ERROR = 8370u32; +pub const ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR: WIN32_ERROR = 8595u32; +pub const ERROR_DS_REPL_LIFETIME_EXCEEDED: WIN32_ERROR = 8614u32; +pub const ERROR_DS_RESERVED_LINK_ID: WIN32_ERROR = 8576u32; +pub const ERROR_DS_RESERVED_MAPI_ID: WIN32_ERROR = 8631u32; +pub const ERROR_DS_RIDMGR_DISABLED: WIN32_ERROR = 8263u32; +pub const ERROR_DS_RIDMGR_INIT_ERROR: WIN32_ERROR = 8211u32; +pub const ERROR_DS_ROLE_NOT_VERIFIED: WIN32_ERROR = 8610u32; +pub const ERROR_DS_ROOT_CANT_BE_SUBREF: WIN32_ERROR = 8326u32; +pub const ERROR_DS_ROOT_MUST_BE_NC: WIN32_ERROR = 8301u32; +pub const ERROR_DS_ROOT_REQUIRES_CLASS_TOP: WIN32_ERROR = 8432u32; +pub const ERROR_DS_SAM_INIT_FAILURE: WIN32_ERROR = 8504u32; +pub const ERROR_DS_SAM_INIT_FAILURE_CONSOLE: WIN32_ERROR = 8562u32; +pub const ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY: WIN32_ERROR = 8530u32; +pub const ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD: WIN32_ERROR = 8529u32; +pub const ERROR_DS_SCHEMA_ALLOC_FAILED: WIN32_ERROR = 8415u32; +pub const ERROR_DS_SCHEMA_NOT_LOADED: WIN32_ERROR = 8414u32; +pub const ERROR_DS_SCHEMA_UPDATE_DISALLOWED: WIN32_ERROR = 8509u32; +pub const ERROR_DS_SECURITY_CHECKING_ERROR: WIN32_ERROR = 8413u32; +pub const ERROR_DS_SECURITY_ILLEGAL_MODIFY: WIN32_ERROR = 8423u32; +pub const ERROR_DS_SEC_DESC_INVALID: WIN32_ERROR = 8354u32; +pub const ERROR_DS_SEC_DESC_TOO_SHORT: WIN32_ERROR = 8353u32; +pub const ERROR_DS_SEMANTIC_ATT_TEST: WIN32_ERROR = 8383u32; +pub const ERROR_DS_SENSITIVE_GROUP_VIOLATION: WIN32_ERROR = 8505u32; +pub const ERROR_DS_SERVER_DOWN: WIN32_ERROR = 8250u32; +pub const ERROR_DS_SHUTTING_DOWN: WIN32_ERROR = 8364u32; +pub const ERROR_DS_SINGLE_USER_MODE_FAILED: WIN32_ERROR = 8590u32; +pub const ERROR_DS_SINGLE_VALUE_CONSTRAINT: WIN32_ERROR = 8321u32; +pub const ERROR_DS_SIZELIMIT_EXCEEDED: WIN32_ERROR = 8227u32; +pub const ERROR_DS_SORT_CONTROL_MISSING: WIN32_ERROR = 8261u32; +pub const ERROR_DS_SOURCE_AUDITING_NOT_ENABLED: WIN32_ERROR = 8552u32; +pub const ERROR_DS_SOURCE_DOMAIN_IN_FOREST: WIN32_ERROR = 8534u32; +pub const ERROR_DS_SPN_VALUE_NOT_UNIQUE_IN_FOREST: WIN32_ERROR = 8647u32; +pub const ERROR_DS_SRC_AND_DST_NC_IDENTICAL: WIN32_ERROR = 8485u32; +pub const ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH: WIN32_ERROR = 8540u32; +pub const ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER: WIN32_ERROR = 8559u32; +pub const ERROR_DS_SRC_GUID_MISMATCH: WIN32_ERROR = 8488u32; +pub const ERROR_DS_SRC_NAME_MISMATCH: WIN32_ERROR = 8484u32; +pub const ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER: WIN32_ERROR = 8538u32; +pub const ERROR_DS_SRC_SID_EXISTS_IN_FOREST: WIN32_ERROR = 8539u32; +pub const ERROR_DS_STRING_SD_CONVERSION_FAILED: WIN32_ERROR = 8522u32; +pub const ERROR_DS_STRONG_AUTH_REQUIRED: WIN32_ERROR = 8232u32; +pub const ERROR_DS_SUBREF_MUST_HAVE_PARENT: WIN32_ERROR = 8356u32; +pub const ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD: WIN32_ERROR = 8376u32; +pub const ERROR_DS_SUB_CLS_TEST_FAIL: WIN32_ERROR = 8391u32; +pub const ERROR_DS_SYNTAX_MISMATCH: WIN32_ERROR = 8384u32; +pub const ERROR_DS_THREAD_LIMIT_EXCEEDED: WIN32_ERROR = 8587u32; +pub const ERROR_DS_TIMELIMIT_EXCEEDED: WIN32_ERROR = 8226u32; +pub const ERROR_DS_TREE_DELETE_NOT_FINISHED: WIN32_ERROR = 8397u32; +pub const ERROR_DS_UNABLE_TO_SURRENDER_ROLES: WIN32_ERROR = 8435u32; +pub const ERROR_DS_UNAVAILABLE: WIN32_ERROR = 8207u32; +pub const ERROR_DS_UNAVAILABLE_CRIT_EXTENSION: WIN32_ERROR = 8236u32; +pub const ERROR_DS_UNDELETE_SAM_VALIDATION_FAILED: WIN32_ERROR = 8645u32; +pub const ERROR_DS_UNICODEPWD_NOT_IN_QUOTES: WIN32_ERROR = 8556u32; +pub const ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER: WIN32_ERROR = 8518u32; +pub const ERROR_DS_UNKNOWN_ERROR: WIN32_ERROR = 8431u32; +pub const ERROR_DS_UNKNOWN_OPERATION: WIN32_ERROR = 8365u32; +pub const ERROR_DS_UNWILLING_TO_PERFORM: WIN32_ERROR = 8245u32; +pub const ERROR_DS_UPN_VALUE_NOT_UNIQUE_IN_FOREST: WIN32_ERROR = 8648u32; +pub const ERROR_DS_USER_BUFFER_TO_SMALL: WIN32_ERROR = 8309u32; +pub const ERROR_DS_VALUE_KEY_NOT_UNIQUE: WIN32_ERROR = 8650u32; +pub const ERROR_DS_VERSION_CHECK_FAILURE: WIN32_ERROR = 643u32; +pub const ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL: WIN32_ERROR = 8611u32; +pub const ERROR_DS_WRONG_LINKED_ATT_SYNTAX: WIN32_ERROR = 8528u32; +pub const ERROR_DS_WRONG_OM_OBJ_CLASS: WIN32_ERROR = 8476u32; +pub const ERROR_DUPLICATE_PRIVILEGES: WIN32_ERROR = 311u32; +pub const ERROR_DUPLICATE_SERVICE_NAME: WIN32_ERROR = 1078u32; +pub const ERROR_DUP_DOMAINNAME: WIN32_ERROR = 1221u32; +pub const ERROR_DUP_NAME: WIN32_ERROR = 52u32; +pub const ERROR_DYNAMIC_CODE_BLOCKED: WIN32_ERROR = 1655u32; +pub const ERROR_DYNLINK_FROM_INVALID_RING: WIN32_ERROR = 196u32; +pub const ERROR_EAS_DIDNT_FIT: WIN32_ERROR = 275u32; +pub const ERROR_EAS_NOT_SUPPORTED: WIN32_ERROR = 282u32; +pub const ERROR_EA_ACCESS_DENIED: WIN32_ERROR = 994u32; +pub const ERROR_EA_FILE_CORRUPT: WIN32_ERROR = 276u32; +pub const ERROR_EA_LIST_INCONSISTENT: WIN32_ERROR = 255u32; +pub const ERROR_EA_TABLE_FULL: WIN32_ERROR = 277u32; +pub const ERROR_EDP_DPL_POLICY_CANT_BE_SATISFIED: WIN32_ERROR = 357u32; +pub const ERROR_EDP_POLICY_DENIES_OPERATION: WIN32_ERROR = 356u32; +pub const ERROR_EFS_ALG_BLOB_TOO_BIG: WIN32_ERROR = 6013u32; +pub const ERROR_EFS_DISABLED: WIN32_ERROR = 6015u32; +pub const ERROR_EFS_SERVER_NOT_TRUSTED: WIN32_ERROR = 6011u32; +pub const ERROR_EFS_VERSION_NOT_SUPPORT: WIN32_ERROR = 6016u32; +pub const ERROR_ELEVATION_REQUIRED: WIN32_ERROR = 740u32; +pub const ERROR_ENCLAVE_FAILURE: WIN32_ERROR = 349u32; +pub const ERROR_ENCLAVE_NOT_TERMINATED: WIN32_ERROR = 814u32; +pub const ERROR_ENCLAVE_VIOLATION: WIN32_ERROR = 815u32; +pub const ERROR_ENCRYPTED_FILE_NOT_SUPPORTED: WIN32_ERROR = 489u32; +pub const ERROR_ENCRYPTED_IO_NOT_POSSIBLE: WIN32_ERROR = 808u32; +pub const ERROR_ENCRYPTING_METADATA_DISALLOWED: WIN32_ERROR = 431u32; +pub const ERROR_ENCRYPTION_DISABLED: WIN32_ERROR = 430u32; +pub const ERROR_ENCRYPTION_FAILED: WIN32_ERROR = 6000u32; +pub const ERROR_ENCRYPTION_POLICY_DENIES_OPERATION: WIN32_ERROR = 6022u32; +pub const ERROR_END_OF_MEDIA: WIN32_ERROR = 1100u32; +pub const ERROR_ENVVAR_NOT_FOUND: WIN32_ERROR = 203u32; +pub const ERROR_EOM_OVERFLOW: WIN32_ERROR = 1129u32; +pub const ERROR_ERRORS_ENCOUNTERED: WIN32_ERROR = 774u32; +pub const ERROR_EVALUATION_EXPIRATION: WIN32_ERROR = 622u32; +pub const ERROR_EVENTLOG_CANT_START: WIN32_ERROR = 1501u32; +pub const ERROR_EVENTLOG_FILE_CHANGED: WIN32_ERROR = 1503u32; +pub const ERROR_EVENTLOG_FILE_CORRUPT: WIN32_ERROR = 1500u32; +pub const ERROR_EVENT_DONE: WIN32_ERROR = 710u32; +pub const ERROR_EVENT_PENDING: WIN32_ERROR = 711u32; +pub const ERROR_EXCEPTION_IN_SERVICE: WIN32_ERROR = 1064u32; +pub const ERROR_EXCL_SEM_ALREADY_OWNED: WIN32_ERROR = 101u32; +pub const ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY: WIN32_ERROR = 217u32; +pub const ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY: WIN32_ERROR = 218u32; +pub const ERROR_EXE_MACHINE_TYPE_MISMATCH: WIN32_ERROR = 216u32; +pub const ERROR_EXE_MARKED_INVALID: WIN32_ERROR = 192u32; +pub const ERROR_EXTENDED_ERROR: WIN32_ERROR = 1208u32; +pub const ERROR_EXTERNAL_BACKING_PROVIDER_UNKNOWN: WIN32_ERROR = 343u32; +pub const ERROR_EXTERNAL_SYSKEY_NOT_SUPPORTED: WIN32_ERROR = 399u32; +pub const ERROR_EXTRANEOUS_INFORMATION: WIN32_ERROR = 677u32; +pub const ERROR_FAILED_DRIVER_ENTRY: WIN32_ERROR = 647u32; +pub const ERROR_FAILED_SERVICE_CONTROLLER_CONNECT: WIN32_ERROR = 1063u32; +pub const ERROR_FAIL_FAST_EXCEPTION: WIN32_ERROR = 1653u32; +pub const ERROR_FAIL_I24: WIN32_ERROR = 83u32; +pub const ERROR_FAIL_NOACTION_REBOOT: WIN32_ERROR = 350u32; +pub const ERROR_FAIL_RESTART: WIN32_ERROR = 352u32; +pub const ERROR_FAIL_SHUTDOWN: WIN32_ERROR = 351u32; +pub const ERROR_FATAL_APP_EXIT: WIN32_ERROR = 713u32; +pub const ERROR_FILEMARK_DETECTED: WIN32_ERROR = 1101u32; +pub const ERROR_FILENAME_EXCED_RANGE: WIN32_ERROR = 206u32; +pub const ERROR_FILE_CHECKED_OUT: WIN32_ERROR = 220u32; +pub const ERROR_FILE_CORRUPT: WIN32_ERROR = 1392u32; +pub const ERROR_FILE_ENCRYPTED: WIN32_ERROR = 6002u32; +pub const ERROR_FILE_EXISTS: WIN32_ERROR = 80u32; +pub const ERROR_FILE_HANDLE_REVOKED: WIN32_ERROR = 806u32; +pub const ERROR_FILE_INVALID: WIN32_ERROR = 1006u32; +pub const ERROR_FILE_LEVEL_TRIM_NOT_SUPPORTED: WIN32_ERROR = 326u32; +pub const ERROR_FILE_METADATA_OPTIMIZATION_IN_PROGRESS: WIN32_ERROR = 809u32; +pub const ERROR_FILE_NOT_ENCRYPTED: WIN32_ERROR = 6007u32; +pub const ERROR_FILE_NOT_FOUND: WIN32_ERROR = 2u32; +pub const ERROR_FILE_NOT_SUPPORTED: WIN32_ERROR = 425u32; +pub const ERROR_FILE_OFFLINE: WIN32_ERROR = 4350u32; +pub const ERROR_FILE_PROTECTED_UNDER_DPL: WIN32_ERROR = 406u32; +pub const ERROR_FILE_READ_ONLY: WIN32_ERROR = 6009u32; +pub const ERROR_FILE_SNAP_INVALID_PARAMETER: WIN32_ERROR = 440u32; +pub const ERROR_FILE_SNAP_IN_PROGRESS: WIN32_ERROR = 435u32; +pub const ERROR_FILE_SNAP_IO_NOT_COORDINATED: WIN32_ERROR = 438u32; +pub const ERROR_FILE_SNAP_MODIFY_NOT_SUPPORTED: WIN32_ERROR = 437u32; +pub const ERROR_FILE_SNAP_UNEXPECTED_ERROR: WIN32_ERROR = 439u32; +pub const ERROR_FILE_SNAP_USER_SECTION_NOT_SUPPORTED: WIN32_ERROR = 436u32; +pub const ERROR_FILE_SYSTEM_LIMITATION: WIN32_ERROR = 665u32; +pub const ERROR_FILE_SYSTEM_VIRTUALIZATION_BUSY: WIN32_ERROR = 371u32; +pub const ERROR_FILE_SYSTEM_VIRTUALIZATION_INVALID_OPERATION: WIN32_ERROR = 385u32; +pub const ERROR_FILE_SYSTEM_VIRTUALIZATION_METADATA_CORRUPT: WIN32_ERROR = 370u32; +pub const ERROR_FILE_SYSTEM_VIRTUALIZATION_PROVIDER_UNKNOWN: WIN32_ERROR = 372u32; +pub const ERROR_FILE_SYSTEM_VIRTUALIZATION_UNAVAILABLE: WIN32_ERROR = 369u32; +pub const ERROR_FILE_TOO_LARGE: WIN32_ERROR = 223u32; +pub const ERROR_FIRMWARE_UPDATED: WIN32_ERROR = 728u32; +pub const ERROR_FLOAT_MULTIPLE_FAULTS: WIN32_ERROR = 630u32; +pub const ERROR_FLOAT_MULTIPLE_TRAPS: WIN32_ERROR = 631u32; +pub const ERROR_FLOPPY_BAD_REGISTERS: WIN32_ERROR = 1125u32; +pub const ERROR_FLOPPY_ID_MARK_NOT_FOUND: WIN32_ERROR = 1122u32; +pub const ERROR_FLOPPY_UNKNOWN_ERROR: WIN32_ERROR = 1124u32; +pub const ERROR_FLOPPY_VOLUME: WIN32_ERROR = 584u32; +pub const ERROR_FLOPPY_WRONG_CYLINDER: WIN32_ERROR = 1123u32; +pub const ERROR_FORMS_AUTH_REQUIRED: WIN32_ERROR = 224u32; +pub const ERROR_FOUND_OUT_OF_SCOPE: WIN32_ERROR = 601u32; +pub const ERROR_FSFILTER_OP_COMPLETED_SUCCESSFULLY: WIN32_ERROR = 762u32; +pub const ERROR_FS_DRIVER_REQUIRED: WIN32_ERROR = 588u32; +pub const ERROR_FS_METADATA_INCONSISTENT: WIN32_ERROR = 510u32; +pub const ERROR_FT_DI_SCAN_REQUIRED: WIN32_ERROR = 339u32; +pub const ERROR_FT_READ_FAILURE: WIN32_ERROR = 415u32; +pub const ERROR_FT_READ_FROM_COPY_FAILURE: WIN32_ERROR = 818u32; +pub const ERROR_FT_READ_RECOVERY_FROM_BACKUP: WIN32_ERROR = 704u32; +pub const ERROR_FT_WRITE_FAILURE: WIN32_ERROR = 338u32; +pub const ERROR_FT_WRITE_RECOVERY: WIN32_ERROR = 705u32; +pub const ERROR_FULLSCREEN_MODE: WIN32_ERROR = 1007u32; +pub const ERROR_FUNCTION_FAILED: WIN32_ERROR = 1627u32; +pub const ERROR_FUNCTION_NOT_CALLED: WIN32_ERROR = 1626u32; +pub const ERROR_GDI_HANDLE_LEAK: WIN32_ERROR = 373u32; +pub const ERROR_GENERIC_NOT_MAPPED: WIN32_ERROR = 1360u32; +pub const ERROR_GEN_FAILURE: WIN32_ERROR = 31u32; +pub const ERROR_GLOBAL_ONLY_HOOK: WIN32_ERROR = 1429u32; +pub const ERROR_GRACEFUL_DISCONNECT: WIN32_ERROR = 1226u32; +pub const ERROR_GROUP_EXISTS: WIN32_ERROR = 1318u32; +pub const ERROR_GUID_SUBSTITUTION_MADE: WIN32_ERROR = 680u32; +pub const ERROR_HANDLES_CLOSED: WIN32_ERROR = 676u32; +pub const ERROR_HANDLE_DISK_FULL: WIN32_ERROR = 39u32; +pub const ERROR_HANDLE_EOF: WIN32_ERROR = 38u32; +pub const ERROR_HANDLE_REVOKED: WIN32_ERROR = 811u32; +pub const ERROR_HAS_SYSTEM_CRITICAL_FILES: WIN32_ERROR = 488u32; +pub const ERROR_HIBERNATED: WIN32_ERROR = 726u32; +pub const ERROR_HIBERNATION_FAILURE: WIN32_ERROR = 656u32; +pub const ERROR_HOOK_NEEDS_HMOD: WIN32_ERROR = 1428u32; +pub const ERROR_HOOK_NOT_INSTALLED: WIN32_ERROR = 1431u32; +pub const ERROR_HOOK_TYPE_NOT_ALLOWED: WIN32_ERROR = 1458u32; +pub const ERROR_HOST_DOWN: WIN32_ERROR = 1256u32; +pub const ERROR_HOST_UNREACHABLE: WIN32_ERROR = 1232u32; +pub const ERROR_HOTKEY_ALREADY_REGISTERED: WIN32_ERROR = 1409u32; +pub const ERROR_HOTKEY_NOT_REGISTERED: WIN32_ERROR = 1419u32; +pub const ERROR_HWNDS_HAVE_DIFF_PARENT: WIN32_ERROR = 1441u32; +pub const ERROR_ILLEGAL_CHARACTER: WIN32_ERROR = 582u32; +pub const ERROR_ILLEGAL_DLL_RELOCATION: WIN32_ERROR = 623u32; +pub const ERROR_ILLEGAL_ELEMENT_ADDRESS: WIN32_ERROR = 1162u32; +pub const ERROR_ILLEGAL_FLOAT_CONTEXT: WIN32_ERROR = 579u32; +pub const ERROR_ILL_FORMED_PASSWORD: WIN32_ERROR = 1324u32; +pub const ERROR_IMAGE_AT_DIFFERENT_BASE: WIN32_ERROR = 807u32; +pub const ERROR_IMAGE_MACHINE_TYPE_MISMATCH: WIN32_ERROR = 706u32; +pub const ERROR_IMAGE_MACHINE_TYPE_MISMATCH_EXE: WIN32_ERROR = 720u32; +pub const ERROR_IMAGE_NOT_AT_BASE: WIN32_ERROR = 700u32; +pub const ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT: WIN32_ERROR = 308u32; +pub const ERROR_IMPLEMENTATION_LIMIT: WIN32_ERROR = 1292u32; +pub const ERROR_INCOMPATIBLE_SERVICE_PRIVILEGE: WIN32_ERROR = 1297u32; +pub const ERROR_INCOMPATIBLE_SERVICE_SID_TYPE: WIN32_ERROR = 1290u32; +pub const ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING: WIN32_ERROR = 304u32; +pub const ERROR_INCORRECT_ACCOUNT_TYPE: WIN32_ERROR = 8646u32; +pub const ERROR_INCORRECT_ADDRESS: WIN32_ERROR = 1241u32; +pub const ERROR_INCORRECT_SIZE: WIN32_ERROR = 1462u32; +pub const ERROR_INDEX_ABSENT: WIN32_ERROR = 1611u32; +pub const ERROR_INDEX_OUT_OF_BOUNDS: WIN32_ERROR = 474u32; +pub const ERROR_INFLOOP_IN_RELOC_CHAIN: WIN32_ERROR = 202u32; +pub const ERROR_INSTALL_ALREADY_RUNNING: WIN32_ERROR = 1618u32; +pub const ERROR_INSTALL_FAILURE: WIN32_ERROR = 1603u32; +pub const ERROR_INSTALL_LANGUAGE_UNSUPPORTED: WIN32_ERROR = 1623u32; +pub const ERROR_INSTALL_LOG_FAILURE: WIN32_ERROR = 1622u32; +pub const ERROR_INSTALL_NOTUSED: WIN32_ERROR = 1634u32; +pub const ERROR_INSTALL_PACKAGE_INVALID: WIN32_ERROR = 1620u32; +pub const ERROR_INSTALL_PACKAGE_OPEN_FAILED: WIN32_ERROR = 1619u32; +pub const ERROR_INSTALL_PACKAGE_REJECTED: WIN32_ERROR = 1625u32; +pub const ERROR_INSTALL_PACKAGE_VERSION: WIN32_ERROR = 1613u32; +pub const ERROR_INSTALL_PLATFORM_UNSUPPORTED: WIN32_ERROR = 1633u32; +pub const ERROR_INSTALL_REJECTED: WIN32_ERROR = 1654u32; +pub const ERROR_INSTALL_REMOTE_DISALLOWED: WIN32_ERROR = 1640u32; +pub const ERROR_INSTALL_REMOTE_PROHIBITED: WIN32_ERROR = 1645u32; +pub const ERROR_INSTALL_SERVICE_FAILURE: WIN32_ERROR = 1601u32; +pub const ERROR_INSTALL_SERVICE_SAFEBOOT: WIN32_ERROR = 1652u32; +pub const ERROR_INSTALL_SOURCE_ABSENT: WIN32_ERROR = 1612u32; +pub const ERROR_INSTALL_SUSPEND: WIN32_ERROR = 1604u32; +pub const ERROR_INSTALL_TEMP_UNWRITABLE: WIN32_ERROR = 1632u32; +pub const ERROR_INSTALL_TRANSFORM_FAILURE: WIN32_ERROR = 1624u32; +pub const ERROR_INSTALL_TRANSFORM_REJECTED: WIN32_ERROR = 1644u32; +pub const ERROR_INSTALL_UI_FAILURE: WIN32_ERROR = 1621u32; +pub const ERROR_INSTALL_USEREXIT: WIN32_ERROR = 1602u32; +pub const ERROR_INSTRUCTION_MISALIGNMENT: WIN32_ERROR = 549u32; +pub const ERROR_INSUFFICIENT_BUFFER: WIN32_ERROR = 122u32; +pub const ERROR_INSUFFICIENT_LOGON_INFO: WIN32_ERROR = 608u32; +pub const ERROR_INSUFFICIENT_POWER: WIN32_ERROR = 639u32; +pub const ERROR_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE: WIN32_ERROR = 781u32; +pub const ERROR_INSUFFICIENT_VIRTUAL_ADDR_RESOURCES: WIN32_ERROR = 473u32; +pub const ERROR_INTERMIXED_KERNEL_EA_OPERATION: WIN32_ERROR = 324u32; +pub const ERROR_INTERNAL_DB_CORRUPTION: WIN32_ERROR = 1358u32; +pub const ERROR_INTERNAL_DB_ERROR: WIN32_ERROR = 1383u32; +pub const ERROR_INTERNAL_ERROR: WIN32_ERROR = 1359u32; +pub const ERROR_INTERRUPT_STILL_CONNECTED: WIN32_ERROR = 764u32; +pub const ERROR_INTERRUPT_VECTOR_ALREADY_CONNECTED: WIN32_ERROR = 763u32; +pub const ERROR_INVALID_ACCEL_HANDLE: WIN32_ERROR = 1403u32; +pub const ERROR_INVALID_ACCESS: WIN32_ERROR = 12u32; +pub const ERROR_INVALID_ACCOUNT_NAME: WIN32_ERROR = 1315u32; +pub const ERROR_INVALID_ACE_CONDITION: WIN32_ERROR = 805u32; +pub const ERROR_INVALID_ACL: WIN32_ERROR = 1336u32; +pub const ERROR_INVALID_ADDRESS: WIN32_ERROR = 487u32; +pub const ERROR_INVALID_AT_INTERRUPT_TIME: WIN32_ERROR = 104u32; +pub const ERROR_INVALID_BLOCK: WIN32_ERROR = 9u32; +pub const ERROR_INVALID_BLOCK_LENGTH: WIN32_ERROR = 1106u32; +pub const ERROR_INVALID_CAP: WIN32_ERROR = 320u32; +pub const ERROR_INVALID_CATEGORY: WIN32_ERROR = 117u32; +pub const ERROR_INVALID_COMBOBOX_MESSAGE: WIN32_ERROR = 1422u32; +pub const ERROR_INVALID_COMMAND_LINE: WIN32_ERROR = 1639u32; +pub const ERROR_INVALID_COMPUTERNAME: WIN32_ERROR = 1210u32; +pub const ERROR_INVALID_CRUNTIME_PARAMETER: WIN32_ERROR = 1288u32; +pub const ERROR_INVALID_CURSOR_HANDLE: WIN32_ERROR = 1402u32; +pub const ERROR_INVALID_DATA: WIN32_ERROR = 13u32; +pub const ERROR_INVALID_DATATYPE: WIN32_ERROR = 1804u32; +pub const ERROR_INVALID_DEVICE_OBJECT_PARAMETER: WIN32_ERROR = 650u32; +pub const ERROR_INVALID_DLL: WIN32_ERROR = 1154u32; +pub const ERROR_INVALID_DOMAINNAME: WIN32_ERROR = 1212u32; +pub const ERROR_INVALID_DOMAIN_ROLE: WIN32_ERROR = 1354u32; +pub const ERROR_INVALID_DOMAIN_STATE: WIN32_ERROR = 1353u32; +pub const ERROR_INVALID_DRIVE: WIN32_ERROR = 15u32; +pub const ERROR_INVALID_DWP_HANDLE: WIN32_ERROR = 1405u32; +pub const ERROR_INVALID_EA_HANDLE: WIN32_ERROR = 278u32; +pub const ERROR_INVALID_EA_NAME: WIN32_ERROR = 254u32; +pub const ERROR_INVALID_EDIT_HEIGHT: WIN32_ERROR = 1424u32; +pub const ERROR_INVALID_ENVIRONMENT: WIN32_ERROR = 1805u32; +pub const ERROR_INVALID_EVENTNAME: WIN32_ERROR = 1211u32; +pub const ERROR_INVALID_EVENT_COUNT: WIN32_ERROR = 151u32; +pub const ERROR_INVALID_EXCEPTION_HANDLER: WIN32_ERROR = 310u32; +pub const ERROR_INVALID_EXE_SIGNATURE: WIN32_ERROR = 191u32; +pub const ERROR_INVALID_FIELD: WIN32_ERROR = 1616u32; +pub const ERROR_INVALID_FIELD_IN_PARAMETER_LIST: WIN32_ERROR = 328u32; +pub const ERROR_INVALID_FILTER_PROC: WIN32_ERROR = 1427u32; +pub const ERROR_INVALID_FLAGS: WIN32_ERROR = 1004u32; +pub const ERROR_INVALID_FLAG_NUMBER: WIN32_ERROR = 186u32; +pub const ERROR_INVALID_FORM_NAME: WIN32_ERROR = 1902u32; +pub const ERROR_INVALID_FORM_SIZE: WIN32_ERROR = 1903u32; +pub const ERROR_INVALID_FUNCTION: WIN32_ERROR = 1u32; +pub const ERROR_INVALID_GROUPNAME: WIN32_ERROR = 1209u32; +pub const ERROR_INVALID_GROUP_ATTRIBUTES: WIN32_ERROR = 1345u32; +pub const ERROR_INVALID_GW_COMMAND: WIN32_ERROR = 1443u32; +pub const ERROR_INVALID_HANDLE: WIN32_ERROR = 6u32; +pub const ERROR_INVALID_HANDLE_STATE: WIN32_ERROR = 1609u32; +pub const ERROR_INVALID_HOOK_FILTER: WIN32_ERROR = 1426u32; +pub const ERROR_INVALID_HOOK_HANDLE: WIN32_ERROR = 1404u32; +pub const ERROR_INVALID_HW_PROFILE: WIN32_ERROR = 619u32; +pub const ERROR_INVALID_ICON_HANDLE: WIN32_ERROR = 1414u32; +pub const ERROR_INVALID_ID_AUTHORITY: WIN32_ERROR = 1343u32; +pub const ERROR_INVALID_IMAGE_HASH: WIN32_ERROR = 577u32; +pub const ERROR_INVALID_IMPORT_OF_NON_DLL: WIN32_ERROR = 1276u32; +pub const ERROR_INVALID_INDEX: WIN32_ERROR = 1413u32; +pub const ERROR_INVALID_KERNEL_INFO_VERSION: WIN32_ERROR = 340u32; +pub const ERROR_INVALID_KEYBOARD_HANDLE: WIN32_ERROR = 1457u32; +pub const ERROR_INVALID_LABEL: WIN32_ERROR = 1299u32; +pub const ERROR_INVALID_LB_MESSAGE: WIN32_ERROR = 1432u32; +pub const ERROR_INVALID_LDT_DESCRIPTOR: WIN32_ERROR = 564u32; +pub const ERROR_INVALID_LDT_OFFSET: WIN32_ERROR = 563u32; +pub const ERROR_INVALID_LDT_SIZE: WIN32_ERROR = 561u32; +pub const ERROR_INVALID_LEVEL: WIN32_ERROR = 124u32; +pub const ERROR_INVALID_LIST_FORMAT: WIN32_ERROR = 153u32; +pub const ERROR_INVALID_LOCK_RANGE: WIN32_ERROR = 307u32; +pub const ERROR_INVALID_LOGON_HOURS: WIN32_ERROR = 1328u32; +pub const ERROR_INVALID_LOGON_TYPE: WIN32_ERROR = 1367u32; +pub const ERROR_INVALID_MEMBER: WIN32_ERROR = 1388u32; +pub const ERROR_INVALID_MENU_HANDLE: WIN32_ERROR = 1401u32; +pub const ERROR_INVALID_MESSAGE: WIN32_ERROR = 1002u32; +pub const ERROR_INVALID_MESSAGEDEST: WIN32_ERROR = 1218u32; +pub const ERROR_INVALID_MESSAGENAME: WIN32_ERROR = 1217u32; +pub const ERROR_INVALID_MINALLOCSIZE: WIN32_ERROR = 195u32; +pub const ERROR_INVALID_MODULETYPE: WIN32_ERROR = 190u32; +pub const ERROR_INVALID_MONITOR_HANDLE: WIN32_ERROR = 1461u32; +pub const ERROR_INVALID_MSGBOX_STYLE: WIN32_ERROR = 1438u32; +pub const ERROR_INVALID_NAME: WIN32_ERROR = 123u32; +pub const ERROR_INVALID_NETNAME: WIN32_ERROR = 1214u32; +pub const ERROR_INVALID_OPLOCK_PROTOCOL: WIN32_ERROR = 301u32; +pub const ERROR_INVALID_ORDINAL: WIN32_ERROR = 182u32; +pub const ERROR_INVALID_OWNER: WIN32_ERROR = 1307u32; +pub const ERROR_INVALID_PACKAGE_SID_LENGTH: WIN32_ERROR = 4253u32; +pub const ERROR_INVALID_PARAMETER: WIN32_ERROR = 87u32; +pub const ERROR_INVALID_PASSWORD: WIN32_ERROR = 86u32; +pub const ERROR_INVALID_PASSWORDNAME: WIN32_ERROR = 1216u32; +pub const ERROR_INVALID_PATCH_XML: WIN32_ERROR = 1650u32; +pub const ERROR_INVALID_PEP_INFO_VERSION: WIN32_ERROR = 341u32; +pub const ERROR_INVALID_PLUGPLAY_DEVICE_PATH: WIN32_ERROR = 620u32; +pub const ERROR_INVALID_PORT_ATTRIBUTES: WIN32_ERROR = 545u32; +pub const ERROR_INVALID_PRIMARY_GROUP: WIN32_ERROR = 1308u32; +pub const ERROR_INVALID_PRINTER_COMMAND: WIN32_ERROR = 1803u32; +pub const ERROR_INVALID_PRINTER_NAME: WIN32_ERROR = 1801u32; +pub const ERROR_INVALID_PRINTER_STATE: WIN32_ERROR = 1906u32; +pub const ERROR_INVALID_PRIORITY: WIN32_ERROR = 1800u32; +pub const ERROR_INVALID_QUOTA_LOWER: WIN32_ERROR = 547u32; +pub const ERROR_INVALID_REPARSE_DATA: WIN32_ERROR = 4392u32; +pub const ERROR_INVALID_SCROLLBAR_RANGE: WIN32_ERROR = 1448u32; +pub const ERROR_INVALID_SECURITY_DESCR: WIN32_ERROR = 1338u32; +pub const ERROR_INVALID_SEGDPL: WIN32_ERROR = 198u32; +pub const ERROR_INVALID_SEGMENT_NUMBER: WIN32_ERROR = 180u32; +pub const ERROR_INVALID_SEPARATOR_FILE: WIN32_ERROR = 1799u32; +pub const ERROR_INVALID_SERVER_STATE: WIN32_ERROR = 1352u32; +pub const ERROR_INVALID_SERVICENAME: WIN32_ERROR = 1213u32; +pub const ERROR_INVALID_SERVICE_ACCOUNT: WIN32_ERROR = 1057u32; +pub const ERROR_INVALID_SERVICE_CONTROL: WIN32_ERROR = 1052u32; +pub const ERROR_INVALID_SERVICE_LOCK: WIN32_ERROR = 1071u32; +pub const ERROR_INVALID_SHARENAME: WIN32_ERROR = 1215u32; +pub const ERROR_INVALID_SHOWWIN_COMMAND: WIN32_ERROR = 1449u32; +pub const ERROR_INVALID_SID: WIN32_ERROR = 1337u32; +pub const ERROR_INVALID_SIGNAL_NUMBER: WIN32_ERROR = 209u32; +pub const ERROR_INVALID_SPI_VALUE: WIN32_ERROR = 1439u32; +pub const ERROR_INVALID_STACKSEG: WIN32_ERROR = 189u32; +pub const ERROR_INVALID_STARTING_CODESEG: WIN32_ERROR = 188u32; +pub const ERROR_INVALID_SUB_AUTHORITY: WIN32_ERROR = 1335u32; +pub const ERROR_INVALID_TABLE: WIN32_ERROR = 1628u32; +pub const ERROR_INVALID_TARGET_HANDLE: WIN32_ERROR = 114u32; +pub const ERROR_INVALID_TASK_INDEX: WIN32_ERROR = 1551u32; +pub const ERROR_INVALID_TASK_NAME: WIN32_ERROR = 1550u32; +pub const ERROR_INVALID_THREAD_ID: WIN32_ERROR = 1444u32; +pub const ERROR_INVALID_TIME: WIN32_ERROR = 1901u32; +pub const ERROR_INVALID_TOKEN: WIN32_ERROR = 315u32; +pub const ERROR_INVALID_UNWIND_TARGET: WIN32_ERROR = 544u32; +pub const ERROR_INVALID_USER_BUFFER: WIN32_ERROR = 1784u32; +pub const ERROR_INVALID_USER_PRINCIPAL_NAME: WIN32_ERROR = 8636u32; +pub const ERROR_INVALID_VARIANT: WIN32_ERROR = 604u32; +pub const ERROR_INVALID_VERIFY_SWITCH: WIN32_ERROR = 118u32; +pub const ERROR_INVALID_WINDOW_HANDLE: WIN32_ERROR = 1400u32; +pub const ERROR_INVALID_WORKSTATION: WIN32_ERROR = 1329u32; +pub const ERROR_IOPL_NOT_ENABLED: WIN32_ERROR = 197u32; +pub const ERROR_IO_DEVICE: WIN32_ERROR = 1117u32; +pub const ERROR_IO_INCOMPLETE: WIN32_ERROR = 996u32; +pub const ERROR_IO_PENDING: WIN32_ERROR = 997u32; +pub const ERROR_IO_PRIVILEGE_FAILED: WIN32_ERROR = 571u32; +pub const ERROR_IO_REISSUE_AS_CACHED: WIN32_ERROR = 3950u32; +pub const ERROR_IPSEC_IKE_TIMED_OUT: WIN32_ERROR = 13805u32; +pub const ERROR_IP_ADDRESS_CONFLICT1: WIN32_ERROR = 611u32; +pub const ERROR_IP_ADDRESS_CONFLICT2: WIN32_ERROR = 612u32; +pub const ERROR_IRQ_BUSY: WIN32_ERROR = 1119u32; +pub const ERROR_IS_JOINED: WIN32_ERROR = 134u32; +pub const ERROR_IS_JOIN_PATH: WIN32_ERROR = 147u32; +pub const ERROR_IS_JOIN_TARGET: WIN32_ERROR = 133u32; +pub const ERROR_IS_SUBSTED: WIN32_ERROR = 135u32; +pub const ERROR_IS_SUBST_PATH: WIN32_ERROR = 146u32; +pub const ERROR_IS_SUBST_TARGET: WIN32_ERROR = 149u32; +pub const ERROR_ITERATED_DATA_EXCEEDS_64k: WIN32_ERROR = 194u32; +pub const ERROR_JOB_NO_CONTAINER: WIN32_ERROR = 1505u32; +pub const ERROR_JOIN_TO_JOIN: WIN32_ERROR = 138u32; +pub const ERROR_JOIN_TO_SUBST: WIN32_ERROR = 140u32; +pub const ERROR_JOURNAL_DELETE_IN_PROGRESS: WIN32_ERROR = 1178u32; +pub const ERROR_JOURNAL_ENTRY_DELETED: WIN32_ERROR = 1181u32; +pub const ERROR_JOURNAL_HOOK_SET: WIN32_ERROR = 1430u32; +pub const ERROR_JOURNAL_NOT_ACTIVE: WIN32_ERROR = 1179u32; +pub const ERROR_KERNEL_APC: WIN32_ERROR = 738u32; +pub const ERROR_KEY_DELETED: WIN32_ERROR = 1018u32; +pub const ERROR_KEY_HAS_CHILDREN: WIN32_ERROR = 1020u32; +pub const ERROR_KM_DRIVER_BLOCKED: WIN32_ERROR = 1930u32; +pub const ERROR_LABEL_TOO_LONG: WIN32_ERROR = 154u32; +pub const ERROR_LAST_ADMIN: WIN32_ERROR = 1322u32; +pub const ERROR_LB_WITHOUT_TABSTOPS: WIN32_ERROR = 1434u32; +pub const ERROR_LICENSE_QUOTA_EXCEEDED: WIN32_ERROR = 1395u32; +pub const ERROR_LINUX_SUBSYSTEM_NOT_PRESENT: WIN32_ERROR = 414u32; +pub const ERROR_LINUX_SUBSYSTEM_UPDATE_REQUIRED: WIN32_ERROR = 444u32; +pub const ERROR_LISTBOX_ID_NOT_FOUND: WIN32_ERROR = 1416u32; +pub const ERROR_LM_CROSS_ENCRYPTION_REQUIRED: WIN32_ERROR = 1390u32; +pub const ERROR_LOCAL_POLICY_MODIFICATION_NOT_SUPPORTED: WIN32_ERROR = 8653u32; +pub const ERROR_LOCAL_USER_SESSION_KEY: WIN32_ERROR = 1303u32; +pub const ERROR_LOCKED: WIN32_ERROR = 212u32; +pub const ERROR_LOCK_FAILED: WIN32_ERROR = 167u32; +pub const ERROR_LOCK_VIOLATION: WIN32_ERROR = 33u32; +pub const ERROR_LOGIN_TIME_RESTRICTION: WIN32_ERROR = 1239u32; +pub const ERROR_LOGIN_WKSTA_RESTRICTION: WIN32_ERROR = 1240u32; +pub const ERROR_LOGON_FAILURE: WIN32_ERROR = 1326u32; +pub const ERROR_LOGON_NOT_GRANTED: WIN32_ERROR = 1380u32; +pub const ERROR_LOGON_SERVER_CONFLICT: WIN32_ERROR = 568u32; +pub const ERROR_LOGON_SESSION_COLLISION: WIN32_ERROR = 1366u32; +pub const ERROR_LOGON_SESSION_EXISTS: WIN32_ERROR = 1363u32; +pub const ERROR_LOGON_TYPE_NOT_GRANTED: WIN32_ERROR = 1385u32; +pub const ERROR_LOG_FILE_FULL: WIN32_ERROR = 1502u32; +pub const ERROR_LOG_HARD_ERROR: WIN32_ERROR = 718u32; +pub const ERROR_LONGJUMP: WIN32_ERROR = 682u32; +pub const ERROR_LOST_MODE_LOGON_RESTRICTION: WIN32_ERROR = 1939u32; +pub const ERROR_LOST_WRITEBEHIND_DATA: WIN32_ERROR = 596u32; +pub const ERROR_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR: WIN32_ERROR = 790u32; +pub const ERROR_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED: WIN32_ERROR = 788u32; +pub const ERROR_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR: WIN32_ERROR = 789u32; +pub const ERROR_LUIDS_EXHAUSTED: WIN32_ERROR = 1334u32; +pub const ERROR_MACHINE_LOCKED: WIN32_ERROR = 1271u32; +pub const ERROR_MAGAZINE_NOT_PRESENT: WIN32_ERROR = 1163u32; +pub const ERROR_MAPPED_ALIGNMENT: WIN32_ERROR = 1132u32; +pub const ERROR_MARKED_TO_DISALLOW_WRITES: WIN32_ERROR = 348u32; +pub const ERROR_MARSHALL_OVERFLOW: WIN32_ERROR = 603u32; +pub const ERROR_MAX_SESSIONS_REACHED: WIN32_ERROR = 353u32; +pub const ERROR_MAX_THRDS_REACHED: WIN32_ERROR = 164u32; +pub const ERROR_MCA_EXCEPTION: WIN32_ERROR = 784u32; +pub const ERROR_MCA_OCCURED: WIN32_ERROR = 651u32; +pub const ERROR_MEDIA_CHANGED: WIN32_ERROR = 1110u32; +pub const ERROR_MEDIA_CHECK: WIN32_ERROR = 679u32; +pub const ERROR_MEMBERS_PRIMARY_GROUP: WIN32_ERROR = 1374u32; +pub const ERROR_MEMBER_IN_ALIAS: WIN32_ERROR = 1378u32; +pub const ERROR_MEMBER_IN_GROUP: WIN32_ERROR = 1320u32; +pub const ERROR_MEMBER_NOT_IN_ALIAS: WIN32_ERROR = 1377u32; +pub const ERROR_MEMBER_NOT_IN_GROUP: WIN32_ERROR = 1321u32; +pub const ERROR_MEMORY_HARDWARE: WIN32_ERROR = 779u32; +pub const ERROR_MENU_ITEM_NOT_FOUND: WIN32_ERROR = 1456u32; +pub const ERROR_MESSAGE_SYNC_ONLY: WIN32_ERROR = 1159u32; +pub const ERROR_META_EXPANSION_TOO_LONG: WIN32_ERROR = 208u32; +pub const ERROR_MISSING_SYSTEMFILE: WIN32_ERROR = 573u32; +pub const ERROR_MOD_NOT_FOUND: WIN32_ERROR = 126u32; +pub const ERROR_MORE_DATA: WIN32_ERROR = 234u32; +pub const ERROR_MORE_WRITES: WIN32_ERROR = 1120u32; +pub const ERROR_MOUNT_POINT_NOT_RESOLVED: WIN32_ERROR = 649u32; +pub const ERROR_MP_PROCESSOR_MISMATCH: WIN32_ERROR = 725u32; +pub const ERROR_MR_MID_NOT_FOUND: WIN32_ERROR = 317u32; +pub const ERROR_MULTIPLE_FAULT_VIOLATION: WIN32_ERROR = 640u32; +pub const ERROR_MUTANT_LIMIT_EXCEEDED: WIN32_ERROR = 587u32; +pub const ERROR_MUTUAL_AUTH_FAILED: WIN32_ERROR = 1397u32; +pub const ERROR_NEGATIVE_SEEK: WIN32_ERROR = 131u32; +pub const ERROR_NESTING_NOT_ALLOWED: WIN32_ERROR = 215u32; +pub const ERROR_NETLOGON_NOT_STARTED: WIN32_ERROR = 1792u32; +pub const ERROR_NETNAME_DELETED: WIN32_ERROR = 64u32; +pub const ERROR_NETWORK_ACCESS_DENIED: WIN32_ERROR = 65u32; +pub const ERROR_NETWORK_ACCESS_DENIED_EDP: WIN32_ERROR = 354u32; +pub const ERROR_NETWORK_BUSY: WIN32_ERROR = 54u32; +pub const ERROR_NETWORK_UNREACHABLE: WIN32_ERROR = 1231u32; +pub const ERROR_NET_OPEN_FAILED: WIN32_ERROR = 570u32; +pub const ERROR_NET_WRITE_FAULT: WIN32_ERROR = 88u32; +pub const ERROR_NOACCESS: WIN32_ERROR = 998u32; +pub const ERROR_NOINTERFACE: WIN32_ERROR = 632u32; +pub const ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT: WIN32_ERROR = 1807u32; +pub const ERROR_NOLOGON_SERVER_TRUST_ACCOUNT: WIN32_ERROR = 1809u32; +pub const ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT: WIN32_ERROR = 1808u32; +pub const ERROR_NONE_MAPPED: WIN32_ERROR = 1332u32; +pub const ERROR_NONPAGED_SYSTEM_RESOURCES: WIN32_ERROR = 1451u32; +pub const ERROR_NON_ACCOUNT_SID: WIN32_ERROR = 1257u32; +pub const ERROR_NON_DOMAIN_SID: WIN32_ERROR = 1258u32; +pub const ERROR_NON_MDICHILD_WINDOW: WIN32_ERROR = 1445u32; +pub const ERROR_NOTHING_TO_TERMINATE: WIN32_ERROR = 758u32; +pub const ERROR_NOTIFICATION_GUID_ALREADY_DEFINED: WIN32_ERROR = 309u32; +pub const ERROR_NOTIFY_CLEANUP: WIN32_ERROR = 745u32; +pub const ERROR_NOTIFY_ENUM_DIR: WIN32_ERROR = 1022u32; +pub const ERROR_NOT_ALLOWED_ON_SYSTEM_FILE: WIN32_ERROR = 313u32; +pub const ERROR_NOT_ALL_ASSIGNED: WIN32_ERROR = 1300u32; +pub const ERROR_NOT_APPCONTAINER: WIN32_ERROR = 4250u32; +pub const ERROR_NOT_AUTHENTICATED: WIN32_ERROR = 1244u32; +pub const ERROR_NOT_A_CLOUD_FILE: WIN32_ERROR = 376u32; +pub const ERROR_NOT_A_CLOUD_SYNC_ROOT: WIN32_ERROR = 405u32; +pub const ERROR_NOT_A_DAX_VOLUME: WIN32_ERROR = 420u32; +pub const ERROR_NOT_A_REPARSE_POINT: WIN32_ERROR = 4390u32; +pub const ERROR_NOT_CAPABLE: WIN32_ERROR = 775u32; +pub const ERROR_NOT_CHILD_WINDOW: WIN32_ERROR = 1442u32; +pub const ERROR_NOT_CONNECTED: WIN32_ERROR = 2250u32; +pub const ERROR_NOT_CONTAINER: WIN32_ERROR = 1207u32; +pub const ERROR_NOT_DAX_MAPPABLE: WIN32_ERROR = 421u32; +pub const ERROR_NOT_DOS_DISK: WIN32_ERROR = 26u32; +pub const ERROR_NOT_ENOUGH_MEMORY: WIN32_ERROR = 8u32; +pub const ERROR_NOT_ENOUGH_QUOTA: WIN32_ERROR = 1816u32; +pub const ERROR_NOT_ENOUGH_SERVER_MEMORY: WIN32_ERROR = 1130u32; +pub const ERROR_NOT_EXPORT_FORMAT: WIN32_ERROR = 6008u32; +pub const ERROR_NOT_FOUND: WIN32_ERROR = 1168u32; +pub const ERROR_NOT_GUI_PROCESS: WIN32_ERROR = 1471u32; +pub const ERROR_NOT_JOINED: WIN32_ERROR = 136u32; +pub const ERROR_NOT_LOCKED: WIN32_ERROR = 158u32; +pub const ERROR_NOT_LOGGED_ON: WIN32_ERROR = 1245u32; +pub const ERROR_NOT_LOGON_PROCESS: WIN32_ERROR = 1362u32; +pub const ERROR_NOT_OWNER: WIN32_ERROR = 288u32; +pub const ERROR_NOT_READY: WIN32_ERROR = 21u32; +pub const ERROR_NOT_READ_FROM_COPY: WIN32_ERROR = 337u32; +pub const ERROR_NOT_REDUNDANT_STORAGE: WIN32_ERROR = 333u32; +pub const ERROR_NOT_REGISTRY_FILE: WIN32_ERROR = 1017u32; +pub const ERROR_NOT_SAFEBOOT_SERVICE: WIN32_ERROR = 1084u32; +pub const ERROR_NOT_SAFE_MODE_DRIVER: WIN32_ERROR = 646u32; +pub const ERROR_NOT_SAME_DEVICE: WIN32_ERROR = 17u32; +pub const ERROR_NOT_SAME_OBJECT: WIN32_ERROR = 1656u32; +pub const ERROR_NOT_SUBSTED: WIN32_ERROR = 137u32; +pub const ERROR_NOT_SUPPORTED: WIN32_ERROR = 50u32; +pub const ERROR_NOT_SUPPORTED_IN_APPCONTAINER: WIN32_ERROR = 4252u32; +pub const ERROR_NOT_SUPPORTED_ON_DAX: WIN32_ERROR = 360u32; +pub const ERROR_NOT_SUPPORTED_ON_SBS: WIN32_ERROR = 1254u32; +pub const ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER: WIN32_ERROR = 8584u32; +pub const ERROR_NOT_SUPPORTED_WITH_AUDITING: WIN32_ERROR = 499u32; +pub const ERROR_NOT_SUPPORTED_WITH_BTT: WIN32_ERROR = 429u32; +pub const ERROR_NOT_SUPPORTED_WITH_BYPASSIO: WIN32_ERROR = 493u32; +pub const ERROR_NOT_SUPPORTED_WITH_CACHED_HANDLE: WIN32_ERROR = 509u32; +pub const ERROR_NOT_SUPPORTED_WITH_COMPRESSION: WIN32_ERROR = 496u32; +pub const ERROR_NOT_SUPPORTED_WITH_DEDUPLICATION: WIN32_ERROR = 498u32; +pub const ERROR_NOT_SUPPORTED_WITH_ENCRYPTION: WIN32_ERROR = 495u32; +pub const ERROR_NOT_SUPPORTED_WITH_MONITORING: WIN32_ERROR = 503u32; +pub const ERROR_NOT_SUPPORTED_WITH_REPLICATION: WIN32_ERROR = 497u32; +pub const ERROR_NOT_SUPPORTED_WITH_SNAPSHOT: WIN32_ERROR = 504u32; +pub const ERROR_NOT_SUPPORTED_WITH_VIRTUALIZATION: WIN32_ERROR = 505u32; +pub const ERROR_NOT_TINY_STREAM: WIN32_ERROR = 598u32; +pub const ERROR_NO_ACE_CONDITION: WIN32_ERROR = 804u32; +pub const ERROR_NO_ASSOCIATION: WIN32_ERROR = 1155u32; +pub const ERROR_NO_BYPASSIO_DRIVER_SUPPORT: WIN32_ERROR = 494u32; +pub const ERROR_NO_CALLBACK_ACTIVE: WIN32_ERROR = 614u32; +pub const ERROR_NO_DATA: WIN32_ERROR = 232u32; +pub const ERROR_NO_DATA_DETECTED: WIN32_ERROR = 1104u32; +pub const ERROR_NO_EFS: WIN32_ERROR = 6004u32; +pub const ERROR_NO_EVENT_PAIR: WIN32_ERROR = 580u32; +pub const ERROR_NO_GUID_TRANSLATION: WIN32_ERROR = 560u32; +pub const ERROR_NO_IMPERSONATION_TOKEN: WIN32_ERROR = 1309u32; +pub const ERROR_NO_INHERITANCE: WIN32_ERROR = 1391u32; +pub const ERROR_NO_LOGON_SERVERS: WIN32_ERROR = 1311u32; +pub const ERROR_NO_LOG_SPACE: WIN32_ERROR = 1019u32; +pub const ERROR_NO_MATCH: WIN32_ERROR = 1169u32; +pub const ERROR_NO_MEDIA_IN_DRIVE: WIN32_ERROR = 1112u32; +pub const ERROR_NO_MORE_DEVICES: WIN32_ERROR = 1248u32; +pub const ERROR_NO_MORE_FILES: WIN32_ERROR = 18u32; +pub const ERROR_NO_MORE_ITEMS: WIN32_ERROR = 259u32; +pub const ERROR_NO_MORE_MATCHES: WIN32_ERROR = 626u32; +pub const ERROR_NO_MORE_SEARCH_HANDLES: WIN32_ERROR = 113u32; +pub const ERROR_NO_MORE_USER_HANDLES: WIN32_ERROR = 1158u32; +pub const ERROR_NO_NETWORK: WIN32_ERROR = 1222u32; +pub const ERROR_NO_NET_OR_BAD_PATH: WIN32_ERROR = 1203u32; +pub const ERROR_NO_NVRAM_RESOURCES: WIN32_ERROR = 1470u32; +pub const ERROR_NO_PAGEFILE: WIN32_ERROR = 578u32; +pub const ERROR_NO_PHYSICALLY_ALIGNED_FREE_SPACE_FOUND: WIN32_ERROR = 408u32; +pub const ERROR_NO_PROC_SLOTS: WIN32_ERROR = 89u32; +pub const ERROR_NO_PROMOTION_ACTIVE: WIN32_ERROR = 8222u32; +pub const ERROR_NO_QUOTAS_FOR_ACCOUNT: WIN32_ERROR = 1302u32; +pub const ERROR_NO_RANGES_PROCESSED: WIN32_ERROR = 312u32; +pub const ERROR_NO_RECOVERY_POLICY: WIN32_ERROR = 6003u32; +pub const ERROR_NO_RECOVERY_PROGRAM: WIN32_ERROR = 1082u32; +pub const ERROR_NO_SCROLLBARS: WIN32_ERROR = 1447u32; +pub const ERROR_NO_SECRETS: WIN32_ERROR = 8620u32; +pub const ERROR_NO_SECURITY_ON_OBJECT: WIN32_ERROR = 1350u32; +pub const ERROR_NO_SHUTDOWN_IN_PROGRESS: WIN32_ERROR = 1116u32; +pub const ERROR_NO_SIGNAL_SENT: WIN32_ERROR = 205u32; +pub const ERROR_NO_SITENAME: WIN32_ERROR = 1919u32; +pub const ERROR_NO_SITE_SETTINGS_OBJECT: WIN32_ERROR = 8619u32; +pub const ERROR_NO_SPOOL_SPACE: WIN32_ERROR = 62u32; +pub const ERROR_NO_SUCH_ALIAS: WIN32_ERROR = 1376u32; +pub const ERROR_NO_SUCH_DEVICE: WIN32_ERROR = 433u32; +pub const ERROR_NO_SUCH_DOMAIN: WIN32_ERROR = 1355u32; +pub const ERROR_NO_SUCH_GROUP: WIN32_ERROR = 1319u32; +pub const ERROR_NO_SUCH_LOGON_SESSION: WIN32_ERROR = 1312u32; +pub const ERROR_NO_SUCH_MEMBER: WIN32_ERROR = 1387u32; +pub const ERROR_NO_SUCH_PACKAGE: WIN32_ERROR = 1364u32; +pub const ERROR_NO_SUCH_PRIVILEGE: WIN32_ERROR = 1313u32; +pub const ERROR_NO_SUCH_SITE: WIN32_ERROR = 1249u32; +pub const ERROR_NO_SUCH_USER: WIN32_ERROR = 1317u32; +pub const ERROR_NO_SYSTEM_MENU: WIN32_ERROR = 1437u32; +pub const ERROR_NO_SYSTEM_RESOURCES: WIN32_ERROR = 1450u32; +pub const ERROR_NO_TASK_QUEUE: WIN32_ERROR = 427u32; +pub const ERROR_NO_TOKEN: WIN32_ERROR = 1008u32; +pub const ERROR_NO_TRACKING_SERVICE: WIN32_ERROR = 1172u32; +pub const ERROR_NO_TRUST_LSA_SECRET: WIN32_ERROR = 1786u32; +pub const ERROR_NO_TRUST_SAM_ACCOUNT: WIN32_ERROR = 1787u32; +pub const ERROR_NO_UNICODE_TRANSLATION: WIN32_ERROR = 1113u32; +pub const ERROR_NO_USER_KEYS: WIN32_ERROR = 6006u32; +pub const ERROR_NO_USER_SESSION_KEY: WIN32_ERROR = 1394u32; +pub const ERROR_NO_VOLUME_ID: WIN32_ERROR = 1173u32; +pub const ERROR_NO_VOLUME_LABEL: WIN32_ERROR = 125u32; +pub const ERROR_NO_WILDCARD_CHARACTERS: WIN32_ERROR = 1417u32; +pub const ERROR_NO_WORK_DONE: WIN32_ERROR = 235u32; +pub const ERROR_NO_WRITABLE_DC_FOUND: WIN32_ERROR = 8621u32; +pub const ERROR_NO_YIELD_PERFORMED: WIN32_ERROR = 721u32; +pub const ERROR_NTLM_BLOCKED: WIN32_ERROR = 1937u32; +pub const ERROR_NT_CROSS_ENCRYPTION_REQUIRED: WIN32_ERROR = 1386u32; +pub const ERROR_NULL_LM_PASSWORD: WIN32_ERROR = 1304u32; +pub const ERROR_OBJECT_IS_IMMUTABLE: WIN32_ERROR = 4449u32; +pub const ERROR_OBJECT_NAME_EXISTS: WIN32_ERROR = 698u32; +pub const ERROR_OBJECT_NOT_EXTERNALLY_BACKED: WIN32_ERROR = 342u32; +pub const ERROR_OFFLOAD_READ_FILE_NOT_SUPPORTED: WIN32_ERROR = 4442u32; +pub const ERROR_OFFLOAD_READ_FLT_NOT_SUPPORTED: WIN32_ERROR = 4440u32; +pub const ERROR_OFFLOAD_WRITE_FILE_NOT_SUPPORTED: WIN32_ERROR = 4443u32; +pub const ERROR_OFFLOAD_WRITE_FLT_NOT_SUPPORTED: WIN32_ERROR = 4441u32; +pub const ERROR_OFFSET_ALIGNMENT_VIOLATION: WIN32_ERROR = 327u32; +pub const ERROR_OLD_WIN_VERSION: WIN32_ERROR = 1150u32; +pub const ERROR_ONLY_IF_CONNECTED: WIN32_ERROR = 1251u32; +pub const ERROR_OPEN_FAILED: WIN32_ERROR = 110u32; +pub const ERROR_OPEN_FILES: WIN32_ERROR = 2401u32; +pub const ERROR_OPERATION_ABORTED: WIN32_ERROR = 995u32; +pub const ERROR_OPERATION_IN_PROGRESS: WIN32_ERROR = 329u32; +pub const ERROR_OPLOCK_BREAK_IN_PROGRESS: WIN32_ERROR = 742u32; +pub const ERROR_OPLOCK_HANDLE_CLOSED: WIN32_ERROR = 803u32; +pub const ERROR_OPLOCK_NOT_GRANTED: WIN32_ERROR = 300u32; +pub const ERROR_OPLOCK_SWITCHED_TO_NEW_HANDLE: WIN32_ERROR = 800u32; +pub const ERROR_ORPHAN_NAME_EXHAUSTED: WIN32_ERROR = 799u32; +pub const ERROR_OUTOFMEMORY: WIN32_ERROR = 14u32; +pub const ERROR_OUT_OF_PAPER: WIN32_ERROR = 28u32; +pub const ERROR_OUT_OF_STRUCTURES: WIN32_ERROR = 84u32; +pub const ERROR_OVERRIDE_NOCHANGES: WIN32_ERROR = 1252u32; +pub const ERROR_PAGED_SYSTEM_RESOURCES: WIN32_ERROR = 1452u32; +pub const ERROR_PAGEFILE_CREATE_FAILED: WIN32_ERROR = 576u32; +pub const ERROR_PAGEFILE_NOT_SUPPORTED: WIN32_ERROR = 491u32; +pub const ERROR_PAGEFILE_QUOTA: WIN32_ERROR = 1454u32; +pub const ERROR_PAGEFILE_QUOTA_EXCEEDED: WIN32_ERROR = 567u32; +pub const ERROR_PAGE_FAULT_COPY_ON_WRITE: WIN32_ERROR = 749u32; +pub const ERROR_PAGE_FAULT_DEMAND_ZERO: WIN32_ERROR = 748u32; +pub const ERROR_PAGE_FAULT_GUARD_PAGE: WIN32_ERROR = 750u32; +pub const ERROR_PAGE_FAULT_PAGING_FILE: WIN32_ERROR = 751u32; +pub const ERROR_PAGE_FAULT_TRANSITION: WIN32_ERROR = 747u32; +pub const ERROR_PARAMETER_QUOTA_EXCEEDED: WIN32_ERROR = 1283u32; +pub const ERROR_PARTIAL_COPY: WIN32_ERROR = 299u32; +pub const ERROR_PARTITION_FAILURE: WIN32_ERROR = 1105u32; +pub const ERROR_PARTITION_TERMINATING: WIN32_ERROR = 1184u32; +pub const ERROR_PASSWORD_CHANGE_REQUIRED: WIN32_ERROR = 1938u32; +pub const ERROR_PASSWORD_EXPIRED: WIN32_ERROR = 1330u32; +pub const ERROR_PASSWORD_MUST_CHANGE: WIN32_ERROR = 1907u32; +pub const ERROR_PASSWORD_RESTRICTION: WIN32_ERROR = 1325u32; +pub const ERROR_PATCH_MANAGED_ADVERTISED_PRODUCT: WIN32_ERROR = 1651u32; +pub const ERROR_PATCH_NO_SEQUENCE: WIN32_ERROR = 1648u32; +pub const ERROR_PATCH_PACKAGE_INVALID: WIN32_ERROR = 1636u32; +pub const ERROR_PATCH_PACKAGE_OPEN_FAILED: WIN32_ERROR = 1635u32; +pub const ERROR_PATCH_PACKAGE_REJECTED: WIN32_ERROR = 1643u32; +pub const ERROR_PATCH_PACKAGE_UNSUPPORTED: WIN32_ERROR = 1637u32; +pub const ERROR_PATCH_REMOVAL_DISALLOWED: WIN32_ERROR = 1649u32; +pub const ERROR_PATCH_REMOVAL_UNSUPPORTED: WIN32_ERROR = 1646u32; +pub const ERROR_PATCH_TARGET_NOT_FOUND: WIN32_ERROR = 1642u32; +pub const ERROR_PATH_BUSY: WIN32_ERROR = 148u32; +pub const ERROR_PATH_NOT_FOUND: WIN32_ERROR = 3u32; +pub const ERROR_PER_USER_TRUST_QUOTA_EXCEEDED: WIN32_ERROR = 1932u32; +pub const ERROR_PIPE_BUSY: WIN32_ERROR = 231u32; +pub const ERROR_PIPE_CONNECTED: WIN32_ERROR = 535u32; +pub const ERROR_PIPE_LISTENING: WIN32_ERROR = 536u32; +pub const ERROR_PIPE_LOCAL: WIN32_ERROR = 229u32; +pub const ERROR_PIPE_NOT_CONNECTED: WIN32_ERROR = 233u32; +pub const ERROR_PKINIT_FAILURE: WIN32_ERROR = 1263u32; +pub const ERROR_PLUGPLAY_QUERY_VETOED: WIN32_ERROR = 683u32; +pub const ERROR_PNP_BAD_MPS_TABLE: WIN32_ERROR = 671u32; +pub const ERROR_PNP_INVALID_ID: WIN32_ERROR = 674u32; +pub const ERROR_PNP_IRQ_TRANSLATION_FAILED: WIN32_ERROR = 673u32; +pub const ERROR_PNP_QUERY_REMOVE_DEVICE_TIMEOUT: WIN32_ERROR = 480u32; +pub const ERROR_PNP_QUERY_REMOVE_RELATED_DEVICE_TIMEOUT: WIN32_ERROR = 481u32; +pub const ERROR_PNP_QUERY_REMOVE_UNRELATED_DEVICE_TIMEOUT: WIN32_ERROR = 482u32; +pub const ERROR_PNP_REBOOT_REQUIRED: WIN32_ERROR = 638u32; +pub const ERROR_PNP_RESTART_ENUMERATION: WIN32_ERROR = 636u32; +pub const ERROR_PNP_TRANSLATION_FAILED: WIN32_ERROR = 672u32; +pub const ERROR_POINT_NOT_FOUND: WIN32_ERROR = 1171u32; +pub const ERROR_POLICY_OBJECT_NOT_FOUND: WIN32_ERROR = 8219u32; +pub const ERROR_POLICY_ONLY_IN_DS: WIN32_ERROR = 8220u32; +pub const ERROR_POPUP_ALREADY_ACTIVE: WIN32_ERROR = 1446u32; +pub const ERROR_PORT_MESSAGE_TOO_LONG: WIN32_ERROR = 546u32; +pub const ERROR_PORT_NOT_SET: WIN32_ERROR = 642u32; +pub const ERROR_PORT_UNREACHABLE: WIN32_ERROR = 1234u32; +pub const ERROR_POSSIBLE_DEADLOCK: WIN32_ERROR = 1131u32; +pub const ERROR_POTENTIAL_FILE_FOUND: WIN32_ERROR = 1180u32; +pub const ERROR_PREDEFINED_HANDLE: WIN32_ERROR = 714u32; +pub const ERROR_PRIMARY_TRANSPORT_CONNECT_FAILED: WIN32_ERROR = 746u32; +pub const ERROR_PRINTER_ALREADY_EXISTS: WIN32_ERROR = 1802u32; +pub const ERROR_PRINTER_DELETED: WIN32_ERROR = 1905u32; +pub const ERROR_PRINTER_DRIVER_ALREADY_INSTALLED: WIN32_ERROR = 1795u32; +pub const ERROR_PRINTQ_FULL: WIN32_ERROR = 61u32; +pub const ERROR_PRINT_CANCELLED: WIN32_ERROR = 63u32; +pub const ERROR_PRIVATE_DIALOG_INDEX: WIN32_ERROR = 1415u32; +pub const ERROR_PRIVILEGE_NOT_HELD: WIN32_ERROR = 1314u32; +pub const ERROR_PROCESS_ABORTED: WIN32_ERROR = 1067u32; +pub const ERROR_PROCESS_IN_JOB: WIN32_ERROR = 760u32; +pub const ERROR_PROCESS_IS_PROTECTED: WIN32_ERROR = 1293u32; +pub const ERROR_PROCESS_MODE_ALREADY_BACKGROUND: WIN32_ERROR = 402u32; +pub const ERROR_PROCESS_MODE_NOT_BACKGROUND: WIN32_ERROR = 403u32; +pub const ERROR_PROCESS_NOT_IN_JOB: WIN32_ERROR = 759u32; +pub const ERROR_PROC_NOT_FOUND: WIN32_ERROR = 127u32; +pub const ERROR_PRODUCT_UNINSTALLED: WIN32_ERROR = 1614u32; +pub const ERROR_PRODUCT_VERSION: WIN32_ERROR = 1638u32; +pub const ERROR_PROFILING_AT_LIMIT: WIN32_ERROR = 553u32; +pub const ERROR_PROFILING_NOT_STARTED: WIN32_ERROR = 550u32; +pub const ERROR_PROFILING_NOT_STOPPED: WIN32_ERROR = 551u32; +pub const ERROR_PROMOTION_ACTIVE: WIN32_ERROR = 8221u32; +pub const ERROR_PROTOCOL_UNREACHABLE: WIN32_ERROR = 1233u32; +pub const ERROR_PWD_HISTORY_CONFLICT: WIN32_ERROR = 617u32; +pub const ERROR_PWD_TOO_LONG: WIN32_ERROR = 657u32; +pub const ERROR_PWD_TOO_RECENT: WIN32_ERROR = 616u32; +pub const ERROR_PWD_TOO_SHORT: WIN32_ERROR = 615u32; +pub const ERROR_QUOTA_ACTIVITY: WIN32_ERROR = 810u32; +pub const ERROR_QUOTA_LIST_INCONSISTENT: WIN32_ERROR = 621u32; +pub const ERROR_RANGE_LIST_CONFLICT: WIN32_ERROR = 627u32; +pub const ERROR_RANGE_NOT_FOUND: WIN32_ERROR = 644u32; +pub const ERROR_READ_FAULT: WIN32_ERROR = 30u32; +pub const ERROR_RECEIVE_EXPEDITED: WIN32_ERROR = 708u32; +pub const ERROR_RECEIVE_PARTIAL: WIN32_ERROR = 707u32; +pub const ERROR_RECEIVE_PARTIAL_EXPEDITED: WIN32_ERROR = 709u32; +pub const ERROR_RECOVERY_FAILURE: WIN32_ERROR = 1279u32; +pub const ERROR_REDIRECTOR_HAS_OPEN_HANDLES: WIN32_ERROR = 1794u32; +pub const ERROR_REDIR_PAUSED: WIN32_ERROR = 72u32; +pub const ERROR_REGISTRY_CORRUPT: WIN32_ERROR = 1015u32; +pub const ERROR_REGISTRY_HIVE_RECOVERED: WIN32_ERROR = 685u32; +pub const ERROR_REGISTRY_IO_FAILED: WIN32_ERROR = 1016u32; +pub const ERROR_REGISTRY_QUOTA_LIMIT: WIN32_ERROR = 613u32; +pub const ERROR_REGISTRY_RECOVERED: WIN32_ERROR = 1014u32; +pub const ERROR_REG_NAT_CONSUMPTION: WIN32_ERROR = 1261u32; +pub const ERROR_RELOC_CHAIN_XEEDS_SEGLIM: WIN32_ERROR = 201u32; +pub const ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED: WIN32_ERROR = 1936u32; +pub const ERROR_REMOTE_SESSION_LIMIT_EXCEEDED: WIN32_ERROR = 1220u32; +pub const ERROR_REMOTE_STORAGE_MEDIA_ERROR: WIN32_ERROR = 4352u32; +pub const ERROR_REMOTE_STORAGE_NOT_ACTIVE: WIN32_ERROR = 4351u32; +pub const ERROR_REM_NOT_LIST: WIN32_ERROR = 51u32; +pub const ERROR_REPARSE: WIN32_ERROR = 741u32; +pub const ERROR_REPARSE_ATTRIBUTE_CONFLICT: WIN32_ERROR = 4391u32; +pub const ERROR_REPARSE_OBJECT: WIN32_ERROR = 755u32; +pub const ERROR_REPARSE_POINT_ENCOUNTERED: WIN32_ERROR = 4395u32; +pub const ERROR_REPARSE_TAG_INVALID: WIN32_ERROR = 4393u32; +pub const ERROR_REPARSE_TAG_MISMATCH: WIN32_ERROR = 4394u32; +pub const ERROR_REPLY_MESSAGE_MISMATCH: WIN32_ERROR = 595u32; +pub const ERROR_REQUEST_ABORTED: WIN32_ERROR = 1235u32; +pub const ERROR_REQUEST_OUT_OF_SEQUENCE: WIN32_ERROR = 776u32; +pub const ERROR_REQUEST_PAUSED: WIN32_ERROR = 3050u32; +pub const ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION: WIN32_ERROR = 1459u32; +pub const ERROR_REQ_NOT_ACCEP: WIN32_ERROR = 71u32; +pub const ERROR_RESIDENT_FILE_NOT_SUPPORTED: WIN32_ERROR = 334u32; +pub const ERROR_RESOURCE_CALL_TIMED_OUT: WIN32_ERROR = 5910u32; +pub const ERROR_RESOURCE_DATA_NOT_FOUND: WIN32_ERROR = 1812u32; +pub const ERROR_RESOURCE_LANG_NOT_FOUND: WIN32_ERROR = 1815u32; +pub const ERROR_RESOURCE_NAME_NOT_FOUND: WIN32_ERROR = 1814u32; +pub const ERROR_RESOURCE_REQUIREMENTS_CHANGED: WIN32_ERROR = 756u32; +pub const ERROR_RESOURCE_TYPE_NOT_FOUND: WIN32_ERROR = 1813u32; +pub const ERROR_RESTART_APPLICATION: WIN32_ERROR = 1467u32; +pub const ERROR_RESUME_HIBERNATION: WIN32_ERROR = 727u32; +pub const ERROR_RETRY: WIN32_ERROR = 1237u32; +pub const ERROR_RETURN_ADDRESS_HIJACK_ATTEMPT: WIN32_ERROR = 1662u32; +pub const ERROR_REVISION_MISMATCH: WIN32_ERROR = 1306u32; +pub const ERROR_RING2SEG_MUST_BE_MOVABLE: WIN32_ERROR = 200u32; +pub const ERROR_RING2_STACK_IN_USE: WIN32_ERROR = 207u32; +pub const ERROR_RMODE_APP: WIN32_ERROR = 1153u32; +pub const ERROR_ROWSNOTRELEASED: WIN32_ERROR = 772u32; +pub const ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT: WIN32_ERROR = 15403u32; +pub const ERROR_RUNLEVEL_SWITCH_TIMEOUT: WIN32_ERROR = 15402u32; +pub const ERROR_RWRAW_ENCRYPTED_FILE_NOT_ENCRYPTED: WIN32_ERROR = 410u32; +pub const ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_FILEOFFSET: WIN32_ERROR = 411u32; +pub const ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_FILERANGE: WIN32_ERROR = 412u32; +pub const ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_PARAMETER: WIN32_ERROR = 413u32; +pub const ERROR_RXACT_COMMITTED: WIN32_ERROR = 744u32; +pub const ERROR_RXACT_COMMIT_FAILURE: WIN32_ERROR = 1370u32; +pub const ERROR_RXACT_COMMIT_NECESSARY: WIN32_ERROR = 678u32; +pub const ERROR_RXACT_INVALID_STATE: WIN32_ERROR = 1369u32; +pub const ERROR_RXACT_STATE_CREATED: WIN32_ERROR = 701u32; +pub const ERROR_SAME_DRIVE: WIN32_ERROR = 143u32; +pub const ERROR_SAM_INIT_FAILURE: WIN32_ERROR = 8541u32; +pub const ERROR_SCOPE_NOT_FOUND: WIN32_ERROR = 318u32; +pub const ERROR_SCREEN_ALREADY_LOCKED: WIN32_ERROR = 1440u32; +pub const ERROR_SCRUB_DATA_DISABLED: WIN32_ERROR = 332u32; +pub const ERROR_SECRET_TOO_LONG: WIN32_ERROR = 1382u32; +pub const ERROR_SECTION_DIRECT_MAP_ONLY: WIN32_ERROR = 819u32; +pub const ERROR_SECTOR_NOT_FOUND: WIN32_ERROR = 27u32; +pub const ERROR_SECURITY_DENIES_OPERATION: WIN32_ERROR = 447u32; +pub const ERROR_SECURITY_STREAM_IS_INCONSISTENT: WIN32_ERROR = 306u32; +pub const ERROR_SEEK: WIN32_ERROR = 25u32; +pub const ERROR_SEEK_ON_DEVICE: WIN32_ERROR = 132u32; +pub const ERROR_SEGMENT_NOTIFICATION: WIN32_ERROR = 702u32; +pub const ERROR_SEM_IS_SET: WIN32_ERROR = 102u32; +pub const ERROR_SEM_NOT_FOUND: WIN32_ERROR = 187u32; +pub const ERROR_SEM_OWNER_DIED: WIN32_ERROR = 105u32; +pub const ERROR_SEM_TIMEOUT: WIN32_ERROR = 121u32; +pub const ERROR_SEM_USER_LIMIT: WIN32_ERROR = 106u32; +pub const ERROR_SERIAL_NO_DEVICE: WIN32_ERROR = 1118u32; +pub const ERROR_SERVER_DISABLED: WIN32_ERROR = 1341u32; +pub const ERROR_SERVER_HAS_OPEN_HANDLES: WIN32_ERROR = 1811u32; +pub const ERROR_SERVER_NOT_DISABLED: WIN32_ERROR = 1342u32; +pub const ERROR_SERVER_SHUTDOWN_IN_PROGRESS: WIN32_ERROR = 1255u32; +pub const ERROR_SERVER_SID_MISMATCH: WIN32_ERROR = 628u32; +pub const ERROR_SERVER_TRANSPORT_CONFLICT: WIN32_ERROR = 816u32; +pub const ERROR_SERVICE_ALREADY_RUNNING: WIN32_ERROR = 1056u32; +pub const ERROR_SERVICE_CANNOT_ACCEPT_CTRL: WIN32_ERROR = 1061u32; +pub const ERROR_SERVICE_DATABASE_LOCKED: WIN32_ERROR = 1055u32; +pub const ERROR_SERVICE_DEPENDENCY_DELETED: WIN32_ERROR = 1075u32; +pub const ERROR_SERVICE_DEPENDENCY_FAIL: WIN32_ERROR = 1068u32; +pub const ERROR_SERVICE_DISABLED: WIN32_ERROR = 1058u32; +pub const ERROR_SERVICE_DOES_NOT_EXIST: WIN32_ERROR = 1060u32; +pub const ERROR_SERVICE_EXISTS: WIN32_ERROR = 1073u32; +pub const ERROR_SERVICE_LOGON_FAILED: WIN32_ERROR = 1069u32; +pub const ERROR_SERVICE_MARKED_FOR_DELETE: WIN32_ERROR = 1072u32; +pub const ERROR_SERVICE_NEVER_STARTED: WIN32_ERROR = 1077u32; +pub const ERROR_SERVICE_NOTIFICATION: WIN32_ERROR = 716u32; +pub const ERROR_SERVICE_NOTIFY_CLIENT_LAGGING: WIN32_ERROR = 1294u32; +pub const ERROR_SERVICE_NOT_ACTIVE: WIN32_ERROR = 1062u32; +pub const ERROR_SERVICE_NOT_FOUND: WIN32_ERROR = 1243u32; +pub const ERROR_SERVICE_NOT_IN_EXE: WIN32_ERROR = 1083u32; +pub const ERROR_SERVICE_NO_THREAD: WIN32_ERROR = 1054u32; +pub const ERROR_SERVICE_REQUEST_TIMEOUT: WIN32_ERROR = 1053u32; +pub const ERROR_SERVICE_SPECIFIC_ERROR: WIN32_ERROR = 1066u32; +pub const ERROR_SERVICE_START_HANG: WIN32_ERROR = 1070u32; +pub const ERROR_SESSION_CREDENTIAL_CONFLICT: WIN32_ERROR = 1219u32; +pub const ERROR_SESSION_KEY_TOO_SHORT: WIN32_ERROR = 501u32; +pub const ERROR_SETCOUNT_ON_BAD_LB: WIN32_ERROR = 1433u32; +pub const ERROR_SETMARK_DETECTED: WIN32_ERROR = 1103u32; +pub const ERROR_SET_CONTEXT_DENIED: WIN32_ERROR = 1660u32; +pub const ERROR_SET_NOT_FOUND: WIN32_ERROR = 1170u32; +pub const ERROR_SET_POWER_STATE_FAILED: WIN32_ERROR = 1141u32; +pub const ERROR_SET_POWER_STATE_VETOED: WIN32_ERROR = 1140u32; +pub const ERROR_SHARED_POLICY: WIN32_ERROR = 8218u32; +pub const ERROR_SHARING_BUFFER_EXCEEDED: WIN32_ERROR = 36u32; +pub const ERROR_SHARING_PAUSED: WIN32_ERROR = 70u32; +pub const ERROR_SHARING_VIOLATION: WIN32_ERROR = 32u32; +pub const ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME: WIN32_ERROR = 305u32; +pub const ERROR_SHUTDOWN_DISKS_NOT_IN_MAINTENANCE_MODE: WIN32_ERROR = 1192u32; +pub const ERROR_SHUTDOWN_IN_PROGRESS: WIN32_ERROR = 1115u32; +pub const ERROR_SHUTDOWN_IS_SCHEDULED: WIN32_ERROR = 1190u32; +pub const ERROR_SHUTDOWN_USERS_LOGGED_ON: WIN32_ERROR = 1191u32; +pub const ERROR_SIGNAL_PENDING: WIN32_ERROR = 162u32; +pub const ERROR_SIGNAL_REFUSED: WIN32_ERROR = 156u32; +pub const ERROR_SINGLE_INSTANCE_APP: WIN32_ERROR = 1152u32; +pub const ERROR_SMARTCARD_SUBSYSTEM_FAILURE: WIN32_ERROR = 1264u32; +pub const ERROR_SMB1_NOT_AVAILABLE: WIN32_ERROR = 384u32; +pub const ERROR_SMB_GUEST_LOGON_BLOCKED: WIN32_ERROR = 1272u32; +pub const ERROR_SMR_GARBAGE_COLLECTION_REQUIRED: WIN32_ERROR = 4445u32; +pub const ERROR_SOME_NOT_MAPPED: WIN32_ERROR = 1301u32; +pub const ERROR_SOURCE_ELEMENT_EMPTY: WIN32_ERROR = 1160u32; +pub const ERROR_SPARSE_FILE_NOT_SUPPORTED: WIN32_ERROR = 490u32; +pub const ERROR_SPECIAL_ACCOUNT: WIN32_ERROR = 1371u32; +pub const ERROR_SPECIAL_GROUP: WIN32_ERROR = 1372u32; +pub const ERROR_SPECIAL_USER: WIN32_ERROR = 1373u32; +pub const ERROR_SRC_SRV_DLL_LOAD_FAILED: WIN32_ERROR = 428u32; +pub const ERROR_STACK_BUFFER_OVERRUN: WIN32_ERROR = 1282u32; +pub const ERROR_STACK_OVERFLOW: WIN32_ERROR = 1001u32; +pub const ERROR_STACK_OVERFLOW_READ: WIN32_ERROR = 599u32; +pub const ERROR_STOPPED_ON_SYMLINK: WIN32_ERROR = 681u32; +pub const ERROR_STORAGE_LOST_DATA_PERSISTENCE: WIN32_ERROR = 368u32; +pub const ERROR_STORAGE_RESERVE_ALREADY_EXISTS: WIN32_ERROR = 418u32; +pub const ERROR_STORAGE_RESERVE_DOES_NOT_EXIST: WIN32_ERROR = 417u32; +pub const ERROR_STORAGE_RESERVE_ID_INVALID: WIN32_ERROR = 416u32; +pub const ERROR_STORAGE_RESERVE_NOT_EMPTY: WIN32_ERROR = 419u32; +pub const ERROR_STORAGE_STACK_ACCESS_DENIED: WIN32_ERROR = 472u32; +pub const ERROR_STORAGE_TOPOLOGY_ID_MISMATCH: WIN32_ERROR = 345u32; +pub const ERROR_STRICT_CFG_VIOLATION: WIN32_ERROR = 1657u32; +pub const ERROR_SUBST_TO_JOIN: WIN32_ERROR = 141u32; +pub const ERROR_SUBST_TO_SUBST: WIN32_ERROR = 139u32; +pub const ERROR_SUCCESS: WIN32_ERROR = 0u32; +pub const ERROR_SUCCESS_REBOOT_INITIATED: WIN32_ERROR = 1641u32; +pub const ERROR_SWAPERROR: WIN32_ERROR = 999u32; +pub const ERROR_SYMLINK_CLASS_DISABLED: WIN32_ERROR = 1463u32; +pub const ERROR_SYMLINK_NOT_SUPPORTED: WIN32_ERROR = 1464u32; +pub const ERROR_SYNCHRONIZATION_REQUIRED: WIN32_ERROR = 569u32; +pub const ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED: WIN32_ERROR = 1274u32; +pub const ERROR_SYSTEM_HIVE_TOO_LARGE: WIN32_ERROR = 653u32; +pub const ERROR_SYSTEM_IMAGE_BAD_SIGNATURE: WIN32_ERROR = 637u32; +pub const ERROR_SYSTEM_POWERSTATE_COMPLEX_TRANSITION: WIN32_ERROR = 783u32; +pub const ERROR_SYSTEM_POWERSTATE_TRANSITION: WIN32_ERROR = 782u32; +pub const ERROR_SYSTEM_PROCESS_TERMINATED: WIN32_ERROR = 591u32; +pub const ERROR_SYSTEM_SHUTDOWN: WIN32_ERROR = 641u32; +pub const ERROR_SYSTEM_TRACE: WIN32_ERROR = 150u32; +pub const ERROR_THREAD_1_INACTIVE: WIN32_ERROR = 210u32; +pub const ERROR_THREAD_ALREADY_IN_TASK: WIN32_ERROR = 1552u32; +pub const ERROR_THREAD_MODE_ALREADY_BACKGROUND: WIN32_ERROR = 400u32; +pub const ERROR_THREAD_MODE_NOT_BACKGROUND: WIN32_ERROR = 401u32; +pub const ERROR_THREAD_NOT_IN_PROCESS: WIN32_ERROR = 566u32; +pub const ERROR_THREAD_WAS_SUSPENDED: WIN32_ERROR = 699u32; +pub const ERROR_TIMEOUT: WIN32_ERROR = 1460u32; +pub const ERROR_TIMER_NOT_CANCELED: WIN32_ERROR = 541u32; +pub const ERROR_TIMER_RESOLUTION_NOT_SET: WIN32_ERROR = 607u32; +pub const ERROR_TIMER_RESUME_IGNORED: WIN32_ERROR = 722u32; +pub const ERROR_TIME_SENSITIVE_THREAD: WIN32_ERROR = 422u32; +pub const ERROR_TIME_SKEW: WIN32_ERROR = 1398u32; +pub const ERROR_TLW_WITH_WSCHILD: WIN32_ERROR = 1406u32; +pub const ERROR_TOKEN_ALREADY_IN_USE: WIN32_ERROR = 1375u32; +pub const ERROR_TOO_MANY_CMDS: WIN32_ERROR = 56u32; +pub const ERROR_TOO_MANY_CONTEXT_IDS: WIN32_ERROR = 1384u32; +pub const ERROR_TOO_MANY_DESCRIPTORS: WIN32_ERROR = 331u32; +pub const ERROR_TOO_MANY_LINKS: WIN32_ERROR = 1142u32; +pub const ERROR_TOO_MANY_LUIDS_REQUESTED: WIN32_ERROR = 1333u32; +pub const ERROR_TOO_MANY_MODULES: WIN32_ERROR = 214u32; +pub const ERROR_TOO_MANY_MUXWAITERS: WIN32_ERROR = 152u32; +pub const ERROR_TOO_MANY_NAMES: WIN32_ERROR = 68u32; +pub const ERROR_TOO_MANY_OPEN_FILES: WIN32_ERROR = 4u32; +pub const ERROR_TOO_MANY_POSTS: WIN32_ERROR = 298u32; +pub const ERROR_TOO_MANY_SECRETS: WIN32_ERROR = 1381u32; +pub const ERROR_TOO_MANY_SEMAPHORES: WIN32_ERROR = 100u32; +pub const ERROR_TOO_MANY_SEM_REQUESTS: WIN32_ERROR = 103u32; +pub const ERROR_TOO_MANY_SESS: WIN32_ERROR = 69u32; +pub const ERROR_TOO_MANY_SIDS: WIN32_ERROR = 1389u32; +pub const ERROR_TOO_MANY_TCBS: WIN32_ERROR = 155u32; +pub const ERROR_TOO_MANY_THREADS: WIN32_ERROR = 565u32; +pub const ERROR_TRANSLATION_COMPLETE: WIN32_ERROR = 757u32; +pub const ERROR_TRUSTED_DOMAIN_FAILURE: WIN32_ERROR = 1788u32; +pub const ERROR_TRUSTED_RELATIONSHIP_FAILURE: WIN32_ERROR = 1789u32; +pub const ERROR_TRUST_FAILURE: WIN32_ERROR = 1790u32; +pub const ERROR_UNABLE_TO_LOCK_MEDIA: WIN32_ERROR = 1108u32; +pub const ERROR_UNABLE_TO_MOVE_REPLACEMENT: WIN32_ERROR = 1176u32; +pub const ERROR_UNABLE_TO_MOVE_REPLACEMENT_2: WIN32_ERROR = 1177u32; +pub const ERROR_UNABLE_TO_REMOVE_REPLACED: WIN32_ERROR = 1175u32; +pub const ERROR_UNABLE_TO_UNLOAD_MEDIA: WIN32_ERROR = 1109u32; +pub const ERROR_UNDEFINED_CHARACTER: WIN32_ERROR = 583u32; +pub const ERROR_UNDEFINED_SCOPE: WIN32_ERROR = 319u32; +pub const ERROR_UNEXPECTED_MM_CREATE_ERR: WIN32_ERROR = 556u32; +pub const ERROR_UNEXPECTED_MM_EXTEND_ERR: WIN32_ERROR = 558u32; +pub const ERROR_UNEXPECTED_MM_MAP_ERROR: WIN32_ERROR = 557u32; +pub const ERROR_UNEXPECTED_NTCACHEMANAGER_ERROR: WIN32_ERROR = 443u32; +pub const ERROR_UNEXP_NET_ERR: WIN32_ERROR = 59u32; +pub const ERROR_UNHANDLED_EXCEPTION: WIN32_ERROR = 574u32; +pub const ERROR_UNIDENTIFIED_ERROR: WIN32_ERROR = 1287u32; +pub const ERROR_UNKNOWN_COMPONENT: WIN32_ERROR = 1607u32; +pub const ERROR_UNKNOWN_FEATURE: WIN32_ERROR = 1606u32; +pub const ERROR_UNKNOWN_PATCH: WIN32_ERROR = 1647u32; +pub const ERROR_UNKNOWN_PORT: WIN32_ERROR = 1796u32; +pub const ERROR_UNKNOWN_PRINTER_DRIVER: WIN32_ERROR = 1797u32; +pub const ERROR_UNKNOWN_PRINTPROCESSOR: WIN32_ERROR = 1798u32; +pub const ERROR_UNKNOWN_PRODUCT: WIN32_ERROR = 1605u32; +pub const ERROR_UNKNOWN_PROPERTY: WIN32_ERROR = 1608u32; +pub const ERROR_UNKNOWN_REVISION: WIN32_ERROR = 1305u32; +pub const ERROR_UNRECOGNIZED_MEDIA: WIN32_ERROR = 1785u32; +pub const ERROR_UNRECOGNIZED_VOLUME: WIN32_ERROR = 1005u32; +pub const ERROR_UNSATISFIED_DEPENDENCIES: WIN32_ERROR = 441u32; +pub const ERROR_UNSUPPORTED_COMPRESSION: WIN32_ERROR = 618u32; +pub const ERROR_UNSUPPORTED_TYPE: WIN32_ERROR = 1630u32; +pub const ERROR_UNTRUSTED_MOUNT_POINT: WIN32_ERROR = 448u32; +pub const ERROR_UNWIND: WIN32_ERROR = 542u32; +pub const ERROR_UNWIND_CONSOLIDATE: WIN32_ERROR = 684u32; +pub const ERROR_USER_APC: WIN32_ERROR = 737u32; +pub const ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED: WIN32_ERROR = 1934u32; +pub const ERROR_USER_EXISTS: WIN32_ERROR = 1316u32; +pub const ERROR_USER_MAPPED_FILE: WIN32_ERROR = 1224u32; +pub const ERROR_USER_PROFILE_LOAD: WIN32_ERROR = 500u32; +pub const ERROR_VALIDATE_CONTINUE: WIN32_ERROR = 625u32; +pub const ERROR_VC_DISCONNECTED: WIN32_ERROR = 240u32; +pub const ERROR_VDM_DISALLOWED: WIN32_ERROR = 1286u32; +pub const ERROR_VDM_HARD_ERROR: WIN32_ERROR = 593u32; +pub const ERROR_VERIFIER_STOP: WIN32_ERROR = 537u32; +pub const ERROR_VERSION_PARSE_ERROR: WIN32_ERROR = 777u32; +pub const ERROR_VIRUS_DELETED: WIN32_ERROR = 226u32; +pub const ERROR_VIRUS_INFECTED: WIN32_ERROR = 225u32; +pub const ERROR_VOLSNAP_HIBERNATE_READY: WIN32_ERROR = 761u32; +pub const ERROR_VOLSNAP_PREPARE_HIBERNATE: WIN32_ERROR = 655u32; +pub const ERROR_VOLUME_MOUNTED: WIN32_ERROR = 743u32; +pub const ERROR_VOLUME_NOT_CLUSTER_ALIGNED: WIN32_ERROR = 407u32; +pub const ERROR_VOLUME_NOT_SIS_ENABLED: WIN32_ERROR = 4500u32; +pub const ERROR_VOLUME_NOT_SUPPORTED: WIN32_ERROR = 492u32; +pub const ERROR_VOLUME_NOT_SUPPORT_EFS: WIN32_ERROR = 6014u32; +pub const ERROR_VOLUME_WRITE_ACCESS_DENIED: WIN32_ERROR = 508u32; +pub const ERROR_WAIT_1: WIN32_ERROR = 731u32; +pub const ERROR_WAIT_2: WIN32_ERROR = 732u32; +pub const ERROR_WAIT_3: WIN32_ERROR = 733u32; +pub const ERROR_WAIT_63: WIN32_ERROR = 734u32; +pub const ERROR_WAIT_FOR_OPLOCK: WIN32_ERROR = 765u32; +pub const ERROR_WAIT_NO_CHILDREN: WIN32_ERROR = 128u32; +pub const ERROR_WAKE_SYSTEM: WIN32_ERROR = 730u32; +pub const ERROR_WAKE_SYSTEM_DEBUGGER: WIN32_ERROR = 675u32; +pub const ERROR_WAS_LOCKED: WIN32_ERROR = 717u32; +pub const ERROR_WAS_UNLOCKED: WIN32_ERROR = 715u32; +pub const ERROR_WEAK_WHFBKEY_BLOCKED: WIN32_ERROR = 8651u32; +pub const ERROR_WINDOW_NOT_COMBOBOX: WIN32_ERROR = 1423u32; +pub const ERROR_WINDOW_NOT_DIALOG: WIN32_ERROR = 1420u32; +pub const ERROR_WINDOW_OF_OTHER_THREAD: WIN32_ERROR = 1408u32; +pub const ERROR_WIP_ENCRYPTION_FAILED: WIN32_ERROR = 6023u32; +pub const ERROR_WOF_FILE_RESOURCE_TABLE_CORRUPT: WIN32_ERROR = 4448u32; +pub const ERROR_WOF_WIM_HEADER_CORRUPT: WIN32_ERROR = 4446u32; +pub const ERROR_WOF_WIM_RESOURCE_TABLE_CORRUPT: WIN32_ERROR = 4447u32; +pub const ERROR_WORKING_SET_QUOTA: WIN32_ERROR = 1453u32; +pub const ERROR_WOW_ASSERTION: WIN32_ERROR = 670u32; +pub const ERROR_WRITE_FAULT: WIN32_ERROR = 29u32; +pub const ERROR_WRITE_PROTECT: WIN32_ERROR = 19u32; +pub const ERROR_WRONG_COMPARTMENT: WIN32_ERROR = 1468u32; +pub const ERROR_WRONG_DISK: WIN32_ERROR = 34u32; +pub const ERROR_WRONG_EFS: WIN32_ERROR = 6005u32; +pub const ERROR_WRONG_PASSWORD: WIN32_ERROR = 1323u32; +pub const ERROR_WRONG_TARGET_NAME: WIN32_ERROR = 1396u32; +pub const ERROR_WX86_ERROR: WIN32_ERROR = 540u32; +pub const ERROR_WX86_WARNING: WIN32_ERROR = 539u32; +pub const ERROR_XMLDSIG_ERROR: WIN32_ERROR = 1466u32; +pub const ERROR_XML_PARSE_ERROR: WIN32_ERROR = 1465u32; +pub type EXCEPTION_DISPOSITION = i32; +pub const EXCEPTION_MAXIMUM_PARAMETERS: u32 = 15u32; +#[repr(C)] +pub struct EXCEPTION_RECORD { + pub ExceptionCode: NTSTATUS, + pub ExceptionFlags: u32, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: *mut ::core::ffi::c_void, + pub NumberParameters: u32, + pub ExceptionInformation: [usize; 15], +} +impl ::core::marker::Copy for EXCEPTION_RECORD {} +impl ::core::clone::Clone for EXCEPTION_RECORD { + fn clone(&self) -> Self { + *self + } +} +pub const EXCEPTION_STACK_OVERFLOW: NTSTATUS = -1073741571i32; +pub const EXTENDED_STARTUPINFO_PRESENT: PROCESS_CREATION_FLAGS = 524288u32; +pub const E_NOTIMPL: HRESULT = -2147467263i32; +pub const ExceptionCollidedUnwind: EXCEPTION_DISPOSITION = 3i32; +pub const ExceptionContinueExecution: EXCEPTION_DISPOSITION = 0i32; +pub const ExceptionContinueSearch: EXCEPTION_DISPOSITION = 1i32; +pub const ExceptionNestedException: EXCEPTION_DISPOSITION = 2i32; +pub type FACILITY_CODE = u32; +pub const FACILITY_NT_BIT: FACILITY_CODE = 268435456u32; +pub const FALSE: BOOL = 0i32; +pub type FARPROC = ::core::option::Option isize>; +#[repr(C)] +pub struct FD_SET { + pub fd_count: u32, + pub fd_array: [SOCKET; 64], +} +impl ::core::marker::Copy for FD_SET {} +impl ::core::clone::Clone for FD_SET { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub struct FILETIME { + pub dwLowDateTime: u32, + pub dwHighDateTime: u32, +} +impl ::core::marker::Copy for FILETIME {} +impl ::core::clone::Clone for FILETIME { + fn clone(&self) -> Self { + *self + } +} +pub type FILE_ACCESS_RIGHTS = u32; +pub const FILE_ADD_FILE: FILE_ACCESS_RIGHTS = 2u32; +pub const FILE_ADD_SUBDIRECTORY: FILE_ACCESS_RIGHTS = 4u32; +#[repr(C)] +pub struct FILE_ALLOCATION_INFO { + pub AllocationSize: i64, +} +impl ::core::marker::Copy for FILE_ALLOCATION_INFO {} +impl ::core::clone::Clone for FILE_ALLOCATION_INFO { + fn clone(&self) -> Self { + *self + } +} +pub const FILE_ALL_ACCESS: FILE_ACCESS_RIGHTS = 2032127u32; +pub const FILE_APPEND_DATA: FILE_ACCESS_RIGHTS = 4u32; +pub const FILE_ATTRIBUTE_ARCHIVE: FILE_FLAGS_AND_ATTRIBUTES = 32u32; +pub const FILE_ATTRIBUTE_COMPRESSED: FILE_FLAGS_AND_ATTRIBUTES = 2048u32; +pub const FILE_ATTRIBUTE_DEVICE: FILE_FLAGS_AND_ATTRIBUTES = 64u32; +pub const FILE_ATTRIBUTE_DIRECTORY: FILE_FLAGS_AND_ATTRIBUTES = 16u32; +pub const FILE_ATTRIBUTE_EA: FILE_FLAGS_AND_ATTRIBUTES = 262144u32; +pub const FILE_ATTRIBUTE_ENCRYPTED: FILE_FLAGS_AND_ATTRIBUTES = 16384u32; +pub const FILE_ATTRIBUTE_HIDDEN: FILE_FLAGS_AND_ATTRIBUTES = 2u32; +pub const FILE_ATTRIBUTE_INTEGRITY_STREAM: FILE_FLAGS_AND_ATTRIBUTES = 32768u32; +pub const FILE_ATTRIBUTE_NORMAL: FILE_FLAGS_AND_ATTRIBUTES = 128u32; +pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: FILE_FLAGS_AND_ATTRIBUTES = 8192u32; +pub const FILE_ATTRIBUTE_NO_SCRUB_DATA: FILE_FLAGS_AND_ATTRIBUTES = 131072u32; +pub const FILE_ATTRIBUTE_OFFLINE: FILE_FLAGS_AND_ATTRIBUTES = 4096u32; +pub const FILE_ATTRIBUTE_PINNED: FILE_FLAGS_AND_ATTRIBUTES = 524288u32; +pub const FILE_ATTRIBUTE_READONLY: FILE_FLAGS_AND_ATTRIBUTES = 1u32; +pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS: FILE_FLAGS_AND_ATTRIBUTES = 4194304u32; +pub const FILE_ATTRIBUTE_RECALL_ON_OPEN: FILE_FLAGS_AND_ATTRIBUTES = 262144u32; +pub const FILE_ATTRIBUTE_REPARSE_POINT: FILE_FLAGS_AND_ATTRIBUTES = 1024u32; +pub const FILE_ATTRIBUTE_SPARSE_FILE: FILE_FLAGS_AND_ATTRIBUTES = 512u32; +pub const FILE_ATTRIBUTE_SYSTEM: FILE_FLAGS_AND_ATTRIBUTES = 4u32; +#[repr(C)] +pub struct FILE_ATTRIBUTE_TAG_INFO { + pub FileAttributes: u32, + pub ReparseTag: u32, +} +impl ::core::marker::Copy for FILE_ATTRIBUTE_TAG_INFO {} +impl ::core::clone::Clone for FILE_ATTRIBUTE_TAG_INFO { + fn clone(&self) -> Self { + *self + } +} +pub const FILE_ATTRIBUTE_TEMPORARY: FILE_FLAGS_AND_ATTRIBUTES = 256u32; +pub const FILE_ATTRIBUTE_UNPINNED: FILE_FLAGS_AND_ATTRIBUTES = 1048576u32; +pub const FILE_ATTRIBUTE_VIRTUAL: FILE_FLAGS_AND_ATTRIBUTES = 65536u32; +#[repr(C)] +pub struct FILE_BASIC_INFO { + pub CreationTime: i64, + pub LastAccessTime: i64, + pub LastWriteTime: i64, + pub ChangeTime: i64, + pub FileAttributes: u32, +} +impl ::core::marker::Copy for FILE_BASIC_INFO {} +impl ::core::clone::Clone for FILE_BASIC_INFO { + fn clone(&self) -> Self { + *self + } +} +pub const FILE_BEGIN: SET_FILE_POINTER_MOVE_METHOD = 0u32; +pub const FILE_COMPLETE_IF_OPLOCKED: NTCREATEFILE_CREATE_OPTIONS = 256u32; +pub const FILE_CONTAINS_EXTENDED_CREATE_INFORMATION: NTCREATEFILE_CREATE_OPTIONS = 268435456u32; +pub const FILE_CREATE: NTCREATEFILE_CREATE_DISPOSITION = 2u32; +pub const FILE_CREATE_PIPE_INSTANCE: FILE_ACCESS_RIGHTS = 4u32; +pub const FILE_CREATE_TREE_CONNECTION: NTCREATEFILE_CREATE_OPTIONS = 128u32; +pub type FILE_CREATION_DISPOSITION = u32; +pub const FILE_CURRENT: SET_FILE_POINTER_MOVE_METHOD = 1u32; +pub const FILE_DELETE_CHILD: FILE_ACCESS_RIGHTS = 64u32; +pub const FILE_DELETE_ON_CLOSE: NTCREATEFILE_CREATE_OPTIONS = 4096u32; +pub const FILE_DIRECTORY_FILE: NTCREATEFILE_CREATE_OPTIONS = 1u32; +pub const FILE_DISALLOW_EXCLUSIVE: NTCREATEFILE_CREATE_OPTIONS = 131072u32; +pub const FILE_DISPOSITION_FLAG_DELETE: FILE_DISPOSITION_INFO_EX_FLAGS = 1u32; +pub const FILE_DISPOSITION_FLAG_DO_NOT_DELETE: FILE_DISPOSITION_INFO_EX_FLAGS = 0u32; +pub const FILE_DISPOSITION_FLAG_FORCE_IMAGE_SECTION_CHECK: FILE_DISPOSITION_INFO_EX_FLAGS = 4u32; +pub const FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE: FILE_DISPOSITION_INFO_EX_FLAGS = 16u32; +pub const FILE_DISPOSITION_FLAG_ON_CLOSE: FILE_DISPOSITION_INFO_EX_FLAGS = 8u32; +pub const FILE_DISPOSITION_FLAG_POSIX_SEMANTICS: FILE_DISPOSITION_INFO_EX_FLAGS = 2u32; +#[repr(C)] +pub struct FILE_DISPOSITION_INFO { + pub DeleteFile: BOOLEAN, +} +impl ::core::marker::Copy for FILE_DISPOSITION_INFO {} +impl ::core::clone::Clone for FILE_DISPOSITION_INFO { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub struct FILE_DISPOSITION_INFO_EX { + pub Flags: FILE_DISPOSITION_INFO_EX_FLAGS, +} +impl ::core::marker::Copy for FILE_DISPOSITION_INFO_EX {} +impl ::core::clone::Clone for FILE_DISPOSITION_INFO_EX { + fn clone(&self) -> Self { + *self + } +} +pub type FILE_DISPOSITION_INFO_EX_FLAGS = u32; +pub const FILE_END: SET_FILE_POINTER_MOVE_METHOD = 2u32; +#[repr(C)] +pub struct FILE_END_OF_FILE_INFO { + pub EndOfFile: i64, +} +impl ::core::marker::Copy for FILE_END_OF_FILE_INFO {} +impl ::core::clone::Clone for FILE_END_OF_FILE_INFO { + fn clone(&self) -> Self { + *self + } +} +pub const FILE_EXECUTE: FILE_ACCESS_RIGHTS = 32u32; +pub type FILE_FLAGS_AND_ATTRIBUTES = u32; +pub const FILE_FLAG_BACKUP_SEMANTICS: FILE_FLAGS_AND_ATTRIBUTES = 33554432u32; +pub const FILE_FLAG_DELETE_ON_CLOSE: FILE_FLAGS_AND_ATTRIBUTES = 67108864u32; +pub const FILE_FLAG_FIRST_PIPE_INSTANCE: FILE_FLAGS_AND_ATTRIBUTES = 524288u32; +pub const FILE_FLAG_NO_BUFFERING: FILE_FLAGS_AND_ATTRIBUTES = 536870912u32; +pub const FILE_FLAG_OPEN_NO_RECALL: FILE_FLAGS_AND_ATTRIBUTES = 1048576u32; +pub const FILE_FLAG_OPEN_REPARSE_POINT: FILE_FLAGS_AND_ATTRIBUTES = 2097152u32; +pub const FILE_FLAG_OVERLAPPED: FILE_FLAGS_AND_ATTRIBUTES = 1073741824u32; +pub const FILE_FLAG_POSIX_SEMANTICS: FILE_FLAGS_AND_ATTRIBUTES = 16777216u32; +pub const FILE_FLAG_RANDOM_ACCESS: FILE_FLAGS_AND_ATTRIBUTES = 268435456u32; +pub const FILE_FLAG_SEQUENTIAL_SCAN: FILE_FLAGS_AND_ATTRIBUTES = 134217728u32; +pub const FILE_FLAG_SESSION_AWARE: FILE_FLAGS_AND_ATTRIBUTES = 8388608u32; +pub const FILE_FLAG_WRITE_THROUGH: FILE_FLAGS_AND_ATTRIBUTES = 2147483648u32; +pub const FILE_GENERIC_EXECUTE: FILE_ACCESS_RIGHTS = 1179808u32; +pub const FILE_GENERIC_READ: FILE_ACCESS_RIGHTS = 1179785u32; +pub const FILE_GENERIC_WRITE: FILE_ACCESS_RIGHTS = 1179926u32; +#[repr(C)] +pub struct FILE_ID_BOTH_DIR_INFO { + pub NextEntryOffset: u32, + pub FileIndex: u32, + pub CreationTime: i64, + pub LastAccessTime: i64, + pub LastWriteTime: i64, + pub ChangeTime: i64, + pub EndOfFile: i64, + pub AllocationSize: i64, + pub FileAttributes: u32, + pub FileNameLength: u32, + pub EaSize: u32, + pub ShortNameLength: i8, + pub ShortName: [u16; 12], + pub FileId: i64, + pub FileName: [u16; 1], +} +impl ::core::marker::Copy for FILE_ID_BOTH_DIR_INFO {} +impl ::core::clone::Clone for FILE_ID_BOTH_DIR_INFO { + fn clone(&self) -> Self { + *self + } +} +pub type FILE_INFO_BY_HANDLE_CLASS = i32; +#[repr(C)] +pub struct FILE_IO_PRIORITY_HINT_INFO { + pub PriorityHint: PRIORITY_HINT, +} +impl ::core::marker::Copy for FILE_IO_PRIORITY_HINT_INFO {} +impl ::core::clone::Clone for FILE_IO_PRIORITY_HINT_INFO { + fn clone(&self) -> Self { + *self + } +} +pub const FILE_LIST_DIRECTORY: FILE_ACCESS_RIGHTS = 1u32; +pub const FILE_NAME_NORMALIZED: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32; +pub const FILE_NAME_OPENED: GETFINALPATHNAMEBYHANDLE_FLAGS = 8u32; +pub const FILE_NON_DIRECTORY_FILE: NTCREATEFILE_CREATE_OPTIONS = 64u32; +pub const FILE_NO_COMPRESSION: NTCREATEFILE_CREATE_OPTIONS = 32768u32; +pub const FILE_NO_EA_KNOWLEDGE: NTCREATEFILE_CREATE_OPTIONS = 512u32; +pub const FILE_NO_INTERMEDIATE_BUFFERING: NTCREATEFILE_CREATE_OPTIONS = 8u32; +pub const FILE_OPEN: NTCREATEFILE_CREATE_DISPOSITION = 1u32; +pub const FILE_OPEN_BY_FILE_ID: NTCREATEFILE_CREATE_OPTIONS = 8192u32; +pub const FILE_OPEN_FOR_BACKUP_INTENT: NTCREATEFILE_CREATE_OPTIONS = 16384u32; +pub const FILE_OPEN_FOR_FREE_SPACE_QUERY: NTCREATEFILE_CREATE_OPTIONS = 8388608u32; +pub const FILE_OPEN_IF: NTCREATEFILE_CREATE_DISPOSITION = 3u32; +pub const FILE_OPEN_NO_RECALL: NTCREATEFILE_CREATE_OPTIONS = 4194304u32; +pub const FILE_OPEN_REPARSE_POINT: NTCREATEFILE_CREATE_OPTIONS = 2097152u32; +pub const FILE_OPEN_REQUIRING_OPLOCK: NTCREATEFILE_CREATE_OPTIONS = 65536u32; +pub const FILE_OVERWRITE: NTCREATEFILE_CREATE_DISPOSITION = 4u32; +pub const FILE_OVERWRITE_IF: NTCREATEFILE_CREATE_DISPOSITION = 5u32; +pub const FILE_RANDOM_ACCESS: NTCREATEFILE_CREATE_OPTIONS = 2048u32; +pub const FILE_READ_ATTRIBUTES: FILE_ACCESS_RIGHTS = 128u32; +pub const FILE_READ_DATA: FILE_ACCESS_RIGHTS = 1u32; +pub const FILE_READ_EA: FILE_ACCESS_RIGHTS = 8u32; +pub const FILE_RESERVE_OPFILTER: NTCREATEFILE_CREATE_OPTIONS = 1048576u32; +pub const FILE_SEQUENTIAL_ONLY: NTCREATEFILE_CREATE_OPTIONS = 4u32; +pub const FILE_SESSION_AWARE: NTCREATEFILE_CREATE_OPTIONS = 262144u32; +pub const FILE_SHARE_DELETE: FILE_SHARE_MODE = 4u32; +pub type FILE_SHARE_MODE = u32; +pub const FILE_SHARE_NONE: FILE_SHARE_MODE = 0u32; +pub const FILE_SHARE_READ: FILE_SHARE_MODE = 1u32; +pub const FILE_SHARE_WRITE: FILE_SHARE_MODE = 2u32; +#[repr(C)] +pub struct FILE_STANDARD_INFO { + pub AllocationSize: i64, + pub EndOfFile: i64, + pub NumberOfLinks: u32, + pub DeletePending: BOOLEAN, + pub Directory: BOOLEAN, +} +impl ::core::marker::Copy for FILE_STANDARD_INFO {} +impl ::core::clone::Clone for FILE_STANDARD_INFO { + fn clone(&self) -> Self { + *self + } +} +pub const FILE_SUPERSEDE: NTCREATEFILE_CREATE_DISPOSITION = 0u32; +pub const FILE_SYNCHRONOUS_IO_ALERT: NTCREATEFILE_CREATE_OPTIONS = 16u32; +pub const FILE_SYNCHRONOUS_IO_NONALERT: NTCREATEFILE_CREATE_OPTIONS = 32u32; +pub const FILE_TRAVERSE: FILE_ACCESS_RIGHTS = 32u32; +pub type FILE_TYPE = u32; +pub const FILE_TYPE_CHAR: FILE_TYPE = 2u32; +pub const FILE_TYPE_DISK: FILE_TYPE = 1u32; +pub const FILE_TYPE_PIPE: FILE_TYPE = 3u32; +pub const FILE_TYPE_REMOTE: FILE_TYPE = 32768u32; +pub const FILE_TYPE_UNKNOWN: FILE_TYPE = 0u32; +pub const FILE_WRITE_ATTRIBUTES: FILE_ACCESS_RIGHTS = 256u32; +pub const FILE_WRITE_DATA: FILE_ACCESS_RIGHTS = 2u32; +pub const FILE_WRITE_EA: FILE_ACCESS_RIGHTS = 16u32; +pub const FILE_WRITE_THROUGH: NTCREATEFILE_CREATE_OPTIONS = 2u32; +pub const FIONBIO: i32 = -2147195266i32; +#[repr(C)] +#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +pub struct FLOATING_SAVE_AREA { + pub ControlWord: u32, + pub StatusWord: u32, + pub TagWord: u32, + pub ErrorOffset: u32, + pub ErrorSelector: u32, + pub DataOffset: u32, + pub DataSelector: u32, + pub RegisterArea: [u8; 80], + pub Cr0NpxState: u32, +} +#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +impl ::core::marker::Copy for FLOATING_SAVE_AREA {} +#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +impl ::core::clone::Clone for FLOATING_SAVE_AREA { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +#[cfg(target_arch = "x86")] +pub struct FLOATING_SAVE_AREA { + pub ControlWord: u32, + pub StatusWord: u32, + pub TagWord: u32, + pub ErrorOffset: u32, + pub ErrorSelector: u32, + pub DataOffset: u32, + pub DataSelector: u32, + pub RegisterArea: [u8; 80], + pub Spare0: u32, +} +#[cfg(target_arch = "x86")] +impl ::core::marker::Copy for FLOATING_SAVE_AREA {} +#[cfg(target_arch = "x86")] +impl ::core::clone::Clone for FLOATING_SAVE_AREA { + fn clone(&self) -> Self { + *self + } +} +pub const FORMAT_MESSAGE_ALLOCATE_BUFFER: FORMAT_MESSAGE_OPTIONS = 256u32; +pub const FORMAT_MESSAGE_ARGUMENT_ARRAY: FORMAT_MESSAGE_OPTIONS = 8192u32; +pub const FORMAT_MESSAGE_FROM_HMODULE: FORMAT_MESSAGE_OPTIONS = 2048u32; +pub const FORMAT_MESSAGE_FROM_STRING: FORMAT_MESSAGE_OPTIONS = 1024u32; +pub const FORMAT_MESSAGE_FROM_SYSTEM: FORMAT_MESSAGE_OPTIONS = 4096u32; +pub const FORMAT_MESSAGE_IGNORE_INSERTS: FORMAT_MESSAGE_OPTIONS = 512u32; +pub type FORMAT_MESSAGE_OPTIONS = u32; +pub const FRS_ERR_SYSVOL_POPULATE_TIMEOUT: i32 = 8014i32; +pub const FSCTL_GET_REPARSE_POINT: u32 = 589992u32; +pub const FSCTL_SET_REPARSE_POINT: u32 = 589988u32; +pub const FileAlignmentInfo: FILE_INFO_BY_HANDLE_CLASS = 17i32; +pub const FileAllocationInfo: FILE_INFO_BY_HANDLE_CLASS = 5i32; +pub const FileAttributeTagInfo: FILE_INFO_BY_HANDLE_CLASS = 9i32; +pub const FileBasicInfo: FILE_INFO_BY_HANDLE_CLASS = 0i32; +pub const FileCaseSensitiveInfo: FILE_INFO_BY_HANDLE_CLASS = 23i32; +pub const FileCompressionInfo: FILE_INFO_BY_HANDLE_CLASS = 8i32; +pub const FileDispositionInfo: FILE_INFO_BY_HANDLE_CLASS = 4i32; +pub const FileDispositionInfoEx: FILE_INFO_BY_HANDLE_CLASS = 21i32; +pub const FileEndOfFileInfo: FILE_INFO_BY_HANDLE_CLASS = 6i32; +pub const FileFullDirectoryInfo: FILE_INFO_BY_HANDLE_CLASS = 14i32; +pub const FileFullDirectoryRestartInfo: FILE_INFO_BY_HANDLE_CLASS = 15i32; +pub const FileIdBothDirectoryInfo: FILE_INFO_BY_HANDLE_CLASS = 10i32; +pub const FileIdBothDirectoryRestartInfo: FILE_INFO_BY_HANDLE_CLASS = 11i32; +pub const FileIdExtdDirectoryInfo: FILE_INFO_BY_HANDLE_CLASS = 19i32; +pub const FileIdExtdDirectoryRestartInfo: FILE_INFO_BY_HANDLE_CLASS = 20i32; +pub const FileIdInfo: FILE_INFO_BY_HANDLE_CLASS = 18i32; +pub const FileIoPriorityHintInfo: FILE_INFO_BY_HANDLE_CLASS = 12i32; +pub const FileNameInfo: FILE_INFO_BY_HANDLE_CLASS = 2i32; +pub const FileNormalizedNameInfo: FILE_INFO_BY_HANDLE_CLASS = 24i32; +pub const FileRemoteProtocolInfo: FILE_INFO_BY_HANDLE_CLASS = 13i32; +pub const FileRenameInfo: FILE_INFO_BY_HANDLE_CLASS = 3i32; +pub const FileRenameInfoEx: FILE_INFO_BY_HANDLE_CLASS = 22i32; +pub const FileStandardInfo: FILE_INFO_BY_HANDLE_CLASS = 1i32; +pub const FileStorageInfo: FILE_INFO_BY_HANDLE_CLASS = 16i32; +pub const FileStreamInfo: FILE_INFO_BY_HANDLE_CLASS = 7i32; +pub type GENERIC_ACCESS_RIGHTS = u32; +pub const GENERIC_ALL: GENERIC_ACCESS_RIGHTS = 268435456u32; +pub const GENERIC_EXECUTE: GENERIC_ACCESS_RIGHTS = 536870912u32; +pub const GENERIC_READ: GENERIC_ACCESS_RIGHTS = 2147483648u32; +pub const GENERIC_WRITE: GENERIC_ACCESS_RIGHTS = 1073741824u32; +pub type GETFINALPATHNAMEBYHANDLE_FLAGS = u32; +#[repr(C)] +pub struct GUID { + pub data1: u32, + pub data2: u16, + pub data3: u16, + pub data4: [u8; 8], +} +impl ::core::marker::Copy for GUID {} +impl ::core::clone::Clone for GUID { + fn clone(&self) -> Self { + *self + } +} +impl GUID { + pub const fn from_u128(uuid: u128) -> Self { + Self { + data1: (uuid >> 96) as u32, + data2: (uuid >> 80 & 0xffff) as u16, + data3: (uuid >> 64 & 0xffff) as u16, + data4: (uuid as u64).to_be_bytes(), + } + } +} +pub type HANDLE = *mut ::core::ffi::c_void; +pub type HANDLE_FLAGS = u32; +pub const HANDLE_FLAG_INHERIT: HANDLE_FLAGS = 1u32; +pub const HANDLE_FLAG_PROTECT_FROM_CLOSE: HANDLE_FLAGS = 2u32; +pub const HIGH_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 128u32; +pub type HMODULE = *mut ::core::ffi::c_void; +pub type HRESULT = i32; +pub const IDLE_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 64u32; +#[repr(C)] +pub struct IN6_ADDR { + pub u: IN6_ADDR_0, +} +impl ::core::marker::Copy for IN6_ADDR {} +impl ::core::clone::Clone for IN6_ADDR { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub union IN6_ADDR_0 { + pub Byte: [u8; 16], + pub Word: [u16; 8], +} +impl ::core::marker::Copy for IN6_ADDR_0 {} +impl ::core::clone::Clone for IN6_ADDR_0 { + fn clone(&self) -> Self { + *self + } +} +pub const INFINITE: u32 = 4294967295u32; +pub const INHERIT_CALLER_PRIORITY: PROCESS_CREATION_FLAGS = 131072u32; +pub const INHERIT_PARENT_AFFINITY: PROCESS_CREATION_FLAGS = 65536u32; +#[repr(C)] +pub union INIT_ONCE { + pub Ptr: *mut ::core::ffi::c_void, +} +impl ::core::marker::Copy for INIT_ONCE {} +impl ::core::clone::Clone for INIT_ONCE { + fn clone(&self) -> Self { + *self + } +} +pub const INIT_ONCE_INIT_FAILED: u32 = 4u32; +pub const INVALID_FILE_ATTRIBUTES: u32 = 4294967295u32; +pub const INVALID_SOCKET: SOCKET = -1i32 as _; +#[repr(C)] +pub struct IN_ADDR { + pub S_un: IN_ADDR_0, +} +impl ::core::marker::Copy for IN_ADDR {} +impl ::core::clone::Clone for IN_ADDR { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub union IN_ADDR_0 { + pub S_un_b: IN_ADDR_0_0, + pub S_un_w: IN_ADDR_0_1, + pub S_addr: u32, +} +impl ::core::marker::Copy for IN_ADDR_0 {} +impl ::core::clone::Clone for IN_ADDR_0 { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub struct IN_ADDR_0_0 { + pub s_b1: u8, + pub s_b2: u8, + pub s_b3: u8, + pub s_b4: u8, +} +impl ::core::marker::Copy for IN_ADDR_0_0 {} +impl ::core::clone::Clone for IN_ADDR_0_0 { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub struct IN_ADDR_0_1 { + pub s_w1: u16, + pub s_w2: u16, +} +impl ::core::marker::Copy for IN_ADDR_0_1 {} +impl ::core::clone::Clone for IN_ADDR_0_1 { + fn clone(&self) -> Self { + *self + } +} +pub const IO_REPARSE_TAG_MOUNT_POINT: u32 = 2684354563u32; +pub const IO_REPARSE_TAG_SYMLINK: u32 = 2684354572u32; +#[repr(C)] +pub struct IO_STATUS_BLOCK { + pub Anonymous: IO_STATUS_BLOCK_0, + pub Information: usize, +} +impl ::core::marker::Copy for IO_STATUS_BLOCK {} +impl ::core::clone::Clone for IO_STATUS_BLOCK { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub union IO_STATUS_BLOCK_0 { + pub Status: NTSTATUS, + pub Pointer: *mut ::core::ffi::c_void, +} +impl ::core::marker::Copy for IO_STATUS_BLOCK_0 {} +impl ::core::clone::Clone for IO_STATUS_BLOCK_0 { + fn clone(&self) -> Self { + *self + } +} +pub type IPPROTO = i32; +pub const IPPROTO_AH: IPPROTO = 51i32; +pub const IPPROTO_CBT: IPPROTO = 7i32; +pub const IPPROTO_DSTOPTS: IPPROTO = 60i32; +pub const IPPROTO_EGP: IPPROTO = 8i32; +pub const IPPROTO_ESP: IPPROTO = 50i32; +pub const IPPROTO_FRAGMENT: IPPROTO = 44i32; +pub const IPPROTO_GGP: IPPROTO = 3i32; +pub const IPPROTO_HOPOPTS: IPPROTO = 0i32; +pub const IPPROTO_ICLFXBM: IPPROTO = 78i32; +pub const IPPROTO_ICMP: IPPROTO = 1i32; +pub const IPPROTO_ICMPV6: IPPROTO = 58i32; +pub const IPPROTO_IDP: IPPROTO = 22i32; +pub const IPPROTO_IGMP: IPPROTO = 2i32; +pub const IPPROTO_IGP: IPPROTO = 9i32; +pub const IPPROTO_IP: IPPROTO = 0i32; +pub const IPPROTO_IPV4: IPPROTO = 4i32; +pub const IPPROTO_IPV6: IPPROTO = 41i32; +pub const IPPROTO_L2TP: IPPROTO = 115i32; +pub const IPPROTO_MAX: IPPROTO = 256i32; +pub const IPPROTO_ND: IPPROTO = 77i32; +pub const IPPROTO_NONE: IPPROTO = 59i32; +pub const IPPROTO_PGM: IPPROTO = 113i32; +pub const IPPROTO_PIM: IPPROTO = 103i32; +pub const IPPROTO_PUP: IPPROTO = 12i32; +pub const IPPROTO_RAW: IPPROTO = 255i32; +pub const IPPROTO_RDP: IPPROTO = 27i32; +pub const IPPROTO_RESERVED_IPSEC: IPPROTO = 258i32; +pub const IPPROTO_RESERVED_IPSECOFFLOAD: IPPROTO = 259i32; +pub const IPPROTO_RESERVED_MAX: IPPROTO = 261i32; +pub const IPPROTO_RESERVED_RAW: IPPROTO = 257i32; +pub const IPPROTO_RESERVED_WNV: IPPROTO = 260i32; +pub const IPPROTO_RM: IPPROTO = 113i32; +pub const IPPROTO_ROUTING: IPPROTO = 43i32; +pub const IPPROTO_SCTP: IPPROTO = 132i32; +pub const IPPROTO_ST: IPPROTO = 5i32; +pub const IPPROTO_TCP: IPPROTO = 6i32; +pub const IPPROTO_UDP: IPPROTO = 17i32; +pub const IPV6_ADD_MEMBERSHIP: i32 = 12i32; +pub const IPV6_DROP_MEMBERSHIP: i32 = 13i32; +#[repr(C)] +pub struct IPV6_MREQ { + pub ipv6mr_multiaddr: IN6_ADDR, + pub ipv6mr_interface: u32, +} +impl ::core::marker::Copy for IPV6_MREQ {} +impl ::core::clone::Clone for IPV6_MREQ { + fn clone(&self) -> Self { + *self + } +} +pub const IPV6_MULTICAST_LOOP: i32 = 11i32; +pub const IPV6_V6ONLY: i32 = 27i32; +pub const IP_ADD_MEMBERSHIP: i32 = 12i32; +pub const IP_DROP_MEMBERSHIP: i32 = 13i32; +#[repr(C)] +pub struct IP_MREQ { + pub imr_multiaddr: IN_ADDR, + pub imr_interface: IN_ADDR, +} +impl ::core::marker::Copy for IP_MREQ {} +impl ::core::clone::Clone for IP_MREQ { + fn clone(&self) -> Self { + *self + } +} +pub const IP_MULTICAST_LOOP: i32 = 11i32; +pub const IP_MULTICAST_TTL: i32 = 10i32; +pub const IP_TTL: i32 = 4i32; +#[repr(C)] +pub struct LINGER { + pub l_onoff: u16, + pub l_linger: u16, +} +impl ::core::marker::Copy for LINGER {} +impl ::core::clone::Clone for LINGER { + fn clone(&self) -> Self { + *self + } +} +pub type LPOVERLAPPED_COMPLETION_ROUTINE = ::core::option::Option< + unsafe extern "system" fn( + dwerrorcode: u32, + dwnumberofbytestransfered: u32, + lpoverlapped: *mut OVERLAPPED, + ) -> (), +>; +pub type LPPROC_THREAD_ATTRIBUTE_LIST = *mut ::core::ffi::c_void; +pub type LPPROGRESS_ROUTINE = ::core::option::Option< + unsafe extern "system" fn( + totalfilesize: i64, + totalbytestransferred: i64, + streamsize: i64, + streambytestransferred: i64, + dwstreamnumber: u32, + dwcallbackreason: LPPROGRESS_ROUTINE_CALLBACK_REASON, + hsourcefile: HANDLE, + hdestinationfile: HANDLE, + lpdata: *const ::core::ffi::c_void, + ) -> u32, +>; +pub type LPPROGRESS_ROUTINE_CALLBACK_REASON = u32; +pub type LPTHREAD_START_ROUTINE = ::core::option::Option< + unsafe extern "system" fn(lpthreadparameter: *mut ::core::ffi::c_void) -> u32, +>; +pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = ::core::option::Option< + unsafe extern "system" fn( + dwerror: u32, + cbtransferred: u32, + lpoverlapped: *mut OVERLAPPED, + dwflags: u32, + ) -> (), +>; +#[repr(C)] +pub struct M128A { + pub Low: u64, + pub High: i64, +} +impl ::core::marker::Copy for M128A {} +impl ::core::clone::Clone for M128A { + fn clone(&self) -> Self { + *self + } +} +pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: u32 = 16384u32; +pub const MAX_PATH: u32 = 260u32; +pub const MB_COMPOSITE: MULTI_BYTE_TO_WIDE_CHAR_FLAGS = 2u32; +pub const MB_ERR_INVALID_CHARS: MULTI_BYTE_TO_WIDE_CHAR_FLAGS = 8u32; +pub const MB_PRECOMPOSED: MULTI_BYTE_TO_WIDE_CHAR_FLAGS = 1u32; +pub const MB_USEGLYPHCHARS: MULTI_BYTE_TO_WIDE_CHAR_FLAGS = 4u32; +pub const MOVEFILE_COPY_ALLOWED: MOVE_FILE_FLAGS = 2u32; +pub const MOVEFILE_CREATE_HARDLINK: MOVE_FILE_FLAGS = 16u32; +pub const MOVEFILE_DELAY_UNTIL_REBOOT: MOVE_FILE_FLAGS = 4u32; +pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE: MOVE_FILE_FLAGS = 32u32; +pub const MOVEFILE_REPLACE_EXISTING: MOVE_FILE_FLAGS = 1u32; +pub const MOVEFILE_WRITE_THROUGH: MOVE_FILE_FLAGS = 8u32; +pub type MOVE_FILE_FLAGS = u32; +pub const MSG_DONTROUTE: SEND_RECV_FLAGS = 4i32; +pub const MSG_OOB: SEND_RECV_FLAGS = 1i32; +pub const MSG_PEEK: SEND_RECV_FLAGS = 2i32; +pub const MSG_PUSH_IMMEDIATE: SEND_RECV_FLAGS = 32i32; +pub const MSG_WAITALL: SEND_RECV_FLAGS = 8i32; +pub type MULTI_BYTE_TO_WIDE_CHAR_FLAGS = u32; +pub const MaximumFileInfoByHandleClass: FILE_INFO_BY_HANDLE_CLASS = 25i32; +pub type NAMED_PIPE_MODE = u32; +pub const NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 32u32; +pub const NO_ERROR: WIN32_ERROR = 0u32; +pub type NTCREATEFILE_CREATE_DISPOSITION = u32; +pub type NTCREATEFILE_CREATE_OPTIONS = u32; +pub type NTSTATUS = i32; +#[repr(C)] +pub struct OBJECT_ATTRIBUTES { + pub Length: u32, + pub RootDirectory: HANDLE, + pub ObjectName: *const UNICODE_STRING, + pub Attributes: u32, + pub SecurityDescriptor: *const ::core::ffi::c_void, + pub SecurityQualityOfService: *const ::core::ffi::c_void, +} +impl ::core::marker::Copy for OBJECT_ATTRIBUTES {} +impl ::core::clone::Clone for OBJECT_ATTRIBUTES { + fn clone(&self) -> Self { + *self + } +} +pub const OBJ_DONT_REPARSE: i32 = 4096i32; +pub const OPEN_ALWAYS: FILE_CREATION_DISPOSITION = 4u32; +pub const OPEN_EXISTING: FILE_CREATION_DISPOSITION = 3u32; +#[repr(C)] +pub struct OVERLAPPED { + pub Internal: usize, + pub InternalHigh: usize, + pub Anonymous: OVERLAPPED_0, + pub hEvent: HANDLE, +} +impl ::core::marker::Copy for OVERLAPPED {} +impl ::core::clone::Clone for OVERLAPPED { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub union OVERLAPPED_0 { + pub Anonymous: OVERLAPPED_0_0, + pub Pointer: *mut ::core::ffi::c_void, +} +impl ::core::marker::Copy for OVERLAPPED_0 {} +impl ::core::clone::Clone for OVERLAPPED_0 { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub struct OVERLAPPED_0_0 { + pub Offset: u32, + pub OffsetHigh: u32, +} +impl ::core::marker::Copy for OVERLAPPED_0_0 {} +impl ::core::clone::Clone for OVERLAPPED_0_0 { + fn clone(&self) -> Self { + *self + } +} +pub type PCSTR = *const u8; +pub type PCWSTR = *const u16; +pub type PIO_APC_ROUTINE = ::core::option::Option< + unsafe extern "system" fn( + apccontext: *mut ::core::ffi::c_void, + iostatusblock: *mut IO_STATUS_BLOCK, + reserved: u32, + ) -> (), +>; +pub const PIPE_ACCEPT_REMOTE_CLIENTS: NAMED_PIPE_MODE = 0u32; +pub const PIPE_ACCESS_DUPLEX: FILE_FLAGS_AND_ATTRIBUTES = 3u32; +pub const PIPE_ACCESS_INBOUND: FILE_FLAGS_AND_ATTRIBUTES = 1u32; +pub const PIPE_ACCESS_OUTBOUND: FILE_FLAGS_AND_ATTRIBUTES = 2u32; +pub const PIPE_CLIENT_END: NAMED_PIPE_MODE = 0u32; +pub const PIPE_NOWAIT: NAMED_PIPE_MODE = 1u32; +pub const PIPE_READMODE_BYTE: NAMED_PIPE_MODE = 0u32; +pub const PIPE_READMODE_MESSAGE: NAMED_PIPE_MODE = 2u32; +pub const PIPE_REJECT_REMOTE_CLIENTS: NAMED_PIPE_MODE = 8u32; +pub const PIPE_SERVER_END: NAMED_PIPE_MODE = 1u32; +pub const PIPE_TYPE_BYTE: NAMED_PIPE_MODE = 0u32; +pub const PIPE_TYPE_MESSAGE: NAMED_PIPE_MODE = 4u32; +pub const PIPE_WAIT: NAMED_PIPE_MODE = 0u32; +pub type PRIORITY_HINT = i32; +pub type PROCESSOR_ARCHITECTURE = u16; +pub type PROCESS_CREATION_FLAGS = u32; +#[repr(C)] +pub struct PROCESS_INFORMATION { + pub hProcess: HANDLE, + pub hThread: HANDLE, + pub dwProcessId: u32, + pub dwThreadId: u32, +} +impl ::core::marker::Copy for PROCESS_INFORMATION {} +impl ::core::clone::Clone for PROCESS_INFORMATION { + fn clone(&self) -> Self { + *self + } +} +pub const PROCESS_MODE_BACKGROUND_BEGIN: PROCESS_CREATION_FLAGS = 1048576u32; +pub const PROCESS_MODE_BACKGROUND_END: PROCESS_CREATION_FLAGS = 2097152u32; +pub const PROFILE_KERNEL: PROCESS_CREATION_FLAGS = 536870912u32; +pub const PROFILE_SERVER: PROCESS_CREATION_FLAGS = 1073741824u32; +pub const PROFILE_USER: PROCESS_CREATION_FLAGS = 268435456u32; +pub const PROGRESS_CONTINUE: u32 = 0u32; +pub type PSTR = *mut u8; +pub type PTIMERAPCROUTINE = ::core::option::Option< + unsafe extern "system" fn( + lpargtocompletionroutine: *const ::core::ffi::c_void, + dwtimerlowvalue: u32, + dwtimerhighvalue: u32, + ) -> (), +>; +pub type PWSTR = *mut u16; +pub const READ_CONTROL: FILE_ACCESS_RIGHTS = 131072u32; +pub const REALTIME_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 256u32; +pub const SD_BOTH: WINSOCK_SHUTDOWN_HOW = 2i32; +pub const SD_RECEIVE: WINSOCK_SHUTDOWN_HOW = 0i32; +pub const SD_SEND: WINSOCK_SHUTDOWN_HOW = 1i32; +pub const SECURITY_ANONYMOUS: FILE_FLAGS_AND_ATTRIBUTES = 0u32; +#[repr(C)] +pub struct SECURITY_ATTRIBUTES { + pub nLength: u32, + pub lpSecurityDescriptor: *mut ::core::ffi::c_void, + pub bInheritHandle: BOOL, +} +impl ::core::marker::Copy for SECURITY_ATTRIBUTES {} +impl ::core::clone::Clone for SECURITY_ATTRIBUTES { + fn clone(&self) -> Self { + *self + } +} +pub const SECURITY_CONTEXT_TRACKING: FILE_FLAGS_AND_ATTRIBUTES = 262144u32; +pub const SECURITY_DELEGATION: FILE_FLAGS_AND_ATTRIBUTES = 196608u32; +pub const SECURITY_EFFECTIVE_ONLY: FILE_FLAGS_AND_ATTRIBUTES = 524288u32; +pub const SECURITY_IDENTIFICATION: FILE_FLAGS_AND_ATTRIBUTES = 65536u32; +pub const SECURITY_IMPERSONATION: FILE_FLAGS_AND_ATTRIBUTES = 131072u32; +pub const SECURITY_SQOS_PRESENT: FILE_FLAGS_AND_ATTRIBUTES = 1048576u32; +pub const SECURITY_VALID_SQOS_FLAGS: FILE_FLAGS_AND_ATTRIBUTES = 2031616u32; +pub type SEND_RECV_FLAGS = i32; +pub type SET_FILE_POINTER_MOVE_METHOD = u32; +#[repr(C)] +pub struct SOCKADDR { + pub sa_family: ADDRESS_FAMILY, + pub sa_data: [u8; 14], +} +impl ::core::marker::Copy for SOCKADDR {} +impl ::core::clone::Clone for SOCKADDR { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub struct SOCKADDR_UN { + pub sun_family: ADDRESS_FAMILY, + pub sun_path: [u8; 108], +} +impl ::core::marker::Copy for SOCKADDR_UN {} +impl ::core::clone::Clone for SOCKADDR_UN { + fn clone(&self) -> Self { + *self + } +} +pub type SOCKET = usize; +pub const SOCKET_ERROR: i32 = -1i32; +pub const SOCK_DGRAM: WINSOCK_SOCKET_TYPE = 2i32; +pub const SOCK_RAW: WINSOCK_SOCKET_TYPE = 3i32; +pub const SOCK_RDM: WINSOCK_SOCKET_TYPE = 4i32; +pub const SOCK_SEQPACKET: WINSOCK_SOCKET_TYPE = 5i32; +pub const SOCK_STREAM: WINSOCK_SOCKET_TYPE = 1i32; +pub const SOL_SOCKET: i32 = 65535i32; +pub const SO_BROADCAST: i32 = 32i32; +pub const SO_ERROR: i32 = 4103i32; +pub const SO_LINGER: i32 = 128i32; +pub const SO_RCVTIMEO: i32 = 4102i32; +pub const SO_SNDTIMEO: i32 = 4101i32; +pub const SPECIFIC_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 65535u32; +#[repr(C)] +pub struct SRWLOCK { + pub Ptr: *mut ::core::ffi::c_void, +} +impl ::core::marker::Copy for SRWLOCK {} +impl ::core::clone::Clone for SRWLOCK { + fn clone(&self) -> Self { + *self + } +} +pub const STACK_SIZE_PARAM_IS_A_RESERVATION: THREAD_CREATION_FLAGS = 65536u32; +pub const STANDARD_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 2031616u32; +pub const STANDARD_RIGHTS_EXECUTE: FILE_ACCESS_RIGHTS = 131072u32; +pub const STANDARD_RIGHTS_READ: FILE_ACCESS_RIGHTS = 131072u32; +pub const STANDARD_RIGHTS_REQUIRED: FILE_ACCESS_RIGHTS = 983040u32; +pub const STANDARD_RIGHTS_WRITE: FILE_ACCESS_RIGHTS = 131072u32; +pub const STARTF_FORCEOFFFEEDBACK: STARTUPINFOW_FLAGS = 128u32; +pub const STARTF_FORCEONFEEDBACK: STARTUPINFOW_FLAGS = 64u32; +pub const STARTF_PREVENTPINNING: STARTUPINFOW_FLAGS = 8192u32; +pub const STARTF_RUNFULLSCREEN: STARTUPINFOW_FLAGS = 32u32; +pub const STARTF_TITLEISAPPID: STARTUPINFOW_FLAGS = 4096u32; +pub const STARTF_TITLEISLINKNAME: STARTUPINFOW_FLAGS = 2048u32; +pub const STARTF_UNTRUSTEDSOURCE: STARTUPINFOW_FLAGS = 32768u32; +pub const STARTF_USECOUNTCHARS: STARTUPINFOW_FLAGS = 8u32; +pub const STARTF_USEFILLATTRIBUTE: STARTUPINFOW_FLAGS = 16u32; +pub const STARTF_USEHOTKEY: STARTUPINFOW_FLAGS = 512u32; +pub const STARTF_USEPOSITION: STARTUPINFOW_FLAGS = 4u32; +pub const STARTF_USESHOWWINDOW: STARTUPINFOW_FLAGS = 1u32; +pub const STARTF_USESIZE: STARTUPINFOW_FLAGS = 2u32; +pub const STARTF_USESTDHANDLES: STARTUPINFOW_FLAGS = 256u32; +#[repr(C)] +pub struct STARTUPINFOEXW { + pub StartupInfo: STARTUPINFOW, + pub lpAttributeList: LPPROC_THREAD_ATTRIBUTE_LIST, +} +impl ::core::marker::Copy for STARTUPINFOEXW {} +impl ::core::clone::Clone for STARTUPINFOEXW { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub struct STARTUPINFOW { + pub cb: u32, + pub lpReserved: PWSTR, + pub lpDesktop: PWSTR, + pub lpTitle: PWSTR, + pub dwX: u32, + pub dwY: u32, + pub dwXSize: u32, + pub dwYSize: u32, + pub dwXCountChars: u32, + pub dwYCountChars: u32, + pub dwFillAttribute: u32, + pub dwFlags: STARTUPINFOW_FLAGS, + pub wShowWindow: u16, + pub cbReserved2: u16, + pub lpReserved2: *mut u8, + pub hStdInput: HANDLE, + pub hStdOutput: HANDLE, + pub hStdError: HANDLE, +} +impl ::core::marker::Copy for STARTUPINFOW {} +impl ::core::clone::Clone for STARTUPINFOW { + fn clone(&self) -> Self { + *self + } +} +pub type STARTUPINFOW_FLAGS = u32; +pub const STATUS_DELETE_PENDING: NTSTATUS = -1073741738i32; +pub const STATUS_END_OF_FILE: NTSTATUS = -1073741807i32; +pub const STATUS_INVALID_PARAMETER: NTSTATUS = -1073741811i32; +pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = -1073741822i32; +pub const STATUS_PENDING: NTSTATUS = 259i32; +pub const STATUS_SUCCESS: NTSTATUS = 0i32; +pub const STD_ERROR_HANDLE: STD_HANDLE = 4294967284u32; +pub type STD_HANDLE = u32; +pub const STD_INPUT_HANDLE: STD_HANDLE = 4294967286u32; +pub const STD_OUTPUT_HANDLE: STD_HANDLE = 4294967285u32; +pub type SYMBOLIC_LINK_FLAGS = u32; +pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: SYMBOLIC_LINK_FLAGS = 2u32; +pub const SYMBOLIC_LINK_FLAG_DIRECTORY: SYMBOLIC_LINK_FLAGS = 1u32; +pub const SYMLINK_FLAG_RELATIVE: u32 = 1u32; +pub type SYNCHRONIZATION_ACCESS_RIGHTS = u32; +pub const SYNCHRONIZE: FILE_ACCESS_RIGHTS = 1048576u32; +#[repr(C)] +pub struct SYSTEM_INFO { + pub Anonymous: SYSTEM_INFO_0, + pub dwPageSize: u32, + pub lpMinimumApplicationAddress: *mut ::core::ffi::c_void, + pub lpMaximumApplicationAddress: *mut ::core::ffi::c_void, + pub dwActiveProcessorMask: usize, + pub dwNumberOfProcessors: u32, + pub dwProcessorType: u32, + pub dwAllocationGranularity: u32, + pub wProcessorLevel: u16, + pub wProcessorRevision: u16, +} +impl ::core::marker::Copy for SYSTEM_INFO {} +impl ::core::clone::Clone for SYSTEM_INFO { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub union SYSTEM_INFO_0 { + pub dwOemId: u32, + pub Anonymous: SYSTEM_INFO_0_0, +} +impl ::core::marker::Copy for SYSTEM_INFO_0 {} +impl ::core::clone::Clone for SYSTEM_INFO_0 { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub struct SYSTEM_INFO_0_0 { + pub wProcessorArchitecture: PROCESSOR_ARCHITECTURE, + pub wReserved: u16, +} +impl ::core::marker::Copy for SYSTEM_INFO_0_0 {} +impl ::core::clone::Clone for SYSTEM_INFO_0_0 { + fn clone(&self) -> Self { + *self + } +} +pub const TCP_NODELAY: i32 = 1i32; +pub const THREAD_CREATE_RUN_IMMEDIATELY: THREAD_CREATION_FLAGS = 0u32; +pub const THREAD_CREATE_SUSPENDED: THREAD_CREATION_FLAGS = 4u32; +pub type THREAD_CREATION_FLAGS = u32; +pub const TIMER_ALL_ACCESS: SYNCHRONIZATION_ACCESS_RIGHTS = 2031619u32; +pub const TIMER_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32; +#[repr(C)] +pub struct TIMEVAL { + pub tv_sec: i32, + pub tv_usec: i32, +} +impl ::core::marker::Copy for TIMEVAL {} +impl ::core::clone::Clone for TIMEVAL { + fn clone(&self) -> Self { + *self + } +} +pub const TLS_OUT_OF_INDEXES: u32 = 4294967295u32; +pub type TOKEN_ACCESS_MASK = u32; +pub const TOKEN_ACCESS_PSEUDO_HANDLE: TOKEN_ACCESS_MASK = 24u32; +pub const TOKEN_ACCESS_PSEUDO_HANDLE_WIN8: TOKEN_ACCESS_MASK = 24u32; +pub const TOKEN_ACCESS_SYSTEM_SECURITY: TOKEN_ACCESS_MASK = 16777216u32; +pub const TOKEN_ADJUST_DEFAULT: TOKEN_ACCESS_MASK = 128u32; +pub const TOKEN_ADJUST_GROUPS: TOKEN_ACCESS_MASK = 64u32; +pub const TOKEN_ADJUST_PRIVILEGES: TOKEN_ACCESS_MASK = 32u32; +pub const TOKEN_ADJUST_SESSIONID: TOKEN_ACCESS_MASK = 256u32; +pub const TOKEN_ALL_ACCESS: TOKEN_ACCESS_MASK = 983551u32; +pub const TOKEN_ASSIGN_PRIMARY: TOKEN_ACCESS_MASK = 1u32; +pub const TOKEN_DELETE: TOKEN_ACCESS_MASK = 65536u32; +pub const TOKEN_DUPLICATE: TOKEN_ACCESS_MASK = 2u32; +pub const TOKEN_EXECUTE: TOKEN_ACCESS_MASK = 131072u32; +pub const TOKEN_IMPERSONATE: TOKEN_ACCESS_MASK = 4u32; +pub const TOKEN_QUERY: TOKEN_ACCESS_MASK = 8u32; +pub const TOKEN_QUERY_SOURCE: TOKEN_ACCESS_MASK = 16u32; +pub const TOKEN_READ: TOKEN_ACCESS_MASK = 131080u32; +pub const TOKEN_READ_CONTROL: TOKEN_ACCESS_MASK = 131072u32; +pub const TOKEN_TRUST_CONSTRAINT_MASK: TOKEN_ACCESS_MASK = 131096u32; +pub const TOKEN_WRITE: TOKEN_ACCESS_MASK = 131296u32; +pub const TOKEN_WRITE_DAC: TOKEN_ACCESS_MASK = 262144u32; +pub const TOKEN_WRITE_OWNER: TOKEN_ACCESS_MASK = 524288u32; +pub const TRUE: BOOL = 1i32; +pub const TRUNCATE_EXISTING: FILE_CREATION_DISPOSITION = 5u32; +#[repr(C)] +pub struct UNICODE_STRING { + pub Length: u16, + pub MaximumLength: u16, + pub Buffer: PWSTR, +} +impl ::core::marker::Copy for UNICODE_STRING {} +impl ::core::clone::Clone for UNICODE_STRING { + fn clone(&self) -> Self { + *self + } +} +pub const VOLUME_NAME_DOS: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32; +pub const VOLUME_NAME_GUID: GETFINALPATHNAMEBYHANDLE_FLAGS = 1u32; +pub const VOLUME_NAME_NONE: GETFINALPATHNAMEBYHANDLE_FLAGS = 4u32; +pub const WAIT_ABANDONED: WAIT_EVENT = 128u32; +pub const WAIT_ABANDONED_0: WAIT_EVENT = 128u32; +pub type WAIT_EVENT = u32; +pub const WAIT_FAILED: WAIT_EVENT = 4294967295u32; +pub const WAIT_IO_COMPLETION: WAIT_EVENT = 192u32; +pub const WAIT_OBJECT_0: WAIT_EVENT = 0u32; +pub const WAIT_TIMEOUT: WAIT_EVENT = 258u32; +pub const WC_ERR_INVALID_CHARS: u32 = 128u32; +pub type WIN32_ERROR = u32; +#[repr(C)] +pub struct WIN32_FIND_DATAW { + pub dwFileAttributes: u32, + pub ftCreationTime: FILETIME, + pub ftLastAccessTime: FILETIME, + pub ftLastWriteTime: FILETIME, + pub nFileSizeHigh: u32, + pub nFileSizeLow: u32, + pub dwReserved0: u32, + pub dwReserved1: u32, + pub cFileName: [u16; 260], + pub cAlternateFileName: [u16; 14], +} +impl ::core::marker::Copy for WIN32_FIND_DATAW {} +impl ::core::clone::Clone for WIN32_FIND_DATAW { + fn clone(&self) -> Self { + *self + } +} +pub type WINSOCK_SHUTDOWN_HOW = i32; +pub type WINSOCK_SOCKET_TYPE = i32; +pub const WRITE_DAC: FILE_ACCESS_RIGHTS = 262144u32; +pub const WRITE_OWNER: FILE_ACCESS_RIGHTS = 524288u32; +pub const WSABASEERR: WSA_ERROR = 10000i32; +#[repr(C)] +pub struct WSABUF { + pub len: u32, + pub buf: PSTR, +} +impl ::core::marker::Copy for WSABUF {} +impl ::core::clone::Clone for WSABUF { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +pub struct WSADATA { + pub wVersion: u16, + pub wHighVersion: u16, + pub iMaxSockets: u16, + pub iMaxUdpDg: u16, + pub lpVendorInfo: PSTR, + pub szDescription: [u8; 257], + pub szSystemStatus: [u8; 129], +} +#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +impl ::core::marker::Copy for WSADATA {} +#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +impl ::core::clone::Clone for WSADATA { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +#[cfg(target_arch = "x86")] +pub struct WSADATA { + pub wVersion: u16, + pub wHighVersion: u16, + pub szDescription: [u8; 257], + pub szSystemStatus: [u8; 129], + pub iMaxSockets: u16, + pub iMaxUdpDg: u16, + pub lpVendorInfo: PSTR, +} +#[cfg(target_arch = "x86")] +impl ::core::marker::Copy for WSADATA {} +#[cfg(target_arch = "x86")] +impl ::core::clone::Clone for WSADATA { + fn clone(&self) -> Self { + *self + } +} +pub const WSAEACCES: WSA_ERROR = 10013i32; +pub const WSAEADDRINUSE: WSA_ERROR = 10048i32; +pub const WSAEADDRNOTAVAIL: WSA_ERROR = 10049i32; +pub const WSAEAFNOSUPPORT: WSA_ERROR = 10047i32; +pub const WSAEALREADY: WSA_ERROR = 10037i32; +pub const WSAEBADF: WSA_ERROR = 10009i32; +pub const WSAECANCELLED: WSA_ERROR = 10103i32; +pub const WSAECONNABORTED: WSA_ERROR = 10053i32; +pub const WSAECONNREFUSED: WSA_ERROR = 10061i32; +pub const WSAECONNRESET: WSA_ERROR = 10054i32; +pub const WSAEDESTADDRREQ: WSA_ERROR = 10039i32; +pub const WSAEDISCON: WSA_ERROR = 10101i32; +pub const WSAEDQUOT: WSA_ERROR = 10069i32; +pub const WSAEFAULT: WSA_ERROR = 10014i32; +pub const WSAEHOSTDOWN: WSA_ERROR = 10064i32; +pub const WSAEHOSTUNREACH: WSA_ERROR = 10065i32; +pub const WSAEINPROGRESS: WSA_ERROR = 10036i32; +pub const WSAEINTR: WSA_ERROR = 10004i32; +pub const WSAEINVAL: WSA_ERROR = 10022i32; +pub const WSAEINVALIDPROCTABLE: WSA_ERROR = 10104i32; +pub const WSAEINVALIDPROVIDER: WSA_ERROR = 10105i32; +pub const WSAEISCONN: WSA_ERROR = 10056i32; +pub const WSAELOOP: WSA_ERROR = 10062i32; +pub const WSAEMFILE: WSA_ERROR = 10024i32; +pub const WSAEMSGSIZE: WSA_ERROR = 10040i32; +pub const WSAENAMETOOLONG: WSA_ERROR = 10063i32; +pub const WSAENETDOWN: WSA_ERROR = 10050i32; +pub const WSAENETRESET: WSA_ERROR = 10052i32; +pub const WSAENETUNREACH: WSA_ERROR = 10051i32; +pub const WSAENOBUFS: WSA_ERROR = 10055i32; +pub const WSAENOMORE: WSA_ERROR = 10102i32; +pub const WSAENOPROTOOPT: WSA_ERROR = 10042i32; +pub const WSAENOTCONN: WSA_ERROR = 10057i32; +pub const WSAENOTEMPTY: WSA_ERROR = 10066i32; +pub const WSAENOTSOCK: WSA_ERROR = 10038i32; +pub const WSAEOPNOTSUPP: WSA_ERROR = 10045i32; +pub const WSAEPFNOSUPPORT: WSA_ERROR = 10046i32; +pub const WSAEPROCLIM: WSA_ERROR = 10067i32; +pub const WSAEPROTONOSUPPORT: WSA_ERROR = 10043i32; +pub const WSAEPROTOTYPE: WSA_ERROR = 10041i32; +pub const WSAEPROVIDERFAILEDINIT: WSA_ERROR = 10106i32; +pub const WSAEREFUSED: WSA_ERROR = 10112i32; +pub const WSAEREMOTE: WSA_ERROR = 10071i32; +pub const WSAESHUTDOWN: WSA_ERROR = 10058i32; +pub const WSAESOCKTNOSUPPORT: WSA_ERROR = 10044i32; +pub const WSAESTALE: WSA_ERROR = 10070i32; +pub const WSAETIMEDOUT: WSA_ERROR = 10060i32; +pub const WSAETOOMANYREFS: WSA_ERROR = 10059i32; +pub const WSAEUSERS: WSA_ERROR = 10068i32; +pub const WSAEWOULDBLOCK: WSA_ERROR = 10035i32; +pub const WSAHOST_NOT_FOUND: WSA_ERROR = 11001i32; +pub const WSANOTINITIALISED: WSA_ERROR = 10093i32; +pub const WSANO_DATA: WSA_ERROR = 11004i32; +pub const WSANO_RECOVERY: WSA_ERROR = 11003i32; +#[repr(C)] +pub struct WSAPROTOCOLCHAIN { + pub ChainLen: i32, + pub ChainEntries: [u32; 7], +} +impl ::core::marker::Copy for WSAPROTOCOLCHAIN {} +impl ::core::clone::Clone for WSAPROTOCOLCHAIN { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +pub struct WSAPROTOCOL_INFOW { + pub dwServiceFlags1: u32, + pub dwServiceFlags2: u32, + pub dwServiceFlags3: u32, + pub dwServiceFlags4: u32, + pub dwProviderFlags: u32, + pub ProviderId: GUID, + pub dwCatalogEntryId: u32, + pub ProtocolChain: WSAPROTOCOLCHAIN, + pub iVersion: i32, + pub iAddressFamily: i32, + pub iMaxSockAddr: i32, + pub iMinSockAddr: i32, + pub iSocketType: i32, + pub iProtocol: i32, + pub iProtocolMaxOffset: i32, + pub iNetworkByteOrder: i32, + pub iSecurityScheme: i32, + pub dwMessageSize: u32, + pub dwProviderReserved: u32, + pub szProtocol: [u16; 256], +} +impl ::core::marker::Copy for WSAPROTOCOL_INFOW {} +impl ::core::clone::Clone for WSAPROTOCOL_INFOW { + fn clone(&self) -> Self { + *self + } +} +pub const WSASERVICE_NOT_FOUND: WSA_ERROR = 10108i32; +pub const WSASYSCALLFAILURE: WSA_ERROR = 10107i32; +pub const WSASYSNOTREADY: WSA_ERROR = 10091i32; +pub const WSATRY_AGAIN: WSA_ERROR = 11002i32; +pub const WSATYPE_NOT_FOUND: WSA_ERROR = 10109i32; +pub const WSAVERNOTSUPPORTED: WSA_ERROR = 10092i32; +pub type WSA_ERROR = i32; +pub const WSA_E_CANCELLED: WSA_ERROR = 10111i32; +pub const WSA_E_NO_MORE: WSA_ERROR = 10110i32; +pub const WSA_FLAG_NO_HANDLE_INHERIT: u32 = 128u32; +pub const WSA_FLAG_OVERLAPPED: u32 = 1u32; +pub const WSA_INVALID_HANDLE: WSA_ERROR = 6i32; +pub const WSA_INVALID_PARAMETER: WSA_ERROR = 87i32; +pub const WSA_IO_INCOMPLETE: WSA_ERROR = 996i32; +pub const WSA_IO_PENDING: WSA_ERROR = 997i32; +pub const WSA_IPSEC_NAME_POLICY_ERROR: WSA_ERROR = 11033i32; +pub const WSA_NOT_ENOUGH_MEMORY: WSA_ERROR = 8i32; +pub const WSA_OPERATION_ABORTED: WSA_ERROR = 995i32; +pub const WSA_QOS_ADMISSION_FAILURE: WSA_ERROR = 11010i32; +pub const WSA_QOS_BAD_OBJECT: WSA_ERROR = 11013i32; +pub const WSA_QOS_BAD_STYLE: WSA_ERROR = 11012i32; +pub const WSA_QOS_EFILTERCOUNT: WSA_ERROR = 11021i32; +pub const WSA_QOS_EFILTERSTYLE: WSA_ERROR = 11019i32; +pub const WSA_QOS_EFILTERTYPE: WSA_ERROR = 11020i32; +pub const WSA_QOS_EFLOWCOUNT: WSA_ERROR = 11023i32; +pub const WSA_QOS_EFLOWDESC: WSA_ERROR = 11026i32; +pub const WSA_QOS_EFLOWSPEC: WSA_ERROR = 11017i32; +pub const WSA_QOS_EOBJLENGTH: WSA_ERROR = 11022i32; +pub const WSA_QOS_EPOLICYOBJ: WSA_ERROR = 11025i32; +pub const WSA_QOS_EPROVSPECBUF: WSA_ERROR = 11018i32; +pub const WSA_QOS_EPSFILTERSPEC: WSA_ERROR = 11028i32; +pub const WSA_QOS_EPSFLOWSPEC: WSA_ERROR = 11027i32; +pub const WSA_QOS_ESDMODEOBJ: WSA_ERROR = 11029i32; +pub const WSA_QOS_ESERVICETYPE: WSA_ERROR = 11016i32; +pub const WSA_QOS_ESHAPERATEOBJ: WSA_ERROR = 11030i32; +pub const WSA_QOS_EUNKOWNPSOBJ: WSA_ERROR = 11024i32; +pub const WSA_QOS_GENERIC_ERROR: WSA_ERROR = 11015i32; +pub const WSA_QOS_NO_RECEIVERS: WSA_ERROR = 11008i32; +pub const WSA_QOS_NO_SENDERS: WSA_ERROR = 11007i32; +pub const WSA_QOS_POLICY_FAILURE: WSA_ERROR = 11011i32; +pub const WSA_QOS_RECEIVERS: WSA_ERROR = 11005i32; +pub const WSA_QOS_REQUEST_CONFIRMED: WSA_ERROR = 11009i32; +pub const WSA_QOS_RESERVED_PETYPE: WSA_ERROR = 11031i32; +pub const WSA_QOS_SENDERS: WSA_ERROR = 11006i32; +pub const WSA_QOS_TRAFFIC_CTRL_ERROR: WSA_ERROR = 11014i32; +pub const WSA_SECURE_HOST_NOT_FOUND: WSA_ERROR = 11032i32; +pub const WSA_WAIT_EVENT_0: WSA_ERROR = 0i32; +pub const WSA_WAIT_IO_COMPLETION: WSA_ERROR = 192i32; +#[repr(C)] +#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +pub struct XSAVE_FORMAT { + pub ControlWord: u16, + pub StatusWord: u16, + pub TagWord: u8, + pub Reserved1: u8, + pub ErrorOpcode: u16, + pub ErrorOffset: u32, + pub ErrorSelector: u16, + pub Reserved2: u16, + pub DataOffset: u32, + pub DataSelector: u16, + pub Reserved3: u16, + pub MxCsr: u32, + pub MxCsr_Mask: u32, + pub FloatRegisters: [M128A; 8], + pub XmmRegisters: [M128A; 16], + pub Reserved4: [u8; 96], +} +#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +impl ::core::marker::Copy for XSAVE_FORMAT {} +#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +impl ::core::clone::Clone for XSAVE_FORMAT { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] +#[cfg(target_arch = "x86")] +pub struct XSAVE_FORMAT { + pub ControlWord: u16, + pub StatusWord: u16, + pub TagWord: u8, + pub Reserved1: u8, + pub ErrorOpcode: u16, + pub ErrorOffset: u32, + pub ErrorSelector: u16, + pub Reserved2: u16, + pub DataOffset: u32, + pub DataSelector: u16, + pub Reserved3: u16, + pub MxCsr: u32, + pub MxCsr_Mask: u32, + pub FloatRegisters: [M128A; 8], + pub XmmRegisters: [M128A; 8], + pub Reserved4: [u8; 224], +} +#[cfg(target_arch = "x86")] +impl ::core::marker::Copy for XSAVE_FORMAT {} +#[cfg(target_arch = "x86")] +impl ::core::clone::Clone for XSAVE_FORMAT { + fn clone(&self) -> Self { + *self + } +} diff --git a/library/std/src/sys/pal/windows/cmath.rs b/library/std/src/sys/pal/windows/cmath.rs new file mode 100644 index 00000000000..36578d5a34e --- /dev/null +++ b/library/std/src/sys/pal/windows/cmath.rs @@ -0,0 +1,96 @@ +#![cfg(not(test))] + +use core::ffi::{c_double, c_float, c_int}; + +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 fn tgamma(n: c_double) -> c_double; + pub fn tgammaf(n: c_float) -> c_float; + pub fn lgamma_r(n: c_double, s: &mut c_int) -> c_double; + pub fn lgammaf_r(n: c_float, s: &mut c_int) -> c_float; +} + +pub use self::shims::*; + +#[cfg(not(all(target_env = "msvc", target_arch = "x86")))] +mod shims { + use core::ffi::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 to 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 core::ffi::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/pal/windows/compat.rs b/library/std/src/sys/pal/windows/compat.rs new file mode 100644 index 00000000000..f60b3a2c700 --- /dev/null +++ b/library/std/src/sys/pal/windows/compat.rs @@ -0,0 +1,244 @@ +//! 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 is implemented simply by storing a function pointer in an atomic. +//! Loading and calling this function will have little or no overhead +//! compared with calling any other dynamically imported function. +//! +//! The stored function pointer starts out as an importer function which will +//! swap itself with the real function when it's called for the first time. If +//! the real function can't be imported then a fallback function is used in its +//! place. While this is low cost for the happy path (where the function is +//! already loaded) it does mean there's some overhead the first time the +//! function is called. In the worst case, multiple threads may all end up +//! importing the same function unnecessarily. + +use crate::ffi::{c_void, CStr}; +use crate::ptr::NonNull; +use crate::sync::atomic::Ordering; +use crate::sys::c; + +// This uses a static initializer to preload some imported functions. +// The CRT (C runtime) executes static initializers before `main` +// is called (for binaries) and before `DllMain` is called (for DLLs). +// +// It works by contributing a global symbol to the `.CRT$XCT` section. +// The linker builds a table of all static initializer functions. +// The CRT startup code then iterates that table, calling each +// initializer function. +// +// NOTE: User code should instead use .CRT$XCU to reliably run after std's initializer. +// If you're reading this and would like a guarantee here, please +// file an issue for discussion; currently we don't guarantee any functionality +// before main. +// See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170 +#[used] +#[link_section = ".CRT$XCT"] +static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init; + +/// Preload some imported functions. +/// +/// Note that any functions included here will be unconditionally loaded in +/// the final binary, regardless of whether or not they're actually used. +/// +/// Therefore, this should be limited to `compat_fn_optional` functions which +/// must be preloaded or any functions where lazier loading demonstrates a +/// negative performance impact in practical situations. +/// +/// Currently we only preload `WaitOnAddress` and `WakeByAddressSingle`. +unsafe extern "C" fn init() { + // In an exe this code is executed before main() so is single threaded. + // In a DLL the system's loader lock will be held thereby synchronizing + // access. So the same best practices apply here as they do to running in DllMain: + // https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices + // + // 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. + + // Attempt to preload the synch functions. + load_synch_functions(); +} + +/// Helper macro for creating CStrs from literals and symbol names. +macro_rules! ansi_str { + (sym $ident:ident) => {{ 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) } +} + +/// 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); +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 { + // SAFETY: A CStr is always null terminated. + let module = c::GetModuleHandleA(name.as_ptr().cast::()); + NonNull::new(module).map(Self) + } + + // Try to get the address of a function. + pub fn proc_address(self, name: &CStr) -> Option> { + unsafe { + // SAFETY: + // `self.0` will always be a valid module. + // A CStr is always null terminated. + let proc = c::GetProcAddress(self.0.as_ptr(), name.as_ptr().cast::()); + // SAFETY: `GetProcAddress` returns None on null. + proc.map(|p| NonNull::new_unchecked(p as *mut c_void)) + } + } +} + +/// 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])* + $vis:vis 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 = 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) -> 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 + } + + #[inline(always)] + pub unsafe fn call($($argname: $argtype),*) -> $rettype { + let func: F = mem::transmute(PTR.load(Ordering::Relaxed)); + func($($argname),*) + } + } + $(#[$meta])* + $vis use $symbol::call as $symbol; + )*) +} + +/// Optionally loaded functions. +/// +/// Actual loading of the function defers to $load_functions. +macro_rules! compat_fn_optional { + ($load_functions:expr; + $( + $(#[$meta:meta])* + $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) $(-> $rettype:ty)?; + )+) => ( + $( + pub mod $symbol { + #[allow(unused_imports)] + use super::*; + use crate::ffi::c_void; + use crate::mem; + use crate::ptr::{self, NonNull}; + use crate::sync::atomic::{AtomicPtr, Ordering}; + + pub(in crate::sys) static PTR: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + + type F = unsafe extern "system" fn($($argtype),*) $(-> $rettype)?; + + #[inline(always)] + pub fn option() -> Option { + // Miri does not understand the way we do preloading + // therefore load the function here instead. + #[cfg(miri)] $load_functions; + NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) }) + } + } + )+ + ) +} + +/// Load all needed functions from "api-ms-win-core-synch-l1-2-0". +pub(super) fn load_synch_functions() { + fn try_load() -> Option<()> { + const MODULE_NAME: &CStr = c"api-ms-win-core-synch-l1-2-0"; + const WAIT_ON_ADDRESS: &CStr = c"WaitOnAddress"; + const WAKE_BY_ADDRESS_SINGLE: &CStr = c"WakeByAddressSingle"; + + // Try loading the library and all the required functions. + // If any step fails, then they all fail. + let library = unsafe { Module::new(MODULE_NAME) }?; + let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?; + let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?; + + c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed); + c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed); + Some(()) + } + + try_load(); +} diff --git a/library/std/src/sys/pal/windows/env.rs b/library/std/src/sys/pal/windows/env.rs new file mode 100644 index 00000000000..f0a99d6200c --- /dev/null +++ b/library/std/src/sys/pal/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/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs new file mode 100644 index 00000000000..42484543686 --- /dev/null +++ b/library/std/src/sys/pal/windows/fs.rs @@ -0,0 +1,1528 @@ +use crate::os::windows::prelude::*; + +use crate::borrow::Cow; +use crate::ffi::{c_void, OsString}; +use crate::fmt; +use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem::{self, MaybeUninit}; +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, Align8}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::thread; + +use super::path::maybe_verbatim; +use super::{api, to_u16s, IoResult}; + +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, + number_of_links: Option, + file_index: Option, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct FileType { + attributes: c::DWORD, + reparse_tag: c::DWORD, +} + +pub struct ReadDir { + handle: FindNextFileHandle, + root: Arc, + first: Option, +} + +struct FindNextFileHandle(c::HANDLE); + +unsafe impl Send for FindNextFileHandle {} +unsafe impl Sync for FindNextFileHandle {} + +pub struct DirEntry { + root: Arc, + 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, + 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, + modified: Option, + created: Option, +} + +impl fmt::Debug for c::FILETIME { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let time = ((self.dwHighDateTime as u64) << 32) | self.dwLowDateTime as u64; + f.debug_tuple("FILETIME").field(&time).finish() + } +} + +#[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; + fn next(&mut self) -> Option> { + if let Some(first) = self.first.take() { + if let Some(e) = DirEntry::new(&self.root, &first) { + return Some(Ok(e)); + } + } + unsafe { + let mut wfd = mem::zeroed(); + loop { + if c::FindNextFileW(self.handle.0, &mut wfd) == 0 { + if api::get_last_error().code == 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, wfd: &c::WIN32_FIND_DATAW) -> Option { + match &wfd.cFileName[0..3] { + // check for '.' and '..' + &[46, 0, ..] | &[46, 46, 0, ..] => return None, + _ => {} + } + + Some(DirEntry { root: root.clone(), data: *wfd }) + } + + pub fn path(&self) -> PathBuf { + self.root.join(self.file_name()) + } + + pub fn file_name(&self) -> OsString { + let filename = super::truncate_utf16_at_nul(&self.data.cFileName); + OsString::from_wide(filename) + } + + pub fn file_type(&self) -> io::Result { + Ok(FileType::new( + self.data.dwFileAttributes, + /* reparse_tag = */ self.data.dwReserved0, + )) + } + + pub fn metadata(&self) -> io::Result { + 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 { + 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 { + 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, + // `CREATE_ALWAYS` has weird semantics so we emulate it using + // `OPEN_ALWAYS` and a manual truncation step. See #115745. + (true, true, false) => c::OPEN_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 { + let path = maybe_verbatim(path)?; + let creation = opts.get_creation_mode()?; + let handle = unsafe { + c::CreateFileW( + path.as_ptr(), + opts.get_access_mode()?, + opts.share_mode, + opts.security_attributes, + creation, + opts.get_flags_and_attributes(), + ptr::null_mut(), + ) + }; + let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) }; + if let Ok(handle) = OwnedHandle::try_from(handle) { + // Manual truncation. See #115745. + if opts.truncate + && creation == c::OPEN_ALWAYS + && unsafe { c::GetLastError() } == c::ERROR_ALREADY_EXISTS + { + unsafe { + // This originally used `FileAllocationInfo` instead of + // `FileEndOfFileInfo` but that wasn't supported by WINE. + // It's arguable which fits the semantics of `OpenOptions` + // better so let's just use the more widely supported method. + let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 }; + let result = c::SetFileInformationByHandle( + handle.as_raw_handle(), + c::FileEndOfFileInfo, + ptr::addr_of!(eof).cast::(), + mem::size_of::() as u32, + ); + if result == 0 { + return Err(io::Error::last_os_error()); + } + } + } + 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 info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 }; + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() + } + + #[cfg(not(target_vendor = "uwp"))] + pub fn file_attr(&self) -> io::Result { + 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 attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed(); + cvt(c::GetFileInformationByHandleEx( + self.handle.as_raw_handle(), + c::FileAttributeTagInfo, + ptr::addr_of_mut!(attr_tag).cast(), + mem::size_of::().try_into().unwrap(), + ))?; + if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + reparse_tag = attr_tag.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 { + 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 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 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 attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed(); + cvt(c::GetFileInformationByHandleEx( + self.handle.as_raw_handle(), + c::FileAttributeTagInfo, + ptr::addr_of_mut!(attr_tag).cast(), + mem::size_of::().try_into().unwrap(), + ))?; + if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + attr.reparse_tag = attr_tag.ReparseTag; + } + } + Ok(attr) + } + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.handle.read(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + 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 { + self.handle.read_at(buf, offset) + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.handle.read_buf(cursor) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.handle.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + 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 { + self.handle.write_at(buf, offset) + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result { + 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 { + Ok(Self { handle: self.handle.try_clone()? }) + } + + // NB: returned pointer is derived from `space`, and has provenance to + // match. A raw pointer is returned rather than a reference in order to + // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`. + fn reparse_point( + &self, + space: &mut Align8<[MaybeUninit]>, + ) -> io::Result<(c::DWORD, *mut c::REPARSE_DATA_BUFFER)> { + unsafe { + let mut bytes = 0; + cvt({ + // Grab this in advance to avoid it invalidating the pointer + // we get from `space.0.as_mut_ptr()`. + let len = space.0.len(); + c::DeviceIoControl( + self.handle.as_raw_handle(), + c::FSCTL_GET_REPARSE_POINT, + ptr::null_mut(), + 0, + space.0.as_mut_ptr().cast(), + len as c::DWORD, + &mut bytes, + ptr::null_mut(), + ) + })?; + const _: () = assert!(core::mem::align_of::() <= 8); + Ok((bytes, space.0.as_mut_ptr().cast::())) + } + } + + fn readlink(&self) -> io::Result { + let mut space = + Align8([MaybeUninit::::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]); + 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: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = + ptr::addr_of_mut!((*buf).rest).cast(); + assert!(info.is_aligned()); + ( + ptr::addr_of_mut!((*info).PathBuffer).cast::(), + (*info).SubstituteNameOffset / 2, + (*info).SubstituteNameLength / 2, + (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0, + ) + } + c::IO_REPARSE_TAG_MOUNT_POINT => { + let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = + ptr::addr_of_mut!((*buf).rest).cast(); + assert!(info.is_aligned()); + ( + ptr::addr_of_mut!((*info).PathBuffer).cast::(), + (*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.add(subst_off.into()); + let subst = slice::from_raw_parts_mut(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]) { + // Turn `\??\` into `\\?\` (a verbatim path). + subst[1] = b'\\' as u16; + // Attempt to convert to a more user-friendly path. + let user = super::args::from_wide_to_user_path( + subst.iter().copied().chain([0]).collect(), + )?; + Ok(PathBuf::from(OsString::from_wide(user.strip_suffix(&[0]).unwrap_or(&user)))) + } else { + Ok(PathBuf::from(OsString::from_wide(subst))) + } + } + } + + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + let info = c::FILE_BASIC_INFO { + CreationTime: 0, + LastAccessTime: 0, + LastWriteTime: 0, + ChangeTime: 0, + FileAttributes: perm.attrs, + }; + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() + } + + 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) + || times.created.map_or(false, is_zero) + { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "Cannot set file timestamp to 0", + )); + } + let is_max = + |t: c::FILETIME| t.dwLowDateTime == c::DWORD::MAX && t.dwHighDateTime == c::DWORD::MAX; + if times.accessed.map_or(false, is_max) + || times.modified.map_or(false, is_max) + || times.created.map_or(false, is_max) + { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "Cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF", + )); + } + cvt(unsafe { + let created = + times.created.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); + let accessed = + times.accessed.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); + let modified = + times.modified.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); + c::SetFileTime(self.as_raw_handle(), created, accessed, modified) + })?; + Ok(()) + } + + /// Get only basic file information such as attributes and file times. + fn basic_info(&self) -> io::Result { + 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 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 info = c::FILE_DISPOSITION_INFO_EX { + Flags: c::FILE_DISPOSITION_FLAG_DELETE + | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS + | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE, + }; + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() + } + + /// 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 info = c::FILE_DISPOSITION_INFO { DeleteFile: c::TRUE as _ }; + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() + } + + /// 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 { + 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: Box; Self::BUFFER_SIZE]>>, +} +impl DirBuff { + const BUFFER_SIZE: usize = 1024; + fn new() -> Self { + Self { + // Safety: `Align8<[MaybeUninit; N]>` does not need + // initialization. + buffer: unsafe { Box::new_uninit().assume_init() }, + } + } + fn capacity(&self) -> usize { + self.buffer.0.len() + } + fn as_mut_ptr(&mut self) -> *mut u8 { + self.buffer.0.as_mut_ptr().cast() + } + /// Returns a `DirBuffIter`. + fn iter(&self) -> DirBuffIter<'_> { + DirBuffIter::new(self) + } +} +impl AsRef<[MaybeUninit]> for DirBuff { + fn as_ref(&self) -> &[MaybeUninit] { + &self.buffer.0 + } +} + +/// An iterator over entries stored in a `DirBuff`. +/// +/// Currently only returns file names (UTF-16 encoded). +struct DirBuffIter<'a> { + buffer: Option<&'a [MaybeUninit]>, + 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 = (Cow<'a, [u16]>, bool); + fn next(&mut self) -> Option { + 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. + // - The OS has guaranteed initialization of the fields of + // `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least + // `FileNameLength` bytes) + let (name, is_directory, next_entry) = unsafe { + let info = buffer.as_ptr().cast::(); + // While this is guaranteed to be aligned in documentation for + // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info + // it does not seem that reality is so kind, and assuming this + // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530) + // presumably, this can be blamed on buggy filesystem drivers, but who knows. + let next_entry = ptr::addr_of!((*info).NextEntryOffset).read_unaligned() as usize; + let length = ptr::addr_of!((*info).FileNameLength).read_unaligned() as usize; + let attrs = ptr::addr_of!((*info).FileAttributes).read_unaligned(); + let name = from_maybe_unaligned( + ptr::addr_of!((*info).FileName).cast::(), + length / size_of::(), + ); + let is_directory = (attrs & 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)), + } + } +} + +unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> { + if p.is_aligned() { + Cow::Borrowed(crate::slice::from_raw_parts(p, len)) + } else { + Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect()) + } +} + +/// 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 { + // 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::PENDING; + let mut 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: &mut 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 for File { + #[inline] + fn as_inner(&self) -> &Handle { + &self.handle + } +} + +impl IntoInner for File { + fn into_inner(self) -> Handle { + self.handle + } +} + +impl FromInner 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 { + Ok(SystemTime::from(self.last_write_time)) + } + + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from(self.last_access_time)) + } + + pub fn created(&self) -> io::Result { + 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 { + self.volume_serial_number + } + + pub fn number_of_links(&self) -> Option { + self.number_of_links + } + + pub fn file_index(&self) -> Option { + self.file_index + } +} +impl From 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()); + } + + pub fn set_created(&mut self, t: SystemTime) { + self.created = 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 { + // We push a `*` to the end of the path which cause the empty path to be + // treated as the current directory. So, for consistency with other platforms, + // we explicitly error on the empty path. + if p.as_os_str().is_empty() { + // Return an error code consistent with other ways of opening files. + // E.g. fs::metadata or File::open. + return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32)); + } + 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 { + 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 { + 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, + ); + // On success, add the handle to the queue. + // If opening the directory fails we treat it the same as a file + if let Ok(child_dir) = child_dir { + dirlist.push(child_dir); + continue; + } + } + 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 { + // 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 { + match metadata(path, ReparsePoint::Follow) { + Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => { + if let Ok(attrs) = lstat(path) { + if !attrs.file_type().is_symlink() { + return Ok(attrs); + } + } + Err(err) + } + result => result, + } +} + +pub fn lstat(path: &Path) -> io::Result { + 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 { + 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 [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)] + .contains(&e.raw_os_error()) => + { + // `ERROR_ACCESS_DENIED` is returned when the user doesn't have permission for the resource. + // One such example is `System Volume Information` as default but can be created as well + // `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 { + 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 { + 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 { + 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::LPCVOID, + ) -> 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, Q: AsRef>( + 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 = + Align8([MaybeUninit::::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]); + let data_ptr = data.0.as_mut_ptr(); + let data_end = data_ptr.add(c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize); + let db = data_ptr.cast::(); + // Zero the header to ensure it's fully initialized, including reserved parameters. + *db = mem::zeroed(); + let reparse_target_slice = { + let buf_start = ptr::addr_of_mut!((*db).ReparseTarget).cast::(); + // Compute offset in bytes and then divide so that we round down + // rather than hit any UB (admittedly this arithmetic should work + // out so that this isn't necessary) + let buf_len_bytes = usize::try_from(data_end.byte_offset_from(buf_start)).unwrap(); + let buf_len_wchars = buf_len_bytes / core::mem::size_of::(); + core::slice::from_raw_parts_mut(buf_start, buf_len_wchars) + }; + + // FIXME: this conversion is very hacky + let iter = br"\??\" + .iter() + .map(|x| *x as u16) + .chain(original.as_os_str().encode_wide()) + .chain(core::iter::once(0)); + let mut i = 0; + for c in iter { + if i >= reparse_target_slice.len() { + return Err(crate::io::const_io_error!( + crate::io::ErrorKind::InvalidFilename, + "Input filename is too long" + )); + } + reparse_target_slice[i] = c; + 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_ptr.cast(), + (*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 { + // 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), + + // `ERROR_CANT_ACCESS_FILE` means that a file exists but that the + // reparse point could not be handled by `CreateFile`. + // This can happen for special files such as: + // * Unix domain sockets which you need to `connect` to + // * App exec links which require using `CreateProcess` + _ if e.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE 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/pal/windows/handle.rs b/library/std/src/sys/pal/windows/handle.rs new file mode 100644 index 00000000000..c4495f81a5a --- /dev/null +++ b/library/std/src/sys/pal/windows/handle.rs @@ -0,0 +1,338 @@ +#![unstable(issue = "none", feature = "windows_handle")] + +#[cfg(test)] +mod tests; + +use crate::cmp; +use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, Read}; +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 { + 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 for Handle { + #[inline] + fn as_inner(&self) -> &OwnedHandle { + &self.0 + } +} + +impl IntoInner for Handle { + fn into_inner(self) -> OwnedHandle { + self.0 + } +} + +impl FromInner 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 { + let res = unsafe { self.synchronous_read(buf.as_mut_ptr().cast(), buf.len(), None) }; + + match res { + Ok(read) => Ok(read), + + // 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 { + 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 { + let res = + unsafe { self.synchronous_read(buf.as_mut_ptr().cast(), buf.len(), Some(offset)) }; + + match res { + Ok(read) => Ok(read), + 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, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + let res = + unsafe { self.synchronous_read(cursor.as_mut().as_mut_ptr(), cursor.capacity(), None) }; + + match res { + Ok(read) => { + // Safety: `read` bytes were written to the initialized portion of the buffer + unsafe { + cursor.advance(read); + } + 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> { + let len = cmp::min(buf.len(), ::MAX as usize) as c::DWORD; + let mut amt = 0; + let res = + cvt(c::ReadFile(self.as_raw_handle(), buf.as_mut_ptr(), 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 { + 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 { + self.synchronous_write(buf, None) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + 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 { + self.synchronous_write(buf, Some(offset)) + } + + pub fn try_clone(&self) -> io::Result { + Ok(Self(self.0.try_clone()?)) + } + + pub fn duplicate( + &self, + access: c::DWORD, + inherit: bool, + options: c::DWORD, + ) -> io::Result { + 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, + len: usize, + offset: Option, + ) -> io::Result { + let mut io_status = c::IO_STATUS_BLOCK::PENDING; + + // 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) -> io::Result { + let mut io_status = c::IO_STATUS_BLOCK::PENDING; + + // 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 { + (**self).read(buf) + } + + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + (**self).read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + (**self).is_read_vectored() + } +} diff --git a/library/std/src/sys/pal/windows/handle/tests.rs b/library/std/src/sys/pal/windows/handle/tests.rs new file mode 100644 index 00000000000..d836dae4c30 --- /dev/null +++ b/library/std/src/sys/pal/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/pal/windows/io.rs b/library/std/src/sys/pal/windows/io.rs new file mode 100644 index 00000000000..649826d25ce --- /dev/null +++ b/library/std/src/sys/pal/windows/io.rs @@ -0,0 +1,161 @@ +use crate::marker::PhantomData; +use crate::mem::size_of; +use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; +use crate::slice; +use crate::sys::c; +use core::ffi::c_void; + +#[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 }, + _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, 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() }, + _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, self.vec.len as usize) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } + } +} + +pub fn is_terminal(h: &impl AsHandle) -> bool { + unsafe { handle_is_console(h.as_handle()) } +} + +unsafe fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { + let handle = handle.as_raw_handle(); + + // A null handle means the process has no console. + if handle.is_null() { + return false; + } + + let mut out = 0; + if c::GetConsoleMode(handle, &mut out) != 0 { + // False positives aren't possible. If we got a console then we definitely have a console. + return true; + } + + // At this point, we *could* have a false negative. We can determine that this is a true + // negative if we can detect the presence of a console on any of the standard I/O streams. If + // another stream has a console, then we know we're in a Windows console and can therefore + // trust the negative. + for std_handle in [c::STD_INPUT_HANDLE, c::STD_OUTPUT_HANDLE, c::STD_ERROR_HANDLE] { + let std_handle = c::GetStdHandle(std_handle); + if !std_handle.is_null() + && std_handle != handle + && c::GetConsoleMode(std_handle, &mut out) != 0 + { + return false; + } + } + + // Otherwise, we fall back to an msys hack to see if we can detect the presence of a pty. + msys_tty_on(handle) +} + +unsafe fn msys_tty_on(handle: c::HANDLE) -> bool { + // Early return if the handle is not a pipe. + if c::GetFileType(handle) != c::FILE_TYPE_PIPE { + return false; + } + + /// Mirrors [`FILE_NAME_INFO`], giving it a fixed length that we can stack + /// allocate + /// + /// [`FILE_NAME_INFO`]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_name_info + #[repr(C)] + #[allow(non_snake_case)] + struct FILE_NAME_INFO { + FileNameLength: u32, + FileName: [u16; c::MAX_PATH as usize], + } + let mut name_info = FILE_NAME_INFO { FileNameLength: 0, FileName: [0; c::MAX_PATH as usize] }; + // Safety: buffer length is fixed. + let res = c::GetFileInformationByHandleEx( + handle, + c::FileNameInfo, + &mut name_info as *mut _ as *mut c_void, + size_of::() as u32, + ); + if res == 0 { + return false; + } + + // Use `get` because `FileNameLength` can be out of range. + let s = match name_info.FileName.get(..name_info.FileNameLength as usize / 2) { + None => return false, + Some(s) => s, + }; + let name = String::from_utf16_lossy(s); + // Get the file name only. + let name = name.rsplit('\\').next().unwrap_or(&name); + // This checks whether 'pty' exists in the file name, which indicates that + // a pseudo-terminal is attached. To mitigate against false positives + // (e.g., an actual file name that contains 'pty'), we also require that + // the file name begins with either the strings 'msys-' or 'cygwin-'.) + let is_msys = name.starts_with("msys-") || name.starts_with("cygwin-"); + let is_pty = name.contains("-pty"); + is_msys && is_pty +} diff --git a/library/std/src/sys/pal/windows/locks/condvar.rs b/library/std/src/sys/pal/windows/locks/condvar.rs new file mode 100644 index 00000000000..66fafa2c00b --- /dev/null +++ b/library/std/src/sys/pal/windows/locks/condvar.rs @@ -0,0 +1,50 @@ +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, +} + +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 fn notify_one(&self) { + unsafe { c::WakeConditionVariable(self.inner.get()) } + } + + #[inline] + pub fn notify_all(&self) { + unsafe { c::WakeAllConditionVariable(self.inner.get()) } + } +} diff --git a/library/std/src/sys/pal/windows/locks/mod.rs b/library/std/src/sys/pal/windows/locks/mod.rs new file mode 100644 index 00000000000..0e0f9eccb21 --- /dev/null +++ b/library/std/src/sys/pal/windows/locks/mod.rs @@ -0,0 +1,6 @@ +mod condvar; +mod mutex; +mod rwlock; +pub use condvar::Condvar; +pub use mutex::Mutex; +pub use rwlock::RwLock; diff --git a/library/std/src/sys/pal/windows/locks/mutex.rs b/library/std/src/sys/pal/windows/locks/mutex.rs new file mode 100644 index 00000000000..ef2f84082cd --- /dev/null +++ b/library/std/src/sys/pal/windows/locks/mutex.rs @@ -0,0 +1,54 @@ +//! 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, +} + +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 fn lock(&self) { + unsafe { + c::AcquireSRWLockExclusive(raw(self)); + } + } + + #[inline] + pub fn try_lock(&self) -> bool { + unsafe { c::TryAcquireSRWLockExclusive(raw(self)) != 0 } + } + + #[inline] + pub unsafe fn unlock(&self) { + c::ReleaseSRWLockExclusive(raw(self)); + } +} diff --git a/library/std/src/sys/pal/windows/locks/rwlock.rs b/library/std/src/sys/pal/windows/locks/rwlock.rs new file mode 100644 index 00000000000..e69415baac4 --- /dev/null +++ b/library/std/src/sys/pal/windows/locks/rwlock.rs @@ -0,0 +1,40 @@ +use crate::cell::UnsafeCell; +use crate::sys::c; + +pub struct RwLock { + inner: UnsafeCell, +} + +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 fn read(&self) { + unsafe { c::AcquireSRWLockShared(self.inner.get()) } + } + #[inline] + pub fn try_read(&self) -> bool { + unsafe { c::TryAcquireSRWLockShared(self.inner.get()) != 0 } + } + #[inline] + pub fn write(&self) { + unsafe { c::AcquireSRWLockExclusive(self.inner.get()) } + } + #[inline] + pub fn try_write(&self) -> bool { + unsafe { 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/pal/windows/memchr.rs b/library/std/src/sys/pal/windows/memchr.rs new file mode 100644 index 00000000000..b9e5bcc1b4b --- /dev/null +++ b/library/std/src/sys/pal/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/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs new file mode 100644 index 00000000000..8b722f01a5d --- /dev/null +++ b/library/std/src/sys/pal/windows/mod.rs @@ -0,0 +1,357 @@ +#![allow(missing_docs, nonstandard_style)] + +use crate::ffi::{OsStr, OsString}; +use crate::io::ErrorKind; +use crate::mem::MaybeUninit; +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 stdio; +pub mod thread; +pub mod thread_local_dtor; +pub mod thread_local_key; +pub mod thread_parking; +pub mod time; +cfg_if::cfg_if! { + if #[cfg(not(target_vendor = "uwp"))] { + pub mod stack_overflow; + } else { + pub mod stack_overflow_uwp; + pub use self::stack_overflow_uwp as stack_overflow; + } +} + +mod api; + +/// Map a Result to io::Result. +trait IoResult { + fn io_result(self) -> crate::io::Result; +} +impl IoResult for Result { + fn io_result(self) -> crate::io::Result { + self.map_err(|e| crate::io::Error::from_raw_os_error(e.code as i32)) + } +} + +// 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, _sigpipe: 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(&c"main"); +} + +// 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(); +} + +#[inline] +pub fn is_interrupted(_errno: i32) -> bool { + false +} + +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 + | c::ERROR_PATH_NOT_FOUND + | c::ERROR_INVALID_DRIVE + | c::ERROR_BAD_NETPATH + | c::ERROR_BAD_NET_NAME => return NotFound, + c::ERROR_NO_DATA => return BrokenPipe, + c::ERROR_INVALID_NAME | c::ERROR_BAD_PATHNAME => 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 { + 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: S) -> crate::io::Result> { + fn inner(s: &OsStr) -> crate::io::Result> { + // 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(mut f1: F1, f2: F2) -> crate::io::Result +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: [MaybeUninit; 512] = MaybeUninit::uninit_array(); + let mut heap_buf: Vec> = 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); + // We used `reserve` and not `reserve_exact`, so in theory we + // may have gotten more than requested. If so, we'd like to use + // it... so long as we won't cause overflow. + n = heap_buf.capacity().min(c::DWORD::MAX as usize); + // Safety: MaybeUninit does not need initialization + 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().cast::(), n as c::DWORD) { + 0 if api::get_last_error().code == 0 => 0, + 0 => return Err(crate::io::Error::last_os_error()), + n => n, + } as usize; + if k == n && api::get_last_error().code == c::ERROR_INSUFFICIENT_BUFFER { + n = n.saturating_mul(2).min(c::DWORD::MAX as usize); + } 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 { + // Safety: First `k` values are initialized. + let slice: &[u16] = MaybeUninit::slice_assume_init_ref(&buf[..k]); + return Ok(f2(slice)); + } + } + } +} + +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: I) -> crate::io::Result { + 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 > ::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(); +} + +/// Align the inner value to 8 bytes. +/// +/// This is enough for almost all of the buffers we're likely to work with in +/// the Windows APIs we use. +#[repr(C, align(8))] +#[derive(Copy, Clone)] +pub(crate) struct Align8(pub T); diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs new file mode 100644 index 00000000000..6cd758ec5c3 --- /dev/null +++ b/library/std/src/sys/pal/windows/net.rs @@ -0,0 +1,497 @@ +#![unstable(issue = "none", feature = "windows_net")] + +use crate::cmp; +use crate::io::{self, BorrowedBuf, BorrowedCursor, 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 core::ffi::{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 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: T) -> io::Result { + 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(mut f: F) -> io::Result +where + T: IsMinusOne, + F: FnMut() -> T, +{ + cvt(f()) +} + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + 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)) } + } 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.0.set_no_inherit()?; + Ok(socket) + } + } + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) }; + cvt(result).map(drop) + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let result = self.connect(addr); + 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: cmp::min(timeout.as_secs(), c_long::MAX as u64) as c_long, + tv_usec: timeout.subsec_micros() as c_long, + }; + + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + + let fds = { + let mut fds = unsafe { mem::zeroed::() }; + fds.fd_count = 1; + fds.fd_array[0] = self.as_raw(); + 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 { + let socket = unsafe { c::accept(self.as_raw(), storage, len) }; + + match socket { + c::INVALID_SOCKET => Err(last_error()), + _ => unsafe { Ok(Self::from_raw(socket)) }, + } + } + + pub fn duplicate(&self) -> io::Result { + Ok(Self(self.0.try_clone()?)) + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { + // 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.capacity(), i32::MAX as usize) as i32; + let result = + unsafe { c::recv(self.as_raw(), buf.as_mut().as_mut_ptr() as *mut _, length, flags) }; + + match result { + c::SOCKET_ERROR => { + let error = unsafe { c::WSAGetLastError() }; + + if error == c::WSAESHUTDOWN { + Ok(()) + } else { + Err(io::Error::from_raw_os_error(error)) + } + } + _ => { + unsafe { buf.advance(result as usize) }; + Ok(()) + } + } + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + // 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(), + bufs.as_mut_ptr() as *mut c::WSABUF, + length, + &mut nread, + &mut flags, + ptr::null_mut(), + None, + ) + }; + + 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 { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), c::MSG_PEEK)?; + Ok(buf.len()) + } + + fn recv_from_with_flags( + &self, + buf: &mut [u8], + flags: c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut storage = unsafe { mem::zeroed::() }; + let mut addrlen = mem::size_of_val(&storage) as c::socklen_t; + let length = cmp::min(buf.len(), ::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(), + 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 { + 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(), + bufs.as_ptr() as *const c::WSABUF as *mut _, + length, + &mut nwritten, + 0, + ptr::null_mut(), + None, + ) + }; + cvt(result).map(|_| nwritten as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn set_timeout(&self, dur: Option, 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> { + 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(), 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(), c::FIONBIO as c_int, &mut nonblocking) }; + cvt(result).map(drop) + } + + pub fn set_linger(&self, linger: Option) -> 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> { + 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 { + let raw: c::BOOL = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn take_error(&self) -> io::Result> { + 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) -> c::SOCKET { + debug_assert_eq!(mem::size_of::(), mem::size_of::()); + debug_assert_eq!(mem::align_of::(), mem::align_of::()); + self.as_inner().as_raw_socket() as c::SOCKET + } + pub unsafe fn from_raw(raw: c::SOCKET) -> Self { + debug_assert_eq!(mem::size_of::(), mem::size_of::()); + debug_assert_eq!(mem::align_of::(), mem::align_of::()); + Self::from_raw_socket(raw as RawSocket) + } +} + +#[unstable(reason = "not public", issue = "none", feature = "fd_read")] +impl<'a> Read for &'a Socket { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &OwnedSocket { + &self.0 + } +} + +impl FromInner for Socket { + fn from_inner(sock: OwnedSocket) -> Socket { + Socket(sock) + } +} + +impl IntoInner 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/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs new file mode 100644 index 00000000000..829dd5eb97a --- /dev/null +++ b/library/std/src/sys/pal/windows/os.rs @@ -0,0 +1,368 @@ +//! 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::{api, to_u16s}; + +pub fn errno() -> i32 { + api::get_last_error().code as i32 +} + +/// Gets a detailed string description for the given error number. +pub fn error_string(mut errnum: i32) -> String { + 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, + 0, + 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 0 as 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, + iter: EnvIterator, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. +pub struct EnvStrDebug<'a> { + iter: &'a EnvIterator, +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + let iter: EnvIterator = (*iter).clone(); + let mut list = f.debug_list(); + for (a, b) in iter { + list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); + } + list.finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { base: _, iter } = self; + EnvStrDebug { iter } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { base: _, iter } = self; + f.debug_list().entries(iter.clone()).finish() + } +} + +impl Iterator for Env { + type Item = (OsString, OsString); + + fn next(&mut self) -> Option<(OsString, OsString)> { + let Self { base: _, iter } = self; + iter.next() + } +} + +#[derive(Clone)] +struct EnvIterator(c::LPWCH); + +impl Iterator for EnvIterator { + type Item = (OsString, OsString); + + fn next(&mut self) -> Option<(OsString, OsString)> { + let Self(cur) = self; + loop { + unsafe { + if **cur == 0 { + return None; + } + let p = *cur as *const u16; + let mut len = 0; + while *p.add(len) != 0 { + len += 1; + } + let s = slice::from_raw_parts(p, len); + *cur = cur.add(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, iter: EnvIterator(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 { + // 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(paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + 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::>(); + 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 { + super::fill_utf16_buf( + |buf, sz| unsafe { c::GetModuleFileNameW(ptr::null_mut(), buf, sz) }, + super::os2path, + ) +} + +pub fn getcwd() -> io::Result { + 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::>(); + p.push(0); + + cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop) +} + +pub fn getenv(k: &OsStr) -> Option { + let k = to_u16s(k).ok()?; + super::fill_utf16_buf( + |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) }, + OsStringExt::from_wide, + ) + .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 { + 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 api::get_last_error().code != 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 { + None +} + +pub fn home_dir() -> Option { + 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() } +} diff --git a/library/std/src/sys/pal/windows/os/tests.rs b/library/std/src/sys/pal/windows/os/tests.rs new file mode 100644 index 00000000000..458d6e11c20 --- /dev/null +++ b/library/std/src/sys/pal/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/pal/windows/os_str.rs b/library/std/src/sys/pal/windows/os_str.rs new file mode 100644 index 00000000000..237854fac4e --- /dev/null +++ b/library/std/src/sys/pal/windows/os_str.rs @@ -0,0 +1,245 @@ +/// 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 for Buf { + fn into_inner(self) -> Wtf8Buf { + self.inner + } +} + +impl FromInner for Buf { + fn from_inner(inner: Wtf8Buf) -> Self { + Buf { inner } + } +} + +impl AsInner for Buf { + #[inline] + 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 { + #[inline] + pub fn into_encoded_bytes(self) -> Vec { + self.inner.into_bytes() + } + + #[inline] + pub unsafe fn from_encoded_bytes_unchecked(s: Vec) -> Self { + Self { inner: Wtf8Buf::from_bytes_unchecked(s) } + } + + 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 { + 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 { + unsafe { mem::transmute(self.inner.into_box()) } + } + + #[inline] + pub fn from_box(boxed: Box) -> Buf { + let inner: Box = unsafe { mem::transmute(boxed) }; + Buf { inner: Wtf8Buf::from_box(inner) } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc { + self.as_slice().into_rc() + } +} + +impl Slice { + #[inline] + pub fn as_encoded_bytes(&self) -> &[u8] { + self.inner.as_bytes() + } + + #[inline] + pub unsafe fn from_encoded_bytes_unchecked(s: &[u8]) -> &Slice { + mem::transmute(Wtf8::from_bytes_unchecked(s)) + } + + #[inline] + pub fn from_str(s: &str) -> &Slice { + unsafe { mem::transmute(Wtf8::from_str(s)) } + } + + pub fn to_str(&self) -> Result<&str, crate::str::Utf8Error> { + self.inner.as_str() + } + + pub fn to_string_lossy(&self) -> Cow<'_, str> { + self.inner.to_string_lossy() + } + + pub fn to_owned(&self) -> Buf { + Buf { inner: self.inner.to_owned() } + } + + pub fn clone_into(&self, buf: &mut Buf) { + self.inner.clone_into(&mut buf.inner) + } + + #[inline] + pub fn into_box(&self) -> Box { + unsafe { mem::transmute(self.inner.into_box()) } + } + + pub fn empty_box() -> Box { + unsafe { mem::transmute(Wtf8::empty_box()) } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + let arc = self.inner.into_arc(); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } + } + + #[inline] + pub fn into_rc(&self) -> Rc { + 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/pal/windows/path.rs b/library/std/src/sys/pal/windows/path.rs new file mode 100644 index 00000000000..d9684f21753 --- /dev/null +++ b/library/std/src/sys/pal/windows/path.rs @@ -0,0 +1,344 @@ +use super::{c, fill_utf16_buf, to_u16s}; +use crate::ffi::{OsStr, OsString}; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; +use crate::ptr; + +#[cfg(test)] +mod tests; + +pub const MAIN_SEP_STR: &str = "\\"; +pub const MAIN_SEP: char = '\\'; + +#[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.as_encoded_bytes().iter().copied().any(is_sep_byte) +} +pub(crate) fn has_trailing_slash(path: &OsStr) -> bool { + let is_verbatim = path.as_encoded_bytes().starts_with(br"\\?\"); + let is_separator = if is_verbatim { is_verbatim_sep } else { is_sep_byte }; + if let Some(&c) = path.as_encoded_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.as_encoded_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.prefix[self.index..] + .starts_with(prefix.as_bytes()) + .then_some(Self { index: self.index + prefix.len(), ..*self }) + } + + fn prefix_bytes(&self) -> &'a [u8] { + &self.path.as_encoded_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 { OsStr::from_encoded_bytes_unchecked(&self.path.as_encoded_bytes()[self.index..]) } + } +} + +pub fn parse_prefix(path: &OsStr) -> Option> { + 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 it has a drive like `C:` then it's a disk. + // Otherwise there is no prefix. + parse_drive(path).map(Disk) + } +} + +// Parses a drive prefix, e.g. "C:" and "C:\whatever" +fn parse_drive(path: &OsStr) -> Option { + // In most DOS systems, it is not possible to have more than 26 drive letters. + // See . + fn is_valid_drive_letter(drive: &u8) -> bool { + drive.is_ascii_alphabetic() + } + + match path.as_encoded_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 { + // only parse two bytes: the drive letter and the drive separator + if path.as_encoded_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.as_encoded_bytes().iter().position(|&x| separator(x)) { + Some(separator_start) => { + let separator_end = separator_start + 1; + + let component = &path.as_encoded_bytes()[..separator_start]; + + // Panic safe + // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index. + let path = &path.as_encoded_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 { + ( + OsStr::from_encoded_bytes_unchecked(component), + OsStr::from_encoded_bytes_unchecked(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> { + let path = to_u16s(path)?; + get_long_path(path, true) +} + +/// Get a normalized absolute path that can bypass path length limits. +/// +/// Setting prefer_verbatim to true suggests a stronger preference for verbatim +/// paths even when not strictly necessary. This allows the Windows API to avoid +/// repeating our work. However, if the path may be given back to users or +/// passed to other application then it's preferable to use non-verbatim paths +/// when possible. Non-verbatim paths are better understood by users and handled +/// by more software. +pub(crate) fn get_long_path(mut path: Vec, prefer_verbatim: bool) -> io::Result> { + // 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]; + + 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(); + + // Only prepend the prefix if needed. + if prefer_verbatim || absolute.len() + 1 >= LEGACY_MAX_PATH { + // 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); + } else { + path.reserve_exact(absolute.len() + 1); + } + path.extend_from_slice(absolute); + path.push(0); + }, + )?; + Ok(path) +} + +/// Make a Windows path absolute. +pub(crate) fn absolute(path: &Path) -> io::Result { + 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.as_encoded_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/pal/windows/path/tests.rs b/library/std/src/sys/pal/windows/path/tests.rs new file mode 100644 index 00000000000..623c6236166 --- /dev/null +++ b/library/std/src/sys/pal/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> { + 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 information. +#[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/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs new file mode 100644 index 00000000000..7624e746f5c --- /dev/null +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -0,0 +1,571 @@ +use crate::os::windows::prelude::*; + +use crate::ffi::OsStr; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; +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::{FromInner, IntoInner}; + +//////////////////////////////////////////////////////////////////////////////// +// Anonymous pipes +//////////////////////////////////////////////////////////////////////////////// + +pub struct AnonPipe { + inner: Handle, +} + +impl IntoInner for AnonPipe { + fn into_inner(self) -> Handle { + self.inner + } +} + +impl FromInner for AnonPipe { + fn from_inner(inner: Handle) -> AnonPipe { + 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 { + // 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::>(); + 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::(); + 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 { + // 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.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner }) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + 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_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { + let result = unsafe { + let len = crate::cmp::min(buf.capacity(), c::DWORD::MAX as usize) as c::DWORD; + self.alertable_io_internal(c::ReadFileEx, buf.as_mut().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(()), + Err(e) => Err(e), + Ok(n) => { + unsafe { + buf.advance(n); + } + Ok(()) + } + } + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + self.handle().read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + 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 { + 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 { + // 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 = None; + struct AsyncResult { + error: u32, + transferred: u32, + } + + // STEP 3: The callback. + unsafe extern "system" fn callback( + dwErrorCode: u32, + dwNumberOfBytesTransferred: u32, + lpOverlapped: *mut c::OVERLAPPED, + ) { + // Set `async_result` using a pointer smuggled through `hEvent`. + let result = + AsyncResult { error: dwErrorCode, transferred: dwNumberOfBytesTransferred }; + *(*lpOverlapped).hEvent.cast::>() = 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, Some(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.transferred as usize), + error => Err(io::Error::from_raw_os_error(error as _)), + } + } +} + +pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> 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, // needs a stable address + dst: &'a mut Vec, + state: State, +} + +#[derive(PartialEq, Debug)] +enum State { + NotReading, + Reading, + Read(usize), +} + +impl<'a> AsyncPipe<'a> { + fn new(pipe: Handle, dst: &'a mut Vec) -> io::Result> { + // 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 = 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 { + 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 { + 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) -> &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/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs new file mode 100644 index 00000000000..9ec775959fd --- /dev/null +++ b/library/std/src/sys/pal/windows/process.rs @@ -0,0 +1,984 @@ +#![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::mem::MaybeUninit; +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::sync::Mutex; +use crate::sys::args::{self, Arg}; +use crate::sys::c::{self, NonZeroDWORD, EXIT_FAILURE, EXIT_SUCCESS}; +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::process::{CommandEnv, CommandEnvs}; +use crate::sys_common::IntoInner; + +use core::ffi::c_void; + +//////////////////////////////////////////////////////////////////////////////// +// 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, +} + +impl EnvKey { + fn new>(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 { + 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 for EnvKey { + fn partial_cmp(&self, other: &str) -> Option { + Some(self.cmp(&EnvKey::new(other))) + } +} +impl PartialEq 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 for EnvKey { + fn from(k: OsString) -> Self { + EnvKey { utf16: k.encode_wide().collect(), os_string: k } + } +} + +impl From 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 for EnvKey { + fn as_ref(&self) -> &OsStr { + &self.os_string + } +} + +pub(crate) fn ensure_no_nuls>(str: T) -> io::Result { + 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, + env: CommandEnv, + cwd: Option, + flags: u32, + detach: bool, // not currently exposed in std::process + stdin: Option, + stdout: Option, + stderr: Option, + force_quotes_enabled: bool, + proc_thread_attributes: BTreeMap, +} + +pub enum Stdio { + Inherit, + InheritSpecific { from_stdio_id: c::DWORD }, + Null, + MakePipe, + Pipe(AnonPipe), + Handle(Handle), +} + +pub struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + +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, + proc_thread_attributes: Default::default(), + } + } + + 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(Path::new) + } + + pub unsafe fn raw_attribute( + &mut self, + attribute: usize, + value: T, + ) { + self.proc_thread_attributes.insert( + attribute, + ProcThreadAttributeValue { size: mem::size_of::(), data: Box::new(value) }, + ); + } + + pub fn spawn( + &mut self, + default: Stdio, + needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + let maybe_env = self.env.capture_if_changed(); + + 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(&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: Mutex<()> = Mutex::new(()); + + let _guard = 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)?; + + let mut si = zeroed_startupinfo(); + + // If at least one of stdin, stdout or stderr are set (i.e. are non null) + // then set the `hStd` fields in `STARTUPINFO`. + // Otherwise skip this and allow the OS to apply its default behaviour. + // This provides more consistent behaviour between Win7 and Win8+. + let is_set = |stdio: &Handle| !stdio.as_raw_handle().is_null(); + if is_set(&stderr) || is_set(&stdout) || is_set(&stdin) { + si.dwFlags |= c::STARTF_USESTDHANDLES; + si.hStdInput = stdin.as_raw_handle(); + si.hStdOutput = stdout.as_raw_handle(); + si.hStdError = stderr.as_raw_handle(); + } + + let si_ptr: *mut c::STARTUPINFOW; + + let mut proc_thread_attribute_list; + let mut si_ex; + + if !self.proc_thread_attributes.is_empty() { + si.cb = mem::size_of::() as u32; + flags |= c::EXTENDED_STARTUPINFO_PRESENT; + + proc_thread_attribute_list = + make_proc_thread_attribute_list(&self.proc_thread_attributes)?; + si_ex = c::STARTUPINFOEXW { + StartupInfo: si, + lpAttributeList: proc_thread_attribute_list.0.as_mut_ptr() as _, + }; + si_ptr = &mut si_ex as *mut _ as _; + } else { + si.cb = mem::size_of::() as c::DWORD; + si_ptr = &mut si as *mut _ as _; + } + + unsafe { + cvt(c::CreateProcessW( + program.as_ptr(), + cmd_str.as_mut_ptr(), + ptr::null_mut(), + ptr::null_mut(), + c::TRUE, + flags, + envp, + dirp, + si_ptr, + &mut pi, + )) + }?; + + unsafe { + Ok(( + Process { + handle: Handle::from_raw_handle(pi.hProcess), + main_thread_handle: Handle::from_raw_handle(pi.hThread), + }, + pipes, + )) + } + } + + pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { + let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; + crate::sys_common::process::wait_with_output(proc, 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, + child_paths: Option<&OsStr>, +) -> io::Result> { + // 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.as_encoded_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 args::to_user_path(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 args::to_user_path(&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.as_encoded_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( + parent_paths: Paths, + child_paths: Option<&OsStr>, + mut exists: Exists, +) -> Option> +where + Paths: FnOnce() -> Option, + Exists: FnMut(PathBuf) -> Option>, +{ + // 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> { + unsafe { + let path = args::to_user_path(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) -> io::Result { + let use_stdio_id = |stdio_id| 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 + }, + // If no stdio handle is available, then propagate the null value. + Err(..) => unsafe { Ok(Handle::from_raw_handle(ptr::null_mut())) }, + }; + match *self { + Stdio::Inherit => use_stdio_id(stdio_id), + Stdio::InheritSpecific { from_stdio_id } => use_stdio_id(from_stdio_id), + + 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::(); + 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(r"\\.\NUL"), &opts).map(|file| file.into_inner()) + } + } + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + Stdio::Pipe(pipe) + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + Stdio::Handle(file.into_inner()) + } +} + +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + Stdio::InheritSpecific { from_stdio_id: c::STD_OUTPUT_HANDLE } + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + Stdio::InheritSpecific { from_stdio_id: c::STD_ERROR_HANDLE } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +/// A value representing a child process. +/// +/// The lifetime of this value is linked to the lifetime of the actual +/// process - the Process destructor calls self.finish() which waits +/// for the process to terminate. +pub struct Process { + handle: Handle, + main_thread_handle: Handle, +} + +impl Process { + pub fn kill(&mut self) -> io::Result<()> { + let result = unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) }; + if result == c::FALSE { + let error = unsafe { c::GetLastError() }; + // TerminateProcess returns ERROR_ACCESS_DENIED if the process has already been + // terminated (by us, or for any other reason). So check if the process was actually + // terminated, and if so, do not return an error. + if error != c::ERROR_ACCESS_DENIED || self.try_wait().is_err() { + return Err(crate::io::Error::from_raw_os_error(error as i32)); + } + } + Ok(()) + } + + pub fn id(&self) -> u32 { + unsafe { c::GetProcessId(self.handle.as_raw_handle()) } + } + + pub fn main_thread_handle(&self) -> BorrowedHandle<'_> { + self.main_thread_handle.as_handle() + } + + pub fn wait(&mut self) -> io::Result { + 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> { + 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, Default)] +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 { + Some(self.0 as i32) + } +} + +/// Converts a raw `c::DWORD` to a type-safe `ExitStatus` by wrapping it without copying. +impl From 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 for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option { + 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 for ExitCode { + fn from(code: u8) -> Self { + ExitCode(c::DWORD::from(code)) + } +} + +impl From for ExitCode { + fn from(code: u32) -> Self { + ExitCode(c::DWORD::from(code)) + } +} + +fn zeroed_startupinfo() -> c::STARTUPINFOW { + c::STARTUPINFOW { + cb: 0, + lpReserved: ptr::null_mut(), + lpDesktop: ptr::null_mut(), + lpTitle: ptr::null_mut(), + dwX: 0, + dwY: 0, + dwXSize: 0, + dwYSize: 0, + dwXCountChars: 0, + dwYCountChars: 0, + dwFillAttribute: 0, + dwFlags: 0, + wShowWindow: 0, + cbReserved2: 0, + lpReserved2: ptr::null_mut(), + hStdInput: ptr::null_mut(), + hStdOutput: ptr::null_mut(), + hStdError: ptr::null_mut(), + } +} + +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> { + // Encode the command and arguments in a command line string such + // that the spawned process may recover them using CommandLineToArgvW. + let mut cmd: Vec = Vec::new(); + + // 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> { + let mut system: Vec = 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>) -> io::Result<(*mut c_void, Vec)> { + // On Windows we pass an "environment block" which is not a char**, but + // rather a concatenation of null-terminated k=v\0 sequences, with a final + // \0 to terminate. + 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)> { + match d { + Some(dir) => { + let mut dir_str: Vec = ensure_no_nuls(dir)?.encode_wide().collect(); + dir_str.push(0); + Ok((dir_str.as_ptr(), dir_str)) + } + None => Ok((ptr::null(), Vec::new())), + } +} + +struct ProcThreadAttributeList(Box<[MaybeUninit]>); + +impl Drop for ProcThreadAttributeList { + fn drop(&mut self) { + let lp_attribute_list = self.0.as_mut_ptr() as _; + unsafe { c::DeleteProcThreadAttributeList(lp_attribute_list) } + } +} + +/// Wrapper around the value data to be used as a Process Thread Attribute. +struct ProcThreadAttributeValue { + data: Box, + size: usize, +} + +fn make_proc_thread_attribute_list( + attributes: &BTreeMap, +) -> io::Result { + // To initialize our ProcThreadAttributeList, we need to determine + // how many bytes to allocate for it. The Windows API simplifies this process + // by allowing us to call `InitializeProcThreadAttributeList` with + // a null pointer to retrieve the required size. + let mut required_size = 0; + let Ok(attribute_count) = attributes.len().try_into() else { + return Err(io::const_io_error!( + ErrorKind::InvalidInput, + "maximum number of ProcThreadAttributes exceeded", + )); + }; + unsafe { + c::InitializeProcThreadAttributeList( + ptr::null_mut(), + attribute_count, + 0, + &mut required_size, + ) + }; + + let mut proc_thread_attribute_list = + ProcThreadAttributeList(vec![MaybeUninit::uninit(); required_size].into_boxed_slice()); + + // Once we've allocated the necessary memory, it's safe to invoke + // `InitializeProcThreadAttributeList` to properly initialize the list. + cvt(unsafe { + c::InitializeProcThreadAttributeList( + proc_thread_attribute_list.0.as_mut_ptr() as *mut _, + attribute_count, + 0, + &mut required_size, + ) + })?; + + // # Add our attributes to the buffer. + // It's theoretically possible for the attribute count to exceed a u32 value. + // Therefore, we ensure that we don't add more attributes than the buffer was initialized for. + for (&attribute, value) in attributes.iter().take(attribute_count as usize) { + let value_ptr = &*value.data as *const (dyn Send + Sync) as _; + cvt(unsafe { + c::UpdateProcThreadAttribute( + proc_thread_attribute_list.0.as_mut_ptr() as _, + 0, + attribute, + value_ptr, + value.size, + ptr::null_mut(), + ptr::null_mut(), + ) + })?; + } + + Ok(proc_thread_attribute_list) +} + +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) { + 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/pal/windows/process/tests.rs b/library/std/src/sys/pal/windows/process/tests.rs new file mode 100644 index 00000000000..3fc0c75240c --- /dev/null +++ b/library/std/src/sys/pal/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::>(), + 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("".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/pal/windows/rand.rs b/library/std/src/sys/pal/windows/rand.rs new file mode 100644 index 00000000000..5d8fd13785a --- /dev/null +++ b/library/std/src/sys/pal/windows/rand.rs @@ -0,0 +1,42 @@ +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 c::nt_success(ret) { v } else { fallback_rng() } +} + +/// Generate random numbers using the fallback RNG function (RtlGenRandom) +/// +/// This is necessary because of a failure to load the SysWOW64 variant of the +/// bcryptprimitives.dll library from code that lives in bcrypt.dll +/// See +#[cfg(not(target_vendor = "uwp"))] +#[inline(never)] +fn fallback_rng() -> (u64, u64) { + use crate::ffi::c_void; + use crate::io; + + let mut v = (0, 0); + let ret = unsafe { + c::RtlGenRandom(&mut v as *mut _ as *mut c_void, 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/pal/windows/stack_overflow.rs b/library/std/src/sys/pal/windows/stack_overflow.rs new file mode 100644 index 00000000000..627763da856 --- /dev/null +++ b/library/std/src/sys/pal/windows/stack_overflow.rs @@ -0,0 +1,44 @@ +#![cfg_attr(test, allow(dead_code))] + +use crate::sys::c; +use crate::thread; + +use super::api; + +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 + && api::get_last_error().code != c::ERROR_CALL_NOT_IMPLEMENTED + { + panic!("failed to reserve stack space for exception handling"); + } + Handler + } +} + +unsafe 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("") + ); + } + c::EXCEPTION_CONTINUE_SEARCH + } +} + +pub unsafe fn init() { + if c::AddVectoredExceptionHandler(0, Some(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/pal/windows/stack_overflow_uwp.rs b/library/std/src/sys/pal/windows/stack_overflow_uwp.rs new file mode 100644 index 00000000000..afdf7f566ae --- /dev/null +++ b/library/std/src/sys/pal/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/pal/windows/stdio.rs b/library/std/src/sys/pal/windows/stdio.rs new file mode 100644 index 00000000000..819a48266d9 --- /dev/null +++ b/library/std/src/sys/pal/windows/stdio.rs @@ -0,0 +1,462 @@ +#![unstable(issue = "none", feature = "windows_stdio")] + +use crate::cmp; +use crate::io; +use crate::mem::MaybeUninit; +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 crate::sys::windows::api; +use core::str::utf8_char_width; + +#[cfg(test)] +mod tests; + +// 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 { + 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 { + 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 { + debug_assert!(!utf8.is_empty()); + + let mut utf16 = [MaybeUninit::::uninit(); MAX_BUFFER_SIZE / 2]; + let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len())]; + + let utf16: &[u16] = unsafe { + // Note that this theoretically checks validity twice in the (most common) case + // where the underlying byte sequence is valid utf-8 (given the check in `write()`). + let result = c::MultiByteToWideChar( + c::CP_UTF8, // CodePage + c::MB_ERR_INVALID_CHARS, // dwFlags + utf8.as_ptr(), // lpMultiByteStr + utf8.len() as c::c_int, // cbMultiByte + utf16.as_mut_ptr() as c::LPWSTR, // lpWideCharStr + utf16.len() as c::c_int, // cchWideChar + ); + assert!(result != 0, "Unexpected error in MultiByteToWideChar"); + + // Safety: MultiByteToWideChar initializes `result` values. + MaybeUninit::slice_assume_init_ref(&utf16[..result as usize]) + }; + + 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_code_unit_remaining = utf16[written]; + if matches!(first_code_unit_remaining, 0xDCEE..=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 { + debug_assert!(data.len() < u32::MAX as 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 { + 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() { + 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 = [MaybeUninit::new(0); 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( + unsafe { MaybeUninit::slice_assume_init_ref(&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 = [MaybeUninit::::uninit(); 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)?; + // Safety `read_u16s_fixup_surrogates` returns the number of items + // initialized. + let utf16s = unsafe { MaybeUninit::slice_assume_init_ref(&utf16_buf[..read]) }; + match utf16_to_utf8(utf16s, 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 [MaybeUninit], + mut amount: usize, + surrogate: &mut u16, +) -> io::Result { + // Insert possibly remaining unpaired surrogate from last read. + let mut start = 0; + if *surrogate != 0 { + buf[0] = MaybeUninit::new(*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 { + // Safety: The returned `amount` is the number of values initialized, + // and it is not 0, so we know that `buf[amount - 1]` have been + // initialized. + let last_char = unsafe { buf[amount - 1].assume_init() }; + if matches!(last_char, 0xD800..=0xDBFF) { + // high surrogate + *surrogate = last_char; + amount -= 1; + } + } + Ok(amount) +} + +// Returns `Ok(n)` if it initialized `n` values in `buf`. +fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit]) -> io::Result { + // 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 input_control = c::CONSOLE_READCONSOLE_CONTROL { + nLength: crate::mem::size_of::() 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, + &input_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 && api::get_last_error().code == c::ERROR_OPERATION_ABORTED { + continue; + } + break; + } + // Safety: if `amount > 0`, then that many bytes were written, so + // `buf[amount as usize - 1]` has been initialized. + if amount > 0 && unsafe { buf[amount as usize - 1].assume_init() } == CTRL_Z { + amount -= 1; + } + Ok(amount as usize) +} + +fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { + debug_assert!(utf16.len() <= c::c_int::MAX as usize); + debug_assert!(utf8.len() <= c::c_int::MAX as usize); + + if utf16.is_empty() { + return Ok(0); + } + + let result = unsafe { + c::WideCharToMultiByte( + c::CP_UTF8, // CodePage + c::WC_ERR_INVALID_CHARS, // dwFlags + utf16.as_ptr(), // lpWideCharStr + utf16.len() as c::c_int, // cchWideChar + utf8.as_mut_ptr(), // lpMultiByteStr + utf8.len() as c::c_int, // cbMultiByte + ptr::null(), // lpDefaultChar + ptr::null_mut(), // lpUsedDefaultChar + ) + }; + if result == 0 { + // We can't really do any better than forget all data and return an error. + Err(io::const_io_error!( + io::ErrorKind::InvalidData, + "Windows stdin in console mode does not support non-UTF-16 input; \ + encountered unpaired surrogate", + )) + } else { + Ok(result as usize) + } +} + +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 { + 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 { + 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 { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/pal/windows/stdio/tests.rs b/library/std/src/sys/pal/windows/stdio/tests.rs new file mode 100644 index 00000000000..1e53e0bee63 --- /dev/null +++ b/library/std/src/sys/pal/windows/stdio/tests.rs @@ -0,0 +1,6 @@ +use super::utf16_to_utf8; + +#[test] +fn zero_size_read() { + assert_eq!(utf16_to_utf8(&[], &mut []).unwrap(), 0); +} diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs new file mode 100644 index 00000000000..1fe74493519 --- /dev/null +++ b/library/std/src/sys/pal/windows/thread.rs @@ -0,0 +1,137 @@ +use crate::ffi::CStr; +use crate::io; +use crate::num::NonZeroUsize; +use crate::os::windows::io::AsRawHandle; +use crate::os::windows::io::HandleOrNull; +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 core::ffi::c_void; + +use super::time::WaitableTimer; +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) -> io::Result { + let p = Box::into_raw(Box::new(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, + Some(thread_start), + p as *mut _, + c::STACK_SIZE_PARAM_IS_A_RESERVATION, + ptr::null_mut(), + ); + let ret = HandleOrNull::from_raw_handle(ret); + 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)(); + } + 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) { + fn high_precision_sleep(dur: Duration) -> Result<(), ()> { + let timer = WaitableTimer::high_resolution()?; + timer.set(dur)?; + timer.wait() + } + // Attempt to use high-precision sleep (Windows 10, version 1803+). + // On error fallback to the standard `Sleep` function. + // Also preserves the zero duration behaviour of `Sleep`. + if dur.is_zero() || high_precision_sleep(dur).is_err() { + 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 { + 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 { + None + } + pub unsafe fn init() -> Option { + None + } +} diff --git a/library/std/src/sys/pal/windows/thread_local_dtor.rs b/library/std/src/sys/pal/windows/thread_local_dtor.rs new file mode 100644 index 00000000000..cf542d2bfb8 --- /dev/null +++ b/library/std/src/sys/pal/windows/thread_local_dtor.rs @@ -0,0 +1,7 @@ +//! Implements thread-local destructors that are not associated with any +//! particular data. + +#![unstable(feature = "thread_local_internals", issue = "none")] +#![cfg(target_thread_local)] + +pub use super::thread_local_key::register_keyless_dtor as register_dtor; diff --git a/library/std/src/sys/pal/windows/thread_local_key.rs b/library/std/src/sys/pal/windows/thread_local_key.rs new file mode 100644 index 00000000000..5eee4a9667b --- /dev/null +++ b/library/std/src/sys/pal/windows/thread_local_key.rs @@ -0,0 +1,341 @@ +use crate::cell::UnsafeCell; +use crate::ptr; +use crate::sync::atomic::{ + AtomicBool, AtomicPtr, AtomicU32, + Ordering::{AcqRel, Acquire, Relaxed, Release}, +}; +use crate::sys::c; + +#[cfg(test)] +mod tests; + +/// An optimization hint. The compiler is often smart enough to know if an atomic +/// is never set and can remove dead code based on that fact. +static HAS_DTORS: AtomicBool = AtomicBool::new(false); + +// Using a per-thread list avoids the problems in synchronizing global state. +#[thread_local] +#[cfg(target_thread_local)] +static DESTRUCTORS: crate::cell::RefCell> = + crate::cell::RefCell::new(Vec::new()); + +// Ensure this can never be inlined because otherwise this may break in dylibs. +// See #44391. +#[inline(never)] +#[cfg(target_thread_local)] +pub unsafe fn register_keyless_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + match DESTRUCTORS.try_borrow_mut() { + Ok(mut dtors) => dtors.push((t, dtor)), + Err(_) => rtabort!("global allocator may not use TLS"), + } + + HAS_DTORS.store(true, Relaxed); +} + +#[inline(never)] // See comment above +#[cfg(target_thread_local)] +/// Runs destructors. This should not be called until thread exit. +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. + loop { + // Use a let-else binding to ensure the `RefCell` guard is dropped + // immediately. Otherwise, a panic would occur if a TLS destructor + // tries to access the list. + let Some((ptr, dtor)) = DESTRUCTORS.borrow_mut().pop() else { + break; + }; + (dtor)(ptr); + } + // We're done so free the memory. + DESTRUCTORS.replace(Vec::new()); +} + +type Key = c::DWORD; +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. +// +// 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 + +pub struct StaticKey { + /// The key value shifted up by one. Since TLS_OUT_OF_INDEXES == DWORD::MAX + /// is not a valid key value, this allows us to use zero as sentinel value + /// without risking overflow. + key: AtomicU32, + dtor: Option, + next: AtomicPtr, + /// Currently, destructors cannot be unregistered, so we cannot use racy + /// initialization for keys. Instead, we need synchronize initialization. + /// Use the Windows-provided `Once` since it does not require TLS. + once: UnsafeCell, +} + +impl StaticKey { + #[inline] + pub const fn new(dtor: Option) -> StaticKey { + StaticKey { + key: AtomicU32::new(0), + dtor, + next: AtomicPtr::new(ptr::null_mut()), + once: UnsafeCell::new(c::INIT_ONCE_STATIC_INIT), + } + } + + #[inline] + pub unsafe fn set(&'static self, val: *mut u8) { + let r = c::TlsSetValue(self.key(), val.cast()); + debug_assert_eq!(r, c::TRUE); + } + + #[inline] + pub unsafe fn get(&'static self) -> *mut u8 { + c::TlsGetValue(self.key()).cast() + } + + #[inline] + unsafe fn key(&'static self) -> Key { + match self.key.load(Acquire) { + 0 => self.init(), + key => key - 1, + } + } + + #[cold] + unsafe fn init(&'static self) -> Key { + if self.dtor.is_some() { + let mut pending = c::FALSE; + let r = c::InitOnceBeginInitialize(self.once.get(), 0, &mut pending, ptr::null_mut()); + assert_eq!(r, c::TRUE); + + if pending == c::FALSE { + // Some other thread initialized the key, load it. + self.key.load(Relaxed) - 1 + } else { + let key = c::TlsAlloc(); + if key == c::TLS_OUT_OF_INDEXES { + // Wakeup the waiting threads before panicking to avoid deadlock. + c::InitOnceComplete(self.once.get(), c::INIT_ONCE_INIT_FAILED, ptr::null_mut()); + panic!("out of TLS indexes"); + } + + self.key.store(key + 1, Release); + register_dtor(self); + + let r = c::InitOnceComplete(self.once.get(), 0, ptr::null_mut()); + debug_assert_eq!(r, c::TRUE); + + key + } + } else { + // If there is no destructor to clean up, we can use racy initialization. + + let key = c::TlsAlloc(); + assert_ne!(key, c::TLS_OUT_OF_INDEXES, "out of TLS indexes"); + + match self.key.compare_exchange(0, key + 1, AcqRel, Acquire) { + Ok(_) => key, + Err(new) => { + // Some other thread completed initialization first, so destroy + // our key and use theirs. + let r = c::TlsFree(key); + debug_assert_eq!(r, c::TRUE); + new - 1 + } + } + } + } +} + +unsafe impl Send for StaticKey {} +unsafe impl Sync for StaticKey {} + +// ------------------------------------------------------------------------- +// 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. + +static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + +/// Should only be called once per key, otherwise loops or breaks may occur in +/// the linked list. +unsafe fn register_dtor(key: &'static StaticKey) { + // Ensure this is never run when native thread locals are available. + assert_eq!(false, cfg!(target_thread_local)); + let this = <*const StaticKey>::cast_mut(key); + // Use acquire ordering to pass along the changes done by the previously + // registered keys when we store the new head with release ordering. + let mut head = DTORS.load(Acquire); + loop { + key.next.store(head, Relaxed); + match DTORS.compare_exchange_weak(head, this, Release, Acquire) { + Ok(_) => break, + Err(new) => head = new, + } + } + HAS_DTORS.store(true, Release); +} + +// ------------------------------------------------------------------------- +// 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 !HAS_DTORS.load(Acquire) { + return; + } + if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH { + #[cfg(not(target_thread_local))] + run_dtors(); + #[cfg(target_thread_local)] + 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 below +unsafe fn run_dtors() { + for _ in 0..5 { + let mut any_run = false; + + // Use acquire ordering to observe key initialization. + let mut cur = DTORS.load(Acquire); + while !cur.is_null() { + let key = (*cur).key.load(Relaxed) - 1; + let dtor = (*cur).dtor.unwrap(); + + let ptr = c::TlsGetValue(key); + if !ptr.is_null() { + c::TlsSetValue(key, ptr::null_mut()); + dtor(ptr as *mut _); + any_run = true; + } + + cur = (*cur).next.load(Relaxed); + } + + if !any_run { + break; + } + } +} diff --git a/library/std/src/sys/pal/windows/thread_local_key/tests.rs b/library/std/src/sys/pal/windows/thread_local_key/tests.rs new file mode 100644 index 00000000000..c739f0caf3e --- /dev/null +++ b/library/std/src/sys/pal/windows/thread_local_key/tests.rs @@ -0,0 +1,57 @@ +// This file only tests the thread local key fallback. +// Windows targets with native thread local support do not use this. +#![cfg(not(target_thread_local))] + +use super::StaticKey; +use crate::ptr; + +#[test] +fn smoke() { + 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(ptr::invalid_mut(1)); + K2.set(ptr::invalid_mut(2)); + assert_eq!(K1.get() as usize, 1); + assert_eq!(K2.get() as usize, 2); + } +} + +#[test] +fn destructors() { + use crate::mem::ManuallyDrop; + use crate::sync::Arc; + use crate::thread; + + unsafe extern "C" fn destruct(ptr: *mut u8) { + drop(Arc::from_raw(ptr as *const ())); + } + + static KEY: StaticKey = StaticKey::new(Some(destruct)); + + let shared1 = Arc::new(()); + let shared2 = Arc::clone(&shared1); + + unsafe { + assert!(KEY.get().is_null()); + KEY.set(Arc::into_raw(shared1) as *mut u8); + } + + thread::spawn(move || unsafe { + assert!(KEY.get().is_null()); + KEY.set(Arc::into_raw(shared2) as *mut u8); + }) + .join() + .unwrap(); + + // Leak the Arc, let the TLS destructor clean it up. + let shared1 = unsafe { ManuallyDrop::new(Arc::from_raw(KEY.get() as *const ())) }; + assert_eq!( + Arc::strong_count(&shared1), + 1, + "destructor should have dropped the other reference on thread exit" + ); +} diff --git a/library/std/src/sys/pal/windows/thread_parking.rs b/library/std/src/sys/pal/windows/thread_parking.rs new file mode 100644 index 00000000000..eb9167cd855 --- /dev/null +++ b/library/std/src/sys/pal/windows/thread_parking.rs @@ -0,0 +1,253 @@ +// 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_in_place(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 { + unsafe { + if let Some(wake_by_address_single) = c::WakeByAddressSingle::option() { + 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. + 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 = 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/pal/windows/time.rs b/library/std/src/sys/pal/windows/time.rs new file mode 100644 index 00000000000..09e78a29304 --- /dev/null +++ b/library/std/src/sys/pal/windows/time.rs @@ -0,0 +1,262 @@ +use crate::cmp::Ordering; +use crate::fmt; +use crate::mem; +use crate::ptr::null; +use crate::sys::c; +use crate::sys_common::IntoInner; +use crate::time::Duration; + +use core::hash::{Hash, Hasher}; +use core::ops::Neg; + +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 { + // 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 { + Some(Instant { t: self.t.checked_add(*other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + 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 { + 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 { + let intervals = self.intervals().checked_add(checked_dur2intervals(other)?)?; + Some(SystemTime::from_intervals(intervals)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + 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 { + 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 for SystemTime { + fn from(t: c::FILETIME) -> SystemTime { + SystemTime { t } + } +} + +impl IntoInner for SystemTime { + fn into_inner(self) -> c::FILETIME { + self.t + } +} + +impl Hash for SystemTime { + fn hash(&self, state: &mut H) { + self.intervals().hash(state) + } +} + +fn checked_dur2intervals(dur: &Duration) -> Option { + 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 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 + } +} + +/// A timer you can wait on. +pub(super) struct WaitableTimer { + handle: c::HANDLE, +} +impl WaitableTimer { + /// Create a high-resolution timer. Will fail before Windows 10, version 1803. + pub fn high_resolution() -> Result { + let handle = unsafe { + c::CreateWaitableTimerExW( + null(), + null(), + c::CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, + c::TIMER_ALL_ACCESS, + ) + }; + if !handle.is_null() { Ok(Self { handle }) } else { Err(()) } + } + pub fn set(&self, duration: Duration) -> Result<(), ()> { + // Convert the Duration to a format similar to FILETIME. + // Negative values are relative times whereas positive values are absolute. + // Therefore we negate the relative duration. + let time = checked_dur2intervals(&duration).ok_or(())?.neg(); + let result = unsafe { c::SetWaitableTimer(self.handle, &time, 0, None, null(), c::FALSE) }; + if result != 0 { Ok(()) } else { Err(()) } + } + pub fn wait(&self) -> Result<(), ()> { + let result = unsafe { c::WaitForSingleObject(self.handle, c::INFINITE) }; + if result != c::WAIT_FAILED { Ok(()) } else { Err(()) } + } +} +impl Drop for WaitableTimer { + fn drop(&mut self) { + unsafe { c::CloseHandle(self.handle) }; + } +} diff --git a/library/std/src/sys/pal/xous/alloc.rs b/library/std/src/sys/pal/xous/alloc.rs new file mode 100644 index 00000000000..b3a3e691e0d --- /dev/null +++ b/library/std/src/sys/pal/xous/alloc.rs @@ -0,0 +1,62 @@ +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) } + } +} + +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; + } + crate::os::xous::ffi::do_yield(); + } + } + + impl Drop for DropLock { + fn drop(&mut self) { + let r = LOCKED.swap(0, SeqCst); + debug_assert_eq!(r, 1); + } + } +} diff --git a/library/std/src/sys/pal/xous/locks/condvar.rs b/library/std/src/sys/pal/xous/locks/condvar.rs new file mode 100644 index 00000000000..1bb38dfa341 --- /dev/null +++ b/library/std/src/sys/pal/xous/locks/condvar.rs @@ -0,0 +1,111 @@ +use super::mutex::Mutex; +use crate::os::xous::ffi::{blocking_scalar, scalar}; +use crate::os::xous::services::ticktimer_server; +use crate::sync::Mutex as StdMutex; +use crate::time::Duration; + +// The implementation is inspired by Andrew D. Birrell's paper +// "Implementing Condition Variables with Semaphores" + +pub struct Condvar { + counter: StdMutex, +} + +unsafe impl Send for Condvar {} +unsafe impl Sync for Condvar {} + +impl Condvar { + #[inline] + #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] + pub const fn new() -> Condvar { + Condvar { counter: StdMutex::new(0) } + } + + pub fn notify_one(&self) { + let mut counter = self.counter.lock().unwrap(); + if *counter <= 0 { + return; + } else { + *counter -= 1; + } + let result = blocking_scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), 1).into(), + ); + drop(counter); + result.expect("failure to send NotifyCondition command"); + } + + pub fn notify_all(&self) { + let mut counter = self.counter.lock().unwrap(); + if *counter <= 0 { + return; + } + let result = blocking_scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), *counter) + .into(), + ); + *counter = 0; + drop(counter); + + result.expect("failure to send NotifyCondition command"); + } + + fn index(&self) -> usize { + self as *const Condvar as usize + } + + pub unsafe fn wait(&self, mutex: &Mutex) { + let mut counter = self.counter.lock().unwrap(); + *counter += 1; + unsafe { mutex.unlock() }; + drop(counter); + + let result = blocking_scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), 0).into(), + ); + unsafe { mutex.lock() }; + + result.expect("Ticktimer: failure to send WaitForCondition command"); + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + let mut counter = self.counter.lock().unwrap(); + *counter += 1; + unsafe { mutex.unlock() }; + drop(counter); + + let mut millis = dur.as_millis() as usize; + if millis == 0 { + millis = 1; + } + + let result = blocking_scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), millis) + .into(), + ); + unsafe { mutex.lock() }; + + let result = result.expect("Ticktimer: failure to send WaitForCondition command")[0] == 0; + + // If we awoke due to a timeout, decrement the wake count, as that would not have + // been done in the `notify()` call. + if !result { + *self.counter.lock().unwrap() -= 1; + } + result + } +} + +impl Drop for Condvar { + fn drop(&mut self) { + scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::FreeCondition(self.index()).into(), + ) + .ok(); + } +} diff --git a/library/std/src/sys/pal/xous/locks/mod.rs b/library/std/src/sys/pal/xous/locks/mod.rs new file mode 100644 index 00000000000..f3c5c5d9fb0 --- /dev/null +++ b/library/std/src/sys/pal/xous/locks/mod.rs @@ -0,0 +1,7 @@ +mod condvar; +mod mutex; +mod rwlock; + +pub use condvar::*; +pub use mutex::*; +pub use rwlock::*; diff --git a/library/std/src/sys/pal/xous/locks/mutex.rs b/library/std/src/sys/pal/xous/locks/mutex.rs new file mode 100644 index 00000000000..ea51776d54e --- /dev/null +++ b/library/std/src/sys/pal/xous/locks/mutex.rs @@ -0,0 +1,116 @@ +use crate::os::xous::ffi::{blocking_scalar, do_yield, scalar}; +use crate::os::xous::services::ticktimer_server; +use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed, Ordering::SeqCst}; + +pub struct Mutex { + /// The "locked" value indicates how many threads are waiting on this + /// Mutex. Possible values are: + /// 0: The lock is unlocked + /// 1: The lock is locked and uncontended + /// >=2: The lock is locked and contended + /// + /// A lock is "contended" when there is more than one thread waiting + /// for a lock, or it is locked for long periods of time. Rather than + /// spinning, these locks send a Message to the ticktimer server + /// requesting that they be woken up when a lock is unlocked. + locked: AtomicUsize, + + /// Whether this Mutex ever was contended, and therefore made a trip + /// to the ticktimer server. If this was never set, then we were never + /// on the slow path and can skip deregistering the mutex. + contended: AtomicBool, +} + +impl Mutex { + #[inline] + #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] + pub const fn new() -> Mutex { + Mutex { locked: AtomicUsize::new(0), contended: AtomicBool::new(false) } + } + + fn index(&self) -> usize { + self as *const Mutex as usize + } + + #[inline] + pub unsafe fn lock(&self) { + // Try multiple times to acquire the lock without resorting to the ticktimer + // server. For locks that are held for a short amount of time, this will + // result in the ticktimer server never getting invoked. The `locked` value + // will be either 0 or 1. + for _attempts in 0..3 { + if unsafe { self.try_lock() } { + return; + } + do_yield(); + } + + // Try one more time to lock. If the lock is released between the previous code and + // here, then the inner `locked` value will be 1 at the end of this. If it was not + // locked, then the value will be more than 1, for example if there are multiple other + // threads waiting on this lock. + if unsafe { self.try_lock_or_poison() } { + return; + } + + // When this mutex is dropped, we will need to deregister it with the server. + self.contended.store(true, Relaxed); + + // The lock is now "contended". When the lock is released, a Message will get sent to the + // ticktimer server to wake it up. Note that this may already have happened, so the actual + // value of `lock` may be anything (0, 1, 2, ...). + blocking_scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::LockMutex(self.index()).into(), + ) + .expect("failure to send LockMutex command"); + } + + #[inline] + pub unsafe fn unlock(&self) { + let prev = self.locked.fetch_sub(1, SeqCst); + + // If the previous value was 1, then this was a "fast path" unlock, so no + // need to involve the Ticktimer server + if prev == 1 { + return; + } + + // If it was 0, then something has gone seriously wrong and the counter + // has just wrapped around. + if prev == 0 { + panic!("mutex lock count underflowed"); + } + + // Unblock one thread that is waiting on this message. + scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::UnlockMutex(self.index()).into(), + ) + .expect("failure to send UnlockMutex command"); + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + self.locked.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() + } + + #[inline] + pub unsafe fn try_lock_or_poison(&self) -> bool { + self.locked.fetch_add(1, SeqCst) == 0 + } +} + +impl Drop for Mutex { + fn drop(&mut self) { + // If there was Mutex contention, then we involved the ticktimer. Free + // the resources associated with this Mutex as it is deallocated. + if self.contended.load(Relaxed) { + scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::FreeMutex(self.index()).into(), + ) + .ok(); + } + } +} diff --git a/library/std/src/sys/pal/xous/locks/rwlock.rs b/library/std/src/sys/pal/xous/locks/rwlock.rs new file mode 100644 index 00000000000..618da758adf --- /dev/null +++ b/library/std/src/sys/pal/xous/locks/rwlock.rs @@ -0,0 +1,72 @@ +use crate::os::xous::ffi::do_yield; +use crate::sync::atomic::{AtomicIsize, Ordering::SeqCst}; + +pub struct RwLock { + /// The "mode" value indicates how many threads are waiting on this + /// Mutex. Possible values are: + /// -1: The lock is locked for writing + /// 0: The lock is unlocked + /// >=1: The lock is locked for reading + /// + /// This currently spins waiting for the lock to be freed. An + /// optimization would be to involve the ticktimer server to + /// coordinate unlocks. + mode: AtomicIsize, +} + +unsafe impl Send for RwLock {} +unsafe impl Sync for RwLock {} + +impl RwLock { + #[inline] + #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] + pub const fn new() -> RwLock { + RwLock { mode: AtomicIsize::new(0) } + } + + #[inline] + pub unsafe fn read(&self) { + while !unsafe { self.try_read() } { + do_yield(); + } + } + + #[inline] + pub unsafe fn try_read(&self) -> bool { + // Non-atomically determine the current value. + let current = self.mode.load(SeqCst); + + // If it's currently locked for writing, then we cannot read. + if current < 0 { + return false; + } + + // Attempt to lock. If the `current` value has changed, then this + // operation will fail and we will not obtain the lock even if we + // could potentially keep it. + let new = current + 1; + self.mode.compare_exchange(current, new, SeqCst, SeqCst).is_ok() + } + + #[inline] + pub unsafe fn write(&self) { + while !unsafe { self.try_write() } { + do_yield(); + } + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + self.mode.compare_exchange(0, -1, SeqCst, SeqCst).is_ok() + } + + #[inline] + pub unsafe fn read_unlock(&self) { + self.mode.fetch_sub(1, SeqCst); + } + + #[inline] + pub unsafe fn write_unlock(&self) { + assert_eq!(self.mode.compare_exchange(-1, 0, SeqCst, SeqCst), Ok(-1)); + } +} diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs new file mode 100644 index 00000000000..c2550dcfd83 --- /dev/null +++ b/library/std/src/sys/pal/xous/mod.rs @@ -0,0 +1,36 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +pub mod alloc; +#[path = "../unsupported/args.rs"] +pub mod args; +#[path = "../unix/cmath.rs"] +pub mod cmath; +#[path = "../unsupported/env.rs"] +pub mod env; +#[path = "../unsupported/fs.rs"] +pub mod fs; +#[path = "../unsupported/io.rs"] +pub mod io; +pub mod locks; +#[path = "../unsupported/net.rs"] +pub mod net; +#[path = "../unsupported/once.rs"] +pub mod once; +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_key; +pub mod thread_parking; +pub mod time; + +#[path = "../unsupported/common.rs"] +mod common; +pub use common::*; diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs new file mode 100644 index 00000000000..8d2eaee8aa6 --- /dev/null +++ b/library/std/src/sys/pal/xous/os.rs @@ -0,0 +1,174 @@ +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::os::xous::ffi::Error as XousError; +use crate::path::{self, PathBuf}; + +#[cfg(not(test))] +#[cfg(feature = "panic_unwind")] +mod eh_unwinding { + pub(crate) struct EhFrameFinder(usize /* eh_frame */); + pub(crate) static mut EH_FRAME_SETTINGS: EhFrameFinder = EhFrameFinder(0); + impl EhFrameFinder { + pub(crate) unsafe fn init(&mut self, eh_frame: usize) { + unsafe { + EH_FRAME_SETTINGS.0 = eh_frame; + } + } + } + unsafe impl unwind::EhFrameFinder for EhFrameFinder { + fn find(&self, _pc: usize) -> Option { + Some(unwind::FrameInfo { + text_base: None, + kind: unwind::FrameInfoKind::EhFrame(self.0), + }) + } + } +} + +#[cfg(not(test))] +mod c_compat { + use crate::os::xous::ffi::exit; + extern "C" { + fn main() -> u32; + } + + #[no_mangle] + pub extern "C" fn abort() { + exit(1); + } + + #[no_mangle] + pub extern "C" fn _start(eh_frame: usize) { + #[cfg(feature = "panic_unwind")] + unsafe { + super::eh_unwinding::EH_FRAME_SETTINGS.init(eh_frame); + unwind::set_custom_eh_frame_finder(&super::eh_unwinding::EH_FRAME_SETTINGS).ok(); + } + exit(unsafe { main() }); + } + + // 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. + #[no_mangle] + // NB. used by both libunwind and libpanic_abort + pub extern "C" fn __rust_abort() -> ! { + exit(101); + } +} + +pub fn errno() -> i32 { + 0 +} + +pub fn error_string(errno: i32) -> String { + Into::::into(errno).to_string() +} + +pub fn getcwd() -> io::Result { + 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 { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + 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 { + unsupported() +} + +pub struct Env(!); + +impl Env { + // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self(inner) = self; + match *inner {} + } +} + +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + match *inner {} + } +} + +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 { + 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 { + None +} + +pub fn exit(code: i32) -> ! { + crate::os::xous::ffi::exit(code as u32); +} + +pub fn getpid() -> u32 { + panic!("no pids on this platform") +} diff --git a/library/std/src/sys/pal/xous/stdio.rs b/library/std/src/sys/pal/xous/stdio.rs new file mode 100644 index 00000000000..2ac694641ba --- /dev/null +++ b/library/std/src/sys/pal/xous/stdio.rs @@ -0,0 +1,131 @@ +use crate::io; + +pub struct Stdin; +pub struct Stdout {} +pub struct Stderr; + +use crate::os::xous::ffi::{lend, try_lend, try_scalar, Connection}; +use crate::os::xous::services::{log_server, try_connect, LogScalar}; + +impl Stdin { + pub const fn new() -> Stdin { + Stdin + } +} + +impl io::Read for Stdin { + fn read(&mut self, _buf: &mut [u8]) -> io::Result { + Ok(0) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout {} + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + #[repr(align(4096))] + struct LendBuffer([u8; 4096]); + let mut lend_buffer = LendBuffer([0u8; 4096]); + let connection = log_server(); + for chunk in buf.chunks(lend_buffer.0.len()) { + for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) { + *dest = *src; + } + lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap(); + } + 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 { + #[repr(align(4096))] + struct LendBuffer([u8; 4096]); + let mut lend_buffer = LendBuffer([0u8; 4096]); + let connection = log_server(); + for chunk in buf.chunks(lend_buffer.0.len()) { + for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) { + *dest = *src; + } + lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap(); + } + 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 +} + +#[derive(Copy, Clone)] +pub struct PanicWriter { + log: Connection, + gfx: Option, +} + +impl io::Write for PanicWriter { + fn write(&mut self, s: &[u8]) -> core::result::Result { + for c in s.chunks(core::mem::size_of::() * 4) { + // Text is grouped into 4x `usize` words. The id is 1100 plus + // the number of characters in this message. + // Ignore errors since we're already panicking. + try_scalar(self.log, LogScalar::AppendPanicMessage(&c).into()).ok(); + } + + // Serialize the text to the graphics panic handler, only if we were able + // to acquire a connection to it. Text length is encoded in the `valid` field, + // the data itself in the buffer. Typically several messages are require to + // fully transmit the entire panic message. + if let Some(gfx) = self.gfx { + #[repr(C, align(4096))] + struct Request([u8; 4096]); + let mut request = Request([0u8; 4096]); + for (&s, d) in s.iter().zip(request.0.iter_mut()) { + *d = s; + } + try_lend(gfx, 0 /* AppendPanicText */, &request.0, 0, s.len()).ok(); + } + Ok(s.len()) + } + + // Tests show that this does not seem to be reliably called at the end of a panic + // print, so, we can't rely on this to e.g. trigger a graphics update. + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub fn panic_output() -> Option { + // Generally this won't fail because every server has already connected, so + // this is likely to succeed. + let log = log_server(); + + // Send the "We're panicking" message (1000). + try_scalar(log, LogScalar::BeginPanic.into()).ok(); + + // This is will fail in the case that the connection table is full, or if the + // graphics server is not running. Most servers do not already have this connection. + let gfx = try_connect("panic-to-screen!"); + + Some(PanicWriter { log, gfx }) +} diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs new file mode 100644 index 00000000000..78c68de7bf3 --- /dev/null +++ b/library/std/src/sys/pal/xous/thread.rs @@ -0,0 +1,144 @@ +use crate::ffi::CStr; +use crate::io; +use crate::num::NonZeroUsize; +use crate::os::xous::ffi::{ + blocking_scalar, create_thread, do_yield, join_thread, map_memory, update_memory_flags, + MemoryFlags, Syscall, ThreadId, +}; +use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; +use crate::time::Duration; +use core::arch::asm; + +pub struct Thread { + tid: ThreadId, +} + +pub const DEFAULT_MIN_STACK_SIZE: usize = 131072; +const MIN_STACK_SIZE: usize = 4096; +pub const GUARD_PAGE_SIZE: usize = 4096; + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box) -> io::Result { + let p = Box::into_raw(Box::new(p)); + let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE); + + if (stack_size & 4095) != 0 { + stack_size = (stack_size + 4095) & !4095; + } + + // Allocate the whole thing, then divide it up after the fact. This ensures that + // even if there's a context switch during this function, the whole stack plus + // guard pages will remain contiguous. + let stack_plus_guard_pages: &mut [u8] = unsafe { + map_memory( + None, + None, + GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE, + MemoryFlags::R | MemoryFlags::W | MemoryFlags::X, + ) + } + .map_err(|code| io::Error::from_raw_os_error(code as i32))?; + + // No access to this page. Note: Write-only pages are illegal, and will + // cause an access violation. + unsafe { + update_memory_flags(&mut stack_plus_guard_pages[0..GUARD_PAGE_SIZE], MemoryFlags::W) + .map_err(|code| io::Error::from_raw_os_error(code as i32))? + }; + + // No access to this page. Note: Write-only pages are illegal, and will + // cause an access violation. + unsafe { + update_memory_flags( + &mut stack_plus_guard_pages[(GUARD_PAGE_SIZE + stack_size)..], + MemoryFlags::W, + ) + .map_err(|code| io::Error::from_raw_os_error(code as i32))? + }; + + let guard_page_pre = stack_plus_guard_pages.as_ptr() as usize; + let tid = create_thread( + thread_start as *mut usize, + &mut stack_plus_guard_pages[GUARD_PAGE_SIZE..(stack_size + GUARD_PAGE_SIZE)], + p as usize, + guard_page_pre, + stack_size, + 0, + ) + .map_err(|code| io::Error::from_raw_os_error(code as i32))?; + + extern "C" fn thread_start(main: *mut usize, guard_page_pre: usize, stack_size: usize) { + unsafe { + // Finally, let's run some code. + Box::from_raw(main as *mut Box)(); + } + + // Destroy TLS, which will free the TLS page and call the destructor for + // any thread local storage. + unsafe { + crate::sys::thread_local_key::destroy_tls(); + } + + // Deallocate the stack memory, along with the guard pages. Afterwards, + // exit the thread by returning to the magic address 0xff80_3000usize, + // which tells the kernel to deallocate this thread. + let mapped_memory_base = guard_page_pre; + let mapped_memory_length = GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE; + unsafe { + asm!( + "ecall", + "ret", + in("a0") Syscall::UnmapMemory as usize, + in("a1") mapped_memory_base, + in("a2") mapped_memory_length, + in("ra") 0xff80_3000usize, + options(nomem, nostack, noreturn) + ); + } + } + + Ok(Thread { tid }) + } + + pub fn yield_now() { + do_yield(); + } + + pub fn set_name(_name: &CStr) { + // nope + } + + pub fn sleep(dur: Duration) { + // Because the sleep server works on units of `usized milliseconds`, split + // the messages up into these chunks. This means we may run into issues + // if you try to sleep a thread for more than 49 days on a 32-bit system. + let mut millis = dur.as_millis(); + while millis > 0 { + let sleep_duration = + if millis > (usize::MAX as _) { usize::MAX } else { millis as usize }; + blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into()) + .expect("failed to send message to ticktimer server"); + millis -= sleep_duration as u128; + } + } + + pub fn join(self) { + join_thread(self.tid).unwrap(); + } +} + +pub fn available_parallelism() -> io::Result { + // We're unicore right now. + Ok(unsafe { NonZeroUsize::new_unchecked(1) }) +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + None + } +} diff --git a/library/std/src/sys/pal/xous/thread_local_key.rs b/library/std/src/sys/pal/xous/thread_local_key.rs new file mode 100644 index 00000000000..3771ea65700 --- /dev/null +++ b/library/std/src/sys/pal/xous/thread_local_key.rs @@ -0,0 +1,190 @@ +use crate::mem::ManuallyDrop; +use crate::ptr; +use crate::sync::atomic::AtomicPtr; +use crate::sync::atomic::AtomicUsize; +use crate::sync::atomic::Ordering::SeqCst; +use core::arch::asm; + +use crate::os::xous::ffi::{map_memory, unmap_memory, MemoryFlags}; + +/// Thread Local Storage +/// +/// Currently, we are limited to 1023 TLS entries. The entries +/// live in a page of memory that's unique per-process, and is +/// stored in the `$tp` register. If this register is 0, then +/// TLS has not been initialized and thread cleanup can be skipped. +/// +/// The index into this register is the `key`. This key is identical +/// between all threads, but indexes a different offset within this +/// pointer. +pub type Key = usize; + +pub type Dtor = unsafe extern "C" fn(*mut u8); + +const TLS_MEMORY_SIZE: usize = 4096; + +/// TLS keys start at `1` to mimic POSIX. +static TLS_KEY_INDEX: AtomicUsize = AtomicUsize::new(1); + +fn tls_ptr_addr() -> *mut usize { + let mut tp: usize; + unsafe { + asm!( + "mv {}, tp", + out(reg) tp, + ); + } + core::ptr::from_exposed_addr_mut::(tp) +} + +/// Create an area of memory that's unique per thread. This area will +/// contain all thread local pointers. +fn tls_ptr() -> *mut usize { + let mut tp = tls_ptr_addr(); + + // If the TP register is `0`, then this thread hasn't initialized + // its TLS yet. Allocate a new page to store this memory. + if tp.is_null() { + tp = unsafe { + map_memory( + None, + None, + TLS_MEMORY_SIZE / core::mem::size_of::(), + MemoryFlags::R | MemoryFlags::W, + ) + } + .expect("Unable to allocate memory for thread local storage") + .as_mut_ptr(); + + unsafe { + // Key #0 is currently unused. + (tp).write_volatile(0); + + // Set the thread's `$tp` register + asm!( + "mv tp, {}", + in(reg) tp as usize, + ); + } + } + tp +} + +/// Allocate a new TLS key. These keys are shared among all threads. +fn tls_alloc() -> usize { + TLS_KEY_INDEX.fetch_add(1, SeqCst) +} + +#[inline] +pub unsafe fn create(dtor: Option) -> Key { + let key = tls_alloc(); + if let Some(f) = dtor { + unsafe { register_dtor(key, f) }; + } + key +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + assert!((key < 1022) && (key >= 1)); + unsafe { tls_ptr().add(key).write_volatile(value as usize) }; +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + assert!((key < 1022) && (key >= 1)); + core::ptr::from_exposed_addr_mut::(unsafe { tls_ptr().add(key).read_volatile() }) +} + +#[inline] +pub unsafe fn destroy(_key: Key) { + panic!("can't destroy keys on Xous"); +} + +// ------------------------------------------------------------------------- +// Dtor registration (stolen from Windows) +// +// Xous 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 = 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, + } + } +} + +pub unsafe fn destroy_tls() { + let tp = tls_ptr_addr(); + + // If the pointer address is 0, then this thread has no TLS. + if tp.is_null() { + return; + } + unsafe { run_dtors() }; + + // Finally, free the TLS array + unsafe { + unmap_memory(core::slice::from_raw_parts_mut( + tp, + TLS_MEMORY_SIZE / core::mem::size_of::(), + )) + .unwrap() + }; +} + +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 = unsafe { get((*cur).key) }; + + if !ptr.is_null() { + unsafe { set((*cur).key, ptr::null_mut()) }; + unsafe { ((*cur).dtor)(ptr as *mut _) }; + any_run = true; + } + + unsafe { cur = (*cur).next }; + } + } +} diff --git a/library/std/src/sys/pal/xous/thread_parking.rs b/library/std/src/sys/pal/xous/thread_parking.rs new file mode 100644 index 00000000000..aa39c6d2718 --- /dev/null +++ b/library/std/src/sys/pal/xous/thread_parking.rs @@ -0,0 +1,94 @@ +use crate::os::xous::ffi::{blocking_scalar, scalar}; +use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; +use crate::pin::Pin; +use crate::ptr; +use crate::sync::atomic::{ + AtomicI8, + Ordering::{Acquire, Release}, +}; +use crate::time::Duration; + +const NOTIFIED: i8 = 1; +const EMPTY: i8 = 0; +const PARKED: i8 = -1; + +pub struct Parker { + state: AtomicI8, +} + +impl Parker { + pub unsafe fn new_in_place(parker: *mut Parker) { + unsafe { parker.write(Parker { state: AtomicI8::new(EMPTY) }) } + } + + fn index(&self) -> usize { + ptr::from_ref(self).addr() + } + + pub unsafe fn park(self: Pin<&Self>) { + // Change NOTIFIED to EMPTY and EMPTY to PARKED. + let state = self.state.fetch_sub(1, Acquire); + if state == NOTIFIED { + return; + } + + // The state was set to PARKED. Wait until the `unpark` wakes us up. + blocking_scalar( + ticktimer_server(), + TicktimerScalar::WaitForCondition(self.index(), 0).into(), + ) + .expect("failed to send WaitForCondition command"); + + self.state.swap(EMPTY, Acquire); + } + + pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) { + // Change NOTIFIED to EMPTY and EMPTY to PARKED. + let state = self.state.fetch_sub(1, Acquire); + if state == NOTIFIED { + return; + } + + // A value of zero indicates an indefinite wait. Clamp the number of + // milliseconds to the allowed range. + let millis = usize::max(timeout.as_millis().try_into().unwrap_or(usize::MAX), 1); + + let was_timeout = blocking_scalar( + ticktimer_server(), + TicktimerScalar::WaitForCondition(self.index(), millis).into(), + ) + .expect("failed to send WaitForCondition command")[0] + != 0; + + let state = self.state.swap(EMPTY, Acquire); + if was_timeout && state == NOTIFIED { + // The state was set to NOTIFIED after we returned from the wait + // but before we reset the state. Therefore, a wakeup is on its + // way, which we need to consume here. + // NOTICE: this is a priority hole. + blocking_scalar( + ticktimer_server(), + TicktimerScalar::WaitForCondition(self.index(), 0).into(), + ) + .expect("failed to send WaitForCondition command"); + } + } + + pub fn unpark(self: Pin<&Self>) { + let state = self.state.swap(NOTIFIED, Release); + if state == PARKED { + // The thread is parked, wake it up. + blocking_scalar( + ticktimer_server(), + TicktimerScalar::NotifyCondition(self.index(), 1).into(), + ) + .expect("failed to send NotifyCondition command"); + } + } +} + +impl Drop for Parker { + fn drop(&mut self) { + scalar(ticktimer_server(), TicktimerScalar::FreeCondition(self.index()).into()).ok(); + } +} diff --git a/library/std/src/sys/pal/xous/time.rs b/library/std/src/sys/pal/xous/time.rs new file mode 100644 index 00000000000..4e4ae67efff --- /dev/null +++ b/library/std/src/sys/pal/xous/time.rs @@ -0,0 +1,57 @@ +use crate::os::xous::ffi::blocking_scalar; +use crate::os::xous::services::{ + systime_server, ticktimer_server, SystimeScalar::GetUtcTimeMs, TicktimerScalar::ElapsedMs, +}; +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 { + let result = blocking_scalar(ticktimer_server(), ElapsedMs.into()) + .expect("failed to request elapsed_ms"); + let lower = result[0]; + let upper = result[1]; + Instant { 0: Duration::from_millis(lower as u64 | (upper as u64) << 32) } + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option { + self.0.checked_sub(other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + self.0.checked_add(*other).map(Instant) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + self.0.checked_sub(*other).map(Instant) + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + let result = blocking_scalar(systime_server(), GetUtcTimeMs.into()) + .expect("failed to request utc time in ms"); + let lower = result[0]; + let upper = result[1]; + SystemTime { 0: Duration::from_millis((upper as u64) << 32 | lower as u64) } + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} diff --git a/library/std/src/sys/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs deleted file mode 100644 index a78084de0fa..00000000000 --- a/library/std/src/sys/personality/dwarf/eh.rs +++ /dev/null @@ -1,257 +0,0 @@ -//! Parsing of GCC-style Language-Specific Data Area (LSDA) -//! For details see: -//! * -//! * -//! * -//! * -//! * -//! -//! A reference implementation may be found in the GCC source tree -//! (`/libgcc/unwind-c.c` as of this writing). - -#![allow(non_upper_case_globals)] -#![allow(unused)] - -use super::DwarfReader; -use core::mem; -use core::ptr; - -pub const DW_EH_PE_omit: u8 = 0xFF; -pub const DW_EH_PE_absptr: u8 = 0x00; - -pub const DW_EH_PE_uleb128: u8 = 0x01; -pub const DW_EH_PE_udata2: u8 = 0x02; -pub const DW_EH_PE_udata4: u8 = 0x03; -pub const DW_EH_PE_udata8: u8 = 0x04; -pub const DW_EH_PE_sleb128: u8 = 0x09; -pub const DW_EH_PE_sdata2: u8 = 0x0A; -pub const DW_EH_PE_sdata4: u8 = 0x0B; -pub const DW_EH_PE_sdata8: u8 = 0x0C; - -pub const DW_EH_PE_pcrel: u8 = 0x10; -pub const DW_EH_PE_textrel: u8 = 0x20; -pub const DW_EH_PE_datarel: u8 = 0x30; -pub const DW_EH_PE_funcrel: u8 = 0x40; -pub const DW_EH_PE_aligned: u8 = 0x50; - -pub const DW_EH_PE_indirect: u8 = 0x80; - -#[derive(Copy, Clone)] -pub struct EHContext<'a> { - pub ip: *const u8, // Current instruction pointer - pub func_start: *const u8, // Pointer to the current function - pub get_text_start: &'a dyn Fn() -> *const u8, // Get pointer to the code section - pub get_data_start: &'a dyn Fn() -> *const u8, // Get pointer to the data section -} - -/// Landing pad. -type LPad = *const u8; -pub enum EHAction { - None, - Cleanup(LPad), - Catch(LPad), - Filter(LPad), - Terminate, -} - -pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm")); - -pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result { - if lsda.is_null() { - return Ok(EHAction::None); - } - - let func_start = context.func_start; - let mut reader = DwarfReader::new(lsda); - - let start_encoding = reader.read::(); - // base address for landing pad offsets - let lpad_base = if start_encoding != DW_EH_PE_omit { - read_encoded_pointer(&mut reader, context, start_encoding)? - } else { - func_start - }; - - let ttype_encoding = reader.read::(); - if ttype_encoding != DW_EH_PE_omit { - // Rust doesn't analyze exception types, so we don't care about the type table - reader.read_uleb128(); - } - - let call_site_encoding = reader.read::(); - let call_site_table_length = reader.read_uleb128(); - let action_table = reader.ptr.add(call_site_table_length as usize); - let ip = context.ip; - - if !USING_SJLJ_EXCEPTIONS { - // read the callsite table - while reader.ptr < action_table { - // these are offsets rather than pointers; - let cs_start = read_encoded_offset(&mut reader, call_site_encoding)?; - let cs_len = read_encoded_offset(&mut reader, call_site_encoding)?; - let cs_lpad = read_encoded_offset(&mut reader, call_site_encoding)?; - let cs_action_entry = reader.read_uleb128(); - // Callsite table is sorted by cs_start, so if we've passed the ip, we - // may stop searching. - if ip < func_start.wrapping_add(cs_start) { - break; - } - if ip < func_start.wrapping_add(cs_start + cs_len) { - if cs_lpad == 0 { - return Ok(EHAction::None); - } else { - let lpad = lpad_base.wrapping_add(cs_lpad); - return Ok(interpret_cs_action(action_table, cs_action_entry, lpad)); - } - } - } - // Ip is not present in the table. This indicates a nounwind call. - Ok(EHAction::Terminate) - } else { - // SjLj version: - // The "IP" is an index into the call-site table, with two exceptions: - // -1 means 'no-action', and 0 means 'terminate'. - match ip.addr() as isize { - -1 => return Ok(EHAction::None), - 0 => return Ok(EHAction::Terminate), - _ => (), - } - let mut idx = ip.addr(); - loop { - let cs_lpad = reader.read_uleb128(); - let cs_action_entry = reader.read_uleb128(); - idx -= 1; - if idx == 0 { - // Can never have null landing pad for sjlj -- that would have - // been indicated by a -1 call site index. - // FIXME(strict provenance) - let lpad = ptr::from_exposed_addr((cs_lpad + 1) as usize); - return Ok(interpret_cs_action(action_table, cs_action_entry, lpad)); - } - } - } -} - -unsafe fn interpret_cs_action( - action_table: *const u8, - cs_action_entry: u64, - lpad: LPad, -) -> EHAction { - if cs_action_entry == 0 { - // If cs_action_entry is 0 then this is a cleanup (Drop::drop). We run these - // for both Rust panics and foreign exceptions. - EHAction::Cleanup(lpad) - } else { - // If lpad != 0 and cs_action_entry != 0, we have to check ttype_index. - // If ttype_index == 0 under the condition, we take cleanup action. - let action_record = action_table.offset(cs_action_entry as isize - 1); - let mut action_reader = DwarfReader::new(action_record); - let ttype_index = action_reader.read_sleb128(); - if ttype_index == 0 { - EHAction::Cleanup(lpad) - } else if ttype_index > 0 { - // Stop unwinding Rust panics at catch_unwind. - EHAction::Catch(lpad) - } else { - EHAction::Filter(lpad) - } - } -} - -#[inline] -fn round_up(unrounded: usize, align: usize) -> Result { - if align.is_power_of_two() { Ok((unrounded + align - 1) & !(align - 1)) } else { Err(()) } -} - -/// Read a offset (`usize`) from `reader` whose encoding is described by `encoding`. -/// -/// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext]. -/// In addition the upper ("application") part must be zero. -/// -/// # Errors -/// Returns `Err` if `encoding` -/// * is not a valid DWARF Exception Header Encoding, -/// * is `DW_EH_PE_omit`, or -/// * has a non-zero application part. -/// -/// [LSB-dwarf-ext]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html -unsafe fn read_encoded_offset(reader: &mut DwarfReader, encoding: u8) -> Result { - if encoding == DW_EH_PE_omit || encoding & 0xF0 != 0 { - return Err(()); - } - let result = match encoding & 0x0F { - // despite the name, LLVM also uses absptr for offsets instead of pointers - DW_EH_PE_absptr => reader.read::(), - DW_EH_PE_uleb128 => reader.read_uleb128() as usize, - DW_EH_PE_udata2 => reader.read::() as usize, - DW_EH_PE_udata4 => reader.read::() as usize, - DW_EH_PE_udata8 => reader.read::() as usize, - DW_EH_PE_sleb128 => reader.read_sleb128() as usize, - DW_EH_PE_sdata2 => reader.read::() as usize, - DW_EH_PE_sdata4 => reader.read::() as usize, - DW_EH_PE_sdata8 => reader.read::() as usize, - _ => return Err(()), - }; - Ok(result) -} - -/// Read a pointer from `reader` whose encoding is described by `encoding`. -/// -/// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext]. -/// -/// # Errors -/// Returns `Err` if `encoding` -/// * is not a valid DWARF Exception Header Encoding, -/// * is `DW_EH_PE_omit`, or -/// * combines `DW_EH_PE_absptr` or `DW_EH_PE_aligned` application part with an integer encoding -/// (not `DW_EH_PE_absptr`) in the value format part. -/// -/// [LSB-dwarf-ext]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html -unsafe fn read_encoded_pointer( - reader: &mut DwarfReader, - context: &EHContext<'_>, - encoding: u8, -) -> Result<*const u8, ()> { - if encoding == DW_EH_PE_omit { - return Err(()); - } - - let base_ptr = match encoding & 0x70 { - DW_EH_PE_absptr => core::ptr::null(), - // relative to address of the encoded value, despite the name - DW_EH_PE_pcrel => reader.ptr, - DW_EH_PE_funcrel => { - if context.func_start.is_null() { - return Err(()); - } - context.func_start - } - DW_EH_PE_textrel => (*context.get_text_start)(), - DW_EH_PE_datarel => (*context.get_data_start)(), - // aligned means the value is aligned to the size of a pointer - DW_EH_PE_aligned => { - reader.ptr = - reader.ptr.with_addr(round_up(reader.ptr.addr(), mem::size_of::<*const u8>())?); - core::ptr::null() - } - _ => return Err(()), - }; - - let mut ptr = if base_ptr.is_null() { - // any value encoding other than absptr would be nonsensical here; - // there would be no source of pointer provenance - if encoding & 0x0F != DW_EH_PE_absptr { - return Err(()); - } - reader.read::<*const u8>() - } else { - let offset = read_encoded_offset(reader, encoding & 0x0F)?; - base_ptr.wrapping_add(offset) - }; - - if encoding & DW_EH_PE_indirect != 0 { - ptr = *(ptr.cast::<*const u8>()); - } - - Ok(ptr) -} diff --git a/library/std/src/sys/personality/dwarf/mod.rs b/library/std/src/sys/personality/dwarf/mod.rs deleted file mode 100644 index 652fbe95a14..00000000000 --- a/library/std/src/sys/personality/dwarf/mod.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! Utilities for parsing DWARF-encoded data streams. -//! See , -//! DWARF-4 standard, Section 7 - "Data Representation" - -// This module is used only by x86_64-pc-windows-gnu for now, but we -// are compiling it everywhere to avoid regressions. -#![allow(unused)] - -#[cfg(test)] -mod tests; - -pub mod eh; - -use core::mem; - -pub struct DwarfReader { - pub ptr: *const u8, -} - -#[repr(C, packed)] -struct Unaligned(T); - -impl DwarfReader { - pub fn new(ptr: *const u8) -> DwarfReader { - DwarfReader { ptr } - } - - // DWARF streams are packed, so e.g., a u32 would not necessarily be aligned - // on a 4-byte boundary. This may cause problems on platforms with strict - // alignment requirements. By wrapping data in a "packed" struct, we are - // telling the backend to generate "misalignment-safe" code. - pub unsafe fn read(&mut self) -> T { - let Unaligned(result) = *(self.ptr as *const Unaligned); - self.ptr = self.ptr.add(mem::size_of::()); - result - } - - // ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable - // Length Data". - pub unsafe fn read_uleb128(&mut self) -> u64 { - let mut shift: usize = 0; - let mut result: u64 = 0; - let mut byte: u8; - loop { - byte = self.read::(); - result |= ((byte & 0x7F) as u64) << shift; - shift += 7; - if byte & 0x80 == 0 { - break; - } - } - result - } - - pub unsafe fn read_sleb128(&mut self) -> i64 { - let mut shift: u32 = 0; - let mut result: u64 = 0; - let mut byte: u8; - loop { - byte = self.read::(); - result |= ((byte & 0x7F) as u64) << shift; - shift += 7; - if byte & 0x80 == 0 { - break; - } - } - // sign-extend - if shift < u64::BITS && (byte & 0x40) != 0 { - result |= (!0 as u64) << shift; - } - result as i64 - } -} diff --git a/library/std/src/sys/personality/dwarf/tests.rs b/library/std/src/sys/personality/dwarf/tests.rs deleted file mode 100644 index 1644f37083a..00000000000 --- a/library/std/src/sys/personality/dwarf/tests.rs +++ /dev/null @@ -1,19 +0,0 @@ -use super::*; - -#[test] -fn dwarf_reader() { - let encoded: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 0xE5, 0x8E, 0x26, 0x9B, 0xF1, 0x59, 0xFF, 0xFF]; - - let mut reader = DwarfReader::new(encoded.as_ptr()); - - unsafe { - assert!(reader.read::() == u8::to_be(1u8)); - assert!(reader.read::() == u16::to_be(0x0203)); - assert!(reader.read::() == u32::to_be(0x04050607)); - - assert!(reader.read_uleb128() == 624485); - assert!(reader.read_sleb128() == -624485); - - assert!(reader.read::() == i8::to_be(-1)); - } -} diff --git a/library/std/src/sys/personality/emcc.rs b/library/std/src/sys/personality/emcc.rs deleted file mode 100644 index cb52ae89b19..00000000000 --- a/library/std/src/sys/personality/emcc.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! On Emscripten Rust panics are wrapped in C++ exceptions, so we just forward -//! to `__gxx_personality_v0` which is provided by Emscripten. - -use crate::ffi::c_int; -use unwind as uw; - -// This is required by the compiler to exist (e.g., it's a lang item), but it's -// never actually called by the compiler. Emscripten EH doesn't use a -// personality function at all, it instead uses __cxa_find_matching_catch. -// Wasm error handling would use __gxx_personality_wasm0. -#[lang = "eh_personality"] -unsafe extern "C" fn rust_eh_personality( - _version: c_int, - _actions: uw::_Unwind_Action, - _exception_class: uw::_Unwind_Exception_Class, - _exception_object: *mut uw::_Unwind_Exception, - _context: *mut uw::_Unwind_Context, -) -> uw::_Unwind_Reason_Code { - core::intrinsics::abort() -} diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs deleted file mode 100644 index 6f317131145..00000000000 --- a/library/std/src/sys/personality/gcc.rs +++ /dev/null @@ -1,293 +0,0 @@ -//! Implementation of panics backed by libgcc/libunwind (in some form). -//! -//! For background on exception handling and stack unwinding please see -//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and -//! documents linked from it. -//! These are also good reads: -//! * -//! * -//! * -//! -//! ## A brief summary -//! -//! Exception handling happens in two phases: a search phase and a cleanup -//! phase. -//! -//! In both phases the unwinder walks stack frames from top to bottom using -//! information from the stack frame unwind sections of the current process's -//! modules ("module" here refers to an OS module, i.e., an executable or a -//! dynamic library). -//! -//! For each stack frame, it invokes the associated "personality routine", whose -//! address is also stored in the unwind info section. -//! -//! In the search phase, the job of a personality routine is to examine -//! exception object being thrown, and to decide whether it should be caught at -//! that stack frame. Once the handler frame has been identified, cleanup phase -//! begins. -//! -//! In the cleanup phase, the unwinder invokes each personality routine again. -//! This time it decides which (if any) cleanup code needs to be run for -//! the current stack frame. If so, the control is transferred to a special -//! branch in the function body, the "landing pad", which invokes destructors, -//! frees memory, etc. At the end of the landing pad, control is transferred -//! back to the unwinder and unwinding resumes. -//! -//! Once stack has been unwound down to the handler frame level, unwinding stops -//! and the last personality routine transfers control to the catch block. - -use super::dwarf::eh::{self, EHAction, EHContext}; -use crate::ffi::c_int; -use unwind as uw; - -// Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister() -// and TargetLowering::getExceptionSelectorRegister() for each architecture, -// then mapped to DWARF register numbers via register definition tables -// (typically RegisterInfo.td, search for "DwarfRegNum"). -// See also https://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register. - -#[cfg(target_arch = "x86")] -const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX - -#[cfg(target_arch = "x86_64")] -const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX - -#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] -const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 / X0, X1 - -#[cfg(target_arch = "m68k")] -const UNWIND_DATA_REG: (i32, i32) = (0, 1); // D0, D1 - -#[cfg(any( - target_arch = "mips", - target_arch = "mips32r6", - target_arch = "mips64", - target_arch = "mips64r6" -))] -const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1 - -#[cfg(target_arch = "csky")] -const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 - -#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] -const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4 / X3, X4 - -#[cfg(target_arch = "s390x")] -const UNWIND_DATA_REG: (i32, i32) = (6, 7); // R6, R7 - -#[cfg(any(target_arch = "sparc", target_arch = "sparc64"))] -const UNWIND_DATA_REG: (i32, i32) = (24, 25); // I0, I1 - -#[cfg(target_arch = "hexagon")] -const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 - -#[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))] -const UNWIND_DATA_REG: (i32, i32) = (10, 11); // x10, x11 - -#[cfg(target_arch = "loongarch64")] -const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 - -// The following code is based on GCC's C and C++ personality routines. For reference, see: -// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc -// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c - -cfg_if::cfg_if! { - if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "netbsd")))] { - // ARM EHABI personality routine. - // https://web.archive.org/web/20190728160938/https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf - // - // iOS uses the default routine instead since it uses SjLj unwinding. - #[lang = "eh_personality"] - unsafe extern "C" fn rust_eh_personality( - state: uw::_Unwind_State, - exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context, - ) -> uw::_Unwind_Reason_Code { - let state = state as c_int; - let action = state & uw::_US_ACTION_MASK as c_int; - let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { - // Backtraces on ARM will call the personality routine with - // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases - // we want to continue unwinding the stack, otherwise all our backtraces - // would end at __rust_try - if state & uw::_US_FORCE_UNWIND as c_int != 0 { - return continue_unwind(exception_object, context); - } - true - } else if action == uw::_US_UNWIND_FRAME_STARTING as c_int { - false - } else if action == uw::_US_UNWIND_FRAME_RESUME as c_int { - return continue_unwind(exception_object, context); - } else { - return uw::_URC_FAILURE; - }; - - // The DWARF unwinder assumes that _Unwind_Context holds things like the function - // and LSDA pointers, however ARM EHABI places them into the exception object. - // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which - // take only the context pointer, GCC personality routines stash a pointer to - // exception_object in the context, using location reserved for ARM's - // "scratch register" (r12). - uw::_Unwind_SetGR(context, uw::UNWIND_POINTER_REG, exception_object as uw::_Unwind_Ptr); - // ...A more principled approach would be to provide the full definition of ARM's - // _Unwind_Context in our libunwind bindings and fetch the required data from there - // directly, bypassing DWARF compatibility functions. - - let eh_action = match find_eh_action(context) { - Ok(action) => action, - Err(_) => return uw::_URC_FAILURE, - }; - if search_phase { - match eh_action { - EHAction::None | EHAction::Cleanup(_) => { - return continue_unwind(exception_object, context); - } - EHAction::Catch(_) | EHAction::Filter(_) => { - // EHABI requires the personality routine to update the - // SP value in the barrier cache of the exception object. - (*exception_object).private[5] = - uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG); - return uw::_URC_HANDLER_FOUND; - } - EHAction::Terminate => return uw::_URC_FAILURE, - } - } else { - match eh_action { - EHAction::None => return continue_unwind(exception_object, context), - EHAction::Filter(_) if state & uw::_US_FORCE_UNWIND as c_int != 0 => return continue_unwind(exception_object, context), - EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => { - uw::_Unwind_SetGR( - context, - UNWIND_DATA_REG.0, - exception_object as uw::_Unwind_Ptr, - ); - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); - uw::_Unwind_SetIP(context, lpad); - return uw::_URC_INSTALL_CONTEXT; - } - EHAction::Terminate => return uw::_URC_FAILURE, - } - } - - // On ARM EHABI the personality routine is responsible for actually - // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). - unsafe fn continue_unwind( - exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context, - ) -> uw::_Unwind_Reason_Code { - if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON { - uw::_URC_CONTINUE_UNWIND - } else { - uw::_URC_FAILURE - } - } - // defined in libgcc - extern "C" { - fn __gnu_unwind_frame( - exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context, - ) -> uw::_Unwind_Reason_Code; - } - } - } else { - // Default personality routine, which is used directly on most targets - // and indirectly on Windows x86_64 via SEH. - unsafe extern "C" fn rust_eh_personality_impl( - version: c_int, - actions: uw::_Unwind_Action, - _exception_class: uw::_Unwind_Exception_Class, - exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context, - ) -> uw::_Unwind_Reason_Code { - if version != 1 { - return uw::_URC_FATAL_PHASE1_ERROR; - } - let eh_action = match find_eh_action(context) { - Ok(action) => action, - Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, - }; - if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { - match eh_action { - EHAction::None | EHAction::Cleanup(_) => uw::_URC_CONTINUE_UNWIND, - EHAction::Catch(_) | EHAction::Filter(_) => uw::_URC_HANDLER_FOUND, - EHAction::Terminate => uw::_URC_FATAL_PHASE1_ERROR, - } - } else { - match eh_action { - EHAction::None => uw::_URC_CONTINUE_UNWIND, - // Forced unwinding hits a terminate action. - EHAction::Filter(_) if actions as i32 & uw::_UA_FORCE_UNWIND as i32 != 0 => uw::_URC_CONTINUE_UNWIND, - EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => { - uw::_Unwind_SetGR( - context, - UNWIND_DATA_REG.0, - exception_object.cast(), - ); - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); - uw::_Unwind_SetIP(context, lpad); - uw::_URC_INSTALL_CONTEXT - } - EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR, - } - } - } - - cfg_if::cfg_if! { - if #[cfg(all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), target_env = "gnu"))] { - // On x86_64 MinGW targets, the unwinding mechanism is SEH however the unwind - // handler data (aka LSDA) uses GCC-compatible encoding. - #[lang = "eh_personality"] - #[allow(nonstandard_style)] - unsafe extern "C" fn rust_eh_personality( - exceptionRecord: *mut uw::EXCEPTION_RECORD, - establisherFrame: uw::LPVOID, - contextRecord: *mut uw::CONTEXT, - dispatcherContext: *mut uw::DISPATCHER_CONTEXT, - ) -> uw::EXCEPTION_DISPOSITION { - uw::_GCC_specific_handler( - exceptionRecord, - establisherFrame, - contextRecord, - dispatcherContext, - rust_eh_personality_impl, - ) - } - } else { - // The personality routine for most of our targets. - #[lang = "eh_personality"] - unsafe extern "C" fn rust_eh_personality( - version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context, - ) -> uw::_Unwind_Reason_Code { - rust_eh_personality_impl( - version, - actions, - exception_class, - exception_object, - context, - ) - } - } - } - } -} - -unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result { - let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; - let mut ip_before_instr: c_int = 0; - let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr); - let eh_context = EHContext { - // The return address points 1 byte past the call instruction, - // which could be in the next IP range in LSDA range table. - // - // `ip = -1` has special meaning, so use wrapping sub to allow for that - ip: if ip_before_instr != 0 { ip } else { ip.wrapping_sub(1) }, - func_start: uw::_Unwind_GetRegionStart(context), - get_text_start: &|| uw::_Unwind_GetTextRelBase(context), - get_data_start: &|| uw::_Unwind_GetDataRelBase(context), - }; - eh::find_eh_action(lsda, &eh_context) -} diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs deleted file mode 100644 index d37b8ce6346..00000000000 --- a/library/std/src/sys/personality/mod.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! This module contains the implementation of the `eh_personality` lang item. -//! -//! The actual implementation is heavily dependent on the target since Rust -//! tries to use the native stack unwinding mechanism whenever possible. -//! -//! This personality function is still required with `-C panic=abort` because -//! it is used to catch foreign exceptions from `extern "C-unwind"` and turn -//! them into aborts. -//! -//! Additionally, ARM EHABI uses the personality function when generating -//! backtraces. - -mod dwarf; - -#[cfg(not(any(test, doctest)))] -cfg_if::cfg_if! { - if #[cfg(target_os = "emscripten")] { - mod emcc; - } else if #[cfg(target_env = "msvc")] { - // This is required by the compiler to exist (e.g., it's a lang item), - // but it's never actually called by the compiler because - // _CxxFrameHandler3 is the personality function that is always used. - // Hence this is just an aborting stub. - #[lang = "eh_personality"] - fn rust_eh_personality() { - core::intrinsics::abort() - } - } else if #[cfg(any( - all(target_family = "windows", target_env = "gnu"), - target_os = "psp", - target_os = "xous", - target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re")), - all(target_vendor = "fortanix", target_env = "sgx"), - ))] { - mod gcc; - } else { - // Targets that don't support unwinding. - // - family=wasm - // - os=none ("bare metal" targets) - // - os=uefi - // - os=espidf - // - os=hermit - // - nvptx64-nvidia-cuda - // - arch=avr - } -} diff --git a/library/std/src/sys/sgx/abi/entry.S b/library/std/src/sys/sgx/abi/entry.S deleted file mode 100644 index 8a063b65dac..00000000000 --- a/library/std/src/sys/sgx/abi/entry.S +++ /dev/null @@ -1,376 +0,0 @@ -/* 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 0x1fbf - -/* 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 enclave 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 enclave .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< !; - -// 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 deleted file mode 100644 index 18e6d5b3fa2..00000000000 --- a/library/std/src/sys/sgx/abi/mem.rs +++ /dev/null @@ -1,93 +0,0 @@ -use core::arch::asm; - -// Do not remove inline: will result in relocation failure -#[inline(always)] -pub(crate) unsafe fn rel_ptr(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(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 deleted file mode 100644 index 9508c387415..00000000000 --- a/library/std/src/sys/sgx/abi/mod.rs +++ /dev/null @@ -1,108 +0,0 @@ -#![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 deleted file mode 100644 index 229b3b3291f..00000000000 --- a/library/std/src/sys/sgx/abi/panic.rs +++ /dev/null @@ -1,42 +0,0 @@ -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 { - 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 { - 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 deleted file mode 100644 index 02dff0ad29f..00000000000 --- a/library/std/src/sys/sgx/abi/reloc.rs +++ /dev/null @@ -1,32 +0,0 @@ -use super::mem; -use crate::slice::from_raw_parts; - -const R_X86_64_RELATIVE: u32 = 8; - -#[repr(packed)] -struct Rela { - 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::>(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 deleted file mode 100644 index 2b23e368cc3..00000000000 --- a/library/std/src/sys/sgx/abi/thread.rs +++ /dev/null @@ -1,17 +0,0 @@ -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() -> *mut u8; - } - let addr = unsafe { get_tcs_addr() }; - match Tcs::new(addr) { - Some(tcs) => tcs, - None => rtabort!("TCS must not be placed at address zero (this is a linker error)"), - } -} diff --git a/library/std/src/sys/sgx/abi/tls/mod.rs b/library/std/src/sys/sgx/abi/tls/mod.rs deleted file mode 100644 index 09c4ab3d3e9..00000000000 --- a/library/std/src/sys/sgx/abi/tls/mod.rs +++ /dev/null @@ -1,133 +0,0 @@ -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>(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) { - // 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) -> 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); - unsafe { Self::current() }.data[index].set(ptr::null_mut()); - 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 deleted file mode 100644 index 4eeff8f6ef7..00000000000 --- a/library/std/src/sys/sgx/abi/tls/sync_bitset.rs +++ /dev/null @@ -1,85 +0,0 @@ -#[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 { - '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>>, - elem_idx: usize, -} - -impl<'a> Iterator for SyncBitsetIter<'a> { - type Item = usize; - - fn next(&mut self) -> Option { - 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 deleted file mode 100644 index d7eb2e139d0..00000000000 --- a/library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs +++ /dev/null @@ -1,25 +0,0 @@ -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::>(), 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 deleted file mode 100644 index f99cea360f1..00000000000 --- a/library/std/src/sys/sgx/abi/usercalls/alloc.rs +++ /dev/null @@ -1,819 +0,0 @@ -#![allow(unused)] - -use crate::arch::asm; -use crate::cell::UnsafeCell; -use crate::cmp; -use crate::convert::TryInto; -use crate::intrinsics; -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 UserSafeSized for FifoDescriptor {} -#[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 UserSafeSized for Cancel {} -#[unstable(feature = "sgx_platform", issue = "56975")] -unsafe impl 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::`. - 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 { - 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: *const u8| -> bool { p.is_aligned_to(Self::align_of()) }; - - 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 UserSafe for T { - fn align_of() -> usize { - mem::align_of::() - } - - unsafe fn from_raw_sized_unchecked(ptr: *mut u8, size: usize) -> *mut Self { - assert_eq!(size, mem::size_of::()); - ptr as _ - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -unsafe impl UserSafe for [T] { - fn align_of() -> usize { - mem::align_of::() - } - - /// # 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::(); - 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` 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`. 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(UnsafeCell); -/// An owned type in userspace memory. `User` is equivalent to `Box` 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(NonNull>); - -#[unstable(feature = "sgx_platform", issue = "56975")] -unsafe impl Send for User {} - -#[unstable(feature = "sgx_platform", issue = "56975")] -unsafe impl Send for User<[T]> {} - -trait NewUserRef { - unsafe fn new_userref(v: T) -> Self; -} - -impl NewUserRef<*mut T> for NonNull> { - unsafe fn new_userref(v: *mut T) -> Self { - // SAFETY: The caller has guaranteed the pointer is valid - unsafe { NonNull::new_unchecked(v as _) } - } -} - -impl NewUserRef> for NonNull> { - unsafe fn new_userref(v: NonNull) -> Self { - // SAFETY: The caller has guaranteed the pointer is valid - unsafe { NonNull::new_userref(v.as_ptr()) } - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl User -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` 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 User -where - T: UserSafe, -{ - /// Allocate space for `T` in user memory. - pub fn uninitialized() -> Self { - Self::new_uninit_bytes(mem::size_of::()) - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl 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::()) - } - - /// 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::())) - }) - } -} - -/// Divide the slice `(ptr, len)` into three parts, where the middle part is -/// aligned to `u64`. -/// -/// The return values `(prefix_len, mid_len, suffix_len)` add back up to `len`. -/// The return values are such that the memory region `(ptr + prefix_len, -/// mid_len)` is the largest possible region where `ptr + prefix_len` is aligned -/// to `u64` and `mid_len` is a multiple of the byte size of `u64`. This means -/// that `prefix_len` and `suffix_len` are guaranteed to be less than the byte -/// size of `u64`, and that `(ptr, prefix_len)` and `(ptr + prefix_len + -/// mid_len, suffix_len)` don't straddle an alignment boundary. -// Standard Rust functions such as `<[u8]>::align_to::` and -// `<*const u8>::align_offset` aren't _guaranteed_ to compute the largest -// possible middle region, and as such can't be used. -fn u64_align_to_guaranteed(ptr: *const u8, mut len: usize) -> (usize, usize, usize) { - const QWORD_SIZE: usize = mem::size_of::(); - - let offset = ptr as usize % QWORD_SIZE; - - let prefix_len = if intrinsics::unlikely(offset > 0) { QWORD_SIZE - offset } else { 0 }; - - len = match len.checked_sub(prefix_len) { - Some(remaining_len) => remaining_len, - None => return (len, 0, 0), - }; - - let suffix_len = len % QWORD_SIZE; - len -= suffix_len; - - (prefix_len, len, suffix_len) -} - -unsafe fn copy_quadwords(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) - ); - } -} - -/// 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) { - /// Like `ptr::copy(src, dst, len)`, except it uses the Intel-recommended - /// instruction sequence for unaligned writes. - unsafe fn write_bytewise_to_userspace(src: *const u8, dst: *mut u8, len: usize) { - if intrinsics::likely(len == 0) { - return; - } - - 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.add(off), - dst = in(reg) dst.add(off), - seg_sel = in(reg) &mut seg_sel, - options(nostack, att_syntax) - ); - } - } - } - - 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.addr().overflowing_add(len).1); - assert!(!dst.addr().overflowing_add(len).1); - - unsafe { - let (len1, len2, len3) = u64_align_to_guaranteed(dst, len); - let (src1, dst1) = (src, dst); - let (src2, dst2) = (src1.add(len1), dst1.add(len1)); - let (src3, dst3) = (src2.add(len2), dst2.add(len2)); - - write_bytewise_to_userspace(src1, dst1, len1); - copy_quadwords(src2, dst2, len2); - write_bytewise_to_userspace(src3, dst3, len3); - } -} - -/// Copies `len` bytes of data from userspace pointer `src` to enclave pointer `dst` -/// -/// This function mitigates AEPIC leak vulnerabilities by ensuring all reads from untrusted memory are 8-byte aligned -/// -/// # Panics -/// This function panics if: -/// -/// * The `src` pointer is null -/// * The `dst` pointer is null -/// * The `src` memory range is not in user memory -/// * The `dst` memory range is not in enclave memory -/// -/// # References -/// - https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00657.html -/// - https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/stale-data-read-from-xapic.html -pub(crate) unsafe fn copy_from_userspace(src: *const u8, dst: *mut u8, len: usize) { - /// Like `ptr::copy(src, dst, len)`, except it uses only u64-aligned reads. - /// - /// # Safety - /// The source memory region must not straddle an alignment boundary. - unsafe fn read_misaligned_from_userspace(src: *const u8, dst: *mut u8, len: usize) { - if intrinsics::likely(len == 0) { - return; - } - - unsafe { - let offset: usize; - let data: u64; - // doing a memory read that's potentially out of bounds for `src`, - // this isn't supported by Rust, so have to use assembly - asm!(" - movl {src:e}, {offset:e} - andl $7, {offset:e} - andq $-8, {src} - movq ({src}), {dst} - ", - src = inout(reg) src => _, - offset = out(reg) offset, - dst = out(reg) data, - options(nostack, att_syntax, readonly, pure) - ); - let data = data.to_le_bytes(); - ptr::copy_nonoverlapping(data.as_ptr().add(offset), dst, len); - } - } - - assert!(!src.is_null()); - assert!(!dst.is_null()); - assert!(is_user_range(src, len)); - assert!(is_enclave_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); - - unsafe { - let (len1, len2, len3) = u64_align_to_guaranteed(src, len); - let (src1, dst1) = (src, dst); - let (src2, dst2) = (src1.add(len1), dst1.add(len1)); - let (src3, dst3) = (src2.add(len2), dst2.add(len2)); - - read_misaligned_from_userspace(src1, dst1, len1); - copy_quadwords(src2, dst2, len2); - read_misaligned_from_userspace(src3, dst3, len3); - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl UserRef -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`. - /// - /// # 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())); - copy_from_userspace( - 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 UserRef -where - T: UserSafe, -{ - /// Copies the value from user memory into enclave memory. - pub fn to_enclave(&self) -> T { - unsafe { - let mut data = mem::MaybeUninit::uninit(); - copy_from_userspace(self.0.get() as _, data.as_mut_ptr() as _, mem::size_of::()); - data.assume_init() - } - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl 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::()).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`. - /// - /// # 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::()).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) { - 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 { - 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; - - #[inline] - fn next(&mut self) -> Option { - 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; - - #[inline] - fn next(&mut self) -> Option { - unsafe { self.0.next().map(|e| UserRef::from_mut_ptr(e)) } - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl Deref for User -where - T: UserSafe, -{ - type Target = UserRef; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.0.as_ptr() } - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl DerefMut for User -where - T: UserSafe, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *self.0.as_ptr() } - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl Drop for User -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, U> CoerceUnsized> for UserRef {} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl Index for UserRef<[T]> -where - [T]: UserSafe, - I: SliceIndex<[T]>, - I::Output: UserSafe, -{ - type Output = UserRef; - - #[inline] - fn index(&self, index: I) -> &UserRef { - 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 IndexMut for UserRef<[T]> -where - [T]: UserSafe, - I: SliceIndex<[T]>, - I::Output: UserSafe, -{ - #[inline] - fn index_mut(&mut self, index: I) -> &mut UserRef { - 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 { - /// 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 { - 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 deleted file mode 100644 index e19e843267a..00000000000 --- a/library/std/src/sys/sgx/abi/usercalls/mod.rs +++ /dev/null @@ -1,329 +0,0 @@ -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 { - 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> { - 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 { - 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, 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::::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 { - 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(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) -> 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) -> 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}") - } -} - -/// Translate the raw result of an SGX usercall. -#[unstable(feature = "sgx_platform", issue = "56975")] -pub trait FromSgxResult { - /// Return type - type Return; - - /// Translate the raw result of an SGX usercall. - fn from_sgx_result(self) -> IoResult; -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl FromSgxResult for (Result, T) { - type Return = T; - - fn from_sgx_result(self) -> IoResult { - if self.0 == RESULT_SUCCESS { - Ok(self.1) - } else { - Err(IoError::from_raw_os_error(check_os_error(self.0))) - } - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl FromSgxResult for Result { - type Return = (); - - fn from_sgx_result(self) -> IoResult { - 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 deleted file mode 100644 index 10c1456d4fd..00000000000 --- a/library/std/src/sys/sgx/abi/usercalls/raw.rs +++ /dev/null @@ -1,269 +0,0 @@ -#![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) -} - -/// A value passed or returned in a CPU register. -#[unstable(feature = "sgx_platform", issue = "56975")] -pub type Register = u64; - -/// Translate a type from/to Register to be used as an argument. -#[unstable(feature = "sgx_platform", issue = "56975")] -pub trait RegisterArgument { - /// Translate a Register to Self. - fn from_register(_: Register) -> Self; - /// Translate self to a Register. - fn into_register(self) -> Register; -} - -/// Translate a pair of Registers to the raw usercall return value. -#[unstable(feature = "sgx_platform", issue = "56975")] -pub trait ReturnValue { - /// Translate a pair of Registers to the raw usercall return value. - 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) => { - #[unstable(feature = "sgx_platform", issue = "56975")] - 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) => { - #[unstable(feature = "sgx_platform", issue = "56975")] - 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) => { - #[unstable(feature = "sgx_platform", issue = "56975")] - 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!( *const T); -define_ra!( *mut T); - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl RegisterArgument for bool { - fn from_register(a: Register) -> bool { - if a != 0 { true } else { false } - } - fn into_register(self) -> Register { - self as _ - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl RegisterArgument for Option> { - fn from_register(a: Register) -> Option> { - NonNull::new(a as _) - } - fn into_register(self) -> Register { - self.map_or(0 as _, NonNull::as_ptr) as _ - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl ReturnValue for ! { - fn from_registers(call: &'static str, _regs: (Register, Register)) -> Self { - rtabort!("Usercall {call}: did not expect to be re-entered"); - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl ReturnValue for () { - fn from_registers(call: &'static str, usercall_retval: (Register, Register)) -> Self { - rtassert!(usercall_retval.0 == 0); - rtassert!(usercall_retval.1 == 0); - () - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl 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) - } -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl 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 deleted file mode 100644 index 58b8eb215d7..00000000000 --- a/library/std/src/sys/sgx/abi/usercalls/tests.rs +++ /dev/null @@ -1,56 +0,0 @@ -use super::alloc::User; -use super::alloc::{copy_from_userspace, copy_to_userspace}; - -#[test] -fn test_copy_to_userspace_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().add(offset), size) }; - - // Verify copy - for byte in 0..size { - unsafe { - assert_eq!(*dst.as_ptr().add(offset + byte), src[byte as usize]); - } - } - } - } -} - -#[test] -fn test_copy_from_userspace_function() { - let mut dst = [0u8; 100]; - let mut src = User::<[u8]>::uninitialized(100); - - src.copy_from_enclave(&[0u8; 100]); - - for size in 0..48 { - // For all possible alignment - for offset in 0..8 { - // overwrite complete dst - dst = [0u8; 100]; - - // Copy src[0..size] to dst + offset - unsafe { copy_from_userspace(src.as_ptr().offset(offset), dst.as_mut_ptr(), size) }; - - // Verify copy - for byte in 0..size { - unsafe { - assert_eq!(dst[byte as usize], *src.as_ptr().offset(offset + byte as isize)); - } - } - } - } -} diff --git a/library/std/src/sys/sgx/alloc.rs b/library/std/src/sys/sgx/alloc.rs deleted file mode 100644 index 4aea28cb83e..00000000000 --- a/library/std/src/sys/sgx/alloc.rs +++ /dev/null @@ -1,98 +0,0 @@ -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> = - 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 deleted file mode 100644 index ef4176c4ac0..00000000000 --- a/library/std/src/sys/sgx/args.rs +++ /dev/null @@ -1,59 +0,0 @@ -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; - -#[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::(); - 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 { - self.0.next().cloned() - } - fn size_hint(&self) -> (usize, Option) { - 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 { - self.0.next_back().cloned() - } -} diff --git a/library/std/src/sys/sgx/condvar.rs b/library/std/src/sys/sgx/condvar.rs deleted file mode 100644 index aa1174664ae..00000000000 --- a/library/std/src/sys/sgx/condvar.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::sys::locks::Mutex; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; -use crate::time::Duration; - -use super::waitqueue::{SpinMutex, WaitQueue, WaitVariable}; - -/// FIXME: `UnsafeList` is not movable. -struct AllocatedCondvar(SpinMutex>); - -pub struct Condvar { - inner: LazyBox, -} - -impl LazyInit for AllocatedCondvar { - fn init() -> Box { - Box::new(AllocatedCondvar(SpinMutex::new(WaitVariable::new(())))) - } -} - -impl Condvar { - pub const fn new() -> Condvar { - Condvar { inner: LazyBox::new() } - } - - #[inline] - pub fn notify_one(&self) { - let _ = WaitQueue::notify_one(self.inner.0.lock()); - } - - #[inline] - pub fn notify_all(&self) { - let _ = WaitQueue::notify_all(self.inner.0.lock()); - } - - pub unsafe fn wait(&self, mutex: &Mutex) { - let guard = self.inner.0.lock(); - WaitQueue::wait(guard, || unsafe { mutex.unlock() }); - mutex.lock() - } - - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - let success = WaitQueue::wait_timeout(&self.inner.0, dur, || unsafe { mutex.unlock() }); - mutex.lock(); - success - } -} diff --git a/library/std/src/sys/sgx/env.rs b/library/std/src/sys/sgx/env.rs deleted file mode 100644 index 8043b7c5213..00000000000 --- a/library/std/src/sys/sgx/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index b3686d0e283..00000000000 --- a/library/std/src/sys/sgx/fd.rs +++ /dev/null @@ -1,89 +0,0 @@ -use fortanix_sgx_abi::Fd; - -use super::abi::usercalls; -use crate::io::{self, BorrowedCursor, 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 { - usercalls::read(self.fd, &mut [IoSliceMut::new(buf)]) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - crate::io::default_read_buf(|b| self.read(b), buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - usercalls::read(self.fd, bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - usercalls::write(self.fd, &[IoSlice::new(buf)]) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - 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 for FileDesc { - #[inline] - fn as_inner(&self) -> &Fd { - &self.fd - } -} - -impl IntoInner for FileDesc { - fn into_inner(self) -> Fd { - let fd = self.fd; - mem::forget(self); - fd - } -} - -impl FromInner 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 deleted file mode 100644 index 9967482197e..00000000000 --- a/library/std/src/sys/sgx/memchr.rs +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 09d3f7638ca..00000000000 --- a/library/std/src/sys/sgx/mod.rs +++ /dev/null @@ -1,175 +0,0 @@ -//! 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)] -#![allow(fuzzy_provenance_casts)] // FIXME: this entire module systematically confuses pointers and integers - -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 thread_parking; -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, _sigpipe: 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() -> crate::io::Result { - 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(v: T) -> crate::io::Result { - 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) - } -} - -#[inline] -pub fn is_interrupted(code: i32) -> bool { - use fortanix_sgx_abi::Error; - code == Error::Interrupted as _ -} - -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: Sized { - fn try_into_inner(self) -> Result; -} diff --git a/library/std/src/sys/sgx/mutex.rs b/library/std/src/sys/sgx/mutex.rs deleted file mode 100644 index 0dbf020ebe0..00000000000 --- a/library/std/src/sys/sgx/mutex.rs +++ /dev/null @@ -1,59 +0,0 @@ -use super::waitqueue::{try_lock_or_false, SpinMutex, WaitQueue, WaitVariable}; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; - -/// FIXME: `UnsafeList` is not movable. -struct AllocatedMutex(SpinMutex>); - -pub struct Mutex { - inner: LazyBox, -} - -impl LazyInit for AllocatedMutex { - fn init() -> Box { - Box::new(AllocatedMutex(SpinMutex::new(WaitVariable::new(false)))) - } -} - -// Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28 -impl Mutex { - pub const fn new() -> Mutex { - Mutex { inner: LazyBox::new() } - } - - #[inline] - pub fn lock(&self) { - let mut guard = self.inner.0.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.0.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 fn try_lock(&self) -> bool { - let mut guard = try_lock_or_false!(self.inner.0); - 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 deleted file mode 100644 index 03620a08f2c..00000000000 --- a/library/std/src/sys/sgx/net.rs +++ /dev/null @@ -1,548 +0,0 @@ -use crate::error; -use crate::fmt; -use crate::io::{self, BorrowedCursor, 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, - local_addr: Option, -} - -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 for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.inner - } -} - -impl TryIntoInner for Socket { - fn try_into_inner(self) -> Result { - let Socket { inner, local_addr } = self; - Arc::try_unwrap(inner).map_err(|inner| Socket { inner, local_addr }) - } -} - -impl FromInner<(FileDesc, Option)> for Socket { - fn from_inner((inner, local_addr): (FileDesc, Option)) -> Socket { - Socket { inner: Arc::new(inner), local_addr } - } -} - -#[derive(Clone)] -pub struct TcpStream { - inner: Socket, - peer_addr: Option, -} - -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 { - 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::()).is_some() { - Ok(e.into_inner().unwrap().downcast::().unwrap().host) - } else { - Err(e) - } - } - } -} - -fn addr_to_sockaddr(addr: &Option) -> io::Result { - 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 { - 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 { - 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) -> 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) -> 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> { - sgx_ineffective(None) - } - - pub fn write_timeout(&self) -> io::Result> { - sgx_ineffective(None) - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - Ok(0) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.inner.inner.read(buf) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.inner.inner.read_buf(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - 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 { - self.inner.inner.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - 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 { - addr_to_sockaddr(&self.peer_addr) - } - - pub fn socket_addr(&self) -> io::Result { - addr_to_sockaddr(&self.inner.local_addr) - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn duplicate(&self) -> io::Result { - Ok(self.clone()) - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn linger(&self) -> io::Result> { - sgx_ineffective(None) - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn nodelay(&self) -> io::Result { - sgx_ineffective(false) - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn ttl(&self) -> io::Result { - sgx_ineffective(DEFAULT_FAKE_TTL) - } - - pub fn take_error(&self) -> io::Result> { - Ok(None) - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - sgx_ineffective(()) - } -} - -impl AsInner for TcpStream { - #[inline] - 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)> for TcpStream { - fn into_inner(self) -> (Socket, Option) { - (self.inner, self.peer_addr) - } -} - -impl FromInner<(Socket, Option)> for TcpStream { - fn from_inner((inner, peer_addr): (Socket, Option)) -> 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 { - 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 { - 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 { - Ok(self.clone()) - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn ttl(&self) -> io::Result { - sgx_ineffective(DEFAULT_FAKE_TTL) - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn only_v6(&self) -> io::Result { - sgx_ineffective(false) - } - - pub fn take_error(&self) -> io::Result> { - Ok(None) - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - sgx_ineffective(()) - } -} - -impl AsInner for TcpListener { - #[inline] - fn as_inner(&self) -> &Socket { - &self.inner - } -} - -impl IntoInner for TcpListener { - fn into_inner(self) -> Socket { - self.inner - } -} - -impl FromInner for TcpListener { - fn from_inner(inner: Socket) -> TcpListener { - TcpListener { inner } - } -} - -pub struct UdpSocket(!); - -impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - 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 { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn broadcast(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v6(&self) -> io::Result { - 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 { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn send(&self, _: &[u8]) -> io::Result { - 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 { - 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 { - self.0 - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(v: &str) -> io::Result { - 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::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 deleted file mode 100644 index 86f4c7d3d56..00000000000 --- a/library/std/src/sys/sgx/os.rs +++ /dev/null @@ -1,187 +0,0 @@ -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 { - 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 { - self.0 - } -} - -#[derive(Debug)] -pub struct JoinPathsError; - -pub fn join_paths(_paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - 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 { - 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>; - -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 struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -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) { - self.iter.size_hint() - } -} - -pub fn env() -> Env { - let clone_to_vec = |map: &HashMap| -> Vec<_> { - map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() - }; - - let iter = get_env_store() - .map(|env| clone_to_vec(&env.lock().unwrap())) - .unwrap_or_default() - .into_iter(); - Env { iter } -} - -pub fn getenv(k: &OsStr) -> Option { - 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 { - 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 deleted file mode 100644 index c805c15e702..00000000000 --- a/library/std/src/sys/sgx/path.rs +++ /dev/null @@ -1,25 +0,0 @@ -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> { - None -} - -pub const MAIN_SEP_STR: &str = "/"; -pub const MAIN_SEP: char = '/'; - -pub(crate) fn absolute(_path: &Path) -> io::Result { - unsupported() -} diff --git a/library/std/src/sys/sgx/rwlock.rs b/library/std/src/sys/sgx/rwlock.rs deleted file mode 100644 index d89de18ca5f..00000000000 --- a/library/std/src/sys/sgx/rwlock.rs +++ /dev/null @@ -1,222 +0,0 @@ -#[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::alloc::Layout; - -struct AllocatedRwLock { - readers: SpinMutex>>, - writer: SpinMutex>, -} - -pub struct RwLock { - inner: LazyBox, -} - -impl LazyInit for AllocatedRwLock { - fn init() -> Box { - Box::new(AllocatedRwLock { - readers: SpinMutex::new(WaitVariable::new(None)), - writer: SpinMutex::new(WaitVariable::new(false)), - }) - } -} - -// Check at compile time that RwLock's size and alignment matches the C definition -// in libunwind (see also `test_c_rwlock_initializer` in `tests`). -const _: () = { - let rust = Layout::new::(); - let c = Layout::new::<*mut ()>(); - assert!(rust.size() == c.size()); - assert!(rust.align() == c.align()); -}; - -impl RwLock { - pub const fn new() -> RwLock { - RwLock { inner: LazyBox::new() } - } - - #[inline] - pub fn read(&self) { - let lock = &*self.inner; - let mut rguard = lock.readers.lock(); - let wguard = lock.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 lock = &*self.inner; - let mut rguard = try_lock_or_false!(lock.readers); - let wguard = try_lock_or_false!(lock.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 fn write(&self) { - let lock = &*self.inner; - let rguard = lock.readers.lock(); - let mut wguard = lock.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 fn try_write(&self) -> bool { - let lock = &*self.inner; - let rguard = try_lock_or_false!(lock.readers); - let mut wguard = try_lock_or_false!(lock.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>>, - wguard: SpinMutexGuard<'_, WaitVariable>, - ) { - *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 lock = &*self.inner; - let rguard = lock.readers.lock(); - let wguard = lock.writer.lock(); - unsafe { self.__read_unlock(rguard, wguard) }; - } - - #[inline] - unsafe fn __write_unlock( - &self, - rguard: SpinMutexGuard<'_, WaitVariable>>, - wguard: SpinMutexGuard<'_, WaitVariable>, - ) { - 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 lock = &*self.inner; - let rguard = lock.readers.lock(); - let wguard = lock.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 lock = &*self.inner; - let rguard = lock.readers.lock(); - let wguard = lock.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 deleted file mode 100644 index 5fd6670afd4..00000000000 --- a/library/std/src/sys/sgx/rwlock/tests.rs +++ /dev/null @@ -1,21 +0,0 @@ -use super::*; -use crate::ptr; - -// 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() { - const C_RWLOCK_INIT: *mut () = ptr::null_mut(); - - // 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::<_, *mut ()>(&RUST_RWLOCK_INIT), C_RWLOCK_INIT); - }; -} diff --git a/library/std/src/sys/sgx/stdio.rs b/library/std/src/sys/sgx/stdio.rs deleted file mode 100644 index 2e680e740fd..00000000000 --- a/library/std/src/sys/sgx/stdio.rs +++ /dev/null @@ -1,88 +0,0 @@ -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 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 { - 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 { - 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 { - 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 { - 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 deleted file mode 100644 index 7ac9d1d64b4..00000000000 --- a/library/std/src/sys/sgx/thread.rs +++ /dev/null @@ -1,157 +0,0 @@ -#![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); - - impl Drop for JoinNotifier { - fn drop(&mut self) { - self.0.take().unwrap().notify(); - } - } - - pub(super) struct Task { - p: Box, - done: JoinNotifier, - } - - impl Task { - pub(super) fn new(p: Box) -> (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>> = None; - - pub(super) fn lock() -> MutexGuard<'static, Vec> { - 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 crate::pin::Pin; - use crate::sync::Arc; - use crate::sys_common::thread_parking::Parker; - - pub struct Notifier(Arc); - - 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) { - Pin::new(&*self.0).unpark() - } - } - - pub struct Waiter(Arc); - - 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) { - // SAFETY: - // This is only ever called on one thread. - unsafe { Pin::new(&*self.0).park() } - } - } - - pub fn new() -> (Notifier, Waiter) { - let inner = Arc::new(Parker::new()); - (Notifier(inner.clone()), Waiter(inner)) - } -} - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, p: Box) -> io::Result { - 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); - } - - /// SGX should protect in-enclave data from the outside (attacker), - /// so there should be no data leakage to the OS, - /// and therefore also no 1-1 mapping between SGX thread names and OS thread names. - /// - /// This is why the method is intentionally No-Op. - pub fn set_name(_name: &CStr) { - // Note that the internally visible SGX thread name is already provided - // by the platform-agnostic (target-agnostic) Rust thread code. - // This can be observed in the [`std::thread::tests::test_named_thread`] test, - // which succeeds as-is with the SGX target. - } - - pub fn sleep(dur: Duration) { - usercalls::wait_timeout(0, dur, || true); - } - - pub fn join(self) { - self.0.wait(); - } -} - -pub fn available_parallelism() -> io::Result { - unsupported() -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/sgx/thread_local_key.rs b/library/std/src/sys/sgx/thread_local_key.rs deleted file mode 100644 index c7a57d3a3d4..00000000000 --- a/library/std/src/sys/sgx/thread_local_key.rs +++ /dev/null @@ -1,23 +0,0 @@ -use super::abi::tls::{Key as AbiKey, Tls}; - -pub type Key = usize; - -#[inline] -pub unsafe fn create(dtor: Option) -> 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)) -} diff --git a/library/std/src/sys/sgx/thread_parking.rs b/library/std/src/sys/sgx/thread_parking.rs deleted file mode 100644 index 0006cd4f1be..00000000000 --- a/library/std/src/sys/sgx/thread_parking.rs +++ /dev/null @@ -1,23 +0,0 @@ -use super::abi::usercalls; -use crate::io::ErrorKind; -use crate::time::Duration; -use fortanix_sgx_abi::{EV_UNPARK, WAIT_INDEFINITE}; - -pub type ThreadId = fortanix_sgx_abi::Tcs; - -pub use super::abi::thread::current; - -pub fn park(_hint: usize) { - usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap(); -} - -pub fn park_timeout(dur: Duration, _hint: usize) { - let timeout = u128::min(dur.as_nanos(), WAIT_INDEFINITE as u128 - 1) as u64; - if let Err(e) = usercalls::wait(EV_UNPARK, timeout) { - assert!(matches!(e.kind(), ErrorKind::TimedOut | ErrorKind::WouldBlock)) - } -} - -pub fn unpark(tid: ThreadId, _hint: usize) { - let _ = usercalls::send(EV_UNPARK, Some(tid)); -} diff --git a/library/std/src/sys/sgx/time.rs b/library/std/src/sys/sgx/time.rs deleted file mode 100644 index db4cf2804bf..00000000000 --- a/library/std/src/sys/sgx/time.rs +++ /dev/null @@ -1,46 +0,0 @@ -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 { - self.0.checked_sub(other.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_add(*other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - 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 { - self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_add(*other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - 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 deleted file mode 100644 index 25eca61d67b..00000000000 --- a/library/std/src/sys/sgx/waitqueue/mod.rs +++ /dev/null @@ -1,262 +0,0 @@ -//! 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` 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::panic::{self, AssertUnwindSafe}; -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 { - queue: WaitQueue, - lock: T, -} - -impl WaitVariable { - 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>>, - 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>, -} -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(self, guard: U) { - drop(guard); - drop(self); - } -} - -impl<'a, T> Deref for WaitGuard<'a, T> { - type Target = SpinMutexGuard<'a, WaitVariable>; - - 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. When `before_wait` panics, - /// this function will abort. - pub fn wait(mut guard: SpinMutexGuard<'_, WaitVariable>, 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); - if let Err(_e) = panic::catch_unwind(AssertUnwindSafe(|| before_wait())) { - rtabort!("Panic before wait on wakeup event") - } - while !entry.lock().wake { - // `entry.wake` is only set in `notify_one` and `notify_all` functions. Both ensure - // the entry is removed from the queue _before_ setting this bool. There are no - // other references to `entry`. - // 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. - /// When `before_wait` panics, this function will abort. - pub fn wait_timeout( - lock: &SpinMutex>, - 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); - if let Err(_e) = panic::catch_unwind(AssertUnwindSafe(|| before_wait())) { - rtabort!("Panic before wait on wakeup event or timeout") - } - usercalls::wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake); - // acquire the wait queue's lock first to avoid deadlock - // and ensure no other function can simultaneously access the list - // (e.g., `notify_one` or `notify_all`) - 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( - mut guard: SpinMutexGuard<'_, WaitVariable>, - ) -> Result, SpinMutexGuard<'_, WaitVariable>> { - // SAFETY: lifetime of the pop() return value is limited to the map - // closure (The closure return value is 'static). The underlying - // stack frame won't be freed until after the lock on the queue is released - // (i.e., `guard` is dropped). - unsafe { - let tcs = guard.queue.inner.pop().map(|entry| -> Tcs { - let mut entry_guard = entry.lock(); - entry_guard.wake = true; - entry_guard.tcs - }); - - if let Some(tcs) = tcs { - 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( - mut guard: SpinMutexGuard<'_, WaitVariable>, - ) -> Result, SpinMutexGuard<'_, WaitVariable>> { - // SAFETY: lifetime of the pop() return values are limited to the - // while loop body. The underlying stack frames won't be freed until - // after the lock on the queue is released (i.e., `guard` is dropped). - 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 deleted file mode 100644 index f6e851ccadd..00000000000 --- a/library/std/src/sys/sgx/waitqueue/spin_mutex.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! 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 { - value: UnsafeCell, - lock: AtomicBool, -} - -unsafe impl Send for SpinMutex {} -unsafe impl Sync for SpinMutex {} - -pub struct SpinMutexGuard<'a, T: 'a> { - mutex: &'a SpinMutex, -} - -impl<'a, T> !Send for SpinMutexGuard<'a, T> {} -unsafe impl<'a, T: Sync> Sync for SpinMutexGuard<'a, T> {} - -impl SpinMutex { - 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> { - 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 deleted file mode 100644 index 4c5994bea61..00000000000 --- a/library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![allow(deprecated)] - -use super::*; -use crate::sync::Arc; -use crate::thread; -use crate::time::Duration; - -#[test] -fn sleep() { - let mutex = Arc::new(SpinMutex::::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 deleted file mode 100644 index bf91fdd08ed..00000000000 --- a/library/std/src/sys/sgx/waitqueue/tests.rs +++ /dev/null @@ -1,20 +0,0 @@ -use super::*; -use crate::sync::Arc; -use crate::thread; - -#[test] -fn queue() { - let wq = Arc::new(SpinMutex::>::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 deleted file mode 100644 index c736cab576e..00000000000 --- a/library/std/src/sys/sgx/waitqueue/unsafe_list.rs +++ /dev/null @@ -1,156 +0,0 @@ -//! 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 { - next: NonNull>, - prev: NonNull>, - value: Option, -} - -impl UnsafeListEntry { - 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 { - head_tail: NonNull>, - head_tail_entry: Option>, -} - -impl UnsafeList { - 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) -> &'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) { - 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 deleted file mode 100644 index c653dee17bc..00000000000 --- a/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs +++ /dev/null @@ -1,105 +0,0 @@ -use super::*; -use crate::cell::Cell; - -/// # Safety -/// List must be valid. -unsafe fn assert_empty(list: &mut UnsafeList) { - assert!(unsafe { list.pop() }.is_none(), "assertion failed: list is not empty"); -} - -#[test] -fn init_empty() { - unsafe { - assert_empty(&mut UnsafeList::::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 deleted file mode 100644 index 32800bd9a9d..00000000000 --- a/library/std/src/sys/solid/abi/fs.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! `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 deleted file mode 100644 index 8440d572cfb..00000000000 --- a/library/std/src/sys/solid/abi/mod.rs +++ /dev/null @@ -1,65 +0,0 @@ -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 deleted file mode 100644 index eb06a6dd927..00000000000 --- a/library/std/src/sys/solid/abi/sockets.rs +++ /dev/null @@ -1,277 +0,0 @@ -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 deleted file mode 100644 index d013bd87610..00000000000 --- a/library/std/src/sys/solid/alloc.rs +++ /dev/null @@ -1,32 +0,0 @@ -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 deleted file mode 100644 index 6855c113b28..00000000000 --- a/library/std/src/sys/solid/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 547b4f3a984..00000000000 --- a/library/std/src/sys/solid/error.rs +++ /dev/null @@ -1,55 +0,0 @@ -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 deleted file mode 100644 index 6c66b93a3e1..00000000000 --- a/library/std/src/sys/solid/fs.rs +++ /dev/null @@ -1,588 +0,0 @@ -use super::{abi, error}; -use crate::{ - ffi::{CStr, CString, OsStr, OsString}, - fmt, - io::{self, BorrowedCursor, IoSlice, IoSliceMut, 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, -} - -pub struct DirEntry { - entry: abi::dirent, - inner: Arc, -} - -#[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(Copy, Clone, Debug, Default)] -pub struct FileTimes {} - -#[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 { - Ok(SystemTime::from_time_t(self.stat.st_mtime)) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from_time_t(self.stat.st_atime)) - } - - pub fn created(&self) -> io::Result { - 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 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.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 { - 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; - - fn next(&mut self) -> Option> { - let entry = unsafe { - let mut out_entry = MaybeUninit::uninit(); - match error::SolidError::err_if_negative(abi::SOLID_FS_ReadDir( - self.inner.dirp, - out_entry.as_mut_ptr(), - )) { - Ok(_) => out_entry.assume_init(), - Err(e) if e.as_raw() == abi::SOLID_ERR_NOTFOUND => return None, - Err(e) => return Some(Err(e.as_io_error())), - } - }; - - (entry.d_name[0] != 0).then(|| Ok(DirEntry { entry, 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 { - lstat(&self.path()) - } - - pub fn file_type(&self) -> io::Result { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - unsafe { - let len = cursor.capacity(); - let mut out_num_bytes = MaybeUninit::uninit(); - error::SolidError::err_if_negative(abi::SOLID_FS_Read( - self.fd.raw(), - cursor.as_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 - cursor.advance(num_bytes_read); - - Ok(()) - } - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - 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 { - 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 { - 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 { - 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 { - unsupported() - } - - pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - unsupported() - } - - pub fn set_times(&self, _times: FileTimes) -> 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 { - // 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 { - // This target doesn't support symlinks - lstat(p) -} - -pub fn lstat(p: &Path) -> io::Result { - 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 { - unsupported() -} - -pub fn copy(from: &Path, to: &Path) -> io::Result { - 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 deleted file mode 100644 index a862bb78702..00000000000 --- a/library/std/src/sys/solid/io.rs +++ /dev/null @@ -1,81 +0,0 @@ -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) } - } -} - -pub fn is_terminal(_: &T) -> bool { - false -} diff --git a/library/std/src/sys/solid/memchr.rs b/library/std/src/sys/solid/memchr.rs deleted file mode 100644 index 452b7a3de1b..00000000000 --- a/library/std/src/sys/solid/memchr.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub fn memchr(needle: u8, haystack: &[u8]) -> Option { - 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 { - 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 deleted file mode 100644 index 5af83653cf8..00000000000 --- a/library/std/src/sys/solid/mod.rs +++ /dev/null @@ -1,97 +0,0 @@ -#![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 mod thread_parking; - pub(super) mod time; - use super::unsupported; -} - -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 use self::itron::thread_parking; -pub mod time; - -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, _sigpipe: u8) {} - -// SAFETY: must be called only once during runtime cleanup. -pub unsafe fn cleanup() {} - -pub fn unsupported() -> crate::io::Result { - 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", - ) -} - -#[inline] -pub fn is_interrupted(code: i32) -> bool { - net::is_interrupted(code) -} - -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 deleted file mode 100644 index a768e2406c8..00000000000 --- a/library/std/src/sys/solid/net.rs +++ /dev/null @@ -1,435 +0,0 @@ -use super::abi; -use crate::{ - cmp, - ffi::CStr, - io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}, - mem, - net::{Shutdown, SocketAddr}, - os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}, - ptr, str, - sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}, - sys_common::{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 -} - -#[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: T) -> io::Result { - 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(mut f: F) -> io::Result -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() -} - -#[inline] -pub fn is_interrupted(er: abi::ER) -> bool { - er == netc::SOLID_NET_ERR_BASE - libc::EINTR -} - -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(OwnedFd); - -impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - 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 { - unsafe { - let fd = cvt(netc::socket(fam, ty, 0))?; - Ok(Self::from_raw_fd(fd)) - } - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - cvt(unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; - Ok(()) - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = self.connect(addr); - 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.as_raw_fd()] }; - - let mut writefds = fds; - let mut errorfds = fds; - - let n = unsafe { - cvt(netc::select( - self.as_raw_fd() + 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 { - let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; - unsafe { Ok(Self::from_raw_fd(fd)) } - } - - pub fn duplicate(&self) -> io::Result { - Ok(Self(self.0.try_clone()?)) - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { - let ret = cvt(unsafe { - netc::recv(self.as_raw_fd(), buf.as_mut().as_mut_ptr().cast(), buf.capacity(), flags) - })?; - unsafe { - buf.advance(ret as usize); - } - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let ret = cvt(unsafe { - netc::readv( - self.as_raw_fd(), - bufs.as_ptr() as *const netc::iovec, - cmp::min(bufs.len(), max_iov()) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - 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.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) - } - - 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 { - let ret = cvt(unsafe { - netc::write( - self.as_raw_fd(), - buf.as_ptr() as *const c_void, - cmp::min(buf.len(), READ_LIMIT), - ) - })?; - Ok(ret as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let ret = cvt(unsafe { - netc::writev( - self.as_raw_fd(), - bufs.as_ptr() as *const netc::iovec, - cmp::min(bufs.len(), max_iov()) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn set_timeout(&self, dur: Option, 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> { - 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.as_raw_fd(), how) })?; - Ok(()) - } - - pub fn set_linger(&self, linger: Option) -> 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> { - 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 { - 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_raw_fd(), netc::FIONBIO, (&mut nonblocking) as *mut c_int as _) - }) - .map(drop) - } - - pub fn take_error(&self) -> io::Result> { - 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_raw_fd() - } -} - -impl FromInner for Socket { - #[inline] - fn from_inner(sock: OwnedFd) -> Socket { - Socket(sock) - } -} - -impl IntoInner for Socket { - #[inline] - fn into_inner(self) -> OwnedFd { - self.0 - } -} - -impl AsFd for Socket { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> c_int { - self.0.as_raw_fd() - } -} - -impl FromRawFd for Socket { - #[inline] - unsafe fn from_raw_fd(fd: c_int) -> Socket { - unsafe { Self(FromRawFd::from_raw_fd(fd)) } - } -} - -impl IntoRawFd for Socket { - #[inline] - fn into_raw_fd(self) -> c_int { - self.0.into_raw_fd() - } -} diff --git a/library/std/src/sys/solid/os.rs b/library/std/src/sys/solid/os.rs deleted file mode 100644 index ff81544ba91..00000000000 --- a/library/std/src/sys/solid/os.rs +++ /dev/null @@ -1,228 +0,0 @@ -use super::unsupported; -use crate::error::Error as StdError; -use crate::ffi::{CStr, 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::sync::{PoisonError, RwLock}; -use crate::sys::common::small_c_string::run_with_cstr; -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 { - 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 { - *self.0 - } -} - -#[derive(Debug)] -pub struct JoinPathsError; - -pub fn join_paths(_paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - 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 { - unsupported() -} - -static ENV_LOCK: RwLock<()> = RwLock::new(()); - -pub fn env_read_lock() -> impl Drop { - ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -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) { - 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_read_lock(); - 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 { - // environment variables with a nul byte can't be set, so their value is - // always None as well - run_with_cstr(k.as_bytes(), |k| { - let _guard = env_read_lock(); - let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - - if v.is_null() { - Ok(None) - } else { - // SAFETY: `v` cannot be mutated while executing this line since we've a read lock - let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); - - Ok(Some(OsStringExt::from_vec(bytes))) - } - }) - .ok() - .flatten() -} - -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), |k| { - run_with_cstr(v.as_bytes(), |v| { - let _guard = ENV_LOCK.write(); - cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) - }) - }) -} - -pub fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), |nbuf| { - let _guard = ENV_LOCK.write(); - cvt_env(unsafe { 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 { - 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 { - 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 deleted file mode 100644 index 7045c9be25b..00000000000 --- a/library/std/src/sys/solid/path.rs +++ /dev/null @@ -1,25 +0,0 @@ -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> { - None -} - -pub const MAIN_SEP_STR: &str = "\\"; -pub const MAIN_SEP: char = '\\'; - -pub(crate) fn absolute(_path: &Path) -> io::Result { - unsupported() -} diff --git a/library/std/src/sys/solid/rwlock.rs b/library/std/src/sys/solid/rwlock.rs deleted file mode 100644 index ecb4eb83b9b..00000000000 --- a/library/std/src/sys/solid/rwlock.rs +++ /dev/null @@ -1,93 +0,0 @@ -//! 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<()>, -} - -// Safety: `num_readers` is protected by `mtx_num_readers` -unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} - -fn new_rwl() -> Result { - 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 fn read(&self) { - let rwl = self.raw(); - expect_success(unsafe { abi::rwl_loc_rdl(rwl) }, &"rwl_loc_rdl"); - } - - #[inline] - pub 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 fn write(&self) { - let rwl = self.raw(); - expect_success(unsafe { abi::rwl_loc_wrl(rwl) }, &"rwl_loc_wrl"); - } - - #[inline] - pub 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 deleted file mode 100644 index 50f0176967b..00000000000 --- a/library/std/src/sys/solid/stdio.rs +++ /dev/null @@ -1,80 +0,0 @@ -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 { - Ok(0) - } -} - -impl Stdout { - pub const fn new() -> Stdout { - Stdout - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - 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 { - 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 { - 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 { - 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 deleted file mode 100644 index 26918a4fcb0..00000000000 --- a/library/std/src/sys/solid/thread_local_dtor.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![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, RefCell}; - -#[thread_local] -static REGISTERED: Cell = Cell::new(false); - -#[thread_local] -static DTORS: RefCell> = RefCell::new(Vec::new()); - -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - if !REGISTERED.get() { - let tid = task::current_task_id_aborting(); - // 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) }; - REGISTERED.set(true); - } - - match DTORS.try_borrow_mut() { - Ok(mut dtors) => dtors.push((t, dtor)), - Err(_) => rtabort!("global allocator may not use TLS"), - } -} - -pub unsafe fn run_dtors() { - let mut list = DTORS.take(); - while !list.is_empty() { - for (ptr, dtor) in list { - unsafe { dtor(ptr) }; - } - - list = DTORS.take(); - } -} - -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 deleted file mode 100644 index b37bf999698..00000000000 --- a/library/std/src/sys/solid/thread_local_key.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub type Key = usize; - -#[inline] -pub unsafe fn create(_dtor: Option) -> 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"); -} diff --git a/library/std/src/sys/solid/time.rs b/library/std/src/sys/solid/time.rs deleted file mode 100644 index f83f1644fe8..00000000000 --- a/library/std/src/sys/solid/time.rs +++ /dev/null @@ -1,56 +0,0 @@ -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 { - 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 { - Some(SystemTime(self.0.checked_add_unsigned(other.as_secs())?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_sub_unsigned(other.as_secs())?)) - } -} diff --git a/library/std/src/sys/teeos/alloc.rs b/library/std/src/sys/teeos/alloc.rs deleted file mode 100644 index e236819aa23..00000000000 --- a/library/std/src/sys/teeos/alloc.rs +++ /dev/null @@ -1,57 +0,0 @@ -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 and - // . - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::malloc(layout.size()) as *mut u8 - } else { - 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) - } - } -} - -#[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::()); - 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/teeos/locks/condvar.rs b/library/std/src/sys/teeos/locks/condvar.rs deleted file mode 100644 index c08e8145b8c..00000000000 --- a/library/std/src/sys/teeos/locks/condvar.rs +++ /dev/null @@ -1,100 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::ptr; -use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed}; -use crate::sys::locks::mutex::{self, Mutex}; -use crate::sys::time::TIMESPEC_MAX; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; -use crate::time::Duration; - -extern "C" { - pub fn pthread_cond_timedwait( - cond: *mut libc::pthread_cond_t, - lock: *mut libc::pthread_mutex_t, - adstime: *const libc::timespec, - ) -> libc::c_int; -} - -struct AllocatedCondvar(UnsafeCell); - -pub struct Condvar { - inner: LazyBox, - mutex: AtomicPtr, -} - -#[inline] -fn raw(c: &Condvar) -> *mut libc::pthread_cond_t { - c.inner.0.get() -} - -unsafe impl Send for AllocatedCondvar {} -unsafe impl Sync for AllocatedCondvar {} - -impl LazyInit for AllocatedCondvar { - fn init() -> Box { - let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER))); - - let r = unsafe { libc::pthread_cond_init(condvar.0.get(), crate::ptr::null()) }; - assert_eq!(r, 0); - - condvar - } -} - -impl Drop for AllocatedCondvar { - #[inline] - fn drop(&mut self) { - let r = unsafe { libc::pthread_cond_destroy(self.0.get()) }; - debug_assert_eq!(r, 0); - } -} - -impl Condvar { - pub const fn new() -> Condvar { - Condvar { inner: LazyBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } - } - - #[inline] - fn verify(&self, mutex: *mut libc::pthread_mutex_t) { - match self.mutex.compare_exchange(ptr::null_mut(), mutex, Relaxed, Relaxed) { - Ok(_) => {} // Stored the address - Err(n) if n == mutex => {} // Lost a race to store the same address - _ => panic!("attempted to use a condition variable with two mutexes"), - } - } - - #[inline] - pub fn notify_one(&self) { - let r = unsafe { libc::pthread_cond_signal(raw(self)) }; - debug_assert_eq!(r, 0); - } - - #[inline] - pub fn notify_all(&self) { - let r = unsafe { libc::pthread_cond_broadcast(raw(self)) }; - debug_assert_eq!(r, 0); - } - - #[inline] - pub unsafe fn wait(&self, mutex: &Mutex) { - let mutex = mutex::raw(mutex); - self.verify(mutex); - let r = libc::pthread_cond_wait(raw(self), mutex); - debug_assert_eq!(r, 0); - } - - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - use crate::sys::time::Timespec; - - let mutex = mutex::raw(mutex); - self.verify(mutex); - - let timeout = Timespec::now(libc::CLOCK_MONOTONIC) - .checked_add_duration(&dur) - .and_then(|t| t.to_timespec()) - .unwrap_or(TIMESPEC_MAX); - - let r = pthread_cond_timedwait(raw(self), mutex, &timeout); - assert!(r == libc::ETIMEDOUT || r == 0); - r == 0 - } -} diff --git a/library/std/src/sys/teeos/locks/mod.rs b/library/std/src/sys/teeos/locks/mod.rs deleted file mode 100644 index c58e9c7fd45..00000000000 --- a/library/std/src/sys/teeos/locks/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod condvar; -#[path = "../../unix/locks/pthread_mutex.rs"] -pub mod mutex; -pub mod rwlock; - -pub(crate) use condvar::Condvar; -pub(crate) use mutex::Mutex; -pub(crate) use rwlock::RwLock; diff --git a/library/std/src/sys/teeos/locks/rwlock.rs b/library/std/src/sys/teeos/locks/rwlock.rs deleted file mode 100644 index 27cdb88788f..00000000000 --- a/library/std/src/sys/teeos/locks/rwlock.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::sys::locks::mutex::Mutex; - -/// we do not supported rwlock, so use mutex to simulate rwlock. -/// it's useful because so many code in std will use rwlock. -pub struct RwLock { - inner: Mutex, -} - -impl RwLock { - #[inline] - pub const fn new() -> RwLock { - RwLock { inner: Mutex::new() } - } - - #[inline] - pub fn read(&self) { - unsafe { self.inner.lock() }; - } - - #[inline] - pub fn try_read(&self) -> bool { - unsafe { self.inner.try_lock() } - } - - #[inline] - pub fn write(&self) { - unsafe { self.inner.lock() }; - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - unsafe { self.inner.try_lock() } - } - - #[inline] - pub unsafe fn read_unlock(&self) { - unsafe { self.inner.unlock() }; - } - - #[inline] - pub unsafe fn write_unlock(&self) { - unsafe { self.inner.unlock() }; - } -} diff --git a/library/std/src/sys/teeos/mod.rs b/library/std/src/sys/teeos/mod.rs deleted file mode 100644 index ed8c54b2c36..00000000000 --- a/library/std/src/sys/teeos/mod.rs +++ /dev/null @@ -1,167 +0,0 @@ -//! System bindings for the Teeos platform -//! -//! This module contains the facade (aka platform-specific) implementations of -//! OS level functionality for Teeos. -#![allow(unsafe_op_in_unsafe_fn)] -#![allow(unused_variables)] -#![allow(dead_code)] - -pub use self::rand::hashmap_random_keys; - -pub mod alloc; -#[path = "../unsupported/args.rs"] -pub mod args; -#[path = "../unix/cmath.rs"] -pub mod cmath; -#[path = "../unsupported/env.rs"] -pub mod env; -pub mod locks; -//pub mod fd; -#[path = "../unsupported/fs.rs"] -pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unix/memchr.rs"] -pub mod memchr; -pub mod net; -#[path = "../unsupported/once.rs"] -pub mod once; -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; -mod rand; -pub mod stdio; -pub mod thread; -pub mod thread_local_dtor; -#[path = "../unix/thread_local_key.rs"] -pub mod thread_local_key; -#[path = "../unsupported/thread_parking.rs"] -pub mod thread_parking; -#[allow(non_upper_case_globals)] -#[path = "../unix/time.rs"] -pub mod time; - -use crate::io::ErrorKind; - -pub fn abort_internal() -> ! { - unsafe { libc::abort() } -} - -// Trusted Applications are loaded as dynamic libraries on Teeos, -// so this should never be called. -pub fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {} - -// SAFETY: must be called only once during runtime cleanup. -// this is not guaranteed to run, for example when the program aborts. -pub unsafe fn cleanup() { - unimplemented!() - // We do NOT have stack overflow handler, because TEE OS will kill TA when it happens. - // So cleanup is commented - // stack_overflow::cleanup(); -} - -#[inline] -pub(crate) fn is_interrupted(errno: i32) -> bool { - errno == libc::EINTR -} - -// Note: code below is 1:1 copied from unix/mod.rs -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: T) -> crate::io::Result { - if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } -} - -pub fn cvt_r(mut f: F) -> crate::io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - loop { - match cvt(f()) { - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} - other => return other, - } - } -} - -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)) } -} - -use crate::io as std_io; -pub fn unsupported() -> std_io::Result { - Err(unsupported_err()) -} - -pub fn unsupported_err() -> std_io::Error { - std_io::Error::new(std_io::ErrorKind::Unsupported, "operation not supported on this platform") -} diff --git a/library/std/src/sys/teeos/net.rs b/library/std/src/sys/teeos/net.rs deleted file mode 100644 index 0df681dbfa5..00000000000 --- a/library/std/src/sys/teeos/net.rs +++ /dev/null @@ -1,372 +0,0 @@ -use crate::fmt; -use crate::io::{self, BorrowedCursor, 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 { - unsupported() - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0 - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 - } - - pub fn is_read_vectored(&self) -> bool { - self.0 - } - - pub fn write(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - self.0 - } - - pub fn is_write_vectored(&self) -> bool { - self.0 - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn linger(&self) -> io::Result> { - self.0 - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn nodelay(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - 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 { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn only_v6(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - 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 { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - 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 { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn broadcast(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v6(&self) -> io::Result { - 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 { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn send(&self, _: &[u8]) -> io::Result { - 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 { - self.0 - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - 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 Socket = UdpSocket; diff --git a/library/std/src/sys/teeos/os.rs b/library/std/src/sys/teeos/os.rs deleted file mode 100644 index e54a92f01f8..00000000000 --- a/library/std/src/sys/teeos/os.rs +++ /dev/null @@ -1,134 +0,0 @@ -//! Implementation of `std::os` functionality for teeos - -use core::marker::PhantomData; - -use crate::error::Error as StdError; -use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::io; -use crate::path; -use crate::path::PathBuf; - -use super::unsupported; - -pub fn errno() -> i32 { - unsafe { (*libc::__errno_location()) as i32 } -} - -// Hardcoded to return 4096, since `sysconf` is only implemented as a stub. -pub fn page_size() -> usize { - // unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; - 4096 -} - -// Everything below are stubs and copied from unsupported.rs - -pub fn error_string(_errno: i32) -> String { - "error string unimplemented".to_string() -} - -pub fn getcwd() -> io::Result { - 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 { - self.0 - } -} - -#[derive(Debug)] -pub struct JoinPathsError; - -pub fn join_paths(_paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - 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 { - unsupported() -} - -pub struct Env(!); - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} - } -} - -impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self(inner) = self; - match *inner {} - } -} - -pub fn env() -> Env { - panic!("not supported on this platform") -} - -pub fn getenv(_: &OsStr) -> Option { - None -} - -pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::Error::new(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) -} - -pub fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::Error::new(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 { - None -} - -pub fn exit(_code: i32) -> ! { - panic!("TA should not call `exit`") -} - -pub fn getpid() -> u32 { - panic!("no pids on this platform") -} diff --git a/library/std/src/sys/teeos/rand.rs b/library/std/src/sys/teeos/rand.rs deleted file mode 100644 index b45c3bb40e7..00000000000 --- a/library/std/src/sys/teeos/rand.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub fn hashmap_random_keys() -> (u64, u64) { - const KEY_LEN: usize = core::mem::size_of::(); - - let mut v = [0u8; KEY_LEN * 2]; - imp::fill_bytes(&mut v); - - let key1 = v[0..KEY_LEN].try_into().unwrap(); - let key2 = v[KEY_LEN..].try_into().unwrap(); - - (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) -} - -mod imp { - extern "C" { - fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t); - } - - pub fn fill_bytes(v: &mut [u8]) { - unsafe { TEE_GenerateRandom(v.as_mut_ptr() as _, v.len() * crate::mem::size_of::()) } - } -} diff --git a/library/std/src/sys/teeos/stdio.rs b/library/std/src/sys/teeos/stdio.rs deleted file mode 100644 index 9ca04f29273..00000000000 --- a/library/std/src/sys/teeos/stdio.rs +++ /dev/null @@ -1,88 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] - -use crate::io; -use core::arch::asm; - -pub struct Stdin; -pub struct Stdout; -pub struct Stderr; - -const KCALL_DEBUG_CMD_PUT_BYTES: i64 = 2; - -unsafe fn debug_call(cap_ref: u64, call_no: i64, arg1: u64, arg2: u64) -> i32 { - let ret: u64; - unsafe { - asm!( - "svc #99", - inout("x0") cap_ref => ret, - in("x1") call_no, - in("x2") arg1, - in("x3") arg2, - ); - } - - ret as i32 -} - -fn print_buf(s: &[u8]) -> io::Result { - // Corresponds to `HM_DEBUG_PUT_BYTES_LIMIT`. - const MAX_LEN: usize = 512; - let len = if s.len() > MAX_LEN { MAX_LEN } else { s.len() }; - let result = unsafe { debug_call(0, KCALL_DEBUG_CMD_PUT_BYTES, s.as_ptr() as u64, len as u64) }; - - if result == 0 { Ok(len) } else { Err(io::Error::from(io::ErrorKind::InvalidInput)) } -} - -impl Stdin { - pub const fn new() -> Stdin { - Stdin - } -} - -impl io::Read for Stdin { - fn read(&mut self, _buf: &mut [u8]) -> io::Result { - Ok(0) - } -} - -impl Stdout { - pub const fn new() -> Stdout { - Stdout - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - print_buf(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 { - print_buf(buf) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub const STDIN_BUF_SIZE: usize = 0; - -pub fn is_ebadf(err: &io::Error) -> bool { - err.raw_os_error() == Some(libc::EBADF as i32) -} - -pub fn panic_output() -> Option { - Some(Stderr::new()) -} diff --git a/library/std/src/sys/teeos/thread.rs b/library/std/src/sys/teeos/thread.rs deleted file mode 100644 index 155f333f906..00000000000 --- a/library/std/src/sys/teeos/thread.rs +++ /dev/null @@ -1,164 +0,0 @@ -use core::convert::TryInto; - -use crate::cmp; -use crate::ffi::CStr; -use crate::io; -use crate::mem; -use crate::num::NonZeroUsize; -use crate::ptr; -use crate::sys::os; -use crate::time::Duration; - -pub const DEFAULT_MIN_STACK_SIZE: usize = 8 * 1024; - -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 {} - -extern "C" { - pub fn TEE_Wait(timeout: u32) -> u32; -} - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(stack: usize, p: Box) -> io::Result { - let p = Box::into_raw(Box::new(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); - assert_eq!( - libc::pthread_attr_settee( - &mut attr, - libc::TEESMP_THREAD_ATTR_CA_INHERIT, - libc::TEESMP_THREAD_ATTR_TASK_ID_INHERIT, - libc::TEESMP_THREAD_ATTR_HAS_SHADOW, - ), - 0, - ); - - 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 { - // The new thread will start running earliest after the next yield. - // We add a yield here, so that the user does not have to. - Thread::yield_now(); - 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. - // this is not necessary in TEE. - //let _handler = stack_overflow::Handler::new(); - // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); - } - ptr::null_mut() - } - } - - pub fn yield_now() { - let ret = unsafe { libc::sched_yield() }; - debug_assert_eq!(ret, 0); - } - - /// This does not do anything on teeos - pub fn set_name(_name: &CStr) { - // Both pthread_setname_np and prctl are not available to the TA, - // so we can't implement this currently. If the need arises please - // contact the teeos rustzone team. - } - - /// only main thread could wait for sometime in teeos - pub fn sleep(dur: Duration) { - let sleep_millis = dur.as_millis(); - let final_sleep: u32 = - if sleep_millis >= u32::MAX as u128 { u32::MAX } else { sleep_millis as u32 }; - unsafe { - let _ = TEE_Wait(final_sleep); - } - } - - /// must join, because no pthread_detach supported - 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) { - // we can not call detach, so just panic if thread spawn without join - panic!("thread must join, detach is not supported!"); - } -} - -// Note: Both `sched_getaffinity` and `sysconf` are available but not functional on -// teeos, so this function always returns an Error! -pub fn available_parallelism() -> io::Result { - Err(io::Error::new( - io::ErrorKind::NotFound, - "The number of hardware threads is not known for the target platform", - )) -} - -// stub -pub mod guard { - use crate::ops::Range; - pub type Guard = Range; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} - -fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { - libc::PTHREAD_STACK_MIN.try_into().expect("Infallible") -} diff --git a/library/std/src/sys/teeos/thread_local_dtor.rs b/library/std/src/sys/teeos/thread_local_dtor.rs deleted file mode 100644 index 5c6bc4d6750..00000000000 --- a/library/std/src/sys/teeos/thread_local_dtor.rs +++ /dev/null @@ -1,4 +0,0 @@ -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/uefi/alloc.rs b/library/std/src/sys/uefi/alloc.rs deleted file mode 100644 index ad3904d82f3..00000000000 --- a/library/std/src/sys/uefi/alloc.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! Global Allocator for UEFI. -//! Uses [r-efi-alloc](https://crates.io/crates/r-efi-alloc) - -use r_efi::protocols::loaded_image; - -use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::sync::OnceLock; -use crate::sys::uefi::helpers; - -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - static EFI_MEMORY_TYPE: OnceLock = OnceLock::new(); - - // Return null pointer if boot services are not available - if crate::os::uefi::env::boot_services().is_none() { - return crate::ptr::null_mut(); - } - - // If boot services is valid then SystemTable is not null. - let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); - - // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this - // will never fail. - let mem_type = EFI_MEMORY_TYPE.get_or_init(|| { - let protocol = helpers::image_handle_protocol::( - loaded_image::PROTOCOL_GUID, - ) - .unwrap(); - // Gives allocations the memory type that the data sections were loaded as. - unsafe { (*protocol.as_ptr()).image_data_type } - }); - - // The caller must ensure non-0 layout - unsafe { r_efi_alloc::raw::alloc(system_table, layout, *mem_type) } - } - - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - // Do nothing if boot services are not available - if crate::os::uefi::env::boot_services().is_none() { - return; - } - - // If boot services is valid then SystemTable is not null. - let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); - // The caller must ensure non-0 layout - unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) } - } -} diff --git a/library/std/src/sys/uefi/args.rs b/library/std/src/sys/uefi/args.rs deleted file mode 100644 index 4ff7be748e9..00000000000 --- a/library/std/src/sys/uefi/args.rs +++ /dev/null @@ -1,158 +0,0 @@ -use r_efi::protocols::loaded_image; - -use crate::env::current_exe; -use crate::ffi::OsString; -use crate::fmt; -use crate::iter::Iterator; -use crate::mem::size_of; -use crate::sys::uefi::helpers; -use crate::vec; - -pub struct Args { - parsed_args_list: vec::IntoIter, -} - -pub fn args() -> Args { - let lazy_current_exe = || Vec::from([current_exe().map(Into::into).unwrap_or_default()]); - - // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this - // will never fail. - let protocol = - helpers::image_handle_protocol::(loaded_image::PROTOCOL_GUID) - .unwrap(); - - let lp_size = unsafe { (*protocol.as_ptr()).load_options_size } as usize; - // Break if we are sure that it cannot be UTF-16 - if lp_size < size_of::() || lp_size % size_of::() != 0 { - return Args { parsed_args_list: lazy_current_exe().into_iter() }; - } - let lp_size = lp_size / size_of::(); - - let lp_cmd_line = unsafe { (*protocol.as_ptr()).load_options as *const u16 }; - if !lp_cmd_line.is_aligned() { - return Args { parsed_args_list: lazy_current_exe().into_iter() }; - } - let lp_cmd_line = unsafe { crate::slice::from_raw_parts(lp_cmd_line, lp_size) }; - - Args { - parsed_args_list: parse_lp_cmd_line(lp_cmd_line) - .unwrap_or_else(lazy_current_exe) - .into_iter(), - } -} - -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 { - self.parsed_args_list.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.parsed_args_list.size_hint() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.parsed_args_list.len() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.parsed_args_list.next_back() - } -} - -/// Implements the UEFI command-line argument parsing algorithm. -/// -/// This implementation is based on what is defined in Section 3.4 of -/// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_Spec_2_0.pdf) -/// -/// Return None in the following cases: -/// - Invalid UTF-16 (unpaired surrogate) -/// - Empty/improper arguments -fn parse_lp_cmd_line(code_units: &[u16]) -> Option> { - const QUOTE: char = '"'; - const SPACE: char = ' '; - const CARET: char = '^'; - const NULL: char = '\0'; - - let mut ret_val = Vec::new(); - let mut code_units_iter = char::decode_utf16(code_units.iter().cloned()).peekable(); - - // The executable name at the beginning is special. - let mut in_quotes = false; - let mut cur = String::new(); - while let Some(w) = code_units_iter.next() { - let w = w.ok()?; - match w { - // break on NULL - NULL => break, - // 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 if !in_quotes => break, - // In all other cases the code unit is taken literally. - _ => cur.push(w), - } - } - - // If exe name is missing, the cli args are invalid - if cur.is_empty() { - return None; - } - - ret_val.push(OsString::from(cur)); - // Skip whitespace. - while code_units_iter.next_if_eq(&Ok(SPACE)).is_some() {} - - // Parse the arguments according to these rules: - // * All code units are taken literally except space, quote and caret. - // * When not `in_quotes`, space separate arguments. Consecutive spaces are - // treated as a single separator. - // * A space `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 caret. - // * A caret can be escaped if preceded by caret. - let mut cur = String::new(); - let mut in_quotes = false; - while let Some(w) = code_units_iter.next() { - let w = w.ok()?; - match w { - // break on NULL - NULL => break, - // If not `in_quotes`, a space or tab ends the argument. - SPACE if !in_quotes => { - ret_val.push(OsString::from(&cur[..])); - cur.truncate(0); - - // Skip whitespace. - while code_units_iter.next_if_eq(&Ok(SPACE)).is_some() {} - } - // Caret can escape quotes or carets - CARET if in_quotes => { - if let Some(x) = code_units_iter.next() { - cur.push(x.ok()?); - } - } - // If quote then flip `in_quotes` - QUOTE => in_quotes = !in_quotes, - // Everything else is always taken literally. - _ => cur.push(w), - } - } - // Push the final argument, if any. - if !cur.is_empty() || in_quotes { - ret_val.push(OsString::from(cur)); - } - Some(ret_val) -} diff --git a/library/std/src/sys/uefi/env.rs b/library/std/src/sys/uefi/env.rs deleted file mode 100644 index c106d5fed3e..00000000000 --- a/library/std/src/sys/uefi/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod os { - pub const FAMILY: &str = ""; - pub const OS: &str = "uefi"; - pub const DLL_PREFIX: &str = ""; - pub const DLL_SUFFIX: &str = ""; - pub const DLL_EXTENSION: &str = ""; - pub const EXE_SUFFIX: &str = ".efi"; - pub const EXE_EXTENSION: &str = "efi"; -} diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs deleted file mode 100644 index 9837cc89f2d..00000000000 --- a/library/std/src/sys/uefi/helpers.rs +++ /dev/null @@ -1,148 +0,0 @@ -//! Contains most of the shared UEFI specific stuff. Some of this might be moved to `std::os::uefi` -//! if needed but no point in adding extra public API when there is not Std support for UEFI in the -//! first place -//! -//! Some Nomenclature -//! * Protocol: -//! - Protocols serve to enable communication between separately built modules, including drivers. -//! - Every protocol has a GUID associated with it. The GUID serves as the name for the protocol. -//! - Protocols are produced and consumed. -//! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) - -use r_efi::efi::{self, Guid}; - -use crate::mem::{size_of, MaybeUninit}; -use crate::os::uefi; -use crate::ptr::NonNull; -use crate::{ - io::{self, const_io_error}, - os::uefi::env::boot_services, -}; - -const BOOT_SERVICES_UNAVAILABLE: io::Error = - const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); - -/// Locate Handles with a particular Protocol GUID -/// Implemented using `EFI_BOOT_SERVICES.LocateHandles()` -/// -/// Returns an array of [Handles](r_efi::efi::Handle) that support a specified protocol. -pub(crate) fn locate_handles(mut guid: Guid) -> io::Result>> { - fn inner( - guid: &mut Guid, - boot_services: NonNull, - buf_size: &mut usize, - buf: *mut r_efi::efi::Handle, - ) -> io::Result<()> { - let r = unsafe { - ((*boot_services.as_ptr()).locate_handle)( - r_efi::efi::BY_PROTOCOL, - guid, - crate::ptr::null_mut(), - buf_size, - buf, - ) - }; - - if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } - } - - let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); - let mut buf_len = 0usize; - - // This should always fail since the size of buffer is 0. This call should update the buf_len - // variable with the required buffer length - match inner(&mut guid, boot_services, &mut buf_len, crate::ptr::null_mut()) { - Ok(()) => unreachable!(), - Err(e) => match e.kind() { - io::ErrorKind::FileTooLarge => {} - _ => return Err(e), - }, - } - - // The returned buf_len is in bytes - assert_eq!(buf_len % size_of::(), 0); - let num_of_handles = buf_len / size_of::(); - let mut buf: Vec = Vec::with_capacity(num_of_handles); - match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) { - Ok(()) => { - // This is safe because the call will succeed only if buf_len >= required length. - // Also, on success, the `buf_len` is updated with the size of bufferv (in bytes) written - unsafe { buf.set_len(num_of_handles) }; - Ok(buf.into_iter().filter_map(|x| NonNull::new(x)).collect()) - } - Err(e) => Err(e), - } -} - -/// Open Protocol on a handle. -/// Internally just a call to `EFI_BOOT_SERVICES.OpenProtocol()`. -/// -/// Queries a handle to determine if it supports a specified protocol. If the protocol is -/// supported by the handle, it opens the protocol on behalf of the calling agent. -pub(crate) fn open_protocol( - handle: NonNull, - mut protocol_guid: Guid, -) -> io::Result> { - let boot_services: NonNull = - boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); - let system_handle = uefi::env::image_handle(); - let mut protocol: MaybeUninit<*mut T> = MaybeUninit::uninit(); - - let r = unsafe { - ((*boot_services.as_ptr()).open_protocol)( - handle.as_ptr(), - &mut protocol_guid, - protocol.as_mut_ptr().cast(), - system_handle.as_ptr(), - crate::ptr::null_mut(), - r_efi::system::OPEN_PROTOCOL_GET_PROTOCOL, - ) - }; - - if r.is_error() { - Err(crate::io::Error::from_raw_os_error(r.as_usize())) - } else { - NonNull::new(unsafe { protocol.assume_init() }) - .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) - } -} - -pub(crate) fn create_event( - signal: u32, - tpl: efi::Tpl, - handler: Option, - context: *mut crate::ffi::c_void, -) -> io::Result> { - let boot_services: NonNull = - boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); - let mut event: r_efi::efi::Event = crate::ptr::null_mut(); - let r = unsafe { - let create_event = (*boot_services.as_ptr()).create_event; - (create_event)(signal, tpl, handler, context, &mut event) - }; - if r.is_error() { - Err(crate::io::Error::from_raw_os_error(r.as_usize())) - } else { - NonNull::new(event).ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) - } -} - -/// # SAFETY -/// - The supplied event must be valid -pub(crate) unsafe fn close_event(evt: NonNull) -> io::Result<()> { - let boot_services: NonNull = - boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); - let r = unsafe { - let close_event = (*boot_services.as_ptr()).close_event; - (close_event)(evt.as_ptr()) - }; - - if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } -} - -/// Get the Protocol for current system handle. -/// Note: Some protocols need to be manually freed. It is the callers responsibility to do so. -pub(crate) fn image_handle_protocol(protocol_guid: Guid) -> Option> { - let system_handle = uefi::env::try_image_handle()?; - open_protocol(system_handle, protocol_guid).ok() -} diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs deleted file mode 100644 index 4edc00e3ea0..00000000000 --- a/library/std/src/sys/uefi/mod.rs +++ /dev/null @@ -1,242 +0,0 @@ -//! Platform-specific extensions to `std` for UEFI platforms. -//! -//! Provides access to platform-level information on UEFI platforms, and -//! exposes UEFI-specific functions that would otherwise be inappropriate as -//! part of the core `std` library. -//! -//! It exposes more ways to deal with platform-specific strings ([`OsStr`], -//! [`OsString`]), allows to set permissions more granularly, extract low-level -//! file descriptors from files and sockets, and has platform-specific helpers -//! for spawning processes. -//! -//! [`OsStr`]: crate::ffi::OsStr -//! [`OsString`]: crate::ffi::OsString - -pub mod alloc; -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/locks/mod.rs"] -pub mod locks; -#[path = "../unsupported/net.rs"] -pub mod net; -#[path = "../unsupported/once.rs"] -pub mod once; -pub mod os; -#[path = "../windows/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; -#[path = "../unsupported/thread.rs"] -pub mod thread; -#[path = "../unsupported/thread_local_key.rs"] -pub mod thread_local_key; -#[path = "../unsupported/thread_parking.rs"] -pub mod thread_parking; -#[path = "../unsupported/time.rs"] -pub mod time; - -mod helpers; - -#[cfg(test)] -mod tests; - -pub type RawOsError = usize; - -use crate::io as std_io; -use crate::os::uefi; -use crate::ptr::NonNull; -use crate::sync::atomic::{AtomicPtr, Ordering}; - -pub mod memchr { - pub use core::slice::memchr::{memchr, memrchr}; -} - -static EXIT_BOOT_SERVICE_EVENT: AtomicPtr = - AtomicPtr::new(crate::ptr::null_mut()); - -/// # SAFETY -/// - must be called only once during runtime initialization. -/// - argc must be 2. -/// - argv must be &[Handle, *mut SystemTable]. -pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { - assert_eq!(argc, 2); - let image_handle = unsafe { NonNull::new(*argv as *mut crate::ffi::c_void).unwrap() }; - let system_table = unsafe { NonNull::new(*argv.add(1) as *mut crate::ffi::c_void).unwrap() }; - unsafe { uefi::env::init_globals(image_handle, system_table) }; - - // Register exit boot services handler - match helpers::create_event( - r_efi::efi::EVT_SIGNAL_EXIT_BOOT_SERVICES, - r_efi::efi::TPL_NOTIFY, - Some(exit_boot_service_handler), - crate::ptr::null_mut(), - ) { - Ok(x) => { - if EXIT_BOOT_SERVICE_EVENT - .compare_exchange( - crate::ptr::null_mut(), - x.as_ptr(), - Ordering::Release, - Ordering::Acquire, - ) - .is_err() - { - abort_internal(); - }; - } - Err(_) => abort_internal(), - } -} - -/// # SAFETY -/// this is not guaranteed to run, for example when the program aborts. -/// - must be called only once during runtime cleanup. -pub unsafe fn cleanup() { - if let Some(exit_boot_service_event) = - NonNull::new(EXIT_BOOT_SERVICE_EVENT.swap(crate::ptr::null_mut(), Ordering::Acquire)) - { - let _ = unsafe { helpers::close_event(exit_boot_service_event) }; - } -} - -#[inline] -pub const fn unsupported() -> std_io::Result { - Err(unsupported_err()) -} - -#[inline] -pub const fn unsupported_err() -> std_io::Error { - std_io::const_io_error!(std_io::ErrorKind::Unsupported, "operation not supported on UEFI",) -} - -pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { - use crate::io::ErrorKind; - use r_efi::efi::Status; - - match r_efi::efi::Status::from_usize(code) { - Status::ALREADY_STARTED - | Status::COMPROMISED_DATA - | Status::CONNECTION_FIN - | Status::CRC_ERROR - | Status::DEVICE_ERROR - | Status::END_OF_MEDIA - | Status::HTTP_ERROR - | Status::ICMP_ERROR - | Status::INCOMPATIBLE_VERSION - | Status::LOAD_ERROR - | Status::MEDIA_CHANGED - | Status::NO_MAPPING - | Status::NO_MEDIA - | Status::NOT_STARTED - | Status::PROTOCOL_ERROR - | Status::PROTOCOL_UNREACHABLE - | Status::TFTP_ERROR - | Status::VOLUME_CORRUPTED => ErrorKind::Other, - Status::BAD_BUFFER_SIZE | Status::INVALID_LANGUAGE => ErrorKind::InvalidData, - Status::ABORTED => ErrorKind::ConnectionAborted, - Status::ACCESS_DENIED => ErrorKind::PermissionDenied, - Status::BUFFER_TOO_SMALL => ErrorKind::FileTooLarge, - Status::CONNECTION_REFUSED => ErrorKind::ConnectionRefused, - Status::CONNECTION_RESET => ErrorKind::ConnectionReset, - Status::END_OF_FILE => ErrorKind::UnexpectedEof, - Status::HOST_UNREACHABLE => ErrorKind::HostUnreachable, - Status::INVALID_PARAMETER => ErrorKind::InvalidInput, - Status::IP_ADDRESS_CONFLICT => ErrorKind::AddrInUse, - Status::NETWORK_UNREACHABLE => ErrorKind::NetworkUnreachable, - Status::NO_RESPONSE => ErrorKind::HostUnreachable, - Status::NOT_FOUND => ErrorKind::NotFound, - Status::NOT_READY => ErrorKind::ResourceBusy, - Status::OUT_OF_RESOURCES => ErrorKind::OutOfMemory, - Status::SECURITY_VIOLATION => ErrorKind::PermissionDenied, - Status::TIMEOUT => ErrorKind::TimedOut, - Status::UNSUPPORTED => ErrorKind::Unsupported, - Status::VOLUME_FULL => ErrorKind::StorageFull, - Status::WRITE_PROTECTED => ErrorKind::ReadOnlyFilesystem, - _ => ErrorKind::Uncategorized, - } -} - -pub fn abort_internal() -> ! { - if let Some(exit_boot_service_event) = - NonNull::new(EXIT_BOOT_SERVICE_EVENT.load(Ordering::Acquire)) - { - let _ = unsafe { helpers::close_event(exit_boot_service_event) }; - } - - if let (Some(boot_services), Some(handle)) = - (uefi::env::boot_services(), uefi::env::try_image_handle()) - { - let boot_services: NonNull = boot_services.cast(); - let _ = unsafe { - ((*boot_services.as_ptr()).exit)( - handle.as_ptr(), - r_efi::efi::Status::ABORTED, - 0, - crate::ptr::null_mut(), - ) - }; - } - - // In case SystemTable and ImageHandle cannot be reached, use `core::intrinsics::abort` - core::intrinsics::abort(); -} - -// 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] -pub extern "C" fn __rust_abort() { - abort_internal(); -} - -#[inline] -pub fn hashmap_random_keys() -> (u64, u64) { - get_random().unwrap() -} - -fn get_random() -> Option<(u64, u64)> { - use r_efi::protocols::rng; - - let mut buf = [0u8; 16]; - let handles = helpers::locate_handles(rng::PROTOCOL_GUID).ok()?; - for handle in handles { - if let Ok(protocol) = helpers::open_protocol::(handle, rng::PROTOCOL_GUID) { - let r = unsafe { - ((*protocol.as_ptr()).get_rng)( - protocol.as_ptr(), - crate::ptr::null_mut(), - buf.len(), - buf.as_mut_ptr(), - ) - }; - if r.is_error() { - continue; - } else { - return Some(( - u64::from_le_bytes(buf[..8].try_into().ok()?), - u64::from_le_bytes(buf[8..].try_into().ok()?), - )); - } - } - } - None -} - -/// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled -extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { - uefi::env::disable_boot_services(); -} - -pub fn is_interrupted(_code: RawOsError) -> bool { - false -} diff --git a/library/std/src/sys/uefi/os.rs b/library/std/src/sys/uefi/os.rs deleted file mode 100644 index e6693db68e6..00000000000 --- a/library/std/src/sys/uefi/os.rs +++ /dev/null @@ -1,237 +0,0 @@ -use super::{unsupported, RawOsError}; -use crate::error::Error as StdError; -use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::io; -use crate::marker::PhantomData; -use crate::os::uefi; -use crate::path::{self, PathBuf}; -use crate::ptr::NonNull; -use r_efi::efi::Status; - -pub fn errno() -> RawOsError { - 0 -} - -pub fn error_string(errno: RawOsError) -> String { - // Keep the List in Alphabetical Order - // The Messages are taken from UEFI Specification Appendix D - Status Codes - match r_efi::efi::Status::from_usize(errno) { - Status::ABORTED => "The operation was aborted.".to_owned(), - Status::ACCESS_DENIED => "Access was denied.".to_owned(), - Status::ALREADY_STARTED => "The protocol has already been started.".to_owned(), - Status::BAD_BUFFER_SIZE => "The buffer was not the proper size for the request.".to_owned(), - Status::BUFFER_TOO_SMALL => { - "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.".to_owned() - } - Status::COMPROMISED_DATA => { - "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.".to_owned() - } - Status::CONNECTION_FIN => { - "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance.".to_owned() - } - Status::CONNECTION_REFUSED => { - "The receiving or transmission operation fails because this connection is refused.".to_owned() - } - Status::CONNECTION_RESET => { - "The connect fails because the connection is reset either by instance itself or the communication peer.".to_owned() - } - Status::CRC_ERROR => "A CRC error was detected.".to_owned(), - Status::DEVICE_ERROR => "The physical device reported an error while attempting the operation.".to_owned() - , - Status::END_OF_FILE => { - "The end of the file was reached.".to_owned() - } - Status::END_OF_MEDIA => { - "Beginning or end of media was reached".to_owned() - } - Status::HOST_UNREACHABLE => { - "The remote host is not reachable.".to_owned() - } - Status::HTTP_ERROR => { - "A HTTP error occurred during the network operation.".to_owned() - } - Status::ICMP_ERROR => { - "An ICMP error occurred during the network operation.".to_owned() - } - Status::INCOMPATIBLE_VERSION => { - "The function encountered an internal version that was incompatible with a version requested by the caller.".to_owned() - } - Status::INVALID_LANGUAGE => { - "The language specified was invalid.".to_owned() - } - Status::INVALID_PARAMETER => { - "A parameter was incorrect.".to_owned() - } - Status::IP_ADDRESS_CONFLICT => { - "There is an address conflict address allocation".to_owned() - } - Status::LOAD_ERROR => { - "The image failed to load.".to_owned() - } - Status::MEDIA_CHANGED => { - "The medium in the device has changed since the last access.".to_owned() - } - Status::NETWORK_UNREACHABLE => { - "The network containing the remote host is not reachable.".to_owned() - } - Status::NO_MAPPING => { - "A mapping to a device does not exist.".to_owned() - } - Status::NO_MEDIA => { - "The device does not contain any medium to perform the operation.".to_owned() - } - Status::NO_RESPONSE => { - "The server was not found or did not respond to the request.".to_owned() - } - Status::NOT_FOUND => "The item was not found.".to_owned(), - Status::NOT_READY => { - "There is no data pending upon return.".to_owned() - } - Status::NOT_STARTED => { - "The protocol has not been started.".to_owned() - } - Status::OUT_OF_RESOURCES => { - "A resource has run out.".to_owned() - } - Status::PROTOCOL_ERROR => { - "A protocol error occurred during the network operation.".to_owned() - } - Status::PROTOCOL_UNREACHABLE => { - "An ICMP protocol unreachable error is received.".to_owned() - } - Status::SECURITY_VIOLATION => { - "The function was not performed due to a security violation.".to_owned() - } - Status::TFTP_ERROR => { - "A TFTP error occurred during the network operation.".to_owned() - } - Status::TIMEOUT => "The timeout time expired.".to_owned(), - Status::UNSUPPORTED => { - "The operation is not supported.".to_owned() - } - Status::VOLUME_FULL => { - "There is no more space on the file system.".to_owned() - } - Status::VOLUME_CORRUPTED => { - "An inconstancy was detected on the file system causing the operating to fail.".to_owned() - } - Status::WRITE_PROTECTED => { - "The device cannot be written to.".to_owned() - } - _ => format!("Status: {}", errno), - } -} - -pub fn getcwd() -> io::Result { - 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 { - self.0 - } -} - -#[derive(Debug)] -pub struct JoinPathsError; - -pub fn join_paths(_paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - 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 {} - -pub fn current_exe() -> io::Result { - unsupported() -} - -pub struct Env(!); - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.0 - } -} - -impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} - } -} - -pub fn env() -> Env { - panic!("not supported on this platform") -} - -pub fn getenv(_: &OsStr) -> Option { - 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 { - None -} - -pub fn exit(code: i32) -> ! { - if let (Some(boot_services), Some(handle)) = - (uefi::env::boot_services(), uefi::env::try_image_handle()) - { - let boot_services: NonNull = boot_services.cast(); - let _ = unsafe { - ((*boot_services.as_ptr()).exit)( - handle.as_ptr(), - Status::from_usize(code as usize), - 0, - crate::ptr::null_mut(), - ) - }; - } - crate::intrinsics::abort() -} - -pub fn getpid() -> u32 { - panic!("no pids on this platform") -} diff --git a/library/std/src/sys/uefi/path.rs b/library/std/src/sys/uefi/path.rs deleted file mode 100644 index 106682eee56..00000000000 --- a/library/std/src/sys/uefi/path.rs +++ /dev/null @@ -1,25 +0,0 @@ -use super::unsupported; -use crate::ffi::OsStr; -use crate::io; -use crate::path::{Path, PathBuf, Prefix}; - -pub const MAIN_SEP_STR: &str = "\\"; -pub const MAIN_SEP: char = '\\'; - -#[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(_p: &OsStr) -> Option> { - None -} - -pub(crate) fn absolute(_path: &Path) -> io::Result { - unsupported() -} diff --git a/library/std/src/sys/uefi/stdio.rs b/library/std/src/sys/uefi/stdio.rs deleted file mode 100644 index a533d8a0575..00000000000 --- a/library/std/src/sys/uefi/stdio.rs +++ /dev/null @@ -1,162 +0,0 @@ -use crate::io; -use crate::iter::Iterator; -use crate::mem::MaybeUninit; -use crate::os::uefi; -use crate::ptr::NonNull; - -const MAX_BUFFER_SIZE: usize = 8192; - -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 { - let st: NonNull = uefi::env::system_table().cast(); - let stdin = unsafe { (*st.as_ptr()).con_in }; - - // Try reading any pending data - let inp = match read_key_stroke(stdin) { - Ok(x) => x, - Err(e) if e == r_efi::efi::Status::NOT_READY => { - // Wait for keypress for new data - wait_stdin(stdin)?; - read_key_stroke(stdin).map_err(|x| io::Error::from_raw_os_error(x.as_usize()))? - } - Err(e) => { - return Err(io::Error::from_raw_os_error(e.as_usize())); - } - }; - - // Check if the key is printiable character - if inp.scan_code != 0x00 { - return Err(io::const_io_error!(io::ErrorKind::Interrupted, "Special Key Press")); - } - - // SAFETY: Iterator will have only 1 character since we are reading only 1 Key - // SAFETY: This character will always be UCS-2 and thus no surrogates. - let ch: char = char::decode_utf16([inp.unicode_char]).next().unwrap().unwrap(); - if ch.len_utf8() > buf.len() { - return Ok(0); - } - - ch.encode_utf8(buf); - - Ok(ch.len_utf8()) - } -} - -impl Stdout { - pub const fn new() -> Stdout { - Stdout - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - let st: NonNull = uefi::env::system_table().cast(); - let stdout = unsafe { (*st.as_ptr()).con_out }; - - write(stdout, 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 { - let st: NonNull = uefi::env::system_table().cast(); - let stderr = unsafe { (*st.as_ptr()).std_err }; - - write(stderr, buf) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -// UCS-2 character should occupy 3 bytes at most in UTF-8 -pub const STDIN_BUF_SIZE: usize = 3; - -pub fn is_ebadf(_err: &io::Error) -> bool { - true -} - -pub fn panic_output() -> Option { - uefi::env::try_system_table().map(|_| Stderr::new()) -} - -fn write( - protocol: *mut r_efi::protocols::simple_text_output::Protocol, - buf: &[u8], -) -> io::Result { - let mut utf16 = [0; MAX_BUFFER_SIZE / 2]; - - // Get valid UTF-8 buffer - let utf8 = match crate::str::from_utf8(buf) { - Ok(x) => x, - Err(e) => unsafe { crate::str::from_utf8_unchecked(&buf[..e.valid_up_to()]) }, - }; - // Clip UTF-8 buffer to max UTF-16 buffer we support - let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len() - 1)]; - - for (i, ch) in utf8.encode_utf16().enumerate() { - utf16[i] = ch; - } - - unsafe { simple_text_output(protocol, &mut utf16) }?; - - Ok(utf8.len()) -} - -unsafe fn simple_text_output( - protocol: *mut r_efi::protocols::simple_text_output::Protocol, - buf: &mut [u16], -) -> io::Result<()> { - let res = unsafe { ((*protocol).output_string)(protocol, buf.as_mut_ptr()) }; - if res.is_error() { Err(io::Error::from_raw_os_error(res.as_usize())) } else { Ok(()) } -} - -fn wait_stdin(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> { - let boot_services: NonNull = - uefi::env::boot_services().unwrap().cast(); - let wait_for_event = unsafe { (*boot_services.as_ptr()).wait_for_event }; - let wait_for_key_event = unsafe { (*stdin).wait_for_key }; - - let r = { - let mut x: usize = 0; - (wait_for_event)(1, [wait_for_key_event].as_mut_ptr(), &mut x) - }; - if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } -} - -fn read_key_stroke( - stdin: *mut r_efi::protocols::simple_text_input::Protocol, -) -> Result { - let mut input_key: MaybeUninit = - MaybeUninit::uninit(); - - let r = unsafe { ((*stdin).read_key_stroke)(stdin, input_key.as_mut_ptr()) }; - - if r.is_error() { - Err(r) - } else { - let input_key = unsafe { input_key.assume_init() }; - Ok(input_key) - } -} diff --git a/library/std/src/sys/uefi/tests.rs b/library/std/src/sys/uefi/tests.rs deleted file mode 100644 index 8806eda3ac0..00000000000 --- a/library/std/src/sys/uefi/tests.rs +++ /dev/null @@ -1,21 +0,0 @@ -use super::alloc::*; - -#[test] -fn align() { - // UEFI ABI specifies that allocation alignment minimum is always 8. So this can be - // statically verified. - assert_eq!(POOL_ALIGNMENT, 8); - - // Loop over allocation-request sizes from 0-256 and alignments from 1-128, and verify - // that in case of overalignment there is at least space for one additional pointer to - // store in the allocation. - for i in 0..256 { - for j in &[1, 2, 4, 8, 16, 32, 64, 128] { - if *j <= 8 { - assert_eq!(align_size(i, *j), i); - } else { - assert!(align_size(i, *j) > i + std::mem::size_of::<*mut ()>()); - } - } - } -} diff --git a/library/std/src/sys/unix/alloc.rs b/library/std/src/sys/unix/alloc.rs deleted file mode 100644 index af0089978ec..00000000000 --- a/library/std/src/sys/unix/alloc.rs +++ /dev/null @@ -1,106 +0,0 @@ -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 and - // . - 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", - target_os = "vita", - ))] { - #[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 { - // C11 aligned_alloc requires that the size be a multiple of the alignment. - // Layout already checks that the size rounded up doesn't overflow isize::MAX. - let align = layout.align(); - let size = layout.size().next_multiple_of(align); - libc::aligned_alloc(align, 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::()); - 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 deleted file mode 100644 index 0f704994f55..00000000000 --- a/library/std/src/sys/unix/android.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! Android ABI-compatibility module -//! -//! The ABI of Android has changed quite a bit over time, and std 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 deleted file mode 100644 index 9f7dcc0416e..00000000000 --- a/library/std/src/sys/unix/args.rs +++ /dev/null @@ -1,282 +0,0 @@ -//! 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, -} - -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 { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - 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 { - 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", - target_os = "aix", - target_os = "nto", - target_os = "hurd", -))] -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 { - 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) }; - let mut args = Vec::with_capacity(argc as usize); - for i in 0..argc { - let ptr = *argv.offset(i) as *const libc::c_char; - - // Some C commandline parsers (e.g. GLib and Qt) are replacing already - // handled arguments in `argv` with `NULL` and move them to the end. That - // means that `argc` might be bigger than the actual number of non-`NULL` - // pointers in `argv` at this point. - // - // To handle this we simply stop iterating at the first `NULL` argument. - // - // `argv` is also guaranteed to be `NULL`-terminated so any non-`NULL` arguments - // after the first `NULL` can safely be ignored. - if ptr.is_null() { - break; - } - - let cstr = CStr::from_ptr(ptr); - args.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); - } - - args - } - } -} - -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", 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::>() - }; - 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 = "tvos", 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(c"processInfo".as_ptr() as *const libc::c_uchar); - let arguments_sel = sel_registerName(c"arguments".as_ptr() as *const libc::c_uchar); - let utf8_sel = sel_registerName(c"UTF8String".as_ptr() as *const libc::c_uchar); - let count_sel = sel_registerName(c"count".as_ptr() as *const libc::c_uchar); - let object_at_sel = - sel_registerName(c"objectAtIndex:".as_ptr() as *const libc::c_uchar); - - let klass = objc_getClass(c"NSProcessInfo".as_ptr() as *const libc::c_uchar); - 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(any(target_os = "espidf", target_os = "vita"))] -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 deleted file mode 100644 index 5346d229116..00000000000 --- a/library/std/src/sys/unix/cmath.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![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; - pub fn tgamma(n: f64) -> f64; - pub fn tgammaf(n: f32) -> f32; - pub fn lgamma_r(n: f64, s: &mut i32) -> f64; - pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; -} diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs deleted file mode 100644 index 3d4ba509829..00000000000 --- a/library/std/src/sys/unix/env.rs +++ /dev/null @@ -1,263 +0,0 @@ -#[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 = "tvos")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "tvos"; - 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(target_os = "hurd")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "hurd"; - 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 = "vita")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "vita"; - 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 = "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 = "nto")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "nto"; - 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 = ""; -} - -#[cfg(target_os = "aix")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "aix"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".a"; - pub const DLL_EXTENSION: &str = "a"; - 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 deleted file mode 100644 index bf1fb3123c4..00000000000 --- a/library/std/src/sys/unix/fd.rs +++ /dev/null @@ -1,557 +0,0 @@ -#![unstable(reason = "not public", issue = "none", feature = "fd")] - -#[cfg(test)] -mod tests; - -use crate::cmp; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; -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", - target_os = "hurd", -))] -use libc::off64_t; -#[cfg(not(any( - target_os = "linux", - target_os = "emscripten", - target_os = "l4re", - target_os = "android", - target_os = "hurd", -)))] -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 = "tvos", - 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", - target_os = "nto", -))] -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 = "tvos", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "nto", - target_os = "openbsd", - target_os = "horizon", - target_os = "vita", - 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 { - 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", target_os = "vita")))] - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let ret = cvt(unsafe { - libc::readv( - self.as_raw_fd(), - bufs.as_mut_ptr() as *mut libc::iovec 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", target_os = "vita"))] - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - 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", target_os = "vita"))) - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - let mut me = self; - (&mut me).read_to_end(buf) - } - - pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - #[cfg(not(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "hurd" - )))] - use libc::pread as pread64; - #[cfg(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "hurd" - ))] - 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, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - let ret = cvt(unsafe { - libc::read( - self.as_raw_fd(), - cursor.as_mut().as_mut_ptr() as *mut libc::c_void, - cmp::min(cursor.capacity(), READ_LIMIT), - ) - })?; - - // Safety: `ret` bytes were written to the initialized portion of the buffer - unsafe { - cursor.advance(ret as usize); - } - Ok(()) - } - - #[cfg(any( - target_os = "emscripten", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - ))] - pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - let ret = cvt(unsafe { - libc::preadv( - self.as_raw_fd(), - bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - - #[cfg(not(any( - target_os = "android", - target_os = "emscripten", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "ios", - target_os = "tvos", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - )))] - pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - io::default_read_vectored(|b| self.read_at(b, offset), bufs) - } - - // We support some old Android versions that do not have `preadv` in libc, - // so we use weak linkage and fallback to a direct syscall if not available. - // - // On 32-bit targets, we don't want to deal with weird ABI issues around - // passing 64-bits parameters to syscalls, so we fallback to the default - // implementation if `preadv` is not available. - #[cfg(all(target_os = "android", target_pointer_width = "64"))] - pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - super::weak::syscall! { - fn preadv( - fd: libc::c_int, - iovec: *const libc::iovec, - n_iovec: libc::c_int, - offset: off64_t - ) -> isize - } - - let ret = cvt(unsafe { - preadv( - self.as_raw_fd(), - bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - - // We support old MacOS and iOS versions that do not have `preadv`. There is - // no `syscall` possible in these platform. - #[cfg(any( - all(target_os = "android", target_pointer_width = "32"), - target_os = "ios", - target_os = "tvos", - target_os = "macos", - ))] - pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); - - match preadv64.get() { - Some(preadv) => { - let ret = cvt(unsafe { - preadv( - self.as_raw_fd(), - bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - None => io::default_read_vectored(|b| self.read_at(b, offset), bufs), - } - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - 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", target_os = "vita")))] - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - 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", target_os = "vita"))] - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - 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", target_os = "vita"))) - } - - pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - #[cfg(not(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "hurd" - )))] - use libc::pwrite as pwrite64; - #[cfg(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "hurd" - ))] - 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(any( - target_os = "emscripten", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - ))] - pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - let ret = cvt(unsafe { - libc::pwritev( - self.as_raw_fd(), - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - - #[cfg(not(any( - target_os = "android", - target_os = "emscripten", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "ios", - target_os = "tvos", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - )))] - pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - io::default_write_vectored(|b| self.write_at(b, offset), bufs) - } - - // We support some old Android versions that do not have `pwritev` in libc, - // so we use weak linkage and fallback to a direct syscall if not available. - // - // On 32-bit targets, we don't want to deal with weird ABI issues around - // passing 64-bits parameters to syscalls, so we fallback to the default - // implementation if `pwritev` is not available. - #[cfg(all(target_os = "android", target_pointer_width = "64"))] - pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - super::weak::syscall! { - fn pwritev( - fd: libc::c_int, - iovec: *const libc::iovec, - n_iovec: libc::c_int, - offset: off64_t - ) -> isize - } - - let ret = cvt(unsafe { - pwritev( - self.as_raw_fd(), - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - - // We support old MacOS and iOS versions that do not have `pwritev`. There is - // no `syscall` possible in these platform. - #[cfg(any( - all(target_os = "android", target_pointer_width = "32"), - target_os = "ios", - target_os = "tvos", - target_os = "macos", - ))] - pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); - - match pwritev64.get() { - Some(pwritev) => { - let ret = cvt(unsafe { - pwritev( - self.as_raw_fd(), - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), max_iov()) as libc::c_int, - offset as _, - ) - })?; - Ok(ret as usize) - } - None => io::default_write_vectored(|b| self.write_at(b, offset), bufs), - } - } - - #[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", - target_os = "nto", - )))] - 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 = "vita")) - ), - 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", - target_os = "nto", - ))] - 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", target_os = "vita"))] - pub fn set_cloexec(&self) -> io::Result<()> { - // FD_CLOEXEC is not supported in ESP-IDF, Horizon OS and Vita but there's no need to, - // because none of them 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 { - Ok(Self(self.0.try_clone()?)) - } -} - -impl<'a> Read for &'a FileDesc { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } - - fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - (**self).read_buf(cursor) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - (**self).read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - (**self).is_read_vectored() - } -} - -impl AsInner for FileDesc { - #[inline] - fn as_inner(&self) -> &OwnedFd { - &self.0 - } -} - -impl IntoInner for FileDesc { - fn into_inner(self) -> OwnedFd { - self.0 - } -} - -impl FromInner 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 { - #[inline] - 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 deleted file mode 100644 index 5d17e46786c..00000000000 --- a/library/std/src/sys/unix/fd/tests.rs +++ /dev/null @@ -1,10 +0,0 @@ -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::>(); - 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 deleted file mode 100644 index 72e7b1b1fc3..00000000000 --- a/library/std/src/sys/unix/fs.rs +++ /dev/null @@ -1,2162 +0,0 @@ -// miri has some special hacks here that make things unused. -#![cfg_attr(miri, allow(unused))] - -use crate::os::unix::prelude::*; - -use crate::ffi::{CStr, OsStr, OsString}; -use crate::fmt; -use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, 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::common::small_c_string::run_path_with_cstr; -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 = "tvos", - target_os = "watchos", -))] -use crate::sys::weak::syscall; -#[cfg(any(target_os = "android", target_os = "macos", target_os = "solaris"))] -use crate::sys::weak::weak; - -use libc::{c_int, mode_t}; - -#[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - target_os = "solaris", - all(target_os = "linux", target_env = "gnu") -))] -use libc::c_char; -#[cfg(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "emscripten", - target_os = "android", - target_os = "hurd" -))] -use libc::dirfd; -#[cfg(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "emscripten", - target_os = "hurd" -))] -use libc::fstatat64; -#[cfg(any( - target_os = "android", - target_os = "solaris", - target_os = "fuchsia", - target_os = "redox", - target_os = "illumos", - target_os = "aix", - target_os = "nto", - target_os = "vita", - all(target_os = "linux", target_env = "musl"), -))] -use libc::readdir as readdir64; -#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] -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", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", -)))] -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( - all(target_os = "linux", not(target_env = "musl")), - target_os = "emscripten", - target_os = "l4re", - target_os = "android", - target_os = "hurd", -)))] -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( - all(target_os = "linux", not(target_env = "musl")), - target_os = "emscripten", - target_os = "l4re", - target_os = "hurd" -))] -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, - } - - #[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> { - use crate::sync::atomic::{AtomicU8, Ordering}; - - // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`. - // We check for it on first failure and remember availability to avoid having to - // do it again. - #[repr(u8)] - enum STATX_STATE{ Unknown = 0, Present, Unavailable } - static STATX_SAVED_STATE: AtomicU8 = AtomicU8::new(STATX_STATE::Unknown as u8); - - syscall! { - fn statx( - fd: c_int, - pathname: *const c_char, - flags: c_int, - mask: libc::c_uint, - statxbuf: *mut libc::statx - ) -> c_int - } - - if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Unavailable as u8 { - return None; - } - - let mut buf: libc::statx = mem::zeroed(); - if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) { - if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Present as u8 { - return Some(Err(err)); - } - - // Availability not checked yet. - // - // First try the cheap way. - if err.raw_os_error() == Some(libc::ENOSYS) { - STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed); - return None; - } - - // Error other than `ENOSYS` is not a good enough indicator -- it is - // known that `EPERM` can be returned as a result of using seccomp to - // block the syscall. - // Availability is checked by performing a call which expects `EFAULT` - // if the syscall is usable. - // See: https://github.com/rust-lang/rust/issues/65662 - // FIXME this can probably just do the call if `EPERM` was received, but - // previous iteration of the code checked it for all errors and for now - // this is retained. - // FIXME what about transient conditions like `ENOMEM`? - let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut())) - .err() - .and_then(|e| e.raw_os_error()); - if err2 == Some(libc::EFAULT) { - STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed); - return Some(Err(err)); - } else { - STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed); - return None; - } - } - - // 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, - end_of_stream: bool, -} - -impl ReadDir { - fn new(inner: InnerReadDir) -> Self { - Self { inner: Arc::new(inner), end_of_stream: false } - } -} - -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", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", -))] -pub struct DirEntry { - dir: Arc, - 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: crate::ffi::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", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", -))] -struct dirent64_min { - d_ino: u64, - #[cfg(not(any( - target_os = "solaris", - target_os = "illumos", - target_os = "aix", - target_os = "nto", - target_os = "vita", - )))] - d_type: u8, -} - -#[cfg(not(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", - target_os = "fuchsia", - target_os = "redox", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", -)))] -pub struct DirEntry { - dir: Arc, - // 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, Debug, Default)] -pub struct FileTimes { - accessed: Option, - modified: Option, - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] - created: Option, -} - -#[derive(Copy, Clone, Eq, Debug)] -pub struct FileType { - mode: mode_t, -} - -impl PartialEq for FileType { - fn eq(&self, other: &Self) -> bool { - self.masked() == other.masked() - } -} - -impl core::hash::Hash for FileType { - fn hash(&self, state: &mut H) { - self.masked().hash(state); - } -} - -#[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 { - Ok(SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64)) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64)) - } - - pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64)) - } -} - -#[cfg(target_os = "aix")] -impl FileAttr { - pub fn modified(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64)) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64)) - } - - pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64)) - } -} - -#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix")))] -impl FileAttr { - #[cfg(not(any( - target_os = "vxworks", - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "hurd", - )))] - pub fn modified(&self) -> io::Result { - #[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", target_os = "vita"))] - pub fn modified(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_mtime as i64, 0)) - } - - #[cfg(any(target_os = "horizon", target_os = "hurd"))] - pub fn modified(&self) -> io::Result { - Ok(SystemTime::from(self.stat.st_mtim)) - } - - #[cfg(not(any( - target_os = "vxworks", - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "hurd", - )))] - pub fn accessed(&self) -> io::Result { - #[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", target_os = "vita"))] - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_atime as i64, 0)) - } - - #[cfg(any(target_os = "horizon", target_os = "hurd"))] - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from(self.stat.st_atim)) - } - - #[cfg(any( - target_os = "freebsd", - target_os = "openbsd", - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - ))] - pub fn created(&self) -> io::Result { - 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 = "tvos", - target_os = "watchos", - target_os = "vita", - )))] - pub fn created(&self) -> io::Result { - 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", - )) - } - - #[cfg(target_os = "vita")] - pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_ctime as i64, 0)) - } -} - -#[cfg(target_os = "nto")] -impl FileAttr { - pub fn modified(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec)) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec)) - } - - pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec)) - } -} - -impl AsInner for FileAttr { - #[inline] - 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 ` - self.mode &= !0o222; - } else { - // add write permission for all classes; equivalent to `chmod a+w ` - self.mode |= 0o222; - } - } - pub fn mode(&self) -> u32 { - self.mode as u32 - } -} - -impl FileTimes { - pub fn set_accessed(&mut self, t: SystemTime) { - self.accessed = Some(t); - } - - pub fn set_modified(&mut self, t: SystemTime) { - self.modified = Some(t); - } - - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] - pub fn set_created(&mut self, t: SystemTime) { - self.created = Some(t); - } -} - -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.masked() == mode - } - - fn masked(&self) -> mode_t { - self.mode & libc::S_IFMT - } -} - -impl FromInner 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; - - #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "fuchsia", - target_os = "redox", - target_os = "illumos", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", - ))] - fn next(&mut self) -> Option> { - if self.end_of_stream { - return None; - } - - 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() { - // We either encountered an error, or reached the end. Either way, - // the next call to next() should return None. - self.end_of_stream = true; - - // To distinguish between errors and end-of-directory, 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))), - }; - } - - // The dirent64 struct is a weird imaginary thing that isn't ever supposed - // to be worked with by value. Its trailing d_name field is declared - // variously as [c_char; 256] or [c_char; 1] on different systems but - // either way that size is meaningless; only the offset of d_name is - // meaningful. The dirent64 pointers that libc returns from readdir64 are - // allowed to point to allocations smaller _or_ LARGER than implied by the - // definition of the struct. - // - // As such, we need to be even more careful with dirent64 than if its - // contents were "simply" partially initialized data. - // - // Like for uninitialized contents, converting entry_ptr to `&dirent64` - // would not be legal. However, unique to dirent64 is that we don't even - // get to use `addr_of!((*entry_ptr).d_name)` because that operation - // requires the full extent of *entry_ptr to be in bounds of the same - // allocation, which is not necessarily the case here. - // - // Instead we must access fields individually through their offsets. - macro_rules! offset_ptr { - ($entry_ptr:expr, $field:ident) => {{ - const OFFSET: isize = mem::offset_of!(dirent64, $field) as isize; - if true { - // Cast to the same type determined by the else branch. - $entry_ptr.byte_offset(OFFSET).cast::<_>() - } else { - #[allow(deref_nullptr)] - { - ptr::addr_of!((*ptr::null::()).$field) - } - } - }}; - } - - // d_name is guaranteed to be null-terminated. - let name = CStr::from_ptr(offset_ptr!(entry_ptr, d_name).cast()); - let name_bytes = name.to_bytes(); - if name_bytes == b"." || name_bytes == b".." { - continue; - } - - #[cfg(not(target_os = "vita"))] - let entry = dirent64_min { - d_ino: *offset_ptr!(entry_ptr, d_ino) as u64, - #[cfg(not(any( - target_os = "solaris", - target_os = "illumos", - target_os = "aix", - target_os = "nto", - )))] - d_type: *offset_ptr!(entry_ptr, d_type) as u8, - }; - - #[cfg(target_os = "vita")] - let entry = dirent64_min { d_ino: 0u64 }; - - return Some(Ok(DirEntry { - entry, - name: name.to_owned(), - dir: Arc::clone(&self.inner), - })); - } - } - } - - #[cfg(not(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "fuchsia", - target_os = "redox", - target_os = "illumos", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", - )))] - fn next(&mut self) -> Option> { - 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) }; - assert!( - r == 0 || crate::io::Error::last_os_error().is_interrupted(), - "unexpected error during closedir: {:?}", - crate::io::Error::last_os_error() - ); - } -} - -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(all( - any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "emscripten", - target_os = "android", - target_os = "hurd" - ), - not(miri) - ))] - pub fn metadata(&self) -> io::Result { - 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(any( - not(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "emscripten", - target_os = "android", - target_os = "hurd", - )), - miri - ))] - pub fn metadata(&self) -> io::Result { - lstat(&self.path()) - } - - #[cfg(any( - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "vxworks", - target_os = "aix", - target_os = "nto", - target_os = "vita", - ))] - pub fn file_type(&self) -> io::Result { - self.metadata().map(|m| m.file_type()) - } - - #[cfg(not(any( - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "vxworks", - target_os = "aix", - target_os = "nto", - target_os = "vita", - )))] - pub fn file_type(&self) -> io::Result { - 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 = "tvos", - 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", - target_os = "vita", - target_os = "aix", - target_os = "nto", - target_os = "hurd", - ))] - 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 = "tvos", - 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 = "tvos", - 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", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", - )))] - 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", - target_os = "aix", - target_os = "nto", - target_os = "vita", - target_os = "hurd", - ))] - 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 { - 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 { - 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 { - run_path_with_cstr(path, |path| File::open_c(path, opts)) - } - - pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result { - 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 { - let fd = self.as_raw_fd(); - - cfg_has_statx! { - if let Some(ret) = unsafe { try_statx( - fd, - c"".as_ptr() 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 = "tvos", - 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 = "tvos", - 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 = "tvos", - 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", - target_os = "nto", - target_os = "hurd", - ))] - 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 = "tvos", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "watchos", - target_os = "nto", - target_os = "hurd", - )))] - 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 { - self.0.read(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - 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 { - self.0.read_at(buf, offset) - } - - pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(cursor) - } - - pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - self.0.read_vectored_at(bufs, offset) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - 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 { - self.0.write_at(buf, offset) - } - - pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - self.0.write_vectored_at(bufs, offset) - } - - #[inline] - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } - - pub fn seek(&self, pos: SeekFrom) -> io::Result { - 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 { - 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(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))] - let to_timespec = |time: Option| match time { - Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), - Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "timestamp is too large to set as a file time" - )), - Some(_) => Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "timestamp is too small to set as a file time" - )), - None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), - }; - cfg_if::cfg_if! { - if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon"))] { - // Redox doesn't appear to support `UTIME_OMIT`. - // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore - // the same as for Redox. - let _ = times; - Err(io::const_io_error!( - io::ErrorKind::Unsupported, - "setting file times not supported", - )) - } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] { - let mut buf = [mem::MaybeUninit::::uninit(); 3]; - let mut num_times = 0; - let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; - attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; - if times.created.is_some() { - buf[num_times].write(to_timespec(times.created)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_CRTIME; - } - if times.modified.is_some() { - buf[num_times].write(to_timespec(times.modified)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_MODTIME; - } - if times.accessed.is_some() { - buf[num_times].write(to_timespec(times.accessed)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; - } - cvt(unsafe { libc::fsetattrlist( - self.as_raw_fd(), - (&attrlist as *const libc::attrlist).cast::().cast_mut(), - buf.as_ptr().cast::().cast_mut(), - num_times * mem::size_of::(), - 0 - ) })?; - Ok(()) - } else if #[cfg(target_os = "android")] { - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; - // futimens requires 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.as_ptr()), - None => return Err(io::const_io_error!( - io::ErrorKind::Unsupported, - "setting file times requires Android API level >= 19", - )), - } - })?; - Ok(()) - } else { - #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))] - { - use crate::sys::{time::__timespec64, weak::weak}; - - // Added in glibc 2.34 - weak!(fn __futimens64(libc::c_int, *const __timespec64) -> libc::c_int); - - if let Some(futimens64) = __futimens64.get() { - let to_timespec = |time: Option| time.map(|time| time.t.to_timespec64()) - .unwrap_or(__timespec64::new(0, libc::UTIME_OMIT as _)); - let times = [to_timespec(times.accessed), to_timespec(times.modified)]; - cvt(unsafe { futimens64(self.as_raw_fd(), times.as_ptr()) })?; - return Ok(()); - } - } - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; - cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) })?; - Ok(()) - } - } - } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder { mode: 0o777 } - } - - pub fn mkdir(&self, p: &Path) -> io::Result<()> { - run_path_with_cstr(p, |p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ())) - } - - pub fn set_mode(&mut self, mode: u32) { - self.mode = mode as mode_t; - } -} - -impl AsInner for File { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } -} - -impl AsInnerMut for File { - #[inline] - fn as_inner_mut(&mut self) -> &mut FileDesc { - &mut self.0 - } -} - -impl IntoInner for File { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -impl FromInner for File { - fn from_inner(file_desc: FileDesc) -> Self { - Self(file_desc) - } -} - -impl AsFd for File { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for File { - #[inline] - 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", - target_os = "illumos", - target_os = "solaris" - ))] - fn get_path(fd: c_int) -> Option { - 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 { - // 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 { - let info = Box::::new_zeroed(); - let mut info = unsafe { info.assume_init() }; - info.kf_structsize = mem::size_of::() 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 { - 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", - target_os = "illumos", - target_os = "solaris" - )))] - fn get_path(_fd: c_int) -> Option { - // FIXME(#24570): implement this for other Unix platforms - None - } - - #[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "freebsd", - target_os = "hurd", - target_os = "netbsd", - target_os = "openbsd", - 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 = "freebsd", - target_os = "hurd", - target_os = "netbsd", - target_os = "openbsd", - 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(path: &Path) -> io::Result { - let ptr = run_path_with_cstr(path, |p| unsafe { Ok(libc::opendir(p.as_ptr())) })?; - if ptr.is_null() { - Err(Error::last_os_error()) - } else { - let root = path.to_path_buf(); - let inner = InnerReadDir { dirp: Dir(ptr), root }; - Ok(ReadDir::new(inner)) - } -} - -pub fn unlink(p: &Path) -> io::Result<()> { - run_path_with_cstr(p, |p| cvt(unsafe { libc::unlink(p.as_ptr()) }).map(|_| ())) -} - -pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - run_path_with_cstr(old, |old| { - run_path_with_cstr(new, |new| { - cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ()) - }) - }) -} - -pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { - run_path_with_cstr(p, |p| cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ())) -} - -pub fn rmdir(p: &Path) -> io::Result<()> { - run_path_with_cstr(p, |p| cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ())) -} - -pub fn readlink(p: &Path) -> io::Result { - run_path_with_cstr(p, |c_path| { - 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<()> { - run_path_with_cstr(original, |original| { - run_path_with_cstr(link, |link| { - cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ()) - }) - }) -} - -pub fn link(original: &Path, link: &Path) -> io::Result<()> { - run_path_with_cstr(original, |original| { - run_path_with_cstr(link, |link| { - cfg_if::cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { - // 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(any(target_os = "macos", target_os = "solaris"))] { - // MacOS (<=10.9) and Solaris 10 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 { - run_path_with_cstr(p, |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 { - run_path_with_cstr(p, |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 { - let r = run_path_with_cstr(p, |path| unsafe { - Ok(libc::realpath(path.as_ptr(), ptr::null_mut())) - })?; - if r.is_null() { - return Err(io::Error::last_os_error()); - } - Ok(PathBuf::from(OsString::from_vec(unsafe { - let buf = CStr::from_ptr(r).to_bytes().to_vec(); - libc::free(r as *mut _); - 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()?; - // fchmod is broken on vita - #[cfg(not(target_os = "vita"))] - 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 = "tvos", - target_os = "watchos", -)))] -pub fn copy(from: &Path, to: &Path) -> io::Result { - 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 { - 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 = "tvos", target_os = "watchos"))] -pub fn copy(from: &Path, to: &Path) -> io::Result { - 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 clonefile_result = run_path_with_cstr(to, |to| { - 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<()> { - run_path_with_cstr(path, |path| { - cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) - .map(|_| ()) - }) -} - -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<()> { - run_path_with_cstr(path, |path| { - cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) - .map(|_| ()) - }) -} - -#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] -pub fn chroot(dir: &Path) -> io::Result<()> { - run_path_with_cstr(dir, |dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ())) -} - -pub use remove_dir_impl::remove_dir_all; - -// Fallback for REDOX, ESP-ID, Horizon, Vita and Miri -#[cfg(any( - target_os = "redox", - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - target_os = "nto", - 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", - target_os = "vita", - target_os = "nto", - miri -)))] -mod remove_dir_impl { - use super::{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::sys::common::small_c_string::run_path_with_cstr; - use crate::sys::{cvt, cvt_r}; - - #[cfg(not(any( - all(target_os = "linux", target_env = "gnu"), - all(target_os = "macos", not(target_arch = "aarch64")) - )))] - use libc::{fdopendir, openat, unlinkat}; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{fdopendir, openat64 as 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 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, p: &CStr) -> io::Result { - 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 `DirEntry`s. - let dummy_root = PathBuf::new(); - let inner = InnerReadDir { dirp, root: dummy_root }; - Ok((ReadDir::new(inner), new_parent_fd)) - } - - #[cfg(any( - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "vxworks", - target_os = "aix", - ))] - fn is_dir(_ent: &DirEntry) -> Option { - None - } - - #[cfg(not(any( - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "vxworks", - target_os = "aix", - )))] - fn is_dir(ent: &DirEntry) -> Option { - match ent.entry.d_type { - libc::DT_UNKNOWN => None, - libc::DT_DIR => Some(true), - _ => Some(false), - } - } - - fn remove_dir_all_recursive(parent_fd: Option, 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 { - run_path_with_cstr(p, |p| remove_dir_all_recursive(None, &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 deleted file mode 100644 index d310be6c7a1..00000000000 --- a/library/std/src/sys/unix/futex.rs +++ /dev/null @@ -1,301 +0,0 @@ -#![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) -> 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::(), // 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) -> 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) -> 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) -> 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) -> bool { - // 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 deleted file mode 100644 index 29c340dd349..00000000000 --- a/library/std/src/sys/unix/io.rs +++ /dev/null @@ -1,82 +0,0 @@ -use crate::marker::PhantomData; -use crate::os::fd::{AsFd, AsRawFd}; -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) } - } -} - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - unsafe { libc::isatty(fd.as_raw_fd()) != 0 } -} diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs deleted file mode 100644 index 18acd5ecccd..00000000000 --- a/library/std/src/sys/unix/kernel_copy.rs +++ /dev/null @@ -1,730 +0,0 @@ -//! 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 it attempts -//! to use them one after another (guided by hints) to figure out which one works 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; -#[cfg(not(any(all(target_os = "linux", target_env = "gnu"), target_os = "hurd")))] -use libc::sendfile as sendfile64; -#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "hurd"))] -use libc::sendfile64; -use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV}; - -#[cfg(test)] -mod tests; - -pub(crate) fn copy_spec( - read: &mut R, - write: &mut W, -) -> Result { - 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 { - Metadata(Metadata), - Socket, - Pipe, - /// We don't have any metadata because the stat syscall failed - NoneObtained, -} - -#[derive(PartialEq)] -enum FdHandle { - Input, - Output, -} - -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, f: FdHandle) -> 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 f == FdHandle::Input && meta.is_file() && meta.len() > 0 => { - true - } - FdMeta::Metadata(meta) if f == FdHandle::Output && meta.is_file() => true, - _ => false, - } - } -} - -/// Returns true either if changes made to the source after a sendfile/splice call won't become -/// visible in the sink or the source has explicitly opted into such behavior (e.g. by splicing -/// a file into a pipe, the pipe being the source in this case). -/// -/// This will prevent File -> Pipe and File -> Socket splicing/sendfile optimizations to uphold -/// the Read/Write API semantics of io::copy. -/// -/// Note: This is not 100% airtight, the caller can use the RawFd conversion methods to turn a -/// regular file into a TcpSocket which will be treated as a socket here without checking. -fn safe_kernel_copy(source: &FdMeta, sink: &FdMeta) -> bool { - match (source, sink) { - // Data arriving from a socket is safe because the sender can't modify the socket buffer. - // Data arriving from a pipe is safe(-ish) because either the sender *copied* - // the bytes into the pipe OR explicitly performed an operation that enables zero-copy, - // thus promising not to modify the data later. - (FdMeta::Socket, _) => true, - (FdMeta::Pipe, _) => true, - (FdMeta::Metadata(meta), _) - if meta.file_type().is_fifo() || meta.file_type().is_socket() => - { - true - } - // Data going into non-pipes/non-sockets is safe because the "later changes may become visible" issue - // only happens for pages sitting in send buffers or pipes. - (_, FdMeta::Metadata(meta)) - if !meta.file_type().is_fifo() && !meta.file_type().is_socket() => - { - true - } - _ => false, - } -} - -struct CopyParams(FdMeta, Option); - -struct Copier<'a, 'b, R: Read + ?Sized, W: Write + ?Sized> { - read: &'a mut R, - write: &'b mut W, -} - -trait SpecCopy { - fn copy(self) -> Result; -} - -impl SpecCopy for Copier<'_, '_, R, W> { - default fn copy(self) -> Result { - generic_copy(self.read, self.write) - } -} - -impl SpecCopy for Copier<'_, '_, R, W> { - fn copy(self) -> Result { - 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 { - 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(FdHandle::Input) - && output_meta.copy_file_range_candidate(FdHandle::Output) - { - 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() && safe_kernel_copy(&input_meta, &output_meta) - { - 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()) - && safe_kernel_copy(&input_meta, &output_meta) - { - 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(&mut self, _writer: &mut W, _limit: u64) -> Result { - 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>>` 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 CopyRead for &mut T -where - T: CopyRead, -{ - fn drain_to(&mut self, writer: &mut W, limit: u64) -> Result { - (**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 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(fd_to_meta(self), Some(self.as_raw_fd())) - } -} - -impl CopyWrite for &File { - fn properties(&self) -> CopyParams { - CopyParams(fd_to_meta(*self), 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(&mut self, writer: &mut W, outer_limit: u64) -> Result { - 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(fd_to_meta(self), Some(self.as_raw_fd())) - } -} - -impl CopyWrite for StderrLock<'_> { - fn properties(&self) -> CopyParams { - CopyParams(fd_to_meta(self), Some(self.as_raw_fd())) - } -} - -impl CopyRead for Take { - fn drain_to(&mut self, writer: &mut W, outer_limit: u64) -> Result { - 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 CopyRead for BufReader { - fn drain_to(&mut self, writer: &mut W, outer_limit: u64) -> Result { - 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 CopyWrite for BufWriter { - fn properties(&self) -> CopyParams { - self.get_ref().properties() - } -} - -fn fd_to_meta(fd: &T) -> FdMeta { - let fd = fd.as_raw_fd(); - let file: ManuallyDrop = 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 { sendfile64(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 deleted file mode 100644 index a524270e3fb..00000000000 --- a/library/std/src/sys/unix/kernel_copy/tests.rs +++ /dev/null @@ -1,312 +0,0 @@ -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(()) -} - -#[test] -fn dont_splice_pipes_from_files() -> Result<()> { - // splicing to a pipe and then modifying the source could lead to changes - // becoming visible in an unexpected order. - - use crate::io::SeekFrom; - use crate::os::unix::fs::FileExt; - use crate::process::{ChildStdin, ChildStdout}; - use crate::sys_common::FromInner; - - let (read_end, write_end) = crate::sys::pipe::anon_pipe()?; - - let mut read_end = ChildStdout::from_inner(read_end); - let mut write_end = ChildStdin::from_inner(write_end); - - let tmp_path = tmpdir(); - let file = tmp_path.join("to_be_modified"); - let mut file = - crate::fs::OpenOptions::new().create_new(true).read(true).write(true).open(file)?; - - const SZ: usize = libc::PIPE_BUF as usize; - - // put data in page cache - let mut buf: [u8; SZ] = [0x01; SZ]; - file.write_all(&buf).unwrap(); - - // copy page into pipe - file.seek(SeekFrom::Start(0)).unwrap(); - assert!(io::copy(&mut file, &mut write_end).unwrap() == SZ as u64); - - // modify file - buf[0] = 0x02; - file.write_at(&buf, 0).unwrap(); - - // read from pipe - read_end.read_exact(buf.as_mut_slice()).unwrap(); - - assert_eq!(buf[0], 0x01, "data in pipe should reflect the original, not later modifications"); - - 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 deleted file mode 100644 index fe9559f2a56..00000000000 --- a/library/std/src/sys/unix/l4re.rs +++ /dev/null @@ -1,560 +0,0 @@ -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, BorrowedCursor, 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 { - unimpl!(); - } - - pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result { - 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 { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - 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 { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn set_timeout(&self, _: Option, _: libc::c_int) -> io::Result<()> { - unimpl!(); - } - - pub fn timeout(&self, _: libc::c_int) -> io::Result> { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - 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 for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } - } - - impl FromInner for Socket { - fn from_inner(file_desc: FileDesc) -> Socket { - Socket(file_desc) - } - } - - impl IntoInner 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 { - #[inline] - 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 { - unimpl!(); - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn write(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn peer_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } - } - - impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re") - } - } - - pub struct TcpListener { - inner: Socket, - } - - impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn only_v6(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for TcpListener { - fn from_inner(socket: Socket) -> TcpListener { - TcpListener { inner: socket } - } - } - - impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re.") - } - } - - pub struct UdpSocket { - inner: Socket, - } - - impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn peer_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result { - 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 { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn broadcast(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v6(&self) -> io::Result { - 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 { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn send(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for UdpSocket { - fn from_inner(socket: Socket) -> UdpSocket { - UdpSocket { inner: socket } - } - } - - impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - 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 { - 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 { - unimpl!(); - } - } - - impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unimpl!(); - } - } -} diff --git a/library/std/src/sys/unix/locks/fuchsia_mutex.rs b/library/std/src/sys/unix/locks/fuchsia_mutex.rs deleted file mode 100644 index 5d89e5a13fd..00000000000 --- a/library/std/src/sys/unix/locks/fuchsia_mutex.rs +++ /dev/null @@ -1,164 +0,0 @@ -//! 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 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 fn try_lock(&self) -> bool { - let thread_self = unsafe { zx_thread_self() }; - self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed).is_ok() - } - - #[inline] - pub fn lock(&self) { - let thread_self = unsafe { zx_thread_self() }; - if let Err(state) = - self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed) - { - unsafe { - self.lock_contested(state, thread_self); - } - } - } - - /// # Safety - /// `thread_self` must be the handle for the current thread. - #[cold] - unsafe 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 occurred, 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 deleted file mode 100644 index 4bd65dd25c2..00000000000 --- a/library/std/src/sys/unix/locks/futex_condvar.rs +++ /dev/null @@ -1,56 +0,0 @@ -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 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 fn notify_one(&self) { - self.futex.fetch_add(1, Relaxed); - futex_wake(&self.futex); - } - - pub 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) -> 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 deleted file mode 100644 index c01229586c3..00000000000 --- a/library/std/src/sys/unix/locks/futex_mutex.rs +++ /dev/null @@ -1,96 +0,0 @@ -use crate::sync::atomic::{ - AtomicU32, - Ordering::{Acquire, Relaxed, Release}, -}; -use crate::sys::futex::{futex_wait, futex_wake}; - -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 fn try_lock(&self) -> bool { - self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_ok() - } - - #[inline] - pub 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 successfully 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 deleted file mode 100644 index aa0de900238..00000000000 --- a/library/std/src/sys/unix/locks/futex_rwlock.rs +++ /dev/null @@ -1,320 +0,0 @@ -use crate::sync::atomic::{ - AtomicU32, - Ordering::{Acquire, Relaxed, Release}, -}; -use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; - -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 necessary. - 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 fn try_read(&self) -> bool { - self.state - .fetch_update(Acquire, Relaxed, |s| is_read_lockable(s).then(|| s + READ_LOCKED)) - .is_ok() - } - - #[inline] - pub 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 fn try_write(&self) -> bool { - self.state - .fetch_update(Acquire, Relaxed, |s| is_unlocked(s).then(|| s + WRITE_LOCKED)) - .is_ok() - } - - #[inline] - pub 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 deleted file mode 100644 index b2e0e49ad73..00000000000 --- a/library/std/src/sys/unix/locks/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -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; - pub(crate) use futex_rwlock::RwLock; - pub(crate) use futex_condvar::Condvar; - } else if #[cfg(target_os = "fuchsia")] { - mod fuchsia_mutex; - mod futex_rwlock; - mod futex_condvar; - pub(crate) use fuchsia_mutex::Mutex; - pub(crate) use futex_rwlock::RwLock; - pub(crate) use futex_condvar::Condvar; - } else { - mod pthread_mutex; - mod pthread_rwlock; - mod pthread_condvar; - pub(crate) use pthread_mutex::Mutex; - pub(crate) use pthread_rwlock::RwLock; - pub(crate) use pthread_condvar::Condvar; - } -} diff --git a/library/std/src/sys/unix/locks/pthread_condvar.rs b/library/std/src/sys/unix/locks/pthread_condvar.rs deleted file mode 100644 index 2dc1b0c601e..00000000000 --- a/library/std/src/sys/unix/locks/pthread_condvar.rs +++ /dev/null @@ -1,206 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::ptr; -use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed}; -use crate::sys::locks::{pthread_mutex, Mutex}; -#[cfg(not(target_os = "nto"))] -use crate::sys::time::TIMESPEC_MAX; -#[cfg(target_os = "nto")] -use crate::sys::time::TIMESPEC_MAX_CAPPED; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; -use crate::time::Duration; - -struct AllocatedCondvar(UnsafeCell); - -pub struct Condvar { - inner: LazyBox, - mutex: AtomicPtr, -} - -#[inline] -fn raw(c: &Condvar) -> *mut libc::pthread_cond_t { - c.inner.0.get() -} - -unsafe impl Send for AllocatedCondvar {} -unsafe impl Sync for AllocatedCondvar {} - -impl LazyInit for AllocatedCondvar { - fn init() -> Box { - let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER))); - - cfg_if::cfg_if! { - if #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - target_os = "l4re", - target_os = "android", - target_os = "redox" - ))] { - // `pthread_condattr_setclock` is unfortunately not supported on these platforms. - } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { - // 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). - let r = unsafe { libc::pthread_cond_init(condvar.0.get(), crate::ptr::null()) }; - assert_eq!(r, 0); - } else { - use crate::mem::MaybeUninit; - let mut attr = MaybeUninit::::uninit(); - let r = unsafe { libc::pthread_condattr_init(attr.as_mut_ptr()) }; - assert_eq!(r, 0); - let r = unsafe { libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC) }; - assert_eq!(r, 0); - let r = unsafe { libc::pthread_cond_init(condvar.0.get(), attr.as_ptr()) }; - assert_eq!(r, 0); - let r = unsafe { libc::pthread_condattr_destroy(attr.as_mut_ptr()) }; - assert_eq!(r, 0); - } - } - - condvar - } -} - -impl Drop for AllocatedCondvar { - #[inline] - fn drop(&mut self) { - let r = unsafe { libc::pthread_cond_destroy(self.0.get()) }; - if cfg!(target_os = "dragonfly") { - // 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); - } else { - debug_assert_eq!(r, 0); - } - } -} - -impl Condvar { - pub const fn new() -> Condvar { - Condvar { inner: LazyBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } - } - - #[inline] - fn verify(&self, mutex: *mut libc::pthread_mutex_t) { - // Relaxed is okay here because we never read through `self.addr`, and only use it to - // compare addresses. - match self.mutex.compare_exchange(ptr::null_mut(), mutex, Relaxed, Relaxed) { - Ok(_) => {} // Stored the address - Err(n) if n == mutex => {} // Lost a race to store the same address - _ => panic!("attempted to use a condition variable with two mutexes"), - } - } - - #[inline] - pub fn notify_one(&self) { - let r = unsafe { libc::pthread_cond_signal(raw(self)) }; - debug_assert_eq!(r, 0); - } - - #[inline] - pub fn notify_all(&self) { - let r = unsafe { libc::pthread_cond_broadcast(raw(self)) }; - debug_assert_eq!(r, 0); - } - - #[inline] - pub unsafe fn wait(&self, mutex: &Mutex) { - let mutex = pthread_mutex::raw(mutex); - self.verify(mutex); - let r = libc::pthread_cond_wait(raw(self), 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 = "tvos", - 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::sys::time::Timespec; - - let mutex = pthread_mutex::raw(mutex); - self.verify(mutex); - - #[cfg(not(target_os = "nto"))] - let timeout = Timespec::now(libc::CLOCK_MONOTONIC) - .checked_add_duration(&dur) - .and_then(|t| t.to_timespec()) - .unwrap_or(TIMESPEC_MAX); - - #[cfg(target_os = "nto")] - let timeout = Timespec::now(libc::CLOCK_MONOTONIC) - .checked_add_duration(&dur) - .and_then(|t| t.to_timespec_capped()) - .unwrap_or(TIMESPEC_MAX_CAPPED); - - let r = libc::pthread_cond_timedwait(raw(self), 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 = "tvos", - 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::sys::time::SystemTime; - use crate::time::Instant; - - let mutex = pthread_mutex::raw(mutex); - self.verify(mutex); - - // 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 returns 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. - let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400)); - - // pthread_cond_timedwait uses system time, but we want to report timeout - // based on stable time. - let now = Instant::now(); - - let timeout = SystemTime::now() - .t - .checked_add_duration(&dur) - .and_then(|t| t.to_timespec()) - .unwrap_or(TIMESPEC_MAX); - - let r = libc::pthread_cond_timedwait(raw(self), 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 - now.elapsed() < dur - } -} diff --git a/library/std/src/sys/unix/locks/pthread_mutex.rs b/library/std/src/sys/unix/locks/pthread_mutex.rs deleted file mode 100644 index 8a78bc1fd73..00000000000 --- a/library/std/src/sys/unix/locks/pthread_mutex.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::mem::{forget, MaybeUninit}; -use crate::sys::cvt_nz; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; - -struct AllocatedMutex(UnsafeCell); - -pub struct Mutex { - inner: LazyBox, -} - -#[inline] -pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t { - m.inner.0.get() -} - -unsafe impl Send for AllocatedMutex {} -unsafe impl Sync for AllocatedMutex {} - -impl LazyInit for AllocatedMutex { - fn init() -> Box { - let mutex = Box::new(AllocatedMutex(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))); - - // 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. - unsafe { - let mut attr = MaybeUninit::::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(mutex.0.get(), attr.0.as_ptr())).unwrap(); - } - - mutex - } - - fn destroy(mutex: Box) { - // We're not allowed to pthread_mutex_destroy a locked mutex, - // so check first if it's unlocked. - if unsafe { libc::pthread_mutex_trylock(mutex.0.get()) == 0 } { - unsafe { libc::pthread_mutex_unlock(mutex.0.get()) }; - 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) { - // In this case, we can just drop it without any checks, - // since it cannot have been locked yet. - } -} - -impl Drop for AllocatedMutex { - #[inline] - fn drop(&mut self) { - let r = unsafe { libc::pthread_mutex_destroy(self.0.get()) }; - if cfg!(target_os = "dragonfly") { - // 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); - } else { - debug_assert_eq!(r, 0); - } - } -} - -impl Mutex { - #[inline] - pub const fn new() -> Mutex { - Mutex { inner: LazyBox::new() } - } - - #[inline] - pub unsafe fn lock(&self) { - let r = libc::pthread_mutex_lock(raw(self)); - debug_assert_eq!(r, 0); - } - - #[inline] - pub unsafe fn unlock(&self) { - let r = libc::pthread_mutex_unlock(raw(self)); - debug_assert_eq!(r, 0); - } - - #[inline] - pub unsafe fn try_lock(&self) -> bool { - libc::pthread_mutex_trylock(raw(self)) == 0 - } -} - -pub(super) struct PthreadMutexAttr<'a>(pub &'a mut MaybeUninit); - -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 deleted file mode 100644 index 04662be9d82..00000000000 --- a/library/std/src/sys/unix/locks/pthread_rwlock.rs +++ /dev/null @@ -1,195 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::mem::forget; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; - -struct AllocatedRwLock { - inner: UnsafeCell, - write_locked: UnsafeCell, // guarded by the `inner` RwLock - num_readers: AtomicUsize, -} - -unsafe impl Send for AllocatedRwLock {} -unsafe impl Sync for AllocatedRwLock {} - -pub struct RwLock { - inner: LazyBox, -} - -impl LazyInit for AllocatedRwLock { - fn init() -> Box { - Box::new(AllocatedRwLock { - inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER), - write_locked: UnsafeCell::new(false), - num_readers: AtomicUsize::new(0), - }) - } - - fn destroy(mut rwlock: Box) { - // 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) { - // In this case, we can just drop it without any checks, - // since it cannot have been locked yet. - } -} - -impl AllocatedRwLock { - #[inline] - unsafe fn raw_unlock(&self) { - let r = libc::pthread_rwlock_unlock(self.inner.get()); - debug_assert_eq!(r, 0); - } -} - -impl Drop for AllocatedRwLock { - fn drop(&mut self) { - let r = unsafe { 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 RwLock { - #[inline] - pub const fn new() -> RwLock { - RwLock { inner: LazyBox::new() } - } - - #[inline] - pub fn read(&self) { - let lock = &*self.inner; - let r = unsafe { libc::pthread_rwlock_rdlock(lock.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 && unsafe { *lock.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. - unsafe { - lock.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); - lock.num_readers.fetch_add(1, Ordering::Relaxed); - } - } - - #[inline] - pub fn try_read(&self) -> bool { - let lock = &*self.inner; - let r = unsafe { libc::pthread_rwlock_tryrdlock(lock.inner.get()) }; - if r == 0 { - if unsafe { *lock.write_locked.get() } { - // `pthread_rwlock_tryrdlock` succeeded when it should not have. - unsafe { - lock.raw_unlock(); - } - false - } else { - lock.num_readers.fetch_add(1, Ordering::Relaxed); - true - } - } else { - false - } - } - - #[inline] - pub fn write(&self) { - let lock = &*self.inner; - let r = unsafe { libc::pthread_rwlock_wrlock(lock.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 && unsafe { *lock.write_locked.get() }) - || lock.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. - unsafe { - lock.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); - } - - unsafe { - *lock.write_locked.get() = true; - } - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - let lock = &*self.inner; - let r = libc::pthread_rwlock_trywrlock(lock.inner.get()); - if r == 0 { - if *lock.write_locked.get() || lock.num_readers.load(Ordering::Relaxed) != 0 { - // `pthread_rwlock_trywrlock` succeeded when it should not have. - lock.raw_unlock(); - false - } else { - *lock.write_locked.get() = true; - true - } - } else { - false - } - } - - #[inline] - pub unsafe fn read_unlock(&self) { - let lock = &*self.inner; - debug_assert!(!*lock.write_locked.get()); - lock.num_readers.fetch_sub(1, Ordering::Relaxed); - lock.raw_unlock(); - } - - #[inline] - pub unsafe fn write_unlock(&self) { - let lock = &*self.inner; - debug_assert_eq!(lock.num_readers.load(Ordering::Relaxed), 0); - debug_assert!(*lock.write_locked.get()); - *lock.write_locked.get() = false; - lock.raw_unlock(); - } -} diff --git a/library/std/src/sys/unix/memchr.rs b/library/std/src/sys/unix/memchr.rs deleted file mode 100644 index 73ba604eccb..00000000000 --- a/library/std/src/sys/unix/memchr.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Original implementation taken from rust-memchr. -// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch - -pub fn memchr(needle: u8, haystack: &[u8]) -> Option { - 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 { - #[cfg(target_os = "linux")] - fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option { - // 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 { - 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 deleted file mode 100644 index b5da5f870ec..00000000000 --- a/library/std/src/sys/unix/mod.rs +++ /dev/null @@ -1,443 +0,0 @@ -#![allow(missing_docs, nonstandard_style)] - -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_parking; -pub mod time; - -#[cfg(target_os = "espidf")] -pub fn init(argc: isize, argv: *const *const u8, _sigpipe: 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. -// See `fn init()` in `library/std/src/rt.rs` for docs on `sigpipe`. -pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: 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. Add `#[unix_sigpipe = "..."]` above `fn main()` to - // alter this behavior. - reset_sigpipe(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(&c"main"); - } - - 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 = "tvos", - target_os = "watchos", - target_os = "redox", - target_os = "l4re", - target_os = "horizon", - target_os = "vita", - )))] - 'poll: { - use crate::sys::os::errno; - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::open as open64; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::open64; - 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, - #[cfg(target_vendor = "unikraft")] - libc::ENOSYS => { - // Not all configurations of Unikraft enable `LIBPOSIX_EVENT`. - break 'poll; - } - 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 open64(c"/dev/null".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", - target_os = "vita", - )))] - { - use crate::sys::os::errno; - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::open as open64; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::open64; - for fd in 0..3 { - if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF { - if open64(c"/dev/null".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(#[allow(unused_variables)] sigpipe: u8) { - #[cfg(not(any( - target_os = "emscripten", - target_os = "fuchsia", - target_os = "horizon", - // Unikraft's `signal` implementation is currently broken: - // https://github.com/unikraft/lib-musl/issues/57 - target_vendor = "unikraft", - )))] - { - // We don't want to add this as a public type to std, nor do we - // want to `include!` a file from the compiler (which would break - // Miri and xargo for example), so we choose to duplicate these - // constants from `compiler/rustc_session/src/config/sigpipe.rs`. - // See the other file for docs. NOTE: Make sure to keep them in - // sync! - mod sigpipe { - pub const DEFAULT: u8 = 0; - pub const INHERIT: u8 = 1; - pub const SIG_IGN: u8 = 2; - pub const SIG_DFL: u8 = 3; - } - - let (sigpipe_attr_specified, handler) = match sigpipe { - sigpipe::DEFAULT => (false, Some(libc::SIG_IGN)), - sigpipe::INHERIT => (true, None), - sigpipe::SIG_IGN => (true, Some(libc::SIG_IGN)), - sigpipe::SIG_DFL => (true, Some(libc::SIG_DFL)), - _ => unreachable!(), - }; - if sigpipe_attr_specified { - UNIX_SIGPIPE_ATTR_SPECIFIED.store(true, crate::sync::atomic::Ordering::Relaxed); - } - if let Some(handler) = handler { - rtassert!(signal(libc::SIGPIPE, handler) != libc::SIG_ERR); - #[cfg(target_os = "hurd")] - { - rtassert!(signal(libc::SIGLOST, handler) != libc::SIG_ERR); - } - } - } - } -} - -// This is set (up to once) in reset_sigpipe. -#[cfg(not(any( - target_os = "espidf", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "horizon", -)))] -static UNIX_SIGPIPE_ATTR_SPECIFIED: crate::sync::atomic::AtomicBool = - crate::sync::atomic::AtomicBool::new(false); - -#[cfg(not(any( - target_os = "espidf", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "horizon", -)))] -pub(crate) fn unix_sigpipe_attr_specified() -> bool { - UNIX_SIGPIPE_ATTR_SPECIFIED.load(crate::sync::atomic::Ordering::Relaxed) -} - -// 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; -#[allow(unused_imports)] -#[cfg(not(target_os = "android"))] -pub use libc::signal; - -#[inline] -pub(crate) fn is_interrupted(errno: i32) -> bool { - errno == libc::EINTR -} - -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, - #[cfg(not(target_os = "aix"))] - 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: T) -> crate::io::Result { - if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } -} - -pub fn cvt_r(mut f: F) -> crate::io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - loop { - match cvt(f()) { - Err(ref e) if e.is_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", kind = "static", modifiers = "-bundle", - cfg(target_feature = "crt-static"))] - #[link(name = "dl", cfg(not(target_feature = "crt-static")))] - #[link(name = "log", cfg(not(target_feature = "crt-static")))] - 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")] - extern "C" {} - } else if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] { - #[link(name = "System")] - #[link(name = "objc")] - #[link(name = "Foundation", kind = "framework")] - 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" {} - } else if #[cfg(target_os = "vita")] { - #[link(name = "pthread", kind = "static", modifiers = "-bundle")] - extern "C" {} - } -} - -#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] -mod unsupported { - use crate::io; - - pub fn unsupported() -> io::Result { - 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 deleted file mode 100644 index ec861f9cb86..00000000000 --- a/library/std/src/sys/unix/net.rs +++ /dev/null @@ -1,591 +0,0 @@ -use crate::cmp; -use crate::ffi::CStr; -use crate::io::{self, BorrowedBuf, BorrowedCursor, 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::unix::IsMinusOne; -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 { - 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 { - unsafe { - cfg_if::cfg_if! { - if #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "illumos", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "nto", - ))] { - // 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 = "hurd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "nto", - ))] { - // 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(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - loop { - let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) }; - if result.is_minus_one() { - let err = crate::sys::os::errno(); - match err { - libc::EINTR => continue, - libc::EISCONN => return Ok(()), - _ => return Err(io::Error::from_raw_os_error(err)), - } - } - return Ok(()); - } - } - - 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.is_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 { - // 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 = "hurd", - 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 { - self.0.duplicate().map(Socket) - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { - let ret = cvt(unsafe { - libc::recv( - self.as_raw_fd(), - buf.as_mut().as_mut_ptr() as *mut c_void, - buf.capacity(), - flags, - ) - })?; - unsafe { - buf.advance(ret as usize); - } - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - 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 { - 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 { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - 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 { - let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?; - Ok(n as usize) - } - - pub fn set_timeout(&self, dur: Option, 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> { - 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) -> 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> { - 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 { - 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_quickack(&self, quickack: bool) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) - } - - #[cfg(any(target_os = "android", target_os = "linux",))] - pub fn quickack(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)?; - 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 { - 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 { - let passcred: libc::c_int = getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)?; - Ok(passcred != 0) - } - - #[cfg(target_os = "freebsd")] - pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { - setsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT, passcred as libc::c_int) - } - - #[cfg(target_os = "freebsd")] - pub fn passcred(&self) -> io::Result { - let passcred: libc::c_int = getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?; - Ok(passcred != 0) - } - - #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "vita")))] - 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(target_os = "vita")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let option = nonblocking as libc::c_int; - setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) - } - - #[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) - } - - #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] - pub fn set_mark(&self, mark: u32) -> io::Result<()> { - #[cfg(target_os = "linux")] - let option = libc::SO_MARK; - #[cfg(target_os = "freebsd")] - let option = libc::SO_USER_COOKIE; - #[cfg(target_os = "openbsd")] - let option = libc::SO_RTABLE; - setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) - } - - pub fn take_error(&self) -> io::Result> { - 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 for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -impl FromInner 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 { - #[inline] - 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 std 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 deleted file mode 100644 index 881b3a25c51..00000000000 --- a/library/std/src/sys/unix/os.rs +++ /dev/null @@ -1,783 +0,0 @@ -//! 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::sync::{PoisonError, RwLock}; -use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; -use crate::sys::cvt; -use crate::sys::fd; -use crate::sys::memchr; -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", - target_os = "hurd", - ), - 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(target_os = "nto", link_name = "__get_errno_ptr")] - #[cfg_attr( - any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "freebsd", - target_os = "watchos" - ), - link_name = "__error" - )] - #[cfg_attr(target_os = "haiku", link_name = "_errnop")] - #[cfg_attr(target_os = "aix", link_name = "_Errno")] - 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( - all( - any(target_os = "linux", target_os = "hurd", target_env = "newlib"), - not(target_env = "ohos") - ), - 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 _; - // We can't always expect a UTF-8 environment. When we don't get that luxury, - // it's better to give a low-quality error message than none at all. - String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into() - } -} - -#[cfg(target_os = "espidf")] -pub fn getcwd() -> io::Result { - Ok(PathBuf::from("/")) -} - -#[cfg(not(target_os = "espidf"))] -pub fn getcwd() -> io::Result { - 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 result = run_path_with_cstr(p, |p| unsafe { Ok(libc::chdir(p.as_ptr())) })?; - if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) } -} - -pub struct SplitPaths<'a> { - iter: iter::Map bool>, fn(&'a [u8]) -> PathBuf>, -} - -pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { - fn bytes_to_path(b: &[u8]) -> PathBuf { - PathBuf::from(::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 { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[derive(Debug)] -pub struct JoinPathsError; - -pub fn join_paths(paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - 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(target_os = "aix")] -pub fn current_exe() -> io::Result { - 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::NotFound, - "an executable path was not found because no arguments were provided through argv" - ))?; - let path = PathBuf::from(exe_path); - if path.is_absolute() { - return path.canonicalize(); - } - // Search PWD to infer current_exe. - if let Some(pstr) = path.to_str() - && pstr.contains("/") - { - return getcwd().map(|cwd| cwd.join(path))?.canonicalize(); - } - // Search PATH to infer current_exe. - if let Some(p) = getenv(OsStr::from_bytes("PATH".as_bytes())) { - for search_path in split_paths(&p) { - let pb = search_path.join(&path); - if pb.is_file() - && let Ok(metadata) = crate::fs::metadata(&pb) - && metadata.permissions().mode() & 0o111 != 0 - { - return pb.canonicalize(); - } - } - } - Err(io::const_io_error!(ErrorKind::NotFound, "an executable path was not found")) -} - -#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] -pub fn current_exe() -> io::Result { - 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 = 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 { - fn sysctl() -> io::Result { - 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 = 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 { - 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 { - 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 = "hurd", - target_os = "android", - target_os = "emscripten" -))] -pub fn current_exe() -> io::Result { - 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(target_os = "nto")] -pub fn current_exe() -> io::Result { - let mut e = crate::fs::read("/proc/self/exefile")?; - // Current versions of QNX Neutrino provide a null-terminated path. - // Ensure the trailing null byte is not returned here. - if let Some(0) = e.last() { - e.pop(); - } - Ok(PathBuf::from(OsString::from_vec(e))) -} - -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] -pub fn current_exe() -> io::Result { - 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 = 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 { - 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(::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 { - unsafe { - let mut info: mem::MaybeUninit = 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::(), - ); - 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 { - crate::fs::read_to_string("sys:exe").map(PathBuf::from) -} - -#[cfg(target_os = "l4re")] -pub fn current_exe() -> io::Result { - use crate::io::ErrorKind; - Err(io::const_io_error!(ErrorKind::Unsupported, "Not yet implemented!")) -} - -#[cfg(target_os = "vxworks")] -pub fn current_exe() -> io::Result { - #[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", target_os = "vita"))] -pub fn current_exe() -> io::Result { - super::unsupported::unsupported() -} - -#[cfg(target_os = "fuchsia")] -pub fn current_exe() -> io::Result { - 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)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -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) { - 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: RwLock<()> = RwLock::new(()); - -pub fn env_read_lock() -> impl Drop { - ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) -} - -/// 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 { - // environment variables with a nul byte can't be set, so their value is - // always None as well - run_with_cstr(k.as_bytes(), |k| { - let _guard = env_read_lock(); - let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - - if v.is_null() { - Ok(None) - } else { - // SAFETY: `v` cannot be mutated while executing this line since we've a read lock - let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); - - Ok(Some(OsStringExt::from_vec(bytes))) - } - }) - .ok() - .flatten() -} - -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), |k| { - run_with_cstr(v.as_bytes(), |v| { - let _guard = ENV_LOCK.write(); - cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) - }) - }) -} - -pub fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), |nbuf| { - let _guard = ENV_LOCK.write(); - cvt(unsafe { 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 { - return crate::env::var_os("HOME").or_else(|| unsafe { fallback() }).map(PathBuf::from); - - #[cfg(any( - target_os = "android", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - target_os = "emscripten", - target_os = "redox", - target_os = "vxworks", - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - ))] - unsafe fn fallback() -> Option { - None - } - #[cfg(not(any( - target_os = "android", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - target_os = "emscripten", - target_os = "redox", - target_os = "vxworks", - target_os = "espidf", - target_os = "horizon", - target_os = "vita", - )))] - unsafe fn fallback() -> Option { - 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::).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 deleted file mode 100644 index efc29955b05..00000000000 --- a/library/std/src/sys/unix/os/tests.rs +++ /dev/null @@ -1,23 +0,0 @@ -#[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 deleted file mode 100644 index 7bd2f656a24..00000000000 --- a/library/std/src/sys/unix/os_str.rs +++ /dev/null @@ -1,288 +0,0 @@ -//! The underlying OsString/OsStr implementation on Unix and many other -//! systems: just a `Vec`/`[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::Utf8Chunks; - -#[cfg(test)] -#[path = "../unix/os_str/tests.rs"] -mod tests; - -#[derive(Hash)] -#[repr(transparent)] -pub struct Buf { - pub inner: Vec, -} - -#[repr(transparent)] -pub struct Slice { - pub inner: [u8], -} - -impl fmt::Debug for Slice { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&Utf8Chunks::new(&self.inner).debug(), f) - } -} - -impl fmt::Display for Slice { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // If we're the empty string then our iterator won't actually yield - // anything, so perform the formatting manually - if self.inner.is_empty() { - return "".fmt(f); - } - - for chunk in Utf8Chunks::new(&self.inner) { - let valid = chunk.valid(); - // If we successfully decoded the whole chunk as a valid string then - // we can return a direct formatting of the string which will also - // respect various formatting flags if possible. - if chunk.invalid().is_empty() { - return valid.fmt(f); - } - - f.write_str(valid)?; - f.write_char(char::REPLACEMENT_CHARACTER)?; - } - Ok(()) - } -} - -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> for Buf { - fn into_inner(self) -> Vec { - self.inner - } -} - -impl AsInner<[u8]> for Buf { - #[inline] - fn as_inner(&self) -> &[u8] { - &self.inner - } -} - -impl Buf { - #[inline] - pub fn into_encoded_bytes(self) -> Vec { - self.inner - } - - #[inline] - pub unsafe fn from_encoded_bytes_unchecked(s: Vec) -> Self { - Self { inner: s } - } - - 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::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 { - unsafe { mem::transmute(self.inner.into_boxed_slice()) } - } - - #[inline] - pub fn from_box(boxed: Box) -> Buf { - let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; - Buf { inner: inner.into_vec() } - } - - #[inline] - pub fn into_arc(&self) -> Arc { - self.as_slice().into_arc() - } - - #[inline] - pub fn into_rc(&self) -> Rc { - self.as_slice().into_rc() - } -} - -impl Slice { - #[inline] - pub fn as_encoded_bytes(&self) -> &[u8] { - &self.inner - } - - #[inline] - pub unsafe fn from_encoded_bytes_unchecked(s: &[u8]) -> &Slice { - unsafe { mem::transmute(s) } - } - - #[inline] - pub fn from_str(s: &str) -> &Slice { - unsafe { Slice::from_encoded_bytes_unchecked(s.as_bytes()) } - } - - pub fn to_str(&self) -> Result<&str, crate::str::Utf8Error> { - str::from_utf8(&self.inner) - } - - 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 { - let boxed: Box<[u8]> = self.inner.into(); - unsafe { mem::transmute(boxed) } - } - - pub fn empty_box() -> Box { - let boxed: Box<[u8]> = Default::default(); - unsafe { mem::transmute(boxed) } - } - - #[inline] - pub fn into_arc(&self) -> Arc { - 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 { - 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 deleted file mode 100644 index e2a99045e41..00000000000 --- a/library/std/src/sys/unix/os_str/tests.rs +++ /dev/null @@ -1,17 +0,0 @@ -use super::*; - -#[test] -fn slice_debug_output() { - let input = unsafe { Slice::from_encoded_bytes_unchecked(b"\xF0hello,\tworld") }; - let expected = r#""\xF0hello,\tworld""#; - let output = format!("{input:?}"); - - assert_eq!(output, expected); -} - -#[test] -fn display() { - assert_eq!("Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye", unsafe { - Slice::from_encoded_bytes_unchecked(b"Hello\xC0\x80 There\xE6\x83 Goodbye").to_string() - },); -} diff --git a/library/std/src/sys/unix/path.rs b/library/std/src/sys/unix/path.rs deleted file mode 100644 index 837f68d3eaf..00000000000 --- a/library/std/src/sys/unix/path.rs +++ /dev/null @@ -1,63 +0,0 @@ -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> { - 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 { - // 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().as_encoded_bytes(); - - let mut normalized = if path.is_absolute() { - // "If a pathname begins with two successive characters, the - // first component following the leading characters may be - // interpreted in an implementation-defined manner, although more than - // two leading characters shall be treated as a single - // 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- character and contains one or more trailing - // characters". - // A trailing 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 deleted file mode 100644 index 33db24e77e4..00000000000 --- a/library/std/src/sys/unix/pipe.rs +++ /dev/null @@ -1,167 +0,0 @@ -use crate::io::{self, BorrowedCursor, 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::{FromInner, 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 = "hurd", - 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 { - self.0.read(buf) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - self.0.read_to_end(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } -} - -impl IntoInner for AnonPipe { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> 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) -> Result { - 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 { - #[inline] - 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)) - } -} - -impl FromInner for AnonPipe { - fn from_inner(fd: FileDesc) -> Self { - Self(fd) - } -} diff --git a/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs deleted file mode 100644 index 074f0a105e3..00000000000 --- a/library/std/src/sys/unix/process/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -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; - -#[cfg_attr(any(target_os = "espidf", target_os = "horizon"), allow(unused))] -mod process_common; - -#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] -mod process_unsupported; - -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", target_os = "vita"))] { - mod process_inner { - pub use super::process_unsupported::*; - } - } 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 deleted file mode 100644 index c5f04fb8b3b..00000000000 --- a/library/std/src/sys/unix/process/process_common.rs +++ /dev/null @@ -1,676 +0,0 @@ -#[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::{FromInner, 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: &CStr = c"null:"; - } else if #[cfg(target_os = "vxworks")] { - const DEV_NULL: &CStr = c"/null"; - } else { - const DEV_NULL: &CStr = c"/dev/null"; - } -} - -// 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")] { - #[allow(dead_code)] - 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::{ - mem::{align_of, size_of}, - slice, - }; - use libc::{c_ulong, sigset_t}; - - // The implementations from bionic (android libc) type pun `sigset_t` as an - // array of `c_ulong`. This works, but lets add a smoke check to make sure - // that doesn't change. - const _: () = assert!( - align_of::() == align_of::() - && (size_of::() % size_of::()) == 0 - ); - - let bit = (signum - 1) as usize; - if set.is_null() || bit >= (8 * size_of::()) { - crate::sys::unix::os::set_errno(libc::EINVAL); - return -1; - } - let raw = slice::from_raw_parts_mut( - set as *mut c_ulong, - size_of::() / size_of::(), - ); - const LONG_BIT: usize = size_of::() * 8; - raw[bit / LONG_BIT] |= 1 << (bit % LONG_BIT); - return 0; - } - } else { - #[allow(unused_imports)] - pub use libc::{sigemptyset, sigaddset}; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -pub struct Command { - program: CString, - args: Vec, - /// 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, - - program_kind: ProgramKind, - cwd: Option, - uid: Option, - gid: Option, - saw_nul: bool, - closures: Vec io::Result<()> + Send + Sync>>, - groups: Option>, - stdin: Option, - stdout: Option, - stderr: Option, - #[cfg(target_os = "linux")] - create_pidfd: bool, - pgroup: Option, -} - -// 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, - pub stdout: Option, - pub stderr: Option, -} - -// 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, -} - -#[derive(Debug)] -pub enum Stdio { - Inherit, - Null, - MakePipe, - Fd(FileDesc), - StaticFd(BorrowedFd<'static>), -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum ProgramKind { - /// A program that would be looked up on the PATH (e.g. `ls`) - PathLookup, - /// A relative path (e.g. `my-dir/foo`, `../foo`, `./foo`) - Relative, - /// An absolute path. - Absolute, -} - -impl ProgramKind { - fn new(program: &OsStr) -> Self { - if program.as_encoded_bytes().starts_with(b"/") { - Self::Absolute - } else if program.as_encoded_bytes().contains(&b'/') { - // If the program has more than one component in it, it is a relative path. - Self::Relative - } else { - Self::PathLookup - } - } -} - -impl Command { - #[cfg(not(target_os = "linux"))] - pub fn new(program: &OsStr) -> Command { - let mut saw_nul = false; - let program_kind = ProgramKind::new(program.as_ref()); - let program = os2c(program, &mut saw_nul); - Command { - argv: Argv(vec![program.as_ptr(), ptr::null()]), - args: vec![program.clone()], - program, - program_kind, - 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_kind = ProgramKind::new(program.as_ref()); - let program = os2c(program, &mut saw_nul); - Command { - argv: Argv(vec![program.as_ptr(), ptr::null()]), - args: vec![program.clone()], - program, - program_kind, - 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()) - } - - #[allow(dead_code)] - pub fn get_program_kind(&self) -> ProgramKind { - self.program_kind - } - - 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 { - &self.cwd - } - #[allow(dead_code)] - pub fn get_uid(&self) -> Option { - self.uid - } - #[allow(dead_code)] - pub fn get_gid(&self) -> Option { - 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 { - self.pgroup - } - - pub fn get_closures(&mut self) -> &mut Vec io::Result<()> + Send + Sync>> { - &mut self.closures - } - - pub unsafe fn pre_exec(&mut self, f: Box 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 { - 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("").unwrap() - }) -} - -// Helper type to manage ownership of the strings within a C-style array. -pub struct CStringArray { - items: Vec, - 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, 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)> { - 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::StaticFd(fd) => { - let fd = FileDesc::from_inner(fd.try_clone_to_owned()?); - Ok((ChildStdio::Owned(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 fd = File::open_c(DEV_NULL, &opts)?; - Ok((ChildStdio::Owned(fd.into_inner()), None)) - } - - #[cfg(target_os = "fuchsia")] - Stdio::Null => Ok((ChildStdio::Null, None)), - } - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - Stdio::Fd(pipe.into_inner()) - } -} - -impl From for Stdio { - fn from(file: File) -> Stdio { - Stdio::Fd(file.into_inner()) - } -} - -impl From for Stdio { - fn from(_: io::Stdout) -> Stdio { - // This ought really to be is Stdio::StaticFd(input_argument.as_fd()). - // But AsFd::as_fd takes its argument by reference, and yields - // a bounded lifetime, so it's no use here. There is no AsStaticFd. - // - // Additionally AsFd is only implemented for the *locked* versions. - // We don't want to lock them here. (The implications of not locking - // are the same as those for process::Stdio::inherit().) - // - // Arguably the hypothetical AsStaticFd and AsFd<'static> - // should be implemented for io::Stdout, not just for StdoutLocked. - Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) }) - } -} - -impl From for Stdio { - fn from(_: io::Stderr) -> Stdio { - Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) }) - } -} - -impl ChildStdio { - pub fn fd(&self) -> Option { - 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 { - // show all attributes but `self.closures` which does not implement `Debug` - // and `self.argv` which is not useful for debugging - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { - let mut debug_command = f.debug_struct("Command"); - debug_command.field("program", &self.program).field("args", &self.args); - if !self.env.is_unchanged() { - debug_command.field("env", &self.env); - } - - if self.cwd.is_some() { - debug_command.field("cwd", &self.cwd); - } - if self.uid.is_some() { - debug_command.field("uid", &self.uid); - } - if self.gid.is_some() { - debug_command.field("gid", &self.gid); - } - - if self.groups.is_some() { - debug_command.field("groups", &self.groups); - } - - if self.stdin.is_some() { - debug_command.field("stdin", &self.stdin); - } - if self.stdout.is_some() { - debug_command.field("stdout", &self.stdout); - } - if self.stderr.is_some() { - debug_command.field("stderr", &self.stderr); - } - if self.pgroup.is_some() { - debug_command.field("pgroup", &self.pgroup); - } - - #[cfg(target_os = "linux")] - { - debug_command.field("create_pidfd", &self.create_pidfd); - } - - debug_command.finish() - } else { - if let Some(ref cwd) = self.cwd { - write!(f, "cd {cwd:?} && ")?; - } - if self.env.does_clear() { - write!(f, "env -i ")?; - // Altered env vars will be printed next, that should exactly work as expected. - } else { - // Removed env vars need the command to be wrapped in `env`. - let mut any_removed = false; - for (key, value_opt) in self.get_envs() { - if value_opt.is_none() { - if !any_removed { - write!(f, "env ")?; - any_removed = true; - } - write!(f, "-u {} ", key.to_string_lossy())?; - } - } - } - // Altered env vars can just be added in front of the program. - for (key, value_opt) in self.get_envs() { - if let Some(value) = value_opt { - write!(f, "{}={value:?} ", key.to_string_lossy())?; - } - } - 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 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) { - 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 deleted file mode 100644 index 4e41efc9096..00000000000 --- a/library/std/src/sys/unix/process/process_common/tests.rs +++ /dev/null @@ -1,194 +0,0 @@ -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() { - // Test to make sure that a signal mask *does* get inherited. - fn test_inner(mut cmd: Command) { - unsafe { - let mut set = mem::MaybeUninit::::uninit(); - let mut old_set = mem::MaybeUninit::::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); - - // Exactly 5 bytes should be read. - let mut buf = [0; 5]; - let ret = t!(stdout_read.read(&mut buf)); - assert_eq!(ret, 5); - assert_eq!(&buf, b"Hello"); - - t!(cat.wait()); - } - } - - // A plain `Command::new` uses the posix_spawn path on many platforms. - let cmd = Command::new(OsStr::new("cat")); - test_inner(cmd); - - // Specifying `pre_exec` forces the fork/exec path. - let mut cmd = Command::new(OsStr::new("cat")); - unsafe { cmd.pre_exec(Box::new(|| Ok(()))) }; - test_inner(cmd); -} - -#[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()); - } -} - -#[test] -fn test_program_kind() { - let vectors = &[ - ("foo", ProgramKind::PathLookup), - ("foo.out", ProgramKind::PathLookup), - ("./foo", ProgramKind::Relative), - ("../foo", ProgramKind::Relative), - ("dir/foo", ProgramKind::Relative), - // Note that paths on Unix can't contain / in them, so this is actually the directory "fo\\" - // followed by the file "o". - ("fo\\/o", ProgramKind::Relative), - ("/foo", ProgramKind::Absolute), - ("/dir/../foo", ProgramKind::Absolute), - ]; - - for (program, expected_kind) in vectors { - assert_eq!( - ProgramKind::new(program.as_ref()), - *expected_kind, - "actual != expected program kind for input {program}", - ); - } -} - -// Test that Rust std handles wait status values (`ExitStatus`) the way that Unix does, -// at least for the values which represent a Unix exit status (`ExitCode`). -// Should work on every #[cfg(unix)] platform. However: -#[cfg(not(any( - // Fuchsia is not Unix and has totally broken std::os::unix. - // https://github.com/rust-lang/rust/issues/58590#issuecomment-836535609 - target_os = "fuchsia", -)))] -#[test] -fn unix_exit_statuses() { - use crate::num::NonZeroI32; - use crate::os::unix::process::ExitStatusExt; - use crate::process::*; - - for exit_code in 0..=0xff { - // FIXME impl From for ExitStatus and then test that here too; - // the two ExitStatus values should be the same - let raw_wait_status = exit_code << 8; - let exit_status = ExitStatus::from_raw(raw_wait_status); - - assert_eq!(exit_status.code(), Some(exit_code)); - - if let Ok(nz) = NonZeroI32::try_from(exit_code) { - assert!(!exit_status.success()); - let es_error = exit_status.exit_ok().unwrap_err(); - assert_eq!(es_error.code().unwrap(), i32::from(nz)); - } else { - assert!(exit_status.success()); - assert_eq!(exit_status.exit_ok(), Ok(())); - } - } -} diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs deleted file mode 100644 index 9931c2af2f1..00000000000 --- a/library/std/src/sys/unix/process/process_fuchsia.rs +++ /dev/null @@ -1,330 +0,0 @@ -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 output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; - crate::sys_common::process::wait_with_output(proc, pipes) - } - - 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 { - 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 { - 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 { - 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::(), - &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> { - 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::(), - &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, Default)] -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 { - // FIXME: support extracting return code as an i64 - self.0.try_into().ok() - } - - pub fn signal(&self) -> Option { - 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 { - 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 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 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 for ExitStatusError { - fn into(self) -> ExitStatus { - ExitStatus(self.0.into()) - } -} - -impl ExitStatusError { - pub fn code(self) -> Option { - // 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 deleted file mode 100644 index ee86a5f88dd..00000000000 --- a/library/std/src/sys/unix/process/process_unix.rs +++ /dev/null @@ -1,1141 +0,0 @@ -use crate::fmt; -use crate::io::{self, Error, ErrorKind}; -use crate::mem; -use crate::num::NonZeroI32; -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::os::unix::io::AsRawFd; - -#[cfg(any( - target_os = "macos", - target_os = "watchos", - target_os = "tvos", - target_os = "freebsd", - all(target_os = "linux", target_env = "gnu"), - all(target_os = "linux", target_env = "musl"), - target_os = "nto", -))] -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", - target_os = "tvos", - target_os = "watchos", -)))] -use libc::{gid_t, uid_t}; - -cfg_if::cfg_if! { - if #[cfg(all(target_os = "nto", target_env = "nto71"))] { - use crate::thread; - use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; - use crate::time::Duration; - use crate::sync::LazyLock; - // Get smallest amount of time we can sleep. - // Return a common value if it cannot be determined. - fn get_clock_resolution() -> Duration { - static MIN_DELAY: LazyLock Duration> = LazyLock::new(|| { - let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 }; - if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0 - { - Duration::from_nanos(mindelay.tv_nsec as u64) - } else { - Duration::from_millis(1) - } - }); - *MIN_DELAY - } - // Arbitrary minimum sleep duration for retrying fork/spawn - const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1); - // Maximum duration of sleeping before giving up and returning an error - const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// 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)); - } - - #[cfg(target_os = "linux")] - let (input, output) = sys::net::Socket::new_pair(libc::AF_UNIX, libc::SOCK_SEQPACKET)?; - - #[cfg(not(target_os = "linux"))] - 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 immediately. - // The child calls `mem::forget` to leak the lock, which is crucial because - // releasing a lock is not async-signal-safe. - let env_lock = sys::os::env_read_lock(); - let pid = unsafe { self.do_fork()? }; - - if pid == 0 { - crate::panic::always_abort(); - mem::forget(env_lock); // avoid non-async-signal-safe unlocking - drop(input); - #[cfg(target_os = "linux")] - if self.get_create_pidfd() { - self.send_pidfd(&output); - } - 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); - - #[cfg(target_os = "linux")] - let pidfd = if self.get_create_pidfd() { self.recv_pidfd(&input) } else { -1 }; - - #[cfg(not(target_os = "linux"))] - let pidfd = -1; - - // 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.is_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 - // similarly SOCK_SEQPACKET messages should arrive whole - assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); - panic!("short read on the CLOEXEC pipe") - } - } - } - } - - pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; - crate::sys_common::process::wait_with_output(proc, pipes) - } - - // WatchOS and TVOS headers mark the `fork`/`exec*` functions with - // `__WATCHOS_PROHIBITED __TVOS_PROHIBITED`, and indicate that the - // `posix_spawn*` functions should be used instead. It isn't entirely clear - // what `PROHIBITED` means here (e.g. if calls to these functions are - // allowed to exist in dead code), but it sounds bad, so we go out of our - // way to avoid that all-together. - #[cfg(any(target_os = "tvos", target_os = "watchos"))] - const ERR_APPLE_TV_WATCH_NO_FORK_EXEC: Error = io::const_io_error!( - ErrorKind::Unsupported, - "`fork`+`exec`-based process spawning is not supported on this target", - ); - - #[cfg(any(target_os = "tvos", target_os = "watchos"))] - unsafe fn do_fork(&mut self) -> Result { - return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); - } - - // Attempts to fork the process. If successful, returns Ok((0, -1)) - // in the child, and Ok((child_pid, -1)) in the parent. - #[cfg(not(any( - target_os = "watchos", - target_os = "tvos", - all(target_os = "nto", target_env = "nto71"), - )))] - unsafe fn do_fork(&mut self) -> Result { - cvt(libc::fork()) - } - - // On QNX Neutrino, fork can fail with EBADF in case "another thread might have opened - // or closed a file descriptor while the fork() was occurring". - // Documentation says "... or try calling fork() again". This is what we do here. - // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html - #[cfg(all(target_os = "nto", target_env = "nto71"))] - unsafe fn do_fork(&mut self) -> Result { - use crate::sys::os::errno; - - let mut delay = MIN_FORKSPAWN_SLEEP; - - loop { - let r = libc::fork(); - if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF { - if delay < get_clock_resolution() { - // We cannot sleep this short (it would be longer). - // Yield instead. - thread::yield_now(); - } else if delay < MAX_FORKSPAWN_SLEEP { - thread::sleep(delay); - } else { - return Err(io::const_io_error!( - ErrorKind::WouldBlock, - "forking returned EBADF too often", - )); - } - delay *= 2; - continue; - } else { - return cvt(r); - } - } - } - - 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) - #[cfg(not(any(target_os = "tvos", target_os = "watchos")))] - unsafe fn do_exec( - &mut self, - stdio: ChildPipes, - maybe_envp: Option<&CStringArray>, - ) -> Result { - 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, crate::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"))] - { - // Inherit the signal mask from the parent rather than resetting it (i.e. do not call - // pthread_sigmask). - - // If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL. - // If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility. - // - // #[unix_sigpipe] is an opportunity to change the default here. - if !crate::sys::unix_sigpipe_attr_specified() { - #[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, crate::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()); - } - } - #[cfg(target_os = "hurd")] - { - let ret = sys::signal(libc::SIGLOST, 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(any(target_os = "tvos", target_os = "watchos"))] - unsafe fn do_exec( - &mut self, - _stdio: ChildPipes, - _maybe_envp: Option<&CStringArray>, - ) -> Result { - return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); - } - - #[cfg(not(any( - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - target_os = "freebsd", - all(target_os = "linux", target_env = "gnu"), - all(target_os = "linux", target_env = "musl"), - target_os = "nto", - )))] - fn posix_spawn( - &mut self, - _: &ChildPipes, - _: Option<&CStringArray>, - ) -> io::Result> { - Ok(None) - } - - // Only support platforms for which posix_spawn() can return ENOENT - // directly. - #[cfg(any( - target_os = "macos", - // FIXME: `target_os = "ios"`? - target_os = "tvos", - target_os = "watchos", - target_os = "freebsd", - all(target_os = "linux", target_env = "gnu"), - all(target_os = "linux", target_env = "musl"), - target_os = "nto", - ))] - fn posix_spawn( - &mut self, - stdio: &ChildPipes, - envp: Option<&CStringArray>, - ) -> io::Result> { - use crate::mem::MaybeUninit; - use crate::sys::{self, cvt_nz, unix_sigpipe_attr_specified}; - - 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); - } - } - - // On QNX Neutrino, posix_spawnp can fail with EBADF in case "another thread might have opened - // or closed a file descriptor while the posix_spawn() was occurring". - // Documentation says "... or try calling posix_spawn() again". This is what we do here. - // See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html - #[cfg(all(target_os = "nto", target_env = "nto71"))] - unsafe fn retrying_libc_posix_spawnp( - pid: *mut pid_t, - file: *const c_char, - file_actions: *const posix_spawn_file_actions_t, - attrp: *const posix_spawnattr_t, - argv: *const *mut c_char, - envp: *const *mut c_char, - ) -> io::Result { - let mut delay = MIN_FORKSPAWN_SLEEP; - loop { - match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) { - libc::EBADF => { - if delay < get_clock_resolution() { - // We cannot sleep this short (it would be longer). - // Yield instead. - thread::yield_now(); - } else if delay < MAX_FORKSPAWN_SLEEP { - thread::sleep(delay); - } else { - return Err(io::const_io_error!( - ErrorKind::WouldBlock, - "posix_spawnp returned EBADF too often", - )); - } - delay *= 2; - continue; - } - r => { - return Ok(r); - } - } - } - } - - // 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!(any(target_os = "macos", target_os = "tvos", target_os = "watchos")) { - // 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. - if self.get_program_kind() == ProgramKind::Relative { - 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); - - 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); - - 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))?; - } - - // Inherit the signal mask from this process rather than resetting it (i.e. do not call - // posix_spawnattr_setsigmask). - - // If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL. - // If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility. - // - // #[unix_sigpipe] is an opportunity to change the default here. - if !unix_sigpipe_attr_specified() { - let mut default_set = MaybeUninit::::uninit(); - cvt(sigemptyset(default_set.as_mut_ptr()))?; - cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGPIPE))?; - #[cfg(target_os = "hurd")] - { - cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGLOST))?; - } - cvt_nz(libc::posix_spawnattr_setsigdefault( - attrs.0.as_mut_ptr(), - default_set.as_ptr(), - ))?; - flags |= libc::POSIX_SPAWN_SETSIGDEF; - } - - 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 _); - - #[cfg(not(target_os = "nto"))] - let spawn_fn = libc::posix_spawnp; - #[cfg(target_os = "nto")] - let spawn_fn = retrying_libc_posix_spawnp; - - let spawn_res = spawn_fn( - &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 _, - ); - - #[cfg(target_os = "nto")] - let spawn_res = spawn_res?; - - cvt_nz(spawn_res)?; - Ok(Some(p)) - } - } - - #[cfg(target_os = "linux")] - fn send_pidfd(&self, sock: &crate::sys::net::Socket) { - use crate::io::IoSlice; - use crate::os::fd::RawFd; - use crate::sys::cvt_r; - use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; - - unsafe { - let child_pid = libc::getpid(); - // pidfd_open sets CLOEXEC by default - let pidfd = libc::syscall(libc::SYS_pidfd_open, child_pid, 0); - - let fds: [c_int; 1] = [pidfd as RawFd]; - - const SCM_MSG_LEN: usize = mem::size_of::<[c_int; 1]>(); - - #[repr(C)] - union Cmsg { - buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }], - _align: libc::cmsghdr, - } - - let mut cmsg: Cmsg = mem::zeroed(); - - // 0-length message to send through the socket so we can pass along the fd - let mut iov = [IoSlice::new(b"")]; - let mut msg: libc::msghdr = mem::zeroed(); - - msg.msg_iov = &mut iov as *mut _ as *mut _; - msg.msg_iovlen = 1; - - // only attach cmsg if we successfully acquired the pidfd - if pidfd >= 0 { - msg.msg_controllen = mem::size_of_val(&cmsg.buf) as _; - msg.msg_control = &mut cmsg.buf as *mut _ as *mut _; - - let hdr = CMSG_FIRSTHDR(&mut msg as *mut _ as *mut _); - (*hdr).cmsg_level = SOL_SOCKET; - (*hdr).cmsg_type = SCM_RIGHTS; - (*hdr).cmsg_len = CMSG_LEN(SCM_MSG_LEN as _) as _; - let data = CMSG_DATA(hdr); - crate::ptr::copy_nonoverlapping( - fds.as_ptr().cast::(), - data as *mut _, - SCM_MSG_LEN, - ); - } - - // we send the 0-length message even if we failed to acquire the pidfd - // so we get a consistent SEQPACKET order - match cvt_r(|| libc::sendmsg(sock.as_raw(), &msg, 0)) { - Ok(0) => {} - other => rtabort!("failed to communicate with parent process. {:?}", other), - } - } - } - - #[cfg(target_os = "linux")] - fn recv_pidfd(&self, sock: &crate::sys::net::Socket) -> pid_t { - use crate::io::IoSliceMut; - use crate::sys::cvt_r; - - use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; - - unsafe { - const SCM_MSG_LEN: usize = mem::size_of::<[c_int; 1]>(); - - #[repr(C)] - union Cmsg { - _buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }], - _align: libc::cmsghdr, - } - let mut cmsg: Cmsg = mem::zeroed(); - // 0-length read to get the fd - let mut iov = [IoSliceMut::new(&mut [])]; - - let mut msg: libc::msghdr = mem::zeroed(); - - msg.msg_iov = &mut iov as *mut _ as *mut _; - msg.msg_iovlen = 1; - msg.msg_controllen = mem::size_of::() as _; - msg.msg_control = &mut cmsg as *mut _ as *mut _; - - match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, libc::MSG_CMSG_CLOEXEC)) { - Err(_) => return -1, - Ok(_) => {} - } - - let hdr = CMSG_FIRSTHDR(&mut msg as *mut _ as *mut _); - if hdr.is_null() - || (*hdr).cmsg_level != SOL_SOCKET - || (*hdr).cmsg_type != SCM_RIGHTS - || (*hdr).cmsg_len != CMSG_LEN(SCM_MSG_LEN as _) as _ - { - return -1; - } - let data = CMSG_DATA(hdr); - - let mut fds = [-1 as c_int]; - - crate::ptr::copy_nonoverlapping( - data as *const _, - fds.as_mut_ptr().cast::(), - SCM_MSG_LEN, - ); - - fds[0] - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Processes -//////////////////////////////////////////////////////////////////////////////// - -/// The unique ID of the process (this should never be negative). -pub struct Process { - pid: pid_t, - status: Option, - // 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 `pidfd_open` syscall was not available). - #[cfg(target_os = "linux")] - pidfd: Option, -} - -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 return Ok because the process has exited already. - if self.status.is_some() { - return Ok(()); - } - #[cfg(target_os = "linux")] - if let Some(pid_fd) = self.pidfd.as_ref() { - // pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too - return cvt(unsafe { - libc::syscall( - libc::SYS_pidfd_send_signal, - pid_fd.as_raw_fd(), - libc::SIGKILL, - crate::ptr::null::<()>(), - 0, - ) - }) - .map(drop); - } - cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) - } - - pub fn wait(&mut self) -> io::Result { - use crate::sys::cvt_r; - if let Some(status) = self.status { - return Ok(status); - } - #[cfg(target_os = "linux")] - if let Some(pid_fd) = self.pidfd.as_ref() { - let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; - - cvt_r(|| unsafe { - libc::waitid(libc::P_PIDFD, pid_fd.as_raw_fd() as u32, &mut siginfo, libc::WEXITED) - })?; - let status = ExitStatus::from_waitid_siginfo(siginfo); - self.status = Some(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> { - if let Some(status) = self.status { - return Ok(Some(status)); - } - #[cfg(target_os = "linux")] - if let Some(pid_fd) = self.pidfd.as_ref() { - let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; - - cvt(unsafe { - libc::waitid( - libc::P_PIDFD, - pid_fd.as_raw_fd() as u32, - &mut siginfo, - libc::WEXITED | libc::WNOHANG, - ) - })?; - if unsafe { siginfo.si_pid() } == 0 { - return Ok(None); - } - let status = ExitStatus::from_waitid_siginfo(siginfo); - self.status = Some(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, Default)] -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) - } - - #[cfg(target_os = "linux")] - pub fn from_waitid_siginfo(siginfo: libc::siginfo_t) -> ExitStatus { - let status = unsafe { siginfo.si_status() }; - - match siginfo.si_code { - libc::CLD_EXITED => ExitStatus((status & 0xff) << 8), - libc::CLD_KILLED => ExitStatus(status), - libc::CLD_DUMPED => ExitStatus(status | 0x80), - libc::CLD_CONTINUED => ExitStatus(0xffff), - libc::CLD_STOPPED | libc::CLD_TRAPPED => ExitStatus(((status & 0xff) << 8) | 0x7f), - _ => unreachable!("waitid() should only return the above codes"), - } - } - - 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 - // process_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 { - self.exited().then(|| libc::WEXITSTATUS(self.0)) - } - - pub fn signal(&self) -> Option { - 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 { - 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 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)", - #[cfg(not(target_os = "l4re"))] - libc::SIGBUS => " (SIGBUS)", - libc::SIGFPE => " (SIGFPE)", - libc::SIGKILL => " (SIGKILL)", - #[cfg(not(target_os = "l4re"))] - libc::SIGUSR1 => " (SIGUSR1)", - libc::SIGSEGV => " (SIGSEGV)", - #[cfg(not(target_os = "l4re"))] - libc::SIGUSR2 => " (SIGUSR2)", - libc::SIGPIPE => " (SIGPIPE)", - libc::SIGALRM => " (SIGALRM)", - libc::SIGTERM => " (SIGTERM)", - #[cfg(not(target_os = "l4re"))] - libc::SIGCHLD => " (SIGCHLD)", - #[cfg(not(target_os = "l4re"))] - libc::SIGCONT => " (SIGCONT)", - #[cfg(not(target_os = "l4re"))] - libc::SIGSTOP => " (SIGSTOP)", - #[cfg(not(target_os = "l4re"))] - libc::SIGTSTP => " (SIGTSTP)", - #[cfg(not(target_os = "l4re"))] - libc::SIGTTIN => " (SIGTTIN)", - #[cfg(not(target_os = "l4re"))] - libc::SIGTTOU => " (SIGTTOU)", - #[cfg(not(target_os = "l4re"))] - libc::SIGURG => " (SIGURG)", - #[cfg(not(target_os = "l4re"))] - libc::SIGXCPU => " (SIGXCPU)", - #[cfg(not(target_os = "l4re"))] - libc::SIGXFSZ => " (SIGXFSZ)", - #[cfg(not(target_os = "l4re"))] - libc::SIGVTALRM => " (SIGVTALRM)", - #[cfg(not(target_os = "l4re"))] - libc::SIGPROF => " (SIGPROF)", - #[cfg(not(target_os = "l4re"))] - libc::SIGWINCH => " (SIGWINCH)", - #[cfg(not(any(target_os = "haiku", target_os = "l4re")))] - libc::SIGIO => " (SIGIO)", - #[cfg(target_os = "haiku")] - libc::SIGPOLL => " (SIGPOLL)", - #[cfg(not(target_os = "l4re"))] - 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(any(target_os = "linux", target_os = "nto"))] - 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", - target_os = "nto", - ))] - 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)", - #[cfg(target_os = "hurd")] - libc::SIGLOST => " (SIGLOST)", - _ => "", - } -} - -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 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 { - 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::Uncategorized, "No pidfd was created.")) - } - - fn take_pidfd(&mut self) -> io::Result { - self.handle - .pidfd - .take() - .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created.")) - } -} - -#[cfg(test)] -#[path = "process_unix/tests.rs"] -mod tests; - -// See [`process_unsupported_wait_status::compare_with_linux`]; -#[cfg(all(test, target_os = "linux"))] -#[path = "process_unsupported/wait_status.rs"] -mod process_unsupported_wait_status; diff --git a/library/std/src/sys/unix/process/process_unix/tests.rs b/library/std/src/sys/unix/process/process_unix/tests.rs deleted file mode 100644 index 6e952ed7c42..00000000000 --- a/library/std/src/sys/unix/process/process_unix/tests.rs +++ /dev/null @@ -1,100 +0,0 @@ -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 tests/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!("{}", ::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 - ); -} - -#[test] -#[cfg(target_os = "linux")] -fn test_command_pidfd() { - use crate::assert_matches::assert_matches; - use crate::os::fd::{AsRawFd, RawFd}; - use crate::os::linux::process::{ChildExt, CommandExt}; - use crate::process::Command; - - let our_pid = crate::process::id(); - let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) }; - let pidfd_open_available = if pidfd >= 0 { - unsafe { libc::close(pidfd as RawFd) }; - true - } else { - false - }; - - // always exercise creation attempts - let mut child = Command::new("false").create_pidfd(true).spawn().unwrap(); - - // but only check if we know that the kernel supports pidfds - if pidfd_open_available { - assert!(child.pidfd().is_ok()); - } - if let Ok(pidfd) = child.pidfd() { - let flags = super::cvt(unsafe { libc::fcntl(pidfd.as_raw_fd(), libc::F_GETFD) }).unwrap(); - assert!(flags & libc::FD_CLOEXEC != 0); - } - let status = child.wait().expect("error waiting on pidfd"); - assert_eq!(status.code(), Some(1)); - - let mut child = Command::new("sleep").arg("1000").create_pidfd(true).spawn().unwrap(); - assert_matches!(child.try_wait(), Ok(None)); - child.kill().expect("failed to kill child"); - let status = child.wait().expect("error waiting on pidfd"); - assert_eq!(status.signal(), Some(libc::SIGKILL)); -} diff --git a/library/std/src/sys/unix/process/process_unsupported.rs b/library/std/src/sys/unix/process/process_unsupported.rs deleted file mode 100644 index 2fbb3192265..00000000000 --- a/library/std/src/sys/unix/process/process_unsupported.rs +++ /dev/null @@ -1,74 +0,0 @@ -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 output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - 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 { - unsupported() - } - - pub fn try_wait(&mut self) -> io::Result> { - unsupported() - } -} - -mod wait_status; -pub use wait_status::ExitStatus; - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitStatusError(NonZero_c_int); - -impl Into for ExitStatusError { - fn into(self) -> ExitStatus { - ExitStatus::from(c_int::from(self.0)) - } -} - -impl ExitStatusError { - pub fn code(self) -> Option { - ExitStatus::from(c_int::from(self.0)).code().map(|st| st.try_into().unwrap()) - } -} diff --git a/library/std/src/sys/unix/process/process_unsupported/wait_status.rs b/library/std/src/sys/unix/process/process_unsupported/wait_status.rs deleted file mode 100644 index 72b7ae18cff..00000000000 --- a/library/std/src/sys/unix/process/process_unsupported/wait_status.rs +++ /dev/null @@ -1,84 +0,0 @@ -//! Emulated wait status for non-Unix #[cfg(unix) platforms -//! -//! Separate module to facilitate testing against a real Unix implementation. -use core::ffi::NonZero_c_int; - -use crate::ffi::c_int; -use crate::fmt; - -use super::ExitStatusError; - -/// Emulated wait status for use by `process_unsupported.rs` -/// -/// Uses the "traditional unix" encoding. For use on platfors which are `#[cfg(unix)]` -/// but do not actually support subprocesses at all. -/// -/// These platforms aren't Unix, but are simply pretending to be for porting convenience. -/// So, we provide a faithful pretence here. -#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] -pub struct ExitStatus { - wait_status: c_int, -} - -/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it -impl From for ExitStatus { - fn from(wait_status: c_int) -> ExitStatus { - ExitStatus { wait_status } - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "emulated wait status: {}", self.wait_status) - } -} - -impl ExitStatus { - pub fn code(&self) -> Option { - // Linux and FreeBSD both agree that values linux 0x80 - // count as "WIFEXITED" even though this is quite mad. - // Likewise the macros disregard all the high bits, so are happy to declare - // out-of-range values to be WIFEXITED, WIFSTOPPED, etc. - let w = self.wait_status; - if (w & 0x7f) == 0 { Some((w & 0xff00) >> 8) } else { None } - } - - #[allow(unused)] - 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 - // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. - match NonZero_c_int::try_from(self.wait_status) { - /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), - /* was zero, couldn't convert */ Err(_) => Ok(()), - } - } - - pub fn signal(&self) -> Option { - let signal = self.wait_status & 0x007f; - if signal > 0 && signal < 0x7f { Some(signal) } else { None } - } - - pub fn core_dumped(&self) -> bool { - self.signal().is_some() && (self.wait_status & 0x80) != 0 - } - - pub fn stopped_signal(&self) -> Option { - let w = self.wait_status; - if (w & 0xff) == 0x7f { Some((w & 0xff00) >> 8) } else { None } - } - - pub fn continued(&self) -> bool { - self.wait_status == 0xffff - } - - pub fn into_raw(&self) -> c_int { - self.wait_status - } -} - -#[cfg(test)] -#[path = "wait_status/tests.rs"] // needed because of strange layout of process_unsupported -mod tests; diff --git a/library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs b/library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs deleted file mode 100644 index 5132eab10a1..00000000000 --- a/library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Note that tests in this file are run on Linux as well as on platforms using process_unsupported - -// Test that our emulation exactly matches Linux -// -// This test runs *on Linux* but it tests -// the implementation used on non-Unix `#[cfg(unix)]` platforms. -// -// I.e. we're using Linux as a proxy for "trad unix". -#[cfg(target_os = "linux")] -#[test] -fn compare_with_linux() { - use super::ExitStatus as Emulated; - use crate::os::unix::process::ExitStatusExt as _; - use crate::process::ExitStatus as Real; - - // Check that we handle out-of-range values similarly, too. - for wstatus in -0xf_ffff..0xf_ffff { - let emulated = Emulated::from(wstatus); - let real = Real::from_raw(wstatus); - - macro_rules! compare { { $method:ident } => { - assert_eq!( - emulated.$method(), - real.$method(), - "{wstatus:#x}.{}()", - stringify!($method), - ); - } } - compare!(code); - compare!(signal); - compare!(core_dumped); - compare!(stopped_signal); - compare!(continued); - compare!(into_raw); - } -} diff --git a/library/std/src/sys/unix/process/process_vxworks.rs b/library/std/src/sys/unix/process/process_vxworks.rs deleted file mode 100644 index 1ff2b2fb383..00000000000 --- a/library/std/src/sys/unix/process/process_vxworks.rs +++ /dev/null @@ -1,264 +0,0 @@ -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 output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; - crate::sys_common::process::wait_with_output(proc, pipes) - } - - 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, -} - -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 return Ok because the process has exited already. - if self.status.is_some() { - Ok(()) - } else { - cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) - } - } - - pub fn wait(&mut self) -> io::Result { - 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> { - 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, Default)] -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 - // process_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 { - if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None } - } - - pub fn signal(&self) -> Option { - 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 { - 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 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 for ExitStatusError { - fn into(self) -> ExitStatus { - ExitStatus(self.0.into()) - } -} - -impl ExitStatusError { - pub fn code(self) -> Option { - 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 deleted file mode 100644 index 2e596486f9c..00000000000 --- a/library/std/src/sys/unix/process/zircon.rs +++ /dev/null @@ -1,309 +0,0 @@ -#![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) -> io::Result -where - T: TryInto + 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 deleted file mode 100644 index cf0fe0f47c5..00000000000 --- a/library/std/src/sys/unix/rand.rs +++ /dev/null @@ -1,301 +0,0 @@ -pub fn hashmap_random_keys() -> (u64, u64) { - const KEY_LEN: usize = core::mem::size_of::(); - - let mut v = [0u8; KEY_LEN * 2]; - imp::fill_bytes(&mut v); - - let key1 = v[0..KEY_LEN].try_into().unwrap(); - let key2 = v[KEY_LEN..].try_into().unwrap(); - - (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) -} - -#[cfg(all( - unix, - not(target_os = "macos"), - not(target_os = "ios"), - not(target_os = "tvos"), - not(target_os = "watchos"), - not(target_os = "openbsd"), - not(target_os = "netbsd"), - not(target_os = "fuchsia"), - not(target_os = "redox"), - not(target_os = "vxworks"), - not(target_os = "emscripten"), - not(target_os = "vita"), -))] -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", target_os = "freebsd"))] - 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", - target_os = "freebsd" - )))] - fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool { - false - } - - #[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "espidf", - target_os = "horizon", - target_os = "freebsd" - ))] - 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_vendor = "apple")] -mod imp { - use crate::io; - use libc::{c_int, c_void, size_t}; - - #[inline(always)] - fn random_failure() -> ! { - panic!("unexpected random generation error: {}", io::Error::last_os_error()); - } - - #[cfg(target_os = "macos")] - fn getentropy_fill_bytes(v: &mut [u8]) { - extern "C" { - fn getentropy(bytes: *mut c_void, count: size_t) -> c_int; - } - - // getentropy(2) permits a maximum buffer size of 256 bytes - for s in v.chunks_mut(256) { - let ret = unsafe { getentropy(s.as_mut_ptr().cast(), s.len()) }; - if ret == -1 { - random_failure() - } - } - } - - #[cfg(not(target_os = "macos"))] - fn ccrandom_fill_bytes(v: &mut [u8]) { - extern "C" { - fn CCRandomGenerateBytes(bytes: *mut c_void, count: size_t) -> c_int; - } - - let ret = unsafe { CCRandomGenerateBytes(v.as_mut_ptr().cast(), v.len()) }; - if ret == -1 { - random_failure() - } - } - - pub fn fill_bytes(v: &mut [u8]) { - // All supported versions of macOS (10.12+) support getentropy. - // - // `getentropy` is measurably faster (via Divan) then the other alternatives so its preferred - // when usable. - #[cfg(target_os = "macos")] - getentropy_fill_bytes(v); - - // On Apple platforms, `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply - // call into `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes` - // manages a CSPRNG which is seeded from the kernel's CSPRNG and which runs on - // its own thread accessed via GCD. This seems needlessly heavyweight for our purposes - // so we only use it on non-Mac OSes where the better entrypoints are blocked. - // - // `CCRandomGenerateBytes` is used instead of `SecRandomCopyBytes` because the former is accessible - // via `libSystem` (libc) while the other needs to link to `Security.framework`. - // - // Note that while `getentropy` has a available attribute in the macOS headers, the lack - // of a header in the iOS (and others) SDK means that its can cause app store rejections. - // Just use `CCRandomGenerateBytes` instead. - #[cfg(not(target_os = "macos"))] - ccrandom_fill_bytes(v); - } -} - -#[cfg(any(target_os = "openbsd", target_os = "emscripten", target_os = "vita"))] -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()); - } - } - } -} - -// FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification. -#[cfg(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 deleted file mode 100644 index 3dbab4cc486..00000000000 --- a/library/std/src/sys/unix/stack_overflow.rs +++ /dev/null @@ -1,223 +0,0 @@ -#![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 = "hurd", - 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; - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::{mmap as mmap64, munmap}; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{mmap64, 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("") - ); - 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 = 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", - target_os = "dragonfly", - ))] - let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK; - #[cfg(not(any( - target_os = "openbsd", - target_os = "netbsd", - target_os = "linux", - target_os = "dragonfly", - )))] - let flags = MAP_PRIVATE | MAP_ANON; - let stackp = - mmap64(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 = "hurd", - 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 deleted file mode 100644 index 97e75f1b5b6..00000000000 --- a/library/std/src/sys/unix/stdio.rs +++ /dev/null @@ -1,99 +0,0 @@ -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::mem::ManuallyDrop; -use crate::os::unix::io::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 { - unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read(buf) } - } - - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_buf(buf) } - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - 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 { - unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write(buf) } - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - unsafe { - ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write_vectored(bufs) - } - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - 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 { - unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write(buf) } - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - unsafe { - ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write_vectored(bufs) - } - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - 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 { - Some(Stderr::new()) -} diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs deleted file mode 100644 index 7e4a01a5ecd..00000000000 --- a/library/std/src/sys/unix/thread.rs +++ /dev/null @@ -1,992 +0,0 @@ -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", target_os = "nto"))] -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) -> io::Result { - let p = Box::into_raw(Box::new(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)(); - } - ptr::null_mut() - } - } - - pub fn yield_now() { - let ret = unsafe { libc::sched_yield() }; - debug_assert_eq!(ret, 0); - } - - #[cfg(target_os = "android")] - pub fn set_name(name: &CStr) { - const PR_SET_NAME: libc::c_int = 15; - 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(target_os = "linux")] - pub fn set_name(name: &CStr) { - const TASK_COMM_LEN: usize = 16; - - unsafe { - // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20. - let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); - let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, 0); - } - } - - #[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 = "tvos", target_os = "watchos"))] - pub fn set_name(name: &CStr) { - unsafe { - let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name); - let res = libc::pthread_setname_np(name.as_ptr()); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, 0); - } - } - - #[cfg(target_os = "netbsd")] - pub fn set_name(name: &CStr) { - unsafe { - let res = libc::pthread_setname_np( - libc::pthread_self(), - c"%s".as_ptr(), - name.as_ptr() as *mut libc::c_void, - ); - debug_assert_eq!(res, 0); - } - } - - #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] - 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() { - #[cfg(target_os = "nto")] - let name = truncate_cstr::<{ libc::_NTO_THREAD_NAME_MAX as usize }>(name); - - let res = unsafe { f(libc::pthread_self(), name.as_ptr()) }; - debug_assert_eq!(res, 0); - } - } - - #[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()); - let res = libc::rename_thread(thread_self, name.as_ptr()); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, libc::B_OK); - } - } - - #[cfg(any( - target_env = "newlib", - target_os = "l4re", - target_os = "emscripten", - target_os = "redox", - target_os = "vxworks", - target_os = "hurd", - target_os = "aix", - ))] - 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); - } -} - -#[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - target_os = "nto", -))] -fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { - let mut result = [0; MAX_WITH_NUL]; - for (src, dst) in cstr.to_bytes().iter().zip(&mut result[..MAX_WITH_NUL - 1]) { - *dst = *src as libc::c_char; - } - result -} - -pub fn available_parallelism() -> io::Result { - cfg_if::cfg_if! { - if #[cfg(any( - target_os = "android", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "hurd", - target_os = "ios", - target_os = "tvos", - target_os = "linux", - target_os = "macos", - target_os = "solaris", - target_os = "illumos", - target_os = "aix", - ))] { - #[allow(unused_assignments)] - #[allow(unused_mut)] - let mut quota = usize::MAX; - - #[cfg(any(target_os = "android", target_os = "linux"))] - { - quota = cgroups::quota().max(1); - let mut set: libc::cpu_set_t = unsafe { mem::zeroed() }; - unsafe { - if libc::sched_getaffinity(0, mem::size_of::(), &mut set) == 0 { - let count = libc::CPU_COUNT(&set) as usize; - let count = count.min(quota); - - // According to sched_getaffinity's API it should always be non-zero, but - // some old MIPS kernels were buggy and zero-initialized the mask if - // none was explicitly set. - // In that case we use the sysconf fallback. - if let Some(count) = NonZeroUsize::new(count) { - return Ok(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 => { - let count = cpus as usize; - // Cover the unusual situation where we were able to get the quota but not the affinity mask - let count = count.min(quota); - Ok(unsafe { NonZeroUsize::new_unchecked(count) }) - } - } - } else if #[cfg(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "openbsd", - target_os = "netbsd", - ))] { - use crate::ptr; - - #[cfg(target_os = "freebsd")] - { - let mut set: libc::cpuset_t = unsafe { mem::zeroed() }; - unsafe { - if libc::cpuset_getaffinity( - libc::CPU_LEVEL_WHICH, - libc::CPU_WHICH_PID, - -1, - mem::size_of::(), - &mut set, - ) == 0 { - let count = libc::CPU_COUNT(&set) as usize; - if count > 0 { - return Ok(NonZeroUsize::new_unchecked(count)); - } - } - } - } - - #[cfg(target_os = "netbsd")] - { - unsafe { - let set = libc::_cpuset_create(); - if !set.is_null() { - let mut count: usize = 0; - if libc::pthread_getaffinity_np(libc::pthread_self(), libc::_cpuset_size(set), set) == 0 { - for i in 0..u64::MAX { - match libc::_cpuset_isset(i, set) { - -1 => break, - 0 => continue, - _ => count = count + 1, - } - } - } - libc::_cpuset_destroy(set); - if let Some(count) = NonZeroUsize::new(count) { - return Ok(count); - } - } - } - } - - 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 = "nto")] { - unsafe { - use libc::_syspage_ptr; - if _syspage_ptr.is_null() { - Err(io::const_io_error!(io::ErrorKind::NotFound, "No syspage available")) - } else { - let cpus = (*_syspage_ptr).num_cpu; - NonZeroUsize::new(cpus as usize) - .ok_or(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")) - } - } - } 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::(), period.parse::()) { - (Ok(limit), Ok(period)) if period > 0 => { - 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::().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)) if period > 0 => 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 = "hurd"), - 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; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} - -#[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris" -))] -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::{mmap as mmap64, mprotect}; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{mmap64, 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; - - #[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 = "hurd", - 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 { - 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 if cfg!(target_os = "openbsd") { - // OpenBSD stack already includes a guard page, and stack is - // immutable. - // - // 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 { - // 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 = mmap64( - 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 { - 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 = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "l4re" - ))] - pub unsafe fn current() -> Option { - 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::(); - 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", target_os = "hurd")) { - 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 deleted file mode 100644 index 58f7ab84101..00000000000 --- a/library/std/src/sys/unix/thread_local_dtor.rs +++ /dev/null @@ -1,124 +0,0 @@ -#![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_attr(bootstrap, allow(unexpected_cfgs))] -#[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "fuchsia", - target_os = "redox", - target_os = "hurd" -))] -// FIXME: The Rust compiler currently omits weakly function definitions (i.e., -// __cxa_thread_atexit_impl) and its metadata from LLVM IR. -#[no_sanitize(cfi, kcfi)] -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; - - /// This is necessary because the __cxa_thread_atexit_impl implementation - /// std links to by default may be a C or C++ implementation that was not - /// compiled using the Clang integer normalization option. - #[cfg(sanitizer_cfi_normalize_integers)] - use core::ffi::c_int; - #[cfg(not(sanitizer_cfi_normalize_integers))] - #[cfi_encoding = "i"] - #[repr(transparent)] - pub struct c_int(pub libc::c_int); - - extern "C" { - #[linkage = "extern_weak"] - static __dso_handle: *mut u8; - #[linkage = "extern_weak"] - static __cxa_thread_atexit_impl: Option< - extern "C" fn( - unsafe extern "C" fn(*mut libc::c_void), - *mut libc::c_void, - *mut libc::c_void, - ) -> c_int, - >; - } - - if let Some(f) = __cxa_thread_atexit_impl { - unsafe { - f( - mem::transmute::< - unsafe extern "C" fn(*mut u8), - unsafe extern "C" fn(*mut libc::c_void), - >(dtor), - t.cast(), - &__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(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - use crate::cell::{Cell, RefCell}; - use crate::ptr; - - #[thread_local] - static REGISTERED: Cell = Cell::new(false); - - #[thread_local] - static DTORS: RefCell> = RefCell::new(Vec::new()); - - if !REGISTERED.get() { - _tlv_atexit(run_dtors, ptr::null_mut()); - REGISTERED.set(true); - } - - extern "C" { - fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); - } - - match DTORS.try_borrow_mut() { - Ok(mut dtors) => dtors.push((t, dtor)), - Err(_) => rtabort!("global allocator may not use TLS"), - } - - unsafe extern "C" fn run_dtors(_: *mut u8) { - let mut list = DTORS.take(); - while !list.is_empty() { - for (ptr, dtor) in list { - dtor(ptr); - } - list = DTORS.take(); - } - } -} - -#[cfg(any( - target_os = "vxworks", - target_os = "horizon", - target_os = "emscripten", - target_os = "aix" -))] -#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten) -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 deleted file mode 100644 index 2b2d079ee4d..00000000000 --- a/library/std/src/sys/unix/thread_local_key.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![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) -> 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); -} diff --git a/library/std/src/sys/unix/thread_parking/darwin.rs b/library/std/src/sys/unix/thread_parking/darwin.rs deleted file mode 100644 index 8231f3cba2d..00000000000 --- a/library/std/src/sys/unix/thread_parking/darwin.rs +++ /dev/null @@ -1,130 +0,0 @@ -//! Thread parking for Darwin-based systems. -//! -//! Darwin actually has futex syscalls (`__ulock_wait`/`__ulock_wake`), but they -//! cannot be used in `std` because they are non-public (their use will lead to -//! rejection from the App Store). -//! -//! Therefore, we need to look for other synchronization primitives. Luckily, Darwin -//! supports semaphores, which allow us to implement the behaviour we need with -//! only one primitive (as opposed to a mutex-condvar pair). We use the semaphore -//! provided by libdispatch, as the underlying Mach semaphore is only dubiously -//! public. - -use crate::pin::Pin; -use crate::sync::atomic::{ - AtomicI8, - Ordering::{Acquire, Release}, -}; -use crate::time::Duration; - -type dispatch_semaphore_t = *mut crate::ffi::c_void; -type dispatch_time_t = u64; - -const DISPATCH_TIME_NOW: dispatch_time_t = 0; -const DISPATCH_TIME_FOREVER: dispatch_time_t = !0; - -// Contained in libSystem.dylib, which is linked by default. -extern "C" { - fn dispatch_time(when: dispatch_time_t, delta: i64) -> dispatch_time_t; - fn dispatch_semaphore_create(val: isize) -> dispatch_semaphore_t; - fn dispatch_semaphore_wait(dsema: dispatch_semaphore_t, timeout: dispatch_time_t) -> isize; - fn dispatch_semaphore_signal(dsema: dispatch_semaphore_t) -> isize; - fn dispatch_release(object: *mut crate::ffi::c_void); -} - -const EMPTY: i8 = 0; -const NOTIFIED: i8 = 1; -const PARKED: i8 = -1; - -pub struct Parker { - semaphore: dispatch_semaphore_t, - state: AtomicI8, -} - -unsafe impl Sync for Parker {} -unsafe impl Send for Parker {} - -impl Parker { - pub unsafe fn new_in_place(parker: *mut Parker) { - let semaphore = dispatch_semaphore_create(0); - assert!( - !semaphore.is_null(), - "failed to create dispatch semaphore for thread synchronization" - ); - parker.write(Parker { semaphore, state: AtomicI8::new(EMPTY) }) - } - - // Does not need `Pin`, but other implementation do. - pub unsafe fn park(self: Pin<&Self>) { - // The semaphore counter must be zero at this point, because unparking - // threads will not actually increase it until we signalled that we - // are waiting. - - // Change NOTIFIED to EMPTY and EMPTY to PARKED. - if self.state.fetch_sub(1, Acquire) == NOTIFIED { - return; - } - - // Another thread may increase the semaphore counter from this point on. - // If it is faster than us, we will decrement it again immediately below. - // If we are faster, we wait. - - // Ensure that the semaphore counter has actually been decremented, even - // if the call timed out for some reason. - while dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER) != 0 {} - - // At this point, the semaphore counter is zero again. - - // We were definitely woken up, so we don't need to check the state. - // Still, we need to reset the state using a swap to observe the state - // change with acquire ordering. - self.state.swap(EMPTY, Acquire); - } - - // Does not need `Pin`, but other implementation do. - pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) { - if self.state.fetch_sub(1, Acquire) == NOTIFIED { - return; - } - - let nanos = dur.as_nanos().try_into().unwrap_or(i64::MAX); - let timeout = dispatch_time(DISPATCH_TIME_NOW, nanos); - - let timeout = dispatch_semaphore_wait(self.semaphore, timeout) != 0; - - let state = self.state.swap(EMPTY, Acquire); - if state == NOTIFIED && timeout { - // If the state was NOTIFIED but semaphore_wait returned without - // decrementing the count because of a timeout, it means another - // thread is about to call semaphore_signal. We must wait for that - // to happen to ensure the semaphore count is reset. - while dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER) != 0 {} - } else { - // Either a timeout occurred and we reset the state before any thread - // tried to wake us up, or we were woken up and reset the state, - // making sure to observe the state change with acquire ordering. - // Either way, the semaphore counter is now zero again. - } - } - - // Does not need `Pin`, but other implementation do. - pub fn unpark(self: Pin<&Self>) { - let state = self.state.swap(NOTIFIED, Release); - if state == PARKED { - unsafe { - dispatch_semaphore_signal(self.semaphore); - } - } - } -} - -impl Drop for Parker { - fn drop(&mut self) { - // SAFETY: - // We always ensure that the semaphore count is reset, so this will - // never cause an exception. - unsafe { - dispatch_release(self.semaphore); - } - } -} diff --git a/library/std/src/sys/unix/thread_parking/mod.rs b/library/std/src/sys/unix/thread_parking/mod.rs deleted file mode 100644 index 185333c072f..00000000000 --- a/library/std/src/sys/unix/thread_parking/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Thread parking on systems without futex support. - -#![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", -)))] - -cfg_if::cfg_if! { - if #[cfg(all( - any( - target_os = "macos", - target_os = "ios", - target_os = "watchos", - target_os = "tvos", - ), - not(miri), - ))] { - mod darwin; - pub use darwin::Parker; - } else if #[cfg(target_os = "netbsd")] { - mod netbsd; - pub use netbsd::{current, park, park_timeout, unpark, ThreadId}; - } else { - mod pthread; - pub use pthread::Parker; - } -} diff --git a/library/std/src/sys/unix/thread_parking/netbsd.rs b/library/std/src/sys/unix/thread_parking/netbsd.rs deleted file mode 100644 index 3be08122138..00000000000 --- a/library/std/src/sys/unix/thread_parking/netbsd.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::ffi::{c_int, c_void}; -use crate::ptr; -use crate::time::Duration; -use libc::{_lwp_self, clockid_t, lwpid_t, time_t, timespec, CLOCK_MONOTONIC}; - -extern "C" { - fn ___lwp_park60( - clock_id: clockid_t, - flags: c_int, - ts: *mut timespec, - unpark: lwpid_t, - hint: *const c_void, - unparkhint: *const c_void, - ) -> c_int; - fn _lwp_unpark(lwp: lwpid_t, hint: *const c_void) -> c_int; -} - -pub type ThreadId = lwpid_t; - -#[inline] -pub fn current() -> ThreadId { - unsafe { _lwp_self() } -} - -#[inline] -pub fn park(hint: usize) { - unsafe { - ___lwp_park60(0, 0, ptr::null_mut(), 0, ptr::invalid(hint), ptr::null()); - } -} - -pub fn park_timeout(dur: Duration, hint: usize) { - let mut timeout = timespec { - // Saturate so that the operation will definitely time out - // (even if it is after the heat death of the universe). - tv_sec: dur.as_secs().try_into().ok().unwrap_or(time_t::MAX), - tv_nsec: dur.subsec_nanos().into(), - }; - - // Timeout needs to be mutable since it is modified on NetBSD 9.0 and - // above. - unsafe { - ___lwp_park60(CLOCK_MONOTONIC, 0, &mut timeout, 0, ptr::invalid(hint), ptr::null()); - } -} - -#[inline] -pub fn unpark(tid: ThreadId, hint: usize) { - unsafe { - _lwp_unpark(tid, ptr::invalid(hint)); - } -} diff --git a/library/std/src/sys/unix/thread_parking/pthread.rs b/library/std/src/sys/unix/thread_parking/pthread.rs deleted file mode 100644 index ae805d84399..00000000000 --- a/library/std/src/sys/unix/thread_parking/pthread.rs +++ /dev/null @@ -1,284 +0,0 @@ -//! Thread parking without `futex` using the `pthread` synchronization primitives. - -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; -#[cfg(not(target_os = "nto"))] -use crate::sys::time::TIMESPEC_MAX; -#[cfg(target_os = "nto")] -use crate::sys::time::TIMESPEC_MAX_CAPPED; -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); -} - -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 = "tvos", - target_os = "watchos", - target_os = "espidf", - target_os = "horizon", - ))] - let (now, dur) = { - use crate::cmp::min; - use crate::sys::time::SystemTime; - - // 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 = "tvos", - target_os = "watchos", - target_os = "espidf", - target_os = "horizon", - )))] - let (now, dur) = { - use crate::sys::time::Timespec; - - (Timespec::now(libc::CLOCK_MONOTONIC), dur) - }; - - #[cfg(not(target_os = "nto"))] - let timeout = - now.checked_add_duration(&dur).and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX); - #[cfg(target_os = "nto")] - let timeout = now - .checked_add_duration(&dur) - .and_then(|t| t.to_timespec_capped()) - .unwrap_or(TIMESPEC_MAX_CAPPED); - let r = libc::pthread_cond_timedwait(cond, lock, &timeout); - debug_assert!(r == libc::ETIMEDOUT || r == 0); -} - -pub struct Parker { - state: AtomicUsize, - lock: UnsafeCell, - cvar: UnsafeCell, - // 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_in_place(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 = "tvos", - target_os = "watchos", - target_os = "l4re", - target_os = "android", - target_os = "redox", - target_os = "vita", - ))] { - 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::::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 deleted file mode 100644 index f62eb828ee5..00000000000 --- a/library/std/src/sys/unix/time.rs +++ /dev/null @@ -1,330 +0,0 @@ -use crate::fmt; -use crate::time::Duration; - -const NSEC_PER_SEC: u64 = 1_000_000_000; -pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; -#[allow(dead_code)] // Used for pthread condvar timeouts -pub const TIMESPEC_MAX: libc::timespec = - libc::timespec { tv_sec: ::MAX, tv_nsec: 1_000_000_000 - 1 }; - -// This additional constant is only used when calling -// `libc::pthread_cond_timedwait`. -#[cfg(target_os = "nto")] -pub(super) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec { - tv_sec: (u64::MAX / NSEC_PER_SEC) as i64, - tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64, -}; - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -#[rustc_layout_scalar_valid_range_end(999_999_999)] -struct Nanoseconds(u32); - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct SystemTime { - pub(crate) t: Timespec, -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) struct Timespec { - tv_sec: i64, - tv_nsec: Nanoseconds, -} - -impl SystemTime { - #[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))] - pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { - SystemTime { t: Timespec::new(tv_sec, tv_nsec) } - } - - pub fn now() -> SystemTime { - SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) } - } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.t.sub_timespec(&other.t) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_sub_duration(other)? }) - } -} - -impl From 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.0) - .finish() - } -} - -impl Timespec { - pub const fn zero() -> Timespec { - Timespec::new(0, 0) - } - - const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { - // On Apple OS, dates before epoch are represented differently than on other - // Unix platforms: e.g. 1/10th of a second before epoch is represented as `seconds=-1` - // and `nanoseconds=100_000_000` on other platforms, but is `seconds=0` and - // `nanoseconds=-900_000_000` on Apple OS. - // - // To compensate, we first detect this special case by checking if both - // seconds and nanoseconds are in range, and then correct the value for seconds - // and nanoseconds to match the common unix representation. - // - // Please note that Apple OS nonetheless accepts the standard unix format when - // setting file times, which makes this compensation round-trippable and generally - // transparent. - #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos" - ))] - let (tv_sec, tv_nsec) = - if (tv_sec <= 0 && tv_sec > i64::MIN) && (tv_nsec < 0 && tv_nsec > -1_000_000_000) { - (tv_sec - 1, tv_nsec + 1_000_000_000) - } else { - (tv_sec, tv_nsec) - }; - assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); - // SAFETY: The assert above checks tv_nsec is within the valid range - Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } - } - - pub fn now(clock: libc::clockid_t) -> Timespec { - use crate::mem::MaybeUninit; - use crate::sys::cvt; - - // Try to use 64-bit time in preparation for Y2038. - #[cfg(all( - target_os = "linux", - target_env = "gnu", - target_pointer_width = "32", - not(target_arch = "riscv32") - ))] - { - 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); - - if let Some(clock_gettime64) = __clock_gettime64.get() { - let mut t = MaybeUninit::uninit(); - cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); - return Timespec::from(unsafe { t.assume_init() }); - } - } - - let mut t = MaybeUninit::uninit(); - cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap(); - Timespec::from(unsafe { t.assume_init() }) - } - - pub fn sub_timespec(&self, other: &Timespec) -> Result { - 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.0 >= other.tv_nsec.0 { - ((self.tv_sec - other.tv_sec) as u64, self.tv_nsec.0 - other.tv_nsec.0) - } else { - ( - (self.tv_sec - other.tv_sec - 1) as u64, - self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0, - ) - }; - - 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 { - let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?; - - // Nano calculations can't overflow because nanos are <1B which fit - // in a u32. - let mut nsec = other.subsec_nanos() + self.tv_nsec.0; - if nsec >= NSEC_PER_SEC as u32 { - nsec -= NSEC_PER_SEC as u32; - secs = secs.checked_add(1)?; - } - Some(Timespec::new(secs, nsec.into())) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?; - - // Similar to above, nanos can't overflow. - let mut nsec = self.tv_nsec.0 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.into())) - } - - #[allow(dead_code)] - pub fn to_timespec(&self) -> Option { - Some(libc::timespec { - tv_sec: self.tv_sec.try_into().ok()?, - tv_nsec: self.tv_nsec.0.try_into().ok()?, - }) - } - - // On QNX Neutrino, the maximum timespec for e.g. pthread_cond_timedwait - // is 2^64 nanoseconds - #[cfg(target_os = "nto")] - pub(super) fn to_timespec_capped(&self) -> Option { - // Check if timeout in nanoseconds would fit into an u64 - if (self.tv_nsec.0 as u64) - .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?) - .is_none() - { - return None; - } - self.to_timespec() - } - - #[cfg(all( - target_os = "linux", - target_env = "gnu", - target_pointer_width = "32", - not(target_arch = "riscv32") - ))] - pub fn to_timespec64(&self) -> __timespec64 { - __timespec64::new(self.tv_sec, self.tv_nsec.0 as _) - } -} - -impl From for Timespec { - fn from(t: libc::timespec) -> Timespec { - Timespec::new(t.tv_sec as i64, t.tv_nsec as i64) - } -} - -#[cfg(all( - target_os = "linux", - target_env = "gnu", - target_pointer_width = "32", - not(target_arch = "riscv32") -))] -#[repr(C)] -pub(crate) struct __timespec64 { - pub(crate) tv_sec: i64, - #[cfg(target_endian = "big")] - _padding: i32, - pub(crate) tv_nsec: i32, - #[cfg(target_endian = "little")] - _padding: i32, -} - -#[cfg(all( - target_os = "linux", - target_env = "gnu", - target_pointer_width = "32", - not(target_arch = "riscv32") -))] -impl __timespec64 { - pub(crate) fn new(tv_sec: i64, tv_nsec: i32) -> Self { - Self { tv_sec, tv_nsec, _padding: 0 } - } -} - -#[cfg(all( - target_os = "linux", - target_env = "gnu", - target_pointer_width = "32", - not(target_arch = "riscv32") -))] -impl From<__timespec64> for Timespec { - fn from(t: __timespec64) -> Timespec { - Timespec::new(t.tv_sec, t.tv_nsec.into()) - } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Instant { - t: Timespec, -} - -impl Instant { - pub fn now() -> Instant { - // https://www.manpagez.com/man/3/clock_gettime/ - // - // CLOCK_UPTIME_RAW clock that increments monotonically, in the same man- - // ner as CLOCK_MONOTONIC_RAW, but that does not incre- - // ment while the system is asleep. The returned value - // is identical to the result of mach_absolute_time() - // after the appropriate mach_timebase conversion is - // applied. - // - // Instant on macos was historically implemented using mach_absolute_time; - // we preserve this value domain out of an abundance of caution. - #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "watchos", - target_os = "tvos" - ))] - const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW; - #[cfg(not(any( - target_os = "macos", - target_os = "ios", - target_os = "watchos", - target_os = "tvos" - )))] - const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC; - Instant { t: Timespec::now(clock_id) } - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option { - self.t.sub_timespec(&other.t).ok() - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - 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.0) - .finish() - } -} diff --git a/library/std/src/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs deleted file mode 100644 index 61088ff16ed..00000000000 --- a/library/std/src/sys/unix/weak.rs +++ /dev/null @@ -1,195 +0,0 @@ -//! Support for "weak linkage" to symbols on Unix -//! -//! Some I/O operations we do in std 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", target_os = "tvos")))] -pub(crate) macro weak { - (fn $name:ident($($t:ty),*) -> $ret:ty) => ( - let ref $name: ExternWeak $ret> = { - extern "C" { - #[linkage = "extern_weak"] - static $name: Option $ret>; - } - #[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", target_os = "tvos"))] -pub(crate) use self::dlsym as weak; - -pub(crate) struct ExternWeak { - weak_ptr: Option, -} - -impl ExternWeak { - #[inline] - pub(crate) fn new(weak_ptr: Option) -> Self { - ExternWeak { weak_ptr } - } - - #[inline] - pub(crate) fn get(&self) -> Option { - 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 $ret> = - DlsymWeak::new(concat!($sym, '\0')); - let $name = &DLSYM; - ) -} -pub(crate) struct DlsymWeak { - name: &'static str, - func: AtomicPtr, - _marker: PhantomData, -} - -impl DlsymWeak { - 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 { - 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 { - assert_eq!(mem::size_of::(), 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 deleted file mode 100644 index d715ae45401..00000000000 --- a/library/std/src/sys/unsupported/alloc.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::ptr::null_mut; - -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { - null_mut() - } - - #[inline] - unsafe fn alloc_zeroed(&self, _layout: Layout) -> *mut u8 { - null_mut() - } - - #[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 { - null_mut() - } -} diff --git a/library/std/src/sys/unsupported/args.rs b/library/std/src/sys/unsupported/args.rs deleted file mode 100644 index a2d75a61976..00000000000 --- a/library/std/src/sys/unsupported/args.rs +++ /dev/null @@ -1,36 +0,0 @@ -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 { - None - } - fn size_hint(&self) -> (usize, Option) { - (0, Some(0)) - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - 0 - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - None - } -} diff --git a/library/std/src/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs deleted file mode 100644 index 5c379992b20..00000000000 --- a/library/std/src/sys/unsupported/common.rs +++ /dev/null @@ -1,40 +0,0 @@ -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, _sigpipe: 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() -> std_io::Result { - 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 is_interrupted(_code: i32) -> bool { - false -} - -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 deleted file mode 100644 index d2efec506c5..00000000000 --- a/library/std/src/sys/unsupported/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 6ac1b5d2bcf..00000000000 --- a/library/std/src/sys/unsupported/fs.rs +++ /dev/null @@ -1,324 +0,0 @@ -use crate::ffi::OsString; -use crate::fmt; -use crate::hash::{Hash, Hasher}; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, 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 { - self.0 - } - - pub fn accessed(&self) -> io::Result { - self.0 - } - - pub fn created(&self) -> io::Result { - 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(&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; - - fn next(&mut self) -> Option> { - 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 { - self.0 - } - - pub fn file_type(&self) -> io::Result { - 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 { - unsupported() - } - - pub fn file_attr(&self) -> io::Result { - 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 { - self.0 - } - - pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 - } - - pub fn is_read_vectored(&self) -> bool { - self.0 - } - - pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.0 - } - - pub fn write(&self, _buf: &[u8]) -> io::Result { - self.0 - } - - pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { - 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 { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - 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 { - 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 { - unsupported() -} - -pub fn readlink(_p: &Path) -> io::Result { - 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 { - unsupported() -} - -pub fn lstat(_p: &Path) -> io::Result { - unsupported() -} - -pub fn canonicalize(_p: &Path) -> io::Result { - unsupported() -} - -pub fn copy(_from: &Path, _to: &Path) -> io::Result { - unsupported() -} diff --git a/library/std/src/sys/unsupported/io.rs b/library/std/src/sys/unsupported/io.rs deleted file mode 100644 index 6372fca74e0..00000000000 --- a/library/std/src/sys/unsupported/io.rs +++ /dev/null @@ -1,51 +0,0 @@ -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::take(&mut self.0); - 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 - } -} - -pub fn is_terminal(_: &T) -> bool { - false -} diff --git a/library/std/src/sys/unsupported/locks/condvar.rs b/library/std/src/sys/unsupported/locks/condvar.rs deleted file mode 100644 index 3f0943b50ee..00000000000 --- a/library/std/src/sys/unsupported/locks/condvar.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::sys::locks::Mutex; -use crate::time::Duration; - -pub struct Condvar {} - -impl Condvar { - #[inline] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - pub const fn new() -> Condvar { - Condvar {} - } - - #[inline] - pub fn notify_one(&self) {} - - #[inline] - pub 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 deleted file mode 100644 index 0e0f9eccb21..00000000000 --- a/library/std/src/sys/unsupported/locks/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod condvar; -mod mutex; -mod rwlock; -pub use condvar::Condvar; -pub use mutex::Mutex; -pub use rwlock::RwLock; diff --git a/library/std/src/sys/unsupported/locks/mutex.rs b/library/std/src/sys/unsupported/locks/mutex.rs deleted file mode 100644 index 4a13c55fb8b..00000000000 --- a/library/std/src/sys/unsupported/locks/mutex.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::cell::Cell; - -pub struct Mutex { - // This platform has no threads, so we can use a Cell here. - locked: Cell, -} - -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} // no threads on this platform - -impl Mutex { - #[inline] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - pub const fn new() -> Mutex { - Mutex { locked: Cell::new(false) } - } - - #[inline] - pub 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 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 deleted file mode 100644 index 789ef9b29e5..00000000000 --- a/library/std/src/sys/unsupported/locks/rwlock.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::cell::Cell; - -pub struct RwLock { - // This platform has no threads, so we can use a Cell here. - mode: Cell, -} - -unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} // no threads on this platform - -impl RwLock { - #[inline] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - pub const fn new() -> RwLock { - RwLock { mode: Cell::new(0) } - } - - #[inline] - pub fn read(&self) { - let m = self.mode.get(); - if m >= 0 { - self.mode.set(m + 1); - } else { - rtabort!("rwlock locked for writing"); - } - } - - #[inline] - pub fn try_read(&self) -> bool { - let m = self.mode.get(); - if m >= 0 { - self.mode.set(m + 1); - true - } else { - false - } - } - - #[inline] - pub fn write(&self) { - if self.mode.replace(-1) != 0 { - rtabort!("rwlock locked for reading") - } - } - - #[inline] - pub 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 deleted file mode 100644 index e1a38de6471..00000000000 --- a/library/std/src/sys/unsupported/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![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 once; -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 thread_parking; -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 deleted file mode 100644 index bbc52703f96..00000000000 --- a/library/std/src/sys/unsupported/net.rs +++ /dev/null @@ -1,370 +0,0 @@ -use crate::fmt; -use crate::io::{self, BorrowedCursor, 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 { - unsupported() - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0 - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 - } - - pub fn is_read_vectored(&self) -> bool { - self.0 - } - - pub fn write(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - self.0 - } - - pub fn is_write_vectored(&self) -> bool { - self.0 - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn linger(&self) -> io::Result> { - self.0 - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn nodelay(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - 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 { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn only_v6(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - 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 { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - 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 { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn broadcast(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v6(&self) -> io::Result { - 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 { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn send(&self, _: &[u8]) -> io::Result { - 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 { - self.0 - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - 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/once.rs b/library/std/src/sys/unsupported/once.rs deleted file mode 100644 index 11fde1888ba..00000000000 --- a/library/std/src/sys/unsupported/once.rs +++ /dev/null @@ -1,100 +0,0 @@ -use crate::cell::Cell; -use crate::sync as public; -use crate::sync::once::ExclusiveState; - -pub struct Once { - state: Cell, -} - -pub struct OnceState { - poisoned: bool, - set_state_to: Cell, -} - -#[derive(Clone, Copy, PartialEq, Eq)] -enum State { - Incomplete, - Poisoned, - Running, - Complete, -} - -struct CompletionGuard<'a> { - state: &'a Cell, - set_state_on_drop_to: State, -} - -impl<'a> Drop for CompletionGuard<'a> { - fn drop(&mut self) { - self.state.set(self.set_state_on_drop_to); - } -} - -// Safety: threads are not supported on this platform. -unsafe impl Sync for Once {} - -impl Once { - #[inline] - #[rustc_const_stable(feature = "const_once_new", since = "1.32.0")] - pub const fn new() -> Once { - Once { state: Cell::new(State::Incomplete) } - } - - #[inline] - pub fn is_completed(&self) -> bool { - self.state.get() == State::Complete - } - - #[inline] - pub(crate) fn state(&mut self) -> ExclusiveState { - match self.state.get() { - State::Incomplete => ExclusiveState::Incomplete, - State::Poisoned => ExclusiveState::Poisoned, - State::Complete => ExclusiveState::Complete, - _ => unreachable!("invalid Once state"), - } - } - - #[cold] - #[track_caller] - pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) { - let state = self.state.get(); - match state { - State::Poisoned if !ignore_poisoning => { - // Panic to propagate the poison. - panic!("Once instance has previously been poisoned"); - } - State::Incomplete | State::Poisoned => { - self.state.set(State::Running); - // `guard` will set the new state on drop. - let mut guard = - CompletionGuard { state: &self.state, set_state_on_drop_to: State::Poisoned }; - // Run the function, letting it know if we're poisoned or not. - let f_state = public::OnceState { - inner: OnceState { - poisoned: state == State::Poisoned, - set_state_to: Cell::new(State::Complete), - }, - }; - f(&f_state); - guard.set_state_on_drop_to = f_state.inner.set_state_to.get(); - } - State::Running => { - panic!("one-time initialization may not be performed recursively"); - } - State::Complete => {} - } - } -} - -impl OnceState { - #[inline] - pub fn is_poisoned(&self) -> bool { - self.poisoned - } - - #[inline] - pub fn poison(&self) { - self.set_state_to.set(State::Poisoned) - } -} diff --git a/library/std/src/sys/unsupported/os.rs b/library/std/src/sys/unsupported/os.rs deleted file mode 100644 index 248b34829f2..00000000000 --- a/library/std/src/sys/unsupported/os.rs +++ /dev/null @@ -1,121 +0,0 @@ -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 { - 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 { - self.0 - } -} - -#[derive(Debug)] -pub struct JoinPathsError; - -pub fn join_paths(_paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - 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 { - unsupported() -} - -pub struct Env(!); - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} - } -} - -impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self(inner) = self; - match *inner {} - } -} - -pub fn env() -> Env { - panic!("not supported on this platform") -} - -pub fn getenv(_: &OsStr) -> Option { - 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 { - 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 deleted file mode 100644 index d7d8f297ae5..00000000000 --- a/library/std/src/sys/unsupported/pipe.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; - -pub struct AnonPipe(!); - -impl AnonPipe { - pub fn read(&self, _buf: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0 - } - - pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 - } - - pub fn is_read_vectored(&self) -> bool { - self.0 - } - - pub fn read_to_end(&self, _buf: &mut Vec) -> io::Result { - self.0 - } - - pub fn write(&self, _buf: &[u8]) -> io::Result { - self.0 - } - - pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { - 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, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { - match p1.0 {} -} diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs deleted file mode 100644 index a639afcc674..00000000000 --- a/library/std/src/sys/unsupported/process.rs +++ /dev/null @@ -1,239 +0,0 @@ -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, - pub stdout: Option, - pub stderr: Option, -} - -// FIXME: This should be a unit struct, so we can always construct it -// The value here should be never used, since we cannot spawn processes. -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() - } - - pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - unsupported() - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - pipe.diverge() - } -} - -impl From for Stdio { - fn from(_: io::Stdout) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") - } -} - -impl From for Stdio { - fn from(_: io::Stderr) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") - } -} - -impl From for Stdio { - fn from(_file: File) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") - } -} - -impl fmt::Debug for Command { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] -#[non_exhaustive] -pub struct ExitStatus(); - -impl ExitStatus { - pub fn exit_ok(&self) -> Result<(), ExitStatusError> { - Ok(()) - } - - pub fn code(&self) -> Option { - Some(0) - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "") - } -} - -pub struct ExitStatusError(!); - -impl Clone for ExitStatusError { - fn clone(&self) -> ExitStatusError { - self.0 - } -} - -impl Copy for ExitStatusError {} - -impl PartialEq for ExitStatusError { - fn eq(&self, _other: &ExitStatusError) -> bool { - self.0 - } -} - -impl Eq for ExitStatusError {} - -impl fmt::Debug for ExitStatusError { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -impl Into for ExitStatusError { - fn into(self) -> ExitStatus { - self.0 - } -} - -impl ExitStatusError { - pub fn code(self) -> Option { - self.0 - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(bool); - -impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(false); - pub const FAILURE: ExitCode = ExitCode(true); - - pub fn as_i32(&self) -> i32 { - self.0 as i32 - } -} - -impl From 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 { - self.0 - } - - pub fn try_wait(&mut self) -> io::Result> { - 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 - } - fn size_hint(&self) -> (usize, Option) { - (0, Some(0)) - } -} - -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 deleted file mode 100644 index b5e3f5be988..00000000000 --- a/library/std/src/sys/unsupported/stdio.rs +++ /dev/null @@ -1,59 +0,0 @@ -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 { - Ok(0) - } -} - -impl Stdout { - pub const fn new() -> Stdout { - Stdout - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - 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 { - 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> { - None -} diff --git a/library/std/src/sys/unsupported/thread.rs b/library/std/src/sys/unsupported/thread.rs deleted file mode 100644 index a8db251de20..00000000000 --- a/library/std/src/sys/unsupported/thread.rs +++ /dev/null @@ -1,46 +0,0 @@ -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) -> io::Result { - 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 { - unsupported() -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/unsupported/thread_local_dtor.rs b/library/std/src/sys/unsupported/thread_local_dtor.rs deleted file mode 100644 index 84660ea5881..00000000000 --- a/library/std/src/sys/unsupported/thread_local_dtor.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![unstable(feature = "thread_local_internals", issue = "none")] - -#[cfg_attr(target_family = "wasm", allow(unused))] // unused on wasm32-unknown-unknown -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 deleted file mode 100644 index b6e5e4cd2e1..00000000000 --- a/library/std/src/sys/unsupported/thread_local_key.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub type Key = usize; - -#[inline] -pub unsafe fn create(_dtor: Option) -> 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"); -} diff --git a/library/std/src/sys/unsupported/thread_parking.rs b/library/std/src/sys/unsupported/thread_parking.rs deleted file mode 100644 index 197078bb186..00000000000 --- a/library/std/src/sys/unsupported/thread_parking.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::pin::Pin; -use crate::time::Duration; - -pub struct Parker {} - -impl Parker { - pub unsafe fn new_in_place(_parker: *mut Parker) {} - pub unsafe fn park(self: Pin<&Self>) {} - pub unsafe fn park_timeout(self: Pin<&Self>, _dur: Duration) {} - pub fn unpark(self: Pin<&Self>) {} -} diff --git a/library/std/src/sys/unsupported/time.rs b/library/std/src/sys/unsupported/time.rs deleted file mode 100644 index 6d67b538a96..00000000000 --- a/library/std/src/sys/unsupported/time.rs +++ /dev/null @@ -1,45 +0,0 @@ -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 { - self.0.checked_sub(other.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_add(*other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - 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 { - self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_add(*other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - 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 deleted file mode 100644 index c42c310e3a2..00000000000 --- a/library/std/src/sys/wasi/args.rs +++ /dev/null @@ -1,62 +0,0 @@ -#![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, -} - -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> { - 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 { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - 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 { - self.iter.next_back() - } -} diff --git a/library/std/src/sys/wasi/env.rs b/library/std/src/sys/wasi/env.rs deleted file mode 100644 index 730e356d7fe..00000000000 --- a/library/std/src/sys/wasi/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index d7295a799da..00000000000 --- a/library/std/src/sys/wasi/fd.rs +++ /dev/null @@ -1,332 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] -#![allow(dead_code)] - -use super::err2io; -use crate::io::{self, BorrowedCursor, 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::>(), mem::size_of::()); - assert_eq!(mem::align_of::>(), mem::align_of::()); - // SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout. - // We decorate our `IoSliceMut` with `repr(transparent)` (see `io.rs`), and - // `crate::io::IoSliceMut` is a `repr(transparent)` wrapper around our type, so this is - // guaranteed. - unsafe { mem::transmute(a) } -} - -fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] { - assert_eq!(mem::size_of::>(), mem::size_of::()); - assert_eq!(mem::align_of::>(), mem::align_of::()); - // SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout. - // We decorate our `IoSlice` with `repr(transparent)` (see `io.rs`), and - // `crate::io::IoSlice` is a `repr(transparent)` wrapper around our type, so this is - // guaranteed. - 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 { - 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 { - 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 { - unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) } - } - - pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { - unsafe { - let bufs = [wasi::Iovec { - buf: buf.as_mut().as_mut_ptr() as *mut u8, - buf_len: buf.capacity(), - }]; - match wasi::fd_read(self.as_raw_fd() as wasi::Fd, &bufs) { - Ok(n) => { - buf.advance(n); - Ok(()) - } - Err(e) => Err(err2io(e)), - } - } - } - - pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result { - unsafe { wasi::fd_write(self.as_raw_fd() as wasi::Fd, ciovec(bufs)).map_err(err2io) } - } - - pub fn seek(&self, pos: SeekFrom) -> io::Result { - 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 { - 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(crate) 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 { - 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 { - 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 { - 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(crate) fn filestat_get(&self) -> io::Result { - 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(crate) fn path_filestat_get( - &self, - flags: wasi::Lookupflags, - path: &str, - ) -> io::Result { - 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 { - 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 { - 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 for WasiFd { - #[inline] - fn as_inner(&self) -> &OwnedFd { - &self.fd - } -} - -impl AsInnerMut for WasiFd { - #[inline] - fn as_inner_mut(&mut self) -> &mut OwnedFd { - &mut self.fd - } -} - -impl IntoInner for WasiFd { - fn into_inner(self) -> OwnedFd { - self.fd - } -} - -impl FromInner 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 { - #[inline] - 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 deleted file mode 100644 index e8238665452..00000000000 --- a/library/std/src/sys/wasi/fs.rs +++ /dev/null @@ -1,810 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] - -use super::fd::WasiFd; -use crate::ffi::{CStr, OsStr, OsString}; -use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, 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::common::small_c_string::run_path_with_cstr; -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, - cookie: Option, - buf: Vec, - offset: usize, - cap: usize, -} - -struct ReadDirInner { - root: PathBuf, - dir: File, -} - -pub struct DirEntry { - meta: wasi::Dirent, - name: Vec, - inner: Arc, -} - -#[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, - rights_inheriting: Option, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct FilePermissions { - readonly: bool, -} - -#[derive(Copy, Clone, Debug, Default)] -pub struct FileTimes { - accessed: Option, - modified: Option, -} - -#[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 { - Ok(SystemTime::from_wasi_timestamp(self.meta.mtim)) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.atim)) - } - - pub fn created(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.ctim)) - } - - pub(crate) 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); - } - - pub fn set_modified(&mut self, t: SystemTime) { - self.modified = Some(t); - } -} - -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(crate) 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; - - fn next(&mut self) -> Option> { - 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::(); - 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 { - metadata_at(&self.inner.dir.fd, 0, OsStr::from_bytes(&self.name).as_ref()) - } - - pub fn file_type(&self) -> io::Result { - 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 { - let (dir, file) = open_parent(path)?; - open_at(&dir, &file, opts) - } - - pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result { - open_at(&self.fd, path, opts) - } - - pub fn file_attr(&self) -> io::Result { - self.fd.filestat_get().map(|meta| FileAttr { meta }) - } - - pub fn metadata_at(&self, flags: wasi::Lookupflags, path: &Path) -> io::Result { - 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 { - self.read_vectored(&mut [IoSliceMut::new(buf)]) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.fd.read(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.fd.read_buf(cursor) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(buf)]) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - 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 { - self.fd.seek(pos) - } - - pub fn duplicate(&self) -> io::Result { - // 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<()> { - let to_timestamp = |time: Option| match time { - Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), - Some(_) => Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "timestamp is too large to set as a file time" - )), - None => Ok(0), - }; - self.fd.filestat_set_times( - to_timestamp(times.accessed)?, - to_timestamp(times.modified)?, - 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 { - read_link(&self.fd, file) - } -} - -impl AsInner for File { - #[inline] - fn as_inner(&self) -> &WasiFd { - &self.fd - } -} - -impl IntoInner for File { - fn into_inner(self) -> WasiFd { - self.fd - } -} - -impl FromInner 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 { - #[inline] - 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 { - 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 { - let (dir, file) = open_parent(p)?; - read_link(&dir, &file) -} - -fn read_link(fd: &WasiFd, file: &Path) -> io::Result { - // 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 { - let (dir, file) = open_parent(p)?; - metadata_at(&dir, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, &file) -} - -pub fn lstat(p: &Path) -> io::Result { - let (dir, file) = open_parent(p)?; - metadata_at(&dir, 0, &file) -} - -fn metadata_at(fd: &WasiFd, flags: wasi::Lookupflags, path: &Path) -> io::Result { - let meta = fd.path_filestat_get(flags, osstr2str(path.as_ref())?)?; - Ok(FileAttr { meta }) -} - -pub fn canonicalize(_p: &Path) -> io::Result { - // 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 { - 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`. 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, PathBuf)> { - run_path_with_cstr(p, |p| { - let mut buf = Vec::::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 { - 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 deleted file mode 100644 index 2cd45df88fa..00000000000 --- a/library/std/src/sys/wasi/io.rs +++ /dev/null @@ -1,79 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] - -use crate::marker::PhantomData; -use crate::os::fd::{AsFd, AsRawFd}; -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) } - } -} - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - unsafe { libc::isatty(fd.as_raw_fd()) != 0 } -} diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs deleted file mode 100644 index 5919cc506d9..00000000000 --- a/library/std/src/sys/wasi/mod.rs +++ /dev/null @@ -1,198 +0,0 @@ -//! 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; -#[allow(unused)] -#[path = "../wasm/atomics/futex.rs"] -pub mod futex; -pub mod io; - -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; - -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; - pub(crate) use futex_mutex::Mutex; - pub(crate) use futex_rwlock::RwLock; - } - } else { - #[path = "../unsupported/locks/mod.rs"] - pub mod locks; - #[path = "../unsupported/once.rs"] - pub mod once; - #[path = "../unsupported/thread_parking.rs"] - pub mod thread_parking; - } -} - -#[path = "../unsupported/common.rs"] -#[deny(unsafe_op_in_unsafe_fn)] -#[allow(unused)] -mod common; -pub use common::*; - -#[inline] -pub fn is_interrupted(errno: i32) -> bool { - errno == wasi::ERRNO_INTR.raw().into() -} - -pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { - use std_io::ErrorKind; - - let Ok(errno) = u16::try_from(errno) else { - return ErrorKind::Uncategorized; - }; - - macro_rules! match_errno { - ($($($errno:ident)|+ => $errkind:ident),*, _ => $wildcard:ident $(,)?) => { - match errno { - $(e if $(e == ::wasi::$errno.raw())||+ => ErrorKind::$errkind),*, - _ => ErrorKind::$wildcard, - } - }; - } - - match_errno! { - ERRNO_2BIG => ArgumentListTooLong, - ERRNO_ACCES => PermissionDenied, - ERRNO_ADDRINUSE => AddrInUse, - ERRNO_ADDRNOTAVAIL => AddrNotAvailable, - ERRNO_AFNOSUPPORT => Unsupported, - ERRNO_AGAIN => WouldBlock, - // ALREADY => "connection already in progress", - // BADF => "bad file descriptor", - // BADMSG => "bad message", - ERRNO_BUSY => ResourceBusy, - // CANCELED => "operation canceled", - // CHILD => "no child processes", - ERRNO_CONNABORTED => ConnectionAborted, - ERRNO_CONNREFUSED => ConnectionRefused, - ERRNO_CONNRESET => ConnectionReset, - ERRNO_DEADLK => Deadlock, - // DESTADDRREQ => "destination address required", - ERRNO_DOM => InvalidInput, - // DQUOT => /* reserved */, - ERRNO_EXIST => AlreadyExists, - // FAULT => "bad address", - ERRNO_FBIG => FileTooLarge, - ERRNO_HOSTUNREACH => HostUnreachable, - // IDRM => "identifier removed", - // ILSEQ => "illegal byte sequence", - // INPROGRESS => "operation in progress", - ERRNO_INTR => Interrupted, - ERRNO_INVAL => InvalidInput, - ERRNO_IO => Uncategorized, - // ISCONN => "socket is connected", - ERRNO_ISDIR => IsADirectory, - ERRNO_LOOP => FilesystemLoop, - // MFILE => "file descriptor value too large", - ERRNO_MLINK => TooManyLinks, - // MSGSIZE => "message too large", - // MULTIHOP => /* reserved */, - ERRNO_NAMETOOLONG => InvalidFilename, - ERRNO_NETDOWN => NetworkDown, - // NETRESET => "connection aborted by network", - ERRNO_NETUNREACH => NetworkUnreachable, - // NFILE => "too many files open in system", - // NOBUFS => "no buffer space available", - ERRNO_NODEV => NotFound, - ERRNO_NOENT => NotFound, - // NOEXEC => "executable file format error", - // NOLCK => "no locks available", - // NOLINK => /* reserved */, - ERRNO_NOMEM => OutOfMemory, - // NOMSG => "no message of the desired type", - // NOPROTOOPT => "protocol not available", - ERRNO_NOSPC => StorageFull, - ERRNO_NOSYS => Unsupported, - ERRNO_NOTCONN => NotConnected, - ERRNO_NOTDIR => NotADirectory, - ERRNO_NOTEMPTY => DirectoryNotEmpty, - // NOTRECOVERABLE => "state not recoverable", - // NOTSOCK => "not a socket", - ERRNO_NOTSUP => Unsupported, - // NOTTY => "inappropriate I/O control operation", - ERRNO_NXIO => NotFound, - // OVERFLOW => "value too large to be stored in data type", - // OWNERDEAD => "previous owner died", - ERRNO_PERM => PermissionDenied, - ERRNO_PIPE => BrokenPipe, - // PROTO => "protocol error", - ERRNO_PROTONOSUPPORT => Unsupported, - // PROTOTYPE => "protocol wrong type for socket", - // RANGE => "result too large", - ERRNO_ROFS => ReadOnlyFilesystem, - ERRNO_SPIPE => NotSeekable, - ERRNO_SRCH => NotFound, - // STALE => /* reserved */, - ERRNO_TIMEDOUT => TimedOut, - ERRNO_TXTBSY => ResourceBusy, - ERRNO_XDEV => CrossesDevices, - ERRNO_NOTCAPABLE => PermissionDenied, - _ => 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; -} - -#[inline] -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 deleted file mode 100644 index 2239880ffbe..00000000000 --- a/library/std/src/sys/wasi/net.rs +++ /dev/null @@ -1,544 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] - -use super::err2io; -use super::fd::WasiFd; -use crate::fmt; -use crate::io::{self, BorrowedCursor, 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 for Socket { - #[inline] - fn as_inner(&self) -> &WasiFd { - &self.0 - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> WasiFd { - self.0 - } -} - -impl FromInner 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 { - #[inline] - 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 { - unsupported() - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn read_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn write_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unsupported() - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.read_vectored(&mut [IoSliceMut::new(buf)]) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.socket().as_inner().read_buf(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.socket().as_inner().read(bufs) - } - - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(buf)]) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.socket().as_inner().write(bufs) - } - - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn peer_addr(&self) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - unsupported() - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let wasi_how = match how { - Shutdown::Read => wasi::SDFLAGS_RD, - Shutdown::Write => wasi::SDFLAGS_WR, - Shutdown::Both => wasi::SDFLAGS_RD | wasi::SDFLAGS_WR, - }; - - unsafe { wasi::sock_shutdown(self.socket().as_raw_fd() as _, wasi_how).map_err(err2io) } - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn linger(&self) -> io::Result> { - unsupported() - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn nodelay(&self) -> io::Result { - unsupported() - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn ttl(&self) -> io::Result { - unsupported() - } - - pub fn take_error(&self) -> io::Result> { - 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) - } - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } -} - -impl FromInner for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - 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 { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - 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 { - unsupported() - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn ttl(&self) -> io::Result { - unsupported() - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn only_v6(&self) -> io::Result { - unsupported() - } - - pub fn take_error(&self) -> io::Result> { - 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) - } - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } -} - -impl AsInner for TcpListener { - #[inline] - fn as_inner(&self) -> &Socket { - &self.inner - } -} - -impl IntoInner for TcpListener { - fn into_inner(self) -> Socket { - self.inner - } -} - -impl FromInner 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 { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - 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 { - unsupported() - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn read_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn write_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn broadcast(&self) -> io::Result { - unsupported() - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn multicast_loop_v4(&self) -> io::Result { - unsupported() - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - unsupported() - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn multicast_loop_v6(&self) -> io::Result { - 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 { - unsupported() - } - - pub fn take_error(&self) -> io::Result> { - unsupported() - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - unsupported() - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unsupported() - } - - pub fn send(&self, _: &[u8]) -> io::Result { - unsupported() - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - unsupported() - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } -} - -impl AsInner for UdpSocket { - #[inline] - fn as_inner(&self) -> &Socket { - &self.inner - } -} - -impl IntoInner for UdpSocket { - fn into_inner(self) -> Socket { - self.inner - } -} - -impl FromInner 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 { - self.0 - } -} - -impl<'a> TryFrom<&'a str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &'a str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - 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 deleted file mode 100644 index d53bddd8e9d..00000000000 --- a/library/std/src/sys/wasi/os.rs +++ /dev/null @@ -1,301 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] - -use crate::error::Error as StdError; -use crate::ffi::{CStr, OsStr, OsString}; -use crate::fmt; -use crate::io; -use crate::marker::PhantomData; -use crate::ops::Drop; -use crate::os::wasi::prelude::*; -use crate::path::{self, PathBuf}; -use crate::str; -use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; -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; - pub fn __wasilibc_get_environ() -> *mut *mut c_char; - } -} - -cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { - // Access to the environment must be protected by a lock in multi-threaded scenarios. - use crate::sync::{PoisonError, RwLock}; - static ENV_LOCK: RwLock<()> = RwLock::new(()); - pub fn env_read_lock() -> impl Drop { - ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) - } - pub fn env_write_lock() -> impl Drop { - ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner) - } - } else { - // No need for a lock if we are single-threaded. - pub fn env_read_lock() -> impl Drop { - Box::new(()) - } - pub fn env_write_lock() -> impl Drop { - Box::new(()) - } - } -} - -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 { - 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 result = run_path_with_cstr(p, |p| unsafe { Ok(libc::chdir(p.as_ptr())) })?; - match result == (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 { - self.0 - } -} - -#[derive(Debug)] -pub struct JoinPathsError; - -pub fn join_paths(_paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - 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 { - unsupported() -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -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) { - self.iter.size_hint() - } -} - -pub fn env() -> Env { - unsafe { - let _guard = env_read_lock(); - - // Use `__wasilibc_get_environ` instead of `environ` here so that we - // don't require wasi-libc to eagerly initialize the environment - // variables. - let mut environ = libc::__wasilibc_get_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 { - // environment variables with a nul byte can't be set, so their value is - // always None as well - run_with_cstr(k.as_bytes(), |k| { - let _guard = env_read_lock(); - let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - - if v.is_null() { - Ok(None) - } else { - // SAFETY: `v` cannot be mutated while executing this line since we've a read lock - let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); - - Ok(Some(OsStringExt::from_vec(bytes))) - } - }) - .ok() - .flatten() -} - -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), |k| { - run_with_cstr(v.as_bytes(), |v| unsafe { - let _guard = env_write_lock(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - }) - }) -} - -pub fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), |nbuf| unsafe { - let _guard = env_write_lock(); - cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - }) -} - -#[allow(dead_code)] -pub fn page_size() -> usize { - unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } -} - -pub fn temp_dir() -> PathBuf { - panic!("no filesystem on wasm") -} - -pub fn home_dir() -> Option { - 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: T) -> io::Result { - 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 deleted file mode 100644 index 4cc0e4ed5a4..00000000000 --- a/library/std/src/sys/wasi/stdio.rs +++ /dev/null @@ -1,112 +0,0 @@ -#![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 { - self.read_vectored(&mut [IoSliceMut::new(data)]) - } - - fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result { - 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 { - self.write_vectored(&[IoSlice::new(data)]) - } - - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - 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 { - self.write_vectored(&[IoSlice::new(data)]) - } - - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - 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 { - Some(Stderr::new()) -} diff --git a/library/std/src/sys/wasi/thread.rs b/library/std/src/sys/wasi/thread.rs deleted file mode 100644 index a0eefa8811a..00000000000 --- a/library/std/src/sys/wasi/thread.rs +++ /dev/null @@ -1,201 +0,0 @@ -use crate::ffi::CStr; -use crate::io; -use crate::mem; -use crate::num::NonZeroUsize; -use crate::sys::unsupported; -use crate::time::Duration; - -cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { - use crate::cmp; - use crate::ptr; - use crate::sys::os; - // Add a few symbols not in upstream `libc` just yet. - mod libc { - pub use crate::ffi; - pub use crate::mem; - pub use libc::*; - - // defined in wasi-libc - // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108 - #[repr(C)] - union pthread_attr_union { - __i: [ffi::c_int; if mem::size_of::() == 8 { 14 } else { 9 }], - __vi: [ffi::c_int; if mem::size_of::() == 8 { 14 } else { 9 }], - __s: [ffi::c_ulong; if mem::size_of::() == 8 { 7 } else { 9 }], - } - - #[repr(C)] - pub struct pthread_attr_t { - __u: pthread_attr_union, - } - - #[allow(non_camel_case_types)] - pub type pthread_t = *mut ffi::c_void; - - extern "C" { - pub fn pthread_create( - native: *mut pthread_t, - attr: *const pthread_attr_t, - f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void, - value: *mut ffi::c_void, - ) -> ffi::c_int; - pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int; - pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int; - pub fn pthread_attr_setstacksize( - attr: *mut pthread_attr_t, - stack_size: libc::size_t, - ) -> ffi::c_int; - pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int; - pub fn pthread_detach(thread: pthread_t) -> ffi::c_int; - } - } - - pub struct Thread { - id: libc::pthread_t, - } - - impl Drop for Thread { - fn drop(&mut self) { - let ret = unsafe { libc::pthread_detach(self.id) }; - debug_assert_eq!(ret, 0); - } - } - } else { - pub struct Thread(!); - } -} - -pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { - pub unsafe fn new(stack: usize, p: Box) -> io::Result { - let p = Box::into_raw(Box::new(p)); - let mut native: libc::pthread_t = mem::zeroed(); - let mut attr: libc::pthread_attr_t = mem::zeroed(); - assert_eq!(libc::pthread_attr_init(&mut attr), 0); - - let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE); - - 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 { - // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); - } - ptr::null_mut() - } - } - } else { - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { - 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) { - cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { - unsafe { - let ret = libc::pthread_join(self.id, ptr::null_mut()); - mem::forget(self); - if ret != 0 { - rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); - } - } - } else { - self.0 - } - } - } -} - -pub fn available_parallelism() -> io::Result { - unsupported() -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/wasi/time.rs b/library/std/src/sys/wasi/time.rs deleted file mode 100644 index 016b06efbdc..00000000000 --- a/library/std/src/sys/wasi/time.rs +++ /dev/null @@ -1,65 +0,0 @@ -#![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 { - self.0.checked_sub(other.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_add(*other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - 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(&self) -> Option { - self.0.as_nanos().try_into().ok() - } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_add(*other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - 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 deleted file mode 100644 index 6dceb1689a8..00000000000 --- a/library/std/src/sys/wasm/alloc.rs +++ /dev/null @@ -1,166 +0,0 @@ -//! 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 deleted file mode 100644 index f4fbe9f4855..00000000000 --- a/library/std/src/sys/wasm/atomics/futex.rs +++ /dev/null @@ -1,34 +0,0 @@ -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) -> 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 deleted file mode 100644 index 714b7049227..00000000000 --- a/library/std/src/sys/wasm/atomics/thread.rs +++ /dev/null @@ -1,55 +0,0 @@ -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) -> io::Result { - 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 { - unsupported() -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/wasm/env.rs b/library/std/src/sys/wasm/env.rs deleted file mode 100644 index 730e356d7fe..00000000000 --- a/library/std/src/sys/wasm/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 6c05b56e1bf..00000000000 --- a/library/std/src/sys/wasm/mod.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! 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; - pub(crate) use futex_mutex::Mutex; - pub(crate) use futex_rwlock::RwLock; - } - #[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/once.rs"] - pub mod once; - #[path = "../unsupported/thread.rs"] - pub mod thread; - #[path = "../unsupported/thread_parking.rs"] - pub mod thread_parking; - } -} - -#[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 deleted file mode 100644 index d53ea16005f..00000000000 --- a/library/std/src/sys/windows/alloc.rs +++ /dev/null @@ -1,247 +0,0 @@ -#![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; - -#[link(name = "kernel32")] -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 = 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).sub(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).sub(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 deleted file mode 100644 index 674a3e1d92d..00000000000 --- a/library/std/src/sys/windows/alloc/tests.rs +++ /dev/null @@ -1,9 +0,0 @@ -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::
() <= MIN_ALIGN); - assert!(mem::align_of::
() <= MIN_ALIGN); -} diff --git a/library/std/src/sys/windows/api.rs b/library/std/src/sys/windows/api.rs deleted file mode 100644 index a7ea59e85f7..00000000000 --- a/library/std/src/sys/windows/api.rs +++ /dev/null @@ -1,157 +0,0 @@ -//! # Safe(r) wrappers around Windows API functions. -//! -//! This module contains fairly thin wrappers around Windows API functions, -//! aimed at centralising safety instead of having unsafe blocks spread -//! throughout higher level code. This makes it much easier to audit FFI safety. -//! -//! Not all functions can be made completely safe without more context but in -//! such cases we should still endeavour to reduce the caller's burden of safety -//! as much as possible. -//! -//! ## Guidelines for wrappers -//! -//! Items here should be named similarly to their raw Windows API name, except -//! that they follow Rust's case conventions. E.g. function names are -//! lower_snake_case. The idea here is that it should be easy for a Windows -//! C/C++ programmer to identify the underlying function that's being wrapped -//! while not looking too out of place in Rust code. -//! -//! Every use of an `unsafe` block must have a related SAFETY comment, even if -//! it's trivially safe (for example, see `get_last_error`). Public unsafe -//! functions must document what the caller has to do to call them safely. -//! -//! Avoid unchecked `as` casts. For integers, either assert that the integer -//! is in range or use `try_into` instead. For pointers, prefer to use -//! `ptr.cast::()` when possible. -//! -//! This module must only depend on core and not on std types as the eventual -//! hope is to have std depend on sys and not the other way around. -//! However, some amount of glue code may currently be necessary so such code -//! should go in sys/windows/mod.rs rather than here. See `IoResult` as an example. - -use core::ffi::c_void; -use core::ptr::addr_of; - -use super::c; - -/// Helper method for getting the size of `T` as a u32. -/// Errors at compile time if the size would overflow. -/// -/// While a type larger than u32::MAX is unlikely, it is possible if only because of a bug. -/// However, one key motivation for this function is to avoid the temptation to -/// use frequent `as` casts. This is risky because they are too powerful. -/// For example, the following will compile today: -/// -/// `std::mem::size_of:: as u32` -/// -/// Note that `size_of` is never actually called, instead a function pointer is -/// converted to a `u32`. Clippy would warn about this but, alas, it's not run -/// on the standard library. -const fn win32_size_of() -> u32 { - // Const assert that the size does not exceed u32::MAX. - // Uses a trait to workaround restriction on using generic types in inner items. - trait Win32SizeOf: Sized { - const WIN32_SIZE_OF: u32 = { - let size = core::mem::size_of::(); - assert!(size <= u32::MAX as usize); - size as u32 - }; - } - impl Win32SizeOf for T {} - - T::WIN32_SIZE_OF -} - -/// The `SetFileInformationByHandle` function takes a generic parameter by -/// making the user specify the type (class), a pointer to the data and its -/// size. This trait allows attaching that information to a Rust type so that -/// [`set_file_information_by_handle`] can be called safely. -/// -/// This trait is designed so that it can support variable sized types. -/// However, currently Rust's std only uses fixed sized structures. -/// -/// # Safety -/// -/// * `as_ptr` must return a pointer to memory that is readable up to `size` bytes. -/// * `CLASS` must accurately reflect the type pointed to by `as_ptr`. E.g. -/// the `FILE_BASIC_INFO` structure has the class `FileBasicInfo`. -pub unsafe trait SetFileInformation { - /// The type of information to set. - const CLASS: i32; - /// A pointer to the file information to set. - fn as_ptr(&self) -> *const c_void; - /// The size of the type pointed to by `as_ptr`. - fn size(&self) -> u32; -} -/// Helper trait for implementing `SetFileInformation` for statically sized types. -unsafe trait SizedSetFileInformation: Sized { - const CLASS: i32; -} -unsafe impl SetFileInformation for T { - const CLASS: i32 = T::CLASS; - fn as_ptr(&self) -> *const c_void { - addr_of!(*self).cast::() - } - fn size(&self) -> u32 { - win32_size_of::() - } -} - -// SAFETY: FILE_BASIC_INFO, FILE_END_OF_FILE_INFO, FILE_ALLOCATION_INFO, -// FILE_DISPOSITION_INFO, FILE_DISPOSITION_INFO_EX and FILE_IO_PRIORITY_HINT_INFO -// are all plain `repr(C)` structs that only contain primitive types. -// The given information classes correctly match with the struct. -unsafe impl SizedSetFileInformation for c::FILE_BASIC_INFO { - const CLASS: i32 = c::FileBasicInfo; -} -unsafe impl SizedSetFileInformation for c::FILE_END_OF_FILE_INFO { - const CLASS: i32 = c::FileEndOfFileInfo; -} -unsafe impl SizedSetFileInformation for c::FILE_ALLOCATION_INFO { - const CLASS: i32 = c::FileAllocationInfo; -} -unsafe impl SizedSetFileInformation for c::FILE_DISPOSITION_INFO { - const CLASS: i32 = c::FileDispositionInfo; -} -unsafe impl SizedSetFileInformation for c::FILE_DISPOSITION_INFO_EX { - const CLASS: i32 = c::FileDispositionInfoEx; -} -unsafe impl SizedSetFileInformation for c::FILE_IO_PRIORITY_HINT_INFO { - const CLASS: i32 = c::FileIoPriorityHintInfo; -} - -#[inline] -pub fn set_file_information_by_handle( - handle: c::HANDLE, - info: &T, -) -> Result<(), WinError> { - unsafe fn set_info( - handle: c::HANDLE, - class: i32, - info: *const c_void, - size: u32, - ) -> Result<(), WinError> { - let result = c::SetFileInformationByHandle(handle, class, info, size); - (result != 0).then_some(()).ok_or_else(get_last_error) - } - // SAFETY: The `SetFileInformation` trait ensures that this is safe. - unsafe { set_info(handle, T::CLASS, info.as_ptr(), info.size()) } -} - -/// Gets the error from the last function. -/// This must be called immediately after the function that sets the error to -/// avoid the risk of another function overwriting it. -pub fn get_last_error() -> WinError { - // SAFETY: This just returns a thread-local u32 and has no other effects. - unsafe { WinError { code: c::GetLastError() } } -} - -/// An error code as returned by [`get_last_error`]. -/// -/// This is usually a 16-bit Win32 error code but may be a 32-bit HRESULT or NTSTATUS. -/// Check the documentation of the Windows API function being called for expected errors. -#[derive(Clone, Copy, PartialEq, Eq)] -#[repr(transparent)] -pub struct WinError { - pub code: u32, -} diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs deleted file mode 100644 index ee7dba6e5b3..00000000000 --- a/library/std/src/sys/windows/args.rs +++ /dev/null @@ -1,379 +0,0 @@ -//! 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::num::NonZeroU16; -use crate::os::windows::prelude::*; -use crate::path::{Path, PathBuf}; -use crate::sys::path::get_long_path; -use crate::sys::process::ensure_no_nuls; -use crate::sys::windows::os::current_exe; -use crate::sys::{c, to_u16s}; -use crate::sys_common::wstr::WStrUnits; -use crate::vec; - -use crate::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 -/// -/// -/// A more in-depth explanation is here: -/// -/// -/// 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 -/// . -/// -/// 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 -/// . -fn parse_lp_cmd_line<'a, F: Fn() -> OsString>( - lp_cmd_line: Option>, - exe_name: F, -) -> Vec { - 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, -} - -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 { - self.parsed_args_list.next() - } - fn size_hint(&self) -> (usize, Option) { - self.parsed_args_list.size_hint() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.parsed_args_list.next_back() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.parsed_args_list.len() - } -} - -#[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, 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.as_encoded_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> { - // 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 = "cmd.exe /d /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); - // Make sure to always quote special command prompt characters, including: - // * Characters `cmd /?` says require quotes. - // * `%` for environment variables, as in `%TMP%`. - // * `|<>` pipe/redirect characters. - const SPECIAL: &[u8] = b"\t &()[]{}^=;!'+,`~%|<>"; - let force_quotes = match arg { - Arg::Regular(arg) if !force_quotes => { - arg.as_encoded_bytes().iter().any(|c| SPECIAL.contains(c)) - } - _ => force_quotes, - }; - 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(path: &Path) -> io::Result> { - from_wide_to_user_path(to_u16s(path)?) -} -pub(crate) fn from_wide_to_user_path(mut path: Vec) -> io::Result> { - 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] { - let mut path: Vec = full_path.into(); - path.push(0); - path - } 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] { - let mut path: Vec = full_path.into(); - path.push(0); - path - } else { - // Restore the 'C' in "UNC". - path[6] = b'C' as u16; - path - } - }, - ) - }, - // For everything else, leave the path unchanged. - _ => get_long_path(path, false), - } -} diff --git a/library/std/src/sys/windows/args/tests.rs b/library/std/src/sys/windows/args/tests.rs deleted file mode 100644 index 82c32d08c5e..00000000000 --- a/library/std/src/sys/windows/args/tests.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::ffi::OsString; -use crate::sys::windows::args::*; - -fn chk(string: &str, parts: &[&str]) { - let mut wide: Vec = 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 = 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 deleted file mode 100644 index d55d9bace81..00000000000 --- a/library/std/src/sys/windows/c.rs +++ /dev/null @@ -1,480 +0,0 @@ -//! 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")] -#![allow(clippy::style)] - -use crate::ffi::CStr; -use crate::mem; -pub use crate::os::raw::c_int; -use crate::os::raw::{c_char, c_long, c_longlong, c_uint, c_ulong, c_ushort, c_void}; -use crate::os::windows::io::{AsRawHandle, BorrowedHandle}; -use crate::ptr; -use core::ffi::NonZero_c_ulong; - -mod windows_sys; -pub use windows_sys::*; - -pub type DWORD = c_ulong; -pub type NonZeroDWORD = NonZero_c_ulong; -pub type LARGE_INTEGER = c_longlong; -#[cfg_attr(target_vendor = "uwp", allow(unused))] -pub type LONG = c_long; -pub type UINT = c_uint; -pub type WCHAR = u16; -pub type USHORT = c_ushort; -pub type SIZE_T = usize; -pub type WORD = u16; -pub type CHAR = c_char; -pub type ULONG = c_ulong; -pub type ACCESS_MASK = DWORD; - -pub type LPCVOID = *const c_void; -pub type LPHANDLE = *mut HANDLE; -pub type LPOVERLAPPED = *mut OVERLAPPED; -pub type LPSECURITY_ATTRIBUTES = *mut SECURITY_ATTRIBUTES; -pub type LPVOID = *mut c_void; -pub type LPWCH = *mut WCHAR; -pub type LPWSTR = *mut WCHAR; - -pub type PLARGE_INTEGER = *mut c_longlong; -pub type PSRWLOCK = *mut SRWLOCK; - -pub type socklen_t = c_int; -pub type ADDRESS_FAMILY = USHORT; -pub use FD_SET as fd_set; -pub use LINGER as linger; -pub use TIMEVAL as timeval; - -pub const INVALID_HANDLE_VALUE: HANDLE = ::core::ptr::invalid_mut(-1i32 as _); - -// https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure?view=msvc-170 -pub const EXIT_SUCCESS: u32 = 0; -pub const EXIT_FAILURE: u32 = 1; - -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 INIT_ONCE_STATIC_INIT: INIT_ONCE = INIT_ONCE { Ptr: ptr::null_mut() }; - -// Some windows_sys types have different signs than the types we use. -pub const OBJ_DONT_REPARSE: u32 = windows_sys::OBJ_DONT_REPARSE as u32; -pub const FRS_ERR_SYSVOL_POPULATE_TIMEOUT: u32 = - windows_sys::FRS_ERR_SYSVOL_POPULATE_TIMEOUT as u32; -pub const AF_INET: c_int = windows_sys::AF_INET as c_int; -pub const AF_INET6: c_int = windows_sys::AF_INET6 as c_int; - -#[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, -} - -// 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 -} - -impl UNICODE_STRING { - pub fn from_ref(slice: &[u16]) -> Self { - let len = mem::size_of_val(slice); - Self { Length: len as _, MaximumLength: len as _, Buffer: slice.as_ptr() as _ } - } -} - -impl Default for OBJECT_ATTRIBUTES { - fn default() -> Self { - Self { - Length: mem::size_of::() as _, - RootDirectory: ptr::null_mut(), - ObjectName: ptr::null_mut(), - Attributes: 0, - SecurityDescriptor: ptr::null_mut(), - SecurityQualityOfService: ptr::null_mut(), - } - } -} - -impl IO_STATUS_BLOCK { - pub const PENDING: Self = - IO_STATUS_BLOCK { Anonymous: IO_STATUS_BLOCK_0 { Status: STATUS_PENDING }, Information: 0 }; - pub fn status(&self) -> NTSTATUS { - // SAFETY: If `self.Anonymous.Status` was set then this is obviously safe. - // If `self.Anonymous.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.Anonymous.Status } - } -} - -/// NB: Use carefully! In general using this as a reference is likely to get the -/// provenance wrong for the `rest` field! -#[repr(C)] -pub struct REPARSE_DATA_BUFFER { - pub ReparseTag: c_uint, - pub ReparseDataLength: c_ushort, - pub Reserved: c_ushort, - pub rest: (), -} - -/// NB: Use carefully! In general using this as a reference is likely to get the -/// provenance wrong for the `PathBuffer` field! -#[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, -} -#[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 SOCKADDR_STORAGE_LH { - pub ss_family: ADDRESS_FAMILY, - pub __ss_pad1: [CHAR; 6], - pub __ss_align: i64, - pub __ss_pad2: [CHAR; 112], -} - -#[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], -} - -// Desktop specific functions & types -cfg_if::cfg_if! { -if #[cfg(not(target_vendor = "uwp"))] { - pub const EXCEPTION_CONTINUE_SEARCH: i32 = 0; -} -} - -pub unsafe extern "system" fn WriteFileEx( - hFile: BorrowedHandle<'_>, - lpBuffer: *mut ::core::ffi::c_void, - nNumberOfBytesToWrite: u32, - lpOverlapped: *mut OVERLAPPED, - lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, -) -> BOOL { - windows_sys::WriteFileEx( - hFile.as_raw_handle(), - lpBuffer.cast::(), - nNumberOfBytesToWrite, - lpOverlapped, - lpCompletionRoutine, - ) -} - -pub unsafe extern "system" fn ReadFileEx( - hFile: BorrowedHandle<'_>, - lpBuffer: *mut ::core::ffi::c_void, - nNumberOfBytesToRead: u32, - lpOverlapped: *mut OVERLAPPED, - lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, -) -> BOOL { - windows_sys::ReadFileEx( - hFile.as_raw_handle(), - lpBuffer.cast::(), - nNumberOfBytesToRead, - lpOverlapped, - lpCompletionRoutine, - ) -} - -// POSIX compatibility shims. -pub unsafe fn recv(socket: SOCKET, buf: *mut c_void, len: c_int, flags: c_int) -> c_int { - windows_sys::recv(socket, buf.cast::(), len, flags) -} -pub unsafe fn send(socket: SOCKET, buf: *const c_void, len: c_int, flags: c_int) -> c_int { - windows_sys::send(socket, buf.cast::(), len, flags) -} -pub unsafe fn recvfrom( - socket: SOCKET, - buf: *mut c_void, - len: c_int, - flags: c_int, - addr: *mut SOCKADDR, - addrlen: *mut c_int, -) -> c_int { - windows_sys::recvfrom(socket, buf.cast::(), len, flags, addr, addrlen) -} -pub unsafe fn sendto( - socket: SOCKET, - buf: *const c_void, - len: c_int, - flags: c_int, - addr: *const SOCKADDR, - addrlen: c_int, -) -> c_int { - windows_sys::sendto(socket, buf.cast::(), len, flags, addr, addrlen) -} -pub unsafe fn getaddrinfo( - node: *const c_char, - service: *const c_char, - hints: *const ADDRINFOA, - res: *mut *mut ADDRINFOA, -) -> c_int { - windows_sys::getaddrinfo(node.cast::(), service.cast::(), hints, res) -} - -cfg_if::cfg_if! { -if #[cfg(not(target_vendor = "uwp"))] { -pub unsafe fn NtReadFile( - filehandle: BorrowedHandle<'_>, - event: HANDLE, - apcroutine: PIO_APC_ROUTINE, - apccontext: *mut c_void, - iostatusblock: &mut IO_STATUS_BLOCK, - buffer: *mut crate::mem::MaybeUninit, - length: ULONG, - byteoffset: Option<&LARGE_INTEGER>, - key: Option<&ULONG>, -) -> NTSTATUS { - windows_sys::NtReadFile( - filehandle.as_raw_handle(), - event, - apcroutine, - apccontext, - iostatusblock, - buffer.cast::(), - length, - byteoffset.map(|o| o as *const i64).unwrap_or(ptr::null()), - key.map(|k| k as *const u32).unwrap_or(ptr::null()), - ) -} -pub unsafe fn NtWriteFile( - filehandle: BorrowedHandle<'_>, - event: HANDLE, - apcroutine: PIO_APC_ROUTINE, - apccontext: *mut c_void, - iostatusblock: &mut IO_STATUS_BLOCK, - buffer: *const u8, - length: ULONG, - byteoffset: Option<&LARGE_INTEGER>, - key: Option<&ULONG>, -) -> NTSTATUS { - windows_sys::NtWriteFile( - filehandle.as_raw_handle(), - event, - apcroutine, - apccontext, - iostatusblock, - buffer.cast::(), - length, - byteoffset.map(|o| o as *const i64).unwrap_or(ptr::null()), - key.map(|k| k as *const u32).unwrap_or(ptr::null()), - ) -} -} -} - -// 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 = c"kernel32"; - - // >= Win10 1607 - // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription - pub fn SetThreadDescription(hthread: HANDLE, lpthreaddescription: PCWSTR) -> 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: *mut FILETIME) -> () { - GetSystemTimeAsFileTime(lpsystemtimeasfiletime) - } - - // >= Win11 / Server 2022 - // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2a - pub fn GetTempPath2W(bufferlength: u32, buffer: PWSTR) -> u32 { - GetTempPathW(bufferlength, buffer) - } -} - -compat_fn_optional! { - crate::sys::compat::load_synch_functions(); - pub fn WaitOnAddress( - address: *const ::core::ffi::c_void, - compareaddress: *const ::core::ffi::c_void, - addresssize: usize, - dwmilliseconds: u32 - ) -> BOOL; - pub fn WakeByAddressSingle(address: *const ::core::ffi::c_void); -} - -compat_fn_with_fallback! { - pub static NTDLL: &CStr = c"ntdll"; - - 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") - } - - // These functions are available on UWP when lazily loaded. They will fail WACK if loaded statically. - #[cfg(target_vendor = "uwp")] - pub fn NtCreateFile( - filehandle: *mut HANDLE, - desiredaccess: FILE_ACCESS_RIGHTS, - objectattributes: *const OBJECT_ATTRIBUTES, - iostatusblock: *mut IO_STATUS_BLOCK, - allocationsize: *const i64, - fileattributes: FILE_FLAGS_AND_ATTRIBUTES, - shareaccess: FILE_SHARE_MODE, - createdisposition: NTCREATEFILE_CREATE_DISPOSITION, - createoptions: NTCREATEFILE_CREATE_OPTIONS, - eabuffer: *const ::core::ffi::c_void, - ealength: u32 - ) -> NTSTATUS { - STATUS_NOT_IMPLEMENTED - } - #[cfg(target_vendor = "uwp")] - pub fn NtReadFile( - filehandle: BorrowedHandle<'_>, - event: HANDLE, - apcroutine: PIO_APC_ROUTINE, - apccontext: *mut c_void, - iostatusblock: &mut IO_STATUS_BLOCK, - buffer: *mut crate::mem::MaybeUninit, - length: ULONG, - byteoffset: Option<&LARGE_INTEGER>, - key: Option<&ULONG> - ) -> NTSTATUS { - STATUS_NOT_IMPLEMENTED - } - #[cfg(target_vendor = "uwp")] - pub fn NtWriteFile( - filehandle: BorrowedHandle<'_>, - event: HANDLE, - apcroutine: PIO_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 - } - #[cfg(target_vendor = "uwp")] - pub fn RtlNtStatusToDosError(Status: NTSTATUS) -> u32 { - Status as u32 - } -} - -// # Arm32 shim -// -// AddVectoredExceptionHandler and WSAStartup use platform-specific types. -// However, Microsoft no longer supports thumbv7a so definitions for those targets -// are not included in the win32 metadata. We work around that by defining them here. -// -// Where possible, these definitions should be kept in sync with https://docs.rs/windows-sys -cfg_if::cfg_if! { -if #[cfg(not(target_vendor = "uwp"))] { - #[link(name = "kernel32")] - extern "system" { - pub fn AddVectoredExceptionHandler( - first: u32, - handler: PVECTORED_EXCEPTION_HANDLER, - ) -> *mut c_void; - } - pub type PVECTORED_EXCEPTION_HANDLER = Option< - unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32, - >; - #[repr(C)] - pub struct EXCEPTION_POINTERS { - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ContextRecord: *mut CONTEXT, - } - #[cfg(target_arch = "arm")] - pub enum CONTEXT {} -}} - -#[link(name = "ws2_32")] -extern "system" { - pub fn WSAStartup(wversionrequested: u16, lpwsadata: *mut WSADATA) -> i32; -} -#[cfg(target_arch = "arm")] -#[repr(C)] -pub struct WSADATA { - pub wVersion: u16, - pub wHighVersion: u16, - pub szDescription: [u8; 257], - pub szSystemStatus: [u8; 129], - pub iMaxSockets: u16, - pub iMaxUdpDg: u16, - pub lpVendorInfo: PSTR, -} diff --git a/library/std/src/sys/windows/c/windows_sys.lst b/library/std/src/sys/windows/c/windows_sys.lst deleted file mode 100644 index f91e1054a04..00000000000 --- a/library/std/src/sys/windows/c/windows_sys.lst +++ /dev/null @@ -1,2596 +0,0 @@ ---out windows_sys.rs ---config flatten std ---filter -// tidy-alphabetical-start -!Windows.Win32.Foundation.INVALID_HANDLE_VALUE -Windows.Wdk.Storage.FileSystem.FILE_COMPLETE_IF_OPLOCKED -Windows.Wdk.Storage.FileSystem.FILE_CONTAINS_EXTENDED_CREATE_INFORMATION -Windows.Wdk.Storage.FileSystem.FILE_CREATE -Windows.Wdk.Storage.FileSystem.FILE_CREATE_TREE_CONNECTION -Windows.Wdk.Storage.FileSystem.FILE_DELETE_ON_CLOSE -Windows.Wdk.Storage.FileSystem.FILE_DIRECTORY_FILE -Windows.Wdk.Storage.FileSystem.FILE_DISALLOW_EXCLUSIVE -Windows.Wdk.Storage.FileSystem.FILE_NO_COMPRESSION -Windows.Wdk.Storage.FileSystem.FILE_NO_EA_KNOWLEDGE -Windows.Wdk.Storage.FileSystem.FILE_NO_INTERMEDIATE_BUFFERING -Windows.Wdk.Storage.FileSystem.FILE_NON_DIRECTORY_FILE -Windows.Wdk.Storage.FileSystem.FILE_OPEN -Windows.Wdk.Storage.FileSystem.FILE_OPEN_BY_FILE_ID -Windows.Wdk.Storage.FileSystem.FILE_OPEN_FOR_BACKUP_INTENT -Windows.Wdk.Storage.FileSystem.FILE_OPEN_FOR_FREE_SPACE_QUERY -Windows.Wdk.Storage.FileSystem.FILE_OPEN_IF -Windows.Wdk.Storage.FileSystem.FILE_OPEN_NO_RECALL -Windows.Wdk.Storage.FileSystem.FILE_OPEN_REPARSE_POINT -Windows.Wdk.Storage.FileSystem.FILE_OPEN_REQUIRING_OPLOCK -Windows.Wdk.Storage.FileSystem.FILE_OVERWRITE -Windows.Wdk.Storage.FileSystem.FILE_OVERWRITE_IF -Windows.Wdk.Storage.FileSystem.FILE_RANDOM_ACCESS -Windows.Wdk.Storage.FileSystem.FILE_RESERVE_OPFILTER -Windows.Wdk.Storage.FileSystem.FILE_SEQUENTIAL_ONLY -Windows.Wdk.Storage.FileSystem.FILE_SESSION_AWARE -Windows.Wdk.Storage.FileSystem.FILE_SUPERSEDE -Windows.Wdk.Storage.FileSystem.FILE_SYNCHRONOUS_IO_ALERT -Windows.Wdk.Storage.FileSystem.FILE_SYNCHRONOUS_IO_NONALERT -Windows.Wdk.Storage.FileSystem.FILE_WRITE_THROUGH -Windows.Wdk.Storage.FileSystem.NtCreateFile -Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_DISPOSITION -Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_OPTIONS -Windows.Wdk.Storage.FileSystem.NtReadFile -Windows.Wdk.Storage.FileSystem.NtWriteFile -Windows.Wdk.Storage.FileSystem.SYMLINK_FLAG_RELATIVE -Windows.Win32.Foundation.BOOL -Windows.Win32.Foundation.BOOLEAN -Windows.Win32.Foundation.CloseHandle -Windows.Win32.Foundation.DNS_ERROR_ADDRESS_REQUIRED -Windows.Win32.Foundation.DNS_ERROR_ALIAS_LOOP -Windows.Win32.Foundation.DNS_ERROR_AUTOZONE_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_AXFR -Windows.Win32.Foundation.DNS_ERROR_BACKGROUND_LOADING -Windows.Win32.Foundation.DNS_ERROR_BAD_KEYMASTER -Windows.Win32.Foundation.DNS_ERROR_BAD_PACKET -Windows.Win32.Foundation.DNS_ERROR_CANNOT_FIND_ROOT_HINTS -Windows.Win32.Foundation.DNS_ERROR_CLIENT_SUBNET_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_CLIENT_SUBNET_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_CLIENT_SUBNET_IS_ACCESSED -Windows.Win32.Foundation.DNS_ERROR_CNAME_COLLISION -Windows.Win32.Foundation.DNS_ERROR_CNAME_LOOP -Windows.Win32.Foundation.DNS_ERROR_DATAFILE_OPEN_FAILURE -Windows.Win32.Foundation.DNS_ERROR_DATAFILE_PARSING -Windows.Win32.Foundation.DNS_ERROR_DEFAULT_SCOPE -Windows.Win32.Foundation.DNS_ERROR_DEFAULT_VIRTUALIZATION_INSTANCE -Windows.Win32.Foundation.DNS_ERROR_DEFAULT_ZONESCOPE -Windows.Win32.Foundation.DNS_ERROR_DELEGATION_REQUIRED -Windows.Win32.Foundation.DNS_ERROR_DNAME_COLLISION -Windows.Win32.Foundation.DNS_ERROR_DNSSEC_IS_DISABLED -Windows.Win32.Foundation.DNS_ERROR_DP_ALREADY_ENLISTED -Windows.Win32.Foundation.DNS_ERROR_DP_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_DP_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_DP_FSMO_ERROR -Windows.Win32.Foundation.DNS_ERROR_DP_NOT_AVAILABLE -Windows.Win32.Foundation.DNS_ERROR_DP_NOT_ENLISTED -Windows.Win32.Foundation.DNS_ERROR_DS_UNAVAILABLE -Windows.Win32.Foundation.DNS_ERROR_DS_ZONE_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_DWORD_VALUE_TOO_LARGE -Windows.Win32.Foundation.DNS_ERROR_DWORD_VALUE_TOO_SMALL -Windows.Win32.Foundation.DNS_ERROR_FILE_WRITEBACK_FAILED -Windows.Win32.Foundation.DNS_ERROR_FORWARDER_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_INCONSISTENT_ROOT_HINTS -Windows.Win32.Foundation.DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME -Windows.Win32.Foundation.DNS_ERROR_INVALID_CLIENT_SUBNET_NAME -Windows.Win32.Foundation.DNS_ERROR_INVALID_DATA -Windows.Win32.Foundation.DNS_ERROR_INVALID_DATAFILE_NAME -Windows.Win32.Foundation.DNS_ERROR_INVALID_INITIAL_ROLLOVER_OFFSET -Windows.Win32.Foundation.DNS_ERROR_INVALID_IP_ADDRESS -Windows.Win32.Foundation.DNS_ERROR_INVALID_KEY_SIZE -Windows.Win32.Foundation.DNS_ERROR_INVALID_NAME -Windows.Win32.Foundation.DNS_ERROR_INVALID_NAME_CHAR -Windows.Win32.Foundation.DNS_ERROR_INVALID_NSEC3_ITERATION_COUNT -Windows.Win32.Foundation.DNS_ERROR_INVALID_POLICY_TABLE -Windows.Win32.Foundation.DNS_ERROR_INVALID_PROPERTY -Windows.Win32.Foundation.DNS_ERROR_INVALID_ROLLOVER_PERIOD -Windows.Win32.Foundation.DNS_ERROR_INVALID_SCOPE_NAME -Windows.Win32.Foundation.DNS_ERROR_INVALID_SCOPE_OPERATION -Windows.Win32.Foundation.DNS_ERROR_INVALID_SIGNATURE_VALIDITY_PERIOD -Windows.Win32.Foundation.DNS_ERROR_INVALID_TYPE -Windows.Win32.Foundation.DNS_ERROR_INVALID_XML -Windows.Win32.Foundation.DNS_ERROR_INVALID_ZONE_OPERATION -Windows.Win32.Foundation.DNS_ERROR_INVALID_ZONE_TYPE -Windows.Win32.Foundation.DNS_ERROR_INVALID_ZONESCOPE_NAME -Windows.Win32.Foundation.DNS_ERROR_KEYMASTER_REQUIRED -Windows.Win32.Foundation.DNS_ERROR_KSP_DOES_NOT_SUPPORT_PROTECTION -Windows.Win32.Foundation.DNS_ERROR_KSP_NOT_ACCESSIBLE -Windows.Win32.Foundation.DNS_ERROR_LOAD_ZONESCOPE_FAILED -Windows.Win32.Foundation.DNS_ERROR_NAME_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_NAME_NOT_IN_ZONE -Windows.Win32.Foundation.DNS_ERROR_NBSTAT_INIT_FAILED -Windows.Win32.Foundation.DNS_ERROR_NEED_SECONDARY_ADDRESSES -Windows.Win32.Foundation.DNS_ERROR_NEED_WINS_SERVERS -Windows.Win32.Foundation.DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE -Windows.Win32.Foundation.DNS_ERROR_NO_CREATE_CACHE_DATA -Windows.Win32.Foundation.DNS_ERROR_NO_DNS_SERVERS -Windows.Win32.Foundation.DNS_ERROR_NO_MEMORY -Windows.Win32.Foundation.DNS_ERROR_NO_PACKET -Windows.Win32.Foundation.DNS_ERROR_NO_TCPIP -Windows.Win32.Foundation.DNS_ERROR_NO_VALID_TRUST_ANCHORS -Windows.Win32.Foundation.DNS_ERROR_NO_ZONE_INFO -Windows.Win32.Foundation.DNS_ERROR_NODE_CREATION_FAILED -Windows.Win32.Foundation.DNS_ERROR_NODE_IS_CNAME -Windows.Win32.Foundation.DNS_ERROR_NODE_IS_DNAME -Windows.Win32.Foundation.DNS_ERROR_NON_RFC_NAME -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_ACTIVE_SKD -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_RODC -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_SIGNED_ZONE -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_UNSIGNED_ZONE -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_ZSK -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_UNDER_DNAME -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_WITH_ZONESCOPES -Windows.Win32.Foundation.DNS_ERROR_NOT_ENOUGH_SIGNING_KEY_DESCRIPTORS -Windows.Win32.Foundation.DNS_ERROR_NOT_UNIQUE -Windows.Win32.Foundation.DNS_ERROR_NSEC3_INCOMPATIBLE_WITH_RSA_SHA1 -Windows.Win32.Foundation.DNS_ERROR_NSEC3_NAME_COLLISION -Windows.Win32.Foundation.DNS_ERROR_NSEC_INCOMPATIBLE_WITH_NSEC3_RSA_SHA1 -Windows.Win32.Foundation.DNS_ERROR_NUMERIC_NAME -Windows.Win32.Foundation.DNS_ERROR_POLICY_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_POLICY_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_CLIENT_SUBNET -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_FQDN -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_INTERFACE -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_NETWORK_PROTOCOL -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_QUERY_TYPE -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_TIME_OF_DAY -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_TRANSPORT_PROTOCOL -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_NAME -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_SETTINGS -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_WEIGHT -Windows.Win32.Foundation.DNS_ERROR_POLICY_LOCKED -Windows.Win32.Foundation.DNS_ERROR_POLICY_MISSING_CRITERIA -Windows.Win32.Foundation.DNS_ERROR_POLICY_PROCESSING_ORDER_INVALID -Windows.Win32.Foundation.DNS_ERROR_POLICY_SCOPE_MISSING -Windows.Win32.Foundation.DNS_ERROR_POLICY_SCOPE_NOT_ALLOWED -Windows.Win32.Foundation.DNS_ERROR_PRIMARY_REQUIRES_DATAFILE -Windows.Win32.Foundation.DNS_ERROR_RCODE -Windows.Win32.Foundation.DNS_ERROR_RCODE_BADKEY -Windows.Win32.Foundation.DNS_ERROR_RCODE_BADSIG -Windows.Win32.Foundation.DNS_ERROR_RCODE_BADTIME -Windows.Win32.Foundation.DNS_ERROR_RCODE_FORMAT_ERROR -Windows.Win32.Foundation.DNS_ERROR_RCODE_NAME_ERROR -Windows.Win32.Foundation.DNS_ERROR_RCODE_NOT_IMPLEMENTED -Windows.Win32.Foundation.DNS_ERROR_RCODE_NOTAUTH -Windows.Win32.Foundation.DNS_ERROR_RCODE_NOTZONE -Windows.Win32.Foundation.DNS_ERROR_RCODE_NXRRSET -Windows.Win32.Foundation.DNS_ERROR_RCODE_REFUSED -Windows.Win32.Foundation.DNS_ERROR_RCODE_SERVER_FAILURE -Windows.Win32.Foundation.DNS_ERROR_RCODE_YXDOMAIN -Windows.Win32.Foundation.DNS_ERROR_RCODE_YXRRSET -Windows.Win32.Foundation.DNS_ERROR_RECORD_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_RECORD_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_RECORD_FORMAT -Windows.Win32.Foundation.DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT -Windows.Win32.Foundation.DNS_ERROR_RECORD_TIMED_OUT -Windows.Win32.Foundation.DNS_ERROR_ROLLOVER_ALREADY_QUEUED -Windows.Win32.Foundation.DNS_ERROR_ROLLOVER_IN_PROGRESS -Windows.Win32.Foundation.DNS_ERROR_ROLLOVER_NOT_POKEABLE -Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_IPV4_PREFIX -Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_IPV6_PREFIX -Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_LEAK_RATE -Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_TC_RATE -Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_WINDOW_SIZE -Windows.Win32.Foundation.DNS_ERROR_RRL_LEAK_RATE_LESSTHAN_TC_RATE -Windows.Win32.Foundation.DNS_ERROR_RRL_NOT_ENABLED -Windows.Win32.Foundation.DNS_ERROR_SCOPE_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_SCOPE_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_SCOPE_LOCKED -Windows.Win32.Foundation.DNS_ERROR_SECONDARY_DATA -Windows.Win32.Foundation.DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP -Windows.Win32.Foundation.DNS_ERROR_SERVERSCOPE_IS_REFERENCED -Windows.Win32.Foundation.DNS_ERROR_SIGNING_KEY_NOT_ACCESSIBLE -Windows.Win32.Foundation.DNS_ERROR_SOA_DELETE_INVALID -Windows.Win32.Foundation.DNS_ERROR_STANDBY_KEY_NOT_PRESENT -Windows.Win32.Foundation.DNS_ERROR_SUBNET_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_SUBNET_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_TOO_MANY_SKDS -Windows.Win32.Foundation.DNS_ERROR_TRY_AGAIN_LATER -Windows.Win32.Foundation.DNS_ERROR_UNEXPECTED_CNG_ERROR -Windows.Win32.Foundation.DNS_ERROR_UNEXPECTED_DATA_PROTECTION_ERROR -Windows.Win32.Foundation.DNS_ERROR_UNKNOWN_RECORD_TYPE -Windows.Win32.Foundation.DNS_ERROR_UNKNOWN_SIGNING_PARAMETER_VERSION -Windows.Win32.Foundation.DNS_ERROR_UNSECURE_PACKET -Windows.Win32.Foundation.DNS_ERROR_UNSUPPORTED_ALGORITHM -Windows.Win32.Foundation.DNS_ERROR_VIRTUALIZATION_INSTANCE_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_VIRTUALIZATION_INSTANCE_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_VIRTUALIZATION_TREE_LOCKED -Windows.Win32.Foundation.DNS_ERROR_WINS_INIT_FAILED -Windows.Win32.Foundation.DNS_ERROR_ZONE_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_ZONE_CONFIGURATION_ERROR -Windows.Win32.Foundation.DNS_ERROR_ZONE_CREATION_FAILED -Windows.Win32.Foundation.DNS_ERROR_ZONE_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_ZONE_HAS_NO_NS_RECORDS -Windows.Win32.Foundation.DNS_ERROR_ZONE_HAS_NO_SOA_RECORD -Windows.Win32.Foundation.DNS_ERROR_ZONE_IS_SHUTDOWN -Windows.Win32.Foundation.DNS_ERROR_ZONE_LOCKED -Windows.Win32.Foundation.DNS_ERROR_ZONE_LOCKED_FOR_SIGNING -Windows.Win32.Foundation.DNS_ERROR_ZONE_NOT_SECONDARY -Windows.Win32.Foundation.DNS_ERROR_ZONE_REQUIRES_MASTER_IP -Windows.Win32.Foundation.DNS_ERROR_ZONESCOPE_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_ZONESCOPE_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_ZONESCOPE_FILE_WRITEBACK_FAILED -Windows.Win32.Foundation.DNS_ERROR_ZONESCOPE_IS_REFERENCED -Windows.Win32.Foundation.DUPLICATE_CLOSE_SOURCE -Windows.Win32.Foundation.DUPLICATE_HANDLE_OPTIONS -Windows.Win32.Foundation.DUPLICATE_SAME_ACCESS -Windows.Win32.Foundation.DuplicateHandle -Windows.Win32.Foundation.E_NOTIMPL -Windows.Win32.Foundation.ERROR_ABANDON_HIBERFILE -Windows.Win32.Foundation.ERROR_ABANDONED_WAIT_0 -Windows.Win32.Foundation.ERROR_ABANDONED_WAIT_63 -Windows.Win32.Foundation.ERROR_ABIOS_ERROR -Windows.Win32.Foundation.ERROR_ACCESS_AUDIT_BY_POLICY -Windows.Win32.Foundation.ERROR_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_ACCESS_DENIED_APPDATA -Windows.Win32.Foundation.ERROR_ACCESS_DISABLED_BY_POLICY -Windows.Win32.Foundation.ERROR_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY -Windows.Win32.Foundation.ERROR_ACCESS_DISABLED_WEBBLADE -Windows.Win32.Foundation.ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER -Windows.Win32.Foundation.ERROR_ACCOUNT_DISABLED -Windows.Win32.Foundation.ERROR_ACCOUNT_EXPIRED -Windows.Win32.Foundation.ERROR_ACCOUNT_LOCKED_OUT -Windows.Win32.Foundation.ERROR_ACCOUNT_RESTRICTION -Windows.Win32.Foundation.ERROR_ACPI_ERROR -Windows.Win32.Foundation.ERROR_ACTIVE_CONNECTIONS -Windows.Win32.Foundation.ERROR_ADAP_HDW_ERR -Windows.Win32.Foundation.ERROR_ADDRESS_ALREADY_ASSOCIATED -Windows.Win32.Foundation.ERROR_ADDRESS_NOT_ASSOCIATED -Windows.Win32.Foundation.ERROR_ALERTED -Windows.Win32.Foundation.ERROR_ALIAS_EXISTS -Windows.Win32.Foundation.ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_ALLOCATE_BUCKET -Windows.Win32.Foundation.ERROR_ALLOTTED_SPACE_EXCEEDED -Windows.Win32.Foundation.ERROR_ALREADY_ASSIGNED -Windows.Win32.Foundation.ERROR_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_ALREADY_FIBER -Windows.Win32.Foundation.ERROR_ALREADY_HAS_STREAM_ID -Windows.Win32.Foundation.ERROR_ALREADY_INITIALIZED -Windows.Win32.Foundation.ERROR_ALREADY_REGISTERED -Windows.Win32.Foundation.ERROR_ALREADY_RUNNING_LKG -Windows.Win32.Foundation.ERROR_ALREADY_THREAD -Windows.Win32.Foundation.ERROR_ALREADY_WAITING -Windows.Win32.Foundation.ERROR_ALREADY_WIN32 -Windows.Win32.Foundation.ERROR_API_UNAVAILABLE -Windows.Win32.Foundation.ERROR_APP_HANG -Windows.Win32.Foundation.ERROR_APP_INIT_FAILURE -Windows.Win32.Foundation.ERROR_APP_WRONG_OS -Windows.Win32.Foundation.ERROR_APPCONTAINER_REQUIRED -Windows.Win32.Foundation.ERROR_APPEXEC_APP_COMPAT_BLOCK -Windows.Win32.Foundation.ERROR_APPEXEC_CALLER_WAIT_TIMEOUT -Windows.Win32.Foundation.ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_LICENSING -Windows.Win32.Foundation.ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_RESOURCES -Windows.Win32.Foundation.ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_TERMINATION -Windows.Win32.Foundation.ERROR_APPEXEC_CONDITION_NOT_SATISFIED -Windows.Win32.Foundation.ERROR_APPEXEC_HANDLE_INVALIDATED -Windows.Win32.Foundation.ERROR_APPEXEC_HOST_ID_MISMATCH -Windows.Win32.Foundation.ERROR_APPEXEC_INVALID_HOST_GENERATION -Windows.Win32.Foundation.ERROR_APPEXEC_INVALID_HOST_STATE -Windows.Win32.Foundation.ERROR_APPEXEC_NO_DONOR -Windows.Win32.Foundation.ERROR_APPEXEC_UNEXPECTED_PROCESS_REGISTRATION -Windows.Win32.Foundation.ERROR_APPEXEC_UNKNOWN_USER -Windows.Win32.Foundation.ERROR_APPHELP_BLOCK -Windows.Win32.Foundation.ERROR_APPX_FILE_NOT_ENCRYPTED -Windows.Win32.Foundation.ERROR_ARBITRATION_UNHANDLED -Windows.Win32.Foundation.ERROR_ARENA_TRASHED -Windows.Win32.Foundation.ERROR_ARITHMETIC_OVERFLOW -Windows.Win32.Foundation.ERROR_ASSERTION_FAILURE -Windows.Win32.Foundation.ERROR_ATOMIC_LOCKS_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_AUDIT_FAILED -Windows.Win32.Foundation.ERROR_AUTHENTICATION_FIREWALL_FAILED -Windows.Win32.Foundation.ERROR_AUTHIP_FAILURE -Windows.Win32.Foundation.ERROR_AUTODATASEG_EXCEEDS_64k -Windows.Win32.Foundation.ERROR_BACKUP_CONTROLLER -Windows.Win32.Foundation.ERROR_BAD_ACCESSOR_FLAGS -Windows.Win32.Foundation.ERROR_BAD_ARGUMENTS -Windows.Win32.Foundation.ERROR_BAD_COMMAND -Windows.Win32.Foundation.ERROR_BAD_COMPRESSION_BUFFER -Windows.Win32.Foundation.ERROR_BAD_CONFIGURATION -Windows.Win32.Foundation.ERROR_BAD_CURRENT_DIRECTORY -Windows.Win32.Foundation.ERROR_BAD_DESCRIPTOR_FORMAT -Windows.Win32.Foundation.ERROR_BAD_DEV_TYPE -Windows.Win32.Foundation.ERROR_BAD_DEVICE -Windows.Win32.Foundation.ERROR_BAD_DEVICE_PATH -Windows.Win32.Foundation.ERROR_BAD_DLL_ENTRYPOINT -Windows.Win32.Foundation.ERROR_BAD_DRIVER_LEVEL -Windows.Win32.Foundation.ERROR_BAD_ENVIRONMENT -Windows.Win32.Foundation.ERROR_BAD_EXE_FORMAT -Windows.Win32.Foundation.ERROR_BAD_FILE_TYPE -Windows.Win32.Foundation.ERROR_BAD_FORMAT -Windows.Win32.Foundation.ERROR_BAD_FUNCTION_TABLE -Windows.Win32.Foundation.ERROR_BAD_IMPERSONATION_LEVEL -Windows.Win32.Foundation.ERROR_BAD_INHERITANCE_ACL -Windows.Win32.Foundation.ERROR_BAD_LENGTH -Windows.Win32.Foundation.ERROR_BAD_LOGON_SESSION_STATE -Windows.Win32.Foundation.ERROR_BAD_MCFG_TABLE -Windows.Win32.Foundation.ERROR_BAD_NET_NAME -Windows.Win32.Foundation.ERROR_BAD_NET_RESP -Windows.Win32.Foundation.ERROR_BAD_NETPATH -Windows.Win32.Foundation.ERROR_BAD_PATHNAME -Windows.Win32.Foundation.ERROR_BAD_PIPE -Windows.Win32.Foundation.ERROR_BAD_PROFILE -Windows.Win32.Foundation.ERROR_BAD_PROVIDER -Windows.Win32.Foundation.ERROR_BAD_QUERY_SYNTAX -Windows.Win32.Foundation.ERROR_BAD_RECOVERY_POLICY -Windows.Win32.Foundation.ERROR_BAD_REM_ADAP -Windows.Win32.Foundation.ERROR_BAD_SERVICE_ENTRYPOINT -Windows.Win32.Foundation.ERROR_BAD_STACK -Windows.Win32.Foundation.ERROR_BAD_THREADID_ADDR -Windows.Win32.Foundation.ERROR_BAD_TOKEN_TYPE -Windows.Win32.Foundation.ERROR_BAD_UNIT -Windows.Win32.Foundation.ERROR_BAD_USER_PROFILE -Windows.Win32.Foundation.ERROR_BAD_USERNAME -Windows.Win32.Foundation.ERROR_BAD_VALIDATION_CLASS -Windows.Win32.Foundation.ERROR_BADDB -Windows.Win32.Foundation.ERROR_BADKEY -Windows.Win32.Foundation.ERROR_BADSTARTPOSITION -Windows.Win32.Foundation.ERROR_BEGINNING_OF_MEDIA -Windows.Win32.Foundation.ERROR_BEYOND_VDL -Windows.Win32.Foundation.ERROR_BIOS_FAILED_TO_CONNECT_INTERRUPT -Windows.Win32.Foundation.ERROR_BLOCK_SHARED -Windows.Win32.Foundation.ERROR_BLOCK_SOURCE_WEAK_REFERENCE_INVALID -Windows.Win32.Foundation.ERROR_BLOCK_TARGET_WEAK_REFERENCE_INVALID -Windows.Win32.Foundation.ERROR_BLOCK_TOO_MANY_REFERENCES -Windows.Win32.Foundation.ERROR_BLOCK_WEAK_REFERENCE_INVALID -Windows.Win32.Foundation.ERROR_BLOCKED_BY_PARENTAL_CONTROLS -Windows.Win32.Foundation.ERROR_BOOT_ALREADY_ACCEPTED -Windows.Win32.Foundation.ERROR_BROKEN_PIPE -Windows.Win32.Foundation.ERROR_BUFFER_ALL_ZEROS -Windows.Win32.Foundation.ERROR_BUFFER_OVERFLOW -Windows.Win32.Foundation.ERROR_BUS_RESET -Windows.Win32.Foundation.ERROR_BUSY -Windows.Win32.Foundation.ERROR_BUSY_DRIVE -Windows.Win32.Foundation.ERROR_BYPASSIO_FLT_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_CACHE_PAGE_LOCKED -Windows.Win32.Foundation.ERROR_CALL_NOT_IMPLEMENTED -Windows.Win32.Foundation.ERROR_CALLBACK_INVOKE_INLINE -Windows.Win32.Foundation.ERROR_CALLBACK_POP_STACK -Windows.Win32.Foundation.ERROR_CALLBACK_SUPPLIED_INVALID_DATA -Windows.Win32.Foundation.ERROR_CAN_NOT_COMPLETE -Windows.Win32.Foundation.ERROR_CANCEL_VIOLATION -Windows.Win32.Foundation.ERROR_CANCELLED -Windows.Win32.Foundation.ERROR_CANNOT_BREAK_OPLOCK -Windows.Win32.Foundation.ERROR_CANNOT_COPY -Windows.Win32.Foundation.ERROR_CANNOT_DETECT_DRIVER_FAILURE -Windows.Win32.Foundation.ERROR_CANNOT_DETECT_PROCESS_ABORT -Windows.Win32.Foundation.ERROR_CANNOT_FIND_WND_CLASS -Windows.Win32.Foundation.ERROR_CANNOT_GRANT_REQUESTED_OPLOCK -Windows.Win32.Foundation.ERROR_CANNOT_IMPERSONATE -Windows.Win32.Foundation.ERROR_CANNOT_LOAD_REGISTRY_FILE -Windows.Win32.Foundation.ERROR_CANNOT_MAKE -Windows.Win32.Foundation.ERROR_CANNOT_OPEN_PROFILE -Windows.Win32.Foundation.ERROR_CANT_ACCESS_DOMAIN_INFO -Windows.Win32.Foundation.ERROR_CANT_ACCESS_FILE -Windows.Win32.Foundation.ERROR_CANT_CLEAR_ENCRYPTION_FLAG -Windows.Win32.Foundation.ERROR_CANT_DISABLE_MANDATORY -Windows.Win32.Foundation.ERROR_CANT_ENABLE_DENY_ONLY -Windows.Win32.Foundation.ERROR_CANT_OPEN_ANONYMOUS -Windows.Win32.Foundation.ERROR_CANT_RESOLVE_FILENAME -Windows.Win32.Foundation.ERROR_CANT_TERMINATE_SELF -Windows.Win32.Foundation.ERROR_CANT_WAIT -Windows.Win32.Foundation.ERROR_CANTFETCHBACKWARDS -Windows.Win32.Foundation.ERROR_CANTOPEN -Windows.Win32.Foundation.ERROR_CANTREAD -Windows.Win32.Foundation.ERROR_CANTSCROLLBACKWARDS -Windows.Win32.Foundation.ERROR_CANTWRITE -Windows.Win32.Foundation.ERROR_CAPAUTHZ_CHANGE_TYPE -Windows.Win32.Foundation.ERROR_CAPAUTHZ_DB_CORRUPTED -Windows.Win32.Foundation.ERROR_CAPAUTHZ_NO_POLICY -Windows.Win32.Foundation.ERROR_CAPAUTHZ_NOT_AUTHORIZED -Windows.Win32.Foundation.ERROR_CAPAUTHZ_NOT_DEVUNLOCKED -Windows.Win32.Foundation.ERROR_CAPAUTHZ_NOT_PROVISIONED -Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_DEV_MODE_REQUIRED -Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_INVALID_CATALOG -Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_NO_AUTH_ENTITY -Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_NO_CAPABILITY_MATCH -Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_PARSE_ERROR -Windows.Win32.Foundation.ERROR_CARDBUS_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_CASE_DIFFERING_NAMES_IN_DIR -Windows.Win32.Foundation.ERROR_CASE_SENSITIVE_PATH -Windows.Win32.Foundation.ERROR_CERTIFICATE_VALIDATION_PREFERENCE_CONFLICT -Windows.Win32.Foundation.ERROR_CHECKING_FILE_SYSTEM -Windows.Win32.Foundation.ERROR_CHECKOUT_REQUIRED -Windows.Win32.Foundation.ERROR_CHILD_MUST_BE_VOLATILE -Windows.Win32.Foundation.ERROR_CHILD_NOT_COMPLETE -Windows.Win32.Foundation.ERROR_CHILD_PROCESS_BLOCKED -Windows.Win32.Foundation.ERROR_CHILD_WINDOW_MENU -Windows.Win32.Foundation.ERROR_CIMFS_IMAGE_CORRUPT -Windows.Win32.Foundation.ERROR_CIMFS_IMAGE_VERSION_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_CIRCULAR_DEPENDENCY -Windows.Win32.Foundation.ERROR_CLASS_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_CLASS_DOES_NOT_EXIST -Windows.Win32.Foundation.ERROR_CLASS_HAS_WINDOWS -Windows.Win32.Foundation.ERROR_CLIENT_SERVER_PARAMETERS_INVALID -Windows.Win32.Foundation.ERROR_CLIPBOARD_NOT_OPEN -Windows.Win32.Foundation.ERROR_CLOUD_FILE_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_ALREADY_CONNECTED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_AUTHENTICATION_FAILED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_CONNECTED_PROVIDER_ONLY -Windows.Win32.Foundation.ERROR_CLOUD_FILE_DEHYDRATION_DISALLOWED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_IN_USE -Windows.Win32.Foundation.ERROR_CLOUD_FILE_INCOMPATIBLE_HARDLINKS -Windows.Win32.Foundation.ERROR_CLOUD_FILE_INSUFFICIENT_RESOURCES -Windows.Win32.Foundation.ERROR_CLOUD_FILE_INVALID_REQUEST -Windows.Win32.Foundation.ERROR_CLOUD_FILE_METADATA_CORRUPT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_METADATA_TOO_LARGE -Windows.Win32.Foundation.ERROR_CLOUD_FILE_NETWORK_UNAVAILABLE -Windows.Win32.Foundation.ERROR_CLOUD_FILE_NOT_IN_SYNC -Windows.Win32.Foundation.ERROR_CLOUD_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_NOT_UNDER_SYNC_ROOT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PINNED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_BLOB_CHECKSUM_MISMATCH -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_BLOB_TOO_LARGE -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_CORRUPT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_LOCK_CONFLICT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_VERSION_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROVIDER_NOT_RUNNING -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROVIDER_TERMINATED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_READ_ONLY_VOLUME -Windows.Win32.Foundation.ERROR_CLOUD_FILE_REQUEST_ABORTED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_REQUEST_CANCELED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_REQUEST_TIMEOUT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_SYNC_ROOT_METADATA_CORRUPT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_TOO_MANY_PROPERTY_BLOBS -Windows.Win32.Foundation.ERROR_CLOUD_FILE_UNSUCCESSFUL -Windows.Win32.Foundation.ERROR_CLOUD_FILE_US_MESSAGE_TIMEOUT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_VALIDATION_FAILED -Windows.Win32.Foundation.ERROR_COMMITMENT_LIMIT -Windows.Win32.Foundation.ERROR_COMMITMENT_MINIMUM -Windows.Win32.Foundation.ERROR_COMPRESSED_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_COMPRESSION_DISABLED -Windows.Win32.Foundation.ERROR_COMPRESSION_NOT_BENEFICIAL -Windows.Win32.Foundation.ERROR_CONNECTED_OTHER_PASSWORD -Windows.Win32.Foundation.ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT -Windows.Win32.Foundation.ERROR_CONNECTION_ABORTED -Windows.Win32.Foundation.ERROR_CONNECTION_ACTIVE -Windows.Win32.Foundation.ERROR_CONNECTION_COUNT_LIMIT -Windows.Win32.Foundation.ERROR_CONNECTION_INVALID -Windows.Win32.Foundation.ERROR_CONNECTION_REFUSED -Windows.Win32.Foundation.ERROR_CONNECTION_UNAVAIL -Windows.Win32.Foundation.ERROR_CONTAINER_ASSIGNED -Windows.Win32.Foundation.ERROR_CONTENT_BLOCKED -Windows.Win32.Foundation.ERROR_CONTEXT_EXPIRED -Windows.Win32.Foundation.ERROR_CONTINUE -Windows.Win32.Foundation.ERROR_CONTROL_C_EXIT -Windows.Win32.Foundation.ERROR_CONTROL_ID_NOT_FOUND -Windows.Win32.Foundation.ERROR_CONVERT_TO_LARGE -Windows.Win32.Foundation.ERROR_CORRUPT_LOG_CLEARED -Windows.Win32.Foundation.ERROR_CORRUPT_LOG_CORRUPTED -Windows.Win32.Foundation.ERROR_CORRUPT_LOG_DELETED_FULL -Windows.Win32.Foundation.ERROR_CORRUPT_LOG_OVERFULL -Windows.Win32.Foundation.ERROR_CORRUPT_LOG_UNAVAILABLE -Windows.Win32.Foundation.ERROR_CORRUPT_SYSTEM_FILE -Windows.Win32.Foundation.ERROR_COULD_NOT_INTERPRET -Windows.Win32.Foundation.ERROR_COUNTER_TIMEOUT -Windows.Win32.Foundation.ERROR_CPU_SET_INVALID -Windows.Win32.Foundation.ERROR_CRASH_DUMP -Windows.Win32.Foundation.ERROR_CRC -Windows.Win32.Foundation.ERROR_CREATE_FAILED -Windows.Win32.Foundation.ERROR_CROSS_PARTITION_VIOLATION -Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE -Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_FILE_NOT_CSE -Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_INVALID_SERVER_RESPONSE -Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_NEW_ENCRYPTED_FILE -Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_UNSUPPORTED_SERVER -Windows.Win32.Foundation.ERROR_CSCSHARE_OFFLINE -Windows.Win32.Foundation.ERROR_CTX_CLIENT_QUERY_TIMEOUT -Windows.Win32.Foundation.ERROR_CTX_MODEM_RESPONSE_TIMEOUT -Windows.Win32.Foundation.ERROR_CURRENT_DIRECTORY -Windows.Win32.Foundation.ERROR_CURRENT_DOMAIN_NOT_ALLOWED -Windows.Win32.Foundation.ERROR_DATA_CHECKSUM_ERROR -Windows.Win32.Foundation.ERROR_DATA_NOT_ACCEPTED -Windows.Win32.Foundation.ERROR_DATABASE_DOES_NOT_EXIST -Windows.Win32.Foundation.ERROR_DATATYPE_MISMATCH -Windows.Win32.Foundation.ERROR_DAX_MAPPING_EXISTS -Windows.Win32.Foundation.ERROR_DBG_COMMAND_EXCEPTION -Windows.Win32.Foundation.ERROR_DBG_CONTINUE -Windows.Win32.Foundation.ERROR_DBG_CONTROL_BREAK -Windows.Win32.Foundation.ERROR_DBG_CONTROL_C -Windows.Win32.Foundation.ERROR_DBG_EXCEPTION_HANDLED -Windows.Win32.Foundation.ERROR_DBG_EXCEPTION_NOT_HANDLED -Windows.Win32.Foundation.ERROR_DBG_PRINTEXCEPTION_C -Windows.Win32.Foundation.ERROR_DBG_REPLY_LATER -Windows.Win32.Foundation.ERROR_DBG_RIPEXCEPTION -Windows.Win32.Foundation.ERROR_DBG_TERMINATE_PROCESS -Windows.Win32.Foundation.ERROR_DBG_TERMINATE_THREAD -Windows.Win32.Foundation.ERROR_DBG_UNABLE_TO_PROVIDE_HANDLE -Windows.Win32.Foundation.ERROR_DC_NOT_FOUND -Windows.Win32.Foundation.ERROR_DDE_FAIL -Windows.Win32.Foundation.ERROR_DEBUG_ATTACH_FAILED -Windows.Win32.Foundation.ERROR_DEBUGGER_INACTIVE -Windows.Win32.Foundation.ERROR_DECRYPTION_FAILED -Windows.Win32.Foundation.ERROR_DELAY_LOAD_FAILED -Windows.Win32.Foundation.ERROR_DELETE_PENDING -Windows.Win32.Foundation.ERROR_DEPENDENT_SERVICES_RUNNING -Windows.Win32.Foundation.ERROR_DESTINATION_ELEMENT_FULL -Windows.Win32.Foundation.ERROR_DESTROY_OBJECT_OF_OTHER_THREAD -Windows.Win32.Foundation.ERROR_DEV_NOT_EXIST -Windows.Win32.Foundation.ERROR_DEVICE_ALREADY_ATTACHED -Windows.Win32.Foundation.ERROR_DEVICE_ALREADY_REMEMBERED -Windows.Win32.Foundation.ERROR_DEVICE_DOOR_OPEN -Windows.Win32.Foundation.ERROR_DEVICE_ENUMERATION_ERROR -Windows.Win32.Foundation.ERROR_DEVICE_FEATURE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_DEVICE_HARDWARE_ERROR -Windows.Win32.Foundation.ERROR_DEVICE_HINT_NAME_BUFFER_TOO_SMALL -Windows.Win32.Foundation.ERROR_DEVICE_IN_MAINTENANCE -Windows.Win32.Foundation.ERROR_DEVICE_IN_USE -Windows.Win32.Foundation.ERROR_DEVICE_NO_RESOURCES -Windows.Win32.Foundation.ERROR_DEVICE_NOT_CONNECTED -Windows.Win32.Foundation.ERROR_DEVICE_NOT_PARTITIONED -Windows.Win32.Foundation.ERROR_DEVICE_REINITIALIZATION_NEEDED -Windows.Win32.Foundation.ERROR_DEVICE_REMOVED -Windows.Win32.Foundation.ERROR_DEVICE_REQUIRES_CLEANING -Windows.Win32.Foundation.ERROR_DEVICE_RESET_REQUIRED -Windows.Win32.Foundation.ERROR_DEVICE_SUPPORT_IN_PROGRESS -Windows.Win32.Foundation.ERROR_DEVICE_UNREACHABLE -Windows.Win32.Foundation.ERROR_DHCP_ADDRESS_CONFLICT -Windows.Win32.Foundation.ERROR_DIFFERENT_SERVICE_ACCOUNT -Windows.Win32.Foundation.ERROR_DIR_EFS_DISALLOWED -Windows.Win32.Foundation.ERROR_DIR_NOT_EMPTY -Windows.Win32.Foundation.ERROR_DIR_NOT_ROOT -Windows.Win32.Foundation.ERROR_DIRECT_ACCESS_HANDLE -Windows.Win32.Foundation.ERROR_DIRECTORY -Windows.Win32.Foundation.ERROR_DIRECTORY_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_DISCARDED -Windows.Win32.Foundation.ERROR_DISK_CHANGE -Windows.Win32.Foundation.ERROR_DISK_CORRUPT -Windows.Win32.Foundation.ERROR_DISK_FULL -Windows.Win32.Foundation.ERROR_DISK_OPERATION_FAILED -Windows.Win32.Foundation.ERROR_DISK_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_DISK_RECALIBRATE_FAILED -Windows.Win32.Foundation.ERROR_DISK_REPAIR_DISABLED -Windows.Win32.Foundation.ERROR_DISK_REPAIR_REDIRECTED -Windows.Win32.Foundation.ERROR_DISK_REPAIR_UNSUCCESSFUL -Windows.Win32.Foundation.ERROR_DISK_RESET_FAILED -Windows.Win32.Foundation.ERROR_DISK_RESOURCES_EXHAUSTED -Windows.Win32.Foundation.ERROR_DISK_TOO_FRAGMENTED -Windows.Win32.Foundation.ERROR_DLL_INIT_FAILED -Windows.Win32.Foundation.ERROR_DLL_INIT_FAILED_LOGOFF -Windows.Win32.Foundation.ERROR_DLL_MIGHT_BE_INCOMPATIBLE -Windows.Win32.Foundation.ERROR_DLL_MIGHT_BE_INSECURE -Windows.Win32.Foundation.ERROR_DLL_NOT_FOUND -Windows.Win32.Foundation.ERROR_DLP_POLICY_DENIES_OPERATION -Windows.Win32.Foundation.ERROR_DLP_POLICY_SILENTLY_FAIL -Windows.Win32.Foundation.ERROR_DLP_POLICY_WARNS_AGAINST_OPERATION -Windows.Win32.Foundation.ERROR_DOMAIN_CONTROLLER_EXISTS -Windows.Win32.Foundation.ERROR_DOMAIN_CONTROLLER_NOT_FOUND -Windows.Win32.Foundation.ERROR_DOMAIN_CTRLR_CONFIG_ERROR -Windows.Win32.Foundation.ERROR_DOMAIN_EXISTS -Windows.Win32.Foundation.ERROR_DOMAIN_LIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_DOMAIN_SID_SAME_AS_LOCAL_WORKSTATION -Windows.Win32.Foundation.ERROR_DOMAIN_TRUST_INCONSISTENT -Windows.Win32.Foundation.ERROR_DOWNGRADE_DETECTED -Windows.Win32.Foundation.ERROR_DPL_NOT_SUPPORTED_FOR_USER -Windows.Win32.Foundation.ERROR_DRIVE_LOCKED -Windows.Win32.Foundation.ERROR_DRIVER_BLOCKED -Windows.Win32.Foundation.ERROR_DRIVER_CANCEL_TIMEOUT -Windows.Win32.Foundation.ERROR_DRIVER_DATABASE_ERROR -Windows.Win32.Foundation.ERROR_DRIVER_FAILED_PRIOR_UNLOAD -Windows.Win32.Foundation.ERROR_DRIVER_FAILED_SLEEP -Windows.Win32.Foundation.ERROR_DRIVER_PROCESS_TERMINATED -Windows.Win32.Foundation.ERROR_DRIVERS_LEAKING_LOCKED_PAGES -Windows.Win32.Foundation.ERROR_DS_ADD_REPLICA_INHIBITED -Windows.Win32.Foundation.ERROR_DS_ADMIN_LIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_AFFECTS_MULTIPLE_DSAS -Windows.Win32.Foundation.ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER -Windows.Win32.Foundation.ERROR_DS_ALIAS_DEREF_PROBLEM -Windows.Win32.Foundation.ERROR_DS_ALIAS_POINTS_TO_ALIAS -Windows.Win32.Foundation.ERROR_DS_ALIAS_PROBLEM -Windows.Win32.Foundation.ERROR_DS_ALIASED_OBJ_MISSING -Windows.Win32.Foundation.ERROR_DS_ATT_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_DS_ATT_IS_NOT_ON_OBJ -Windows.Win32.Foundation.ERROR_DS_ATT_NOT_DEF_FOR_CLASS -Windows.Win32.Foundation.ERROR_DS_ATT_NOT_DEF_IN_SCHEMA -Windows.Win32.Foundation.ERROR_DS_ATT_SCHEMA_REQ_ID -Windows.Win32.Foundation.ERROR_DS_ATT_SCHEMA_REQ_SYNTAX -Windows.Win32.Foundation.ERROR_DS_ATT_VAL_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS -Windows.Win32.Foundation.ERROR_DS_ATTRIBUTE_OWNED_BY_SAM -Windows.Win32.Foundation.ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED -Windows.Win32.Foundation.ERROR_DS_AUDIT_FAILURE -Windows.Win32.Foundation.ERROR_DS_AUTH_METHOD_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_DS_AUTH_UNKNOWN -Windows.Win32.Foundation.ERROR_DS_AUTHORIZATION_FAILED -Windows.Win32.Foundation.ERROR_DS_AUX_CLS_TEST_FAIL -Windows.Win32.Foundation.ERROR_DS_BACKLINK_WITHOUT_LINK -Windows.Win32.Foundation.ERROR_DS_BAD_ATT_SCHEMA_SYNTAX -Windows.Win32.Foundation.ERROR_DS_BAD_HIERARCHY_FILE -Windows.Win32.Foundation.ERROR_DS_BAD_INSTANCE_TYPE -Windows.Win32.Foundation.ERROR_DS_BAD_NAME_SYNTAX -Windows.Win32.Foundation.ERROR_DS_BAD_RDN_ATT_ID_SYNTAX -Windows.Win32.Foundation.ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED -Windows.Win32.Foundation.ERROR_DS_BUSY -Windows.Win32.Foundation.ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD -Windows.Win32.Foundation.ERROR_DS_CANT_ADD_ATT_VALUES -Windows.Win32.Foundation.ERROR_DS_CANT_ADD_SYSTEM_ONLY -Windows.Win32.Foundation.ERROR_DS_CANT_ADD_TO_GC -Windows.Win32.Foundation.ERROR_DS_CANT_CACHE_ATT -Windows.Win32.Foundation.ERROR_DS_CANT_CACHE_CLASS -Windows.Win32.Foundation.ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC -Windows.Win32.Foundation.ERROR_DS_CANT_CREATE_UNDER_SCHEMA -Windows.Win32.Foundation.ERROR_DS_CANT_DEL_MASTER_CROSSREF -Windows.Win32.Foundation.ERROR_DS_CANT_DELETE -Windows.Win32.Foundation.ERROR_DS_CANT_DELETE_DSA_OBJ -Windows.Win32.Foundation.ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC -Windows.Win32.Foundation.ERROR_DS_CANT_DEREF_ALIAS -Windows.Win32.Foundation.ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN -Windows.Win32.Foundation.ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF -Windows.Win32.Foundation.ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN -Windows.Win32.Foundation.ERROR_DS_CANT_FIND_DSA_OBJ -Windows.Win32.Foundation.ERROR_DS_CANT_FIND_EXPECTED_NC -Windows.Win32.Foundation.ERROR_DS_CANT_FIND_NC_IN_CACHE -Windows.Win32.Foundation.ERROR_DS_CANT_MIX_MASTER_AND_REPS -Windows.Win32.Foundation.ERROR_DS_CANT_MOD_OBJ_CLASS -Windows.Win32.Foundation.ERROR_DS_CANT_MOD_PRIMARYGROUPID -Windows.Win32.Foundation.ERROR_DS_CANT_MOD_SYSTEM_ONLY -Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_ACCOUNT_GROUP -Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_APP_BASIC_GROUP -Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_APP_QUERY_GROUP -Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_DELETED_OBJECT -Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_RESOURCE_GROUP -Windows.Win32.Foundation.ERROR_DS_CANT_ON_NON_LEAF -Windows.Win32.Foundation.ERROR_DS_CANT_ON_RDN -Windows.Win32.Foundation.ERROR_DS_CANT_REM_MISSING_ATT -Windows.Win32.Foundation.ERROR_DS_CANT_REM_MISSING_ATT_VAL -Windows.Win32.Foundation.ERROR_DS_CANT_REMOVE_ATT_CACHE -Windows.Win32.Foundation.ERROR_DS_CANT_REMOVE_CLASS_CACHE -Windows.Win32.Foundation.ERROR_DS_CANT_REPLACE_HIDDEN_REC -Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_ATTS -Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_CHILD -Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_DN -Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_INSTANCE -Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_SD -Windows.Win32.Foundation.ERROR_DS_CANT_START -Windows.Win32.Foundation.ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ -Windows.Win32.Foundation.ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS -Windows.Win32.Foundation.ERROR_DS_CHILDREN_EXIST -Windows.Win32.Foundation.ERROR_DS_CLASS_MUST_BE_CONCRETE -Windows.Win32.Foundation.ERROR_DS_CLASS_NOT_DSA -Windows.Win32.Foundation.ERROR_DS_CLIENT_LOOP -Windows.Win32.Foundation.ERROR_DS_CODE_INCONSISTENCY -Windows.Win32.Foundation.ERROR_DS_COMPARE_FALSE -Windows.Win32.Foundation.ERROR_DS_COMPARE_TRUE -Windows.Win32.Foundation.ERROR_DS_CONFIDENTIALITY_REQUIRED -Windows.Win32.Foundation.ERROR_DS_CONFIG_PARAM_MISSING -Windows.Win32.Foundation.ERROR_DS_CONSTRAINT_VIOLATION -Windows.Win32.Foundation.ERROR_DS_CONSTRUCTED_ATT_MOD -Windows.Win32.Foundation.ERROR_DS_CONTROL_NOT_FOUND -Windows.Win32.Foundation.ERROR_DS_COULDNT_CONTACT_FSMO -Windows.Win32.Foundation.ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE -Windows.Win32.Foundation.ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE -Windows.Win32.Foundation.ERROR_DS_COULDNT_UPDATE_SPNS -Windows.Win32.Foundation.ERROR_DS_COUNTING_AB_INDICES_FAILED -Windows.Win32.Foundation.ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE -Windows.Win32.Foundation.ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2 -Windows.Win32.Foundation.ERROR_DS_CROSS_DOM_MOVE_ERROR -Windows.Win32.Foundation.ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD -Windows.Win32.Foundation.ERROR_DS_CROSS_NC_DN_RENAME -Windows.Win32.Foundation.ERROR_DS_CROSS_REF_BUSY -Windows.Win32.Foundation.ERROR_DS_CROSS_REF_EXISTS -Windows.Win32.Foundation.ERROR_DS_DATABASE_ERROR -Windows.Win32.Foundation.ERROR_DS_DECODING_ERROR -Windows.Win32.Foundation.ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED -Windows.Win32.Foundation.ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_DIFFERENT_REPL_EPOCHS -Windows.Win32.Foundation.ERROR_DS_DISALLOWED_IN_SYSTEM_CONTAINER -Windows.Win32.Foundation.ERROR_DS_DISALLOWED_NC_REDIRECT -Windows.Win32.Foundation.ERROR_DS_DNS_LOOKUP_FAILURE -Windows.Win32.Foundation.ERROR_DS_DOMAIN_NAME_EXISTS_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_DOMAIN_RENAME_IN_PROGRESS -Windows.Win32.Foundation.ERROR_DS_DOMAIN_VERSION_TOO_HIGH -Windows.Win32.Foundation.ERROR_DS_DOMAIN_VERSION_TOO_LOW -Windows.Win32.Foundation.ERROR_DS_DRA_ABANDON_SYNC -Windows.Win32.Foundation.ERROR_DS_DRA_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_DS_DRA_BAD_DN -Windows.Win32.Foundation.ERROR_DS_DRA_BAD_INSTANCE_TYPE -Windows.Win32.Foundation.ERROR_DS_DRA_BAD_NC -Windows.Win32.Foundation.ERROR_DS_DRA_BUSY -Windows.Win32.Foundation.ERROR_DS_DRA_CONNECTION_FAILED -Windows.Win32.Foundation.ERROR_DS_DRA_CORRUPT_UTD_VECTOR -Windows.Win32.Foundation.ERROR_DS_DRA_DB_ERROR -Windows.Win32.Foundation.ERROR_DS_DRA_DN_EXISTS -Windows.Win32.Foundation.ERROR_DS_DRA_EARLIER_SCHEMA_CONFLICT -Windows.Win32.Foundation.ERROR_DS_DRA_EXTN_CONNECTION_FAILED -Windows.Win32.Foundation.ERROR_DS_DRA_GENERIC -Windows.Win32.Foundation.ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET -Windows.Win32.Foundation.ERROR_DS_DRA_INCONSISTENT_DIT -Windows.Win32.Foundation.ERROR_DS_DRA_INTERNAL_ERROR -Windows.Win32.Foundation.ERROR_DS_DRA_INVALID_PARAMETER -Windows.Win32.Foundation.ERROR_DS_DRA_MAIL_PROBLEM -Windows.Win32.Foundation.ERROR_DS_DRA_MISSING_KRBTGT_SECRET -Windows.Win32.Foundation.ERROR_DS_DRA_MISSING_PARENT -Windows.Win32.Foundation.ERROR_DS_DRA_NAME_COLLISION -Windows.Win32.Foundation.ERROR_DS_DRA_NO_REPLICA -Windows.Win32.Foundation.ERROR_DS_DRA_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_DS_DRA_OBJ_IS_REP_SOURCE -Windows.Win32.Foundation.ERROR_DS_DRA_OBJ_NC_MISMATCH -Windows.Win32.Foundation.ERROR_DS_DRA_OUT_OF_MEM -Windows.Win32.Foundation.ERROR_DS_DRA_OUT_SCHEDULE_WINDOW -Windows.Win32.Foundation.ERROR_DS_DRA_PREEMPTED -Windows.Win32.Foundation.ERROR_DS_DRA_RECYCLED_TARGET -Windows.Win32.Foundation.ERROR_DS_DRA_REF_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_DS_DRA_REF_NOT_FOUND -Windows.Win32.Foundation.ERROR_DS_DRA_REPL_PENDING -Windows.Win32.Foundation.ERROR_DS_DRA_RPC_CANCELLED -Windows.Win32.Foundation.ERROR_DS_DRA_SCHEMA_CONFLICT -Windows.Win32.Foundation.ERROR_DS_DRA_SCHEMA_INFO_SHIP -Windows.Win32.Foundation.ERROR_DS_DRA_SCHEMA_MISMATCH -Windows.Win32.Foundation.ERROR_DS_DRA_SECRETS_DENIED -Windows.Win32.Foundation.ERROR_DS_DRA_SHUTDOWN -Windows.Win32.Foundation.ERROR_DS_DRA_SINK_DISABLED -Windows.Win32.Foundation.ERROR_DS_DRA_SOURCE_DISABLED -Windows.Win32.Foundation.ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA -Windows.Win32.Foundation.ERROR_DS_DRA_SOURCE_REINSTALLED -Windows.Win32.Foundation.ERROR_DS_DRS_EXTENSIONS_CHANGED -Windows.Win32.Foundation.ERROR_DS_DS_REQUIRED -Windows.Win32.Foundation.ERROR_DS_DSA_MUST_BE_INT_MASTER -Windows.Win32.Foundation.ERROR_DS_DST_DOMAIN_NOT_NATIVE -Windows.Win32.Foundation.ERROR_DS_DST_NC_MISMATCH -Windows.Win32.Foundation.ERROR_DS_DUP_LDAP_DISPLAY_NAME -Windows.Win32.Foundation.ERROR_DS_DUP_LINK_ID -Windows.Win32.Foundation.ERROR_DS_DUP_MAPI_ID -Windows.Win32.Foundation.ERROR_DS_DUP_MSDS_INTID -Windows.Win32.Foundation.ERROR_DS_DUP_OID -Windows.Win32.Foundation.ERROR_DS_DUP_RDN -Windows.Win32.Foundation.ERROR_DS_DUP_SCHEMA_ID_GUID -Windows.Win32.Foundation.ERROR_DS_DUPLICATE_ID_FOUND -Windows.Win32.Foundation.ERROR_DS_ENCODING_ERROR -Windows.Win32.Foundation.ERROR_DS_EPOCH_MISMATCH -Windows.Win32.Foundation.ERROR_DS_EXISTING_AD_CHILD_NC -Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_AUX_CLS -Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_MAY_HAVE -Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_MUST_HAVE -Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_POSS_SUP -Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_RDNATTID -Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_SUB_CLS -Windows.Win32.Foundation.ERROR_DS_FILTER_UNKNOWN -Windows.Win32.Foundation.ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS -Windows.Win32.Foundation.ERROR_DS_FLAT_NAME_EXISTS_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_FOREST_VERSION_TOO_HIGH -Windows.Win32.Foundation.ERROR_DS_FOREST_VERSION_TOO_LOW -Windows.Win32.Foundation.ERROR_DS_GC_NOT_AVAILABLE -Windows.Win32.Foundation.ERROR_DS_GC_REQUIRED -Windows.Win32.Foundation.ERROR_DS_GCVERIFY_ERROR -Windows.Win32.Foundation.ERROR_DS_GENERIC_ERROR -Windows.Win32.Foundation.ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER -Windows.Win32.Foundation.ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER -Windows.Win32.Foundation.ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER -Windows.Win32.Foundation.ERROR_DS_GOVERNSID_MISSING -Windows.Win32.Foundation.ERROR_DS_GROUP_CONVERSION_ERROR -Windows.Win32.Foundation.ERROR_DS_HAVE_PRIMARY_MEMBERS -Windows.Win32.Foundation.ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED -Windows.Win32.Foundation.ERROR_DS_HIERARCHY_TABLE_TOO_DEEP -Windows.Win32.Foundation.ERROR_DS_HIGH_ADLDS_FFL -Windows.Win32.Foundation.ERROR_DS_HIGH_DSA_VERSION -Windows.Win32.Foundation.ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD -Windows.Win32.Foundation.ERROR_DS_ILLEGAL_MOD_OPERATION -Windows.Win32.Foundation.ERROR_DS_ILLEGAL_SUPERIOR -Windows.Win32.Foundation.ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION -Windows.Win32.Foundation.ERROR_DS_INAPPROPRIATE_AUTH -Windows.Win32.Foundation.ERROR_DS_INAPPROPRIATE_MATCHING -Windows.Win32.Foundation.ERROR_DS_INCOMPATIBLE_CONTROLS_USED -Windows.Win32.Foundation.ERROR_DS_INCOMPATIBLE_VERSION -Windows.Win32.Foundation.ERROR_DS_INCORRECT_ROLE_OWNER -Windows.Win32.Foundation.ERROR_DS_INIT_FAILURE -Windows.Win32.Foundation.ERROR_DS_INIT_FAILURE_CONSOLE -Windows.Win32.Foundation.ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE -Windows.Win32.Foundation.ERROR_DS_INSTALL_NO_SRC_SCH_VERSION -Windows.Win32.Foundation.ERROR_DS_INSTALL_SCHEMA_MISMATCH -Windows.Win32.Foundation.ERROR_DS_INSUFF_ACCESS_RIGHTS -Windows.Win32.Foundation.ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT -Windows.Win32.Foundation.ERROR_DS_INTERNAL_FAILURE -Windows.Win32.Foundation.ERROR_DS_INVALID_ATTRIBUTE_SYNTAX -Windows.Win32.Foundation.ERROR_DS_INVALID_DMD -Windows.Win32.Foundation.ERROR_DS_INVALID_DN_SYNTAX -Windows.Win32.Foundation.ERROR_DS_INVALID_GROUP_TYPE -Windows.Win32.Foundation.ERROR_DS_INVALID_LDAP_DISPLAY_NAME -Windows.Win32.Foundation.ERROR_DS_INVALID_NAME_FOR_SPN -Windows.Win32.Foundation.ERROR_DS_INVALID_ROLE_OWNER -Windows.Win32.Foundation.ERROR_DS_INVALID_SCRIPT -Windows.Win32.Foundation.ERROR_DS_INVALID_SEARCH_FLAG -Windows.Win32.Foundation.ERROR_DS_INVALID_SEARCH_FLAG_SUBTREE -Windows.Win32.Foundation.ERROR_DS_INVALID_SEARCH_FLAG_TUPLE -Windows.Win32.Foundation.ERROR_DS_IS_LEAF -Windows.Win32.Foundation.ERROR_DS_KEY_NOT_UNIQUE -Windows.Win32.Foundation.ERROR_DS_LDAP_SEND_QUEUE_FULL -Windows.Win32.Foundation.ERROR_DS_LINK_ID_NOT_AVAILABLE -Windows.Win32.Foundation.ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER -Windows.Win32.Foundation.ERROR_DS_LOCAL_ERROR -Windows.Win32.Foundation.ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY -Windows.Win32.Foundation.ERROR_DS_LOOP_DETECT -Windows.Win32.Foundation.ERROR_DS_LOW_ADLDS_FFL -Windows.Win32.Foundation.ERROR_DS_LOW_DSA_VERSION -Windows.Win32.Foundation.ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4 -Windows.Win32.Foundation.ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_MAPI_ID_NOT_AVAILABLE -Windows.Win32.Foundation.ERROR_DS_MASTERDSA_REQUIRED -Windows.Win32.Foundation.ERROR_DS_MAX_OBJ_SIZE_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY -Windows.Win32.Foundation.ERROR_DS_MISSING_EXPECTED_ATT -Windows.Win32.Foundation.ERROR_DS_MISSING_FOREST_TRUST -Windows.Win32.Foundation.ERROR_DS_MISSING_FSMO_SETTINGS -Windows.Win32.Foundation.ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER -Windows.Win32.Foundation.ERROR_DS_MISSING_REQUIRED_ATT -Windows.Win32.Foundation.ERROR_DS_MISSING_SUPREF -Windows.Win32.Foundation.ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG -Windows.Win32.Foundation.ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE -Windows.Win32.Foundation.ERROR_DS_MODIFYDN_WRONG_GRANDPARENT -Windows.Win32.Foundation.ERROR_DS_MUST_BE_RUN_ON_DST_DC -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_DOMAIN_ONLY -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_NO_MAPPING -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_NOT_FOUND -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_NOT_UNIQUE -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_RESOLVING -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_TRUST_REFERRAL -Windows.Win32.Foundation.ERROR_DS_NAME_NOT_UNIQUE -Windows.Win32.Foundation.ERROR_DS_NAME_REFERENCE_INVALID -Windows.Win32.Foundation.ERROR_DS_NAME_TOO_LONG -Windows.Win32.Foundation.ERROR_DS_NAME_TOO_MANY_PARTS -Windows.Win32.Foundation.ERROR_DS_NAME_TYPE_UNKNOWN -Windows.Win32.Foundation.ERROR_DS_NAME_UNPARSEABLE -Windows.Win32.Foundation.ERROR_DS_NAME_VALUE_TOO_LONG -Windows.Win32.Foundation.ERROR_DS_NAMING_MASTER_GC -Windows.Win32.Foundation.ERROR_DS_NAMING_VIOLATION -Windows.Win32.Foundation.ERROR_DS_NC_MUST_HAVE_NC_PARENT -Windows.Win32.Foundation.ERROR_DS_NC_STILL_HAS_DSAS -Windows.Win32.Foundation.ERROR_DS_NCNAME_MISSING_CR_REF -Windows.Win32.Foundation.ERROR_DS_NCNAME_MUST_BE_NC -Windows.Win32.Foundation.ERROR_DS_NO_ATTRIBUTE_OR_VALUE -Windows.Win32.Foundation.ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN -Windows.Win32.Foundation.ERROR_DS_NO_CHAINED_EVAL -Windows.Win32.Foundation.ERROR_DS_NO_CHAINING -Windows.Win32.Foundation.ERROR_DS_NO_CHECKPOINT_WITH_PDC -Windows.Win32.Foundation.ERROR_DS_NO_CROSSREF_FOR_NC -Windows.Win32.Foundation.ERROR_DS_NO_DELETED_NAME -Windows.Win32.Foundation.ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS -Windows.Win32.Foundation.ERROR_DS_NO_MORE_RIDS -Windows.Win32.Foundation.ERROR_DS_NO_MSDS_INTID -Windows.Win32.Foundation.ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN -Windows.Win32.Foundation.ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN -Windows.Win32.Foundation.ERROR_DS_NO_NTDSA_OBJECT -Windows.Win32.Foundation.ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC -Windows.Win32.Foundation.ERROR_DS_NO_PARENT_OBJECT -Windows.Win32.Foundation.ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION -Windows.Win32.Foundation.ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA -Windows.Win32.Foundation.ERROR_DS_NO_REF_DOMAIN -Windows.Win32.Foundation.ERROR_DS_NO_REQUESTED_ATTS_FOUND -Windows.Win32.Foundation.ERROR_DS_NO_RESULTS_RETURNED -Windows.Win32.Foundation.ERROR_DS_NO_RIDS_ALLOCATED -Windows.Win32.Foundation.ERROR_DS_NO_SERVER_OBJECT -Windows.Win32.Foundation.ERROR_DS_NO_SUCH_OBJECT -Windows.Win32.Foundation.ERROR_DS_NO_TREE_DELETE_ABOVE_NC -Windows.Win32.Foundation.ERROR_DS_NON_ASQ_SEARCH -Windows.Win32.Foundation.ERROR_DS_NON_BASE_SEARCH -Windows.Win32.Foundation.ERROR_DS_NONEXISTENT_MAY_HAVE -Windows.Win32.Foundation.ERROR_DS_NONEXISTENT_MUST_HAVE -Windows.Win32.Foundation.ERROR_DS_NONEXISTENT_POSS_SUP -Windows.Win32.Foundation.ERROR_DS_NONSAFE_SCHEMA_CHANGE -Windows.Win32.Foundation.ERROR_DS_NOT_AN_OBJECT -Windows.Win32.Foundation.ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC -Windows.Win32.Foundation.ERROR_DS_NOT_CLOSEST -Windows.Win32.Foundation.ERROR_DS_NOT_INSTALLED -Windows.Win32.Foundation.ERROR_DS_NOT_ON_BACKLINK -Windows.Win32.Foundation.ERROR_DS_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_DS_NOT_SUPPORTED_SORT_ORDER -Windows.Win32.Foundation.ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX -Windows.Win32.Foundation.ERROR_DS_NTDSCRIPT_PROCESS_ERROR -Windows.Win32.Foundation.ERROR_DS_NTDSCRIPT_SYNTAX_ERROR -Windows.Win32.Foundation.ERROR_DS_OBJ_CLASS_NOT_DEFINED -Windows.Win32.Foundation.ERROR_DS_OBJ_CLASS_NOT_SUBCLASS -Windows.Win32.Foundation.ERROR_DS_OBJ_CLASS_VIOLATION -Windows.Win32.Foundation.ERROR_DS_OBJ_GUID_EXISTS -Windows.Win32.Foundation.ERROR_DS_OBJ_NOT_FOUND -Windows.Win32.Foundation.ERROR_DS_OBJ_STRING_NAME_EXISTS -Windows.Win32.Foundation.ERROR_DS_OBJ_TOO_LARGE -Windows.Win32.Foundation.ERROR_DS_OBJECT_BEING_REMOVED -Windows.Win32.Foundation.ERROR_DS_OBJECT_CLASS_REQUIRED -Windows.Win32.Foundation.ERROR_DS_OBJECT_RESULTS_TOO_LARGE -Windows.Win32.Foundation.ERROR_DS_OFFSET_RANGE_ERROR -Windows.Win32.Foundation.ERROR_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS -Windows.Win32.Foundation.ERROR_DS_OID_NOT_FOUND -Windows.Win32.Foundation.ERROR_DS_OPERATIONS_ERROR -Windows.Win32.Foundation.ERROR_DS_OUT_OF_SCOPE -Windows.Win32.Foundation.ERROR_DS_OUT_OF_VERSION_STORE -Windows.Win32.Foundation.ERROR_DS_PARAM_ERROR -Windows.Win32.Foundation.ERROR_DS_PARENT_IS_AN_ALIAS -Windows.Win32.Foundation.ERROR_DS_PDC_OPERATION_IN_PROGRESS -Windows.Win32.Foundation.ERROR_DS_PER_ATTRIBUTE_AUTHZ_FAILED_DURING_ADD -Windows.Win32.Foundation.ERROR_DS_POLICY_NOT_KNOWN -Windows.Win32.Foundation.ERROR_DS_PROTOCOL_ERROR -Windows.Win32.Foundation.ERROR_DS_RANGE_CONSTRAINT -Windows.Win32.Foundation.ERROR_DS_RDN_DOESNT_MATCH_SCHEMA -Windows.Win32.Foundation.ERROR_DS_RECALCSCHEMA_FAILED -Windows.Win32.Foundation.ERROR_DS_REFERRAL -Windows.Win32.Foundation.ERROR_DS_REFERRAL_LIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_REFUSING_FSMO_ROLES -Windows.Win32.Foundation.ERROR_DS_REMOTE_CROSSREF_OP_FAILED -Windows.Win32.Foundation.ERROR_DS_REPL_LIFETIME_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR -Windows.Win32.Foundation.ERROR_DS_REPLICATOR_ONLY -Windows.Win32.Foundation.ERROR_DS_RESERVED_LINK_ID -Windows.Win32.Foundation.ERROR_DS_RESERVED_MAPI_ID -Windows.Win32.Foundation.ERROR_DS_RIDMGR_DISABLED -Windows.Win32.Foundation.ERROR_DS_RIDMGR_INIT_ERROR -Windows.Win32.Foundation.ERROR_DS_ROLE_NOT_VERIFIED -Windows.Win32.Foundation.ERROR_DS_ROOT_CANT_BE_SUBREF -Windows.Win32.Foundation.ERROR_DS_ROOT_MUST_BE_NC -Windows.Win32.Foundation.ERROR_DS_ROOT_REQUIRES_CLASS_TOP -Windows.Win32.Foundation.ERROR_DS_SAM_INIT_FAILURE -Windows.Win32.Foundation.ERROR_DS_SAM_INIT_FAILURE_CONSOLE -Windows.Win32.Foundation.ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY -Windows.Win32.Foundation.ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD -Windows.Win32.Foundation.ERROR_DS_SCHEMA_ALLOC_FAILED -Windows.Win32.Foundation.ERROR_DS_SCHEMA_NOT_LOADED -Windows.Win32.Foundation.ERROR_DS_SCHEMA_UPDATE_DISALLOWED -Windows.Win32.Foundation.ERROR_DS_SEC_DESC_INVALID -Windows.Win32.Foundation.ERROR_DS_SEC_DESC_TOO_SHORT -Windows.Win32.Foundation.ERROR_DS_SECURITY_CHECKING_ERROR -Windows.Win32.Foundation.ERROR_DS_SECURITY_ILLEGAL_MODIFY -Windows.Win32.Foundation.ERROR_DS_SEMANTIC_ATT_TEST -Windows.Win32.Foundation.ERROR_DS_SENSITIVE_GROUP_VIOLATION -Windows.Win32.Foundation.ERROR_DS_SERVER_DOWN -Windows.Win32.Foundation.ERROR_DS_SHUTTING_DOWN -Windows.Win32.Foundation.ERROR_DS_SINGLE_USER_MODE_FAILED -Windows.Win32.Foundation.ERROR_DS_SINGLE_VALUE_CONSTRAINT -Windows.Win32.Foundation.ERROR_DS_SIZELIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_SORT_CONTROL_MISSING -Windows.Win32.Foundation.ERROR_DS_SOURCE_AUDITING_NOT_ENABLED -Windows.Win32.Foundation.ERROR_DS_SOURCE_DOMAIN_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_SPN_VALUE_NOT_UNIQUE_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_SRC_AND_DST_NC_IDENTICAL -Windows.Win32.Foundation.ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH -Windows.Win32.Foundation.ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER -Windows.Win32.Foundation.ERROR_DS_SRC_GUID_MISMATCH -Windows.Win32.Foundation.ERROR_DS_SRC_NAME_MISMATCH -Windows.Win32.Foundation.ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER -Windows.Win32.Foundation.ERROR_DS_SRC_SID_EXISTS_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_STRING_SD_CONVERSION_FAILED -Windows.Win32.Foundation.ERROR_DS_STRONG_AUTH_REQUIRED -Windows.Win32.Foundation.ERROR_DS_SUB_CLS_TEST_FAIL -Windows.Win32.Foundation.ERROR_DS_SUBREF_MUST_HAVE_PARENT -Windows.Win32.Foundation.ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD -Windows.Win32.Foundation.ERROR_DS_SYNTAX_MISMATCH -Windows.Win32.Foundation.ERROR_DS_THREAD_LIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_TIMELIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_TREE_DELETE_NOT_FINISHED -Windows.Win32.Foundation.ERROR_DS_UNABLE_TO_SURRENDER_ROLES -Windows.Win32.Foundation.ERROR_DS_UNAVAILABLE -Windows.Win32.Foundation.ERROR_DS_UNAVAILABLE_CRIT_EXTENSION -Windows.Win32.Foundation.ERROR_DS_UNDELETE_SAM_VALIDATION_FAILED -Windows.Win32.Foundation.ERROR_DS_UNICODEPWD_NOT_IN_QUOTES -Windows.Win32.Foundation.ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER -Windows.Win32.Foundation.ERROR_DS_UNKNOWN_ERROR -Windows.Win32.Foundation.ERROR_DS_UNKNOWN_OPERATION -Windows.Win32.Foundation.ERROR_DS_UNWILLING_TO_PERFORM -Windows.Win32.Foundation.ERROR_DS_UPN_VALUE_NOT_UNIQUE_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_USER_BUFFER_TO_SMALL -Windows.Win32.Foundation.ERROR_DS_VALUE_KEY_NOT_UNIQUE -Windows.Win32.Foundation.ERROR_DS_VERSION_CHECK_FAILURE -Windows.Win32.Foundation.ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL -Windows.Win32.Foundation.ERROR_DS_WRONG_LINKED_ATT_SYNTAX -Windows.Win32.Foundation.ERROR_DS_WRONG_OM_OBJ_CLASS -Windows.Win32.Foundation.ERROR_DUP_DOMAINNAME -Windows.Win32.Foundation.ERROR_DUP_NAME -Windows.Win32.Foundation.ERROR_DUPLICATE_PRIVILEGES -Windows.Win32.Foundation.ERROR_DUPLICATE_SERVICE_NAME -Windows.Win32.Foundation.ERROR_DYNAMIC_CODE_BLOCKED -Windows.Win32.Foundation.ERROR_DYNLINK_FROM_INVALID_RING -Windows.Win32.Foundation.ERROR_EA_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_EA_FILE_CORRUPT -Windows.Win32.Foundation.ERROR_EA_LIST_INCONSISTENT -Windows.Win32.Foundation.ERROR_EA_TABLE_FULL -Windows.Win32.Foundation.ERROR_EAS_DIDNT_FIT -Windows.Win32.Foundation.ERROR_EAS_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_EDP_DPL_POLICY_CANT_BE_SATISFIED -Windows.Win32.Foundation.ERROR_EDP_POLICY_DENIES_OPERATION -Windows.Win32.Foundation.ERROR_EFS_ALG_BLOB_TOO_BIG -Windows.Win32.Foundation.ERROR_EFS_DISABLED -Windows.Win32.Foundation.ERROR_EFS_SERVER_NOT_TRUSTED -Windows.Win32.Foundation.ERROR_EFS_VERSION_NOT_SUPPORT -Windows.Win32.Foundation.ERROR_ELEVATION_REQUIRED -Windows.Win32.Foundation.ERROR_ENCLAVE_FAILURE -Windows.Win32.Foundation.ERROR_ENCLAVE_NOT_TERMINATED -Windows.Win32.Foundation.ERROR_ENCLAVE_VIOLATION -Windows.Win32.Foundation.ERROR_ENCRYPTED_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_ENCRYPTED_IO_NOT_POSSIBLE -Windows.Win32.Foundation.ERROR_ENCRYPTING_METADATA_DISALLOWED -Windows.Win32.Foundation.ERROR_ENCRYPTION_DISABLED -Windows.Win32.Foundation.ERROR_ENCRYPTION_FAILED -Windows.Win32.Foundation.ERROR_ENCRYPTION_POLICY_DENIES_OPERATION -Windows.Win32.Foundation.ERROR_END_OF_MEDIA -Windows.Win32.Foundation.ERROR_ENVVAR_NOT_FOUND -Windows.Win32.Foundation.ERROR_EOM_OVERFLOW -Windows.Win32.Foundation.ERROR_ERRORS_ENCOUNTERED -Windows.Win32.Foundation.ERROR_EVALUATION_EXPIRATION -Windows.Win32.Foundation.ERROR_EVENT_DONE -Windows.Win32.Foundation.ERROR_EVENT_PENDING -Windows.Win32.Foundation.ERROR_EVENTLOG_CANT_START -Windows.Win32.Foundation.ERROR_EVENTLOG_FILE_CHANGED -Windows.Win32.Foundation.ERROR_EVENTLOG_FILE_CORRUPT -Windows.Win32.Foundation.ERROR_EXCEPTION_IN_SERVICE -Windows.Win32.Foundation.ERROR_EXCL_SEM_ALREADY_OWNED -Windows.Win32.Foundation.ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY -Windows.Win32.Foundation.ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY -Windows.Win32.Foundation.ERROR_EXE_MACHINE_TYPE_MISMATCH -Windows.Win32.Foundation.ERROR_EXE_MARKED_INVALID -Windows.Win32.Foundation.ERROR_EXTENDED_ERROR -Windows.Win32.Foundation.ERROR_EXTERNAL_BACKING_PROVIDER_UNKNOWN -Windows.Win32.Foundation.ERROR_EXTERNAL_SYSKEY_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_EXTRANEOUS_INFORMATION -Windows.Win32.Foundation.ERROR_FAIL_FAST_EXCEPTION -Windows.Win32.Foundation.ERROR_FAIL_I24 -Windows.Win32.Foundation.ERROR_FAIL_NOACTION_REBOOT -Windows.Win32.Foundation.ERROR_FAIL_RESTART -Windows.Win32.Foundation.ERROR_FAIL_SHUTDOWN -Windows.Win32.Foundation.ERROR_FAILED_DRIVER_ENTRY -Windows.Win32.Foundation.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT -Windows.Win32.Foundation.ERROR_FATAL_APP_EXIT -Windows.Win32.Foundation.ERROR_FILE_CHECKED_OUT -Windows.Win32.Foundation.ERROR_FILE_CORRUPT -Windows.Win32.Foundation.ERROR_FILE_ENCRYPTED -Windows.Win32.Foundation.ERROR_FILE_EXISTS -Windows.Win32.Foundation.ERROR_FILE_HANDLE_REVOKED -Windows.Win32.Foundation.ERROR_FILE_INVALID -Windows.Win32.Foundation.ERROR_FILE_LEVEL_TRIM_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_FILE_METADATA_OPTIMIZATION_IN_PROGRESS -Windows.Win32.Foundation.ERROR_FILE_NOT_ENCRYPTED -Windows.Win32.Foundation.ERROR_FILE_NOT_FOUND -Windows.Win32.Foundation.ERROR_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_FILE_OFFLINE -Windows.Win32.Foundation.ERROR_FILE_PROTECTED_UNDER_DPL -Windows.Win32.Foundation.ERROR_FILE_READ_ONLY -Windows.Win32.Foundation.ERROR_FILE_SNAP_IN_PROGRESS -Windows.Win32.Foundation.ERROR_FILE_SNAP_INVALID_PARAMETER -Windows.Win32.Foundation.ERROR_FILE_SNAP_IO_NOT_COORDINATED -Windows.Win32.Foundation.ERROR_FILE_SNAP_MODIFY_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_FILE_SNAP_UNEXPECTED_ERROR -Windows.Win32.Foundation.ERROR_FILE_SNAP_USER_SECTION_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_FILE_SYSTEM_LIMITATION -Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_BUSY -Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_INVALID_OPERATION -Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_METADATA_CORRUPT -Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_PROVIDER_UNKNOWN -Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_UNAVAILABLE -Windows.Win32.Foundation.ERROR_FILE_TOO_LARGE -Windows.Win32.Foundation.ERROR_FILEMARK_DETECTED -Windows.Win32.Foundation.ERROR_FILENAME_EXCED_RANGE -Windows.Win32.Foundation.ERROR_FIRMWARE_UPDATED -Windows.Win32.Foundation.ERROR_FLOAT_MULTIPLE_FAULTS -Windows.Win32.Foundation.ERROR_FLOAT_MULTIPLE_TRAPS -Windows.Win32.Foundation.ERROR_FLOPPY_BAD_REGISTERS -Windows.Win32.Foundation.ERROR_FLOPPY_ID_MARK_NOT_FOUND -Windows.Win32.Foundation.ERROR_FLOPPY_UNKNOWN_ERROR -Windows.Win32.Foundation.ERROR_FLOPPY_VOLUME -Windows.Win32.Foundation.ERROR_FLOPPY_WRONG_CYLINDER -Windows.Win32.Foundation.ERROR_FORMS_AUTH_REQUIRED -Windows.Win32.Foundation.ERROR_FOUND_OUT_OF_SCOPE -Windows.Win32.Foundation.ERROR_FS_DRIVER_REQUIRED -Windows.Win32.Foundation.ERROR_FS_METADATA_INCONSISTENT -Windows.Win32.Foundation.ERROR_FSFILTER_OP_COMPLETED_SUCCESSFULLY -Windows.Win32.Foundation.ERROR_FT_DI_SCAN_REQUIRED -Windows.Win32.Foundation.ERROR_FT_READ_FAILURE -Windows.Win32.Foundation.ERROR_FT_READ_FROM_COPY_FAILURE -Windows.Win32.Foundation.ERROR_FT_READ_RECOVERY_FROM_BACKUP -Windows.Win32.Foundation.ERROR_FT_WRITE_FAILURE -Windows.Win32.Foundation.ERROR_FT_WRITE_RECOVERY -Windows.Win32.Foundation.ERROR_FULLSCREEN_MODE -Windows.Win32.Foundation.ERROR_FUNCTION_FAILED -Windows.Win32.Foundation.ERROR_FUNCTION_NOT_CALLED -Windows.Win32.Foundation.ERROR_GDI_HANDLE_LEAK -Windows.Win32.Foundation.ERROR_GEN_FAILURE -Windows.Win32.Foundation.ERROR_GENERIC_NOT_MAPPED -Windows.Win32.Foundation.ERROR_GLOBAL_ONLY_HOOK -Windows.Win32.Foundation.ERROR_GRACEFUL_DISCONNECT -Windows.Win32.Foundation.ERROR_GROUP_EXISTS -Windows.Win32.Foundation.ERROR_GUID_SUBSTITUTION_MADE -Windows.Win32.Foundation.ERROR_HANDLE_DISK_FULL -Windows.Win32.Foundation.ERROR_HANDLE_EOF -Windows.Win32.Foundation.ERROR_HANDLE_REVOKED -Windows.Win32.Foundation.ERROR_HANDLES_CLOSED -Windows.Win32.Foundation.ERROR_HAS_SYSTEM_CRITICAL_FILES -Windows.Win32.Foundation.ERROR_HIBERNATED -Windows.Win32.Foundation.ERROR_HIBERNATION_FAILURE -Windows.Win32.Foundation.ERROR_HOOK_NEEDS_HMOD -Windows.Win32.Foundation.ERROR_HOOK_NOT_INSTALLED -Windows.Win32.Foundation.ERROR_HOOK_TYPE_NOT_ALLOWED -Windows.Win32.Foundation.ERROR_HOST_DOWN -Windows.Win32.Foundation.ERROR_HOST_UNREACHABLE -Windows.Win32.Foundation.ERROR_HOTKEY_ALREADY_REGISTERED -Windows.Win32.Foundation.ERROR_HOTKEY_NOT_REGISTERED -Windows.Win32.Foundation.ERROR_HWNDS_HAVE_DIFF_PARENT -Windows.Win32.Foundation.ERROR_ILL_FORMED_PASSWORD -Windows.Win32.Foundation.ERROR_ILLEGAL_CHARACTER -Windows.Win32.Foundation.ERROR_ILLEGAL_DLL_RELOCATION -Windows.Win32.Foundation.ERROR_ILLEGAL_ELEMENT_ADDRESS -Windows.Win32.Foundation.ERROR_ILLEGAL_FLOAT_CONTEXT -Windows.Win32.Foundation.ERROR_IMAGE_AT_DIFFERENT_BASE -Windows.Win32.Foundation.ERROR_IMAGE_MACHINE_TYPE_MISMATCH -Windows.Win32.Foundation.ERROR_IMAGE_MACHINE_TYPE_MISMATCH_EXE -Windows.Win32.Foundation.ERROR_IMAGE_NOT_AT_BASE -Windows.Win32.Foundation.ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT -Windows.Win32.Foundation.ERROR_IMPLEMENTATION_LIMIT -Windows.Win32.Foundation.ERROR_INCOMPATIBLE_SERVICE_PRIVILEGE -Windows.Win32.Foundation.ERROR_INCOMPATIBLE_SERVICE_SID_TYPE -Windows.Win32.Foundation.ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING -Windows.Win32.Foundation.ERROR_INCORRECT_ACCOUNT_TYPE -Windows.Win32.Foundation.ERROR_INCORRECT_ADDRESS -Windows.Win32.Foundation.ERROR_INCORRECT_SIZE -Windows.Win32.Foundation.ERROR_INDEX_ABSENT -Windows.Win32.Foundation.ERROR_INDEX_OUT_OF_BOUNDS -Windows.Win32.Foundation.ERROR_INFLOOP_IN_RELOC_CHAIN -Windows.Win32.Foundation.ERROR_INSTALL_ALREADY_RUNNING -Windows.Win32.Foundation.ERROR_INSTALL_FAILURE -Windows.Win32.Foundation.ERROR_INSTALL_LANGUAGE_UNSUPPORTED -Windows.Win32.Foundation.ERROR_INSTALL_LOG_FAILURE -Windows.Win32.Foundation.ERROR_INSTALL_NOTUSED -Windows.Win32.Foundation.ERROR_INSTALL_PACKAGE_INVALID -Windows.Win32.Foundation.ERROR_INSTALL_PACKAGE_OPEN_FAILED -Windows.Win32.Foundation.ERROR_INSTALL_PACKAGE_REJECTED -Windows.Win32.Foundation.ERROR_INSTALL_PACKAGE_VERSION -Windows.Win32.Foundation.ERROR_INSTALL_PLATFORM_UNSUPPORTED -Windows.Win32.Foundation.ERROR_INSTALL_REJECTED -Windows.Win32.Foundation.ERROR_INSTALL_REMOTE_DISALLOWED -Windows.Win32.Foundation.ERROR_INSTALL_REMOTE_PROHIBITED -Windows.Win32.Foundation.ERROR_INSTALL_SERVICE_FAILURE -Windows.Win32.Foundation.ERROR_INSTALL_SERVICE_SAFEBOOT -Windows.Win32.Foundation.ERROR_INSTALL_SOURCE_ABSENT -Windows.Win32.Foundation.ERROR_INSTALL_SUSPEND -Windows.Win32.Foundation.ERROR_INSTALL_TEMP_UNWRITABLE -Windows.Win32.Foundation.ERROR_INSTALL_TRANSFORM_FAILURE -Windows.Win32.Foundation.ERROR_INSTALL_TRANSFORM_REJECTED -Windows.Win32.Foundation.ERROR_INSTALL_UI_FAILURE -Windows.Win32.Foundation.ERROR_INSTALL_USEREXIT -Windows.Win32.Foundation.ERROR_INSTRUCTION_MISALIGNMENT -Windows.Win32.Foundation.ERROR_INSUFFICIENT_BUFFER -Windows.Win32.Foundation.ERROR_INSUFFICIENT_LOGON_INFO -Windows.Win32.Foundation.ERROR_INSUFFICIENT_POWER -Windows.Win32.Foundation.ERROR_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE -Windows.Win32.Foundation.ERROR_INSUFFICIENT_VIRTUAL_ADDR_RESOURCES -Windows.Win32.Foundation.ERROR_INTERMIXED_KERNEL_EA_OPERATION -Windows.Win32.Foundation.ERROR_INTERNAL_DB_CORRUPTION -Windows.Win32.Foundation.ERROR_INTERNAL_DB_ERROR -Windows.Win32.Foundation.ERROR_INTERNAL_ERROR -Windows.Win32.Foundation.ERROR_INTERRUPT_STILL_CONNECTED -Windows.Win32.Foundation.ERROR_INTERRUPT_VECTOR_ALREADY_CONNECTED -Windows.Win32.Foundation.ERROR_INVALID_ACCEL_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_ACCESS -Windows.Win32.Foundation.ERROR_INVALID_ACCOUNT_NAME -Windows.Win32.Foundation.ERROR_INVALID_ACE_CONDITION -Windows.Win32.Foundation.ERROR_INVALID_ACL -Windows.Win32.Foundation.ERROR_INVALID_ADDRESS -Windows.Win32.Foundation.ERROR_INVALID_AT_INTERRUPT_TIME -Windows.Win32.Foundation.ERROR_INVALID_BLOCK -Windows.Win32.Foundation.ERROR_INVALID_BLOCK_LENGTH -Windows.Win32.Foundation.ERROR_INVALID_CAP -Windows.Win32.Foundation.ERROR_INVALID_CATEGORY -Windows.Win32.Foundation.ERROR_INVALID_COMBOBOX_MESSAGE -Windows.Win32.Foundation.ERROR_INVALID_COMMAND_LINE -Windows.Win32.Foundation.ERROR_INVALID_COMPUTERNAME -Windows.Win32.Foundation.ERROR_INVALID_CRUNTIME_PARAMETER -Windows.Win32.Foundation.ERROR_INVALID_CURSOR_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_DATA -Windows.Win32.Foundation.ERROR_INVALID_DATATYPE -Windows.Win32.Foundation.ERROR_INVALID_DEVICE_OBJECT_PARAMETER -Windows.Win32.Foundation.ERROR_INVALID_DLL -Windows.Win32.Foundation.ERROR_INVALID_DOMAIN_ROLE -Windows.Win32.Foundation.ERROR_INVALID_DOMAIN_STATE -Windows.Win32.Foundation.ERROR_INVALID_DOMAINNAME -Windows.Win32.Foundation.ERROR_INVALID_DRIVE -Windows.Win32.Foundation.ERROR_INVALID_DWP_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_EA_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_EA_NAME -Windows.Win32.Foundation.ERROR_INVALID_EDIT_HEIGHT -Windows.Win32.Foundation.ERROR_INVALID_ENVIRONMENT -Windows.Win32.Foundation.ERROR_INVALID_EVENT_COUNT -Windows.Win32.Foundation.ERROR_INVALID_EVENTNAME -Windows.Win32.Foundation.ERROR_INVALID_EXCEPTION_HANDLER -Windows.Win32.Foundation.ERROR_INVALID_EXE_SIGNATURE -Windows.Win32.Foundation.ERROR_INVALID_FIELD -Windows.Win32.Foundation.ERROR_INVALID_FIELD_IN_PARAMETER_LIST -Windows.Win32.Foundation.ERROR_INVALID_FILTER_PROC -Windows.Win32.Foundation.ERROR_INVALID_FLAG_NUMBER -Windows.Win32.Foundation.ERROR_INVALID_FLAGS -Windows.Win32.Foundation.ERROR_INVALID_FORM_NAME -Windows.Win32.Foundation.ERROR_INVALID_FORM_SIZE -Windows.Win32.Foundation.ERROR_INVALID_FUNCTION -Windows.Win32.Foundation.ERROR_INVALID_GROUP_ATTRIBUTES -Windows.Win32.Foundation.ERROR_INVALID_GROUPNAME -Windows.Win32.Foundation.ERROR_INVALID_GW_COMMAND -Windows.Win32.Foundation.ERROR_INVALID_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_HANDLE_STATE -Windows.Win32.Foundation.ERROR_INVALID_HOOK_FILTER -Windows.Win32.Foundation.ERROR_INVALID_HOOK_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_HW_PROFILE -Windows.Win32.Foundation.ERROR_INVALID_ICON_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_ID_AUTHORITY -Windows.Win32.Foundation.ERROR_INVALID_IMAGE_HASH -Windows.Win32.Foundation.ERROR_INVALID_IMPORT_OF_NON_DLL -Windows.Win32.Foundation.ERROR_INVALID_INDEX -Windows.Win32.Foundation.ERROR_INVALID_KERNEL_INFO_VERSION -Windows.Win32.Foundation.ERROR_INVALID_KEYBOARD_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_LABEL -Windows.Win32.Foundation.ERROR_INVALID_LB_MESSAGE -Windows.Win32.Foundation.ERROR_INVALID_LDT_DESCRIPTOR -Windows.Win32.Foundation.ERROR_INVALID_LDT_OFFSET -Windows.Win32.Foundation.ERROR_INVALID_LDT_SIZE -Windows.Win32.Foundation.ERROR_INVALID_LEVEL -Windows.Win32.Foundation.ERROR_INVALID_LIST_FORMAT -Windows.Win32.Foundation.ERROR_INVALID_LOCK_RANGE -Windows.Win32.Foundation.ERROR_INVALID_LOGON_HOURS -Windows.Win32.Foundation.ERROR_INVALID_LOGON_TYPE -Windows.Win32.Foundation.ERROR_INVALID_MEMBER -Windows.Win32.Foundation.ERROR_INVALID_MENU_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_MESSAGE -Windows.Win32.Foundation.ERROR_INVALID_MESSAGEDEST -Windows.Win32.Foundation.ERROR_INVALID_MESSAGENAME -Windows.Win32.Foundation.ERROR_INVALID_MINALLOCSIZE -Windows.Win32.Foundation.ERROR_INVALID_MODULETYPE -Windows.Win32.Foundation.ERROR_INVALID_MONITOR_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_MSGBOX_STYLE -Windows.Win32.Foundation.ERROR_INVALID_NAME -Windows.Win32.Foundation.ERROR_INVALID_NETNAME -Windows.Win32.Foundation.ERROR_INVALID_OPLOCK_PROTOCOL -Windows.Win32.Foundation.ERROR_INVALID_ORDINAL -Windows.Win32.Foundation.ERROR_INVALID_OWNER -Windows.Win32.Foundation.ERROR_INVALID_PACKAGE_SID_LENGTH -Windows.Win32.Foundation.ERROR_INVALID_PARAMETER -Windows.Win32.Foundation.ERROR_INVALID_PASSWORD -Windows.Win32.Foundation.ERROR_INVALID_PASSWORDNAME -Windows.Win32.Foundation.ERROR_INVALID_PATCH_XML -Windows.Win32.Foundation.ERROR_INVALID_PEP_INFO_VERSION -Windows.Win32.Foundation.ERROR_INVALID_PLUGPLAY_DEVICE_PATH -Windows.Win32.Foundation.ERROR_INVALID_PORT_ATTRIBUTES -Windows.Win32.Foundation.ERROR_INVALID_PRIMARY_GROUP -Windows.Win32.Foundation.ERROR_INVALID_PRINTER_COMMAND -Windows.Win32.Foundation.ERROR_INVALID_PRINTER_NAME -Windows.Win32.Foundation.ERROR_INVALID_PRINTER_STATE -Windows.Win32.Foundation.ERROR_INVALID_PRIORITY -Windows.Win32.Foundation.ERROR_INVALID_QUOTA_LOWER -Windows.Win32.Foundation.ERROR_INVALID_REPARSE_DATA -Windows.Win32.Foundation.ERROR_INVALID_SCROLLBAR_RANGE -Windows.Win32.Foundation.ERROR_INVALID_SECURITY_DESCR -Windows.Win32.Foundation.ERROR_INVALID_SEGDPL -Windows.Win32.Foundation.ERROR_INVALID_SEGMENT_NUMBER -Windows.Win32.Foundation.ERROR_INVALID_SEPARATOR_FILE -Windows.Win32.Foundation.ERROR_INVALID_SERVER_STATE -Windows.Win32.Foundation.ERROR_INVALID_SERVICE_ACCOUNT -Windows.Win32.Foundation.ERROR_INVALID_SERVICE_CONTROL -Windows.Win32.Foundation.ERROR_INVALID_SERVICE_LOCK -Windows.Win32.Foundation.ERROR_INVALID_SERVICENAME -Windows.Win32.Foundation.ERROR_INVALID_SHARENAME -Windows.Win32.Foundation.ERROR_INVALID_SHOWWIN_COMMAND -Windows.Win32.Foundation.ERROR_INVALID_SID -Windows.Win32.Foundation.ERROR_INVALID_SIGNAL_NUMBER -Windows.Win32.Foundation.ERROR_INVALID_SPI_VALUE -Windows.Win32.Foundation.ERROR_INVALID_STACKSEG -Windows.Win32.Foundation.ERROR_INVALID_STARTING_CODESEG -Windows.Win32.Foundation.ERROR_INVALID_SUB_AUTHORITY -Windows.Win32.Foundation.ERROR_INVALID_TABLE -Windows.Win32.Foundation.ERROR_INVALID_TARGET_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_TASK_INDEX -Windows.Win32.Foundation.ERROR_INVALID_TASK_NAME -Windows.Win32.Foundation.ERROR_INVALID_THREAD_ID -Windows.Win32.Foundation.ERROR_INVALID_TIME -Windows.Win32.Foundation.ERROR_INVALID_TOKEN -Windows.Win32.Foundation.ERROR_INVALID_UNWIND_TARGET -Windows.Win32.Foundation.ERROR_INVALID_USER_BUFFER -Windows.Win32.Foundation.ERROR_INVALID_USER_PRINCIPAL_NAME -Windows.Win32.Foundation.ERROR_INVALID_VARIANT -Windows.Win32.Foundation.ERROR_INVALID_VERIFY_SWITCH -Windows.Win32.Foundation.ERROR_INVALID_WINDOW_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_WORKSTATION -Windows.Win32.Foundation.ERROR_IO_DEVICE -Windows.Win32.Foundation.ERROR_IO_INCOMPLETE -Windows.Win32.Foundation.ERROR_IO_PENDING -Windows.Win32.Foundation.ERROR_IO_PRIVILEGE_FAILED -Windows.Win32.Foundation.ERROR_IO_REISSUE_AS_CACHED -Windows.Win32.Foundation.ERROR_IOPL_NOT_ENABLED -Windows.Win32.Foundation.ERROR_IP_ADDRESS_CONFLICT1 -Windows.Win32.Foundation.ERROR_IP_ADDRESS_CONFLICT2 -Windows.Win32.Foundation.ERROR_IPSEC_IKE_TIMED_OUT -Windows.Win32.Foundation.ERROR_IRQ_BUSY -Windows.Win32.Foundation.ERROR_IS_JOIN_PATH -Windows.Win32.Foundation.ERROR_IS_JOIN_TARGET -Windows.Win32.Foundation.ERROR_IS_JOINED -Windows.Win32.Foundation.ERROR_IS_SUBST_PATH -Windows.Win32.Foundation.ERROR_IS_SUBST_TARGET -Windows.Win32.Foundation.ERROR_IS_SUBSTED -Windows.Win32.Foundation.ERROR_ITERATED_DATA_EXCEEDS_64k -Windows.Win32.Foundation.ERROR_JOB_NO_CONTAINER -Windows.Win32.Foundation.ERROR_JOIN_TO_JOIN -Windows.Win32.Foundation.ERROR_JOIN_TO_SUBST -Windows.Win32.Foundation.ERROR_JOURNAL_DELETE_IN_PROGRESS -Windows.Win32.Foundation.ERROR_JOURNAL_ENTRY_DELETED -Windows.Win32.Foundation.ERROR_JOURNAL_HOOK_SET -Windows.Win32.Foundation.ERROR_JOURNAL_NOT_ACTIVE -Windows.Win32.Foundation.ERROR_KERNEL_APC -Windows.Win32.Foundation.ERROR_KEY_DELETED -Windows.Win32.Foundation.ERROR_KEY_HAS_CHILDREN -Windows.Win32.Foundation.ERROR_KM_DRIVER_BLOCKED -Windows.Win32.Foundation.ERROR_LABEL_TOO_LONG -Windows.Win32.Foundation.ERROR_LAST_ADMIN -Windows.Win32.Foundation.ERROR_LB_WITHOUT_TABSTOPS -Windows.Win32.Foundation.ERROR_LICENSE_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_LINUX_SUBSYSTEM_NOT_PRESENT -Windows.Win32.Foundation.ERROR_LINUX_SUBSYSTEM_UPDATE_REQUIRED -Windows.Win32.Foundation.ERROR_LISTBOX_ID_NOT_FOUND -Windows.Win32.Foundation.ERROR_LM_CROSS_ENCRYPTION_REQUIRED -Windows.Win32.Foundation.ERROR_LOCAL_POLICY_MODIFICATION_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_LOCAL_USER_SESSION_KEY -Windows.Win32.Foundation.ERROR_LOCK_FAILED -Windows.Win32.Foundation.ERROR_LOCK_VIOLATION -Windows.Win32.Foundation.ERROR_LOCKED -Windows.Win32.Foundation.ERROR_LOG_FILE_FULL -Windows.Win32.Foundation.ERROR_LOG_HARD_ERROR -Windows.Win32.Foundation.ERROR_LOGIN_TIME_RESTRICTION -Windows.Win32.Foundation.ERROR_LOGIN_WKSTA_RESTRICTION -Windows.Win32.Foundation.ERROR_LOGON_FAILURE -Windows.Win32.Foundation.ERROR_LOGON_NOT_GRANTED -Windows.Win32.Foundation.ERROR_LOGON_SERVER_CONFLICT -Windows.Win32.Foundation.ERROR_LOGON_SESSION_COLLISION -Windows.Win32.Foundation.ERROR_LOGON_SESSION_EXISTS -Windows.Win32.Foundation.ERROR_LOGON_TYPE_NOT_GRANTED -Windows.Win32.Foundation.ERROR_LONGJUMP -Windows.Win32.Foundation.ERROR_LOST_MODE_LOGON_RESTRICTION -Windows.Win32.Foundation.ERROR_LOST_WRITEBEHIND_DATA -Windows.Win32.Foundation.ERROR_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR -Windows.Win32.Foundation.ERROR_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED -Windows.Win32.Foundation.ERROR_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR -Windows.Win32.Foundation.ERROR_LUIDS_EXHAUSTED -Windows.Win32.Foundation.ERROR_MACHINE_LOCKED -Windows.Win32.Foundation.ERROR_MAGAZINE_NOT_PRESENT -Windows.Win32.Foundation.ERROR_MAPPED_ALIGNMENT -Windows.Win32.Foundation.ERROR_MARKED_TO_DISALLOW_WRITES -Windows.Win32.Foundation.ERROR_MARSHALL_OVERFLOW -Windows.Win32.Foundation.ERROR_MAX_SESSIONS_REACHED -Windows.Win32.Foundation.ERROR_MAX_THRDS_REACHED -Windows.Win32.Foundation.ERROR_MCA_EXCEPTION -Windows.Win32.Foundation.ERROR_MCA_OCCURED -Windows.Win32.Foundation.ERROR_MEDIA_CHANGED -Windows.Win32.Foundation.ERROR_MEDIA_CHECK -Windows.Win32.Foundation.ERROR_MEMBER_IN_ALIAS -Windows.Win32.Foundation.ERROR_MEMBER_IN_GROUP -Windows.Win32.Foundation.ERROR_MEMBER_NOT_IN_ALIAS -Windows.Win32.Foundation.ERROR_MEMBER_NOT_IN_GROUP -Windows.Win32.Foundation.ERROR_MEMBERS_PRIMARY_GROUP -Windows.Win32.Foundation.ERROR_MEMORY_HARDWARE -Windows.Win32.Foundation.ERROR_MENU_ITEM_NOT_FOUND -Windows.Win32.Foundation.ERROR_MESSAGE_SYNC_ONLY -Windows.Win32.Foundation.ERROR_META_EXPANSION_TOO_LONG -Windows.Win32.Foundation.ERROR_MISSING_SYSTEMFILE -Windows.Win32.Foundation.ERROR_MOD_NOT_FOUND -Windows.Win32.Foundation.ERROR_MORE_DATA -Windows.Win32.Foundation.ERROR_MORE_WRITES -Windows.Win32.Foundation.ERROR_MOUNT_POINT_NOT_RESOLVED -Windows.Win32.Foundation.ERROR_MP_PROCESSOR_MISMATCH -Windows.Win32.Foundation.ERROR_MR_MID_NOT_FOUND -Windows.Win32.Foundation.ERROR_MULTIPLE_FAULT_VIOLATION -Windows.Win32.Foundation.ERROR_MUTANT_LIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_MUTUAL_AUTH_FAILED -Windows.Win32.Foundation.ERROR_NEGATIVE_SEEK -Windows.Win32.Foundation.ERROR_NESTING_NOT_ALLOWED -Windows.Win32.Foundation.ERROR_NET_OPEN_FAILED -Windows.Win32.Foundation.ERROR_NET_WRITE_FAULT -Windows.Win32.Foundation.ERROR_NETLOGON_NOT_STARTED -Windows.Win32.Foundation.ERROR_NETNAME_DELETED -Windows.Win32.Foundation.ERROR_NETWORK_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_NETWORK_ACCESS_DENIED_EDP -Windows.Win32.Foundation.ERROR_NETWORK_BUSY -Windows.Win32.Foundation.ERROR_NETWORK_UNREACHABLE -Windows.Win32.Foundation.ERROR_NO_ACE_CONDITION -Windows.Win32.Foundation.ERROR_NO_ASSOCIATION -Windows.Win32.Foundation.ERROR_NO_BYPASSIO_DRIVER_SUPPORT -Windows.Win32.Foundation.ERROR_NO_CALLBACK_ACTIVE -Windows.Win32.Foundation.ERROR_NO_DATA -Windows.Win32.Foundation.ERROR_NO_DATA_DETECTED -Windows.Win32.Foundation.ERROR_NO_EFS -Windows.Win32.Foundation.ERROR_NO_EVENT_PAIR -Windows.Win32.Foundation.ERROR_NO_GUID_TRANSLATION -Windows.Win32.Foundation.ERROR_NO_IMPERSONATION_TOKEN -Windows.Win32.Foundation.ERROR_NO_INHERITANCE -Windows.Win32.Foundation.ERROR_NO_LOG_SPACE -Windows.Win32.Foundation.ERROR_NO_LOGON_SERVERS -Windows.Win32.Foundation.ERROR_NO_MATCH -Windows.Win32.Foundation.ERROR_NO_MEDIA_IN_DRIVE -Windows.Win32.Foundation.ERROR_NO_MORE_DEVICES -Windows.Win32.Foundation.ERROR_NO_MORE_FILES -Windows.Win32.Foundation.ERROR_NO_MORE_ITEMS -Windows.Win32.Foundation.ERROR_NO_MORE_MATCHES -Windows.Win32.Foundation.ERROR_NO_MORE_SEARCH_HANDLES -Windows.Win32.Foundation.ERROR_NO_MORE_USER_HANDLES -Windows.Win32.Foundation.ERROR_NO_NET_OR_BAD_PATH -Windows.Win32.Foundation.ERROR_NO_NETWORK -Windows.Win32.Foundation.ERROR_NO_NVRAM_RESOURCES -Windows.Win32.Foundation.ERROR_NO_PAGEFILE -Windows.Win32.Foundation.ERROR_NO_PHYSICALLY_ALIGNED_FREE_SPACE_FOUND -Windows.Win32.Foundation.ERROR_NO_PROC_SLOTS -Windows.Win32.Foundation.ERROR_NO_PROMOTION_ACTIVE -Windows.Win32.Foundation.ERROR_NO_QUOTAS_FOR_ACCOUNT -Windows.Win32.Foundation.ERROR_NO_RANGES_PROCESSED -Windows.Win32.Foundation.ERROR_NO_RECOVERY_POLICY -Windows.Win32.Foundation.ERROR_NO_RECOVERY_PROGRAM -Windows.Win32.Foundation.ERROR_NO_SCROLLBARS -Windows.Win32.Foundation.ERROR_NO_SECRETS -Windows.Win32.Foundation.ERROR_NO_SECURITY_ON_OBJECT -Windows.Win32.Foundation.ERROR_NO_SHUTDOWN_IN_PROGRESS -Windows.Win32.Foundation.ERROR_NO_SIGNAL_SENT -Windows.Win32.Foundation.ERROR_NO_SITE_SETTINGS_OBJECT -Windows.Win32.Foundation.ERROR_NO_SITENAME -Windows.Win32.Foundation.ERROR_NO_SPOOL_SPACE -Windows.Win32.Foundation.ERROR_NO_SUCH_ALIAS -Windows.Win32.Foundation.ERROR_NO_SUCH_DEVICE -Windows.Win32.Foundation.ERROR_NO_SUCH_DOMAIN -Windows.Win32.Foundation.ERROR_NO_SUCH_GROUP -Windows.Win32.Foundation.ERROR_NO_SUCH_LOGON_SESSION -Windows.Win32.Foundation.ERROR_NO_SUCH_MEMBER -Windows.Win32.Foundation.ERROR_NO_SUCH_PACKAGE -Windows.Win32.Foundation.ERROR_NO_SUCH_PRIVILEGE -Windows.Win32.Foundation.ERROR_NO_SUCH_SITE -Windows.Win32.Foundation.ERROR_NO_SUCH_USER -Windows.Win32.Foundation.ERROR_NO_SYSTEM_MENU -Windows.Win32.Foundation.ERROR_NO_SYSTEM_RESOURCES -Windows.Win32.Foundation.ERROR_NO_TASK_QUEUE -Windows.Win32.Foundation.ERROR_NO_TOKEN -Windows.Win32.Foundation.ERROR_NO_TRACKING_SERVICE -Windows.Win32.Foundation.ERROR_NO_TRUST_LSA_SECRET -Windows.Win32.Foundation.ERROR_NO_TRUST_SAM_ACCOUNT -Windows.Win32.Foundation.ERROR_NO_UNICODE_TRANSLATION -Windows.Win32.Foundation.ERROR_NO_USER_KEYS -Windows.Win32.Foundation.ERROR_NO_USER_SESSION_KEY -Windows.Win32.Foundation.ERROR_NO_VOLUME_ID -Windows.Win32.Foundation.ERROR_NO_VOLUME_LABEL -Windows.Win32.Foundation.ERROR_NO_WILDCARD_CHARACTERS -Windows.Win32.Foundation.ERROR_NO_WORK_DONE -Windows.Win32.Foundation.ERROR_NO_WRITABLE_DC_FOUND -Windows.Win32.Foundation.ERROR_NO_YIELD_PERFORMED -Windows.Win32.Foundation.ERROR_NOACCESS -Windows.Win32.Foundation.ERROR_NOINTERFACE -Windows.Win32.Foundation.ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT -Windows.Win32.Foundation.ERROR_NOLOGON_SERVER_TRUST_ACCOUNT -Windows.Win32.Foundation.ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT -Windows.Win32.Foundation.ERROR_NON_ACCOUNT_SID -Windows.Win32.Foundation.ERROR_NON_DOMAIN_SID -Windows.Win32.Foundation.ERROR_NON_MDICHILD_WINDOW -Windows.Win32.Foundation.ERROR_NONE_MAPPED -Windows.Win32.Foundation.ERROR_NONPAGED_SYSTEM_RESOURCES -Windows.Win32.Foundation.ERROR_NOT_A_CLOUD_FILE -Windows.Win32.Foundation.ERROR_NOT_A_CLOUD_SYNC_ROOT -Windows.Win32.Foundation.ERROR_NOT_A_DAX_VOLUME -Windows.Win32.Foundation.ERROR_NOT_A_REPARSE_POINT -Windows.Win32.Foundation.ERROR_NOT_ALL_ASSIGNED -Windows.Win32.Foundation.ERROR_NOT_ALLOWED_ON_SYSTEM_FILE -Windows.Win32.Foundation.ERROR_NOT_APPCONTAINER -Windows.Win32.Foundation.ERROR_NOT_AUTHENTICATED -Windows.Win32.Foundation.ERROR_NOT_CAPABLE -Windows.Win32.Foundation.ERROR_NOT_CHILD_WINDOW -Windows.Win32.Foundation.ERROR_NOT_CONNECTED -Windows.Win32.Foundation.ERROR_NOT_CONTAINER -Windows.Win32.Foundation.ERROR_NOT_DAX_MAPPABLE -Windows.Win32.Foundation.ERROR_NOT_DOS_DISK -Windows.Win32.Foundation.ERROR_NOT_ENOUGH_MEMORY -Windows.Win32.Foundation.ERROR_NOT_ENOUGH_QUOTA -Windows.Win32.Foundation.ERROR_NOT_ENOUGH_SERVER_MEMORY -Windows.Win32.Foundation.ERROR_NOT_EXPORT_FORMAT -Windows.Win32.Foundation.ERROR_NOT_FOUND -Windows.Win32.Foundation.ERROR_NOT_GUI_PROCESS -Windows.Win32.Foundation.ERROR_NOT_JOINED -Windows.Win32.Foundation.ERROR_NOT_LOCKED -Windows.Win32.Foundation.ERROR_NOT_LOGGED_ON -Windows.Win32.Foundation.ERROR_NOT_LOGON_PROCESS -Windows.Win32.Foundation.ERROR_NOT_OWNER -Windows.Win32.Foundation.ERROR_NOT_READ_FROM_COPY -Windows.Win32.Foundation.ERROR_NOT_READY -Windows.Win32.Foundation.ERROR_NOT_REDUNDANT_STORAGE -Windows.Win32.Foundation.ERROR_NOT_REGISTRY_FILE -Windows.Win32.Foundation.ERROR_NOT_SAFE_MODE_DRIVER -Windows.Win32.Foundation.ERROR_NOT_SAFEBOOT_SERVICE -Windows.Win32.Foundation.ERROR_NOT_SAME_DEVICE -Windows.Win32.Foundation.ERROR_NOT_SAME_OBJECT -Windows.Win32.Foundation.ERROR_NOT_SUBSTED -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_IN_APPCONTAINER -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_ON_DAX -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_ON_SBS -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_AUDITING -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_BTT -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_BYPASSIO -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_CACHED_HANDLE -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_COMPRESSION -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_DEDUPLICATION -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_ENCRYPTION -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_MONITORING -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_REPLICATION -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_SNAPSHOT -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_VIRTUALIZATION -Windows.Win32.Foundation.ERROR_NOT_TINY_STREAM -Windows.Win32.Foundation.ERROR_NOTHING_TO_TERMINATE -Windows.Win32.Foundation.ERROR_NOTIFICATION_GUID_ALREADY_DEFINED -Windows.Win32.Foundation.ERROR_NOTIFY_CLEANUP -Windows.Win32.Foundation.ERROR_NOTIFY_ENUM_DIR -Windows.Win32.Foundation.ERROR_NT_CROSS_ENCRYPTION_REQUIRED -Windows.Win32.Foundation.ERROR_NTLM_BLOCKED -Windows.Win32.Foundation.ERROR_NULL_LM_PASSWORD -Windows.Win32.Foundation.ERROR_OBJECT_IS_IMMUTABLE -Windows.Win32.Foundation.ERROR_OBJECT_NAME_EXISTS -Windows.Win32.Foundation.ERROR_OBJECT_NOT_EXTERNALLY_BACKED -Windows.Win32.Foundation.ERROR_OFFLOAD_READ_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_OFFLOAD_READ_FLT_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_OFFLOAD_WRITE_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_OFFLOAD_WRITE_FLT_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_OFFSET_ALIGNMENT_VIOLATION -Windows.Win32.Foundation.ERROR_OLD_WIN_VERSION -Windows.Win32.Foundation.ERROR_ONLY_IF_CONNECTED -Windows.Win32.Foundation.ERROR_OPEN_FAILED -Windows.Win32.Foundation.ERROR_OPEN_FILES -Windows.Win32.Foundation.ERROR_OPERATION_ABORTED -Windows.Win32.Foundation.ERROR_OPERATION_IN_PROGRESS -Windows.Win32.Foundation.ERROR_OPLOCK_BREAK_IN_PROGRESS -Windows.Win32.Foundation.ERROR_OPLOCK_HANDLE_CLOSED -Windows.Win32.Foundation.ERROR_OPLOCK_NOT_GRANTED -Windows.Win32.Foundation.ERROR_OPLOCK_SWITCHED_TO_NEW_HANDLE -Windows.Win32.Foundation.ERROR_ORPHAN_NAME_EXHAUSTED -Windows.Win32.Foundation.ERROR_OUT_OF_PAPER -Windows.Win32.Foundation.ERROR_OUT_OF_STRUCTURES -Windows.Win32.Foundation.ERROR_OUTOFMEMORY -Windows.Win32.Foundation.ERROR_OVERRIDE_NOCHANGES -Windows.Win32.Foundation.ERROR_PAGE_FAULT_COPY_ON_WRITE -Windows.Win32.Foundation.ERROR_PAGE_FAULT_DEMAND_ZERO -Windows.Win32.Foundation.ERROR_PAGE_FAULT_GUARD_PAGE -Windows.Win32.Foundation.ERROR_PAGE_FAULT_PAGING_FILE -Windows.Win32.Foundation.ERROR_PAGE_FAULT_TRANSITION -Windows.Win32.Foundation.ERROR_PAGED_SYSTEM_RESOURCES -Windows.Win32.Foundation.ERROR_PAGEFILE_CREATE_FAILED -Windows.Win32.Foundation.ERROR_PAGEFILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_PAGEFILE_QUOTA -Windows.Win32.Foundation.ERROR_PAGEFILE_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_PARAMETER_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_PARTIAL_COPY -Windows.Win32.Foundation.ERROR_PARTITION_FAILURE -Windows.Win32.Foundation.ERROR_PARTITION_TERMINATING -Windows.Win32.Foundation.ERROR_PASSWORD_CHANGE_REQUIRED -Windows.Win32.Foundation.ERROR_PASSWORD_EXPIRED -Windows.Win32.Foundation.ERROR_PASSWORD_MUST_CHANGE -Windows.Win32.Foundation.ERROR_PASSWORD_RESTRICTION -Windows.Win32.Foundation.ERROR_PATCH_MANAGED_ADVERTISED_PRODUCT -Windows.Win32.Foundation.ERROR_PATCH_NO_SEQUENCE -Windows.Win32.Foundation.ERROR_PATCH_PACKAGE_INVALID -Windows.Win32.Foundation.ERROR_PATCH_PACKAGE_OPEN_FAILED -Windows.Win32.Foundation.ERROR_PATCH_PACKAGE_REJECTED -Windows.Win32.Foundation.ERROR_PATCH_PACKAGE_UNSUPPORTED -Windows.Win32.Foundation.ERROR_PATCH_REMOVAL_DISALLOWED -Windows.Win32.Foundation.ERROR_PATCH_REMOVAL_UNSUPPORTED -Windows.Win32.Foundation.ERROR_PATCH_TARGET_NOT_FOUND -Windows.Win32.Foundation.ERROR_PATH_BUSY -Windows.Win32.Foundation.ERROR_PATH_NOT_FOUND -Windows.Win32.Foundation.ERROR_PER_USER_TRUST_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_PIPE_BUSY -Windows.Win32.Foundation.ERROR_PIPE_CONNECTED -Windows.Win32.Foundation.ERROR_PIPE_LISTENING -Windows.Win32.Foundation.ERROR_PIPE_LOCAL -Windows.Win32.Foundation.ERROR_PIPE_NOT_CONNECTED -Windows.Win32.Foundation.ERROR_PKINIT_FAILURE -Windows.Win32.Foundation.ERROR_PLUGPLAY_QUERY_VETOED -Windows.Win32.Foundation.ERROR_PNP_BAD_MPS_TABLE -Windows.Win32.Foundation.ERROR_PNP_INVALID_ID -Windows.Win32.Foundation.ERROR_PNP_IRQ_TRANSLATION_FAILED -Windows.Win32.Foundation.ERROR_PNP_QUERY_REMOVE_DEVICE_TIMEOUT -Windows.Win32.Foundation.ERROR_PNP_QUERY_REMOVE_RELATED_DEVICE_TIMEOUT -Windows.Win32.Foundation.ERROR_PNP_QUERY_REMOVE_UNRELATED_DEVICE_TIMEOUT -Windows.Win32.Foundation.ERROR_PNP_REBOOT_REQUIRED -Windows.Win32.Foundation.ERROR_PNP_RESTART_ENUMERATION -Windows.Win32.Foundation.ERROR_PNP_TRANSLATION_FAILED -Windows.Win32.Foundation.ERROR_POINT_NOT_FOUND -Windows.Win32.Foundation.ERROR_POLICY_OBJECT_NOT_FOUND -Windows.Win32.Foundation.ERROR_POLICY_ONLY_IN_DS -Windows.Win32.Foundation.ERROR_POPUP_ALREADY_ACTIVE -Windows.Win32.Foundation.ERROR_PORT_MESSAGE_TOO_LONG -Windows.Win32.Foundation.ERROR_PORT_NOT_SET -Windows.Win32.Foundation.ERROR_PORT_UNREACHABLE -Windows.Win32.Foundation.ERROR_POSSIBLE_DEADLOCK -Windows.Win32.Foundation.ERROR_POTENTIAL_FILE_FOUND -Windows.Win32.Foundation.ERROR_PREDEFINED_HANDLE -Windows.Win32.Foundation.ERROR_PRIMARY_TRANSPORT_CONNECT_FAILED -Windows.Win32.Foundation.ERROR_PRINT_CANCELLED -Windows.Win32.Foundation.ERROR_PRINTER_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_PRINTER_DELETED -Windows.Win32.Foundation.ERROR_PRINTER_DRIVER_ALREADY_INSTALLED -Windows.Win32.Foundation.ERROR_PRINTQ_FULL -Windows.Win32.Foundation.ERROR_PRIVATE_DIALOG_INDEX -Windows.Win32.Foundation.ERROR_PRIVILEGE_NOT_HELD -Windows.Win32.Foundation.ERROR_PROC_NOT_FOUND -Windows.Win32.Foundation.ERROR_PROCESS_ABORTED -Windows.Win32.Foundation.ERROR_PROCESS_IN_JOB -Windows.Win32.Foundation.ERROR_PROCESS_IS_PROTECTED -Windows.Win32.Foundation.ERROR_PROCESS_MODE_ALREADY_BACKGROUND -Windows.Win32.Foundation.ERROR_PROCESS_MODE_NOT_BACKGROUND -Windows.Win32.Foundation.ERROR_PROCESS_NOT_IN_JOB -Windows.Win32.Foundation.ERROR_PRODUCT_UNINSTALLED -Windows.Win32.Foundation.ERROR_PRODUCT_VERSION -Windows.Win32.Foundation.ERROR_PROFILING_AT_LIMIT -Windows.Win32.Foundation.ERROR_PROFILING_NOT_STARTED -Windows.Win32.Foundation.ERROR_PROFILING_NOT_STOPPED -Windows.Win32.Foundation.ERROR_PROMOTION_ACTIVE -Windows.Win32.Foundation.ERROR_PROTOCOL_UNREACHABLE -Windows.Win32.Foundation.ERROR_PWD_HISTORY_CONFLICT -Windows.Win32.Foundation.ERROR_PWD_TOO_LONG -Windows.Win32.Foundation.ERROR_PWD_TOO_RECENT -Windows.Win32.Foundation.ERROR_PWD_TOO_SHORT -Windows.Win32.Foundation.ERROR_QUOTA_ACTIVITY -Windows.Win32.Foundation.ERROR_QUOTA_LIST_INCONSISTENT -Windows.Win32.Foundation.ERROR_RANGE_LIST_CONFLICT -Windows.Win32.Foundation.ERROR_RANGE_NOT_FOUND -Windows.Win32.Foundation.ERROR_READ_FAULT -Windows.Win32.Foundation.ERROR_RECEIVE_EXPEDITED -Windows.Win32.Foundation.ERROR_RECEIVE_PARTIAL -Windows.Win32.Foundation.ERROR_RECEIVE_PARTIAL_EXPEDITED -Windows.Win32.Foundation.ERROR_RECOVERY_FAILURE -Windows.Win32.Foundation.ERROR_REDIR_PAUSED -Windows.Win32.Foundation.ERROR_REDIRECTOR_HAS_OPEN_HANDLES -Windows.Win32.Foundation.ERROR_REG_NAT_CONSUMPTION -Windows.Win32.Foundation.ERROR_REGISTRY_CORRUPT -Windows.Win32.Foundation.ERROR_REGISTRY_HIVE_RECOVERED -Windows.Win32.Foundation.ERROR_REGISTRY_IO_FAILED -Windows.Win32.Foundation.ERROR_REGISTRY_QUOTA_LIMIT -Windows.Win32.Foundation.ERROR_REGISTRY_RECOVERED -Windows.Win32.Foundation.ERROR_RELOC_CHAIN_XEEDS_SEGLIM -Windows.Win32.Foundation.ERROR_REM_NOT_LIST -Windows.Win32.Foundation.ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED -Windows.Win32.Foundation.ERROR_REMOTE_SESSION_LIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_REMOTE_STORAGE_MEDIA_ERROR -Windows.Win32.Foundation.ERROR_REMOTE_STORAGE_NOT_ACTIVE -Windows.Win32.Foundation.ERROR_REPARSE -Windows.Win32.Foundation.ERROR_REPARSE_ATTRIBUTE_CONFLICT -Windows.Win32.Foundation.ERROR_REPARSE_OBJECT -Windows.Win32.Foundation.ERROR_REPARSE_POINT_ENCOUNTERED -Windows.Win32.Foundation.ERROR_REPARSE_TAG_INVALID -Windows.Win32.Foundation.ERROR_REPARSE_TAG_MISMATCH -Windows.Win32.Foundation.ERROR_REPLY_MESSAGE_MISMATCH -Windows.Win32.Foundation.ERROR_REQ_NOT_ACCEP -Windows.Win32.Foundation.ERROR_REQUEST_ABORTED -Windows.Win32.Foundation.ERROR_REQUEST_OUT_OF_SEQUENCE -Windows.Win32.Foundation.ERROR_REQUEST_PAUSED -Windows.Win32.Foundation.ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION -Windows.Win32.Foundation.ERROR_RESIDENT_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_RESOURCE_CALL_TIMED_OUT -Windows.Win32.Foundation.ERROR_RESOURCE_DATA_NOT_FOUND -Windows.Win32.Foundation.ERROR_RESOURCE_LANG_NOT_FOUND -Windows.Win32.Foundation.ERROR_RESOURCE_NAME_NOT_FOUND -Windows.Win32.Foundation.ERROR_RESOURCE_REQUIREMENTS_CHANGED -Windows.Win32.Foundation.ERROR_RESOURCE_TYPE_NOT_FOUND -Windows.Win32.Foundation.ERROR_RESTART_APPLICATION -Windows.Win32.Foundation.ERROR_RESUME_HIBERNATION -Windows.Win32.Foundation.ERROR_RETRY -Windows.Win32.Foundation.ERROR_RETURN_ADDRESS_HIJACK_ATTEMPT -Windows.Win32.Foundation.ERROR_REVISION_MISMATCH -Windows.Win32.Foundation.ERROR_RING2_STACK_IN_USE -Windows.Win32.Foundation.ERROR_RING2SEG_MUST_BE_MOVABLE -Windows.Win32.Foundation.ERROR_RMODE_APP -Windows.Win32.Foundation.ERROR_ROWSNOTRELEASED -Windows.Win32.Foundation.ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT -Windows.Win32.Foundation.ERROR_RUNLEVEL_SWITCH_TIMEOUT -Windows.Win32.Foundation.ERROR_RWRAW_ENCRYPTED_FILE_NOT_ENCRYPTED -Windows.Win32.Foundation.ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_FILEOFFSET -Windows.Win32.Foundation.ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_FILERANGE -Windows.Win32.Foundation.ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_PARAMETER -Windows.Win32.Foundation.ERROR_RXACT_COMMIT_FAILURE -Windows.Win32.Foundation.ERROR_RXACT_COMMIT_NECESSARY -Windows.Win32.Foundation.ERROR_RXACT_COMMITTED -Windows.Win32.Foundation.ERROR_RXACT_INVALID_STATE -Windows.Win32.Foundation.ERROR_RXACT_STATE_CREATED -Windows.Win32.Foundation.ERROR_SAM_INIT_FAILURE -Windows.Win32.Foundation.ERROR_SAME_DRIVE -Windows.Win32.Foundation.ERROR_SCOPE_NOT_FOUND -Windows.Win32.Foundation.ERROR_SCREEN_ALREADY_LOCKED -Windows.Win32.Foundation.ERROR_SCRUB_DATA_DISABLED -Windows.Win32.Foundation.ERROR_SECRET_TOO_LONG -Windows.Win32.Foundation.ERROR_SECTION_DIRECT_MAP_ONLY -Windows.Win32.Foundation.ERROR_SECTOR_NOT_FOUND -Windows.Win32.Foundation.ERROR_SECURITY_DENIES_OPERATION -Windows.Win32.Foundation.ERROR_SECURITY_STREAM_IS_INCONSISTENT -Windows.Win32.Foundation.ERROR_SEEK -Windows.Win32.Foundation.ERROR_SEEK_ON_DEVICE -Windows.Win32.Foundation.ERROR_SEGMENT_NOTIFICATION -Windows.Win32.Foundation.ERROR_SEM_IS_SET -Windows.Win32.Foundation.ERROR_SEM_NOT_FOUND -Windows.Win32.Foundation.ERROR_SEM_OWNER_DIED -Windows.Win32.Foundation.ERROR_SEM_TIMEOUT -Windows.Win32.Foundation.ERROR_SEM_USER_LIMIT -Windows.Win32.Foundation.ERROR_SERIAL_NO_DEVICE -Windows.Win32.Foundation.ERROR_SERVER_DISABLED -Windows.Win32.Foundation.ERROR_SERVER_HAS_OPEN_HANDLES -Windows.Win32.Foundation.ERROR_SERVER_NOT_DISABLED -Windows.Win32.Foundation.ERROR_SERVER_SHUTDOWN_IN_PROGRESS -Windows.Win32.Foundation.ERROR_SERVER_SID_MISMATCH -Windows.Win32.Foundation.ERROR_SERVER_TRANSPORT_CONFLICT -Windows.Win32.Foundation.ERROR_SERVICE_ALREADY_RUNNING -Windows.Win32.Foundation.ERROR_SERVICE_CANNOT_ACCEPT_CTRL -Windows.Win32.Foundation.ERROR_SERVICE_DATABASE_LOCKED -Windows.Win32.Foundation.ERROR_SERVICE_DEPENDENCY_DELETED -Windows.Win32.Foundation.ERROR_SERVICE_DEPENDENCY_FAIL -Windows.Win32.Foundation.ERROR_SERVICE_DISABLED -Windows.Win32.Foundation.ERROR_SERVICE_DOES_NOT_EXIST -Windows.Win32.Foundation.ERROR_SERVICE_EXISTS -Windows.Win32.Foundation.ERROR_SERVICE_LOGON_FAILED -Windows.Win32.Foundation.ERROR_SERVICE_MARKED_FOR_DELETE -Windows.Win32.Foundation.ERROR_SERVICE_NEVER_STARTED -Windows.Win32.Foundation.ERROR_SERVICE_NO_THREAD -Windows.Win32.Foundation.ERROR_SERVICE_NOT_ACTIVE -Windows.Win32.Foundation.ERROR_SERVICE_NOT_FOUND -Windows.Win32.Foundation.ERROR_SERVICE_NOT_IN_EXE -Windows.Win32.Foundation.ERROR_SERVICE_NOTIFICATION -Windows.Win32.Foundation.ERROR_SERVICE_NOTIFY_CLIENT_LAGGING -Windows.Win32.Foundation.ERROR_SERVICE_REQUEST_TIMEOUT -Windows.Win32.Foundation.ERROR_SERVICE_SPECIFIC_ERROR -Windows.Win32.Foundation.ERROR_SERVICE_START_HANG -Windows.Win32.Foundation.ERROR_SESSION_CREDENTIAL_CONFLICT -Windows.Win32.Foundation.ERROR_SESSION_KEY_TOO_SHORT -Windows.Win32.Foundation.ERROR_SET_CONTEXT_DENIED -Windows.Win32.Foundation.ERROR_SET_NOT_FOUND -Windows.Win32.Foundation.ERROR_SET_POWER_STATE_FAILED -Windows.Win32.Foundation.ERROR_SET_POWER_STATE_VETOED -Windows.Win32.Foundation.ERROR_SETCOUNT_ON_BAD_LB -Windows.Win32.Foundation.ERROR_SETMARK_DETECTED -Windows.Win32.Foundation.ERROR_SHARED_POLICY -Windows.Win32.Foundation.ERROR_SHARING_BUFFER_EXCEEDED -Windows.Win32.Foundation.ERROR_SHARING_PAUSED -Windows.Win32.Foundation.ERROR_SHARING_VIOLATION -Windows.Win32.Foundation.ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME -Windows.Win32.Foundation.ERROR_SHUTDOWN_DISKS_NOT_IN_MAINTENANCE_MODE -Windows.Win32.Foundation.ERROR_SHUTDOWN_IN_PROGRESS -Windows.Win32.Foundation.ERROR_SHUTDOWN_IS_SCHEDULED -Windows.Win32.Foundation.ERROR_SHUTDOWN_USERS_LOGGED_ON -Windows.Win32.Foundation.ERROR_SIGNAL_PENDING -Windows.Win32.Foundation.ERROR_SIGNAL_REFUSED -Windows.Win32.Foundation.ERROR_SINGLE_INSTANCE_APP -Windows.Win32.Foundation.ERROR_SMARTCARD_SUBSYSTEM_FAILURE -Windows.Win32.Foundation.ERROR_SMB1_NOT_AVAILABLE -Windows.Win32.Foundation.ERROR_SMB_GUEST_LOGON_BLOCKED -Windows.Win32.Foundation.ERROR_SMR_GARBAGE_COLLECTION_REQUIRED -Windows.Win32.Foundation.ERROR_SOME_NOT_MAPPED -Windows.Win32.Foundation.ERROR_SOURCE_ELEMENT_EMPTY -Windows.Win32.Foundation.ERROR_SPARSE_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_SPECIAL_ACCOUNT -Windows.Win32.Foundation.ERROR_SPECIAL_GROUP -Windows.Win32.Foundation.ERROR_SPECIAL_USER -Windows.Win32.Foundation.ERROR_SRC_SRV_DLL_LOAD_FAILED -Windows.Win32.Foundation.ERROR_STACK_BUFFER_OVERRUN -Windows.Win32.Foundation.ERROR_STACK_OVERFLOW -Windows.Win32.Foundation.ERROR_STACK_OVERFLOW_READ -Windows.Win32.Foundation.ERROR_STOPPED_ON_SYMLINK -Windows.Win32.Foundation.ERROR_STORAGE_LOST_DATA_PERSISTENCE -Windows.Win32.Foundation.ERROR_STORAGE_RESERVE_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_STORAGE_RESERVE_DOES_NOT_EXIST -Windows.Win32.Foundation.ERROR_STORAGE_RESERVE_ID_INVALID -Windows.Win32.Foundation.ERROR_STORAGE_RESERVE_NOT_EMPTY -Windows.Win32.Foundation.ERROR_STORAGE_STACK_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_STORAGE_TOPOLOGY_ID_MISMATCH -Windows.Win32.Foundation.ERROR_STRICT_CFG_VIOLATION -Windows.Win32.Foundation.ERROR_SUBST_TO_JOIN -Windows.Win32.Foundation.ERROR_SUBST_TO_SUBST -Windows.Win32.Foundation.ERROR_SUCCESS -Windows.Win32.Foundation.ERROR_SUCCESS_REBOOT_INITIATED -Windows.Win32.Foundation.ERROR_SWAPERROR -Windows.Win32.Foundation.ERROR_SYMLINK_CLASS_DISABLED -Windows.Win32.Foundation.ERROR_SYMLINK_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED -Windows.Win32.Foundation.ERROR_SYNCHRONIZATION_REQUIRED -Windows.Win32.Foundation.ERROR_SYSTEM_HIVE_TOO_LARGE -Windows.Win32.Foundation.ERROR_SYSTEM_IMAGE_BAD_SIGNATURE -Windows.Win32.Foundation.ERROR_SYSTEM_POWERSTATE_COMPLEX_TRANSITION -Windows.Win32.Foundation.ERROR_SYSTEM_POWERSTATE_TRANSITION -Windows.Win32.Foundation.ERROR_SYSTEM_PROCESS_TERMINATED -Windows.Win32.Foundation.ERROR_SYSTEM_SHUTDOWN -Windows.Win32.Foundation.ERROR_SYSTEM_TRACE -Windows.Win32.Foundation.ERROR_THREAD_1_INACTIVE -Windows.Win32.Foundation.ERROR_THREAD_ALREADY_IN_TASK -Windows.Win32.Foundation.ERROR_THREAD_MODE_ALREADY_BACKGROUND -Windows.Win32.Foundation.ERROR_THREAD_MODE_NOT_BACKGROUND -Windows.Win32.Foundation.ERROR_THREAD_NOT_IN_PROCESS -Windows.Win32.Foundation.ERROR_THREAD_WAS_SUSPENDED -Windows.Win32.Foundation.ERROR_TIME_SENSITIVE_THREAD -Windows.Win32.Foundation.ERROR_TIME_SKEW -Windows.Win32.Foundation.ERROR_TIMEOUT -Windows.Win32.Foundation.ERROR_TIMER_NOT_CANCELED -Windows.Win32.Foundation.ERROR_TIMER_RESOLUTION_NOT_SET -Windows.Win32.Foundation.ERROR_TIMER_RESUME_IGNORED -Windows.Win32.Foundation.ERROR_TLW_WITH_WSCHILD -Windows.Win32.Foundation.ERROR_TOKEN_ALREADY_IN_USE -Windows.Win32.Foundation.ERROR_TOO_MANY_CMDS -Windows.Win32.Foundation.ERROR_TOO_MANY_CONTEXT_IDS -Windows.Win32.Foundation.ERROR_TOO_MANY_DESCRIPTORS -Windows.Win32.Foundation.ERROR_TOO_MANY_LINKS -Windows.Win32.Foundation.ERROR_TOO_MANY_LUIDS_REQUESTED -Windows.Win32.Foundation.ERROR_TOO_MANY_MODULES -Windows.Win32.Foundation.ERROR_TOO_MANY_MUXWAITERS -Windows.Win32.Foundation.ERROR_TOO_MANY_NAMES -Windows.Win32.Foundation.ERROR_TOO_MANY_OPEN_FILES -Windows.Win32.Foundation.ERROR_TOO_MANY_POSTS -Windows.Win32.Foundation.ERROR_TOO_MANY_SECRETS -Windows.Win32.Foundation.ERROR_TOO_MANY_SEM_REQUESTS -Windows.Win32.Foundation.ERROR_TOO_MANY_SEMAPHORES -Windows.Win32.Foundation.ERROR_TOO_MANY_SESS -Windows.Win32.Foundation.ERROR_TOO_MANY_SIDS -Windows.Win32.Foundation.ERROR_TOO_MANY_TCBS -Windows.Win32.Foundation.ERROR_TOO_MANY_THREADS -Windows.Win32.Foundation.ERROR_TRANSLATION_COMPLETE -Windows.Win32.Foundation.ERROR_TRUST_FAILURE -Windows.Win32.Foundation.ERROR_TRUSTED_DOMAIN_FAILURE -Windows.Win32.Foundation.ERROR_TRUSTED_RELATIONSHIP_FAILURE -Windows.Win32.Foundation.ERROR_UNABLE_TO_LOCK_MEDIA -Windows.Win32.Foundation.ERROR_UNABLE_TO_MOVE_REPLACEMENT -Windows.Win32.Foundation.ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 -Windows.Win32.Foundation.ERROR_UNABLE_TO_REMOVE_REPLACED -Windows.Win32.Foundation.ERROR_UNABLE_TO_UNLOAD_MEDIA -Windows.Win32.Foundation.ERROR_UNDEFINED_CHARACTER -Windows.Win32.Foundation.ERROR_UNDEFINED_SCOPE -Windows.Win32.Foundation.ERROR_UNEXP_NET_ERR -Windows.Win32.Foundation.ERROR_UNEXPECTED_MM_CREATE_ERR -Windows.Win32.Foundation.ERROR_UNEXPECTED_MM_EXTEND_ERR -Windows.Win32.Foundation.ERROR_UNEXPECTED_MM_MAP_ERROR -Windows.Win32.Foundation.ERROR_UNEXPECTED_NTCACHEMANAGER_ERROR -Windows.Win32.Foundation.ERROR_UNHANDLED_EXCEPTION -Windows.Win32.Foundation.ERROR_UNIDENTIFIED_ERROR -Windows.Win32.Foundation.ERROR_UNKNOWN_COMPONENT -Windows.Win32.Foundation.ERROR_UNKNOWN_FEATURE -Windows.Win32.Foundation.ERROR_UNKNOWN_PATCH -Windows.Win32.Foundation.ERROR_UNKNOWN_PORT -Windows.Win32.Foundation.ERROR_UNKNOWN_PRINTER_DRIVER -Windows.Win32.Foundation.ERROR_UNKNOWN_PRINTPROCESSOR -Windows.Win32.Foundation.ERROR_UNKNOWN_PRODUCT -Windows.Win32.Foundation.ERROR_UNKNOWN_PROPERTY -Windows.Win32.Foundation.ERROR_UNKNOWN_REVISION -Windows.Win32.Foundation.ERROR_UNRECOGNIZED_MEDIA -Windows.Win32.Foundation.ERROR_UNRECOGNIZED_VOLUME -Windows.Win32.Foundation.ERROR_UNSATISFIED_DEPENDENCIES -Windows.Win32.Foundation.ERROR_UNSUPPORTED_COMPRESSION -Windows.Win32.Foundation.ERROR_UNSUPPORTED_TYPE -Windows.Win32.Foundation.ERROR_UNTRUSTED_MOUNT_POINT -Windows.Win32.Foundation.ERROR_UNWIND -Windows.Win32.Foundation.ERROR_UNWIND_CONSOLIDATE -Windows.Win32.Foundation.ERROR_USER_APC -Windows.Win32.Foundation.ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_USER_EXISTS -Windows.Win32.Foundation.ERROR_USER_MAPPED_FILE -Windows.Win32.Foundation.ERROR_USER_PROFILE_LOAD -Windows.Win32.Foundation.ERROR_VALIDATE_CONTINUE -Windows.Win32.Foundation.ERROR_VC_DISCONNECTED -Windows.Win32.Foundation.ERROR_VDM_DISALLOWED -Windows.Win32.Foundation.ERROR_VDM_HARD_ERROR -Windows.Win32.Foundation.ERROR_VERIFIER_STOP -Windows.Win32.Foundation.ERROR_VERSION_PARSE_ERROR -Windows.Win32.Foundation.ERROR_VIRUS_DELETED -Windows.Win32.Foundation.ERROR_VIRUS_INFECTED -Windows.Win32.Foundation.ERROR_VOLSNAP_HIBERNATE_READY -Windows.Win32.Foundation.ERROR_VOLSNAP_PREPARE_HIBERNATE -Windows.Win32.Foundation.ERROR_VOLUME_MOUNTED -Windows.Win32.Foundation.ERROR_VOLUME_NOT_CLUSTER_ALIGNED -Windows.Win32.Foundation.ERROR_VOLUME_NOT_SIS_ENABLED -Windows.Win32.Foundation.ERROR_VOLUME_NOT_SUPPORT_EFS -Windows.Win32.Foundation.ERROR_VOLUME_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_VOLUME_WRITE_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_WAIT_1 -Windows.Win32.Foundation.ERROR_WAIT_2 -Windows.Win32.Foundation.ERROR_WAIT_3 -Windows.Win32.Foundation.ERROR_WAIT_63 -Windows.Win32.Foundation.ERROR_WAIT_FOR_OPLOCK -Windows.Win32.Foundation.ERROR_WAIT_NO_CHILDREN -Windows.Win32.Foundation.ERROR_WAKE_SYSTEM -Windows.Win32.Foundation.ERROR_WAKE_SYSTEM_DEBUGGER -Windows.Win32.Foundation.ERROR_WAS_LOCKED -Windows.Win32.Foundation.ERROR_WAS_UNLOCKED -Windows.Win32.Foundation.ERROR_WEAK_WHFBKEY_BLOCKED -Windows.Win32.Foundation.ERROR_WINDOW_NOT_COMBOBOX -Windows.Win32.Foundation.ERROR_WINDOW_NOT_DIALOG -Windows.Win32.Foundation.ERROR_WINDOW_OF_OTHER_THREAD -Windows.Win32.Foundation.ERROR_WIP_ENCRYPTION_FAILED -Windows.Win32.Foundation.ERROR_WOF_FILE_RESOURCE_TABLE_CORRUPT -Windows.Win32.Foundation.ERROR_WOF_WIM_HEADER_CORRUPT -Windows.Win32.Foundation.ERROR_WOF_WIM_RESOURCE_TABLE_CORRUPT -Windows.Win32.Foundation.ERROR_WORKING_SET_QUOTA -Windows.Win32.Foundation.ERROR_WOW_ASSERTION -Windows.Win32.Foundation.ERROR_WRITE_FAULT -Windows.Win32.Foundation.ERROR_WRITE_PROTECT -Windows.Win32.Foundation.ERROR_WRONG_COMPARTMENT -Windows.Win32.Foundation.ERROR_WRONG_DISK -Windows.Win32.Foundation.ERROR_WRONG_EFS -Windows.Win32.Foundation.ERROR_WRONG_PASSWORD -Windows.Win32.Foundation.ERROR_WRONG_TARGET_NAME -Windows.Win32.Foundation.ERROR_WX86_ERROR -Windows.Win32.Foundation.ERROR_WX86_WARNING -Windows.Win32.Foundation.ERROR_XML_PARSE_ERROR -Windows.Win32.Foundation.ERROR_XMLDSIG_ERROR -Windows.Win32.Foundation.EXCEPTION_STACK_OVERFLOW -Windows.Win32.Foundation.FALSE -Windows.Win32.Foundation.FARPROC -Windows.Win32.Foundation.FILETIME -Windows.Win32.Foundation.FRS_ERR_SYSVOL_POPULATE_TIMEOUT -Windows.Win32.Foundation.GENERIC_ACCESS_RIGHTS -Windows.Win32.Foundation.GENERIC_ALL -Windows.Win32.Foundation.GENERIC_EXECUTE -Windows.Win32.Foundation.GENERIC_READ -Windows.Win32.Foundation.GENERIC_WRITE -Windows.Win32.Foundation.GetLastError -Windows.Win32.Foundation.HANDLE -Windows.Win32.Foundation.HANDLE_FLAG_INHERIT -Windows.Win32.Foundation.HANDLE_FLAG_PROTECT_FROM_CLOSE -Windows.Win32.Foundation.HANDLE_FLAGS -Windows.Win32.Foundation.HMODULE -Windows.Win32.Foundation.MAX_PATH -Windows.Win32.Foundation.NO_ERROR -Windows.Win32.Foundation.NTSTATUS -Windows.Win32.Foundation.RtlNtStatusToDosError -Windows.Win32.Foundation.SetHandleInformation -Windows.Win32.Foundation.SetLastError -Windows.Win32.Foundation.STATUS_DELETE_PENDING -Windows.Win32.Foundation.STATUS_END_OF_FILE -Windows.Win32.Foundation.STATUS_INVALID_PARAMETER -Windows.Win32.Foundation.STATUS_NOT_IMPLEMENTED -Windows.Win32.Foundation.STATUS_PENDING -Windows.Win32.Foundation.STATUS_SUCCESS -Windows.Win32.Foundation.TRUE -Windows.Win32.Foundation.UNICODE_STRING -Windows.Win32.Foundation.WAIT_ABANDONED -Windows.Win32.Foundation.WAIT_ABANDONED_0 -Windows.Win32.Foundation.WAIT_FAILED -Windows.Win32.Foundation.WAIT_IO_COMPLETION -Windows.Win32.Foundation.WAIT_OBJECT_0 -Windows.Win32.Foundation.WAIT_TIMEOUT -Windows.Win32.Foundation.WIN32_ERROR -Windows.Win32.Globalization.COMPARESTRING_RESULT -Windows.Win32.Globalization.CompareStringOrdinal -Windows.Win32.Globalization.CP_UTF8 -Windows.Win32.Globalization.CSTR_EQUAL -Windows.Win32.Globalization.CSTR_GREATER_THAN -Windows.Win32.Globalization.CSTR_LESS_THAN -Windows.Win32.Globalization.MB_COMPOSITE -Windows.Win32.Globalization.MB_ERR_INVALID_CHARS -Windows.Win32.Globalization.MB_PRECOMPOSED -Windows.Win32.Globalization.MB_USEGLYPHCHARS -Windows.Win32.Globalization.MULTI_BYTE_TO_WIDE_CHAR_FLAGS -Windows.Win32.Globalization.MultiByteToWideChar -Windows.Win32.Globalization.WC_ERR_INVALID_CHARS -Windows.Win32.Globalization.WideCharToMultiByte -Windows.Win32.Networking.WinSock.accept -Windows.Win32.Networking.WinSock.ADDRESS_FAMILY -Windows.Win32.Networking.WinSock.ADDRINFOA -Windows.Win32.Networking.WinSock.AF_INET -Windows.Win32.Networking.WinSock.AF_INET6 -Windows.Win32.Networking.WinSock.AF_UNIX -Windows.Win32.Networking.WinSock.AF_UNSPEC -Windows.Win32.Networking.WinSock.bind -Windows.Win32.Networking.WinSock.closesocket -Windows.Win32.Networking.WinSock.connect -Windows.Win32.Networking.WinSock.FD_SET -Windows.Win32.Networking.WinSock.FIONBIO -Windows.Win32.Networking.WinSock.freeaddrinfo -Windows.Win32.Networking.WinSock.getaddrinfo -Windows.Win32.Networking.WinSock.getpeername -Windows.Win32.Networking.WinSock.getsockname -Windows.Win32.Networking.WinSock.getsockopt -Windows.Win32.Networking.WinSock.IN6_ADDR -Windows.Win32.Networking.WinSock.IN_ADDR -Windows.Win32.Networking.WinSock.INVALID_SOCKET -Windows.Win32.Networking.WinSock.ioctlsocket -Windows.Win32.Networking.WinSock.IP_ADD_MEMBERSHIP -Windows.Win32.Networking.WinSock.IP_DROP_MEMBERSHIP -Windows.Win32.Networking.WinSock.IP_MREQ -Windows.Win32.Networking.WinSock.IP_MULTICAST_LOOP -Windows.Win32.Networking.WinSock.IP_MULTICAST_TTL -Windows.Win32.Networking.WinSock.IP_TTL -Windows.Win32.Networking.WinSock.IPPROTO -Windows.Win32.Networking.WinSock.IPPROTO_AH -Windows.Win32.Networking.WinSock.IPPROTO_CBT -Windows.Win32.Networking.WinSock.IPPROTO_DSTOPTS -Windows.Win32.Networking.WinSock.IPPROTO_EGP -Windows.Win32.Networking.WinSock.IPPROTO_ESP -Windows.Win32.Networking.WinSock.IPPROTO_FRAGMENT -Windows.Win32.Networking.WinSock.IPPROTO_GGP -Windows.Win32.Networking.WinSock.IPPROTO_HOPOPTS -Windows.Win32.Networking.WinSock.IPPROTO_ICLFXBM -Windows.Win32.Networking.WinSock.IPPROTO_ICMP -Windows.Win32.Networking.WinSock.IPPROTO_ICMPV6 -Windows.Win32.Networking.WinSock.IPPROTO_IDP -Windows.Win32.Networking.WinSock.IPPROTO_IGMP -Windows.Win32.Networking.WinSock.IPPROTO_IGP -Windows.Win32.Networking.WinSock.IPPROTO_IP -Windows.Win32.Networking.WinSock.IPPROTO_IPV4 -Windows.Win32.Networking.WinSock.IPPROTO_IPV6 -Windows.Win32.Networking.WinSock.IPPROTO_L2TP -Windows.Win32.Networking.WinSock.IPPROTO_MAX -Windows.Win32.Networking.WinSock.IPPROTO_ND -Windows.Win32.Networking.WinSock.IPPROTO_NONE -Windows.Win32.Networking.WinSock.IPPROTO_PGM -Windows.Win32.Networking.WinSock.IPPROTO_PIM -Windows.Win32.Networking.WinSock.IPPROTO_PUP -Windows.Win32.Networking.WinSock.IPPROTO_RAW -Windows.Win32.Networking.WinSock.IPPROTO_RDP -Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_IPSEC -Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_IPSECOFFLOAD -Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_MAX -Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_RAW -Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_WNV -Windows.Win32.Networking.WinSock.IPPROTO_RM -Windows.Win32.Networking.WinSock.IPPROTO_ROUTING -Windows.Win32.Networking.WinSock.IPPROTO_SCTP -Windows.Win32.Networking.WinSock.IPPROTO_ST -Windows.Win32.Networking.WinSock.IPPROTO_TCP -Windows.Win32.Networking.WinSock.IPPROTO_UDP -Windows.Win32.Networking.WinSock.IPV6_ADD_MEMBERSHIP -Windows.Win32.Networking.WinSock.IPV6_DROP_MEMBERSHIP -Windows.Win32.Networking.WinSock.IPV6_MREQ -Windows.Win32.Networking.WinSock.IPV6_MULTICAST_LOOP -Windows.Win32.Networking.WinSock.IPV6_V6ONLY -Windows.Win32.Networking.WinSock.LINGER -Windows.Win32.Networking.WinSock.listen -Windows.Win32.Networking.WinSock.LPWSAOVERLAPPED_COMPLETION_ROUTINE -Windows.Win32.Networking.WinSock.MSG_DONTROUTE -Windows.Win32.Networking.WinSock.MSG_OOB -Windows.Win32.Networking.WinSock.MSG_PEEK -Windows.Win32.Networking.WinSock.MSG_PUSH_IMMEDIATE -Windows.Win32.Networking.WinSock.MSG_WAITALL -Windows.Win32.Networking.WinSock.recv -Windows.Win32.Networking.WinSock.recvfrom -Windows.Win32.Networking.WinSock.SD_BOTH -Windows.Win32.Networking.WinSock.SD_RECEIVE -Windows.Win32.Networking.WinSock.SD_SEND -Windows.Win32.Networking.WinSock.select -Windows.Win32.Networking.WinSock.send -Windows.Win32.Networking.WinSock.SEND_RECV_FLAGS -Windows.Win32.Networking.WinSock.sendto -Windows.Win32.Networking.WinSock.setsockopt -Windows.Win32.Networking.WinSock.shutdown -Windows.Win32.Networking.WinSock.SO_BROADCAST -Windows.Win32.Networking.WinSock.SO_ERROR -Windows.Win32.Networking.WinSock.SO_LINGER -Windows.Win32.Networking.WinSock.SO_RCVTIMEO -Windows.Win32.Networking.WinSock.SO_SNDTIMEO -Windows.Win32.Networking.WinSock.SOCK_DGRAM -Windows.Win32.Networking.WinSock.SOCK_RAW -Windows.Win32.Networking.WinSock.SOCK_RDM -Windows.Win32.Networking.WinSock.SOCK_SEQPACKET -Windows.Win32.Networking.WinSock.SOCK_STREAM -Windows.Win32.Networking.WinSock.SOCKADDR -Windows.Win32.Networking.WinSock.SOCKADDR_UN -Windows.Win32.Networking.WinSock.SOCKET -Windows.Win32.Networking.WinSock.SOCKET_ERROR -Windows.Win32.Networking.WinSock.SOL_SOCKET -Windows.Win32.Networking.WinSock.TCP_NODELAY -Windows.Win32.Networking.WinSock.TIMEVAL -Windows.Win32.Networking.WinSock.WINSOCK_SHUTDOWN_HOW -Windows.Win32.Networking.WinSock.WINSOCK_SOCKET_TYPE -Windows.Win32.Networking.WinSock.WSA_E_CANCELLED -Windows.Win32.Networking.WinSock.WSA_E_NO_MORE -Windows.Win32.Networking.WinSock.WSA_ERROR -Windows.Win32.Networking.WinSock.WSA_FLAG_NO_HANDLE_INHERIT -Windows.Win32.Networking.WinSock.WSA_FLAG_OVERLAPPED -Windows.Win32.Networking.WinSock.WSA_INVALID_HANDLE -Windows.Win32.Networking.WinSock.WSA_INVALID_PARAMETER -Windows.Win32.Networking.WinSock.WSA_IO_INCOMPLETE -Windows.Win32.Networking.WinSock.WSA_IO_PENDING -Windows.Win32.Networking.WinSock.WSA_IPSEC_NAME_POLICY_ERROR -Windows.Win32.Networking.WinSock.WSA_NOT_ENOUGH_MEMORY -Windows.Win32.Networking.WinSock.WSA_OPERATION_ABORTED -Windows.Win32.Networking.WinSock.WSA_QOS_ADMISSION_FAILURE -Windows.Win32.Networking.WinSock.WSA_QOS_BAD_OBJECT -Windows.Win32.Networking.WinSock.WSA_QOS_BAD_STYLE -Windows.Win32.Networking.WinSock.WSA_QOS_EFILTERCOUNT -Windows.Win32.Networking.WinSock.WSA_QOS_EFILTERSTYLE -Windows.Win32.Networking.WinSock.WSA_QOS_EFILTERTYPE -Windows.Win32.Networking.WinSock.WSA_QOS_EFLOWCOUNT -Windows.Win32.Networking.WinSock.WSA_QOS_EFLOWDESC -Windows.Win32.Networking.WinSock.WSA_QOS_EFLOWSPEC -Windows.Win32.Networking.WinSock.WSA_QOS_EOBJLENGTH -Windows.Win32.Networking.WinSock.WSA_QOS_EPOLICYOBJ -Windows.Win32.Networking.WinSock.WSA_QOS_EPROVSPECBUF -Windows.Win32.Networking.WinSock.WSA_QOS_EPSFILTERSPEC -Windows.Win32.Networking.WinSock.WSA_QOS_EPSFLOWSPEC -Windows.Win32.Networking.WinSock.WSA_QOS_ESDMODEOBJ -Windows.Win32.Networking.WinSock.WSA_QOS_ESERVICETYPE -Windows.Win32.Networking.WinSock.WSA_QOS_ESHAPERATEOBJ -Windows.Win32.Networking.WinSock.WSA_QOS_EUNKOWNPSOBJ -Windows.Win32.Networking.WinSock.WSA_QOS_GENERIC_ERROR -Windows.Win32.Networking.WinSock.WSA_QOS_NO_RECEIVERS -Windows.Win32.Networking.WinSock.WSA_QOS_NO_SENDERS -Windows.Win32.Networking.WinSock.WSA_QOS_POLICY_FAILURE -Windows.Win32.Networking.WinSock.WSA_QOS_RECEIVERS -Windows.Win32.Networking.WinSock.WSA_QOS_REQUEST_CONFIRMED -Windows.Win32.Networking.WinSock.WSA_QOS_RESERVED_PETYPE -Windows.Win32.Networking.WinSock.WSA_QOS_SENDERS -Windows.Win32.Networking.WinSock.WSA_QOS_TRAFFIC_CTRL_ERROR -Windows.Win32.Networking.WinSock.WSA_SECURE_HOST_NOT_FOUND -Windows.Win32.Networking.WinSock.WSA_WAIT_EVENT_0 -Windows.Win32.Networking.WinSock.WSA_WAIT_IO_COMPLETION -Windows.Win32.Networking.WinSock.WSABASEERR -Windows.Win32.Networking.WinSock.WSABUF -Windows.Win32.Networking.WinSock.WSACleanup -Windows.Win32.Networking.WinSock.WSADATA -Windows.Win32.Networking.WinSock.WSADuplicateSocketW -Windows.Win32.Networking.WinSock.WSAEACCES -Windows.Win32.Networking.WinSock.WSAEADDRINUSE -Windows.Win32.Networking.WinSock.WSAEADDRNOTAVAIL -Windows.Win32.Networking.WinSock.WSAEAFNOSUPPORT -Windows.Win32.Networking.WinSock.WSAEALREADY -Windows.Win32.Networking.WinSock.WSAEBADF -Windows.Win32.Networking.WinSock.WSAECANCELLED -Windows.Win32.Networking.WinSock.WSAECONNABORTED -Windows.Win32.Networking.WinSock.WSAECONNREFUSED -Windows.Win32.Networking.WinSock.WSAECONNRESET -Windows.Win32.Networking.WinSock.WSAEDESTADDRREQ -Windows.Win32.Networking.WinSock.WSAEDISCON -Windows.Win32.Networking.WinSock.WSAEDQUOT -Windows.Win32.Networking.WinSock.WSAEFAULT -Windows.Win32.Networking.WinSock.WSAEHOSTDOWN -Windows.Win32.Networking.WinSock.WSAEHOSTUNREACH -Windows.Win32.Networking.WinSock.WSAEINPROGRESS -Windows.Win32.Networking.WinSock.WSAEINTR -Windows.Win32.Networking.WinSock.WSAEINVAL -Windows.Win32.Networking.WinSock.WSAEINVALIDPROCTABLE -Windows.Win32.Networking.WinSock.WSAEINVALIDPROVIDER -Windows.Win32.Networking.WinSock.WSAEISCONN -Windows.Win32.Networking.WinSock.WSAELOOP -Windows.Win32.Networking.WinSock.WSAEMFILE -Windows.Win32.Networking.WinSock.WSAEMSGSIZE -Windows.Win32.Networking.WinSock.WSAENAMETOOLONG -Windows.Win32.Networking.WinSock.WSAENETDOWN -Windows.Win32.Networking.WinSock.WSAENETRESET -Windows.Win32.Networking.WinSock.WSAENETUNREACH -Windows.Win32.Networking.WinSock.WSAENOBUFS -Windows.Win32.Networking.WinSock.WSAENOMORE -Windows.Win32.Networking.WinSock.WSAENOPROTOOPT -Windows.Win32.Networking.WinSock.WSAENOTCONN -Windows.Win32.Networking.WinSock.WSAENOTEMPTY -Windows.Win32.Networking.WinSock.WSAENOTSOCK -Windows.Win32.Networking.WinSock.WSAEOPNOTSUPP -Windows.Win32.Networking.WinSock.WSAEPFNOSUPPORT -Windows.Win32.Networking.WinSock.WSAEPROCLIM -Windows.Win32.Networking.WinSock.WSAEPROTONOSUPPORT -Windows.Win32.Networking.WinSock.WSAEPROTOTYPE -Windows.Win32.Networking.WinSock.WSAEPROVIDERFAILEDINIT -Windows.Win32.Networking.WinSock.WSAEREFUSED -Windows.Win32.Networking.WinSock.WSAEREMOTE -Windows.Win32.Networking.WinSock.WSAESHUTDOWN -Windows.Win32.Networking.WinSock.WSAESOCKTNOSUPPORT -Windows.Win32.Networking.WinSock.WSAESTALE -Windows.Win32.Networking.WinSock.WSAETIMEDOUT -Windows.Win32.Networking.WinSock.WSAETOOMANYREFS -Windows.Win32.Networking.WinSock.WSAEUSERS -Windows.Win32.Networking.WinSock.WSAEWOULDBLOCK -Windows.Win32.Networking.WinSock.WSAGetLastError -Windows.Win32.Networking.WinSock.WSAHOST_NOT_FOUND -Windows.Win32.Networking.WinSock.WSANO_DATA -Windows.Win32.Networking.WinSock.WSANO_RECOVERY -Windows.Win32.Networking.WinSock.WSANOTINITIALISED -Windows.Win32.Networking.WinSock.WSAPROTOCOL_INFOW -Windows.Win32.Networking.WinSock.WSAPROTOCOLCHAIN -Windows.Win32.Networking.WinSock.WSARecv -Windows.Win32.Networking.WinSock.WSASend -Windows.Win32.Networking.WinSock.WSASERVICE_NOT_FOUND -Windows.Win32.Networking.WinSock.WSASocketW -Windows.Win32.Networking.WinSock.WSASYSCALLFAILURE -Windows.Win32.Networking.WinSock.WSASYSNOTREADY -Windows.Win32.Networking.WinSock.WSATRY_AGAIN -Windows.Win32.Networking.WinSock.WSATYPE_NOT_FOUND -Windows.Win32.Networking.WinSock.WSAVERNOTSUPPORTED -Windows.Win32.Security.Authentication.Identity.RtlGenRandom -Windows.Win32.Security.Cryptography.BCRYPT_ALG_HANDLE -Windows.Win32.Security.Cryptography.BCRYPT_USE_SYSTEM_PREFERRED_RNG -Windows.Win32.Security.Cryptography.BCryptGenRandom -Windows.Win32.Security.Cryptography.BCRYPTGENRANDOM_FLAGS -Windows.Win32.Security.SECURITY_ATTRIBUTES -Windows.Win32.Security.TOKEN_ACCESS_MASK -Windows.Win32.Security.TOKEN_ACCESS_PSEUDO_HANDLE -Windows.Win32.Security.TOKEN_ACCESS_PSEUDO_HANDLE_WIN8 -Windows.Win32.Security.TOKEN_ACCESS_SYSTEM_SECURITY -Windows.Win32.Security.TOKEN_ADJUST_DEFAULT -Windows.Win32.Security.TOKEN_ADJUST_GROUPS -Windows.Win32.Security.TOKEN_ADJUST_PRIVILEGES -Windows.Win32.Security.TOKEN_ADJUST_SESSIONID -Windows.Win32.Security.TOKEN_ALL_ACCESS -Windows.Win32.Security.TOKEN_ASSIGN_PRIMARY -Windows.Win32.Security.TOKEN_DELETE -Windows.Win32.Security.TOKEN_DUPLICATE -Windows.Win32.Security.TOKEN_EXECUTE -Windows.Win32.Security.TOKEN_IMPERSONATE -Windows.Win32.Security.TOKEN_QUERY -Windows.Win32.Security.TOKEN_QUERY_SOURCE -Windows.Win32.Security.TOKEN_READ -Windows.Win32.Security.TOKEN_READ_CONTROL -Windows.Win32.Security.TOKEN_TRUST_CONSTRAINT_MASK -Windows.Win32.Security.TOKEN_WRITE -Windows.Win32.Security.TOKEN_WRITE_DAC -Windows.Win32.Security.TOKEN_WRITE_OWNER -Windows.Win32.Storage.FileSystem.BY_HANDLE_FILE_INFORMATION -Windows.Win32.Storage.FileSystem.CALLBACK_CHUNK_FINISHED -Windows.Win32.Storage.FileSystem.CALLBACK_STREAM_SWITCH -Windows.Win32.Storage.FileSystem.CopyFileExW -Windows.Win32.Storage.FileSystem.CREATE_ALWAYS -Windows.Win32.Storage.FileSystem.CREATE_NEW -Windows.Win32.Storage.FileSystem.CreateDirectoryW -Windows.Win32.Storage.FileSystem.CreateFileW -Windows.Win32.Storage.FileSystem.CreateHardLinkW -Windows.Win32.Storage.FileSystem.CreateSymbolicLinkW -Windows.Win32.Storage.FileSystem.DELETE -Windows.Win32.Storage.FileSystem.DeleteFileW -Windows.Win32.Storage.FileSystem.FILE_ACCESS_RIGHTS -Windows.Win32.Storage.FileSystem.FILE_ADD_FILE -Windows.Win32.Storage.FileSystem.FILE_ADD_SUBDIRECTORY -Windows.Win32.Storage.FileSystem.FILE_ALL_ACCESS -Windows.Win32.Storage.FileSystem.FILE_ALLOCATION_INFO -Windows.Win32.Storage.FileSystem.FILE_APPEND_DATA -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_ARCHIVE -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_COMPRESSED -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_DEVICE -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_DIRECTORY -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_EA -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_ENCRYPTED -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_HIDDEN -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_INTEGRITY_STREAM -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_NO_SCRUB_DATA -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_NORMAL -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_NOT_CONTENT_INDEXED -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_OFFLINE -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_PINNED -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_READONLY -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_RECALL_ON_OPEN -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_REPARSE_POINT -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_SPARSE_FILE -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_SYSTEM -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_TAG_INFO -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_TEMPORARY -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_UNPINNED -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_VIRTUAL -Windows.Win32.Storage.FileSystem.FILE_BASIC_INFO -Windows.Win32.Storage.FileSystem.FILE_BEGIN -Windows.Win32.Storage.FileSystem.FILE_CREATE_PIPE_INSTANCE -Windows.Win32.Storage.FileSystem.FILE_CREATION_DISPOSITION -Windows.Win32.Storage.FileSystem.FILE_CURRENT -Windows.Win32.Storage.FileSystem.FILE_DELETE_CHILD -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_DELETE -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_DO_NOT_DELETE -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_FORCE_IMAGE_SECTION_CHECK -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_ON_CLOSE -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_POSIX_SEMANTICS -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_INFO -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_INFO_EX -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_INFO_EX_FLAGS -Windows.Win32.Storage.FileSystem.FILE_END -Windows.Win32.Storage.FileSystem.FILE_END_OF_FILE_INFO -Windows.Win32.Storage.FileSystem.FILE_EXECUTE -Windows.Win32.Storage.FileSystem.FILE_FLAG_BACKUP_SEMANTICS -Windows.Win32.Storage.FileSystem.FILE_FLAG_DELETE_ON_CLOSE -Windows.Win32.Storage.FileSystem.FILE_FLAG_FIRST_PIPE_INSTANCE -Windows.Win32.Storage.FileSystem.FILE_FLAG_NO_BUFFERING -Windows.Win32.Storage.FileSystem.FILE_FLAG_OPEN_NO_RECALL -Windows.Win32.Storage.FileSystem.FILE_FLAG_OPEN_REPARSE_POINT -Windows.Win32.Storage.FileSystem.FILE_FLAG_OVERLAPPED -Windows.Win32.Storage.FileSystem.FILE_FLAG_POSIX_SEMANTICS -Windows.Win32.Storage.FileSystem.FILE_FLAG_RANDOM_ACCESS -Windows.Win32.Storage.FileSystem.FILE_FLAG_SEQUENTIAL_SCAN -Windows.Win32.Storage.FileSystem.FILE_FLAG_SESSION_AWARE -Windows.Win32.Storage.FileSystem.FILE_FLAG_WRITE_THROUGH -Windows.Win32.Storage.FileSystem.FILE_FLAGS_AND_ATTRIBUTES -Windows.Win32.Storage.FileSystem.FILE_GENERIC_EXECUTE -Windows.Win32.Storage.FileSystem.FILE_GENERIC_READ -Windows.Win32.Storage.FileSystem.FILE_GENERIC_WRITE -Windows.Win32.Storage.FileSystem.FILE_ID_BOTH_DIR_INFO -Windows.Win32.Storage.FileSystem.FILE_INFO_BY_HANDLE_CLASS -Windows.Win32.Storage.FileSystem.FILE_IO_PRIORITY_HINT_INFO -Windows.Win32.Storage.FileSystem.FILE_LIST_DIRECTORY -Windows.Win32.Storage.FileSystem.FILE_NAME_NORMALIZED -Windows.Win32.Storage.FileSystem.FILE_NAME_OPENED -Windows.Win32.Storage.FileSystem.FILE_READ_ATTRIBUTES -Windows.Win32.Storage.FileSystem.FILE_READ_DATA -Windows.Win32.Storage.FileSystem.FILE_READ_EA -Windows.Win32.Storage.FileSystem.FILE_SHARE_DELETE -Windows.Win32.Storage.FileSystem.FILE_SHARE_MODE -Windows.Win32.Storage.FileSystem.FILE_SHARE_NONE -Windows.Win32.Storage.FileSystem.FILE_SHARE_READ -Windows.Win32.Storage.FileSystem.FILE_SHARE_WRITE -Windows.Win32.Storage.FileSystem.FILE_STANDARD_INFO -Windows.Win32.Storage.FileSystem.FILE_TRAVERSE -Windows.Win32.Storage.FileSystem.FILE_TYPE -Windows.Win32.Storage.FileSystem.FILE_TYPE_CHAR -Windows.Win32.Storage.FileSystem.FILE_TYPE_DISK -Windows.Win32.Storage.FileSystem.FILE_TYPE_PIPE -Windows.Win32.Storage.FileSystem.FILE_TYPE_REMOTE -Windows.Win32.Storage.FileSystem.FILE_TYPE_UNKNOWN -Windows.Win32.Storage.FileSystem.FILE_WRITE_ATTRIBUTES -Windows.Win32.Storage.FileSystem.FILE_WRITE_DATA -Windows.Win32.Storage.FileSystem.FILE_WRITE_EA -Windows.Win32.Storage.FileSystem.FileAlignmentInfo -Windows.Win32.Storage.FileSystem.FileAllocationInfo -Windows.Win32.Storage.FileSystem.FileAttributeTagInfo -Windows.Win32.Storage.FileSystem.FileBasicInfo -Windows.Win32.Storage.FileSystem.FileCaseSensitiveInfo -Windows.Win32.Storage.FileSystem.FileCompressionInfo -Windows.Win32.Storage.FileSystem.FileDispositionInfo -Windows.Win32.Storage.FileSystem.FileDispositionInfoEx -Windows.Win32.Storage.FileSystem.FileEndOfFileInfo -Windows.Win32.Storage.FileSystem.FileFullDirectoryInfo -Windows.Win32.Storage.FileSystem.FileFullDirectoryRestartInfo -Windows.Win32.Storage.FileSystem.FileIdBothDirectoryInfo -Windows.Win32.Storage.FileSystem.FileIdBothDirectoryRestartInfo -Windows.Win32.Storage.FileSystem.FileIdExtdDirectoryInfo -Windows.Win32.Storage.FileSystem.FileIdExtdDirectoryRestartInfo -Windows.Win32.Storage.FileSystem.FileIdInfo -Windows.Win32.Storage.FileSystem.FileIoPriorityHintInfo -Windows.Win32.Storage.FileSystem.FileNameInfo -Windows.Win32.Storage.FileSystem.FileNormalizedNameInfo -Windows.Win32.Storage.FileSystem.FileRemoteProtocolInfo -Windows.Win32.Storage.FileSystem.FileRenameInfo -Windows.Win32.Storage.FileSystem.FileRenameInfoEx -Windows.Win32.Storage.FileSystem.FileStandardInfo -Windows.Win32.Storage.FileSystem.FileStorageInfo -Windows.Win32.Storage.FileSystem.FileStreamInfo -Windows.Win32.Storage.FileSystem.FindClose -Windows.Win32.Storage.FileSystem.FindFirstFileW -Windows.Win32.Storage.FileSystem.FindNextFileW -Windows.Win32.Storage.FileSystem.FlushFileBuffers -Windows.Win32.Storage.FileSystem.GetFileAttributesW -Windows.Win32.Storage.FileSystem.GetFileInformationByHandle -Windows.Win32.Storage.FileSystem.GetFileInformationByHandleEx -Windows.Win32.Storage.FileSystem.GetFileType -Windows.Win32.Storage.FileSystem.GETFINALPATHNAMEBYHANDLE_FLAGS -Windows.Win32.Storage.FileSystem.GetFinalPathNameByHandleW -Windows.Win32.Storage.FileSystem.GetFullPathNameW -Windows.Win32.Storage.FileSystem.GetTempPathW -Windows.Win32.Storage.FileSystem.INVALID_FILE_ATTRIBUTES -Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE -Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE_CALLBACK_REASON -Windows.Win32.Storage.FileSystem.MAXIMUM_REPARSE_DATA_BUFFER_SIZE -Windows.Win32.Storage.FileSystem.MaximumFileInfoByHandleClass -Windows.Win32.Storage.FileSystem.MOVE_FILE_FLAGS -Windows.Win32.Storage.FileSystem.MOVEFILE_COPY_ALLOWED -Windows.Win32.Storage.FileSystem.MOVEFILE_CREATE_HARDLINK -Windows.Win32.Storage.FileSystem.MOVEFILE_DELAY_UNTIL_REBOOT -Windows.Win32.Storage.FileSystem.MOVEFILE_FAIL_IF_NOT_TRACKABLE -Windows.Win32.Storage.FileSystem.MOVEFILE_REPLACE_EXISTING -Windows.Win32.Storage.FileSystem.MOVEFILE_WRITE_THROUGH -Windows.Win32.Storage.FileSystem.MoveFileExW -Windows.Win32.Storage.FileSystem.OPEN_ALWAYS -Windows.Win32.Storage.FileSystem.OPEN_EXISTING -Windows.Win32.Storage.FileSystem.PIPE_ACCESS_DUPLEX -Windows.Win32.Storage.FileSystem.PIPE_ACCESS_INBOUND -Windows.Win32.Storage.FileSystem.PIPE_ACCESS_OUTBOUND -Windows.Win32.Storage.FileSystem.READ_CONTROL -Windows.Win32.Storage.FileSystem.ReadFile -Windows.Win32.Storage.FileSystem.ReadFileEx -Windows.Win32.Storage.FileSystem.RemoveDirectoryW -Windows.Win32.Storage.FileSystem.SECURITY_ANONYMOUS -Windows.Win32.Storage.FileSystem.SECURITY_CONTEXT_TRACKING -Windows.Win32.Storage.FileSystem.SECURITY_DELEGATION -Windows.Win32.Storage.FileSystem.SECURITY_EFFECTIVE_ONLY -Windows.Win32.Storage.FileSystem.SECURITY_IDENTIFICATION -Windows.Win32.Storage.FileSystem.SECURITY_IMPERSONATION -Windows.Win32.Storage.FileSystem.SECURITY_SQOS_PRESENT -Windows.Win32.Storage.FileSystem.SECURITY_VALID_SQOS_FLAGS -Windows.Win32.Storage.FileSystem.SET_FILE_POINTER_MOVE_METHOD -Windows.Win32.Storage.FileSystem.SetFileAttributesW -Windows.Win32.Storage.FileSystem.SetFileInformationByHandle -Windows.Win32.Storage.FileSystem.SetFilePointerEx -Windows.Win32.Storage.FileSystem.SetFileTime -Windows.Win32.Storage.FileSystem.SPECIFIC_RIGHTS_ALL -Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_ALL -Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_EXECUTE -Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_READ -Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_REQUIRED -Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_WRITE -Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE -Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAG_DIRECTORY -Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAGS -Windows.Win32.Storage.FileSystem.SYNCHRONIZE -Windows.Win32.Storage.FileSystem.TRUNCATE_EXISTING -Windows.Win32.Storage.FileSystem.VOLUME_NAME_DOS -Windows.Win32.Storage.FileSystem.VOLUME_NAME_GUID -Windows.Win32.Storage.FileSystem.VOLUME_NAME_NONE -Windows.Win32.Storage.FileSystem.WIN32_FIND_DATAW -Windows.Win32.Storage.FileSystem.WRITE_DAC -Windows.Win32.Storage.FileSystem.WRITE_OWNER -Windows.Win32.Storage.FileSystem.WriteFileEx -Windows.Win32.System.Console.CONSOLE_MODE -Windows.Win32.System.Console.CONSOLE_READCONSOLE_CONTROL -Windows.Win32.System.Console.DISABLE_NEWLINE_AUTO_RETURN -Windows.Win32.System.Console.ENABLE_AUTO_POSITION -Windows.Win32.System.Console.ENABLE_ECHO_INPUT -Windows.Win32.System.Console.ENABLE_EXTENDED_FLAGS -Windows.Win32.System.Console.ENABLE_INSERT_MODE -Windows.Win32.System.Console.ENABLE_LINE_INPUT -Windows.Win32.System.Console.ENABLE_LVB_GRID_WORLDWIDE -Windows.Win32.System.Console.ENABLE_MOUSE_INPUT -Windows.Win32.System.Console.ENABLE_PROCESSED_INPUT -Windows.Win32.System.Console.ENABLE_PROCESSED_OUTPUT -Windows.Win32.System.Console.ENABLE_QUICK_EDIT_MODE -Windows.Win32.System.Console.ENABLE_VIRTUAL_TERMINAL_INPUT -Windows.Win32.System.Console.ENABLE_VIRTUAL_TERMINAL_PROCESSING -Windows.Win32.System.Console.ENABLE_WINDOW_INPUT -Windows.Win32.System.Console.ENABLE_WRAP_AT_EOL_OUTPUT -Windows.Win32.System.Console.GetConsoleMode -Windows.Win32.System.Console.GetStdHandle -Windows.Win32.System.Console.ReadConsoleW -Windows.Win32.System.Console.STD_ERROR_HANDLE -Windows.Win32.System.Console.STD_HANDLE -Windows.Win32.System.Console.STD_INPUT_HANDLE -Windows.Win32.System.Console.STD_OUTPUT_HANDLE -Windows.Win32.System.Console.WriteConsoleW -Windows.Win32.System.Diagnostics.Debug.ARM64_NT_NEON128 -Windows.Win32.System.Diagnostics.Debug.CONTEXT -Windows.Win32.System.Diagnostics.Debug.EXCEPTION_RECORD -Windows.Win32.System.Diagnostics.Debug.FACILITY_CODE -Windows.Win32.System.Diagnostics.Debug.FACILITY_NT_BIT -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_ALLOCATE_BUFFER -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_ARGUMENT_ARRAY -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_HMODULE -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_STRING -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_SYSTEM -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_IGNORE_INSERTS -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_OPTIONS -Windows.Win32.System.Diagnostics.Debug.FormatMessageW -Windows.Win32.System.Diagnostics.Debug.M128A -Windows.Win32.System.Diagnostics.Debug.XSAVE_FORMAT -Windows.Win32.System.Environment.FreeEnvironmentStringsW -Windows.Win32.System.Environment.GetCommandLineW -Windows.Win32.System.Environment.GetCurrentDirectoryW -Windows.Win32.System.Environment.GetEnvironmentStringsW -Windows.Win32.System.Environment.GetEnvironmentVariableW -Windows.Win32.System.Environment.SetCurrentDirectoryW -Windows.Win32.System.Environment.SetEnvironmentVariableW -Windows.Win32.System.IO.CancelIo -Windows.Win32.System.IO.DeviceIoControl -Windows.Win32.System.IO.GetOverlappedResult -Windows.Win32.System.IO.LPOVERLAPPED_COMPLETION_ROUTINE -Windows.Win32.System.IO.OVERLAPPED -Windows.Win32.System.Ioctl.FSCTL_GET_REPARSE_POINT -Windows.Win32.System.Ioctl.FSCTL_SET_REPARSE_POINT -Windows.Win32.System.Kernel.EXCEPTION_DISPOSITION -Windows.Win32.System.Kernel.ExceptionCollidedUnwind -Windows.Win32.System.Kernel.ExceptionContinueExecution -Windows.Win32.System.Kernel.ExceptionContinueSearch -Windows.Win32.System.Kernel.ExceptionNestedException -Windows.Win32.System.Kernel.FLOATING_SAVE_AREA -Windows.Win32.System.Kernel.OBJ_DONT_REPARSE -Windows.Win32.System.LibraryLoader.GetModuleFileNameW -Windows.Win32.System.LibraryLoader.GetModuleHandleA -Windows.Win32.System.LibraryLoader.GetModuleHandleW -Windows.Win32.System.LibraryLoader.GetProcAddress -Windows.Win32.System.Performance.QueryPerformanceCounter -Windows.Win32.System.Performance.QueryPerformanceFrequency -Windows.Win32.System.Pipes.CreateNamedPipeW -Windows.Win32.System.Pipes.NAMED_PIPE_MODE -Windows.Win32.System.Pipes.PIPE_ACCEPT_REMOTE_CLIENTS -Windows.Win32.System.Pipes.PIPE_CLIENT_END -Windows.Win32.System.Pipes.PIPE_NOWAIT -Windows.Win32.System.Pipes.PIPE_READMODE_BYTE -Windows.Win32.System.Pipes.PIPE_READMODE_MESSAGE -Windows.Win32.System.Pipes.PIPE_REJECT_REMOTE_CLIENTS -Windows.Win32.System.Pipes.PIPE_SERVER_END -Windows.Win32.System.Pipes.PIPE_TYPE_BYTE -Windows.Win32.System.Pipes.PIPE_TYPE_MESSAGE -Windows.Win32.System.Pipes.PIPE_WAIT -Windows.Win32.System.SystemInformation.GetSystemDirectoryW -Windows.Win32.System.SystemInformation.GetSystemInfo -Windows.Win32.System.SystemInformation.GetSystemTimeAsFileTime -Windows.Win32.System.SystemInformation.GetWindowsDirectoryW -Windows.Win32.System.SystemInformation.PROCESSOR_ARCHITECTURE -Windows.Win32.System.SystemInformation.SYSTEM_INFO -Windows.Win32.System.SystemServices.DLL_PROCESS_DETACH -Windows.Win32.System.SystemServices.DLL_THREAD_DETACH -Windows.Win32.System.SystemServices.EXCEPTION_MAXIMUM_PARAMETERS -Windows.Win32.System.SystemServices.IO_REPARSE_TAG_MOUNT_POINT -Windows.Win32.System.SystemServices.IO_REPARSE_TAG_SYMLINK -Windows.Win32.System.Threading.ABOVE_NORMAL_PRIORITY_CLASS -Windows.Win32.System.Threading.AcquireSRWLockExclusive -Windows.Win32.System.Threading.AcquireSRWLockShared -Windows.Win32.System.Threading.ALL_PROCESSOR_GROUPS -Windows.Win32.System.Threading.BELOW_NORMAL_PRIORITY_CLASS -Windows.Win32.System.Threading.CREATE_BREAKAWAY_FROM_JOB -Windows.Win32.System.Threading.CREATE_DEFAULT_ERROR_MODE -Windows.Win32.System.Threading.CREATE_FORCEDOS -Windows.Win32.System.Threading.CREATE_IGNORE_SYSTEM_DEFAULT -Windows.Win32.System.Threading.CREATE_NEW_CONSOLE -Windows.Win32.System.Threading.CREATE_NEW_PROCESS_GROUP -Windows.Win32.System.Threading.CREATE_NO_WINDOW -Windows.Win32.System.Threading.CREATE_PRESERVE_CODE_AUTHZ_LEVEL -Windows.Win32.System.Threading.CREATE_PROTECTED_PROCESS -Windows.Win32.System.Threading.CREATE_SECURE_PROCESS -Windows.Win32.System.Threading.CREATE_SEPARATE_WOW_VDM -Windows.Win32.System.Threading.CREATE_SHARED_WOW_VDM -Windows.Win32.System.Threading.CREATE_SUSPENDED -Windows.Win32.System.Threading.CREATE_UNICODE_ENVIRONMENT -Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_HIGH_RESOLUTION -Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_MANUAL_RESET -Windows.Win32.System.Threading.CreateEventW -Windows.Win32.System.Threading.CreateProcessW -Windows.Win32.System.Threading.CreateThread -Windows.Win32.System.Threading.CreateWaitableTimerExW -Windows.Win32.System.Threading.DEBUG_ONLY_THIS_PROCESS -Windows.Win32.System.Threading.DEBUG_PROCESS -Windows.Win32.System.Threading.DeleteProcThreadAttributeList -Windows.Win32.System.Threading.DETACHED_PROCESS -Windows.Win32.System.Threading.ExitProcess -Windows.Win32.System.Threading.EXTENDED_STARTUPINFO_PRESENT -Windows.Win32.System.Threading.GetActiveProcessorCount -Windows.Win32.System.Threading.GetCurrentProcess -Windows.Win32.System.Threading.GetCurrentProcessId -Windows.Win32.System.Threading.GetCurrentThread -Windows.Win32.System.Threading.GetExitCodeProcess -Windows.Win32.System.Threading.GetProcessId -Windows.Win32.System.Threading.HIGH_PRIORITY_CLASS -Windows.Win32.System.Threading.IDLE_PRIORITY_CLASS -Windows.Win32.System.Threading.INFINITE -Windows.Win32.System.Threading.INHERIT_CALLER_PRIORITY -Windows.Win32.System.Threading.INHERIT_PARENT_AFFINITY -Windows.Win32.System.Threading.INIT_ONCE_INIT_FAILED -Windows.Win32.System.Threading.InitializeProcThreadAttributeList -Windows.Win32.System.Threading.InitOnceBeginInitialize -Windows.Win32.System.Threading.InitOnceComplete -Windows.Win32.System.Threading.LPPROC_THREAD_ATTRIBUTE_LIST -Windows.Win32.System.Threading.LPTHREAD_START_ROUTINE -Windows.Win32.System.Threading.NORMAL_PRIORITY_CLASS -Windows.Win32.System.Threading.OpenProcessToken -Windows.Win32.System.Threading.PROCESS_CREATION_FLAGS -Windows.Win32.System.Threading.PROCESS_INFORMATION -Windows.Win32.System.Threading.PROCESS_MODE_BACKGROUND_BEGIN -Windows.Win32.System.Threading.PROCESS_MODE_BACKGROUND_END -Windows.Win32.System.Threading.PROFILE_KERNEL -Windows.Win32.System.Threading.PROFILE_SERVER -Windows.Win32.System.Threading.PROFILE_USER -Windows.Win32.System.Threading.REALTIME_PRIORITY_CLASS -Windows.Win32.System.Threading.ReleaseSRWLockExclusive -Windows.Win32.System.Threading.ReleaseSRWLockShared -Windows.Win32.System.Threading.SetThreadStackGuarantee -Windows.Win32.System.Threading.SetWaitableTimer -Windows.Win32.System.Threading.Sleep -Windows.Win32.System.Threading.SleepConditionVariableSRW -Windows.Win32.System.Threading.SleepEx -Windows.Win32.System.Threading.STACK_SIZE_PARAM_IS_A_RESERVATION -Windows.Win32.System.Threading.STARTF_FORCEOFFFEEDBACK -Windows.Win32.System.Threading.STARTF_FORCEONFEEDBACK -Windows.Win32.System.Threading.STARTF_PREVENTPINNING -Windows.Win32.System.Threading.STARTF_RUNFULLSCREEN -Windows.Win32.System.Threading.STARTF_TITLEISAPPID -Windows.Win32.System.Threading.STARTF_TITLEISLINKNAME -Windows.Win32.System.Threading.STARTF_UNTRUSTEDSOURCE -Windows.Win32.System.Threading.STARTF_USECOUNTCHARS -Windows.Win32.System.Threading.STARTF_USEFILLATTRIBUTE -Windows.Win32.System.Threading.STARTF_USEHOTKEY -Windows.Win32.System.Threading.STARTF_USEPOSITION -Windows.Win32.System.Threading.STARTF_USESHOWWINDOW -Windows.Win32.System.Threading.STARTF_USESIZE -Windows.Win32.System.Threading.STARTF_USESTDHANDLES -Windows.Win32.System.Threading.STARTUPINFOEXW -Windows.Win32.System.Threading.STARTUPINFOW -Windows.Win32.System.Threading.STARTUPINFOW_FLAGS -Windows.Win32.System.Threading.SwitchToThread -Windows.Win32.System.Threading.TerminateProcess -Windows.Win32.System.Threading.THREAD_CREATE_RUN_IMMEDIATELY -Windows.Win32.System.Threading.THREAD_CREATE_SUSPENDED -Windows.Win32.System.Threading.THREAD_CREATION_FLAGS -Windows.Win32.System.Threading.TIMER_ALL_ACCESS -Windows.Win32.System.Threading.TIMER_MODIFY_STATE -Windows.Win32.System.Threading.TLS_OUT_OF_INDEXES -Windows.Win32.System.Threading.TlsAlloc -Windows.Win32.System.Threading.TlsFree -Windows.Win32.System.Threading.TlsGetValue -Windows.Win32.System.Threading.TlsSetValue -Windows.Win32.System.Threading.TryAcquireSRWLockExclusive -Windows.Win32.System.Threading.TryAcquireSRWLockShared -Windows.Win32.System.Threading.UpdateProcThreadAttribute -Windows.Win32.System.Threading.WaitForMultipleObjects -Windows.Win32.System.Threading.WaitForSingleObject -Windows.Win32.System.Threading.WakeAllConditionVariable -Windows.Win32.System.Threading.WakeConditionVariable -Windows.Win32.System.WindowsProgramming.PROGRESS_CONTINUE -Windows.Win32.UI.Shell.GetUserProfileDirectoryW -// tidy-alphabetical-end - diff --git a/library/std/src/sys/windows/c/windows_sys.rs b/library/std/src/sys/windows/c/windows_sys.rs deleted file mode 100644 index b38b70c8983..00000000000 --- a/library/std/src/sys/windows/c/windows_sys.rs +++ /dev/null @@ -1,4353 +0,0 @@ -// This file is autogenerated. -// -// To add bindings, edit windows_sys.lst then use `./x run generate-windows-sys` to -// regenerate the bindings. -// -// ignore-tidy-filelength -// Bindings generated by `windows-bindgen` 0.52.0 - -#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] -#[link(name = "advapi32")] -extern "system" { - pub fn OpenProcessToken( - processhandle: HANDLE, - desiredaccess: TOKEN_ACCESS_MASK, - tokenhandle: *mut HANDLE, - ) -> BOOL; -} -#[link(name = "advapi32")] -extern "system" { - #[link_name = "SystemFunction036"] - pub fn RtlGenRandom(randombuffer: *mut ::core::ffi::c_void, randombufferlength: u32) - -> BOOLEAN; -} -#[link(name = "bcrypt")] -extern "system" { - pub fn BCryptGenRandom( - halgorithm: BCRYPT_ALG_HANDLE, - pbbuffer: *mut u8, - cbbuffer: u32, - dwflags: BCRYPTGENRANDOM_FLAGS, - ) -> NTSTATUS; -} -#[link(name = "kernel32")] -extern "system" { - pub fn AcquireSRWLockExclusive(srwlock: *mut SRWLOCK) -> (); -} -#[link(name = "kernel32")] -extern "system" { - pub fn AcquireSRWLockShared(srwlock: *mut SRWLOCK) -> (); -} -#[link(name = "kernel32")] -extern "system" { - pub fn CancelIo(hfile: HANDLE) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CloseHandle(hobject: HANDLE) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CompareStringOrdinal( - lpstring1: PCWSTR, - cchcount1: i32, - lpstring2: PCWSTR, - cchcount2: i32, - bignorecase: BOOL, - ) -> COMPARESTRING_RESULT; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CopyFileExW( - lpexistingfilename: PCWSTR, - lpnewfilename: PCWSTR, - lpprogressroutine: LPPROGRESS_ROUTINE, - lpdata: *const ::core::ffi::c_void, - pbcancel: *mut BOOL, - dwcopyflags: u32, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateDirectoryW( - lppathname: PCWSTR, - lpsecurityattributes: *const SECURITY_ATTRIBUTES, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateEventW( - lpeventattributes: *const SECURITY_ATTRIBUTES, - bmanualreset: BOOL, - binitialstate: BOOL, - lpname: PCWSTR, - ) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateFileW( - lpfilename: PCWSTR, - dwdesiredaccess: u32, - dwsharemode: FILE_SHARE_MODE, - lpsecurityattributes: *const SECURITY_ATTRIBUTES, - dwcreationdisposition: FILE_CREATION_DISPOSITION, - dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, - htemplatefile: HANDLE, - ) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateHardLinkW( - lpfilename: PCWSTR, - lpexistingfilename: PCWSTR, - lpsecurityattributes: *const SECURITY_ATTRIBUTES, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateNamedPipeW( - lpname: PCWSTR, - dwopenmode: FILE_FLAGS_AND_ATTRIBUTES, - dwpipemode: NAMED_PIPE_MODE, - nmaxinstances: u32, - noutbuffersize: u32, - ninbuffersize: u32, - ndefaulttimeout: u32, - lpsecurityattributes: *const SECURITY_ATTRIBUTES, - ) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateProcessW( - lpapplicationname: PCWSTR, - lpcommandline: PWSTR, - lpprocessattributes: *const SECURITY_ATTRIBUTES, - lpthreadattributes: *const SECURITY_ATTRIBUTES, - binherithandles: BOOL, - dwcreationflags: PROCESS_CREATION_FLAGS, - lpenvironment: *const ::core::ffi::c_void, - lpcurrentdirectory: PCWSTR, - lpstartupinfo: *const STARTUPINFOW, - lpprocessinformation: *mut PROCESS_INFORMATION, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateSymbolicLinkW( - lpsymlinkfilename: PCWSTR, - lptargetfilename: PCWSTR, - dwflags: SYMBOLIC_LINK_FLAGS, - ) -> BOOLEAN; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateThread( - lpthreadattributes: *const SECURITY_ATTRIBUTES, - dwstacksize: usize, - lpstartaddress: LPTHREAD_START_ROUTINE, - lpparameter: *const ::core::ffi::c_void, - dwcreationflags: THREAD_CREATION_FLAGS, - lpthreadid: *mut u32, - ) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateWaitableTimerExW( - lptimerattributes: *const SECURITY_ATTRIBUTES, - lptimername: PCWSTR, - dwflags: u32, - dwdesiredaccess: u32, - ) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn DeleteFileW(lpfilename: PCWSTR) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn DeleteProcThreadAttributeList(lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST) -> (); -} -#[link(name = "kernel32")] -extern "system" { - pub fn DeviceIoControl( - hdevice: HANDLE, - dwiocontrolcode: u32, - lpinbuffer: *const ::core::ffi::c_void, - ninbuffersize: u32, - lpoutbuffer: *mut ::core::ffi::c_void, - noutbuffersize: u32, - lpbytesreturned: *mut u32, - lpoverlapped: *mut OVERLAPPED, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn DuplicateHandle( - hsourceprocesshandle: HANDLE, - hsourcehandle: HANDLE, - htargetprocesshandle: HANDLE, - lptargethandle: *mut HANDLE, - dwdesiredaccess: u32, - binherithandle: BOOL, - dwoptions: DUPLICATE_HANDLE_OPTIONS, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn ExitProcess(uexitcode: u32) -> !; -} -#[link(name = "kernel32")] -extern "system" { - pub fn FindClose(hfindfile: HANDLE) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn FindFirstFileW(lpfilename: PCWSTR, lpfindfiledata: *mut WIN32_FIND_DATAW) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn FindNextFileW(hfindfile: HANDLE, lpfindfiledata: *mut WIN32_FIND_DATAW) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn FlushFileBuffers(hfile: HANDLE) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn FormatMessageW( - dwflags: FORMAT_MESSAGE_OPTIONS, - lpsource: *const ::core::ffi::c_void, - dwmessageid: u32, - dwlanguageid: u32, - lpbuffer: PWSTR, - nsize: u32, - arguments: *const *const i8, - ) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn FreeEnvironmentStringsW(penv: PCWSTR) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetActiveProcessorCount(groupnumber: u16) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetCommandLineW() -> PCWSTR; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetConsoleMode(hconsolehandle: HANDLE, lpmode: *mut CONSOLE_MODE) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetCurrentDirectoryW(nbufferlength: u32, lpbuffer: PWSTR) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetCurrentProcess() -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetCurrentProcessId() -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetCurrentThread() -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetEnvironmentStringsW() -> PWSTR; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetEnvironmentVariableW(lpname: PCWSTR, lpbuffer: PWSTR, nsize: u32) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetExitCodeProcess(hprocess: HANDLE, lpexitcode: *mut u32) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetFileAttributesW(lpfilename: PCWSTR) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetFileInformationByHandle( - hfile: HANDLE, - lpfileinformation: *mut BY_HANDLE_FILE_INFORMATION, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetFileInformationByHandleEx( - hfile: HANDLE, - fileinformationclass: FILE_INFO_BY_HANDLE_CLASS, - lpfileinformation: *mut ::core::ffi::c_void, - dwbuffersize: u32, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetFileType(hfile: HANDLE) -> FILE_TYPE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetFinalPathNameByHandleW( - hfile: HANDLE, - lpszfilepath: PWSTR, - cchfilepath: u32, - dwflags: GETFINALPATHNAMEBYHANDLE_FLAGS, - ) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetFullPathNameW( - lpfilename: PCWSTR, - nbufferlength: u32, - lpbuffer: PWSTR, - lpfilepart: *mut PWSTR, - ) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetLastError() -> WIN32_ERROR; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetModuleFileNameW(hmodule: HMODULE, lpfilename: PWSTR, nsize: u32) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetModuleHandleA(lpmodulename: PCSTR) -> HMODULE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetModuleHandleW(lpmodulename: PCWSTR) -> HMODULE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetOverlappedResult( - hfile: HANDLE, - lpoverlapped: *const OVERLAPPED, - lpnumberofbytestransferred: *mut u32, - bwait: BOOL, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetProcAddress(hmodule: HMODULE, lpprocname: PCSTR) -> FARPROC; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetProcessId(process: HANDLE) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetStdHandle(nstdhandle: STD_HANDLE) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetSystemDirectoryW(lpbuffer: PWSTR, usize: u32) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetSystemInfo(lpsysteminfo: *mut SYSTEM_INFO) -> (); -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetSystemTimeAsFileTime(lpsystemtimeasfiletime: *mut FILETIME) -> (); -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetTempPathW(nbufferlength: u32, lpbuffer: PWSTR) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetWindowsDirectoryW(lpbuffer: PWSTR, usize: u32) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn InitOnceBeginInitialize( - lpinitonce: *mut INIT_ONCE, - dwflags: u32, - fpending: *mut BOOL, - lpcontext: *mut *mut ::core::ffi::c_void, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn InitOnceComplete( - lpinitonce: *mut INIT_ONCE, - dwflags: u32, - lpcontext: *const ::core::ffi::c_void, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn InitializeProcThreadAttributeList( - lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST, - dwattributecount: u32, - dwflags: u32, - lpsize: *mut usize, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn MoveFileExW( - lpexistingfilename: PCWSTR, - lpnewfilename: PCWSTR, - dwflags: MOVE_FILE_FLAGS, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn MultiByteToWideChar( - codepage: u32, - dwflags: MULTI_BYTE_TO_WIDE_CHAR_FLAGS, - lpmultibytestr: PCSTR, - cbmultibyte: i32, - lpwidecharstr: PWSTR, - cchwidechar: i32, - ) -> i32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn QueryPerformanceCounter(lpperformancecount: *mut i64) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn QueryPerformanceFrequency(lpfrequency: *mut i64) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn ReadConsoleW( - hconsoleinput: HANDLE, - lpbuffer: *mut ::core::ffi::c_void, - nnumberofcharstoread: u32, - lpnumberofcharsread: *mut u32, - pinputcontrol: *const CONSOLE_READCONSOLE_CONTROL, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn ReadFile( - hfile: HANDLE, - lpbuffer: *mut u8, - nnumberofbytestoread: u32, - lpnumberofbytesread: *mut u32, - lpoverlapped: *mut OVERLAPPED, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn ReadFileEx( - hfile: HANDLE, - lpbuffer: *mut u8, - nnumberofbytestoread: u32, - lpoverlapped: *mut OVERLAPPED, - lpcompletionroutine: LPOVERLAPPED_COMPLETION_ROUTINE, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn ReleaseSRWLockExclusive(srwlock: *mut SRWLOCK) -> (); -} -#[link(name = "kernel32")] -extern "system" { - pub fn ReleaseSRWLockShared(srwlock: *mut SRWLOCK) -> (); -} -#[link(name = "kernel32")] -extern "system" { - pub fn RemoveDirectoryW(lppathname: PCWSTR) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetCurrentDirectoryW(lppathname: PCWSTR) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetEnvironmentVariableW(lpname: PCWSTR, lpvalue: PCWSTR) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetFileAttributesW( - lpfilename: PCWSTR, - dwfileattributes: FILE_FLAGS_AND_ATTRIBUTES, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetFileInformationByHandle( - hfile: HANDLE, - fileinformationclass: FILE_INFO_BY_HANDLE_CLASS, - lpfileinformation: *const ::core::ffi::c_void, - dwbuffersize: u32, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetFilePointerEx( - hfile: HANDLE, - lidistancetomove: i64, - lpnewfilepointer: *mut i64, - dwmovemethod: SET_FILE_POINTER_MOVE_METHOD, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetFileTime( - hfile: HANDLE, - lpcreationtime: *const FILETIME, - lplastaccesstime: *const FILETIME, - lplastwritetime: *const FILETIME, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetHandleInformation(hobject: HANDLE, dwmask: u32, dwflags: HANDLE_FLAGS) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetLastError(dwerrcode: WIN32_ERROR) -> (); -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetThreadStackGuarantee(stacksizeinbytes: *mut u32) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetWaitableTimer( - htimer: HANDLE, - lpduetime: *const i64, - lperiod: i32, - pfncompletionroutine: PTIMERAPCROUTINE, - lpargtocompletionroutine: *const ::core::ffi::c_void, - fresume: BOOL, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn Sleep(dwmilliseconds: u32) -> (); -} -#[link(name = "kernel32")] -extern "system" { - pub fn SleepConditionVariableSRW( - conditionvariable: *mut CONDITION_VARIABLE, - srwlock: *mut SRWLOCK, - dwmilliseconds: u32, - flags: u32, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SleepEx(dwmilliseconds: u32, balertable: BOOL) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SwitchToThread() -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TerminateProcess(hprocess: HANDLE, uexitcode: u32) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TlsAlloc() -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TlsFree(dwtlsindex: u32) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TlsGetValue(dwtlsindex: u32) -> *mut ::core::ffi::c_void; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TlsSetValue(dwtlsindex: u32, lptlsvalue: *const ::core::ffi::c_void) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TryAcquireSRWLockExclusive(srwlock: *mut SRWLOCK) -> BOOLEAN; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TryAcquireSRWLockShared(srwlock: *mut SRWLOCK) -> BOOLEAN; -} -#[link(name = "kernel32")] -extern "system" { - pub fn UpdateProcThreadAttribute( - lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST, - dwflags: u32, - attribute: usize, - lpvalue: *const ::core::ffi::c_void, - cbsize: usize, - lppreviousvalue: *mut ::core::ffi::c_void, - lpreturnsize: *const usize, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn WaitForMultipleObjects( - ncount: u32, - lphandles: *const HANDLE, - bwaitall: BOOL, - dwmilliseconds: u32, - ) -> WAIT_EVENT; -} -#[link(name = "kernel32")] -extern "system" { - pub fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> WAIT_EVENT; -} -#[link(name = "kernel32")] -extern "system" { - pub fn WakeAllConditionVariable(conditionvariable: *mut CONDITION_VARIABLE) -> (); -} -#[link(name = "kernel32")] -extern "system" { - pub fn WakeConditionVariable(conditionvariable: *mut CONDITION_VARIABLE) -> (); -} -#[link(name = "kernel32")] -extern "system" { - pub fn WideCharToMultiByte( - codepage: u32, - dwflags: u32, - lpwidecharstr: PCWSTR, - cchwidechar: i32, - lpmultibytestr: PSTR, - cbmultibyte: i32, - lpdefaultchar: PCSTR, - lpuseddefaultchar: *mut BOOL, - ) -> i32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn WriteConsoleW( - hconsoleoutput: HANDLE, - lpbuffer: *const ::core::ffi::c_void, - nnumberofcharstowrite: u32, - lpnumberofcharswritten: *mut u32, - lpreserved: *const ::core::ffi::c_void, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn WriteFileEx( - hfile: HANDLE, - lpbuffer: *const u8, - nnumberofbytestowrite: u32, - lpoverlapped: *mut OVERLAPPED, - lpcompletionroutine: LPOVERLAPPED_COMPLETION_ROUTINE, - ) -> BOOL; -} -#[link(name = "ntdll")] -extern "system" { - pub fn NtCreateFile( - filehandle: *mut HANDLE, - desiredaccess: FILE_ACCESS_RIGHTS, - objectattributes: *const OBJECT_ATTRIBUTES, - iostatusblock: *mut IO_STATUS_BLOCK, - allocationsize: *const i64, - fileattributes: FILE_FLAGS_AND_ATTRIBUTES, - shareaccess: FILE_SHARE_MODE, - createdisposition: NTCREATEFILE_CREATE_DISPOSITION, - createoptions: NTCREATEFILE_CREATE_OPTIONS, - eabuffer: *const ::core::ffi::c_void, - ealength: u32, - ) -> NTSTATUS; -} -#[link(name = "ntdll")] -extern "system" { - pub fn NtReadFile( - filehandle: HANDLE, - event: HANDLE, - apcroutine: PIO_APC_ROUTINE, - apccontext: *const ::core::ffi::c_void, - iostatusblock: *mut IO_STATUS_BLOCK, - buffer: *mut ::core::ffi::c_void, - length: u32, - byteoffset: *const i64, - key: *const u32, - ) -> NTSTATUS; -} -#[link(name = "ntdll")] -extern "system" { - pub fn NtWriteFile( - filehandle: HANDLE, - event: HANDLE, - apcroutine: PIO_APC_ROUTINE, - apccontext: *const ::core::ffi::c_void, - iostatusblock: *mut IO_STATUS_BLOCK, - buffer: *const ::core::ffi::c_void, - length: u32, - byteoffset: *const i64, - key: *const u32, - ) -> NTSTATUS; -} -#[link(name = "ntdll")] -extern "system" { - pub fn RtlNtStatusToDosError(status: NTSTATUS) -> u32; -} -#[link(name = "userenv")] -extern "system" { - pub fn GetUserProfileDirectoryW( - htoken: HANDLE, - lpprofiledir: PWSTR, - lpcchsize: *mut u32, - ) -> BOOL; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn WSACleanup() -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn WSADuplicateSocketW( - s: SOCKET, - dwprocessid: u32, - lpprotocolinfo: *mut WSAPROTOCOL_INFOW, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn WSAGetLastError() -> WSA_ERROR; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn WSARecv( - s: SOCKET, - lpbuffers: *const WSABUF, - dwbuffercount: u32, - lpnumberofbytesrecvd: *mut u32, - lpflags: *mut u32, - lpoverlapped: *mut OVERLAPPED, - lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn WSASend( - s: SOCKET, - lpbuffers: *const WSABUF, - dwbuffercount: u32, - lpnumberofbytessent: *mut u32, - dwflags: u32, - lpoverlapped: *mut OVERLAPPED, - lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn WSASocketW( - af: i32, - r#type: i32, - protocol: i32, - lpprotocolinfo: *const WSAPROTOCOL_INFOW, - g: u32, - dwflags: u32, - ) -> SOCKET; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn accept(s: SOCKET, addr: *mut SOCKADDR, addrlen: *mut i32) -> SOCKET; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn bind(s: SOCKET, name: *const SOCKADDR, namelen: i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn closesocket(s: SOCKET) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn connect(s: SOCKET, name: *const SOCKADDR, namelen: i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn freeaddrinfo(paddrinfo: *const ADDRINFOA) -> (); -} -#[link(name = "ws2_32")] -extern "system" { - pub fn getaddrinfo( - pnodename: PCSTR, - pservicename: PCSTR, - phints: *const ADDRINFOA, - ppresult: *mut *mut ADDRINFOA, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn getpeername(s: SOCKET, name: *mut SOCKADDR, namelen: *mut i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn getsockname(s: SOCKET, name: *mut SOCKADDR, namelen: *mut i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn getsockopt(s: SOCKET, level: i32, optname: i32, optval: PSTR, optlen: *mut i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn ioctlsocket(s: SOCKET, cmd: i32, argp: *mut u32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn listen(s: SOCKET, backlog: i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn recv(s: SOCKET, buf: PSTR, len: i32, flags: SEND_RECV_FLAGS) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn recvfrom( - s: SOCKET, - buf: PSTR, - len: i32, - flags: i32, - from: *mut SOCKADDR, - fromlen: *mut i32, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn select( - nfds: i32, - readfds: *mut FD_SET, - writefds: *mut FD_SET, - exceptfds: *mut FD_SET, - timeout: *const TIMEVAL, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn send(s: SOCKET, buf: PCSTR, len: i32, flags: SEND_RECV_FLAGS) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn sendto( - s: SOCKET, - buf: PCSTR, - len: i32, - flags: i32, - to: *const SOCKADDR, - tolen: i32, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn setsockopt(s: SOCKET, level: i32, optname: i32, optval: PCSTR, optlen: i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn shutdown(s: SOCKET, how: WINSOCK_SHUTDOWN_HOW) -> i32; -} -pub const ABOVE_NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 32768u32; -pub type ADDRESS_FAMILY = u16; -#[repr(C)] -pub struct ADDRINFOA { - pub ai_flags: i32, - pub ai_family: i32, - pub ai_socktype: i32, - pub ai_protocol: i32, - pub ai_addrlen: usize, - pub ai_canonname: PSTR, - pub ai_addr: *mut SOCKADDR, - pub ai_next: *mut ADDRINFOA, -} -impl ::core::marker::Copy for ADDRINFOA {} -impl ::core::clone::Clone for ADDRINFOA { - fn clone(&self) -> Self { - *self - } -} -pub const AF_INET: ADDRESS_FAMILY = 2u16; -pub const AF_INET6: ADDRESS_FAMILY = 23u16; -pub const AF_UNIX: u16 = 1u16; -pub const AF_UNSPEC: ADDRESS_FAMILY = 0u16; -pub const ALL_PROCESSOR_GROUPS: u16 = 65535u16; -#[repr(C)] -pub union ARM64_NT_NEON128 { - pub Anonymous: ARM64_NT_NEON128_0, - pub D: [f64; 2], - pub S: [f32; 4], - pub H: [u16; 8], - pub B: [u8; 16], -} -impl ::core::marker::Copy for ARM64_NT_NEON128 {} -impl ::core::clone::Clone for ARM64_NT_NEON128 { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub struct ARM64_NT_NEON128_0 { - pub Low: u64, - pub High: i64, -} -impl ::core::marker::Copy for ARM64_NT_NEON128_0 {} -impl ::core::clone::Clone for ARM64_NT_NEON128_0 { - fn clone(&self) -> Self { - *self - } -} -pub type BCRYPTGENRANDOM_FLAGS = u32; -pub type BCRYPT_ALG_HANDLE = *mut ::core::ffi::c_void; -pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: BCRYPTGENRANDOM_FLAGS = 2u32; -pub const BELOW_NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 16384u32; -pub type BOOL = i32; -pub type BOOLEAN = u8; -#[repr(C)] -pub struct BY_HANDLE_FILE_INFORMATION { - pub dwFileAttributes: u32, - pub ftCreationTime: FILETIME, - pub ftLastAccessTime: FILETIME, - pub ftLastWriteTime: FILETIME, - pub dwVolumeSerialNumber: u32, - pub nFileSizeHigh: u32, - pub nFileSizeLow: u32, - pub nNumberOfLinks: u32, - pub nFileIndexHigh: u32, - pub nFileIndexLow: u32, -} -impl ::core::marker::Copy for BY_HANDLE_FILE_INFORMATION {} -impl ::core::clone::Clone for BY_HANDLE_FILE_INFORMATION { - fn clone(&self) -> Self { - *self - } -} -pub const CALLBACK_CHUNK_FINISHED: LPPROGRESS_ROUTINE_CALLBACK_REASON = 0u32; -pub const CALLBACK_STREAM_SWITCH: LPPROGRESS_ROUTINE_CALLBACK_REASON = 1u32; -pub type COMPARESTRING_RESULT = i32; -#[repr(C)] -pub struct CONDITION_VARIABLE { - pub Ptr: *mut ::core::ffi::c_void, -} -impl ::core::marker::Copy for CONDITION_VARIABLE {} -impl ::core::clone::Clone for CONDITION_VARIABLE { - fn clone(&self) -> Self { - *self - } -} -pub type CONSOLE_MODE = u32; -#[repr(C)] -pub struct CONSOLE_READCONSOLE_CONTROL { - pub nLength: u32, - pub nInitialChars: u32, - pub dwCtrlWakeupMask: u32, - pub dwControlKeyState: u32, -} -impl ::core::marker::Copy for CONSOLE_READCONSOLE_CONTROL {} -impl ::core::clone::Clone for CONSOLE_READCONSOLE_CONTROL { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -#[cfg(target_arch = "aarch64")] -pub struct CONTEXT { - pub ContextFlags: CONTEXT_FLAGS, - pub Cpsr: u32, - pub Anonymous: CONTEXT_0, - pub Sp: u64, - pub Pc: u64, - pub V: [ARM64_NT_NEON128; 32], - pub Fpcr: u32, - pub Fpsr: u32, - pub Bcr: [u32; 8], - pub Bvr: [u64; 8], - pub Wcr: [u32; 2], - pub Wvr: [u64; 2], -} -#[cfg(target_arch = "aarch64")] -impl ::core::marker::Copy for CONTEXT {} -#[cfg(target_arch = "aarch64")] -impl ::core::clone::Clone for CONTEXT { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -#[cfg(target_arch = "aarch64")] -pub union CONTEXT_0 { - pub Anonymous: CONTEXT_0_0, - pub X: [u64; 31], -} -#[cfg(target_arch = "aarch64")] -impl ::core::marker::Copy for CONTEXT_0 {} -#[cfg(target_arch = "aarch64")] -impl ::core::clone::Clone for CONTEXT_0 { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -#[cfg(target_arch = "aarch64")] -pub struct CONTEXT_0_0 { - pub X0: u64, - pub X1: u64, - pub X2: u64, - pub X3: u64, - pub X4: u64, - pub X5: u64, - pub X6: u64, - pub X7: u64, - pub X8: u64, - pub X9: u64, - pub X10: u64, - pub X11: u64, - pub X12: u64, - pub X13: u64, - pub X14: u64, - pub X15: u64, - pub X16: u64, - pub X17: u64, - pub X18: u64, - pub X19: u64, - pub X20: u64, - pub X21: u64, - pub X22: u64, - pub X23: u64, - pub X24: u64, - pub X25: u64, - pub X26: u64, - pub X27: u64, - pub X28: u64, - pub Fp: u64, - pub Lr: u64, -} -#[cfg(target_arch = "aarch64")] -impl ::core::marker::Copy for CONTEXT_0_0 {} -#[cfg(target_arch = "aarch64")] -impl ::core::clone::Clone for CONTEXT_0_0 { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -#[cfg(target_arch = "x86_64")] -pub struct CONTEXT { - pub P1Home: u64, - pub P2Home: u64, - pub P3Home: u64, - pub P4Home: u64, - pub P5Home: u64, - pub P6Home: u64, - pub ContextFlags: CONTEXT_FLAGS, - pub MxCsr: u32, - pub SegCs: u16, - pub SegDs: u16, - pub SegEs: u16, - pub SegFs: u16, - pub SegGs: u16, - pub SegSs: u16, - pub EFlags: u32, - pub Dr0: u64, - pub Dr1: u64, - pub Dr2: u64, - pub Dr3: u64, - pub Dr6: u64, - pub Dr7: u64, - pub Rax: u64, - pub Rcx: u64, - pub Rdx: u64, - pub Rbx: u64, - pub Rsp: u64, - pub Rbp: u64, - pub Rsi: u64, - pub Rdi: u64, - pub R8: u64, - pub R9: u64, - pub R10: u64, - pub R11: u64, - pub R12: u64, - pub R13: u64, - pub R14: u64, - pub R15: u64, - pub Rip: u64, - pub Anonymous: CONTEXT_0, - pub VectorRegister: [M128A; 26], - pub VectorControl: u64, - pub DebugControl: u64, - pub LastBranchToRip: u64, - pub LastBranchFromRip: u64, - pub LastExceptionToRip: u64, - pub LastExceptionFromRip: u64, -} -#[cfg(target_arch = "x86_64")] -impl ::core::marker::Copy for CONTEXT {} -#[cfg(target_arch = "x86_64")] -impl ::core::clone::Clone for CONTEXT { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -#[cfg(target_arch = "x86_64")] -pub union CONTEXT_0 { - pub FltSave: XSAVE_FORMAT, - pub Anonymous: CONTEXT_0_0, -} -#[cfg(target_arch = "x86_64")] -impl ::core::marker::Copy for CONTEXT_0 {} -#[cfg(target_arch = "x86_64")] -impl ::core::clone::Clone for CONTEXT_0 { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -#[cfg(target_arch = "x86_64")] -pub struct CONTEXT_0_0 { - pub Header: [M128A; 2], - pub Legacy: [M128A; 8], - pub Xmm0: M128A, - pub Xmm1: M128A, - pub Xmm2: M128A, - pub Xmm3: M128A, - pub Xmm4: M128A, - pub Xmm5: M128A, - pub Xmm6: M128A, - pub Xmm7: M128A, - pub Xmm8: M128A, - pub Xmm9: M128A, - pub Xmm10: M128A, - pub Xmm11: M128A, - pub Xmm12: M128A, - pub Xmm13: M128A, - pub Xmm14: M128A, - pub Xmm15: M128A, -} -#[cfg(target_arch = "x86_64")] -impl ::core::marker::Copy for CONTEXT_0_0 {} -#[cfg(target_arch = "x86_64")] -impl ::core::clone::Clone for CONTEXT_0_0 { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -#[cfg(target_arch = "x86")] -pub struct CONTEXT { - pub ContextFlags: CONTEXT_FLAGS, - pub Dr0: u32, - pub Dr1: u32, - pub Dr2: u32, - pub Dr3: u32, - pub Dr6: u32, - pub Dr7: u32, - pub FloatSave: FLOATING_SAVE_AREA, - pub SegGs: u32, - pub SegFs: u32, - pub SegEs: u32, - pub SegDs: u32, - pub Edi: u32, - pub Esi: u32, - pub Ebx: u32, - pub Edx: u32, - pub Ecx: u32, - pub Eax: u32, - pub Ebp: u32, - pub Eip: u32, - pub SegCs: u32, - pub EFlags: u32, - pub Esp: u32, - pub SegSs: u32, - pub ExtendedRegisters: [u8; 512], -} -#[cfg(target_arch = "x86")] -impl ::core::marker::Copy for CONTEXT {} -#[cfg(target_arch = "x86")] -impl ::core::clone::Clone for CONTEXT { - fn clone(&self) -> Self { - *self - } -} -pub type CONTEXT_FLAGS = u32; -pub const CP_UTF8: u32 = 65001u32; -pub const CREATE_ALWAYS: FILE_CREATION_DISPOSITION = 2u32; -pub const CREATE_BREAKAWAY_FROM_JOB: PROCESS_CREATION_FLAGS = 16777216u32; -pub const CREATE_DEFAULT_ERROR_MODE: PROCESS_CREATION_FLAGS = 67108864u32; -pub const CREATE_FORCEDOS: PROCESS_CREATION_FLAGS = 8192u32; -pub const CREATE_IGNORE_SYSTEM_DEFAULT: PROCESS_CREATION_FLAGS = 2147483648u32; -pub const CREATE_NEW: FILE_CREATION_DISPOSITION = 1u32; -pub const CREATE_NEW_CONSOLE: PROCESS_CREATION_FLAGS = 16u32; -pub const CREATE_NEW_PROCESS_GROUP: PROCESS_CREATION_FLAGS = 512u32; -pub const CREATE_NO_WINDOW: PROCESS_CREATION_FLAGS = 134217728u32; -pub const CREATE_PRESERVE_CODE_AUTHZ_LEVEL: PROCESS_CREATION_FLAGS = 33554432u32; -pub const CREATE_PROTECTED_PROCESS: PROCESS_CREATION_FLAGS = 262144u32; -pub const CREATE_SECURE_PROCESS: PROCESS_CREATION_FLAGS = 4194304u32; -pub const CREATE_SEPARATE_WOW_VDM: PROCESS_CREATION_FLAGS = 2048u32; -pub const CREATE_SHARED_WOW_VDM: PROCESS_CREATION_FLAGS = 4096u32; -pub const CREATE_SUSPENDED: PROCESS_CREATION_FLAGS = 4u32; -pub const CREATE_UNICODE_ENVIRONMENT: PROCESS_CREATION_FLAGS = 1024u32; -pub const CREATE_WAITABLE_TIMER_HIGH_RESOLUTION: u32 = 2u32; -pub const CREATE_WAITABLE_TIMER_MANUAL_RESET: u32 = 1u32; -pub const CSTR_EQUAL: COMPARESTRING_RESULT = 2i32; -pub const CSTR_GREATER_THAN: COMPARESTRING_RESULT = 3i32; -pub const CSTR_LESS_THAN: COMPARESTRING_RESULT = 1i32; -pub const DEBUG_ONLY_THIS_PROCESS: PROCESS_CREATION_FLAGS = 2u32; -pub const DEBUG_PROCESS: PROCESS_CREATION_FLAGS = 1u32; -pub const DELETE: FILE_ACCESS_RIGHTS = 65536u32; -pub const DETACHED_PROCESS: PROCESS_CREATION_FLAGS = 8u32; -pub const DISABLE_NEWLINE_AUTO_RETURN: CONSOLE_MODE = 8u32; -pub const DLL_PROCESS_DETACH: u32 = 0u32; -pub const DLL_THREAD_DETACH: u32 = 3u32; -pub const DNS_ERROR_ADDRESS_REQUIRED: WIN32_ERROR = 9573u32; -pub const DNS_ERROR_ALIAS_LOOP: WIN32_ERROR = 9722u32; -pub const DNS_ERROR_AUTOZONE_ALREADY_EXISTS: WIN32_ERROR = 9610u32; -pub const DNS_ERROR_AXFR: WIN32_ERROR = 9752u32; -pub const DNS_ERROR_BACKGROUND_LOADING: WIN32_ERROR = 9568u32; -pub const DNS_ERROR_BAD_KEYMASTER: WIN32_ERROR = 9122u32; -pub const DNS_ERROR_BAD_PACKET: WIN32_ERROR = 9502u32; -pub const DNS_ERROR_CANNOT_FIND_ROOT_HINTS: WIN32_ERROR = 9564u32; -pub const DNS_ERROR_CLIENT_SUBNET_ALREADY_EXISTS: WIN32_ERROR = 9977u32; -pub const DNS_ERROR_CLIENT_SUBNET_DOES_NOT_EXIST: WIN32_ERROR = 9976u32; -pub const DNS_ERROR_CLIENT_SUBNET_IS_ACCESSED: WIN32_ERROR = 9975u32; -pub const DNS_ERROR_CNAME_COLLISION: WIN32_ERROR = 9709u32; -pub const DNS_ERROR_CNAME_LOOP: WIN32_ERROR = 9707u32; -pub const DNS_ERROR_DATAFILE_OPEN_FAILURE: WIN32_ERROR = 9653u32; -pub const DNS_ERROR_DATAFILE_PARSING: WIN32_ERROR = 9655u32; -pub const DNS_ERROR_DEFAULT_SCOPE: WIN32_ERROR = 9960u32; -pub const DNS_ERROR_DEFAULT_VIRTUALIZATION_INSTANCE: WIN32_ERROR = 9925u32; -pub const DNS_ERROR_DEFAULT_ZONESCOPE: WIN32_ERROR = 9953u32; -pub const DNS_ERROR_DELEGATION_REQUIRED: WIN32_ERROR = 9571u32; -pub const DNS_ERROR_DNAME_COLLISION: WIN32_ERROR = 9721u32; -pub const DNS_ERROR_DNSSEC_IS_DISABLED: WIN32_ERROR = 9125u32; -pub const DNS_ERROR_DP_ALREADY_ENLISTED: WIN32_ERROR = 9904u32; -pub const DNS_ERROR_DP_ALREADY_EXISTS: WIN32_ERROR = 9902u32; -pub const DNS_ERROR_DP_DOES_NOT_EXIST: WIN32_ERROR = 9901u32; -pub const DNS_ERROR_DP_FSMO_ERROR: WIN32_ERROR = 9906u32; -pub const DNS_ERROR_DP_NOT_AVAILABLE: WIN32_ERROR = 9905u32; -pub const DNS_ERROR_DP_NOT_ENLISTED: WIN32_ERROR = 9903u32; -pub const DNS_ERROR_DS_UNAVAILABLE: WIN32_ERROR = 9717u32; -pub const DNS_ERROR_DS_ZONE_ALREADY_EXISTS: WIN32_ERROR = 9718u32; -pub const DNS_ERROR_DWORD_VALUE_TOO_LARGE: WIN32_ERROR = 9567u32; -pub const DNS_ERROR_DWORD_VALUE_TOO_SMALL: WIN32_ERROR = 9566u32; -pub const DNS_ERROR_FILE_WRITEBACK_FAILED: WIN32_ERROR = 9654u32; -pub const DNS_ERROR_FORWARDER_ALREADY_EXISTS: WIN32_ERROR = 9619u32; -pub const DNS_ERROR_INCONSISTENT_ROOT_HINTS: WIN32_ERROR = 9565u32; -pub const DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME: WIN32_ERROR = 9924u32; -pub const DNS_ERROR_INVALID_CLIENT_SUBNET_NAME: WIN32_ERROR = 9984u32; -pub const DNS_ERROR_INVALID_DATA: WIN32_ERROR = 13u32; -pub const DNS_ERROR_INVALID_DATAFILE_NAME: WIN32_ERROR = 9652u32; -pub const DNS_ERROR_INVALID_INITIAL_ROLLOVER_OFFSET: WIN32_ERROR = 9115u32; -pub const DNS_ERROR_INVALID_IP_ADDRESS: WIN32_ERROR = 9552u32; -pub const DNS_ERROR_INVALID_KEY_SIZE: WIN32_ERROR = 9106u32; -pub const DNS_ERROR_INVALID_NAME: WIN32_ERROR = 123u32; -pub const DNS_ERROR_INVALID_NAME_CHAR: WIN32_ERROR = 9560u32; -pub const DNS_ERROR_INVALID_NSEC3_ITERATION_COUNT: WIN32_ERROR = 9124u32; -pub const DNS_ERROR_INVALID_POLICY_TABLE: WIN32_ERROR = 9572u32; -pub const DNS_ERROR_INVALID_PROPERTY: WIN32_ERROR = 9553u32; -pub const DNS_ERROR_INVALID_ROLLOVER_PERIOD: WIN32_ERROR = 9114u32; -pub const DNS_ERROR_INVALID_SCOPE_NAME: WIN32_ERROR = 9958u32; -pub const DNS_ERROR_INVALID_SCOPE_OPERATION: WIN32_ERROR = 9961u32; -pub const DNS_ERROR_INVALID_SIGNATURE_VALIDITY_PERIOD: WIN32_ERROR = 9123u32; -pub const DNS_ERROR_INVALID_TYPE: WIN32_ERROR = 9551u32; -pub const DNS_ERROR_INVALID_XML: WIN32_ERROR = 9126u32; -pub const DNS_ERROR_INVALID_ZONESCOPE_NAME: WIN32_ERROR = 9954u32; -pub const DNS_ERROR_INVALID_ZONE_OPERATION: WIN32_ERROR = 9603u32; -pub const DNS_ERROR_INVALID_ZONE_TYPE: WIN32_ERROR = 9611u32; -pub const DNS_ERROR_KEYMASTER_REQUIRED: WIN32_ERROR = 9101u32; -pub const DNS_ERROR_KSP_DOES_NOT_SUPPORT_PROTECTION: WIN32_ERROR = 9108u32; -pub const DNS_ERROR_KSP_NOT_ACCESSIBLE: WIN32_ERROR = 9112u32; -pub const DNS_ERROR_LOAD_ZONESCOPE_FAILED: WIN32_ERROR = 9956u32; -pub const DNS_ERROR_NAME_DOES_NOT_EXIST: WIN32_ERROR = 9714u32; -pub const DNS_ERROR_NAME_NOT_IN_ZONE: WIN32_ERROR = 9706u32; -pub const DNS_ERROR_NBSTAT_INIT_FAILED: WIN32_ERROR = 9617u32; -pub const DNS_ERROR_NEED_SECONDARY_ADDRESSES: WIN32_ERROR = 9614u32; -pub const DNS_ERROR_NEED_WINS_SERVERS: WIN32_ERROR = 9616u32; -pub const DNS_ERROR_NODE_CREATION_FAILED: WIN32_ERROR = 9703u32; -pub const DNS_ERROR_NODE_IS_CNAME: WIN32_ERROR = 9708u32; -pub const DNS_ERROR_NODE_IS_DNAME: WIN32_ERROR = 9720u32; -pub const DNS_ERROR_NON_RFC_NAME: WIN32_ERROR = 9556u32; -pub const DNS_ERROR_NOT_ALLOWED_ON_ACTIVE_SKD: WIN32_ERROR = 9119u32; -pub const DNS_ERROR_NOT_ALLOWED_ON_RODC: WIN32_ERROR = 9569u32; -pub const DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER: WIN32_ERROR = 9562u32; -pub const DNS_ERROR_NOT_ALLOWED_ON_SIGNED_ZONE: WIN32_ERROR = 9102u32; -pub const DNS_ERROR_NOT_ALLOWED_ON_UNSIGNED_ZONE: WIN32_ERROR = 9121u32; -pub const DNS_ERROR_NOT_ALLOWED_ON_ZSK: WIN32_ERROR = 9118u32; -pub const DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION: WIN32_ERROR = 9563u32; -pub const DNS_ERROR_NOT_ALLOWED_UNDER_DNAME: WIN32_ERROR = 9570u32; -pub const DNS_ERROR_NOT_ALLOWED_WITH_ZONESCOPES: WIN32_ERROR = 9955u32; -pub const DNS_ERROR_NOT_ENOUGH_SIGNING_KEY_DESCRIPTORS: WIN32_ERROR = 9104u32; -pub const DNS_ERROR_NOT_UNIQUE: WIN32_ERROR = 9555u32; -pub const DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE: WIN32_ERROR = 9719u32; -pub const DNS_ERROR_NO_CREATE_CACHE_DATA: WIN32_ERROR = 9713u32; -pub const DNS_ERROR_NO_DNS_SERVERS: WIN32_ERROR = 9852u32; -pub const DNS_ERROR_NO_MEMORY: WIN32_ERROR = 14u32; -pub const DNS_ERROR_NO_PACKET: WIN32_ERROR = 9503u32; -pub const DNS_ERROR_NO_TCPIP: WIN32_ERROR = 9851u32; -pub const DNS_ERROR_NO_VALID_TRUST_ANCHORS: WIN32_ERROR = 9127u32; -pub const DNS_ERROR_NO_ZONE_INFO: WIN32_ERROR = 9602u32; -pub const DNS_ERROR_NSEC3_INCOMPATIBLE_WITH_RSA_SHA1: WIN32_ERROR = 9103u32; -pub const DNS_ERROR_NSEC3_NAME_COLLISION: WIN32_ERROR = 9129u32; -pub const DNS_ERROR_NSEC_INCOMPATIBLE_WITH_NSEC3_RSA_SHA1: WIN32_ERROR = 9130u32; -pub const DNS_ERROR_NUMERIC_NAME: WIN32_ERROR = 9561u32; -pub const DNS_ERROR_POLICY_ALREADY_EXISTS: WIN32_ERROR = 9971u32; -pub const DNS_ERROR_POLICY_DOES_NOT_EXIST: WIN32_ERROR = 9972u32; -pub const DNS_ERROR_POLICY_INVALID_CRITERIA: WIN32_ERROR = 9973u32; -pub const DNS_ERROR_POLICY_INVALID_CRITERIA_CLIENT_SUBNET: WIN32_ERROR = 9990u32; -pub const DNS_ERROR_POLICY_INVALID_CRITERIA_FQDN: WIN32_ERROR = 9994u32; -pub const DNS_ERROR_POLICY_INVALID_CRITERIA_INTERFACE: WIN32_ERROR = 9993u32; -pub const DNS_ERROR_POLICY_INVALID_CRITERIA_NETWORK_PROTOCOL: WIN32_ERROR = 9992u32; -pub const DNS_ERROR_POLICY_INVALID_CRITERIA_QUERY_TYPE: WIN32_ERROR = 9995u32; -pub const DNS_ERROR_POLICY_INVALID_CRITERIA_TIME_OF_DAY: WIN32_ERROR = 9996u32; -pub const DNS_ERROR_POLICY_INVALID_CRITERIA_TRANSPORT_PROTOCOL: WIN32_ERROR = 9991u32; -pub const DNS_ERROR_POLICY_INVALID_NAME: WIN32_ERROR = 9982u32; -pub const DNS_ERROR_POLICY_INVALID_SETTINGS: WIN32_ERROR = 9974u32; -pub const DNS_ERROR_POLICY_INVALID_WEIGHT: WIN32_ERROR = 9981u32; -pub const DNS_ERROR_POLICY_LOCKED: WIN32_ERROR = 9980u32; -pub const DNS_ERROR_POLICY_MISSING_CRITERIA: WIN32_ERROR = 9983u32; -pub const DNS_ERROR_POLICY_PROCESSING_ORDER_INVALID: WIN32_ERROR = 9985u32; -pub const DNS_ERROR_POLICY_SCOPE_MISSING: WIN32_ERROR = 9986u32; -pub const DNS_ERROR_POLICY_SCOPE_NOT_ALLOWED: WIN32_ERROR = 9987u32; -pub const DNS_ERROR_PRIMARY_REQUIRES_DATAFILE: WIN32_ERROR = 9651u32; -pub const DNS_ERROR_RCODE: WIN32_ERROR = 9504u32; -pub const DNS_ERROR_RCODE_BADKEY: WIN32_ERROR = 9017u32; -pub const DNS_ERROR_RCODE_BADSIG: WIN32_ERROR = 9016u32; -pub const DNS_ERROR_RCODE_BADTIME: WIN32_ERROR = 9018u32; -pub const DNS_ERROR_RCODE_FORMAT_ERROR: WIN32_ERROR = 9001u32; -pub const DNS_ERROR_RCODE_NAME_ERROR: WIN32_ERROR = 9003u32; -pub const DNS_ERROR_RCODE_NOTAUTH: WIN32_ERROR = 9009u32; -pub const DNS_ERROR_RCODE_NOTZONE: WIN32_ERROR = 9010u32; -pub const DNS_ERROR_RCODE_NOT_IMPLEMENTED: WIN32_ERROR = 9004u32; -pub const DNS_ERROR_RCODE_NXRRSET: WIN32_ERROR = 9008u32; -pub const DNS_ERROR_RCODE_REFUSED: WIN32_ERROR = 9005u32; -pub const DNS_ERROR_RCODE_SERVER_FAILURE: WIN32_ERROR = 9002u32; -pub const DNS_ERROR_RCODE_YXDOMAIN: WIN32_ERROR = 9006u32; -pub const DNS_ERROR_RCODE_YXRRSET: WIN32_ERROR = 9007u32; -pub const DNS_ERROR_RECORD_ALREADY_EXISTS: WIN32_ERROR = 9711u32; -pub const DNS_ERROR_RECORD_DOES_NOT_EXIST: WIN32_ERROR = 9701u32; -pub const DNS_ERROR_RECORD_FORMAT: WIN32_ERROR = 9702u32; -pub const DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT: WIN32_ERROR = 9710u32; -pub const DNS_ERROR_RECORD_TIMED_OUT: WIN32_ERROR = 9705u32; -pub const DNS_ERROR_ROLLOVER_ALREADY_QUEUED: WIN32_ERROR = 9120u32; -pub const DNS_ERROR_ROLLOVER_IN_PROGRESS: WIN32_ERROR = 9116u32; -pub const DNS_ERROR_ROLLOVER_NOT_POKEABLE: WIN32_ERROR = 9128u32; -pub const DNS_ERROR_RRL_INVALID_IPV4_PREFIX: WIN32_ERROR = 9913u32; -pub const DNS_ERROR_RRL_INVALID_IPV6_PREFIX: WIN32_ERROR = 9914u32; -pub const DNS_ERROR_RRL_INVALID_LEAK_RATE: WIN32_ERROR = 9916u32; -pub const DNS_ERROR_RRL_INVALID_TC_RATE: WIN32_ERROR = 9915u32; -pub const DNS_ERROR_RRL_INVALID_WINDOW_SIZE: WIN32_ERROR = 9912u32; -pub const DNS_ERROR_RRL_LEAK_RATE_LESSTHAN_TC_RATE: WIN32_ERROR = 9917u32; -pub const DNS_ERROR_RRL_NOT_ENABLED: WIN32_ERROR = 9911u32; -pub const DNS_ERROR_SCOPE_ALREADY_EXISTS: WIN32_ERROR = 9963u32; -pub const DNS_ERROR_SCOPE_DOES_NOT_EXIST: WIN32_ERROR = 9959u32; -pub const DNS_ERROR_SCOPE_LOCKED: WIN32_ERROR = 9962u32; -pub const DNS_ERROR_SECONDARY_DATA: WIN32_ERROR = 9712u32; -pub const DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP: WIN32_ERROR = 9612u32; -pub const DNS_ERROR_SERVERSCOPE_IS_REFERENCED: WIN32_ERROR = 9988u32; -pub const DNS_ERROR_SIGNING_KEY_NOT_ACCESSIBLE: WIN32_ERROR = 9107u32; -pub const DNS_ERROR_SOA_DELETE_INVALID: WIN32_ERROR = 9618u32; -pub const DNS_ERROR_STANDBY_KEY_NOT_PRESENT: WIN32_ERROR = 9117u32; -pub const DNS_ERROR_SUBNET_ALREADY_EXISTS: WIN32_ERROR = 9979u32; -pub const DNS_ERROR_SUBNET_DOES_NOT_EXIST: WIN32_ERROR = 9978u32; -pub const DNS_ERROR_TOO_MANY_SKDS: WIN32_ERROR = 9113u32; -pub const DNS_ERROR_TRY_AGAIN_LATER: WIN32_ERROR = 9554u32; -pub const DNS_ERROR_UNEXPECTED_CNG_ERROR: WIN32_ERROR = 9110u32; -pub const DNS_ERROR_UNEXPECTED_DATA_PROTECTION_ERROR: WIN32_ERROR = 9109u32; -pub const DNS_ERROR_UNKNOWN_RECORD_TYPE: WIN32_ERROR = 9704u32; -pub const DNS_ERROR_UNKNOWN_SIGNING_PARAMETER_VERSION: WIN32_ERROR = 9111u32; -pub const DNS_ERROR_UNSECURE_PACKET: WIN32_ERROR = 9505u32; -pub const DNS_ERROR_UNSUPPORTED_ALGORITHM: WIN32_ERROR = 9105u32; -pub const DNS_ERROR_VIRTUALIZATION_INSTANCE_ALREADY_EXISTS: WIN32_ERROR = 9921u32; -pub const DNS_ERROR_VIRTUALIZATION_INSTANCE_DOES_NOT_EXIST: WIN32_ERROR = 9922u32; -pub const DNS_ERROR_VIRTUALIZATION_TREE_LOCKED: WIN32_ERROR = 9923u32; -pub const DNS_ERROR_WINS_INIT_FAILED: WIN32_ERROR = 9615u32; -pub const DNS_ERROR_ZONESCOPE_ALREADY_EXISTS: WIN32_ERROR = 9951u32; -pub const DNS_ERROR_ZONESCOPE_DOES_NOT_EXIST: WIN32_ERROR = 9952u32; -pub const DNS_ERROR_ZONESCOPE_FILE_WRITEBACK_FAILED: WIN32_ERROR = 9957u32; -pub const DNS_ERROR_ZONESCOPE_IS_REFERENCED: WIN32_ERROR = 9989u32; -pub const DNS_ERROR_ZONE_ALREADY_EXISTS: WIN32_ERROR = 9609u32; -pub const DNS_ERROR_ZONE_CONFIGURATION_ERROR: WIN32_ERROR = 9604u32; -pub const DNS_ERROR_ZONE_CREATION_FAILED: WIN32_ERROR = 9608u32; -pub const DNS_ERROR_ZONE_DOES_NOT_EXIST: WIN32_ERROR = 9601u32; -pub const DNS_ERROR_ZONE_HAS_NO_NS_RECORDS: WIN32_ERROR = 9606u32; -pub const DNS_ERROR_ZONE_HAS_NO_SOA_RECORD: WIN32_ERROR = 9605u32; -pub const DNS_ERROR_ZONE_IS_SHUTDOWN: WIN32_ERROR = 9621u32; -pub const DNS_ERROR_ZONE_LOCKED: WIN32_ERROR = 9607u32; -pub const DNS_ERROR_ZONE_LOCKED_FOR_SIGNING: WIN32_ERROR = 9622u32; -pub const DNS_ERROR_ZONE_NOT_SECONDARY: WIN32_ERROR = 9613u32; -pub const DNS_ERROR_ZONE_REQUIRES_MASTER_IP: WIN32_ERROR = 9620u32; -pub const DUPLICATE_CLOSE_SOURCE: DUPLICATE_HANDLE_OPTIONS = 1u32; -pub type DUPLICATE_HANDLE_OPTIONS = u32; -pub const DUPLICATE_SAME_ACCESS: DUPLICATE_HANDLE_OPTIONS = 2u32; -pub const ENABLE_AUTO_POSITION: CONSOLE_MODE = 256u32; -pub const ENABLE_ECHO_INPUT: CONSOLE_MODE = 4u32; -pub const ENABLE_EXTENDED_FLAGS: CONSOLE_MODE = 128u32; -pub const ENABLE_INSERT_MODE: CONSOLE_MODE = 32u32; -pub const ENABLE_LINE_INPUT: CONSOLE_MODE = 2u32; -pub const ENABLE_LVB_GRID_WORLDWIDE: CONSOLE_MODE = 16u32; -pub const ENABLE_MOUSE_INPUT: CONSOLE_MODE = 16u32; -pub const ENABLE_PROCESSED_INPUT: CONSOLE_MODE = 1u32; -pub const ENABLE_PROCESSED_OUTPUT: CONSOLE_MODE = 1u32; -pub const ENABLE_QUICK_EDIT_MODE: CONSOLE_MODE = 64u32; -pub const ENABLE_VIRTUAL_TERMINAL_INPUT: CONSOLE_MODE = 512u32; -pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING: CONSOLE_MODE = 4u32; -pub const ENABLE_WINDOW_INPUT: CONSOLE_MODE = 8u32; -pub const ENABLE_WRAP_AT_EOL_OUTPUT: CONSOLE_MODE = 2u32; -pub const ERROR_ABANDONED_WAIT_0: WIN32_ERROR = 735u32; -pub const ERROR_ABANDONED_WAIT_63: WIN32_ERROR = 736u32; -pub const ERROR_ABANDON_HIBERFILE: WIN32_ERROR = 787u32; -pub const ERROR_ABIOS_ERROR: WIN32_ERROR = 538u32; -pub const ERROR_ACCESS_AUDIT_BY_POLICY: WIN32_ERROR = 785u32; -pub const ERROR_ACCESS_DENIED: WIN32_ERROR = 5u32; -pub const ERROR_ACCESS_DENIED_APPDATA: WIN32_ERROR = 502u32; -pub const ERROR_ACCESS_DISABLED_BY_POLICY: WIN32_ERROR = 1260u32; -pub const ERROR_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY: WIN32_ERROR = 786u32; -pub const ERROR_ACCESS_DISABLED_WEBBLADE: WIN32_ERROR = 1277u32; -pub const ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER: WIN32_ERROR = 1278u32; -pub const ERROR_ACCOUNT_DISABLED: WIN32_ERROR = 1331u32; -pub const ERROR_ACCOUNT_EXPIRED: WIN32_ERROR = 1793u32; -pub const ERROR_ACCOUNT_LOCKED_OUT: WIN32_ERROR = 1909u32; -pub const ERROR_ACCOUNT_RESTRICTION: WIN32_ERROR = 1327u32; -pub const ERROR_ACPI_ERROR: WIN32_ERROR = 669u32; -pub const ERROR_ACTIVE_CONNECTIONS: WIN32_ERROR = 2402u32; -pub const ERROR_ADAP_HDW_ERR: WIN32_ERROR = 57u32; -pub const ERROR_ADDRESS_ALREADY_ASSOCIATED: WIN32_ERROR = 1227u32; -pub const ERROR_ADDRESS_NOT_ASSOCIATED: WIN32_ERROR = 1228u32; -pub const ERROR_ALERTED: WIN32_ERROR = 739u32; -pub const ERROR_ALIAS_EXISTS: WIN32_ERROR = 1379u32; -pub const ERROR_ALLOCATE_BUCKET: WIN32_ERROR = 602u32; -pub const ERROR_ALLOTTED_SPACE_EXCEEDED: WIN32_ERROR = 1344u32; -pub const ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED: WIN32_ERROR = 1933u32; -pub const ERROR_ALREADY_ASSIGNED: WIN32_ERROR = 85u32; -pub const ERROR_ALREADY_EXISTS: WIN32_ERROR = 183u32; -pub const ERROR_ALREADY_FIBER: WIN32_ERROR = 1280u32; -pub const ERROR_ALREADY_HAS_STREAM_ID: WIN32_ERROR = 4444u32; -pub const ERROR_ALREADY_INITIALIZED: WIN32_ERROR = 1247u32; -pub const ERROR_ALREADY_REGISTERED: WIN32_ERROR = 1242u32; -pub const ERROR_ALREADY_RUNNING_LKG: WIN32_ERROR = 1074u32; -pub const ERROR_ALREADY_THREAD: WIN32_ERROR = 1281u32; -pub const ERROR_ALREADY_WAITING: WIN32_ERROR = 1904u32; -pub const ERROR_ALREADY_WIN32: WIN32_ERROR = 719u32; -pub const ERROR_API_UNAVAILABLE: WIN32_ERROR = 15841u32; -pub const ERROR_APPCONTAINER_REQUIRED: WIN32_ERROR = 4251u32; -pub const ERROR_APPEXEC_APP_COMPAT_BLOCK: WIN32_ERROR = 3068u32; -pub const ERROR_APPEXEC_CALLER_WAIT_TIMEOUT: WIN32_ERROR = 3069u32; -pub const ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_LICENSING: WIN32_ERROR = 3071u32; -pub const ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_RESOURCES: WIN32_ERROR = 3072u32; -pub const ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_TERMINATION: WIN32_ERROR = 3070u32; -pub const ERROR_APPEXEC_CONDITION_NOT_SATISFIED: WIN32_ERROR = 3060u32; -pub const ERROR_APPEXEC_HANDLE_INVALIDATED: WIN32_ERROR = 3061u32; -pub const ERROR_APPEXEC_HOST_ID_MISMATCH: WIN32_ERROR = 3066u32; -pub const ERROR_APPEXEC_INVALID_HOST_GENERATION: WIN32_ERROR = 3062u32; -pub const ERROR_APPEXEC_INVALID_HOST_STATE: WIN32_ERROR = 3064u32; -pub const ERROR_APPEXEC_NO_DONOR: WIN32_ERROR = 3065u32; -pub const ERROR_APPEXEC_UNEXPECTED_PROCESS_REGISTRATION: WIN32_ERROR = 3063u32; -pub const ERROR_APPEXEC_UNKNOWN_USER: WIN32_ERROR = 3067u32; -pub const ERROR_APPHELP_BLOCK: WIN32_ERROR = 1259u32; -pub const ERROR_APPX_FILE_NOT_ENCRYPTED: WIN32_ERROR = 409u32; -pub const ERROR_APP_HANG: WIN32_ERROR = 1298u32; -pub const ERROR_APP_INIT_FAILURE: WIN32_ERROR = 575u32; -pub const ERROR_APP_WRONG_OS: WIN32_ERROR = 1151u32; -pub const ERROR_ARBITRATION_UNHANDLED: WIN32_ERROR = 723u32; -pub const ERROR_ARENA_TRASHED: WIN32_ERROR = 7u32; -pub const ERROR_ARITHMETIC_OVERFLOW: WIN32_ERROR = 534u32; -pub const ERROR_ASSERTION_FAILURE: WIN32_ERROR = 668u32; -pub const ERROR_ATOMIC_LOCKS_NOT_SUPPORTED: WIN32_ERROR = 174u32; -pub const ERROR_AUDIT_FAILED: WIN32_ERROR = 606u32; -pub const ERROR_AUTHENTICATION_FIREWALL_FAILED: WIN32_ERROR = 1935u32; -pub const ERROR_AUTHIP_FAILURE: WIN32_ERROR = 1469u32; -pub const ERROR_AUTODATASEG_EXCEEDS_64k: WIN32_ERROR = 199u32; -pub const ERROR_BACKUP_CONTROLLER: WIN32_ERROR = 586u32; -pub const ERROR_BADDB: WIN32_ERROR = 1009u32; -pub const ERROR_BADKEY: WIN32_ERROR = 1010u32; -pub const ERROR_BADSTARTPOSITION: WIN32_ERROR = 778u32; -pub const ERROR_BAD_ACCESSOR_FLAGS: WIN32_ERROR = 773u32; -pub const ERROR_BAD_ARGUMENTS: WIN32_ERROR = 160u32; -pub const ERROR_BAD_COMMAND: WIN32_ERROR = 22u32; -pub const ERROR_BAD_COMPRESSION_BUFFER: WIN32_ERROR = 605u32; -pub const ERROR_BAD_CONFIGURATION: WIN32_ERROR = 1610u32; -pub const ERROR_BAD_CURRENT_DIRECTORY: WIN32_ERROR = 703u32; -pub const ERROR_BAD_DESCRIPTOR_FORMAT: WIN32_ERROR = 1361u32; -pub const ERROR_BAD_DEVICE: WIN32_ERROR = 1200u32; -pub const ERROR_BAD_DEVICE_PATH: WIN32_ERROR = 330u32; -pub const ERROR_BAD_DEV_TYPE: WIN32_ERROR = 66u32; -pub const ERROR_BAD_DLL_ENTRYPOINT: WIN32_ERROR = 609u32; -pub const ERROR_BAD_DRIVER_LEVEL: WIN32_ERROR = 119u32; -pub const ERROR_BAD_ENVIRONMENT: WIN32_ERROR = 10u32; -pub const ERROR_BAD_EXE_FORMAT: WIN32_ERROR = 193u32; -pub const ERROR_BAD_FILE_TYPE: WIN32_ERROR = 222u32; -pub const ERROR_BAD_FORMAT: WIN32_ERROR = 11u32; -pub const ERROR_BAD_FUNCTION_TABLE: WIN32_ERROR = 559u32; -pub const ERROR_BAD_IMPERSONATION_LEVEL: WIN32_ERROR = 1346u32; -pub const ERROR_BAD_INHERITANCE_ACL: WIN32_ERROR = 1340u32; -pub const ERROR_BAD_LENGTH: WIN32_ERROR = 24u32; -pub const ERROR_BAD_LOGON_SESSION_STATE: WIN32_ERROR = 1365u32; -pub const ERROR_BAD_MCFG_TABLE: WIN32_ERROR = 791u32; -pub const ERROR_BAD_NETPATH: WIN32_ERROR = 53u32; -pub const ERROR_BAD_NET_NAME: WIN32_ERROR = 67u32; -pub const ERROR_BAD_NET_RESP: WIN32_ERROR = 58u32; -pub const ERROR_BAD_PATHNAME: WIN32_ERROR = 161u32; -pub const ERROR_BAD_PIPE: WIN32_ERROR = 230u32; -pub const ERROR_BAD_PROFILE: WIN32_ERROR = 1206u32; -pub const ERROR_BAD_PROVIDER: WIN32_ERROR = 1204u32; -pub const ERROR_BAD_QUERY_SYNTAX: WIN32_ERROR = 1615u32; -pub const ERROR_BAD_RECOVERY_POLICY: WIN32_ERROR = 6012u32; -pub const ERROR_BAD_REM_ADAP: WIN32_ERROR = 60u32; -pub const ERROR_BAD_SERVICE_ENTRYPOINT: WIN32_ERROR = 610u32; -pub const ERROR_BAD_STACK: WIN32_ERROR = 543u32; -pub const ERROR_BAD_THREADID_ADDR: WIN32_ERROR = 159u32; -pub const ERROR_BAD_TOKEN_TYPE: WIN32_ERROR = 1349u32; -pub const ERROR_BAD_UNIT: WIN32_ERROR = 20u32; -pub const ERROR_BAD_USERNAME: WIN32_ERROR = 2202u32; -pub const ERROR_BAD_USER_PROFILE: WIN32_ERROR = 1253u32; -pub const ERROR_BAD_VALIDATION_CLASS: WIN32_ERROR = 1348u32; -pub const ERROR_BEGINNING_OF_MEDIA: WIN32_ERROR = 1102u32; -pub const ERROR_BEYOND_VDL: WIN32_ERROR = 1289u32; -pub const ERROR_BIOS_FAILED_TO_CONNECT_INTERRUPT: WIN32_ERROR = 585u32; -pub const ERROR_BLOCKED_BY_PARENTAL_CONTROLS: WIN32_ERROR = 346u32; -pub const ERROR_BLOCK_SHARED: WIN32_ERROR = 514u32; -pub const ERROR_BLOCK_SOURCE_WEAK_REFERENCE_INVALID: WIN32_ERROR = 512u32; -pub const ERROR_BLOCK_TARGET_WEAK_REFERENCE_INVALID: WIN32_ERROR = 513u32; -pub const ERROR_BLOCK_TOO_MANY_REFERENCES: WIN32_ERROR = 347u32; -pub const ERROR_BLOCK_WEAK_REFERENCE_INVALID: WIN32_ERROR = 511u32; -pub const ERROR_BOOT_ALREADY_ACCEPTED: WIN32_ERROR = 1076u32; -pub const ERROR_BROKEN_PIPE: WIN32_ERROR = 109u32; -pub const ERROR_BUFFER_ALL_ZEROS: WIN32_ERROR = 754u32; -pub const ERROR_BUFFER_OVERFLOW: WIN32_ERROR = 111u32; -pub const ERROR_BUSY: WIN32_ERROR = 170u32; -pub const ERROR_BUSY_DRIVE: WIN32_ERROR = 142u32; -pub const ERROR_BUS_RESET: WIN32_ERROR = 1111u32; -pub const ERROR_BYPASSIO_FLT_NOT_SUPPORTED: WIN32_ERROR = 506u32; -pub const ERROR_CACHE_PAGE_LOCKED: WIN32_ERROR = 752u32; -pub const ERROR_CALLBACK_INVOKE_INLINE: WIN32_ERROR = 812u32; -pub const ERROR_CALLBACK_POP_STACK: WIN32_ERROR = 768u32; -pub const ERROR_CALLBACK_SUPPLIED_INVALID_DATA: WIN32_ERROR = 1273u32; -pub const ERROR_CALL_NOT_IMPLEMENTED: WIN32_ERROR = 120u32; -pub const ERROR_CANCELLED: WIN32_ERROR = 1223u32; -pub const ERROR_CANCEL_VIOLATION: WIN32_ERROR = 173u32; -pub const ERROR_CANNOT_BREAK_OPLOCK: WIN32_ERROR = 802u32; -pub const ERROR_CANNOT_COPY: WIN32_ERROR = 266u32; -pub const ERROR_CANNOT_DETECT_DRIVER_FAILURE: WIN32_ERROR = 1080u32; -pub const ERROR_CANNOT_DETECT_PROCESS_ABORT: WIN32_ERROR = 1081u32; -pub const ERROR_CANNOT_FIND_WND_CLASS: WIN32_ERROR = 1407u32; -pub const ERROR_CANNOT_GRANT_REQUESTED_OPLOCK: WIN32_ERROR = 801u32; -pub const ERROR_CANNOT_IMPERSONATE: WIN32_ERROR = 1368u32; -pub const ERROR_CANNOT_LOAD_REGISTRY_FILE: WIN32_ERROR = 589u32; -pub const ERROR_CANNOT_MAKE: WIN32_ERROR = 82u32; -pub const ERROR_CANNOT_OPEN_PROFILE: WIN32_ERROR = 1205u32; -pub const ERROR_CANTFETCHBACKWARDS: WIN32_ERROR = 770u32; -pub const ERROR_CANTOPEN: WIN32_ERROR = 1011u32; -pub const ERROR_CANTREAD: WIN32_ERROR = 1012u32; -pub const ERROR_CANTSCROLLBACKWARDS: WIN32_ERROR = 771u32; -pub const ERROR_CANTWRITE: WIN32_ERROR = 1013u32; -pub const ERROR_CANT_ACCESS_DOMAIN_INFO: WIN32_ERROR = 1351u32; -pub const ERROR_CANT_ACCESS_FILE: WIN32_ERROR = 1920u32; -pub const ERROR_CANT_CLEAR_ENCRYPTION_FLAG: WIN32_ERROR = 432u32; -pub const ERROR_CANT_DISABLE_MANDATORY: WIN32_ERROR = 1310u32; -pub const ERROR_CANT_ENABLE_DENY_ONLY: WIN32_ERROR = 629u32; -pub const ERROR_CANT_OPEN_ANONYMOUS: WIN32_ERROR = 1347u32; -pub const ERROR_CANT_RESOLVE_FILENAME: WIN32_ERROR = 1921u32; -pub const ERROR_CANT_TERMINATE_SELF: WIN32_ERROR = 555u32; -pub const ERROR_CANT_WAIT: WIN32_ERROR = 554u32; -pub const ERROR_CAN_NOT_COMPLETE: WIN32_ERROR = 1003u32; -pub const ERROR_CAPAUTHZ_CHANGE_TYPE: WIN32_ERROR = 451u32; -pub const ERROR_CAPAUTHZ_DB_CORRUPTED: WIN32_ERROR = 455u32; -pub const ERROR_CAPAUTHZ_NOT_AUTHORIZED: WIN32_ERROR = 453u32; -pub const ERROR_CAPAUTHZ_NOT_DEVUNLOCKED: WIN32_ERROR = 450u32; -pub const ERROR_CAPAUTHZ_NOT_PROVISIONED: WIN32_ERROR = 452u32; -pub const ERROR_CAPAUTHZ_NO_POLICY: WIN32_ERROR = 454u32; -pub const ERROR_CAPAUTHZ_SCCD_DEV_MODE_REQUIRED: WIN32_ERROR = 459u32; -pub const ERROR_CAPAUTHZ_SCCD_INVALID_CATALOG: WIN32_ERROR = 456u32; -pub const ERROR_CAPAUTHZ_SCCD_NO_AUTH_ENTITY: WIN32_ERROR = 457u32; -pub const ERROR_CAPAUTHZ_SCCD_NO_CAPABILITY_MATCH: WIN32_ERROR = 460u32; -pub const ERROR_CAPAUTHZ_SCCD_PARSE_ERROR: WIN32_ERROR = 458u32; -pub const ERROR_CARDBUS_NOT_SUPPORTED: WIN32_ERROR = 724u32; -pub const ERROR_CASE_DIFFERING_NAMES_IN_DIR: WIN32_ERROR = 424u32; -pub const ERROR_CASE_SENSITIVE_PATH: WIN32_ERROR = 442u32; -pub const ERROR_CERTIFICATE_VALIDATION_PREFERENCE_CONFLICT: WIN32_ERROR = 817u32; -pub const ERROR_CHECKING_FILE_SYSTEM: WIN32_ERROR = 712u32; -pub const ERROR_CHECKOUT_REQUIRED: WIN32_ERROR = 221u32; -pub const ERROR_CHILD_MUST_BE_VOLATILE: WIN32_ERROR = 1021u32; -pub const ERROR_CHILD_NOT_COMPLETE: WIN32_ERROR = 129u32; -pub const ERROR_CHILD_PROCESS_BLOCKED: WIN32_ERROR = 367u32; -pub const ERROR_CHILD_WINDOW_MENU: WIN32_ERROR = 1436u32; -pub const ERROR_CIMFS_IMAGE_CORRUPT: WIN32_ERROR = 470u32; -pub const ERROR_CIMFS_IMAGE_VERSION_NOT_SUPPORTED: WIN32_ERROR = 471u32; -pub const ERROR_CIRCULAR_DEPENDENCY: WIN32_ERROR = 1059u32; -pub const ERROR_CLASS_ALREADY_EXISTS: WIN32_ERROR = 1410u32; -pub const ERROR_CLASS_DOES_NOT_EXIST: WIN32_ERROR = 1411u32; -pub const ERROR_CLASS_HAS_WINDOWS: WIN32_ERROR = 1412u32; -pub const ERROR_CLIENT_SERVER_PARAMETERS_INVALID: WIN32_ERROR = 597u32; -pub const ERROR_CLIPBOARD_NOT_OPEN: WIN32_ERROR = 1418u32; -pub const ERROR_CLOUD_FILE_ACCESS_DENIED: WIN32_ERROR = 395u32; -pub const ERROR_CLOUD_FILE_ALREADY_CONNECTED: WIN32_ERROR = 378u32; -pub const ERROR_CLOUD_FILE_AUTHENTICATION_FAILED: WIN32_ERROR = 386u32; -pub const ERROR_CLOUD_FILE_CONNECTED_PROVIDER_ONLY: WIN32_ERROR = 382u32; -pub const ERROR_CLOUD_FILE_DEHYDRATION_DISALLOWED: WIN32_ERROR = 434u32; -pub const ERROR_CLOUD_FILE_INCOMPATIBLE_HARDLINKS: WIN32_ERROR = 396u32; -pub const ERROR_CLOUD_FILE_INSUFFICIENT_RESOURCES: WIN32_ERROR = 387u32; -pub const ERROR_CLOUD_FILE_INVALID_REQUEST: WIN32_ERROR = 380u32; -pub const ERROR_CLOUD_FILE_IN_USE: WIN32_ERROR = 391u32; -pub const ERROR_CLOUD_FILE_METADATA_CORRUPT: WIN32_ERROR = 363u32; -pub const ERROR_CLOUD_FILE_METADATA_TOO_LARGE: WIN32_ERROR = 364u32; -pub const ERROR_CLOUD_FILE_NETWORK_UNAVAILABLE: WIN32_ERROR = 388u32; -pub const ERROR_CLOUD_FILE_NOT_IN_SYNC: WIN32_ERROR = 377u32; -pub const ERROR_CLOUD_FILE_NOT_SUPPORTED: WIN32_ERROR = 379u32; -pub const ERROR_CLOUD_FILE_NOT_UNDER_SYNC_ROOT: WIN32_ERROR = 390u32; -pub const ERROR_CLOUD_FILE_PINNED: WIN32_ERROR = 392u32; -pub const ERROR_CLOUD_FILE_PROPERTY_BLOB_CHECKSUM_MISMATCH: WIN32_ERROR = 366u32; -pub const ERROR_CLOUD_FILE_PROPERTY_BLOB_TOO_LARGE: WIN32_ERROR = 365u32; -pub const ERROR_CLOUD_FILE_PROPERTY_CORRUPT: WIN32_ERROR = 394u32; -pub const ERROR_CLOUD_FILE_PROPERTY_LOCK_CONFLICT: WIN32_ERROR = 397u32; -pub const ERROR_CLOUD_FILE_PROPERTY_VERSION_NOT_SUPPORTED: WIN32_ERROR = 375u32; -pub const ERROR_CLOUD_FILE_PROVIDER_NOT_RUNNING: WIN32_ERROR = 362u32; -pub const ERROR_CLOUD_FILE_PROVIDER_TERMINATED: WIN32_ERROR = 404u32; -pub const ERROR_CLOUD_FILE_READ_ONLY_VOLUME: WIN32_ERROR = 381u32; -pub const ERROR_CLOUD_FILE_REQUEST_ABORTED: WIN32_ERROR = 393u32; -pub const ERROR_CLOUD_FILE_REQUEST_CANCELED: WIN32_ERROR = 398u32; -pub const ERROR_CLOUD_FILE_REQUEST_TIMEOUT: WIN32_ERROR = 426u32; -pub const ERROR_CLOUD_FILE_SYNC_ROOT_METADATA_CORRUPT: WIN32_ERROR = 358u32; -pub const ERROR_CLOUD_FILE_TOO_MANY_PROPERTY_BLOBS: WIN32_ERROR = 374u32; -pub const ERROR_CLOUD_FILE_UNSUCCESSFUL: WIN32_ERROR = 389u32; -pub const ERROR_CLOUD_FILE_US_MESSAGE_TIMEOUT: WIN32_ERROR = 475u32; -pub const ERROR_CLOUD_FILE_VALIDATION_FAILED: WIN32_ERROR = 383u32; -pub const ERROR_COMMITMENT_LIMIT: WIN32_ERROR = 1455u32; -pub const ERROR_COMMITMENT_MINIMUM: WIN32_ERROR = 635u32; -pub const ERROR_COMPRESSED_FILE_NOT_SUPPORTED: WIN32_ERROR = 335u32; -pub const ERROR_COMPRESSION_DISABLED: WIN32_ERROR = 769u32; -pub const ERROR_COMPRESSION_NOT_BENEFICIAL: WIN32_ERROR = 344u32; -pub const ERROR_CONNECTED_OTHER_PASSWORD: WIN32_ERROR = 2108u32; -pub const ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT: WIN32_ERROR = 2109u32; -pub const ERROR_CONNECTION_ABORTED: WIN32_ERROR = 1236u32; -pub const ERROR_CONNECTION_ACTIVE: WIN32_ERROR = 1230u32; -pub const ERROR_CONNECTION_COUNT_LIMIT: WIN32_ERROR = 1238u32; -pub const ERROR_CONNECTION_INVALID: WIN32_ERROR = 1229u32; -pub const ERROR_CONNECTION_REFUSED: WIN32_ERROR = 1225u32; -pub const ERROR_CONNECTION_UNAVAIL: WIN32_ERROR = 1201u32; -pub const ERROR_CONTAINER_ASSIGNED: WIN32_ERROR = 1504u32; -pub const ERROR_CONTENT_BLOCKED: WIN32_ERROR = 1296u32; -pub const ERROR_CONTEXT_EXPIRED: WIN32_ERROR = 1931u32; -pub const ERROR_CONTINUE: WIN32_ERROR = 1246u32; -pub const ERROR_CONTROL_C_EXIT: WIN32_ERROR = 572u32; -pub const ERROR_CONTROL_ID_NOT_FOUND: WIN32_ERROR = 1421u32; -pub const ERROR_CONVERT_TO_LARGE: WIN32_ERROR = 600u32; -pub const ERROR_CORRUPT_LOG_CLEARED: WIN32_ERROR = 798u32; -pub const ERROR_CORRUPT_LOG_CORRUPTED: WIN32_ERROR = 795u32; -pub const ERROR_CORRUPT_LOG_DELETED_FULL: WIN32_ERROR = 797u32; -pub const ERROR_CORRUPT_LOG_OVERFULL: WIN32_ERROR = 794u32; -pub const ERROR_CORRUPT_LOG_UNAVAILABLE: WIN32_ERROR = 796u32; -pub const ERROR_CORRUPT_SYSTEM_FILE: WIN32_ERROR = 634u32; -pub const ERROR_COULD_NOT_INTERPRET: WIN32_ERROR = 552u32; -pub const ERROR_COUNTER_TIMEOUT: WIN32_ERROR = 1121u32; -pub const ERROR_CPU_SET_INVALID: WIN32_ERROR = 813u32; -pub const ERROR_CRASH_DUMP: WIN32_ERROR = 753u32; -pub const ERROR_CRC: WIN32_ERROR = 23u32; -pub const ERROR_CREATE_FAILED: WIN32_ERROR = 1631u32; -pub const ERROR_CROSS_PARTITION_VIOLATION: WIN32_ERROR = 1661u32; -pub const ERROR_CSCSHARE_OFFLINE: WIN32_ERROR = 1262u32; -pub const ERROR_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE: WIN32_ERROR = 6019u32; -pub const ERROR_CS_ENCRYPTION_FILE_NOT_CSE: WIN32_ERROR = 6021u32; -pub const ERROR_CS_ENCRYPTION_INVALID_SERVER_RESPONSE: WIN32_ERROR = 6017u32; -pub const ERROR_CS_ENCRYPTION_NEW_ENCRYPTED_FILE: WIN32_ERROR = 6020u32; -pub const ERROR_CS_ENCRYPTION_UNSUPPORTED_SERVER: WIN32_ERROR = 6018u32; -pub const ERROR_CTX_CLIENT_QUERY_TIMEOUT: WIN32_ERROR = 7040u32; -pub const ERROR_CTX_MODEM_RESPONSE_TIMEOUT: WIN32_ERROR = 7012u32; -pub const ERROR_CURRENT_DIRECTORY: WIN32_ERROR = 16u32; -pub const ERROR_CURRENT_DOMAIN_NOT_ALLOWED: WIN32_ERROR = 1399u32; -pub const ERROR_DATABASE_DOES_NOT_EXIST: WIN32_ERROR = 1065u32; -pub const ERROR_DATATYPE_MISMATCH: WIN32_ERROR = 1629u32; -pub const ERROR_DATA_CHECKSUM_ERROR: WIN32_ERROR = 323u32; -pub const ERROR_DATA_NOT_ACCEPTED: WIN32_ERROR = 592u32; -pub const ERROR_DAX_MAPPING_EXISTS: WIN32_ERROR = 361u32; -pub const ERROR_DBG_COMMAND_EXCEPTION: WIN32_ERROR = 697u32; -pub const ERROR_DBG_CONTINUE: WIN32_ERROR = 767u32; -pub const ERROR_DBG_CONTROL_BREAK: WIN32_ERROR = 696u32; -pub const ERROR_DBG_CONTROL_C: WIN32_ERROR = 693u32; -pub const ERROR_DBG_EXCEPTION_HANDLED: WIN32_ERROR = 766u32; -pub const ERROR_DBG_EXCEPTION_NOT_HANDLED: WIN32_ERROR = 688u32; -pub const ERROR_DBG_PRINTEXCEPTION_C: WIN32_ERROR = 694u32; -pub const ERROR_DBG_REPLY_LATER: WIN32_ERROR = 689u32; -pub const ERROR_DBG_RIPEXCEPTION: WIN32_ERROR = 695u32; -pub const ERROR_DBG_TERMINATE_PROCESS: WIN32_ERROR = 692u32; -pub const ERROR_DBG_TERMINATE_THREAD: WIN32_ERROR = 691u32; -pub const ERROR_DBG_UNABLE_TO_PROVIDE_HANDLE: WIN32_ERROR = 690u32; -pub const ERROR_DC_NOT_FOUND: WIN32_ERROR = 1425u32; -pub const ERROR_DDE_FAIL: WIN32_ERROR = 1156u32; -pub const ERROR_DEBUGGER_INACTIVE: WIN32_ERROR = 1284u32; -pub const ERROR_DEBUG_ATTACH_FAILED: WIN32_ERROR = 590u32; -pub const ERROR_DECRYPTION_FAILED: WIN32_ERROR = 6001u32; -pub const ERROR_DELAY_LOAD_FAILED: WIN32_ERROR = 1285u32; -pub const ERROR_DELETE_PENDING: WIN32_ERROR = 303u32; -pub const ERROR_DEPENDENT_SERVICES_RUNNING: WIN32_ERROR = 1051u32; -pub const ERROR_DESTINATION_ELEMENT_FULL: WIN32_ERROR = 1161u32; -pub const ERROR_DESTROY_OBJECT_OF_OTHER_THREAD: WIN32_ERROR = 1435u32; -pub const ERROR_DEVICE_ALREADY_ATTACHED: WIN32_ERROR = 548u32; -pub const ERROR_DEVICE_ALREADY_REMEMBERED: WIN32_ERROR = 1202u32; -pub const ERROR_DEVICE_DOOR_OPEN: WIN32_ERROR = 1166u32; -pub const ERROR_DEVICE_ENUMERATION_ERROR: WIN32_ERROR = 648u32; -pub const ERROR_DEVICE_FEATURE_NOT_SUPPORTED: WIN32_ERROR = 316u32; -pub const ERROR_DEVICE_HARDWARE_ERROR: WIN32_ERROR = 483u32; -pub const ERROR_DEVICE_HINT_NAME_BUFFER_TOO_SMALL: WIN32_ERROR = 355u32; -pub const ERROR_DEVICE_IN_MAINTENANCE: WIN32_ERROR = 359u32; -pub const ERROR_DEVICE_IN_USE: WIN32_ERROR = 2404u32; -pub const ERROR_DEVICE_NOT_CONNECTED: WIN32_ERROR = 1167u32; -pub const ERROR_DEVICE_NOT_PARTITIONED: WIN32_ERROR = 1107u32; -pub const ERROR_DEVICE_NO_RESOURCES: WIN32_ERROR = 322u32; -pub const ERROR_DEVICE_REINITIALIZATION_NEEDED: WIN32_ERROR = 1164u32; -pub const ERROR_DEVICE_REMOVED: WIN32_ERROR = 1617u32; -pub const ERROR_DEVICE_REQUIRES_CLEANING: WIN32_ERROR = 1165u32; -pub const ERROR_DEVICE_RESET_REQUIRED: WIN32_ERROR = 507u32; -pub const ERROR_DEVICE_SUPPORT_IN_PROGRESS: WIN32_ERROR = 171u32; -pub const ERROR_DEVICE_UNREACHABLE: WIN32_ERROR = 321u32; -pub const ERROR_DEV_NOT_EXIST: WIN32_ERROR = 55u32; -pub const ERROR_DHCP_ADDRESS_CONFLICT: WIN32_ERROR = 4100u32; -pub const ERROR_DIFFERENT_SERVICE_ACCOUNT: WIN32_ERROR = 1079u32; -pub const ERROR_DIRECTORY: WIN32_ERROR = 267u32; -pub const ERROR_DIRECTORY_NOT_SUPPORTED: WIN32_ERROR = 336u32; -pub const ERROR_DIRECT_ACCESS_HANDLE: WIN32_ERROR = 130u32; -pub const ERROR_DIR_EFS_DISALLOWED: WIN32_ERROR = 6010u32; -pub const ERROR_DIR_NOT_EMPTY: WIN32_ERROR = 145u32; -pub const ERROR_DIR_NOT_ROOT: WIN32_ERROR = 144u32; -pub const ERROR_DISCARDED: WIN32_ERROR = 157u32; -pub const ERROR_DISK_CHANGE: WIN32_ERROR = 107u32; -pub const ERROR_DISK_CORRUPT: WIN32_ERROR = 1393u32; -pub const ERROR_DISK_FULL: WIN32_ERROR = 112u32; -pub const ERROR_DISK_OPERATION_FAILED: WIN32_ERROR = 1127u32; -pub const ERROR_DISK_QUOTA_EXCEEDED: WIN32_ERROR = 1295u32; -pub const ERROR_DISK_RECALIBRATE_FAILED: WIN32_ERROR = 1126u32; -pub const ERROR_DISK_REPAIR_DISABLED: WIN32_ERROR = 780u32; -pub const ERROR_DISK_REPAIR_REDIRECTED: WIN32_ERROR = 792u32; -pub const ERROR_DISK_REPAIR_UNSUCCESSFUL: WIN32_ERROR = 793u32; -pub const ERROR_DISK_RESET_FAILED: WIN32_ERROR = 1128u32; -pub const ERROR_DISK_RESOURCES_EXHAUSTED: WIN32_ERROR = 314u32; -pub const ERROR_DISK_TOO_FRAGMENTED: WIN32_ERROR = 302u32; -pub const ERROR_DLL_INIT_FAILED: WIN32_ERROR = 1114u32; -pub const ERROR_DLL_INIT_FAILED_LOGOFF: WIN32_ERROR = 624u32; -pub const ERROR_DLL_MIGHT_BE_INCOMPATIBLE: WIN32_ERROR = 687u32; -pub const ERROR_DLL_MIGHT_BE_INSECURE: WIN32_ERROR = 686u32; -pub const ERROR_DLL_NOT_FOUND: WIN32_ERROR = 1157u32; -pub const ERROR_DLP_POLICY_DENIES_OPERATION: WIN32_ERROR = 446u32; -pub const ERROR_DLP_POLICY_SILENTLY_FAIL: WIN32_ERROR = 449u32; -pub const ERROR_DLP_POLICY_WARNS_AGAINST_OPERATION: WIN32_ERROR = 445u32; -pub const ERROR_DOMAIN_CONTROLLER_EXISTS: WIN32_ERROR = 1250u32; -pub const ERROR_DOMAIN_CONTROLLER_NOT_FOUND: WIN32_ERROR = 1908u32; -pub const ERROR_DOMAIN_CTRLR_CONFIG_ERROR: WIN32_ERROR = 581u32; -pub const ERROR_DOMAIN_EXISTS: WIN32_ERROR = 1356u32; -pub const ERROR_DOMAIN_LIMIT_EXCEEDED: WIN32_ERROR = 1357u32; -pub const ERROR_DOMAIN_SID_SAME_AS_LOCAL_WORKSTATION: WIN32_ERROR = 8644u32; -pub const ERROR_DOMAIN_TRUST_INCONSISTENT: WIN32_ERROR = 1810u32; -pub const ERROR_DOWNGRADE_DETECTED: WIN32_ERROR = 1265u32; -pub const ERROR_DPL_NOT_SUPPORTED_FOR_USER: WIN32_ERROR = 423u32; -pub const ERROR_DRIVERS_LEAKING_LOCKED_PAGES: WIN32_ERROR = 729u32; -pub const ERROR_DRIVER_BLOCKED: WIN32_ERROR = 1275u32; -pub const ERROR_DRIVER_CANCEL_TIMEOUT: WIN32_ERROR = 594u32; -pub const ERROR_DRIVER_DATABASE_ERROR: WIN32_ERROR = 652u32; -pub const ERROR_DRIVER_FAILED_PRIOR_UNLOAD: WIN32_ERROR = 654u32; -pub const ERROR_DRIVER_FAILED_SLEEP: WIN32_ERROR = 633u32; -pub const ERROR_DRIVER_PROCESS_TERMINATED: WIN32_ERROR = 1291u32; -pub const ERROR_DRIVE_LOCKED: WIN32_ERROR = 108u32; -pub const ERROR_DS_ADD_REPLICA_INHIBITED: WIN32_ERROR = 8302u32; -pub const ERROR_DS_ADMIN_LIMIT_EXCEEDED: WIN32_ERROR = 8228u32; -pub const ERROR_DS_AFFECTS_MULTIPLE_DSAS: WIN32_ERROR = 8249u32; -pub const ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER: WIN32_ERROR = 8578u32; -pub const ERROR_DS_ALIASED_OBJ_MISSING: WIN32_ERROR = 8334u32; -pub const ERROR_DS_ALIAS_DEREF_PROBLEM: WIN32_ERROR = 8244u32; -pub const ERROR_DS_ALIAS_POINTS_TO_ALIAS: WIN32_ERROR = 8336u32; -pub const ERROR_DS_ALIAS_PROBLEM: WIN32_ERROR = 8241u32; -pub const ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS: WIN32_ERROR = 8205u32; -pub const ERROR_DS_ATTRIBUTE_OWNED_BY_SAM: WIN32_ERROR = 8346u32; -pub const ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED: WIN32_ERROR = 8204u32; -pub const ERROR_DS_ATT_ALREADY_EXISTS: WIN32_ERROR = 8318u32; -pub const ERROR_DS_ATT_IS_NOT_ON_OBJ: WIN32_ERROR = 8310u32; -pub const ERROR_DS_ATT_NOT_DEF_FOR_CLASS: WIN32_ERROR = 8317u32; -pub const ERROR_DS_ATT_NOT_DEF_IN_SCHEMA: WIN32_ERROR = 8303u32; -pub const ERROR_DS_ATT_SCHEMA_REQ_ID: WIN32_ERROR = 8399u32; -pub const ERROR_DS_ATT_SCHEMA_REQ_SYNTAX: WIN32_ERROR = 8416u32; -pub const ERROR_DS_ATT_VAL_ALREADY_EXISTS: WIN32_ERROR = 8323u32; -pub const ERROR_DS_AUDIT_FAILURE: WIN32_ERROR = 8625u32; -pub const ERROR_DS_AUTHORIZATION_FAILED: WIN32_ERROR = 8599u32; -pub const ERROR_DS_AUTH_METHOD_NOT_SUPPORTED: WIN32_ERROR = 8231u32; -pub const ERROR_DS_AUTH_UNKNOWN: WIN32_ERROR = 8234u32; -pub const ERROR_DS_AUX_CLS_TEST_FAIL: WIN32_ERROR = 8389u32; -pub const ERROR_DS_BACKLINK_WITHOUT_LINK: WIN32_ERROR = 8482u32; -pub const ERROR_DS_BAD_ATT_SCHEMA_SYNTAX: WIN32_ERROR = 8400u32; -pub const ERROR_DS_BAD_HIERARCHY_FILE: WIN32_ERROR = 8425u32; -pub const ERROR_DS_BAD_INSTANCE_TYPE: WIN32_ERROR = 8313u32; -pub const ERROR_DS_BAD_NAME_SYNTAX: WIN32_ERROR = 8335u32; -pub const ERROR_DS_BAD_RDN_ATT_ID_SYNTAX: WIN32_ERROR = 8392u32; -pub const ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED: WIN32_ERROR = 8426u32; -pub const ERROR_DS_BUSY: WIN32_ERROR = 8206u32; -pub const ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD: WIN32_ERROR = 8585u32; -pub const ERROR_DS_CANT_ADD_ATT_VALUES: WIN32_ERROR = 8320u32; -pub const ERROR_DS_CANT_ADD_SYSTEM_ONLY: WIN32_ERROR = 8358u32; -pub const ERROR_DS_CANT_ADD_TO_GC: WIN32_ERROR = 8550u32; -pub const ERROR_DS_CANT_CACHE_ATT: WIN32_ERROR = 8401u32; -pub const ERROR_DS_CANT_CACHE_CLASS: WIN32_ERROR = 8402u32; -pub const ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC: WIN32_ERROR = 8553u32; -pub const ERROR_DS_CANT_CREATE_UNDER_SCHEMA: WIN32_ERROR = 8510u32; -pub const ERROR_DS_CANT_DELETE: WIN32_ERROR = 8398u32; -pub const ERROR_DS_CANT_DELETE_DSA_OBJ: WIN32_ERROR = 8340u32; -pub const ERROR_DS_CANT_DEL_MASTER_CROSSREF: WIN32_ERROR = 8375u32; -pub const ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC: WIN32_ERROR = 8604u32; -pub const ERROR_DS_CANT_DEREF_ALIAS: WIN32_ERROR = 8337u32; -pub const ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN: WIN32_ERROR = 8603u32; -pub const ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF: WIN32_ERROR = 8589u32; -pub const ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN: WIN32_ERROR = 8537u32; -pub const ERROR_DS_CANT_FIND_DSA_OBJ: WIN32_ERROR = 8419u32; -pub const ERROR_DS_CANT_FIND_EXPECTED_NC: WIN32_ERROR = 8420u32; -pub const ERROR_DS_CANT_FIND_NC_IN_CACHE: WIN32_ERROR = 8421u32; -pub const ERROR_DS_CANT_MIX_MASTER_AND_REPS: WIN32_ERROR = 8331u32; -pub const ERROR_DS_CANT_MOD_OBJ_CLASS: WIN32_ERROR = 8215u32; -pub const ERROR_DS_CANT_MOD_PRIMARYGROUPID: WIN32_ERROR = 8506u32; -pub const ERROR_DS_CANT_MOD_SYSTEM_ONLY: WIN32_ERROR = 8369u32; -pub const ERROR_DS_CANT_MOVE_ACCOUNT_GROUP: WIN32_ERROR = 8498u32; -pub const ERROR_DS_CANT_MOVE_APP_BASIC_GROUP: WIN32_ERROR = 8608u32; -pub const ERROR_DS_CANT_MOVE_APP_QUERY_GROUP: WIN32_ERROR = 8609u32; -pub const ERROR_DS_CANT_MOVE_DELETED_OBJECT: WIN32_ERROR = 8489u32; -pub const ERROR_DS_CANT_MOVE_RESOURCE_GROUP: WIN32_ERROR = 8499u32; -pub const ERROR_DS_CANT_ON_NON_LEAF: WIN32_ERROR = 8213u32; -pub const ERROR_DS_CANT_ON_RDN: WIN32_ERROR = 8214u32; -pub const ERROR_DS_CANT_REMOVE_ATT_CACHE: WIN32_ERROR = 8403u32; -pub const ERROR_DS_CANT_REMOVE_CLASS_CACHE: WIN32_ERROR = 8404u32; -pub const ERROR_DS_CANT_REM_MISSING_ATT: WIN32_ERROR = 8324u32; -pub const ERROR_DS_CANT_REM_MISSING_ATT_VAL: WIN32_ERROR = 8325u32; -pub const ERROR_DS_CANT_REPLACE_HIDDEN_REC: WIN32_ERROR = 8424u32; -pub const ERROR_DS_CANT_RETRIEVE_ATTS: WIN32_ERROR = 8481u32; -pub const ERROR_DS_CANT_RETRIEVE_CHILD: WIN32_ERROR = 8422u32; -pub const ERROR_DS_CANT_RETRIEVE_DN: WIN32_ERROR = 8405u32; -pub const ERROR_DS_CANT_RETRIEVE_INSTANCE: WIN32_ERROR = 8407u32; -pub const ERROR_DS_CANT_RETRIEVE_SD: WIN32_ERROR = 8526u32; -pub const ERROR_DS_CANT_START: WIN32_ERROR = 8531u32; -pub const ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ: WIN32_ERROR = 8560u32; -pub const ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS: WIN32_ERROR = 8493u32; -pub const ERROR_DS_CHILDREN_EXIST: WIN32_ERROR = 8332u32; -pub const ERROR_DS_CLASS_MUST_BE_CONCRETE: WIN32_ERROR = 8359u32; -pub const ERROR_DS_CLASS_NOT_DSA: WIN32_ERROR = 8343u32; -pub const ERROR_DS_CLIENT_LOOP: WIN32_ERROR = 8259u32; -pub const ERROR_DS_CODE_INCONSISTENCY: WIN32_ERROR = 8408u32; -pub const ERROR_DS_COMPARE_FALSE: WIN32_ERROR = 8229u32; -pub const ERROR_DS_COMPARE_TRUE: WIN32_ERROR = 8230u32; -pub const ERROR_DS_CONFIDENTIALITY_REQUIRED: WIN32_ERROR = 8237u32; -pub const ERROR_DS_CONFIG_PARAM_MISSING: WIN32_ERROR = 8427u32; -pub const ERROR_DS_CONSTRAINT_VIOLATION: WIN32_ERROR = 8239u32; -pub const ERROR_DS_CONSTRUCTED_ATT_MOD: WIN32_ERROR = 8475u32; -pub const ERROR_DS_CONTROL_NOT_FOUND: WIN32_ERROR = 8258u32; -pub const ERROR_DS_COULDNT_CONTACT_FSMO: WIN32_ERROR = 8367u32; -pub const ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE: WIN32_ERROR = 8503u32; -pub const ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE: WIN32_ERROR = 8502u32; -pub const ERROR_DS_COULDNT_UPDATE_SPNS: WIN32_ERROR = 8525u32; -pub const ERROR_DS_COUNTING_AB_INDICES_FAILED: WIN32_ERROR = 8428u32; -pub const ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD: WIN32_ERROR = 8491u32; -pub const ERROR_DS_CROSS_DOM_MOVE_ERROR: WIN32_ERROR = 8216u32; -pub const ERROR_DS_CROSS_NC_DN_RENAME: WIN32_ERROR = 8368u32; -pub const ERROR_DS_CROSS_REF_BUSY: WIN32_ERROR = 8602u32; -pub const ERROR_DS_CROSS_REF_EXISTS: WIN32_ERROR = 8374u32; -pub const ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE: WIN32_ERROR = 8495u32; -pub const ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2: WIN32_ERROR = 8586u32; -pub const ERROR_DS_DATABASE_ERROR: WIN32_ERROR = 8409u32; -pub const ERROR_DS_DECODING_ERROR: WIN32_ERROR = 8253u32; -pub const ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED: WIN32_ERROR = 8536u32; -pub const ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST: WIN32_ERROR = 8535u32; -pub const ERROR_DS_DIFFERENT_REPL_EPOCHS: WIN32_ERROR = 8593u32; -pub const ERROR_DS_DISALLOWED_IN_SYSTEM_CONTAINER: WIN32_ERROR = 8615u32; -pub const ERROR_DS_DISALLOWED_NC_REDIRECT: WIN32_ERROR = 8640u32; -pub const ERROR_DS_DNS_LOOKUP_FAILURE: WIN32_ERROR = 8524u32; -pub const ERROR_DS_DOMAIN_NAME_EXISTS_IN_FOREST: WIN32_ERROR = 8634u32; -pub const ERROR_DS_DOMAIN_RENAME_IN_PROGRESS: WIN32_ERROR = 8612u32; -pub const ERROR_DS_DOMAIN_VERSION_TOO_HIGH: WIN32_ERROR = 8564u32; -pub const ERROR_DS_DOMAIN_VERSION_TOO_LOW: WIN32_ERROR = 8566u32; -pub const ERROR_DS_DRA_ABANDON_SYNC: WIN32_ERROR = 8462u32; -pub const ERROR_DS_DRA_ACCESS_DENIED: WIN32_ERROR = 8453u32; -pub const ERROR_DS_DRA_BAD_DN: WIN32_ERROR = 8439u32; -pub const ERROR_DS_DRA_BAD_INSTANCE_TYPE: WIN32_ERROR = 8445u32; -pub const ERROR_DS_DRA_BAD_NC: WIN32_ERROR = 8440u32; -pub const ERROR_DS_DRA_BUSY: WIN32_ERROR = 8438u32; -pub const ERROR_DS_DRA_CONNECTION_FAILED: WIN32_ERROR = 8444u32; -pub const ERROR_DS_DRA_CORRUPT_UTD_VECTOR: WIN32_ERROR = 8629u32; -pub const ERROR_DS_DRA_DB_ERROR: WIN32_ERROR = 8451u32; -pub const ERROR_DS_DRA_DN_EXISTS: WIN32_ERROR = 8441u32; -pub const ERROR_DS_DRA_EARLIER_SCHEMA_CONFLICT: WIN32_ERROR = 8544u32; -pub const ERROR_DS_DRA_EXTN_CONNECTION_FAILED: WIN32_ERROR = 8466u32; -pub const ERROR_DS_DRA_GENERIC: WIN32_ERROR = 8436u32; -pub const ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET: WIN32_ERROR = 8464u32; -pub const ERROR_DS_DRA_INCONSISTENT_DIT: WIN32_ERROR = 8443u32; -pub const ERROR_DS_DRA_INTERNAL_ERROR: WIN32_ERROR = 8442u32; -pub const ERROR_DS_DRA_INVALID_PARAMETER: WIN32_ERROR = 8437u32; -pub const ERROR_DS_DRA_MAIL_PROBLEM: WIN32_ERROR = 8447u32; -pub const ERROR_DS_DRA_MISSING_KRBTGT_SECRET: WIN32_ERROR = 8633u32; -pub const ERROR_DS_DRA_MISSING_PARENT: WIN32_ERROR = 8460u32; -pub const ERROR_DS_DRA_NAME_COLLISION: WIN32_ERROR = 8458u32; -pub const ERROR_DS_DRA_NOT_SUPPORTED: WIN32_ERROR = 8454u32; -pub const ERROR_DS_DRA_NO_REPLICA: WIN32_ERROR = 8452u32; -pub const ERROR_DS_DRA_OBJ_IS_REP_SOURCE: WIN32_ERROR = 8450u32; -pub const ERROR_DS_DRA_OBJ_NC_MISMATCH: WIN32_ERROR = 8545u32; -pub const ERROR_DS_DRA_OUT_OF_MEM: WIN32_ERROR = 8446u32; -pub const ERROR_DS_DRA_OUT_SCHEDULE_WINDOW: WIN32_ERROR = 8617u32; -pub const ERROR_DS_DRA_PREEMPTED: WIN32_ERROR = 8461u32; -pub const ERROR_DS_DRA_RECYCLED_TARGET: WIN32_ERROR = 8639u32; -pub const ERROR_DS_DRA_REF_ALREADY_EXISTS: WIN32_ERROR = 8448u32; -pub const ERROR_DS_DRA_REF_NOT_FOUND: WIN32_ERROR = 8449u32; -pub const ERROR_DS_DRA_REPL_PENDING: WIN32_ERROR = 8477u32; -pub const ERROR_DS_DRA_RPC_CANCELLED: WIN32_ERROR = 8455u32; -pub const ERROR_DS_DRA_SCHEMA_CONFLICT: WIN32_ERROR = 8543u32; -pub const ERROR_DS_DRA_SCHEMA_INFO_SHIP: WIN32_ERROR = 8542u32; -pub const ERROR_DS_DRA_SCHEMA_MISMATCH: WIN32_ERROR = 8418u32; -pub const ERROR_DS_DRA_SECRETS_DENIED: WIN32_ERROR = 8630u32; -pub const ERROR_DS_DRA_SHUTDOWN: WIN32_ERROR = 8463u32; -pub const ERROR_DS_DRA_SINK_DISABLED: WIN32_ERROR = 8457u32; -pub const ERROR_DS_DRA_SOURCE_DISABLED: WIN32_ERROR = 8456u32; -pub const ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA: WIN32_ERROR = 8465u32; -pub const ERROR_DS_DRA_SOURCE_REINSTALLED: WIN32_ERROR = 8459u32; -pub const ERROR_DS_DRS_EXTENSIONS_CHANGED: WIN32_ERROR = 8594u32; -pub const ERROR_DS_DSA_MUST_BE_INT_MASTER: WIN32_ERROR = 8342u32; -pub const ERROR_DS_DST_DOMAIN_NOT_NATIVE: WIN32_ERROR = 8496u32; -pub const ERROR_DS_DST_NC_MISMATCH: WIN32_ERROR = 8486u32; -pub const ERROR_DS_DS_REQUIRED: WIN32_ERROR = 8478u32; -pub const ERROR_DS_DUPLICATE_ID_FOUND: WIN32_ERROR = 8605u32; -pub const ERROR_DS_DUP_LDAP_DISPLAY_NAME: WIN32_ERROR = 8382u32; -pub const ERROR_DS_DUP_LINK_ID: WIN32_ERROR = 8468u32; -pub const ERROR_DS_DUP_MAPI_ID: WIN32_ERROR = 8380u32; -pub const ERROR_DS_DUP_MSDS_INTID: WIN32_ERROR = 8597u32; -pub const ERROR_DS_DUP_OID: WIN32_ERROR = 8379u32; -pub const ERROR_DS_DUP_RDN: WIN32_ERROR = 8378u32; -pub const ERROR_DS_DUP_SCHEMA_ID_GUID: WIN32_ERROR = 8381u32; -pub const ERROR_DS_ENCODING_ERROR: WIN32_ERROR = 8252u32; -pub const ERROR_DS_EPOCH_MISMATCH: WIN32_ERROR = 8483u32; -pub const ERROR_DS_EXISTING_AD_CHILD_NC: WIN32_ERROR = 8613u32; -pub const ERROR_DS_EXISTS_IN_AUX_CLS: WIN32_ERROR = 8393u32; -pub const ERROR_DS_EXISTS_IN_MAY_HAVE: WIN32_ERROR = 8386u32; -pub const ERROR_DS_EXISTS_IN_MUST_HAVE: WIN32_ERROR = 8385u32; -pub const ERROR_DS_EXISTS_IN_POSS_SUP: WIN32_ERROR = 8395u32; -pub const ERROR_DS_EXISTS_IN_RDNATTID: WIN32_ERROR = 8598u32; -pub const ERROR_DS_EXISTS_IN_SUB_CLS: WIN32_ERROR = 8394u32; -pub const ERROR_DS_FILTER_UNKNOWN: WIN32_ERROR = 8254u32; -pub const ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS: WIN32_ERROR = 8555u32; -pub const ERROR_DS_FLAT_NAME_EXISTS_IN_FOREST: WIN32_ERROR = 8635u32; -pub const ERROR_DS_FOREST_VERSION_TOO_HIGH: WIN32_ERROR = 8563u32; -pub const ERROR_DS_FOREST_VERSION_TOO_LOW: WIN32_ERROR = 8565u32; -pub const ERROR_DS_GCVERIFY_ERROR: WIN32_ERROR = 8417u32; -pub const ERROR_DS_GC_NOT_AVAILABLE: WIN32_ERROR = 8217u32; -pub const ERROR_DS_GC_REQUIRED: WIN32_ERROR = 8547u32; -pub const ERROR_DS_GENERIC_ERROR: WIN32_ERROR = 8341u32; -pub const ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER: WIN32_ERROR = 8519u32; -pub const ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER: WIN32_ERROR = 8516u32; -pub const ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER: WIN32_ERROR = 8517u32; -pub const ERROR_DS_GOVERNSID_MISSING: WIN32_ERROR = 8410u32; -pub const ERROR_DS_GROUP_CONVERSION_ERROR: WIN32_ERROR = 8607u32; -pub const ERROR_DS_HAVE_PRIMARY_MEMBERS: WIN32_ERROR = 8521u32; -pub const ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED: WIN32_ERROR = 8429u32; -pub const ERROR_DS_HIERARCHY_TABLE_TOO_DEEP: WIN32_ERROR = 8628u32; -pub const ERROR_DS_HIGH_ADLDS_FFL: WIN32_ERROR = 8641u32; -pub const ERROR_DS_HIGH_DSA_VERSION: WIN32_ERROR = 8642u32; -pub const ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD: WIN32_ERROR = 8507u32; -pub const ERROR_DS_ILLEGAL_MOD_OPERATION: WIN32_ERROR = 8311u32; -pub const ERROR_DS_ILLEGAL_SUPERIOR: WIN32_ERROR = 8345u32; -pub const ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION: WIN32_ERROR = 8492u32; -pub const ERROR_DS_INAPPROPRIATE_AUTH: WIN32_ERROR = 8233u32; -pub const ERROR_DS_INAPPROPRIATE_MATCHING: WIN32_ERROR = 8238u32; -pub const ERROR_DS_INCOMPATIBLE_CONTROLS_USED: WIN32_ERROR = 8574u32; -pub const ERROR_DS_INCOMPATIBLE_VERSION: WIN32_ERROR = 8567u32; -pub const ERROR_DS_INCORRECT_ROLE_OWNER: WIN32_ERROR = 8210u32; -pub const ERROR_DS_INIT_FAILURE: WIN32_ERROR = 8532u32; -pub const ERROR_DS_INIT_FAILURE_CONSOLE: WIN32_ERROR = 8561u32; -pub const ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE: WIN32_ERROR = 8512u32; -pub const ERROR_DS_INSTALL_NO_SRC_SCH_VERSION: WIN32_ERROR = 8511u32; -pub const ERROR_DS_INSTALL_SCHEMA_MISMATCH: WIN32_ERROR = 8467u32; -pub const ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT: WIN32_ERROR = 8606u32; -pub const ERROR_DS_INSUFF_ACCESS_RIGHTS: WIN32_ERROR = 8344u32; -pub const ERROR_DS_INTERNAL_FAILURE: WIN32_ERROR = 8430u32; -pub const ERROR_DS_INVALID_ATTRIBUTE_SYNTAX: WIN32_ERROR = 8203u32; -pub const ERROR_DS_INVALID_DMD: WIN32_ERROR = 8360u32; -pub const ERROR_DS_INVALID_DN_SYNTAX: WIN32_ERROR = 8242u32; -pub const ERROR_DS_INVALID_GROUP_TYPE: WIN32_ERROR = 8513u32; -pub const ERROR_DS_INVALID_LDAP_DISPLAY_NAME: WIN32_ERROR = 8479u32; -pub const ERROR_DS_INVALID_NAME_FOR_SPN: WIN32_ERROR = 8554u32; -pub const ERROR_DS_INVALID_ROLE_OWNER: WIN32_ERROR = 8366u32; -pub const ERROR_DS_INVALID_SCRIPT: WIN32_ERROR = 8600u32; -pub const ERROR_DS_INVALID_SEARCH_FLAG: WIN32_ERROR = 8500u32; -pub const ERROR_DS_INVALID_SEARCH_FLAG_SUBTREE: WIN32_ERROR = 8626u32; -pub const ERROR_DS_INVALID_SEARCH_FLAG_TUPLE: WIN32_ERROR = 8627u32; -pub const ERROR_DS_IS_LEAF: WIN32_ERROR = 8243u32; -pub const ERROR_DS_KEY_NOT_UNIQUE: WIN32_ERROR = 8527u32; -pub const ERROR_DS_LDAP_SEND_QUEUE_FULL: WIN32_ERROR = 8616u32; -pub const ERROR_DS_LINK_ID_NOT_AVAILABLE: WIN32_ERROR = 8577u32; -pub const ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER: WIN32_ERROR = 8520u32; -pub const ERROR_DS_LOCAL_ERROR: WIN32_ERROR = 8251u32; -pub const ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY: WIN32_ERROR = 8548u32; -pub const ERROR_DS_LOOP_DETECT: WIN32_ERROR = 8246u32; -pub const ERROR_DS_LOW_ADLDS_FFL: WIN32_ERROR = 8643u32; -pub const ERROR_DS_LOW_DSA_VERSION: WIN32_ERROR = 8568u32; -pub const ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4: WIN32_ERROR = 8572u32; -pub const ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED: WIN32_ERROR = 8557u32; -pub const ERROR_DS_MAPI_ID_NOT_AVAILABLE: WIN32_ERROR = 8632u32; -pub const ERROR_DS_MASTERDSA_REQUIRED: WIN32_ERROR = 8314u32; -pub const ERROR_DS_MAX_OBJ_SIZE_EXCEEDED: WIN32_ERROR = 8304u32; -pub const ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY: WIN32_ERROR = 8201u32; -pub const ERROR_DS_MISSING_EXPECTED_ATT: WIN32_ERROR = 8411u32; -pub const ERROR_DS_MISSING_FOREST_TRUST: WIN32_ERROR = 8649u32; -pub const ERROR_DS_MISSING_FSMO_SETTINGS: WIN32_ERROR = 8434u32; -pub const ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER: WIN32_ERROR = 8497u32; -pub const ERROR_DS_MISSING_REQUIRED_ATT: WIN32_ERROR = 8316u32; -pub const ERROR_DS_MISSING_SUPREF: WIN32_ERROR = 8406u32; -pub const ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG: WIN32_ERROR = 8581u32; -pub const ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE: WIN32_ERROR = 8579u32; -pub const ERROR_DS_MODIFYDN_WRONG_GRANDPARENT: WIN32_ERROR = 8582u32; -pub const ERROR_DS_MUST_BE_RUN_ON_DST_DC: WIN32_ERROR = 8558u32; -pub const ERROR_DS_NAME_ERROR_DOMAIN_ONLY: WIN32_ERROR = 8473u32; -pub const ERROR_DS_NAME_ERROR_NOT_FOUND: WIN32_ERROR = 8470u32; -pub const ERROR_DS_NAME_ERROR_NOT_UNIQUE: WIN32_ERROR = 8471u32; -pub const ERROR_DS_NAME_ERROR_NO_MAPPING: WIN32_ERROR = 8472u32; -pub const ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING: WIN32_ERROR = 8474u32; -pub const ERROR_DS_NAME_ERROR_RESOLVING: WIN32_ERROR = 8469u32; -pub const ERROR_DS_NAME_ERROR_TRUST_REFERRAL: WIN32_ERROR = 8583u32; -pub const ERROR_DS_NAME_NOT_UNIQUE: WIN32_ERROR = 8571u32; -pub const ERROR_DS_NAME_REFERENCE_INVALID: WIN32_ERROR = 8373u32; -pub const ERROR_DS_NAME_TOO_LONG: WIN32_ERROR = 8348u32; -pub const ERROR_DS_NAME_TOO_MANY_PARTS: WIN32_ERROR = 8347u32; -pub const ERROR_DS_NAME_TYPE_UNKNOWN: WIN32_ERROR = 8351u32; -pub const ERROR_DS_NAME_UNPARSEABLE: WIN32_ERROR = 8350u32; -pub const ERROR_DS_NAME_VALUE_TOO_LONG: WIN32_ERROR = 8349u32; -pub const ERROR_DS_NAMING_MASTER_GC: WIN32_ERROR = 8523u32; -pub const ERROR_DS_NAMING_VIOLATION: WIN32_ERROR = 8247u32; -pub const ERROR_DS_NCNAME_MISSING_CR_REF: WIN32_ERROR = 8412u32; -pub const ERROR_DS_NCNAME_MUST_BE_NC: WIN32_ERROR = 8357u32; -pub const ERROR_DS_NC_MUST_HAVE_NC_PARENT: WIN32_ERROR = 8494u32; -pub const ERROR_DS_NC_STILL_HAS_DSAS: WIN32_ERROR = 8546u32; -pub const ERROR_DS_NONEXISTENT_MAY_HAVE: WIN32_ERROR = 8387u32; -pub const ERROR_DS_NONEXISTENT_MUST_HAVE: WIN32_ERROR = 8388u32; -pub const ERROR_DS_NONEXISTENT_POSS_SUP: WIN32_ERROR = 8390u32; -pub const ERROR_DS_NONSAFE_SCHEMA_CHANGE: WIN32_ERROR = 8508u32; -pub const ERROR_DS_NON_ASQ_SEARCH: WIN32_ERROR = 8624u32; -pub const ERROR_DS_NON_BASE_SEARCH: WIN32_ERROR = 8480u32; -pub const ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX: WIN32_ERROR = 8377u32; -pub const ERROR_DS_NOT_AN_OBJECT: WIN32_ERROR = 8352u32; -pub const ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC: WIN32_ERROR = 8487u32; -pub const ERROR_DS_NOT_CLOSEST: WIN32_ERROR = 8588u32; -pub const ERROR_DS_NOT_INSTALLED: WIN32_ERROR = 8200u32; -pub const ERROR_DS_NOT_ON_BACKLINK: WIN32_ERROR = 8362u32; -pub const ERROR_DS_NOT_SUPPORTED: WIN32_ERROR = 8256u32; -pub const ERROR_DS_NOT_SUPPORTED_SORT_ORDER: WIN32_ERROR = 8570u32; -pub const ERROR_DS_NO_ATTRIBUTE_OR_VALUE: WIN32_ERROR = 8202u32; -pub const ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN: WIN32_ERROR = 8569u32; -pub const ERROR_DS_NO_CHAINED_EVAL: WIN32_ERROR = 8328u32; -pub const ERROR_DS_NO_CHAINING: WIN32_ERROR = 8327u32; -pub const ERROR_DS_NO_CHECKPOINT_WITH_PDC: WIN32_ERROR = 8551u32; -pub const ERROR_DS_NO_CROSSREF_FOR_NC: WIN32_ERROR = 8363u32; -pub const ERROR_DS_NO_DELETED_NAME: WIN32_ERROR = 8355u32; -pub const ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS: WIN32_ERROR = 8549u32; -pub const ERROR_DS_NO_MORE_RIDS: WIN32_ERROR = 8209u32; -pub const ERROR_DS_NO_MSDS_INTID: WIN32_ERROR = 8596u32; -pub const ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN: WIN32_ERROR = 8514u32; -pub const ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN: WIN32_ERROR = 8515u32; -pub const ERROR_DS_NO_NTDSA_OBJECT: WIN32_ERROR = 8623u32; -pub const ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC: WIN32_ERROR = 8580u32; -pub const ERROR_DS_NO_PARENT_OBJECT: WIN32_ERROR = 8329u32; -pub const ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION: WIN32_ERROR = 8533u32; -pub const ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA: WIN32_ERROR = 8306u32; -pub const ERROR_DS_NO_REF_DOMAIN: WIN32_ERROR = 8575u32; -pub const ERROR_DS_NO_REQUESTED_ATTS_FOUND: WIN32_ERROR = 8308u32; -pub const ERROR_DS_NO_RESULTS_RETURNED: WIN32_ERROR = 8257u32; -pub const ERROR_DS_NO_RIDS_ALLOCATED: WIN32_ERROR = 8208u32; -pub const ERROR_DS_NO_SERVER_OBJECT: WIN32_ERROR = 8622u32; -pub const ERROR_DS_NO_SUCH_OBJECT: WIN32_ERROR = 8240u32; -pub const ERROR_DS_NO_TREE_DELETE_ABOVE_NC: WIN32_ERROR = 8501u32; -pub const ERROR_DS_NTDSCRIPT_PROCESS_ERROR: WIN32_ERROR = 8592u32; -pub const ERROR_DS_NTDSCRIPT_SYNTAX_ERROR: WIN32_ERROR = 8591u32; -pub const ERROR_DS_OBJECT_BEING_REMOVED: WIN32_ERROR = 8339u32; -pub const ERROR_DS_OBJECT_CLASS_REQUIRED: WIN32_ERROR = 8315u32; -pub const ERROR_DS_OBJECT_RESULTS_TOO_LARGE: WIN32_ERROR = 8248u32; -pub const ERROR_DS_OBJ_CLASS_NOT_DEFINED: WIN32_ERROR = 8371u32; -pub const ERROR_DS_OBJ_CLASS_NOT_SUBCLASS: WIN32_ERROR = 8372u32; -pub const ERROR_DS_OBJ_CLASS_VIOLATION: WIN32_ERROR = 8212u32; -pub const ERROR_DS_OBJ_GUID_EXISTS: WIN32_ERROR = 8361u32; -pub const ERROR_DS_OBJ_NOT_FOUND: WIN32_ERROR = 8333u32; -pub const ERROR_DS_OBJ_STRING_NAME_EXISTS: WIN32_ERROR = 8305u32; -pub const ERROR_DS_OBJ_TOO_LARGE: WIN32_ERROR = 8312u32; -pub const ERROR_DS_OFFSET_RANGE_ERROR: WIN32_ERROR = 8262u32; -pub const ERROR_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS: WIN32_ERROR = 8637u32; -pub const ERROR_DS_OID_NOT_FOUND: WIN32_ERROR = 8638u32; -pub const ERROR_DS_OPERATIONS_ERROR: WIN32_ERROR = 8224u32; -pub const ERROR_DS_OUT_OF_SCOPE: WIN32_ERROR = 8338u32; -pub const ERROR_DS_OUT_OF_VERSION_STORE: WIN32_ERROR = 8573u32; -pub const ERROR_DS_PARAM_ERROR: WIN32_ERROR = 8255u32; -pub const ERROR_DS_PARENT_IS_AN_ALIAS: WIN32_ERROR = 8330u32; -pub const ERROR_DS_PDC_OPERATION_IN_PROGRESS: WIN32_ERROR = 8490u32; -pub const ERROR_DS_PER_ATTRIBUTE_AUTHZ_FAILED_DURING_ADD: WIN32_ERROR = 8652u32; -pub const ERROR_DS_POLICY_NOT_KNOWN: WIN32_ERROR = 8618u32; -pub const ERROR_DS_PROTOCOL_ERROR: WIN32_ERROR = 8225u32; -pub const ERROR_DS_RANGE_CONSTRAINT: WIN32_ERROR = 8322u32; -pub const ERROR_DS_RDN_DOESNT_MATCH_SCHEMA: WIN32_ERROR = 8307u32; -pub const ERROR_DS_RECALCSCHEMA_FAILED: WIN32_ERROR = 8396u32; -pub const ERROR_DS_REFERRAL: WIN32_ERROR = 8235u32; -pub const ERROR_DS_REFERRAL_LIMIT_EXCEEDED: WIN32_ERROR = 8260u32; -pub const ERROR_DS_REFUSING_FSMO_ROLES: WIN32_ERROR = 8433u32; -pub const ERROR_DS_REMOTE_CROSSREF_OP_FAILED: WIN32_ERROR = 8601u32; -pub const ERROR_DS_REPLICATOR_ONLY: WIN32_ERROR = 8370u32; -pub const ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR: WIN32_ERROR = 8595u32; -pub const ERROR_DS_REPL_LIFETIME_EXCEEDED: WIN32_ERROR = 8614u32; -pub const ERROR_DS_RESERVED_LINK_ID: WIN32_ERROR = 8576u32; -pub const ERROR_DS_RESERVED_MAPI_ID: WIN32_ERROR = 8631u32; -pub const ERROR_DS_RIDMGR_DISABLED: WIN32_ERROR = 8263u32; -pub const ERROR_DS_RIDMGR_INIT_ERROR: WIN32_ERROR = 8211u32; -pub const ERROR_DS_ROLE_NOT_VERIFIED: WIN32_ERROR = 8610u32; -pub const ERROR_DS_ROOT_CANT_BE_SUBREF: WIN32_ERROR = 8326u32; -pub const ERROR_DS_ROOT_MUST_BE_NC: WIN32_ERROR = 8301u32; -pub const ERROR_DS_ROOT_REQUIRES_CLASS_TOP: WIN32_ERROR = 8432u32; -pub const ERROR_DS_SAM_INIT_FAILURE: WIN32_ERROR = 8504u32; -pub const ERROR_DS_SAM_INIT_FAILURE_CONSOLE: WIN32_ERROR = 8562u32; -pub const ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY: WIN32_ERROR = 8530u32; -pub const ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD: WIN32_ERROR = 8529u32; -pub const ERROR_DS_SCHEMA_ALLOC_FAILED: WIN32_ERROR = 8415u32; -pub const ERROR_DS_SCHEMA_NOT_LOADED: WIN32_ERROR = 8414u32; -pub const ERROR_DS_SCHEMA_UPDATE_DISALLOWED: WIN32_ERROR = 8509u32; -pub const ERROR_DS_SECURITY_CHECKING_ERROR: WIN32_ERROR = 8413u32; -pub const ERROR_DS_SECURITY_ILLEGAL_MODIFY: WIN32_ERROR = 8423u32; -pub const ERROR_DS_SEC_DESC_INVALID: WIN32_ERROR = 8354u32; -pub const ERROR_DS_SEC_DESC_TOO_SHORT: WIN32_ERROR = 8353u32; -pub const ERROR_DS_SEMANTIC_ATT_TEST: WIN32_ERROR = 8383u32; -pub const ERROR_DS_SENSITIVE_GROUP_VIOLATION: WIN32_ERROR = 8505u32; -pub const ERROR_DS_SERVER_DOWN: WIN32_ERROR = 8250u32; -pub const ERROR_DS_SHUTTING_DOWN: WIN32_ERROR = 8364u32; -pub const ERROR_DS_SINGLE_USER_MODE_FAILED: WIN32_ERROR = 8590u32; -pub const ERROR_DS_SINGLE_VALUE_CONSTRAINT: WIN32_ERROR = 8321u32; -pub const ERROR_DS_SIZELIMIT_EXCEEDED: WIN32_ERROR = 8227u32; -pub const ERROR_DS_SORT_CONTROL_MISSING: WIN32_ERROR = 8261u32; -pub const ERROR_DS_SOURCE_AUDITING_NOT_ENABLED: WIN32_ERROR = 8552u32; -pub const ERROR_DS_SOURCE_DOMAIN_IN_FOREST: WIN32_ERROR = 8534u32; -pub const ERROR_DS_SPN_VALUE_NOT_UNIQUE_IN_FOREST: WIN32_ERROR = 8647u32; -pub const ERROR_DS_SRC_AND_DST_NC_IDENTICAL: WIN32_ERROR = 8485u32; -pub const ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH: WIN32_ERROR = 8540u32; -pub const ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER: WIN32_ERROR = 8559u32; -pub const ERROR_DS_SRC_GUID_MISMATCH: WIN32_ERROR = 8488u32; -pub const ERROR_DS_SRC_NAME_MISMATCH: WIN32_ERROR = 8484u32; -pub const ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER: WIN32_ERROR = 8538u32; -pub const ERROR_DS_SRC_SID_EXISTS_IN_FOREST: WIN32_ERROR = 8539u32; -pub const ERROR_DS_STRING_SD_CONVERSION_FAILED: WIN32_ERROR = 8522u32; -pub const ERROR_DS_STRONG_AUTH_REQUIRED: WIN32_ERROR = 8232u32; -pub const ERROR_DS_SUBREF_MUST_HAVE_PARENT: WIN32_ERROR = 8356u32; -pub const ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD: WIN32_ERROR = 8376u32; -pub const ERROR_DS_SUB_CLS_TEST_FAIL: WIN32_ERROR = 8391u32; -pub const ERROR_DS_SYNTAX_MISMATCH: WIN32_ERROR = 8384u32; -pub const ERROR_DS_THREAD_LIMIT_EXCEEDED: WIN32_ERROR = 8587u32; -pub const ERROR_DS_TIMELIMIT_EXCEEDED: WIN32_ERROR = 8226u32; -pub const ERROR_DS_TREE_DELETE_NOT_FINISHED: WIN32_ERROR = 8397u32; -pub const ERROR_DS_UNABLE_TO_SURRENDER_ROLES: WIN32_ERROR = 8435u32; -pub const ERROR_DS_UNAVAILABLE: WIN32_ERROR = 8207u32; -pub const ERROR_DS_UNAVAILABLE_CRIT_EXTENSION: WIN32_ERROR = 8236u32; -pub const ERROR_DS_UNDELETE_SAM_VALIDATION_FAILED: WIN32_ERROR = 8645u32; -pub const ERROR_DS_UNICODEPWD_NOT_IN_QUOTES: WIN32_ERROR = 8556u32; -pub const ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER: WIN32_ERROR = 8518u32; -pub const ERROR_DS_UNKNOWN_ERROR: WIN32_ERROR = 8431u32; -pub const ERROR_DS_UNKNOWN_OPERATION: WIN32_ERROR = 8365u32; -pub const ERROR_DS_UNWILLING_TO_PERFORM: WIN32_ERROR = 8245u32; -pub const ERROR_DS_UPN_VALUE_NOT_UNIQUE_IN_FOREST: WIN32_ERROR = 8648u32; -pub const ERROR_DS_USER_BUFFER_TO_SMALL: WIN32_ERROR = 8309u32; -pub const ERROR_DS_VALUE_KEY_NOT_UNIQUE: WIN32_ERROR = 8650u32; -pub const ERROR_DS_VERSION_CHECK_FAILURE: WIN32_ERROR = 643u32; -pub const ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL: WIN32_ERROR = 8611u32; -pub const ERROR_DS_WRONG_LINKED_ATT_SYNTAX: WIN32_ERROR = 8528u32; -pub const ERROR_DS_WRONG_OM_OBJ_CLASS: WIN32_ERROR = 8476u32; -pub const ERROR_DUPLICATE_PRIVILEGES: WIN32_ERROR = 311u32; -pub const ERROR_DUPLICATE_SERVICE_NAME: WIN32_ERROR = 1078u32; -pub const ERROR_DUP_DOMAINNAME: WIN32_ERROR = 1221u32; -pub const ERROR_DUP_NAME: WIN32_ERROR = 52u32; -pub const ERROR_DYNAMIC_CODE_BLOCKED: WIN32_ERROR = 1655u32; -pub const ERROR_DYNLINK_FROM_INVALID_RING: WIN32_ERROR = 196u32; -pub const ERROR_EAS_DIDNT_FIT: WIN32_ERROR = 275u32; -pub const ERROR_EAS_NOT_SUPPORTED: WIN32_ERROR = 282u32; -pub const ERROR_EA_ACCESS_DENIED: WIN32_ERROR = 994u32; -pub const ERROR_EA_FILE_CORRUPT: WIN32_ERROR = 276u32; -pub const ERROR_EA_LIST_INCONSISTENT: WIN32_ERROR = 255u32; -pub const ERROR_EA_TABLE_FULL: WIN32_ERROR = 277u32; -pub const ERROR_EDP_DPL_POLICY_CANT_BE_SATISFIED: WIN32_ERROR = 357u32; -pub const ERROR_EDP_POLICY_DENIES_OPERATION: WIN32_ERROR = 356u32; -pub const ERROR_EFS_ALG_BLOB_TOO_BIG: WIN32_ERROR = 6013u32; -pub const ERROR_EFS_DISABLED: WIN32_ERROR = 6015u32; -pub const ERROR_EFS_SERVER_NOT_TRUSTED: WIN32_ERROR = 6011u32; -pub const ERROR_EFS_VERSION_NOT_SUPPORT: WIN32_ERROR = 6016u32; -pub const ERROR_ELEVATION_REQUIRED: WIN32_ERROR = 740u32; -pub const ERROR_ENCLAVE_FAILURE: WIN32_ERROR = 349u32; -pub const ERROR_ENCLAVE_NOT_TERMINATED: WIN32_ERROR = 814u32; -pub const ERROR_ENCLAVE_VIOLATION: WIN32_ERROR = 815u32; -pub const ERROR_ENCRYPTED_FILE_NOT_SUPPORTED: WIN32_ERROR = 489u32; -pub const ERROR_ENCRYPTED_IO_NOT_POSSIBLE: WIN32_ERROR = 808u32; -pub const ERROR_ENCRYPTING_METADATA_DISALLOWED: WIN32_ERROR = 431u32; -pub const ERROR_ENCRYPTION_DISABLED: WIN32_ERROR = 430u32; -pub const ERROR_ENCRYPTION_FAILED: WIN32_ERROR = 6000u32; -pub const ERROR_ENCRYPTION_POLICY_DENIES_OPERATION: WIN32_ERROR = 6022u32; -pub const ERROR_END_OF_MEDIA: WIN32_ERROR = 1100u32; -pub const ERROR_ENVVAR_NOT_FOUND: WIN32_ERROR = 203u32; -pub const ERROR_EOM_OVERFLOW: WIN32_ERROR = 1129u32; -pub const ERROR_ERRORS_ENCOUNTERED: WIN32_ERROR = 774u32; -pub const ERROR_EVALUATION_EXPIRATION: WIN32_ERROR = 622u32; -pub const ERROR_EVENTLOG_CANT_START: WIN32_ERROR = 1501u32; -pub const ERROR_EVENTLOG_FILE_CHANGED: WIN32_ERROR = 1503u32; -pub const ERROR_EVENTLOG_FILE_CORRUPT: WIN32_ERROR = 1500u32; -pub const ERROR_EVENT_DONE: WIN32_ERROR = 710u32; -pub const ERROR_EVENT_PENDING: WIN32_ERROR = 711u32; -pub const ERROR_EXCEPTION_IN_SERVICE: WIN32_ERROR = 1064u32; -pub const ERROR_EXCL_SEM_ALREADY_OWNED: WIN32_ERROR = 101u32; -pub const ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY: WIN32_ERROR = 217u32; -pub const ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY: WIN32_ERROR = 218u32; -pub const ERROR_EXE_MACHINE_TYPE_MISMATCH: WIN32_ERROR = 216u32; -pub const ERROR_EXE_MARKED_INVALID: WIN32_ERROR = 192u32; -pub const ERROR_EXTENDED_ERROR: WIN32_ERROR = 1208u32; -pub const ERROR_EXTERNAL_BACKING_PROVIDER_UNKNOWN: WIN32_ERROR = 343u32; -pub const ERROR_EXTERNAL_SYSKEY_NOT_SUPPORTED: WIN32_ERROR = 399u32; -pub const ERROR_EXTRANEOUS_INFORMATION: WIN32_ERROR = 677u32; -pub const ERROR_FAILED_DRIVER_ENTRY: WIN32_ERROR = 647u32; -pub const ERROR_FAILED_SERVICE_CONTROLLER_CONNECT: WIN32_ERROR = 1063u32; -pub const ERROR_FAIL_FAST_EXCEPTION: WIN32_ERROR = 1653u32; -pub const ERROR_FAIL_I24: WIN32_ERROR = 83u32; -pub const ERROR_FAIL_NOACTION_REBOOT: WIN32_ERROR = 350u32; -pub const ERROR_FAIL_RESTART: WIN32_ERROR = 352u32; -pub const ERROR_FAIL_SHUTDOWN: WIN32_ERROR = 351u32; -pub const ERROR_FATAL_APP_EXIT: WIN32_ERROR = 713u32; -pub const ERROR_FILEMARK_DETECTED: WIN32_ERROR = 1101u32; -pub const ERROR_FILENAME_EXCED_RANGE: WIN32_ERROR = 206u32; -pub const ERROR_FILE_CHECKED_OUT: WIN32_ERROR = 220u32; -pub const ERROR_FILE_CORRUPT: WIN32_ERROR = 1392u32; -pub const ERROR_FILE_ENCRYPTED: WIN32_ERROR = 6002u32; -pub const ERROR_FILE_EXISTS: WIN32_ERROR = 80u32; -pub const ERROR_FILE_HANDLE_REVOKED: WIN32_ERROR = 806u32; -pub const ERROR_FILE_INVALID: WIN32_ERROR = 1006u32; -pub const ERROR_FILE_LEVEL_TRIM_NOT_SUPPORTED: WIN32_ERROR = 326u32; -pub const ERROR_FILE_METADATA_OPTIMIZATION_IN_PROGRESS: WIN32_ERROR = 809u32; -pub const ERROR_FILE_NOT_ENCRYPTED: WIN32_ERROR = 6007u32; -pub const ERROR_FILE_NOT_FOUND: WIN32_ERROR = 2u32; -pub const ERROR_FILE_NOT_SUPPORTED: WIN32_ERROR = 425u32; -pub const ERROR_FILE_OFFLINE: WIN32_ERROR = 4350u32; -pub const ERROR_FILE_PROTECTED_UNDER_DPL: WIN32_ERROR = 406u32; -pub const ERROR_FILE_READ_ONLY: WIN32_ERROR = 6009u32; -pub const ERROR_FILE_SNAP_INVALID_PARAMETER: WIN32_ERROR = 440u32; -pub const ERROR_FILE_SNAP_IN_PROGRESS: WIN32_ERROR = 435u32; -pub const ERROR_FILE_SNAP_IO_NOT_COORDINATED: WIN32_ERROR = 438u32; -pub const ERROR_FILE_SNAP_MODIFY_NOT_SUPPORTED: WIN32_ERROR = 437u32; -pub const ERROR_FILE_SNAP_UNEXPECTED_ERROR: WIN32_ERROR = 439u32; -pub const ERROR_FILE_SNAP_USER_SECTION_NOT_SUPPORTED: WIN32_ERROR = 436u32; -pub const ERROR_FILE_SYSTEM_LIMITATION: WIN32_ERROR = 665u32; -pub const ERROR_FILE_SYSTEM_VIRTUALIZATION_BUSY: WIN32_ERROR = 371u32; -pub const ERROR_FILE_SYSTEM_VIRTUALIZATION_INVALID_OPERATION: WIN32_ERROR = 385u32; -pub const ERROR_FILE_SYSTEM_VIRTUALIZATION_METADATA_CORRUPT: WIN32_ERROR = 370u32; -pub const ERROR_FILE_SYSTEM_VIRTUALIZATION_PROVIDER_UNKNOWN: WIN32_ERROR = 372u32; -pub const ERROR_FILE_SYSTEM_VIRTUALIZATION_UNAVAILABLE: WIN32_ERROR = 369u32; -pub const ERROR_FILE_TOO_LARGE: WIN32_ERROR = 223u32; -pub const ERROR_FIRMWARE_UPDATED: WIN32_ERROR = 728u32; -pub const ERROR_FLOAT_MULTIPLE_FAULTS: WIN32_ERROR = 630u32; -pub const ERROR_FLOAT_MULTIPLE_TRAPS: WIN32_ERROR = 631u32; -pub const ERROR_FLOPPY_BAD_REGISTERS: WIN32_ERROR = 1125u32; -pub const ERROR_FLOPPY_ID_MARK_NOT_FOUND: WIN32_ERROR = 1122u32; -pub const ERROR_FLOPPY_UNKNOWN_ERROR: WIN32_ERROR = 1124u32; -pub const ERROR_FLOPPY_VOLUME: WIN32_ERROR = 584u32; -pub const ERROR_FLOPPY_WRONG_CYLINDER: WIN32_ERROR = 1123u32; -pub const ERROR_FORMS_AUTH_REQUIRED: WIN32_ERROR = 224u32; -pub const ERROR_FOUND_OUT_OF_SCOPE: WIN32_ERROR = 601u32; -pub const ERROR_FSFILTER_OP_COMPLETED_SUCCESSFULLY: WIN32_ERROR = 762u32; -pub const ERROR_FS_DRIVER_REQUIRED: WIN32_ERROR = 588u32; -pub const ERROR_FS_METADATA_INCONSISTENT: WIN32_ERROR = 510u32; -pub const ERROR_FT_DI_SCAN_REQUIRED: WIN32_ERROR = 339u32; -pub const ERROR_FT_READ_FAILURE: WIN32_ERROR = 415u32; -pub const ERROR_FT_READ_FROM_COPY_FAILURE: WIN32_ERROR = 818u32; -pub const ERROR_FT_READ_RECOVERY_FROM_BACKUP: WIN32_ERROR = 704u32; -pub const ERROR_FT_WRITE_FAILURE: WIN32_ERROR = 338u32; -pub const ERROR_FT_WRITE_RECOVERY: WIN32_ERROR = 705u32; -pub const ERROR_FULLSCREEN_MODE: WIN32_ERROR = 1007u32; -pub const ERROR_FUNCTION_FAILED: WIN32_ERROR = 1627u32; -pub const ERROR_FUNCTION_NOT_CALLED: WIN32_ERROR = 1626u32; -pub const ERROR_GDI_HANDLE_LEAK: WIN32_ERROR = 373u32; -pub const ERROR_GENERIC_NOT_MAPPED: WIN32_ERROR = 1360u32; -pub const ERROR_GEN_FAILURE: WIN32_ERROR = 31u32; -pub const ERROR_GLOBAL_ONLY_HOOK: WIN32_ERROR = 1429u32; -pub const ERROR_GRACEFUL_DISCONNECT: WIN32_ERROR = 1226u32; -pub const ERROR_GROUP_EXISTS: WIN32_ERROR = 1318u32; -pub const ERROR_GUID_SUBSTITUTION_MADE: WIN32_ERROR = 680u32; -pub const ERROR_HANDLES_CLOSED: WIN32_ERROR = 676u32; -pub const ERROR_HANDLE_DISK_FULL: WIN32_ERROR = 39u32; -pub const ERROR_HANDLE_EOF: WIN32_ERROR = 38u32; -pub const ERROR_HANDLE_REVOKED: WIN32_ERROR = 811u32; -pub const ERROR_HAS_SYSTEM_CRITICAL_FILES: WIN32_ERROR = 488u32; -pub const ERROR_HIBERNATED: WIN32_ERROR = 726u32; -pub const ERROR_HIBERNATION_FAILURE: WIN32_ERROR = 656u32; -pub const ERROR_HOOK_NEEDS_HMOD: WIN32_ERROR = 1428u32; -pub const ERROR_HOOK_NOT_INSTALLED: WIN32_ERROR = 1431u32; -pub const ERROR_HOOK_TYPE_NOT_ALLOWED: WIN32_ERROR = 1458u32; -pub const ERROR_HOST_DOWN: WIN32_ERROR = 1256u32; -pub const ERROR_HOST_UNREACHABLE: WIN32_ERROR = 1232u32; -pub const ERROR_HOTKEY_ALREADY_REGISTERED: WIN32_ERROR = 1409u32; -pub const ERROR_HOTKEY_NOT_REGISTERED: WIN32_ERROR = 1419u32; -pub const ERROR_HWNDS_HAVE_DIFF_PARENT: WIN32_ERROR = 1441u32; -pub const ERROR_ILLEGAL_CHARACTER: WIN32_ERROR = 582u32; -pub const ERROR_ILLEGAL_DLL_RELOCATION: WIN32_ERROR = 623u32; -pub const ERROR_ILLEGAL_ELEMENT_ADDRESS: WIN32_ERROR = 1162u32; -pub const ERROR_ILLEGAL_FLOAT_CONTEXT: WIN32_ERROR = 579u32; -pub const ERROR_ILL_FORMED_PASSWORD: WIN32_ERROR = 1324u32; -pub const ERROR_IMAGE_AT_DIFFERENT_BASE: WIN32_ERROR = 807u32; -pub const ERROR_IMAGE_MACHINE_TYPE_MISMATCH: WIN32_ERROR = 706u32; -pub const ERROR_IMAGE_MACHINE_TYPE_MISMATCH_EXE: WIN32_ERROR = 720u32; -pub const ERROR_IMAGE_NOT_AT_BASE: WIN32_ERROR = 700u32; -pub const ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT: WIN32_ERROR = 308u32; -pub const ERROR_IMPLEMENTATION_LIMIT: WIN32_ERROR = 1292u32; -pub const ERROR_INCOMPATIBLE_SERVICE_PRIVILEGE: WIN32_ERROR = 1297u32; -pub const ERROR_INCOMPATIBLE_SERVICE_SID_TYPE: WIN32_ERROR = 1290u32; -pub const ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING: WIN32_ERROR = 304u32; -pub const ERROR_INCORRECT_ACCOUNT_TYPE: WIN32_ERROR = 8646u32; -pub const ERROR_INCORRECT_ADDRESS: WIN32_ERROR = 1241u32; -pub const ERROR_INCORRECT_SIZE: WIN32_ERROR = 1462u32; -pub const ERROR_INDEX_ABSENT: WIN32_ERROR = 1611u32; -pub const ERROR_INDEX_OUT_OF_BOUNDS: WIN32_ERROR = 474u32; -pub const ERROR_INFLOOP_IN_RELOC_CHAIN: WIN32_ERROR = 202u32; -pub const ERROR_INSTALL_ALREADY_RUNNING: WIN32_ERROR = 1618u32; -pub const ERROR_INSTALL_FAILURE: WIN32_ERROR = 1603u32; -pub const ERROR_INSTALL_LANGUAGE_UNSUPPORTED: WIN32_ERROR = 1623u32; -pub const ERROR_INSTALL_LOG_FAILURE: WIN32_ERROR = 1622u32; -pub const ERROR_INSTALL_NOTUSED: WIN32_ERROR = 1634u32; -pub const ERROR_INSTALL_PACKAGE_INVALID: WIN32_ERROR = 1620u32; -pub const ERROR_INSTALL_PACKAGE_OPEN_FAILED: WIN32_ERROR = 1619u32; -pub const ERROR_INSTALL_PACKAGE_REJECTED: WIN32_ERROR = 1625u32; -pub const ERROR_INSTALL_PACKAGE_VERSION: WIN32_ERROR = 1613u32; -pub const ERROR_INSTALL_PLATFORM_UNSUPPORTED: WIN32_ERROR = 1633u32; -pub const ERROR_INSTALL_REJECTED: WIN32_ERROR = 1654u32; -pub const ERROR_INSTALL_REMOTE_DISALLOWED: WIN32_ERROR = 1640u32; -pub const ERROR_INSTALL_REMOTE_PROHIBITED: WIN32_ERROR = 1645u32; -pub const ERROR_INSTALL_SERVICE_FAILURE: WIN32_ERROR = 1601u32; -pub const ERROR_INSTALL_SERVICE_SAFEBOOT: WIN32_ERROR = 1652u32; -pub const ERROR_INSTALL_SOURCE_ABSENT: WIN32_ERROR = 1612u32; -pub const ERROR_INSTALL_SUSPEND: WIN32_ERROR = 1604u32; -pub const ERROR_INSTALL_TEMP_UNWRITABLE: WIN32_ERROR = 1632u32; -pub const ERROR_INSTALL_TRANSFORM_FAILURE: WIN32_ERROR = 1624u32; -pub const ERROR_INSTALL_TRANSFORM_REJECTED: WIN32_ERROR = 1644u32; -pub const ERROR_INSTALL_UI_FAILURE: WIN32_ERROR = 1621u32; -pub const ERROR_INSTALL_USEREXIT: WIN32_ERROR = 1602u32; -pub const ERROR_INSTRUCTION_MISALIGNMENT: WIN32_ERROR = 549u32; -pub const ERROR_INSUFFICIENT_BUFFER: WIN32_ERROR = 122u32; -pub const ERROR_INSUFFICIENT_LOGON_INFO: WIN32_ERROR = 608u32; -pub const ERROR_INSUFFICIENT_POWER: WIN32_ERROR = 639u32; -pub const ERROR_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE: WIN32_ERROR = 781u32; -pub const ERROR_INSUFFICIENT_VIRTUAL_ADDR_RESOURCES: WIN32_ERROR = 473u32; -pub const ERROR_INTERMIXED_KERNEL_EA_OPERATION: WIN32_ERROR = 324u32; -pub const ERROR_INTERNAL_DB_CORRUPTION: WIN32_ERROR = 1358u32; -pub const ERROR_INTERNAL_DB_ERROR: WIN32_ERROR = 1383u32; -pub const ERROR_INTERNAL_ERROR: WIN32_ERROR = 1359u32; -pub const ERROR_INTERRUPT_STILL_CONNECTED: WIN32_ERROR = 764u32; -pub const ERROR_INTERRUPT_VECTOR_ALREADY_CONNECTED: WIN32_ERROR = 763u32; -pub const ERROR_INVALID_ACCEL_HANDLE: WIN32_ERROR = 1403u32; -pub const ERROR_INVALID_ACCESS: WIN32_ERROR = 12u32; -pub const ERROR_INVALID_ACCOUNT_NAME: WIN32_ERROR = 1315u32; -pub const ERROR_INVALID_ACE_CONDITION: WIN32_ERROR = 805u32; -pub const ERROR_INVALID_ACL: WIN32_ERROR = 1336u32; -pub const ERROR_INVALID_ADDRESS: WIN32_ERROR = 487u32; -pub const ERROR_INVALID_AT_INTERRUPT_TIME: WIN32_ERROR = 104u32; -pub const ERROR_INVALID_BLOCK: WIN32_ERROR = 9u32; -pub const ERROR_INVALID_BLOCK_LENGTH: WIN32_ERROR = 1106u32; -pub const ERROR_INVALID_CAP: WIN32_ERROR = 320u32; -pub const ERROR_INVALID_CATEGORY: WIN32_ERROR = 117u32; -pub const ERROR_INVALID_COMBOBOX_MESSAGE: WIN32_ERROR = 1422u32; -pub const ERROR_INVALID_COMMAND_LINE: WIN32_ERROR = 1639u32; -pub const ERROR_INVALID_COMPUTERNAME: WIN32_ERROR = 1210u32; -pub const ERROR_INVALID_CRUNTIME_PARAMETER: WIN32_ERROR = 1288u32; -pub const ERROR_INVALID_CURSOR_HANDLE: WIN32_ERROR = 1402u32; -pub const ERROR_INVALID_DATA: WIN32_ERROR = 13u32; -pub const ERROR_INVALID_DATATYPE: WIN32_ERROR = 1804u32; -pub const ERROR_INVALID_DEVICE_OBJECT_PARAMETER: WIN32_ERROR = 650u32; -pub const ERROR_INVALID_DLL: WIN32_ERROR = 1154u32; -pub const ERROR_INVALID_DOMAINNAME: WIN32_ERROR = 1212u32; -pub const ERROR_INVALID_DOMAIN_ROLE: WIN32_ERROR = 1354u32; -pub const ERROR_INVALID_DOMAIN_STATE: WIN32_ERROR = 1353u32; -pub const ERROR_INVALID_DRIVE: WIN32_ERROR = 15u32; -pub const ERROR_INVALID_DWP_HANDLE: WIN32_ERROR = 1405u32; -pub const ERROR_INVALID_EA_HANDLE: WIN32_ERROR = 278u32; -pub const ERROR_INVALID_EA_NAME: WIN32_ERROR = 254u32; -pub const ERROR_INVALID_EDIT_HEIGHT: WIN32_ERROR = 1424u32; -pub const ERROR_INVALID_ENVIRONMENT: WIN32_ERROR = 1805u32; -pub const ERROR_INVALID_EVENTNAME: WIN32_ERROR = 1211u32; -pub const ERROR_INVALID_EVENT_COUNT: WIN32_ERROR = 151u32; -pub const ERROR_INVALID_EXCEPTION_HANDLER: WIN32_ERROR = 310u32; -pub const ERROR_INVALID_EXE_SIGNATURE: WIN32_ERROR = 191u32; -pub const ERROR_INVALID_FIELD: WIN32_ERROR = 1616u32; -pub const ERROR_INVALID_FIELD_IN_PARAMETER_LIST: WIN32_ERROR = 328u32; -pub const ERROR_INVALID_FILTER_PROC: WIN32_ERROR = 1427u32; -pub const ERROR_INVALID_FLAGS: WIN32_ERROR = 1004u32; -pub const ERROR_INVALID_FLAG_NUMBER: WIN32_ERROR = 186u32; -pub const ERROR_INVALID_FORM_NAME: WIN32_ERROR = 1902u32; -pub const ERROR_INVALID_FORM_SIZE: WIN32_ERROR = 1903u32; -pub const ERROR_INVALID_FUNCTION: WIN32_ERROR = 1u32; -pub const ERROR_INVALID_GROUPNAME: WIN32_ERROR = 1209u32; -pub const ERROR_INVALID_GROUP_ATTRIBUTES: WIN32_ERROR = 1345u32; -pub const ERROR_INVALID_GW_COMMAND: WIN32_ERROR = 1443u32; -pub const ERROR_INVALID_HANDLE: WIN32_ERROR = 6u32; -pub const ERROR_INVALID_HANDLE_STATE: WIN32_ERROR = 1609u32; -pub const ERROR_INVALID_HOOK_FILTER: WIN32_ERROR = 1426u32; -pub const ERROR_INVALID_HOOK_HANDLE: WIN32_ERROR = 1404u32; -pub const ERROR_INVALID_HW_PROFILE: WIN32_ERROR = 619u32; -pub const ERROR_INVALID_ICON_HANDLE: WIN32_ERROR = 1414u32; -pub const ERROR_INVALID_ID_AUTHORITY: WIN32_ERROR = 1343u32; -pub const ERROR_INVALID_IMAGE_HASH: WIN32_ERROR = 577u32; -pub const ERROR_INVALID_IMPORT_OF_NON_DLL: WIN32_ERROR = 1276u32; -pub const ERROR_INVALID_INDEX: WIN32_ERROR = 1413u32; -pub const ERROR_INVALID_KERNEL_INFO_VERSION: WIN32_ERROR = 340u32; -pub const ERROR_INVALID_KEYBOARD_HANDLE: WIN32_ERROR = 1457u32; -pub const ERROR_INVALID_LABEL: WIN32_ERROR = 1299u32; -pub const ERROR_INVALID_LB_MESSAGE: WIN32_ERROR = 1432u32; -pub const ERROR_INVALID_LDT_DESCRIPTOR: WIN32_ERROR = 564u32; -pub const ERROR_INVALID_LDT_OFFSET: WIN32_ERROR = 563u32; -pub const ERROR_INVALID_LDT_SIZE: WIN32_ERROR = 561u32; -pub const ERROR_INVALID_LEVEL: WIN32_ERROR = 124u32; -pub const ERROR_INVALID_LIST_FORMAT: WIN32_ERROR = 153u32; -pub const ERROR_INVALID_LOCK_RANGE: WIN32_ERROR = 307u32; -pub const ERROR_INVALID_LOGON_HOURS: WIN32_ERROR = 1328u32; -pub const ERROR_INVALID_LOGON_TYPE: WIN32_ERROR = 1367u32; -pub const ERROR_INVALID_MEMBER: WIN32_ERROR = 1388u32; -pub const ERROR_INVALID_MENU_HANDLE: WIN32_ERROR = 1401u32; -pub const ERROR_INVALID_MESSAGE: WIN32_ERROR = 1002u32; -pub const ERROR_INVALID_MESSAGEDEST: WIN32_ERROR = 1218u32; -pub const ERROR_INVALID_MESSAGENAME: WIN32_ERROR = 1217u32; -pub const ERROR_INVALID_MINALLOCSIZE: WIN32_ERROR = 195u32; -pub const ERROR_INVALID_MODULETYPE: WIN32_ERROR = 190u32; -pub const ERROR_INVALID_MONITOR_HANDLE: WIN32_ERROR = 1461u32; -pub const ERROR_INVALID_MSGBOX_STYLE: WIN32_ERROR = 1438u32; -pub const ERROR_INVALID_NAME: WIN32_ERROR = 123u32; -pub const ERROR_INVALID_NETNAME: WIN32_ERROR = 1214u32; -pub const ERROR_INVALID_OPLOCK_PROTOCOL: WIN32_ERROR = 301u32; -pub const ERROR_INVALID_ORDINAL: WIN32_ERROR = 182u32; -pub const ERROR_INVALID_OWNER: WIN32_ERROR = 1307u32; -pub const ERROR_INVALID_PACKAGE_SID_LENGTH: WIN32_ERROR = 4253u32; -pub const ERROR_INVALID_PARAMETER: WIN32_ERROR = 87u32; -pub const ERROR_INVALID_PASSWORD: WIN32_ERROR = 86u32; -pub const ERROR_INVALID_PASSWORDNAME: WIN32_ERROR = 1216u32; -pub const ERROR_INVALID_PATCH_XML: WIN32_ERROR = 1650u32; -pub const ERROR_INVALID_PEP_INFO_VERSION: WIN32_ERROR = 341u32; -pub const ERROR_INVALID_PLUGPLAY_DEVICE_PATH: WIN32_ERROR = 620u32; -pub const ERROR_INVALID_PORT_ATTRIBUTES: WIN32_ERROR = 545u32; -pub const ERROR_INVALID_PRIMARY_GROUP: WIN32_ERROR = 1308u32; -pub const ERROR_INVALID_PRINTER_COMMAND: WIN32_ERROR = 1803u32; -pub const ERROR_INVALID_PRINTER_NAME: WIN32_ERROR = 1801u32; -pub const ERROR_INVALID_PRINTER_STATE: WIN32_ERROR = 1906u32; -pub const ERROR_INVALID_PRIORITY: WIN32_ERROR = 1800u32; -pub const ERROR_INVALID_QUOTA_LOWER: WIN32_ERROR = 547u32; -pub const ERROR_INVALID_REPARSE_DATA: WIN32_ERROR = 4392u32; -pub const ERROR_INVALID_SCROLLBAR_RANGE: WIN32_ERROR = 1448u32; -pub const ERROR_INVALID_SECURITY_DESCR: WIN32_ERROR = 1338u32; -pub const ERROR_INVALID_SEGDPL: WIN32_ERROR = 198u32; -pub const ERROR_INVALID_SEGMENT_NUMBER: WIN32_ERROR = 180u32; -pub const ERROR_INVALID_SEPARATOR_FILE: WIN32_ERROR = 1799u32; -pub const ERROR_INVALID_SERVER_STATE: WIN32_ERROR = 1352u32; -pub const ERROR_INVALID_SERVICENAME: WIN32_ERROR = 1213u32; -pub const ERROR_INVALID_SERVICE_ACCOUNT: WIN32_ERROR = 1057u32; -pub const ERROR_INVALID_SERVICE_CONTROL: WIN32_ERROR = 1052u32; -pub const ERROR_INVALID_SERVICE_LOCK: WIN32_ERROR = 1071u32; -pub const ERROR_INVALID_SHARENAME: WIN32_ERROR = 1215u32; -pub const ERROR_INVALID_SHOWWIN_COMMAND: WIN32_ERROR = 1449u32; -pub const ERROR_INVALID_SID: WIN32_ERROR = 1337u32; -pub const ERROR_INVALID_SIGNAL_NUMBER: WIN32_ERROR = 209u32; -pub const ERROR_INVALID_SPI_VALUE: WIN32_ERROR = 1439u32; -pub const ERROR_INVALID_STACKSEG: WIN32_ERROR = 189u32; -pub const ERROR_INVALID_STARTING_CODESEG: WIN32_ERROR = 188u32; -pub const ERROR_INVALID_SUB_AUTHORITY: WIN32_ERROR = 1335u32; -pub const ERROR_INVALID_TABLE: WIN32_ERROR = 1628u32; -pub const ERROR_INVALID_TARGET_HANDLE: WIN32_ERROR = 114u32; -pub const ERROR_INVALID_TASK_INDEX: WIN32_ERROR = 1551u32; -pub const ERROR_INVALID_TASK_NAME: WIN32_ERROR = 1550u32; -pub const ERROR_INVALID_THREAD_ID: WIN32_ERROR = 1444u32; -pub const ERROR_INVALID_TIME: WIN32_ERROR = 1901u32; -pub const ERROR_INVALID_TOKEN: WIN32_ERROR = 315u32; -pub const ERROR_INVALID_UNWIND_TARGET: WIN32_ERROR = 544u32; -pub const ERROR_INVALID_USER_BUFFER: WIN32_ERROR = 1784u32; -pub const ERROR_INVALID_USER_PRINCIPAL_NAME: WIN32_ERROR = 8636u32; -pub const ERROR_INVALID_VARIANT: WIN32_ERROR = 604u32; -pub const ERROR_INVALID_VERIFY_SWITCH: WIN32_ERROR = 118u32; -pub const ERROR_INVALID_WINDOW_HANDLE: WIN32_ERROR = 1400u32; -pub const ERROR_INVALID_WORKSTATION: WIN32_ERROR = 1329u32; -pub const ERROR_IOPL_NOT_ENABLED: WIN32_ERROR = 197u32; -pub const ERROR_IO_DEVICE: WIN32_ERROR = 1117u32; -pub const ERROR_IO_INCOMPLETE: WIN32_ERROR = 996u32; -pub const ERROR_IO_PENDING: WIN32_ERROR = 997u32; -pub const ERROR_IO_PRIVILEGE_FAILED: WIN32_ERROR = 571u32; -pub const ERROR_IO_REISSUE_AS_CACHED: WIN32_ERROR = 3950u32; -pub const ERROR_IPSEC_IKE_TIMED_OUT: WIN32_ERROR = 13805u32; -pub const ERROR_IP_ADDRESS_CONFLICT1: WIN32_ERROR = 611u32; -pub const ERROR_IP_ADDRESS_CONFLICT2: WIN32_ERROR = 612u32; -pub const ERROR_IRQ_BUSY: WIN32_ERROR = 1119u32; -pub const ERROR_IS_JOINED: WIN32_ERROR = 134u32; -pub const ERROR_IS_JOIN_PATH: WIN32_ERROR = 147u32; -pub const ERROR_IS_JOIN_TARGET: WIN32_ERROR = 133u32; -pub const ERROR_IS_SUBSTED: WIN32_ERROR = 135u32; -pub const ERROR_IS_SUBST_PATH: WIN32_ERROR = 146u32; -pub const ERROR_IS_SUBST_TARGET: WIN32_ERROR = 149u32; -pub const ERROR_ITERATED_DATA_EXCEEDS_64k: WIN32_ERROR = 194u32; -pub const ERROR_JOB_NO_CONTAINER: WIN32_ERROR = 1505u32; -pub const ERROR_JOIN_TO_JOIN: WIN32_ERROR = 138u32; -pub const ERROR_JOIN_TO_SUBST: WIN32_ERROR = 140u32; -pub const ERROR_JOURNAL_DELETE_IN_PROGRESS: WIN32_ERROR = 1178u32; -pub const ERROR_JOURNAL_ENTRY_DELETED: WIN32_ERROR = 1181u32; -pub const ERROR_JOURNAL_HOOK_SET: WIN32_ERROR = 1430u32; -pub const ERROR_JOURNAL_NOT_ACTIVE: WIN32_ERROR = 1179u32; -pub const ERROR_KERNEL_APC: WIN32_ERROR = 738u32; -pub const ERROR_KEY_DELETED: WIN32_ERROR = 1018u32; -pub const ERROR_KEY_HAS_CHILDREN: WIN32_ERROR = 1020u32; -pub const ERROR_KM_DRIVER_BLOCKED: WIN32_ERROR = 1930u32; -pub const ERROR_LABEL_TOO_LONG: WIN32_ERROR = 154u32; -pub const ERROR_LAST_ADMIN: WIN32_ERROR = 1322u32; -pub const ERROR_LB_WITHOUT_TABSTOPS: WIN32_ERROR = 1434u32; -pub const ERROR_LICENSE_QUOTA_EXCEEDED: WIN32_ERROR = 1395u32; -pub const ERROR_LINUX_SUBSYSTEM_NOT_PRESENT: WIN32_ERROR = 414u32; -pub const ERROR_LINUX_SUBSYSTEM_UPDATE_REQUIRED: WIN32_ERROR = 444u32; -pub const ERROR_LISTBOX_ID_NOT_FOUND: WIN32_ERROR = 1416u32; -pub const ERROR_LM_CROSS_ENCRYPTION_REQUIRED: WIN32_ERROR = 1390u32; -pub const ERROR_LOCAL_POLICY_MODIFICATION_NOT_SUPPORTED: WIN32_ERROR = 8653u32; -pub const ERROR_LOCAL_USER_SESSION_KEY: WIN32_ERROR = 1303u32; -pub const ERROR_LOCKED: WIN32_ERROR = 212u32; -pub const ERROR_LOCK_FAILED: WIN32_ERROR = 167u32; -pub const ERROR_LOCK_VIOLATION: WIN32_ERROR = 33u32; -pub const ERROR_LOGIN_TIME_RESTRICTION: WIN32_ERROR = 1239u32; -pub const ERROR_LOGIN_WKSTA_RESTRICTION: WIN32_ERROR = 1240u32; -pub const ERROR_LOGON_FAILURE: WIN32_ERROR = 1326u32; -pub const ERROR_LOGON_NOT_GRANTED: WIN32_ERROR = 1380u32; -pub const ERROR_LOGON_SERVER_CONFLICT: WIN32_ERROR = 568u32; -pub const ERROR_LOGON_SESSION_COLLISION: WIN32_ERROR = 1366u32; -pub const ERROR_LOGON_SESSION_EXISTS: WIN32_ERROR = 1363u32; -pub const ERROR_LOGON_TYPE_NOT_GRANTED: WIN32_ERROR = 1385u32; -pub const ERROR_LOG_FILE_FULL: WIN32_ERROR = 1502u32; -pub const ERROR_LOG_HARD_ERROR: WIN32_ERROR = 718u32; -pub const ERROR_LONGJUMP: WIN32_ERROR = 682u32; -pub const ERROR_LOST_MODE_LOGON_RESTRICTION: WIN32_ERROR = 1939u32; -pub const ERROR_LOST_WRITEBEHIND_DATA: WIN32_ERROR = 596u32; -pub const ERROR_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR: WIN32_ERROR = 790u32; -pub const ERROR_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED: WIN32_ERROR = 788u32; -pub const ERROR_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR: WIN32_ERROR = 789u32; -pub const ERROR_LUIDS_EXHAUSTED: WIN32_ERROR = 1334u32; -pub const ERROR_MACHINE_LOCKED: WIN32_ERROR = 1271u32; -pub const ERROR_MAGAZINE_NOT_PRESENT: WIN32_ERROR = 1163u32; -pub const ERROR_MAPPED_ALIGNMENT: WIN32_ERROR = 1132u32; -pub const ERROR_MARKED_TO_DISALLOW_WRITES: WIN32_ERROR = 348u32; -pub const ERROR_MARSHALL_OVERFLOW: WIN32_ERROR = 603u32; -pub const ERROR_MAX_SESSIONS_REACHED: WIN32_ERROR = 353u32; -pub const ERROR_MAX_THRDS_REACHED: WIN32_ERROR = 164u32; -pub const ERROR_MCA_EXCEPTION: WIN32_ERROR = 784u32; -pub const ERROR_MCA_OCCURED: WIN32_ERROR = 651u32; -pub const ERROR_MEDIA_CHANGED: WIN32_ERROR = 1110u32; -pub const ERROR_MEDIA_CHECK: WIN32_ERROR = 679u32; -pub const ERROR_MEMBERS_PRIMARY_GROUP: WIN32_ERROR = 1374u32; -pub const ERROR_MEMBER_IN_ALIAS: WIN32_ERROR = 1378u32; -pub const ERROR_MEMBER_IN_GROUP: WIN32_ERROR = 1320u32; -pub const ERROR_MEMBER_NOT_IN_ALIAS: WIN32_ERROR = 1377u32; -pub const ERROR_MEMBER_NOT_IN_GROUP: WIN32_ERROR = 1321u32; -pub const ERROR_MEMORY_HARDWARE: WIN32_ERROR = 779u32; -pub const ERROR_MENU_ITEM_NOT_FOUND: WIN32_ERROR = 1456u32; -pub const ERROR_MESSAGE_SYNC_ONLY: WIN32_ERROR = 1159u32; -pub const ERROR_META_EXPANSION_TOO_LONG: WIN32_ERROR = 208u32; -pub const ERROR_MISSING_SYSTEMFILE: WIN32_ERROR = 573u32; -pub const ERROR_MOD_NOT_FOUND: WIN32_ERROR = 126u32; -pub const ERROR_MORE_DATA: WIN32_ERROR = 234u32; -pub const ERROR_MORE_WRITES: WIN32_ERROR = 1120u32; -pub const ERROR_MOUNT_POINT_NOT_RESOLVED: WIN32_ERROR = 649u32; -pub const ERROR_MP_PROCESSOR_MISMATCH: WIN32_ERROR = 725u32; -pub const ERROR_MR_MID_NOT_FOUND: WIN32_ERROR = 317u32; -pub const ERROR_MULTIPLE_FAULT_VIOLATION: WIN32_ERROR = 640u32; -pub const ERROR_MUTANT_LIMIT_EXCEEDED: WIN32_ERROR = 587u32; -pub const ERROR_MUTUAL_AUTH_FAILED: WIN32_ERROR = 1397u32; -pub const ERROR_NEGATIVE_SEEK: WIN32_ERROR = 131u32; -pub const ERROR_NESTING_NOT_ALLOWED: WIN32_ERROR = 215u32; -pub const ERROR_NETLOGON_NOT_STARTED: WIN32_ERROR = 1792u32; -pub const ERROR_NETNAME_DELETED: WIN32_ERROR = 64u32; -pub const ERROR_NETWORK_ACCESS_DENIED: WIN32_ERROR = 65u32; -pub const ERROR_NETWORK_ACCESS_DENIED_EDP: WIN32_ERROR = 354u32; -pub const ERROR_NETWORK_BUSY: WIN32_ERROR = 54u32; -pub const ERROR_NETWORK_UNREACHABLE: WIN32_ERROR = 1231u32; -pub const ERROR_NET_OPEN_FAILED: WIN32_ERROR = 570u32; -pub const ERROR_NET_WRITE_FAULT: WIN32_ERROR = 88u32; -pub const ERROR_NOACCESS: WIN32_ERROR = 998u32; -pub const ERROR_NOINTERFACE: WIN32_ERROR = 632u32; -pub const ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT: WIN32_ERROR = 1807u32; -pub const ERROR_NOLOGON_SERVER_TRUST_ACCOUNT: WIN32_ERROR = 1809u32; -pub const ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT: WIN32_ERROR = 1808u32; -pub const ERROR_NONE_MAPPED: WIN32_ERROR = 1332u32; -pub const ERROR_NONPAGED_SYSTEM_RESOURCES: WIN32_ERROR = 1451u32; -pub const ERROR_NON_ACCOUNT_SID: WIN32_ERROR = 1257u32; -pub const ERROR_NON_DOMAIN_SID: WIN32_ERROR = 1258u32; -pub const ERROR_NON_MDICHILD_WINDOW: WIN32_ERROR = 1445u32; -pub const ERROR_NOTHING_TO_TERMINATE: WIN32_ERROR = 758u32; -pub const ERROR_NOTIFICATION_GUID_ALREADY_DEFINED: WIN32_ERROR = 309u32; -pub const ERROR_NOTIFY_CLEANUP: WIN32_ERROR = 745u32; -pub const ERROR_NOTIFY_ENUM_DIR: WIN32_ERROR = 1022u32; -pub const ERROR_NOT_ALLOWED_ON_SYSTEM_FILE: WIN32_ERROR = 313u32; -pub const ERROR_NOT_ALL_ASSIGNED: WIN32_ERROR = 1300u32; -pub const ERROR_NOT_APPCONTAINER: WIN32_ERROR = 4250u32; -pub const ERROR_NOT_AUTHENTICATED: WIN32_ERROR = 1244u32; -pub const ERROR_NOT_A_CLOUD_FILE: WIN32_ERROR = 376u32; -pub const ERROR_NOT_A_CLOUD_SYNC_ROOT: WIN32_ERROR = 405u32; -pub const ERROR_NOT_A_DAX_VOLUME: WIN32_ERROR = 420u32; -pub const ERROR_NOT_A_REPARSE_POINT: WIN32_ERROR = 4390u32; -pub const ERROR_NOT_CAPABLE: WIN32_ERROR = 775u32; -pub const ERROR_NOT_CHILD_WINDOW: WIN32_ERROR = 1442u32; -pub const ERROR_NOT_CONNECTED: WIN32_ERROR = 2250u32; -pub const ERROR_NOT_CONTAINER: WIN32_ERROR = 1207u32; -pub const ERROR_NOT_DAX_MAPPABLE: WIN32_ERROR = 421u32; -pub const ERROR_NOT_DOS_DISK: WIN32_ERROR = 26u32; -pub const ERROR_NOT_ENOUGH_MEMORY: WIN32_ERROR = 8u32; -pub const ERROR_NOT_ENOUGH_QUOTA: WIN32_ERROR = 1816u32; -pub const ERROR_NOT_ENOUGH_SERVER_MEMORY: WIN32_ERROR = 1130u32; -pub const ERROR_NOT_EXPORT_FORMAT: WIN32_ERROR = 6008u32; -pub const ERROR_NOT_FOUND: WIN32_ERROR = 1168u32; -pub const ERROR_NOT_GUI_PROCESS: WIN32_ERROR = 1471u32; -pub const ERROR_NOT_JOINED: WIN32_ERROR = 136u32; -pub const ERROR_NOT_LOCKED: WIN32_ERROR = 158u32; -pub const ERROR_NOT_LOGGED_ON: WIN32_ERROR = 1245u32; -pub const ERROR_NOT_LOGON_PROCESS: WIN32_ERROR = 1362u32; -pub const ERROR_NOT_OWNER: WIN32_ERROR = 288u32; -pub const ERROR_NOT_READY: WIN32_ERROR = 21u32; -pub const ERROR_NOT_READ_FROM_COPY: WIN32_ERROR = 337u32; -pub const ERROR_NOT_REDUNDANT_STORAGE: WIN32_ERROR = 333u32; -pub const ERROR_NOT_REGISTRY_FILE: WIN32_ERROR = 1017u32; -pub const ERROR_NOT_SAFEBOOT_SERVICE: WIN32_ERROR = 1084u32; -pub const ERROR_NOT_SAFE_MODE_DRIVER: WIN32_ERROR = 646u32; -pub const ERROR_NOT_SAME_DEVICE: WIN32_ERROR = 17u32; -pub const ERROR_NOT_SAME_OBJECT: WIN32_ERROR = 1656u32; -pub const ERROR_NOT_SUBSTED: WIN32_ERROR = 137u32; -pub const ERROR_NOT_SUPPORTED: WIN32_ERROR = 50u32; -pub const ERROR_NOT_SUPPORTED_IN_APPCONTAINER: WIN32_ERROR = 4252u32; -pub const ERROR_NOT_SUPPORTED_ON_DAX: WIN32_ERROR = 360u32; -pub const ERROR_NOT_SUPPORTED_ON_SBS: WIN32_ERROR = 1254u32; -pub const ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER: WIN32_ERROR = 8584u32; -pub const ERROR_NOT_SUPPORTED_WITH_AUDITING: WIN32_ERROR = 499u32; -pub const ERROR_NOT_SUPPORTED_WITH_BTT: WIN32_ERROR = 429u32; -pub const ERROR_NOT_SUPPORTED_WITH_BYPASSIO: WIN32_ERROR = 493u32; -pub const ERROR_NOT_SUPPORTED_WITH_CACHED_HANDLE: WIN32_ERROR = 509u32; -pub const ERROR_NOT_SUPPORTED_WITH_COMPRESSION: WIN32_ERROR = 496u32; -pub const ERROR_NOT_SUPPORTED_WITH_DEDUPLICATION: WIN32_ERROR = 498u32; -pub const ERROR_NOT_SUPPORTED_WITH_ENCRYPTION: WIN32_ERROR = 495u32; -pub const ERROR_NOT_SUPPORTED_WITH_MONITORING: WIN32_ERROR = 503u32; -pub const ERROR_NOT_SUPPORTED_WITH_REPLICATION: WIN32_ERROR = 497u32; -pub const ERROR_NOT_SUPPORTED_WITH_SNAPSHOT: WIN32_ERROR = 504u32; -pub const ERROR_NOT_SUPPORTED_WITH_VIRTUALIZATION: WIN32_ERROR = 505u32; -pub const ERROR_NOT_TINY_STREAM: WIN32_ERROR = 598u32; -pub const ERROR_NO_ACE_CONDITION: WIN32_ERROR = 804u32; -pub const ERROR_NO_ASSOCIATION: WIN32_ERROR = 1155u32; -pub const ERROR_NO_BYPASSIO_DRIVER_SUPPORT: WIN32_ERROR = 494u32; -pub const ERROR_NO_CALLBACK_ACTIVE: WIN32_ERROR = 614u32; -pub const ERROR_NO_DATA: WIN32_ERROR = 232u32; -pub const ERROR_NO_DATA_DETECTED: WIN32_ERROR = 1104u32; -pub const ERROR_NO_EFS: WIN32_ERROR = 6004u32; -pub const ERROR_NO_EVENT_PAIR: WIN32_ERROR = 580u32; -pub const ERROR_NO_GUID_TRANSLATION: WIN32_ERROR = 560u32; -pub const ERROR_NO_IMPERSONATION_TOKEN: WIN32_ERROR = 1309u32; -pub const ERROR_NO_INHERITANCE: WIN32_ERROR = 1391u32; -pub const ERROR_NO_LOGON_SERVERS: WIN32_ERROR = 1311u32; -pub const ERROR_NO_LOG_SPACE: WIN32_ERROR = 1019u32; -pub const ERROR_NO_MATCH: WIN32_ERROR = 1169u32; -pub const ERROR_NO_MEDIA_IN_DRIVE: WIN32_ERROR = 1112u32; -pub const ERROR_NO_MORE_DEVICES: WIN32_ERROR = 1248u32; -pub const ERROR_NO_MORE_FILES: WIN32_ERROR = 18u32; -pub const ERROR_NO_MORE_ITEMS: WIN32_ERROR = 259u32; -pub const ERROR_NO_MORE_MATCHES: WIN32_ERROR = 626u32; -pub const ERROR_NO_MORE_SEARCH_HANDLES: WIN32_ERROR = 113u32; -pub const ERROR_NO_MORE_USER_HANDLES: WIN32_ERROR = 1158u32; -pub const ERROR_NO_NETWORK: WIN32_ERROR = 1222u32; -pub const ERROR_NO_NET_OR_BAD_PATH: WIN32_ERROR = 1203u32; -pub const ERROR_NO_NVRAM_RESOURCES: WIN32_ERROR = 1470u32; -pub const ERROR_NO_PAGEFILE: WIN32_ERROR = 578u32; -pub const ERROR_NO_PHYSICALLY_ALIGNED_FREE_SPACE_FOUND: WIN32_ERROR = 408u32; -pub const ERROR_NO_PROC_SLOTS: WIN32_ERROR = 89u32; -pub const ERROR_NO_PROMOTION_ACTIVE: WIN32_ERROR = 8222u32; -pub const ERROR_NO_QUOTAS_FOR_ACCOUNT: WIN32_ERROR = 1302u32; -pub const ERROR_NO_RANGES_PROCESSED: WIN32_ERROR = 312u32; -pub const ERROR_NO_RECOVERY_POLICY: WIN32_ERROR = 6003u32; -pub const ERROR_NO_RECOVERY_PROGRAM: WIN32_ERROR = 1082u32; -pub const ERROR_NO_SCROLLBARS: WIN32_ERROR = 1447u32; -pub const ERROR_NO_SECRETS: WIN32_ERROR = 8620u32; -pub const ERROR_NO_SECURITY_ON_OBJECT: WIN32_ERROR = 1350u32; -pub const ERROR_NO_SHUTDOWN_IN_PROGRESS: WIN32_ERROR = 1116u32; -pub const ERROR_NO_SIGNAL_SENT: WIN32_ERROR = 205u32; -pub const ERROR_NO_SITENAME: WIN32_ERROR = 1919u32; -pub const ERROR_NO_SITE_SETTINGS_OBJECT: WIN32_ERROR = 8619u32; -pub const ERROR_NO_SPOOL_SPACE: WIN32_ERROR = 62u32; -pub const ERROR_NO_SUCH_ALIAS: WIN32_ERROR = 1376u32; -pub const ERROR_NO_SUCH_DEVICE: WIN32_ERROR = 433u32; -pub const ERROR_NO_SUCH_DOMAIN: WIN32_ERROR = 1355u32; -pub const ERROR_NO_SUCH_GROUP: WIN32_ERROR = 1319u32; -pub const ERROR_NO_SUCH_LOGON_SESSION: WIN32_ERROR = 1312u32; -pub const ERROR_NO_SUCH_MEMBER: WIN32_ERROR = 1387u32; -pub const ERROR_NO_SUCH_PACKAGE: WIN32_ERROR = 1364u32; -pub const ERROR_NO_SUCH_PRIVILEGE: WIN32_ERROR = 1313u32; -pub const ERROR_NO_SUCH_SITE: WIN32_ERROR = 1249u32; -pub const ERROR_NO_SUCH_USER: WIN32_ERROR = 1317u32; -pub const ERROR_NO_SYSTEM_MENU: WIN32_ERROR = 1437u32; -pub const ERROR_NO_SYSTEM_RESOURCES: WIN32_ERROR = 1450u32; -pub const ERROR_NO_TASK_QUEUE: WIN32_ERROR = 427u32; -pub const ERROR_NO_TOKEN: WIN32_ERROR = 1008u32; -pub const ERROR_NO_TRACKING_SERVICE: WIN32_ERROR = 1172u32; -pub const ERROR_NO_TRUST_LSA_SECRET: WIN32_ERROR = 1786u32; -pub const ERROR_NO_TRUST_SAM_ACCOUNT: WIN32_ERROR = 1787u32; -pub const ERROR_NO_UNICODE_TRANSLATION: WIN32_ERROR = 1113u32; -pub const ERROR_NO_USER_KEYS: WIN32_ERROR = 6006u32; -pub const ERROR_NO_USER_SESSION_KEY: WIN32_ERROR = 1394u32; -pub const ERROR_NO_VOLUME_ID: WIN32_ERROR = 1173u32; -pub const ERROR_NO_VOLUME_LABEL: WIN32_ERROR = 125u32; -pub const ERROR_NO_WILDCARD_CHARACTERS: WIN32_ERROR = 1417u32; -pub const ERROR_NO_WORK_DONE: WIN32_ERROR = 235u32; -pub const ERROR_NO_WRITABLE_DC_FOUND: WIN32_ERROR = 8621u32; -pub const ERROR_NO_YIELD_PERFORMED: WIN32_ERROR = 721u32; -pub const ERROR_NTLM_BLOCKED: WIN32_ERROR = 1937u32; -pub const ERROR_NT_CROSS_ENCRYPTION_REQUIRED: WIN32_ERROR = 1386u32; -pub const ERROR_NULL_LM_PASSWORD: WIN32_ERROR = 1304u32; -pub const ERROR_OBJECT_IS_IMMUTABLE: WIN32_ERROR = 4449u32; -pub const ERROR_OBJECT_NAME_EXISTS: WIN32_ERROR = 698u32; -pub const ERROR_OBJECT_NOT_EXTERNALLY_BACKED: WIN32_ERROR = 342u32; -pub const ERROR_OFFLOAD_READ_FILE_NOT_SUPPORTED: WIN32_ERROR = 4442u32; -pub const ERROR_OFFLOAD_READ_FLT_NOT_SUPPORTED: WIN32_ERROR = 4440u32; -pub const ERROR_OFFLOAD_WRITE_FILE_NOT_SUPPORTED: WIN32_ERROR = 4443u32; -pub const ERROR_OFFLOAD_WRITE_FLT_NOT_SUPPORTED: WIN32_ERROR = 4441u32; -pub const ERROR_OFFSET_ALIGNMENT_VIOLATION: WIN32_ERROR = 327u32; -pub const ERROR_OLD_WIN_VERSION: WIN32_ERROR = 1150u32; -pub const ERROR_ONLY_IF_CONNECTED: WIN32_ERROR = 1251u32; -pub const ERROR_OPEN_FAILED: WIN32_ERROR = 110u32; -pub const ERROR_OPEN_FILES: WIN32_ERROR = 2401u32; -pub const ERROR_OPERATION_ABORTED: WIN32_ERROR = 995u32; -pub const ERROR_OPERATION_IN_PROGRESS: WIN32_ERROR = 329u32; -pub const ERROR_OPLOCK_BREAK_IN_PROGRESS: WIN32_ERROR = 742u32; -pub const ERROR_OPLOCK_HANDLE_CLOSED: WIN32_ERROR = 803u32; -pub const ERROR_OPLOCK_NOT_GRANTED: WIN32_ERROR = 300u32; -pub const ERROR_OPLOCK_SWITCHED_TO_NEW_HANDLE: WIN32_ERROR = 800u32; -pub const ERROR_ORPHAN_NAME_EXHAUSTED: WIN32_ERROR = 799u32; -pub const ERROR_OUTOFMEMORY: WIN32_ERROR = 14u32; -pub const ERROR_OUT_OF_PAPER: WIN32_ERROR = 28u32; -pub const ERROR_OUT_OF_STRUCTURES: WIN32_ERROR = 84u32; -pub const ERROR_OVERRIDE_NOCHANGES: WIN32_ERROR = 1252u32; -pub const ERROR_PAGED_SYSTEM_RESOURCES: WIN32_ERROR = 1452u32; -pub const ERROR_PAGEFILE_CREATE_FAILED: WIN32_ERROR = 576u32; -pub const ERROR_PAGEFILE_NOT_SUPPORTED: WIN32_ERROR = 491u32; -pub const ERROR_PAGEFILE_QUOTA: WIN32_ERROR = 1454u32; -pub const ERROR_PAGEFILE_QUOTA_EXCEEDED: WIN32_ERROR = 567u32; -pub const ERROR_PAGE_FAULT_COPY_ON_WRITE: WIN32_ERROR = 749u32; -pub const ERROR_PAGE_FAULT_DEMAND_ZERO: WIN32_ERROR = 748u32; -pub const ERROR_PAGE_FAULT_GUARD_PAGE: WIN32_ERROR = 750u32; -pub const ERROR_PAGE_FAULT_PAGING_FILE: WIN32_ERROR = 751u32; -pub const ERROR_PAGE_FAULT_TRANSITION: WIN32_ERROR = 747u32; -pub const ERROR_PARAMETER_QUOTA_EXCEEDED: WIN32_ERROR = 1283u32; -pub const ERROR_PARTIAL_COPY: WIN32_ERROR = 299u32; -pub const ERROR_PARTITION_FAILURE: WIN32_ERROR = 1105u32; -pub const ERROR_PARTITION_TERMINATING: WIN32_ERROR = 1184u32; -pub const ERROR_PASSWORD_CHANGE_REQUIRED: WIN32_ERROR = 1938u32; -pub const ERROR_PASSWORD_EXPIRED: WIN32_ERROR = 1330u32; -pub const ERROR_PASSWORD_MUST_CHANGE: WIN32_ERROR = 1907u32; -pub const ERROR_PASSWORD_RESTRICTION: WIN32_ERROR = 1325u32; -pub const ERROR_PATCH_MANAGED_ADVERTISED_PRODUCT: WIN32_ERROR = 1651u32; -pub const ERROR_PATCH_NO_SEQUENCE: WIN32_ERROR = 1648u32; -pub const ERROR_PATCH_PACKAGE_INVALID: WIN32_ERROR = 1636u32; -pub const ERROR_PATCH_PACKAGE_OPEN_FAILED: WIN32_ERROR = 1635u32; -pub const ERROR_PATCH_PACKAGE_REJECTED: WIN32_ERROR = 1643u32; -pub const ERROR_PATCH_PACKAGE_UNSUPPORTED: WIN32_ERROR = 1637u32; -pub const ERROR_PATCH_REMOVAL_DISALLOWED: WIN32_ERROR = 1649u32; -pub const ERROR_PATCH_REMOVAL_UNSUPPORTED: WIN32_ERROR = 1646u32; -pub const ERROR_PATCH_TARGET_NOT_FOUND: WIN32_ERROR = 1642u32; -pub const ERROR_PATH_BUSY: WIN32_ERROR = 148u32; -pub const ERROR_PATH_NOT_FOUND: WIN32_ERROR = 3u32; -pub const ERROR_PER_USER_TRUST_QUOTA_EXCEEDED: WIN32_ERROR = 1932u32; -pub const ERROR_PIPE_BUSY: WIN32_ERROR = 231u32; -pub const ERROR_PIPE_CONNECTED: WIN32_ERROR = 535u32; -pub const ERROR_PIPE_LISTENING: WIN32_ERROR = 536u32; -pub const ERROR_PIPE_LOCAL: WIN32_ERROR = 229u32; -pub const ERROR_PIPE_NOT_CONNECTED: WIN32_ERROR = 233u32; -pub const ERROR_PKINIT_FAILURE: WIN32_ERROR = 1263u32; -pub const ERROR_PLUGPLAY_QUERY_VETOED: WIN32_ERROR = 683u32; -pub const ERROR_PNP_BAD_MPS_TABLE: WIN32_ERROR = 671u32; -pub const ERROR_PNP_INVALID_ID: WIN32_ERROR = 674u32; -pub const ERROR_PNP_IRQ_TRANSLATION_FAILED: WIN32_ERROR = 673u32; -pub const ERROR_PNP_QUERY_REMOVE_DEVICE_TIMEOUT: WIN32_ERROR = 480u32; -pub const ERROR_PNP_QUERY_REMOVE_RELATED_DEVICE_TIMEOUT: WIN32_ERROR = 481u32; -pub const ERROR_PNP_QUERY_REMOVE_UNRELATED_DEVICE_TIMEOUT: WIN32_ERROR = 482u32; -pub const ERROR_PNP_REBOOT_REQUIRED: WIN32_ERROR = 638u32; -pub const ERROR_PNP_RESTART_ENUMERATION: WIN32_ERROR = 636u32; -pub const ERROR_PNP_TRANSLATION_FAILED: WIN32_ERROR = 672u32; -pub const ERROR_POINT_NOT_FOUND: WIN32_ERROR = 1171u32; -pub const ERROR_POLICY_OBJECT_NOT_FOUND: WIN32_ERROR = 8219u32; -pub const ERROR_POLICY_ONLY_IN_DS: WIN32_ERROR = 8220u32; -pub const ERROR_POPUP_ALREADY_ACTIVE: WIN32_ERROR = 1446u32; -pub const ERROR_PORT_MESSAGE_TOO_LONG: WIN32_ERROR = 546u32; -pub const ERROR_PORT_NOT_SET: WIN32_ERROR = 642u32; -pub const ERROR_PORT_UNREACHABLE: WIN32_ERROR = 1234u32; -pub const ERROR_POSSIBLE_DEADLOCK: WIN32_ERROR = 1131u32; -pub const ERROR_POTENTIAL_FILE_FOUND: WIN32_ERROR = 1180u32; -pub const ERROR_PREDEFINED_HANDLE: WIN32_ERROR = 714u32; -pub const ERROR_PRIMARY_TRANSPORT_CONNECT_FAILED: WIN32_ERROR = 746u32; -pub const ERROR_PRINTER_ALREADY_EXISTS: WIN32_ERROR = 1802u32; -pub const ERROR_PRINTER_DELETED: WIN32_ERROR = 1905u32; -pub const ERROR_PRINTER_DRIVER_ALREADY_INSTALLED: WIN32_ERROR = 1795u32; -pub const ERROR_PRINTQ_FULL: WIN32_ERROR = 61u32; -pub const ERROR_PRINT_CANCELLED: WIN32_ERROR = 63u32; -pub const ERROR_PRIVATE_DIALOG_INDEX: WIN32_ERROR = 1415u32; -pub const ERROR_PRIVILEGE_NOT_HELD: WIN32_ERROR = 1314u32; -pub const ERROR_PROCESS_ABORTED: WIN32_ERROR = 1067u32; -pub const ERROR_PROCESS_IN_JOB: WIN32_ERROR = 760u32; -pub const ERROR_PROCESS_IS_PROTECTED: WIN32_ERROR = 1293u32; -pub const ERROR_PROCESS_MODE_ALREADY_BACKGROUND: WIN32_ERROR = 402u32; -pub const ERROR_PROCESS_MODE_NOT_BACKGROUND: WIN32_ERROR = 403u32; -pub const ERROR_PROCESS_NOT_IN_JOB: WIN32_ERROR = 759u32; -pub const ERROR_PROC_NOT_FOUND: WIN32_ERROR = 127u32; -pub const ERROR_PRODUCT_UNINSTALLED: WIN32_ERROR = 1614u32; -pub const ERROR_PRODUCT_VERSION: WIN32_ERROR = 1638u32; -pub const ERROR_PROFILING_AT_LIMIT: WIN32_ERROR = 553u32; -pub const ERROR_PROFILING_NOT_STARTED: WIN32_ERROR = 550u32; -pub const ERROR_PROFILING_NOT_STOPPED: WIN32_ERROR = 551u32; -pub const ERROR_PROMOTION_ACTIVE: WIN32_ERROR = 8221u32; -pub const ERROR_PROTOCOL_UNREACHABLE: WIN32_ERROR = 1233u32; -pub const ERROR_PWD_HISTORY_CONFLICT: WIN32_ERROR = 617u32; -pub const ERROR_PWD_TOO_LONG: WIN32_ERROR = 657u32; -pub const ERROR_PWD_TOO_RECENT: WIN32_ERROR = 616u32; -pub const ERROR_PWD_TOO_SHORT: WIN32_ERROR = 615u32; -pub const ERROR_QUOTA_ACTIVITY: WIN32_ERROR = 810u32; -pub const ERROR_QUOTA_LIST_INCONSISTENT: WIN32_ERROR = 621u32; -pub const ERROR_RANGE_LIST_CONFLICT: WIN32_ERROR = 627u32; -pub const ERROR_RANGE_NOT_FOUND: WIN32_ERROR = 644u32; -pub const ERROR_READ_FAULT: WIN32_ERROR = 30u32; -pub const ERROR_RECEIVE_EXPEDITED: WIN32_ERROR = 708u32; -pub const ERROR_RECEIVE_PARTIAL: WIN32_ERROR = 707u32; -pub const ERROR_RECEIVE_PARTIAL_EXPEDITED: WIN32_ERROR = 709u32; -pub const ERROR_RECOVERY_FAILURE: WIN32_ERROR = 1279u32; -pub const ERROR_REDIRECTOR_HAS_OPEN_HANDLES: WIN32_ERROR = 1794u32; -pub const ERROR_REDIR_PAUSED: WIN32_ERROR = 72u32; -pub const ERROR_REGISTRY_CORRUPT: WIN32_ERROR = 1015u32; -pub const ERROR_REGISTRY_HIVE_RECOVERED: WIN32_ERROR = 685u32; -pub const ERROR_REGISTRY_IO_FAILED: WIN32_ERROR = 1016u32; -pub const ERROR_REGISTRY_QUOTA_LIMIT: WIN32_ERROR = 613u32; -pub const ERROR_REGISTRY_RECOVERED: WIN32_ERROR = 1014u32; -pub const ERROR_REG_NAT_CONSUMPTION: WIN32_ERROR = 1261u32; -pub const ERROR_RELOC_CHAIN_XEEDS_SEGLIM: WIN32_ERROR = 201u32; -pub const ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED: WIN32_ERROR = 1936u32; -pub const ERROR_REMOTE_SESSION_LIMIT_EXCEEDED: WIN32_ERROR = 1220u32; -pub const ERROR_REMOTE_STORAGE_MEDIA_ERROR: WIN32_ERROR = 4352u32; -pub const ERROR_REMOTE_STORAGE_NOT_ACTIVE: WIN32_ERROR = 4351u32; -pub const ERROR_REM_NOT_LIST: WIN32_ERROR = 51u32; -pub const ERROR_REPARSE: WIN32_ERROR = 741u32; -pub const ERROR_REPARSE_ATTRIBUTE_CONFLICT: WIN32_ERROR = 4391u32; -pub const ERROR_REPARSE_OBJECT: WIN32_ERROR = 755u32; -pub const ERROR_REPARSE_POINT_ENCOUNTERED: WIN32_ERROR = 4395u32; -pub const ERROR_REPARSE_TAG_INVALID: WIN32_ERROR = 4393u32; -pub const ERROR_REPARSE_TAG_MISMATCH: WIN32_ERROR = 4394u32; -pub const ERROR_REPLY_MESSAGE_MISMATCH: WIN32_ERROR = 595u32; -pub const ERROR_REQUEST_ABORTED: WIN32_ERROR = 1235u32; -pub const ERROR_REQUEST_OUT_OF_SEQUENCE: WIN32_ERROR = 776u32; -pub const ERROR_REQUEST_PAUSED: WIN32_ERROR = 3050u32; -pub const ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION: WIN32_ERROR = 1459u32; -pub const ERROR_REQ_NOT_ACCEP: WIN32_ERROR = 71u32; -pub const ERROR_RESIDENT_FILE_NOT_SUPPORTED: WIN32_ERROR = 334u32; -pub const ERROR_RESOURCE_CALL_TIMED_OUT: WIN32_ERROR = 5910u32; -pub const ERROR_RESOURCE_DATA_NOT_FOUND: WIN32_ERROR = 1812u32; -pub const ERROR_RESOURCE_LANG_NOT_FOUND: WIN32_ERROR = 1815u32; -pub const ERROR_RESOURCE_NAME_NOT_FOUND: WIN32_ERROR = 1814u32; -pub const ERROR_RESOURCE_REQUIREMENTS_CHANGED: WIN32_ERROR = 756u32; -pub const ERROR_RESOURCE_TYPE_NOT_FOUND: WIN32_ERROR = 1813u32; -pub const ERROR_RESTART_APPLICATION: WIN32_ERROR = 1467u32; -pub const ERROR_RESUME_HIBERNATION: WIN32_ERROR = 727u32; -pub const ERROR_RETRY: WIN32_ERROR = 1237u32; -pub const ERROR_RETURN_ADDRESS_HIJACK_ATTEMPT: WIN32_ERROR = 1662u32; -pub const ERROR_REVISION_MISMATCH: WIN32_ERROR = 1306u32; -pub const ERROR_RING2SEG_MUST_BE_MOVABLE: WIN32_ERROR = 200u32; -pub const ERROR_RING2_STACK_IN_USE: WIN32_ERROR = 207u32; -pub const ERROR_RMODE_APP: WIN32_ERROR = 1153u32; -pub const ERROR_ROWSNOTRELEASED: WIN32_ERROR = 772u32; -pub const ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT: WIN32_ERROR = 15403u32; -pub const ERROR_RUNLEVEL_SWITCH_TIMEOUT: WIN32_ERROR = 15402u32; -pub const ERROR_RWRAW_ENCRYPTED_FILE_NOT_ENCRYPTED: WIN32_ERROR = 410u32; -pub const ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_FILEOFFSET: WIN32_ERROR = 411u32; -pub const ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_FILERANGE: WIN32_ERROR = 412u32; -pub const ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_PARAMETER: WIN32_ERROR = 413u32; -pub const ERROR_RXACT_COMMITTED: WIN32_ERROR = 744u32; -pub const ERROR_RXACT_COMMIT_FAILURE: WIN32_ERROR = 1370u32; -pub const ERROR_RXACT_COMMIT_NECESSARY: WIN32_ERROR = 678u32; -pub const ERROR_RXACT_INVALID_STATE: WIN32_ERROR = 1369u32; -pub const ERROR_RXACT_STATE_CREATED: WIN32_ERROR = 701u32; -pub const ERROR_SAME_DRIVE: WIN32_ERROR = 143u32; -pub const ERROR_SAM_INIT_FAILURE: WIN32_ERROR = 8541u32; -pub const ERROR_SCOPE_NOT_FOUND: WIN32_ERROR = 318u32; -pub const ERROR_SCREEN_ALREADY_LOCKED: WIN32_ERROR = 1440u32; -pub const ERROR_SCRUB_DATA_DISABLED: WIN32_ERROR = 332u32; -pub const ERROR_SECRET_TOO_LONG: WIN32_ERROR = 1382u32; -pub const ERROR_SECTION_DIRECT_MAP_ONLY: WIN32_ERROR = 819u32; -pub const ERROR_SECTOR_NOT_FOUND: WIN32_ERROR = 27u32; -pub const ERROR_SECURITY_DENIES_OPERATION: WIN32_ERROR = 447u32; -pub const ERROR_SECURITY_STREAM_IS_INCONSISTENT: WIN32_ERROR = 306u32; -pub const ERROR_SEEK: WIN32_ERROR = 25u32; -pub const ERROR_SEEK_ON_DEVICE: WIN32_ERROR = 132u32; -pub const ERROR_SEGMENT_NOTIFICATION: WIN32_ERROR = 702u32; -pub const ERROR_SEM_IS_SET: WIN32_ERROR = 102u32; -pub const ERROR_SEM_NOT_FOUND: WIN32_ERROR = 187u32; -pub const ERROR_SEM_OWNER_DIED: WIN32_ERROR = 105u32; -pub const ERROR_SEM_TIMEOUT: WIN32_ERROR = 121u32; -pub const ERROR_SEM_USER_LIMIT: WIN32_ERROR = 106u32; -pub const ERROR_SERIAL_NO_DEVICE: WIN32_ERROR = 1118u32; -pub const ERROR_SERVER_DISABLED: WIN32_ERROR = 1341u32; -pub const ERROR_SERVER_HAS_OPEN_HANDLES: WIN32_ERROR = 1811u32; -pub const ERROR_SERVER_NOT_DISABLED: WIN32_ERROR = 1342u32; -pub const ERROR_SERVER_SHUTDOWN_IN_PROGRESS: WIN32_ERROR = 1255u32; -pub const ERROR_SERVER_SID_MISMATCH: WIN32_ERROR = 628u32; -pub const ERROR_SERVER_TRANSPORT_CONFLICT: WIN32_ERROR = 816u32; -pub const ERROR_SERVICE_ALREADY_RUNNING: WIN32_ERROR = 1056u32; -pub const ERROR_SERVICE_CANNOT_ACCEPT_CTRL: WIN32_ERROR = 1061u32; -pub const ERROR_SERVICE_DATABASE_LOCKED: WIN32_ERROR = 1055u32; -pub const ERROR_SERVICE_DEPENDENCY_DELETED: WIN32_ERROR = 1075u32; -pub const ERROR_SERVICE_DEPENDENCY_FAIL: WIN32_ERROR = 1068u32; -pub const ERROR_SERVICE_DISABLED: WIN32_ERROR = 1058u32; -pub const ERROR_SERVICE_DOES_NOT_EXIST: WIN32_ERROR = 1060u32; -pub const ERROR_SERVICE_EXISTS: WIN32_ERROR = 1073u32; -pub const ERROR_SERVICE_LOGON_FAILED: WIN32_ERROR = 1069u32; -pub const ERROR_SERVICE_MARKED_FOR_DELETE: WIN32_ERROR = 1072u32; -pub const ERROR_SERVICE_NEVER_STARTED: WIN32_ERROR = 1077u32; -pub const ERROR_SERVICE_NOTIFICATION: WIN32_ERROR = 716u32; -pub const ERROR_SERVICE_NOTIFY_CLIENT_LAGGING: WIN32_ERROR = 1294u32; -pub const ERROR_SERVICE_NOT_ACTIVE: WIN32_ERROR = 1062u32; -pub const ERROR_SERVICE_NOT_FOUND: WIN32_ERROR = 1243u32; -pub const ERROR_SERVICE_NOT_IN_EXE: WIN32_ERROR = 1083u32; -pub const ERROR_SERVICE_NO_THREAD: WIN32_ERROR = 1054u32; -pub const ERROR_SERVICE_REQUEST_TIMEOUT: WIN32_ERROR = 1053u32; -pub const ERROR_SERVICE_SPECIFIC_ERROR: WIN32_ERROR = 1066u32; -pub const ERROR_SERVICE_START_HANG: WIN32_ERROR = 1070u32; -pub const ERROR_SESSION_CREDENTIAL_CONFLICT: WIN32_ERROR = 1219u32; -pub const ERROR_SESSION_KEY_TOO_SHORT: WIN32_ERROR = 501u32; -pub const ERROR_SETCOUNT_ON_BAD_LB: WIN32_ERROR = 1433u32; -pub const ERROR_SETMARK_DETECTED: WIN32_ERROR = 1103u32; -pub const ERROR_SET_CONTEXT_DENIED: WIN32_ERROR = 1660u32; -pub const ERROR_SET_NOT_FOUND: WIN32_ERROR = 1170u32; -pub const ERROR_SET_POWER_STATE_FAILED: WIN32_ERROR = 1141u32; -pub const ERROR_SET_POWER_STATE_VETOED: WIN32_ERROR = 1140u32; -pub const ERROR_SHARED_POLICY: WIN32_ERROR = 8218u32; -pub const ERROR_SHARING_BUFFER_EXCEEDED: WIN32_ERROR = 36u32; -pub const ERROR_SHARING_PAUSED: WIN32_ERROR = 70u32; -pub const ERROR_SHARING_VIOLATION: WIN32_ERROR = 32u32; -pub const ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME: WIN32_ERROR = 305u32; -pub const ERROR_SHUTDOWN_DISKS_NOT_IN_MAINTENANCE_MODE: WIN32_ERROR = 1192u32; -pub const ERROR_SHUTDOWN_IN_PROGRESS: WIN32_ERROR = 1115u32; -pub const ERROR_SHUTDOWN_IS_SCHEDULED: WIN32_ERROR = 1190u32; -pub const ERROR_SHUTDOWN_USERS_LOGGED_ON: WIN32_ERROR = 1191u32; -pub const ERROR_SIGNAL_PENDING: WIN32_ERROR = 162u32; -pub const ERROR_SIGNAL_REFUSED: WIN32_ERROR = 156u32; -pub const ERROR_SINGLE_INSTANCE_APP: WIN32_ERROR = 1152u32; -pub const ERROR_SMARTCARD_SUBSYSTEM_FAILURE: WIN32_ERROR = 1264u32; -pub const ERROR_SMB1_NOT_AVAILABLE: WIN32_ERROR = 384u32; -pub const ERROR_SMB_GUEST_LOGON_BLOCKED: WIN32_ERROR = 1272u32; -pub const ERROR_SMR_GARBAGE_COLLECTION_REQUIRED: WIN32_ERROR = 4445u32; -pub const ERROR_SOME_NOT_MAPPED: WIN32_ERROR = 1301u32; -pub const ERROR_SOURCE_ELEMENT_EMPTY: WIN32_ERROR = 1160u32; -pub const ERROR_SPARSE_FILE_NOT_SUPPORTED: WIN32_ERROR = 490u32; -pub const ERROR_SPECIAL_ACCOUNT: WIN32_ERROR = 1371u32; -pub const ERROR_SPECIAL_GROUP: WIN32_ERROR = 1372u32; -pub const ERROR_SPECIAL_USER: WIN32_ERROR = 1373u32; -pub const ERROR_SRC_SRV_DLL_LOAD_FAILED: WIN32_ERROR = 428u32; -pub const ERROR_STACK_BUFFER_OVERRUN: WIN32_ERROR = 1282u32; -pub const ERROR_STACK_OVERFLOW: WIN32_ERROR = 1001u32; -pub const ERROR_STACK_OVERFLOW_READ: WIN32_ERROR = 599u32; -pub const ERROR_STOPPED_ON_SYMLINK: WIN32_ERROR = 681u32; -pub const ERROR_STORAGE_LOST_DATA_PERSISTENCE: WIN32_ERROR = 368u32; -pub const ERROR_STORAGE_RESERVE_ALREADY_EXISTS: WIN32_ERROR = 418u32; -pub const ERROR_STORAGE_RESERVE_DOES_NOT_EXIST: WIN32_ERROR = 417u32; -pub const ERROR_STORAGE_RESERVE_ID_INVALID: WIN32_ERROR = 416u32; -pub const ERROR_STORAGE_RESERVE_NOT_EMPTY: WIN32_ERROR = 419u32; -pub const ERROR_STORAGE_STACK_ACCESS_DENIED: WIN32_ERROR = 472u32; -pub const ERROR_STORAGE_TOPOLOGY_ID_MISMATCH: WIN32_ERROR = 345u32; -pub const ERROR_STRICT_CFG_VIOLATION: WIN32_ERROR = 1657u32; -pub const ERROR_SUBST_TO_JOIN: WIN32_ERROR = 141u32; -pub const ERROR_SUBST_TO_SUBST: WIN32_ERROR = 139u32; -pub const ERROR_SUCCESS: WIN32_ERROR = 0u32; -pub const ERROR_SUCCESS_REBOOT_INITIATED: WIN32_ERROR = 1641u32; -pub const ERROR_SWAPERROR: WIN32_ERROR = 999u32; -pub const ERROR_SYMLINK_CLASS_DISABLED: WIN32_ERROR = 1463u32; -pub const ERROR_SYMLINK_NOT_SUPPORTED: WIN32_ERROR = 1464u32; -pub const ERROR_SYNCHRONIZATION_REQUIRED: WIN32_ERROR = 569u32; -pub const ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED: WIN32_ERROR = 1274u32; -pub const ERROR_SYSTEM_HIVE_TOO_LARGE: WIN32_ERROR = 653u32; -pub const ERROR_SYSTEM_IMAGE_BAD_SIGNATURE: WIN32_ERROR = 637u32; -pub const ERROR_SYSTEM_POWERSTATE_COMPLEX_TRANSITION: WIN32_ERROR = 783u32; -pub const ERROR_SYSTEM_POWERSTATE_TRANSITION: WIN32_ERROR = 782u32; -pub const ERROR_SYSTEM_PROCESS_TERMINATED: WIN32_ERROR = 591u32; -pub const ERROR_SYSTEM_SHUTDOWN: WIN32_ERROR = 641u32; -pub const ERROR_SYSTEM_TRACE: WIN32_ERROR = 150u32; -pub const ERROR_THREAD_1_INACTIVE: WIN32_ERROR = 210u32; -pub const ERROR_THREAD_ALREADY_IN_TASK: WIN32_ERROR = 1552u32; -pub const ERROR_THREAD_MODE_ALREADY_BACKGROUND: WIN32_ERROR = 400u32; -pub const ERROR_THREAD_MODE_NOT_BACKGROUND: WIN32_ERROR = 401u32; -pub const ERROR_THREAD_NOT_IN_PROCESS: WIN32_ERROR = 566u32; -pub const ERROR_THREAD_WAS_SUSPENDED: WIN32_ERROR = 699u32; -pub const ERROR_TIMEOUT: WIN32_ERROR = 1460u32; -pub const ERROR_TIMER_NOT_CANCELED: WIN32_ERROR = 541u32; -pub const ERROR_TIMER_RESOLUTION_NOT_SET: WIN32_ERROR = 607u32; -pub const ERROR_TIMER_RESUME_IGNORED: WIN32_ERROR = 722u32; -pub const ERROR_TIME_SENSITIVE_THREAD: WIN32_ERROR = 422u32; -pub const ERROR_TIME_SKEW: WIN32_ERROR = 1398u32; -pub const ERROR_TLW_WITH_WSCHILD: WIN32_ERROR = 1406u32; -pub const ERROR_TOKEN_ALREADY_IN_USE: WIN32_ERROR = 1375u32; -pub const ERROR_TOO_MANY_CMDS: WIN32_ERROR = 56u32; -pub const ERROR_TOO_MANY_CONTEXT_IDS: WIN32_ERROR = 1384u32; -pub const ERROR_TOO_MANY_DESCRIPTORS: WIN32_ERROR = 331u32; -pub const ERROR_TOO_MANY_LINKS: WIN32_ERROR = 1142u32; -pub const ERROR_TOO_MANY_LUIDS_REQUESTED: WIN32_ERROR = 1333u32; -pub const ERROR_TOO_MANY_MODULES: WIN32_ERROR = 214u32; -pub const ERROR_TOO_MANY_MUXWAITERS: WIN32_ERROR = 152u32; -pub const ERROR_TOO_MANY_NAMES: WIN32_ERROR = 68u32; -pub const ERROR_TOO_MANY_OPEN_FILES: WIN32_ERROR = 4u32; -pub const ERROR_TOO_MANY_POSTS: WIN32_ERROR = 298u32; -pub const ERROR_TOO_MANY_SECRETS: WIN32_ERROR = 1381u32; -pub const ERROR_TOO_MANY_SEMAPHORES: WIN32_ERROR = 100u32; -pub const ERROR_TOO_MANY_SEM_REQUESTS: WIN32_ERROR = 103u32; -pub const ERROR_TOO_MANY_SESS: WIN32_ERROR = 69u32; -pub const ERROR_TOO_MANY_SIDS: WIN32_ERROR = 1389u32; -pub const ERROR_TOO_MANY_TCBS: WIN32_ERROR = 155u32; -pub const ERROR_TOO_MANY_THREADS: WIN32_ERROR = 565u32; -pub const ERROR_TRANSLATION_COMPLETE: WIN32_ERROR = 757u32; -pub const ERROR_TRUSTED_DOMAIN_FAILURE: WIN32_ERROR = 1788u32; -pub const ERROR_TRUSTED_RELATIONSHIP_FAILURE: WIN32_ERROR = 1789u32; -pub const ERROR_TRUST_FAILURE: WIN32_ERROR = 1790u32; -pub const ERROR_UNABLE_TO_LOCK_MEDIA: WIN32_ERROR = 1108u32; -pub const ERROR_UNABLE_TO_MOVE_REPLACEMENT: WIN32_ERROR = 1176u32; -pub const ERROR_UNABLE_TO_MOVE_REPLACEMENT_2: WIN32_ERROR = 1177u32; -pub const ERROR_UNABLE_TO_REMOVE_REPLACED: WIN32_ERROR = 1175u32; -pub const ERROR_UNABLE_TO_UNLOAD_MEDIA: WIN32_ERROR = 1109u32; -pub const ERROR_UNDEFINED_CHARACTER: WIN32_ERROR = 583u32; -pub const ERROR_UNDEFINED_SCOPE: WIN32_ERROR = 319u32; -pub const ERROR_UNEXPECTED_MM_CREATE_ERR: WIN32_ERROR = 556u32; -pub const ERROR_UNEXPECTED_MM_EXTEND_ERR: WIN32_ERROR = 558u32; -pub const ERROR_UNEXPECTED_MM_MAP_ERROR: WIN32_ERROR = 557u32; -pub const ERROR_UNEXPECTED_NTCACHEMANAGER_ERROR: WIN32_ERROR = 443u32; -pub const ERROR_UNEXP_NET_ERR: WIN32_ERROR = 59u32; -pub const ERROR_UNHANDLED_EXCEPTION: WIN32_ERROR = 574u32; -pub const ERROR_UNIDENTIFIED_ERROR: WIN32_ERROR = 1287u32; -pub const ERROR_UNKNOWN_COMPONENT: WIN32_ERROR = 1607u32; -pub const ERROR_UNKNOWN_FEATURE: WIN32_ERROR = 1606u32; -pub const ERROR_UNKNOWN_PATCH: WIN32_ERROR = 1647u32; -pub const ERROR_UNKNOWN_PORT: WIN32_ERROR = 1796u32; -pub const ERROR_UNKNOWN_PRINTER_DRIVER: WIN32_ERROR = 1797u32; -pub const ERROR_UNKNOWN_PRINTPROCESSOR: WIN32_ERROR = 1798u32; -pub const ERROR_UNKNOWN_PRODUCT: WIN32_ERROR = 1605u32; -pub const ERROR_UNKNOWN_PROPERTY: WIN32_ERROR = 1608u32; -pub const ERROR_UNKNOWN_REVISION: WIN32_ERROR = 1305u32; -pub const ERROR_UNRECOGNIZED_MEDIA: WIN32_ERROR = 1785u32; -pub const ERROR_UNRECOGNIZED_VOLUME: WIN32_ERROR = 1005u32; -pub const ERROR_UNSATISFIED_DEPENDENCIES: WIN32_ERROR = 441u32; -pub const ERROR_UNSUPPORTED_COMPRESSION: WIN32_ERROR = 618u32; -pub const ERROR_UNSUPPORTED_TYPE: WIN32_ERROR = 1630u32; -pub const ERROR_UNTRUSTED_MOUNT_POINT: WIN32_ERROR = 448u32; -pub const ERROR_UNWIND: WIN32_ERROR = 542u32; -pub const ERROR_UNWIND_CONSOLIDATE: WIN32_ERROR = 684u32; -pub const ERROR_USER_APC: WIN32_ERROR = 737u32; -pub const ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED: WIN32_ERROR = 1934u32; -pub const ERROR_USER_EXISTS: WIN32_ERROR = 1316u32; -pub const ERROR_USER_MAPPED_FILE: WIN32_ERROR = 1224u32; -pub const ERROR_USER_PROFILE_LOAD: WIN32_ERROR = 500u32; -pub const ERROR_VALIDATE_CONTINUE: WIN32_ERROR = 625u32; -pub const ERROR_VC_DISCONNECTED: WIN32_ERROR = 240u32; -pub const ERROR_VDM_DISALLOWED: WIN32_ERROR = 1286u32; -pub const ERROR_VDM_HARD_ERROR: WIN32_ERROR = 593u32; -pub const ERROR_VERIFIER_STOP: WIN32_ERROR = 537u32; -pub const ERROR_VERSION_PARSE_ERROR: WIN32_ERROR = 777u32; -pub const ERROR_VIRUS_DELETED: WIN32_ERROR = 226u32; -pub const ERROR_VIRUS_INFECTED: WIN32_ERROR = 225u32; -pub const ERROR_VOLSNAP_HIBERNATE_READY: WIN32_ERROR = 761u32; -pub const ERROR_VOLSNAP_PREPARE_HIBERNATE: WIN32_ERROR = 655u32; -pub const ERROR_VOLUME_MOUNTED: WIN32_ERROR = 743u32; -pub const ERROR_VOLUME_NOT_CLUSTER_ALIGNED: WIN32_ERROR = 407u32; -pub const ERROR_VOLUME_NOT_SIS_ENABLED: WIN32_ERROR = 4500u32; -pub const ERROR_VOLUME_NOT_SUPPORTED: WIN32_ERROR = 492u32; -pub const ERROR_VOLUME_NOT_SUPPORT_EFS: WIN32_ERROR = 6014u32; -pub const ERROR_VOLUME_WRITE_ACCESS_DENIED: WIN32_ERROR = 508u32; -pub const ERROR_WAIT_1: WIN32_ERROR = 731u32; -pub const ERROR_WAIT_2: WIN32_ERROR = 732u32; -pub const ERROR_WAIT_3: WIN32_ERROR = 733u32; -pub const ERROR_WAIT_63: WIN32_ERROR = 734u32; -pub const ERROR_WAIT_FOR_OPLOCK: WIN32_ERROR = 765u32; -pub const ERROR_WAIT_NO_CHILDREN: WIN32_ERROR = 128u32; -pub const ERROR_WAKE_SYSTEM: WIN32_ERROR = 730u32; -pub const ERROR_WAKE_SYSTEM_DEBUGGER: WIN32_ERROR = 675u32; -pub const ERROR_WAS_LOCKED: WIN32_ERROR = 717u32; -pub const ERROR_WAS_UNLOCKED: WIN32_ERROR = 715u32; -pub const ERROR_WEAK_WHFBKEY_BLOCKED: WIN32_ERROR = 8651u32; -pub const ERROR_WINDOW_NOT_COMBOBOX: WIN32_ERROR = 1423u32; -pub const ERROR_WINDOW_NOT_DIALOG: WIN32_ERROR = 1420u32; -pub const ERROR_WINDOW_OF_OTHER_THREAD: WIN32_ERROR = 1408u32; -pub const ERROR_WIP_ENCRYPTION_FAILED: WIN32_ERROR = 6023u32; -pub const ERROR_WOF_FILE_RESOURCE_TABLE_CORRUPT: WIN32_ERROR = 4448u32; -pub const ERROR_WOF_WIM_HEADER_CORRUPT: WIN32_ERROR = 4446u32; -pub const ERROR_WOF_WIM_RESOURCE_TABLE_CORRUPT: WIN32_ERROR = 4447u32; -pub const ERROR_WORKING_SET_QUOTA: WIN32_ERROR = 1453u32; -pub const ERROR_WOW_ASSERTION: WIN32_ERROR = 670u32; -pub const ERROR_WRITE_FAULT: WIN32_ERROR = 29u32; -pub const ERROR_WRITE_PROTECT: WIN32_ERROR = 19u32; -pub const ERROR_WRONG_COMPARTMENT: WIN32_ERROR = 1468u32; -pub const ERROR_WRONG_DISK: WIN32_ERROR = 34u32; -pub const ERROR_WRONG_EFS: WIN32_ERROR = 6005u32; -pub const ERROR_WRONG_PASSWORD: WIN32_ERROR = 1323u32; -pub const ERROR_WRONG_TARGET_NAME: WIN32_ERROR = 1396u32; -pub const ERROR_WX86_ERROR: WIN32_ERROR = 540u32; -pub const ERROR_WX86_WARNING: WIN32_ERROR = 539u32; -pub const ERROR_XMLDSIG_ERROR: WIN32_ERROR = 1466u32; -pub const ERROR_XML_PARSE_ERROR: WIN32_ERROR = 1465u32; -pub type EXCEPTION_DISPOSITION = i32; -pub const EXCEPTION_MAXIMUM_PARAMETERS: u32 = 15u32; -#[repr(C)] -pub struct EXCEPTION_RECORD { - pub ExceptionCode: NTSTATUS, - pub ExceptionFlags: u32, - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ExceptionAddress: *mut ::core::ffi::c_void, - pub NumberParameters: u32, - pub ExceptionInformation: [usize; 15], -} -impl ::core::marker::Copy for EXCEPTION_RECORD {} -impl ::core::clone::Clone for EXCEPTION_RECORD { - fn clone(&self) -> Self { - *self - } -} -pub const EXCEPTION_STACK_OVERFLOW: NTSTATUS = -1073741571i32; -pub const EXTENDED_STARTUPINFO_PRESENT: PROCESS_CREATION_FLAGS = 524288u32; -pub const E_NOTIMPL: HRESULT = -2147467263i32; -pub const ExceptionCollidedUnwind: EXCEPTION_DISPOSITION = 3i32; -pub const ExceptionContinueExecution: EXCEPTION_DISPOSITION = 0i32; -pub const ExceptionContinueSearch: EXCEPTION_DISPOSITION = 1i32; -pub const ExceptionNestedException: EXCEPTION_DISPOSITION = 2i32; -pub type FACILITY_CODE = u32; -pub const FACILITY_NT_BIT: FACILITY_CODE = 268435456u32; -pub const FALSE: BOOL = 0i32; -pub type FARPROC = ::core::option::Option isize>; -#[repr(C)] -pub struct FD_SET { - pub fd_count: u32, - pub fd_array: [SOCKET; 64], -} -impl ::core::marker::Copy for FD_SET {} -impl ::core::clone::Clone for FD_SET { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub struct FILETIME { - pub dwLowDateTime: u32, - pub dwHighDateTime: u32, -} -impl ::core::marker::Copy for FILETIME {} -impl ::core::clone::Clone for FILETIME { - fn clone(&self) -> Self { - *self - } -} -pub type FILE_ACCESS_RIGHTS = u32; -pub const FILE_ADD_FILE: FILE_ACCESS_RIGHTS = 2u32; -pub const FILE_ADD_SUBDIRECTORY: FILE_ACCESS_RIGHTS = 4u32; -#[repr(C)] -pub struct FILE_ALLOCATION_INFO { - pub AllocationSize: i64, -} -impl ::core::marker::Copy for FILE_ALLOCATION_INFO {} -impl ::core::clone::Clone for FILE_ALLOCATION_INFO { - fn clone(&self) -> Self { - *self - } -} -pub const FILE_ALL_ACCESS: FILE_ACCESS_RIGHTS = 2032127u32; -pub const FILE_APPEND_DATA: FILE_ACCESS_RIGHTS = 4u32; -pub const FILE_ATTRIBUTE_ARCHIVE: FILE_FLAGS_AND_ATTRIBUTES = 32u32; -pub const FILE_ATTRIBUTE_COMPRESSED: FILE_FLAGS_AND_ATTRIBUTES = 2048u32; -pub const FILE_ATTRIBUTE_DEVICE: FILE_FLAGS_AND_ATTRIBUTES = 64u32; -pub const FILE_ATTRIBUTE_DIRECTORY: FILE_FLAGS_AND_ATTRIBUTES = 16u32; -pub const FILE_ATTRIBUTE_EA: FILE_FLAGS_AND_ATTRIBUTES = 262144u32; -pub const FILE_ATTRIBUTE_ENCRYPTED: FILE_FLAGS_AND_ATTRIBUTES = 16384u32; -pub const FILE_ATTRIBUTE_HIDDEN: FILE_FLAGS_AND_ATTRIBUTES = 2u32; -pub const FILE_ATTRIBUTE_INTEGRITY_STREAM: FILE_FLAGS_AND_ATTRIBUTES = 32768u32; -pub const FILE_ATTRIBUTE_NORMAL: FILE_FLAGS_AND_ATTRIBUTES = 128u32; -pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: FILE_FLAGS_AND_ATTRIBUTES = 8192u32; -pub const FILE_ATTRIBUTE_NO_SCRUB_DATA: FILE_FLAGS_AND_ATTRIBUTES = 131072u32; -pub const FILE_ATTRIBUTE_OFFLINE: FILE_FLAGS_AND_ATTRIBUTES = 4096u32; -pub const FILE_ATTRIBUTE_PINNED: FILE_FLAGS_AND_ATTRIBUTES = 524288u32; -pub const FILE_ATTRIBUTE_READONLY: FILE_FLAGS_AND_ATTRIBUTES = 1u32; -pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS: FILE_FLAGS_AND_ATTRIBUTES = 4194304u32; -pub const FILE_ATTRIBUTE_RECALL_ON_OPEN: FILE_FLAGS_AND_ATTRIBUTES = 262144u32; -pub const FILE_ATTRIBUTE_REPARSE_POINT: FILE_FLAGS_AND_ATTRIBUTES = 1024u32; -pub const FILE_ATTRIBUTE_SPARSE_FILE: FILE_FLAGS_AND_ATTRIBUTES = 512u32; -pub const FILE_ATTRIBUTE_SYSTEM: FILE_FLAGS_AND_ATTRIBUTES = 4u32; -#[repr(C)] -pub struct FILE_ATTRIBUTE_TAG_INFO { - pub FileAttributes: u32, - pub ReparseTag: u32, -} -impl ::core::marker::Copy for FILE_ATTRIBUTE_TAG_INFO {} -impl ::core::clone::Clone for FILE_ATTRIBUTE_TAG_INFO { - fn clone(&self) -> Self { - *self - } -} -pub const FILE_ATTRIBUTE_TEMPORARY: FILE_FLAGS_AND_ATTRIBUTES = 256u32; -pub const FILE_ATTRIBUTE_UNPINNED: FILE_FLAGS_AND_ATTRIBUTES = 1048576u32; -pub const FILE_ATTRIBUTE_VIRTUAL: FILE_FLAGS_AND_ATTRIBUTES = 65536u32; -#[repr(C)] -pub struct FILE_BASIC_INFO { - pub CreationTime: i64, - pub LastAccessTime: i64, - pub LastWriteTime: i64, - pub ChangeTime: i64, - pub FileAttributes: u32, -} -impl ::core::marker::Copy for FILE_BASIC_INFO {} -impl ::core::clone::Clone for FILE_BASIC_INFO { - fn clone(&self) -> Self { - *self - } -} -pub const FILE_BEGIN: SET_FILE_POINTER_MOVE_METHOD = 0u32; -pub const FILE_COMPLETE_IF_OPLOCKED: NTCREATEFILE_CREATE_OPTIONS = 256u32; -pub const FILE_CONTAINS_EXTENDED_CREATE_INFORMATION: NTCREATEFILE_CREATE_OPTIONS = 268435456u32; -pub const FILE_CREATE: NTCREATEFILE_CREATE_DISPOSITION = 2u32; -pub const FILE_CREATE_PIPE_INSTANCE: FILE_ACCESS_RIGHTS = 4u32; -pub const FILE_CREATE_TREE_CONNECTION: NTCREATEFILE_CREATE_OPTIONS = 128u32; -pub type FILE_CREATION_DISPOSITION = u32; -pub const FILE_CURRENT: SET_FILE_POINTER_MOVE_METHOD = 1u32; -pub const FILE_DELETE_CHILD: FILE_ACCESS_RIGHTS = 64u32; -pub const FILE_DELETE_ON_CLOSE: NTCREATEFILE_CREATE_OPTIONS = 4096u32; -pub const FILE_DIRECTORY_FILE: NTCREATEFILE_CREATE_OPTIONS = 1u32; -pub const FILE_DISALLOW_EXCLUSIVE: NTCREATEFILE_CREATE_OPTIONS = 131072u32; -pub const FILE_DISPOSITION_FLAG_DELETE: FILE_DISPOSITION_INFO_EX_FLAGS = 1u32; -pub const FILE_DISPOSITION_FLAG_DO_NOT_DELETE: FILE_DISPOSITION_INFO_EX_FLAGS = 0u32; -pub const FILE_DISPOSITION_FLAG_FORCE_IMAGE_SECTION_CHECK: FILE_DISPOSITION_INFO_EX_FLAGS = 4u32; -pub const FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE: FILE_DISPOSITION_INFO_EX_FLAGS = 16u32; -pub const FILE_DISPOSITION_FLAG_ON_CLOSE: FILE_DISPOSITION_INFO_EX_FLAGS = 8u32; -pub const FILE_DISPOSITION_FLAG_POSIX_SEMANTICS: FILE_DISPOSITION_INFO_EX_FLAGS = 2u32; -#[repr(C)] -pub struct FILE_DISPOSITION_INFO { - pub DeleteFile: BOOLEAN, -} -impl ::core::marker::Copy for FILE_DISPOSITION_INFO {} -impl ::core::clone::Clone for FILE_DISPOSITION_INFO { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub struct FILE_DISPOSITION_INFO_EX { - pub Flags: FILE_DISPOSITION_INFO_EX_FLAGS, -} -impl ::core::marker::Copy for FILE_DISPOSITION_INFO_EX {} -impl ::core::clone::Clone for FILE_DISPOSITION_INFO_EX { - fn clone(&self) -> Self { - *self - } -} -pub type FILE_DISPOSITION_INFO_EX_FLAGS = u32; -pub const FILE_END: SET_FILE_POINTER_MOVE_METHOD = 2u32; -#[repr(C)] -pub struct FILE_END_OF_FILE_INFO { - pub EndOfFile: i64, -} -impl ::core::marker::Copy for FILE_END_OF_FILE_INFO {} -impl ::core::clone::Clone for FILE_END_OF_FILE_INFO { - fn clone(&self) -> Self { - *self - } -} -pub const FILE_EXECUTE: FILE_ACCESS_RIGHTS = 32u32; -pub type FILE_FLAGS_AND_ATTRIBUTES = u32; -pub const FILE_FLAG_BACKUP_SEMANTICS: FILE_FLAGS_AND_ATTRIBUTES = 33554432u32; -pub const FILE_FLAG_DELETE_ON_CLOSE: FILE_FLAGS_AND_ATTRIBUTES = 67108864u32; -pub const FILE_FLAG_FIRST_PIPE_INSTANCE: FILE_FLAGS_AND_ATTRIBUTES = 524288u32; -pub const FILE_FLAG_NO_BUFFERING: FILE_FLAGS_AND_ATTRIBUTES = 536870912u32; -pub const FILE_FLAG_OPEN_NO_RECALL: FILE_FLAGS_AND_ATTRIBUTES = 1048576u32; -pub const FILE_FLAG_OPEN_REPARSE_POINT: FILE_FLAGS_AND_ATTRIBUTES = 2097152u32; -pub const FILE_FLAG_OVERLAPPED: FILE_FLAGS_AND_ATTRIBUTES = 1073741824u32; -pub const FILE_FLAG_POSIX_SEMANTICS: FILE_FLAGS_AND_ATTRIBUTES = 16777216u32; -pub const FILE_FLAG_RANDOM_ACCESS: FILE_FLAGS_AND_ATTRIBUTES = 268435456u32; -pub const FILE_FLAG_SEQUENTIAL_SCAN: FILE_FLAGS_AND_ATTRIBUTES = 134217728u32; -pub const FILE_FLAG_SESSION_AWARE: FILE_FLAGS_AND_ATTRIBUTES = 8388608u32; -pub const FILE_FLAG_WRITE_THROUGH: FILE_FLAGS_AND_ATTRIBUTES = 2147483648u32; -pub const FILE_GENERIC_EXECUTE: FILE_ACCESS_RIGHTS = 1179808u32; -pub const FILE_GENERIC_READ: FILE_ACCESS_RIGHTS = 1179785u32; -pub const FILE_GENERIC_WRITE: FILE_ACCESS_RIGHTS = 1179926u32; -#[repr(C)] -pub struct FILE_ID_BOTH_DIR_INFO { - pub NextEntryOffset: u32, - pub FileIndex: u32, - pub CreationTime: i64, - pub LastAccessTime: i64, - pub LastWriteTime: i64, - pub ChangeTime: i64, - pub EndOfFile: i64, - pub AllocationSize: i64, - pub FileAttributes: u32, - pub FileNameLength: u32, - pub EaSize: u32, - pub ShortNameLength: i8, - pub ShortName: [u16; 12], - pub FileId: i64, - pub FileName: [u16; 1], -} -impl ::core::marker::Copy for FILE_ID_BOTH_DIR_INFO {} -impl ::core::clone::Clone for FILE_ID_BOTH_DIR_INFO { - fn clone(&self) -> Self { - *self - } -} -pub type FILE_INFO_BY_HANDLE_CLASS = i32; -#[repr(C)] -pub struct FILE_IO_PRIORITY_HINT_INFO { - pub PriorityHint: PRIORITY_HINT, -} -impl ::core::marker::Copy for FILE_IO_PRIORITY_HINT_INFO {} -impl ::core::clone::Clone for FILE_IO_PRIORITY_HINT_INFO { - fn clone(&self) -> Self { - *self - } -} -pub const FILE_LIST_DIRECTORY: FILE_ACCESS_RIGHTS = 1u32; -pub const FILE_NAME_NORMALIZED: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32; -pub const FILE_NAME_OPENED: GETFINALPATHNAMEBYHANDLE_FLAGS = 8u32; -pub const FILE_NON_DIRECTORY_FILE: NTCREATEFILE_CREATE_OPTIONS = 64u32; -pub const FILE_NO_COMPRESSION: NTCREATEFILE_CREATE_OPTIONS = 32768u32; -pub const FILE_NO_EA_KNOWLEDGE: NTCREATEFILE_CREATE_OPTIONS = 512u32; -pub const FILE_NO_INTERMEDIATE_BUFFERING: NTCREATEFILE_CREATE_OPTIONS = 8u32; -pub const FILE_OPEN: NTCREATEFILE_CREATE_DISPOSITION = 1u32; -pub const FILE_OPEN_BY_FILE_ID: NTCREATEFILE_CREATE_OPTIONS = 8192u32; -pub const FILE_OPEN_FOR_BACKUP_INTENT: NTCREATEFILE_CREATE_OPTIONS = 16384u32; -pub const FILE_OPEN_FOR_FREE_SPACE_QUERY: NTCREATEFILE_CREATE_OPTIONS = 8388608u32; -pub const FILE_OPEN_IF: NTCREATEFILE_CREATE_DISPOSITION = 3u32; -pub const FILE_OPEN_NO_RECALL: NTCREATEFILE_CREATE_OPTIONS = 4194304u32; -pub const FILE_OPEN_REPARSE_POINT: NTCREATEFILE_CREATE_OPTIONS = 2097152u32; -pub const FILE_OPEN_REQUIRING_OPLOCK: NTCREATEFILE_CREATE_OPTIONS = 65536u32; -pub const FILE_OVERWRITE: NTCREATEFILE_CREATE_DISPOSITION = 4u32; -pub const FILE_OVERWRITE_IF: NTCREATEFILE_CREATE_DISPOSITION = 5u32; -pub const FILE_RANDOM_ACCESS: NTCREATEFILE_CREATE_OPTIONS = 2048u32; -pub const FILE_READ_ATTRIBUTES: FILE_ACCESS_RIGHTS = 128u32; -pub const FILE_READ_DATA: FILE_ACCESS_RIGHTS = 1u32; -pub const FILE_READ_EA: FILE_ACCESS_RIGHTS = 8u32; -pub const FILE_RESERVE_OPFILTER: NTCREATEFILE_CREATE_OPTIONS = 1048576u32; -pub const FILE_SEQUENTIAL_ONLY: NTCREATEFILE_CREATE_OPTIONS = 4u32; -pub const FILE_SESSION_AWARE: NTCREATEFILE_CREATE_OPTIONS = 262144u32; -pub const FILE_SHARE_DELETE: FILE_SHARE_MODE = 4u32; -pub type FILE_SHARE_MODE = u32; -pub const FILE_SHARE_NONE: FILE_SHARE_MODE = 0u32; -pub const FILE_SHARE_READ: FILE_SHARE_MODE = 1u32; -pub const FILE_SHARE_WRITE: FILE_SHARE_MODE = 2u32; -#[repr(C)] -pub struct FILE_STANDARD_INFO { - pub AllocationSize: i64, - pub EndOfFile: i64, - pub NumberOfLinks: u32, - pub DeletePending: BOOLEAN, - pub Directory: BOOLEAN, -} -impl ::core::marker::Copy for FILE_STANDARD_INFO {} -impl ::core::clone::Clone for FILE_STANDARD_INFO { - fn clone(&self) -> Self { - *self - } -} -pub const FILE_SUPERSEDE: NTCREATEFILE_CREATE_DISPOSITION = 0u32; -pub const FILE_SYNCHRONOUS_IO_ALERT: NTCREATEFILE_CREATE_OPTIONS = 16u32; -pub const FILE_SYNCHRONOUS_IO_NONALERT: NTCREATEFILE_CREATE_OPTIONS = 32u32; -pub const FILE_TRAVERSE: FILE_ACCESS_RIGHTS = 32u32; -pub type FILE_TYPE = u32; -pub const FILE_TYPE_CHAR: FILE_TYPE = 2u32; -pub const FILE_TYPE_DISK: FILE_TYPE = 1u32; -pub const FILE_TYPE_PIPE: FILE_TYPE = 3u32; -pub const FILE_TYPE_REMOTE: FILE_TYPE = 32768u32; -pub const FILE_TYPE_UNKNOWN: FILE_TYPE = 0u32; -pub const FILE_WRITE_ATTRIBUTES: FILE_ACCESS_RIGHTS = 256u32; -pub const FILE_WRITE_DATA: FILE_ACCESS_RIGHTS = 2u32; -pub const FILE_WRITE_EA: FILE_ACCESS_RIGHTS = 16u32; -pub const FILE_WRITE_THROUGH: NTCREATEFILE_CREATE_OPTIONS = 2u32; -pub const FIONBIO: i32 = -2147195266i32; -#[repr(C)] -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] -pub struct FLOATING_SAVE_AREA { - pub ControlWord: u32, - pub StatusWord: u32, - pub TagWord: u32, - pub ErrorOffset: u32, - pub ErrorSelector: u32, - pub DataOffset: u32, - pub DataSelector: u32, - pub RegisterArea: [u8; 80], - pub Cr0NpxState: u32, -} -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] -impl ::core::marker::Copy for FLOATING_SAVE_AREA {} -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] -impl ::core::clone::Clone for FLOATING_SAVE_AREA { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -#[cfg(target_arch = "x86")] -pub struct FLOATING_SAVE_AREA { - pub ControlWord: u32, - pub StatusWord: u32, - pub TagWord: u32, - pub ErrorOffset: u32, - pub ErrorSelector: u32, - pub DataOffset: u32, - pub DataSelector: u32, - pub RegisterArea: [u8; 80], - pub Spare0: u32, -} -#[cfg(target_arch = "x86")] -impl ::core::marker::Copy for FLOATING_SAVE_AREA {} -#[cfg(target_arch = "x86")] -impl ::core::clone::Clone for FLOATING_SAVE_AREA { - fn clone(&self) -> Self { - *self - } -} -pub const FORMAT_MESSAGE_ALLOCATE_BUFFER: FORMAT_MESSAGE_OPTIONS = 256u32; -pub const FORMAT_MESSAGE_ARGUMENT_ARRAY: FORMAT_MESSAGE_OPTIONS = 8192u32; -pub const FORMAT_MESSAGE_FROM_HMODULE: FORMAT_MESSAGE_OPTIONS = 2048u32; -pub const FORMAT_MESSAGE_FROM_STRING: FORMAT_MESSAGE_OPTIONS = 1024u32; -pub const FORMAT_MESSAGE_FROM_SYSTEM: FORMAT_MESSAGE_OPTIONS = 4096u32; -pub const FORMAT_MESSAGE_IGNORE_INSERTS: FORMAT_MESSAGE_OPTIONS = 512u32; -pub type FORMAT_MESSAGE_OPTIONS = u32; -pub const FRS_ERR_SYSVOL_POPULATE_TIMEOUT: i32 = 8014i32; -pub const FSCTL_GET_REPARSE_POINT: u32 = 589992u32; -pub const FSCTL_SET_REPARSE_POINT: u32 = 589988u32; -pub const FileAlignmentInfo: FILE_INFO_BY_HANDLE_CLASS = 17i32; -pub const FileAllocationInfo: FILE_INFO_BY_HANDLE_CLASS = 5i32; -pub const FileAttributeTagInfo: FILE_INFO_BY_HANDLE_CLASS = 9i32; -pub const FileBasicInfo: FILE_INFO_BY_HANDLE_CLASS = 0i32; -pub const FileCaseSensitiveInfo: FILE_INFO_BY_HANDLE_CLASS = 23i32; -pub const FileCompressionInfo: FILE_INFO_BY_HANDLE_CLASS = 8i32; -pub const FileDispositionInfo: FILE_INFO_BY_HANDLE_CLASS = 4i32; -pub const FileDispositionInfoEx: FILE_INFO_BY_HANDLE_CLASS = 21i32; -pub const FileEndOfFileInfo: FILE_INFO_BY_HANDLE_CLASS = 6i32; -pub const FileFullDirectoryInfo: FILE_INFO_BY_HANDLE_CLASS = 14i32; -pub const FileFullDirectoryRestartInfo: FILE_INFO_BY_HANDLE_CLASS = 15i32; -pub const FileIdBothDirectoryInfo: FILE_INFO_BY_HANDLE_CLASS = 10i32; -pub const FileIdBothDirectoryRestartInfo: FILE_INFO_BY_HANDLE_CLASS = 11i32; -pub const FileIdExtdDirectoryInfo: FILE_INFO_BY_HANDLE_CLASS = 19i32; -pub const FileIdExtdDirectoryRestartInfo: FILE_INFO_BY_HANDLE_CLASS = 20i32; -pub const FileIdInfo: FILE_INFO_BY_HANDLE_CLASS = 18i32; -pub const FileIoPriorityHintInfo: FILE_INFO_BY_HANDLE_CLASS = 12i32; -pub const FileNameInfo: FILE_INFO_BY_HANDLE_CLASS = 2i32; -pub const FileNormalizedNameInfo: FILE_INFO_BY_HANDLE_CLASS = 24i32; -pub const FileRemoteProtocolInfo: FILE_INFO_BY_HANDLE_CLASS = 13i32; -pub const FileRenameInfo: FILE_INFO_BY_HANDLE_CLASS = 3i32; -pub const FileRenameInfoEx: FILE_INFO_BY_HANDLE_CLASS = 22i32; -pub const FileStandardInfo: FILE_INFO_BY_HANDLE_CLASS = 1i32; -pub const FileStorageInfo: FILE_INFO_BY_HANDLE_CLASS = 16i32; -pub const FileStreamInfo: FILE_INFO_BY_HANDLE_CLASS = 7i32; -pub type GENERIC_ACCESS_RIGHTS = u32; -pub const GENERIC_ALL: GENERIC_ACCESS_RIGHTS = 268435456u32; -pub const GENERIC_EXECUTE: GENERIC_ACCESS_RIGHTS = 536870912u32; -pub const GENERIC_READ: GENERIC_ACCESS_RIGHTS = 2147483648u32; -pub const GENERIC_WRITE: GENERIC_ACCESS_RIGHTS = 1073741824u32; -pub type GETFINALPATHNAMEBYHANDLE_FLAGS = u32; -#[repr(C)] -pub struct GUID { - pub data1: u32, - pub data2: u16, - pub data3: u16, - pub data4: [u8; 8], -} -impl ::core::marker::Copy for GUID {} -impl ::core::clone::Clone for GUID { - fn clone(&self) -> Self { - *self - } -} -impl GUID { - pub const fn from_u128(uuid: u128) -> Self { - Self { - data1: (uuid >> 96) as u32, - data2: (uuid >> 80 & 0xffff) as u16, - data3: (uuid >> 64 & 0xffff) as u16, - data4: (uuid as u64).to_be_bytes(), - } - } -} -pub type HANDLE = *mut ::core::ffi::c_void; -pub type HANDLE_FLAGS = u32; -pub const HANDLE_FLAG_INHERIT: HANDLE_FLAGS = 1u32; -pub const HANDLE_FLAG_PROTECT_FROM_CLOSE: HANDLE_FLAGS = 2u32; -pub const HIGH_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 128u32; -pub type HMODULE = *mut ::core::ffi::c_void; -pub type HRESULT = i32; -pub const IDLE_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 64u32; -#[repr(C)] -pub struct IN6_ADDR { - pub u: IN6_ADDR_0, -} -impl ::core::marker::Copy for IN6_ADDR {} -impl ::core::clone::Clone for IN6_ADDR { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub union IN6_ADDR_0 { - pub Byte: [u8; 16], - pub Word: [u16; 8], -} -impl ::core::marker::Copy for IN6_ADDR_0 {} -impl ::core::clone::Clone for IN6_ADDR_0 { - fn clone(&self) -> Self { - *self - } -} -pub const INFINITE: u32 = 4294967295u32; -pub const INHERIT_CALLER_PRIORITY: PROCESS_CREATION_FLAGS = 131072u32; -pub const INHERIT_PARENT_AFFINITY: PROCESS_CREATION_FLAGS = 65536u32; -#[repr(C)] -pub union INIT_ONCE { - pub Ptr: *mut ::core::ffi::c_void, -} -impl ::core::marker::Copy for INIT_ONCE {} -impl ::core::clone::Clone for INIT_ONCE { - fn clone(&self) -> Self { - *self - } -} -pub const INIT_ONCE_INIT_FAILED: u32 = 4u32; -pub const INVALID_FILE_ATTRIBUTES: u32 = 4294967295u32; -pub const INVALID_SOCKET: SOCKET = -1i32 as _; -#[repr(C)] -pub struct IN_ADDR { - pub S_un: IN_ADDR_0, -} -impl ::core::marker::Copy for IN_ADDR {} -impl ::core::clone::Clone for IN_ADDR { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub union IN_ADDR_0 { - pub S_un_b: IN_ADDR_0_0, - pub S_un_w: IN_ADDR_0_1, - pub S_addr: u32, -} -impl ::core::marker::Copy for IN_ADDR_0 {} -impl ::core::clone::Clone for IN_ADDR_0 { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub struct IN_ADDR_0_0 { - pub s_b1: u8, - pub s_b2: u8, - pub s_b3: u8, - pub s_b4: u8, -} -impl ::core::marker::Copy for IN_ADDR_0_0 {} -impl ::core::clone::Clone for IN_ADDR_0_0 { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub struct IN_ADDR_0_1 { - pub s_w1: u16, - pub s_w2: u16, -} -impl ::core::marker::Copy for IN_ADDR_0_1 {} -impl ::core::clone::Clone for IN_ADDR_0_1 { - fn clone(&self) -> Self { - *self - } -} -pub const IO_REPARSE_TAG_MOUNT_POINT: u32 = 2684354563u32; -pub const IO_REPARSE_TAG_SYMLINK: u32 = 2684354572u32; -#[repr(C)] -pub struct IO_STATUS_BLOCK { - pub Anonymous: IO_STATUS_BLOCK_0, - pub Information: usize, -} -impl ::core::marker::Copy for IO_STATUS_BLOCK {} -impl ::core::clone::Clone for IO_STATUS_BLOCK { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub union IO_STATUS_BLOCK_0 { - pub Status: NTSTATUS, - pub Pointer: *mut ::core::ffi::c_void, -} -impl ::core::marker::Copy for IO_STATUS_BLOCK_0 {} -impl ::core::clone::Clone for IO_STATUS_BLOCK_0 { - fn clone(&self) -> Self { - *self - } -} -pub type IPPROTO = i32; -pub const IPPROTO_AH: IPPROTO = 51i32; -pub const IPPROTO_CBT: IPPROTO = 7i32; -pub const IPPROTO_DSTOPTS: IPPROTO = 60i32; -pub const IPPROTO_EGP: IPPROTO = 8i32; -pub const IPPROTO_ESP: IPPROTO = 50i32; -pub const IPPROTO_FRAGMENT: IPPROTO = 44i32; -pub const IPPROTO_GGP: IPPROTO = 3i32; -pub const IPPROTO_HOPOPTS: IPPROTO = 0i32; -pub const IPPROTO_ICLFXBM: IPPROTO = 78i32; -pub const IPPROTO_ICMP: IPPROTO = 1i32; -pub const IPPROTO_ICMPV6: IPPROTO = 58i32; -pub const IPPROTO_IDP: IPPROTO = 22i32; -pub const IPPROTO_IGMP: IPPROTO = 2i32; -pub const IPPROTO_IGP: IPPROTO = 9i32; -pub const IPPROTO_IP: IPPROTO = 0i32; -pub const IPPROTO_IPV4: IPPROTO = 4i32; -pub const IPPROTO_IPV6: IPPROTO = 41i32; -pub const IPPROTO_L2TP: IPPROTO = 115i32; -pub const IPPROTO_MAX: IPPROTO = 256i32; -pub const IPPROTO_ND: IPPROTO = 77i32; -pub const IPPROTO_NONE: IPPROTO = 59i32; -pub const IPPROTO_PGM: IPPROTO = 113i32; -pub const IPPROTO_PIM: IPPROTO = 103i32; -pub const IPPROTO_PUP: IPPROTO = 12i32; -pub const IPPROTO_RAW: IPPROTO = 255i32; -pub const IPPROTO_RDP: IPPROTO = 27i32; -pub const IPPROTO_RESERVED_IPSEC: IPPROTO = 258i32; -pub const IPPROTO_RESERVED_IPSECOFFLOAD: IPPROTO = 259i32; -pub const IPPROTO_RESERVED_MAX: IPPROTO = 261i32; -pub const IPPROTO_RESERVED_RAW: IPPROTO = 257i32; -pub const IPPROTO_RESERVED_WNV: IPPROTO = 260i32; -pub const IPPROTO_RM: IPPROTO = 113i32; -pub const IPPROTO_ROUTING: IPPROTO = 43i32; -pub const IPPROTO_SCTP: IPPROTO = 132i32; -pub const IPPROTO_ST: IPPROTO = 5i32; -pub const IPPROTO_TCP: IPPROTO = 6i32; -pub const IPPROTO_UDP: IPPROTO = 17i32; -pub const IPV6_ADD_MEMBERSHIP: i32 = 12i32; -pub const IPV6_DROP_MEMBERSHIP: i32 = 13i32; -#[repr(C)] -pub struct IPV6_MREQ { - pub ipv6mr_multiaddr: IN6_ADDR, - pub ipv6mr_interface: u32, -} -impl ::core::marker::Copy for IPV6_MREQ {} -impl ::core::clone::Clone for IPV6_MREQ { - fn clone(&self) -> Self { - *self - } -} -pub const IPV6_MULTICAST_LOOP: i32 = 11i32; -pub const IPV6_V6ONLY: i32 = 27i32; -pub const IP_ADD_MEMBERSHIP: i32 = 12i32; -pub const IP_DROP_MEMBERSHIP: i32 = 13i32; -#[repr(C)] -pub struct IP_MREQ { - pub imr_multiaddr: IN_ADDR, - pub imr_interface: IN_ADDR, -} -impl ::core::marker::Copy for IP_MREQ {} -impl ::core::clone::Clone for IP_MREQ { - fn clone(&self) -> Self { - *self - } -} -pub const IP_MULTICAST_LOOP: i32 = 11i32; -pub const IP_MULTICAST_TTL: i32 = 10i32; -pub const IP_TTL: i32 = 4i32; -#[repr(C)] -pub struct LINGER { - pub l_onoff: u16, - pub l_linger: u16, -} -impl ::core::marker::Copy for LINGER {} -impl ::core::clone::Clone for LINGER { - fn clone(&self) -> Self { - *self - } -} -pub type LPOVERLAPPED_COMPLETION_ROUTINE = ::core::option::Option< - unsafe extern "system" fn( - dwerrorcode: u32, - dwnumberofbytestransfered: u32, - lpoverlapped: *mut OVERLAPPED, - ) -> (), ->; -pub type LPPROC_THREAD_ATTRIBUTE_LIST = *mut ::core::ffi::c_void; -pub type LPPROGRESS_ROUTINE = ::core::option::Option< - unsafe extern "system" fn( - totalfilesize: i64, - totalbytestransferred: i64, - streamsize: i64, - streambytestransferred: i64, - dwstreamnumber: u32, - dwcallbackreason: LPPROGRESS_ROUTINE_CALLBACK_REASON, - hsourcefile: HANDLE, - hdestinationfile: HANDLE, - lpdata: *const ::core::ffi::c_void, - ) -> u32, ->; -pub type LPPROGRESS_ROUTINE_CALLBACK_REASON = u32; -pub type LPTHREAD_START_ROUTINE = ::core::option::Option< - unsafe extern "system" fn(lpthreadparameter: *mut ::core::ffi::c_void) -> u32, ->; -pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = ::core::option::Option< - unsafe extern "system" fn( - dwerror: u32, - cbtransferred: u32, - lpoverlapped: *mut OVERLAPPED, - dwflags: u32, - ) -> (), ->; -#[repr(C)] -pub struct M128A { - pub Low: u64, - pub High: i64, -} -impl ::core::marker::Copy for M128A {} -impl ::core::clone::Clone for M128A { - fn clone(&self) -> Self { - *self - } -} -pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: u32 = 16384u32; -pub const MAX_PATH: u32 = 260u32; -pub const MB_COMPOSITE: MULTI_BYTE_TO_WIDE_CHAR_FLAGS = 2u32; -pub const MB_ERR_INVALID_CHARS: MULTI_BYTE_TO_WIDE_CHAR_FLAGS = 8u32; -pub const MB_PRECOMPOSED: MULTI_BYTE_TO_WIDE_CHAR_FLAGS = 1u32; -pub const MB_USEGLYPHCHARS: MULTI_BYTE_TO_WIDE_CHAR_FLAGS = 4u32; -pub const MOVEFILE_COPY_ALLOWED: MOVE_FILE_FLAGS = 2u32; -pub const MOVEFILE_CREATE_HARDLINK: MOVE_FILE_FLAGS = 16u32; -pub const MOVEFILE_DELAY_UNTIL_REBOOT: MOVE_FILE_FLAGS = 4u32; -pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE: MOVE_FILE_FLAGS = 32u32; -pub const MOVEFILE_REPLACE_EXISTING: MOVE_FILE_FLAGS = 1u32; -pub const MOVEFILE_WRITE_THROUGH: MOVE_FILE_FLAGS = 8u32; -pub type MOVE_FILE_FLAGS = u32; -pub const MSG_DONTROUTE: SEND_RECV_FLAGS = 4i32; -pub const MSG_OOB: SEND_RECV_FLAGS = 1i32; -pub const MSG_PEEK: SEND_RECV_FLAGS = 2i32; -pub const MSG_PUSH_IMMEDIATE: SEND_RECV_FLAGS = 32i32; -pub const MSG_WAITALL: SEND_RECV_FLAGS = 8i32; -pub type MULTI_BYTE_TO_WIDE_CHAR_FLAGS = u32; -pub const MaximumFileInfoByHandleClass: FILE_INFO_BY_HANDLE_CLASS = 25i32; -pub type NAMED_PIPE_MODE = u32; -pub const NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 32u32; -pub const NO_ERROR: WIN32_ERROR = 0u32; -pub type NTCREATEFILE_CREATE_DISPOSITION = u32; -pub type NTCREATEFILE_CREATE_OPTIONS = u32; -pub type NTSTATUS = i32; -#[repr(C)] -pub struct OBJECT_ATTRIBUTES { - pub Length: u32, - pub RootDirectory: HANDLE, - pub ObjectName: *const UNICODE_STRING, - pub Attributes: u32, - pub SecurityDescriptor: *const ::core::ffi::c_void, - pub SecurityQualityOfService: *const ::core::ffi::c_void, -} -impl ::core::marker::Copy for OBJECT_ATTRIBUTES {} -impl ::core::clone::Clone for OBJECT_ATTRIBUTES { - fn clone(&self) -> Self { - *self - } -} -pub const OBJ_DONT_REPARSE: i32 = 4096i32; -pub const OPEN_ALWAYS: FILE_CREATION_DISPOSITION = 4u32; -pub const OPEN_EXISTING: FILE_CREATION_DISPOSITION = 3u32; -#[repr(C)] -pub struct OVERLAPPED { - pub Internal: usize, - pub InternalHigh: usize, - pub Anonymous: OVERLAPPED_0, - pub hEvent: HANDLE, -} -impl ::core::marker::Copy for OVERLAPPED {} -impl ::core::clone::Clone for OVERLAPPED { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub union OVERLAPPED_0 { - pub Anonymous: OVERLAPPED_0_0, - pub Pointer: *mut ::core::ffi::c_void, -} -impl ::core::marker::Copy for OVERLAPPED_0 {} -impl ::core::clone::Clone for OVERLAPPED_0 { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub struct OVERLAPPED_0_0 { - pub Offset: u32, - pub OffsetHigh: u32, -} -impl ::core::marker::Copy for OVERLAPPED_0_0 {} -impl ::core::clone::Clone for OVERLAPPED_0_0 { - fn clone(&self) -> Self { - *self - } -} -pub type PCSTR = *const u8; -pub type PCWSTR = *const u16; -pub type PIO_APC_ROUTINE = ::core::option::Option< - unsafe extern "system" fn( - apccontext: *mut ::core::ffi::c_void, - iostatusblock: *mut IO_STATUS_BLOCK, - reserved: u32, - ) -> (), ->; -pub const PIPE_ACCEPT_REMOTE_CLIENTS: NAMED_PIPE_MODE = 0u32; -pub const PIPE_ACCESS_DUPLEX: FILE_FLAGS_AND_ATTRIBUTES = 3u32; -pub const PIPE_ACCESS_INBOUND: FILE_FLAGS_AND_ATTRIBUTES = 1u32; -pub const PIPE_ACCESS_OUTBOUND: FILE_FLAGS_AND_ATTRIBUTES = 2u32; -pub const PIPE_CLIENT_END: NAMED_PIPE_MODE = 0u32; -pub const PIPE_NOWAIT: NAMED_PIPE_MODE = 1u32; -pub const PIPE_READMODE_BYTE: NAMED_PIPE_MODE = 0u32; -pub const PIPE_READMODE_MESSAGE: NAMED_PIPE_MODE = 2u32; -pub const PIPE_REJECT_REMOTE_CLIENTS: NAMED_PIPE_MODE = 8u32; -pub const PIPE_SERVER_END: NAMED_PIPE_MODE = 1u32; -pub const PIPE_TYPE_BYTE: NAMED_PIPE_MODE = 0u32; -pub const PIPE_TYPE_MESSAGE: NAMED_PIPE_MODE = 4u32; -pub const PIPE_WAIT: NAMED_PIPE_MODE = 0u32; -pub type PRIORITY_HINT = i32; -pub type PROCESSOR_ARCHITECTURE = u16; -pub type PROCESS_CREATION_FLAGS = u32; -#[repr(C)] -pub struct PROCESS_INFORMATION { - pub hProcess: HANDLE, - pub hThread: HANDLE, - pub dwProcessId: u32, - pub dwThreadId: u32, -} -impl ::core::marker::Copy for PROCESS_INFORMATION {} -impl ::core::clone::Clone for PROCESS_INFORMATION { - fn clone(&self) -> Self { - *self - } -} -pub const PROCESS_MODE_BACKGROUND_BEGIN: PROCESS_CREATION_FLAGS = 1048576u32; -pub const PROCESS_MODE_BACKGROUND_END: PROCESS_CREATION_FLAGS = 2097152u32; -pub const PROFILE_KERNEL: PROCESS_CREATION_FLAGS = 536870912u32; -pub const PROFILE_SERVER: PROCESS_CREATION_FLAGS = 1073741824u32; -pub const PROFILE_USER: PROCESS_CREATION_FLAGS = 268435456u32; -pub const PROGRESS_CONTINUE: u32 = 0u32; -pub type PSTR = *mut u8; -pub type PTIMERAPCROUTINE = ::core::option::Option< - unsafe extern "system" fn( - lpargtocompletionroutine: *const ::core::ffi::c_void, - dwtimerlowvalue: u32, - dwtimerhighvalue: u32, - ) -> (), ->; -pub type PWSTR = *mut u16; -pub const READ_CONTROL: FILE_ACCESS_RIGHTS = 131072u32; -pub const REALTIME_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 256u32; -pub const SD_BOTH: WINSOCK_SHUTDOWN_HOW = 2i32; -pub const SD_RECEIVE: WINSOCK_SHUTDOWN_HOW = 0i32; -pub const SD_SEND: WINSOCK_SHUTDOWN_HOW = 1i32; -pub const SECURITY_ANONYMOUS: FILE_FLAGS_AND_ATTRIBUTES = 0u32; -#[repr(C)] -pub struct SECURITY_ATTRIBUTES { - pub nLength: u32, - pub lpSecurityDescriptor: *mut ::core::ffi::c_void, - pub bInheritHandle: BOOL, -} -impl ::core::marker::Copy for SECURITY_ATTRIBUTES {} -impl ::core::clone::Clone for SECURITY_ATTRIBUTES { - fn clone(&self) -> Self { - *self - } -} -pub const SECURITY_CONTEXT_TRACKING: FILE_FLAGS_AND_ATTRIBUTES = 262144u32; -pub const SECURITY_DELEGATION: FILE_FLAGS_AND_ATTRIBUTES = 196608u32; -pub const SECURITY_EFFECTIVE_ONLY: FILE_FLAGS_AND_ATTRIBUTES = 524288u32; -pub const SECURITY_IDENTIFICATION: FILE_FLAGS_AND_ATTRIBUTES = 65536u32; -pub const SECURITY_IMPERSONATION: FILE_FLAGS_AND_ATTRIBUTES = 131072u32; -pub const SECURITY_SQOS_PRESENT: FILE_FLAGS_AND_ATTRIBUTES = 1048576u32; -pub const SECURITY_VALID_SQOS_FLAGS: FILE_FLAGS_AND_ATTRIBUTES = 2031616u32; -pub type SEND_RECV_FLAGS = i32; -pub type SET_FILE_POINTER_MOVE_METHOD = u32; -#[repr(C)] -pub struct SOCKADDR { - pub sa_family: ADDRESS_FAMILY, - pub sa_data: [u8; 14], -} -impl ::core::marker::Copy for SOCKADDR {} -impl ::core::clone::Clone for SOCKADDR { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub struct SOCKADDR_UN { - pub sun_family: ADDRESS_FAMILY, - pub sun_path: [u8; 108], -} -impl ::core::marker::Copy for SOCKADDR_UN {} -impl ::core::clone::Clone for SOCKADDR_UN { - fn clone(&self) -> Self { - *self - } -} -pub type SOCKET = usize; -pub const SOCKET_ERROR: i32 = -1i32; -pub const SOCK_DGRAM: WINSOCK_SOCKET_TYPE = 2i32; -pub const SOCK_RAW: WINSOCK_SOCKET_TYPE = 3i32; -pub const SOCK_RDM: WINSOCK_SOCKET_TYPE = 4i32; -pub const SOCK_SEQPACKET: WINSOCK_SOCKET_TYPE = 5i32; -pub const SOCK_STREAM: WINSOCK_SOCKET_TYPE = 1i32; -pub const SOL_SOCKET: i32 = 65535i32; -pub const SO_BROADCAST: i32 = 32i32; -pub const SO_ERROR: i32 = 4103i32; -pub const SO_LINGER: i32 = 128i32; -pub const SO_RCVTIMEO: i32 = 4102i32; -pub const SO_SNDTIMEO: i32 = 4101i32; -pub const SPECIFIC_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 65535u32; -#[repr(C)] -pub struct SRWLOCK { - pub Ptr: *mut ::core::ffi::c_void, -} -impl ::core::marker::Copy for SRWLOCK {} -impl ::core::clone::Clone for SRWLOCK { - fn clone(&self) -> Self { - *self - } -} -pub const STACK_SIZE_PARAM_IS_A_RESERVATION: THREAD_CREATION_FLAGS = 65536u32; -pub const STANDARD_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 2031616u32; -pub const STANDARD_RIGHTS_EXECUTE: FILE_ACCESS_RIGHTS = 131072u32; -pub const STANDARD_RIGHTS_READ: FILE_ACCESS_RIGHTS = 131072u32; -pub const STANDARD_RIGHTS_REQUIRED: FILE_ACCESS_RIGHTS = 983040u32; -pub const STANDARD_RIGHTS_WRITE: FILE_ACCESS_RIGHTS = 131072u32; -pub const STARTF_FORCEOFFFEEDBACK: STARTUPINFOW_FLAGS = 128u32; -pub const STARTF_FORCEONFEEDBACK: STARTUPINFOW_FLAGS = 64u32; -pub const STARTF_PREVENTPINNING: STARTUPINFOW_FLAGS = 8192u32; -pub const STARTF_RUNFULLSCREEN: STARTUPINFOW_FLAGS = 32u32; -pub const STARTF_TITLEISAPPID: STARTUPINFOW_FLAGS = 4096u32; -pub const STARTF_TITLEISLINKNAME: STARTUPINFOW_FLAGS = 2048u32; -pub const STARTF_UNTRUSTEDSOURCE: STARTUPINFOW_FLAGS = 32768u32; -pub const STARTF_USECOUNTCHARS: STARTUPINFOW_FLAGS = 8u32; -pub const STARTF_USEFILLATTRIBUTE: STARTUPINFOW_FLAGS = 16u32; -pub const STARTF_USEHOTKEY: STARTUPINFOW_FLAGS = 512u32; -pub const STARTF_USEPOSITION: STARTUPINFOW_FLAGS = 4u32; -pub const STARTF_USESHOWWINDOW: STARTUPINFOW_FLAGS = 1u32; -pub const STARTF_USESIZE: STARTUPINFOW_FLAGS = 2u32; -pub const STARTF_USESTDHANDLES: STARTUPINFOW_FLAGS = 256u32; -#[repr(C)] -pub struct STARTUPINFOEXW { - pub StartupInfo: STARTUPINFOW, - pub lpAttributeList: LPPROC_THREAD_ATTRIBUTE_LIST, -} -impl ::core::marker::Copy for STARTUPINFOEXW {} -impl ::core::clone::Clone for STARTUPINFOEXW { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub struct STARTUPINFOW { - pub cb: u32, - pub lpReserved: PWSTR, - pub lpDesktop: PWSTR, - pub lpTitle: PWSTR, - pub dwX: u32, - pub dwY: u32, - pub dwXSize: u32, - pub dwYSize: u32, - pub dwXCountChars: u32, - pub dwYCountChars: u32, - pub dwFillAttribute: u32, - pub dwFlags: STARTUPINFOW_FLAGS, - pub wShowWindow: u16, - pub cbReserved2: u16, - pub lpReserved2: *mut u8, - pub hStdInput: HANDLE, - pub hStdOutput: HANDLE, - pub hStdError: HANDLE, -} -impl ::core::marker::Copy for STARTUPINFOW {} -impl ::core::clone::Clone for STARTUPINFOW { - fn clone(&self) -> Self { - *self - } -} -pub type STARTUPINFOW_FLAGS = u32; -pub const STATUS_DELETE_PENDING: NTSTATUS = -1073741738i32; -pub const STATUS_END_OF_FILE: NTSTATUS = -1073741807i32; -pub const STATUS_INVALID_PARAMETER: NTSTATUS = -1073741811i32; -pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = -1073741822i32; -pub const STATUS_PENDING: NTSTATUS = 259i32; -pub const STATUS_SUCCESS: NTSTATUS = 0i32; -pub const STD_ERROR_HANDLE: STD_HANDLE = 4294967284u32; -pub type STD_HANDLE = u32; -pub const STD_INPUT_HANDLE: STD_HANDLE = 4294967286u32; -pub const STD_OUTPUT_HANDLE: STD_HANDLE = 4294967285u32; -pub type SYMBOLIC_LINK_FLAGS = u32; -pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: SYMBOLIC_LINK_FLAGS = 2u32; -pub const SYMBOLIC_LINK_FLAG_DIRECTORY: SYMBOLIC_LINK_FLAGS = 1u32; -pub const SYMLINK_FLAG_RELATIVE: u32 = 1u32; -pub type SYNCHRONIZATION_ACCESS_RIGHTS = u32; -pub const SYNCHRONIZE: FILE_ACCESS_RIGHTS = 1048576u32; -#[repr(C)] -pub struct SYSTEM_INFO { - pub Anonymous: SYSTEM_INFO_0, - pub dwPageSize: u32, - pub lpMinimumApplicationAddress: *mut ::core::ffi::c_void, - pub lpMaximumApplicationAddress: *mut ::core::ffi::c_void, - pub dwActiveProcessorMask: usize, - pub dwNumberOfProcessors: u32, - pub dwProcessorType: u32, - pub dwAllocationGranularity: u32, - pub wProcessorLevel: u16, - pub wProcessorRevision: u16, -} -impl ::core::marker::Copy for SYSTEM_INFO {} -impl ::core::clone::Clone for SYSTEM_INFO { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub union SYSTEM_INFO_0 { - pub dwOemId: u32, - pub Anonymous: SYSTEM_INFO_0_0, -} -impl ::core::marker::Copy for SYSTEM_INFO_0 {} -impl ::core::clone::Clone for SYSTEM_INFO_0 { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub struct SYSTEM_INFO_0_0 { - pub wProcessorArchitecture: PROCESSOR_ARCHITECTURE, - pub wReserved: u16, -} -impl ::core::marker::Copy for SYSTEM_INFO_0_0 {} -impl ::core::clone::Clone for SYSTEM_INFO_0_0 { - fn clone(&self) -> Self { - *self - } -} -pub const TCP_NODELAY: i32 = 1i32; -pub const THREAD_CREATE_RUN_IMMEDIATELY: THREAD_CREATION_FLAGS = 0u32; -pub const THREAD_CREATE_SUSPENDED: THREAD_CREATION_FLAGS = 4u32; -pub type THREAD_CREATION_FLAGS = u32; -pub const TIMER_ALL_ACCESS: SYNCHRONIZATION_ACCESS_RIGHTS = 2031619u32; -pub const TIMER_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32; -#[repr(C)] -pub struct TIMEVAL { - pub tv_sec: i32, - pub tv_usec: i32, -} -impl ::core::marker::Copy for TIMEVAL {} -impl ::core::clone::Clone for TIMEVAL { - fn clone(&self) -> Self { - *self - } -} -pub const TLS_OUT_OF_INDEXES: u32 = 4294967295u32; -pub type TOKEN_ACCESS_MASK = u32; -pub const TOKEN_ACCESS_PSEUDO_HANDLE: TOKEN_ACCESS_MASK = 24u32; -pub const TOKEN_ACCESS_PSEUDO_HANDLE_WIN8: TOKEN_ACCESS_MASK = 24u32; -pub const TOKEN_ACCESS_SYSTEM_SECURITY: TOKEN_ACCESS_MASK = 16777216u32; -pub const TOKEN_ADJUST_DEFAULT: TOKEN_ACCESS_MASK = 128u32; -pub const TOKEN_ADJUST_GROUPS: TOKEN_ACCESS_MASK = 64u32; -pub const TOKEN_ADJUST_PRIVILEGES: TOKEN_ACCESS_MASK = 32u32; -pub const TOKEN_ADJUST_SESSIONID: TOKEN_ACCESS_MASK = 256u32; -pub const TOKEN_ALL_ACCESS: TOKEN_ACCESS_MASK = 983551u32; -pub const TOKEN_ASSIGN_PRIMARY: TOKEN_ACCESS_MASK = 1u32; -pub const TOKEN_DELETE: TOKEN_ACCESS_MASK = 65536u32; -pub const TOKEN_DUPLICATE: TOKEN_ACCESS_MASK = 2u32; -pub const TOKEN_EXECUTE: TOKEN_ACCESS_MASK = 131072u32; -pub const TOKEN_IMPERSONATE: TOKEN_ACCESS_MASK = 4u32; -pub const TOKEN_QUERY: TOKEN_ACCESS_MASK = 8u32; -pub const TOKEN_QUERY_SOURCE: TOKEN_ACCESS_MASK = 16u32; -pub const TOKEN_READ: TOKEN_ACCESS_MASK = 131080u32; -pub const TOKEN_READ_CONTROL: TOKEN_ACCESS_MASK = 131072u32; -pub const TOKEN_TRUST_CONSTRAINT_MASK: TOKEN_ACCESS_MASK = 131096u32; -pub const TOKEN_WRITE: TOKEN_ACCESS_MASK = 131296u32; -pub const TOKEN_WRITE_DAC: TOKEN_ACCESS_MASK = 262144u32; -pub const TOKEN_WRITE_OWNER: TOKEN_ACCESS_MASK = 524288u32; -pub const TRUE: BOOL = 1i32; -pub const TRUNCATE_EXISTING: FILE_CREATION_DISPOSITION = 5u32; -#[repr(C)] -pub struct UNICODE_STRING { - pub Length: u16, - pub MaximumLength: u16, - pub Buffer: PWSTR, -} -impl ::core::marker::Copy for UNICODE_STRING {} -impl ::core::clone::Clone for UNICODE_STRING { - fn clone(&self) -> Self { - *self - } -} -pub const VOLUME_NAME_DOS: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32; -pub const VOLUME_NAME_GUID: GETFINALPATHNAMEBYHANDLE_FLAGS = 1u32; -pub const VOLUME_NAME_NONE: GETFINALPATHNAMEBYHANDLE_FLAGS = 4u32; -pub const WAIT_ABANDONED: WAIT_EVENT = 128u32; -pub const WAIT_ABANDONED_0: WAIT_EVENT = 128u32; -pub type WAIT_EVENT = u32; -pub const WAIT_FAILED: WAIT_EVENT = 4294967295u32; -pub const WAIT_IO_COMPLETION: WAIT_EVENT = 192u32; -pub const WAIT_OBJECT_0: WAIT_EVENT = 0u32; -pub const WAIT_TIMEOUT: WAIT_EVENT = 258u32; -pub const WC_ERR_INVALID_CHARS: u32 = 128u32; -pub type WIN32_ERROR = u32; -#[repr(C)] -pub struct WIN32_FIND_DATAW { - pub dwFileAttributes: u32, - pub ftCreationTime: FILETIME, - pub ftLastAccessTime: FILETIME, - pub ftLastWriteTime: FILETIME, - pub nFileSizeHigh: u32, - pub nFileSizeLow: u32, - pub dwReserved0: u32, - pub dwReserved1: u32, - pub cFileName: [u16; 260], - pub cAlternateFileName: [u16; 14], -} -impl ::core::marker::Copy for WIN32_FIND_DATAW {} -impl ::core::clone::Clone for WIN32_FIND_DATAW { - fn clone(&self) -> Self { - *self - } -} -pub type WINSOCK_SHUTDOWN_HOW = i32; -pub type WINSOCK_SOCKET_TYPE = i32; -pub const WRITE_DAC: FILE_ACCESS_RIGHTS = 262144u32; -pub const WRITE_OWNER: FILE_ACCESS_RIGHTS = 524288u32; -pub const WSABASEERR: WSA_ERROR = 10000i32; -#[repr(C)] -pub struct WSABUF { - pub len: u32, - pub buf: PSTR, -} -impl ::core::marker::Copy for WSABUF {} -impl ::core::clone::Clone for WSABUF { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] -pub struct WSADATA { - pub wVersion: u16, - pub wHighVersion: u16, - pub iMaxSockets: u16, - pub iMaxUdpDg: u16, - pub lpVendorInfo: PSTR, - pub szDescription: [u8; 257], - pub szSystemStatus: [u8; 129], -} -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] -impl ::core::marker::Copy for WSADATA {} -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] -impl ::core::clone::Clone for WSADATA { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -#[cfg(target_arch = "x86")] -pub struct WSADATA { - pub wVersion: u16, - pub wHighVersion: u16, - pub szDescription: [u8; 257], - pub szSystemStatus: [u8; 129], - pub iMaxSockets: u16, - pub iMaxUdpDg: u16, - pub lpVendorInfo: PSTR, -} -#[cfg(target_arch = "x86")] -impl ::core::marker::Copy for WSADATA {} -#[cfg(target_arch = "x86")] -impl ::core::clone::Clone for WSADATA { - fn clone(&self) -> Self { - *self - } -} -pub const WSAEACCES: WSA_ERROR = 10013i32; -pub const WSAEADDRINUSE: WSA_ERROR = 10048i32; -pub const WSAEADDRNOTAVAIL: WSA_ERROR = 10049i32; -pub const WSAEAFNOSUPPORT: WSA_ERROR = 10047i32; -pub const WSAEALREADY: WSA_ERROR = 10037i32; -pub const WSAEBADF: WSA_ERROR = 10009i32; -pub const WSAECANCELLED: WSA_ERROR = 10103i32; -pub const WSAECONNABORTED: WSA_ERROR = 10053i32; -pub const WSAECONNREFUSED: WSA_ERROR = 10061i32; -pub const WSAECONNRESET: WSA_ERROR = 10054i32; -pub const WSAEDESTADDRREQ: WSA_ERROR = 10039i32; -pub const WSAEDISCON: WSA_ERROR = 10101i32; -pub const WSAEDQUOT: WSA_ERROR = 10069i32; -pub const WSAEFAULT: WSA_ERROR = 10014i32; -pub const WSAEHOSTDOWN: WSA_ERROR = 10064i32; -pub const WSAEHOSTUNREACH: WSA_ERROR = 10065i32; -pub const WSAEINPROGRESS: WSA_ERROR = 10036i32; -pub const WSAEINTR: WSA_ERROR = 10004i32; -pub const WSAEINVAL: WSA_ERROR = 10022i32; -pub const WSAEINVALIDPROCTABLE: WSA_ERROR = 10104i32; -pub const WSAEINVALIDPROVIDER: WSA_ERROR = 10105i32; -pub const WSAEISCONN: WSA_ERROR = 10056i32; -pub const WSAELOOP: WSA_ERROR = 10062i32; -pub const WSAEMFILE: WSA_ERROR = 10024i32; -pub const WSAEMSGSIZE: WSA_ERROR = 10040i32; -pub const WSAENAMETOOLONG: WSA_ERROR = 10063i32; -pub const WSAENETDOWN: WSA_ERROR = 10050i32; -pub const WSAENETRESET: WSA_ERROR = 10052i32; -pub const WSAENETUNREACH: WSA_ERROR = 10051i32; -pub const WSAENOBUFS: WSA_ERROR = 10055i32; -pub const WSAENOMORE: WSA_ERROR = 10102i32; -pub const WSAENOPROTOOPT: WSA_ERROR = 10042i32; -pub const WSAENOTCONN: WSA_ERROR = 10057i32; -pub const WSAENOTEMPTY: WSA_ERROR = 10066i32; -pub const WSAENOTSOCK: WSA_ERROR = 10038i32; -pub const WSAEOPNOTSUPP: WSA_ERROR = 10045i32; -pub const WSAEPFNOSUPPORT: WSA_ERROR = 10046i32; -pub const WSAEPROCLIM: WSA_ERROR = 10067i32; -pub const WSAEPROTONOSUPPORT: WSA_ERROR = 10043i32; -pub const WSAEPROTOTYPE: WSA_ERROR = 10041i32; -pub const WSAEPROVIDERFAILEDINIT: WSA_ERROR = 10106i32; -pub const WSAEREFUSED: WSA_ERROR = 10112i32; -pub const WSAEREMOTE: WSA_ERROR = 10071i32; -pub const WSAESHUTDOWN: WSA_ERROR = 10058i32; -pub const WSAESOCKTNOSUPPORT: WSA_ERROR = 10044i32; -pub const WSAESTALE: WSA_ERROR = 10070i32; -pub const WSAETIMEDOUT: WSA_ERROR = 10060i32; -pub const WSAETOOMANYREFS: WSA_ERROR = 10059i32; -pub const WSAEUSERS: WSA_ERROR = 10068i32; -pub const WSAEWOULDBLOCK: WSA_ERROR = 10035i32; -pub const WSAHOST_NOT_FOUND: WSA_ERROR = 11001i32; -pub const WSANOTINITIALISED: WSA_ERROR = 10093i32; -pub const WSANO_DATA: WSA_ERROR = 11004i32; -pub const WSANO_RECOVERY: WSA_ERROR = 11003i32; -#[repr(C)] -pub struct WSAPROTOCOLCHAIN { - pub ChainLen: i32, - pub ChainEntries: [u32; 7], -} -impl ::core::marker::Copy for WSAPROTOCOLCHAIN {} -impl ::core::clone::Clone for WSAPROTOCOLCHAIN { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub struct WSAPROTOCOL_INFOW { - pub dwServiceFlags1: u32, - pub dwServiceFlags2: u32, - pub dwServiceFlags3: u32, - pub dwServiceFlags4: u32, - pub dwProviderFlags: u32, - pub ProviderId: GUID, - pub dwCatalogEntryId: u32, - pub ProtocolChain: WSAPROTOCOLCHAIN, - pub iVersion: i32, - pub iAddressFamily: i32, - pub iMaxSockAddr: i32, - pub iMinSockAddr: i32, - pub iSocketType: i32, - pub iProtocol: i32, - pub iProtocolMaxOffset: i32, - pub iNetworkByteOrder: i32, - pub iSecurityScheme: i32, - pub dwMessageSize: u32, - pub dwProviderReserved: u32, - pub szProtocol: [u16; 256], -} -impl ::core::marker::Copy for WSAPROTOCOL_INFOW {} -impl ::core::clone::Clone for WSAPROTOCOL_INFOW { - fn clone(&self) -> Self { - *self - } -} -pub const WSASERVICE_NOT_FOUND: WSA_ERROR = 10108i32; -pub const WSASYSCALLFAILURE: WSA_ERROR = 10107i32; -pub const WSASYSNOTREADY: WSA_ERROR = 10091i32; -pub const WSATRY_AGAIN: WSA_ERROR = 11002i32; -pub const WSATYPE_NOT_FOUND: WSA_ERROR = 10109i32; -pub const WSAVERNOTSUPPORTED: WSA_ERROR = 10092i32; -pub type WSA_ERROR = i32; -pub const WSA_E_CANCELLED: WSA_ERROR = 10111i32; -pub const WSA_E_NO_MORE: WSA_ERROR = 10110i32; -pub const WSA_FLAG_NO_HANDLE_INHERIT: u32 = 128u32; -pub const WSA_FLAG_OVERLAPPED: u32 = 1u32; -pub const WSA_INVALID_HANDLE: WSA_ERROR = 6i32; -pub const WSA_INVALID_PARAMETER: WSA_ERROR = 87i32; -pub const WSA_IO_INCOMPLETE: WSA_ERROR = 996i32; -pub const WSA_IO_PENDING: WSA_ERROR = 997i32; -pub const WSA_IPSEC_NAME_POLICY_ERROR: WSA_ERROR = 11033i32; -pub const WSA_NOT_ENOUGH_MEMORY: WSA_ERROR = 8i32; -pub const WSA_OPERATION_ABORTED: WSA_ERROR = 995i32; -pub const WSA_QOS_ADMISSION_FAILURE: WSA_ERROR = 11010i32; -pub const WSA_QOS_BAD_OBJECT: WSA_ERROR = 11013i32; -pub const WSA_QOS_BAD_STYLE: WSA_ERROR = 11012i32; -pub const WSA_QOS_EFILTERCOUNT: WSA_ERROR = 11021i32; -pub const WSA_QOS_EFILTERSTYLE: WSA_ERROR = 11019i32; -pub const WSA_QOS_EFILTERTYPE: WSA_ERROR = 11020i32; -pub const WSA_QOS_EFLOWCOUNT: WSA_ERROR = 11023i32; -pub const WSA_QOS_EFLOWDESC: WSA_ERROR = 11026i32; -pub const WSA_QOS_EFLOWSPEC: WSA_ERROR = 11017i32; -pub const WSA_QOS_EOBJLENGTH: WSA_ERROR = 11022i32; -pub const WSA_QOS_EPOLICYOBJ: WSA_ERROR = 11025i32; -pub const WSA_QOS_EPROVSPECBUF: WSA_ERROR = 11018i32; -pub const WSA_QOS_EPSFILTERSPEC: WSA_ERROR = 11028i32; -pub const WSA_QOS_EPSFLOWSPEC: WSA_ERROR = 11027i32; -pub const WSA_QOS_ESDMODEOBJ: WSA_ERROR = 11029i32; -pub const WSA_QOS_ESERVICETYPE: WSA_ERROR = 11016i32; -pub const WSA_QOS_ESHAPERATEOBJ: WSA_ERROR = 11030i32; -pub const WSA_QOS_EUNKOWNPSOBJ: WSA_ERROR = 11024i32; -pub const WSA_QOS_GENERIC_ERROR: WSA_ERROR = 11015i32; -pub const WSA_QOS_NO_RECEIVERS: WSA_ERROR = 11008i32; -pub const WSA_QOS_NO_SENDERS: WSA_ERROR = 11007i32; -pub const WSA_QOS_POLICY_FAILURE: WSA_ERROR = 11011i32; -pub const WSA_QOS_RECEIVERS: WSA_ERROR = 11005i32; -pub const WSA_QOS_REQUEST_CONFIRMED: WSA_ERROR = 11009i32; -pub const WSA_QOS_RESERVED_PETYPE: WSA_ERROR = 11031i32; -pub const WSA_QOS_SENDERS: WSA_ERROR = 11006i32; -pub const WSA_QOS_TRAFFIC_CTRL_ERROR: WSA_ERROR = 11014i32; -pub const WSA_SECURE_HOST_NOT_FOUND: WSA_ERROR = 11032i32; -pub const WSA_WAIT_EVENT_0: WSA_ERROR = 0i32; -pub const WSA_WAIT_IO_COMPLETION: WSA_ERROR = 192i32; -#[repr(C)] -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] -pub struct XSAVE_FORMAT { - pub ControlWord: u16, - pub StatusWord: u16, - pub TagWord: u8, - pub Reserved1: u8, - pub ErrorOpcode: u16, - pub ErrorOffset: u32, - pub ErrorSelector: u16, - pub Reserved2: u16, - pub DataOffset: u32, - pub DataSelector: u16, - pub Reserved3: u16, - pub MxCsr: u32, - pub MxCsr_Mask: u32, - pub FloatRegisters: [M128A; 8], - pub XmmRegisters: [M128A; 16], - pub Reserved4: [u8; 96], -} -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] -impl ::core::marker::Copy for XSAVE_FORMAT {} -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] -impl ::core::clone::Clone for XSAVE_FORMAT { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -#[cfg(target_arch = "x86")] -pub struct XSAVE_FORMAT { - pub ControlWord: u16, - pub StatusWord: u16, - pub TagWord: u8, - pub Reserved1: u8, - pub ErrorOpcode: u16, - pub ErrorOffset: u32, - pub ErrorSelector: u16, - pub Reserved2: u16, - pub DataOffset: u32, - pub DataSelector: u16, - pub Reserved3: u16, - pub MxCsr: u32, - pub MxCsr_Mask: u32, - pub FloatRegisters: [M128A; 8], - pub XmmRegisters: [M128A; 8], - pub Reserved4: [u8; 224], -} -#[cfg(target_arch = "x86")] -impl ::core::marker::Copy for XSAVE_FORMAT {} -#[cfg(target_arch = "x86")] -impl ::core::clone::Clone for XSAVE_FORMAT { - fn clone(&self) -> Self { - *self - } -} diff --git a/library/std/src/sys/windows/cmath.rs b/library/std/src/sys/windows/cmath.rs deleted file mode 100644 index 36578d5a34e..00000000000 --- a/library/std/src/sys/windows/cmath.rs +++ /dev/null @@ -1,96 +0,0 @@ -#![cfg(not(test))] - -use core::ffi::{c_double, c_float, c_int}; - -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 fn tgamma(n: c_double) -> c_double; - pub fn tgammaf(n: c_float) -> c_float; - pub fn lgamma_r(n: c_double, s: &mut c_int) -> c_double; - pub fn lgammaf_r(n: c_float, s: &mut c_int) -> c_float; -} - -pub use self::shims::*; - -#[cfg(not(all(target_env = "msvc", target_arch = "x86")))] -mod shims { - use core::ffi::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 to 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 core::ffi::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 deleted file mode 100644 index f60b3a2c700..00000000000 --- a/library/std/src/sys/windows/compat.rs +++ /dev/null @@ -1,244 +0,0 @@ -//! 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 is implemented simply by storing a function pointer in an atomic. -//! Loading and calling this function will have little or no overhead -//! compared with calling any other dynamically imported function. -//! -//! The stored function pointer starts out as an importer function which will -//! swap itself with the real function when it's called for the first time. If -//! the real function can't be imported then a fallback function is used in its -//! place. While this is low cost for the happy path (where the function is -//! already loaded) it does mean there's some overhead the first time the -//! function is called. In the worst case, multiple threads may all end up -//! importing the same function unnecessarily. - -use crate::ffi::{c_void, CStr}; -use crate::ptr::NonNull; -use crate::sync::atomic::Ordering; -use crate::sys::c; - -// This uses a static initializer to preload some imported functions. -// The CRT (C runtime) executes static initializers before `main` -// is called (for binaries) and before `DllMain` is called (for DLLs). -// -// It works by contributing a global symbol to the `.CRT$XCT` section. -// The linker builds a table of all static initializer functions. -// The CRT startup code then iterates that table, calling each -// initializer function. -// -// NOTE: User code should instead use .CRT$XCU to reliably run after std's initializer. -// If you're reading this and would like a guarantee here, please -// file an issue for discussion; currently we don't guarantee any functionality -// before main. -// See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170 -#[used] -#[link_section = ".CRT$XCT"] -static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init; - -/// Preload some imported functions. -/// -/// Note that any functions included here will be unconditionally loaded in -/// the final binary, regardless of whether or not they're actually used. -/// -/// Therefore, this should be limited to `compat_fn_optional` functions which -/// must be preloaded or any functions where lazier loading demonstrates a -/// negative performance impact in practical situations. -/// -/// Currently we only preload `WaitOnAddress` and `WakeByAddressSingle`. -unsafe extern "C" fn init() { - // In an exe this code is executed before main() so is single threaded. - // In a DLL the system's loader lock will be held thereby synchronizing - // access. So the same best practices apply here as they do to running in DllMain: - // https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices - // - // 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. - - // Attempt to preload the synch functions. - load_synch_functions(); -} - -/// Helper macro for creating CStrs from literals and symbol names. -macro_rules! ansi_str { - (sym $ident:ident) => {{ 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) } -} - -/// 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); -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 { - // SAFETY: A CStr is always null terminated. - let module = c::GetModuleHandleA(name.as_ptr().cast::()); - NonNull::new(module).map(Self) - } - - // Try to get the address of a function. - pub fn proc_address(self, name: &CStr) -> Option> { - unsafe { - // SAFETY: - // `self.0` will always be a valid module. - // A CStr is always null terminated. - let proc = c::GetProcAddress(self.0.as_ptr(), name.as_ptr().cast::()); - // SAFETY: `GetProcAddress` returns None on null. - proc.map(|p| NonNull::new_unchecked(p as *mut c_void)) - } - } -} - -/// 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])* - $vis:vis 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 = 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) -> 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 - } - - #[inline(always)] - pub unsafe fn call($($argname: $argtype),*) -> $rettype { - let func: F = mem::transmute(PTR.load(Ordering::Relaxed)); - func($($argname),*) - } - } - $(#[$meta])* - $vis use $symbol::call as $symbol; - )*) -} - -/// Optionally loaded functions. -/// -/// Actual loading of the function defers to $load_functions. -macro_rules! compat_fn_optional { - ($load_functions:expr; - $( - $(#[$meta:meta])* - $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) $(-> $rettype:ty)?; - )+) => ( - $( - pub mod $symbol { - #[allow(unused_imports)] - use super::*; - use crate::ffi::c_void; - use crate::mem; - use crate::ptr::{self, NonNull}; - use crate::sync::atomic::{AtomicPtr, Ordering}; - - pub(in crate::sys) static PTR: AtomicPtr = AtomicPtr::new(ptr::null_mut()); - - type F = unsafe extern "system" fn($($argtype),*) $(-> $rettype)?; - - #[inline(always)] - pub fn option() -> Option { - // Miri does not understand the way we do preloading - // therefore load the function here instead. - #[cfg(miri)] $load_functions; - NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) }) - } - } - )+ - ) -} - -/// Load all needed functions from "api-ms-win-core-synch-l1-2-0". -pub(super) fn load_synch_functions() { - fn try_load() -> Option<()> { - const MODULE_NAME: &CStr = c"api-ms-win-core-synch-l1-2-0"; - const WAIT_ON_ADDRESS: &CStr = c"WaitOnAddress"; - const WAKE_BY_ADDRESS_SINGLE: &CStr = c"WakeByAddressSingle"; - - // Try loading the library and all the required functions. - // If any step fails, then they all fail. - let library = unsafe { Module::new(MODULE_NAME) }?; - let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?; - let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?; - - c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed); - c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed); - Some(()) - } - - try_load(); -} diff --git a/library/std/src/sys/windows/env.rs b/library/std/src/sys/windows/env.rs deleted file mode 100644 index f0a99d6200c..00000000000 --- a/library/std/src/sys/windows/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 42484543686..00000000000 --- a/library/std/src/sys/windows/fs.rs +++ /dev/null @@ -1,1528 +0,0 @@ -use crate::os::windows::prelude::*; - -use crate::borrow::Cow; -use crate::ffi::{c_void, OsString}; -use crate::fmt; -use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem::{self, MaybeUninit}; -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, Align8}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::thread; - -use super::path::maybe_verbatim; -use super::{api, to_u16s, IoResult}; - -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, - number_of_links: Option, - file_index: Option, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct FileType { - attributes: c::DWORD, - reparse_tag: c::DWORD, -} - -pub struct ReadDir { - handle: FindNextFileHandle, - root: Arc, - first: Option, -} - -struct FindNextFileHandle(c::HANDLE); - -unsafe impl Send for FindNextFileHandle {} -unsafe impl Sync for FindNextFileHandle {} - -pub struct DirEntry { - root: Arc, - 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, - 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, - modified: Option, - created: Option, -} - -impl fmt::Debug for c::FILETIME { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let time = ((self.dwHighDateTime as u64) << 32) | self.dwLowDateTime as u64; - f.debug_tuple("FILETIME").field(&time).finish() - } -} - -#[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; - fn next(&mut self) -> Option> { - if let Some(first) = self.first.take() { - if let Some(e) = DirEntry::new(&self.root, &first) { - return Some(Ok(e)); - } - } - unsafe { - let mut wfd = mem::zeroed(); - loop { - if c::FindNextFileW(self.handle.0, &mut wfd) == 0 { - if api::get_last_error().code == 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, wfd: &c::WIN32_FIND_DATAW) -> Option { - match &wfd.cFileName[0..3] { - // check for '.' and '..' - &[46, 0, ..] | &[46, 46, 0, ..] => return None, - _ => {} - } - - Some(DirEntry { root: root.clone(), data: *wfd }) - } - - pub fn path(&self) -> PathBuf { - self.root.join(self.file_name()) - } - - pub fn file_name(&self) -> OsString { - let filename = super::truncate_utf16_at_nul(&self.data.cFileName); - OsString::from_wide(filename) - } - - pub fn file_type(&self) -> io::Result { - Ok(FileType::new( - self.data.dwFileAttributes, - /* reparse_tag = */ self.data.dwReserved0, - )) - } - - pub fn metadata(&self) -> io::Result { - 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 { - 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 { - 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, - // `CREATE_ALWAYS` has weird semantics so we emulate it using - // `OPEN_ALWAYS` and a manual truncation step. See #115745. - (true, true, false) => c::OPEN_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 { - let path = maybe_verbatim(path)?; - let creation = opts.get_creation_mode()?; - let handle = unsafe { - c::CreateFileW( - path.as_ptr(), - opts.get_access_mode()?, - opts.share_mode, - opts.security_attributes, - creation, - opts.get_flags_and_attributes(), - ptr::null_mut(), - ) - }; - let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) }; - if let Ok(handle) = OwnedHandle::try_from(handle) { - // Manual truncation. See #115745. - if opts.truncate - && creation == c::OPEN_ALWAYS - && unsafe { c::GetLastError() } == c::ERROR_ALREADY_EXISTS - { - unsafe { - // This originally used `FileAllocationInfo` instead of - // `FileEndOfFileInfo` but that wasn't supported by WINE. - // It's arguable which fits the semantics of `OpenOptions` - // better so let's just use the more widely supported method. - let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 }; - let result = c::SetFileInformationByHandle( - handle.as_raw_handle(), - c::FileEndOfFileInfo, - ptr::addr_of!(eof).cast::(), - mem::size_of::() as u32, - ); - if result == 0 { - return Err(io::Error::last_os_error()); - } - } - } - 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 info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 }; - api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() - } - - #[cfg(not(target_vendor = "uwp"))] - pub fn file_attr(&self) -> io::Result { - 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 attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed(); - cvt(c::GetFileInformationByHandleEx( - self.handle.as_raw_handle(), - c::FileAttributeTagInfo, - ptr::addr_of_mut!(attr_tag).cast(), - mem::size_of::().try_into().unwrap(), - ))?; - if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - reparse_tag = attr_tag.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 { - 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 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 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 attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed(); - cvt(c::GetFileInformationByHandleEx( - self.handle.as_raw_handle(), - c::FileAttributeTagInfo, - ptr::addr_of_mut!(attr_tag).cast(), - mem::size_of::().try_into().unwrap(), - ))?; - if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - attr.reparse_tag = attr_tag.ReparseTag; - } - } - Ok(attr) - } - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.handle.read(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - 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 { - self.handle.read_at(buf, offset) - } - - pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.handle.read_buf(cursor) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.handle.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - 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 { - self.handle.write_at(buf, offset) - } - - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } - - pub fn seek(&self, pos: SeekFrom) -> io::Result { - 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 { - Ok(Self { handle: self.handle.try_clone()? }) - } - - // NB: returned pointer is derived from `space`, and has provenance to - // match. A raw pointer is returned rather than a reference in order to - // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`. - fn reparse_point( - &self, - space: &mut Align8<[MaybeUninit]>, - ) -> io::Result<(c::DWORD, *mut c::REPARSE_DATA_BUFFER)> { - unsafe { - let mut bytes = 0; - cvt({ - // Grab this in advance to avoid it invalidating the pointer - // we get from `space.0.as_mut_ptr()`. - let len = space.0.len(); - c::DeviceIoControl( - self.handle.as_raw_handle(), - c::FSCTL_GET_REPARSE_POINT, - ptr::null_mut(), - 0, - space.0.as_mut_ptr().cast(), - len as c::DWORD, - &mut bytes, - ptr::null_mut(), - ) - })?; - const _: () = assert!(core::mem::align_of::() <= 8); - Ok((bytes, space.0.as_mut_ptr().cast::())) - } - } - - fn readlink(&self) -> io::Result { - let mut space = - Align8([MaybeUninit::::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]); - 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: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = - ptr::addr_of_mut!((*buf).rest).cast(); - assert!(info.is_aligned()); - ( - ptr::addr_of_mut!((*info).PathBuffer).cast::(), - (*info).SubstituteNameOffset / 2, - (*info).SubstituteNameLength / 2, - (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0, - ) - } - c::IO_REPARSE_TAG_MOUNT_POINT => { - let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = - ptr::addr_of_mut!((*buf).rest).cast(); - assert!(info.is_aligned()); - ( - ptr::addr_of_mut!((*info).PathBuffer).cast::(), - (*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.add(subst_off.into()); - let subst = slice::from_raw_parts_mut(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]) { - // Turn `\??\` into `\\?\` (a verbatim path). - subst[1] = b'\\' as u16; - // Attempt to convert to a more user-friendly path. - let user = super::args::from_wide_to_user_path( - subst.iter().copied().chain([0]).collect(), - )?; - Ok(PathBuf::from(OsString::from_wide(user.strip_suffix(&[0]).unwrap_or(&user)))) - } else { - Ok(PathBuf::from(OsString::from_wide(subst))) - } - } - } - - pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { - let info = c::FILE_BASIC_INFO { - CreationTime: 0, - LastAccessTime: 0, - LastWriteTime: 0, - ChangeTime: 0, - FileAttributes: perm.attrs, - }; - api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() - } - - 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) - || times.created.map_or(false, is_zero) - { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "Cannot set file timestamp to 0", - )); - } - let is_max = - |t: c::FILETIME| t.dwLowDateTime == c::DWORD::MAX && t.dwHighDateTime == c::DWORD::MAX; - if times.accessed.map_or(false, is_max) - || times.modified.map_or(false, is_max) - || times.created.map_or(false, is_max) - { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "Cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF", - )); - } - cvt(unsafe { - let created = - times.created.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); - let accessed = - times.accessed.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); - let modified = - times.modified.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null()); - c::SetFileTime(self.as_raw_handle(), created, accessed, modified) - })?; - Ok(()) - } - - /// Get only basic file information such as attributes and file times. - fn basic_info(&self) -> io::Result { - 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 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 info = c::FILE_DISPOSITION_INFO_EX { - Flags: c::FILE_DISPOSITION_FLAG_DELETE - | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS - | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE, - }; - api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() - } - - /// 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 info = c::FILE_DISPOSITION_INFO { DeleteFile: c::TRUE as _ }; - api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() - } - - /// 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 { - 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: Box; Self::BUFFER_SIZE]>>, -} -impl DirBuff { - const BUFFER_SIZE: usize = 1024; - fn new() -> Self { - Self { - // Safety: `Align8<[MaybeUninit; N]>` does not need - // initialization. - buffer: unsafe { Box::new_uninit().assume_init() }, - } - } - fn capacity(&self) -> usize { - self.buffer.0.len() - } - fn as_mut_ptr(&mut self) -> *mut u8 { - self.buffer.0.as_mut_ptr().cast() - } - /// Returns a `DirBuffIter`. - fn iter(&self) -> DirBuffIter<'_> { - DirBuffIter::new(self) - } -} -impl AsRef<[MaybeUninit]> for DirBuff { - fn as_ref(&self) -> &[MaybeUninit] { - &self.buffer.0 - } -} - -/// An iterator over entries stored in a `DirBuff`. -/// -/// Currently only returns file names (UTF-16 encoded). -struct DirBuffIter<'a> { - buffer: Option<&'a [MaybeUninit]>, - 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 = (Cow<'a, [u16]>, bool); - fn next(&mut self) -> Option { - 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. - // - The OS has guaranteed initialization of the fields of - // `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least - // `FileNameLength` bytes) - let (name, is_directory, next_entry) = unsafe { - let info = buffer.as_ptr().cast::(); - // While this is guaranteed to be aligned in documentation for - // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info - // it does not seem that reality is so kind, and assuming this - // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530) - // presumably, this can be blamed on buggy filesystem drivers, but who knows. - let next_entry = ptr::addr_of!((*info).NextEntryOffset).read_unaligned() as usize; - let length = ptr::addr_of!((*info).FileNameLength).read_unaligned() as usize; - let attrs = ptr::addr_of!((*info).FileAttributes).read_unaligned(); - let name = from_maybe_unaligned( - ptr::addr_of!((*info).FileName).cast::(), - length / size_of::(), - ); - let is_directory = (attrs & 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)), - } - } -} - -unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> { - if p.is_aligned() { - Cow::Borrowed(crate::slice::from_raw_parts(p, len)) - } else { - Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect()) - } -} - -/// 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 { - // 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::PENDING; - let mut 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: &mut 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 for File { - #[inline] - fn as_inner(&self) -> &Handle { - &self.handle - } -} - -impl IntoInner for File { - fn into_inner(self) -> Handle { - self.handle - } -} - -impl FromInner 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 { - Ok(SystemTime::from(self.last_write_time)) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from(self.last_access_time)) - } - - pub fn created(&self) -> io::Result { - 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 { - self.volume_serial_number - } - - pub fn number_of_links(&self) -> Option { - self.number_of_links - } - - pub fn file_index(&self) -> Option { - self.file_index - } -} -impl From 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()); - } - - pub fn set_created(&mut self, t: SystemTime) { - self.created = 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 { - // We push a `*` to the end of the path which cause the empty path to be - // treated as the current directory. So, for consistency with other platforms, - // we explicitly error on the empty path. - if p.as_os_str().is_empty() { - // Return an error code consistent with other ways of opening files. - // E.g. fs::metadata or File::open. - return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32)); - } - 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 { - 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 { - 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, - ); - // On success, add the handle to the queue. - // If opening the directory fails we treat it the same as a file - if let Ok(child_dir) = child_dir { - dirlist.push(child_dir); - continue; - } - } - 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 { - // 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 { - match metadata(path, ReparsePoint::Follow) { - Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => { - if let Ok(attrs) = lstat(path) { - if !attrs.file_type().is_symlink() { - return Ok(attrs); - } - } - Err(err) - } - result => result, - } -} - -pub fn lstat(path: &Path) -> io::Result { - 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 { - 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 [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)] - .contains(&e.raw_os_error()) => - { - // `ERROR_ACCESS_DENIED` is returned when the user doesn't have permission for the resource. - // One such example is `System Volume Information` as default but can be created as well - // `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 { - 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 { - 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 { - 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::LPCVOID, - ) -> 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, Q: AsRef>( - 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 = - Align8([MaybeUninit::::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]); - let data_ptr = data.0.as_mut_ptr(); - let data_end = data_ptr.add(c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize); - let db = data_ptr.cast::(); - // Zero the header to ensure it's fully initialized, including reserved parameters. - *db = mem::zeroed(); - let reparse_target_slice = { - let buf_start = ptr::addr_of_mut!((*db).ReparseTarget).cast::(); - // Compute offset in bytes and then divide so that we round down - // rather than hit any UB (admittedly this arithmetic should work - // out so that this isn't necessary) - let buf_len_bytes = usize::try_from(data_end.byte_offset_from(buf_start)).unwrap(); - let buf_len_wchars = buf_len_bytes / core::mem::size_of::(); - core::slice::from_raw_parts_mut(buf_start, buf_len_wchars) - }; - - // FIXME: this conversion is very hacky - let iter = br"\??\" - .iter() - .map(|x| *x as u16) - .chain(original.as_os_str().encode_wide()) - .chain(core::iter::once(0)); - let mut i = 0; - for c in iter { - if i >= reparse_target_slice.len() { - return Err(crate::io::const_io_error!( - crate::io::ErrorKind::InvalidFilename, - "Input filename is too long" - )); - } - reparse_target_slice[i] = c; - 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_ptr.cast(), - (*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 { - // 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), - - // `ERROR_CANT_ACCESS_FILE` means that a file exists but that the - // reparse point could not be handled by `CreateFile`. - // This can happen for special files such as: - // * Unix domain sockets which you need to `connect` to - // * App exec links which require using `CreateProcess` - _ if e.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE 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 deleted file mode 100644 index c4495f81a5a..00000000000 --- a/library/std/src/sys/windows/handle.rs +++ /dev/null @@ -1,338 +0,0 @@ -#![unstable(issue = "none", feature = "windows_handle")] - -#[cfg(test)] -mod tests; - -use crate::cmp; -use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, Read}; -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 { - 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 for Handle { - #[inline] - fn as_inner(&self) -> &OwnedHandle { - &self.0 - } -} - -impl IntoInner for Handle { - fn into_inner(self) -> OwnedHandle { - self.0 - } -} - -impl FromInner 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 { - let res = unsafe { self.synchronous_read(buf.as_mut_ptr().cast(), buf.len(), None) }; - - match res { - Ok(read) => Ok(read), - - // 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 { - 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 { - let res = - unsafe { self.synchronous_read(buf.as_mut_ptr().cast(), buf.len(), Some(offset)) }; - - match res { - Ok(read) => Ok(read), - 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, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - let res = - unsafe { self.synchronous_read(cursor.as_mut().as_mut_ptr(), cursor.capacity(), None) }; - - match res { - Ok(read) => { - // Safety: `read` bytes were written to the initialized portion of the buffer - unsafe { - cursor.advance(read); - } - 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> { - let len = cmp::min(buf.len(), ::MAX as usize) as c::DWORD; - let mut amt = 0; - let res = - cvt(c::ReadFile(self.as_raw_handle(), buf.as_mut_ptr(), 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 { - 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 { - self.synchronous_write(buf, None) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - 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 { - self.synchronous_write(buf, Some(offset)) - } - - pub fn try_clone(&self) -> io::Result { - Ok(Self(self.0.try_clone()?)) - } - - pub fn duplicate( - &self, - access: c::DWORD, - inherit: bool, - options: c::DWORD, - ) -> io::Result { - 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, - len: usize, - offset: Option, - ) -> io::Result { - let mut io_status = c::IO_STATUS_BLOCK::PENDING; - - // 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) -> io::Result { - let mut io_status = c::IO_STATUS_BLOCK::PENDING; - - // 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 { - (**self).read(buf) - } - - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - (**self).read_buf(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - (**self).read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - (**self).is_read_vectored() - } -} diff --git a/library/std/src/sys/windows/handle/tests.rs b/library/std/src/sys/windows/handle/tests.rs deleted file mode 100644 index d836dae4c30..00000000000 --- a/library/std/src/sys/windows/handle/tests.rs +++ /dev/null @@ -1,22 +0,0 @@ -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 deleted file mode 100644 index 649826d25ce..00000000000 --- a/library/std/src/sys/windows/io.rs +++ /dev/null @@ -1,161 +0,0 @@ -use crate::marker::PhantomData; -use crate::mem::size_of; -use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; -use crate::slice; -use crate::sys::c; -use core::ffi::c_void; - -#[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 }, - _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, 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() }, - _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, self.vec.len as usize) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } - } -} - -pub fn is_terminal(h: &impl AsHandle) -> bool { - unsafe { handle_is_console(h.as_handle()) } -} - -unsafe fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { - let handle = handle.as_raw_handle(); - - // A null handle means the process has no console. - if handle.is_null() { - return false; - } - - let mut out = 0; - if c::GetConsoleMode(handle, &mut out) != 0 { - // False positives aren't possible. If we got a console then we definitely have a console. - return true; - } - - // At this point, we *could* have a false negative. We can determine that this is a true - // negative if we can detect the presence of a console on any of the standard I/O streams. If - // another stream has a console, then we know we're in a Windows console and can therefore - // trust the negative. - for std_handle in [c::STD_INPUT_HANDLE, c::STD_OUTPUT_HANDLE, c::STD_ERROR_HANDLE] { - let std_handle = c::GetStdHandle(std_handle); - if !std_handle.is_null() - && std_handle != handle - && c::GetConsoleMode(std_handle, &mut out) != 0 - { - return false; - } - } - - // Otherwise, we fall back to an msys hack to see if we can detect the presence of a pty. - msys_tty_on(handle) -} - -unsafe fn msys_tty_on(handle: c::HANDLE) -> bool { - // Early return if the handle is not a pipe. - if c::GetFileType(handle) != c::FILE_TYPE_PIPE { - return false; - } - - /// Mirrors [`FILE_NAME_INFO`], giving it a fixed length that we can stack - /// allocate - /// - /// [`FILE_NAME_INFO`]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_name_info - #[repr(C)] - #[allow(non_snake_case)] - struct FILE_NAME_INFO { - FileNameLength: u32, - FileName: [u16; c::MAX_PATH as usize], - } - let mut name_info = FILE_NAME_INFO { FileNameLength: 0, FileName: [0; c::MAX_PATH as usize] }; - // Safety: buffer length is fixed. - let res = c::GetFileInformationByHandleEx( - handle, - c::FileNameInfo, - &mut name_info as *mut _ as *mut c_void, - size_of::() as u32, - ); - if res == 0 { - return false; - } - - // Use `get` because `FileNameLength` can be out of range. - let s = match name_info.FileName.get(..name_info.FileNameLength as usize / 2) { - None => return false, - Some(s) => s, - }; - let name = String::from_utf16_lossy(s); - // Get the file name only. - let name = name.rsplit('\\').next().unwrap_or(&name); - // This checks whether 'pty' exists in the file name, which indicates that - // a pseudo-terminal is attached. To mitigate against false positives - // (e.g., an actual file name that contains 'pty'), we also require that - // the file name begins with either the strings 'msys-' or 'cygwin-'.) - let is_msys = name.starts_with("msys-") || name.starts_with("cygwin-"); - let is_pty = name.contains("-pty"); - is_msys && is_pty -} diff --git a/library/std/src/sys/windows/locks/condvar.rs b/library/std/src/sys/windows/locks/condvar.rs deleted file mode 100644 index 66fafa2c00b..00000000000 --- a/library/std/src/sys/windows/locks/condvar.rs +++ /dev/null @@ -1,50 +0,0 @@ -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, -} - -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 fn notify_one(&self) { - unsafe { c::WakeConditionVariable(self.inner.get()) } - } - - #[inline] - pub fn notify_all(&self) { - unsafe { 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 deleted file mode 100644 index 0e0f9eccb21..00000000000 --- a/library/std/src/sys/windows/locks/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod condvar; -mod mutex; -mod rwlock; -pub use condvar::Condvar; -pub use mutex::Mutex; -pub use rwlock::RwLock; diff --git a/library/std/src/sys/windows/locks/mutex.rs b/library/std/src/sys/windows/locks/mutex.rs deleted file mode 100644 index ef2f84082cd..00000000000 --- a/library/std/src/sys/windows/locks/mutex.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! 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, -} - -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 fn lock(&self) { - unsafe { - c::AcquireSRWLockExclusive(raw(self)); - } - } - - #[inline] - pub fn try_lock(&self) -> bool { - unsafe { 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 deleted file mode 100644 index e69415baac4..00000000000 --- a/library/std/src/sys/windows/locks/rwlock.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::sys::c; - -pub struct RwLock { - inner: UnsafeCell, -} - -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 fn read(&self) { - unsafe { c::AcquireSRWLockShared(self.inner.get()) } - } - #[inline] - pub fn try_read(&self) -> bool { - unsafe { c::TryAcquireSRWLockShared(self.inner.get()) != 0 } - } - #[inline] - pub fn write(&self) { - unsafe { c::AcquireSRWLockExclusive(self.inner.get()) } - } - #[inline] - pub fn try_write(&self) -> bool { - unsafe { 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 deleted file mode 100644 index b9e5bcc1b4b..00000000000 --- a/library/std/src/sys/windows/memchr.rs +++ /dev/null @@ -1,5 +0,0 @@ -// 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 deleted file mode 100644 index 8b722f01a5d..00000000000 --- a/library/std/src/sys/windows/mod.rs +++ /dev/null @@ -1,357 +0,0 @@ -#![allow(missing_docs, nonstandard_style)] - -use crate::ffi::{OsStr, OsString}; -use crate::io::ErrorKind; -use crate::mem::MaybeUninit; -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 stdio; -pub mod thread; -pub mod thread_local_dtor; -pub mod thread_local_key; -pub mod thread_parking; -pub mod time; -cfg_if::cfg_if! { - if #[cfg(not(target_vendor = "uwp"))] { - pub mod stack_overflow; - } else { - pub mod stack_overflow_uwp; - pub use self::stack_overflow_uwp as stack_overflow; - } -} - -mod api; - -/// Map a Result to io::Result. -trait IoResult { - fn io_result(self) -> crate::io::Result; -} -impl IoResult for Result { - fn io_result(self) -> crate::io::Result { - self.map_err(|e| crate::io::Error::from_raw_os_error(e.code as i32)) - } -} - -// 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, _sigpipe: 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(&c"main"); -} - -// 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(); -} - -#[inline] -pub fn is_interrupted(_errno: i32) -> bool { - false -} - -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 - | c::ERROR_PATH_NOT_FOUND - | c::ERROR_INVALID_DRIVE - | c::ERROR_BAD_NETPATH - | c::ERROR_BAD_NET_NAME => return NotFound, - c::ERROR_NO_DATA => return BrokenPipe, - c::ERROR_INVALID_NAME | c::ERROR_BAD_PATHNAME => 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 { - 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: S) -> crate::io::Result> { - fn inner(s: &OsStr) -> crate::io::Result> { - // 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(mut f1: F1, f2: F2) -> crate::io::Result -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: [MaybeUninit; 512] = MaybeUninit::uninit_array(); - let mut heap_buf: Vec> = 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); - // We used `reserve` and not `reserve_exact`, so in theory we - // may have gotten more than requested. If so, we'd like to use - // it... so long as we won't cause overflow. - n = heap_buf.capacity().min(c::DWORD::MAX as usize); - // Safety: MaybeUninit does not need initialization - 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().cast::(), n as c::DWORD) { - 0 if api::get_last_error().code == 0 => 0, - 0 => return Err(crate::io::Error::last_os_error()), - n => n, - } as usize; - if k == n && api::get_last_error().code == c::ERROR_INSUFFICIENT_BUFFER { - n = n.saturating_mul(2).min(c::DWORD::MAX as usize); - } 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 { - // Safety: First `k` values are initialized. - let slice: &[u16] = MaybeUninit::slice_assume_init_ref(&buf[..k]); - return Ok(f2(slice)); - } - } - } -} - -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: I) -> crate::io::Result { - 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 > ::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(); -} - -/// Align the inner value to 8 bytes. -/// -/// This is enough for almost all of the buffers we're likely to work with in -/// the Windows APIs we use. -#[repr(C, align(8))] -#[derive(Copy, Clone)] -pub(crate) struct Align8(pub T); diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs deleted file mode 100644 index 6cd758ec5c3..00000000000 --- a/library/std/src/sys/windows/net.rs +++ /dev/null @@ -1,497 +0,0 @@ -#![unstable(issue = "none", feature = "windows_net")] - -use crate::cmp; -use crate::io::{self, BorrowedBuf, BorrowedCursor, 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 core::ffi::{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 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: T) -> io::Result { - 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(mut f: F) -> io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - cvt(f()) -} - -impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - 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)) } - } 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.0.set_no_inherit()?; - Ok(socket) - } - } - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) }; - cvt(result).map(drop) - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let result = self.connect(addr); - 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: cmp::min(timeout.as_secs(), c_long::MAX as u64) as c_long, - tv_usec: timeout.subsec_micros() as c_long, - }; - - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - - let fds = { - let mut fds = unsafe { mem::zeroed::() }; - fds.fd_count = 1; - fds.fd_array[0] = self.as_raw(); - 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 { - let socket = unsafe { c::accept(self.as_raw(), storage, len) }; - - match socket { - c::INVALID_SOCKET => Err(last_error()), - _ => unsafe { Ok(Self::from_raw(socket)) }, - } - } - - pub fn duplicate(&self) -> io::Result { - Ok(Self(self.0.try_clone()?)) - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { - // 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.capacity(), i32::MAX as usize) as i32; - let result = - unsafe { c::recv(self.as_raw(), buf.as_mut().as_mut_ptr() as *mut _, length, flags) }; - - match result { - c::SOCKET_ERROR => { - let error = unsafe { c::WSAGetLastError() }; - - if error == c::WSAESHUTDOWN { - Ok(()) - } else { - Err(io::Error::from_raw_os_error(error)) - } - } - _ => { - unsafe { buf.advance(result as usize) }; - Ok(()) - } - } - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - // 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(), - bufs.as_mut_ptr() as *mut c::WSABUF, - length, - &mut nread, - &mut flags, - ptr::null_mut(), - None, - ) - }; - - 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 { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), c::MSG_PEEK)?; - Ok(buf.len()) - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage = unsafe { mem::zeroed::() }; - let mut addrlen = mem::size_of_val(&storage) as c::socklen_t; - let length = cmp::min(buf.len(), ::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(), - 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 { - 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(), - bufs.as_ptr() as *const c::WSABUF as *mut _, - length, - &mut nwritten, - 0, - ptr::null_mut(), - None, - ) - }; - cvt(result).map(|_| nwritten as usize) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn set_timeout(&self, dur: Option, 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> { - 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(), 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(), c::FIONBIO as c_int, &mut nonblocking) }; - cvt(result).map(drop) - } - - pub fn set_linger(&self, linger: Option) -> 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> { - 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 { - let raw: c::BOOL = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; - Ok(raw != 0) - } - - pub fn take_error(&self) -> io::Result> { - 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) -> c::SOCKET { - debug_assert_eq!(mem::size_of::(), mem::size_of::()); - debug_assert_eq!(mem::align_of::(), mem::align_of::()); - self.as_inner().as_raw_socket() as c::SOCKET - } - pub unsafe fn from_raw(raw: c::SOCKET) -> Self { - debug_assert_eq!(mem::size_of::(), mem::size_of::()); - debug_assert_eq!(mem::align_of::(), mem::align_of::()); - Self::from_raw_socket(raw as RawSocket) - } -} - -#[unstable(reason = "not public", issue = "none", feature = "fd_read")] -impl<'a> Read for &'a Socket { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &OwnedSocket { - &self.0 - } -} - -impl FromInner for Socket { - fn from_inner(sock: OwnedSocket) -> Socket { - Socket(sock) - } -} - -impl IntoInner 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 deleted file mode 100644 index 829dd5eb97a..00000000000 --- a/library/std/src/sys/windows/os.rs +++ /dev/null @@ -1,368 +0,0 @@ -//! 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::{api, to_u16s}; - -pub fn errno() -> i32 { - api::get_last_error().code as i32 -} - -/// Gets a detailed string description for the given error number. -pub fn error_string(mut errnum: i32) -> String { - 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, - 0, - 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 0 as 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, - iter: EnvIterator, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. -pub struct EnvStrDebug<'a> { - iter: &'a EnvIterator, -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - let iter: EnvIterator = (*iter).clone(); - let mut list = f.debug_list(); - for (a, b) in iter { - list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); - } - list.finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { base: _, iter } = self; - EnvStrDebug { iter } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { base: _, iter } = self; - f.debug_list().entries(iter.clone()).finish() - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self { base: _, iter } = self; - iter.next() - } -} - -#[derive(Clone)] -struct EnvIterator(c::LPWCH); - -impl Iterator for EnvIterator { - type Item = (OsString, OsString); - - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self(cur) = self; - loop { - unsafe { - if **cur == 0 { - return None; - } - let p = *cur as *const u16; - let mut len = 0; - while *p.add(len) != 0 { - len += 1; - } - let s = slice::from_raw_parts(p, len); - *cur = cur.add(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, iter: EnvIterator(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 { - // 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(paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - 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::>(); - 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 { - super::fill_utf16_buf( - |buf, sz| unsafe { c::GetModuleFileNameW(ptr::null_mut(), buf, sz) }, - super::os2path, - ) -} - -pub fn getcwd() -> io::Result { - 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::>(); - p.push(0); - - cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop) -} - -pub fn getenv(k: &OsStr) -> Option { - let k = to_u16s(k).ok()?; - super::fill_utf16_buf( - |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) }, - OsStringExt::from_wide, - ) - .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 { - 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 api::get_last_error().code != 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 { - None -} - -pub fn home_dir() -> Option { - 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() } -} diff --git a/library/std/src/sys/windows/os/tests.rs b/library/std/src/sys/windows/os/tests.rs deleted file mode 100644 index 458d6e11c20..00000000000 --- a/library/std/src/sys/windows/os/tests.rs +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 237854fac4e..00000000000 --- a/library/std/src/sys/windows/os_str.rs +++ /dev/null @@ -1,245 +0,0 @@ -/// 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 for Buf { - fn into_inner(self) -> Wtf8Buf { - self.inner - } -} - -impl FromInner for Buf { - fn from_inner(inner: Wtf8Buf) -> Self { - Buf { inner } - } -} - -impl AsInner for Buf { - #[inline] - 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 { - #[inline] - pub fn into_encoded_bytes(self) -> Vec { - self.inner.into_bytes() - } - - #[inline] - pub unsafe fn from_encoded_bytes_unchecked(s: Vec) -> Self { - Self { inner: Wtf8Buf::from_bytes_unchecked(s) } - } - - 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 { - 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 { - unsafe { mem::transmute(self.inner.into_box()) } - } - - #[inline] - pub fn from_box(boxed: Box) -> Buf { - let inner: Box = unsafe { mem::transmute(boxed) }; - Buf { inner: Wtf8Buf::from_box(inner) } - } - - #[inline] - pub fn into_arc(&self) -> Arc { - self.as_slice().into_arc() - } - - #[inline] - pub fn into_rc(&self) -> Rc { - self.as_slice().into_rc() - } -} - -impl Slice { - #[inline] - pub fn as_encoded_bytes(&self) -> &[u8] { - self.inner.as_bytes() - } - - #[inline] - pub unsafe fn from_encoded_bytes_unchecked(s: &[u8]) -> &Slice { - mem::transmute(Wtf8::from_bytes_unchecked(s)) - } - - #[inline] - pub fn from_str(s: &str) -> &Slice { - unsafe { mem::transmute(Wtf8::from_str(s)) } - } - - pub fn to_str(&self) -> Result<&str, crate::str::Utf8Error> { - self.inner.as_str() - } - - pub fn to_string_lossy(&self) -> Cow<'_, str> { - self.inner.to_string_lossy() - } - - pub fn to_owned(&self) -> Buf { - Buf { inner: self.inner.to_owned() } - } - - pub fn clone_into(&self, buf: &mut Buf) { - self.inner.clone_into(&mut buf.inner) - } - - #[inline] - pub fn into_box(&self) -> Box { - unsafe { mem::transmute(self.inner.into_box()) } - } - - pub fn empty_box() -> Box { - unsafe { mem::transmute(Wtf8::empty_box()) } - } - - #[inline] - pub fn into_arc(&self) -> Arc { - let arc = self.inner.into_arc(); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } - } - - #[inline] - pub fn into_rc(&self) -> Rc { - 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 deleted file mode 100644 index d9684f21753..00000000000 --- a/library/std/src/sys/windows/path.rs +++ /dev/null @@ -1,344 +0,0 @@ -use super::{c, fill_utf16_buf, to_u16s}; -use crate::ffi::{OsStr, OsString}; -use crate::io; -use crate::path::{Path, PathBuf, Prefix}; -use crate::ptr; - -#[cfg(test)] -mod tests; - -pub const MAIN_SEP_STR: &str = "\\"; -pub const MAIN_SEP: char = '\\'; - -#[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.as_encoded_bytes().iter().copied().any(is_sep_byte) -} -pub(crate) fn has_trailing_slash(path: &OsStr) -> bool { - let is_verbatim = path.as_encoded_bytes().starts_with(br"\\?\"); - let is_separator = if is_verbatim { is_verbatim_sep } else { is_sep_byte }; - if let Some(&c) = path.as_encoded_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.as_encoded_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.prefix[self.index..] - .starts_with(prefix.as_bytes()) - .then_some(Self { index: self.index + prefix.len(), ..*self }) - } - - fn prefix_bytes(&self) -> &'a [u8] { - &self.path.as_encoded_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 { OsStr::from_encoded_bytes_unchecked(&self.path.as_encoded_bytes()[self.index..]) } - } -} - -pub fn parse_prefix(path: &OsStr) -> Option> { - 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 it has a drive like `C:` then it's a disk. - // Otherwise there is no prefix. - parse_drive(path).map(Disk) - } -} - -// Parses a drive prefix, e.g. "C:" and "C:\whatever" -fn parse_drive(path: &OsStr) -> Option { - // In most DOS systems, it is not possible to have more than 26 drive letters. - // See . - fn is_valid_drive_letter(drive: &u8) -> bool { - drive.is_ascii_alphabetic() - } - - match path.as_encoded_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 { - // only parse two bytes: the drive letter and the drive separator - if path.as_encoded_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.as_encoded_bytes().iter().position(|&x| separator(x)) { - Some(separator_start) => { - let separator_end = separator_start + 1; - - let component = &path.as_encoded_bytes()[..separator_start]; - - // Panic safe - // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index. - let path = &path.as_encoded_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 { - ( - OsStr::from_encoded_bytes_unchecked(component), - OsStr::from_encoded_bytes_unchecked(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> { - let path = to_u16s(path)?; - get_long_path(path, true) -} - -/// Get a normalized absolute path that can bypass path length limits. -/// -/// Setting prefer_verbatim to true suggests a stronger preference for verbatim -/// paths even when not strictly necessary. This allows the Windows API to avoid -/// repeating our work. However, if the path may be given back to users or -/// passed to other application then it's preferable to use non-verbatim paths -/// when possible. Non-verbatim paths are better understood by users and handled -/// by more software. -pub(crate) fn get_long_path(mut path: Vec, prefer_verbatim: bool) -> io::Result> { - // 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]; - - 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(); - - // Only prepend the prefix if needed. - if prefer_verbatim || absolute.len() + 1 >= LEGACY_MAX_PATH { - // 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); - } else { - path.reserve_exact(absolute.len() + 1); - } - path.extend_from_slice(absolute); - path.push(0); - }, - )?; - Ok(path) -} - -/// Make a Windows path absolute. -pub(crate) fn absolute(path: &Path) -> io::Result { - 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.as_encoded_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 deleted file mode 100644 index 623c6236166..00000000000 --- a/library/std/src/sys/windows/path/tests.rs +++ /dev/null @@ -1,137 +0,0 @@ -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> { - 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 information. -#[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 deleted file mode 100644 index 7624e746f5c..00000000000 --- a/library/std/src/sys/windows/pipe.rs +++ /dev/null @@ -1,571 +0,0 @@ -use crate::os::windows::prelude::*; - -use crate::ffi::OsStr; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; -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::{FromInner, IntoInner}; - -//////////////////////////////////////////////////////////////////////////////// -// Anonymous pipes -//////////////////////////////////////////////////////////////////////////////// - -pub struct AnonPipe { - inner: Handle, -} - -impl IntoInner for AnonPipe { - fn into_inner(self) -> Handle { - self.inner - } -} - -impl FromInner for AnonPipe { - fn from_inner(inner: Handle) -> AnonPipe { - 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 { - // 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::>(); - 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::(); - 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 { - // 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.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner }) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - 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_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { - let result = unsafe { - let len = crate::cmp::min(buf.capacity(), c::DWORD::MAX as usize) as c::DWORD; - self.alertable_io_internal(c::ReadFileEx, buf.as_mut().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(()), - Err(e) => Err(e), - Ok(n) => { - unsafe { - buf.advance(n); - } - Ok(()) - } - } - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.inner.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - self.handle().read_to_end(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - 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 { - 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 { - // 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 = None; - struct AsyncResult { - error: u32, - transferred: u32, - } - - // STEP 3: The callback. - unsafe extern "system" fn callback( - dwErrorCode: u32, - dwNumberOfBytesTransferred: u32, - lpOverlapped: *mut c::OVERLAPPED, - ) { - // Set `async_result` using a pointer smuggled through `hEvent`. - let result = - AsyncResult { error: dwErrorCode, transferred: dwNumberOfBytesTransferred }; - *(*lpOverlapped).hEvent.cast::>() = 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, Some(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.transferred as usize), - error => Err(io::Error::from_raw_os_error(error as _)), - } - } -} - -pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> 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, // needs a stable address - dst: &'a mut Vec, - state: State, -} - -#[derive(PartialEq, Debug)] -enum State { - NotReading, - Reading, - Read(usize), -} - -impl<'a> AsyncPipe<'a> { - fn new(pipe: Handle, dst: &'a mut Vec) -> io::Result> { - // 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 = 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 { - 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 { - 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) -> &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 deleted file mode 100644 index 9ec775959fd..00000000000 --- a/library/std/src/sys/windows/process.rs +++ /dev/null @@ -1,984 +0,0 @@ -#![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::mem::MaybeUninit; -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::sync::Mutex; -use crate::sys::args::{self, Arg}; -use crate::sys::c::{self, NonZeroDWORD, EXIT_FAILURE, EXIT_SUCCESS}; -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::process::{CommandEnv, CommandEnvs}; -use crate::sys_common::IntoInner; - -use core::ffi::c_void; - -//////////////////////////////////////////////////////////////////////////////// -// 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, -} - -impl EnvKey { - fn new>(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 { - 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 for EnvKey { - fn partial_cmp(&self, other: &str) -> Option { - Some(self.cmp(&EnvKey::new(other))) - } -} -impl PartialEq 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 for EnvKey { - fn from(k: OsString) -> Self { - EnvKey { utf16: k.encode_wide().collect(), os_string: k } - } -} - -impl From 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 for EnvKey { - fn as_ref(&self) -> &OsStr { - &self.os_string - } -} - -pub(crate) fn ensure_no_nuls>(str: T) -> io::Result { - 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, - env: CommandEnv, - cwd: Option, - flags: u32, - detach: bool, // not currently exposed in std::process - stdin: Option, - stdout: Option, - stderr: Option, - force_quotes_enabled: bool, - proc_thread_attributes: BTreeMap, -} - -pub enum Stdio { - Inherit, - InheritSpecific { from_stdio_id: c::DWORD }, - Null, - MakePipe, - Pipe(AnonPipe), - Handle(Handle), -} - -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - -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, - proc_thread_attributes: Default::default(), - } - } - - 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(Path::new) - } - - pub unsafe fn raw_attribute( - &mut self, - attribute: usize, - value: T, - ) { - self.proc_thread_attributes.insert( - attribute, - ProcThreadAttributeValue { size: mem::size_of::(), data: Box::new(value) }, - ); - } - - pub fn spawn( - &mut self, - default: Stdio, - needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - let maybe_env = self.env.capture_if_changed(); - - 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(&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: Mutex<()> = Mutex::new(()); - - let _guard = 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)?; - - let mut si = zeroed_startupinfo(); - - // If at least one of stdin, stdout or stderr are set (i.e. are non null) - // then set the `hStd` fields in `STARTUPINFO`. - // Otherwise skip this and allow the OS to apply its default behaviour. - // This provides more consistent behaviour between Win7 and Win8+. - let is_set = |stdio: &Handle| !stdio.as_raw_handle().is_null(); - if is_set(&stderr) || is_set(&stdout) || is_set(&stdin) { - si.dwFlags |= c::STARTF_USESTDHANDLES; - si.hStdInput = stdin.as_raw_handle(); - si.hStdOutput = stdout.as_raw_handle(); - si.hStdError = stderr.as_raw_handle(); - } - - let si_ptr: *mut c::STARTUPINFOW; - - let mut proc_thread_attribute_list; - let mut si_ex; - - if !self.proc_thread_attributes.is_empty() { - si.cb = mem::size_of::() as u32; - flags |= c::EXTENDED_STARTUPINFO_PRESENT; - - proc_thread_attribute_list = - make_proc_thread_attribute_list(&self.proc_thread_attributes)?; - si_ex = c::STARTUPINFOEXW { - StartupInfo: si, - lpAttributeList: proc_thread_attribute_list.0.as_mut_ptr() as _, - }; - si_ptr = &mut si_ex as *mut _ as _; - } else { - si.cb = mem::size_of::() as c::DWORD; - si_ptr = &mut si as *mut _ as _; - } - - unsafe { - cvt(c::CreateProcessW( - program.as_ptr(), - cmd_str.as_mut_ptr(), - ptr::null_mut(), - ptr::null_mut(), - c::TRUE, - flags, - envp, - dirp, - si_ptr, - &mut pi, - )) - }?; - - unsafe { - Ok(( - Process { - handle: Handle::from_raw_handle(pi.hProcess), - main_thread_handle: Handle::from_raw_handle(pi.hThread), - }, - pipes, - )) - } - } - - pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { - let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; - crate::sys_common::process::wait_with_output(proc, 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, - child_paths: Option<&OsStr>, -) -> io::Result> { - // 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.as_encoded_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 args::to_user_path(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 args::to_user_path(&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.as_encoded_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( - parent_paths: Paths, - child_paths: Option<&OsStr>, - mut exists: Exists, -) -> Option> -where - Paths: FnOnce() -> Option, - Exists: FnMut(PathBuf) -> Option>, -{ - // 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> { - unsafe { - let path = args::to_user_path(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) -> io::Result { - let use_stdio_id = |stdio_id| 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 - }, - // If no stdio handle is available, then propagate the null value. - Err(..) => unsafe { Ok(Handle::from_raw_handle(ptr::null_mut())) }, - }; - match *self { - Stdio::Inherit => use_stdio_id(stdio_id), - Stdio::InheritSpecific { from_stdio_id } => use_stdio_id(from_stdio_id), - - 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::(); - 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(r"\\.\NUL"), &opts).map(|file| file.into_inner()) - } - } - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - Stdio::Pipe(pipe) - } -} - -impl From for Stdio { - fn from(file: File) -> Stdio { - Stdio::Handle(file.into_inner()) - } -} - -impl From for Stdio { - fn from(_: io::Stdout) -> Stdio { - Stdio::InheritSpecific { from_stdio_id: c::STD_OUTPUT_HANDLE } - } -} - -impl From for Stdio { - fn from(_: io::Stderr) -> Stdio { - Stdio::InheritSpecific { from_stdio_id: c::STD_ERROR_HANDLE } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Processes -//////////////////////////////////////////////////////////////////////////////// - -/// A value representing a child process. -/// -/// The lifetime of this value is linked to the lifetime of the actual -/// process - the Process destructor calls self.finish() which waits -/// for the process to terminate. -pub struct Process { - handle: Handle, - main_thread_handle: Handle, -} - -impl Process { - pub fn kill(&mut self) -> io::Result<()> { - let result = unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) }; - if result == c::FALSE { - let error = unsafe { c::GetLastError() }; - // TerminateProcess returns ERROR_ACCESS_DENIED if the process has already been - // terminated (by us, or for any other reason). So check if the process was actually - // terminated, and if so, do not return an error. - if error != c::ERROR_ACCESS_DENIED || self.try_wait().is_err() { - return Err(crate::io::Error::from_raw_os_error(error as i32)); - } - } - Ok(()) - } - - pub fn id(&self) -> u32 { - unsafe { c::GetProcessId(self.handle.as_raw_handle()) } - } - - pub fn main_thread_handle(&self) -> BorrowedHandle<'_> { - self.main_thread_handle.as_handle() - } - - pub fn wait(&mut self) -> io::Result { - 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> { - 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, Default)] -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 { - Some(self.0 as i32) - } -} - -/// Converts a raw `c::DWORD` to a type-safe `ExitStatus` by wrapping it without copying. -impl From 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 for ExitStatusError { - fn into(self) -> ExitStatus { - ExitStatus(self.0.into()) - } -} - -impl ExitStatusError { - pub fn code(self) -> Option { - 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 for ExitCode { - fn from(code: u8) -> Self { - ExitCode(c::DWORD::from(code)) - } -} - -impl From for ExitCode { - fn from(code: u32) -> Self { - ExitCode(c::DWORD::from(code)) - } -} - -fn zeroed_startupinfo() -> c::STARTUPINFOW { - c::STARTUPINFOW { - cb: 0, - lpReserved: ptr::null_mut(), - lpDesktop: ptr::null_mut(), - lpTitle: ptr::null_mut(), - dwX: 0, - dwY: 0, - dwXSize: 0, - dwYSize: 0, - dwXCountChars: 0, - dwYCountChars: 0, - dwFillAttribute: 0, - dwFlags: 0, - wShowWindow: 0, - cbReserved2: 0, - lpReserved2: ptr::null_mut(), - hStdInput: ptr::null_mut(), - hStdOutput: ptr::null_mut(), - hStdError: ptr::null_mut(), - } -} - -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> { - // Encode the command and arguments in a command line string such - // that the spawned process may recover them using CommandLineToArgvW. - let mut cmd: Vec = Vec::new(); - - // 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> { - let mut system: Vec = 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>) -> io::Result<(*mut c_void, Vec)> { - // On Windows we pass an "environment block" which is not a char**, but - // rather a concatenation of null-terminated k=v\0 sequences, with a final - // \0 to terminate. - 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)> { - match d { - Some(dir) => { - let mut dir_str: Vec = ensure_no_nuls(dir)?.encode_wide().collect(); - dir_str.push(0); - Ok((dir_str.as_ptr(), dir_str)) - } - None => Ok((ptr::null(), Vec::new())), - } -} - -struct ProcThreadAttributeList(Box<[MaybeUninit]>); - -impl Drop for ProcThreadAttributeList { - fn drop(&mut self) { - let lp_attribute_list = self.0.as_mut_ptr() as _; - unsafe { c::DeleteProcThreadAttributeList(lp_attribute_list) } - } -} - -/// Wrapper around the value data to be used as a Process Thread Attribute. -struct ProcThreadAttributeValue { - data: Box, - size: usize, -} - -fn make_proc_thread_attribute_list( - attributes: &BTreeMap, -) -> io::Result { - // To initialize our ProcThreadAttributeList, we need to determine - // how many bytes to allocate for it. The Windows API simplifies this process - // by allowing us to call `InitializeProcThreadAttributeList` with - // a null pointer to retrieve the required size. - let mut required_size = 0; - let Ok(attribute_count) = attributes.len().try_into() else { - return Err(io::const_io_error!( - ErrorKind::InvalidInput, - "maximum number of ProcThreadAttributes exceeded", - )); - }; - unsafe { - c::InitializeProcThreadAttributeList( - ptr::null_mut(), - attribute_count, - 0, - &mut required_size, - ) - }; - - let mut proc_thread_attribute_list = - ProcThreadAttributeList(vec![MaybeUninit::uninit(); required_size].into_boxed_slice()); - - // Once we've allocated the necessary memory, it's safe to invoke - // `InitializeProcThreadAttributeList` to properly initialize the list. - cvt(unsafe { - c::InitializeProcThreadAttributeList( - proc_thread_attribute_list.0.as_mut_ptr() as *mut _, - attribute_count, - 0, - &mut required_size, - ) - })?; - - // # Add our attributes to the buffer. - // It's theoretically possible for the attribute count to exceed a u32 value. - // Therefore, we ensure that we don't add more attributes than the buffer was initialized for. - for (&attribute, value) in attributes.iter().take(attribute_count as usize) { - let value_ptr = &*value.data as *const (dyn Send + Sync) as _; - cvt(unsafe { - c::UpdateProcThreadAttribute( - proc_thread_attribute_list.0.as_mut_ptr() as _, - 0, - attribute, - value_ptr, - value.size, - ptr::null_mut(), - ptr::null_mut(), - ) - })?; - } - - Ok(proc_thread_attribute_list) -} - -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) { - 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 deleted file mode 100644 index 3fc0c75240c..00000000000 --- a/library/std/src/sys/windows/process/tests.rs +++ /dev/null @@ -1,222 +0,0 @@ -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::>(), - 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("".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 deleted file mode 100644 index 5d8fd13785a..00000000000 --- a/library/std/src/sys/windows/rand.rs +++ /dev/null @@ -1,42 +0,0 @@ -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 c::nt_success(ret) { v } else { fallback_rng() } -} - -/// Generate random numbers using the fallback RNG function (RtlGenRandom) -/// -/// This is necessary because of a failure to load the SysWOW64 variant of the -/// bcryptprimitives.dll library from code that lives in bcrypt.dll -/// See -#[cfg(not(target_vendor = "uwp"))] -#[inline(never)] -fn fallback_rng() -> (u64, u64) { - use crate::ffi::c_void; - use crate::io; - - let mut v = (0, 0); - let ret = unsafe { - c::RtlGenRandom(&mut v as *mut _ as *mut c_void, 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 deleted file mode 100644 index 627763da856..00000000000 --- a/library/std/src/sys/windows/stack_overflow.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![cfg_attr(test, allow(dead_code))] - -use crate::sys::c; -use crate::thread; - -use super::api; - -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 - && api::get_last_error().code != c::ERROR_CALL_NOT_IMPLEMENTED - { - panic!("failed to reserve stack space for exception handling"); - } - Handler - } -} - -unsafe 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("") - ); - } - c::EXCEPTION_CONTINUE_SEARCH - } -} - -pub unsafe fn init() { - if c::AddVectoredExceptionHandler(0, Some(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 deleted file mode 100644 index afdf7f566ae..00000000000 --- a/library/std/src/sys/windows/stack_overflow_uwp.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![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 deleted file mode 100644 index 819a48266d9..00000000000 --- a/library/std/src/sys/windows/stdio.rs +++ /dev/null @@ -1,462 +0,0 @@ -#![unstable(issue = "none", feature = "windows_stdio")] - -use crate::cmp; -use crate::io; -use crate::mem::MaybeUninit; -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 crate::sys::windows::api; -use core::str::utf8_char_width; - -#[cfg(test)] -mod tests; - -// 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 { - 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 { - 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 { - debug_assert!(!utf8.is_empty()); - - let mut utf16 = [MaybeUninit::::uninit(); MAX_BUFFER_SIZE / 2]; - let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len())]; - - let utf16: &[u16] = unsafe { - // Note that this theoretically checks validity twice in the (most common) case - // where the underlying byte sequence is valid utf-8 (given the check in `write()`). - let result = c::MultiByteToWideChar( - c::CP_UTF8, // CodePage - c::MB_ERR_INVALID_CHARS, // dwFlags - utf8.as_ptr(), // lpMultiByteStr - utf8.len() as c::c_int, // cbMultiByte - utf16.as_mut_ptr() as c::LPWSTR, // lpWideCharStr - utf16.len() as c::c_int, // cchWideChar - ); - assert!(result != 0, "Unexpected error in MultiByteToWideChar"); - - // Safety: MultiByteToWideChar initializes `result` values. - MaybeUninit::slice_assume_init_ref(&utf16[..result as usize]) - }; - - 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_code_unit_remaining = utf16[written]; - if matches!(first_code_unit_remaining, 0xDCEE..=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 { - debug_assert!(data.len() < u32::MAX as 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 { - 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() { - 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 = [MaybeUninit::new(0); 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( - unsafe { MaybeUninit::slice_assume_init_ref(&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 = [MaybeUninit::::uninit(); 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)?; - // Safety `read_u16s_fixup_surrogates` returns the number of items - // initialized. - let utf16s = unsafe { MaybeUninit::slice_assume_init_ref(&utf16_buf[..read]) }; - match utf16_to_utf8(utf16s, 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 [MaybeUninit], - mut amount: usize, - surrogate: &mut u16, -) -> io::Result { - // Insert possibly remaining unpaired surrogate from last read. - let mut start = 0; - if *surrogate != 0 { - buf[0] = MaybeUninit::new(*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 { - // Safety: The returned `amount` is the number of values initialized, - // and it is not 0, so we know that `buf[amount - 1]` have been - // initialized. - let last_char = unsafe { buf[amount - 1].assume_init() }; - if matches!(last_char, 0xD800..=0xDBFF) { - // high surrogate - *surrogate = last_char; - amount -= 1; - } - } - Ok(amount) -} - -// Returns `Ok(n)` if it initialized `n` values in `buf`. -fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit]) -> io::Result { - // 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 input_control = c::CONSOLE_READCONSOLE_CONTROL { - nLength: crate::mem::size_of::() 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, - &input_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 && api::get_last_error().code == c::ERROR_OPERATION_ABORTED { - continue; - } - break; - } - // Safety: if `amount > 0`, then that many bytes were written, so - // `buf[amount as usize - 1]` has been initialized. - if amount > 0 && unsafe { buf[amount as usize - 1].assume_init() } == CTRL_Z { - amount -= 1; - } - Ok(amount as usize) -} - -fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { - debug_assert!(utf16.len() <= c::c_int::MAX as usize); - debug_assert!(utf8.len() <= c::c_int::MAX as usize); - - if utf16.is_empty() { - return Ok(0); - } - - let result = unsafe { - c::WideCharToMultiByte( - c::CP_UTF8, // CodePage - c::WC_ERR_INVALID_CHARS, // dwFlags - utf16.as_ptr(), // lpWideCharStr - utf16.len() as c::c_int, // cchWideChar - utf8.as_mut_ptr(), // lpMultiByteStr - utf8.len() as c::c_int, // cbMultiByte - ptr::null(), // lpDefaultChar - ptr::null_mut(), // lpUsedDefaultChar - ) - }; - if result == 0 { - // We can't really do any better than forget all data and return an error. - Err(io::const_io_error!( - io::ErrorKind::InvalidData, - "Windows stdin in console mode does not support non-UTF-16 input; \ - encountered unpaired surrogate", - )) - } else { - Ok(result as usize) - } -} - -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 { - 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 { - 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 { - Some(Stderr::new()) -} diff --git a/library/std/src/sys/windows/stdio/tests.rs b/library/std/src/sys/windows/stdio/tests.rs deleted file mode 100644 index 1e53e0bee63..00000000000 --- a/library/std/src/sys/windows/stdio/tests.rs +++ /dev/null @@ -1,6 +0,0 @@ -use super::utf16_to_utf8; - -#[test] -fn zero_size_read() { - assert_eq!(utf16_to_utf8(&[], &mut []).unwrap(), 0); -} diff --git a/library/std/src/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs deleted file mode 100644 index 1fe74493519..00000000000 --- a/library/std/src/sys/windows/thread.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::ffi::CStr; -use crate::io; -use crate::num::NonZeroUsize; -use crate::os::windows::io::AsRawHandle; -use crate::os::windows::io::HandleOrNull; -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 core::ffi::c_void; - -use super::time::WaitableTimer; -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) -> io::Result { - let p = Box::into_raw(Box::new(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, - Some(thread_start), - p as *mut _, - c::STACK_SIZE_PARAM_IS_A_RESERVATION, - ptr::null_mut(), - ); - let ret = HandleOrNull::from_raw_handle(ret); - 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)(); - } - 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) { - fn high_precision_sleep(dur: Duration) -> Result<(), ()> { - let timer = WaitableTimer::high_resolution()?; - timer.set(dur)?; - timer.wait() - } - // Attempt to use high-precision sleep (Windows 10, version 1803+). - // On error fallback to the standard `Sleep` function. - // Also preserves the zero duration behaviour of `Sleep`. - if dur.is_zero() || high_precision_sleep(dur).is_err() { - 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 { - 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 { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/windows/thread_local_dtor.rs b/library/std/src/sys/windows/thread_local_dtor.rs deleted file mode 100644 index cf542d2bfb8..00000000000 --- a/library/std/src/sys/windows/thread_local_dtor.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! Implements thread-local destructors that are not associated with any -//! particular data. - -#![unstable(feature = "thread_local_internals", issue = "none")] -#![cfg(target_thread_local)] - -pub use super::thread_local_key::register_keyless_dtor as register_dtor; diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs deleted file mode 100644 index 5eee4a9667b..00000000000 --- a/library/std/src/sys/windows/thread_local_key.rs +++ /dev/null @@ -1,341 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::ptr; -use crate::sync::atomic::{ - AtomicBool, AtomicPtr, AtomicU32, - Ordering::{AcqRel, Acquire, Relaxed, Release}, -}; -use crate::sys::c; - -#[cfg(test)] -mod tests; - -/// An optimization hint. The compiler is often smart enough to know if an atomic -/// is never set and can remove dead code based on that fact. -static HAS_DTORS: AtomicBool = AtomicBool::new(false); - -// Using a per-thread list avoids the problems in synchronizing global state. -#[thread_local] -#[cfg(target_thread_local)] -static DESTRUCTORS: crate::cell::RefCell> = - crate::cell::RefCell::new(Vec::new()); - -// Ensure this can never be inlined because otherwise this may break in dylibs. -// See #44391. -#[inline(never)] -#[cfg(target_thread_local)] -pub unsafe fn register_keyless_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - match DESTRUCTORS.try_borrow_mut() { - Ok(mut dtors) => dtors.push((t, dtor)), - Err(_) => rtabort!("global allocator may not use TLS"), - } - - HAS_DTORS.store(true, Relaxed); -} - -#[inline(never)] // See comment above -#[cfg(target_thread_local)] -/// Runs destructors. This should not be called until thread exit. -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. - loop { - // Use a let-else binding to ensure the `RefCell` guard is dropped - // immediately. Otherwise, a panic would occur if a TLS destructor - // tries to access the list. - let Some((ptr, dtor)) = DESTRUCTORS.borrow_mut().pop() else { - break; - }; - (dtor)(ptr); - } - // We're done so free the memory. - DESTRUCTORS.replace(Vec::new()); -} - -type Key = c::DWORD; -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. -// -// 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 - -pub struct StaticKey { - /// The key value shifted up by one. Since TLS_OUT_OF_INDEXES == DWORD::MAX - /// is not a valid key value, this allows us to use zero as sentinel value - /// without risking overflow. - key: AtomicU32, - dtor: Option, - next: AtomicPtr, - /// Currently, destructors cannot be unregistered, so we cannot use racy - /// initialization for keys. Instead, we need synchronize initialization. - /// Use the Windows-provided `Once` since it does not require TLS. - once: UnsafeCell, -} - -impl StaticKey { - #[inline] - pub const fn new(dtor: Option) -> StaticKey { - StaticKey { - key: AtomicU32::new(0), - dtor, - next: AtomicPtr::new(ptr::null_mut()), - once: UnsafeCell::new(c::INIT_ONCE_STATIC_INIT), - } - } - - #[inline] - pub unsafe fn set(&'static self, val: *mut u8) { - let r = c::TlsSetValue(self.key(), val.cast()); - debug_assert_eq!(r, c::TRUE); - } - - #[inline] - pub unsafe fn get(&'static self) -> *mut u8 { - c::TlsGetValue(self.key()).cast() - } - - #[inline] - unsafe fn key(&'static self) -> Key { - match self.key.load(Acquire) { - 0 => self.init(), - key => key - 1, - } - } - - #[cold] - unsafe fn init(&'static self) -> Key { - if self.dtor.is_some() { - let mut pending = c::FALSE; - let r = c::InitOnceBeginInitialize(self.once.get(), 0, &mut pending, ptr::null_mut()); - assert_eq!(r, c::TRUE); - - if pending == c::FALSE { - // Some other thread initialized the key, load it. - self.key.load(Relaxed) - 1 - } else { - let key = c::TlsAlloc(); - if key == c::TLS_OUT_OF_INDEXES { - // Wakeup the waiting threads before panicking to avoid deadlock. - c::InitOnceComplete(self.once.get(), c::INIT_ONCE_INIT_FAILED, ptr::null_mut()); - panic!("out of TLS indexes"); - } - - self.key.store(key + 1, Release); - register_dtor(self); - - let r = c::InitOnceComplete(self.once.get(), 0, ptr::null_mut()); - debug_assert_eq!(r, c::TRUE); - - key - } - } else { - // If there is no destructor to clean up, we can use racy initialization. - - let key = c::TlsAlloc(); - assert_ne!(key, c::TLS_OUT_OF_INDEXES, "out of TLS indexes"); - - match self.key.compare_exchange(0, key + 1, AcqRel, Acquire) { - Ok(_) => key, - Err(new) => { - // Some other thread completed initialization first, so destroy - // our key and use theirs. - let r = c::TlsFree(key); - debug_assert_eq!(r, c::TRUE); - new - 1 - } - } - } - } -} - -unsafe impl Send for StaticKey {} -unsafe impl Sync for StaticKey {} - -// ------------------------------------------------------------------------- -// 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. - -static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); - -/// Should only be called once per key, otherwise loops or breaks may occur in -/// the linked list. -unsafe fn register_dtor(key: &'static StaticKey) { - // Ensure this is never run when native thread locals are available. - assert_eq!(false, cfg!(target_thread_local)); - let this = <*const StaticKey>::cast_mut(key); - // Use acquire ordering to pass along the changes done by the previously - // registered keys when we store the new head with release ordering. - let mut head = DTORS.load(Acquire); - loop { - key.next.store(head, Relaxed); - match DTORS.compare_exchange_weak(head, this, Release, Acquire) { - Ok(_) => break, - Err(new) => head = new, - } - } - HAS_DTORS.store(true, Release); -} - -// ------------------------------------------------------------------------- -// 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 !HAS_DTORS.load(Acquire) { - return; - } - if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH { - #[cfg(not(target_thread_local))] - run_dtors(); - #[cfg(target_thread_local)] - 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 below -unsafe fn run_dtors() { - for _ in 0..5 { - let mut any_run = false; - - // Use acquire ordering to observe key initialization. - let mut cur = DTORS.load(Acquire); - while !cur.is_null() { - let key = (*cur).key.load(Relaxed) - 1; - let dtor = (*cur).dtor.unwrap(); - - let ptr = c::TlsGetValue(key); - if !ptr.is_null() { - c::TlsSetValue(key, ptr::null_mut()); - dtor(ptr as *mut _); - any_run = true; - } - - cur = (*cur).next.load(Relaxed); - } - - if !any_run { - break; - } - } -} diff --git a/library/std/src/sys/windows/thread_local_key/tests.rs b/library/std/src/sys/windows/thread_local_key/tests.rs deleted file mode 100644 index c739f0caf3e..00000000000 --- a/library/std/src/sys/windows/thread_local_key/tests.rs +++ /dev/null @@ -1,57 +0,0 @@ -// This file only tests the thread local key fallback. -// Windows targets with native thread local support do not use this. -#![cfg(not(target_thread_local))] - -use super::StaticKey; -use crate::ptr; - -#[test] -fn smoke() { - 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(ptr::invalid_mut(1)); - K2.set(ptr::invalid_mut(2)); - assert_eq!(K1.get() as usize, 1); - assert_eq!(K2.get() as usize, 2); - } -} - -#[test] -fn destructors() { - use crate::mem::ManuallyDrop; - use crate::sync::Arc; - use crate::thread; - - unsafe extern "C" fn destruct(ptr: *mut u8) { - drop(Arc::from_raw(ptr as *const ())); - } - - static KEY: StaticKey = StaticKey::new(Some(destruct)); - - let shared1 = Arc::new(()); - let shared2 = Arc::clone(&shared1); - - unsafe { - assert!(KEY.get().is_null()); - KEY.set(Arc::into_raw(shared1) as *mut u8); - } - - thread::spawn(move || unsafe { - assert!(KEY.get().is_null()); - KEY.set(Arc::into_raw(shared2) as *mut u8); - }) - .join() - .unwrap(); - - // Leak the Arc, let the TLS destructor clean it up. - let shared1 = unsafe { ManuallyDrop::new(Arc::from_raw(KEY.get() as *const ())) }; - assert_eq!( - Arc::strong_count(&shared1), - 1, - "destructor should have dropped the other reference on thread exit" - ); -} diff --git a/library/std/src/sys/windows/thread_parking.rs b/library/std/src/sys/windows/thread_parking.rs deleted file mode 100644 index eb9167cd855..00000000000 --- a/library/std/src/sys/windows/thread_parking.rs +++ /dev/null @@ -1,253 +0,0 @@ -// 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_in_place(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 { - unsafe { - if let Some(wake_by_address_single) = c::WakeByAddressSingle::option() { - 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. - 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 = 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 deleted file mode 100644 index 09e78a29304..00000000000 --- a/library/std/src/sys/windows/time.rs +++ /dev/null @@ -1,262 +0,0 @@ -use crate::cmp::Ordering; -use crate::fmt; -use crate::mem; -use crate::ptr::null; -use crate::sys::c; -use crate::sys_common::IntoInner; -use crate::time::Duration; - -use core::hash::{Hash, Hasher}; -use core::ops::Neg; - -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 { - // 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 { - Some(Instant { t: self.t.checked_add(*other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - 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 { - 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 { - let intervals = self.intervals().checked_add(checked_dur2intervals(other)?)?; - Some(SystemTime::from_intervals(intervals)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - 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 { - 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 for SystemTime { - fn from(t: c::FILETIME) -> SystemTime { - SystemTime { t } - } -} - -impl IntoInner for SystemTime { - fn into_inner(self) -> c::FILETIME { - self.t - } -} - -impl Hash for SystemTime { - fn hash(&self, state: &mut H) { - self.intervals().hash(state) - } -} - -fn checked_dur2intervals(dur: &Duration) -> Option { - 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 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 - } -} - -/// A timer you can wait on. -pub(super) struct WaitableTimer { - handle: c::HANDLE, -} -impl WaitableTimer { - /// Create a high-resolution timer. Will fail before Windows 10, version 1803. - pub fn high_resolution() -> Result { - let handle = unsafe { - c::CreateWaitableTimerExW( - null(), - null(), - c::CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, - c::TIMER_ALL_ACCESS, - ) - }; - if !handle.is_null() { Ok(Self { handle }) } else { Err(()) } - } - pub fn set(&self, duration: Duration) -> Result<(), ()> { - // Convert the Duration to a format similar to FILETIME. - // Negative values are relative times whereas positive values are absolute. - // Therefore we negate the relative duration. - let time = checked_dur2intervals(&duration).ok_or(())?.neg(); - let result = unsafe { c::SetWaitableTimer(self.handle, &time, 0, None, null(), c::FALSE) }; - if result != 0 { Ok(()) } else { Err(()) } - } - pub fn wait(&self) -> Result<(), ()> { - let result = unsafe { c::WaitForSingleObject(self.handle, c::INFINITE) }; - if result != c::WAIT_FAILED { Ok(()) } else { Err(()) } - } -} -impl Drop for WaitableTimer { - fn drop(&mut self) { - unsafe { c::CloseHandle(self.handle) }; - } -} diff --git a/library/std/src/sys/xous/alloc.rs b/library/std/src/sys/xous/alloc.rs deleted file mode 100644 index b3a3e691e0d..00000000000 --- a/library/std/src/sys/xous/alloc.rs +++ /dev/null @@ -1,62 +0,0 @@ -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) } - } -} - -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; - } - crate::os::xous::ffi::do_yield(); - } - } - - impl Drop for DropLock { - fn drop(&mut self) { - let r = LOCKED.swap(0, SeqCst); - debug_assert_eq!(r, 1); - } - } -} diff --git a/library/std/src/sys/xous/locks/condvar.rs b/library/std/src/sys/xous/locks/condvar.rs deleted file mode 100644 index 1bb38dfa341..00000000000 --- a/library/std/src/sys/xous/locks/condvar.rs +++ /dev/null @@ -1,111 +0,0 @@ -use super::mutex::Mutex; -use crate::os::xous::ffi::{blocking_scalar, scalar}; -use crate::os::xous::services::ticktimer_server; -use crate::sync::Mutex as StdMutex; -use crate::time::Duration; - -// The implementation is inspired by Andrew D. Birrell's paper -// "Implementing Condition Variables with Semaphores" - -pub struct Condvar { - counter: StdMutex, -} - -unsafe impl Send for Condvar {} -unsafe impl Sync for Condvar {} - -impl Condvar { - #[inline] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - pub const fn new() -> Condvar { - Condvar { counter: StdMutex::new(0) } - } - - pub fn notify_one(&self) { - let mut counter = self.counter.lock().unwrap(); - if *counter <= 0 { - return; - } else { - *counter -= 1; - } - let result = blocking_scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), 1).into(), - ); - drop(counter); - result.expect("failure to send NotifyCondition command"); - } - - pub fn notify_all(&self) { - let mut counter = self.counter.lock().unwrap(); - if *counter <= 0 { - return; - } - let result = blocking_scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), *counter) - .into(), - ); - *counter = 0; - drop(counter); - - result.expect("failure to send NotifyCondition command"); - } - - fn index(&self) -> usize { - self as *const Condvar as usize - } - - pub unsafe fn wait(&self, mutex: &Mutex) { - let mut counter = self.counter.lock().unwrap(); - *counter += 1; - unsafe { mutex.unlock() }; - drop(counter); - - let result = blocking_scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), 0).into(), - ); - unsafe { mutex.lock() }; - - result.expect("Ticktimer: failure to send WaitForCondition command"); - } - - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - let mut counter = self.counter.lock().unwrap(); - *counter += 1; - unsafe { mutex.unlock() }; - drop(counter); - - let mut millis = dur.as_millis() as usize; - if millis == 0 { - millis = 1; - } - - let result = blocking_scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), millis) - .into(), - ); - unsafe { mutex.lock() }; - - let result = result.expect("Ticktimer: failure to send WaitForCondition command")[0] == 0; - - // If we awoke due to a timeout, decrement the wake count, as that would not have - // been done in the `notify()` call. - if !result { - *self.counter.lock().unwrap() -= 1; - } - result - } -} - -impl Drop for Condvar { - fn drop(&mut self) { - scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::FreeCondition(self.index()).into(), - ) - .ok(); - } -} diff --git a/library/std/src/sys/xous/locks/mod.rs b/library/std/src/sys/xous/locks/mod.rs deleted file mode 100644 index f3c5c5d9fb0..00000000000 --- a/library/std/src/sys/xous/locks/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod condvar; -mod mutex; -mod rwlock; - -pub use condvar::*; -pub use mutex::*; -pub use rwlock::*; diff --git a/library/std/src/sys/xous/locks/mutex.rs b/library/std/src/sys/xous/locks/mutex.rs deleted file mode 100644 index ea51776d54e..00000000000 --- a/library/std/src/sys/xous/locks/mutex.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::os::xous::ffi::{blocking_scalar, do_yield, scalar}; -use crate::os::xous::services::ticktimer_server; -use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed, Ordering::SeqCst}; - -pub struct Mutex { - /// The "locked" value indicates how many threads are waiting on this - /// Mutex. Possible values are: - /// 0: The lock is unlocked - /// 1: The lock is locked and uncontended - /// >=2: The lock is locked and contended - /// - /// A lock is "contended" when there is more than one thread waiting - /// for a lock, or it is locked for long periods of time. Rather than - /// spinning, these locks send a Message to the ticktimer server - /// requesting that they be woken up when a lock is unlocked. - locked: AtomicUsize, - - /// Whether this Mutex ever was contended, and therefore made a trip - /// to the ticktimer server. If this was never set, then we were never - /// on the slow path and can skip deregistering the mutex. - contended: AtomicBool, -} - -impl Mutex { - #[inline] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - pub const fn new() -> Mutex { - Mutex { locked: AtomicUsize::new(0), contended: AtomicBool::new(false) } - } - - fn index(&self) -> usize { - self as *const Mutex as usize - } - - #[inline] - pub unsafe fn lock(&self) { - // Try multiple times to acquire the lock without resorting to the ticktimer - // server. For locks that are held for a short amount of time, this will - // result in the ticktimer server never getting invoked. The `locked` value - // will be either 0 or 1. - for _attempts in 0..3 { - if unsafe { self.try_lock() } { - return; - } - do_yield(); - } - - // Try one more time to lock. If the lock is released between the previous code and - // here, then the inner `locked` value will be 1 at the end of this. If it was not - // locked, then the value will be more than 1, for example if there are multiple other - // threads waiting on this lock. - if unsafe { self.try_lock_or_poison() } { - return; - } - - // When this mutex is dropped, we will need to deregister it with the server. - self.contended.store(true, Relaxed); - - // The lock is now "contended". When the lock is released, a Message will get sent to the - // ticktimer server to wake it up. Note that this may already have happened, so the actual - // value of `lock` may be anything (0, 1, 2, ...). - blocking_scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::LockMutex(self.index()).into(), - ) - .expect("failure to send LockMutex command"); - } - - #[inline] - pub unsafe fn unlock(&self) { - let prev = self.locked.fetch_sub(1, SeqCst); - - // If the previous value was 1, then this was a "fast path" unlock, so no - // need to involve the Ticktimer server - if prev == 1 { - return; - } - - // If it was 0, then something has gone seriously wrong and the counter - // has just wrapped around. - if prev == 0 { - panic!("mutex lock count underflowed"); - } - - // Unblock one thread that is waiting on this message. - scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::UnlockMutex(self.index()).into(), - ) - .expect("failure to send UnlockMutex command"); - } - - #[inline] - pub unsafe fn try_lock(&self) -> bool { - self.locked.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() - } - - #[inline] - pub unsafe fn try_lock_or_poison(&self) -> bool { - self.locked.fetch_add(1, SeqCst) == 0 - } -} - -impl Drop for Mutex { - fn drop(&mut self) { - // If there was Mutex contention, then we involved the ticktimer. Free - // the resources associated with this Mutex as it is deallocated. - if self.contended.load(Relaxed) { - scalar( - ticktimer_server(), - crate::os::xous::services::TicktimerScalar::FreeMutex(self.index()).into(), - ) - .ok(); - } - } -} diff --git a/library/std/src/sys/xous/locks/rwlock.rs b/library/std/src/sys/xous/locks/rwlock.rs deleted file mode 100644 index 618da758adf..00000000000 --- a/library/std/src/sys/xous/locks/rwlock.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::os::xous::ffi::do_yield; -use crate::sync::atomic::{AtomicIsize, Ordering::SeqCst}; - -pub struct RwLock { - /// The "mode" value indicates how many threads are waiting on this - /// Mutex. Possible values are: - /// -1: The lock is locked for writing - /// 0: The lock is unlocked - /// >=1: The lock is locked for reading - /// - /// This currently spins waiting for the lock to be freed. An - /// optimization would be to involve the ticktimer server to - /// coordinate unlocks. - mode: AtomicIsize, -} - -unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} - -impl RwLock { - #[inline] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - pub const fn new() -> RwLock { - RwLock { mode: AtomicIsize::new(0) } - } - - #[inline] - pub unsafe fn read(&self) { - while !unsafe { self.try_read() } { - do_yield(); - } - } - - #[inline] - pub unsafe fn try_read(&self) -> bool { - // Non-atomically determine the current value. - let current = self.mode.load(SeqCst); - - // If it's currently locked for writing, then we cannot read. - if current < 0 { - return false; - } - - // Attempt to lock. If the `current` value has changed, then this - // operation will fail and we will not obtain the lock even if we - // could potentially keep it. - let new = current + 1; - self.mode.compare_exchange(current, new, SeqCst, SeqCst).is_ok() - } - - #[inline] - pub unsafe fn write(&self) { - while !unsafe { self.try_write() } { - do_yield(); - } - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - self.mode.compare_exchange(0, -1, SeqCst, SeqCst).is_ok() - } - - #[inline] - pub unsafe fn read_unlock(&self) { - self.mode.fetch_sub(1, SeqCst); - } - - #[inline] - pub unsafe fn write_unlock(&self) { - assert_eq!(self.mode.compare_exchange(-1, 0, SeqCst, SeqCst), Ok(-1)); - } -} diff --git a/library/std/src/sys/xous/mod.rs b/library/std/src/sys/xous/mod.rs deleted file mode 100644 index c2550dcfd83..00000000000 --- a/library/std/src/sys/xous/mod.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] - -pub mod alloc; -#[path = "../unsupported/args.rs"] -pub mod args; -#[path = "../unix/cmath.rs"] -pub mod cmath; -#[path = "../unsupported/env.rs"] -pub mod env; -#[path = "../unsupported/fs.rs"] -pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -pub mod locks; -#[path = "../unsupported/net.rs"] -pub mod net; -#[path = "../unsupported/once.rs"] -pub mod once; -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_key; -pub mod thread_parking; -pub mod time; - -#[path = "../unsupported/common.rs"] -mod common; -pub use common::*; diff --git a/library/std/src/sys/xous/os.rs b/library/std/src/sys/xous/os.rs deleted file mode 100644 index 8d2eaee8aa6..00000000000 --- a/library/std/src/sys/xous/os.rs +++ /dev/null @@ -1,174 +0,0 @@ -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::os::xous::ffi::Error as XousError; -use crate::path::{self, PathBuf}; - -#[cfg(not(test))] -#[cfg(feature = "panic_unwind")] -mod eh_unwinding { - pub(crate) struct EhFrameFinder(usize /* eh_frame */); - pub(crate) static mut EH_FRAME_SETTINGS: EhFrameFinder = EhFrameFinder(0); - impl EhFrameFinder { - pub(crate) unsafe fn init(&mut self, eh_frame: usize) { - unsafe { - EH_FRAME_SETTINGS.0 = eh_frame; - } - } - } - unsafe impl unwind::EhFrameFinder for EhFrameFinder { - fn find(&self, _pc: usize) -> Option { - Some(unwind::FrameInfo { - text_base: None, - kind: unwind::FrameInfoKind::EhFrame(self.0), - }) - } - } -} - -#[cfg(not(test))] -mod c_compat { - use crate::os::xous::ffi::exit; - extern "C" { - fn main() -> u32; - } - - #[no_mangle] - pub extern "C" fn abort() { - exit(1); - } - - #[no_mangle] - pub extern "C" fn _start(eh_frame: usize) { - #[cfg(feature = "panic_unwind")] - unsafe { - super::eh_unwinding::EH_FRAME_SETTINGS.init(eh_frame); - unwind::set_custom_eh_frame_finder(&super::eh_unwinding::EH_FRAME_SETTINGS).ok(); - } - exit(unsafe { main() }); - } - - // 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. - #[no_mangle] - // NB. used by both libunwind and libpanic_abort - pub extern "C" fn __rust_abort() -> ! { - exit(101); - } -} - -pub fn errno() -> i32 { - 0 -} - -pub fn error_string(errno: i32) -> String { - Into::::into(errno).to_string() -} - -pub fn getcwd() -> io::Result { - 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 { - self.0 - } -} - -#[derive(Debug)] -pub struct JoinPathsError; - -pub fn join_paths(_paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - 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 { - unsupported() -} - -pub struct Env(!); - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} - } -} - -impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} - } -} - -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 { - 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 { - None -} - -pub fn exit(code: i32) -> ! { - crate::os::xous::ffi::exit(code as u32); -} - -pub fn getpid() -> u32 { - panic!("no pids on this platform") -} diff --git a/library/std/src/sys/xous/stdio.rs b/library/std/src/sys/xous/stdio.rs deleted file mode 100644 index 2ac694641ba..00000000000 --- a/library/std/src/sys/xous/stdio.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::io; - -pub struct Stdin; -pub struct Stdout {} -pub struct Stderr; - -use crate::os::xous::ffi::{lend, try_lend, try_scalar, Connection}; -use crate::os::xous::services::{log_server, try_connect, LogScalar}; - -impl Stdin { - pub const fn new() -> Stdin { - Stdin - } -} - -impl io::Read for Stdin { - fn read(&mut self, _buf: &mut [u8]) -> io::Result { - Ok(0) - } -} - -impl Stdout { - pub const fn new() -> Stdout { - Stdout {} - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - #[repr(align(4096))] - struct LendBuffer([u8; 4096]); - let mut lend_buffer = LendBuffer([0u8; 4096]); - let connection = log_server(); - for chunk in buf.chunks(lend_buffer.0.len()) { - for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) { - *dest = *src; - } - lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap(); - } - 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 { - #[repr(align(4096))] - struct LendBuffer([u8; 4096]); - let mut lend_buffer = LendBuffer([0u8; 4096]); - let connection = log_server(); - for chunk in buf.chunks(lend_buffer.0.len()) { - for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) { - *dest = *src; - } - lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap(); - } - 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 -} - -#[derive(Copy, Clone)] -pub struct PanicWriter { - log: Connection, - gfx: Option, -} - -impl io::Write for PanicWriter { - fn write(&mut self, s: &[u8]) -> core::result::Result { - for c in s.chunks(core::mem::size_of::() * 4) { - // Text is grouped into 4x `usize` words. The id is 1100 plus - // the number of characters in this message. - // Ignore errors since we're already panicking. - try_scalar(self.log, LogScalar::AppendPanicMessage(&c).into()).ok(); - } - - // Serialize the text to the graphics panic handler, only if we were able - // to acquire a connection to it. Text length is encoded in the `valid` field, - // the data itself in the buffer. Typically several messages are require to - // fully transmit the entire panic message. - if let Some(gfx) = self.gfx { - #[repr(C, align(4096))] - struct Request([u8; 4096]); - let mut request = Request([0u8; 4096]); - for (&s, d) in s.iter().zip(request.0.iter_mut()) { - *d = s; - } - try_lend(gfx, 0 /* AppendPanicText */, &request.0, 0, s.len()).ok(); - } - Ok(s.len()) - } - - // Tests show that this does not seem to be reliably called at the end of a panic - // print, so, we can't rely on this to e.g. trigger a graphics update. - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub fn panic_output() -> Option { - // Generally this won't fail because every server has already connected, so - // this is likely to succeed. - let log = log_server(); - - // Send the "We're panicking" message (1000). - try_scalar(log, LogScalar::BeginPanic.into()).ok(); - - // This is will fail in the case that the connection table is full, or if the - // graphics server is not running. Most servers do not already have this connection. - let gfx = try_connect("panic-to-screen!"); - - Some(PanicWriter { log, gfx }) -} diff --git a/library/std/src/sys/xous/thread.rs b/library/std/src/sys/xous/thread.rs deleted file mode 100644 index 78c68de7bf3..00000000000 --- a/library/std/src/sys/xous/thread.rs +++ /dev/null @@ -1,144 +0,0 @@ -use crate::ffi::CStr; -use crate::io; -use crate::num::NonZeroUsize; -use crate::os::xous::ffi::{ - blocking_scalar, create_thread, do_yield, join_thread, map_memory, update_memory_flags, - MemoryFlags, Syscall, ThreadId, -}; -use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; -use crate::time::Duration; -use core::arch::asm; - -pub struct Thread { - tid: ThreadId, -} - -pub const DEFAULT_MIN_STACK_SIZE: usize = 131072; -const MIN_STACK_SIZE: usize = 4096; -pub const GUARD_PAGE_SIZE: usize = 4096; - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(stack: usize, p: Box) -> io::Result { - let p = Box::into_raw(Box::new(p)); - let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE); - - if (stack_size & 4095) != 0 { - stack_size = (stack_size + 4095) & !4095; - } - - // Allocate the whole thing, then divide it up after the fact. This ensures that - // even if there's a context switch during this function, the whole stack plus - // guard pages will remain contiguous. - let stack_plus_guard_pages: &mut [u8] = unsafe { - map_memory( - None, - None, - GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE, - MemoryFlags::R | MemoryFlags::W | MemoryFlags::X, - ) - } - .map_err(|code| io::Error::from_raw_os_error(code as i32))?; - - // No access to this page. Note: Write-only pages are illegal, and will - // cause an access violation. - unsafe { - update_memory_flags(&mut stack_plus_guard_pages[0..GUARD_PAGE_SIZE], MemoryFlags::W) - .map_err(|code| io::Error::from_raw_os_error(code as i32))? - }; - - // No access to this page. Note: Write-only pages are illegal, and will - // cause an access violation. - unsafe { - update_memory_flags( - &mut stack_plus_guard_pages[(GUARD_PAGE_SIZE + stack_size)..], - MemoryFlags::W, - ) - .map_err(|code| io::Error::from_raw_os_error(code as i32))? - }; - - let guard_page_pre = stack_plus_guard_pages.as_ptr() as usize; - let tid = create_thread( - thread_start as *mut usize, - &mut stack_plus_guard_pages[GUARD_PAGE_SIZE..(stack_size + GUARD_PAGE_SIZE)], - p as usize, - guard_page_pre, - stack_size, - 0, - ) - .map_err(|code| io::Error::from_raw_os_error(code as i32))?; - - extern "C" fn thread_start(main: *mut usize, guard_page_pre: usize, stack_size: usize) { - unsafe { - // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); - } - - // Destroy TLS, which will free the TLS page and call the destructor for - // any thread local storage. - unsafe { - crate::sys::thread_local_key::destroy_tls(); - } - - // Deallocate the stack memory, along with the guard pages. Afterwards, - // exit the thread by returning to the magic address 0xff80_3000usize, - // which tells the kernel to deallocate this thread. - let mapped_memory_base = guard_page_pre; - let mapped_memory_length = GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE; - unsafe { - asm!( - "ecall", - "ret", - in("a0") Syscall::UnmapMemory as usize, - in("a1") mapped_memory_base, - in("a2") mapped_memory_length, - in("ra") 0xff80_3000usize, - options(nomem, nostack, noreturn) - ); - } - } - - Ok(Thread { tid }) - } - - pub fn yield_now() { - do_yield(); - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - // Because the sleep server works on units of `usized milliseconds`, split - // the messages up into these chunks. This means we may run into issues - // if you try to sleep a thread for more than 49 days on a 32-bit system. - let mut millis = dur.as_millis(); - while millis > 0 { - let sleep_duration = - if millis > (usize::MAX as _) { usize::MAX } else { millis as usize }; - blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into()) - .expect("failed to send message to ticktimer server"); - millis -= sleep_duration as u128; - } - } - - pub fn join(self) { - join_thread(self.tid).unwrap(); - } -} - -pub fn available_parallelism() -> io::Result { - // We're unicore right now. - Ok(unsafe { NonZeroUsize::new_unchecked(1) }) -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/xous/thread_local_key.rs b/library/std/src/sys/xous/thread_local_key.rs deleted file mode 100644 index 3771ea65700..00000000000 --- a/library/std/src/sys/xous/thread_local_key.rs +++ /dev/null @@ -1,190 +0,0 @@ -use crate::mem::ManuallyDrop; -use crate::ptr; -use crate::sync::atomic::AtomicPtr; -use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; -use core::arch::asm; - -use crate::os::xous::ffi::{map_memory, unmap_memory, MemoryFlags}; - -/// Thread Local Storage -/// -/// Currently, we are limited to 1023 TLS entries. The entries -/// live in a page of memory that's unique per-process, and is -/// stored in the `$tp` register. If this register is 0, then -/// TLS has not been initialized and thread cleanup can be skipped. -/// -/// The index into this register is the `key`. This key is identical -/// between all threads, but indexes a different offset within this -/// pointer. -pub type Key = usize; - -pub type Dtor = unsafe extern "C" fn(*mut u8); - -const TLS_MEMORY_SIZE: usize = 4096; - -/// TLS keys start at `1` to mimic POSIX. -static TLS_KEY_INDEX: AtomicUsize = AtomicUsize::new(1); - -fn tls_ptr_addr() -> *mut usize { - let mut tp: usize; - unsafe { - asm!( - "mv {}, tp", - out(reg) tp, - ); - } - core::ptr::from_exposed_addr_mut::(tp) -} - -/// Create an area of memory that's unique per thread. This area will -/// contain all thread local pointers. -fn tls_ptr() -> *mut usize { - let mut tp = tls_ptr_addr(); - - // If the TP register is `0`, then this thread hasn't initialized - // its TLS yet. Allocate a new page to store this memory. - if tp.is_null() { - tp = unsafe { - map_memory( - None, - None, - TLS_MEMORY_SIZE / core::mem::size_of::(), - MemoryFlags::R | MemoryFlags::W, - ) - } - .expect("Unable to allocate memory for thread local storage") - .as_mut_ptr(); - - unsafe { - // Key #0 is currently unused. - (tp).write_volatile(0); - - // Set the thread's `$tp` register - asm!( - "mv tp, {}", - in(reg) tp as usize, - ); - } - } - tp -} - -/// Allocate a new TLS key. These keys are shared among all threads. -fn tls_alloc() -> usize { - TLS_KEY_INDEX.fetch_add(1, SeqCst) -} - -#[inline] -pub unsafe fn create(dtor: Option) -> Key { - let key = tls_alloc(); - if let Some(f) = dtor { - unsafe { register_dtor(key, f) }; - } - key -} - -#[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - assert!((key < 1022) && (key >= 1)); - unsafe { tls_ptr().add(key).write_volatile(value as usize) }; -} - -#[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - assert!((key < 1022) && (key >= 1)); - core::ptr::from_exposed_addr_mut::(unsafe { tls_ptr().add(key).read_volatile() }) -} - -#[inline] -pub unsafe fn destroy(_key: Key) { - panic!("can't destroy keys on Xous"); -} - -// ------------------------------------------------------------------------- -// Dtor registration (stolen from Windows) -// -// Xous 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 = 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, - } - } -} - -pub unsafe fn destroy_tls() { - let tp = tls_ptr_addr(); - - // If the pointer address is 0, then this thread has no TLS. - if tp.is_null() { - return; - } - unsafe { run_dtors() }; - - // Finally, free the TLS array - unsafe { - unmap_memory(core::slice::from_raw_parts_mut( - tp, - TLS_MEMORY_SIZE / core::mem::size_of::(), - )) - .unwrap() - }; -} - -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 = unsafe { get((*cur).key) }; - - if !ptr.is_null() { - unsafe { set((*cur).key, ptr::null_mut()) }; - unsafe { ((*cur).dtor)(ptr as *mut _) }; - any_run = true; - } - - unsafe { cur = (*cur).next }; - } - } -} diff --git a/library/std/src/sys/xous/thread_parking.rs b/library/std/src/sys/xous/thread_parking.rs deleted file mode 100644 index aa39c6d2718..00000000000 --- a/library/std/src/sys/xous/thread_parking.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::os::xous::ffi::{blocking_scalar, scalar}; -use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; -use crate::pin::Pin; -use crate::ptr; -use crate::sync::atomic::{ - AtomicI8, - Ordering::{Acquire, Release}, -}; -use crate::time::Duration; - -const NOTIFIED: i8 = 1; -const EMPTY: i8 = 0; -const PARKED: i8 = -1; - -pub struct Parker { - state: AtomicI8, -} - -impl Parker { - pub unsafe fn new_in_place(parker: *mut Parker) { - unsafe { parker.write(Parker { state: AtomicI8::new(EMPTY) }) } - } - - fn index(&self) -> usize { - ptr::from_ref(self).addr() - } - - pub unsafe fn park(self: Pin<&Self>) { - // Change NOTIFIED to EMPTY and EMPTY to PARKED. - let state = self.state.fetch_sub(1, Acquire); - if state == NOTIFIED { - return; - } - - // The state was set to PARKED. Wait until the `unpark` wakes us up. - blocking_scalar( - ticktimer_server(), - TicktimerScalar::WaitForCondition(self.index(), 0).into(), - ) - .expect("failed to send WaitForCondition command"); - - self.state.swap(EMPTY, Acquire); - } - - pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) { - // Change NOTIFIED to EMPTY and EMPTY to PARKED. - let state = self.state.fetch_sub(1, Acquire); - if state == NOTIFIED { - return; - } - - // A value of zero indicates an indefinite wait. Clamp the number of - // milliseconds to the allowed range. - let millis = usize::max(timeout.as_millis().try_into().unwrap_or(usize::MAX), 1); - - let was_timeout = blocking_scalar( - ticktimer_server(), - TicktimerScalar::WaitForCondition(self.index(), millis).into(), - ) - .expect("failed to send WaitForCondition command")[0] - != 0; - - let state = self.state.swap(EMPTY, Acquire); - if was_timeout && state == NOTIFIED { - // The state was set to NOTIFIED after we returned from the wait - // but before we reset the state. Therefore, a wakeup is on its - // way, which we need to consume here. - // NOTICE: this is a priority hole. - blocking_scalar( - ticktimer_server(), - TicktimerScalar::WaitForCondition(self.index(), 0).into(), - ) - .expect("failed to send WaitForCondition command"); - } - } - - pub fn unpark(self: Pin<&Self>) { - let state = self.state.swap(NOTIFIED, Release); - if state == PARKED { - // The thread is parked, wake it up. - blocking_scalar( - ticktimer_server(), - TicktimerScalar::NotifyCondition(self.index(), 1).into(), - ) - .expect("failed to send NotifyCondition command"); - } - } -} - -impl Drop for Parker { - fn drop(&mut self) { - scalar(ticktimer_server(), TicktimerScalar::FreeCondition(self.index()).into()).ok(); - } -} diff --git a/library/std/src/sys/xous/time.rs b/library/std/src/sys/xous/time.rs deleted file mode 100644 index 4e4ae67efff..00000000000 --- a/library/std/src/sys/xous/time.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::os::xous::ffi::blocking_scalar; -use crate::os::xous::services::{ - systime_server, ticktimer_server, SystimeScalar::GetUtcTimeMs, TicktimerScalar::ElapsedMs, -}; -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 { - let result = blocking_scalar(ticktimer_server(), ElapsedMs.into()) - .expect("failed to request elapsed_ms"); - let lower = result[0]; - let upper = result[1]; - Instant { 0: Duration::from_millis(lower as u64 | (upper as u64) << 32) } - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option { - self.0.checked_sub(other.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - self.0.checked_add(*other).map(Instant) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - self.0.checked_sub(*other).map(Instant) - } -} - -impl SystemTime { - pub fn now() -> SystemTime { - let result = blocking_scalar(systime_server(), GetUtcTimeMs.into()) - .expect("failed to request utc time in ms"); - let lower = result[0]; - let upper = result[1]; - SystemTime { 0: Duration::from_millis((upper as u64) << 32 | lower as u64) } - } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_add(*other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_sub(*other)?)) - } -} -- cgit 1.4.1-3-g733a5