blob: 6db0cac7f04e24edbb1ae91e2539611f493073fe [file] [log] [blame]
Eric Fiselierc1699862018-07-20 01:22:32 +00001//===----------------------------------------------------------------------===////
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 "experimental/__config"
Eric Fiselier9158bfd2018-07-23 02:00:52 +000014#include "array"
Eric Fiselierc1699862018-07-20 01:22:32 +000015#include "chrono"
16#include "cstdlib"
17#include "climits"
18
19#include <unistd.h>
20#include <sys/stat.h>
21#include <sys/statvfs.h>
22#include <fcntl.h> /* values for fchmodat */
23
24#include <experimental/filesystem>
25
26#if (__APPLE__)
27#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
28#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101300
29#define _LIBCXX_USE_UTIMENSAT
30#endif
31#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
32#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 110000
33#define _LIBCXX_USE_UTIMENSAT
34#endif
35#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__)
36#if __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 110000
37#define _LIBCXX_USE_UTIMENSAT
38#endif
39#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__)
40#if __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 40000
41#define _LIBCXX_USE_UTIMENSAT
42#endif
43#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
44#else
45// We can use the presence of UTIME_OMIT to detect platforms that provide
46// utimensat.
47#if defined(UTIME_OMIT)
48#define _LIBCXX_USE_UTIMENSAT
49#endif
50#endif // __APPLE__
51
52#if !defined(_LIBCXX_USE_UTIMENSAT)
53#include <sys/time.h> // for ::utimes as used in __last_write_time
54#endif
55
56#if !defined(UTIME_OMIT)
57#include <sys/time.h> // for ::utimes as used in __last_write_time
58#endif
59
60#if defined(__GNUC__)
61#pragma GCC diagnostic push
62#pragma GCC diagnostic ignored "-Wunused-function"
63#endif
64
65_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
66
67namespace detail {
68namespace {
69
Eric Fiselier9158bfd2018-07-23 02:00:52 +000070static std::string format_string_imp(const char* msg, ...) {
71 // we might need a second shot at this, so pre-emptivly make a copy
72 struct GuardVAList {
73 va_list& target;
74 bool active = true;
Eric Fiseliere3081d52018-07-23 03:06:57 +000075 GuardVAList(va_list &target) : target(target), active(true) {}
Eric Fiselier9158bfd2018-07-23 02:00:52 +000076 void clear() {
77 if (active)
78 va_end(target);
79 active = false;
80 }
81 ~GuardVAList() {
82 if (active)
83 va_end(target);
84 }
85 };
86 va_list args;
87 va_start(args, msg);
Eric Fiseliere3081d52018-07-23 03:06:57 +000088 GuardVAList args_guard(args);
Eric Fiselier9158bfd2018-07-23 02:00:52 +000089
90 va_list args_cp;
91 va_copy(args_cp, args);
Eric Fiseliere3081d52018-07-23 03:06:57 +000092 GuardVAList args_copy_guard(args_cp);
Eric Fiselier9158bfd2018-07-23 02:00:52 +000093
94 std::array<char, 256> local_buff;
95 std::size_t size = local_buff.size();
96 auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp);
97
98 args_copy_guard.clear();
99
100 // handle empty expansion
101 if (ret == 0)
102 return std::string{};
103 if (static_cast<std::size_t>(ret) < size)
104 return std::string(local_buff.data());
105
106 // we did not provide a long enough buffer on our first attempt.
107 // add 1 to size to account for null-byte in size cast to prevent overflow
108 size = static_cast<std::size_t>(ret) + 1;
109 auto buff_ptr = std::unique_ptr<char[]>(new char[size]);
110 ret = ::vsnprintf(buff_ptr.get(), size, msg, args);
111 return std::string(buff_ptr.get());
112}
113
114const char* unwrap(string const& s) { return s.c_str(); }
115const char* unwrap(path const& p) { return p.native().c_str(); }
116template <class Arg>
117Arg const& unwrap(Arg const& a) {
118 static_assert(!is_class<Arg>::value, "cannot pass class here");
119 return a;
120}
121
122template <class... Args>
123std::string format_string(const char* fmt, Args const&... args) {
124 return format_string_imp(fmt, unwrap(args)...);
125}
126
Eric Fiselierc1699862018-07-20 01:22:32 +0000127std::error_code capture_errno() {
128 _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
129 return std::error_code(errno, std::generic_category());
130}
131
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000132template <class T>
133T error_value();
134template <>
Eric Fiseliere3081d52018-07-23 03:06:57 +0000135_LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {}
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000136template <>
137constexpr bool error_value<bool>() {
138 return false;
139}
140template <>
141constexpr uintmax_t error_value<uintmax_t>() {
142 return uintmax_t(-1);
143}
144template <>
Eric Fiseliere3081d52018-07-23 03:06:57 +0000145_LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000146 return file_time_type::min();
147}
148template <>
149path error_value<path>() {
150 return {};
Eric Fiselierc1699862018-07-20 01:22:32 +0000151}
152
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000153template <class T>
154struct ErrorHandler {
155 const char* func_name;
156 error_code* ec = nullptr;
157 const path* p1 = nullptr;
158 const path* p2 = nullptr;
159
160 ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
161 const path* p2 = nullptr)
162 : func_name(fname), ec(ec), p1(p1), p2(p2) {
163 if (ec)
164 ec->clear();
165 }
166
167 T report(const error_code& m_ec) const {
168 if (ec) {
169 *ec = m_ec;
170 return error_value<T>();
171 }
172 string what = string("in ") + func_name;
173 switch (bool(p1) + bool(p2)) {
174 case 0:
175 __throw_filesystem_error(what, m_ec);
176 case 1:
177 __throw_filesystem_error(what, *p1, m_ec);
178 case 2:
179 __throw_filesystem_error(what, *p1, *p2, m_ec);
180 }
181 _LIBCPP_UNREACHABLE();
182 }
183
184 template <class... Args>
185 T report(const error_code& m_ec, const char* msg, Args const&... args) const {
186 if (ec) {
187 *ec = m_ec;
188 return error_value<T>();
189 }
190 string what =
191 string("in ") + func_name + ": " + format_string(msg, args...);
192 switch (bool(p1) + bool(p2)) {
193 case 0:
194 __throw_filesystem_error(what, m_ec);
195 case 1:
196 __throw_filesystem_error(what, *p1, m_ec);
197 case 2:
198 __throw_filesystem_error(what, *p1, *p2, m_ec);
199 }
200 _LIBCPP_UNREACHABLE();
201 }
202
203 T report(errc const& err) const { return report(make_error_code(err)); }
204
205 template <class... Args>
206 T report(errc const& err, const char* msg, Args const&... args) const {
207 return report(make_error_code(err), msg, args...);
208 }
209
210private:
211 ErrorHandler(ErrorHandler const&) = delete;
212 ErrorHandler& operator=(ErrorHandler const&) = delete;
213};
Eric Fiselierc1699862018-07-20 01:22:32 +0000214
215namespace time_util {
216
217using namespace chrono;
218
219template <class FileTimeT,
220 bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
221struct fs_time_util_base {
Eric Fiselier6d4be632018-07-22 21:56:40 +0000222 static constexpr seconds::rep max_seconds =
Eric Fiselierc1699862018-07-20 01:22:32 +0000223 duration_cast<seconds>(FileTimeT::duration::max()).count();
224
Eric Fiselier6d4be632018-07-22 21:56:40 +0000225 static constexpr nanoseconds::rep max_nsec =
Eric Fiselierc1699862018-07-20 01:22:32 +0000226 duration_cast<nanoseconds>(FileTimeT::duration::max() -
227 seconds(max_seconds))
228 .count();
229
Eric Fiselier6d4be632018-07-22 21:56:40 +0000230 static constexpr seconds::rep min_seconds =
Eric Fiselierc1699862018-07-20 01:22:32 +0000231 duration_cast<seconds>(FileTimeT::duration::min()).count();
232
Eric Fiselier6d4be632018-07-22 21:56:40 +0000233 static constexpr nanoseconds::rep min_nsec_timespec =
Eric Fiselierc1699862018-07-20 01:22:32 +0000234 duration_cast<nanoseconds>(
235 (FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1))
236 .count();
237
238 // Static assert that these values properly round trip.
239 static_assert((seconds(min_seconds) +
240 duration_cast<microseconds>(nanoseconds(min_nsec_timespec))) -
241 duration_cast<microseconds>(seconds(1)) ==
242 FileTimeT::duration::min(),
243 "");
244};
245
246template <class FileTimeT>
247struct fs_time_util_base<FileTimeT, true> {
248 static const long long max_seconds;
249 static const long long max_nsec;
250 static const long long min_seconds;
251 static const long long min_nsec_timespec;
252};
253
254template <class FileTimeT>
255const long long fs_time_util_base<FileTimeT, true>::max_seconds =
256 duration_cast<seconds>(FileTimeT::duration::max()).count();
257
258template <class FileTimeT>
259const long long fs_time_util_base<FileTimeT, true>::max_nsec =
260 duration_cast<nanoseconds>(FileTimeT::duration::max() -
261 seconds(max_seconds))
262 .count();
263
264template <class FileTimeT>
265const long long fs_time_util_base<FileTimeT, true>::min_seconds =
266 duration_cast<seconds>(FileTimeT::duration::min()).count();
267
268template <class FileTimeT>
269const long long fs_time_util_base<FileTimeT, true>::min_nsec_timespec =
270 duration_cast<nanoseconds>(
271 (FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1))
272 .count();
273
274template <class FileTimeT, class TimeT, class TimeSpecT>
275struct fs_time_util : fs_time_util_base<FileTimeT> {
276 using Base = fs_time_util_base<FileTimeT>;
277 using Base::max_nsec;
278 using Base::max_seconds;
279 using Base::min_nsec_timespec;
280 using Base::min_seconds;
281
282public:
283 template <class CType, class ChronoType>
284 static bool checked_set(CType* out, ChronoType time) {
285 using Lim = numeric_limits<CType>;
286 if (time > Lim::max() || time < Lim::min())
287 return false;
288 *out = static_cast<CType>(time);
289 return true;
290 }
291
292 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
293 if (tm.tv_sec >= 0) {
294 return (tm.tv_sec < max_seconds) ||
295 (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
296 } else if (tm.tv_sec == (min_seconds - 1)) {
297 return tm.tv_nsec >= min_nsec_timespec;
298 } else {
299 return (tm.tv_sec >= min_seconds);
300 }
301 }
302
303 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
304 auto secs = duration_cast<seconds>(tm.time_since_epoch());
305 auto nsecs = duration_cast<nanoseconds>(tm.time_since_epoch() - secs);
306 if (nsecs.count() < 0) {
307 secs = secs + seconds(1);
308 nsecs = nsecs + seconds(1);
309 }
310 using TLim = numeric_limits<TimeT>;
311 if (secs.count() >= 0)
312 return secs.count() <= TLim::max();
313 return secs.count() >= TLim::min();
314 }
315
316 static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
317 convert_timespec(TimeSpecT tm) {
318 auto adj_msec = duration_cast<microseconds>(nanoseconds(tm.tv_nsec));
319 if (tm.tv_sec >= 0) {
320 auto Dur = seconds(tm.tv_sec) + microseconds(adj_msec);
321 return FileTimeT(Dur);
322 } else if (duration_cast<microseconds>(nanoseconds(tm.tv_nsec)).count() ==
323 0) {
324 return FileTimeT(seconds(tm.tv_sec));
325 } else { // tm.tv_sec < 0
326 auto adj_subsec =
327 duration_cast<microseconds>(seconds(1) - nanoseconds(tm.tv_nsec));
328 auto Dur = seconds(tm.tv_sec + 1) - adj_subsec;
329 return FileTimeT(Dur);
330 }
331 }
332
333 template <class SubSecDurT, class SubSecT>
334 static bool set_times_checked(TimeT* sec_out, SubSecT* subsec_out,
335 FileTimeT tp) {
336 auto dur = tp.time_since_epoch();
337 auto sec_dur = duration_cast<seconds>(dur);
338 auto subsec_dur = duration_cast<SubSecDurT>(dur - sec_dur);
339 // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
340 if (subsec_dur.count() < 0) {
341 if (sec_dur.count() > min_seconds) {
342 sec_dur -= seconds(1);
343 subsec_dur += seconds(1);
344 } else {
345 subsec_dur = SubSecDurT::zero();
346 }
347 }
348 return checked_set(sec_out, sec_dur.count()) &&
349 checked_set(subsec_out, subsec_dur.count());
350 }
351};
352
353} // namespace time_util
354
355
Eric Fiselier41cdcbe2018-07-20 01:44:33 +0000356using TimeSpec = struct ::timespec;
357using StatT = struct ::stat;
Eric Fiselierc1699862018-07-20 01:22:32 +0000358
359using FSTime = time_util::fs_time_util<file_time_type, time_t, struct timespec>;
360
361#if defined(__APPLE__)
362TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
363TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
364#else
365TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
366TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
367#endif
368
369#if !defined(_LIBCXX_USE_UTIMENSAT)
370using TimeStruct = struct ::timeval;
371using TimeStructArray = TimeStruct[2];
372#else
Eric Fiseliere3081d52018-07-23 03:06:57 +0000373using TimeStruct = TimeSpec;
Eric Fiselierc1699862018-07-20 01:22:32 +0000374using TimeStructArray = TimeStruct[2];
375#endif
376
377bool SetFileTimes(const path& p, TimeStructArray const& TS,
378 std::error_code& ec) {
379#if !defined(_LIBCXX_USE_UTIMENSAT)
380 if (::utimes(p.c_str(), TS) == -1)
381#else
382 if (::utimensat(AT_FDCWD, p.c_str(), TS, 0) == -1)
383#endif
384 {
385 ec = capture_errno();
386 return true;
387 }
388 return false;
389}
390
391void SetTimeStructTo(TimeStruct& TS, TimeSpec ToTS) {
392 using namespace chrono;
393 TS.tv_sec = ToTS.tv_sec;
394#if !defined(_LIBCXX_USE_UTIMENSAT)
395 TS.tv_usec = duration_cast<microseconds>(nanoseconds(ToTS.tv_nsec)).count();
396#else
397 TS.tv_nsec = ToTS.tv_nsec;
398#endif
399}
400
401bool SetTimeStructTo(TimeStruct& TS, file_time_type NewTime) {
402 using namespace chrono;
403#if !defined(_LIBCXX_USE_UTIMENSAT)
404 return !FSTime::set_times_checked<microseconds>(&TS.tv_sec, &TS.tv_usec,
405 NewTime);
406#else
407 return !FSTime::set_times_checked<nanoseconds>(&TS.tv_sec, &TS.tv_nsec,
408 NewTime);
409#endif
410}
411
412} // namespace
413} // end namespace detail
414
415_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
416
Eric Fiseliere3081d52018-07-23 03:06:57 +0000417
Eric Fiselierc1699862018-07-20 01:22:32 +0000418
419#endif // FILESYSTEM_COMMON_H