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