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