]>
Commit | Line | Data |
---|---|---|
b7449926 | 1 | #![allow(missing_docs, nonstandard_style)] |
1a4d82fc | 2 | |
923072b8 | 3 | use crate::ffi::{CStr, OsStr, OsString}; |
532ac7d7 XL |
4 | use crate::io::ErrorKind; |
5 | use crate::os::windows::ffi::{OsStrExt, OsStringExt}; | |
6 | use crate::path::PathBuf; | |
7 | use crate::time::Duration; | |
1a4d82fc | 8 | |
abe05a73 XL |
9 | pub use self::rand::hashmap_random_keys; |
10 | ||
dfeec247 XL |
11 | #[macro_use] |
12 | pub mod compat; | |
c1a9b12d | 13 | |
a1dfa0c6 | 14 | pub mod alloc; |
c30ab7b3 | 15 | pub mod args; |
1a4d82fc | 16 | pub mod c; |
abe05a73 | 17 | pub mod cmath; |
c30ab7b3 | 18 | pub mod env; |
d9579d0f | 19 | pub mod fs; |
85aaf69f | 20 | pub mod handle; |
9fa01778 | 21 | pub mod io; |
5e7ed085 | 22 | pub mod locks; |
c30ab7b3 | 23 | pub mod memchr; |
85aaf69f | 24 | pub mod net; |
1a4d82fc | 25 | pub mod os; |
85aaf69f | 26 | pub mod os_str; |
c30ab7b3 | 27 | pub mod path; |
d9579d0f AL |
28 | pub mod pipe; |
29 | pub mod process; | |
7453a54e | 30 | pub mod rand; |
1a4d82fc | 31 | pub mod thread; |
3dfed10e XL |
32 | pub mod thread_local_dtor; |
33 | pub mod thread_local_key; | |
fc512014 | 34 | pub mod thread_parker; |
85aaf69f | 35 | pub mod time; |
416331ca XL |
36 | cfg_if::cfg_if! { |
37 | if #[cfg(not(target_vendor = "uwp"))] { | |
38 | pub mod stdio; | |
39 | pub mod stack_overflow; | |
40 | } else { | |
41 | pub mod stdio_uwp; | |
42 | pub mod stack_overflow_uwp; | |
43 | pub use self::stdio_uwp as stdio; | |
44 | pub use self::stack_overflow_uwp as stack_overflow; | |
45 | } | |
46 | } | |
1a4d82fc | 47 | |
cdc7bbd5 XL |
48 | // SAFETY: must be called only once during runtime initialization. |
49 | // NOTE: this is not guaranteed to run, for example when Rust code is called externally. | |
50 | pub unsafe fn init(_argc: isize, _argv: *const *const u8) { | |
51 | stack_overflow::init(); | |
923072b8 FG |
52 | |
53 | // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already | |
54 | // exists, we have to call it ourselves. | |
55 | thread::Thread::set_name(&CStr::from_bytes_with_nul_unchecked(b"main\0")); | |
cdc7bbd5 XL |
56 | } |
57 | ||
58 | // SAFETY: must be called only once during runtime cleanup. | |
59 | // NOTE: this is not guaranteed to run, for example when the program aborts. | |
60 | pub unsafe fn cleanup() { | |
61 | net::cleanup(); | |
62 | } | |
e9174d1e | 63 | |
85aaf69f | 64 | pub fn decode_error_kind(errno: i32) -> ErrorKind { |
136023e0 XL |
65 | use ErrorKind::*; |
66 | ||
92a42be0 | 67 | match errno as c::DWORD { |
136023e0 XL |
68 | c::ERROR_ACCESS_DENIED => return PermissionDenied, |
69 | c::ERROR_ALREADY_EXISTS => return AlreadyExists, | |
70 | c::ERROR_FILE_EXISTS => return AlreadyExists, | |
71 | c::ERROR_BROKEN_PIPE => return BrokenPipe, | |
72 | c::ERROR_FILE_NOT_FOUND => return NotFound, | |
73 | c::ERROR_PATH_NOT_FOUND => return NotFound, | |
74 | c::ERROR_NO_DATA => return BrokenPipe, | |
5099ac24 | 75 | c::ERROR_INVALID_NAME => return InvalidFilename, |
136023e0 XL |
76 | c::ERROR_INVALID_PARAMETER => return InvalidInput, |
77 | c::ERROR_NOT_ENOUGH_MEMORY | c::ERROR_OUTOFMEMORY => return OutOfMemory, | |
f035d41b XL |
78 | c::ERROR_SEM_TIMEOUT |
79 | | c::WAIT_TIMEOUT | |
80 | | c::ERROR_DRIVER_CANCEL_TIMEOUT | |
81 | | c::ERROR_OPERATION_ABORTED | |
82 | | c::ERROR_SERVICE_REQUEST_TIMEOUT | |
83 | | c::ERROR_COUNTER_TIMEOUT | |
84 | | c::ERROR_TIMEOUT | |
85 | | c::ERROR_RESOURCE_CALL_TIMED_OUT | |
86 | | c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT | |
87 | | c::ERROR_CTX_CLIENT_QUERY_TIMEOUT | |
88 | | c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT | |
89 | | c::ERROR_DS_TIMELIMIT_EXCEEDED | |
90 | | c::DNS_ERROR_RECORD_TIMED_OUT | |
91 | | c::ERROR_IPSEC_IKE_TIMED_OUT | |
92 | | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT | |
136023e0 XL |
93 | | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return TimedOut, |
94 | c::ERROR_CALL_NOT_IMPLEMENTED => return Unsupported, | |
95 | c::ERROR_HOST_UNREACHABLE => return HostUnreachable, | |
96 | c::ERROR_NETWORK_UNREACHABLE => return NetworkUnreachable, | |
97 | c::ERROR_DIRECTORY => return NotADirectory, | |
98 | c::ERROR_DIRECTORY_NOT_SUPPORTED => return IsADirectory, | |
99 | c::ERROR_DIR_NOT_EMPTY => return DirectoryNotEmpty, | |
100 | c::ERROR_WRITE_PROTECT => return ReadOnlyFilesystem, | |
101 | c::ERROR_DISK_FULL | c::ERROR_HANDLE_DISK_FULL => return StorageFull, | |
102 | c::ERROR_SEEK_ON_DEVICE => return NotSeekable, | |
103 | c::ERROR_DISK_QUOTA_EXCEEDED => return FilesystemQuotaExceeded, | |
104 | c::ERROR_FILE_TOO_LARGE => return FileTooLarge, | |
105 | c::ERROR_BUSY => return ResourceBusy, | |
106 | c::ERROR_POSSIBLE_DEADLOCK => return Deadlock, | |
107 | c::ERROR_NOT_SAME_DEVICE => return CrossesDevices, | |
108 | c::ERROR_TOO_MANY_LINKS => return TooManyLinks, | |
5099ac24 | 109 | c::ERROR_FILENAME_EXCED_RANGE => return InvalidFilename, |
92a42be0 SL |
110 | _ => {} |
111 | } | |
112 | ||
113 | match errno { | |
136023e0 XL |
114 | c::WSAEACCES => PermissionDenied, |
115 | c::WSAEADDRINUSE => AddrInUse, | |
116 | c::WSAEADDRNOTAVAIL => AddrNotAvailable, | |
117 | c::WSAECONNABORTED => ConnectionAborted, | |
118 | c::WSAECONNREFUSED => ConnectionRefused, | |
119 | c::WSAECONNRESET => ConnectionReset, | |
120 | c::WSAEINVAL => InvalidInput, | |
121 | c::WSAENOTCONN => NotConnected, | |
122 | c::WSAEWOULDBLOCK => WouldBlock, | |
123 | c::WSAETIMEDOUT => TimedOut, | |
124 | c::WSAEHOSTUNREACH => HostUnreachable, | |
125 | c::WSAENETDOWN => NetworkDown, | |
126 | c::WSAENETUNREACH => NetworkUnreachable, | |
85aaf69f | 127 | |
136023e0 | 128 | _ => Uncategorized, |
85aaf69f SL |
129 | } |
130 | } | |
131 | ||
ba9703b0 XL |
132 | pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option<usize> { |
133 | let ptr = haystack.as_ptr(); | |
ba9703b0 XL |
134 | let mut start = &haystack[..]; |
135 | ||
136 | // For performance reasons unfold the loop eight times. | |
3dfed10e XL |
137 | while start.len() >= 8 { |
138 | macro_rules! if_return { | |
139 | ($($n:literal,)+) => { | |
140 | $( | |
141 | if start[$n] == needle { | |
5e7ed085 | 142 | return Some(((&start[$n] as *const u16).addr() - ptr.addr()) / 2); |
3dfed10e XL |
143 | } |
144 | )+ | |
145 | } | |
ba9703b0 XL |
146 | } |
147 | ||
3dfed10e XL |
148 | if_return!(0, 1, 2, 3, 4, 5, 6, 7,); |
149 | ||
ba9703b0 | 150 | start = &start[8..]; |
ba9703b0 XL |
151 | } |
152 | ||
3dfed10e | 153 | for c in start { |
ba9703b0 | 154 | if *c == needle { |
5e7ed085 | 155 | return Some(((c as *const u16).addr() - ptr.addr()) / 2); |
ba9703b0 XL |
156 | } |
157 | } | |
158 | None | |
159 | } | |
160 | ||
532ac7d7 XL |
161 | pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> crate::io::Result<Vec<u16>> { |
162 | fn inner(s: &OsStr) -> crate::io::Result<Vec<u16>> { | |
04454e1e FG |
163 | // Most paths are ASCII, so reserve capacity for as much as there are bytes |
164 | // in the OsStr plus one for the null-terminating character. We are not | |
165 | // wasting bytes here as paths created by this function are primarily used | |
166 | // in an ephemeral fashion. | |
167 | let mut maybe_result = Vec::with_capacity(s.len() + 1); | |
168 | maybe_result.extend(s.encode_wide()); | |
169 | ||
ba9703b0 | 170 | if unrolled_find_u16s(0, &maybe_result).is_some() { |
5099ac24 | 171 | return Err(crate::io::const_io_error!( |
dfeec247 | 172 | ErrorKind::InvalidInput, |
5099ac24 | 173 | "strings passed to WinAPI cannot contain NULs", |
dfeec247 | 174 | )); |
92a42be0 SL |
175 | } |
176 | maybe_result.push(0); | |
177 | Ok(maybe_result) | |
178 | } | |
179 | inner(s.as_ref()) | |
85aaf69f SL |
180 | } |
181 | ||
b039eaaf SL |
182 | // Many Windows APIs follow a pattern of where we hand a buffer and then they |
183 | // will report back to us how large the buffer should be or how many bytes | |
85aaf69f SL |
184 | // currently reside in the buffer. This function is an abstraction over these |
185 | // functions by making them easier to call. | |
186 | // | |
187 | // The first callback, `f1`, is yielded a (pointer, len) pair which can be | |
188 | // passed to a syscall. The `ptr` is valid for `len` items (u16 in this case). | |
189 | // The closure is expected to return what the syscall returns which will be | |
190 | // interpreted by this function to determine if the syscall needs to be invoked | |
191 | // again (with more buffer space). | |
192 | // | |
193 | // Once the syscall has completed (errors bail out early) the second closure is | |
194 | // yielded the data which has been read from the syscall. The return value | |
195 | // from this closure is then the return value of the function. | |
532ac7d7 | 196 | fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> crate::io::Result<T> |
dfeec247 XL |
197 | where |
198 | F1: FnMut(*mut u16, c::DWORD) -> c::DWORD, | |
199 | F2: FnOnce(&[u16]) -> T, | |
85aaf69f SL |
200 | { |
201 | // Start off with a stack buf but then spill over to the heap if we end up | |
202 | // needing more space. | |
04454e1e FG |
203 | // |
204 | // This initial size also works around `GetFullPathNameW` returning | |
205 | // incorrect size hints for some short paths: | |
206 | // https://github.com/dylni/normpath/issues/5 | |
85aaf69f SL |
207 | let mut stack_buf = [0u16; 512]; |
208 | let mut heap_buf = Vec::new(); | |
209 | unsafe { | |
210 | let mut n = stack_buf.len(); | |
211 | loop { | |
212 | let buf = if n <= stack_buf.len() { | |
213 | &mut stack_buf[..] | |
214 | } else { | |
215 | let extra = n - heap_buf.len(); | |
216 | heap_buf.reserve(extra); | |
217 | heap_buf.set_len(n); | |
218 | &mut heap_buf[..] | |
219 | }; | |
220 | ||
221 | // This function is typically called on windows API functions which | |
222 | // will return the correct length of the string, but these functions | |
223 | // also return the `0` on error. In some cases, however, the | |
224 | // returned "correct length" may actually be 0! | |
225 | // | |
226 | // To handle this case we call `SetLastError` to reset it to 0 and | |
227 | // then check it again if we get the "0 error value". If the "last | |
228 | // error" is still 0 then we interpret it as a 0 length buffer and | |
229 | // not an actual error. | |
230 | c::SetLastError(0); | |
92a42be0 SL |
231 | let k = match f1(buf.as_mut_ptr(), n as c::DWORD) { |
232 | 0 if c::GetLastError() == 0 => 0, | |
532ac7d7 | 233 | 0 => return Err(crate::io::Error::last_os_error()), |
85aaf69f SL |
234 | n => n, |
235 | } as usize; | |
92a42be0 | 236 | if k == n && c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER { |
85aaf69f | 237 | n *= 2; |
5e7ed085 | 238 | } else if k > n { |
85aaf69f | 239 | n = k; |
5e7ed085 FG |
240 | } else if k == n { |
241 | // It is impossible to reach this point. | |
242 | // On success, k is the returned string length excluding the null. | |
243 | // On failure, k is the required buffer length including the null. | |
244 | // Therefore k never equals n. | |
245 | unreachable!(); | |
85aaf69f | 246 | } else { |
dfeec247 | 247 | return Ok(f2(&buf[..k])); |
85aaf69f SL |
248 | } |
249 | } | |
250 | } | |
251 | } | |
252 | ||
c34b1796 AL |
253 | fn os2path(s: &[u16]) -> PathBuf { |
254 | PathBuf::from(OsString::from_wide(s)) | |
85aaf69f SL |
255 | } |
256 | ||
416331ca | 257 | pub fn truncate_utf16_at_nul(v: &[u16]) -> &[u16] { |
ba9703b0 | 258 | match unrolled_find_u16s(0, v) { |
85aaf69f SL |
259 | // don't include the 0 |
260 | Some(i) => &v[..i], | |
dfeec247 | 261 | None => v, |
85aaf69f SL |
262 | } |
263 | } | |
264 | ||
c30ab7b3 | 265 | pub trait IsZero { |
3157f602 XL |
266 | fn is_zero(&self) -> bool; |
267 | } | |
268 | ||
269 | macro_rules! impl_is_zero { | |
270 | ($($t:ident)*) => ($(impl IsZero for $t { | |
271 | fn is_zero(&self) -> bool { | |
272 | *self == 0 | |
273 | } | |
274 | })*) | |
275 | } | |
276 | ||
277 | impl_is_zero! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize } | |
278 | ||
532ac7d7 | 279 | pub fn cvt<I: IsZero>(i: I) -> crate::io::Result<I> { |
dfeec247 | 280 | if i.is_zero() { Err(crate::io::Error::last_os_error()) } else { Ok(i) } |
85aaf69f SL |
281 | } |
282 | ||
c30ab7b3 | 283 | pub fn dur2timeout(dur: Duration) -> c::DWORD { |
d9579d0f AL |
284 | // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the |
285 | // timeouts in windows APIs are typically u32 milliseconds. To translate, we | |
286 | // have two pieces to take care of: | |
287 | // | |
288 | // * Nanosecond precision is rounded up | |
289 | // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE | |
290 | // (never time out). | |
dfeec247 XL |
291 | dur.as_secs() |
292 | .checked_mul(1000) | |
293 | .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000)) | |
294 | .and_then(|ms| ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 { 1 } else { 0 })) | |
f035d41b | 295 | .map(|ms| if ms > <c::DWORD>::MAX as u64 { c::INFINITE } else { ms as c::DWORD }) |
dfeec247 | 296 | .unwrap_or(c::INFINITE) |
d9579d0f | 297 | } |
c30ab7b3 | 298 | |
3dfed10e XL |
299 | /// Use `__fastfail` to abort the process |
300 | /// | |
301 | /// This is the same implementation as in libpanic_abort's `__rust_start_panic`. See | |
302 | /// that function for more information on `__fastfail` | |
b7449926 | 303 | #[allow(unreachable_code)] |
f9f354fc | 304 | pub fn abort_internal() -> ! { |
04454e1e | 305 | #[allow(unused)] |
1b1a35ee | 306 | const FAST_FAIL_FATAL_APP_EXIT: usize = 7; |
5099ac24 | 307 | #[cfg(not(miri))] // inline assembly does not work in Miri |
f9f354fc | 308 | unsafe { |
1b1a35ee XL |
309 | cfg_if::cfg_if! { |
310 | if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { | |
a2a8927a | 311 | core::arch::asm!("int $$0x29", in("ecx") FAST_FAIL_FATAL_APP_EXIT); |
1b1a35ee XL |
312 | crate::intrinsics::unreachable(); |
313 | } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] { | |
a2a8927a | 314 | core::arch::asm!(".inst 0xDEFB", in("r0") FAST_FAIL_FATAL_APP_EXIT); |
1b1a35ee XL |
315 | crate::intrinsics::unreachable(); |
316 | } else if #[cfg(target_arch = "aarch64")] { | |
a2a8927a | 317 | core::arch::asm!("brk 0xF003", in("x0") FAST_FAIL_FATAL_APP_EXIT); |
1b1a35ee XL |
318 | crate::intrinsics::unreachable(); |
319 | } | |
320 | } | |
b7449926 | 321 | } |
f035d41b | 322 | crate::intrinsics::abort(); |
c30ab7b3 | 323 | } |