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 <__assert>
13 #include <__config>
14 #include <array>
15 #include <cerrno>
16 #include <chrono>
17 #include <climits>
18 #include <cstdarg>
19 #include <ctime>
20 #include <filesystem>
21 #include <ratio>
22 #include <utility>
23 #include <system_error>
24
25 #if defined(_LIBCPP_WIN32API)
26 # define WIN32_LEAN_AND_MEAN
27 # define NOMINMAX
28 # include <windows.h>
29 #else
30 # include <dirent.h> // for DIR & friends
31 # include <fcntl.h> /* values for fchmodat */
32 # include <sys/stat.h>
33 # include <sys/statvfs.h>
34 # include <sys/time.h> // for ::utimes as used in __last_write_time
35 # include <unistd.h>
36 #endif // defined(_LIBCPP_WIN32API)
37
38 // We can use the presence of UTIME_OMIT to detect platforms that provide utimensat.
39 #if defined(UTIME_OMIT)
40 # define _LIBCPP_USE_UTIMENSAT
41 #endif
42
43 // TODO: Check whether these functions actually need internal linkage, or if they can be made normal header functions
44 _LIBCPP_DIAGNOSTIC_PUSH
45 _LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wunused-function")
46 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-function")
47 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-template")
48
49 #if defined(_LIBCPP_WIN32API)
50 # define PATHSTR(x) (L##x)
51 # define PATH_CSTR_FMT "\"%ls\""
52 #else
53 # define PATHSTR(x) (x)
54 # define PATH_CSTR_FMT "\"%s\""
55 #endif
56
57 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
58
59 namespace detail {
60
61 #if defined(_LIBCPP_WIN32API)
62 // Non anonymous, to allow access from two translation units.
63 errc __win_err_to_errc(int err);
64 #endif
65
66 namespace {
67
68 static _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 0) string
format_string_impl(const char * msg,va_list ap)69 format_string_impl(const char* msg, va_list ap) {
70 array<char, 256> buf;
71
72 va_list apcopy;
73 va_copy(apcopy, ap);
74 int ret = ::vsnprintf(buf.data(), buf.size(), msg, apcopy);
75 va_end(apcopy);
76
77 string result;
78 if (static_cast<size_t>(ret) < buf.size()) {
79 result.assign(buf.data(), static_cast<size_t>(ret));
80 } else {
81 // we did not provide a long enough buffer on our first attempt. The
82 // return value is the number of bytes (excluding the null byte) that are
83 // needed for formatting.
84 size_t size_with_null = static_cast<size_t>(ret) + 1;
85 result.__resize_default_init(size_with_null - 1);
86 ret = ::vsnprintf(&result[0], size_with_null, msg, ap);
87 _LIBCPP_ASSERT(static_cast<size_t>(ret) == (size_with_null - 1), "TODO");
88 }
89 return result;
90 }
91
92 static _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2) string
format_string(const char * msg,...)93 format_string(const char* msg, ...) {
94 string ret;
95 va_list ap;
96 va_start(ap, msg);
97 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
98 try {
99 #endif // _LIBCPP_HAS_NO_EXCEPTIONS
100 ret = format_string_impl(msg, ap);
101 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
102 } catch (...) {
103 va_end(ap);
104 throw;
105 }
106 #endif // _LIBCPP_HAS_NO_EXCEPTIONS
107 va_end(ap);
108 return ret;
109 }
110
capture_errno()111 error_code capture_errno() {
112 _LIBCPP_ASSERT(errno != 0, "Expected errno to be non-zero");
113 return error_code(errno, generic_category());
114 }
115
116 #if defined(_LIBCPP_WIN32API)
make_windows_error(int err)117 error_code make_windows_error(int err) {
118 return make_error_code(__win_err_to_errc(err));
119 }
120 #endif
121
122 template <class T>
123 T error_value();
124 template <>
125 _LIBCPP_CONSTEXPR_SINCE_CXX14 void error_value<void>() {}
126 template <>
127 bool error_value<bool>() {
128 return false;
129 }
130 #if __SIZEOF_SIZE_T__ != __SIZEOF_LONG_LONG__
131 template <>
132 size_t error_value<size_t>() {
133 return size_t(-1);
134 }
135 #endif
136 template <>
137 uintmax_t error_value<uintmax_t>() {
138 return uintmax_t(-1);
139 }
140 template <>
141 _LIBCPP_CONSTEXPR_SINCE_CXX14 file_time_type error_value<file_time_type>() {
142 return file_time_type::min();
143 }
144 template <>
145 path error_value<path>() {
146 return {};
147 }
148
149 template <class T>
150 struct ErrorHandler {
151 const char* func_name_;
152 error_code* ec_ = nullptr;
153 const path* p1_ = nullptr;
154 const path* p2_ = nullptr;
155
156 ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
157 const path* p2 = nullptr)
func_name_ErrorHandler158 : func_name_(fname), ec_(ec), p1_(p1), p2_(p2) {
159 if (ec_)
160 ec_->clear();
161 }
162
reportErrorHandler163 T report(const error_code& ec) const {
164 if (ec_) {
165 *ec_ = ec;
166 return error_value<T>();
167 }
168 string what = string("in ") + func_name_;
169 switch (bool(p1_) + bool(p2_)) {
170 case 0:
171 __throw_filesystem_error(what, ec);
172 case 1:
173 __throw_filesystem_error(what, *p1_, ec);
174 case 2:
175 __throw_filesystem_error(what, *p1_, *p2_, ec);
176 }
177 __libcpp_unreachable();
178 }
179
180 _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 0)
report_implErrorHandler181 void report_impl(const error_code& ec, const char* msg, va_list ap) const {
182 if (ec_) {
183 *ec_ = ec;
184 return;
185 }
186 string what =
187 string("in ") + func_name_ + ": " + format_string_impl(msg, ap);
188 switch (bool(p1_) + bool(p2_)) {
189 case 0:
190 __throw_filesystem_error(what, ec);
191 case 1:
192 __throw_filesystem_error(what, *p1_, ec);
193 case 2:
194 __throw_filesystem_error(what, *p1_, *p2_, ec);
195 }
196 __libcpp_unreachable();
197 }
198
199 _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4)
reportErrorHandler200 T report(const error_code& ec, const char* msg, ...) const {
201 va_list ap;
202 va_start(ap, msg);
203 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
204 try {
205 #endif // _LIBCPP_HAS_NO_EXCEPTIONS
206 report_impl(ec, msg, ap);
207 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
208 } catch (...) {
209 va_end(ap);
210 throw;
211 }
212 #endif // _LIBCPP_HAS_NO_EXCEPTIONS
213 va_end(ap);
214 return error_value<T>();
215 }
216
reportErrorHandler217 T report(errc const& err) const {
218 return report(make_error_code(err));
219 }
220
221 _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4)
reportErrorHandler222 T report(errc const& err, const char* msg, ...) const {
223 va_list ap;
224 va_start(ap, msg);
225 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
226 try {
227 #endif // _LIBCPP_HAS_NO_EXCEPTIONS
228 report_impl(make_error_code(err), msg, ap);
229 #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
230 } catch (...) {
231 va_end(ap);
232 throw;
233 }
234 #endif // _LIBCPP_HAS_NO_EXCEPTIONS
235 va_end(ap);
236 return error_value<T>();
237 }
238
239 private:
240 ErrorHandler(ErrorHandler const&) = delete;
241 ErrorHandler& operator=(ErrorHandler const&) = delete;
242 };
243
244 using chrono::duration;
245 using chrono::duration_cast;
246
247 #if defined(_LIBCPP_WIN32API)
248 // Various C runtime versions (UCRT, or the legacy msvcrt.dll used by
249 // some mingw toolchains) provide different stat function implementations,
250 // with a number of limitations with respect to what we want from the
251 // stat function. Instead provide our own (in the anonymous detail namespace
252 // in posix_compat.h) which does exactly what we want, along with our own
253 // stat structure and flag macros.
254
255 struct TimeSpec {
256 int64_t tv_sec;
257 int64_t tv_nsec;
258 };
259 struct StatT {
260 unsigned st_mode;
261 TimeSpec st_atim;
262 TimeSpec st_mtim;
263 uint64_t st_dev; // FILE_ID_INFO::VolumeSerialNumber
264 struct FileIdStruct {
265 unsigned char id[16]; // FILE_ID_INFO::FileId
266 bool operator==(const FileIdStruct &other) const {
267 for (int i = 0; i < 16; i++)
268 if (id[i] != other.id[i])
269 return false;
270 return true;
271 }
272 } st_ino;
273 uint32_t st_nlink;
274 uintmax_t st_size;
275 };
276
277 #else
278 using TimeSpec = struct timespec;
279 using TimeVal = struct timeval;
280 using StatT = struct stat;
281 #endif
282
283 template <class FileTimeT, class TimeT,
284 bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
285 struct time_util_base {
286 using rep = typename FileTimeT::rep;
287 using fs_duration = typename FileTimeT::duration;
288 using fs_seconds = duration<rep>;
289 using fs_nanoseconds = duration<rep, nano>;
290 using fs_microseconds = duration<rep, micro>;
291
292 static constexpr rep max_seconds =
293 duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
294
295 static constexpr rep max_nsec =
296 duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
297 fs_seconds(max_seconds))
298 .count();
299
300 static constexpr rep min_seconds =
301 duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
302
303 static constexpr rep min_nsec_timespec =
304 duration_cast<fs_nanoseconds>(
305 (FileTimeT::duration::min() - fs_seconds(min_seconds)) +
306 fs_seconds(1))
307 .count();
308
309 private:
get_min_nsecstime_util_base310 static _LIBCPP_CONSTEXPR_SINCE_CXX14 fs_duration get_min_nsecs() {
311 return duration_cast<fs_duration>(
312 fs_nanoseconds(min_nsec_timespec) -
313 duration_cast<fs_nanoseconds>(fs_seconds(1)));
314 }
315 // Static assert that these values properly round trip.
316 static_assert(fs_seconds(min_seconds) + get_min_nsecs() ==
317 FileTimeT::duration::min(),
318 "value doesn't roundtrip");
319
check_rangetime_util_base320 static _LIBCPP_CONSTEXPR_SINCE_CXX14 bool check_range() {
321 // This kinda sucks, but it's what happens when we don't have __int128_t.
322 if (sizeof(TimeT) == sizeof(rep)) {
323 typedef duration<long long, ratio<3600 * 24 * 365> > Years;
324 return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&
325 duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);
326 }
327 return max_seconds >= numeric_limits<TimeT>::max() &&
328 min_seconds <= numeric_limits<TimeT>::min();
329 }
330 static_assert(check_range(), "the representable range is unacceptable small");
331 };
332
333 template <class FileTimeT, class TimeT>
334 struct time_util_base<FileTimeT, TimeT, true> {
335 using rep = typename FileTimeT::rep;
336 using fs_duration = typename FileTimeT::duration;
337 using fs_seconds = duration<rep>;
338 using fs_nanoseconds = duration<rep, nano>;
339 using fs_microseconds = duration<rep, micro>;
340
341 static const rep max_seconds;
342 static const rep max_nsec;
343 static const rep min_seconds;
344 static const rep min_nsec_timespec;
345 };
346
347 template <class FileTimeT, class TimeT>
348 const typename FileTimeT::rep
349 time_util_base<FileTimeT, TimeT, true>::max_seconds =
350 duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
351
352 template <class FileTimeT, class TimeT>
353 const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =
354 duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
355 fs_seconds(max_seconds))
356 .count();
357
358 template <class FileTimeT, class TimeT>
359 const typename FileTimeT::rep
360 time_util_base<FileTimeT, TimeT, true>::min_seconds =
361 duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
362
363 template <class FileTimeT, class TimeT>
364 const typename FileTimeT::rep
365 time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =
366 duration_cast<fs_nanoseconds>((FileTimeT::duration::min() -
367 fs_seconds(min_seconds)) +
368 fs_seconds(1))
369 .count();
370
371 template <class FileTimeT, class TimeT, class TimeSpecT>
372 struct time_util : time_util_base<FileTimeT, TimeT> {
373 using Base = time_util_base<FileTimeT, TimeT>;
374 using Base::max_nsec;
375 using Base::max_seconds;
376 using Base::min_nsec_timespec;
377 using Base::min_seconds;
378
379 using typename Base::fs_duration;
380 using typename Base::fs_microseconds;
381 using typename Base::fs_nanoseconds;
382 using typename Base::fs_seconds;
383
384 public:
385 template <class CType, class ChronoType>
386 static _LIBCPP_CONSTEXPR_SINCE_CXX14 bool checked_set(CType* out,
387 ChronoType time) {
388 using Lim = numeric_limits<CType>;
389 if (time > Lim::max() || time < Lim::min())
390 return false;
391 *out = static_cast<CType>(time);
392 return true;
393 }
394
395 static _LIBCPP_CONSTEXPR_SINCE_CXX14 bool is_representable(TimeSpecT tm) {
396 if (tm.tv_sec >= 0) {
397 return tm.tv_sec < max_seconds ||
398 (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
399 } else if (tm.tv_sec == (min_seconds - 1)) {
400 return tm.tv_nsec >= min_nsec_timespec;
401 } else {
402 return tm.tv_sec >= min_seconds;
403 }
404 }
405
406 static _LIBCPP_CONSTEXPR_SINCE_CXX14 bool is_representable(FileTimeT tm) {
407 auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());
408 auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);
409 if (nsecs.count() < 0) {
410 secs = secs + fs_seconds(1);
411 nsecs = nsecs + fs_seconds(1);
412 }
413 using TLim = numeric_limits<TimeT>;
414 if (secs.count() >= 0)
415 return secs.count() <= TLim::max();
416 return secs.count() >= TLim::min();
417 }
418
419 static _LIBCPP_CONSTEXPR_SINCE_CXX14 FileTimeT
420 convert_from_timespec(TimeSpecT tm) {
421 if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {
422 return FileTimeT(fs_seconds(tm.tv_sec) +
423 duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));
424 } else { // tm.tv_sec < 0
425 auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) -
426 fs_nanoseconds(tm.tv_nsec));
427 auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;
428 return FileTimeT(Dur);
429 }
430 }
431
432 template <class SubSecT>
433 static _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
434 set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {
435 auto dur = tp.time_since_epoch();
436 auto sec_dur = duration_cast<fs_seconds>(dur);
437 auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);
438 // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
439 if (subsec_dur.count() < 0) {
440 if (sec_dur.count() > min_seconds) {
441 sec_dur = sec_dur - fs_seconds(1);
442 subsec_dur = subsec_dur + fs_seconds(1);
443 } else {
444 subsec_dur = fs_nanoseconds::zero();
445 }
446 }
447 return checked_set(sec_out, sec_dur.count()) &&
448 checked_set(subsec_out, subsec_dur.count());
449 }
450 static _LIBCPP_CONSTEXPR_SINCE_CXX14 bool convert_to_timespec(TimeSpecT& dest,
451 FileTimeT tp) {
452 if (!is_representable(tp))
453 return false;
454 return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
455 }
456 };
457
458 #if defined(_LIBCPP_WIN32API)
459 using fs_time = time_util<file_time_type, int64_t, TimeSpec>;
460 #else
461 using fs_time = time_util<file_time_type, time_t, TimeSpec>;
462 #endif
463
464 #if defined(__APPLE__)
465 inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
466 inline TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
467 #elif defined(__MVS__)
468 inline TimeSpec extract_mtime(StatT const& st) {
469 TimeSpec TS = {st.st_mtime, 0};
470 return TS;
471 }
472 inline TimeSpec extract_atime(StatT const& st) {
473 TimeSpec TS = {st.st_atime, 0};
474 return TS;
475 }
476 #elif defined(_AIX)
477 inline TimeSpec extract_mtime(StatT const& st) {
478 TimeSpec TS = {st.st_mtime, st.st_mtime_n};
479 return TS;
480 }
481 inline TimeSpec extract_atime(StatT const& st) {
482 TimeSpec TS = {st.st_atime, st.st_atime_n};
483 return TS;
484 }
485 #else
486 inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
487 inline TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
488 #endif
489
490 #if !defined(_LIBCPP_WIN32API)
491 inline TimeVal make_timeval(TimeSpec const& ts) {
492 using namespace chrono;
493 auto Convert = [](long nsec) {
494 using int_type = decltype(std::declval<TimeVal>().tv_usec);
495 auto dur = duration_cast<microseconds>(nanoseconds(nsec)).count();
496 return static_cast<int_type>(dur);
497 };
498 TimeVal TV = {};
499 TV.tv_sec = ts.tv_sec;
500 TV.tv_usec = Convert(ts.tv_nsec);
501 return TV;
502 }
503
504 inline bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS,
505 error_code& ec) {
506 TimeVal ConvertedTS[2] = {make_timeval(TS[0]), make_timeval(TS[1])};
507 if (::utimes(p.c_str(), ConvertedTS) == -1) {
508 ec = capture_errno();
509 return true;
510 }
511 return false;
512 }
513
514 #if defined(_LIBCPP_USE_UTIMENSAT)
515 bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS,
516 error_code& ec) {
517 if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1) {
518 ec = capture_errno();
519 return true;
520 }
521 return false;
522 }
523 #endif
524
525 bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
526 error_code& ec) {
527 #if !defined(_LIBCPP_USE_UTIMENSAT)
528 return posix_utimes(p, TS, ec);
529 #else
530 return posix_utimensat(p, TS, ec);
531 #endif
532 }
533
534 #if defined(DT_BLK)
535 template <class DirEntT, class = decltype(DirEntT::d_type)>
536 static file_type get_file_type(DirEntT* ent, int) {
537 switch (ent->d_type) {
538 case DT_BLK:
539 return file_type::block;
540 case DT_CHR:
541 return file_type::character;
542 case DT_DIR:
543 return file_type::directory;
544 case DT_FIFO:
545 return file_type::fifo;
546 case DT_LNK:
547 return file_type::symlink;
548 case DT_REG:
549 return file_type::regular;
550 case DT_SOCK:
551 return file_type::socket;
552 // Unlike in lstat, hitting "unknown" here simply means that the underlying
553 // filesystem doesn't support d_type. Report is as 'none' so we correctly
554 // set the cache to empty.
555 case DT_UNKNOWN:
556 break;
557 }
558 return file_type::none;
559 }
560 #endif // defined(DT_BLK)
561
562 template <class DirEntT>
563 static file_type get_file_type(DirEntT*, long) {
564 return file_type::none;
565 }
566
567 static pair<string_view, file_type> posix_readdir(DIR* dir_stream,
568 error_code& ec) {
569 struct dirent* dir_entry_ptr = nullptr;
570 errno = 0; // zero errno in order to detect errors
571 ec.clear();
572 if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) {
573 if (errno)
574 ec = capture_errno();
575 return {};
576 } else {
577 return {dir_entry_ptr->d_name, get_file_type(dir_entry_ptr, 0)};
578 }
579 }
580
581 #else // _LIBCPP_WIN32API
582
583 static file_type get_file_type(const WIN32_FIND_DATAW& data) {
584 if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
585 data.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
586 return file_type::symlink;
587 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
588 return file_type::directory;
589 return file_type::regular;
590 }
591 static uintmax_t get_file_size(const WIN32_FIND_DATAW& data) {
592 return (static_cast<uint64_t>(data.nFileSizeHigh) << 32) + data.nFileSizeLow;
593 }
594 static file_time_type get_write_time(const WIN32_FIND_DATAW& data) {
595 ULARGE_INTEGER tmp;
596 const FILETIME& time = data.ftLastWriteTime;
597 tmp.u.LowPart = time.dwLowDateTime;
598 tmp.u.HighPart = time.dwHighDateTime;
599 return file_time_type(file_time_type::duration(tmp.QuadPart));
600 }
601
602 #endif // !_LIBCPP_WIN32API
603
604 } // namespace
605 } // end namespace detail
606
607 _LIBCPP_END_NAMESPACE_FILESYSTEM
608
609 _LIBCPP_DIAGNOSTIC_POP
610
611 #endif // FILESYSTEM_COMMON_H
612