rustpython_common/
os.rs

1// TODO: we can move more os-specific bindings/interfaces from stdlib::{os, posix, nt} to here
2
3use std::{io, str::Utf8Error};
4
5pub trait ErrorExt {
6    fn posix_errno(&self) -> i32;
7}
8
9impl ErrorExt for io::Error {
10    #[cfg(not(windows))]
11    fn posix_errno(&self) -> i32 {
12        self.raw_os_error().unwrap_or(0)
13    }
14    #[cfg(windows)]
15    fn posix_errno(&self) -> i32 {
16        let winerror = self.raw_os_error().unwrap_or(0);
17        winerror_to_errno(winerror)
18    }
19}
20
21#[cfg(windows)]
22pub fn last_os_error() -> io::Error {
23    let err = io::Error::last_os_error();
24    // FIXME: probably not ideal, we need a bigger dichotomy between GetLastError and errno
25    if err.raw_os_error() == Some(0) {
26        extern "C" {
27            fn _get_errno(pValue: *mut i32) -> i32;
28        }
29        let mut errno = 0;
30        unsafe { suppress_iph!(_get_errno(&mut errno)) };
31        let errno = errno_to_winerror(errno);
32        io::Error::from_raw_os_error(errno)
33    } else {
34        err
35    }
36}
37
38#[cfg(not(windows))]
39pub fn last_os_error() -> io::Error {
40    io::Error::last_os_error()
41}
42
43#[cfg(windows)]
44pub fn last_posix_errno() -> i32 {
45    let err = io::Error::last_os_error();
46    if err.raw_os_error() == Some(0) {
47        extern "C" {
48            fn _get_errno(pValue: *mut i32) -> i32;
49        }
50        let mut errno = 0;
51        unsafe { suppress_iph!(_get_errno(&mut errno)) };
52        errno
53    } else {
54        err.posix_errno()
55    }
56}
57
58#[cfg(not(windows))]
59pub fn last_posix_errno() -> i32 {
60    last_os_error().posix_errno()
61}
62
63#[cfg(unix)]
64pub fn bytes_as_osstr(b: &[u8]) -> Result<&std::ffi::OsStr, Utf8Error> {
65    use std::os::unix::ffi::OsStrExt;
66    Ok(std::ffi::OsStr::from_bytes(b))
67}
68
69#[cfg(not(unix))]
70pub fn bytes_as_osstr(b: &[u8]) -> Result<&std::ffi::OsStr, Utf8Error> {
71    Ok(std::str::from_utf8(b)?.as_ref())
72}
73
74#[cfg(unix)]
75pub use std::os::unix::ffi;
76#[cfg(target_os = "wasi")]
77pub use std::os::wasi::ffi;
78
79#[cfg(windows)]
80pub fn errno_to_winerror(errno: i32) -> i32 {
81    use libc::*;
82    use windows_sys::Win32::Foundation::*;
83    let winerror = match errno {
84        ENOENT => ERROR_FILE_NOT_FOUND,
85        E2BIG => ERROR_BAD_ENVIRONMENT,
86        ENOEXEC => ERROR_BAD_FORMAT,
87        EBADF => ERROR_INVALID_HANDLE,
88        ECHILD => ERROR_WAIT_NO_CHILDREN,
89        EAGAIN => ERROR_NO_PROC_SLOTS,
90        ENOMEM => ERROR_NOT_ENOUGH_MEMORY,
91        EACCES => ERROR_ACCESS_DENIED,
92        EEXIST => ERROR_FILE_EXISTS,
93        EXDEV => ERROR_NOT_SAME_DEVICE,
94        ENOTDIR => ERROR_DIRECTORY,
95        EMFILE => ERROR_TOO_MANY_OPEN_FILES,
96        ENOSPC => ERROR_DISK_FULL,
97        EPIPE => ERROR_BROKEN_PIPE,
98        ENOTEMPTY => ERROR_DIR_NOT_EMPTY,
99        EILSEQ => ERROR_NO_UNICODE_TRANSLATION,
100        EINVAL => ERROR_INVALID_FUNCTION,
101        _ => ERROR_INVALID_FUNCTION,
102    };
103    winerror as _
104}
105
106// winerror: https://learn.microsoft.com/windows/win32/debug/system-error-codes--0-499-
107// errno: https://learn.microsoft.com/cpp/c-runtime-library/errno-constants?view=msvc-170
108#[cfg(windows)]
109pub fn winerror_to_errno(winerror: i32) -> i32 {
110    use libc::*;
111    use windows_sys::Win32::{
112        Foundation::*,
113        Networking::WinSock::{WSAEACCES, WSAEBADF, WSAEFAULT, WSAEINTR, WSAEINVAL, WSAEMFILE},
114    };
115    // Unwrap FACILITY_WIN32 HRESULT errors.
116    // if ((winerror & 0xFFFF0000) == 0x80070000) {
117    //     winerror &= 0x0000FFFF;
118    // }
119
120    // Winsock error codes (10000-11999) are errno values.
121    if (10000..12000).contains(&winerror) {
122        match winerror {
123            WSAEINTR | WSAEBADF | WSAEACCES | WSAEFAULT | WSAEINVAL | WSAEMFILE => {
124                // Winsock definitions of errno values. See WinSock2.h
125                return winerror - 10000;
126            }
127            _ => return winerror as _,
128        }
129    }
130
131    #[allow(non_upper_case_globals)]
132    match winerror as u32 {
133        ERROR_FILE_NOT_FOUND
134        | ERROR_PATH_NOT_FOUND
135        | ERROR_INVALID_DRIVE
136        | ERROR_NO_MORE_FILES
137        | ERROR_BAD_NETPATH
138        | ERROR_BAD_NET_NAME
139        | ERROR_BAD_PATHNAME
140        | ERROR_FILENAME_EXCED_RANGE => ENOENT,
141        ERROR_BAD_ENVIRONMENT => E2BIG,
142        ERROR_BAD_FORMAT
143        | ERROR_INVALID_STARTING_CODESEG
144        | ERROR_INVALID_STACKSEG
145        | ERROR_INVALID_MODULETYPE
146        | ERROR_INVALID_EXE_SIGNATURE
147        | ERROR_EXE_MARKED_INVALID
148        | ERROR_BAD_EXE_FORMAT
149        | ERROR_ITERATED_DATA_EXCEEDS_64k
150        | ERROR_INVALID_MINALLOCSIZE
151        | ERROR_DYNLINK_FROM_INVALID_RING
152        | ERROR_IOPL_NOT_ENABLED
153        | ERROR_INVALID_SEGDPL
154        | ERROR_AUTODATASEG_EXCEEDS_64k
155        | ERROR_RING2SEG_MUST_BE_MOVABLE
156        | ERROR_RELOC_CHAIN_XEEDS_SEGLIM
157        | ERROR_INFLOOP_IN_RELOC_CHAIN => ENOEXEC,
158        ERROR_INVALID_HANDLE | ERROR_INVALID_TARGET_HANDLE | ERROR_DIRECT_ACCESS_HANDLE => EBADF,
159        ERROR_WAIT_NO_CHILDREN | ERROR_CHILD_NOT_COMPLETE => ECHILD,
160        ERROR_NO_PROC_SLOTS | ERROR_MAX_THRDS_REACHED | ERROR_NESTING_NOT_ALLOWED => EAGAIN,
161        ERROR_ARENA_TRASHED
162        | ERROR_NOT_ENOUGH_MEMORY
163        | ERROR_INVALID_BLOCK
164        | ERROR_NOT_ENOUGH_QUOTA => ENOMEM,
165        ERROR_ACCESS_DENIED
166        | ERROR_CURRENT_DIRECTORY
167        | ERROR_WRITE_PROTECT
168        | ERROR_BAD_UNIT
169        | ERROR_NOT_READY
170        | ERROR_BAD_COMMAND
171        | ERROR_CRC
172        | ERROR_BAD_LENGTH
173        | ERROR_SEEK
174        | ERROR_NOT_DOS_DISK
175        | ERROR_SECTOR_NOT_FOUND
176        | ERROR_OUT_OF_PAPER
177        | ERROR_WRITE_FAULT
178        | ERROR_READ_FAULT
179        | ERROR_GEN_FAILURE
180        | ERROR_SHARING_VIOLATION
181        | ERROR_LOCK_VIOLATION
182        | ERROR_WRONG_DISK
183        | ERROR_SHARING_BUFFER_EXCEEDED
184        | ERROR_NETWORK_ACCESS_DENIED
185        | ERROR_CANNOT_MAKE
186        | ERROR_FAIL_I24
187        | ERROR_DRIVE_LOCKED
188        | ERROR_SEEK_ON_DEVICE
189        | ERROR_NOT_LOCKED
190        | ERROR_LOCK_FAILED
191        | 35 => EACCES,
192        ERROR_FILE_EXISTS | ERROR_ALREADY_EXISTS => EEXIST,
193        ERROR_NOT_SAME_DEVICE => EXDEV,
194        ERROR_DIRECTORY => ENOTDIR,
195        ERROR_TOO_MANY_OPEN_FILES => EMFILE,
196        ERROR_DISK_FULL => ENOSPC,
197        ERROR_BROKEN_PIPE | ERROR_NO_DATA => EPIPE,
198        ERROR_DIR_NOT_EMPTY => ENOTEMPTY,
199        ERROR_NO_UNICODE_TRANSLATION => EILSEQ,
200        ERROR_INVALID_FUNCTION
201        | ERROR_INVALID_ACCESS
202        | ERROR_INVALID_DATA
203        | ERROR_INVALID_PARAMETER
204        | ERROR_NEGATIVE_SEEK => EINVAL,
205        _ => EINVAL,
206    }
207}