Home | History | Annotate | Download | only in filesystem
      1 //===----------------------------------------------------------------------===////
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is dual licensed under the MIT and the University of Illinois Open
      6 // Source Licenses. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===////
      9 
     10 #ifndef FILESYSTEM_COMMON_H
     11 #define FILESYSTEM_COMMON_H
     12 
     13 #include "__config"
     14 #include "filesystem"
     15 #include "array"
     16 #include "chrono"
     17 #include "cstdlib"
     18 #include "climits"
     19 
     20 #include <unistd.h>
     21 #include <sys/stat.h>
     22 #include <sys/statvfs.h>
     23 #include <sys/time.h> // for ::utimes as used in __last_write_time
     24 #include <fcntl.h>    /* values for fchmodat */
     25 
     26 #include "../include/apple_availability.h"
     27 
     28 #if !defined(__APPLE__)
     29 // We can use the presence of UTIME_OMIT to detect platforms that provide
     30 // utimensat.
     31 #if defined(UTIME_OMIT)
     32 #define _LIBCPP_USE_UTIMENSAT
     33 #endif
     34 #endif
     35 
     36 #if defined(__GNUC__)
     37 #pragma GCC diagnostic push
     38 #pragma GCC diagnostic ignored "-Wunused-function"
     39 #endif
     40 
     41 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
     42 
     43 namespace detail {
     44 namespace {
     45 
     46 static string format_string_imp(const char* msg, ...) {
     47   // we might need a second shot at this, so pre-emptivly make a copy
     48   struct GuardVAList {
     49     va_list& target;
     50     bool active = true;
     51     GuardVAList(va_list& target) : target(target), active(true) {}
     52     void clear() {
     53       if (active)
     54         va_end(target);
     55       active = false;
     56     }
     57     ~GuardVAList() {
     58       if (active)
     59         va_end(target);
     60     }
     61   };
     62   va_list args;
     63   va_start(args, msg);
     64   GuardVAList args_guard(args);
     65 
     66   va_list args_cp;
     67   va_copy(args_cp, args);
     68   GuardVAList args_copy_guard(args_cp);
     69 
     70   std::string result;
     71 
     72   array<char, 256> local_buff;
     73   size_t size_with_null = local_buff.size();
     74   auto ret = ::vsnprintf(local_buff.data(), size_with_null, msg, args_cp);
     75 
     76   args_copy_guard.clear();
     77 
     78   // handle empty expansion
     79   if (ret == 0)
     80     return result;
     81   if (static_cast<size_t>(ret) < size_with_null) {
     82     result.assign(local_buff.data(), static_cast<size_t>(ret));
     83     return result;
     84   }
     85 
     86   // we did not provide a long enough buffer on our first attempt. The
     87   // return value is the number of bytes (excluding the null byte) that are
     88   // needed for formatting.
     89   size_with_null = static_cast<size_t>(ret) + 1;
     90   result.__resize_default_init(size_with_null - 1);
     91   ret = ::vsnprintf(&result[0], size_with_null, msg, args);
     92   _LIBCPP_ASSERT(static_cast<size_t>(ret) == (size_with_null - 1), "TODO");
     93 
     94   return result;
     95 }
     96 
     97 const char* unwrap(string const& s) { return s.c_str(); }
     98 const char* unwrap(path const& p) { return p.native().c_str(); }
     99 template <class Arg>
    100 Arg const& unwrap(Arg const& a) {
    101   static_assert(!is_class<Arg>::value, "cannot pass class here");
    102   return a;
    103 }
    104 
    105 template <class... Args>
    106 string format_string(const char* fmt, Args const&... args) {
    107   return format_string_imp(fmt, unwrap(args)...);
    108 }
    109 
    110 error_code capture_errno() {
    111   _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
    112   return error_code(errno, generic_category());
    113 }
    114 
    115 template <class T>
    116 T error_value();
    117 template <>
    118 _LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {}
    119 template <>
    120 bool error_value<bool>() {
    121   return false;
    122 }
    123 template <>
    124 uintmax_t error_value<uintmax_t>() {
    125   return uintmax_t(-1);
    126 }
    127 template <>
    128 _LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() {
    129   return file_time_type::min();
    130 }
    131 template <>
    132 path error_value<path>() {
    133   return {};
    134 }
    135 
    136 template <class T>
    137 struct ErrorHandler {
    138   const char* func_name;
    139   error_code* ec = nullptr;
    140   const path* p1 = nullptr;
    141   const path* p2 = nullptr;
    142 
    143   ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
    144                const path* p2 = nullptr)
    145       : func_name(fname), ec(ec), p1(p1), p2(p2) {
    146     if (ec)
    147       ec->clear();
    148   }
    149 
    150   T report(const error_code& m_ec) const {
    151     if (ec) {
    152       *ec = m_ec;
    153       return error_value<T>();
    154     }
    155     string what = string("in ") + func_name;
    156     switch (bool(p1) + bool(p2)) {
    157     case 0:
    158       __throw_filesystem_error(what, m_ec);
    159     case 1:
    160       __throw_filesystem_error(what, *p1, m_ec);
    161     case 2:
    162       __throw_filesystem_error(what, *p1, *p2, m_ec);
    163     }
    164     _LIBCPP_UNREACHABLE();
    165   }
    166 
    167   template <class... Args>
    168   T report(const error_code& m_ec, const char* msg, Args const&... args) const {
    169     if (ec) {
    170       *ec = m_ec;
    171       return error_value<T>();
    172     }
    173     string what =
    174         string("in ") + func_name + ": " + format_string(msg, args...);
    175     switch (bool(p1) + bool(p2)) {
    176     case 0:
    177       __throw_filesystem_error(what, m_ec);
    178     case 1:
    179       __throw_filesystem_error(what, *p1, m_ec);
    180     case 2:
    181       __throw_filesystem_error(what, *p1, *p2, m_ec);
    182     }
    183     _LIBCPP_UNREACHABLE();
    184   }
    185 
    186   T report(errc const& err) const { return report(make_error_code(err)); }
    187 
    188   template <class... Args>
    189   T report(errc const& err, const char* msg, Args const&... args) const {
    190     return report(make_error_code(err), msg, args...);
    191   }
    192 
    193 private:
    194   ErrorHandler(ErrorHandler const&) = delete;
    195   ErrorHandler& operator=(ErrorHandler const&) = delete;
    196 };
    197 
    198 using chrono::duration;
    199 using chrono::duration_cast;
    200 
    201 using TimeSpec = struct ::timespec;
    202 using StatT = struct ::stat;
    203 
    204 template <class FileTimeT, class TimeT,
    205           bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
    206 struct time_util_base {
    207   using rep = typename FileTimeT::rep;
    208   using fs_duration = typename FileTimeT::duration;
    209   using fs_seconds = duration<rep>;
    210   using fs_nanoseconds = duration<rep, nano>;
    211   using fs_microseconds = duration<rep, micro>;
    212 
    213   static constexpr rep max_seconds =
    214       duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
    215 
    216   static constexpr rep max_nsec =
    217       duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
    218                                     fs_seconds(max_seconds))
    219           .count();
    220 
    221   static constexpr rep min_seconds =
    222       duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
    223 
    224   static constexpr rep min_nsec_timespec =
    225       duration_cast<fs_nanoseconds>(
    226           (FileTimeT::duration::min() - fs_seconds(min_seconds)) +
    227           fs_seconds(1))
    228           .count();
    229 
    230 private:
    231 #if _LIBCPP_STD_VER > 11 && !defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
    232   static constexpr fs_duration get_min_nsecs() {
    233     return duration_cast<fs_duration>(
    234         fs_nanoseconds(min_nsec_timespec) -
    235         duration_cast<fs_nanoseconds>(fs_seconds(1)));
    236   }
    237   // Static assert that these values properly round trip.
    238   static_assert(fs_seconds(min_seconds) + get_min_nsecs() ==
    239                     FileTimeT::duration::min(),
    240                 "value doesn't roundtrip");
    241 
    242   static constexpr bool check_range() {
    243     // This kinda sucks, but it's what happens when we don't have __int128_t.
    244     if (sizeof(TimeT) == sizeof(rep)) {
    245       typedef duration<long long, ratio<3600 * 24 * 365> > Years;
    246       return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&
    247              duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);
    248     }
    249     return max_seconds >= numeric_limits<TimeT>::max() &&
    250            min_seconds <= numeric_limits<TimeT>::min();
    251   }
    252   static_assert(check_range(), "the representable range is unacceptable small");
    253 #endif
    254 };
    255 
    256 template <class FileTimeT, class TimeT>
    257 struct time_util_base<FileTimeT, TimeT, true> {
    258   using rep = typename FileTimeT::rep;
    259   using fs_duration = typename FileTimeT::duration;
    260   using fs_seconds = duration<rep>;
    261   using fs_nanoseconds = duration<rep, nano>;
    262   using fs_microseconds = duration<rep, micro>;
    263 
    264   static const rep max_seconds;
    265   static const rep max_nsec;
    266   static const rep min_seconds;
    267   static const rep min_nsec_timespec;
    268 };
    269 
    270 template <class FileTimeT, class TimeT>
    271 const typename FileTimeT::rep
    272     time_util_base<FileTimeT, TimeT, true>::max_seconds =
    273         duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
    274 
    275 template <class FileTimeT, class TimeT>
    276 const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =
    277     duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
    278                                   fs_seconds(max_seconds))
    279         .count();
    280 
    281 template <class FileTimeT, class TimeT>
    282 const typename FileTimeT::rep
    283     time_util_base<FileTimeT, TimeT, true>::min_seconds =
    284         duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
    285 
    286 template <class FileTimeT, class TimeT>
    287 const typename FileTimeT::rep
    288     time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =
    289         duration_cast<fs_nanoseconds>((FileTimeT::duration::min() -
    290                                        fs_seconds(min_seconds)) +
    291                                       fs_seconds(1))
    292             .count();
    293 
    294 template <class FileTimeT, class TimeT, class TimeSpecT>
    295 struct time_util : time_util_base<FileTimeT, TimeT> {
    296   using Base = time_util_base<FileTimeT, TimeT>;
    297   using Base::max_nsec;
    298   using Base::max_seconds;
    299   using Base::min_nsec_timespec;
    300   using Base::min_seconds;
    301 
    302   using typename Base::fs_duration;
    303   using typename Base::fs_microseconds;
    304   using typename Base::fs_nanoseconds;
    305   using typename Base::fs_seconds;
    306 
    307 public:
    308   template <class CType, class ChronoType>
    309   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out,
    310                                                         ChronoType time) {
    311     using Lim = numeric_limits<CType>;
    312     if (time > Lim::max() || time < Lim::min())
    313       return false;
    314     *out = static_cast<CType>(time);
    315     return true;
    316   }
    317 
    318   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
    319     if (tm.tv_sec >= 0) {
    320       return tm.tv_sec < max_seconds ||
    321              (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
    322     } else if (tm.tv_sec == (min_seconds - 1)) {
    323       return tm.tv_nsec >= min_nsec_timespec;
    324     } else {
    325       return tm.tv_sec >= min_seconds;
    326     }
    327   }
    328 
    329   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
    330     auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());
    331     auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);
    332     if (nsecs.count() < 0) {
    333       secs = secs + fs_seconds(1);
    334       nsecs = nsecs + fs_seconds(1);
    335     }
    336     using TLim = numeric_limits<TimeT>;
    337     if (secs.count() >= 0)
    338       return secs.count() <= TLim::max();
    339     return secs.count() >= TLim::min();
    340   }
    341 
    342   static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
    343   convert_from_timespec(TimeSpecT tm) {
    344     if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {
    345       return FileTimeT(fs_seconds(tm.tv_sec) +
    346                        duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));
    347     } else { // tm.tv_sec < 0
    348       auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) -
    349                                                    fs_nanoseconds(tm.tv_nsec));
    350       auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;
    351       return FileTimeT(Dur);
    352     }
    353   }
    354 
    355   template <class SubSecT>
    356   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool
    357   set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {
    358     auto dur = tp.time_since_epoch();
    359     auto sec_dur = duration_cast<fs_seconds>(dur);
    360     auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);
    361     // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
    362     if (subsec_dur.count() < 0) {
    363       if (sec_dur.count() > min_seconds) {
    364         sec_dur = sec_dur - fs_seconds(1);
    365         subsec_dur = subsec_dur + fs_seconds(1);
    366       } else {
    367         subsec_dur = fs_nanoseconds::zero();
    368       }
    369     }
    370     return checked_set(sec_out, sec_dur.count()) &&
    371            checked_set(subsec_out, subsec_dur.count());
    372   }
    373   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest,
    374                                                                 FileTimeT tp) {
    375     if (!is_representable(tp))
    376       return false;
    377     return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
    378   }
    379 };
    380 
    381 using fs_time = time_util<file_time_type, time_t, TimeSpec>;
    382 
    383 #if defined(__APPLE__)
    384 TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
    385 TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
    386 #else
    387 TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
    388 TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
    389 #endif
    390 
    391 // allow the utimes implementation to compile even it we're not going
    392 // to use it.
    393 
    394 bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS,
    395                   error_code& ec) {
    396   using namespace chrono;
    397   auto Convert = [](long nsec) {
    398     using int_type = decltype(std::declval< ::timeval>().tv_usec);
    399     auto dur = duration_cast<microseconds>(nanoseconds(nsec)).count();
    400     return static_cast<int_type>(dur);
    401   };
    402   struct ::timeval ConvertedTS[2] = {{TS[0].tv_sec, Convert(TS[0].tv_nsec)},
    403                                      {TS[1].tv_sec, Convert(TS[1].tv_nsec)}};
    404   if (::utimes(p.c_str(), ConvertedTS) == -1) {
    405     ec = capture_errno();
    406     return true;
    407   }
    408   return false;
    409 }
    410 
    411 #if defined(_LIBCPP_USE_UTIMENSAT)
    412 bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS,
    413                      error_code& ec) {
    414   if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1) {
    415     ec = capture_errno();
    416     return true;
    417   }
    418   return false;
    419 }
    420 #endif
    421 
    422 bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
    423                     error_code& ec) {
    424 #if !defined(_LIBCPP_USE_UTIMENSAT)
    425   return posix_utimes(p, TS, ec);
    426 #else
    427   return posix_utimensat(p, TS, ec);
    428 #endif
    429 }
    430 
    431 } // namespace
    432 } // end namespace detail
    433 
    434 _LIBCPP_END_NAMESPACE_FILESYSTEM
    435 
    436 #endif // FILESYSTEM_COMMON_H
    437