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/tool_filetime.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 src/tool_filetime.c (limited to 'src/tool_filetime.c') 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)) */ -- cgit v1.2.3