#include <sys/param.h>
#include <sys/types.h>
#include <sys/tzfile.h>
#include <sys/atomic.h>
#include <sys/debug.h>
#include <sys/time.h>
#include <smbsrv/smb_kproto.h>
time_t tzh_leapcnt = 0;
struct tm
*smb_gmtime_r(time_t *clock, struct tm *result);
time_t
smb_timegm(struct tm *tm);
struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
static const int days_in_month[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
uint64_t
smb_time_unix_to_nt(timestruc_t *unix_time)
{
uint64_t nt_time;
if ((unix_time->tv_sec == 0) && (unix_time->tv_nsec == 0))
return (0);
nt_time = unix_time->tv_sec;
nt_time *= 10000000;
nt_time += unix_time->tv_nsec / 100;
return (nt_time + NT_TIME_BIAS);
}
void
smb_time_nt_to_unix(uint64_t nt_time, timestruc_t *unix_time)
{
uint32_t seconds;
ASSERT(unix_time);
if ((nt_time == 0) || (nt_time == -1)) {
unix_time->tv_sec = 0;
unix_time->tv_nsec = 0;
return;
}
if (nt_time <= NT_TIME_BIAS) {
unix_time->tv_sec = 0;
unix_time->tv_nsec = 100;
return;
}
nt_time -= NT_TIME_BIAS;
seconds = nt_time / 10000000;
unix_time->tv_sec = seconds;
unix_time->tv_nsec = (nt_time % 10000000) * 100;
}
int32_t
smb_time_dos_to_unix(int16_t date, int16_t time)
{
struct tm atm;
if (((date == 0) || (time == 0)) ||
((date == -1) || (time == -1))) {
return (0);
}
atm.tm_year = ((date >> 9) & 0x3F) + 80;
atm.tm_mon = ((date >> 5) & 0x0F) - 1;
atm.tm_mday = ((date >> 0) & 0x1F);
atm.tm_hour = ((time >> 11) & 0x1F);
atm.tm_min = ((time >> 5) & 0x3F);
atm.tm_sec = ((time >> 0) & 0x1F) << 1;
return (smb_timegm(&atm));
}
void
smb_time_unix_to_dos(int32_t ux_time, int16_t *date_p, int16_t *time_p)
{
struct tm atm;
int i;
time_t tmp_time;
if (ux_time == 0) {
*date_p = 0;
*time_p = 0;
return;
}
tmp_time = (time_t)ux_time;
(void) smb_gmtime_r(&tmp_time, &atm);
if (date_p) {
i = 0;
i += atm.tm_year - 80;
i <<= 4;
i += atm.tm_mon + 1;
i <<= 5;
i += atm.tm_mday;
*date_p = (short)i;
}
if (time_p) {
i = 0;
i += atm.tm_hour;
i <<= 6;
i += atm.tm_min;
i <<= 5;
i += atm.tm_sec >> 1;
*time_p = (short)i;
}
}
struct tm *
smb_gmtime_r(time_t *clock, struct tm *result)
{
time_t tsec;
int year;
int month;
int sec_per_month;
if (clock == 0 || result == 0)
return (0);
bzero(result, sizeof (struct tm));
tsec = *clock;
tsec -= tzh_leapcnt;
result->tm_wday = tsec / SECSPERDAY;
result->tm_wday = (result->tm_wday + TM_THURSDAY) % DAYSPERWEEK;
year = EPOCH_YEAR;
while (tsec >= (isleap(year) ? (SECSPERDAY * DAYSPERLYEAR) :
(SECSPERDAY * DAYSPERNYEAR))) {
if (isleap(year))
tsec -= SECSPERDAY * DAYSPERLYEAR;
else
tsec -= SECSPERDAY * DAYSPERNYEAR;
++year;
}
result->tm_year = year - TM_YEAR_BASE;
result->tm_yday = tsec / SECSPERDAY;
for (month = TM_JANUARY; month <= TM_DECEMBER; ++month) {
sec_per_month = days_in_month[month] * SECSPERDAY;
if (month == TM_FEBRUARY && isleap(year))
sec_per_month += SECSPERDAY;
if (tsec < sec_per_month)
break;
tsec -= sec_per_month;
}
result->tm_mon = month;
result->tm_mday = (tsec / SECSPERDAY) + 1;
tsec %= SECSPERDAY;
result->tm_sec = tsec % 60;
tsec /= 60;
result->tm_min = tsec % 60;
tsec /= 60;
result->tm_hour = (int)tsec;
return (result);
}
time_t
smb_timegm(struct tm *tm)
{
time_t tsec;
int dd;
int mm;
int yy;
int year;
if (tm == 0)
return (-1);
year = tm->tm_year + TM_YEAR_BASE;
tsec = tzh_leapcnt;
for (yy = EPOCH_YEAR; yy < year; ++yy) {
if (isleap(yy))
tsec += SECSPERDAY * DAYSPERLYEAR;
else
tsec += SECSPERDAY * DAYSPERNYEAR;
}
for (mm = TM_JANUARY; mm < tm->tm_mon; ++mm) {
dd = days_in_month[mm] * SECSPERDAY;
if (mm == TM_FEBRUARY && isleap(year))
dd += SECSPERDAY;
tsec += dd;
}
tsec += (tm->tm_mday - 1) * SECSPERDAY;
tsec += tm->tm_sec;
tsec += tm->tm_min * SECSPERMIN;
tsec += tm->tm_hour * SECSPERHOUR;
tm->tm_isdst = 0;
(void) smb_gmtime_r(&tsec, tm);
return (tsec);
}