1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include <__assert>
10#include <__config>
11#include <__system_error/throw_system_error.h>
12#include <__verbose_abort>
13#include <cerrno>
14#include <cstdio>
15#include <cstdlib>
16#include <cstring>
17#include <optional>
18#include <string.h>
19#include <string>
20#include <system_error>
21
22#include "include/config_elast.h"
23
24#if defined(_LIBCPP_WIN32API)
25# include <windows.h>
26# include <winerror.h>
27#endif
28
29_LIBCPP_BEGIN_NAMESPACE_STD
30_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
31
32#if defined(_LIBCPP_WIN32API)
33
34namespace {
35std::optional<errc> __win_err_to_errc(int err) {
36 switch (err) {
37 case ERROR_ACCESS_DENIED:
38 return errc::permission_denied;
39 case ERROR_ALREADY_EXISTS:
40 return errc::file_exists;
41 case ERROR_BAD_NETPATH:
42 return errc::no_such_file_or_directory;
43 case ERROR_BAD_PATHNAME:
44 return errc::no_such_file_or_directory;
45 case ERROR_BAD_UNIT:
46 return errc::no_such_device;
47 case ERROR_BROKEN_PIPE:
48 return errc::broken_pipe;
49 case ERROR_BUFFER_OVERFLOW:
50 return errc::filename_too_long;
51 case ERROR_BUSY:
52 return errc::device_or_resource_busy;
53 case ERROR_BUSY_DRIVE:
54 return errc::device_or_resource_busy;
55 case ERROR_CANNOT_MAKE:
56 return errc::permission_denied;
57 case ERROR_CANTOPEN:
58 return errc::io_error;
59 case ERROR_CANTREAD:
60 return errc::io_error;
61 case ERROR_CANTWRITE:
62 return errc::io_error;
63 case ERROR_CURRENT_DIRECTORY:
64 return errc::permission_denied;
65 case ERROR_DEV_NOT_EXIST:
66 return errc::no_such_device;
67 case ERROR_DEVICE_IN_USE:
68 return errc::device_or_resource_busy;
69 case ERROR_DIR_NOT_EMPTY:
70 return errc::directory_not_empty;
71 case ERROR_DIRECTORY:
72 return errc::invalid_argument;
73 case ERROR_DISK_FULL:
74 return errc::no_space_on_device;
75 case ERROR_FILE_EXISTS:
76 return errc::file_exists;
77 case ERROR_FILE_NOT_FOUND:
78 return errc::no_such_file_or_directory;
79 case ERROR_HANDLE_DISK_FULL:
80 return errc::no_space_on_device;
81 case ERROR_INVALID_ACCESS:
82 return errc::permission_denied;
83 case ERROR_INVALID_DRIVE:
84 return errc::no_such_device;
85 case ERROR_INVALID_FUNCTION:
86 return errc::function_not_supported;
87 case ERROR_INVALID_HANDLE:
88 return errc::invalid_argument;
89 case ERROR_INVALID_NAME:
90 return errc::no_such_file_or_directory;
91 case ERROR_INVALID_PARAMETER:
92 return errc::invalid_argument;
93 case ERROR_LOCK_VIOLATION:
94 return errc::no_lock_available;
95 case ERROR_LOCKED:
96 return errc::no_lock_available;
97 case ERROR_NEGATIVE_SEEK:
98 return errc::invalid_argument;
99 case ERROR_NETNAME_DELETED:
100 return errc::no_such_file_or_directory;
101 case ERROR_NOACCESS:
102 return errc::permission_denied;
103 case ERROR_NOT_ENOUGH_MEMORY:
104 return errc::not_enough_memory;
105 case ERROR_NOT_READY:
106 return errc::resource_unavailable_try_again;
107 case ERROR_NOT_SAME_DEVICE:
108 return errc::cross_device_link;
109 case ERROR_NOT_SUPPORTED:
110 return errc::not_supported;
111 case ERROR_OPEN_FAILED:
112 return errc::io_error;
113 case ERROR_OPEN_FILES:
114 return errc::device_or_resource_busy;
115 case ERROR_OPERATION_ABORTED:
116 return errc::operation_canceled;
117 case ERROR_OUTOFMEMORY:
118 return errc::not_enough_memory;
119 case ERROR_PATH_NOT_FOUND:
120 return errc::no_such_file_or_directory;
121 case ERROR_READ_FAULT:
122 return errc::io_error;
123 case ERROR_REPARSE_TAG_INVALID:
124 return errc::invalid_argument;
125 case ERROR_RETRY:
126 return errc::resource_unavailable_try_again;
127 case ERROR_SEEK:
128 return errc::io_error;
129 case ERROR_SHARING_VIOLATION:
130 return errc::permission_denied;
131 case ERROR_TOO_MANY_OPEN_FILES:
132 return errc::too_many_files_open;
133 case ERROR_WRITE_FAULT:
134 return errc::io_error;
135 case ERROR_WRITE_PROTECT:
136 return errc::permission_denied;
137 default:
138 return {};
139 }
140}
141} // namespace
142#endif
143
144namespace {
145#if _LIBCPP_HAS_THREADS
146
147// GLIBC also uses 1024 as the maximum buffer size internally.
148constexpr size_t strerror_buff_size = 1024;
149
150string do_strerror_r(int ev);
151
152# if defined(_LIBCPP_MSVCRT_LIKE)
153string do_strerror_r(int ev) {
154 char buffer[strerror_buff_size];
155 if (::strerror_s(buffer, strerror_buff_size, ev) == 0)
156 return string(buffer);
157 std::snprintf(buffer, strerror_buff_size, "unknown error %d", ev);
158 return string(buffer);
159}
160# else
161
162// Only one of the two following functions will be used, depending on
163// the return type of strerror_r:
164
165// For the GNU variant, a char* return value:
166__attribute__((unused)) const char* handle_strerror_r_return(char* strerror_return, char* buffer) {
167 // GNU always returns a string pointer in its return value. The
168 // string might point to either the input buffer, or a static
169 // buffer, but we don't care which.
170 return strerror_return;
171}
172
173// For the POSIX variant: an int return value.
174__attribute__((unused)) const char* handle_strerror_r_return(int strerror_return, char* buffer) {
175 // The POSIX variant either:
176 // - fills in the provided buffer and returns 0
177 // - returns a positive error value, or
178 // - returns -1 and fills in errno with an error value.
179 if (strerror_return == 0)
180 return buffer;
181
182 // Only handle EINVAL. Other errors abort.
183 int new_errno = strerror_return == -1 ? errno : strerror_return;
184 if (new_errno == EINVAL)
185 return "";
186
187 _LIBCPP_ASSERT_INTERNAL(new_errno == ERANGE, "unexpected error from ::strerror_r");
188 // FIXME maybe? 'strerror_buff_size' is likely to exceed the
189 // maximum error size so ERANGE shouldn't be returned.
190 std::abort();
191}
192
193// This function handles both GNU and POSIX variants, dispatching to
194// one of the two above functions.
195string do_strerror_r(int ev) {
196 char buffer[strerror_buff_size];
197 // Preserve errno around the call. (The C++ standard requires that
198 // system_error functions not modify errno).
199 const int old_errno = errno;
200 const char* error_message = handle_strerror_r_return(strerror_return: ::strerror_r(errnum: ev, buf: buffer, buflen: strerror_buff_size), buffer);
201 // If we didn't get any message, print one now.
202 if (!error_message[0]) {
203 std::snprintf(s: buffer, maxlen: strerror_buff_size, format: "Unknown error %d", ev);
204 error_message = buffer;
205 }
206 errno = old_errno;
207 return string(error_message);
208}
209# endif
210
211#endif // _LIBCPP_HAS_THREADS
212
213string make_error_str(const error_code& ec, string what_arg) {
214 if (ec) {
215 if (!what_arg.empty()) {
216 what_arg += ": ";
217 }
218 what_arg += ec.message();
219 }
220 return what_arg;
221}
222
223string make_error_str(const error_code& ec) {
224 if (ec) {
225 return ec.message();
226 }
227 return string();
228}
229} // namespace
230
231string __do_message::message(int ev) const {
232#if !_LIBCPP_HAS_THREADS
233 return string(::strerror(ev));
234#else
235 return do_strerror_r(ev);
236#endif
237}
238
239class _LIBCPP_HIDDEN __generic_error_category : public __do_message {
240public:
241 virtual const char* name() const noexcept;
242 virtual string message(int ev) const;
243};
244
245const char* __generic_error_category::name() const noexcept { return "generic"; }
246
247string __generic_error_category::message(int ev) const {
248#ifdef _LIBCPP_ELAST
249 if (ev > _LIBCPP_ELAST)
250 return string("unspecified generic_category error");
251#endif // _LIBCPP_ELAST
252 return __do_message::message(ev);
253}
254
255const error_category& generic_category() noexcept {
256 union AvoidDestroyingGenericCategory {
257 __generic_error_category generic_error_category;
258 constexpr explicit AvoidDestroyingGenericCategory() : generic_error_category() {}
259 ~AvoidDestroyingGenericCategory() {}
260 };
261 constinit static AvoidDestroyingGenericCategory helper;
262 return helper.generic_error_category;
263}
264
265class _LIBCPP_HIDDEN __system_error_category : public __do_message {
266public:
267 virtual const char* name() const noexcept;
268 virtual string message(int ev) const;
269 virtual error_condition default_error_condition(int ev) const noexcept;
270};
271
272const char* __system_error_category::name() const noexcept { return "system"; }
273
274string __system_error_category::message(int ev) const {
275#ifdef _LIBCPP_WIN32API
276 std::string result;
277 char* str = nullptr;
278 unsigned long num_chars = ::FormatMessageA(
279 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
280 nullptr,
281 ev,
282 0,
283 reinterpret_cast<char*>(&str),
284 0,
285 nullptr);
286 auto is_whitespace = [](char ch) { return ch == '\n' || ch == '\r' || ch == ' '; };
287 while (num_chars > 0 && is_whitespace(str[num_chars - 1]))
288 --num_chars;
289
290 if (num_chars)
291 result = std::string(str, num_chars);
292 else
293 result = "Unknown error";
294
295 LocalFree(str);
296 return result;
297#else
298# ifdef _LIBCPP_ELAST
299 if (ev > _LIBCPP_ELAST)
300 return string("unspecified system_category error");
301# endif // _LIBCPP_ELAST
302 return __do_message::message(ev);
303#endif
304}
305
306error_condition __system_error_category::default_error_condition(int ev) const noexcept {
307#ifdef _LIBCPP_WIN32API
308 // Remap windows error codes to generic error codes if possible.
309 if (ev == 0)
310 return error_condition(0, generic_category());
311 if (auto maybe_errc = __win_err_to_errc(ev))
312 return error_condition(static_cast<int>(*maybe_errc), generic_category());
313 return error_condition(ev, system_category());
314#else
315# ifdef _LIBCPP_ELAST
316 if (ev > _LIBCPP_ELAST)
317 return error_condition(ev, system_category());
318# endif // _LIBCPP_ELAST
319 return error_condition(ev, generic_category());
320#endif
321}
322
323const error_category& system_category() noexcept {
324 union AvoidDestroyingSystemCategory {
325 __system_error_category system_error_category;
326 constexpr explicit AvoidDestroyingSystemCategory() : system_error_category() {}
327 ~AvoidDestroyingSystemCategory() {}
328 };
329 constinit static AvoidDestroyingSystemCategory helper;
330 return helper.system_error_category;
331}
332
333// error_condition
334
335string error_condition::message() const { return __cat_->message(ev: __val_); }
336
337// error_code
338
339string error_code::message() const { return __cat_->message(ev: __val_); }
340
341// system_error
342
343system_error::system_error(error_code ec, const string& what_arg)
344 : runtime_error(make_error_str(ec, what_arg)), __ec_(ec) {}
345
346system_error::system_error(error_code ec, const char* what_arg)
347 : runtime_error(make_error_str(ec, what_arg)), __ec_(ec) {}
348
349system_error::system_error(error_code ec) : runtime_error(make_error_str(ec)), __ec_(ec) {}
350
351system_error::system_error(int ev, const error_category& ecat, const string& what_arg)
352 : runtime_error(make_error_str(ec: error_code(ev, ecat), what_arg)), __ec_(error_code(ev, ecat)) {}
353
354system_error::system_error(int ev, const error_category& ecat, const char* what_arg)
355 : runtime_error(make_error_str(ec: error_code(ev, ecat), what_arg)), __ec_(error_code(ev, ecat)) {}
356
357system_error::system_error(int ev, const error_category& ecat)
358 : runtime_error(make_error_str(ec: error_code(ev, ecat))), __ec_(error_code(ev, ecat)) {}
359
360system_error::~system_error() noexcept {}
361
362void __throw_system_error(int ev, const char* what_arg) {
363#if _LIBCPP_HAS_EXCEPTIONS
364 std::__throw_system_error(ec: error_code(ev, generic_category()), what_arg: what_arg);
365#else
366 // The above could also handle the no-exception case, but for size, avoid referencing system_category() unnecessarily.
367 _LIBCPP_VERBOSE_ABORT(
368 "system_error was thrown in -fno-exceptions mode with error %i and message \"%s\"", ev, what_arg);
369#endif
370}
371
372_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
373_LIBCPP_END_NAMESPACE_STD
374