1 //===----------------------------------------------------------------------===////
3 // The LLVM Compiler Infrastructure
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===////
10 #ifndef FILESYSTEM_COMMON_H
11 #define FILESYSTEM_COMMON_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 */
26 #include "../include/apple_availability.h"
28 #if !defined(__APPLE__)
29 // We can use the presence of UTIME_OMIT to detect platforms that provide
31 #if defined(UTIME_OMIT)
32 #define _LIBCPP_USE_UTIMENSAT
37 #pragma GCC diagnostic push
38 #pragma GCC diagnostic ignored "-Wunused-function"
41 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
46 static string format_string_imp(const char* msg, ...) {
47 // we might need a second shot at this, so pre-emptivly make a copy
51 GuardVAList(va_list& target) : target(target), active(true) {}
64 GuardVAList args_guard(args);
67 va_copy(args_cp, args);
68 GuardVAList args_copy_guard(args_cp);
70 array<char, 256> local_buff;
71 size_t size = local_buff.size();
72 auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp);
74 args_copy_guard.clear();
76 // handle empty expansion
79 if (static_cast<size_t>(ret) < size)
80 return string(local_buff.data());
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());
90 const char* unwrap(string const& s) { return s.c_str(); }
91 const char* unwrap(path const& p) { return p.native().c_str(); }
93 Arg const& unwrap(Arg const& a) {
94 static_assert(!is_class<Arg>::value, "cannot pass class here");
98 template <class... Args>
99 string format_string(const char* fmt, Args const&... args) {
100 return format_string_imp(fmt, unwrap(args)...);
103 error_code capture_errno() {
104 _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
105 return error_code(errno, generic_category());
111 _LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {}
113 bool error_value<bool>() {
117 uintmax_t error_value<uintmax_t>() {
118 return uintmax_t(-1);
121 _LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() {
122 return file_time_type::min();
125 path error_value<path>() {
130 struct ErrorHandler {
131 const char* func_name;
132 error_code* ec = nullptr;
133 const path* p1 = nullptr;
134 const path* p2 = nullptr;
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) {
143 T report(const error_code& m_ec) const {
146 return error_value<T>();
148 string what = string("in ") + func_name;
149 switch (bool(p1) + bool(p2)) {
151 __throw_filesystem_error(what, m_ec);
153 __throw_filesystem_error(what, *p1, m_ec);
155 __throw_filesystem_error(what, *p1, *p2, m_ec);
157 _LIBCPP_UNREACHABLE();
160 template <class... Args>
161 T report(const error_code& m_ec, const char* msg, Args const&... args) const {
164 return error_value<T>();
167 string("in ") + func_name + ": " + format_string(msg, args...);
168 switch (bool(p1) + bool(p2)) {
170 __throw_filesystem_error(what, m_ec);
172 __throw_filesystem_error(what, *p1, m_ec);
174 __throw_filesystem_error(what, *p1, *p2, m_ec);
176 _LIBCPP_UNREACHABLE();
179 T report(errc const& err) const { return report(make_error_code(err)); }
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...);
187 ErrorHandler(ErrorHandler const&) = delete;
188 ErrorHandler& operator=(ErrorHandler const&) = delete;
191 using chrono::duration;
192 using chrono::duration_cast;
194 using TimeSpec = struct ::timespec;
195 using StatT = struct ::stat;
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>;
206 static constexpr rep max_seconds =
207 duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
209 static constexpr rep max_nsec =
210 duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
211 fs_seconds(max_seconds))
214 static constexpr rep min_seconds =
215 duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
217 static constexpr rep min_nsec_timespec =
218 duration_cast<fs_nanoseconds>(
219 (FileTimeT::duration::min() - fs_seconds(min_seconds)) +
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)));
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");
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);
242 return max_seconds >= numeric_limits<TimeT>::max() &&
243 min_seconds <= numeric_limits<TimeT>::min();
245 static_assert(check_range(), "the representable range is unacceptable small");
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>;
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;
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();
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))
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();
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)) +
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;
295 using typename Base::fs_duration;
296 using typename Base::fs_microseconds;
297 using typename Base::fs_nanoseconds;
298 using typename Base::fs_seconds;
301 template <class CType, class ChronoType>
302 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out,
304 using Lim = numeric_limits<CType>;
305 if (time > Lim::max() || time < Lim::min())
307 *out = static_cast<CType>(time);
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;
318 return tm.tv_sec >= min_seconds;
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);
329 using TLim = numeric_limits<TimeT>;
330 if (secs.count() >= 0)
331 return secs.count() <= TLim::max();
332 return secs.count() >= TLim::min();
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);
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);
360 subsec_dur = fs_nanoseconds::zero();
363 return checked_set(sec_out, sec_dur.count()) &&
364 checked_set(subsec_out, subsec_dur.count());
366 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest,
368 if (!is_representable(tp))
370 return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
374 using fs_time = time_util<file_time_type, time_t, TimeSpec>;
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; }
380 TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
381 TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
384 // allow the utimes implementation to compile even it we're not going
387 bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS,
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);
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();
404 #if defined(_LIBCPP_USE_UTIMENSAT)
405 bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS,
407 if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1) {
408 ec = capture_errno();
415 bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
417 #if !defined(_LIBCPP_USE_UTIMENSAT)
418 return posix_utimes(p, TS, ec);
420 return posix_utimensat(p, TS, ec);
425 } // end namespace detail
427 _LIBCPP_END_NAMESPACE_FILESYSTEM
429 #endif // FILESYSTEM_COMMON_H