Home | History | Annotate | Line # | Download | only in filesystem
      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
     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
     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 
    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)
    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)
    152       : func_name_(fname), ec_(ec), p1_(p1), p2_(p2) {
    153     if (ec_)
    154       ec_->clear();
    155   }
    156 
    157   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)
    175   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)
    194   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 
    211   T report(errc const& err) const {
    212     return report(make_error_code(err));
    213   }
    214 
    215   _LIBCPP_FORMAT_PRINTF(3, 4)
    216   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:
    304   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 
    314   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