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