1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24#include "tool_filetime.h" 25#include "tool_cfgable.h" 26#include "tool_msgs.h" 27#include "curlx.h" 28 29#ifdef HAVE_UTIME_H 30# include <utime.h> 31#elif defined(HAVE_SYS_UTIME_H) 32# include <sys/utime.h> 33#endif 34 35/* Returns 0 on success, non-zero on file problems */ 36int getfiletime(const char *filename, struct GlobalConfig *global, 37 curl_off_t *stamp) 38{ 39 int rc = 1; 40 41/* Windows stat() may attempt to adjust the unix GMT file time by a daylight 42 saving time offset and since it's GMT that is bad behavior. When we have 43 access to a 64-bit type we can bypass stat and get the times directly. */ 44#if defined(_WIN32) 45 HANDLE hfile; 46 TCHAR *tchar_filename = curlx_convert_UTF8_to_tchar((char *)filename); 47 48 hfile = CreateFile(tchar_filename, FILE_READ_ATTRIBUTES, 49 (FILE_SHARE_READ | FILE_SHARE_WRITE | 50 FILE_SHARE_DELETE), 51 NULL, OPEN_EXISTING, 0, NULL); 52 curlx_unicodefree(tchar_filename); 53 if(hfile != INVALID_HANDLE_VALUE) { 54 FILETIME ft; 55 if(GetFileTime(hfile, NULL, NULL, &ft)) { 56 curl_off_t converted = (curl_off_t)ft.dwLowDateTime 57 | ((curl_off_t)ft.dwHighDateTime) << 32; 58 59 if(converted < CURL_OFF_T_C(116444736000000000)) 60 warnf(global, "Failed to get filetime: underflow"); 61 else { 62 *stamp = (converted - CURL_OFF_T_C(116444736000000000)) / 10000000; 63 rc = 0; 64 } 65 } 66 else { 67 warnf(global, "Failed to get filetime: " 68 "GetFileTime failed: GetLastError %u", 69 (unsigned int)GetLastError()); 70 } 71 CloseHandle(hfile); 72 } 73 else if(GetLastError() != ERROR_FILE_NOT_FOUND) { 74 warnf(global, "Failed to get filetime: " 75 "CreateFile failed: GetLastError %u", 76 (unsigned int)GetLastError()); 77 } 78#else 79 struct_stat statbuf; 80 if(-1 != stat(filename, &statbuf)) { 81 *stamp = (curl_off_t)statbuf.st_mtime; 82 rc = 0; 83 } 84 else 85 warnf(global, "Failed to get filetime: %s", strerror(errno)); 86#endif 87 return rc; 88} 89 90#if defined(HAVE_UTIME) || defined(HAVE_UTIMES) || defined(_WIN32) 91void setfiletime(curl_off_t filetime, const char *filename, 92 struct GlobalConfig *global) 93{ 94 if(filetime >= 0) { 95/* Windows utime() may attempt to adjust the unix GMT file time by a daylight 96 saving time offset and since it's GMT that is bad behavior. When we have 97 access to a 64-bit type we can bypass utime and set the times directly. */ 98#if defined(_WIN32) 99 HANDLE hfile; 100 TCHAR *tchar_filename = curlx_convert_UTF8_to_tchar((char *)filename); 101 102 /* 910670515199 is the maximum unix filetime that can be used as a 103 Windows FILETIME without overflow: 30827-12-31T23:59:59. */ 104 if(filetime > CURL_OFF_T_C(910670515199)) { 105 warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T 106 " on outfile: overflow", filetime); 107 curlx_unicodefree(tchar_filename); 108 return; 109 } 110 111 hfile = CreateFile(tchar_filename, FILE_WRITE_ATTRIBUTES, 112 (FILE_SHARE_READ | FILE_SHARE_WRITE | 113 FILE_SHARE_DELETE), 114 NULL, OPEN_EXISTING, 0, NULL); 115 curlx_unicodefree(tchar_filename); 116 if(hfile != INVALID_HANDLE_VALUE) { 117 curl_off_t converted = ((curl_off_t)filetime * 10000000) + 118 CURL_OFF_T_C(116444736000000000); 119 FILETIME ft; 120 ft.dwLowDateTime = (DWORD)(converted & 0xFFFFFFFF); 121 ft.dwHighDateTime = (DWORD)(converted >> 32); 122 if(!SetFileTime(hfile, NULL, &ft, &ft)) { 123 warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T 124 " on outfile: SetFileTime failed: GetLastError %u", 125 filetime, (unsigned int)GetLastError()); 126 } 127 CloseHandle(hfile); 128 } 129 else { 130 warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T 131 " on outfile: CreateFile failed: GetLastError %u", 132 filetime, (unsigned int)GetLastError()); 133 } 134 135#elif defined(HAVE_UTIMES) 136 struct timeval times[2]; 137 times[0].tv_sec = times[1].tv_sec = (time_t)filetime; 138 times[0].tv_usec = times[1].tv_usec = 0; 139 if(utimes(filename, times)) { 140 warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T 141 " on '%s': %s", filetime, filename, strerror(errno)); 142 } 143 144#elif defined(HAVE_UTIME) 145 struct utimbuf times; 146 times.actime = (time_t)filetime; 147 times.modtime = (time_t)filetime; 148 if(utime(filename, ×)) { 149 warnf(global, "Failed to set filetime %" CURL_FORMAT_CURL_OFF_T 150 " on '%s': %s", filetime, filename, strerror(errno)); 151 } 152#endif 153 } 154} 155#endif /* defined(HAVE_UTIME) || defined(HAVE_UTIMES) || \ 156 defined(_WIN32) */ 157