1*7d1ffc32SGordon Ross /*
2*7d1ffc32SGordon Ross  * CDDL HEADER START
3*7d1ffc32SGordon Ross  *
4*7d1ffc32SGordon Ross  * The contents of this file are subject to the terms of the
5*7d1ffc32SGordon Ross  * Common Development and Distribution License (the "License").
6*7d1ffc32SGordon Ross  * You may not use this file except in compliance with the License.
7*7d1ffc32SGordon Ross  *
8*7d1ffc32SGordon Ross  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*7d1ffc32SGordon Ross  * or http://www.opensolaris.org/os/licensing.
10*7d1ffc32SGordon Ross  * See the License for the specific language governing permissions
11*7d1ffc32SGordon Ross  * and limitations under the License.
12*7d1ffc32SGordon Ross  *
13*7d1ffc32SGordon Ross  * When distributing Covered Code, include this CDDL HEADER in each
14*7d1ffc32SGordon Ross  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*7d1ffc32SGordon Ross  * If applicable, add the following below this CDDL HEADER, with the
16*7d1ffc32SGordon Ross  * fields enclosed by brackets "[]" replaced with your own identifying
17*7d1ffc32SGordon Ross  * information: Portions Copyright [yyyy] [name of copyright owner]
18*7d1ffc32SGordon Ross  *
19*7d1ffc32SGordon Ross  * CDDL HEADER END
20*7d1ffc32SGordon Ross  */
21*7d1ffc32SGordon Ross 
22*7d1ffc32SGordon Ross /*
23*7d1ffc32SGordon Ross  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24*7d1ffc32SGordon Ross  * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
25*7d1ffc32SGordon Ross  */
26*7d1ffc32SGordon Ross 
27*7d1ffc32SGordon Ross /*
28*7d1ffc32SGordon Ross  * A few excerpts from smb_kutil.c
29*7d1ffc32SGordon Ross  */
30*7d1ffc32SGordon Ross 
31*7d1ffc32SGordon Ross #include <sys/param.h>
32*7d1ffc32SGordon Ross #include <sys/types.h>
33*7d1ffc32SGordon Ross #include <sys/tzfile.h>
34*7d1ffc32SGordon Ross #include <sys/atomic.h>
35*7d1ffc32SGordon Ross #include <sys/debug.h>
36*7d1ffc32SGordon Ross #include <sys/time.h>
37*7d1ffc32SGordon Ross #include <smbsrv/smb_kproto.h>
38*7d1ffc32SGordon Ross 
39*7d1ffc32SGordon Ross time_t tzh_leapcnt = 0;
40*7d1ffc32SGordon Ross 
41*7d1ffc32SGordon Ross struct tm
42*7d1ffc32SGordon Ross *smb_gmtime_r(time_t *clock, struct tm *result);
43*7d1ffc32SGordon Ross 
44*7d1ffc32SGordon Ross time_t
45*7d1ffc32SGordon Ross smb_timegm(struct tm *tm);
46*7d1ffc32SGordon Ross 
47*7d1ffc32SGordon Ross struct	tm {
48*7d1ffc32SGordon Ross 	int	tm_sec;
49*7d1ffc32SGordon Ross 	int	tm_min;
50*7d1ffc32SGordon Ross 	int	tm_hour;
51*7d1ffc32SGordon Ross 	int	tm_mday;
52*7d1ffc32SGordon Ross 	int	tm_mon;
53*7d1ffc32SGordon Ross 	int	tm_year;
54*7d1ffc32SGordon Ross 	int	tm_wday;
55*7d1ffc32SGordon Ross 	int	tm_yday;
56*7d1ffc32SGordon Ross 	int	tm_isdst;
57*7d1ffc32SGordon Ross };
58*7d1ffc32SGordon Ross 
59*7d1ffc32SGordon Ross static const int days_in_month[] = {
60*7d1ffc32SGordon Ross 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
61*7d1ffc32SGordon Ross };
62*7d1ffc32SGordon Ross 
63*7d1ffc32SGordon Ross uint64_t
smb_time_unix_to_nt(timestruc_t * unix_time)64*7d1ffc32SGordon Ross smb_time_unix_to_nt(timestruc_t *unix_time)
65*7d1ffc32SGordon Ross {
66*7d1ffc32SGordon Ross 	uint64_t nt_time;
67*7d1ffc32SGordon Ross 
68*7d1ffc32SGordon Ross 	if ((unix_time->tv_sec == 0) && (unix_time->tv_nsec == 0))
69*7d1ffc32SGordon Ross 		return (0);
70*7d1ffc32SGordon Ross 
71*7d1ffc32SGordon Ross 	nt_time = unix_time->tv_sec;
72*7d1ffc32SGordon Ross 	nt_time *= 10000000;  /* seconds to 100ns */
73*7d1ffc32SGordon Ross 	nt_time += unix_time->tv_nsec / 100;
74*7d1ffc32SGordon Ross 	return (nt_time + NT_TIME_BIAS);
75*7d1ffc32SGordon Ross }
76*7d1ffc32SGordon Ross 
77*7d1ffc32SGordon Ross void
smb_time_nt_to_unix(uint64_t nt_time,timestruc_t * unix_time)78*7d1ffc32SGordon Ross smb_time_nt_to_unix(uint64_t nt_time, timestruc_t *unix_time)
79*7d1ffc32SGordon Ross {
80*7d1ffc32SGordon Ross 	uint32_t seconds;
81*7d1ffc32SGordon Ross 
82*7d1ffc32SGordon Ross 	ASSERT(unix_time);
83*7d1ffc32SGordon Ross 
84*7d1ffc32SGordon Ross 	if ((nt_time == 0) || (nt_time == -1)) {
85*7d1ffc32SGordon Ross 		unix_time->tv_sec = 0;
86*7d1ffc32SGordon Ross 		unix_time->tv_nsec = 0;
87*7d1ffc32SGordon Ross 		return;
88*7d1ffc32SGordon Ross 	}
89*7d1ffc32SGordon Ross 
90*7d1ffc32SGordon Ross 	/*
91*7d1ffc32SGordon Ross 	 * Can't represent times less than or equal NT_TIME_BIAS,
92*7d1ffc32SGordon Ross 	 * so convert them to the oldest date we can store.
93*7d1ffc32SGordon Ross 	 * Note that time zero is "special" being converted
94*7d1ffc32SGordon Ross 	 * both directions as 0:0 (unix-to-nt, nt-to-unix).
95*7d1ffc32SGordon Ross 	 */
96*7d1ffc32SGordon Ross 	if (nt_time <= NT_TIME_BIAS) {
97*7d1ffc32SGordon Ross 		unix_time->tv_sec = 0;
98*7d1ffc32SGordon Ross 		unix_time->tv_nsec = 100;
99*7d1ffc32SGordon Ross 		return;
100*7d1ffc32SGordon Ross 	}
101*7d1ffc32SGordon Ross 
102*7d1ffc32SGordon Ross 	nt_time -= NT_TIME_BIAS;
103*7d1ffc32SGordon Ross 	seconds = nt_time / 10000000;
104*7d1ffc32SGordon Ross 	unix_time->tv_sec = seconds;
105*7d1ffc32SGordon Ross 	unix_time->tv_nsec = (nt_time  % 10000000) * 100;
106*7d1ffc32SGordon Ross }
107*7d1ffc32SGordon Ross 
108*7d1ffc32SGordon Ross 
109*7d1ffc32SGordon Ross /*
110*7d1ffc32SGordon Ross  * smb_time_dos_to_unix
111*7d1ffc32SGordon Ross  *
112*7d1ffc32SGordon Ross  * Convert SMB_DATE & SMB_TIME values to a unix timestamp.
113*7d1ffc32SGordon Ross  *
114*7d1ffc32SGordon Ross  * A date/time field of 0 means that that server file system
115*7d1ffc32SGordon Ross  * assigned value need not be changed. The behaviour when the
116*7d1ffc32SGordon Ross  * date/time field is set to -1 is not documented but is
117*7d1ffc32SGordon Ross  * generally treated like 0.
118*7d1ffc32SGordon Ross  * If date or time is 0 or -1 the unix time is returned as 0
119*7d1ffc32SGordon Ross  * so that the caller can identify and handle this special case.
120*7d1ffc32SGordon Ross  */
121*7d1ffc32SGordon Ross int32_t
smb_time_dos_to_unix(int16_t date,int16_t time)122*7d1ffc32SGordon Ross smb_time_dos_to_unix(int16_t date, int16_t time)
123*7d1ffc32SGordon Ross {
124*7d1ffc32SGordon Ross 	struct tm	atm;
125*7d1ffc32SGordon Ross 
126*7d1ffc32SGordon Ross 	if (((date == 0) || (time == 0)) ||
127*7d1ffc32SGordon Ross 	    ((date == -1) || (time == -1))) {
128*7d1ffc32SGordon Ross 		return (0);
129*7d1ffc32SGordon Ross 	}
130*7d1ffc32SGordon Ross 
131*7d1ffc32SGordon Ross 	atm.tm_year = ((date >>  9) & 0x3F) + 80;
132*7d1ffc32SGordon Ross 	atm.tm_mon  = ((date >>  5) & 0x0F) - 1;
133*7d1ffc32SGordon Ross 	atm.tm_mday = ((date >>  0) & 0x1F);
134*7d1ffc32SGordon Ross 	atm.tm_hour = ((time >> 11) & 0x1F);
135*7d1ffc32SGordon Ross 	atm.tm_min  = ((time >>  5) & 0x3F);
136*7d1ffc32SGordon Ross 	atm.tm_sec  = ((time >>  0) & 0x1F) << 1;
137*7d1ffc32SGordon Ross 
138*7d1ffc32SGordon Ross 	return (smb_timegm(&atm));
139*7d1ffc32SGordon Ross }
140*7d1ffc32SGordon Ross 
141*7d1ffc32SGordon Ross void
smb_time_unix_to_dos(int32_t ux_time,int16_t * date_p,int16_t * time_p)142*7d1ffc32SGordon Ross smb_time_unix_to_dos(int32_t ux_time, int16_t *date_p, int16_t *time_p)
143*7d1ffc32SGordon Ross {
144*7d1ffc32SGordon Ross 	struct tm	atm;
145*7d1ffc32SGordon Ross 	int		i;
146*7d1ffc32SGordon Ross 	time_t		tmp_time;
147*7d1ffc32SGordon Ross 
148*7d1ffc32SGordon Ross 	if (ux_time == 0) {
149*7d1ffc32SGordon Ross 		*date_p = 0;
150*7d1ffc32SGordon Ross 		*time_p = 0;
151*7d1ffc32SGordon Ross 		return;
152*7d1ffc32SGordon Ross 	}
153*7d1ffc32SGordon Ross 
154*7d1ffc32SGordon Ross 	tmp_time = (time_t)ux_time;
155*7d1ffc32SGordon Ross 	(void) smb_gmtime_r(&tmp_time, &atm);
156*7d1ffc32SGordon Ross 
157*7d1ffc32SGordon Ross 	if (date_p) {
158*7d1ffc32SGordon Ross 		i = 0;
159*7d1ffc32SGordon Ross 		i += atm.tm_year - 80;
160*7d1ffc32SGordon Ross 		i <<= 4;
161*7d1ffc32SGordon Ross 		i += atm.tm_mon + 1;
162*7d1ffc32SGordon Ross 		i <<= 5;
163*7d1ffc32SGordon Ross 		i += atm.tm_mday;
164*7d1ffc32SGordon Ross 
165*7d1ffc32SGordon Ross 		*date_p = (short)i;
166*7d1ffc32SGordon Ross 	}
167*7d1ffc32SGordon Ross 	if (time_p) {
168*7d1ffc32SGordon Ross 		i = 0;
169*7d1ffc32SGordon Ross 		i += atm.tm_hour;
170*7d1ffc32SGordon Ross 		i <<= 6;
171*7d1ffc32SGordon Ross 		i += atm.tm_min;
172*7d1ffc32SGordon Ross 		i <<= 5;
173*7d1ffc32SGordon Ross 		i += atm.tm_sec >> 1;
174*7d1ffc32SGordon Ross 
175*7d1ffc32SGordon Ross 		*time_p = (short)i;
176*7d1ffc32SGordon Ross 	}
177*7d1ffc32SGordon Ross }
178*7d1ffc32SGordon Ross 
179*7d1ffc32SGordon Ross /*
180*7d1ffc32SGordon Ross  * smb_gmtime_r
181*7d1ffc32SGordon Ross  *
182*7d1ffc32SGordon Ross  * Thread-safe version of smb_gmtime. Returns a null pointer if either
183*7d1ffc32SGordon Ross  * input parameter is a null pointer. Otherwise returns a pointer
184*7d1ffc32SGordon Ross  * to result.
185*7d1ffc32SGordon Ross  *
186*7d1ffc32SGordon Ross  * Day of the week calculation: the Epoch was a thursday.
187*7d1ffc32SGordon Ross  *
188*7d1ffc32SGordon Ross  * There are no timezone corrections so tm_isdst and tm_gmtoff are
189*7d1ffc32SGordon Ross  * always zero, and the zone is always WET.
190*7d1ffc32SGordon Ross  */
191*7d1ffc32SGordon Ross struct tm *
smb_gmtime_r(time_t * clock,struct tm * result)192*7d1ffc32SGordon Ross smb_gmtime_r(time_t *clock, struct tm *result)
193*7d1ffc32SGordon Ross {
194*7d1ffc32SGordon Ross 	time_t tsec;
195*7d1ffc32SGordon Ross 	int year;
196*7d1ffc32SGordon Ross 	int month;
197*7d1ffc32SGordon Ross 	int sec_per_month;
198*7d1ffc32SGordon Ross 
199*7d1ffc32SGordon Ross 	if (clock == 0 || result == 0)
200*7d1ffc32SGordon Ross 		return (0);
201*7d1ffc32SGordon Ross 
202*7d1ffc32SGordon Ross 	bzero(result, sizeof (struct tm));
203*7d1ffc32SGordon Ross 	tsec = *clock;
204*7d1ffc32SGordon Ross 	tsec -= tzh_leapcnt;
205*7d1ffc32SGordon Ross 
206*7d1ffc32SGordon Ross 	result->tm_wday = tsec / SECSPERDAY;
207*7d1ffc32SGordon Ross 	result->tm_wday = (result->tm_wday + TM_THURSDAY) % DAYSPERWEEK;
208*7d1ffc32SGordon Ross 
209*7d1ffc32SGordon Ross 	year = EPOCH_YEAR;
210*7d1ffc32SGordon Ross 	while (tsec >= (isleap(year) ? (SECSPERDAY * DAYSPERLYEAR) :
211*7d1ffc32SGordon Ross 	    (SECSPERDAY * DAYSPERNYEAR))) {
212*7d1ffc32SGordon Ross 		if (isleap(year))
213*7d1ffc32SGordon Ross 			tsec -= SECSPERDAY * DAYSPERLYEAR;
214*7d1ffc32SGordon Ross 		else
215*7d1ffc32SGordon Ross 			tsec -= SECSPERDAY * DAYSPERNYEAR;
216*7d1ffc32SGordon Ross 
217*7d1ffc32SGordon Ross 		++year;
218*7d1ffc32SGordon Ross 	}
219*7d1ffc32SGordon Ross 
220*7d1ffc32SGordon Ross 	result->tm_year = year - TM_YEAR_BASE;
221*7d1ffc32SGordon Ross 	result->tm_yday = tsec / SECSPERDAY;
222*7d1ffc32SGordon Ross 
223*7d1ffc32SGordon Ross 	for (month = TM_JANUARY; month <= TM_DECEMBER; ++month) {
224*7d1ffc32SGordon Ross 		sec_per_month = days_in_month[month] * SECSPERDAY;
225*7d1ffc32SGordon Ross 
226*7d1ffc32SGordon Ross 		if (month == TM_FEBRUARY && isleap(year))
227*7d1ffc32SGordon Ross 			sec_per_month += SECSPERDAY;
228*7d1ffc32SGordon Ross 
229*7d1ffc32SGordon Ross 		if (tsec < sec_per_month)
230*7d1ffc32SGordon Ross 			break;
231*7d1ffc32SGordon Ross 
232*7d1ffc32SGordon Ross 		tsec -= sec_per_month;
233*7d1ffc32SGordon Ross 	}
234*7d1ffc32SGordon Ross 
235*7d1ffc32SGordon Ross 	result->tm_mon = month;
236*7d1ffc32SGordon Ross 	result->tm_mday = (tsec / SECSPERDAY) + 1;
237*7d1ffc32SGordon Ross 	tsec %= SECSPERDAY;
238*7d1ffc32SGordon Ross 	result->tm_sec = tsec % 60;
239*7d1ffc32SGordon Ross 	tsec /= 60;
240*7d1ffc32SGordon Ross 	result->tm_min = tsec % 60;
241*7d1ffc32SGordon Ross 	tsec /= 60;
242*7d1ffc32SGordon Ross 	result->tm_hour = (int)tsec;
243*7d1ffc32SGordon Ross 
244*7d1ffc32SGordon Ross 	return (result);
245*7d1ffc32SGordon Ross }
246*7d1ffc32SGordon Ross 
247*7d1ffc32SGordon Ross 
248*7d1ffc32SGordon Ross /*
249*7d1ffc32SGordon Ross  * smb_timegm
250*7d1ffc32SGordon Ross  *
251*7d1ffc32SGordon Ross  * Converts the broken-down time in tm to a time value, i.e. the number
252*7d1ffc32SGordon Ross  * of seconds since the Epoch (00:00:00 UTC, January 1, 1970). This is
253*7d1ffc32SGordon Ross  * not a POSIX or ANSI function. Per the man page, the input values of
254*7d1ffc32SGordon Ross  * tm_wday and tm_yday are ignored and, as the input data is assumed to
255*7d1ffc32SGordon Ross  * represent GMT, we force tm_isdst and tm_gmtoff to 0.
256*7d1ffc32SGordon Ross  *
257*7d1ffc32SGordon Ross  * Before returning the clock time, we use smb_gmtime_r to set up tm_wday
258*7d1ffc32SGordon Ross  * and tm_yday, and bring the other fields within normal range. I don't
259*7d1ffc32SGordon Ross  * think this is really how it should be done but it's convenient for
260*7d1ffc32SGordon Ross  * now.
261*7d1ffc32SGordon Ross  */
262*7d1ffc32SGordon Ross time_t
smb_timegm(struct tm * tm)263*7d1ffc32SGordon Ross smb_timegm(struct tm *tm)
264*7d1ffc32SGordon Ross {
265*7d1ffc32SGordon Ross 	time_t tsec;
266*7d1ffc32SGordon Ross 	int dd;
267*7d1ffc32SGordon Ross 	int mm;
268*7d1ffc32SGordon Ross 	int yy;
269*7d1ffc32SGordon Ross 	int year;
270*7d1ffc32SGordon Ross 
271*7d1ffc32SGordon Ross 	if (tm == 0)
272*7d1ffc32SGordon Ross 		return (-1);
273*7d1ffc32SGordon Ross 
274*7d1ffc32SGordon Ross 	year = tm->tm_year + TM_YEAR_BASE;
275*7d1ffc32SGordon Ross 	tsec = tzh_leapcnt;
276*7d1ffc32SGordon Ross 
277*7d1ffc32SGordon Ross 	for (yy = EPOCH_YEAR; yy < year; ++yy) {
278*7d1ffc32SGordon Ross 		if (isleap(yy))
279*7d1ffc32SGordon Ross 			tsec += SECSPERDAY * DAYSPERLYEAR;
280*7d1ffc32SGordon Ross 		else
281*7d1ffc32SGordon Ross 			tsec += SECSPERDAY * DAYSPERNYEAR;
282*7d1ffc32SGordon Ross 	}
283*7d1ffc32SGordon Ross 
284*7d1ffc32SGordon Ross 	for (mm = TM_JANUARY; mm < tm->tm_mon; ++mm) {
285*7d1ffc32SGordon Ross 		dd = days_in_month[mm] * SECSPERDAY;
286*7d1ffc32SGordon Ross 
287*7d1ffc32SGordon Ross 		if (mm == TM_FEBRUARY && isleap(year))
288*7d1ffc32SGordon Ross 			dd += SECSPERDAY;
289*7d1ffc32SGordon Ross 
290*7d1ffc32SGordon Ross 		tsec += dd;
291*7d1ffc32SGordon Ross 	}
292*7d1ffc32SGordon Ross 
293*7d1ffc32SGordon Ross 	tsec += (tm->tm_mday - 1) * SECSPERDAY;
294*7d1ffc32SGordon Ross 	tsec += tm->tm_sec;
295*7d1ffc32SGordon Ross 	tsec += tm->tm_min * SECSPERMIN;
296*7d1ffc32SGordon Ross 	tsec += tm->tm_hour * SECSPERHOUR;
297*7d1ffc32SGordon Ross 
298*7d1ffc32SGordon Ross 	tm->tm_isdst = 0;
299*7d1ffc32SGordon Ross 	(void) smb_gmtime_r(&tsec, tm);
300*7d1ffc32SGordon Ross 	return (tsec);
301*7d1ffc32SGordon Ross }
302