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