--- /dev/null	Mon Aug  5 17:15:32 2019
+++ new/usr/src/cmd/smbsrv/test-msgbuf/test_misc.c	Mon Aug  5 17:15:32 2019
@@ -0,0 +1,301 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * A few excerpts from smb_kutil.c
+ */
+
+#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;  /* seconds to 100ns */
+	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;
+	}
+
+	/*
+	 * Can't represent times less than or equal NT_TIME_BIAS,
+	 * so convert them to the oldest date we can store.
+	 * Note that time zero is "special" being converted
+	 * both directions as 0:0 (unix-to-nt, nt-to-unix).
+	 */
+	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;
+}
+
+
+/*
+ * smb_time_dos_to_unix
+ *
+ * Convert SMB_DATE & SMB_TIME values to a unix timestamp.
+ *
+ * A date/time field of 0 means that that server file system
+ * assigned value need not be changed. The behaviour when the
+ * date/time field is set to -1 is not documented but is
+ * generally treated like 0.
+ * If date or time is 0 or -1 the unix time is returned as 0
+ * so that the caller can identify and handle this special case.
+ */
+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;
+	}
+}
+
+/*
+ * smb_gmtime_r
+ *
+ * Thread-safe version of smb_gmtime. Returns a null pointer if either
+ * input parameter is a null pointer. Otherwise returns a pointer
+ * to result.
+ *
+ * Day of the week calculation: the Epoch was a thursday.
+ *
+ * There are no timezone corrections so tm_isdst and tm_gmtoff are
+ * always zero, and the zone is always WET.
+ */
+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);
+}
+
+
+/*
+ * smb_timegm
+ *
+ * Converts the broken-down time in tm to a time value, i.e. the number
+ * of seconds since the Epoch (00:00:00 UTC, January 1, 1970). This is
+ * not a POSIX or ANSI function. Per the man page, the input values of
+ * tm_wday and tm_yday are ignored and, as the input data is assumed to
+ * represent GMT, we force tm_isdst and tm_gmtoff to 0.
+ *
+ * Before returning the clock time, we use smb_gmtime_r to set up tm_wday
+ * and tm_yday, and bring the other fields within normal range. I don't
+ * think this is really how it should be done but it's convenient for
+ * now.
+ */
+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);
+}
