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
61namespace detail {
62
63#if defined(_LIBCPP_WIN32API)
64// Non anonymous, to allow access from two translation units.
65errc __win_err_to_errc(int err);
66#endif
67
68namespace {
69
70static _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 0) string
71format_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
94static _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2) string
95format_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
113error_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)
119error_code make_windows_error(int err) {
120  return make_error_code(__win_err_to_errc(err));
121}
122#endif
123
124template <class T>
125T error_value();
126template <>
127_LIBCPP_CONSTEXPR_SINCE_CXX14 void error_value<void>() {}
128template <>
129bool error_value<bool>() {
130  return false;
131}
132#if __SIZEOF_SIZE_T__ != __SIZEOF_LONG_LONG__
133template <>
134size_t error_value<size_t>() {
135  return size_t(-1);
136}
137#endif
138template <>
139uintmax_t error_value<uintmax_t>() {
140  return uintmax_t(-1);
141}
142template <>
143_LIBCPP_CONSTEXPR_SINCE_CXX14 file_time_type error_value<file_time_type>() {
144  return file_time_type::min();
145}
146template <>
147path error_value<path>() {
148  return {};
149}
150
151template <class T>
152struct 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)
160      : func_name_(fname), ec_(ec), p1_(p1), p2_(p2) {
161    if (ec_)
162      ec_->clear();
163  }
164
165  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)
183  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)
202  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
219  T report(errc const& err) const {
220    return report(make_error_code(err));
221  }
222
223  _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4)
224  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
241private:
242  ErrorHandler(ErrorHandler const&) = delete;
243  ErrorHandler& operator=(ErrorHandler const&) = delete;
244};
245
246using chrono::duration;
247using 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
257struct TimeSpec {
258  int64_t tv_sec;
259  int64_t tv_nsec;
260};
261struct 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
280using TimeSpec = struct timespec;
281using TimeVal = struct timeval;
282using StatT = struct stat;
283#endif
284
285template <class FileTimeT, class TimeT,
286          bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
287struct 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
311private:
312  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
322  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
335template <class FileTimeT, class TimeT>
336struct 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
349template <class FileTimeT, class TimeT>
350const typename FileTimeT::rep
351    time_util_base<FileTimeT, TimeT, true>::max_seconds =
352        duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
353
354template <class FileTimeT, class TimeT>
355const 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
360template <class FileTimeT, class TimeT>
361const typename FileTimeT::rep
362    time_util_base<FileTimeT, TimeT, true>::min_seconds =
363        duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
364
365template <class FileTimeT, class TimeT>
366const 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
373template <class FileTimeT, class TimeT, class TimeSpecT>
374struct 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
386public:
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)
461using fs_time = time_util<file_time_type, int64_t, TimeSpec>;
462#else
463using fs_time = time_util<file_time_type, time_t, TimeSpec>;
464#endif
465
466#if defined(__APPLE__)
467inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
468inline TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
469#elif defined(__MVS__)
470inline TimeSpec extract_mtime(StatT const& st) {
471  TimeSpec TS = {st.st_mtime, 0};
472  return TS;
473}
474inline TimeSpec extract_atime(StatT const& st) {
475  TimeSpec TS = {st.st_atime, 0};
476  return TS;
477}
478#elif defined(_AIX)
479inline TimeSpec extract_mtime(StatT const& st) {
480  TimeSpec TS = {st.st_mtime, st.st_mtime_n};
481  return TS;
482}
483inline TimeSpec extract_atime(StatT const& st) {
484  TimeSpec TS = {st.st_atime, st.st_atime_n};
485  return TS;
486}
487#else
488inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
489inline TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
490#endif
491
492#if !defined(_LIBCPP_WIN32API)
493inline 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
506inline 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)
517bool 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
527bool 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)
537template <class DirEntT, class = decltype(DirEntT::d_type)>
538static 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
564template <class DirEntT>
565static file_type get_file_type(DirEntT*, long) {
566  return file_type::none;
567}
568
569static 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
585static 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}
593static uintmax_t get_file_size(const WIN32_FIND_DATAW& data) {
594  return (static_cast<uint64_t>(data.nFileSizeHigh) << 32) + data.nFileSizeLow;
595}
596static 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