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