xref: /third_party/curl/src/tool_filetime.c (revision 13498266)
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, &times)) {
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