blob: 0dd6e9ee4389d01e1f9ffdea144d5ee6a5968613 [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"
14#include "chrono"
15#include "cstdlib"
16#include "climits"
17
18#include <unistd.h>
19#include <sys/stat.h>
20#include <sys/statvfs.h>
21#include <fcntl.h> /* values for fchmodat */
22
23#include <experimental/filesystem>
24
25#if (__APPLE__)
26#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
27#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101300
28#define _LIBCXX_USE_UTIMENSAT
29#endif
30#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
31#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 110000
32#define _LIBCXX_USE_UTIMENSAT
33#endif
34#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__)
35#if __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 110000
36#define _LIBCXX_USE_UTIMENSAT
37#endif
38#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__)
39#if __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 40000
40#define _LIBCXX_USE_UTIMENSAT
41#endif
42#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
43#else
44// We can use the presence of UTIME_OMIT to detect platforms that provide
45// utimensat.
46#if defined(UTIME_OMIT)
47#define _LIBCXX_USE_UTIMENSAT
48#endif
49#endif // __APPLE__
50
51#if !defined(_LIBCXX_USE_UTIMENSAT)
52#include <sys/time.h> // for ::utimes as used in __last_write_time
53#endif
54
55#if !defined(UTIME_OMIT)
56#include <sys/time.h> // for ::utimes as used in __last_write_time
57#endif
58
59#if defined(__GNUC__)
60#pragma GCC diagnostic push
61#pragma GCC diagnostic ignored "-Wunused-function"
62#endif
63
64_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
65
66namespace detail {
67namespace {
68
69std::error_code capture_errno() {
70 _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
71 return std::error_code(errno, std::generic_category());
72}
73
74void set_or_throw(std::error_code const& m_ec, std::error_code* ec,
75 const char* msg, path const& p = {}, path const& p2 = {}) {
76 if (ec) {
77 *ec = m_ec;
78 } else {
79 string msg_s("std::experimental::filesystem::");
80 msg_s += msg;
81 __throw_filesystem_error(msg_s, p, p2, m_ec);
82 }
83}
84
85void set_or_throw(std::error_code* ec, const char* msg, path const& p = {},
86 path const& p2 = {}) {
87 return set_or_throw(capture_errno(), ec, msg, p, p2);
88}
89
90namespace time_util {
91
92using namespace chrono;
93
94template <class FileTimeT,
95 bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
96struct fs_time_util_base {
97 static constexpr auto max_seconds =
98 duration_cast<seconds>(FileTimeT::duration::max()).count();
99
100 static constexpr auto max_nsec =
101 duration_cast<nanoseconds>(FileTimeT::duration::max() -
102 seconds(max_seconds))
103 .count();
104
105 static constexpr auto min_seconds =
106 duration_cast<seconds>(FileTimeT::duration::min()).count();
107
108 static constexpr auto min_nsec_timespec =
109 duration_cast<nanoseconds>(
110 (FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1))
111 .count();
112
113 // Static assert that these values properly round trip.
114 static_assert((seconds(min_seconds) +
115 duration_cast<microseconds>(nanoseconds(min_nsec_timespec))) -
116 duration_cast<microseconds>(seconds(1)) ==
117 FileTimeT::duration::min(),
118 "");
119};
120
121template <class FileTimeT>
122struct fs_time_util_base<FileTimeT, true> {
123 static const long long max_seconds;
124 static const long long max_nsec;
125 static const long long min_seconds;
126 static const long long min_nsec_timespec;
127};
128
129template <class FileTimeT>
130const long long fs_time_util_base<FileTimeT, true>::max_seconds =
131 duration_cast<seconds>(FileTimeT::duration::max()).count();
132
133template <class FileTimeT>
134const long long fs_time_util_base<FileTimeT, true>::max_nsec =
135 duration_cast<nanoseconds>(FileTimeT::duration::max() -
136 seconds(max_seconds))
137 .count();
138
139template <class FileTimeT>
140const long long fs_time_util_base<FileTimeT, true>::min_seconds =
141 duration_cast<seconds>(FileTimeT::duration::min()).count();
142
143template <class FileTimeT>
144const long long fs_time_util_base<FileTimeT, true>::min_nsec_timespec =
145 duration_cast<nanoseconds>(
146 (FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1))
147 .count();
148
149template <class FileTimeT, class TimeT, class TimeSpecT>
150struct fs_time_util : fs_time_util_base<FileTimeT> {
151 using Base = fs_time_util_base<FileTimeT>;
152 using Base::max_nsec;
153 using Base::max_seconds;
154 using Base::min_nsec_timespec;
155 using Base::min_seconds;
156
157public:
158 template <class CType, class ChronoType>
159 static bool checked_set(CType* out, ChronoType time) {
160 using Lim = numeric_limits<CType>;
161 if (time > Lim::max() || time < Lim::min())
162 return false;
163 *out = static_cast<CType>(time);
164 return true;
165 }
166
167 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
168 if (tm.tv_sec >= 0) {
169 return (tm.tv_sec < max_seconds) ||
170 (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
171 } else if (tm.tv_sec == (min_seconds - 1)) {
172 return tm.tv_nsec >= min_nsec_timespec;
173 } else {
174 return (tm.tv_sec >= min_seconds);
175 }
176 }
177
178 static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
179 auto secs = duration_cast<seconds>(tm.time_since_epoch());
180 auto nsecs = duration_cast<nanoseconds>(tm.time_since_epoch() - secs);
181 if (nsecs.count() < 0) {
182 secs = secs + seconds(1);
183 nsecs = nsecs + seconds(1);
184 }
185 using TLim = numeric_limits<TimeT>;
186 if (secs.count() >= 0)
187 return secs.count() <= TLim::max();
188 return secs.count() >= TLim::min();
189 }
190
191 static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
192 convert_timespec(TimeSpecT tm) {
193 auto adj_msec = duration_cast<microseconds>(nanoseconds(tm.tv_nsec));
194 if (tm.tv_sec >= 0) {
195 auto Dur = seconds(tm.tv_sec) + microseconds(adj_msec);
196 return FileTimeT(Dur);
197 } else if (duration_cast<microseconds>(nanoseconds(tm.tv_nsec)).count() ==
198 0) {
199 return FileTimeT(seconds(tm.tv_sec));
200 } else { // tm.tv_sec < 0
201 auto adj_subsec =
202 duration_cast<microseconds>(seconds(1) - nanoseconds(tm.tv_nsec));
203 auto Dur = seconds(tm.tv_sec + 1) - adj_subsec;
204 return FileTimeT(Dur);
205 }
206 }
207
208 template <class SubSecDurT, class SubSecT>
209 static bool set_times_checked(TimeT* sec_out, SubSecT* subsec_out,
210 FileTimeT tp) {
211 auto dur = tp.time_since_epoch();
212 auto sec_dur = duration_cast<seconds>(dur);
213 auto subsec_dur = duration_cast<SubSecDurT>(dur - sec_dur);
214 // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
215 if (subsec_dur.count() < 0) {
216 if (sec_dur.count() > min_seconds) {
217 sec_dur -= seconds(1);
218 subsec_dur += seconds(1);
219 } else {
220 subsec_dur = SubSecDurT::zero();
221 }
222 }
223 return checked_set(sec_out, sec_dur.count()) &&
224 checked_set(subsec_out, subsec_dur.count());
225 }
226};
227
228} // namespace time_util
229
230
Eric Fiselier41cdcbe2018-07-20 01:44:33 +0000231using TimeSpec = struct ::timespec;
232using StatT = struct ::stat;
Eric Fiselierc1699862018-07-20 01:22:32 +0000233
234using FSTime = time_util::fs_time_util<file_time_type, time_t, struct timespec>;
235
236#if defined(__APPLE__)
237TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
238TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
239#else
240TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
241TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
242#endif
243
244#if !defined(_LIBCXX_USE_UTIMENSAT)
245using TimeStruct = struct ::timeval;
246using TimeStructArray = TimeStruct[2];
247#else
248using TimeStruct = struct ::timespec;
249using TimeStructArray = TimeStruct[2];
250#endif
251
252bool SetFileTimes(const path& p, TimeStructArray const& TS,
253 std::error_code& ec) {
254#if !defined(_LIBCXX_USE_UTIMENSAT)
255 if (::utimes(p.c_str(), TS) == -1)
256#else
257 if (::utimensat(AT_FDCWD, p.c_str(), TS, 0) == -1)
258#endif
259 {
260 ec = capture_errno();
261 return true;
262 }
263 return false;
264}
265
266void SetTimeStructTo(TimeStruct& TS, TimeSpec ToTS) {
267 using namespace chrono;
268 TS.tv_sec = ToTS.tv_sec;
269#if !defined(_LIBCXX_USE_UTIMENSAT)
270 TS.tv_usec = duration_cast<microseconds>(nanoseconds(ToTS.tv_nsec)).count();
271#else
272 TS.tv_nsec = ToTS.tv_nsec;
273#endif
274}
275
276bool SetTimeStructTo(TimeStruct& TS, file_time_type NewTime) {
277 using namespace chrono;
278#if !defined(_LIBCXX_USE_UTIMENSAT)
279 return !FSTime::set_times_checked<microseconds>(&TS.tv_sec, &TS.tv_usec,
280 NewTime);
281#else
282 return !FSTime::set_times_checked<nanoseconds>(&TS.tv_sec, &TS.tv_nsec,
283 NewTime);
284#endif
285}
286
287} // namespace
288} // end namespace detail
289
290_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
291
292#if defined(__GNUC__)
293#pragma GCC diagnostic pop
294#endif
295
296#endif // FILESYSTEM_COMMON_H