#include "archive_platform.h"
#include "archive_private.h"
#include "archive_time_private.h"
+#include <limits.h>
#include <stdlib.h>
+#include <string.h>
#define NTFS_EPOC_TIME ARCHIVE_LITERAL_ULL(11644473600)
#define NTFS_TICKS ARCHIVE_LITERAL_ULL(10000000)
#define NTFS_EPOC_TICKS (NTFS_EPOC_TIME * NTFS_TICKS)
#define DOS_MIN_TIME 0x00210000U
#define DOS_MAX_TIME 0xff9fbf7dU
-/* The min/max DOS Unix time are locale-dependant, so they're static variables,
- * initialised on first use. */
-static char dos_initialised = 0;
-static int64_t dos_max_unix;
-static int64_t dos_min_unix;
#if defined(_WIN32) && !defined(__CYGWIN__)
#include <winnt.h>
{
uint16_t msTime, msDate;
struct tm ts;
+ time_t t;
msTime = (0xFFFF & dos_time);
msDate = (dos_time >> 16);
+
+ memset(&ts, 0, sizeof(ts));
ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */
ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */
ts.tm_mday = msDate & 0x1f; /* Day of month. */
ts.tm_min = (msTime >> 5) & 0x3f;
ts.tm_sec = (msTime << 1) & 0x3e;
ts.tm_isdst = -1;
- return mktime(&ts);
+ t = mktime(&ts);
+ return (int64_t)(t == (time_t)-1 ? INT32_MAX : t);
}
/* Convert into MSDOS-style date/time. */
struct tm tmbuf;
#endif
- if (!dos_initialised) {
- dos_max_unix = dos_to_unix(DOS_MAX_TIME);
- dos_min_unix = dos_to_unix(DOS_MIN_TIME);
- dos_initialised = 1;
- }
- if (unix_time >= dos_max_unix) {
- return DOS_MAX_TIME;
- }
- else if(unix_time <= dos_min_unix) {
- return DOS_MIN_TIME;
+ if (sizeof(time_t) < sizeof(int64_t) && (int64_t)ut != unix_time) {
+ ut = (time_t)(unix_time > 0 ? INT32_MAX : INT32_MIN);
}
- else {
+
#if defined(HAVE_LOCALTIME_S)
- t = localtime_s(&tmbuf, &ut) ? NULL : &tmbuf;
+ t = localtime_s(&tmbuf, &ut) ? NULL : &tmbuf;
#elif defined(HAVE_LOCALTIME_R)
- t = localtime_r(&ut, &tmbuf);
+ t = localtime_r(&ut, &tmbuf);
#else
- t = localtime(&ut);
+ t = localtime(&ut);
#endif
- dt = 0;
- dt += ((t->tm_year - 80) & 0x7f) << 9;
- dt += ((t->tm_mon + 1) & 0x0f) << 5;
- dt += (t->tm_mday & 0x1f);
- dt <<= 16;
- dt += (t->tm_hour & 0x1f) << 11;
- dt += (t->tm_min & 0x3f) << 5;
- dt += (t->tm_sec & 0x3e) >> 1; /* Only counting every 2 seconds. */
- return dt;
+ dt = 0;
+ if (t != NULL && t->tm_year >= INT_MIN + 80) {
+ const int year = t->tm_year - 80;
+
+ if (year & ~0x7f) {
+ dt = year > 0 ? DOS_MAX_TIME : DOS_MIN_TIME;
+ }
+ else {
+ dt += (year & 0x7f) << 9;
+ dt += ((t->tm_mon + 1) & 0x0f) << 5;
+ dt += (t->tm_mday & 0x1f);
+ dt <<= 16;
+ dt += (t->tm_hour & 0x1f) << 11;
+ dt += (t->tm_min & 0x3f) << 5;
+ /* Only counting every 2 seconds. */
+ dt += (t->tm_sec & 0x3e) >> 1;
+ }
+ }
+ if (dt > DOS_MAX_TIME) {
+ dt = DOS_MAX_TIME;
}
+ else if (dt < DOS_MIN_TIME) {
+ dt = DOS_MIN_TIME;
+ }
+ return dt;
}
-/* Convert NTFS time to Unix sec/ncse */
+/* Convert NTFS time to Unix sec/nsec */
void
ntfs_to_unix(uint64_t ntfs, int64_t* secs, uint32_t* nsecs)
{
- ntfs -= NTFS_EPOC_TICKS;
- lldiv_t tdiv = lldiv(ntfs, NTFS_TICKS);
- *secs = tdiv.quot;
- *nsecs = tdiv.rem * 100;
+ if (ntfs > INT64_MAX) {
+ ntfs -= NTFS_EPOC_TICKS;
+ *secs = ntfs / NTFS_TICKS;
+ *nsecs = 100 * (ntfs % NTFS_TICKS);
+ }
+ else {
+ lldiv_t tdiv;
+ int64_t value = (int64_t)ntfs - (int64_t)NTFS_EPOC_TICKS;
+
+ tdiv = lldiv(value, NTFS_TICKS);
+ *secs = tdiv.quot;
+ *nsecs = (uint32_t)(tdiv.rem * 100);
+ }
}
/* Convert Unix sec/nsec to NTFS time */
uint64_t
unix_to_ntfs(int64_t secs, uint32_t nsecs)
{
- uint64_t ntfs = secs + NTFS_EPOC_TIME;
+ uint64_t ntfs;
+
+ if (secs < -(int64_t)NTFS_EPOC_TIME)
+ return 0;
+
+ ntfs = secs + NTFS_EPOC_TIME;
+
+ if (ntfs > UINT64_MAX / NTFS_TICKS)
+ return UINT64_MAX;
+
ntfs *= NTFS_TICKS;
+
+ if (ntfs > UINT64_MAX - nsecs/100)
+ return UINT64_MAX;
+
return ntfs + nsecs/100;
}