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