From d25b0503795f1fbf557632ce870298f52f2a78c1 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Mon, 5 Feb 2018 21:57:39 +0100 Subject: time-cond: fix reading the file modification time on Windows On Windows, stat() may adjust the unix file time by a daylight saving time offset. Avoid this by calling GetFileTime() instead. Fixes #2164 Closes #2204 --- src/Makefile.inc | 2 + src/tool_cfgable.h | 2 +- src/tool_filetime.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tool_filetime.h | 38 +++++++++++++ src/tool_getparam.c | 17 +++--- src/tool_operate.c | 95 ++------------------------------ 6 files changed, 210 insertions(+), 98 deletions(-) create mode 100644 src/tool_filetime.c create mode 100644 src/tool_filetime.h diff --git a/src/Makefile.inc b/src/Makefile.inc index d3c7c2bbf..e0506c7f5 100644 --- a/src/Makefile.inc +++ b/src/Makefile.inc @@ -37,6 +37,7 @@ CURL_CFILES = \ tool_dirhie.c \ tool_doswin.c \ tool_easysrc.c \ + tool_filetime.c \ tool_formparse.c \ tool_getparam.c \ tool_getpass.c \ @@ -77,6 +78,7 @@ CURL_HFILES = \ tool_dirhie.h \ tool_doswin.h \ tool_easysrc.h \ + tool_filetime.h \ tool_formparse.h \ tool_getparam.h \ tool_getpass.h \ diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 713739e7a..0f316775d 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -169,7 +169,7 @@ struct OperationConfig { long proxy_ssl_version; long ip_version; curl_TimeCond timecond; - time_t condtime; + curl_off_t condtime; struct curl_slist *headers; struct curl_slist *proxyheaders; curl_mime *mimepost; diff --git a/src/tool_filetime.c b/src/tool_filetime.c new file mode 100644 index 000000000..6071e44d2 --- /dev/null +++ b/src/tool_filetime.c @@ -0,0 +1,154 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "tool_filetime.h" + +#ifdef HAVE_UTIME_H +# include +#elif defined(HAVE_SYS_UTIME_H) +# include +#endif + +curl_off_t getfiletime(const char *filename, FILE *error_stream) +{ + curl_off_t result = -1; + +/* Windows stat() may attempt to adjust the unix GMT file time 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 stat and get the times directly. */ +#if defined(WIN32) && (SIZEOF_CURL_OFF_T >= 8) + HANDLE hfile; + + hfile = CreateFileA(filename, FILE_READ_ATTRIBUTES, + (FILE_SHARE_READ | FILE_SHARE_WRITE | + FILE_SHARE_DELETE), + NULL, OPEN_EXISTING, 0, NULL); + if(hfile != INVALID_HANDLE_VALUE) { + FILETIME ft; + if(GetFileTime(hfile, NULL, NULL, &ft)) { + curl_off_t converted = (curl_off_t)ft.dwLowDateTime + | ((curl_off_t)ft.dwHighDateTime) << 32; + + if(converted < CURL_OFF_T_C(116444736000000000)) { + fprintf(error_stream, + "Failed to get filetime: underflow\n"); + } + else { + result = (converted - CURL_OFF_T_C(116444736000000000)) / 10000000; + } + } + else { + fprintf(error_stream, + "Failed to get filetime: " + "GetFileTime failed: GetLastError %u\n", + (unsigned int)GetLastError()); + } + CloseHandle(hfile); + } + else if(GetLastError() != ERROR_FILE_NOT_FOUND) { + fprintf(error_stream, + "Failed to get filetime: " + "CreateFile failed: GetLastError %u\n", + (unsigned int)GetLastError()); + } +#else + struct_stat statbuf; + if(-1 != stat(filename, &statbuf)) { + result = (curl_off_t)statbuf.st_mtime; + } + else if(errno != ENOENT) { + fprintf(error_stream, + "Failed to get filetime: %s\n", strerror(errno)); + } +#endif + return result; +} + +#if defined(HAVE_UTIME) || defined(HAVE_UTIMES) || \ + (defined(WIN32) && (SIZEOF_CURL_OFF_T >= 8)) +void setfiletime(curl_off_t filetime, const char *filename, + FILE *error_stream) +{ + if(filetime >= 0) { +/* Windows utime() may attempt to adjust the unix GMT file time 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) && (SIZEOF_CURL_OFF_T >= 8) + HANDLE hfile; + + /* 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)) { + fprintf(error_stream, + "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T + " on outfile: overflow\n", filetime); + return; + } + + hfile = CreateFileA(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(error_stream, + "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T + " on outfile: SetFileTime failed: GetLastError %u\n", + filetime, (unsigned int)GetLastError()); + } + CloseHandle(hfile); + } + else { + fprintf(error_stream, + "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T + " on outfile: CreateFile failed: GetLastError %u\n", + filetime, (unsigned int)GetLastError()); + } + +#elif defined(HAVE_UTIMES) + struct timeval times[2]; + times[0].tv_sec = times[1].tv_sec = (time_t)filetime; + times[0].tv_usec = times[1].tv_usec = 0; + if(utimes(filename, times)) { + fprintf(error_stream, + "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T + " on outfile: %s\n", filetime, strerror(errno)); + } + +#elif defined(HAVE_UTIME) + struct utimbuf times; + times.actime = (time_t)filetime; + times.modtime = (time_t)filetime; + if(utime(filename, ×)) { + fprintf(error_stream, + "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T + " on outfile: %s\n", filetime, strerror(errno)); + } +#endif + } +} +#endif /* defined(HAVE_UTIME) || defined(HAVE_UTIMES) || \ + (defined(WIN32) && (SIZEOF_CURL_OFF_T >= 8)) */ diff --git a/src/tool_filetime.h b/src/tool_filetime.h new file mode 100644 index 000000000..966a70b46 --- /dev/null +++ b/src/tool_filetime.h @@ -0,0 +1,38 @@ +#ifndef HEADER_CURL_TOOL_FILETIME_H +#define HEADER_CURL_TOOL_FILETIME_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "tool_setup.h" + +curl_off_t getfiletime(const char *filename, FILE *error_stream); + +#if defined(HAVE_UTIME) || defined(HAVE_UTIMES) || \ + (defined(WIN32) && (SIZEOF_CURL_OFF_T >= 8)) +void setfiletime(curl_off_t filetime, const char *filename, + FILE *error_stream); +#else +#define setfiletime(a,b,c) Curl_nop_stmt +#endif /* defined(HAVE_UTIME) || defined(HAVE_UTIMES) || \ + (defined(WIN32) && (SIZEOF_CURL_OFF_T >= 8)) */ + +#endif /* HEADER_CURL_TOOL_FILETIME_H */ + diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 015d63551..46e7dd3cd 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -31,6 +31,7 @@ #include "tool_cfgable.h" #include "tool_cb_prg.h" #include "tool_convert.h" +#include "tool_filetime.h" #include "tool_formparse.h" #include "tool_getparam.h" #include "tool_helpers.h" @@ -2087,11 +2088,15 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ break; } now = time(NULL); - config->condtime = curl_getdate(nextarg, &now); - if(-1 == (int)config->condtime) { + config->condtime = (curl_off_t)curl_getdate(nextarg, &now); + if(-1 == config->condtime) { /* now let's see if it is a file name to get the time from instead! */ - struct_stat statbuf; - if(-1 == stat(nextarg, &statbuf)) { + curl_off_t filetime = getfiletime(nextarg, config->global->errors); + if(filetime >= 0) { + /* pull the time out from the file */ + config->condtime = filetime; + } + else { /* failed, remove time condition */ config->timecond = CURL_TIMECOND_NONE; warnf(global, @@ -2099,10 +2104,6 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ "a file name). Disabling time condition. " "See curl_getdate(3) for valid date syntax.\n"); } - else { - /* pull the time out from the file */ - config->condtime = statbuf.st_mtime; - } } break; default: /* unknown flag */ diff --git a/src/tool_operate.c b/src/tool_operate.c index 5401955af..f326a0d0c 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -25,12 +25,6 @@ # include #endif -#ifdef HAVE_UTIME_H -# include -#elif defined(HAVE_SYS_UTIME_H) -# include -#endif - #ifdef HAVE_LOCALE_H # include #endif @@ -56,6 +50,7 @@ #include "tool_dirhie.h" #include "tool_doswin.h" #include "tool_easysrc.h" +#include "tool_filetime.h" #include "tool_getparam.h" #include "tool_helpers.h" #include "tool_homedir.h" @@ -174,79 +169,6 @@ static curl_off_t VmsSpecialSize(const char *name, } #endif /* __VMS */ -#if defined(HAVE_UTIME) || \ - (defined(WIN32) && (SIZEOF_CURL_OFF_T >= 8)) -static void setfiletime(long filetime, const char *filename, - FILE *error_stream) -{ - 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) && (SIZEOF_CURL_OFF_T >= 8) - HANDLE hfile; - -#if (SIZEOF_LONG >= 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)) { - fprintf(error_stream, - "Failed to set filetime %ld on outfile: overflow\n", - filetime); - return; - } -#endif /* SIZEOF_LONG >= 8 */ - - hfile = CreateFileA(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(error_stream, - "Failed to set filetime %ld on outfile: " - "SetFileTime failed: GetLastError %u\n", - filetime, GetLastError()); - } - CloseHandle(hfile); - } - else { - fprintf(error_stream, - "Failed to set filetime %ld on outfile: " - "CreateFile failed: GetLastError %u\n", - filetime, GetLastError()); - } - -#elif defined(HAVE_UTIMES) - struct timeval times[2]; - times[0].tv_sec = times[1].tv_sec = filetime; - times[0].tv_usec = times[1].tv_usec = 0; - if(utimes(filename, times)) { - fprintf(error_stream, - "Failed to set filetime %ld on outfile: errno %d\n", - filetime, errno); - } - -#elif defined(HAVE_UTIME) - struct utimbuf times; - times.actime = (time_t)filetime; - times.modtime = (time_t)filetime; - if(utime(filename, ×)) { - fprintf(error_stream, - "Failed to set filetime %ld on outfile: errno %d\n", - filetime, errno); - } -#endif - } -} -#endif /* defined(HAVE_UTIME) || \ - (defined(WIN32) && (SIZEOF_CURL_OFF_T >= 8)) */ - #define BUFFER_SIZE (100*1024) static CURLcode operate_do(struct GlobalConfig *global, @@ -710,7 +632,7 @@ static CURLcode operate_do(struct GlobalConfig *global, * to be considered with one appended if implied CC */ #ifdef __VMS - /* Calculate the real upload site for VMS */ + /* Calculate the real upload size for VMS */ infd = -1; if(stat(uploadfile, &fileinfo) == 0) { fileinfo.st_size = VmsSpecialSize(uploadfile, &fileinfo); @@ -1232,7 +1154,7 @@ static CURLcode operate_do(struct GlobalConfig *global, #endif my_setopt_enum(curl, CURLOPT_TIMECONDITION, (long)config->timecond); - my_setopt(curl, CURLOPT_TIMEVALUE, (long)config->condtime); + my_setopt(curl, CURLOPT_TIMEVALUE_LARGE, config->condtime); my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest); customrequest_helper(config, config->httpreq, config->customrequest); my_setopt(curl, CURLOPT_STDERR, global->errors); @@ -1841,18 +1763,13 @@ static CURLcode operate_do(struct GlobalConfig *global, } #endif -#if defined(HAVE_UTIME) || \ - (defined(WIN32) && (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) - setfiletime(filetime, outs.filename, config->global->errors); + curl_off_t filetime = -1; + curl_easy_getinfo(curl, CURLINFO_FILETIME_T, &filetime); + setfiletime(filetime, outs.filename, config->global->errors); } -#endif /* defined(HAVE_UTIME) || \ - (defined(WIN32) && (SIZEOF_CURL_OFF_T >= 8)) */ #ifdef USE_METALINK if(!metalink && config->use_metalink && result == CURLE_OK) { -- cgit v1.2.3