]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libc++/src/filesystem/filesystem_common.h
Merge clang 7.0.1 and several follow-up changes
[FreeBSD/FreeBSD.git] / contrib / libc++ / src / filesystem / filesystem_common.h
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   array<char, 256> local_buff;
71   size_t size = local_buff.size();
72   auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp);
73
74   args_copy_guard.clear();
75
76   // handle empty expansion
77   if (ret == 0)
78     return string{};
79   if (static_cast<size_t>(ret) < size)
80     return string(local_buff.data());
81
82   // we did not provide a long enough buffer on our first attempt.
83   // add 1 to size to account for null-byte in size cast to prevent overflow
84   size = static_cast<size_t>(ret) + 1;
85   auto buff_ptr = unique_ptr<char[]>(new char[size]);
86   ret = ::vsnprintf(buff_ptr.get(), size, msg, args);
87   return string(buff_ptr.get());
88 }
89
90 const char* unwrap(string const& s) { return s.c_str(); }
91 const char* unwrap(path const& p) { return p.native().c_str(); }
92 template <class Arg>
93 Arg const& unwrap(Arg const& a) {
94   static_assert(!is_class<Arg>::value, "cannot pass class here");
95   return a;
96 }
97
98 template <class... Args>
99 string format_string(const char* fmt, Args const&... args) {
100   return format_string_imp(fmt, unwrap(args)...);
101 }
102
103 error_code capture_errno() {
104   _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
105   return error_code(errno, generic_category());
106 }
107
108 template <class T>
109 T error_value();
110 template <>
111 _LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {}
112 template <>
113 bool error_value<bool>() {
114   return false;
115 }
116 template <>
117 uintmax_t error_value<uintmax_t>() {
118   return uintmax_t(-1);
119 }
120 template <>
121 _LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() {
122   return file_time_type::min();
123 }
124 template <>
125 path error_value<path>() {
126   return {};
127 }
128
129 template <class T>
130 struct ErrorHandler {
131   const char* func_name;
132   error_code* ec = nullptr;
133   const path* p1 = nullptr;
134   const path* p2 = nullptr;
135
136   ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
137                const path* p2 = nullptr)
138       : func_name(fname), ec(ec), p1(p1), p2(p2) {
139     if (ec)
140       ec->clear();
141   }
142
143   T report(const error_code& m_ec) const {
144     if (ec) {
145       *ec = m_ec;
146       return error_value<T>();
147     }
148     string what = string("in ") + func_name;
149     switch (bool(p1) + bool(p2)) {
150     case 0:
151       __throw_filesystem_error(what, m_ec);
152     case 1:
153       __throw_filesystem_error(what, *p1, m_ec);
154     case 2:
155       __throw_filesystem_error(what, *p1, *p2, m_ec);
156     }
157     _LIBCPP_UNREACHABLE();
158   }
159
160   template <class... Args>
161   T report(const error_code& m_ec, const char* msg, Args const&... args) const {
162     if (ec) {
163       *ec = m_ec;
164       return error_value<T>();
165     }
166     string what =
167         string("in ") + func_name + ": " + format_string(msg, args...);
168     switch (bool(p1) + bool(p2)) {
169     case 0:
170       __throw_filesystem_error(what, m_ec);
171     case 1:
172       __throw_filesystem_error(what, *p1, m_ec);
173     case 2:
174       __throw_filesystem_error(what, *p1, *p2, m_ec);
175     }
176     _LIBCPP_UNREACHABLE();
177   }
178
179   T report(errc const& err) const { return report(make_error_code(err)); }
180
181   template <class... Args>
182   T report(errc const& err, const char* msg, Args const&... args) const {
183     return report(make_error_code(err), msg, args...);
184   }
185
186 private:
187   ErrorHandler(ErrorHandler const&) = delete;
188   ErrorHandler& operator=(ErrorHandler const&) = delete;
189 };
190
191 using chrono::duration;
192 using chrono::duration_cast;
193
194 using TimeSpec = struct ::timespec;
195 using StatT = struct ::stat;
196
197 template <class FileTimeT, class TimeT,
198           bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
199 struct time_util_base {
200   using rep = typename FileTimeT::rep;
201   using fs_duration = typename FileTimeT::duration;
202   using fs_seconds = duration<rep>;
203   using fs_nanoseconds = duration<rep, nano>;
204   using fs_microseconds = duration<rep, micro>;
205
206   static constexpr rep max_seconds =
207       duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
208
209   static constexpr rep max_nsec =
210       duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
211                                     fs_seconds(max_seconds))
212           .count();
213
214   static constexpr rep min_seconds =
215       duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
216
217   static constexpr rep min_nsec_timespec =
218       duration_cast<fs_nanoseconds>(
219           (FileTimeT::duration::min() - fs_seconds(min_seconds)) +
220           fs_seconds(1))
221           .count();
222
223 private:
224 #if _LIBCPP_STD_VER > 11 && !defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
225   static constexpr fs_duration get_min_nsecs() {
226     return duration_cast<fs_duration>(
227         fs_nanoseconds(min_nsec_timespec) -
228         duration_cast<fs_nanoseconds>(fs_seconds(1)));
229   }
230   // Static assert that these values properly round trip.
231   static_assert(fs_seconds(min_seconds) + get_min_nsecs() ==
232                     FileTimeT::duration::min(),
233                 "value doesn't roundtrip");
234
235   static constexpr bool check_range() {
236     // This kinda sucks, but it's what happens when we don't have __int128_t.
237     if (sizeof(TimeT) == sizeof(rep)) {
238       typedef duration<long long, ratio<3600 * 24 * 365> > Years;
239       return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&
240              duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);
241     }
242     return max_seconds >= numeric_limits<TimeT>::max() &&
243            min_seconds <= numeric_limits<TimeT>::min();
244   }
245   static_assert(check_range(), "the representable range is unacceptable small");
246 #endif
247 };
248
249 template <class FileTimeT, class TimeT>
250 struct time_util_base<FileTimeT, TimeT, true> {
251   using rep = typename FileTimeT::rep;
252   using fs_duration = typename FileTimeT::duration;
253   using fs_seconds = duration<rep>;
254   using fs_nanoseconds = duration<rep, nano>;
255   using fs_microseconds = duration<rep, micro>;
256
257   static const rep max_seconds;
258   static const rep max_nsec;
259   static const rep min_seconds;
260   static const rep min_nsec_timespec;
261 };
262
263 template <class FileTimeT, class TimeT>
264 const typename FileTimeT::rep
265     time_util_base<FileTimeT, TimeT, true>::max_seconds =
266         duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
267
268 template <class FileTimeT, class TimeT>
269 const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =
270     duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
271                                   fs_seconds(max_seconds))
272         .count();
273
274 template <class FileTimeT, class TimeT>
275 const typename FileTimeT::rep
276     time_util_base<FileTimeT, TimeT, true>::min_seconds =
277         duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
278
279 template <class FileTimeT, class TimeT>
280 const typename FileTimeT::rep
281     time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =
282         duration_cast<fs_nanoseconds>((FileTimeT::duration::min() -
283                                        fs_seconds(min_seconds)) +
284                                       fs_seconds(1))
285             .count();
286
287 template <class FileTimeT, class TimeT, class TimeSpecT>
288 struct time_util : time_util_base<FileTimeT, TimeT> {
289   using Base = time_util_base<FileTimeT, TimeT>;
290   using Base::max_nsec;
291   using Base::max_seconds;
292   using Base::min_nsec_timespec;
293   using Base::min_seconds;
294
295   using typename Base::fs_duration;
296   using typename Base::fs_microseconds;
297   using typename Base::fs_nanoseconds;
298   using typename Base::fs_seconds;
299
300 public:
301   template <class CType, class ChronoType>
302   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out,
303                                                         ChronoType time) {
304     using Lim = numeric_limits<CType>;
305     if (time > Lim::max() || time < Lim::min())
306       return false;
307     *out = static_cast<CType>(time);
308     return true;
309   }
310
311   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
312     if (tm.tv_sec >= 0) {
313       return tm.tv_sec < max_seconds ||
314              (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
315     } else if (tm.tv_sec == (min_seconds - 1)) {
316       return tm.tv_nsec >= min_nsec_timespec;
317     } else {
318       return tm.tv_sec >= min_seconds;
319     }
320   }
321
322   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
323     auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());
324     auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);
325     if (nsecs.count() < 0) {
326       secs = secs + fs_seconds(1);
327       nsecs = nsecs + fs_seconds(1);
328     }
329     using TLim = numeric_limits<TimeT>;
330     if (secs.count() >= 0)
331       return secs.count() <= TLim::max();
332     return secs.count() >= TLim::min();
333   }
334
335   static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
336   convert_from_timespec(TimeSpecT tm) {
337     if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {
338       return FileTimeT(fs_seconds(tm.tv_sec) +
339                        duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));
340     } else { // tm.tv_sec < 0
341       auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) -
342                                                    fs_nanoseconds(tm.tv_nsec));
343       auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;
344       return FileTimeT(Dur);
345     }
346   }
347
348   template <class SubSecT>
349   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool
350   set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {
351     auto dur = tp.time_since_epoch();
352     auto sec_dur = duration_cast<fs_seconds>(dur);
353     auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);
354     // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
355     if (subsec_dur.count() < 0) {
356       if (sec_dur.count() > min_seconds) {
357         sec_dur = sec_dur - fs_seconds(1);
358         subsec_dur = subsec_dur + fs_seconds(1);
359       } else {
360         subsec_dur = fs_nanoseconds::zero();
361       }
362     }
363     return checked_set(sec_out, sec_dur.count()) &&
364            checked_set(subsec_out, subsec_dur.count());
365   }
366   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest,
367                                                                 FileTimeT tp) {
368     if (!is_representable(tp))
369       return false;
370     return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
371   }
372 };
373
374 using fs_time = time_util<file_time_type, time_t, TimeSpec>;
375
376 #if defined(__APPLE__)
377 TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
378 TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
379 #else
380 TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
381 TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
382 #endif
383
384 // allow the utimes implementation to compile even it we're not going
385 // to use it.
386
387 bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS,
388                   error_code& ec) {
389   using namespace chrono;
390   auto Convert = [](long nsec) {
391     using int_type = decltype(std::declval< ::timeval>().tv_usec);
392     auto dur = duration_cast<microseconds>(nanoseconds(nsec)).count();
393     return static_cast<int_type>(dur);
394   };
395   struct ::timeval ConvertedTS[2] = {{TS[0].tv_sec, Convert(TS[0].tv_nsec)},
396                                      {TS[1].tv_sec, Convert(TS[1].tv_nsec)}};
397   if (::utimes(p.c_str(), ConvertedTS) == -1) {
398     ec = capture_errno();
399     return true;
400   }
401   return false;
402 }
403
404 #if defined(_LIBCPP_USE_UTIMENSAT)
405 bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS,
406                      error_code& ec) {
407   if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1) {
408     ec = capture_errno();
409     return true;
410   }
411   return false;
412 }
413 #endif
414
415 bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
416                     error_code& ec) {
417 #if !defined(_LIBCPP_USE_UTIMENSAT)
418   return posix_utimes(p, TS, ec);
419 #else
420   return posix_utimensat(p, TS, ec);
421 #endif
422 }
423
424 } // namespace
425 } // end namespace detail
426
427 _LIBCPP_END_NAMESPACE_FILESYSTEM
428
429 #endif // FILESYSTEM_COMMON_H