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