NIOCore: handle error description on Windows (#2219)

We would previously attempt to convert an error to a description without
consideration for the provenance on Windows.  This would result in a
failure if the error code was not from `errno`.  Account for the source
of the error and translate it appropriately.  We can not retrieve
descriptions on errors that may originate from Windows or WinSock as
well as the C library.
This commit is contained in:
Saleem Abdulrasool 2022-07-07 05:07:34 -07:00 committed by GitHub
parent 8b160c3681
commit 48916a49af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 49 additions and 0 deletions

View File

@ -14,7 +14,20 @@
#if os(Windows)
import ucrt
import func WinSDK.FormatMessageW
import func WinSDK.LocalFree
import let WinSDK.FORMAT_MESSAGE_ALLOCATE_BUFFER
import let WinSDK.FORMAT_MESSAGE_FROM_SYSTEM
import let WinSDK.FORMAT_MESSAGE_IGNORE_INSERTS
import let WinSDK.LANG_NEUTRAL
import let WinSDK.SUBLANG_DEFAULT
import typealias WinSDK.DWORD
import typealias WinSDK.WCHAR
import typealias WinSDK.WORD
internal func MAKELANGID(_ p: WORD, _ s: WORD) -> DWORD {
return DWORD((s << 10) | p)
}
#elseif os(Linux) || os(Android)
import Glibc
#elseif os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
@ -107,13 +120,49 @@ private func reasonForError(errnoCode: CInt, reason: String) -> String {
}
}
#if os(Windows)
private func reasonForWinError(_ code: DWORD) -> String {
let dwFlags: DWORD = DWORD(FORMAT_MESSAGE_ALLOCATE_BUFFER)
| DWORD(FORMAT_MESSAGE_FROM_SYSTEM)
| DWORD(FORMAT_MESSAGE_IGNORE_INSERTS)
var buffer: UnsafeMutablePointer<WCHAR>?
// We use `FORMAT_MESSAGE_ALLOCATE_BUFFER` in flags which means that the
// buffer will be allocated by the call to `FormatMessageW`. The function
// expects a `LPWSTR` and expects the user to type-pun in this case.
let dwResult: DWORD = withUnsafeMutablePointer(to: &buffer) {
$0.withMemoryRebound(to: WCHAR.self, capacity: 2) {
FormatMessageW(dwFlags, nil, code,
MAKELANGID(WORD(LANG_NEUTRAL), WORD(SUBLANG_DEFAULT)),
$0, 0, nil)
}
}
guard dwResult > 0, let message = buffer else {
return "unknown error \(code)"
}
defer { LocalFree(buffer) }
return String(decodingCString: message, as: UTF16.self)
}
#endif
extension IOError: CustomStringConvertible {
public var description: String {
return self.localizedDescription
}
public var localizedDescription: String {
#if os(Windows)
switch self.error {
case .errno(let errno):
return reasonForError(errnoCode: errno, reason: self.failureDescription)
case .windows(let code):
return reasonForWinError(code)
case .winsock(let code):
return reasonForWinError(DWORD(code))
}
#else
return reasonForError(errnoCode: self.errnoCode, reason: self.failureDescription)
#endif
}
}