| 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 |  | 
|---|
| 9 | Module Name: | 
|---|
| 10 |  | 
|---|
| 11 | filetime.cpp | 
|---|
| 12 |  | 
|---|
| 13 | Abstract: | 
|---|
| 14 |  | 
|---|
| 15 | Implementation of the file WIN API related to file time. | 
|---|
| 16 |  | 
|---|
| 17 | Notes: | 
|---|
| 18 |  | 
|---|
| 19 | One very important thing to note is that on BSD systems, the stat structure | 
|---|
| 20 | stores nanoseconds for the time-related fields. This is implemented by | 
|---|
| 21 | replacing the time_t fields st_atime, st_mtime, and st_ctime by timespec | 
|---|
| 22 | structures, instead named st_atimespec, st_mtimespec, and st_ctimespec. | 
|---|
| 23 |  | 
|---|
| 24 | However, if _POSIX_SOURCE is defined, the fields are time_t values and use | 
|---|
| 25 | their POSIX names. For compatibility purposes, when _POSIX_SOURCE is NOT | 
|---|
| 26 | defined, 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 |  | 
|---|
| 34 | Furthermore, if _POSIX_SOURCE is defined, the structure still has | 
|---|
| 35 | additional fields for nanoseconds, named st_atimensec, st_mtimensec, and | 
|---|
| 36 | st_ctimensec. | 
|---|
| 37 |  | 
|---|
| 38 | In the PAL, there is a configure check to see if the system supports | 
|---|
| 39 | nanoseconds for the time-related fields. This source file also sets macros | 
|---|
| 40 | so that STAT_ATIME_NSEC etc. will always refer to the appropriate field | 
|---|
| 41 | if it exists, and are defined as 0 otherwise. | 
|---|
| 42 |  | 
|---|
| 43 | -- | 
|---|
| 44 |  | 
|---|
| 45 | Also note that there is no analog to "creation time" on Linux systems. | 
|---|
| 46 | Instead, we use the inode change time, which is set to the current time | 
|---|
| 47 | whenever mtime changes or when chmod, chown, etc. syscalls modify the | 
|---|
| 48 | file status; or mtime if older. Ideally we would use birthtime when | 
|---|
| 49 | available. | 
|---|
| 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 |  | 
|---|
| 69 | using namespace CorUnix; | 
|---|
| 70 |  | 
|---|
| 71 | SET_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 | */ | 
|---|
| 99 | static const __int64 SECS_BETWEEN_1601_AND_1970_EPOCHS = 11644473600LL; | 
|---|
| 100 | static const __int64 SECS_TO_100NS = 10000000; /* 10^7 */ | 
|---|
| 101 |  | 
|---|
| 102 | #ifdef __APPLE__ | 
|---|
| 103 | static const __int64 SECS_BETWEEN_1601_AND_2001_EPOCHS = 12622780800LL; | 
|---|
| 104 | #endif // __APPLE__ | 
|---|
| 105 |  | 
|---|
| 106 | /*++ | 
|---|
| 107 | Function: | 
|---|
| 108 | CompareFileTime | 
|---|
| 109 |  | 
|---|
| 110 | See MSDN doc. | 
|---|
| 111 | --*/ | 
|---|
| 112 | LONG | 
|---|
| 113 | PALAPI | 
|---|
| 114 | CompareFileTime( | 
|---|
| 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 | /*++ | 
|---|
| 152 | Function: | 
|---|
| 153 | GetSystemTimeAsFileTime | 
|---|
| 154 |  | 
|---|
| 155 | See MSDN doc. | 
|---|
| 156 | --*/ | 
|---|
| 157 | VOID | 
|---|
| 158 | PALAPI | 
|---|
| 159 | GetSystemTimeAsFileTime( | 
|---|
| 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 | /*++ | 
|---|
| 194 | Function: | 
|---|
| 195 | FILECFAbsoluteTimeToFileTime | 
|---|
| 196 |  | 
|---|
| 197 | Convert a CFAbsoluteTime value to a win32 FILETIME structure, as described | 
|---|
| 198 | in MSDN documentation. CFAbsoluteTime is the number of seconds elapsed since | 
|---|
| 199 | 00:00 01 January 2001 UTC (Mac OS X epoch), while FILETIME represents a | 
|---|
| 200 | 64-bit number of 100-nanosecond intervals that have passed since 00:00 | 
|---|
| 201 | 01 January 1601 UTC (win32 epoch). | 
|---|
| 202 | --*/ | 
|---|
| 203 | FILETIME 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 | /*++ | 
|---|
| 222 | Function: | 
|---|
| 223 | FILEUnixTimeToFileTime | 
|---|
| 224 |  | 
|---|
| 225 | Convert a time_t value to a win32 FILETIME structure, as described in | 
|---|
| 226 | MSDN documentation. time_t is the number of seconds elapsed since | 
|---|
| 227 | 00:00 01 January 1970 UTC (Unix epoch), while FILETIME represents a | 
|---|
| 228 | 64-bit number of 100-nanosecond intervals that have passed since 00:00 | 
|---|
| 229 | 01 January 1601 UTC (win32 epoch). | 
|---|
| 230 | --*/ | 
|---|
| 231 | FILETIME 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 | /*++ | 
|---|
| 250 | Function: | 
|---|
| 251 | FILEFileTimeToUnixTime | 
|---|
| 252 |  | 
|---|
| 253 | See FILEUnixTimeToFileTime above. | 
|---|
| 254 |  | 
|---|
| 255 | This function takes a win32 FILETIME structures, returns the equivalent | 
|---|
| 256 | time_t value, and, if the nsec parameter is non-null, also returns the | 
|---|
| 257 | nanoseconds. | 
|---|
| 258 |  | 
|---|
| 259 | NOTE: a 32-bit time_t is only capable of representing dates between | 
|---|
| 260 | 13 December 1901 and 19 January 2038. This function will calculate the | 
|---|
| 261 | number of seconds (positive or negative) since the Unix epoch, however if | 
|---|
| 262 | this value is outside of the range of 32-bit numbers, the result will be | 
|---|
| 263 | truncated on systems with a 32-bit time_t. | 
|---|
| 264 | --*/ | 
|---|
| 265 | time_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 | /** | 
|---|
| 301 | Function | 
|---|
| 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 | --*/ | 
|---|
| 310 | BOOL 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 |  | 
|---|