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 <__optional/optional.h>
12#include <__system_error/throw_system_error.h>
13#include <__verbose_abort>
14#include <cerrno>
15#include <cstdio>
16#include <cstdlib>
17#include <cstring>
18#include <string.h>
19#include <string>
20#include <system_error>
21
22#include "include/config_elast.h"
23
24#ifdef _WIN32
25# include <windows.h>
26# include <winerror.h>
27#endif
28
29_LIBCPP_BEGIN_NAMESPACE_STD
30_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
31
32#ifdef _WIN32
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# ifdef _WIN32
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 // Use no-destroy since this removes the function local static guard variable.
262 _LIBCPP_NO_DESTROY constinit static AvoidDestroyingGenericCategory helper;
263 return helper.generic_error_category;
264}
265
266class _LIBCPP_HIDDEN __system_error_category : public __do_message {
267public:
268 virtual const char* name() const noexcept;
269 virtual string message(int ev) const;
270 virtual error_condition default_error_condition(int ev) const noexcept;
271};
272
273const char* __system_error_category::name() const noexcept { return "system"; }
274
275string __system_error_category::message(int ev) const {
276#ifdef _WIN32
277 std::string result;
278 char* str = nullptr;
279 unsigned long num_chars = ::FormatMessageA(
280 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
281 nullptr,
282 ev,
283 0,
284 reinterpret_cast<char*>(&str),
285 0,
286 nullptr);
287 auto is_whitespace = [](char ch) { return ch == '\n' || ch == '\r' || ch == ' '; };
288 while (num_chars > 0 && is_whitespace(str[num_chars - 1]))
289 --num_chars;
290
291 if (num_chars)
292 result = std::string(str, num_chars);
293 else
294 result = "Unknown error";
295
296 LocalFree(str);
297 return result;
298#else
299# ifdef _LIBCPP_ELAST
300 if (ev > _LIBCPP_ELAST)
301 return string("unspecified system_category error");
302# endif // _LIBCPP_ELAST
303 return __do_message::message(ev);
304#endif
305}
306
307error_condition __system_error_category::default_error_condition(int ev) const noexcept {
308#ifdef _WIN32
309 // Remap windows error codes to generic error codes if possible.
310 if (ev == 0)
311 return error_condition(0, generic_category());
312 if (auto maybe_errc = __win_err_to_errc(ev))
313 return error_condition(static_cast<int>(*maybe_errc), generic_category());
314 return error_condition(ev, system_category());
315#else
316# ifdef _LIBCPP_ELAST
317 if (ev > _LIBCPP_ELAST)
318 return error_condition(ev, system_category());
319# endif // _LIBCPP_ELAST
320 return error_condition(ev, generic_category());
321#endif
322}
323
324const error_category& system_category() noexcept {
325 union AvoidDestroyingSystemCategory {
326 __system_error_category system_error_category;
327 constexpr explicit AvoidDestroyingSystemCategory() : system_error_category() {}
328 ~AvoidDestroyingSystemCategory() {}
329 };
330 // Use no-destroy since this removes the function local static guard variable.
331 _LIBCPP_NO_DESTROY constinit static AvoidDestroyingSystemCategory helper;
332 return helper.system_error_category;
333}
334
335// error_condition
336
337string error_condition::message() const { return __cat_->message(ev: __val_); }
338
339// error_code
340
341string error_code::message() const { return __cat_->message(ev: __val_); }
342
343// system_error
344
345system_error::system_error(error_code ec, const string& what_arg)
346 : runtime_error(make_error_str(ec, what_arg)), __ec_(ec) {}
347
348system_error::system_error(error_code ec, const char* what_arg)
349 : runtime_error(make_error_str(ec, what_arg)), __ec_(ec) {}
350
351system_error::system_error(error_code ec) : runtime_error(make_error_str(ec)), __ec_(ec) {}
352
353system_error::system_error(int ev, const error_category& ecat, const string& what_arg)
354 : runtime_error(make_error_str(ec: error_code(ev, ecat), what_arg)), __ec_(error_code(ev, ecat)) {}
355
356system_error::system_error(int ev, const error_category& ecat, const char* what_arg)
357 : runtime_error(make_error_str(ec: error_code(ev, ecat), what_arg)), __ec_(error_code(ev, ecat)) {}
358
359system_error::system_error(int ev, const error_category& ecat)
360 : runtime_error(make_error_str(ec: error_code(ev, ecat))), __ec_(error_code(ev, ecat)) {}
361
362system_error::~system_error() noexcept {}
363
364void __throw_system_error(int ev, const char* what_arg) {
365#if _LIBCPP_HAS_EXCEPTIONS
366 std::__throw_system_error(ec: error_code(ev, generic_category()), what_arg: what_arg);
367#else
368 // The above could also handle the no-exception case, but for size, avoid referencing system_category() unnecessarily.
369 _LIBCPP_VERBOSE_ABORT(
370 "system_error was thrown in -fno-exceptions mode with error %i and message \"%s\"", ev, what_arg);
371#endif
372}
373
374_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
375_LIBCPP_END_NAMESPACE_STD
376