1use crate::crt_fd;
5#[cfg(windows)]
6use crate::fs;
7#[cfg(windows)]
8pub use crate::posix::rename;
9#[cfg(any(unix, target_os = "wasi"))]
10pub use crate::posix_unix_like::rename;
11#[cfg(any(unix, windows))]
12use core::ffi::CStr;
13use core::str::Utf8Error;
14#[cfg(windows)]
15use core::time::Duration;
16use std::{
17 env,
18 ffi::{OsStr, OsString},
19 io,
20 path::PathBuf,
21 process::ExitCode,
22};
23#[cfg(windows)]
24use {
25 std::{os::windows::io::AsRawHandle, path::Path},
26 windows_sys::Win32::{
27 Foundation::FILETIME,
28 Storage::FileSystem::{FILE_FLAG_BACKUP_SEMANTICS, SetFilePointerEx, SetFileTime},
29 System::SystemInformation::{GetSystemInfo, SYSTEM_INFO},
30 },
31};
32
33bitflagset::bitflag! {
34 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
35 #[repr(u8)]
36 pub enum AccessFlag {
37 X = 0,
38 W = 1,
39 R = 2,
40 }
41}
42
43bitflagset::bitflagset! {
44 #[derive(Copy, Clone, PartialEq, Eq)]
45 pub struct AccessMode(u8): AccessFlag
46}
47
48pub const F_OK: u8 = AccessMode::empty().bits();
49pub const X_OK: u8 = AccessMode::from_element(AccessFlag::X).bits();
50pub const W_OK: u8 = AccessMode::from_element(AccessFlag::W).bits();
51pub const R_OK: u8 = AccessMode::from_element(AccessFlag::R).bits();
52
53#[cfg(any(unix, target_os = "wasi"))]
54pub use libc::AT_FDCWD;
55#[cfg(not(any(unix, target_os = "wasi")))]
56pub const AT_FDCWD: i32 = -100;
57
58#[cfg(unix)]
59pub use libc::{AT_REMOVEDIR, AT_SYMLINK_FOLLOW, AT_SYMLINK_NOFOLLOW};
60
61#[cfg(any(
62 target_os = "freebsd",
63 target_os = "linux",
64 target_os = "netbsd",
65 target_vendor = "apple"
66))]
67pub use libc::AT_EACCESS;
68
69#[cfg(target_os = "android")]
71pub const AT_EACCESS: i32 = 0x200;
72
73#[cfg(all(unix, not(target_os = "redox")))]
74pub use libc::{ST_NOSUID, ST_RDONLY};
75
76#[cfg(any(unix, windows, target_os = "wasi"))]
79pub use libc::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY};
80
81#[cfg(unix)]
82pub use libc::{O_ACCMODE, O_CLOEXEC, O_DIRECTORY, O_NOFOLLOW, O_NONBLOCK};
83
84#[cfg(any(
85 target_os = "android",
86 target_os = "dragonfly",
87 target_os = "freebsd",
88 target_os = "linux",
89 target_os = "macos",
90 target_os = "netbsd",
91 target_os = "redox"
92))]
93pub use libc::{O_ASYNC, O_NDELAY, O_NOCTTY};
94
95#[cfg(any(
96 target_os = "android",
97 target_os = "freebsd",
98 target_os = "linux",
99 target_os = "macos",
100 target_os = "netbsd"
101))]
102pub use libc::O_DSYNC;
103
104#[cfg(any(
105 target_os = "android",
106 target_os = "dragonfly",
107 target_os = "freebsd",
108 target_os = "linux",
109 target_os = "macos",
110 target_os = "netbsd"
111))]
112pub use libc::O_SYNC;
113
114#[cfg(any(
115 target_os = "dragonfly",
116 target_os = "freebsd",
117 target_os = "macos",
118 target_os = "netbsd",
119 target_os = "redox"
120))]
121pub use libc::O_FSYNC;
122
123#[cfg(any(target_os = "linux", target_os = "android"))]
124pub use libc::{O_DIRECT, O_LARGEFILE, O_NOATIME, O_PATH, O_RSYNC, O_TMPFILE};
125
126#[cfg(target_os = "freebsd")]
127pub use libc::{O_DIRECT, O_PATH};
128
129#[cfg(target_os = "redox")]
130pub use libc::O_PATH;
131
132#[cfg(target_os = "netbsd")]
133pub use libc::{O_DIRECT, O_RSYNC};
134
135#[cfg(target_os = "dragonfly")]
136pub use libc::O_DIRECT;
137
138#[cfg(any(target_os = "macos", target_os = "ios"))]
139pub use libc::{O_EVTONLY, O_EXEC, O_EXLOCK, O_NOFOLLOW_ANY, O_SEARCH, O_SHLOCK, O_SYMLINK};
140
141#[cfg(any(
142 target_os = "dragonfly",
143 target_os = "freebsd",
144 target_os = "netbsd",
145 target_os = "redox"
146))]
147pub use libc::{O_EXLOCK, O_SHLOCK};
148
149#[cfg(target_os = "redox")]
150pub use libc::O_SYMLINK;
151
152#[cfg(windows)]
153pub use libc::{O_BINARY, O_NOINHERIT, O_RANDOM, O_SEQUENTIAL, O_TEMPORARY, O_TEXT};
154
155#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
156mod wasm_oflag {
157 pub const O_RDONLY: i32 = 0x0000;
158 pub const O_WRONLY: i32 = 0x0001;
159 pub const O_RDWR: i32 = 0x0002;
160 pub const O_ACCMODE: i32 = 0x0003;
161 pub const O_NONBLOCK: i32 = 0x0004;
162 pub const O_APPEND: i32 = 0x0008;
163 pub const O_SYNC: i32 = 0x0080;
164 pub const O_NOFOLLOW: i32 = 0x0100;
165 pub const O_CREAT: i32 = 0x0200;
166 pub const O_TRUNC: i32 = 0x0400;
167 pub const O_EXCL: i32 = 0x0800;
168 pub const O_DIRECTORY: i32 = 0x0010_0000;
169 pub const O_CLOEXEC: i32 = 0x0100_0000;
170 pub const O_NDELAY: i32 = O_NONBLOCK;
171}
172
173#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
174pub use wasm_oflag::*;
175
176bitflagset::bitflag! {
177 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
179 #[repr(u8)]
180 pub enum ChFlag {
181 UfNodump = 0,
182 UfImmutable = 1,
183 UfAppend = 2,
184 UfOpaque = 3,
185 UfNounlink = 4,
186 UfCompressed = 5,
187 UfTracked = 6,
188 UfDatavault = 7,
189 UfHidden = 15,
190 SfArchived = 16,
191 SfImmutable = 17,
192 SfAppend = 18,
193 SfNounlink = 20,
194 SfSnapshot = 21,
195 SfFirmlink = 23,
196 SfDataless = 30,
197 }
198}
199
200bitflagset::bitflagset! {
201 #[derive(Copy, Clone, PartialEq, Eq)]
202 pub struct ChFlags(u32): ChFlag
203}
204
205impl ChFlags {
206 pub const UF_SETTABLE: Self = Self::from_bits_retain(0x0000ffff);
208 pub const SF_SETTABLE: Self = Self::from_bits_retain(if cfg!(target_os = "macos") {
209 0x3fff0000
210 } else {
211 0xffff0000
212 });
213 #[cfg(target_os = "macos")]
214 pub const SF_SUPPORTED: Self = Self::from_bits_retain(0x009f0000);
215 #[cfg(target_os = "macos")]
216 pub const SF_SYNTHETIC: Self = Self::from_bits_retain(0xc0000000);
217}
218
219pub const UF_NODUMP: u32 = ChFlags::from_element(ChFlag::UfNodump).bits();
220pub const UF_IMMUTABLE: u32 = ChFlags::from_element(ChFlag::UfImmutable).bits();
221pub const UF_APPEND: u32 = ChFlags::from_element(ChFlag::UfAppend).bits();
222pub const UF_OPAQUE: u32 = ChFlags::from_element(ChFlag::UfOpaque).bits();
223pub const UF_NOUNLINK: u32 = ChFlags::from_element(ChFlag::UfNounlink).bits();
224pub const UF_COMPRESSED: u32 = ChFlags::from_element(ChFlag::UfCompressed).bits();
225pub const UF_TRACKED: u32 = ChFlags::from_element(ChFlag::UfTracked).bits();
226pub const UF_DATAVAULT: u32 = ChFlags::from_element(ChFlag::UfDatavault).bits();
227pub const UF_HIDDEN: u32 = ChFlags::from_element(ChFlag::UfHidden).bits();
228pub const UF_SETTABLE: u32 = ChFlags::UF_SETTABLE.bits();
229pub const SF_ARCHIVED: u32 = ChFlags::from_element(ChFlag::SfArchived).bits();
230pub const SF_IMMUTABLE: u32 = ChFlags::from_element(ChFlag::SfImmutable).bits();
231pub const SF_APPEND: u32 = ChFlags::from_element(ChFlag::SfAppend).bits();
232pub const SF_NOUNLINK: u32 = ChFlags::from_element(ChFlag::SfNounlink).bits();
233pub const SF_SNAPSHOT: u32 = ChFlags::from_element(ChFlag::SfSnapshot).bits();
234pub const SF_FIRMLINK: u32 = ChFlags::from_element(ChFlag::SfFirmlink).bits();
235pub const SF_DATALESS: u32 = ChFlags::from_element(ChFlag::SfDataless).bits();
236pub const SF_SETTABLE: u32 = ChFlags::SF_SETTABLE.bits();
237#[cfg(target_os = "macos")]
238pub const SF_SUPPORTED: u32 = ChFlags::SF_SUPPORTED.bits();
239#[cfg(target_os = "macos")]
240pub const SF_SYNTHETIC: u32 = ChFlags::SF_SYNTHETIC.bits();
241
242const _: () = {
243 assert!(UF_NODUMP == 0x00000001);
244 assert!(UF_IMMUTABLE == 0x00000002);
245 assert!(UF_APPEND == 0x00000004);
246 assert!(UF_OPAQUE == 0x00000008);
247 assert!(UF_NOUNLINK == 0x00000010);
248 assert!(UF_COMPRESSED == 0x00000020);
249 assert!(UF_TRACKED == 0x00000040);
250 assert!(UF_DATAVAULT == 0x00000080);
251 assert!(UF_HIDDEN == 0x00008000);
252 assert!(UF_SETTABLE == 0x0000ffff);
253 assert!(SF_ARCHIVED == 0x00010000);
254 assert!(SF_IMMUTABLE == 0x00020000);
255 assert!(SF_APPEND == 0x00040000);
256 assert!(SF_NOUNLINK == 0x00100000);
257 assert!(SF_SNAPSHOT == 0x00200000);
258 assert!(SF_FIRMLINK == 0x00800000);
259 assert!(SF_DATALESS == 0x40000000);
260};
261
262pub const S_IFDOOR: u32 = 0;
265pub const S_IFPORT: u32 = 0;
266pub const S_IFWHT: u32 = if cfg!(target_os = "macos") {
267 0o160000
268} else {
269 0
270};
271
272#[cfg(not(any(unix, windows, target_os = "wasi")))]
273pub fn rename(
274 from: impl AsRef<std::path::Path>,
275 from_fd: Option<crt_fd::Borrowed<'_>>,
276 to: impl AsRef<std::path::Path>,
277 to_fd: Option<crt_fd::Borrowed<'_>>,
278) -> io::Result<()> {
279 if from_fd.is_none() && to_fd.is_none() {
280 std::fs::rename(from, to)
281 } else {
282 Err(io::Error::new(
283 io::ErrorKind::Unsupported,
284 "renameat is not available on this platform",
285 ))
286 }
287}
288
289pub fn exit_code(code: u32) -> ExitCode {
294 #[cfg(windows)]
295 {
296 if code > u8::MAX as u32 {
301 std::process::exit(code as i32)
302 }
303 }
304 ExitCode::from(code as u8)
305}
306
307pub fn current_dir() -> io::Result<PathBuf> {
308 env::current_dir()
309}
310
311#[must_use]
312pub fn temp_dir() -> PathBuf {
313 env::temp_dir()
314}
315
316pub fn var(key: &str) -> Result<String, env::VarError> {
317 env::var(key)
318}
319
320pub fn var_os(key: impl AsRef<OsStr>) -> Option<OsString> {
321 env::var_os(key)
322}
323
324#[must_use]
325pub fn vars_os() -> env::VarsOs {
326 env::vars_os()
327}
328
329#[must_use]
330pub fn vars() -> env::Vars {
331 env::vars()
332}
333
334pub unsafe fn set_var(key: impl AsRef<OsStr>, value: impl AsRef<OsStr>) {
338 unsafe { env::set_var(key, value) };
339}
340
341pub unsafe fn remove_var(key: impl AsRef<OsStr>) {
345 unsafe { env::remove_var(key) };
346}
347
348#[cfg(windows)]
355pub fn publish_drive_current_directory() -> io::Result<()> {
356 use std::os::windows::ffi::OsStrExt;
357 use windows_sys::Win32::System::Environment::SetEnvironmentVariableW;
358
359 let mut cwd_wide: Vec<u16> = env::current_dir()?.as_os_str().encode_wide().collect();
360 let unc_like = cwd_wide.len() >= 2
361 && ((cwd_wide[0] == b'\\' as u16 && cwd_wide[1] == b'\\' as u16)
362 || (cwd_wide[0] == b'/' as u16 && cwd_wide[1] == b'/' as u16));
363 if unc_like || cwd_wide.is_empty() {
364 return Ok(());
365 }
366 let env_name = [b'=' as u16, cwd_wide[0], b':' as u16, 0];
367 cwd_wide.push(0);
368 let ok = unsafe { SetEnvironmentVariableW(env_name.as_ptr(), cwd_wide.as_ptr()) };
369 if ok == 0 {
370 Err(io::Error::last_os_error())
371 } else {
372 Ok(())
373 }
374}
375
376#[cfg(windows)]
378pub fn environment_has_drive_current_directory() -> bool {
379 use windows_sys::Win32::System::Environment::{
380 FreeEnvironmentStringsW, GetEnvironmentStringsW,
381 };
382
383 let block = unsafe { GetEnvironmentStringsW() };
384 if block.is_null() {
385 return false;
386 }
387 let mut present = false;
388 let mut entry = block;
389 unsafe {
390 while *entry != 0 {
391 if *entry == b'=' as u16 {
392 present = true;
393 break;
394 }
395 while *entry != 0 {
396 entry = entry.add(1);
397 }
398 entry = entry.add(1);
399 }
400 FreeEnvironmentStringsW(block);
401 }
402 present
403}
404
405#[cfg(windows)]
408pub fn ensure_drive_current_directory() {
409 if !environment_has_drive_current_directory() {
410 let _ = publish_drive_current_directory();
411 }
412}
413
414pub fn set_current_dir(path: impl AsRef<std::path::Path>) -> io::Result<()> {
415 env::set_current_dir(&path)?;
416 #[cfg(windows)]
417 publish_drive_current_directory()?;
418 Ok(())
419}
420
421#[must_use]
422pub fn process_id() -> u32 {
423 std::process::id()
424}
425
426#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
427pub fn cpu_count() -> usize {
428 num_cpus::get()
429}
430
431#[cfg(not(any(not(target_arch = "wasm32"), target_os = "wasi")))]
432pub fn cpu_count() -> usize {
433 1
434}
435
436#[cfg(unix)]
437pub fn page_size() -> usize {
438 rustix::param::page_size()
439}
440
441#[cfg(target_arch = "wasm32")]
442pub const fn page_size() -> usize {
443 1024 * 64
445}
446
447#[cfg(windows)]
448pub fn page_size() -> usize {
449 let mut info = SYSTEM_INFO::default();
450 unsafe {
451 GetSystemInfo(&mut info);
452 }
453 info.dwPageSize as _
454}
455
456#[cfg(unix)]
457pub fn alloc_granularity() -> usize {
458 page_size()
461}
462
463#[cfg(target_arch = "wasm32")]
464pub const fn alloc_granularity() -> usize {
465 page_size()
467}
468
469#[cfg(windows)]
470pub fn alloc_granularity() -> usize {
471 let mut info = SYSTEM_INFO::default();
472 unsafe {
473 GetSystemInfo(&mut info);
474 }
475 info.dwAllocationGranularity as _
476}
477
478pub fn device_encoding(_fd: i32) -> Option<String> {
479 #[cfg(any(
480 target_os = "android",
481 target_os = "redox",
482 all(target_arch = "wasm32", not(target_os = "wasi"))
483 ))]
484 {
485 Some("UTF-8".to_owned())
486 }
487
488 #[cfg(windows)]
489 {
490 use windows_sys::Win32::System::Console;
491 let cp = match _fd {
492 0 => unsafe { Console::GetConsoleCP() },
493 1 | 2 => unsafe { Console::GetConsoleOutputCP() },
494 _ => 0,
495 };
496
497 Some(format!("cp{cp}"))
498 }
499
500 #[cfg(not(any(
501 target_os = "android",
502 target_os = "redox",
503 windows,
504 all(target_arch = "wasm32", not(target_os = "wasi"))
505 )))]
506 {
507 let encoding = unsafe {
508 let encoding = libc::nl_langinfo(libc::CODESET);
509 if encoding.is_null() || encoding.read() == b'\0' as libc::c_char {
510 "UTF-8".to_owned()
511 } else {
512 core::ffi::CStr::from_ptr(encoding)
513 .to_string_lossy()
514 .into_owned()
515 }
516 };
517
518 Some(encoding)
519 }
520}
521
522pub fn exit(code: i32) -> ! {
523 std::process::exit(code)
524}
525
526pub fn abort() -> ! {
528 unsafe extern "C" {
529 fn abort() -> !;
530 }
531 unsafe { abort() }
532}
533
534pub fn urandom(size: usize) -> io::Result<Vec<u8>> {
536 let mut buf = vec![0u8; size];
537 getrandom::fill(&mut buf).map_err(io::Error::from)?;
538 Ok(buf)
539}
540
541#[cfg(any(unix, windows, target_os = "wasi"))]
542pub fn isatty(fd: i32) -> bool {
543 unsafe { suppress_iph!(libc::isatty(fd)) != 0 }
544}
545
546#[cfg(not(any(unix, windows, target_os = "wasi")))]
547pub fn isatty(_fd: i32) -> bool {
548 false
549}
550
551#[cfg(any(unix, windows))]
552pub fn system(command: &CStr) -> libc::c_int {
553 unsafe { libc::system(command.as_ptr()) }
554}
555
556#[cfg(target_os = "linux")]
557pub fn copy_file_range(
558 src: crt_fd::Borrowed<'_>,
559 offset_src: Option<&mut u64>,
560 dst: crt_fd::Borrowed<'_>,
561 offset_dst: Option<&mut u64>,
562 count: usize,
563) -> rustix::io::Result<usize> {
564 rustix::fs::copy_file_range(src, offset_src, dst, offset_dst, count)
567}
568
569#[cfg(windows)]
570pub fn seek_fd(
571 fd: crt_fd::Borrowed<'_>,
572 position: crt_fd::Offset,
573 how: i32,
574) -> io::Result<crt_fd::Offset> {
575 use crate::windows::CheckWin32Bool;
576
577 let handle = crt_fd::as_handle(fd)?;
578 let mut new_position = 0;
583 unsafe {
584 SetFilePointerEx(
585 handle.as_raw_handle(),
586 position,
587 &mut new_position,
588 how as _,
589 )
590 }
591 .check_win32_bool()?;
592 Ok(new_position)
593}
594
595#[cfg(any(unix, target_os = "wasi"))]
596pub fn seek_fd(
597 fd: crt_fd::Borrowed<'_>,
598 position: crt_fd::Offset,
599 how: i32,
600) -> io::Result<crt_fd::Offset> {
601 unsafe { suppress_iph!(libc::lseek(fd.as_raw(), position, how)) }.check_libc_neg()
602}
603
604#[cfg(windows)]
605fn filetime_from_duration(duration: Duration) -> FILETIME {
606 let intervals = ((duration.as_secs() as i64 + 11644473600) * 10_000_000)
607 + (duration.subsec_nanos() as i64 / 100);
608 FILETIME {
609 dwLowDateTime: intervals as u32,
610 dwHighDateTime: (intervals >> 32) as u32,
611 }
612}
613
614#[cfg(windows)]
615pub fn set_file_times(
616 path: impl AsRef<Path>,
617 access: Duration,
618 modified: Duration,
619) -> io::Result<()> {
620 use crate::windows::CheckWin32Bool;
621 let access = filetime_from_duration(access);
622 let modified = filetime_from_duration(modified);
623 let file = fs::open_write_with_custom_flags(path, FILE_FLAG_BACKUP_SEMANTICS)?;
624 unsafe {
625 SetFileTime(
626 file.as_raw_handle() as _,
627 core::ptr::null(),
628 &access,
629 &modified,
630 )
631 }
632 .check_win32_bool()
633}
634
635pub trait ErrorExt {
636 fn posix_errno(&self) -> i32;
637}
638
639impl ErrorExt for io::Error {
640 #[cfg(not(windows))]
641 fn posix_errno(&self) -> i32 {
642 self.raw_os_error().unwrap_or(0)
643 }
644 #[cfg(windows)]
645 fn posix_errno(&self) -> i32 {
646 if let Some(crt) = self.get_ref().and_then(|e| e.downcast_ref::<CrtErrno>()) {
649 return crt.0;
650 }
651 let winerror = self.raw_os_error().unwrap_or(0);
652 winerror_to_errno(winerror)
653 }
654}
655
656#[cfg(windows)]
665#[derive(Debug)]
666struct CrtErrno(i32);
667
668#[cfg(windows)]
669impl core::fmt::Display for CrtErrno {
670 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
671 match crate::errno::strerror_string(self.0) {
672 Some(msg) => f.write_str(&msg),
673 None => write!(f, "os error {}", self.0),
674 }
675 }
676}
677
678#[cfg(windows)]
679impl core::error::Error for CrtErrno {}
680
681#[cfg(windows)]
687#[must_use]
688pub fn io_error_from_errno(errno: i32) -> io::Error {
689 let kind = io::Error::from_raw_os_error(errno_to_winerror(errno)).kind();
690 io::Error::new(kind, CrtErrno(errno))
691}
692
693#[cfg(all(not(windows), not(target_arch = "wasm32")))]
694impl ErrorExt for rustix::io::Errno {
695 fn posix_errno(&self) -> i32 {
696 self.raw_os_error()
697 }
698}
699
700#[cfg(windows)]
703#[must_use]
704pub fn errno_io_error() -> io::Error {
705 io_error_from_errno(get_errno())
706}
707
708#[cfg(not(windows))]
709#[must_use]
710pub fn errno_io_error() -> io::Error {
711 std::io::Error::last_os_error()
712}
713
714pub trait CheckLibcResult: Sized {
719 fn check_libc_neg(self) -> io::Result<Self>;
721}
722
723macro_rules! impl_check_libc_result {
724 ($($ty:ty),* $(,)?) => {
725 $(
726 impl CheckLibcResult for $ty {
727 #[inline]
728 fn check_libc_neg(self) -> io::Result<Self> {
729 if self < 0 { Err(errno_io_error()) } else { Ok(self) }
730 }
731 }
732 )*
733 };
734}
735
736impl_check_libc_result!(i16, i32, i64, isize);
737
738pub trait CheckLibcZero {
741 fn check_libc_zero(self) -> io::Result<()>;
743}
744
745macro_rules! impl_check_libc_zero {
746 ($($ty:ty),* $(,)?) => {
747 $(
748 impl CheckLibcZero for $ty {
749 #[inline]
750 fn check_libc_zero(self) -> io::Result<()> {
751 if self == 0 { Ok(()) } else { Err(errno_io_error()) }
752 }
753 }
754 )*
755 };
756}
757
758impl_check_libc_zero!(i32, i64, isize);
759
760#[cfg(windows)]
761pub fn get_errno() -> i32 {
762 unsafe extern "C" {
763 fn _get_errno(pValue: *mut i32) -> i32;
764 }
765 let mut errno = 0;
766 unsafe { suppress_iph!(_get_errno(&mut errno)) };
767 errno
768}
769
770#[cfg(not(windows))]
771#[must_use]
772pub fn get_errno() -> i32 {
773 std::io::Error::last_os_error().posix_errno()
774}
775
776pub fn clear_errno() {
777 set_errno(0);
778}
779
780#[cfg(windows)]
782pub fn set_errno(value: i32) {
783 unsafe extern "C" {
784 fn _set_errno(value: i32) -> i32;
785 }
786 unsafe { suppress_iph!(_set_errno(value)) };
787}
788
789#[cfg(unix)]
790pub fn set_errno(value: i32) {
791 nix::errno::Errno::from_raw(value).set();
792}
793
794#[cfg(target_os = "wasi")]
795pub fn set_errno(value: i32) {
796 unsafe {
797 *libc::__errno_location() = value;
798 }
799}
800
801#[cfg(not(any(unix, windows, target_os = "wasi")))]
802pub fn set_errno(_value: i32) {}
803
804#[cfg(any(unix, all(target_os = "wasi", not(target_env = "p2"))))]
806pub fn bytes_as_os_str(b: &[u8]) -> Result<&std::ffi::OsStr, Utf8Error> {
807 use self::ffi::OsStrExt;
808 Ok(std::ffi::OsStr::from_bytes(b))
809}
810
811#[cfg(not(any(unix, all(target_os = "wasi", not(target_env = "p2")))))]
812pub fn bytes_as_os_str(b: &[u8]) -> Result<&std::ffi::OsStr, Utf8Error> {
813 Ok(core::str::from_utf8(b)?.as_ref())
814}
815
816#[cfg(unix)]
817pub use std::os::unix::ffi;
818
819#[cfg(all(target_os = "wasi", not(target_env = "p2")))]
821pub use std::os::wasi::ffi;
822
823#[cfg(all(target_os = "wasi", target_env = "p2"))]
826pub mod ffi {
827 use std::ffi::{OsStr, OsString};
828
829 pub trait OsStrExt: sealed::Sealed {
830 fn as_bytes(&self) -> &[u8];
831 fn from_bytes(slice: &[u8]) -> &Self;
832 }
833
834 impl OsStrExt for OsStr {
835 fn as_bytes(&self) -> &[u8] {
836 self.to_str().expect("wasip2 strings are UTF-8").as_bytes()
838 }
839
840 fn from_bytes(slice: &[u8]) -> &OsStr {
841 OsStr::new(core::str::from_utf8(slice).expect("wasip2 strings are UTF-8"))
843 }
844 }
845
846 pub trait OsStringExt: sealed::Sealed {
847 fn from_vec(vec: Vec<u8>) -> Self;
848 fn into_vec(self) -> Vec<u8>;
849 }
850
851 impl OsStringExt for OsString {
852 fn from_vec(vec: Vec<u8>) -> OsString {
853 OsString::from(String::from_utf8(vec).expect("wasip2 strings are UTF-8"))
855 }
856
857 fn into_vec(self) -> Vec<u8> {
858 self.to_str()
860 .expect("wasip2 strings are UTF-8")
861 .as_bytes()
862 .to_vec()
863 }
864 }
865
866 mod sealed {
867 pub trait Sealed {}
868 impl Sealed for std::ffi::OsStr {}
869 impl Sealed for std::ffi::OsString {}
870 }
871}
872
873#[cfg(windows)]
874#[must_use]
875pub fn errno_to_winerror(errno: i32) -> i32 {
876 use libc::*;
877 use windows_sys::Win32::Foundation::*;
878 let winerror = match errno {
879 ENOENT => ERROR_FILE_NOT_FOUND,
880 E2BIG => ERROR_BAD_ENVIRONMENT,
881 ENOEXEC => ERROR_BAD_FORMAT,
882 EBADF => ERROR_INVALID_HANDLE,
883 ECHILD => ERROR_WAIT_NO_CHILDREN,
884 EAGAIN => ERROR_NO_PROC_SLOTS,
885 ENOMEM => ERROR_NOT_ENOUGH_MEMORY,
886 EACCES => ERROR_ACCESS_DENIED,
887 EEXIST => ERROR_FILE_EXISTS,
888 EXDEV => ERROR_NOT_SAME_DEVICE,
889 ENOTDIR => ERROR_DIRECTORY,
890 EMFILE => ERROR_TOO_MANY_OPEN_FILES,
891 ENOSPC => ERROR_DISK_FULL,
892 EPIPE => ERROR_BROKEN_PIPE,
893 ENOTEMPTY => ERROR_DIR_NOT_EMPTY,
894 EILSEQ => ERROR_NO_UNICODE_TRANSLATION,
895 EINVAL => ERROR_INVALID_FUNCTION,
896 _ => ERROR_INVALID_FUNCTION,
897 };
898 winerror as _
899}
900
901#[cfg(windows)]
904#[must_use]
905pub fn winerror_to_errno(winerror: i32) -> i32 {
906 use libc::*;
907 use windows_sys::Win32::{
908 Foundation::*,
909 Networking::WinSock::{
910 WSAEACCES, WSAEBADF, WSAECONNABORTED, WSAECONNREFUSED, WSAECONNRESET, WSAEFAULT,
911 WSAEINTR, WSAEINVAL, WSAEMFILE,
912 },
913 };
914 if (10000..12000).contains(&winerror) {
921 match winerror {
922 WSAEINTR | WSAEBADF | WSAEACCES | WSAEFAULT | WSAEINVAL | WSAEMFILE => {
923 return winerror - 10000;
925 }
926 _ => return winerror as _,
927 }
928 }
929
930 #[allow(non_upper_case_globals)]
931 match winerror as u32 {
932 ERROR_FILE_NOT_FOUND
933 | ERROR_PATH_NOT_FOUND
934 | ERROR_INVALID_DRIVE
935 | ERROR_NO_MORE_FILES
936 | ERROR_BAD_NETPATH
937 | ERROR_BAD_NET_NAME
938 | ERROR_BAD_PATHNAME
939 | ERROR_FILENAME_EXCED_RANGE => ENOENT,
940 ERROR_BAD_ENVIRONMENT => E2BIG,
941 ERROR_BAD_FORMAT
942 | ERROR_INVALID_STARTING_CODESEG
943 | ERROR_INVALID_STACKSEG
944 | ERROR_INVALID_MODULETYPE
945 | ERROR_INVALID_EXE_SIGNATURE
946 | ERROR_EXE_MARKED_INVALID
947 | ERROR_BAD_EXE_FORMAT
948 | ERROR_ITERATED_DATA_EXCEEDS_64k
949 | ERROR_INVALID_MINALLOCSIZE
950 | ERROR_DYNLINK_FROM_INVALID_RING
951 | ERROR_IOPL_NOT_ENABLED
952 | ERROR_INVALID_SEGDPL
953 | ERROR_AUTODATASEG_EXCEEDS_64k
954 | ERROR_RING2SEG_MUST_BE_MOVABLE
955 | ERROR_RELOC_CHAIN_XEEDS_SEGLIM
956 | ERROR_INFLOOP_IN_RELOC_CHAIN => ENOEXEC,
957 ERROR_INVALID_HANDLE | ERROR_INVALID_TARGET_HANDLE | ERROR_DIRECT_ACCESS_HANDLE => EBADF,
958 ERROR_WAIT_NO_CHILDREN | ERROR_CHILD_NOT_COMPLETE => ECHILD,
959 ERROR_NO_PROC_SLOTS | ERROR_MAX_THRDS_REACHED | ERROR_NESTING_NOT_ALLOWED => EAGAIN,
960 ERROR_ARENA_TRASHED
961 | ERROR_NOT_ENOUGH_MEMORY
962 | ERROR_INVALID_BLOCK
963 | ERROR_NOT_ENOUGH_QUOTA => ENOMEM,
964 ERROR_ACCESS_DENIED
965 | ERROR_CURRENT_DIRECTORY
966 | ERROR_WRITE_PROTECT
967 | ERROR_BAD_UNIT
968 | ERROR_NOT_READY
969 | ERROR_BAD_COMMAND
970 | ERROR_CRC
971 | ERROR_BAD_LENGTH
972 | ERROR_SEEK
973 | ERROR_NOT_DOS_DISK
974 | ERROR_SECTOR_NOT_FOUND
975 | ERROR_OUT_OF_PAPER
976 | ERROR_WRITE_FAULT
977 | ERROR_READ_FAULT
978 | ERROR_GEN_FAILURE
979 | ERROR_SHARING_VIOLATION
980 | ERROR_LOCK_VIOLATION
981 | ERROR_WRONG_DISK
982 | ERROR_SHARING_BUFFER_EXCEEDED
983 | ERROR_NETWORK_ACCESS_DENIED
984 | ERROR_CANNOT_MAKE
985 | ERROR_FAIL_I24
986 | ERROR_DRIVE_LOCKED
987 | ERROR_SEEK_ON_DEVICE
988 | ERROR_NOT_LOCKED
989 | ERROR_LOCK_FAILED
990 | 35 => EACCES,
991 ERROR_FILE_EXISTS | ERROR_ALREADY_EXISTS => EEXIST,
992 ERROR_NOT_SAME_DEVICE => EXDEV,
993 ERROR_DIRECTORY => ENOTDIR,
994 ERROR_TOO_MANY_OPEN_FILES => EMFILE,
995 ERROR_DISK_FULL => ENOSPC,
996 ERROR_BROKEN_PIPE | ERROR_NO_DATA => EPIPE,
997 ERROR_DIR_NOT_EMPTY => ENOTEMPTY,
998 ERROR_NO_UNICODE_TRANSLATION => EILSEQ,
999 ERROR_CONNECTION_REFUSED => WSAECONNREFUSED,
1002 ERROR_CONNECTION_ABORTED => WSAECONNABORTED,
1003 ERROR_NETNAME_DELETED => WSAECONNRESET,
1004 ERROR_INVALID_FUNCTION
1005 | ERROR_INVALID_ACCESS
1006 | ERROR_INVALID_DATA
1007 | ERROR_INVALID_PARAMETER
1008 | ERROR_NEGATIVE_SEEK => EINVAL,
1009 _ => EINVAL,
1010 }
1011}
1012
1013#[cfg(test)]
1014mod tests {
1015 #[test]
1016 fn o_rdonly_is_zero() {
1017 assert_eq!(super::O_RDONLY, 0);
1018 }
1019
1020 #[test]
1021 fn o_wronly_is_one() {
1022 assert_eq!(super::O_WRONLY, 1);
1023 }
1024}