diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2017-04-24 14:12:06 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2017-04-24 14:12:06 +0200 |
commit | 8e25b3cb8be2a23b1e667bae614dc928bba798c9 (patch) | |
tree | d56a2dd5a2fa736ac7a81f81f8bc5b2ce7821b8b | |
parent | b8526f8f0cb24c457da8c88877e516943e3e1902 (diff) |
Reimplement file_mtime() to use GetFileAttributesEx() on Windows
On Windows struct stat does not contain the nanoseconds member which means
we effectively had one second timestamp resolution.
-rw-r--r-- | butl/filesystem.cxx | 66 |
1 files changed, 52 insertions, 14 deletions
diff --git a/butl/filesystem.cxx b/butl/filesystem.cxx index 1873bfa..5258a57 100644 --- a/butl/filesystem.cxx +++ b/butl/filesystem.cxx @@ -405,9 +405,12 @@ namespace butl return s->st_mtime_n; // AIX 5.2 and later. } - template <typename S> - inline constexpr int - mnsec (...) {return 0;} + // Things are not going to end up well with only seconds resolution so + // let's make it a compile error. + // + // template <typename S> + // inline constexpr int + // mnsec (...) {return 0;} template <typename S> inline constexpr auto @@ -430,9 +433,9 @@ namespace butl return s->st_atime_n; // AIX 5.2 and later. } - template <typename S> - inline constexpr int - ansec (...) {return 0;} + // template <typename S> + // inline constexpr int + // ansec (...) {return 0;} void mventry (const path& from, const path& to, cpflags fl) @@ -538,10 +541,6 @@ namespace butl #ifndef _WIN32 struct stat s; if (stat (p, &s) != 0) -#else - struct _stat s; - if (_stat (p, &s) != 0) -#endif { if (errno == ENOENT || errno == ENOTDIR) return timestamp_nonexistent; @@ -549,11 +548,50 @@ namespace butl throw_generic_error (errno); } - return S_ISREG (s.st_mode) - ? system_clock::from_time_t (s.st_mtime) + + if (!S_ISREG (s.st_mode)) + return timestamp_nonexistent; + + return system_clock::from_time_t (s.st_mtime) + + chrono::duration_cast<duration> ( + chrono::nanoseconds (mnsec<struct stat> (&s, true))); +#else + + WIN32_FILE_ATTRIBUTE_DATA s; + + if (!GetFileAttributesExA (p, GetFileExInfoStandard, &s)) + { + DWORD ec (GetLastError ()); + + if (ec == ERROR_FILE_NOT_FOUND || + ec == ERROR_PATH_NOT_FOUND || + ec == ERROR_INVALID_NAME || + ec == ERROR_INVALID_DRIVE || + ec == ERROR_BAD_PATHNAME || + ec == ERROR_BAD_NETPATH) + return timestamp_nonexistent; + + throw_system_error (ec); + } + + if (s.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY != 0) + return timestamp_nonexistent; + + // Time in FILETIME is in 100 nanosecond "ticks" since "Windows epoch" + // (1601-01-01T00:00:00Z). To convert it to "UNIX epoch" + // (1970-01-01T00:00:00Z) we need to subtract 11644473600 seconds. + // + const FILETIME& t (s.ftLastWriteTime); + + uint64_t ns ((static_cast<uint64_t> (t.dwHighDateTime) << 32) | + t.dwLowDateTime); + + ns -= 11644473600ULL * 10000000; // Now in UNIX epoch. + ns *= 100; // Now in nanoseconds. + + return timestamp ( chrono::duration_cast<duration> ( - chrono::nanoseconds (mnsec<struct stat> (&s, true))) - : timestamp_nonexistent; + chrono::nanoseconds (ns))); +#endif } permissions |