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
42namespace detail {
43namespace {
44
45static 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
96const char* unwrap(string const& s) { return s.c_str(); }
97const char* unwrap(path const& p) { return p.native().c_str(); }
98template <class Arg>
99Arg const& unwrap(Arg const& a) {
100  static_assert(!is_class<Arg>::value, "cannot pass class here");
101  return a;
102}
103
104template <class... Args>
105string format_string(const char* fmt, Args const&... args) {
106  return format_string_imp(fmt, unwrap(args)...);
107}
108
109error_code capture_errno() {
110  _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
111  return error_code(errno, generic_category());
112}
113
114template <class T>
115T error_value();
116template <>
117_LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {}
118template <>
119bool error_value<bool>() {
120  return false;
121}
122template <>
123uintmax_t error_value<uintmax_t>() {
124  return uintmax_t(-1);
125}
126template <>
127_LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() {
128  return file_time_type::min();
129}
130template <>
131path error_value<path>() {
132  return {};
133}
134
135template <class T>
136struct 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
192private:
193  ErrorHandler(ErrorHandler const&) = delete;
194  ErrorHandler& operator=(ErrorHandler const&) = delete;
195};
196
197using chrono::duration;
198using chrono::duration_cast;
199
200using TimeSpec = struct ::timespec;
201using StatT = struct ::stat;
202
203template <class FileTimeT, class TimeT,
204          bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
205struct 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
229private:
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
255template <class FileTimeT, class TimeT>
256struct 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
269template <class FileTimeT, class TimeT>
270const typename FileTimeT::rep
271    time_util_base<FileTimeT, TimeT, true>::max_seconds =
272        duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
273
274template <class FileTimeT, class TimeT>
275const 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
280template <class FileTimeT, class TimeT>
281const typename FileTimeT::rep
282    time_util_base<FileTimeT, TimeT, true>::min_seconds =
283        duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
284
285template <class FileTimeT, class TimeT>
286const 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
293template <class FileTimeT, class TimeT, class TimeSpecT>
294struct 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
306public:
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
380using fs_time = time_util<file_time_type, time_t, TimeSpec>;
381
382#if defined(__APPLE__)
383TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
384TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
385#else
386TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
387TimeSpec 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
393bool 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)
411bool 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
421bool 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
436