blob: fe5c42f5e6d0b30fa239ceaa7bca1c4b844b6f5e [file] [log] [blame]
Eric Fiselierc1699862018-07-20 01:22:32 +00001//===----------------------------------------------------------------------===////
2//
Chandler Carruth57b08b02019-01-19 10:56:40 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Eric Fiselierc1699862018-07-20 01:22:32 +00006//
7//===----------------------------------------------------------------------===////
8
9#ifndef FILESYSTEM_COMMON_H
10#define FILESYSTEM_COMMON_H
11
Eric Fiselier998a5c82018-07-27 03:07:09 +000012#include "__config"
13#include "filesystem"
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 Fiselier998a5c82018-07-27 03:07:09 +000023#include <fcntl.h> /* values for fchmodat */
Eric Fiselierc1699862018-07-20 01:22:32 +000024
Eric Fiselier998a5c82018-07-27 03:07:09 +000025#include "../include/apple_availability.h"
Eric Fiselierc55ac102018-07-25 20:51:49 +000026
27#if !defined(__APPLE__)
Eric Fiselierc1699862018-07-20 01:22:32 +000028// We can use the presence of UTIME_OMIT to detect platforms that provide
29// utimensat.
30#if defined(UTIME_OMIT)
Eric Fiselierc55ac102018-07-25 20:51:49 +000031#define _LIBCPP_USE_UTIMENSAT
Eric Fiselierc1699862018-07-20 01:22:32 +000032#endif
Eric Fiselierc55ac102018-07-25 20:51:49 +000033#endif
Eric Fiselierc1699862018-07-20 01:22:32 +000034
Eric Fiselierc1699862018-07-20 01:22:32 +000035#if defined(__GNUC__)
36#pragma GCC diagnostic push
37#pragma GCC diagnostic ignored "-Wunused-function"
38#endif
39
Eric Fiselier998a5c82018-07-27 03:07:09 +000040_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
Eric Fiselierc1699862018-07-20 01:22:32 +000041
42namespace detail {
43namespace {
44
Eric Fiselierc48dba42018-07-23 11:46:47 +000045static string format_string_imp(const char* msg, ...) {
Eric Fiselier9158bfd2018-07-23 02:00:52 +000046 // we might need a second shot at this, so pre-emptivly make a copy
47 struct GuardVAList {
48 va_list& target;
49 bool active = true;
Eric Fiselier998a5c82018-07-27 03:07:09 +000050 GuardVAList(va_list& target) : target(target), active(true) {}
Eric Fiselier9158bfd2018-07-23 02:00:52 +000051 void clear() {
52 if (active)
53 va_end(target);
54 active = false;
55 }
56 ~GuardVAList() {
57 if (active)
58 va_end(target);
59 }
60 };
61 va_list args;
62 va_start(args, msg);
Eric Fiseliere3081d52018-07-23 03:06:57 +000063 GuardVAList args_guard(args);
Eric Fiselier9158bfd2018-07-23 02:00:52 +000064
65 va_list args_cp;
66 va_copy(args_cp, args);
Eric Fiseliere3081d52018-07-23 03:06:57 +000067 GuardVAList args_copy_guard(args_cp);
Eric Fiselier9158bfd2018-07-23 02:00:52 +000068
Eric Fiselier01a87ef2018-11-26 20:15:38 +000069 std::string result;
70
Eric Fiselierc48dba42018-07-23 11:46:47 +000071 array<char, 256> local_buff;
Eric Fiselier01a87ef2018-11-26 20:15:38 +000072 size_t size_with_null = local_buff.size();
73 auto ret = ::vsnprintf(local_buff.data(), size_with_null, msg, args_cp);
Eric Fiselier9158bfd2018-07-23 02:00:52 +000074
75 args_copy_guard.clear();
76
77 // handle empty expansion
78 if (ret == 0)
Eric Fiselier01a87ef2018-11-26 20:15:38 +000079 return result;
80 if (static_cast<size_t>(ret) < size_with_null) {
81 result.assign(local_buff.data(), static_cast<size_t>(ret));
82 return result;
83 }
Eric Fiselier9158bfd2018-07-23 02:00:52 +000084
Eric Fiselier01a87ef2018-11-26 20:15:38 +000085 // we did not provide a long enough buffer on our first attempt. The
86 // return value is the number of bytes (excluding the null byte) that are
87 // needed for formatting.
88 size_with_null = static_cast<size_t>(ret) + 1;
89 result.__resize_default_init(size_with_null - 1);
90 ret = ::vsnprintf(&result[0], size_with_null, msg, args);
91 _LIBCPP_ASSERT(static_cast<size_t>(ret) == (size_with_null - 1), "TODO");
92
93 return result;
Eric Fiselier9158bfd2018-07-23 02:00:52 +000094}
95
96const char* unwrap(string const& s) { return s.c_str(); }
97const char* unwrap(path const& p) { return p.native().c_str(); }
98template <class Arg>
99Arg const& unwrap(Arg const& a) {
100 static_assert(!is_class<Arg>::value, "cannot pass class here");
101 return a;
102}
103
104template <class... Args>
Eric Fiselierc48dba42018-07-23 11:46:47 +0000105string format_string(const char* fmt, Args const&... args) {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000106 return format_string_imp(fmt, unwrap(args)...);
107}
108
Eric Fiselierc48dba42018-07-23 11:46:47 +0000109error_code capture_errno() {
Eric Fiselierc1699862018-07-20 01:22:32 +0000110 _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
Eric Fiselierc48dba42018-07-23 11:46:47 +0000111 return error_code(errno, generic_category());
Eric Fiselierc1699862018-07-20 01:22:32 +0000112}
113
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000114template <class T>
115T error_value();
116template <>
Eric Fiseliere3081d52018-07-23 03:06:57 +0000117_LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {}
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000118template <>
Eric Fiselierd77f3ef2018-07-25 21:01:45 +0000119bool error_value<bool>() {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000120 return false;
121}
122template <>
Eric Fiselierd77f3ef2018-07-25 21:01:45 +0000123uintmax_t error_value<uintmax_t>() {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000124 return uintmax_t(-1);
125}
126template <>
Eric Fiseliere3081d52018-07-23 03:06:57 +0000127_LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() {
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000128 return file_time_type::min();
129}
130template <>
131path error_value<path>() {
132 return {};
Eric Fiselierc1699862018-07-20 01:22:32 +0000133}
134
Eric Fiselier9158bfd2018-07-23 02:00:52 +0000135template <class T>
136struct ErrorHandler {
137 const char* func_name;
138 error_code* ec = nullptr;
139 const path* p1 = nullptr;
140 const path* p2 = nullptr;
141
142 ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
143 const path* p2 = nullptr)
144 : func_name(fname), ec(ec), p1(p1), p2(p2) {
145 if (ec)
146 ec->clear();
147 }
148
149 T report(const error_code& m_ec) const {
150 if (ec) {
151 *ec = m_ec;
152 return error_value<T>();
153 }
154 string what = string("in ") + func_name;
155 switch (bool(p1) + bool(p2)) {
156 case 0:
157 __throw_filesystem_error(what, m_ec);
158 case 1:
159 __throw_filesystem_error(what, *p1, m_ec);
160 case 2:
161 __throw_filesystem_error(what, *p1, *p2, m_ec);
162 }
163 _LIBCPP_UNREACHABLE();
164 }
165
166 template <class... Args>
167 T report(const error_code& m_ec, const char* msg, Args const&... args) const {
168 if (ec) {
169 *ec = m_ec;
170 return error_value<T>();
171 }
172 string what =
173 string("in ") + func_name + ": " + format_string(msg, args...);
174 switch (bool(p1) + bool(p2)) {
175 case 0:
176 __throw_filesystem_error(what, m_ec);
177 case 1:
178 __throw_filesystem_error(what, *p1, m_ec);
179 case 2:
180 __throw_filesystem_error(what, *p1, *p2, m_ec);
181 }
182 _LIBCPP_UNREACHABLE();
183 }
184
185 T report(errc const& err) const { return report(make_error_code(err)); }
186
187 template <class... Args>
188 T report(errc const& err, const char* msg, Args const&... args) const {
189 return report(make_error_code(err), msg, args...);
190 }
191
192private:
193 ErrorHandler(ErrorHandler const&) = delete;
194 ErrorHandler& operator=(ErrorHandler const&) = delete;
195};
Eric Fiselierc1699862018-07-20 01:22:32 +0000196
Eric Fiselierc55ac102018-07-25 20:51:49 +0000197using chrono::duration;
198using chrono::duration_cast;
Eric Fiselierc1699862018-07-20 01:22:32 +0000199
Eric Fiselierc55ac102018-07-25 20:51:49 +0000200using TimeSpec = struct ::timespec;
201using StatT = struct ::stat;
Eric Fiselierc1699862018-07-20 01:22:32 +0000202
Eric Fiselierc55ac102018-07-25 20:51:49 +0000203template <class FileTimeT, class TimeT,
Eric Fiselierc1699862018-07-20 01:22:32 +0000204 bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
Eric Fiselierc55ac102018-07-25 20:51:49 +0000205struct time_util_base {
206 using rep = typename FileTimeT::rep;
207 using fs_duration = typename FileTimeT::duration;
208 using fs_seconds = duration<rep>;
209 using fs_nanoseconds = duration<rep, nano>;
210 using fs_microseconds = duration<rep, micro>;
Eric Fiselierc1699862018-07-20 01:22:32 +0000211
Eric Fiselierc55ac102018-07-25 20:51:49 +0000212 static constexpr rep max_seconds =
213 duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
214
215 static constexpr rep max_nsec =
216 duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
217 fs_seconds(max_seconds))
Eric Fiselierc1699862018-07-20 01:22:32 +0000218 .count();
219
Eric Fiselierc55ac102018-07-25 20:51:49 +0000220 static constexpr rep min_seconds =
221 duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
Eric Fiselierc1699862018-07-20 01:22:32 +0000222
Eric Fiselierc55ac102018-07-25 20:51:49 +0000223 static constexpr rep min_nsec_timespec =
224 duration_cast<fs_nanoseconds>(
225 (FileTimeT::duration::min() - fs_seconds(min_seconds)) +
226 fs_seconds(1))
Eric Fiselierc1699862018-07-20 01:22:32 +0000227 .count();
228
Eric Fiselierc55ac102018-07-25 20:51:49 +0000229private:
Eric Fiselierb7e6c1d2018-07-25 22:07:36 +0000230#if _LIBCPP_STD_VER > 11 && !defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
Eric Fiselierc55ac102018-07-25 20:51:49 +0000231 static constexpr fs_duration get_min_nsecs() {
232 return duration_cast<fs_duration>(
233 fs_nanoseconds(min_nsec_timespec) -
234 duration_cast<fs_nanoseconds>(fs_seconds(1)));
235 }
Eric Fiselierc1699862018-07-20 01:22:32 +0000236 // Static assert that these values properly round trip.
Eric Fiselierc55ac102018-07-25 20:51:49 +0000237 static_assert(fs_seconds(min_seconds) + get_min_nsecs() ==
Eric Fiselierc1699862018-07-20 01:22:32 +0000238 FileTimeT::duration::min(),
Eric Fiselierc55ac102018-07-25 20:51:49 +0000239 "value doesn't roundtrip");
240
241 static constexpr bool check_range() {
242 // This kinda sucks, but it's what happens when we don't have __int128_t.
243 if (sizeof(TimeT) == sizeof(rep)) {
244 typedef duration<long long, ratio<3600 * 24 * 365> > Years;
245 return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&
246 duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);
247 }
248 return max_seconds >= numeric_limits<TimeT>::max() &&
249 min_seconds <= numeric_limits<TimeT>::min();
250 }
251 static_assert(check_range(), "the representable range is unacceptable small");
252#endif
Eric Fiselierc1699862018-07-20 01:22:32 +0000253};
254
Eric Fiselierc55ac102018-07-25 20:51:49 +0000255template <class FileTimeT, class TimeT>
256struct time_util_base<FileTimeT, TimeT, true> {
257 using rep = typename FileTimeT::rep;
258 using fs_duration = typename FileTimeT::duration;
259 using fs_seconds = duration<rep>;
260 using fs_nanoseconds = duration<rep, nano>;
261 using fs_microseconds = duration<rep, micro>;
262
263 static const rep max_seconds;
264 static const rep max_nsec;
265 static const rep min_seconds;
266 static const rep min_nsec_timespec;
Eric Fiselierc1699862018-07-20 01:22:32 +0000267};
268
Eric Fiselierc55ac102018-07-25 20:51:49 +0000269template <class FileTimeT, class TimeT>
270const typename FileTimeT::rep
271 time_util_base<FileTimeT, TimeT, true>::max_seconds =
272 duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
Eric Fiselierc1699862018-07-20 01:22:32 +0000273
Eric Fiselierc55ac102018-07-25 20:51:49 +0000274template <class FileTimeT, class TimeT>
275const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =
276 duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
277 fs_seconds(max_seconds))
Eric Fiselierc1699862018-07-20 01:22:32 +0000278 .count();
279
Eric Fiselierc55ac102018-07-25 20:51:49 +0000280template <class FileTimeT, class TimeT>
281const typename FileTimeT::rep
282 time_util_base<FileTimeT, TimeT, true>::min_seconds =
283 duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
Eric Fiselierc1699862018-07-20 01:22:32 +0000284
Eric Fiselierc55ac102018-07-25 20:51:49 +0000285template <class FileTimeT, class TimeT>
286const typename FileTimeT::rep
287 time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =
288 duration_cast<fs_nanoseconds>((FileTimeT::duration::min() -
289 fs_seconds(min_seconds)) +
290 fs_seconds(1))
291 .count();
Eric Fiselierc1699862018-07-20 01:22:32 +0000292
293template <class FileTimeT, class TimeT, class TimeSpecT>
Eric Fiselierc55ac102018-07-25 20:51:49 +0000294struct time_util : time_util_base<FileTimeT, TimeT> {
295 using Base = time_util_base<FileTimeT, TimeT>;
Eric Fiselierc1699862018-07-20 01:22:32 +0000296 using Base::max_nsec;
297 using Base::max_seconds;
298 using Base::min_nsec_timespec;
299 using Base::min_seconds;
300
Eric Fiselierc55ac102018-07-25 20:51:49 +0000301 using typename Base::fs_duration;
302 using typename Base::fs_microseconds;
303 using typename Base::fs_nanoseconds;
304 using typename Base::fs_seconds;
305
Eric Fiselierc1699862018-07-20 01:22:32 +0000306public:
307 template <class CType, class ChronoType>
Eric Fiselierc55ac102018-07-25 20:51:49 +0000308 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out,
309 ChronoType time) {
Eric Fiselierc1699862018-07-20 01:22:32 +0000310 using Lim = numeric_limits<CType>;
311 if (time > Lim::max() || time < Lim::min())
312 return false;
313 *out = static_cast<CType>(time);
314 return true;
315 }
316
317 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
318 if (tm.tv_sec >= 0) {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000319 return tm.tv_sec < max_seconds ||
Eric Fiselierc1699862018-07-20 01:22:32 +0000320 (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
321 } else if (tm.tv_sec == (min_seconds - 1)) {
322 return tm.tv_nsec >= min_nsec_timespec;
323 } else {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000324 return tm.tv_sec >= min_seconds;
Eric Fiselierc1699862018-07-20 01:22:32 +0000325 }
326 }
327
328 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000329 auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());
330 auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);
Eric Fiselierc1699862018-07-20 01:22:32 +0000331 if (nsecs.count() < 0) {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000332 secs = secs + fs_seconds(1);
333 nsecs = nsecs + fs_seconds(1);
Eric Fiselierc1699862018-07-20 01:22:32 +0000334 }
335 using TLim = numeric_limits<TimeT>;
336 if (secs.count() >= 0)
337 return secs.count() <= TLim::max();
338 return secs.count() >= TLim::min();
339 }
340
341 static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
Eric Fiselierc55ac102018-07-25 20:51:49 +0000342 convert_from_timespec(TimeSpecT tm) {
343 if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {
344 return FileTimeT(fs_seconds(tm.tv_sec) +
345 duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));
Eric Fiselierc1699862018-07-20 01:22:32 +0000346 } else { // tm.tv_sec < 0
Eric Fiselierc55ac102018-07-25 20:51:49 +0000347 auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) -
348 fs_nanoseconds(tm.tv_nsec));
349 auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;
Eric Fiselierc1699862018-07-20 01:22:32 +0000350 return FileTimeT(Dur);
351 }
352 }
353
Eric Fiselierc55ac102018-07-25 20:51:49 +0000354 template <class SubSecT>
355 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool
356 set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {
Eric Fiselierc1699862018-07-20 01:22:32 +0000357 auto dur = tp.time_since_epoch();
Eric Fiselierc55ac102018-07-25 20:51:49 +0000358 auto sec_dur = duration_cast<fs_seconds>(dur);
359 auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);
Eric Fiselierc1699862018-07-20 01:22:32 +0000360 // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
361 if (subsec_dur.count() < 0) {
362 if (sec_dur.count() > min_seconds) {
Eric Fiselierce344372018-07-25 21:53:43 +0000363 sec_dur = sec_dur - fs_seconds(1);
364 subsec_dur = subsec_dur + fs_seconds(1);
Eric Fiselierc1699862018-07-20 01:22:32 +0000365 } else {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000366 subsec_dur = fs_nanoseconds::zero();
Eric Fiselierc1699862018-07-20 01:22:32 +0000367 }
368 }
369 return checked_set(sec_out, sec_dur.count()) &&
370 checked_set(subsec_out, subsec_dur.count());
371 }
Eric Fiselierc55ac102018-07-25 20:51:49 +0000372 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest,
373 FileTimeT tp) {
374 if (!is_representable(tp))
375 return false;
376 return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
377 }
Eric Fiselierc1699862018-07-20 01:22:32 +0000378};
379
Eric Fiselierc55ac102018-07-25 20:51:49 +0000380using fs_time = time_util<file_time_type, time_t, TimeSpec>;
Eric Fiselierc1699862018-07-20 01:22:32 +0000381
382#if defined(__APPLE__)
383TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
384TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
385#else
386TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
387TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
388#endif
389
Eric Fiseliera0a7c1f2018-07-26 03:57:26 +0000390// allow the utimes implementation to compile even it we're not going
391// to use it.
392
393bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS,
Eric Fiselier998a5c82018-07-27 03:07:09 +0000394 error_code& ec) {
Eric Fiselierc55ac102018-07-25 20:51:49 +0000395 using namespace chrono;
Alex Lorenz70cf5c42018-07-25 23:59:54 +0000396 auto Convert = [](long nsec) {
Eric Fiselier998a5c82018-07-27 03:07:09 +0000397 using int_type = decltype(std::declval< ::timeval>().tv_usec);
Alex Lorenz70cf5c42018-07-25 23:59:54 +0000398 auto dur = duration_cast<microseconds>(nanoseconds(nsec)).count();
399 return static_cast<int_type>(dur);
Eric Fiselierc55ac102018-07-25 20:51:49 +0000400 };
401 struct ::timeval ConvertedTS[2] = {{TS[0].tv_sec, Convert(TS[0].tv_nsec)},
402 {TS[1].tv_sec, Convert(TS[1].tv_nsec)}};
Eric Fiseliera0a7c1f2018-07-26 03:57:26 +0000403 if (::utimes(p.c_str(), ConvertedTS) == -1) {
Eric Fiselierc1699862018-07-20 01:22:32 +0000404 ec = capture_errno();
405 return true;
406 }
407 return false;
408}
409
Eric Fiseliera0a7c1f2018-07-26 03:57:26 +0000410#if defined(_LIBCPP_USE_UTIMENSAT)
411bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS,
Eric Fiselier998a5c82018-07-27 03:07:09 +0000412 error_code& ec) {
413 if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1) {
Eric Fiseliera0a7c1f2018-07-26 03:57:26 +0000414 ec = capture_errno();
415 return true;
416 }
417 return false;
Eric Fiselierc1699862018-07-20 01:22:32 +0000418}
Eric Fiseliera0a7c1f2018-07-26 03:57:26 +0000419#endif
420
421bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
422 error_code& ec) {
423#if !defined(_LIBCPP_USE_UTIMENSAT)
424 return posix_utimes(p, TS, ec);
425#else
426 return posix_utimensat(p, TS, ec);
427#endif
428}
429
Eric Fiselierc1699862018-07-20 01:22:32 +0000430} // namespace
431} // end namespace detail
432
Eric Fiselier998a5c82018-07-27 03:07:09 +0000433_LIBCPP_END_NAMESPACE_FILESYSTEM
Eric Fiselierc1699862018-07-20 01:22:32 +0000434
Eric Fiselierc1699862018-07-20 01:22:32 +0000435#endif // FILESYSTEM_COMMON_H