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