diff options
author | Jay Satiro <raysatiro@yahoo.com> | 2016-11-11 02:48:52 -0500 |
---|---|---|
committer | Jay Satiro <raysatiro@yahoo.com> | 2016-12-28 21:19:40 -0500 |
commit | ee3c83f39c90126cabb9be896931725c32f22e09 (patch) | |
tree | 6946a6c24c6e25d2612063d0f24b8b6845f5afa6 | |
parent | 89b789884680134d5090c6de2e6b621e908b1902 (diff) |
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
-rw-r--r-- | docs/FAQ | 10 | ||||
-rw-r--r-- | src/tool_operate.c | 51 |
2 files changed, 54 insertions, 7 deletions
@@ -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! diff --git a/src/tool_operate.c b/src/tool_operate.c index 26662aec5..4fa32bcf3 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -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, ×); /* set the time we got */ + if(utime(outs.filename, ×)) { + fprintf(config->global->errors, + "Failed to set filetime %ld on outfile: errno %d\n", + filetime, errno); + } +#endif } } -#endif +#endif /* defined(HAVE_UTIME) || \ + (defined(WIN32) && (CURL_SIZEOF_CURL_OFF_T >= 8)) */ #ifdef USE_METALINK if(!metalink && config->use_metalink && result == CURLE_OK) { |