1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*++
6
7
8
9Module Name:
10
11 filetime.cpp
12
13Abstract:
14
15 Implementation of the file WIN API related to file time.
16
17Notes:
18
19One very important thing to note is that on BSD systems, the stat structure
20stores nanoseconds for the time-related fields. This is implemented by
21replacing the time_t fields st_atime, st_mtime, and st_ctime by timespec
22structures, instead named st_atimespec, st_mtimespec, and st_ctimespec.
23
24However, if _POSIX_SOURCE is defined, the fields are time_t values and use
25their POSIX names. For compatibility purposes, when _POSIX_SOURCE is NOT
26defined, the time-related fields are defined in sys/stat.h as:
27
28#ifndef _POSIX_SOURCE
29#define st_atime st_atimespec.tv_sec
30#define st_mtime st_mtimespec.tv_sec
31#define st_ctime st_ctimespec.tv_sec
32#endif
33
34Furthermore, if _POSIX_SOURCE is defined, the structure still has
35additional fields for nanoseconds, named st_atimensec, st_mtimensec, and
36st_ctimensec.
37
38In the PAL, there is a configure check to see if the system supports
39nanoseconds for the time-related fields. This source file also sets macros
40so that STAT_ATIME_NSEC etc. will always refer to the appropriate field
41if it exists, and are defined as 0 otherwise.
42
43--
44
45Also note that there is no analog to "creation time" on Linux systems.
46Instead, we use the inode change time, which is set to the current time
47whenever mtime changes or when chmod, chown, etc. syscalls modify the
48file status; or mtime if older. Ideally we would use birthtime when
49available.
50
51
52--*/
53
54#include "pal/corunix.hpp"
55#include "pal/dbgmsg.h"
56#include "pal/filetime.h"
57#include "pal/thread.hpp"
58#include "pal/file.hpp"
59
60#include <sys/types.h>
61#include <sys/stat.h>
62#include <utime.h>
63#include <time.h>
64
65#if HAVE_SYS_TIME_H
66#include <sys/time.h>
67#endif // HAVE_SYS_TIME_H
68
69using namespace CorUnix;
70
71SET_DEFAULT_DEBUG_CHANNEL(FILE);
72
73// In safemath.h, Template SafeInt uses macro _ASSERTE, which need to use variable
74// defdbgchan defined by SET_DEFAULT_DEBUG_CHANNEL. Therefore, the include statement
75// should be placed after the SET_DEFAULT_DEBUG_CHANNEL(FILE)
76#include <safemath.h>
77
78/* Magic number explanation:
79
80 To 1970:
81 Both epochs are Gregorian. 1970 - 1601 = 369. Assuming a leap
82 year every four years, 369 / 4 = 92. However, 1700, 1800, and 1900
83 were NOT leap years, so 89 leap years, 280 non-leap years.
84 89 * 366 + 280 * 365 = 134774 days between epochs. Of course
85 60 * 60 * 24 = 86400 seconds per day, so 134774 * 86400 =
86 11644473600 = SECS_BETWEEN_1601_AND_1970_EPOCHS.
87
88 To 2001:
89 Again, both epochs are Gregorian. 2001 - 1601 = 400. Assuming a leap
90 year every four years, 400 / 4 = 100. However, 1700, 1800, and 1900
91 were NOT leap years (2000 was because it was divisible by 400), so
92 97 leap years, 303 non-leap years.
93 97 * 366 + 303 * 365 = 146097 days between epochs. 146097 * 86400 =
94 12622780800 = SECS_BETWEEN_1601_AND_2001_EPOCHS.
95
96 This result is also confirmed in the MSDN documentation on how
97 to convert a time_t value to a win32 FILETIME.
98*/
99static const __int64 SECS_BETWEEN_1601_AND_1970_EPOCHS = 11644473600LL;
100static const __int64 SECS_TO_100NS = 10000000; /* 10^7 */
101
102#ifdef __APPLE__
103static const __int64 SECS_BETWEEN_1601_AND_2001_EPOCHS = 12622780800LL;
104#endif // __APPLE__
105
106/*++
107Function:
108 CompareFileTime
109
110See MSDN doc.
111--*/
112LONG
113PALAPI
114CompareFileTime(
115 IN CONST FILETIME *lpFileTime1,
116 IN CONST FILETIME *lpFileTime2)
117{
118 __int64 First;
119 __int64 Second;
120
121 long Ret;
122
123 PERF_ENTRY(CompareFileTime);
124 ENTRY("CompareFileTime(lpFileTime1=%p lpFileTime2=%p)\n",
125 lpFileTime1, lpFileTime2);
126
127 First = ((__int64)lpFileTime1->dwHighDateTime << 32) +
128 lpFileTime1->dwLowDateTime;
129 Second = ((__int64)lpFileTime2->dwHighDateTime << 32) +
130 lpFileTime2->dwLowDateTime;
131
132 if ( First < Second )
133 {
134 Ret = -1;
135 }
136 else if ( First > Second )
137 {
138 Ret = 1;
139 }
140 else
141 {
142 Ret = 0;
143 }
144
145 LOGEXIT("CompareFileTime returns LONG %ld\n", Ret);
146 PERF_EXIT(CompareFileTime);
147 return Ret;
148}
149
150
151/*++
152Function:
153 GetSystemTimeAsFileTime
154
155See MSDN doc.
156--*/
157VOID
158PALAPI
159GetSystemTimeAsFileTime(
160 OUT LPFILETIME lpSystemTimeAsFileTime)
161{
162 PERF_ENTRY(GetSystemTimeAsFileTime);
163 ENTRY("GetSystemTimeAsFileTime(lpSystemTimeAsFileTime=%p)\n",
164 lpSystemTimeAsFileTime);
165
166#if HAVE_WORKING_CLOCK_GETTIME
167 struct timespec Time;
168 if (clock_gettime(CLOCK_REALTIME, &Time) == 0)
169 {
170 *lpSystemTimeAsFileTime = FILEUnixTimeToFileTime( Time.tv_sec, Time.tv_nsec );
171 }
172#else
173 struct timeval Time;
174 if (gettimeofday(&Time, NULL) == 0)
175 {
176 /* use (tv_usec * 1000) because 2nd arg is in nanoseconds */
177 *lpSystemTimeAsFileTime = FILEUnixTimeToFileTime( Time.tv_sec, Time.tv_usec * 1000);
178 }
179#endif
180 else
181 {
182 /* no way to indicate failure, so set time to zero */
183 ASSERT("clock_gettime or gettimeofday failed");
184 *lpSystemTimeAsFileTime = FILEUnixTimeToFileTime( 0, 0 );
185 }
186
187 LOGEXIT("GetSystemTimeAsFileTime returns.\n");
188 PERF_EXIT(GetSystemTimeAsFileTime);
189}
190
191
192#ifdef __APPLE__
193/*++
194Function:
195 FILECFAbsoluteTimeToFileTime
196
197Convert a CFAbsoluteTime value to a win32 FILETIME structure, as described
198in MSDN documentation. CFAbsoluteTime is the number of seconds elapsed since
19900:00 01 January 2001 UTC (Mac OS X epoch), while FILETIME represents a
20064-bit number of 100-nanosecond intervals that have passed since 00:00
20101 January 1601 UTC (win32 epoch).
202--*/
203FILETIME FILECFAbsoluteTimeToFileTime( CFAbsoluteTime sec )
204{
205 __int64 Result;
206 FILETIME Ret;
207
208 Result = ((__int64)sec + SECS_BETWEEN_1601_AND_2001_EPOCHS) * SECS_TO_100NS;
209
210 Ret.dwLowDateTime = (DWORD)Result;
211 Ret.dwHighDateTime = (DWORD)(Result >> 32);
212
213 TRACE("CFAbsoluteTime = [%9f] converts to Win32 FILETIME = [%#x:%#x]\n",
214 sec, Ret.dwHighDateTime, Ret.dwLowDateTime);
215
216 return Ret;
217}
218#endif // __APPLE__
219
220
221/*++
222Function:
223 FILEUnixTimeToFileTime
224
225Convert a time_t value to a win32 FILETIME structure, as described in
226MSDN documentation. time_t is the number of seconds elapsed since
22700:00 01 January 1970 UTC (Unix epoch), while FILETIME represents a
22864-bit number of 100-nanosecond intervals that have passed since 00:00
22901 January 1601 UTC (win32 epoch).
230--*/
231FILETIME FILEUnixTimeToFileTime( time_t sec, long nsec )
232{
233 __int64 Result;
234 FILETIME Ret;
235
236 Result = ((__int64)sec + SECS_BETWEEN_1601_AND_1970_EPOCHS) * SECS_TO_100NS +
237 (nsec / 100);
238
239 Ret.dwLowDateTime = (DWORD)Result;
240 Ret.dwHighDateTime = (DWORD)(Result >> 32);
241
242 TRACE("Unix time = [%ld.%09ld] converts to Win32 FILETIME = [%#x:%#x]\n",
243 sec, nsec, Ret.dwHighDateTime, Ret.dwLowDateTime);
244
245 return Ret;
246}
247
248
249/*++
250Function:
251 FILEFileTimeToUnixTime
252
253See FILEUnixTimeToFileTime above.
254
255This function takes a win32 FILETIME structures, returns the equivalent
256time_t value, and, if the nsec parameter is non-null, also returns the
257nanoseconds.
258
259NOTE: a 32-bit time_t is only capable of representing dates between
26013 December 1901 and 19 January 2038. This function will calculate the
261number of seconds (positive or negative) since the Unix epoch, however if
262this value is outside of the range of 32-bit numbers, the result will be
263truncated on systems with a 32-bit time_t.
264--*/
265time_t FILEFileTimeToUnixTime( FILETIME FileTime, long *nsec )
266{
267 __int64 UnixTime;
268
269 /* get the full win32 value, in 100ns */
270 UnixTime = ((__int64)FileTime.dwHighDateTime << 32) +
271 FileTime.dwLowDateTime;
272
273 /* convert to the Unix epoch */
274 UnixTime -= (SECS_BETWEEN_1601_AND_1970_EPOCHS * SECS_TO_100NS);
275
276 TRACE("nsec=%p\n", nsec);
277
278 if ( nsec )
279 {
280 /* get the number of 100ns, convert to ns */
281 *nsec = (UnixTime % SECS_TO_100NS) * 100;
282 }
283
284 UnixTime /= SECS_TO_100NS; /* now convert to seconds */
285
286 if ( (time_t)UnixTime != UnixTime )
287 {
288 WARN("Resulting value is too big for a time_t value\n");
289 }
290
291 TRACE("Win32 FILETIME = [%#x:%#x] converts to Unix time = [%ld.%09ld]\n",
292 FileTime.dwHighDateTime, FileTime.dwLowDateTime ,(long) UnixTime,
293 nsec?*nsec:0L);
294
295 return (time_t)UnixTime;
296}
297
298
299
300/**
301Function
302
303 FileTimeToSystemTime()
304
305 Helper function for FileTimeToDosTime.
306 Converts the necessary file time attibutes to system time, for
307 easier manipulation in FileTimeToDosTime.
308
309--*/
310BOOL PALAPI FileTimeToSystemTime( CONST FILETIME * lpFileTime,
311 LPSYSTEMTIME lpSystemTime )
312{
313 UINT64 FileTime = 0;
314 time_t UnixFileTime = 0;
315 struct tm * UnixSystemTime = 0;
316
317 /* Combine the file time. */
318 FileTime = lpFileTime->dwHighDateTime;
319 FileTime <<= 32;
320 FileTime |= (UINT)lpFileTime->dwLowDateTime;
321 bool isSafe = ClrSafeInt<UINT64>::subtraction(
322 FileTime,
323 SECS_BETWEEN_1601_AND_1970_EPOCHS * SECS_TO_100NS,
324 FileTime);
325
326 if (isSafe == true)
327 {
328#if HAVE_GMTIME_R
329 struct tm timeBuf;
330#endif /* HAVE_GMTIME_R */
331 /* Convert file time to unix time. */
332 if (((INT64)FileTime) < 0)
333 {
334 UnixFileTime = -1 - ( ( -FileTime - 1 ) / 10000000 );
335 }
336 else
337 {
338 UnixFileTime = FileTime / 10000000;
339 }
340
341 /* Convert unix file time to Unix System time. */
342#if HAVE_GMTIME_R
343 UnixSystemTime = gmtime_r( &UnixFileTime, &timeBuf );
344#else /* HAVE_GMTIME_R */
345 UnixSystemTime = gmtime( &UnixFileTime );
346#endif /* HAVE_GMTIME_R */
347
348 /* Convert unix system time to Windows system time. */
349 lpSystemTime->wDay = UnixSystemTime->tm_mday;
350
351 /* Unix time counts January as a 0, under Windows it is 1*/
352 lpSystemTime->wMonth = UnixSystemTime->tm_mon + 1;
353 /* Unix time returns the year - 1900, Windows returns the current year*/
354 lpSystemTime->wYear = UnixSystemTime->tm_year + 1900;
355
356 lpSystemTime->wSecond = UnixSystemTime->tm_sec;
357 lpSystemTime->wMinute = UnixSystemTime->tm_min;
358 lpSystemTime->wHour = UnixSystemTime->tm_hour;
359 return TRUE;
360 }
361 else
362 {
363 ERROR( "The file time is to large.\n" );
364 SetLastError(ERROR_INVALID_PARAMETER);
365 return FALSE;
366 }
367}
368
369