blob: 5b1f4482a75ec9337db010c81522871ea505a6a7 [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;
75 void clear() {
76 if (active)
77 va_end(target);
78 active = false;
79 }
80 ~GuardVAList() {
81 if (active)
82 va_end(target);
83 }
84 };
85 va_list args;
86 va_start(args, msg);
87 GuardVAList args_guard = {args};
88
89 va_list args_cp;
90 va_copy(args_cp, args);
91 GuardVAList args_copy_guard = {args_cp};
92
93 std::array<char, 256> local_buff;
94 std::size_t size = local_buff.size();
95 auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp);
96
97 args_copy_guard.clear();
98
99 // handle empty expansion
100 if (ret == 0)
101 return std::string{};
102 if (static_cast<std::size_t>(ret) < size)
103 return std::string(local_buff.data());
104
105 // we did not provide a long enough buffer on our first attempt.
106 // add 1 to size to account for null-byte in size cast to prevent overflow
107 size = static_cast<std::size_t>(ret) + 1;
108 auto buff_ptr = std::unique_ptr<char[]>(new char[size]);
109 ret = ::vsnprintf(buff_ptr.get(), size, msg, args);
110 return std::string(buff_ptr.get());
111}
112
113const char* unwrap(string const& s) { return s.c_str(); }
114const char* unwrap(path const& p) { return p.native().c_str(); }
115template <class Arg>
116Arg const& unwrap(Arg const& a) {
117 static_assert(!is_class<Arg>::value, "cannot pass class here");
118 return a;
119}
120
121template <class... Args>
122std::string format_string(const char* fmt, Args const&... args) {
123 return format_string_imp(fmt, unwrap(args)...);
124}
125
Eric Fiselierc1699862018-07-20 01:22:32 +0000126std::error_code capture_errno() {
127 _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
128 return std::error_code(errno, std::generic_category());
129}
130
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000131template <class T>
132T error_value();
133template <>
134constexpr void error_value<void>() {}
135template <>
136constexpr bool error_value<bool>() {
137 return false;
138}
139template <>
140constexpr uintmax_t error_value<uintmax_t>() {
141 return uintmax_t(-1);
142}
143template <>
144constexpr file_time_type error_value<file_time_type>() {
145 return file_time_type::min();
146}
147template <>
148path error_value<path>() {
149 return {};
Eric Fiselierc1699862018-07-20 01:22:32 +0000150}
151
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000152template <class T>
153struct ErrorHandler {
154 const char* func_name;
155 error_code* ec = nullptr;
156 const path* p1 = nullptr;
157 const path* p2 = nullptr;
158
159 ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
160 const path* p2 = nullptr)
161 : func_name(fname), ec(ec), p1(p1), p2(p2) {
162 if (ec)
163 ec->clear();
164 }
165
166 T report(const error_code& m_ec) const {
167 if (ec) {
168 *ec = m_ec;
169 return error_value<T>();
170 }
171 string what = string("in ") + func_name;
172 switch (bool(p1) + bool(p2)) {
173 case 0:
174 __throw_filesystem_error(what, m_ec);
175 case 1:
176 __throw_filesystem_error(what, *p1, m_ec);
177 case 2:
178 __throw_filesystem_error(what, *p1, *p2, m_ec);
179 }
180 _LIBCPP_UNREACHABLE();
181 }
182
183 template <class... Args>
184 T report(const error_code& m_ec, const char* msg, Args const&... args) const {
185 if (ec) {
186 *ec = m_ec;
187 return error_value<T>();
188 }
189 string what =
190 string("in ") + func_name + ": " + format_string(msg, args...);
191 switch (bool(p1) + bool(p2)) {
192 case 0:
193 __throw_filesystem_error(what, m_ec);
194 case 1:
195 __throw_filesystem_error(what, *p1, m_ec);
196 case 2:
197 __throw_filesystem_error(what, *p1, *p2, m_ec);
198 }
199 _LIBCPP_UNREACHABLE();
200 }
201
202 T report(errc const& err) const { return report(make_error_code(err)); }
203
204 template <class... Args>
205 T report(errc const& err, const char* msg, Args const&... args) const {
206 return report(make_error_code(err), msg, args...);
207 }
208
209private:
210 ErrorHandler(ErrorHandler const&) = delete;
211 ErrorHandler& operator=(ErrorHandler const&) = delete;
212};
Eric Fiselierc1699862018-07-20 01:22:32 +0000213
214namespace time_util {
215
216using namespace chrono;
217
218template <class FileTimeT,
219 bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
220struct fs_time_util_base {
Eric Fiselier6d4be632018-07-22 21:56:40 +0000221 static constexpr seconds::rep max_seconds =
Eric Fiselierc1699862018-07-20 01:22:32 +0000222 duration_cast<seconds>(FileTimeT::duration::max()).count();
223
Eric Fiselier6d4be632018-07-22 21:56:40 +0000224 static constexpr nanoseconds::rep max_nsec =
Eric Fiselierc1699862018-07-20 01:22:32 +0000225 duration_cast<nanoseconds>(FileTimeT::duration::max() -
226 seconds(max_seconds))
227 .count();
228
Eric Fiselier6d4be632018-07-22 21:56:40 +0000229 static constexpr seconds::rep min_seconds =
Eric Fiselierc1699862018-07-20 01:22:32 +0000230 duration_cast<seconds>(FileTimeT::duration::min()).count();
231
Eric Fiselier6d4be632018-07-22 21:56:40 +0000232 static constexpr nanoseconds::rep min_nsec_timespec =
Eric Fiselierc1699862018-07-20 01:22:32 +0000233 duration_cast<nanoseconds>(
234 (FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1))
235 .count();
236
237 // Static assert that these values properly round trip.
238 static_assert((seconds(min_seconds) +
239 duration_cast<microseconds>(nanoseconds(min_nsec_timespec))) -
240 duration_cast<microseconds>(seconds(1)) ==
241 FileTimeT::duration::min(),
242 "");
243};
244
245template <class FileTimeT>
246struct fs_time_util_base<FileTimeT, true> {
247 static const long long max_seconds;
248 static const long long max_nsec;
249 static const long long min_seconds;
250 static const long long min_nsec_timespec;
251};
252
253template <class FileTimeT>
254const long long fs_time_util_base<FileTimeT, true>::max_seconds =
255 duration_cast<seconds>(FileTimeT::duration::max()).count();
256
257template <class FileTimeT>
258const long long fs_time_util_base<FileTimeT, true>::max_nsec =
259 duration_cast<nanoseconds>(FileTimeT::duration::max() -
260 seconds(max_seconds))
261 .count();
262
263template <class FileTimeT>
264const long long fs_time_util_base<FileTimeT, true>::min_seconds =
265 duration_cast<seconds>(FileTimeT::duration::min()).count();
266
267template <class FileTimeT>
268const long long fs_time_util_base<FileTimeT, true>::min_nsec_timespec =
269 duration_cast<nanoseconds>(
270 (FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1))
271 .count();
272
273template <class FileTimeT, class TimeT, class TimeSpecT>
274struct fs_time_util : fs_time_util_base<FileTimeT> {
275 using Base = fs_time_util_base<FileTimeT>;
276 using Base::max_nsec;
277 using Base::max_seconds;
278 using Base::min_nsec_timespec;
279 using Base::min_seconds;
280
281public:
282 template <class CType, class ChronoType>
283 static bool checked_set(CType* out, ChronoType time) {
284 using Lim = numeric_limits<CType>;
285 if (time > Lim::max() || time < Lim::min())
286 return false;
287 *out = static_cast<CType>(time);
288 return true;
289 }
290
291 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
292 if (tm.tv_sec >= 0) {
293 return (tm.tv_sec < max_seconds) ||
294 (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
295 } else if (tm.tv_sec == (min_seconds - 1)) {
296 return tm.tv_nsec >= min_nsec_timespec;
297 } else {
298 return (tm.tv_sec >= min_seconds);
299 }
300 }
301
302 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
303 auto secs = duration_cast<seconds>(tm.time_since_epoch());
304 auto nsecs = duration_cast<nanoseconds>(tm.time_since_epoch() - secs);
305 if (nsecs.count() < 0) {
306 secs = secs + seconds(1);
307 nsecs = nsecs + seconds(1);
308 }
309 using TLim = numeric_limits<TimeT>;
310 if (secs.count() >= 0)
311 return secs.count() <= TLim::max();
312 return secs.count() >= TLim::min();
313 }
314
315 static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
316 convert_timespec(TimeSpecT tm) {
317 auto adj_msec = duration_cast<microseconds>(nanoseconds(tm.tv_nsec));
318 if (tm.tv_sec >= 0) {
319 auto Dur = seconds(tm.tv_sec) + microseconds(adj_msec);
320 return FileTimeT(Dur);
321 } else if (duration_cast<microseconds>(nanoseconds(tm.tv_nsec)).count() ==
322 0) {
323 return FileTimeT(seconds(tm.tv_sec));
324 } else { // tm.tv_sec < 0
325 auto adj_subsec =
326 duration_cast<microseconds>(seconds(1) - nanoseconds(tm.tv_nsec));
327 auto Dur = seconds(tm.tv_sec + 1) - adj_subsec;
328 return FileTimeT(Dur);
329 }
330 }
331
332 template <class SubSecDurT, class SubSecT>
333 static bool set_times_checked(TimeT* sec_out, SubSecT* subsec_out,
334 FileTimeT tp) {
335 auto dur = tp.time_since_epoch();
336 auto sec_dur = duration_cast<seconds>(dur);
337 auto subsec_dur = duration_cast<SubSecDurT>(dur - sec_dur);
338 // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
339 if (subsec_dur.count() < 0) {
340 if (sec_dur.count() > min_seconds) {
341 sec_dur -= seconds(1);
342 subsec_dur += seconds(1);
343 } else {
344 subsec_dur = SubSecDurT::zero();
345 }
346 }
347 return checked_set(sec_out, sec_dur.count()) &&
348 checked_set(subsec_out, subsec_dur.count());
349 }
350};
351
352} // namespace time_util
353
354
Eric Fiselier41cdcbe2018-07-20 01:44:33 +0000355using TimeSpec = struct ::timespec;
356using StatT = struct ::stat;
Eric Fiselierc1699862018-07-20 01:22:32 +0000357
358using FSTime = time_util::fs_time_util<file_time_type, time_t, struct timespec>;
359
360#if defined(__APPLE__)
361TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
362TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
363#else
364TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
365TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
366#endif
367
368#if !defined(_LIBCXX_USE_UTIMENSAT)
369using TimeStruct = struct ::timeval;
370using TimeStructArray = TimeStruct[2];
371#else
372using TimeStruct = struct ::timespec;
373using TimeStructArray = TimeStruct[2];
374#endif
375
376bool SetFileTimes(const path& p, TimeStructArray const& TS,
377 std::error_code& ec) {
378#if !defined(_LIBCXX_USE_UTIMENSAT)
379 if (::utimes(p.c_str(), TS) == -1)
380#else
381 if (::utimensat(AT_FDCWD, p.c_str(), TS, 0) == -1)
382#endif
383 {
384 ec = capture_errno();
385 return true;
386 }
387 return false;
388}
389
390void SetTimeStructTo(TimeStruct& TS, TimeSpec ToTS) {
391 using namespace chrono;
392 TS.tv_sec = ToTS.tv_sec;
393#if !defined(_LIBCXX_USE_UTIMENSAT)
394 TS.tv_usec = duration_cast<microseconds>(nanoseconds(ToTS.tv_nsec)).count();
395#else
396 TS.tv_nsec = ToTS.tv_nsec;
397#endif
398}
399
400bool SetTimeStructTo(TimeStruct& TS, file_time_type NewTime) {
401 using namespace chrono;
402#if !defined(_LIBCXX_USE_UTIMENSAT)
403 return !FSTime::set_times_checked<microseconds>(&TS.tv_sec, &TS.tv_usec,
404 NewTime);
405#else
406 return !FSTime::set_times_checked<nanoseconds>(&TS.tv_sec, &TS.tv_nsec,
407 NewTime);
408#endif
409}
410
411} // namespace
412} // end namespace detail
413
414_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
415
416#if defined(__GNUC__)
417#pragma GCC diagnostic pop
418#endif
419
420#endif // FILESYSTEM_COMMON_H