mirror of
https://github.com/llvm-mirror/libcxx.git
synced 2025-10-24 20:29:39 +08:00
Improve 'last_write_time(...)' accuracy and detect overflow errors.
The ::stat struct on Linux, FreeBSD, and OS X provides the access and modification times as an instance of 'timespec', which has a nanosecond resolution. The 'st_mtime' and 'st_atime' members simply reference the 'tv_sec' value of the timespec struct. This patch changes 'last_write_time(...)' so that it extracts both the seconds and nanoseconds values of the last modification time, providing a more accurate implementation of 'last_write_time(...)'. Additionally this patch fixes a possible signed integer overflow bug. The 'file_time_type' type cannot represent all possible values returned by the filesystem. Attempting to construct a 'file_time_type' from one of these values is undefined behavior. This patch avoids that UB by detecting possible overflows before the conversion. git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@282634 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -509,7 +509,6 @@ bool set_times_checked(time_t* sec_out, SubSecT* subsec_out, file_time_type tp)
|
||||
// The tv_nsec and tv_usec fields must not be negative so adjust accordingly
|
||||
if (subsec_dur.count() < 0) {
|
||||
if (sec_dur.count() > min_seconds) {
|
||||
|
||||
sec_dur -= seconds(1);
|
||||
subsec_dur += seconds(1);
|
||||
} else {
|
||||
@@ -520,11 +519,24 @@ bool set_times_checked(time_t* sec_out, SubSecT* subsec_out, file_time_type tp)
|
||||
&& checked_set(subsec_out, subsec_dur.count());
|
||||
}
|
||||
|
||||
using TimeSpec = struct ::timespec;
|
||||
using StatT = struct ::stat;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
|
||||
TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
|
||||
#else
|
||||
TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
|
||||
__attribute__((unused)) // Suppress warning
|
||||
TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
|
||||
#endif
|
||||
|
||||
}} // end namespace detail
|
||||
|
||||
|
||||
file_time_type __last_write_time(const path& p, std::error_code *ec)
|
||||
{
|
||||
using namespace ::std::chrono;
|
||||
std::error_code m_ec;
|
||||
struct ::stat st;
|
||||
detail::posix_stat(p, st, &m_ec);
|
||||
@@ -532,8 +544,25 @@ file_time_type __last_write_time(const path& p, std::error_code *ec)
|
||||
set_or_throw(m_ec, ec, "last_write_time", p);
|
||||
return file_time_type::min();
|
||||
}
|
||||
if (ec) ec->clear();
|
||||
return file_time_type::clock::from_time_t(st.st_mtime);
|
||||
#ifndef _LIBCPP_HAS_NO_INT128
|
||||
using IntMax = __int128_t;
|
||||
#else
|
||||
using IntMax = intmax_t;
|
||||
#endif
|
||||
// FIXME: The value may not be representable as file_time_type. Fix
|
||||
// file_time_type so it can represent all possible values returned by the
|
||||
// filesystem. For now we do the calculation with the largest possible types
|
||||
// and then truncate, this prevents signed integer overflow bugs.
|
||||
auto ts = detail::extract_mtime(st);
|
||||
const auto NsDur = duration<IntMax, nano>(ts.tv_nsec) + seconds(ts.tv_sec);
|
||||
if (NsDur > file_time_type::max().time_since_epoch() ||
|
||||
NsDur < file_time_type::min().time_since_epoch()) {
|
||||
set_or_throw(error_code(EOVERFLOW, generic_category()), ec,
|
||||
"last_write_time", p);
|
||||
return file_time_type::min();
|
||||
}
|
||||
if (ec) ec->clear();
|
||||
return file_time_type(duration_cast<file_time_type::duration>(NsDur));
|
||||
}
|
||||
|
||||
void __last_write_time(const path& p, file_time_type new_time,
|
||||
@@ -554,9 +583,10 @@ void __last_write_time(const path& p, file_time_type new_time,
|
||||
set_or_throw(m_ec, ec, "last_write_time", p);
|
||||
return;
|
||||
}
|
||||
auto atime = detail::extract_atime(st);
|
||||
struct ::timeval tbuf[2];
|
||||
tbuf[0].tv_sec = st.st_atime;
|
||||
tbuf[0].tv_usec = 0;
|
||||
tbuf[0].tv_sec = atime.tv_sec;
|
||||
tbuf[0].tv_usec = duration_cast<microseconds>(nanoseconds(atime.tv_nsec)).count();
|
||||
const bool overflowed = !detail::set_times_checked<microseconds>(
|
||||
&tbuf[1].tv_sec, &tbuf[1].tv_usec, new_time);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user