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 #ifndef FILESYSTEM_ERROR_H
10 #define FILESYSTEM_ERROR_H
11 
12 #include <__assert>
13 #include <__config>
14 #include <cerrno>
15 #include <cstdarg>
16 #include <cstddef>
17 #include <cstdint>
18 #include <filesystem>
19 #include <string>
20 #include <system_error>
21 #include <utility> // __libcpp_unreachable
22 
23 #include "format_string.h"
24 
25 #if defined(_LIBCPP_WIN32API)
26 #  define WIN32_LEAN_AND_MEAN
27 #  define NOMINMAX
28 #  include <windows.h> // ERROR_* macros
29 #endif
30 
31 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
32 
33 namespace detail {
34 
35 #if defined(_LIBCPP_WIN32API)
36 
__win_err_to_errc(int err)37 inline errc __win_err_to_errc(int err) {
38   constexpr struct {
39     DWORD win;
40     errc errc;
41   } win_error_mapping[] = {
42       {ERROR_ACCESS_DENIED, errc::permission_denied},
43       {ERROR_ALREADY_EXISTS, errc::file_exists},
44       {ERROR_BAD_NETPATH, errc::no_such_file_or_directory},
45       {ERROR_BAD_PATHNAME, errc::no_such_file_or_directory},
46       {ERROR_BAD_UNIT, errc::no_such_device},
47       {ERROR_BROKEN_PIPE, errc::broken_pipe},
48       {ERROR_BUFFER_OVERFLOW, errc::filename_too_long},
49       {ERROR_BUSY, errc::device_or_resource_busy},
50       {ERROR_BUSY_DRIVE, errc::device_or_resource_busy},
51       {ERROR_CANNOT_MAKE, errc::permission_denied},
52       {ERROR_CANTOPEN, errc::io_error},
53       {ERROR_CANTREAD, errc::io_error},
54       {ERROR_CANTWRITE, errc::io_error},
55       {ERROR_CURRENT_DIRECTORY, errc::permission_denied},
56       {ERROR_DEV_NOT_EXIST, errc::no_such_device},
57       {ERROR_DEVICE_IN_USE, errc::device_or_resource_busy},
58       {ERROR_DIR_NOT_EMPTY, errc::directory_not_empty},
59       {ERROR_DIRECTORY, errc::invalid_argument},
60       {ERROR_DISK_FULL, errc::no_space_on_device},
61       {ERROR_FILE_EXISTS, errc::file_exists},
62       {ERROR_FILE_NOT_FOUND, errc::no_such_file_or_directory},
63       {ERROR_HANDLE_DISK_FULL, errc::no_space_on_device},
64       {ERROR_INVALID_ACCESS, errc::permission_denied},
65       {ERROR_INVALID_DRIVE, errc::no_such_device},
66       {ERROR_INVALID_FUNCTION, errc::function_not_supported},
67       {ERROR_INVALID_HANDLE, errc::invalid_argument},
68       {ERROR_INVALID_NAME, errc::no_such_file_or_directory},
69       {ERROR_INVALID_PARAMETER, errc::invalid_argument},
70       {ERROR_LOCK_VIOLATION, errc::no_lock_available},
71       {ERROR_LOCKED, errc::no_lock_available},
72       {ERROR_NEGATIVE_SEEK, errc::invalid_argument},
73       {ERROR_NOACCESS, errc::permission_denied},
74       {ERROR_NOT_ENOUGH_MEMORY, errc::not_enough_memory},
75       {ERROR_NOT_READY, errc::resource_unavailable_try_again},
76       {ERROR_NOT_SAME_DEVICE, errc::cross_device_link},
77       {ERROR_NOT_SUPPORTED, errc::not_supported},
78       {ERROR_OPEN_FAILED, errc::io_error},
79       {ERROR_OPEN_FILES, errc::device_or_resource_busy},
80       {ERROR_OPERATION_ABORTED, errc::operation_canceled},
81       {ERROR_OUTOFMEMORY, errc::not_enough_memory},
82       {ERROR_PATH_NOT_FOUND, errc::no_such_file_or_directory},
83       {ERROR_READ_FAULT, errc::io_error},
84       {ERROR_REPARSE_TAG_INVALID, errc::invalid_argument},
85       {ERROR_RETRY, errc::resource_unavailable_try_again},
86       {ERROR_SEEK, errc::io_error},
87       {ERROR_SHARING_VIOLATION, errc::permission_denied},
88       {ERROR_TOO_MANY_OPEN_FILES, errc::too_many_files_open},
89       {ERROR_WRITE_FAULT, errc::io_error},
90       {ERROR_WRITE_PROTECT, errc::permission_denied},
91   };
92 
93   for (const auto& pair : win_error_mapping)
94     if (pair.win == static_cast<DWORD>(err))
95       return pair.errc;
96   return errc::invalid_argument;
97 }
98 
99 #endif // _LIBCPP_WIN32API
100 
capture_errno()101 inline error_code capture_errno() {
102   _LIBCPP_ASSERT_INTERNAL(errno != 0, "Expected errno to be non-zero");
103   return error_code(errno, generic_category());
104 }
105 
106 #if defined(_LIBCPP_WIN32API)
make_windows_error(int err)107 inline error_code make_windows_error(int err) { return make_error_code(__win_err_to_errc(err)); }
108 #endif
109 
110 template <class T>
111 T error_value();
112 template <>
113 inline constexpr void error_value<void>() {}
114 template <>
115 inline bool error_value<bool>() {
116   return false;
117 }
118 #if __SIZEOF_SIZE_T__ != __SIZEOF_LONG_LONG__
119 template <>
120 inline size_t error_value<size_t>() {
121   return size_t(-1);
122 }
123 #endif
124 template <>
125 inline uintmax_t error_value<uintmax_t>() {
126   return uintmax_t(-1);
127 }
128 template <>
129 inline constexpr file_time_type error_value<file_time_type>() {
130   return file_time_type::min();
131 }
132 template <>
133 inline path error_value<path>() {
134   return {};
135 }
136 
137 template <class T>
138 struct ErrorHandler {
139   const char* func_name_;
140   error_code* ec_ = nullptr;
141   const path* p1_ = nullptr;
142   const path* p2_ = nullptr;
143 
144   ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr, const path* p2 = nullptr)
func_name_ErrorHandler145       : func_name_(fname), ec_(ec), p1_(p1), p2_(p2) {
146     if (ec_)
147       ec_->clear();
148   }
149 
reportErrorHandler150   T report(const error_code& ec) const {
151     if (ec_) {
152       *ec_ = ec;
153       return error_value<T>();
154     }
155     string what = string("in ") + func_name_;
156     switch (bool(p1_) + bool(p2_)) {
157     case 0:
158       __throw_filesystem_error(what, ec);
159     case 1:
160       __throw_filesystem_error(what, *p1_, ec);
161     case 2:
162       __throw_filesystem_error(what, *p1_, *p2_, ec);
163     }
164     __libcpp_unreachable();
165   }
166 
167   _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 0)
report_implErrorHandler168   void report_impl(const error_code& ec, const char* msg, va_list ap) const {
169     if (ec_) {
170       *ec_ = ec;
171       return;
172     }
173     string what = string("in ") + func_name_ + ": " + detail::vformat_string(msg, ap);
174     switch (bool(p1_) + bool(p2_)) {
175     case 0:
176       __throw_filesystem_error(what, ec);
177     case 1:
178       __throw_filesystem_error(what, *p1_, ec);
179     case 2:
180       __throw_filesystem_error(what, *p1_, *p2_, ec);
181     }
182     __libcpp_unreachable();
183   }
184 
185   _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4)
reportErrorHandler186   T report(const error_code& ec, const char* msg, ...) const {
187     va_list ap;
188     va_start(ap, msg);
189 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
190     try {
191 #endif // _LIBCPP_HAS_NO_EXCEPTIONS
192       report_impl(ec, msg, ap);
193 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
194     } catch (...) {
195       va_end(ap);
196       throw;
197     }
198 #endif // _LIBCPP_HAS_NO_EXCEPTIONS
199     va_end(ap);
200     return error_value<T>();
201   }
202 
reportErrorHandler203   T report(errc const& err) const { return report(make_error_code(err)); }
204 
205   _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4)
reportErrorHandler206   T report(errc const& err, const char* msg, ...) const {
207     va_list ap;
208     va_start(ap, msg);
209 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
210     try {
211 #endif // _LIBCPP_HAS_NO_EXCEPTIONS
212       report_impl(make_error_code(err), msg, ap);
213 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
214     } catch (...) {
215       va_end(ap);
216       throw;
217     }
218 #endif // _LIBCPP_HAS_NO_EXCEPTIONS
219     va_end(ap);
220     return error_value<T>();
221   }
222 
223 private:
224   ErrorHandler(ErrorHandler const&)            = delete;
225   ErrorHandler& operator=(ErrorHandler const&) = delete;
226 };
227 
228 } // end namespace detail
229 
230 _LIBCPP_END_NAMESPACE_FILESYSTEM
231 
232 #endif // FILESYSTEM_ERROR_H
233