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_FILENAME_EXCED_RANGE => return InvalidFilename,
65        c::ERROR_CANT_RESOLVE_FILENAME => return FilesystemLoop,
66        _ => {}
67    }
68
69    match errno {
70        c::WSAEACCES => PermissionDenied,
71        c::WSAEADDRINUSE => AddrInUse,
72        c::WSAEADDRNOTAVAIL => AddrNotAvailable,
73        c::WSAECONNABORTED => ConnectionAborted,
74        c::WSAECONNREFUSED => ConnectionRefused,
75        c::WSAECONNRESET => ConnectionReset,
76        c::WSAEINVAL => InvalidInput,
77        c::WSAENOTCONN => NotConnected,
78        c::WSAEWOULDBLOCK => WouldBlock,
79        c::WSAETIMEDOUT => TimedOut,
80        c::WSAEHOSTUNREACH => HostUnreachable,
81        c::WSAENETDOWN => NetworkDown,
82        c::WSAENETUNREACH => NetworkUnreachable,
83        c::WSAEDQUOT => QuotaExceeded,
84        // Not a perfect mapping but this error is only returned when writing to
85        // a socket after shutting down the write-end. On Unix targets, EPIPE is
86        // returned in those cases.
87        c::WSAESHUTDOWN => BrokenPipe,
88
89        _ => Uncategorized,
90    }
91}
92
93/// Gets a detailed string description for the given error number.
94pub fn error_string(mut errnum: i32) -> String {
95    let mut buf = [0 as c::WCHAR; 2048];
96
97    unsafe {
98        let mut module = ptr::null_mut();
99        let mut flags = 0;
100
101        // NTSTATUS errors may be encoded as HRESULT, which may returned from
102        // GetLastError. For more information about Windows error codes, see
103        // `[MS-ERREF]`: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a
104        if (errnum & c::FACILITY_NT_BIT as i32) != 0 {
105            // format according to https://support.microsoft.com/en-us/help/259693
106            const NTDLL_DLL: &[u16] = &[
107                'N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, '.' as _, 'D' as _, 'L' as _,
108                'L' as _, 0,
109            ];
110            module = c::GetModuleHandleW(NTDLL_DLL.as_ptr());
111
112            if !module.is_null() {
113                errnum ^= c::FACILITY_NT_BIT as i32;
114                flags = c::FORMAT_MESSAGE_FROM_HMODULE;
115            }
116        }
117
118        let res = c::FormatMessageW(
119            flags | c::FORMAT_MESSAGE_FROM_SYSTEM | c::FORMAT_MESSAGE_IGNORE_INSERTS,
120            module,
121            errnum as u32,
122            0,
123            buf.as_mut_ptr(),
124            buf.len() as u32,
125            ptr::null(),
126        ) as usize;
127        if res == 0 {
128            // Sometimes FormatMessageW can fail e.g., system doesn't like 0 as langId,
129            let fm_err = errno();
130            return format!("OS Error {errnum} (FormatMessageW() returned error {fm_err})");
131        }
132
133        match String::from_utf16(&buf[..res]) {
134            Ok(mut msg) => {
135                // Trim trailing CRLF inserted by FormatMessageW
136                let len = msg.trim_ascii_end().len();
137                msg.truncate(len);
138                msg
139            }
140            Err(..) => format!(
141                "OS Error {} (FormatMessageW() returned \
142                 invalid UTF-16)",
143                errnum
144            ),
145        }
146    }
147}