error.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. // Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //go:build windows
  5. package wingoes
  6. import (
  7. "fmt"
  8. "golang.org/x/sys/windows"
  9. )
  10. // Error represents various error codes that may be encountered when coding
  11. // against Windows APIs, including HRESULTs, windows.NTStatus, and windows.Errno.
  12. type Error HRESULT
  13. // Errors are HRESULTs under the hood because the HRESULT encoding allows for
  14. // all the other common types of Windows errors to be encoded within them.
  15. const (
  16. hrS_OK = HRESULT(0)
  17. hrE_ABORT = HRESULT(-((0x80004004 ^ 0xFFFFFFFF) + 1))
  18. hrE_FAIL = HRESULT(-((0x80004005 ^ 0xFFFFFFFF) + 1))
  19. hrE_NOINTERFACE = HRESULT(-((0x80004002 ^ 0xFFFFFFFF) + 1))
  20. hrE_NOTIMPL = HRESULT(-((0x80004001 ^ 0xFFFFFFFF) + 1))
  21. hrE_POINTER = HRESULT(-((0x80004003 ^ 0xFFFFFFFF) + 1))
  22. hrE_UNEXPECTED = HRESULT(-((0x8000FFFF ^ 0xFFFFFFFF) + 1))
  23. hrTYPE_E_WRONGTYPEKIND = HRESULT(-((0x8002802A ^ 0xFFFFFFFF) + 1))
  24. )
  25. // S_FALSE is a peculiar HRESULT value which means that the call executed
  26. // successfully, but returned false as its result.
  27. const S_FALSE = HRESULT(1)
  28. var (
  29. // genericError encodes an Error whose message string is very generic.
  30. genericError = Error(hresultFromFacilityAndCode(hrFail, facilityWin32, hrCode(windows.ERROR_UNIDENTIFIED_ERROR)))
  31. )
  32. // Common HRESULT codes that don't use Win32 facilities, but have meanings that
  33. // we can manually translate to Win32 error codes.
  34. var commonHRESULTToErrno = map[HRESULT]windows.Errno{
  35. hrE_ABORT: windows.ERROR_REQUEST_ABORTED,
  36. hrE_FAIL: windows.ERROR_UNIDENTIFIED_ERROR,
  37. hrE_NOINTERFACE: windows.ERROR_NOINTERFACE,
  38. hrE_NOTIMPL: windows.ERROR_CALL_NOT_IMPLEMENTED,
  39. hrE_UNEXPECTED: windows.ERROR_INTERNAL_ERROR,
  40. }
  41. type hrCode uint16
  42. type hrFacility uint16
  43. type failBit bool
  44. const (
  45. hrFlagBitsMask = 0xF8000000
  46. hrFacilityMax = 0x00001FFF
  47. hrFacilityMask = hrFacilityMax << 16
  48. hrCodeMax = 0x0000FFFF
  49. hrCodeMask = hrCodeMax
  50. hrFailBit = 0x80000000
  51. hrCustomerBit = 0x20000000 // Also defined as syscall.APPLICATION_ERROR
  52. hrFacilityNTBit = 0x10000000
  53. )
  54. const (
  55. facilityWin32 = hrFacility(7)
  56. )
  57. // Succeeded returns true when hr is successful, but its actual error code
  58. // may include additional status information.
  59. func (hr HRESULT) Succeeded() bool {
  60. return hr >= 0
  61. }
  62. // Failed returns true when hr contains a failure code.
  63. func (hr HRESULT) Failed() bool {
  64. return hr < 0
  65. }
  66. func (hr HRESULT) isNT() bool {
  67. return (hr & (hrCustomerBit | hrFacilityNTBit)) == hrFacilityNTBit
  68. }
  69. func (hr HRESULT) isCustomer() bool {
  70. return (hr & hrCustomerBit) != 0
  71. }
  72. // isNormal returns true when the customer and NT bits are cleared, ie hr's
  73. // encoding contains valid facility and code fields.
  74. func (hr HRESULT) isNormal() bool {
  75. return (hr & (hrCustomerBit | hrFacilityNTBit)) == 0
  76. }
  77. // facility returns the facility bits of hr. Only valid when isNormal is true.
  78. func (hr HRESULT) facility() hrFacility {
  79. return hrFacility((uint32(hr) >> 16) & hrFacilityMax)
  80. }
  81. // facility returns the code bits of hr. Only valid when isNormal is true.
  82. func (hr HRESULT) code() hrCode {
  83. return hrCode(uint32(hr) & hrCodeMask)
  84. }
  85. const (
  86. hrFail = failBit(true)
  87. hrSuccess = failBit(false)
  88. )
  89. func hresultFromFacilityAndCode(isFail failBit, f hrFacility, c hrCode) HRESULT {
  90. var r uint32
  91. if isFail {
  92. r |= hrFailBit
  93. }
  94. r |= (uint32(f) << 16) & hrFacilityMask
  95. r |= uint32(c) & hrCodeMask
  96. return HRESULT(r)
  97. }
  98. // ErrorFromErrno creates an Error from e.
  99. func ErrorFromErrno(e windows.Errno) Error {
  100. if e == windows.ERROR_SUCCESS {
  101. return Error(hrS_OK)
  102. }
  103. if ue := uint32(e); (ue & hrFlagBitsMask) == hrCustomerBit {
  104. // syscall.APPLICATION_ERROR == hrCustomerBit, so the only other thing
  105. // we need to do to transform this into an HRESULT is add the fail flag
  106. return Error(HRESULT(ue | hrFailBit))
  107. }
  108. if uint32(e) > hrCodeMax {
  109. // Can't be encoded in HRESULT, return generic error instead
  110. return genericError
  111. }
  112. return Error(hresultFromFacilityAndCode(hrFail, facilityWin32, hrCode(e)))
  113. }
  114. // ErrorFromNTStatus creates an Error from s.
  115. func ErrorFromNTStatus(s windows.NTStatus) Error {
  116. if s == windows.STATUS_SUCCESS {
  117. return Error(hrS_OK)
  118. }
  119. return Error(HRESULT(s) | hrFacilityNTBit)
  120. }
  121. // ErrorFromHRESULT creates an Error from hr.
  122. func ErrorFromHRESULT(hr HRESULT) Error {
  123. return Error(hr)
  124. }
  125. // NewError converts e into an Error if e's type is supported. It returns
  126. // both the Error and a bool indicating whether the conversion was successful.
  127. func NewError(e any) (Error, bool) {
  128. switch v := e.(type) {
  129. case Error:
  130. return v, true
  131. case windows.NTStatus:
  132. return ErrorFromNTStatus(v), true
  133. case windows.Errno:
  134. return ErrorFromErrno(v), true
  135. case HRESULT:
  136. return ErrorFromHRESULT(v), true
  137. default:
  138. return ErrorFromHRESULT(hrTYPE_E_WRONGTYPEKIND), false
  139. }
  140. }
  141. // IsOK returns true when the Error is unconditionally successful.
  142. func (e Error) IsOK() bool {
  143. return HRESULT(e) == hrS_OK
  144. }
  145. // Succeeded returns true when the Error is successful, but its error code
  146. // may include additional status information.
  147. func (e Error) Succeeded() bool {
  148. return HRESULT(e).Succeeded()
  149. }
  150. // Failed returns true when the Error contains a failure code.
  151. func (e Error) Failed() bool {
  152. return HRESULT(e).Failed()
  153. }
  154. // AsHRESULT converts the Error to a HRESULT.
  155. func (e Error) AsHRESULT() HRESULT {
  156. return HRESULT(e)
  157. }
  158. type errnoFailHandler func(hr HRESULT) windows.Errno
  159. func (e Error) toErrno(f errnoFailHandler) windows.Errno {
  160. hr := HRESULT(e)
  161. if hr == hrS_OK {
  162. return windows.ERROR_SUCCESS
  163. }
  164. if hr.isCustomer() {
  165. return windows.Errno(uint32(e) ^ hrFailBit)
  166. }
  167. if hr.isNT() {
  168. return e.AsNTStatus().Errno()
  169. }
  170. if hr.facility() == facilityWin32 {
  171. return windows.Errno(hr.code())
  172. }
  173. if errno, ok := commonHRESULTToErrno[hr]; ok {
  174. return errno
  175. }
  176. return f(hr)
  177. }
  178. // AsError converts the Error to a windows.Errno, but panics if not possible.
  179. func (e Error) AsErrno() windows.Errno {
  180. handler := func(hr HRESULT) windows.Errno {
  181. panic(fmt.Sprintf("wingoes.Error: Called AsErrno on a non-convertable HRESULT 0x%08X", uint32(hr)))
  182. return windows.ERROR_UNIDENTIFIED_ERROR
  183. }
  184. return e.toErrno(handler)
  185. }
  186. type ntStatusFailHandler func(hr HRESULT) windows.NTStatus
  187. func (e Error) toNTStatus(f ntStatusFailHandler) windows.NTStatus {
  188. hr := HRESULT(e)
  189. if hr == hrS_OK {
  190. return windows.STATUS_SUCCESS
  191. }
  192. if hr.isNT() {
  193. return windows.NTStatus(hr ^ hrFacilityNTBit)
  194. }
  195. return f(hr)
  196. }
  197. // AsNTStatus converts the Error to a windows.NTStatus, but panics if not possible.
  198. func (e Error) AsNTStatus() windows.NTStatus {
  199. handler := func(hr HRESULT) windows.NTStatus {
  200. panic(fmt.Sprintf("windows.Error: Called AsNTStatus on a non-NTSTATUS HRESULT 0x%08X", uint32(hr)))
  201. return windows.STATUS_UNSUCCESSFUL
  202. }
  203. return e.toNTStatus(handler)
  204. }
  205. // TryAsErrno converts the Error to a windows.Errno, or returns defval if
  206. // such a conversion is not possible.
  207. func (e Error) TryAsErrno(defval windows.Errno) windows.Errno {
  208. handler := func(hr HRESULT) windows.Errno {
  209. return defval
  210. }
  211. return e.toErrno(handler)
  212. }
  213. // TryAsNTStatus converts the Error to a windows.NTStatus, or returns defval if
  214. // such a conversion is not possible.
  215. func (e Error) TryAsNTStatus(defval windows.NTStatus) windows.NTStatus {
  216. handler := func(hr HRESULT) windows.NTStatus {
  217. return defval
  218. }
  219. return e.toNTStatus(handler)
  220. }
  221. // IsAvailableAsHRESULT returns true if e may be converted to an HRESULT.
  222. func (e Error) IsAvailableAsHRESULT() bool {
  223. return true
  224. }
  225. // IsAvailableAsErrno returns true if e may be converted to a windows.Errno.
  226. func (e Error) IsAvailableAsErrno() bool {
  227. hr := HRESULT(e)
  228. if hr.isCustomer() || e.IsAvailableAsNTStatus() || (hr.facility() == facilityWin32) {
  229. return true
  230. }
  231. _, convertable := commonHRESULTToErrno[hr]
  232. return convertable
  233. }
  234. // IsAvailableAsNTStatus returns true if e may be converted to a windows.NTStatus.
  235. func (e Error) IsAvailableAsNTStatus() bool {
  236. return HRESULT(e) == hrS_OK || HRESULT(e).isNT()
  237. }
  238. // Error produces a human-readable message describing Error e.
  239. func (e Error) Error() string {
  240. if HRESULT(e).isCustomer() {
  241. return windows.Errno(uint32(e) ^ hrFailBit).Error()
  242. }
  243. buf := make([]uint16, 300)
  244. const flags = windows.FORMAT_MESSAGE_FROM_SYSTEM | windows.FORMAT_MESSAGE_IGNORE_INSERTS
  245. lenExclNul, err := windows.FormatMessage(flags, 0, uint32(e), 0, buf, nil)
  246. if err != nil {
  247. return fmt.Sprintf("wingoes.Error 0x%08X", uint32(e))
  248. }
  249. for ; lenExclNul > 0 && (buf[lenExclNul-1] == '\n' || buf[lenExclNul-1] == '\r'); lenExclNul-- {
  250. }
  251. return windows.UTF16ToString(buf[:lenExclNul])
  252. }