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