From 8e25b3cb8be2a23b1e667bae614dc928bba798c9 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 24 Apr 2017 14:12:06 +0200 Subject: 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. --- butl/filesystem.cxx | 66 +++++++++++++++++++++++++++++++++++++++++------------ 1 file 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 - 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 + // inline constexpr int + // mnsec (...) {return 0;} template inline constexpr auto @@ -430,9 +433,9 @@ namespace butl return s->st_atime_n; // AIX 5.2 and later. } - template - inline constexpr int - ansec (...) {return 0;} + // template + // 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 ( + chrono::nanoseconds (mnsec (&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 (t.dwHighDateTime) << 32) | + t.dwLowDateTime); + + ns -= 11644473600ULL * 10000000; // Now in UNIX epoch. + ns *= 100; // Now in nanoseconds. + + return timestamp ( chrono::duration_cast ( - chrono::nanoseconds (mnsec (&s, true))) - : timestamp_nonexistent; + chrono::nanoseconds (ns))); +#endif } permissions -- cgit v1.1