blob: 94f4d41cc61abcd3a9ebcb814f7df24ba711c42e [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>
Eric Fiseliera0a7c1f2018-07-26 03:57:26 +000022#include <sys/time.h> // for ::utimes as used in __last_write_time
Eric Fiselierc1699862018-07-20 01:22:32 +000023#include <fcntl.h> /* values for fchmodat */
24
25#include <experimental/filesystem>
26
Eric Fiselierc55ac102018-07-25 20:51:49 +000027#include "../../include/apple_availability.h"
28
29#if !defined(__APPLE__)
Eric Fiselierc1699862018-07-20 01:22:32 +000030// We can use the presence of UTIME_OMIT to detect platforms that provide
31// utimensat.
32#if defined(UTIME_OMIT)
Eric Fiselierc55ac102018-07-25 20:51:49 +000033#define _LIBCPP_USE_UTIMENSAT
Eric Fiselierc1699862018-07-20 01:22:32 +000034#endif
Eric Fiselierc55ac102018-07-25 20:51:49 +000035#endif
Eric Fiselierc1699862018-07-20 01:22:32 +000036
Eric Fiselierc1699862018-07-20 01:22:32 +000037#if defined(__GNUC__)
38#pragma GCC diagnostic push
39#pragma GCC diagnostic ignored "-Wunused-function"
40#endif
41
42_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
43
44namespace detail {
45namespace {
46
Eric Fiselierc48dba42018-07-23 11:46:47 +000047static string format_string_imp(const char* msg, ...) {
Eric Fiselier9158bfd2018-07-23 02:00:52 +000048 // we might need a second shot at this, so pre-emptivly make a copy
49 struct GuardVAList {
50 va_list& target;
51 bool active = true;
Eric Fiseliere3081d52018-07-23 03:06:57 +000052 GuardVAList(va_list &target) : target(target), active(true) {}
Eric Fiselier9158bfd2018-07-23 02:00:52 +000053 void clear() {
54 if (active)
55 va_end(target);
56 active = false;
57 }
58 ~GuardVAList() {
59 if (active)
60 va_end(target);
61 }
62 };
63 va_list args;
64 va_start(args, msg);
Eric Fiseliere3081d52018-07-23 03:06:57 +000065 GuardVAList args_guard(args);
Eric Fiselier9158bfd2018-07-23 02:00:52 +000066
67 va_list args_cp;
68 va_copy(args_cp, args);
Eric Fiseliere3081d52018-07-23 03:06:57 +000069 GuardVAList args_copy_guard(args_cp);
Eric Fiselier9158bfd2018-07-23 02:00:52 +000070
Eric Fiselierc48dba42018-07-23 11:46:47 +000071 array<char, 256> local_buff;
72 size_t size = local_buff.size();
Eric Fiselier9158bfd2018-07-23 02:00:52 +000073 auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp);
74
75 args_copy_guard.clear();
76
77 // handle empty expansion
78 if (ret == 0)
Eric Fiselierc48dba42018-07-23 11:46:47 +000079 return string{};
80 if (static_cast<size_t>(ret) < size)
81 return string(local_buff.data());
Eric Fiselier9158bfd2018-07-23 02:00:52 +000082
83 // we did not provide a long enough buffer on our first attempt.
84 // add 1 to size to account for null-byte in size cast to prevent overflow
Eric Fiselierc48dba42018-07-23 11:46:47 +000085 size = static_cast<size_t>(ret) + 1;
86 auto buff_ptr = unique_ptr<char[]>(new char[size]);
Eric Fiselier9158bfd2018-07-23 02:00:52 +000087 ret = ::vsnprintf(buff_ptr.get(), size, msg, args);
Eric Fiselierc48dba42018-07-23 11:46:47 +000088 return string(buff_ptr.get());
Eric Fiselier9158bfd2018-07-23 02:00:52 +000089}
90
91const char* unwrap(string const& s) { return s.c_str(); }
92const char* unwrap(path const& p) { return p.native().c_str(); }
93template <class Arg>
94Arg const& unwrap(Arg const& a) {
95 static_assert(!is_class<Arg>::value, "cannot pass class here");
96 return a;
97}
98
99template <class... Args>
Eric Fiselierc48dba42018-07-23 11:46:47 +0000100string format_string(const char* fmt, Args const&... args) {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000101 return format_string_imp(fmt, unwrap(args)...);
102}
103
Eric Fiselierc48dba42018-07-23 11:46:47 +0000104error_code capture_errno() {
Eric Fiselierc1699862018-07-20 01:22:32 +0000105 _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
Eric Fiselierc48dba42018-07-23 11:46:47 +0000106 return error_code(errno, generic_category());
Eric Fiselierc1699862018-07-20 01:22:32 +0000107}
108
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000109template <class T>
110T error_value();
111template <>
Eric Fiseliere3081d52018-07-23 03:06:57 +0000112_LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {}
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000113template <>
Eric Fiselierd77f3ef2018-07-25 21:01:45 +0000114bool error_value<bool>() {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000115 return false;
116}
117template <>
Eric Fiselierd77f3ef2018-07-25 21:01:45 +0000118uintmax_t error_value<uintmax_t>() {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000119 return uintmax_t(-1);
120}
121template <>
Eric Fiseliere3081d52018-07-23 03:06:57 +0000122_LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000123 return file_time_type::min();
124}
125template <>
126path error_value<path>() {
127 return {};
Eric Fiselierc1699862018-07-20 01:22:32 +0000128}
129
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000130template <class T>
131struct ErrorHandler {
132 const char* func_name;
133 error_code* ec = nullptr;
134 const path* p1 = nullptr;
135 const path* p2 = nullptr;
136
137 ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
138 const path* p2 = nullptr)
139 : func_name(fname), ec(ec), p1(p1), p2(p2) {
140 if (ec)
141 ec->clear();
142 }
143
144 T report(const error_code& m_ec) const {
145 if (ec) {
146 *ec = m_ec;
147 return error_value<T>();
148 }
149 string what = string("in ") + func_name;
150 switch (bool(p1) + bool(p2)) {
151 case 0:
152 __throw_filesystem_error(what, m_ec);
153 case 1:
154 __throw_filesystem_error(what, *p1, m_ec);
155 case 2:
156 __throw_filesystem_error(what, *p1, *p2, m_ec);
157 }
158 _LIBCPP_UNREACHABLE();
159 }
160
161 template <class... Args>
162 T report(const error_code& m_ec, const char* msg, Args const&... args) const {
163 if (ec) {
164 *ec = m_ec;
165 return error_value<T>();
166 }
167 string what =
168 string("in ") + func_name + ": " + format_string(msg, args...);
169 switch (bool(p1) + bool(p2)) {
170 case 0:
171 __throw_filesystem_error(what, m_ec);
172 case 1:
173 __throw_filesystem_error(what, *p1, m_ec);
174 case 2:
175 __throw_filesystem_error(what, *p1, *p2, m_ec);
176 }
177 _LIBCPP_UNREACHABLE();
178 }
179
180 T report(errc const& err) const { return report(make_error_code(err)); }
181
182 template <class... Args>
183 T report(errc const& err, const char* msg, Args const&... args) const {
184 return report(make_error_code(err), msg, args...);
185 }
186
187private:
188 ErrorHandler(ErrorHandler const&) = delete;
189 ErrorHandler& operator=(ErrorHandler const&) = delete;
190};
Eric Fiselierc1699862018-07-20 01:22:32 +0000191
Eric Fiselierc55ac102018-07-25 20:51:49 +0000192using chrono::duration;
193using chrono::duration_cast;
Eric Fiselierc1699862018-07-20 01:22:32 +0000194
Eric Fiselierc55ac102018-07-25 20:51:49 +0000195using TimeSpec = struct ::timespec;
196using StatT = struct ::stat;
Eric Fiselierc1699862018-07-20 01:22:32 +0000197
Eric Fiselierc55ac102018-07-25 20:51:49 +0000198template <class FileTimeT, class TimeT,
Eric Fiselierc1699862018-07-20 01:22:32 +0000199 bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
Eric Fiselierc55ac102018-07-25 20:51:49 +0000200struct time_util_base {
201 using rep = typename FileTimeT::rep;
202 using fs_duration = typename FileTimeT::duration;
203 using fs_seconds = duration<rep>;
204 using fs_nanoseconds = duration<rep, nano>;
205 using fs_microseconds = duration<rep, micro>;
Eric Fiselierc1699862018-07-20 01:22:32 +0000206
Eric Fiselierc55ac102018-07-25 20:51:49 +0000207 static constexpr rep max_seconds =
208 duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
209
210 static constexpr rep max_nsec =
211 duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
212 fs_seconds(max_seconds))
Eric Fiselierc1699862018-07-20 01:22:32 +0000213 .count();
214
Eric Fiselierc55ac102018-07-25 20:51:49 +0000215 static constexpr rep min_seconds =
216 duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
Eric Fiselierc1699862018-07-20 01:22:32 +0000217
Eric Fiselierc55ac102018-07-25 20:51:49 +0000218 static constexpr rep min_nsec_timespec =
219 duration_cast<fs_nanoseconds>(
220 (FileTimeT::duration::min() - fs_seconds(min_seconds)) +
221 fs_seconds(1))
Eric Fiselierc1699862018-07-20 01:22:32 +0000222 .count();
223
Eric Fiselierc55ac102018-07-25 20:51:49 +0000224private:
Eric Fiselierb7e6c1d2018-07-25 22:07:36 +0000225#if _LIBCPP_STD_VER > 11 && !defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
Eric Fiselierc55ac102018-07-25 20:51:49 +0000226 static constexpr fs_duration get_min_nsecs() {
227 return duration_cast<fs_duration>(
228 fs_nanoseconds(min_nsec_timespec) -
229 duration_cast<fs_nanoseconds>(fs_seconds(1)));
230 }
Eric Fiselierc1699862018-07-20 01:22:32 +0000231 // Static assert that these values properly round trip.
Eric Fiselierc55ac102018-07-25 20:51:49 +0000232 static_assert(fs_seconds(min_seconds) + get_min_nsecs() ==
Eric Fiselierc1699862018-07-20 01:22:32 +0000233 FileTimeT::duration::min(),
Eric Fiselierc55ac102018-07-25 20:51:49 +0000234 "value doesn't roundtrip");
235
236 static constexpr bool check_range() {
237 // This kinda sucks, but it's what happens when we don't have __int128_t.
238 if (sizeof(TimeT) == sizeof(rep)) {
239 typedef duration<long long, ratio<3600 * 24 * 365> > Years;
240 return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&
241 duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);
242 }
243 return max_seconds >= numeric_limits<TimeT>::max() &&
244 min_seconds <= numeric_limits<TimeT>::min();
245 }
246 static_assert(check_range(), "the representable range is unacceptable small");
247#endif
Eric Fiselierc1699862018-07-20 01:22:32 +0000248};
249
Eric Fiselierc55ac102018-07-25 20:51:49 +0000250template <class FileTimeT, class TimeT>
251struct time_util_base<FileTimeT, TimeT, true> {
252 using rep = typename FileTimeT::rep;
253 using fs_duration = typename FileTimeT::duration;
254 using fs_seconds = duration<rep>;
255 using fs_nanoseconds = duration<rep, nano>;
256 using fs_microseconds = duration<rep, micro>;
257
258 static const rep max_seconds;
259 static const rep max_nsec;
260 static const rep min_seconds;
261 static const rep min_nsec_timespec;
Eric Fiselierc1699862018-07-20 01:22:32 +0000262};
263
Eric Fiselierc55ac102018-07-25 20:51:49 +0000264template <class FileTimeT, class TimeT>
265const typename FileTimeT::rep
266 time_util_base<FileTimeT, TimeT, true>::max_seconds =
267 duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
Eric Fiselierc1699862018-07-20 01:22:32 +0000268
Eric Fiselierc55ac102018-07-25 20:51:49 +0000269template <class FileTimeT, class TimeT>
270const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =
271 duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
272 fs_seconds(max_seconds))
Eric Fiselierc1699862018-07-20 01:22:32 +0000273 .count();
274
Eric Fiselierc55ac102018-07-25 20:51:49 +0000275template <class FileTimeT, class TimeT>
276const typename FileTimeT::rep
277 time_util_base<FileTimeT, TimeT, true>::min_seconds =
278 duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
Eric Fiselierc1699862018-07-20 01:22:32 +0000279
Eric Fiselierc55ac102018-07-25 20:51:49 +0000280template <class FileTimeT, class TimeT>
281const typename FileTimeT::rep
282 time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =
283 duration_cast<fs_nanoseconds>((FileTimeT::duration::min() -
284 fs_seconds(min_seconds)) +
285 fs_seconds(1))
286 .count();
Eric Fiselierc1699862018-07-20 01:22:32 +0000287
288template <class FileTimeT, class TimeT, class TimeSpecT>
Eric Fiselierc55ac102018-07-25 20:51:49 +0000289struct time_util : time_util_base<FileTimeT, TimeT> {
290 using Base = time_util_base<FileTimeT, TimeT>;
Eric Fiselierc1699862018-07-20 01:22:32 +0000291 using Base::max_nsec;
292 using Base::max_seconds;
293 using Base::min_nsec_timespec;
294 using Base::min_seconds;
295
Eric Fiselierc55ac102018-07-25 20:51:49 +0000296 using typename Base::fs_duration;
297 using typename Base::fs_microseconds;
298 using typename Base::fs_nanoseconds;
299 using typename Base::fs_seconds;
300
Eric Fiselierc1699862018-07-20 01:22:32 +0000301public:
302 template <class CType, class ChronoType>
Eric Fiselierc55ac102018-07-25 20:51:49 +0000303 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out,
304 ChronoType time) {
Eric Fiselierc1699862018-07-20 01:22:32 +0000305 using Lim = numeric_limits<CType>;
306 if (time > Lim::max() || time < Lim::min())
307 return false;
308 *out = static_cast<CType>(time);
309 return true;
310 }
311
312 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
313 if (tm.tv_sec >= 0) {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000314 return tm.tv_sec < max_seconds ||
Eric Fiselierc1699862018-07-20 01:22:32 +0000315 (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
316 } else if (tm.tv_sec == (min_seconds - 1)) {
317 return tm.tv_nsec >= min_nsec_timespec;
318 } else {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000319 return tm.tv_sec >= min_seconds;
Eric Fiselierc1699862018-07-20 01:22:32 +0000320 }
321 }
322
323 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000324 auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());
325 auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);
Eric Fiselierc1699862018-07-20 01:22:32 +0000326 if (nsecs.count() < 0) {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000327 secs = secs + fs_seconds(1);
328 nsecs = nsecs + fs_seconds(1);
Eric Fiselierc1699862018-07-20 01:22:32 +0000329 }
330 using TLim = numeric_limits<TimeT>;
331 if (secs.count() >= 0)
332 return secs.count() <= TLim::max();
333 return secs.count() >= TLim::min();
334 }
335
336 static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
Eric Fiselierc55ac102018-07-25 20:51:49 +0000337 convert_from_timespec(TimeSpecT tm) {
338 if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {
339 return FileTimeT(fs_seconds(tm.tv_sec) +
340 duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));
Eric Fiselierc1699862018-07-20 01:22:32 +0000341 } else { // tm.tv_sec < 0
Eric Fiselierc55ac102018-07-25 20:51:49 +0000342 auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) -
343 fs_nanoseconds(tm.tv_nsec));
344 auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;
Eric Fiselierc1699862018-07-20 01:22:32 +0000345 return FileTimeT(Dur);
346 }
347 }
348
Eric Fiselierc55ac102018-07-25 20:51:49 +0000349 template <class SubSecT>
350 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool
351 set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {
Eric Fiselierc1699862018-07-20 01:22:32 +0000352 auto dur = tp.time_since_epoch();
Eric Fiselierc55ac102018-07-25 20:51:49 +0000353 auto sec_dur = duration_cast<fs_seconds>(dur);
354 auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);
Eric Fiselierc1699862018-07-20 01:22:32 +0000355 // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
356 if (subsec_dur.count() < 0) {
357 if (sec_dur.count() > min_seconds) {
Eric Fiselierce344372018-07-25 21:53:43 +0000358 sec_dur = sec_dur - fs_seconds(1);
359 subsec_dur = subsec_dur + fs_seconds(1);
Eric Fiselierc1699862018-07-20 01:22:32 +0000360 } else {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000361 subsec_dur = fs_nanoseconds::zero();
Eric Fiselierc1699862018-07-20 01:22:32 +0000362 }
363 }
364 return checked_set(sec_out, sec_dur.count()) &&
365 checked_set(subsec_out, subsec_dur.count());
366 }
Eric Fiselierc55ac102018-07-25 20:51:49 +0000367 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest,
368 FileTimeT tp) {
369 if (!is_representable(tp))
370 return false;
371 return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
372 }
Eric Fiselierc1699862018-07-20 01:22:32 +0000373};
374
Eric Fiselierc55ac102018-07-25 20:51:49 +0000375using fs_time = time_util<file_time_type, time_t, TimeSpec>;
Eric Fiselierc1699862018-07-20 01:22:32 +0000376
377#if defined(__APPLE__)
378TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
379TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
380#else
381TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
382TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
383#endif
384
Eric Fiseliera0a7c1f2018-07-26 03:57:26 +0000385// allow the utimes implementation to compile even it we're not going
386// to use it.
387
388bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS,
Eric Fiselierc55ac102018-07-25 20:51:49 +0000389 error_code& ec) {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000390 using namespace chrono;
Alex Lorenz70cf5c42018-07-25 23:59:54 +0000391 auto Convert = [](long nsec) {
392 using int_type = decltype(std::declval<::timeval>().tv_usec);
393 auto dur = duration_cast<microseconds>(nanoseconds(nsec)).count();
394 return static_cast<int_type>(dur);
Eric Fiselierc55ac102018-07-25 20:51:49 +0000395 };
396 struct ::timeval ConvertedTS[2] = {{TS[0].tv_sec, Convert(TS[0].tv_nsec)},
397 {TS[1].tv_sec, Convert(TS[1].tv_nsec)}};
Eric Fiseliera0a7c1f2018-07-26 03:57:26 +0000398 if (::utimes(p.c_str(), ConvertedTS) == -1) {
Eric Fiselierc1699862018-07-20 01:22:32 +0000399 ec = capture_errno();
400 return true;
401 }
402 return false;
403}
404
Eric Fiseliera0a7c1f2018-07-26 03:57:26 +0000405#if defined(_LIBCPP_USE_UTIMENSAT)
406bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS,
407 error_code& ec) {
408 if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1)
409 {
410 ec = capture_errno();
411 return true;
412 }
413 return false;
Eric Fiselierc1699862018-07-20 01:22:32 +0000414}
Eric Fiseliera0a7c1f2018-07-26 03:57:26 +0000415#endif
416
417bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
418 error_code& ec) {
419#if !defined(_LIBCPP_USE_UTIMENSAT)
420 return posix_utimes(p, TS, ec);
421#else
422 return posix_utimensat(p, TS, ec);
423#endif
424}
425
Eric Fiselierc1699862018-07-20 01:22:32 +0000426
427} // namespace
428} // end namespace detail
429
430_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
431
Eric Fiselierc1699862018-07-20 01:22:32 +0000432#endif // FILESYSTEM_COMMON_H