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_TIME_UTILS_H
10 #define FILESYSTEM_TIME_UTILS_H
18 #include <system_error>
19 #include <type_traits>
23 #include "format_string.h"
25 #if defined(_LIBCPP_WIN32API)
26 # define WIN32_LEAN_AND_MEAN
31 # include <sys/stat.h>
32 # include <sys/time.h> // for ::utimes as used in __last_write_time
35 // We can use the presence of UTIME_OMIT to detect platforms that provide utimensat.
36 #if defined(UTIME_OMIT)
37 # define _LIBCPP_USE_UTIMENSAT
40 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
44 #if defined(_LIBCPP_WIN32API)
45 // Various C runtime versions (UCRT, or the legacy msvcrt.dll used by
46 // some mingw toolchains) provide different stat function implementations,
47 // with a number of limitations with respect to what we want from the
48 // stat function. Instead provide our own which does exactly what we want,
49 // along with our own stat structure and flag macros.
59 uint64_t st_dev
; // FILE_ID_INFO::VolumeSerialNumber
61 unsigned char id
[16]; // FILE_ID_INFO::FileId
62 bool operator==(const FileIdStruct
& other
) const {
63 for (int i
= 0; i
< 16; i
++)
64 if (id
[i
] != other
.id
[i
])
73 // There were 369 years and 89 leap days from the Windows epoch
74 // (1601) to the Unix epoch (1970).
75 # define FILE_TIME_OFFSET_SECS (uint64_t(369 * 365 + 89) * (24 * 60 * 60))
77 inline TimeSpec
filetime_to_timespec(LARGE_INTEGER li
) {
79 ret
.tv_sec
= li
.QuadPart
/ 10000000 - FILE_TIME_OFFSET_SECS
;
80 ret
.tv_nsec
= (li
.QuadPart
% 10000000) * 100;
84 inline TimeSpec
filetime_to_timespec(FILETIME ft
) {
86 li
.LowPart
= ft
.dwLowDateTime
;
87 li
.HighPart
= ft
.dwHighDateTime
;
88 return filetime_to_timespec(li
);
91 inline FILETIME
timespec_to_filetime(TimeSpec ts
) {
93 li
.QuadPart
= ts
.tv_nsec
/ 100 + (ts
.tv_sec
+ FILE_TIME_OFFSET_SECS
) * 10000000;
95 ft
.dwLowDateTime
= li
.LowPart
;
96 ft
.dwHighDateTime
= li
.HighPart
;
101 using TimeSpec
= struct timespec
;
102 using TimeVal
= struct timeval
;
103 using StatT
= struct stat
;
105 inline TimeVal
make_timeval(TimeSpec
const& ts
) {
106 using namespace chrono
;
107 auto Convert
= [](long nsec
) {
108 using int_type
= decltype(std::declval
<TimeVal
>().tv_usec
);
109 auto dur
= duration_cast
<microseconds
>(nanoseconds(nsec
)).count();
110 return static_cast<int_type
>(dur
);
113 TV
.tv_sec
= ts
.tv_sec
;
114 TV
.tv_usec
= Convert(ts
.tv_nsec
);
119 using chrono::duration
;
120 using chrono::duration_cast
;
122 template <class FileTimeT
, class TimeT
, bool IsFloat
= is_floating_point
<typename
FileTimeT::rep
>::value
>
123 struct time_util_base
{
124 using rep
= typename
FileTimeT::rep
;
125 using fs_duration
= typename
FileTimeT::duration
;
126 using fs_seconds
= duration
<rep
>;
127 using fs_nanoseconds
= duration
<rep
, nano
>;
128 using fs_microseconds
= duration
<rep
, micro
>;
130 static constexpr rep max_seconds
= duration_cast
<fs_seconds
>(FileTimeT::duration::max()).count();
132 static constexpr rep max_nsec
=
133 duration_cast
<fs_nanoseconds
>(FileTimeT::duration::max() - fs_seconds(max_seconds
)).count();
135 static constexpr rep min_seconds
= duration_cast
<fs_seconds
>(FileTimeT::duration::min()).count();
137 static constexpr rep min_nsec_timespec
=
138 duration_cast
<fs_nanoseconds
>((FileTimeT::duration::min() - fs_seconds(min_seconds
)) + fs_seconds(1)).count();
141 static constexpr fs_duration
get_min_nsecs() {
142 return duration_cast
<fs_duration
>(fs_nanoseconds(min_nsec_timespec
) - duration_cast
<fs_nanoseconds
>(fs_seconds(1)));
144 // Static assert that these values properly round trip.
145 static_assert(fs_seconds(min_seconds
) + get_min_nsecs() == FileTimeT::duration::min(), "value doesn't roundtrip");
147 static constexpr bool check_range() {
148 // This kinda sucks, but it's what happens when we don't have __int128_t.
149 if (sizeof(TimeT
) == sizeof(rep
)) {
150 typedef duration
<long long, ratio
<3600 * 24 * 365> > Years
;
151 return duration_cast
<Years
>(fs_seconds(max_seconds
)) > Years(250) &&
152 duration_cast
<Years
>(fs_seconds(min_seconds
)) < Years(-250);
154 return max_seconds
>= numeric_limits
<TimeT
>::max() && min_seconds
<= numeric_limits
<TimeT
>::min();
156 #if _LIBCPP_STD_VER >= 14
157 static_assert(check_range(), "the representable range is unacceptable small");
161 template <class FileTimeT
, class TimeT
>
162 struct time_util_base
<FileTimeT
, TimeT
, true> {
163 using rep
= typename
FileTimeT::rep
;
164 using fs_duration
= typename
FileTimeT::duration
;
165 using fs_seconds
= duration
<rep
>;
166 using fs_nanoseconds
= duration
<rep
, nano
>;
167 using fs_microseconds
= duration
<rep
, micro
>;
169 static const rep max_seconds
;
170 static const rep max_nsec
;
171 static const rep min_seconds
;
172 static const rep min_nsec_timespec
;
175 template <class FileTimeT
, class TimeT
>
176 const typename
FileTimeT::rep time_util_base
<FileTimeT
, TimeT
, true>::max_seconds
=
177 duration_cast
<fs_seconds
>(FileTimeT::duration::max()).count();
179 template <class FileTimeT
, class TimeT
>
180 const typename
FileTimeT::rep time_util_base
<FileTimeT
, TimeT
, true>::max_nsec
=
181 duration_cast
<fs_nanoseconds
>(FileTimeT::duration::max() - fs_seconds(max_seconds
)).count();
183 template <class FileTimeT
, class TimeT
>
184 const typename
FileTimeT::rep time_util_base
<FileTimeT
, TimeT
, true>::min_seconds
=
185 duration_cast
<fs_seconds
>(FileTimeT::duration::min()).count();
187 template <class FileTimeT
, class TimeT
>
188 const typename
FileTimeT::rep time_util_base
<FileTimeT
, TimeT
, true>::min_nsec_timespec
=
189 duration_cast
<fs_nanoseconds
>((FileTimeT::duration::min() - fs_seconds(min_seconds
)) + fs_seconds(1)).count();
191 template <class FileTimeT
, class TimeT
, class TimeSpecT
>
192 struct time_util
: time_util_base
<FileTimeT
, TimeT
> {
193 using Base
= time_util_base
<FileTimeT
, TimeT
>;
194 using Base::max_nsec
;
195 using Base::max_seconds
;
196 using Base::min_nsec_timespec
;
197 using Base::min_seconds
;
199 using typename
Base::fs_duration
;
200 using typename
Base::fs_microseconds
;
201 using typename
Base::fs_nanoseconds
;
202 using typename
Base::fs_seconds
;
205 template <class CType
, class ChronoType
>
206 static constexpr bool checked_set(CType
* out
, ChronoType time
) {
207 using Lim
= numeric_limits
<CType
>;
208 if (time
> Lim::max() || time
< Lim::min())
210 *out
= static_cast<CType
>(time
);
214 static constexpr bool is_representable(TimeSpecT tm
) {
215 if (tm
.tv_sec
>= 0) {
216 return tm
.tv_sec
< max_seconds
|| (tm
.tv_sec
== max_seconds
&& tm
.tv_nsec
<= max_nsec
);
217 } else if (tm
.tv_sec
== (min_seconds
- 1)) {
218 return tm
.tv_nsec
>= min_nsec_timespec
;
220 return tm
.tv_sec
>= min_seconds
;
224 static constexpr bool is_representable(FileTimeT tm
) {
225 auto secs
= duration_cast
<fs_seconds
>(tm
.time_since_epoch());
226 auto nsecs
= duration_cast
<fs_nanoseconds
>(tm
.time_since_epoch() - secs
);
227 if (nsecs
.count() < 0) {
228 secs
= secs
+ fs_seconds(1);
229 nsecs
= nsecs
+ fs_seconds(1);
231 using TLim
= numeric_limits
<TimeT
>;
232 if (secs
.count() >= 0)
233 return secs
.count() <= TLim::max();
234 return secs
.count() >= TLim::min();
237 static constexpr FileTimeT
convert_from_timespec(TimeSpecT tm
) {
238 if (tm
.tv_sec
>= 0 || tm
.tv_nsec
== 0) {
239 return FileTimeT(fs_seconds(tm
.tv_sec
) + duration_cast
<fs_duration
>(fs_nanoseconds(tm
.tv_nsec
)));
240 } else { // tm.tv_sec < 0
241 auto adj_subsec
= duration_cast
<fs_duration
>(fs_seconds(1) - fs_nanoseconds(tm
.tv_nsec
));
242 auto Dur
= fs_seconds(tm
.tv_sec
+ 1) - adj_subsec
;
243 return FileTimeT(Dur
);
247 template <class SubSecT
>
248 static constexpr bool set_times_checked(TimeT
* sec_out
, SubSecT
* subsec_out
, FileTimeT tp
) {
249 auto dur
= tp
.time_since_epoch();
250 auto sec_dur
= duration_cast
<fs_seconds
>(dur
);
251 auto subsec_dur
= duration_cast
<fs_nanoseconds
>(dur
- sec_dur
);
252 // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
253 if (subsec_dur
.count() < 0) {
254 if (sec_dur
.count() > min_seconds
) {
255 sec_dur
= sec_dur
- fs_seconds(1);
256 subsec_dur
= subsec_dur
+ fs_seconds(1);
258 subsec_dur
= fs_nanoseconds::zero();
261 return checked_set(sec_out
, sec_dur
.count()) && checked_set(subsec_out
, subsec_dur
.count());
263 static constexpr bool convert_to_timespec(TimeSpecT
& dest
, FileTimeT tp
) {
264 if (!is_representable(tp
))
266 return set_times_checked(&dest
.tv_sec
, &dest
.tv_nsec
, tp
);
270 #if defined(_LIBCPP_WIN32API)
271 using fs_time
= time_util
<file_time_type
, int64_t, TimeSpec
>;
273 using fs_time
= time_util
<file_time_type
, time_t, TimeSpec
>;
276 #if defined(__APPLE__)
277 inline TimeSpec
extract_mtime(StatT
const& st
) { return st
.st_mtimespec
; }
278 inline TimeSpec
extract_atime(StatT
const& st
) { return st
.st_atimespec
; }
279 #elif defined(__MVS__)
280 inline TimeSpec
extract_mtime(StatT
const& st
) {
281 TimeSpec TS
= {st
.st_mtime
, 0};
284 inline TimeSpec
extract_atime(StatT
const& st
) {
285 TimeSpec TS
= {st
.st_atime
, 0};
289 inline TimeSpec
extract_mtime(StatT
const& st
) {
290 TimeSpec TS
= {st
.st_mtime
, st
.st_mtime_n
};
293 inline TimeSpec
extract_atime(StatT
const& st
) {
294 TimeSpec TS
= {st
.st_atime
, st
.st_atime_n
};
298 inline TimeSpec
extract_mtime(StatT
const& st
) { return st
.st_mtim
; }
299 inline TimeSpec
extract_atime(StatT
const& st
) { return st
.st_atim
; }
302 #ifndef _LIBCPP_HAS_NO_FILESYSTEM
304 # if !defined(_LIBCPP_WIN32API)
305 inline bool posix_utimes(const path
& p
, std::array
<TimeSpec
, 2> const& TS
, error_code
& ec
) {
306 TimeVal ConvertedTS
[2] = {make_timeval(TS
[0]), make_timeval(TS
[1])};
307 if (::utimes(p
.c_str(), ConvertedTS
) == -1) {
308 ec
= capture_errno();
314 # if defined(_LIBCPP_USE_UTIMENSAT)
315 inline bool posix_utimensat(const path
& p
, std::array
<TimeSpec
, 2> const& TS
, error_code
& ec
) {
316 if (::utimensat(AT_FDCWD
, p
.c_str(), TS
.data(), 0) == -1) {
317 ec
= capture_errno();
324 inline bool set_file_times(const path
& p
, std::array
<TimeSpec
, 2> const& TS
, error_code
& ec
) {
325 # if !defined(_LIBCPP_USE_UTIMENSAT)
326 return posix_utimes(p
, TS
, ec
);
328 return posix_utimensat(p
, TS
, ec
);
332 # endif // !_LIBCPP_WIN32API
334 inline file_time_type
__extract_last_write_time(const path
& p
, const StatT
& st
, error_code
* ec
) {
335 using detail::fs_time
;
336 ErrorHandler
<file_time_type
> err("last_write_time", ec
, &p
);
338 auto ts
= detail::extract_mtime(st
);
339 if (!fs_time::is_representable(ts
))
340 return err
.report(errc::value_too_large
);
342 return fs_time::convert_from_timespec(ts
);
345 #endif // !_LIBCPP_HAS_NO_FILESYSTEM
347 } // end namespace detail
349 _LIBCPP_END_NAMESPACE_FILESYSTEM
351 #endif // FILESYSTEM_TIME_UTILS_H