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_COMMON_H
10 #define FILESYSTEM_COMMON_H
11
12 #include "__config"
13 #include "filesystem"
14 #include "array"
15 #include "chrono"
16 #include "climits"
17 #include "cstdlib"
18 #include "ctime"
19
20 #if !defined(_LIBCPP_WIN32API)
21 # include <unistd.h>
22 # include <sys/stat.h>
23 # include <sys/statvfs.h>
24 # include <sys/time.h> // for ::utimes as used in __last_write_time
25 # include <fcntl.h> /* values for fchmodat */
26 #endif
27
28 #include "../include/apple_availability.h"
29
30 #if !defined(__APPLE__)
31 // We can use the presence of UTIME_OMIT to detect platforms that provide
32 // utimensat.
33 #if defined(UTIME_OMIT)
34 #define _LIBCPP_USE_UTIMENSAT
35 #endif
36 #endif
37
38 #if defined(__GNUC__) || defined(__clang__)
39 #pragma GCC diagnostic push
40 #pragma GCC diagnostic ignored "-Wunused-function"
41 #endif
42
43 #if defined(_LIBCPP_WIN32API)
44 #define PS(x) (L##x)
45 #define PATH_CSTR_FMT "\"%ls\""
46 #else
47 #define PS(x) (x)
48 #define PATH_CSTR_FMT "\"%s\""
49 #endif
50
51 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
52
53 namespace detail {
54
55 #if defined(_LIBCPP_WIN32API)
56 // Non anonymous, to allow access from two translation units.
57 errc __win_err_to_errc(int err);
58 #endif
59
60 namespace {
61
62 static _LIBCPP_FORMAT_PRINTF(1, 0) string
format_string_impl(const char * msg,va_list ap)63 format_string_impl(const char* msg, va_list ap) {
64 array<char, 256> buf;
65
66 va_list apcopy;
67 va_copy(apcopy, ap);
68 int ret = ::vsnprintf(buf.data(), buf.size(), msg, apcopy);
69 va_end(apcopy);
70
71 string result;
72 if (static_cast<size_t>(ret) < buf.size()) {
73 result.assign(buf.data(), static_cast<size_t>(ret));
74 } else {
75 // we did not provide a long enough buffer on our first attempt. The
76 // return value is the number of bytes (excluding the null byte) that are
77 // needed for formatting.
78 size_t size_with_null = static_cast<size_t>(ret) + 1;
79 result.__resize_default_init(size_with_null - 1);
80 ret = ::vsnprintf(&result[0], size_with_null, msg, ap);
81 _LIBCPP_ASSERT(static_cast<size_t>(ret) == (size_with_null - 1), "TODO");
82 }
83 return result;
84 }
85
86 static _LIBCPP_FORMAT_PRINTF(1, 2) string
format_string(const char * msg,...)87 format_string(const char* msg, ...) {
88 string ret;
89 va_list ap;
90 va_start(ap, msg);
91 #ifndef _LIBCPP_NO_EXCEPTIONS
92 try {
93 #endif // _LIBCPP_NO_EXCEPTIONS
94 ret = format_string_impl(msg, ap);
95 #ifndef _LIBCPP_NO_EXCEPTIONS
96 } catch (...) {
97 va_end(ap);
98 throw;
99 }
100 #endif // _LIBCPP_NO_EXCEPTIONS
101 va_end(ap);
102 return ret;
103 }
104
capture_errno()105 error_code capture_errno() {
106 _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
107 return error_code(errno, generic_category());
108 }
109
110 #if defined(_LIBCPP_WIN32API)
make_windows_error(int err)111 error_code make_windows_error(int err) {
112 return make_error_code(__win_err_to_errc(err));
113 }
114 #endif
115
116 template <class T>
117 T error_value();
118 template <>
119 _LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {}
120 template <>
121 bool error_value<bool>() {
122 return false;
123 }
124 #if __SIZEOF_SIZE_T__ != __SIZEOF_LONG_LONG__
125 template <>
126 size_t error_value<size_t>() {
127 return size_t(-1);
128 }
129 #endif
130 template <>
131 uintmax_t error_value<uintmax_t>() {
132 return uintmax_t(-1);
133 }
134 template <>
135 _LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() {
136 return file_time_type::min();
137 }
138 template <>
139 path error_value<path>() {
140 return {};
141 }
142
143 template <class T>
144 struct ErrorHandler {
145 const char* func_name_;
146 error_code* ec_ = nullptr;
147 const path* p1_ = nullptr;
148 const path* p2_ = nullptr;
149
150 ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
151 const path* p2 = nullptr)
func_name_ErrorHandler152 : func_name_(fname), ec_(ec), p1_(p1), p2_(p2) {
153 if (ec_)
154 ec_->clear();
155 }
156
reportErrorHandler157 T report(const error_code& ec) const {
158 if (ec_) {
159 *ec_ = ec;
160 return error_value<T>();
161 }
162 string what = string("in ") + func_name_;
163 switch (bool(p1_) + bool(p2_)) {
164 case 0:
165 __throw_filesystem_error(what, ec);
166 case 1:
167 __throw_filesystem_error(what, *p1_, ec);
168 case 2:
169 __throw_filesystem_error(what, *p1_, *p2_, ec);
170 }
171 _LIBCPP_UNREACHABLE();
172 }
173
174 _LIBCPP_FORMAT_PRINTF(3, 0)
report_implErrorHandler175 void report_impl(const error_code& ec, const char* msg, va_list ap) const {
176 if (ec_) {
177 *ec_ = ec;
178 return;
179 }
180 string what =
181 string("in ") + func_name_ + ": " + format_string_impl(msg, ap);
182 switch (bool(p1_) + bool(p2_)) {
183 case 0:
184 __throw_filesystem_error(what, ec);
185 case 1:
186 __throw_filesystem_error(what, *p1_, ec);
187 case 2:
188 __throw_filesystem_error(what, *p1_, *p2_, ec);
189 }
190 _LIBCPP_UNREACHABLE();
191 }
192
193 _LIBCPP_FORMAT_PRINTF(3, 4)
reportErrorHandler194 T report(const error_code& ec, const char* msg, ...) const {
195 va_list ap;
196 va_start(ap, msg);
197 #ifndef _LIBCPP_NO_EXCEPTIONS
198 try {
199 #endif // _LIBCPP_NO_EXCEPTIONS
200 report_impl(ec, msg, ap);
201 #ifndef _LIBCPP_NO_EXCEPTIONS
202 } catch (...) {
203 va_end(ap);
204 throw;
205 }
206 #endif // _LIBCPP_NO_EXCEPTIONS
207 va_end(ap);
208 return error_value<T>();
209 }
210
reportErrorHandler211 T report(errc const& err) const {
212 return report(make_error_code(err));
213 }
214
215 _LIBCPP_FORMAT_PRINTF(3, 4)
reportErrorHandler216 T report(errc const& err, const char* msg, ...) const {
217 va_list ap;
218 va_start(ap, msg);
219 #ifndef _LIBCPP_NO_EXCEPTIONS
220 try {
221 #endif // _LIBCPP_NO_EXCEPTIONS
222 report_impl(make_error_code(err), msg, ap);
223 #ifndef _LIBCPP_NO_EXCEPTIONS
224 } catch (...) {
225 va_end(ap);
226 throw;
227 }
228 #endif // _LIBCPP_NO_EXCEPTIONS
229 va_end(ap);
230 return error_value<T>();
231 }
232
233 private:
234 ErrorHandler(ErrorHandler const&) = delete;
235 ErrorHandler& operator=(ErrorHandler const&) = delete;
236 };
237
238 using chrono::duration;
239 using chrono::duration_cast;
240
241 #if defined(_LIBCPP_WIN32API)
242 // Various C runtime versions (UCRT, or the legacy msvcrt.dll used by
243 // some mingw toolchains) provide different stat function implementations,
244 // with a number of limitations with respect to what we want from the
245 // stat function. Instead provide our own (in the anonymous detail namespace
246 // in posix_compat.h) which does exactly what we want, along with our own
247 // stat structure and flag macros.
248
249 struct TimeSpec {
250 int64_t tv_sec;
251 int64_t tv_nsec;
252 };
253 struct StatT {
254 unsigned st_mode;
255 TimeSpec st_atim;
256 TimeSpec st_mtim;
257 uint64_t st_dev; // FILE_ID_INFO::VolumeSerialNumber
258 struct FileIdStruct {
259 unsigned char id[16]; // FILE_ID_INFO::FileId
260 bool operator==(const FileIdStruct &other) const {
261 for (int i = 0; i < 16; i++)
262 if (id[i] != other.id[i])
263 return false;
264 return true;
265 }
266 } st_ino;
267 uint32_t st_nlink;
268 uintmax_t st_size;
269 };
270
271 #else
272 using TimeSpec = struct timespec;
273 using TimeVal = struct timeval;
274 using StatT = struct stat;
275 #endif
276
277 template <class FileTimeT, class TimeT,
278 bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
279 struct time_util_base {
280 using rep = typename FileTimeT::rep;
281 using fs_duration = typename FileTimeT::duration;
282 using fs_seconds = duration<rep>;
283 using fs_nanoseconds = duration<rep, nano>;
284 using fs_microseconds = duration<rep, micro>;
285
286 static constexpr rep max_seconds =
287 duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
288
289 static constexpr rep max_nsec =
290 duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
291 fs_seconds(max_seconds))
292 .count();
293
294 static constexpr rep min_seconds =
295 duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
296
297 static constexpr rep min_nsec_timespec =
298 duration_cast<fs_nanoseconds>(
299 (FileTimeT::duration::min() - fs_seconds(min_seconds)) +
300 fs_seconds(1))
301 .count();
302
303 private:
get_min_nsecstime_util_base304 static _LIBCPP_CONSTEXPR_AFTER_CXX11 fs_duration get_min_nsecs() {
305 return duration_cast<fs_duration>(
306 fs_nanoseconds(min_nsec_timespec) -
307 duration_cast<fs_nanoseconds>(fs_seconds(1)));
308 }
309 // Static assert that these values properly round trip.
310 static_assert(fs_seconds(min_seconds) + get_min_nsecs() ==
311 FileTimeT::duration::min(),
312 "value doesn't roundtrip");
313
check_rangetime_util_base314 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool check_range() {
315 // This kinda sucks, but it's what happens when we don't have __int128_t.
316 if (sizeof(TimeT) == sizeof(rep)) {
317 typedef duration<long long, ratio<3600 * 24 * 365> > Years;
318 return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&
319 duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);
320 }
321 return max_seconds >= numeric_limits<TimeT>::max() &&
322 min_seconds <= numeric_limits<TimeT>::min();
323 }
324 static_assert(check_range(), "the representable range is unacceptable small");
325 };
326
327 template <class FileTimeT, class TimeT>
328 struct time_util_base<FileTimeT, TimeT, true> {
329 using rep = typename FileTimeT::rep;
330 using fs_duration = typename FileTimeT::duration;
331 using fs_seconds = duration<rep>;
332 using fs_nanoseconds = duration<rep, nano>;
333 using fs_microseconds = duration<rep, micro>;
334
335 static const rep max_seconds;
336 static const rep max_nsec;
337 static const rep min_seconds;
338 static const rep min_nsec_timespec;
339 };
340
341 template <class FileTimeT, class TimeT>
342 const typename FileTimeT::rep
343 time_util_base<FileTimeT, TimeT, true>::max_seconds =
344 duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
345
346 template <class FileTimeT, class TimeT>
347 const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =
348 duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
349 fs_seconds(max_seconds))
350 .count();
351
352 template <class FileTimeT, class TimeT>
353 const typename FileTimeT::rep
354 time_util_base<FileTimeT, TimeT, true>::min_seconds =
355 duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
356
357 template <class FileTimeT, class TimeT>
358 const typename FileTimeT::rep
359 time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =
360 duration_cast<fs_nanoseconds>((FileTimeT::duration::min() -
361 fs_seconds(min_seconds)) +
362 fs_seconds(1))
363 .count();
364
365 template <class FileTimeT, class TimeT, class TimeSpecT>
366 struct time_util : time_util_base<FileTimeT, TimeT> {
367 using Base = time_util_base<FileTimeT, TimeT>;
368 using Base::max_nsec;
369 using Base::max_seconds;
370 using Base::min_nsec_timespec;
371 using Base::min_seconds;
372
373 using typename Base::fs_duration;
374 using typename Base::fs_microseconds;
375 using typename Base::fs_nanoseconds;
376 using typename Base::fs_seconds;
377
378 public:
379 template <class CType, class ChronoType>
380 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out,
381 ChronoType time) {
382 using Lim = numeric_limits<CType>;
383 if (time > Lim::max() || time < Lim::min())
384 return false;
385 *out = static_cast<CType>(time);
386 return true;
387 }
388
389 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
390 if (tm.tv_sec >= 0) {
391 return tm.tv_sec < max_seconds ||
392 (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
393 } else if (tm.tv_sec == (min_seconds - 1)) {
394 return tm.tv_nsec >= min_nsec_timespec;
395 } else {
396 return tm.tv_sec >= min_seconds;
397 }
398 }
399
400 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
401 auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());
402 auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);
403 if (nsecs.count() < 0) {
404 secs = secs + fs_seconds(1);
405 nsecs = nsecs + fs_seconds(1);
406 }
407 using TLim = numeric_limits<TimeT>;
408 if (secs.count() >= 0)
409 return secs.count() <= TLim::max();
410 return secs.count() >= TLim::min();
411 }
412
413 static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
414 convert_from_timespec(TimeSpecT tm) {
415 if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {
416 return FileTimeT(fs_seconds(tm.tv_sec) +
417 duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));
418 } else { // tm.tv_sec < 0
419 auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) -
420 fs_nanoseconds(tm.tv_nsec));
421 auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;
422 return FileTimeT(Dur);
423 }
424 }
425
426 template <class SubSecT>
427 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool
428 set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {
429 auto dur = tp.time_since_epoch();
430 auto sec_dur = duration_cast<fs_seconds>(dur);
431 auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);
432 // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
433 if (subsec_dur.count() < 0) {
434 if (sec_dur.count() > min_seconds) {
435 sec_dur = sec_dur - fs_seconds(1);
436 subsec_dur = subsec_dur + fs_seconds(1);
437 } else {
438 subsec_dur = fs_nanoseconds::zero();
439 }
440 }
441 return checked_set(sec_out, sec_dur.count()) &&
442 checked_set(subsec_out, subsec_dur.count());
443 }
444 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest,
445 FileTimeT tp) {
446 if (!is_representable(tp))
447 return false;
448 return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
449 }
450 };
451
452 #if defined(_LIBCPP_WIN32API)
453 using fs_time = time_util<file_time_type, int64_t, TimeSpec>;
454 #else
455 using fs_time = time_util<file_time_type, time_t, TimeSpec>;
456 #endif
457
458 #if defined(__APPLE__)
459 inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
460 inline TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
461 #elif defined(__MVS__)
462 inline TimeSpec extract_mtime(StatT const& st) {
463 TimeSpec TS = {st.st_mtime, 0};
464 return TS;
465 }
466 inline TimeSpec extract_atime(StatT const& st) {
467 TimeSpec TS = {st.st_atime, 0};
468 return TS;
469 }
470 #elif defined(_AIX)
471 inline TimeSpec extract_mtime(StatT const& st) {
472 TimeSpec TS = {st.st_mtime, st.st_mtime_n};
473 return TS;
474 }
475 inline TimeSpec extract_atime(StatT const& st) {
476 TimeSpec TS = {st.st_atime, st.st_atime_n};
477 return TS;
478 }
479 #else
480 inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
481 inline TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
482 #endif
483
484 #if !defined(_LIBCPP_WIN32API)
485 inline TimeVal make_timeval(TimeSpec const& ts) {
486 using namespace chrono;
487 auto Convert = [](long nsec) {
488 using int_type = decltype(std::declval<TimeVal>().tv_usec);
489 auto dur = duration_cast<microseconds>(nanoseconds(nsec)).count();
490 return static_cast<int_type>(dur);
491 };
492 TimeVal TV = {};
493 TV.tv_sec = ts.tv_sec;
494 TV.tv_usec = Convert(ts.tv_nsec);
495 return TV;
496 }
497
498 inline bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS,
499 error_code& ec) {
500 TimeVal ConvertedTS[2] = {make_timeval(TS[0]), make_timeval(TS[1])};
501 if (::utimes(p.c_str(), ConvertedTS) == -1) {
502 ec = capture_errno();
503 return true;
504 }
505 return false;
506 }
507
508 #if defined(_LIBCPP_USE_UTIMENSAT)
509 bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS,
510 error_code& ec) {
511 if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1) {
512 ec = capture_errno();
513 return true;
514 }
515 return false;
516 }
517 #endif
518
519 bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
520 error_code& ec) {
521 #if !defined(_LIBCPP_USE_UTIMENSAT)
522 return posix_utimes(p, TS, ec);
523 #else
524 return posix_utimensat(p, TS, ec);
525 #endif
526 }
527 #endif /* !_LIBCPP_WIN32API */
528
529 } // namespace
530 } // end namespace detail
531
532 _LIBCPP_END_NAMESPACE_FILESYSTEM
533
534 #endif // FILESYSTEM_COMMON_H
535