blob: a74cae457ecd65a39a6365627be69711f9265fd6 [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
Eric Fiselierc55ac102018-07-25 20:51:49 +000026#include "../../include/apple_availability.h"
27
28#if !defined(__APPLE__)
Eric Fiselierc1699862018-07-20 01:22:32 +000029// We can use the presence of UTIME_OMIT to detect platforms that provide
30// utimensat.
31#if defined(UTIME_OMIT)
Eric Fiselierc55ac102018-07-25 20:51:49 +000032#define _LIBCPP_USE_UTIMENSAT
Eric Fiselierc1699862018-07-20 01:22:32 +000033#endif
Eric Fiselierc55ac102018-07-25 20:51:49 +000034#endif
Eric Fiselierc1699862018-07-20 01:22:32 +000035
Eric Fiselierc55ac102018-07-25 20:51:49 +000036#if !defined(_LIBCPP_USE_UTIMENSAT)
Eric Fiselierc1699862018-07-20 01:22:32 +000037#include <sys/time.h> // for ::utimes as used in __last_write_time
38#endif
39
40#if !defined(UTIME_OMIT)
41#include <sys/time.h> // for ::utimes as used in __last_write_time
42#endif
43
44#if defined(__GNUC__)
45#pragma GCC diagnostic push
46#pragma GCC diagnostic ignored "-Wunused-function"
47#endif
48
49_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
50
51namespace detail {
52namespace {
53
Eric Fiselierc48dba42018-07-23 11:46:47 +000054static string format_string_imp(const char* msg, ...) {
Eric Fiselier9158bfd2018-07-23 02:00:52 +000055 // we might need a second shot at this, so pre-emptivly make a copy
56 struct GuardVAList {
57 va_list& target;
58 bool active = true;
Eric Fiseliere3081d52018-07-23 03:06:57 +000059 GuardVAList(va_list &target) : target(target), active(true) {}
Eric Fiselier9158bfd2018-07-23 02:00:52 +000060 void clear() {
61 if (active)
62 va_end(target);
63 active = false;
64 }
65 ~GuardVAList() {
66 if (active)
67 va_end(target);
68 }
69 };
70 va_list args;
71 va_start(args, msg);
Eric Fiseliere3081d52018-07-23 03:06:57 +000072 GuardVAList args_guard(args);
Eric Fiselier9158bfd2018-07-23 02:00:52 +000073
74 va_list args_cp;
75 va_copy(args_cp, args);
Eric Fiseliere3081d52018-07-23 03:06:57 +000076 GuardVAList args_copy_guard(args_cp);
Eric Fiselier9158bfd2018-07-23 02:00:52 +000077
Eric Fiselierc48dba42018-07-23 11:46:47 +000078 array<char, 256> local_buff;
79 size_t size = local_buff.size();
Eric Fiselier9158bfd2018-07-23 02:00:52 +000080 auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp);
81
82 args_copy_guard.clear();
83
84 // handle empty expansion
85 if (ret == 0)
Eric Fiselierc48dba42018-07-23 11:46:47 +000086 return string{};
87 if (static_cast<size_t>(ret) < size)
88 return string(local_buff.data());
Eric Fiselier9158bfd2018-07-23 02:00:52 +000089
90 // we did not provide a long enough buffer on our first attempt.
91 // add 1 to size to account for null-byte in size cast to prevent overflow
Eric Fiselierc48dba42018-07-23 11:46:47 +000092 size = static_cast<size_t>(ret) + 1;
93 auto buff_ptr = unique_ptr<char[]>(new char[size]);
Eric Fiselier9158bfd2018-07-23 02:00:52 +000094 ret = ::vsnprintf(buff_ptr.get(), size, msg, args);
Eric Fiselierc48dba42018-07-23 11:46:47 +000095 return string(buff_ptr.get());
Eric Fiselier9158bfd2018-07-23 02:00:52 +000096}
97
98const char* unwrap(string const& s) { return s.c_str(); }
99const char* unwrap(path const& p) { return p.native().c_str(); }
100template <class Arg>
101Arg const& unwrap(Arg const& a) {
102 static_assert(!is_class<Arg>::value, "cannot pass class here");
103 return a;
104}
105
106template <class... Args>
Eric Fiselierc48dba42018-07-23 11:46:47 +0000107string format_string(const char* fmt, Args const&... args) {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000108 return format_string_imp(fmt, unwrap(args)...);
109}
110
Eric Fiselierc48dba42018-07-23 11:46:47 +0000111error_code capture_errno() {
Eric Fiselierc1699862018-07-20 01:22:32 +0000112 _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
Eric Fiselierc48dba42018-07-23 11:46:47 +0000113 return error_code(errno, generic_category());
Eric Fiselierc1699862018-07-20 01:22:32 +0000114}
115
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000116template <class T>
117T error_value();
118template <>
Eric Fiseliere3081d52018-07-23 03:06:57 +0000119_LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {}
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000120template <>
Eric Fiselierd77f3ef2018-07-25 21:01:45 +0000121bool error_value<bool>() {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000122 return false;
123}
124template <>
Eric Fiselierd77f3ef2018-07-25 21:01:45 +0000125uintmax_t error_value<uintmax_t>() {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000126 return uintmax_t(-1);
127}
128template <>
Eric Fiseliere3081d52018-07-23 03:06:57 +0000129_LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000130 return file_time_type::min();
131}
132template <>
133path error_value<path>() {
134 return {};
Eric Fiselierc1699862018-07-20 01:22:32 +0000135}
136
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000137template <class T>
138struct ErrorHandler {
139 const char* func_name;
140 error_code* ec = nullptr;
141 const path* p1 = nullptr;
142 const path* p2 = nullptr;
143
144 ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
145 const path* p2 = nullptr)
146 : func_name(fname), ec(ec), p1(p1), p2(p2) {
147 if (ec)
148 ec->clear();
149 }
150
151 T report(const error_code& m_ec) const {
152 if (ec) {
153 *ec = m_ec;
154 return error_value<T>();
155 }
156 string what = string("in ") + func_name;
157 switch (bool(p1) + bool(p2)) {
158 case 0:
159 __throw_filesystem_error(what, m_ec);
160 case 1:
161 __throw_filesystem_error(what, *p1, m_ec);
162 case 2:
163 __throw_filesystem_error(what, *p1, *p2, m_ec);
164 }
165 _LIBCPP_UNREACHABLE();
166 }
167
168 template <class... Args>
169 T report(const error_code& m_ec, const char* msg, Args const&... args) const {
170 if (ec) {
171 *ec = m_ec;
172 return error_value<T>();
173 }
174 string what =
175 string("in ") + func_name + ": " + format_string(msg, args...);
176 switch (bool(p1) + bool(p2)) {
177 case 0:
178 __throw_filesystem_error(what, m_ec);
179 case 1:
180 __throw_filesystem_error(what, *p1, m_ec);
181 case 2:
182 __throw_filesystem_error(what, *p1, *p2, m_ec);
183 }
184 _LIBCPP_UNREACHABLE();
185 }
186
187 T report(errc const& err) const { return report(make_error_code(err)); }
188
189 template <class... Args>
190 T report(errc const& err, const char* msg, Args const&... args) const {
191 return report(make_error_code(err), msg, args...);
192 }
193
194private:
195 ErrorHandler(ErrorHandler const&) = delete;
196 ErrorHandler& operator=(ErrorHandler const&) = delete;
197};
Eric Fiselierc1699862018-07-20 01:22:32 +0000198
Eric Fiselierc55ac102018-07-25 20:51:49 +0000199using chrono::duration;
200using chrono::duration_cast;
Eric Fiselierc1699862018-07-20 01:22:32 +0000201
Eric Fiselierc55ac102018-07-25 20:51:49 +0000202using TimeSpec = struct ::timespec;
203using StatT = struct ::stat;
Eric Fiselierc1699862018-07-20 01:22:32 +0000204
Eric Fiselierc55ac102018-07-25 20:51:49 +0000205template <class FileTimeT, class TimeT,
Eric Fiselierc1699862018-07-20 01:22:32 +0000206 bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
Eric Fiselierc55ac102018-07-25 20:51:49 +0000207struct time_util_base {
208 using rep = typename FileTimeT::rep;
209 using fs_duration = typename FileTimeT::duration;
210 using fs_seconds = duration<rep>;
211 using fs_nanoseconds = duration<rep, nano>;
212 using fs_microseconds = duration<rep, micro>;
Eric Fiselierc1699862018-07-20 01:22:32 +0000213
Eric Fiselierc55ac102018-07-25 20:51:49 +0000214 static constexpr rep max_seconds =
215 duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
216
217 static constexpr rep max_nsec =
218 duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
219 fs_seconds(max_seconds))
Eric Fiselierc1699862018-07-20 01:22:32 +0000220 .count();
221
Eric Fiselierc55ac102018-07-25 20:51:49 +0000222 static constexpr rep min_seconds =
223 duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
Eric Fiselierc1699862018-07-20 01:22:32 +0000224
Eric Fiselierc55ac102018-07-25 20:51:49 +0000225 static constexpr rep min_nsec_timespec =
226 duration_cast<fs_nanoseconds>(
227 (FileTimeT::duration::min() - fs_seconds(min_seconds)) +
228 fs_seconds(1))
Eric Fiselierc1699862018-07-20 01:22:32 +0000229 .count();
230
Eric Fiselierc55ac102018-07-25 20:51:49 +0000231private:
Eric Fiselierb7e6c1d2018-07-25 22:07:36 +0000232#if _LIBCPP_STD_VER > 11 && !defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
Eric Fiselierc55ac102018-07-25 20:51:49 +0000233 static constexpr fs_duration get_min_nsecs() {
234 return duration_cast<fs_duration>(
235 fs_nanoseconds(min_nsec_timespec) -
236 duration_cast<fs_nanoseconds>(fs_seconds(1)));
237 }
Eric Fiselierc1699862018-07-20 01:22:32 +0000238 // Static assert that these values properly round trip.
Eric Fiselierc55ac102018-07-25 20:51:49 +0000239 static_assert(fs_seconds(min_seconds) + get_min_nsecs() ==
Eric Fiselierc1699862018-07-20 01:22:32 +0000240 FileTimeT::duration::min(),
Eric Fiselierc55ac102018-07-25 20:51:49 +0000241 "value doesn't roundtrip");
242
243 static constexpr bool check_range() {
244 // This kinda sucks, but it's what happens when we don't have __int128_t.
245 if (sizeof(TimeT) == sizeof(rep)) {
246 typedef duration<long long, ratio<3600 * 24 * 365> > Years;
247 return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&
248 duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);
249 }
250 return max_seconds >= numeric_limits<TimeT>::max() &&
251 min_seconds <= numeric_limits<TimeT>::min();
252 }
253 static_assert(check_range(), "the representable range is unacceptable small");
254#endif
Eric Fiselierc1699862018-07-20 01:22:32 +0000255};
256
Eric Fiselierc55ac102018-07-25 20:51:49 +0000257template <class FileTimeT, class TimeT>
258struct time_util_base<FileTimeT, TimeT, true> {
259 using rep = typename FileTimeT::rep;
260 using fs_duration = typename FileTimeT::duration;
261 using fs_seconds = duration<rep>;
262 using fs_nanoseconds = duration<rep, nano>;
263 using fs_microseconds = duration<rep, micro>;
264
265 static const rep max_seconds;
266 static const rep max_nsec;
267 static const rep min_seconds;
268 static const rep min_nsec_timespec;
Eric Fiselierc1699862018-07-20 01:22:32 +0000269};
270
Eric Fiselierc55ac102018-07-25 20:51:49 +0000271template <class FileTimeT, class TimeT>
272const typename FileTimeT::rep
273 time_util_base<FileTimeT, TimeT, true>::max_seconds =
274 duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
Eric Fiselierc1699862018-07-20 01:22:32 +0000275
Eric Fiselierc55ac102018-07-25 20:51:49 +0000276template <class FileTimeT, class TimeT>
277const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =
278 duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
279 fs_seconds(max_seconds))
Eric Fiselierc1699862018-07-20 01:22:32 +0000280 .count();
281
Eric Fiselierc55ac102018-07-25 20:51:49 +0000282template <class FileTimeT, class TimeT>
283const typename FileTimeT::rep
284 time_util_base<FileTimeT, TimeT, true>::min_seconds =
285 duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
Eric Fiselierc1699862018-07-20 01:22:32 +0000286
Eric Fiselierc55ac102018-07-25 20:51:49 +0000287template <class FileTimeT, class TimeT>
288const typename FileTimeT::rep
289 time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =
290 duration_cast<fs_nanoseconds>((FileTimeT::duration::min() -
291 fs_seconds(min_seconds)) +
292 fs_seconds(1))
293 .count();
Eric Fiselierc1699862018-07-20 01:22:32 +0000294
295template <class FileTimeT, class TimeT, class TimeSpecT>
Eric Fiselierc55ac102018-07-25 20:51:49 +0000296struct time_util : time_util_base<FileTimeT, TimeT> {
297 using Base = time_util_base<FileTimeT, TimeT>;
Eric Fiselierc1699862018-07-20 01:22:32 +0000298 using Base::max_nsec;
299 using Base::max_seconds;
300 using Base::min_nsec_timespec;
301 using Base::min_seconds;
302
Eric Fiselierc55ac102018-07-25 20:51:49 +0000303 using typename Base::fs_duration;
304 using typename Base::fs_microseconds;
305 using typename Base::fs_nanoseconds;
306 using typename Base::fs_seconds;
307
Eric Fiselierc1699862018-07-20 01:22:32 +0000308public:
309 template <class CType, class ChronoType>
Eric Fiselierc55ac102018-07-25 20:51:49 +0000310 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out,
311 ChronoType time) {
Eric Fiselierc1699862018-07-20 01:22:32 +0000312 using Lim = numeric_limits<CType>;
313 if (time > Lim::max() || time < Lim::min())
314 return false;
315 *out = static_cast<CType>(time);
316 return true;
317 }
318
319 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
320 if (tm.tv_sec >= 0) {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000321 return tm.tv_sec < max_seconds ||
Eric Fiselierc1699862018-07-20 01:22:32 +0000322 (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
323 } else if (tm.tv_sec == (min_seconds - 1)) {
324 return tm.tv_nsec >= min_nsec_timespec;
325 } else {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000326 return tm.tv_sec >= min_seconds;
Eric Fiselierc1699862018-07-20 01:22:32 +0000327 }
328 }
329
330 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000331 auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());
332 auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);
Eric Fiselierc1699862018-07-20 01:22:32 +0000333 if (nsecs.count() < 0) {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000334 secs = secs + fs_seconds(1);
335 nsecs = nsecs + fs_seconds(1);
Eric Fiselierc1699862018-07-20 01:22:32 +0000336 }
337 using TLim = numeric_limits<TimeT>;
338 if (secs.count() >= 0)
339 return secs.count() <= TLim::max();
340 return secs.count() >= TLim::min();
341 }
342
343 static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
Eric Fiselierc55ac102018-07-25 20:51:49 +0000344 convert_from_timespec(TimeSpecT tm) {
345 if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {
346 return FileTimeT(fs_seconds(tm.tv_sec) +
347 duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));
Eric Fiselierc1699862018-07-20 01:22:32 +0000348 } else { // tm.tv_sec < 0
Eric Fiselierc55ac102018-07-25 20:51:49 +0000349 auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) -
350 fs_nanoseconds(tm.tv_nsec));
351 auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;
Eric Fiselierc1699862018-07-20 01:22:32 +0000352 return FileTimeT(Dur);
353 }
354 }
355
Eric Fiselierc55ac102018-07-25 20:51:49 +0000356 template <class SubSecT>
357 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool
358 set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {
Eric Fiselierc1699862018-07-20 01:22:32 +0000359 auto dur = tp.time_since_epoch();
Eric Fiselierc55ac102018-07-25 20:51:49 +0000360 auto sec_dur = duration_cast<fs_seconds>(dur);
361 auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);
Eric Fiselierc1699862018-07-20 01:22:32 +0000362 // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
363 if (subsec_dur.count() < 0) {
364 if (sec_dur.count() > min_seconds) {
Eric Fiselierce344372018-07-25 21:53:43 +0000365 sec_dur = sec_dur - fs_seconds(1);
366 subsec_dur = subsec_dur + fs_seconds(1);
Eric Fiselierc1699862018-07-20 01:22:32 +0000367 } else {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000368 subsec_dur = fs_nanoseconds::zero();
Eric Fiselierc1699862018-07-20 01:22:32 +0000369 }
370 }
371 return checked_set(sec_out, sec_dur.count()) &&
372 checked_set(subsec_out, subsec_dur.count());
373 }
Eric Fiselierc55ac102018-07-25 20:51:49 +0000374 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest,
375 FileTimeT tp) {
376 if (!is_representable(tp))
377 return false;
378 return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
379 }
Eric Fiselierc1699862018-07-20 01:22:32 +0000380};
381
Eric Fiselierc55ac102018-07-25 20:51:49 +0000382using fs_time = time_util<file_time_type, time_t, TimeSpec>;
Eric Fiselierc1699862018-07-20 01:22:32 +0000383
384#if defined(__APPLE__)
385TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
386TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
387#else
388TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
389TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
390#endif
391
Eric Fiselierc55ac102018-07-25 20:51:49 +0000392bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
393 error_code& ec) {
394#if !defined(_LIBCPP_USE_UTIMENSAT)
395 using namespace chrono;
Alex Lorenz33b4c8a2018-07-25 21:50:44 +0000396 auto Convert = [](long nsec) -> decltype(std::declval<::timeval>().tv_usec) {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000397 return duration_cast<microseconds>(nanoseconds(nsec)).count();
398 };
399 struct ::timeval ConvertedTS[2] = {{TS[0].tv_sec, Convert(TS[0].tv_nsec)},
400 {TS[1].tv_sec, Convert(TS[1].tv_nsec)}};
401 if (::utimes(p.c_str(), ConvertedTS) == -1)
Eric Fiselierc1699862018-07-20 01:22:32 +0000402#else
Eric Fiselierc55ac102018-07-25 20:51:49 +0000403 if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1)
Eric Fiselierc1699862018-07-20 01:22:32 +0000404#endif
405 {
406 ec = capture_errno();
407 return true;
408 }
409 return false;
410}
411
Eric Fiselierc55ac102018-07-25 20:51:49 +0000412bool set_time_spec_to(TimeSpec& TS, file_time_type NewTime) {
413 return !fs_time::set_times_checked(
414 &TS.tv_sec, &TS.tv_nsec, NewTime);
Eric Fiselierc1699862018-07-20 01:22:32 +0000415}
416
417} // namespace
418} // end namespace detail
419
420_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
421
Eric Fiselierc1699862018-07-20 01:22:32 +0000422#endif // FILESYSTEM_COMMON_H