xref: /freebsd/contrib/libarchive/libarchive/archive_time.c (revision 2e113ef82465598b8c26e0ca415fbe90677fbd47)
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 
26 #include "archive_platform.h"
27 #include "archive_private.h"
28 #include "archive_time_private.h"
29 #include <limits.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #define NTFS_EPOC_TIME ARCHIVE_LITERAL_ULL(11644473600)
34 #define NTFS_TICKS ARCHIVE_LITERAL_ULL(10000000)
35 #define NTFS_EPOC_TICKS (NTFS_EPOC_TIME * NTFS_TICKS)
36 #define DOS_MIN_TIME 0x00210000U
37 #define DOS_MAX_TIME 0xff9fbf7dU
38 
39 #if defined(_WIN32) && !defined(__CYGWIN__)
40 #include <winnt.h>
41 /* Windows FILETIME to NTFS time. */
42 uint64_t
FILETIME_to_ntfs(const FILETIME * filetime)43 FILETIME_to_ntfs(const FILETIME* filetime)
44 {
45 	ULARGE_INTEGER utc;
46 	utc.HighPart = filetime->dwHighDateTime;
47 	utc.LowPart  = filetime->dwLowDateTime;
48 	return utc.QuadPart;
49 }
50 #endif
51 
52 /* Convert an MSDOS-style date/time into Unix-style time. */
53 int64_t
dos_to_unix(uint32_t dos_time)54 dos_to_unix(uint32_t dos_time)
55 {
56 	uint16_t msTime, msDate;
57 	struct tm ts;
58 	time_t t;
59 
60 	msTime = (0xFFFF & dos_time);
61 	msDate = (dos_time >> 16);
62 
63 	memset(&ts, 0, sizeof(ts));
64 	ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */
65 	ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */
66 	ts.tm_mday = msDate & 0x1f; /* Day of month. */
67 	ts.tm_hour = (msTime >> 11) & 0x1f;
68 	ts.tm_min = (msTime >> 5) & 0x3f;
69 	ts.tm_sec = (msTime << 1) & 0x3e;
70 	ts.tm_isdst = -1;
71 	t = mktime(&ts);
72 	return (int64_t)(t == (time_t)-1 ? INT32_MAX : t);
73 }
74 
75 /* Convert into MSDOS-style date/time. */
76 uint32_t
unix_to_dos(int64_t unix_time)77 unix_to_dos(int64_t unix_time)
78 {
79 	struct tm *t;
80 	uint32_t dt;
81 	time_t ut = unix_time;
82 #if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
83 	struct tm tmbuf;
84 #endif
85 
86 	if (sizeof(time_t) < sizeof(int64_t) && (int64_t)ut != unix_time) {
87 		ut = (time_t)(unix_time > 0 ? INT32_MAX : INT32_MIN);
88 	}
89 
90 #if defined(HAVE_LOCALTIME_S)
91 	t = localtime_s(&tmbuf, &ut) ? NULL : &tmbuf;
92 #elif defined(HAVE_LOCALTIME_R)
93 	t = localtime_r(&ut, &tmbuf);
94 #else
95 	t = localtime(&ut);
96 #endif
97 	dt = 0;
98 	if (t != NULL && t->tm_year >= INT_MIN + 80) {
99 		const int year = t->tm_year - 80;
100 
101 		if (year & ~0x7f) {
102 			dt = year > 0 ? DOS_MAX_TIME : DOS_MIN_TIME;
103 		}
104 		else {
105 			dt += (year & 0x7f) << 9;
106 			dt += ((t->tm_mon + 1) & 0x0f) << 5;
107 			dt += (t->tm_mday & 0x1f);
108 			dt <<= 16;
109 			dt += (t->tm_hour & 0x1f) << 11;
110 			dt += (t->tm_min & 0x3f) << 5;
111 			/* Only counting every 2 seconds. */
112 			dt += (t->tm_sec & 0x3e) >> 1;
113 		}
114 	}
115 	if (dt > DOS_MAX_TIME) {
116 		dt = DOS_MAX_TIME;
117 	}
118 	else if (dt < DOS_MIN_TIME) {
119 		dt = DOS_MIN_TIME;
120 	}
121 	return dt;
122 }
123 
124 /* Convert NTFS time to Unix sec/nsec */
125 void
ntfs_to_unix(uint64_t ntfs,int64_t * secs,uint32_t * nsecs)126 ntfs_to_unix(uint64_t ntfs, int64_t* secs, uint32_t* nsecs)
127 {
128 	if (ntfs > INT64_MAX) {
129 		ntfs -= NTFS_EPOC_TICKS;
130 		*secs = ntfs / NTFS_TICKS;
131 		*nsecs = 100 * (ntfs % NTFS_TICKS);
132 	}
133 	else {
134 		lldiv_t tdiv;
135 		int64_t value = (int64_t)ntfs - (int64_t)NTFS_EPOC_TICKS;
136 
137 		tdiv = lldiv(value, NTFS_TICKS);
138 		*secs = tdiv.quot;
139 		*nsecs = (uint32_t)(tdiv.rem * 100);
140 	}
141 }
142 
143 /* Convert Unix sec/nsec to NTFS time */
144 uint64_t
unix_to_ntfs(int64_t secs,uint32_t nsecs)145 unix_to_ntfs(int64_t secs, uint32_t nsecs)
146 {
147 	uint64_t ntfs;
148 
149 	if (secs < -(int64_t)NTFS_EPOC_TIME)
150 		return 0;
151 
152 	ntfs = secs + NTFS_EPOC_TIME;
153 
154 	if (ntfs > UINT64_MAX / NTFS_TICKS)
155 		return UINT64_MAX;
156 
157 	ntfs *= NTFS_TICKS;
158 
159 	if (ntfs > UINT64_MAX - nsecs/100)
160 		return UINT64_MAX;
161 
162 	return ntfs + nsecs/100;
163 }
164