Commit ee3c83f3 authored by Jay Satiro's avatar Jay Satiro
Browse files

tool_operate: Fix --remote-time incorrect times on Windows

- Use Windows API SetFileTime to set the file time instead of utime.

Avoid utime on Windows if possible because it may apply a daylight
saving time offset to our UTC file time.

Bug: https://curl.haxx.se/mail/archive-2016-11/0033.html
Reported-by: Tim

Closes https://github.com/curl/curl/pull/1121
parent 89b78988
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -1016,10 +1016,12 @@ FAQ

  4.13 Why is curl -R on Windows one hour off?

  During daylight savings time, when -R is used, curl will set a time that
  appears one hour off. This happens due to a flaw in how Windows stores and
  uses file modification times and it is not easily worked around. For details
  on this problem, read this: http://www.codeproject.com/datetime/dstbugs.asp
  Since curl 7.53.0 this issue should be fixed as long as curl was built with
  any modern compiler that allows for a 64-bit curl_off_t type. For older
  compilers or prior curl versions it may set a time that appears one hour off.
  This happens due to a flaw in how Windows stores and uses file modification
  times and it is not easily worked around. For more details read this:
  http://www.codeproject.com/datetime/dstbugs.asp

  4.14 Redirects work in browser but not with curl!

+48 −3
Original line number Diff line number Diff line
@@ -1727,20 +1727,65 @@ static CURLcode operate_do(struct GlobalConfig *global,
        }
#endif

#ifdef HAVE_UTIME
#if defined(HAVE_UTIME) || \
    (defined(WIN32) && (CURL_SIZEOF_CURL_OFF_T >= 8))
        /* File time can only be set _after_ the file has been closed */
        if(!result && config->remote_time && outs.s_isreg && outs.filename) {
          /* Ask libcurl if we got a remote file time */
          long filetime = -1;
          curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime);
          if(filetime >= 0) {
/* Windows utime() may attempt to adjust our unix gmt 'filetime' by a daylight
   saving time offset and since it's GMT that is bad behavior. When we have
   access to a 64-bit type we can bypass utime and set the times directly. */
#if defined(WIN32) && (CURL_SIZEOF_CURL_OFF_T >= 8)
            /* 910670515199 is the maximum unix filetime that can be used as a
               Windows FILETIME without overflow: 30827-12-31T23:59:59. */
            if(filetime <= CURL_OFF_T_C(910670515199)) {
              HANDLE hfile = CreateFileA(outs.filename, FILE_WRITE_ATTRIBUTES,
                                         (FILE_SHARE_READ | FILE_SHARE_WRITE |
                                          FILE_SHARE_DELETE),
                                         NULL, OPEN_EXISTING, 0, NULL);
              if(hfile != INVALID_HANDLE_VALUE) {
                curl_off_t converted = ((curl_off_t)filetime * 10000000) +
                                       CURL_OFF_T_C(116444736000000000);
                FILETIME ft;
                ft.dwLowDateTime = (DWORD)(converted & 0xFFFFFFFF);
                ft.dwHighDateTime = (DWORD)(converted >> 32);
                if(!SetFileTime(hfile, NULL, &ft, &ft)) {
                  fprintf(config->global->errors,
                          "Failed to set filetime %ld on outfile: "
                          "SetFileTime failed: GetLastError %u\n",
                          filetime, GetLastError());
                }
                CloseHandle(hfile);
              }
              else {
                fprintf(config->global->errors,
                        "Failed to set filetime %ld on outfile: "
                        "CreateFile failed: GetLastError %u\n",
                        filetime, GetLastError());
              }
            }
            else {
              fprintf(config->global->errors,
                      "Failed to set filetime %ld on outfile: overflow\n",
                      filetime);
            }
#elif defined(HAVE_UTIME)
            struct utimbuf times;
            times.actime = (time_t)filetime;
            times.modtime = (time_t)filetime;
            utime(outs.filename, &times); /* set the time we got */
          }
            if(utime(outs.filename, &times)) {
              fprintf(config->global->errors,
                      "Failed to set filetime %ld on outfile: errno %d\n",
                      filetime, errno);
            }
#endif
          }
        }
#endif /* defined(HAVE_UTIME) || \
          (defined(WIN32) && (CURL_SIZEOF_CURL_OFF_T >= 8)) */

#ifdef USE_METALINK
        if(!metalink && config->use_metalink && result == CURLE_OK) {