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