Procházet zdrojové kódy

Merge pull request #1079 from aria2/mingw-fix-remote-time-dst

mingw: Use SetFileTime to avoid DST adjustment
Tatsuhiro Tsujikawa před 7 roky
rodič
revize
c882ae3d9c
1 změnil soubory, kde provedl 42 přidání a 4 odebrání
  1. 42 4
      src/File.cc

+ 42 - 4
src/File.cc

@@ -122,9 +122,9 @@ bool File::remove()
 
 #ifdef __MINGW32__
 namespace {
-HANDLE openFile(const std::string& filename)
+HANDLE openFile(const std::string& filename, bool readOnly = true)
 {
-  DWORD desiredAccess = GENERIC_READ;
+  DWORD desiredAccess = GENERIC_READ | (readOnly ? 0 : GENERIC_WRITE);
   DWORD sharedMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
   DWORD creationDisp = OPEN_EXISTING;
   return CreateFileW(utf8ToWChar(filename).c_str(), desiredAccess, sharedMode,
@@ -317,12 +317,50 @@ bool File::utime(const Time& actime, const Time& modtime) const
   struct timeval times[2] = {{actime.getTimeFromEpoch(), 0},
                              {modtime.getTimeFromEpoch(), 0}};
   return utimes(name_.c_str(), times) == 0;
-#else  // !HAVE_UTIMES
+#elif defined(__MINGW32__)
+  auto hn = openFile(name_, false);
+  if (hn == INVALID_HANDLE_VALUE) {
+    auto errNum = GetLastError();
+    A2_LOG_ERROR(fmt(EX_FILE_OPEN, name_.c_str(),
+                     util::formatLastError(errNum).c_str()));
+    return false;
+  }
+  // Use SetFileTime because Windows _wutime takes DST into
+  // consideration.
+  //
+  // std::chrono::time_point::time_since_epoch returns the amount of
+  // time between it and epoch Jan 1, 1970.  OTOH, FILETIME structure
+  // expects the epoch as Jan 1, 1601.  The resolution is 100
+  // nanoseconds.
+  constexpr auto offset = 116444736000000000LL;
+  uint64_t at = std::chrono::duration_cast<std::chrono::nanoseconds>(
+                    actime.getTime().time_since_epoch())
+                        .count() /
+                    100 +
+                offset;
+  uint64_t mt = std::chrono::duration_cast<std::chrono::nanoseconds>(
+                    modtime.getTime().time_since_epoch())
+                        .count() /
+                    100 +
+                offset;
+  FILETIME att{static_cast<DWORD>(at & 0xffffffff),
+               static_cast<DWORD>(at >> 32)};
+  FILETIME mtt{static_cast<DWORD>(mt & 0xffffffff),
+               static_cast<DWORD>(mt >> 32)};
+  auto rv = SetFileTime(hn, nullptr, &att, &mtt);
+  if (!rv) {
+    auto errNum = GetLastError();
+    A2_LOG_ERROR(fmt("SetFileTime failed, cause: %s",
+                     util::formatLastError(errNum).c_str()));
+  }
+  CloseHandle(hn);
+  return rv;
+#else  // !defined(HAVE_UTIMES) && !defined(__MINGW32__)
   a2utimbuf ub;
   ub.actime = actime.getTimeFromEpoch();
   ub.modtime = modtime.getTimeFromEpoch();
   return a2utime(utf8ToWChar(name_).c_str(), &ub) == 0;
-#endif // !HAVE_UTIMES
+#endif // !defined(HAVE_UTIMES) && !defined(__MINGW32__)
 }
 
 Time File::getModifiedTime()