archive_time.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /*-
  2. * Copyright © 2025 ARJANEN Loïc Jean David
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
  15. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  16. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  17. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
  18. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  19. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  20. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  21. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  23. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. #include "archive_platform.h"
  26. #include "archive_private.h"
  27. #include "archive_time_private.h"
  28. #include <limits.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #define NTFS_EPOC_TIME ARCHIVE_LITERAL_ULL(11644473600)
  32. #define NTFS_TICKS ARCHIVE_LITERAL_ULL(10000000)
  33. #define NTFS_EPOC_TICKS (NTFS_EPOC_TIME * NTFS_TICKS)
  34. #define DOS_MIN_TIME 0x00210000U
  35. #define DOS_MAX_TIME 0xff9fbf7dU
  36. #if defined(_WIN32) && !defined(__CYGWIN__)
  37. #include <winnt.h>
  38. /* Windows FILETIME to NTFS time. */
  39. uint64_t
  40. FILETIME_to_ntfs(const FILETIME* filetime)
  41. {
  42. ULARGE_INTEGER utc;
  43. utc.HighPart = filetime->dwHighDateTime;
  44. utc.LowPart = filetime->dwLowDateTime;
  45. return utc.QuadPart;
  46. }
  47. #endif
  48. /* Convert an MSDOS-style date/time into Unix-style time. */
  49. int64_t
  50. dos_to_unix(uint32_t dos_time)
  51. {
  52. uint16_t msTime, msDate;
  53. struct tm ts;
  54. time_t t;
  55. msTime = (0xFFFF & dos_time);
  56. msDate = (dos_time >> 16);
  57. memset(&ts, 0, sizeof(ts));
  58. ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */
  59. ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */
  60. ts.tm_mday = msDate & 0x1f; /* Day of month. */
  61. ts.tm_hour = (msTime >> 11) & 0x1f;
  62. ts.tm_min = (msTime >> 5) & 0x3f;
  63. ts.tm_sec = (msTime << 1) & 0x3e;
  64. ts.tm_isdst = -1;
  65. t = mktime(&ts);
  66. return (int64_t)(t == (time_t)-1 ? INT32_MAX : t);
  67. }
  68. /* Convert into MSDOS-style date/time. */
  69. uint32_t
  70. unix_to_dos(int64_t unix_time)
  71. {
  72. struct tm *t;
  73. uint32_t dt;
  74. time_t ut = unix_time;
  75. #if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
  76. struct tm tmbuf;
  77. #endif
  78. if (sizeof(time_t) < sizeof(int64_t) && (int64_t)ut != unix_time) {
  79. ut = (time_t)(unix_time > 0 ? INT32_MAX : INT32_MIN);
  80. }
  81. #if defined(HAVE_LOCALTIME_S)
  82. t = localtime_s(&tmbuf, &ut) ? NULL : &tmbuf;
  83. #elif defined(HAVE_LOCALTIME_R)
  84. t = localtime_r(&ut, &tmbuf);
  85. #else
  86. t = localtime(&ut);
  87. #endif
  88. dt = 0;
  89. if (t != NULL && t->tm_year >= INT_MIN + 80) {
  90. const int year = t->tm_year - 80;
  91. if (year & ~0x7f) {
  92. dt = year > 0 ? DOS_MAX_TIME : DOS_MIN_TIME;
  93. }
  94. else {
  95. dt += (year & 0x7f) << 9;
  96. dt += ((t->tm_mon + 1) & 0x0f) << 5;
  97. dt += (t->tm_mday & 0x1f);
  98. dt <<= 16;
  99. dt += (t->tm_hour & 0x1f) << 11;
  100. dt += (t->tm_min & 0x3f) << 5;
  101. /* Only counting every 2 seconds. */
  102. dt += (t->tm_sec & 0x3e) >> 1;
  103. }
  104. }
  105. if (dt > DOS_MAX_TIME) {
  106. dt = DOS_MAX_TIME;
  107. }
  108. else if (dt < DOS_MIN_TIME) {
  109. dt = DOS_MIN_TIME;
  110. }
  111. return dt;
  112. }
  113. /* Convert NTFS time to Unix sec/nsec */
  114. void
  115. ntfs_to_unix(uint64_t ntfs, int64_t* secs, uint32_t* nsecs)
  116. {
  117. if (ntfs > INT64_MAX) {
  118. ntfs -= NTFS_EPOC_TICKS;
  119. *secs = ntfs / NTFS_TICKS;
  120. *nsecs = 100 * (ntfs % NTFS_TICKS);
  121. }
  122. else {
  123. lldiv_t tdiv;
  124. int64_t value = (int64_t)ntfs - (int64_t)NTFS_EPOC_TICKS;
  125. tdiv = lldiv(value, NTFS_TICKS);
  126. *secs = tdiv.quot;
  127. *nsecs = (uint32_t)(tdiv.rem * 100);
  128. }
  129. }
  130. /* Convert Unix sec/nsec to NTFS time */
  131. uint64_t
  132. unix_to_ntfs(int64_t secs, uint32_t nsecs)
  133. {
  134. uint64_t ntfs;
  135. if (secs < -(int64_t)NTFS_EPOC_TIME)
  136. return 0;
  137. ntfs = secs + NTFS_EPOC_TIME;
  138. if (ntfs > UINT64_MAX / NTFS_TICKS)
  139. return UINT64_MAX;
  140. ntfs *= NTFS_TICKS;
  141. if (ntfs > UINT64_MAX - nsecs/100)
  142. return UINT64_MAX;
  143. return ntfs + nsecs/100;
  144. }