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