Skip to main content

std\sys\io\error/
windows.rs

1use crate::sys::pal::{api, c};
2use crate::{io, ptr};
3
4#[cfg(test)]
5mod tests;
6
7pub fn errno() -> i32 {
8    api::get_last_error().code as i32
9}
10
11#[inline]
12pub fn is_interrupted(_errno: i32) -> bool {
13    false
14}
15
16pub fn decode_error_kind(errno: i32) -> io::ErrorKind {
17    use io::ErrorKind::*;
18
19    match errno as u32 {
20        c::ERROR_ACCESS_DENIED => return PermissionDenied,
21        c::ERROR_ALREADY_EXISTS => return AlreadyExists,
22        c::ERROR_FILE_EXISTS => return AlreadyExists,
23        c::ERROR_BROKEN_PIPE => return BrokenPipe,
24        c::ERROR_FILE_NOT_FOUND
25        | c::ERROR_PATH_NOT_FOUND
26        | c::ERROR_INVALID_DRIVE
27        | c::ERROR_BAD_NETPATH
28        | c::ERROR_BAD_NET_NAME => return NotFound,
29        c::ERROR_NO_DATA => return BrokenPipe,
30        c::ERROR_INVALID_NAME | c::ERROR_BAD_PATHNAME => return InvalidFilename,
31        c::ERROR_INVALID_PARAMETER => return InvalidInput,
32        c::ERROR_NOT_ENOUGH_MEMORY | c::ERROR_OUTOFMEMORY => return OutOfMemory,
33        c::ERROR_SEM_TIMEOUT
34        | c::WAIT_TIMEOUT
35        | c::ERROR_DRIVER_CANCEL_TIMEOUT
36        | c::ERROR_OPERATION_ABORTED
37        | c::ERROR_SERVICE_REQUEST_TIMEOUT
38        | c::ERROR_COUNTER_TIMEOUT
39        | c::ERROR_TIMEOUT
40        | c::ERROR_RESOURCE_CALL_TIMED_OUT
41        | c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT
42        | c::ERROR_CTX_CLIENT_QUERY_TIMEOUT
43        | c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT
44        | c::ERROR_DS_TIMELIMIT_EXCEEDED
45        | c::DNS_ERROR_RECORD_TIMED_OUT
46        | c::ERROR_IPSEC_IKE_TIMED_OUT
47        | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT
48        | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return TimedOut,
49        c::ERROR_CALL_NOT_IMPLEMENTED => return Unsupported,
50        c::ERROR_HOST_UNREACHABLE => return HostUnreachable,
51        c::ERROR_NETWORK_UNREACHABLE => return NetworkUnreachable,
52        c::ERROR_DIRECTORY => return NotADirectory,
53        c::ERROR_DIRECTORY_NOT_SUPPORTED => return IsADirectory,
54        c::ERROR_DIR_NOT_EMPTY => return DirectoryNotEmpty,
55        c::ERROR_WRITE_PROTECT => return ReadOnlyFilesystem,
56        c::ERROR_DISK_FULL | c::ERROR_HANDLE_DISK_FULL => return StorageFull,
57        c::ERROR_SEEK_ON_DEVICE => return NotSeekable,
58        c::ERROR_DISK_QUOTA_EXCEEDED => return QuotaExceeded,
59        c::ERROR_FILE_TOO_LARGE => return FileTooLarge,
60        c::ERROR_BUSY => return ResourceBusy,
61        c::ERROR_POSSIBLE_DEADLOCK => return Deadlock,
62        c::ERROR_NOT_SAME_DEVICE => return CrossesDevices,
63        c::ERROR_TOO_MANY_LINKS => return TooManyLinks,
64        c::ERROR_TOO_MANY_OPEN_FILES => return TooManyOpenFiles,
65        c::ERROR_FILENAME_EXCED_RANGE => return InvalidFilename,
66        c::ERROR_CANT_RESOLVE_FILENAME => return FilesystemLoop,
67        _ => {}
68    }
69
70    match errno {
71        c::WSAEACCES => PermissionDenied,
72        c::WSAEADDRINUSE => AddrInUse,
73        c::WSAEADDRNOTAVAIL => AddrNotAvailable,
74        c::WSAECONNABORTED => ConnectionAborted,
75        c::WSAECONNREFUSED => ConnectionRefused,
76        c::WSAECONNRESET => ConnectionReset,
77        c::WSAEINVAL => InvalidInput,
78        c::WSAENOTCONN => NotConnected,
79        c::WSAEWOULDBLOCK => WouldBlock,
80        c::WSAETIMEDOUT => TimedOut,
81        c::WSAEHOSTUNREACH => HostUnreachable,
82        c::WSAENETDOWN => NetworkDown,
83        c::WSAENETUNREACH => NetworkUnreachable,
84        c::WSAEDQUOT => QuotaExceeded,
85        c::WSAEMFILE => TooManyOpenFiles,
86        // Not a perfect mapping but this error is only returned when writing to
87        // a socket after shutting down the write-end. On Unix targets, EPIPE is
88        // returned in those cases.
89        c::WSAESHUTDOWN => BrokenPipe,
90
91        _ => Uncategorized,
92    }
93}
94
95/// Gets a detailed string description for the given error number.
96pub fn error_string(mut errnum: i32) -> String {
97    let mut buf = [0 as c::WCHAR; 2048];
98
99    unsafe {
100        let mut module = ptr::null_mut();
101        let mut flags = 0;
102
103        // NTSTATUS errors may be encoded as HRESULT, which may returned from
104        // GetLastError. For more information about Windows error codes, see
105        // `[MS-ERREF]`: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a
106        if (errnum & c::FACILITY_NT_BIT as i32) != 0 {
107            // format according to https://support.microsoft.com/en-us/help/259693
108            const NTDLL_DLL: &[u16] = &[
109                'N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, '.' as _, 'D' as _, 'L' as _,
110                'L' as _, 0,
111            ];
112            module = c::GetModuleHandleW(NTDLL_DLL.as_ptr());
113
114            if !module.is_null() {
115                errnum ^= c::FACILITY_NT_BIT as i32;
116                flags = c::FORMAT_MESSAGE_FROM_HMODULE;
117            }
118        }
119
120        let res = c::FormatMessageW(
121            flags | c::FORMAT_MESSAGE_FROM_SYSTEM | c::FORMAT_MESSAGE_IGNORE_INSERTS,
122            module,
123            errnum as u32,
124            0,
125            buf.as_mut_ptr(),
126            buf.len() as u32,
127            ptr::null(),
128        ) as usize;
129        if res == 0 {
130            // Sometimes FormatMessageW can fail e.g., system doesn't like 0 as langId,
131            let fm_err = errno();
132            return format!("OS Error {errnum} (FormatMessageW() returned error {fm_err})");
133        }
134
135        match String::from_utf16(&buf[..res]) {
136            Ok(mut msg) => {
137                // Trim trailing CRLF inserted by FormatMessageW
138                let len = msg.trim_ascii_end().len();
139                msg.truncate(len);
140                msg
141            }
142            Err(..) => format!(
143                "OS Error {} (FormatMessageW() returned \
144                 invalid UTF-16)",
145                errnum
146            ),
147        }
148    }
149}