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__)
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 #else
46 #define PS(x) (x)
47 #endif
48 
49 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
50 
51 namespace detail {
52 
53 #if defined(_LIBCPP_WIN32API)
54 // Non anonymous, to allow access from two translation units.
55 errc __win_err_to_errc(int err);
56 #endif
57 
58 namespace {
59 
format_string_imp(const char * msg,...)60 static string format_string_imp(const char* msg, ...) {
61   // we might need a second shot at this, so pre-emptivly make a copy
62   struct GuardVAList {
63     va_list& target;
64     bool active = true;
65     GuardVAList(va_list& tgt) : target(tgt), active(true) {}
66     void clear() {
67       if (active)
68         va_end(target);
69       active = false;
70     }
71     ~GuardVAList() {
72       if (active)
73         va_end(target);
74     }
75   };
76   va_list args;
77   va_start(args, msg);
78   GuardVAList args_guard(args);
79 
80   va_list args_cp;
81   va_copy(args_cp, args);
82   GuardVAList args_copy_guard(args_cp);
83 
84   std::string result;
85 
86   array<char, 256> local_buff;
87   size_t size_with_null = local_buff.size();
88   auto ret = ::vsnprintf(local_buff.data(), size_with_null, msg, args_cp);
89 
90   args_copy_guard.clear();
91 
92   // handle empty expansion
93   if (ret == 0)
94     return result;
95   if (static_cast<size_t>(ret) < size_with_null) {
96     result.assign(local_buff.data(), static_cast<size_t>(ret));
97     return result;
98   }
99 
100   // we did not provide a long enough buffer on our first attempt. The
101   // return value is the number of bytes (excluding the null byte) that are
102   // needed for formatting.
103   size_with_null = static_cast<size_t>(ret) + 1;
104   result.__resize_default_init(size_with_null - 1);
105   ret = ::vsnprintf(&result[0], size_with_null, msg, args);
106   _LIBCPP_ASSERT(static_cast<size_t>(ret) == (size_with_null - 1), "TODO");
107 
108   return result;
109 }
110 
unwrap(path::string_type const & s)111 const path::value_type* unwrap(path::string_type const& s) { return s.c_str(); }
unwrap(path const & p)112 const path::value_type* unwrap(path const& p) { return p.native().c_str(); }
113 template <class Arg>
unwrap(Arg const & a)114 Arg const& unwrap(Arg const& a) {
115   static_assert(!is_class<Arg>::value, "cannot pass class here");
116   return a;
117 }
118 
119 template <class... Args>
format_string(const char * fmt,Args const &...args)120 string format_string(const char* fmt, Args const&... args) {
121   return format_string_imp(fmt, unwrap(args)...);
122 }
123 
capture_errno()124 error_code capture_errno() {
125   _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
126   return error_code(errno, generic_category());
127 }
128 
129 #if defined(_LIBCPP_WIN32API)
make_windows_error(int err)130 error_code make_windows_error(int err) {
131   return make_error_code(__win_err_to_errc(err));
132 }
133 #endif
134 
135 template <class T>
136 T error_value();
137 template <>
138 _LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {}
139 template <>
140 bool error_value<bool>() {
141   return false;
142 }
143 #if __SIZEOF_SIZE_T__ != __SIZEOF_LONG_LONG__
144 template <>
145 size_t error_value<size_t>() {
146   return size_t(-1);
147 }
148 #endif
149 template <>
150 uintmax_t error_value<uintmax_t>() {
151   return uintmax_t(-1);
152 }
153 template <>
154 _LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() {
155   return file_time_type::min();
156 }
157 template <>
158 path error_value<path>() {
159   return {};
160 }
161 
162 template <class T>
163 struct ErrorHandler {
164   const char* func_name_;
165   error_code* ec_ = nullptr;
166   const path* p1_ = nullptr;
167   const path* p2_ = nullptr;
168 
169   ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
170                const path* p2 = nullptr)
func_name_ErrorHandler171       : func_name_(fname), ec_(ec), p1_(p1), p2_(p2) {
172     if (ec_)
173       ec_->clear();
174   }
175 
reportErrorHandler176   T report(const error_code& ec) const {
177     if (ec_) {
178       *ec_ = ec;
179       return error_value<T>();
180     }
181     string what = string("in ") + func_name_;
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   template <class... Args>
reportErrorHandler194   T report(const error_code& ec, const char* msg, Args const&... args) const {
195     if (ec_) {
196       *ec_ = ec;
197       return error_value<T>();
198     }
199     string what =
200         string("in ") + func_name_ + ": " + format_string(msg, args...);
201     switch (bool(p1_) + bool(p2_)) {
202     case 0:
203       __throw_filesystem_error(what, ec);
204     case 1:
205       __throw_filesystem_error(what, *p1_, ec);
206     case 2:
207       __throw_filesystem_error(what, *p1_, *p2_, ec);
208     }
209     _LIBCPP_UNREACHABLE();
210   }
211 
reportErrorHandler212   T report(errc const& err) const { return report(make_error_code(err)); }
213 
214   template <class... Args>
reportErrorHandler215   T report(errc const& err, const char* msg, Args const&... args) const {
216     return report(make_error_code(err), msg, args...);
217   }
218 
219 private:
220   ErrorHandler(ErrorHandler const&) = delete;
221   ErrorHandler& operator=(ErrorHandler const&) = delete;
222 };
223 
224 using chrono::duration;
225 using chrono::duration_cast;
226 
227 using TimeSpec = struct timespec;
228 using TimeVal = struct timeval;
229 using StatT = struct stat;
230 
231 template <class FileTimeT, class TimeT,
232           bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
233 struct time_util_base {
234   using rep = typename FileTimeT::rep;
235   using fs_duration = typename FileTimeT::duration;
236   using fs_seconds = duration<rep>;
237   using fs_nanoseconds = duration<rep, nano>;
238   using fs_microseconds = duration<rep, micro>;
239 
240   static constexpr rep max_seconds =
241       duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
242 
243   static constexpr rep max_nsec =
244       duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
245                                     fs_seconds(max_seconds))
246           .count();
247 
248   static constexpr rep min_seconds =
249       duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
250 
251   static constexpr rep min_nsec_timespec =
252       duration_cast<fs_nanoseconds>(
253           (FileTimeT::duration::min() - fs_seconds(min_seconds)) +
254           fs_seconds(1))
255           .count();
256 
257 private:
258 #if _LIBCPP_STD_VER > 11 && !defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
get_min_nsecstime_util_base259   static constexpr fs_duration get_min_nsecs() {
260     return duration_cast<fs_duration>(
261         fs_nanoseconds(min_nsec_timespec) -
262         duration_cast<fs_nanoseconds>(fs_seconds(1)));
263   }
264   // Static assert that these values properly round trip.
265   static_assert(fs_seconds(min_seconds) + get_min_nsecs() ==
266                     FileTimeT::duration::min(),
267                 "value doesn't roundtrip");
268 
check_rangetime_util_base269   static constexpr bool check_range() {
270     // This kinda sucks, but it's what happens when we don't have __int128_t.
271     if (sizeof(TimeT) == sizeof(rep)) {
272       typedef duration<long long, ratio<3600 * 24 * 365> > Years;
273       return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&
274              duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);
275     }
276     return max_seconds >= numeric_limits<TimeT>::max() &&
277            min_seconds <= numeric_limits<TimeT>::min();
278   }
279   static_assert(check_range(), "the representable range is unacceptable small");
280 #endif
281 };
282 
283 template <class FileTimeT, class TimeT>
284 struct time_util_base<FileTimeT, TimeT, true> {
285   using rep = typename FileTimeT::rep;
286   using fs_duration = typename FileTimeT::duration;
287   using fs_seconds = duration<rep>;
288   using fs_nanoseconds = duration<rep, nano>;
289   using fs_microseconds = duration<rep, micro>;
290 
291   static const rep max_seconds;
292   static const rep max_nsec;
293   static const rep min_seconds;
294   static const rep min_nsec_timespec;
295 };
296 
297 template <class FileTimeT, class TimeT>
298 const typename FileTimeT::rep
299     time_util_base<FileTimeT, TimeT, true>::max_seconds =
300         duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
301 
302 template <class FileTimeT, class TimeT>
303 const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =
304     duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
305                                   fs_seconds(max_seconds))
306         .count();
307 
308 template <class FileTimeT, class TimeT>
309 const typename FileTimeT::rep
310     time_util_base<FileTimeT, TimeT, true>::min_seconds =
311         duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
312 
313 template <class FileTimeT, class TimeT>
314 const typename FileTimeT::rep
315     time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =
316         duration_cast<fs_nanoseconds>((FileTimeT::duration::min() -
317                                        fs_seconds(min_seconds)) +
318                                       fs_seconds(1))
319             .count();
320 
321 template <class FileTimeT, class TimeT, class TimeSpecT>
322 struct time_util : time_util_base<FileTimeT, TimeT> {
323   using Base = time_util_base<FileTimeT, TimeT>;
324   using Base::max_nsec;
325   using Base::max_seconds;
326   using Base::min_nsec_timespec;
327   using Base::min_seconds;
328 
329   using typename Base::fs_duration;
330   using typename Base::fs_microseconds;
331   using typename Base::fs_nanoseconds;
332   using typename Base::fs_seconds;
333 
334 public:
335   template <class CType, class ChronoType>
336   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out,
337                                                         ChronoType time) {
338     using Lim = numeric_limits<CType>;
339     if (time > Lim::max() || time < Lim::min())
340       return false;
341     *out = static_cast<CType>(time);
342     return true;
343   }
344 
345   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
346     if (tm.tv_sec >= 0) {
347       return tm.tv_sec < max_seconds ||
348              (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
349     } else if (tm.tv_sec == (min_seconds - 1)) {
350       return tm.tv_nsec >= min_nsec_timespec;
351     } else {
352       return tm.tv_sec >= min_seconds;
353     }
354   }
355 
356   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
357     auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());
358     auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);
359     if (nsecs.count() < 0) {
360       secs = secs + fs_seconds(1);
361       nsecs = nsecs + fs_seconds(1);
362     }
363     using TLim = numeric_limits<TimeT>;
364     if (secs.count() >= 0)
365       return secs.count() <= TLim::max();
366     return secs.count() >= TLim::min();
367   }
368 
369   static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
370   convert_from_timespec(TimeSpecT tm) {
371     if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {
372       return FileTimeT(fs_seconds(tm.tv_sec) +
373                        duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));
374     } else { // tm.tv_sec < 0
375       auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) -
376                                                    fs_nanoseconds(tm.tv_nsec));
377       auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;
378       return FileTimeT(Dur);
379     }
380   }
381 
382   template <class SubSecT>
383   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool
384   set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {
385     auto dur = tp.time_since_epoch();
386     auto sec_dur = duration_cast<fs_seconds>(dur);
387     auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);
388     // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
389     if (subsec_dur.count() < 0) {
390       if (sec_dur.count() > min_seconds) {
391         sec_dur = sec_dur - fs_seconds(1);
392         subsec_dur = subsec_dur + fs_seconds(1);
393       } else {
394         subsec_dur = fs_nanoseconds::zero();
395       }
396     }
397     return checked_set(sec_out, sec_dur.count()) &&
398            checked_set(subsec_out, subsec_dur.count());
399   }
400   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest,
401                                                                 FileTimeT tp) {
402     if (!is_representable(tp))
403       return false;
404     return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
405   }
406 };
407 
408 using fs_time = time_util<file_time_type, time_t, TimeSpec>;
409 
410 #if defined(__APPLE__)
411 inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
412 inline TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
413 #elif defined(__MVS__)
414 inline TimeSpec extract_mtime(StatT const& st) {
415   TimeSpec TS = {st.st_mtime, 0};
416   return TS;
417 }
418 inline TimeSpec extract_atime(StatT const& st) {
419   TimeSpec TS = {st.st_atime, 0};
420   return TS;
421 }
422 #else
423 inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
424 inline TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
425 #endif
426 
427 inline TimeVal make_timeval(TimeSpec const& ts) {
428   using namespace chrono;
429   auto Convert = [](long nsec) {
430     using int_type = decltype(std::declval<TimeVal>().tv_usec);
431     auto dur = duration_cast<microseconds>(nanoseconds(nsec)).count();
432     return static_cast<int_type>(dur);
433   };
434   TimeVal TV = {};
435   TV.tv_sec = ts.tv_sec;
436   TV.tv_usec = Convert(ts.tv_nsec);
437   return TV;
438 }
439 
440 inline bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS,
441                   error_code& ec) {
442   TimeVal ConvertedTS[2] = {make_timeval(TS[0]), make_timeval(TS[1])};
443   if (::utimes(p.c_str(), ConvertedTS) == -1) {
444     ec = capture_errno();
445     return true;
446   }
447   return false;
448 }
449 
450 #if defined(_LIBCPP_USE_UTIMENSAT)
451 bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS,
452                      error_code& ec) {
453   if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1) {
454     ec = capture_errno();
455     return true;
456   }
457   return false;
458 }
459 #endif
460 
461 bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
462                     error_code& ec) {
463 #if !defined(_LIBCPP_USE_UTIMENSAT)
464   return posix_utimes(p, TS, ec);
465 #else
466   return posix_utimensat(p, TS, ec);
467 #endif
468 }
469 
470 } // namespace
471 } // end namespace detail
472 
473 _LIBCPP_END_NAMESPACE_FILESYSTEM
474 
475 #endif // FILESYSTEM_COMMON_H
476