From ee3c83f39c90126cabb9be896931725c32f22e09 Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Fri, 11 Nov 2016 02:48:52 -0500 Subject: 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 --- src/tool_operate.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) (limited to 'src/tool_operate.c') 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) { -- cgit v1.2.3