• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
format_string_imp(const char * msg,...)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 
unwrap(string const & s)97 const char* unwrap(string const& s) { return s.c_str(); }
unwrap(path const & p)98 const char* unwrap(path const& p) { return p.native().c_str(); }
99 template <class Arg>
unwrap(Arg const & a)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>
format_string(const char * fmt,Args const &...args)106 string format_string(const char* fmt, Args const&... args) {
107   return format_string_imp(fmt, unwrap(args)...);
108 }
109 
capture_errno()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)
func_nameErrorHandler145       : func_name(fname), ec(ec), p1(p1), p2(p2) {
146     if (ec)
147       ec->clear();
148   }
149 
reportErrorHandler150   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>
reportErrorHandler168   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 
reportErrorHandler186   T report(errc const& err) const { return report(make_error_code(err)); }
187 
188   template <class... Args>
reportErrorHandler189   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)
get_min_nsecstime_util_base232   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 
check_rangetime_util_base242   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