1 //===----------------------------------------------------------------------===////
3 // 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
7 //===----------------------------------------------------------------------===////
9 #ifndef FILESYSTEM_COMMON_H
10 #define FILESYSTEM_COMMON_H
22 #include <sys/statvfs.h>
23 #include <sys/time.h> // for ::utimes as used in __last_write_time
24 #include <fcntl.h> /* values for fchmodat */
26 #include "../include/apple_availability.h"
28 #if !defined(__APPLE__)
29 // We can use the presence of UTIME_OMIT to detect platforms that provide
31 #if defined(UTIME_OMIT)
32 #define _LIBCPP_USE_UTIMENSAT
37 #pragma GCC diagnostic push
38 #pragma GCC diagnostic ignored "-Wunused-function"
41 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
46 static string
format_string_imp(const char* msg
, ...) {
47 // we might need a second shot at this, so pre-emptivly make a copy
51 GuardVAList(va_list& tgt
) : target(tgt
), active(true) {}
64 GuardVAList
args_guard(args
);
67 va_copy(args_cp
, args
);
68 GuardVAList
args_copy_guard(args_cp
);
72 array
<char, 256> local_buff
;
73 size_t size_with_null
= local_buff
.size();
74 auto ret
= ::vsnprintf(local_buff
.data(), size_with_null
, msg
, args_cp
);
76 args_copy_guard
.clear();
78 // handle empty expansion
81 if (static_cast<size_t>(ret
) < size_with_null
) {
82 result
.assign(local_buff
.data(), static_cast<size_t>(ret
));
86 // we did not provide a long enough buffer on our first attempt. The
87 // return value is the number of bytes (excluding the null byte) that are
88 // needed for formatting.
89 size_with_null
= static_cast<size_t>(ret
) + 1;
90 result
.__resize_default_init(size_with_null
- 1);
91 ret
= ::vsnprintf(&result
[0], size_with_null
, msg
, args
);
92 _LIBCPP_ASSERT(static_cast<size_t>(ret
) == (size_with_null
- 1), "TODO");
97 const char* unwrap(string
const& s
) { return s
.c_str(); }
98 const char* unwrap(path
const& p
) { return p
.native().c_str(); }
100 Arg
const& unwrap(Arg
const& a
) {
101 static_assert(!is_class
<Arg
>::value
, "cannot pass class here");
105 template <class... Args
>
106 string
format_string(const char* fmt
, Args
const&... args
) {
107 return format_string_imp(fmt
, unwrap(args
)...);
110 error_code
capture_errno() {
111 _LIBCPP_ASSERT(errno
, "Expected errno to be non-zero");
112 return error_code(errno
, generic_category());
118 _LIBCPP_CONSTEXPR_AFTER_CXX11
void error_value
<void>() {}
120 bool error_value
<bool>() {
124 uintmax_t error_value
<uintmax_t>() {
125 return uintmax_t(-1);
128 _LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value
<file_time_type
>() {
129 return file_time_type::min();
132 path error_value
<path
>() {
137 struct ErrorHandler
{
138 const char* func_name_
;
139 error_code
* ec_
= nullptr;
140 const path
* p1_
= nullptr;
141 const path
* p2_
= nullptr;
143 ErrorHandler(const char* fname
, error_code
* ec
, const path
* p1
= nullptr,
144 const path
* p2
= nullptr)
145 : func_name_(fname
), ec_(ec
), p1_(p1
), p2_(p2
) {
150 T
report(const error_code
& ec
) const {
153 return error_value
<T
>();
155 string what
= string("in ") + func_name_
;
156 switch (bool(p1_
) + bool(p2_
)) {
158 __throw_filesystem_error(what
, ec
);
160 __throw_filesystem_error(what
, *p1_
, ec
);
162 __throw_filesystem_error(what
, *p1_
, *p2_
, ec
);
164 _LIBCPP_UNREACHABLE();
167 template <class... Args
>
168 T
report(const error_code
& ec
, const char* msg
, Args
const&... args
) const {
171 return error_value
<T
>();
174 string("in ") + func_name_
+ ": " + format_string(msg
, args
...);
175 switch (bool(p1_
) + bool(p2_
)) {
177 __throw_filesystem_error(what
, ec
);
179 __throw_filesystem_error(what
, *p1_
, ec
);
181 __throw_filesystem_error(what
, *p1_
, *p2_
, ec
);
183 _LIBCPP_UNREACHABLE();
186 T
report(errc
const& err
) const { return report(make_error_code(err
)); }
188 template <class... Args
>
189 T
report(errc
const& err
, const char* msg
, Args
const&... args
) const {
190 return report(make_error_code(err
), msg
, args
...);
194 ErrorHandler(ErrorHandler
const&) = delete;
195 ErrorHandler
& operator=(ErrorHandler
const&) = delete;
198 using chrono::duration
;
199 using chrono::duration_cast
;
201 using TimeSpec
= struct timespec
;
202 using TimeVal
= struct timeval
;
203 using StatT
= struct stat
;
205 template <class FileTimeT
, class TimeT
,
206 bool IsFloat
= is_floating_point
<typename
FileTimeT::rep
>::value
>
207 struct 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
>;
214 static constexpr rep max_seconds
=
215 duration_cast
<fs_seconds
>(FileTimeT::duration::max()).count();
217 static constexpr rep max_nsec
=
218 duration_cast
<fs_nanoseconds
>(FileTimeT::duration::max() -
219 fs_seconds(max_seconds
))
222 static constexpr rep min_seconds
=
223 duration_cast
<fs_seconds
>(FileTimeT::duration::min()).count();
225 static constexpr rep min_nsec_timespec
=
226 duration_cast
<fs_nanoseconds
>(
227 (FileTimeT::duration::min() - fs_seconds(min_seconds
)) +
232 #if _LIBCPP_STD_VER > 11 && !defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
233 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)));
238 // Static assert that these values properly round trip.
239 static_assert(fs_seconds(min_seconds
) + get_min_nsecs() ==
240 FileTimeT::duration::min(),
241 "value doesn't roundtrip");
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);
250 return max_seconds
>= numeric_limits
<TimeT
>::max() &&
251 min_seconds
<= numeric_limits
<TimeT
>::min();
253 static_assert(check_range(), "the representable range is unacceptable small");
257 template <class FileTimeT
, class TimeT
>
258 struct 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
>;
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
;
271 template <class FileTimeT
, class TimeT
>
272 const typename
FileTimeT::rep
273 time_util_base
<FileTimeT
, TimeT
, true>::max_seconds
=
274 duration_cast
<fs_seconds
>(FileTimeT::duration::max()).count();
276 template <class FileTimeT
, class TimeT
>
277 const typename
FileTimeT::rep time_util_base
<FileTimeT
, TimeT
, true>::max_nsec
=
278 duration_cast
<fs_nanoseconds
>(FileTimeT::duration::max() -
279 fs_seconds(max_seconds
))
282 template <class FileTimeT
, class TimeT
>
283 const typename
FileTimeT::rep
284 time_util_base
<FileTimeT
, TimeT
, true>::min_seconds
=
285 duration_cast
<fs_seconds
>(FileTimeT::duration::min()).count();
287 template <class FileTimeT
, class TimeT
>
288 const 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
)) +
295 template <class FileTimeT
, class TimeT
, class TimeSpecT
>
296 struct time_util
: time_util_base
<FileTimeT
, TimeT
> {
297 using Base
= time_util_base
<FileTimeT
, TimeT
>;
298 using Base::max_nsec
;
299 using Base::max_seconds
;
300 using Base::min_nsec_timespec
;
301 using Base::min_seconds
;
303 using typename
Base::fs_duration
;
304 using typename
Base::fs_microseconds
;
305 using typename
Base::fs_nanoseconds
;
306 using typename
Base::fs_seconds
;
309 template <class CType
, class ChronoType
>
310 static _LIBCPP_CONSTEXPR_AFTER_CXX11
bool checked_set(CType
* out
,
312 using Lim
= numeric_limits
<CType
>;
313 if (time
> Lim::max() || time
< Lim::min())
315 *out
= static_cast<CType
>(time
);
319 static _LIBCPP_CONSTEXPR_AFTER_CXX11
bool is_representable(TimeSpecT tm
) {
320 if (tm
.tv_sec
>= 0) {
321 return tm
.tv_sec
< max_seconds
||
322 (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
;
326 return tm
.tv_sec
>= min_seconds
;
330 static _LIBCPP_CONSTEXPR_AFTER_CXX11
bool is_representable(FileTimeT tm
) {
331 auto secs
= duration_cast
<fs_seconds
>(tm
.time_since_epoch());
332 auto nsecs
= duration_cast
<fs_nanoseconds
>(tm
.time_since_epoch() - secs
);
333 if (nsecs
.count() < 0) {
334 secs
= secs
+ fs_seconds(1);
335 nsecs
= nsecs
+ fs_seconds(1);
337 using TLim
= numeric_limits
<TimeT
>;
338 if (secs
.count() >= 0)
339 return secs
.count() <= TLim::max();
340 return secs
.count() >= TLim::min();
343 static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
344 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
)));
348 } else { // tm.tv_sec < 0
349 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
;
352 return FileTimeT(Dur
);
356 template <class SubSecT
>
357 static _LIBCPP_CONSTEXPR_AFTER_CXX11
bool
358 set_times_checked(TimeT
* sec_out
, SubSecT
* subsec_out
, FileTimeT tp
) {
359 auto dur
= tp
.time_since_epoch();
360 auto sec_dur
= duration_cast
<fs_seconds
>(dur
);
361 auto subsec_dur
= duration_cast
<fs_nanoseconds
>(dur
- sec_dur
);
362 // 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
) {
365 sec_dur
= sec_dur
- fs_seconds(1);
366 subsec_dur
= subsec_dur
+ fs_seconds(1);
368 subsec_dur
= fs_nanoseconds::zero();
371 return checked_set(sec_out
, sec_dur
.count()) &&
372 checked_set(subsec_out
, subsec_dur
.count());
374 static _LIBCPP_CONSTEXPR_AFTER_CXX11
bool convert_to_timespec(TimeSpecT
& dest
,
376 if (!is_representable(tp
))
378 return set_times_checked(&dest
.tv_sec
, &dest
.tv_nsec
, tp
);
382 using fs_time
= time_util
<file_time_type
, time_t, TimeSpec
>;
384 #if defined(__APPLE__)
385 inline TimeSpec
extract_mtime(StatT
const& st
) { return st
.st_mtimespec
; }
386 inline TimeSpec
extract_atime(StatT
const& st
) { return st
.st_atimespec
; }
387 #elif defined(__MVS__)
388 inline TimeSpec
extract_mtime(StatT
const& st
) {
389 TimeSpec TS
= {st
.st_mtime
, 0};
392 inline TimeSpec
extract_atime(StatT
const& st
) {
393 TimeSpec TS
= {st
.st_atime
, 0};
397 inline TimeSpec
extract_mtime(StatT
const& st
) { return st
.st_mtim
; }
398 inline TimeSpec
extract_atime(StatT
const& st
) { return st
.st_atim
; }
401 inline TimeVal
make_timeval(TimeSpec
const& ts
) {
402 using namespace chrono
;
403 auto Convert
= [](long nsec
) {
404 using int_type
= decltype(std::declval
<TimeVal
>().tv_usec
);
405 auto dur
= duration_cast
<microseconds
>(nanoseconds(nsec
)).count();
406 return static_cast<int_type
>(dur
);
409 TV
.tv_sec
= ts
.tv_sec
;
410 TV
.tv_usec
= Convert(ts
.tv_nsec
);
414 inline bool posix_utimes(const path
& p
, std::array
<TimeSpec
, 2> const& TS
,
416 TimeVal ConvertedTS
[2] = {make_timeval(TS
[0]), make_timeval(TS
[1])};
417 if (::utimes(p
.c_str(), ConvertedTS
) == -1) {
418 ec
= capture_errno();
424 #if defined(_LIBCPP_USE_UTIMENSAT)
425 bool posix_utimensat(const path
& p
, std::array
<TimeSpec
, 2> const& TS
,
427 if (::utimensat(AT_FDCWD
, p
.c_str(), TS
.data(), 0) == -1) {
428 ec
= capture_errno();
435 bool set_file_times(const path
& p
, std::array
<TimeSpec
, 2> const& TS
,
437 #if !defined(_LIBCPP_USE_UTIMENSAT)
438 return posix_utimes(p
, TS
, ec
);
440 return posix_utimensat(p
, TS
, ec
);
445 } // end namespace detail
447 _LIBCPP_END_NAMESPACE_FILESYSTEM
449 #endif // FILESYSTEM_COMMON_H