changeset 21414:1ff4dd5d126b

7587 SMB should support enhanced Unicode Reviewed by: Matt Barden <matt.barden@nexenta.com> Reviewed by: Evan Layton <evan.layton@nexenta.com> Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com> Approved by: Richard Lowe <richlowe@richlowe.net>
author Gordon Ross <gwr@nexenta.com>
date Tue, 06 Nov 2018 01:27:53 -0500
parents 8646c591b8eb
children 4526915ff3e9
files exception_lists/check_rtime exception_lists/packaging usr/src/cmd/smbsrv/Makefile usr/src/cmd/smbsrv/test-msgbuf/.dbxrc usr/src/cmd/smbsrv/test-msgbuf/Makefile usr/src/cmd/smbsrv/test-msgbuf/Run.sh usr/src/cmd/smbsrv/test-msgbuf/test_conv.c usr/src/cmd/smbsrv/test-msgbuf/test_defs.h usr/src/cmd/smbsrv/test-msgbuf/test_main.c usr/src/cmd/smbsrv/test-msgbuf/test_mbmarshal.c usr/src/cmd/smbsrv/test-msgbuf/test_misc.c usr/src/cmd/smbsrv/test-msgbuf/test_msgbuf.c usr/src/common/smbsrv/smb_match.c usr/src/common/smbsrv/smb_msgbuf.c usr/src/common/smbsrv/smb_string.c usr/src/common/smbsrv/smb_utf8.c usr/src/lib/smbsrv/libsmb/common/mapfile-vers usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c usr/src/uts/common/fs/smbsrv/smb_vops.c usr/src/uts/common/smbsrv/string.h
diffstat 20 files changed, 2969 insertions(+), 439 deletions(-) [+]
line wrap: on
line diff
--- a/exception_lists/check_rtime	Fri Oct 02 15:45:00 2015 -0400
+++ b/exception_lists/check_rtime	Tue Nov 06 01:27:53 2018 -0500
@@ -228,6 +228,7 @@
 FORBIDDEN_DEP usr/lib/MACH(smbfs)/libfksmbfs.so.1
 FORBIDDEN_DEP usr/lib/MACH(smbsrv)/libfksmbsrv.so.1
 FORBIDDEN_DEP usr/lib/smbsrv/fksmbd
+FORBIDDEN_DEP usr/lib/smbsrv/test-msgbuf
 FORBIDDEN_DEP usr/sbin/amd64/zdb
 FORBIDDEN_DEP usr/sbin/i86/zdb
 FORBIDDEN_DEP usr/sbin/sparcv7/zdb
--- a/exception_lists/packaging	Fri Oct 02 15:45:00 2015 -0400
+++ b/exception_lists/packaging	Tue Nov 06 01:27:53 2018 -0500
@@ -603,6 +603,7 @@
 usr/lib/smbsrv/libmlsvc.so
 usr/lib/smbsrv/libsmb.so
 usr/lib/smbsrv/libsmbns.so
+usr/lib/smbsrv/test-msgbuf
 #
 #
 # Private/Internal 64-bit libraries of smbsrv. Do not ship.
--- a/usr/src/cmd/smbsrv/Makefile	Fri Oct 02 15:45:00 2015 -0400
+++ b/usr/src/cmd/smbsrv/Makefile	Tue Nov 06 01:27:53 2018 -0500
@@ -25,7 +25,8 @@
 # Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
 #
 
-SUBDIRS = smbadm smbd smbstat dtrace fksmbd bind-helper
+SUBDIRS = smbadm smbd smbstat dtrace fksmbd bind-helper \
+	test-msgbuf
 MSGSUBDIRS = smbadm smbstat
 
 include ../Makefile.cmd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/smbsrv/test-msgbuf/.dbxrc	Tue Nov 06 01:27:53 2018 -0500
@@ -0,0 +1,22 @@
+
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
+#
+
+set -o emacs
+
+setenv ROOT ${CODEMGR_WS}/proto/root_i386
+setenv LD_LIBRARY_PATH ${ROOT}/usr/lib/smbsrv:${ROOT}/usr/lib:${ROOT}/lib
+
+echo debug ${ROOT}/usr/lib/smbsrv/test-msgbuf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/smbsrv/test-msgbuf/Makefile	Tue Nov 06 01:27:53 2018 -0500
@@ -0,0 +1,124 @@
+#
+# 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.
+#
+
+
+PROG=	test-msgbuf
+
+OBJS_LOCAL=	test_main.o test_misc.o test_conv.o \
+		test_mbmarshal.o test_msgbuf.o
+OBJS_SMBSRV=	smb_mbuf_marshaling.o smb_mbuf_util.o smb_alloc.o
+OBJS_SMBCMN=	smb_msgbuf.o smb_string.o smb_utf8.o
+
+OBJS=	${OBJS_LOCAL} ${OBJS_SMBSRV}  ${OBJS_SMBCMN}
+
+SMBSRV_SRCDIR=../../../uts/common/fs/smbsrv
+SMBSRV_CMNDIR=../../../common/smbsrv
+SRCS=	${OBJS_LOCAL:.o=.c} \
+	${OBJS_SMBSRV:%.o=${SMBSRV_SRCDIR}/%.c} \
+	${OBJS_SMBCMN:%.o=${SMBSRV_CMNDIR}/%.c}
+
+include ../../Makefile.cmd
+include ../../Makefile.ctf
+
+# Note: need our sys includes _before_ ENVCPPFLAGS, proto etc.
+CPPFLAGS.first += -I.
+CPPFLAGS.first += -I../../../lib/libfakekernel/common
+CPPFLAGS.first += -I../../../lib/smbsrv/libfksmbsrv/common
+
+INCS +=	-I../../../uts/common
+INCS +=	-I../../../uts/common/smbsrv
+INCS +=	-I../../../common/smbsrv
+
+CSTD=		$(CSTD_GNU99)
+C99LMODE=	-Xc99=%all
+
+CFLAGS += $(CCVERBOSE)
+CFLAGS64 += $(CCVERBOSE)
+
+CERRWARN	+= -_gcc=-Wno-parentheses
+
+CPPFLAGS += -D_REENTRANT
+CPPFLAGS += -DTESTJIG
+CPPFLAGS += -Dsyslog=smb_syslog
+CPPFLAGS += -D_LARGEFILE64_SOURCE=1
+# Always debug here
+CPPFLAGS += -DDEBUG
+CPPFLAGS += $(INCS)
+
+LDFLAGS += $(ZNOLAZYLOAD)
+LDFLAGS += '-R$$ORIGIN/..'
+LDLIBS += -lfakekernel -lcmdutils
+
+LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2
+LINTFLAGS += -xerroff=E_NAME_USED_NOT_DEF2
+LINTFLAGS += -xerroff=E_INCONS_ARG_DECL2
+LINTFLAGS += -xerroff=E_INCONS_VAL_TYPE_DECL2
+
+ROOTSMBDDIR = $(ROOTLIB)/smbsrv
+ROOTSMBDFILE = $(PROG:%=$(ROOTSMBDDIR)/%)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+	$(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+clean:
+	-$(RM) $(OBJS)
+
+lint:	# lint_SRCS
+
+include ../../Makefile.targ
+
+install: all $(ROOTSMBDFILE)
+
+test_main.o : test_main.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -D_KMEMUSER -c test_main.c
+	$(POST_PROCESS_O)
+
+test_misc.o : test_misc.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \
+	  -I../../../uts/common/smbsrv \
+	  -I../../../common/smbsrv -c test_misc.c
+	$(POST_PROCESS_O)
+
+# OBJS_SMBSRV
+%.o: ../../../uts/common/fs/smbsrv/%.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \
+	  -I../../../uts/common/smbsrv \
+	  -I../../../common/smbsrv -c $<
+	$(POST_PROCESS_O)
+
+# OBJS_SMBCMN
+%.o: ../../../common/smbsrv/%.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \
+	  -I../../../uts/common/smbsrv \
+	  -I../../../common/smbsrv -c $<
+	$(POST_PROCESS_O)
+
+$(ROOTSMBDDIR)/%: %
+	$(INS.file)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/smbsrv/test-msgbuf/Run.sh	Tue Nov 06 01:27:53 2018 -0500
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
+#
+
+# Helper program to run test-msgbuf (unit test program)
+# using binaries from the proto area.
+
+[ -n "$CODEMGR_WS" ] || {
+  echo "Need a buildenv to set CODEMGR_WS=..."
+  exit 1;
+}
+
+ROOT=${CODEMGR_WS}/proto/root_i386
+LD_LIBRARY_PATH=$ROOT/usr/lib/smbsrv:$ROOT/usr/lib:$ROOT/lib
+export LD_LIBRARY_PATH
+export UMEM_DEBUG=default
+
+# run with the passed options
+exec $ROOT/usr/lib/smbsrv/test-msgbuf "$@"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/smbsrv/test-msgbuf/test_conv.c	Tue Nov 06 01:27:53 2018 -0500
@@ -0,0 +1,342 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
+ */
+
+/*
+ * Test conversion of strings UTF-8 to/from UTF-16 etc.
+ *
+ * This tests both 16-bit unicode symbols (UCS-2) and so called
+ * "enhanced" unicode symbols such as the "poop emoji" that are
+ * above 65535 and encode to four bytes as UTF-8.
+ */
+
+#include <sys/types.h>
+#include <sys/debug.h>
+#include <sys/u8_textprep.h>
+#include <smbsrv/string.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "test_defs.h"
+
+#define	U_FW_A	0xff21		// full-width A (A)
+static const char fwA[4] = "\xef\xbc\xa1";
+
+#define	U_POOP	0x1f4a9		// poop emoji (💩)
+static const char poop[5] = "\xf0\x9f\x92\xa9";
+
+static char mbsa[] = "A\xef\xbc\xa1.";		// A fwA . (5)
+static char mbsp[] = "P\xf0\x9f\x92\xa9.";	// P poop . (6)
+static smb_wchar_t wcsa[] = { 'A', U_FW_A, '.', 0 };	// (3)
+static smb_wchar_t wcsp[] = { 'P', 0xd83d, 0xdca9, '.', 0 }; // (4)
+
+
+static void
+conv_wctomb()
+{
+	char mbs[8];
+	int len;
+
+	len = smb_wctomb(mbs, U_FW_A);
+	if (len != 3) {
+		printf("Fail: conv_wctomb fwA ret=%d\n", len);
+		return;
+	}
+	mbs[len] = '\0';
+	if (strcmp(mbs, fwA)) {
+		printf("Fail: conv_wctomb fwA cmp:\n");
+		hexdump((uchar_t *)mbs, len+1);
+		return;
+	}
+
+	len = smb_wctomb(mbs, U_POOP);
+	if (len != 4) {
+		printf("Fail: conv_wctomb poop ret=%d\n", len);
+		return;
+	}
+	mbs[len] = '\0';
+	if (strcmp(mbs, poop)) {
+		printf("Fail: conv_wctomb poop cmp:\n");
+		hexdump((uchar_t *)mbs, len+1);
+		return;
+	}
+
+	/* null wc to mbs should return 1 and put a null */
+	len = smb_wctomb(mbs, 0);
+	if (len != 1) {
+		printf("Fail: conv_wctomb null ret=%d\n", len);
+		return;
+	}
+	if (mbs[0] != '\0') {
+		printf("Fail: conv_wctomb null cmp:\n");
+		hexdump((uchar_t *)mbs, len+1);
+		return;
+	}
+
+	printf("Pass: conv_wctomb\n");
+}
+
+static void
+conv_mbtowc()
+{
+	uint32_t wch = 0;
+	int len;
+
+	/*
+	 * The (void *) cast here is to let this build both
+	 * before and after an interface change in smb_mbtowc
+	 * (uint16_t vs uint32_t)
+	 */
+	len = smb_mbtowc((void *)&wch, fwA, 4);
+	if (len != 3) {
+		printf("Fail: conv_mbtowc fwA ret=%d\n", len);
+		return;
+	}
+	if (wch != U_FW_A) {
+		printf("Fail: conv_mbtowc fwA cmp: 0x%x\n", wch);
+		return;
+	}
+
+	len = smb_mbtowc((void *)&wch, poop, 4); // poop emoji
+	if (len != 4) {
+		printf("Fail: conv_mbtowc poop ret=%d\n", len);
+		return;
+	}
+	if (wch != U_POOP) {
+		printf("Fail: conv_mbtowc poop cmp: 0x%x\n", wch);
+		return;
+	}
+
+	/* null mbs to wc should return 0 (and set wch=0) */
+	len = smb_mbtowc((void *)&wch, "", 4);
+	if (len != 0) {
+		printf("Fail: conv_mbtowc null ret=%d\n", len);
+		return;
+	}
+	if (wch != 0) {
+		printf("Fail: conv_mbtowc null cmp: 0x%x\n", wch);
+		return;
+	}
+
+	printf("Pass: conv_mbtowc\n");
+}
+
+static void
+conv_wcstombs()
+{
+	char tmbs[16];
+	int len;
+
+	len = smb_wcstombs(tmbs, wcsa, sizeof (tmbs));
+	if (len != 5) {
+		printf("Fail: conv_wcstombs A ret=%d\n", len);
+		return;
+	}
+	if (strcmp(tmbs, mbsa)) {
+		printf("Fail: conv_wcstombs A cmp:\n");
+		hexdump((uchar_t *)tmbs, len+2);
+		return;
+	}
+
+	len = smb_wcstombs(tmbs, wcsp, sizeof (tmbs));
+	if (len != 6) {
+		printf("Fail: conv_wcstombs f ret=%d\n", len);
+		return;
+	}
+	if (strcmp(tmbs, mbsp)) {
+		printf("Fail: conv_wcstombs f cmp:\n");
+		hexdump((uchar_t *)tmbs, len+2);
+		return;
+	}
+
+	printf("Pass: conv_wcstombs\n");
+}
+
+static void
+conv_mbstowcs()
+{
+	smb_wchar_t twcs[8];
+	uint32_t wch = 0;
+	int len;
+
+	len = smb_mbstowcs(twcs, mbsa, sizeof (twcs));
+	if (len != 3) {
+		printf("Fail: conv_mbstowcs A ret=%d\n", len);
+		return;
+	}
+	if (memcmp(twcs, wcsa, len+2)) {
+		printf("Fail: conv_mbstowcs A cmp: 0x%x\n", wch);
+		hexdump((uchar_t *)twcs, len+2);
+		return;
+	}
+
+	len = smb_mbstowcs(twcs, mbsp, sizeof (twcs));
+	if (len != 4) {
+		printf("Fail: conv_mbstowcs P ret=%d\n", len);
+		return;
+	}
+	if (memcmp(twcs, wcsp, len+2)) {
+		printf("Fail: conv_mbstowcs P cmp: 0x%x\n", wch);
+		hexdump((uchar_t *)twcs, len+2);
+		return;
+	}
+
+	printf("Pass: conv_mbstowcs\n");
+}
+
+/*
+ * An OEM string that will require iconv.
+ */
+static uchar_t fubar_oem[] = "F\201bar";	// CP850 x81 (ü)
+static char fubar_mbs[] = "F\303\274bar";	// UTF8 xC3 xBC
+
+
+static void
+conv_oemtombs()
+{
+	char tmbs[16];
+	int len;
+
+	len = smb_oemtombs(tmbs, (uchar_t *)"foo", 4);
+	if (len != 3) {
+		printf("Fail: conv_wctomb foo ret=%d\n", len);
+		return;
+	}
+	if (strcmp(tmbs, "foo")) {
+		printf("Fail: conv_wctomb foo cmp:\n");
+		hexdump((uchar_t *)tmbs, len+1);
+		return;
+	}
+
+	len = smb_oemtombs(tmbs, fubar_oem, 7);
+	if (len != 6) {
+		printf("Fail: conv_oemtombs fubar ret=%d\n", len);
+		return;
+	}
+	if (strcmp(tmbs, fubar_mbs)) {
+		printf("Fail: conv_oemtombs fubar cmp:\n");
+		hexdump((uchar_t *)tmbs, len+1);
+		return;
+	}
+
+	printf("Pass: conv_oemtombs\n");
+}
+
+static void
+conv_mbstooem()
+{
+	uint8_t oemcs[8];
+	uint32_t wch = 0;
+	int len;
+
+	len = smb_mbstooem(oemcs, "foo", 8);
+	if (len != 3) {
+		printf("Fail: conv_mbstooem foo ret=%d\n", len);
+		return;
+	}
+	if (memcmp(oemcs, "foo", len+1)) {
+		printf("Fail: conv_mbstooem P cmp: 0x%x\n", wch);
+		hexdump((uchar_t *)oemcs, len+1);
+		return;
+	}
+
+	len = smb_mbstooem(oemcs, fubar_mbs, 8);
+	if (len != 5) {
+		printf("Fail: conv_mbstooem fubar ret=%d\n", len);
+		return;
+	}
+	if (memcmp(oemcs, (char *)fubar_oem, len+1)) {
+		printf("Fail: conv_mbstooem fubar cmp: 0x%x\n", wch);
+		hexdump((uchar_t *)oemcs, len+1);
+		return;
+	}
+
+	len = smb_mbstooem(oemcs, mbsp, 8);
+	if (len != 3) {
+		printf("Fail: conv_mbstooem poop ret=%d\n", len);
+		return;
+	}
+	if (memcmp(oemcs, "P?.", len+1)) {
+		printf("Fail: conv_mbstooem poop cmp: 0x%x\n", wch);
+		hexdump((uchar_t *)oemcs, len+1);
+		return;
+	}
+
+	printf("Pass: conv_mbstooem\n");
+}
+
+static void
+conv_sbequiv_strlen()
+{
+	int len;
+
+	len = (int)smb_sbequiv_strlen("a");
+	if (len != 1) {
+		printf("Fail: conv_sbequiv_strlen (a) len=%d\n", len);
+		return;
+	}
+
+	len = (int)smb_sbequiv_strlen(fubar_mbs);
+	if (len != strlen((char *)fubar_oem)) {
+		printf("Fail: conv_sbequiv_strlen (fubar) len=%d\n", len);
+		return;
+	}
+
+	len = (int)smb_sbequiv_strlen(mbsp);
+	if (len != 3) {	// "P?."
+		printf("Fail: conv_sbequiv_strlen (poop) len=%d\n", len);
+		return;
+	}
+
+	printf("Pass: conv_sbequiv_strlen\n");
+}
+
+static void
+conv_wcequiv_strlen()
+{
+	int len;
+
+	len = (int)smb_wcequiv_strlen("a");
+	if (len != 2) {
+		printf("Fail: conv_wcequiv_strlen (a) len=%d\n", len);
+		return;
+	}
+
+	len = (int)smb_wcequiv_strlen(fwA);
+	if (len != 2) {
+		printf("Fail: conv_wcequiv_strlen (fwA) len=%d\n", len);
+		return;
+	}
+
+	len = (int)smb_wcequiv_strlen(poop);
+	if (len != 4) {
+		printf("Fail: conv_wcequiv_strlen (poop) len=%d\n", len);
+		return;
+	}
+
+	printf("Pass: conv_wcequiv_strlen\n");
+}
+
+void
+test_conv()
+{
+	conv_wctomb();
+	conv_mbtowc();
+	conv_wcstombs();
+	conv_mbstowcs();
+	conv_oemtombs();
+	conv_mbstooem();
+	conv_sbequiv_strlen();
+	conv_wcequiv_strlen();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/smbsrv/test-msgbuf/test_defs.h	Tue Nov 06 01:27:53 2018 -0500
@@ -0,0 +1,36 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
+ */
+
+#ifndef _TEST_DEFS_H
+#define	_TEST_DEFS_H
+
+/*
+ * Describe the purpose of the file here.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void hexdump(const uchar_t *buf, int len);
+extern void test_conv(void);
+extern void test_mbmarshal(void);
+extern void test_msgbuf(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TEST_DEFS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/smbsrv/test-msgbuf/test_main.c	Tue Nov 06 01:27:53 2018 -0500
@@ -0,0 +1,86 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
+ */
+
+/*
+ * Test & debug program for smb_msgbuf.c and smb_mbuf_marshaling.c
+ */
+
+#include <sys/types.h>
+#include <sys/debug.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "test_defs.h"
+
+
+int
+main(int argc, char *argv[])
+{
+
+	test_conv();
+	test_mbmarshal();
+	test_msgbuf();
+
+	return (0);
+}
+
+void
+hexdump(const uchar_t *buf, int len)
+{
+	int idx;
+	char ascii[24];
+	char *pa = ascii;
+
+	memset(ascii, '\0', sizeof (ascii));
+
+	idx = 0;
+	while (len--) {
+		if ((idx & 15) == 0) {
+			printf("%04X: ", idx);
+			pa = ascii;
+		}
+		if (*buf > ' ' && *buf <= '~')
+			*pa++ = *buf;
+		else
+			*pa++ = '.';
+		printf("%02x ", *buf++);
+
+		idx++;
+		if ((idx & 3) == 0) {
+			*pa++ = ' ';
+			putchar(' ');
+		}
+		if ((idx & 15) == 0) {
+			*pa = '\0';
+			printf("%s\n", ascii);
+		}
+	}
+
+	if ((idx & 15) != 0) {
+		*pa = '\0';
+		/* column align the last ascii row */
+		while ((idx & 15) != 0) {
+			if ((idx & 3) == 0)
+				putchar(' ');
+			printf("   ");
+			idx++;
+		}
+		printf("%s\n", ascii);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/smbsrv/test-msgbuf/test_mbmarshal.c	Tue Nov 06 01:27:53 2018 -0500
@@ -0,0 +1,680 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
+ */
+
+/*
+ * Test putting/getting unicode strings in mbchains.
+ */
+
+#include <sys/types.h>
+#include <sys/debug.h>
+#include <sys/varargs.h>
+#include <smbsrv/smb_kproto.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include "test_defs.h"
+
+static char mbsa[] = "A\xef\xbc\xa1.";		// A fwA . (5)
+static char mbsp[] = "P\xf0\x9f\x92\xa9.";	// P poop . (6)
+static smb_wchar_t wcsa[] = { 'A', 0xff21, '.', 0 };	// (3)
+static smb_wchar_t wcsp[] = { 'P', 0xd83d, 0xdca9, '.', 0 }; // (4)
+
+smb_session_t test_ssn;
+smb_request_t test_sr;
+
+/*
+ * Put ASCII string with NULL
+ */
+static void
+mbm_put_a0()
+{
+	uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+	mbuf_chain_t *mbc;
+	int rc;
+
+	mbc = smb_mbc_alloc(100);
+
+	rc = smb_mbc_encodef(mbc, "sw", "one", 42);
+	if (rc != 0) {
+		printf("Fail: mbm_put_a0 encode\n");
+		goto out;
+	}
+	if (mbc->chain->m_len != 6) {
+		printf("Fail: mbm_put_a0 len=%d\n",
+		    mbc->chain->m_len);
+		return;
+	}
+
+	if (memcmp(mbc->chain->m_data, wire, 6)) {
+		printf("Fail: mbm_put_a0 cmp:\n");
+		hexdump((uchar_t *)mbc->chain->m_data, 6);
+		return;
+	}
+
+	printf("Pass: mbm_put_a0\n");
+
+out:
+	smb_mbc_free(mbc);
+}
+
+/*
+ * Put ASCII string, no NULL
+ */
+static void
+mbm_put_a1()
+{
+	uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+	mbuf_chain_t *mbc;
+	int rc;
+
+	mbc = smb_mbc_alloc(100);
+
+	rc = smb_mbc_encodef(mbc, "4sw", "one.", 42);
+	if (rc != 0) {
+		printf("Fail: mbm_put_a1 encode\n");
+		goto out;
+	}
+	if (mbc->chain->m_len != 6) {
+		printf("Fail: mbm_put_a1 len=%d\n",
+		    mbc->chain->m_len);
+		return;
+	}
+
+	if (memcmp(mbc->chain->m_data, wire, 6)) {
+		printf("Fail: mbm_put_a1 cmp:\n");
+		hexdump((uchar_t *)mbc->chain->m_data, 6);
+		return;
+	}
+
+	printf("Pass: mbm_put_a1\n");
+
+out:
+	smb_mbc_free(mbc);
+}
+
+static void
+mbm_put_apad()
+{
+	uint8_t wire[] = { 'o', 'n', 'e', 0, 0 };
+	mbuf_chain_t *mbc;
+	int rc;
+
+	mbc = smb_mbc_alloc(100);
+
+	/* Encode with wire length > strlen */
+	rc = smb_mbc_encodef(mbc, "5s", "one");
+	if (rc != 0) {
+		printf("Fail: mbm_put_apad encode\n");
+		goto out;
+	}
+	if (mbc->chain->m_len != 5) {
+		printf("Fail: mbm_put_apad len=%d\n",
+		    mbc->chain->m_len);
+		return;
+	}
+
+	if (memcmp(mbc->chain->m_data, wire, 5)) {
+		printf("Fail: mbm_put_apad cmp:\n");
+		hexdump((uchar_t *)mbc->chain->m_data, 5);
+		return;
+	}
+
+	printf("Pass: mbm_put_apad\n");
+
+out:
+	smb_mbc_free(mbc);
+}
+
+static void
+mbm_put_atrunc()
+{
+	uint8_t wire[] = { 'o', 'n', 'e', 't', };
+	mbuf_chain_t *mbc;
+	int rc;
+
+	mbc = smb_mbc_alloc(100);
+
+	/* Encode with wire length < strlen */
+	rc = smb_mbc_encodef(mbc, "4s", "onetwo");
+	if (rc != 0) {
+		printf("Fail: mbm_put_atrunc encode\n");
+		goto out;
+	}
+	/* Trunc should put exactly 4 */
+	if (mbc->chain->m_len != 4) {
+		printf("Fail: mbm_put_atrunc len=%d\n",
+		    mbc->chain->m_len);
+		return;
+	}
+
+	if (memcmp(mbc->chain->m_data, wire, 4)) {
+		printf("Fail: mbm_put_atrunc cmp:\n");
+		hexdump((uchar_t *)mbc->chain->m_data, 4);
+		return;
+	}
+
+	printf("Pass: mbm_put_atrunc\n");
+
+out:
+	smb_mbc_free(mbc);
+}
+
+/*
+ * Put unicode string with NULL
+ */
+static void
+mbm_put_u0()
+{
+	uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+	mbuf_chain_t *mbc;
+	int rc;
+
+	mbc = smb_mbc_alloc(100);
+
+	rc = smb_mbc_encodef(mbc, "Uw", "one", 42);
+	if (rc != 0) {
+		printf("Fail: mbm_put_u0 encode\n");
+		goto out;
+	}
+	if (mbc->chain->m_len != 10) {
+		printf("Fail: mbm_put_u0 len=%d\n",
+		    mbc->chain->m_len);
+		return;
+	}
+
+	if (memcmp(mbc->chain->m_data, wire, 10)) {
+		printf("Fail: mbm_put_u0 cmp:\n");
+		hexdump((uchar_t *)mbc->chain->m_data, 10);
+		return;
+	}
+
+	printf("Pass: mbm_put_u0\n");
+
+out:
+	smb_mbc_free(mbc);
+}
+
+/*
+ * Put unicode string, no NULL
+ */
+static void
+mbm_put_u1()
+{
+	uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+	mbuf_chain_t *mbc;
+	int rc;
+
+	mbc = smb_mbc_alloc(100);
+
+	rc = smb_mbc_encodef(mbc, "8Uw", "one.", 42);
+	if (rc != 0) {
+		printf("Fail: mbm_put_u1 encode\n");
+		goto out;
+	}
+	if (mbc->chain->m_len != 10) {
+		printf("Fail: mbm_put_u1 len=%d\n",
+		    mbc->chain->m_len);
+		return;
+	}
+
+	if (memcmp(mbc->chain->m_data, wire, 10)) {
+		printf("Fail: mbm_put_u1 cmp:\n");
+		hexdump((uchar_t *)mbc->chain->m_data, 10);
+		return;
+	}
+
+	printf("Pass: mbm_put_u1\n");
+
+out:
+	smb_mbc_free(mbc);
+}
+
+static void
+mbm_put_u3()
+{
+	mbuf_chain_t *mbc;
+	int rc;
+
+	mbc = smb_mbc_alloc(100);
+
+	rc = smb_mbc_encodef(mbc, "U", mbsa);
+	if (rc != 0) {
+		printf("Fail: mbm_put_u3 encode\n");
+		goto out;
+	}
+	if (mbc->chain->m_len != 8) {
+		printf("Fail: mbm_put_u3 len=%d\n",
+		    mbc->chain->m_len);
+		return;
+	}
+
+	if (memcmp(mbc->chain->m_data, wcsa, 8)) {
+		printf("Fail: mbm_put_u3 cmp:\n");
+		hexdump((uchar_t *)mbc->chain->m_data, 8);
+		return;
+	}
+
+	printf("Pass: mbm_put_u3\n");
+
+out:
+	smb_mbc_free(mbc);
+}
+
+static void
+mbm_put_u4()
+{
+	mbuf_chain_t *mbc;
+	int rc;
+
+	mbc = smb_mbc_alloc(100);
+
+	rc = smb_mbc_encodef(mbc, "U", mbsp);
+	if (rc != 0) {
+		printf("Fail: mbm_put_u4 encode\n");
+		goto out;
+	}
+	if (mbc->chain->m_len != 10) {
+		printf("Fail: mbm_put_u4 len=%d\n",
+		    mbc->chain->m_len);
+		return;
+	}
+
+	if (memcmp(mbc->chain->m_data, wcsp, 10)) {
+		printf("Fail: mbm_put_u4 cmp:\n");
+		hexdump((uchar_t *)mbc->chain->m_data, 10);
+		return;
+	}
+
+	printf("Pass: mbm_put_u4\n");
+
+out:
+	smb_mbc_free(mbc);
+}
+
+static void
+mbm_put_upad()
+{
+	uint16_t wire[] = { 'o', 'n', 'e', 0, 0 };
+	mbuf_chain_t *mbc;
+	int rc;
+
+	mbc = smb_mbc_alloc(100);
+
+	/* Encode with wire length > strlen */
+	rc = smb_mbc_encodef(mbc, "10U", "one");
+	if (rc != 0) {
+		printf("Fail: mbm_put_upad encode\n");
+		goto out;
+	}
+	if (mbc->chain->m_len != 10) {
+		printf("Fail: mbm_put_upad len=%d\n",
+		    mbc->chain->m_len);
+		return;
+	}
+
+	if (memcmp(mbc->chain->m_data, wire, 10)) {
+		printf("Fail: mbm_put_upad cmp:\n");
+		hexdump((uchar_t *)mbc->chain->m_data, 10);
+		return;
+	}
+
+	printf("Pass: mbm_put_upad\n");
+
+out:
+	smb_mbc_free(mbc);
+}
+
+static void
+mbm_put_utrunc()
+{
+	uint16_t wire[] = { 'o', 'n', 'e', 't' };
+	mbuf_chain_t *mbc;
+	int rc;
+
+	mbc = smb_mbc_alloc(100);
+
+	/* Encode with wire length < strlen */
+	rc = smb_mbc_encodef(mbc, "8U", "onetwo");
+	if (rc != 0) {
+		printf("Fail: mbm_put_utrunc encode\n");
+		goto out;
+	}
+	/* Trunc should put exactly 8 */
+	if (mbc->chain->m_len != 8) {
+		printf("Fail: mbm_put_utrunc len=%d\n",
+		    mbc->chain->m_len);
+		return;
+	}
+
+	if (memcmp(mbc->chain->m_data, wire, 8)) {
+		printf("Fail: mbm_put_utrunc cmp:\n");
+		hexdump((uchar_t *)mbc->chain->m_data, 8);
+		return;
+	}
+
+	printf("Pass: mbm_put_utrunc\n");
+
+out:
+	smb_mbc_free(mbc);
+}
+
+/*
+ * Parse an ascii string.
+ */
+static void
+mbm_get_a0()
+{
+	uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+	mbuf_chain_t mbc;
+	char *s;
+	int rc;
+	uint16_t w;
+
+	bzero(&mbc, sizeof (mbc));
+	MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire));
+
+	rc = smb_mbc_decodef(&mbc, "%sw", &test_sr, &s, &w);
+	if (rc != 0) {
+		printf("Fail: mbm_get_a0 decode\n");
+		goto out;
+	}
+	/*
+	 * Decode a word after the string to make sure we
+	 * end up positioned correctly after the string.
+	 */
+	if (w != 42) {
+		printf("Fail: mbm_get_a0 w=%d\n", w);
+		return;
+	}
+	if (strcmp(s, "one") != 0) {
+		printf("Fail: mbm_get_a0 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: mbm_get_a0\n");
+
+out:
+	MBC_FLUSH(&mbc);
+}
+
+/*
+ * Parse an ascii string, no NULL
+ */
+static void
+mbm_get_a1()
+{
+	uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+	mbuf_chain_t mbc;
+	char *s;
+	int rc;
+	uint16_t w;
+
+	bzero(&mbc, sizeof (mbc));
+	MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire));
+
+	rc = smb_mbc_decodef(&mbc, "%3s.w", &test_sr, &s, &w);
+	if (rc != 0) {
+		printf("Fail: mbm_get_a1 decode\n");
+		goto out;
+	}
+	/*
+	 * Decode a word after the string to make sure we
+	 * end up positioned correctly after the string.
+	 */
+	if (w != 42) {
+		printf("Fail: mbm_get_a1 w=%d\n", w);
+		return;
+	}
+	if (strcmp(s, "one") != 0) {
+		printf("Fail: mbm_get_a1 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: mbm_get_a1\n");
+
+out:
+	MBC_FLUSH(&mbc);
+}
+
+/* parse exactly to end of data */
+static void
+mbm_get_a2()
+{
+	uint8_t wire[] = { 'o', 'n', 'e' };
+	mbuf_chain_t mbc;
+	char *s;
+	int rc;
+
+	bzero(&mbc, sizeof (mbc));
+	MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire));
+
+	rc = smb_mbc_decodef(&mbc, "%3s", &test_sr, &s);
+	if (rc != 0) {
+		printf("Fail: mbm_get_a2 decode\n");
+		goto out;
+	}
+	if (mbc.chain_offset != 3) {
+		printf("Fail: mbm_get_a2 wrong pos\n");
+		return;
+	}
+	if (strcmp(s, "one") != 0) {
+		printf("Fail: mbm_get_a2 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: mbm_get_a2\n");
+
+out:
+	MBC_FLUSH(&mbc);
+}
+
+/*
+ * Parse a unicode string.
+ */
+static void
+mbm_get_u0()
+{
+	uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+	mbuf_chain_t mbc;
+	char *s;
+	int rc;
+	uint16_t w;
+
+	bzero(&mbc, sizeof (mbc));
+	MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire));
+
+	rc = smb_mbc_decodef(&mbc, "%Uw", &test_sr, &s, &w);
+	if (rc != 0) {
+		printf("Fail: mbm_get_u0 decode\n");
+		goto out;
+	}
+	/*
+	 * Decode a word after the string to make sure we
+	 * end up positioned correctly after the string.
+	 */
+	if (w != 42) {
+		printf("Fail: mbm_get_u0 w=%d\n", w);
+		return;
+	}
+	if (strcmp(s, "one") != 0) {
+		printf("Fail: mbm_get_u0 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: mbm_get_u0\n");
+
+out:
+	MBC_FLUSH(&mbc);
+}
+
+/*
+ * Parse a string that's NOT null terminated.
+ */
+static void
+mbm_get_u1()
+{
+	uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+	mbuf_chain_t mbc;
+	char *s;
+	int rc;
+	uint16_t w;
+
+	bzero(&mbc, sizeof (mbc));
+	MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire));
+
+	rc = smb_mbc_decodef(&mbc, "%6U..w", &test_sr, &s, &w);
+	if (rc != 0) {
+		printf("Fail: mbm_get_u1 decode\n");
+		goto out;
+	}
+	/*
+	 * Decode a word after the string to make sure we
+	 * end up positioned correctly after the string.
+	 */
+	if (w != 42) {
+		printf("Fail: mbm_get_u1 w=%d\n", w);
+		return;
+	}
+	if (strcmp(s, "one") != 0) {
+		printf("Fail: mbm_get_u1 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: mbm_get_u1\n");
+
+out:
+	MBC_FLUSH(&mbc);
+}
+
+/* parse exactly to end of data */
+static void
+mbm_get_u2()
+{
+	uint16_t wire[] = { 't', 'w', 'o' };
+	mbuf_chain_t mbc;
+	char *s;
+	int rc;
+
+	bzero(&mbc, sizeof (mbc));
+	MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire));
+
+	rc = smb_mbc_decodef(&mbc, "%6U", &test_sr, &s);
+	if (rc != 0) {
+		printf("Fail: mbm_get_u2 decode\n");
+		goto out;
+	}
+	if (mbc.chain_offset != 6) {
+		printf("Fail: mbm_get_u2 wrong pos\n");
+		return;
+	}
+	if (strcmp(s, "two") != 0) {
+		printf("Fail: mbm_get_u2 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: mbm_get_a2\n");
+
+out:
+	MBC_FLUSH(&mbc);
+}
+
+static void
+mbm_get_u3()
+{
+	mbuf_chain_t mbc;
+	char *s;
+	int rc;
+
+	bzero(&mbc, sizeof (mbc));
+	MBC_ATTACH_BUF(&mbc, (uchar_t *)wcsa, sizeof (wcsa));
+
+	rc = smb_mbc_decodef(&mbc, "%#U", &test_sr, sizeof (wcsa), &s);
+	if (rc != 0) {
+		printf("Fail: mbm_get_u3 decode\n");
+		goto out;
+	}
+	if (strcmp(s, mbsa) != 0) {
+		printf("Fail: mbm_get_u3 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: mbm_get_u3\n");
+
+out:
+	MBC_FLUSH(&mbc);
+}
+
+static void
+mbm_get_u4()
+{
+	mbuf_chain_t mbc;
+	char *s;
+	int rc;
+
+	bzero(&mbc, sizeof (mbc));
+	MBC_ATTACH_BUF(&mbc, (uchar_t *)wcsp, sizeof (wcsp));
+
+	rc = smb_mbc_decodef(&mbc, "%#U", &test_sr, sizeof (wcsp), &s);
+	if (rc != 0) {
+		printf("Fail: mbm_get_u4 decode\n");
+		goto out;
+	}
+	if (strcmp(s, mbsp) != 0) {
+		printf("Fail: mbm_get_u4 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: mbm_get_u4\n");
+
+out:
+	MBC_FLUSH(&mbc);
+}
+
+void
+test_mbmarshal()
+{
+
+	smb_mbc_init();
+
+	test_ssn.dialect = 0x210;	// SMB 2.1
+	test_sr.session = &test_ssn;
+	test_sr.sr_magic = SMB_REQ_MAGIC;
+	smb_srm_init(&test_sr);
+
+	mbm_put_a0();
+	mbm_put_a1();
+	mbm_put_apad();
+	mbm_put_atrunc();
+
+	mbm_put_u0();
+	mbm_put_u1();
+	mbm_put_u3();
+	mbm_put_u4();
+	mbm_put_upad();
+	mbm_put_utrunc();
+
+	mbm_get_a0();
+	mbm_get_a1();
+	mbm_get_a2();
+	mbm_get_u0();
+	mbm_get_u1();
+	mbm_get_u2();
+	mbm_get_u3();
+	mbm_get_u4();
+
+	smb_srm_fini(&test_sr);
+	smb_mbc_fini();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/smbsrv/test-msgbuf/test_misc.c	Tue Nov 06 01:27:53 2018 -0500
@@ -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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/smbsrv/test-msgbuf/test_msgbuf.c	Tue Nov 06 01:27:53 2018 -0500
@@ -0,0 +1,638 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
+ */
+
+/*
+ * Test putting/getting unicode strings in mbchains.
+ */
+
+#include <sys/types.h>
+#include <sys/debug.h>
+#include <sys/varargs.h>
+#include <smbsrv/smb_kproto.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include "test_defs.h"
+
+static char mbsa[] = "A\xef\xbc\xa1.";		// A fwA . (5)
+static char mbsp[] = "P\xf0\x9f\x92\xa9.";	// P poop . (6)
+static smb_wchar_t wcsa[] = { 'A', 0xff21, '.', 0 };	// (3)
+static smb_wchar_t wcsp[] = { 'P', 0xd83d, 0xdca9, '.', 0 }; // (4)
+
+/*
+ * Put ASCII string with NULL
+ */
+static void
+msg_put_a0()
+{
+	uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+	uint8_t temp[32];
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	int rc;
+
+	smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+	rc = smb_msgbuf_encode(&mb, "sw", "one", 42);
+	if (rc != 6) {
+		printf("Fail: msg_put_a0 encode\n");
+		goto out;
+	}
+
+	if (memcmp(temp, wire, 6)) {
+		printf("Fail: msg_put_a0 cmp:\n");
+		hexdump((uchar_t *)temp, 6);
+		return;
+	}
+
+	printf("Pass: msg_put_a0\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+/*
+ * Put ASCII string, no NULL
+ */
+static void
+msg_put_a1()
+{
+	uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+	uint8_t temp[32];
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	int rc;
+
+	smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+	rc = smb_msgbuf_encode(&mb, "4sw", "one.", 42);
+	if (rc != 6) {
+		printf("Fail: msg_put_a1 encode\n");
+		goto out;
+	}
+
+	if (memcmp(temp, wire, 6)) {
+		printf("Fail: msg_put_a1 cmp:\n");
+		hexdump((uchar_t *)temp, 6);
+		return;
+	}
+
+	printf("Pass: msg_put_a1\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+static void
+msg_put_apad()
+{
+	uint8_t wire[] = { 'o', 'n', 'e', 0, 0 };
+	uint8_t temp[32];
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	int rc;
+
+	smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+	/* Encode with wire length > strlen */
+	rc = smb_msgbuf_encode(&mb, "5s", "one");
+	if (rc != 5) {
+		printf("Fail: msg_put_apad encode\n");
+		goto out;
+	}
+
+	if (memcmp(temp, wire, 5)) {
+		printf("Fail: msg_put_apad cmp:\n");
+		hexdump((uchar_t *)temp, 5);
+		return;
+	}
+
+	printf("Pass: msg_put_apad\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+static void
+msg_put_atrunc()
+{
+	uint8_t wire[] = { 'o', 'n', 'e', 't', };
+	uint8_t temp[32];
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	int rc;
+
+	smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+	/* Encode with wire length < strlen */
+	rc = smb_msgbuf_encode(&mb, "4s", "onetwo");
+	/* Trunc should put exactly 4 */
+	if (rc != 4) {
+		printf("Fail: msg_put_atrunc encode\n");
+		goto out;
+	}
+
+	if (memcmp(temp, wire, 4)) {
+		printf("Fail: msg_put_atrunc cmp:\n");
+		hexdump((uchar_t *)temp, 4);
+		return;
+	}
+
+	printf("Pass: msg_put_atrunc\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+/*
+ * Put unicode string with NULL
+ */
+static void
+msg_put_u0()
+{
+	uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+	uint8_t temp[32];
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	int rc;
+
+	smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+	rc = smb_msgbuf_encode(&mb, "Uw", "one", 42);
+	if (rc != 10) {
+		printf("Fail: msg_put_u0 encode\n");
+		goto out;
+	}
+
+	if (memcmp(temp, wire, 10)) {
+		printf("Fail: msg_put_u0 cmp:\n");
+		hexdump((uchar_t *)temp, 10);
+		return;
+	}
+
+	printf("Pass: msg_put_u0\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+/*
+ * Put unicode string, no NULL
+ */
+static void
+msg_put_u1()
+{
+	uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+	uint8_t temp[32];
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	int rc;
+
+	smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+	rc = smb_msgbuf_encode(&mb, "8Uw", "one.", 42);
+	if (rc != 10) {
+		printf("Fail: msg_put_u1 encode\n");
+		goto out;
+	}
+
+	if (memcmp(temp, wire, 10)) {
+		printf("Fail: msg_put_u1 cmp:\n");
+		hexdump((uchar_t *)temp, 10);
+		return;
+	}
+
+	printf("Pass: msg_put_u1\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+static void
+msg_put_u3()
+{
+	uint8_t temp[32];
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	int rc;
+
+	smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+	rc = smb_msgbuf_encode(&mb, "U", mbsa);
+	if (rc != 8) {
+		printf("Fail: msg_put_u3 encode\n");
+		goto out;
+	}
+
+	if (memcmp(temp, wcsa, 8)) {
+		printf("Fail: msg_put_u3 cmp:\n");
+		hexdump((uchar_t *)temp, 8);
+		return;
+	}
+
+	printf("Pass: msg_put_u3\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+static void
+msg_put_u4()
+{
+	uint8_t temp[32];
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	int rc;
+
+	smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+	rc = smb_msgbuf_encode(&mb, "U", mbsp);
+	if (rc != 10) {
+		printf("Fail: msg_put_u4 encode\n");
+		goto out;
+	}
+
+	if (memcmp(temp, wcsp, 10)) {
+		printf("Fail: msg_put_u4 cmp:\n");
+		hexdump((uchar_t *)temp, 10);
+		return;
+	}
+
+	printf("Pass: msg_put_u4\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+static void
+msg_put_upad()
+{
+	uint16_t wire[] = { 'o', 'n', 'e', 0, 0 };
+	uint8_t temp[32];
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	int rc;
+
+	smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+	/* Encode with wire length > strlen */
+	rc = smb_msgbuf_encode(&mb, "10U", "one");
+	if (rc != 10) {
+		printf("Fail: msg_put_upad encode\n");
+		goto out;
+	}
+
+	if (memcmp(temp, wire, 10)) {
+		printf("Fail: msg_put_upad cmp:\n");
+		hexdump((uchar_t *)temp, 10);
+		return;
+	}
+
+	printf("Pass: msg_put_upad\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+static void
+msg_put_utrunc()
+{
+	uint16_t wire[] = { 'o', 'n', 'e', 't' };
+	uint8_t temp[32];
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	int rc;
+
+	smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+	/* Encode with wire length < strlen */
+	rc = smb_msgbuf_encode(&mb, "8U", "onetwo");
+	/* Trunc should put exactly 8 */
+	if (rc != 8) {
+		printf("Fail: msg_put_utrunc encode\n");
+		goto out;
+	}
+
+	if (memcmp(temp, wire, 8)) {
+		printf("Fail: msg_put_utrunc cmp:\n");
+		hexdump((uchar_t *)temp, 8);
+		return;
+	}
+
+	printf("Pass: msg_put_utrunc\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+/*
+ * Parse an ascii string.
+ */
+static void
+msg_get_a0()
+{
+	uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	char *s;
+	int rc;
+	uint16_t w;
+
+	smb_msgbuf_init(&mb, wire, sizeof (wire), mbflags);
+
+	rc = smb_msgbuf_decode(&mb, "sw", &s, &w);
+	if (rc != 6) {
+		printf("Fail: msg_get_a0 decode\n");
+		goto out;
+	}
+	/*
+	 * Decode a word after the string to make sure we
+	 * end up positioned correctly after the string.
+	 */
+	if (w != 42) {
+		printf("Fail: msg_get_a0 w=%d\n", w);
+		return;
+	}
+	if (strcmp(s, "one") != 0) {
+		printf("Fail: msg_get_a0 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: msg_get_a0\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+/*
+ * Parse an ascii string, no NULL
+ */
+static void
+msg_get_a1()
+{
+	uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	char *s;
+	int rc;
+	uint16_t w;
+
+	smb_msgbuf_init(&mb, wire, sizeof (wire), mbflags);
+
+	rc = smb_msgbuf_decode(&mb, "3s.w", &s, &w);
+	if (rc != 6) {
+		printf("Fail: msg_get_a1 decode\n");
+		goto out;
+	}
+	/*
+	 * Decode a word after the string to make sure we
+	 * end up positioned correctly after the string.
+	 */
+	if (w != 42) {
+		printf("Fail: msg_get_a1 w=%d\n", w);
+		return;
+	}
+	if (strcmp(s, "one") != 0) {
+		printf("Fail: msg_get_a1 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: msg_get_a1\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+/* parse exactly to end of data */
+static void
+msg_get_a2()
+{
+	uint8_t wire[] = { 'o', 'n', 'e' };
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	char *s;
+	int rc;
+
+	smb_msgbuf_init(&mb, wire, sizeof (wire), mbflags);
+
+	rc = smb_msgbuf_decode(&mb, "3s", &s);
+	if (rc != 3) {
+		printf("Fail: msg_get_a2 decode\n");
+		goto out;
+	}
+	if (mb.scan != mb.end) {
+		printf("Fail: msg_get_a2 wrong pos\n");
+		return;
+	}
+	if (strcmp(s, "one") != 0) {
+		printf("Fail: msg_get_a2 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: msg_get_a2\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+/*
+ * Parse a unicode string.
+ */
+static void
+msg_get_u0()
+{
+	uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	char *s;
+	int rc;
+	uint16_t w;
+
+	smb_msgbuf_init(&mb, (uint8_t *)wire, sizeof (wire), mbflags);
+
+	rc = smb_msgbuf_decode(&mb, "Uw", &s, &w);
+	if (rc != 10) {
+		printf("Fail: msg_get_u0 decode\n");
+		goto out;
+	}
+	/*
+	 * Decode a word after the string to make sure we
+	 * end up positioned correctly after the string.
+	 */
+	if (w != 42) {
+		printf("Fail: msg_get_u0 w=%d\n", w);
+		return;
+	}
+	if (strcmp(s, "one") != 0) {
+		printf("Fail: msg_get_u0 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: msg_get_u0\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+/*
+ * Parse a string that's NOT null terminated.
+ */
+static void
+msg_get_u1()
+{
+	uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	char *s;
+	int rc;
+	uint16_t w;
+
+	smb_msgbuf_init(&mb, (uint8_t *)wire, sizeof (wire), mbflags);
+
+	rc = smb_msgbuf_decode(&mb, "6U..w", &s, &w);
+	if (rc != 10) {
+		printf("Fail: msg_get_u1 decode\n");
+		goto out;
+	}
+	/*
+	 * Decode a word after the string to make sure we
+	 * end up positioned correctly after the string.
+	 */
+	if (w != 42) {
+		printf("Fail: msg_get_u1 w=%d\n", w);
+		return;
+	}
+	if (strcmp(s, "one") != 0) {
+		printf("Fail: msg_get_u1 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: msg_get_u1\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+/* parse exactly to end of data */
+static void
+msg_get_u2()
+{
+	uint16_t wire[] = { 'o', 'n', 'e' };
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	char *s;
+	int rc;
+
+	smb_msgbuf_init(&mb, (uint8_t *)wire, sizeof (wire), mbflags);
+
+	rc = smb_msgbuf_decode(&mb, "6U", &s);
+	if (rc != 6) {
+		printf("Fail: msg_get_u2 decode\n");
+		goto out;
+	}
+	if (mb.scan != mb.end) {
+		printf("Fail: msg_get_u2 wrong pos\n");
+		return;
+	}
+	if (strcmp(s, "one") != 0) {
+		printf("Fail: msg_get_u2 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: msg_get_u2\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+static void
+msg_get_u3()
+{
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	char *s;
+	int rc;
+
+	smb_msgbuf_init(&mb, (uint8_t *)wcsa, sizeof (wcsa), mbflags);
+
+	rc = smb_msgbuf_decode(&mb, "#U", sizeof (wcsa), &s);
+	if (rc != 8) {
+		printf("Fail: msg_get_u3 decode\n");
+		goto out;
+	}
+	if (strcmp(s, mbsa) != 0) {
+		printf("Fail: msg_get_u3 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: msg_get_u3\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+static void
+msg_get_u4()
+{
+	smb_msgbuf_t mb;
+	int mbflags = 0;
+	char *s;
+	int rc;
+
+	smb_msgbuf_init(&mb, (uint8_t *)wcsp, sizeof (wcsp), mbflags);
+
+	rc = smb_msgbuf_decode(&mb, "#U", sizeof (wcsp), &s);
+	if (rc != 10) {
+		printf("Fail: msg_get_u4 decode\n");
+		goto out;
+	}
+	if (strcmp(s, mbsp) != 0) {
+		printf("Fail: msg_get_u4 cmp: <%s>\n", s);
+		return;
+	}
+
+	printf("Pass: msg_get_u4\n");
+
+out:
+	smb_msgbuf_term(&mb);
+}
+
+void
+test_msgbuf()
+{
+
+	msg_put_a0();
+	msg_put_a1();
+	msg_put_apad();
+	msg_put_atrunc();
+
+	msg_put_u0();
+	msg_put_u1();
+	msg_put_u3();
+	msg_put_u4();
+	msg_put_upad();
+	msg_put_utrunc();
+
+	msg_get_a0();
+	msg_get_a1();
+	msg_get_a2();
+	msg_get_u0();
+	msg_get_u1();
+	msg_get_u2();
+	msg_get_u3();
+	msg_get_u4();
+
+}
--- a/usr/src/common/smbsrv/smb_match.c	Fri Oct 02 15:45:00 2015 -0400
+++ b/usr/src/common/smbsrv/smb_match.c	Tue Nov 06 01:27:53 2018 -0500
@@ -142,7 +142,7 @@
 	const char	*limit;
 	char		pc;		/* current pattern char */
 	int		rc;
-	smb_wchar_t	wcpat, wcstr;	/* current wchar in pat, str */
+	uint32_t	wcpat, wcstr;	/* current wchar in pat, str */
 	int		nbpat, nbstr;	/* multi-byte length of it */
 
 	if (priv->depth >= SMB_MATCH_DEPTH_MAX)
--- a/usr/src/common/smbsrv/smb_msgbuf.c	Fri Oct 02 15:45:00 2015 -0400
+++ b/usr/src/common/smbsrv/smb_msgbuf.c	Tue Nov 06 01:27:53 2018 -0500
@@ -22,7 +22,7 @@
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
- * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
@@ -53,6 +53,12 @@
 static void *smb_msgbuf_malloc(smb_msgbuf_t *, size_t);
 static int smb_msgbuf_chkerc(char *text, int erc);
 
+static int msgbuf_get_oem_string(smb_msgbuf_t *, char **, int);
+static int msgbuf_get_unicode_string(smb_msgbuf_t *, char **, int);
+static int msgbuf_put_oem_string(smb_msgbuf_t *, char *, int);
+static int msgbuf_put_unicode_string(smb_msgbuf_t *, char *, int);
+
+
 /*
  * Returns the offset or number of bytes used within the buffer.
  */
@@ -177,7 +183,7 @@
  * Decode a smb_msgbuf buffer as indicated by the format string into
  * the variable arg list. This is similar to a scanf operation.
  *
- * On success, returns the number of bytes encoded. Otherwise
+ * On success, returns the number of bytes decoded. Otherwise
  * returns a -ve error code.
  */
 int
@@ -213,15 +219,12 @@
 static int
 buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap)
 {
-	uint32_t ival;
 	uint8_t c;
 	uint8_t *bvalp;
 	uint16_t *wvalp;
 	uint32_t *lvalp;
 	uint64_t *llvalp;
-	char *cvalp;
 	char **cvalpp;
-	smb_wchar_t wchar;
 	boolean_t repc_specified;
 	int repc;
 	int rc;
@@ -324,75 +327,23 @@
 				goto unicode_translation;
 			/*FALLTHROUGH*/
 
-		case 's': /* get string */
-			if (!repc_specified)
-				repc = strlen((const char *)mb->scan) + 1;
-			if (smb_msgbuf_has_space(mb, repc) == 0)
-				return (SMB_MSGBUF_UNDERFLOW);
-			if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0)
-				return (SMB_MSGBUF_UNDERFLOW);
+		case 's': /* get OEM string */
 			cvalpp = va_arg(ap, char **);
-			*cvalpp = cvalp;
-			/* Translate OEM to mbs */
-			while (repc > 0) {
-				wchar = *mb->scan++;
-				repc--;
-				if (wchar == 0)
-					break;
-				ival = smb_wctomb(cvalp, wchar);
-				cvalp += ival;
-			}
-			*cvalp = '\0';
-			if (repc > 0)
-				mb->scan += repc;
+			if (!repc_specified)
+				repc = 0;
+			rc = msgbuf_get_oem_string(mb, cvalpp, repc);
+			if (rc != 0)
+				return (rc);
 			break;
 
-		case 'U': /* get unicode string */
+		case 'U': /* get UTF-16 string */
 unicode_translation:
-			/*
-			 * Unicode strings are always word aligned.
-			 * The malloc'd area is larger than the
-			 * original string because the UTF-8 chars
-			 * may be longer than the wide-chars.
-			 */
-			smb_msgbuf_word_align(mb);
-			if (!repc_specified) {
-				/*
-				 * Count bytes, including the null.
-				 */
-				uint8_t *tmp_scan = mb->scan;
-				repc = 2; /* the null */
-				while ((wchar = LE_IN16(tmp_scan)) != 0) {
-					tmp_scan += 2;
-					repc += 2;
-				}
-			}
-			if (smb_msgbuf_has_space(mb, repc) == 0)
-				return (SMB_MSGBUF_UNDERFLOW);
-			/*
-			 * Get space for translated string
-			 * Allocates worst-case size.
-			 */
-			if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0)
-				return (SMB_MSGBUF_UNDERFLOW);
 			cvalpp = va_arg(ap, char **);
-			*cvalpp = cvalp;
-			/*
-			 * Translate unicode to mbs, stopping after
-			 * null or repc limit.
-			 */
-			while (repc >= 2) {
-				wchar = LE_IN16(mb->scan);
-				mb->scan += 2;
-				repc -= 2;
-				if (wchar == 0)
-					break;
-				ival = smb_wctomb(cvalp, wchar);
-				cvalp += ival;
-			}
-			*cvalp = '\0';
-			if (repc > 0)
-				mb->scan += repc;
+			if (!repc_specified)
+				repc = 0;
+			rc = msgbuf_get_unicode_string(mb, cvalpp, repc);
+			if (rc != 0)
+				return (rc);
 			break;
 
 		case 'M':
@@ -416,6 +367,151 @@
 	return (SMB_MSGBUF_SUCCESS);
 }
 
+/*
+ * msgbuf_get_oem_string
+ *
+ * Decode an OEM string, returning its UTF-8 form in strpp,
+ * allocated using smb_msgbuf_malloc (automatically freed).
+ * If max_bytes != 0, consume at most max_bytes of the mb.
+ * See also: mbc_marshal_get_oem_string
+ */
+static int
+msgbuf_get_oem_string(smb_msgbuf_t *mb, char **strpp, int max_bytes)
+{
+	char		*mbs;
+	uint8_t		*oembuf = NULL;
+	int		oemlen;		// len of OEM string, w/o null
+	int		datalen;	// OtW data len
+	int		mbsmax;		// max len of ret str
+	int		rlen;
+
+	if (max_bytes == 0)
+		max_bytes = 0xffff;
+
+	/*
+	 * Determine the OtW data length and OEM string length
+	 * Note: oemlen is the string length (w/o null) and
+	 * datalen is how much we move mb->scan
+	 */
+	datalen = 0;
+	oemlen = 0;
+	for (;;) {
+		if (datalen >= max_bytes)
+			break;
+		/* in-line smb_msgbuf_has_space */
+		if ((mb->scan + datalen) >= mb->end)
+			return (SMB_MSGBUF_UNDERFLOW);
+		datalen++;
+		if (mb->scan[datalen - 1] == 0)
+			break;
+		oemlen++;
+	}
+
+	/*
+	 * Get datalen bytes into a temp buffer
+	 * sized with room to add a null.
+	 * Free oembuf in smb_msgbuf_term
+	 */
+	oembuf = smb_msgbuf_malloc(mb, datalen + 1);
+	if (oembuf == NULL)
+		return (SMB_MSGBUF_UNDERFLOW);
+	bcopy(mb->scan, oembuf, datalen);
+	mb->scan += datalen;
+	oembuf[oemlen] = '\0';
+
+	/*
+	 * Get the buffer we'll return and convert to UTF-8.
+	 * May take as much as double the space.
+	 */
+	mbsmax = oemlen * 2;
+	mbs = smb_msgbuf_malloc(mb, mbsmax + 1);
+	if (mbs == NULL)
+		return (SMB_MSGBUF_UNDERFLOW);
+	rlen = smb_oemtombs(mbs, oembuf, mbsmax);
+	if (rlen < 0)
+		return (SMB_MSGBUF_UNDERFLOW);
+	if (rlen > mbsmax)
+		rlen = mbsmax;
+	mbs[rlen] = '\0';
+	*strpp = mbs;
+	return (0);
+}
+
+/*
+ * msgbuf_get_unicode_string
+ *
+ * Decode a UTF-16 string, returning its UTF-8 form in strpp,
+ * allocated using smb_msgbuf_malloc (automatically freed).
+ * If max_bytes != 0, consume at most max_bytes of the mb.
+ * See also: mbc_marshal_get_unicode_string
+ */
+static int
+msgbuf_get_unicode_string(smb_msgbuf_t *mb, char **strpp, int max_bytes)
+{
+	char		*mbs;
+	uint16_t	*wcsbuf = NULL;
+	int		wcslen;		// wchar count
+	int		datalen;	// OtW data len
+	size_t		mbsmax;		// max len of ret str
+	size_t		rlen;
+
+	if (max_bytes == 0)
+		max_bytes = 0xffff;
+
+	/*
+	 * Unicode strings are always word aligned.
+	 */
+	smb_msgbuf_word_align(mb);
+
+	/*
+	 * Determine the OtW data length and (WC) string length
+	 * Note: wcslen counts 16-bit wide_chars (w/o null),
+	 * and datalen is how much we move mb->scan
+	 */
+	datalen = 0;
+	wcslen = 0;
+	for (;;) {
+		if (datalen >= max_bytes)
+			break;
+		/* in-line smb_msgbuf_has_space */
+		if ((mb->scan + datalen) >= mb->end)
+			return (SMB_MSGBUF_UNDERFLOW);
+		datalen += 2;
+		if (mb->scan[datalen - 2] == 0 &&
+		    mb->scan[datalen - 1] == 0)
+			break;
+		wcslen++;
+	}
+
+	/*
+	 * Get datalen bytes into a temp buffer
+	 * sized with room to add a (WC) null.
+	 * Note: wcsbuf has little-endian order
+	 */
+	wcsbuf = smb_msgbuf_malloc(mb, datalen + 2);
+	if (wcsbuf == NULL)
+		return (SMB_MSGBUF_UNDERFLOW);
+	bcopy(mb->scan, wcsbuf, datalen);
+	mb->scan += datalen;
+	wcsbuf[wcslen] = 0;
+
+	/*
+	 * Get the buffer we'll return and convert to UTF-8.
+	 * May take as much 4X number of wide chars.
+	 */
+	mbsmax = wcslen * MTS_MB_CUR_MAX;
+	mbs = smb_msgbuf_malloc(mb, mbsmax + 1);
+	if (mbs == NULL)
+		return (SMB_MSGBUF_UNDERFLOW);
+	rlen = smb_wcstombs(mbs, wcsbuf, mbsmax);
+	if (rlen == (size_t)-1)
+		return (SMB_MSGBUF_UNDERFLOW);
+	if (rlen > mbsmax)
+		rlen = mbsmax;
+	mbs[rlen] = '\0';
+	*strpp = mbs;
+	return (0);
+}
 
 /*
  * smb_msgbuf_encode
@@ -466,8 +562,6 @@
 	uint8_t *bvalp;
 	char *cvalp;
 	uint8_t c;
-	smb_wchar_t wchar;
-	int count;
 	boolean_t repc_specified;
 	int repc;
 	int rc;
@@ -571,80 +665,23 @@
 				goto unicode_translation;
 			/* FALLTHROUGH */
 
-		case 's': /* put string */
+		case 's': /* put OEM string */
 			cvalp = va_arg(ap, char *);
-			if (!repc_specified) {
-				repc = smb_sbequiv_strlen(cvalp);
-				if (repc == -1)
-					return (SMB_MSGBUF_OVERFLOW);
-				if (!(mb->flags & SMB_MSGBUF_NOTERM))
-					repc++;
-			}
-			if (smb_msgbuf_has_space(mb, repc) == 0)
-				return (SMB_MSGBUF_OVERFLOW);
-			while (repc > 0) {
-				count = smb_mbtowc(&wchar, cvalp,
-				    MTS_MB_CHAR_MAX);
-				if (count < 0)
-					return (SMB_MSGBUF_DATA_ERROR);
-				cvalp += count;
-				if (wchar == 0)
-					break;
-				*mb->scan++ = (uint8_t)wchar;
-				repc--;
-				if (wchar & 0xff00) {
-					*mb->scan++ = wchar >> 8;
-					repc--;
-				}
-			}
-			if (*cvalp == '\0' && repc > 0 &&
-			    (mb->flags & SMB_MSGBUF_NOTERM) == 0) {
-				*mb->scan++ = 0;
-				repc--;
-			}
-			while (repc > 0) {
-				*mb->scan++ = 0;
-				repc--;
-			}
+			if (!repc_specified)
+				repc = 0;
+			rc = msgbuf_put_oem_string(mb, cvalp, repc);
+			if (rc != 0)
+				return (rc);
 			break;
 
-		case 'U': /* put unicode string */
+		case 'U': /* put UTF-16 string */
 unicode_translation:
-			/*
-			 * Unicode strings are always word aligned.
-			 */
-			smb_msgbuf_word_align(mb);
 			cvalp = va_arg(ap, char *);
-			if (!repc_specified) {
-				repc = smb_wcequiv_strlen(cvalp);
-				if (!(mb->flags & SMB_MSGBUF_NOTERM))
-					repc += 2;
-			}
-			if (!smb_msgbuf_has_space(mb, repc))
-				return (SMB_MSGBUF_OVERFLOW);
-			while (repc >= 2) {
-				count = smb_mbtowc(&wchar, cvalp,
-				    MTS_MB_CHAR_MAX);
-				if (count < 0)
-					return (SMB_MSGBUF_DATA_ERROR);
-				cvalp += count;
-				if (wchar == 0)
-					break;
-
-				LE_OUT16(mb->scan, wchar);
-				mb->scan += 2;
-				repc -= 2;
-			}
-			if (*cvalp == '\0' && repc >= 2 &&
-			    (mb->flags & SMB_MSGBUF_NOTERM) == 0) {
-				LE_OUT16(mb->scan, 0);
-				mb->scan += 2;
-				repc -= 2;
-			}
-			while (repc > 0) {
-				*mb->scan++ = 0;
-				repc--;
-			}
+			if (!repc_specified)
+				repc = 0;
+			rc = msgbuf_put_unicode_string(mb, cvalp, repc);
+			if (rc != 0)
+				return (rc);
 			break;
 
 		case 'M':
@@ -665,6 +702,141 @@
 	return (SMB_MSGBUF_SUCCESS);
 }
 
+/*
+ * Marshal a UTF-8 string (str) into mbc, converting to OEM codeset.
+ * Also write a null unless the repc count limits the length we put.
+ * When (repc > 0) the length we marshal must be exactly repc, and
+ * truncate or pad the mb data as necessary.
+ * See also: mbc_marshal_put_oem_string
+ */
+static int
+msgbuf_put_oem_string(smb_msgbuf_t *mb, char *mbs, int repc)
+{
+	uint8_t		*oembuf = NULL;
+	uint8_t		*s;
+	int		oemlen;
+	int		rlen;
+
+	/*
+	 * Compute length of converted OEM string,
+	 * NOT including null terminator
+	 */
+	if ((oemlen = smb_sbequiv_strlen(mbs)) == -1)
+		return (SMB_MSGBUF_DATA_ERROR);
+
+	/*
+	 * If repc not specified, put whole string + NULL,
+	 * otherwise will truncate or pad as needed.
+	 */
+	if (repc <= 0) {
+		repc = oemlen;
+		if ((mb->flags & SMB_MSGBUF_NOTERM) == 0)
+			repc += sizeof (char);
+	}
+	if (smb_msgbuf_has_space(mb, repc) == 0)
+		return (SMB_MSGBUF_OVERFLOW);
+
+	/*
+	 * Convert into a temporary buffer
+	 * Free oembuf in smb_msgbuf_term.
+	 */
+	oembuf = smb_msgbuf_malloc(mb, oemlen + 1);
+	if (oembuf == NULL)
+		return (SMB_MSGBUF_UNDERFLOW);
+	rlen = smb_mbstooem(oembuf, mbs, oemlen);
+	if (rlen < 0)
+		return (SMB_MSGBUF_DATA_ERROR);
+	if (rlen > oemlen)
+		rlen = oemlen;
+	oembuf[rlen] = '\0';
+
+	/*
+	 * Copy the converted string into the message,
+	 * truncated or paded as required.
+	 */
+	s = oembuf;
+	while (repc > 0) {
+		*mb->scan++ = *s;
+		if (*s != '\0')
+			s++;
+		repc--;
+	}
+
+	return (0);
+}
+
+/*
+ * Marshal a UTF-8 string (str) into mbc, converting to UTF-16.
+ * Also write a null unless the repc count limits the length.
+ * When (repc > 0) the length we marshal must be exactly repc,
+ * and truncate or pad the mb data as necessary.
+ * See also: mbc_marshal_put_unicode_string
+ */
+static int
+msgbuf_put_unicode_string(smb_msgbuf_t *mb, char *mbs, int repc)
+{
+	smb_wchar_t	*wcsbuf = NULL;
+	smb_wchar_t	*wp;
+	size_t		wcslen, wcsbytes;
+	size_t		rlen;
+
+	/* align to word boundary */
+	smb_msgbuf_word_align(mb);
+
+	/*
+	 * Compute length of converted UTF-16 string,
+	 * NOT including null terminator (in bytes).
+	 */
+	wcsbytes = smb_wcequiv_strlen(mbs);
+	if (wcsbytes == (size_t)-1)
+		return (SMB_MSGBUF_DATA_ERROR);
+
+	/*
+	 * If repc not specified, put whole string + NULL,
+	 * otherwise will truncate or pad as needed.
+	 */
+	if (repc <= 0) {
+		repc = (int)wcsbytes;
+		if ((mb->flags & SMB_MSGBUF_NOTERM) == 0)
+			repc += sizeof (smb_wchar_t);
+	}
+	if (smb_msgbuf_has_space(mb, repc) == 0)
+		return (SMB_MSGBUF_OVERFLOW);
+
+	/*
+	 * Convert into a temporary buffer
+	 * Free wcsbuf in smb_msgbuf_term
+	 */
+	wcslen = wcsbytes / 2;
+	wcsbuf = smb_msgbuf_malloc(mb, wcsbytes + 2);
+	if (wcsbuf == NULL)
+		return (SMB_MSGBUF_UNDERFLOW);
+	rlen = smb_mbstowcs(wcsbuf, mbs, wcslen);
+	if (rlen == (size_t)-1)
+		return (SMB_MSGBUF_DATA_ERROR);
+	if (rlen > wcslen)
+		rlen = wcslen;
+	wcsbuf[rlen] = 0;
+
+	/*
+	 * Copy the converted string into the message,
+	 * truncated or paded as required.  Preserve
+	 * little-endian order while copying.
+	 */
+	wp = wcsbuf;
+	while (repc > 1) {
+		smb_wchar_t wchar = LE_IN16(wp);
+		LE_OUT16(mb->scan, wchar);
+		mb->scan += 2;
+		if (wchar != 0)
+			wp++;
+		repc -= sizeof (smb_wchar_t);
+	}
+	if (repc > 0)
+		*mb->scan++ = '\0';
+
+	return (0);
+}
 
 /*
  * smb_msgbuf_malloc
--- a/usr/src/common/smbsrv/smb_string.c	Fri Oct 02 15:45:00 2015 -0400
+++ b/usr/src/common/smbsrv/smb_string.c	Tue Nov 06 01:27:53 2018 -0500
@@ -174,8 +174,8 @@
  * If the specified character is lowercase, the uppercase value will
  * be returned. Otherwise the original value will be returned.
  */
-int
-smb_toupper(int c)
+uint32_t
+smb_toupper(uint32_t c)
 {
 	uint16_t mask = is_unicode ? 0xffff : 0xff;
 
@@ -187,8 +187,8 @@
  * If the specified character is uppercase, the lowercase value will
  * be returned. Otherwise the original value will be returned.
  */
-int
-smb_tolower(int c)
+uint32_t
+smb_tolower(uint32_t c)
 {
 	uint16_t mask = is_unicode ? 0xffff : 0xff;
 
@@ -204,7 +204,7 @@
 char *
 smb_strupr(char *s)
 {
-	smb_wchar_t c;
+	uint32_t c;
 	char *p = s;
 
 	while (*p) {
@@ -235,7 +235,7 @@
 char *
 smb_strlwr(char *s)
 {
-	smb_wchar_t c;
+	uint32_t c;
 	char *p = s;
 
 	while (*p) {
@@ -264,7 +264,7 @@
 int
 smb_isstrlwr(const char *s)
 {
-	smb_wchar_t c;
+	uint32_t c;
 	int n;
 	const char *p = s;
 
@@ -295,7 +295,7 @@
 int
 smb_isstrupr(const char *s)
 {
-	smb_wchar_t c;
+	uint32_t c;
 	int n;
 	const char *p = s;
 
@@ -440,7 +440,7 @@
  * unc_server	server or domain name with no leading/trailing '\'
  * unc_share	share name with no leading/trailing '\'
  * unc_path	relative path to the share with no leading/trailing '\'
- * 		it is valid for unc_path to be NULL.
+ *		it is valid for unc_path to be NULL.
  *
  * Upon successful return of this function, smb_unc_free()
  * MUST be called when returned 'unc' is no longer needed.
--- a/usr/src/common/smbsrv/smb_utf8.c	Fri Oct 02 15:45:00 2015 -0400
+++ b/usr/src/common/smbsrv/smb_utf8.c	Tue Nov 06 01:27:53 2018 -0500
@@ -22,32 +22,12 @@
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
- * Multibyte/wide-char conversion routines. Wide-char encoding provides
- * a fixed size character encoding that maps to the Unicode 16-bit
- * (UCS-2) character set standard. Multibyte or UCS transformation
- * format (UTF) encoding is a variable length character encoding scheme
- * that s compatible with existing ASCII characters and guarantees that
- * the resultant strings do not contain embedded null characters. Both
- * types of encoding provide a null terminator: single byte for UTF-8
- * and a wide-char null for Unicode. See RFC 2044.
- *
- * The table below illustrates the UTF-8 encoding scheme. The letter x
- * indicates bits available for encoding the character value.
- *
- *	UCS-2			UTF-8 octet sequence (binary)
- *	0x0000-0x007F	0xxxxxxx
- *	0x0080-0x07FF	110xxxxx 10xxxxxx
- *	0x0800-0xFFFF	1110xxxx 10xxxxxx 10xxxxxx
- *
- * RFC 2044
- * UTF-8,a transformation format of UNICODE and ISO 10646
- * F. Yergeau
- * Alis Technologies
- * October 1996
+ * Multibyte/wide-char conversion routines. SMB uses UTF-16 on the wire
+ * (smb_wchar_t) and we use UTF-8 internally (our multi-byte, or mbs).
  */
 
 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
@@ -60,6 +40,7 @@
 #include <iconv.h>
 #include <assert.h>
 #endif	/* _KERNEL || _FAKE_KERNEL */
+#include <sys/u8_textprep.h>
 #include <smbsrv/string.h>
 
 
@@ -76,26 +57,37 @@
  * multibyte character is encountered.
  */
 size_t
-smb_mbstowcs(smb_wchar_t *wcstring, const char *mbstring, size_t nwchars)
+smb_mbstowcs(smb_wchar_t *wcs, const char *mbs, size_t nwchars)
 {
-	int len;
-	smb_wchar_t	*start = wcstring;
+	size_t mbslen, wcslen;
+	int err;
 
-	while (nwchars--) {
-		len = smb_mbtowc(wcstring, mbstring, MTS_MB_CHAR_MAX);
-		if (len < 0) {
-			*wcstring = 0;
-			return ((size_t)-1);
-		}
-
-		if (*mbstring == 0)
-			break;
-
-		++wcstring;
-		mbstring += len;
+	/* NULL or empty input is allowed. */
+	if (mbs == NULL || *mbs == '\0') {
+		if (wcs != NULL && nwchars > 0)
+			*wcs = 0;
+		return (0);
 	}
 
-	return (wcstring - start);
+	/*
+	 * Traditional mbstowcs(3C) allows wcs==NULL to get the length.
+	 * SMB never calls it that way, but let's future-proof.
+	 */
+	if (wcs == NULL) {
+		return ((size_t)-1);
+	}
+
+	mbslen = strlen(mbs);
+	wcslen = nwchars;
+	err = uconv_u8tou16((const uchar_t *)mbs, &mbslen,
+	    wcs, &wcslen, UCONV_OUT_LITTLE_ENDIAN);
+	if (err != 0)
+		return ((size_t)-1);
+
+	if (wcslen < nwchars)
+		wcs[wcslen] = 0;
+
+	return (wcslen);
 }
 
 
@@ -114,49 +106,36 @@
  * states.  Otherwise it should be return 0.
  *
  * If mbchar is non-null, returns the number of bytes processed in
- * mbchar.  If mbchar is invalid, returns -1.
+ * mbchar.  If mbchar is null, convert the null (wcharp=0) but
+ * return length zero.  If mbchar is invalid, returns -1.
  */
 int /*ARGSUSED*/
-smb_mbtowc(smb_wchar_t *wcharp, const char *mbchar, size_t nbytes)
+smb_mbtowc(uint32_t *wcharp, const char *mbchar, size_t nbytes)
 {
-	unsigned char mbyte;
-	smb_wchar_t wide_char;
-	int count;
-	int bytes_left;
+	uint32_t wide_char;
+	int count, err;
+	size_t mblen;
+	size_t wclen;
 
 	if (mbchar == NULL)
 		return (0); /* no shift states */
 
-	/* 0xxxxxxx -> 1 byte ASCII encoding */
-	if (((mbyte = *mbchar++) & 0x80) == 0) {
-		if (wcharp)
-			*wcharp = (smb_wchar_t)mbyte;
-
-		return (mbyte ? 1 : 0);
-	}
-
-	/* 10xxxxxx -> invalid first byte */
-	if ((mbyte & 0x40) == 0)
+	/*
+	 * How many bytes in this symbol?
+	 */
+	count = u8_validate((char *)mbchar, nbytes, NULL, 0, &err);
+	if (count < 0)
 		return (-1);
 
-	wide_char = mbyte;
-	if ((mbyte & 0x20) == 0) {
-		wide_char &= 0x1f;
-		bytes_left = 1;
-	} else if ((mbyte & 0x10) == 0) {
-		wide_char &= 0x0f;
-		bytes_left = 2;
-	} else {
+	mblen = count;
+	wclen = 1;
+	err = uconv_u8tou32((const uchar_t *)mbchar, &mblen,
+	    &wide_char, &wclen, UCONV_OUT_SYSTEM_ENDIAN);
+	if (err != 0)
 		return (-1);
-	}
-
-	count = 1;
-	while (bytes_left--) {
-		if (((mbyte = *mbchar++) & 0xc0) != 0x80)
-			return (-1);
-
-		count++;
-		wide_char = (wide_char << 6) | (mbyte & 0x3f);
+	if (wclen == 0) {
+		wide_char = 0;
+		count = 0;
 	}
 
 	if (wcharp)
@@ -174,25 +153,27 @@
  * mbchar must be large enough to accommodate the multibyte character.
  *
  * Returns the numberof bytes written to mbchar.
+ * Note: handles null like any 1-byte char.
  */
 int
-smb_wctomb(char *mbchar, smb_wchar_t wchar)
+smb_wctomb(char *mbchar, uint32_t wchar)
 {
-	if ((wchar & ~0x7f) == 0) {
-		*mbchar = (char)wchar;
-		return (1);
-	}
+	char junk[MTS_MB_CUR_MAX+1];
+	size_t mblen;
+	size_t wclen;
+	int err;
 
-	if ((wchar & ~0x7ff) == 0) {
-		*mbchar++ = (wchar >> 6) | 0xc0;
-		*mbchar = (wchar & 0x3f) | 0x80;
-		return (2);
-	}
+	if (mbchar == NULL)
+		mbchar = junk;
 
-	*mbchar++ = (wchar >> 12) | 0xe0;
-	*mbchar++ = ((wchar >> 6) & 0x3f) | 0x80;
-	*mbchar = (wchar & 0x3f) | 0x80;
-	return (3);
+	mblen = MTS_MB_CUR_MAX;
+	wclen = 1;
+	err = uconv_u32tou8(&wchar, &wclen, (uchar_t *)mbchar, &mblen,
+	    UCONV_IN_SYSTEM_ENDIAN | UCONV_IGNORE_NULL);
+	if (err != 0)
+		return (-1);
+
+	return ((int)mblen);
 }
 
 
@@ -206,46 +187,46 @@
  * terminated if there is room.
  *
  * Returns the number of bytes converted, not counting the terminating
- * null byte.
+ * null byte. Returns -1 if an invalid WC sequence is encountered.
  */
 size_t
-smb_wcstombs(char *mbstring, const smb_wchar_t *wcstring, size_t nbytes)
+smb_wcstombs(char *mbs, const smb_wchar_t *wcs, size_t nbytes)
 {
-	char *start = mbstring;
-	const smb_wchar_t *wcp = wcstring;
-	smb_wchar_t wide_char = 0;
-	char buf[4];
-	size_t len;
-
-	if ((mbstring == NULL) || (wcstring == NULL))
-		return (0);
+	size_t mbslen, wcslen;
+	int err;
 
-	while (nbytes > MTS_MB_CHAR_MAX) {
-		wide_char = *wcp++;
-		len = smb_wctomb(mbstring, wide_char);
-
-		if (wide_char == 0)
-			/*LINTED E_PTRDIFF_OVERFLOW*/
-			return (mbstring - start);
-
-		mbstring += len;
-		nbytes -= len;
+	/* NULL or empty input is allowed. */
+	if (wcs == NULL || *wcs == 0) {
+		if (mbs != NULL && nbytes > 0)
+			*mbs = '\0';
+		return (0);
 	}
 
-	while (wide_char && nbytes) {
-		wide_char = *wcp++;
-		if ((len = smb_wctomb(buf, wide_char)) > nbytes) {
-			*mbstring = 0;
-			break;
-		}
-
-		bcopy(buf, mbstring, len);
-		mbstring += len;
-		nbytes -= len;
+	/*
+	 * Traditional wcstombs(3C) allows mbs==NULL to get the length.
+	 * SMB never calls it that way, but let's future-proof.
+	 */
+	if (mbs == NULL) {
+		return ((size_t)-1);
 	}
 
-	/*LINTED E_PTRDIFF_OVERFLOW*/
-	return (mbstring - start);
+	/*
+	 * Compute wcslen
+	 */
+	wcslen = 0;
+	while (wcs[wcslen] != 0)
+		wcslen++;
+
+	mbslen = nbytes;
+	err = uconv_u16tou8(wcs, &wcslen,
+	    (uchar_t *)mbs, &mbslen, UCONV_IN_LITTLE_ENDIAN);
+	if (err != 0)
+		return ((size_t)-1);
+
+	if (mbslen < nbytes)
+		mbs[mbslen] = '\0';
+
+	return (mbslen);
 }
 
 
@@ -257,7 +238,7 @@
 size_t
 smb_wcequiv_strlen(const char *mbs)
 {
-	smb_wchar_t	wide_char;
+	uint32_t	wide_char;
 	size_t bytes;
 	size_t len = 0;
 
@@ -265,9 +246,15 @@
 		bytes = smb_mbtowc(&wide_char, mbs, MTS_MB_CHAR_MAX);
 		if (bytes == ((size_t)-1))
 			return ((size_t)-1);
+		mbs += bytes;
 
 		len += sizeof (smb_wchar_t);
-		mbs += bytes;
+		if (bytes > 3) {
+			/*
+			 * Extended unicode, so TWO smb_wchar_t
+			 */
+			len += sizeof (smb_wchar_t);
+		}
 	}
 
 	return (len);
@@ -277,27 +264,37 @@
 /*
  * Returns the number of bytes that would be written if the multi-
  * byte string mbs was converted to an OEM character string,
- * not counting the terminating null character.
+ * (smb_mbstooem) not counting the terminating null character.
  */
 size_t
 smb_sbequiv_strlen(const char *mbs)
 {
-	smb_wchar_t	wide_char;
 	size_t nbytes;
 	size_t len = 0;
 
 	while (*mbs) {
-		nbytes = smb_mbtowc(&wide_char, mbs, MTS_MB_CHAR_MAX);
+		nbytes = smb_mbtowc(NULL, mbs, MTS_MB_CHAR_MAX);
 		if (nbytes == ((size_t)-1))
 			return ((size_t)-1);
+		if (nbytes == 0)
+			break;
 
-		/*
-		 * Assume OEM characters are 1-byte (for now).
-		 * That's true for cp850, which is the only
-		 * codeset this currently supports.  See:
-		 * smb_oem.c : smb_oem_codeset
-		 */
-		++len;
+		if (nbytes == 1) {
+			/* ASCII */
+			len++;
+		} else if (nbytes < 8) {
+			/* Compute OEM length */
+			char mbsbuf[8];
+			uint8_t oembuf[8];
+			int oemlen;
+			(void) strlcpy(mbsbuf, mbs, nbytes+1);
+			oemlen = smb_mbstooem(oembuf, mbsbuf, 8);
+			if (oemlen < 0)
+				return ((size_t)-1);
+			len += oemlen;
+		} else {
+			return ((size_t)-1);
+		}
 
 		mbs += nbytes;
 	}
--- a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers	Fri Oct 02 15:45:00 2015 -0400
+++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers	Tue Nov 06 01:27:53 2018 -0500
@@ -270,6 +270,7 @@
 	smb_lookup_name;
 	smb_lookup_sid;
 	smb_match_netlogon_seqnum;
+	smb_mbstooem;
 	smb_mbstowcs;
 	smb_mbtowc;
 	smb_msgbuf_base;
@@ -308,6 +309,7 @@
 	smb_nic_getnum;
 	smb_nic_init;
 	smb_notify_dc_changed;
+	smb_oemtombs;
 	smb_priv_getbyname;
 	smb_priv_getbyvalue;
 	smb_priv_presentable_ids;
--- a/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c	Fri Oct 02 15:45:00 2015 -0400
+++ b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c	Tue Nov 06 01:27:53 2018 -0500
@@ -22,7 +22,7 @@
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
@@ -45,8 +45,7 @@
 static int mbc_marshal_put_short(mbuf_chain_t *mbc, uint16_t);
 static int mbc_marshal_put_long(mbuf_chain_t *mbc, uint32_t);
 static int mbc_marshal_put_long_long(mbuf_chain_t *mbc, uint64_t);
-static int mbc_marshal_put_oem_string(smb_request_t *, mbuf_chain_t *,
-    char *, int);
+static int mbc_marshal_put_oem_string(mbuf_chain_t *, char *, int);
 static int mbc_marshal_put_unicode_string(mbuf_chain_t *, char *, int);
 static int mbc_marshal_put_uio(mbuf_chain_t *, struct uio *);
 static int mbc_marshal_put_mbufs(mbuf_chain_t *mbc, mbuf_t *m);
@@ -310,7 +309,7 @@
 				goto unicode_translation;
 			/* FALLTHROUGH */
 
-		case 's':	/* OEM string */
+		case 's':	/* get OEM string */
 oem_conversion:
 			ASSERT(sr != NULL);
 			charpp = va_arg(ap, char **);
@@ -321,14 +320,12 @@
 				return (-1);
 			break;
 
-		case 'U': /* Convert from unicode */
+		case 'U':	/* get UTF-16 string */
 unicode_translation:
 			ASSERT(sr != 0);
 			charpp = va_arg(ap, char **);
 			if (!repc_specified)
 				repc = 0;
-			if (mbc->chain_offset & 1)
-				mbc->chain_offset++;
 			if (mbc_marshal_get_unicode_string(sr,
 			    mbc, charpp, repc) != 0)
 				return (-1);
@@ -519,7 +516,7 @@
 int
 smb_mbc_vencodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
 {
-	char 		*charp;
+	char		*charp;
 	uint8_t		*cvalp;
 	timestruc_t	*tvp;
 	smb_vdb_t	*vdp;
@@ -680,20 +677,18 @@
 				goto unicode_translation;
 			/* FALLTHROUGH */
 
-		case 's':	/* OEM string */
+		case 's':	/* put OEM string */
 oem_conversion:
 			charp = va_arg(ap, char *);
 			if (!repc_specified)
 				repc = 0;
-			if (mbc_marshal_put_oem_string(sr, mbc,
+			if (mbc_marshal_put_oem_string(mbc,
 			    charp, repc) != 0)
 				return (DECODE_NO_MORE_DATA);
 			break;
 
-		case 'U': /* Convert to unicode, align to word boundary */
+		case 'U':	/* put UTF-16 string */
 unicode_translation:
-			if (mbc->chain_offset & 1)
-				mbc->chain_offset++;
 			charp = va_arg(ap, char *);
 			if (!repc_specified)
 				repc = 0;
@@ -1071,91 +1066,147 @@
 /*
  * Marshal a UTF-8 string (str) into mbc, converting to OEM codeset.
  * Also write a null unless the repc count limits the length we put.
+ * When (repc > 0) the length we marshal must be exactly repc, and
+ * truncate or pad the mbc data as necessary.
+ * See also: msgbuf_put_oem_string
  */
 static int
-mbc_marshal_put_oem_string(
-    smb_request_t *sr,
-    mbuf_chain_t *mbc,
-    char *str,
-    int repc)
+mbc_marshal_put_oem_string(mbuf_chain_t *mbc, char *mbs, int repc)
 {
-	uint8_t		*s, *oembuf;
-	int		buflen;
+	uint8_t		*oembuf = NULL;
+	uint8_t		*s;
 	int		oemlen;
-	int		putlen;
+	int		rlen;
+	int		rc;
+
+	/*
+	 * Compute length of converted OEM string,
+	 * NOT including null terminator
+	 */
+	if ((oemlen = smb_sbequiv_strlen(mbs)) == -1)
+		return (DECODE_NO_MORE_DATA);
+
+	/*
+	 * If repc not specified, put whole string + NULL,
+	 * otherwise will truncate or pad as needed.
+	 */
+	if (repc <= 0)
+		repc = oemlen + 1;
+	if (mbc_marshal_make_room(mbc, repc))
+		return (DECODE_NO_MORE_DATA);
 
 	/*
-	 * First convert to OEM string.  The OEM string
-	 * will be no longer than the UTF-8 string.
+	 * Convert into a temporary buffer
+	 * Free oembuf before return.
 	 */
-	buflen = strlen(str) + 1;
-	oembuf = smb_srm_zalloc(sr, buflen);
-	oemlen = smb_mbstooem(oembuf, str, buflen);
-	if (oemlen == -1)
-		return (DECODE_NO_MORE_DATA);
-
-	/* null terminator */
-	if (oemlen < buflen)
-		oembuf[oemlen++] = '\0';
+	oembuf = smb_mem_zalloc(oemlen + 1);
+	ASSERT(oembuf != NULL);
+	rlen = smb_mbstooem(oembuf, mbs, oemlen);
+	if (rlen < 0) {
+		rc = DECODE_NO_MORE_DATA;
+		goto out;
+	}
+	if (rlen > oemlen)
+		rlen = oemlen;
+	oembuf[rlen] = '\0';
 
-	/* If specified, repc limits the length. */
-	putlen = oemlen;
-	if ((repc > 0) && (repc < putlen))
-		putlen = repc;
-
-	if (mbc_marshal_make_room(mbc, putlen))
-		return (DECODE_NO_MORE_DATA);
+	/*
+	 * Copy the converted string into the message,
+	 * truncated or paded as required.
+	 */
+	s = oembuf;
+	while (repc > 0) {
+		mbc_marshal_store_byte(mbc, *s);
+		if (*s != '\0')
+			s++;
+		repc--;
+	}
+	rc = 0;
 
-	s = oembuf;
-	while (putlen > 0) {
-		mbc_marshal_store_byte(mbc, *s);
-		s++;
-		putlen--;
-	}
-
-	return (0);
+out:
+	if (oembuf != NULL)
+		smb_mem_free(oembuf);
+	return (rc);
 }
 
 /*
  * Marshal a UTF-8 string (str) into mbc, converting to UTF-16.
- * Also write a UTF-16 null (2 bytes) unless the repc count
- * limits the length we put into the mbc.
+ * Also write a null unless the repc count limits the length.
+ * When (repc > 0) the length we marshal must be exactly repc,
+ * and truncate or pad the mbc data as necessary.
+ * See also: msgbuf_put_unicode_string
  */
 static int
-mbc_marshal_put_unicode_string(mbuf_chain_t *mbc, char *str, int repc)
+mbc_marshal_put_unicode_string(mbuf_chain_t *mbc, char *mbs, int repc)
 {
-	smb_wchar_t	wchar;
-	int		consumed;
-	int		length;
+	smb_wchar_t	*wcsbuf = NULL;
+	smb_wchar_t	*wp;
+	size_t		wcslen, wcsbytes;
+	size_t		rlen;
+	int		rc;
+
+	/* align to word boundary */
+	if (mbc->chain_offset & 1) {
+		if (mbc_marshal_make_room(mbc, 1))
+			return (DECODE_NO_MORE_DATA);
+		mbc_marshal_store_byte(mbc, 0);
+	}
 
-	if ((length = smb_wcequiv_strlen(str)) == -1)
+	/*
+	 * Compute length of converted UTF-16 string,
+	 * NOT including null terminator (in bytes).
+	 */
+	wcsbytes = smb_wcequiv_strlen(mbs);
+	if (wcsbytes == (size_t)-1)
+		return (DECODE_NO_MORE_DATA);
+
+	/*
+	 * If repc not specified, put whole string + NULL,
+	 * otherwise will truncate or pad as needed.
+	 */
+	if (repc <= 0)
+		repc = wcsbytes + 2;
+	if (mbc_marshal_make_room(mbc, repc))
 		return (DECODE_NO_MORE_DATA);
 
-	/* null terminator */
-	length += sizeof (smb_wchar_t);
-
-	/* If specified, repc limits the length. */
-	if ((repc > 0) && (repc < length))
-		length = repc;
+	/*
+	 * Convert into a temporary buffer
+	 * Free wcsbuf before return.
+	 */
+	wcslen = wcsbytes / 2;
+	wcsbuf = smb_mem_zalloc(wcsbytes + 2);
+	ASSERT(wcsbuf != NULL);
+	rlen = smb_mbstowcs(wcsbuf, mbs, wcslen);
+	if (rlen == (size_t)-1) {
+		rc = DECODE_NO_MORE_DATA;
+		goto out;
+	}
+	if (rlen > wcslen)
+		rlen = wcslen;
+	wcsbuf[rlen] = 0;
 
-	if (mbc_marshal_make_room(mbc, length))
-		return (DECODE_NO_MORE_DATA);
-	while (length > 0) {
-		consumed = smb_mbtowc(&wchar, str, MTS_MB_CHAR_MAX);
-		if (consumed == -1)
-			break;	/* Invalid sequence */
-		/*
-		 * Note that consumed will be 0 when the null terminator
-		 * is encountered and str will not be advanced beyond
-		 * that point. Length will continue to be decremented so
-		 * we won't get stuck here.
-		 */
-		str += consumed;
+	/*
+	 * Copy the converted string into the message,
+	 * truncated or paded as required.  Preserve
+	 * little-endian order while copying.
+	 */
+	wp = wcsbuf;
+	while (repc > 1) {
+		smb_wchar_t wchar = LE_IN16(wp);
 		mbc_marshal_store_byte(mbc, wchar);
 		mbc_marshal_store_byte(mbc, wchar >> 8);
-		length -= sizeof (smb_wchar_t);
+		if (wchar != 0)
+			wp++;
+		repc -= sizeof (smb_wchar_t);
 	}
-	return (0);
+	if (repc > 0)
+		mbc_marshal_store_byte(mbc, 0);
+
+	rc = 0;
+out:
+	if (wcsbuf != NULL)
+		smb_mem_free(wcsbuf);
+	return (rc);
 }
 
 static int /*ARGSUSED*/
@@ -1394,61 +1445,71 @@
  *
  * Decode an OEM string, returning its UTF-8 form in strpp,
  * allocated using smb_srm_zalloc (automatically freed).
- * If repc != 0, consume no more than repc bytes.
+ * If max_bytes != 0, consume at most max_bytes of the mbc.
+ * See also: msgbuf_get_oem_string
  */
 static int
-mbc_marshal_get_oem_string(
-    smb_request_t	*sr,
-    mbuf_chain_t	*mbc,
-    char		**strpp,
-    int			repc)
+mbc_marshal_get_oem_string(smb_request_t *sr,
+    mbuf_chain_t *mbc, char **strpp, int max_bytes)
 {
-	uint8_t		*ch, *rcvbuf;
-	char 		*mbsbuf;
-	int		mbslen, mbsmax;
-	int		buflen;
-	int		oemlen;
+	char		*mbs;
+	uint8_t		*oembuf = NULL;
+	int		oemlen, oemmax;
+	int		mbsmax;
+	int		rlen;
+	int		rc;
 
-	buflen = MALLOC_QUANTUM;
-	rcvbuf = smb_srm_zalloc(sr, buflen);
+	if (max_bytes == 0)
+		max_bytes = 0xffff;
 
-	if (repc == 0)
-		repc = 0xffff;
-
+	/*
+	 * Get the OtW data into a temporary buffer.
+	 * Free oembuf before return.
+	 */
 	oemlen = 0;
-	ch = rcvbuf;
+	oemmax = MALLOC_QUANTUM;
+	oembuf = smb_mem_alloc(oemmax);
 	for (;;) {
-		while (oemlen < buflen) {
-			if (repc-- <= 0) {
-				*ch++ = 0;
-				goto multibyte_encode;
-			}
-			if (MBC_ROOM_FOR(mbc, sizeof (char)) == 0) {
-				/* Data will never be available */
-				return (DECODE_NO_MORE_DATA);
-			}
-			if ((*ch++ = mbc_marshal_fetch_byte(mbc)) == 0)
-				goto multibyte_encode;
-			oemlen++;
+		uint8_t ch;
+
+		if (oemlen >= max_bytes)
+			break;
+		if ((oemlen + 2) >= oemmax) {
+			oemmax += MALLOC_QUANTUM;
+			oembuf = smb_mem_realloc(oembuf, oemmax);
+		}
+		if (mbc_marshal_get_char(mbc, &ch) != 0) {
+			rc = DECODE_NO_MORE_DATA;
+			goto out;
 		}
-		buflen += MALLOC_QUANTUM;
-		rcvbuf = smb_srm_rezalloc(sr, rcvbuf, buflen);
-		ch = rcvbuf + oemlen;
+		if (ch == 0)
+			break;
+		oembuf[oemlen++] = ch;
 	}
+	oembuf[oemlen] = '\0';
 
-multibyte_encode:
 	/*
-	 * UTF-8 encode the return string for internal system use.
-	 * Allocated size is worst-case: 3x larger than OEM.
+	 * Get the buffer we'll return and convert to UTF-8.
+	 * May take as much as double the space.
 	 */
-	mbsmax = (oemlen + 1) * MTS_MB_CHAR_MAX;
-	mbsbuf = smb_srm_zalloc(sr, mbsmax);
-	mbslen = smb_oemtombs(mbsbuf, rcvbuf, mbsmax);
-	if (mbslen == -1)
-		return (DECODE_NO_MORE_DATA);
+	mbsmax = oemlen * 2;
+	mbs = smb_srm_zalloc(sr, mbsmax + 1);
+	ASSERT(mbs != NULL);
+	rlen = smb_oemtombs(mbs, oembuf, mbsmax);
+	if (rlen < 0) {
+		rc = DECODE_NO_MORE_DATA;
+		goto out;
+	}
+	if (rlen > mbsmax)
+		rlen = mbsmax;
+	mbs[rlen] = '\0';
+	*strpp = mbs;
+	rc = 0;
 
-	*strpp = mbsbuf;
-	return (0);
+out:
+	if (oembuf != NULL)
+		smb_mem_free(oembuf);
+	return (rc);
 }
 
 /*
@@ -1456,46 +1517,83 @@
  *
  * Decode a UTF-16 string, returning its UTF-8 form in strpp,
  * allocated using smb_srm_zalloc (automatically freed).
- * If repc != 0, consume no more than repc bytes.
+ * If max_bytes != 0, consume at most max_bytes of the mbc.
+ * See also: msgbuf_get_unicode_string
  */
 static int
 mbc_marshal_get_unicode_string(smb_request_t *sr,
-    mbuf_chain_t *mbc, char **strpp, int repc)
+    mbuf_chain_t *mbc, char **strpp, int max_bytes)
 {
-	int		max;
-	uint16_t	wchar;
-	char		*ch;
-	int		emitted;
-	int		length = 0;
+	char		*mbs;
+	uint16_t	*wcsbuf = NULL;
+	int		wcslen;		// wchar count
+	int		wcsmax;		// byte count
+	size_t		mbsmax;
+	size_t		rlen;
+	int		rc;
+
+	if (max_bytes == 0)
+		max_bytes = 0xffff;
 
-	if (repc == 0)
-		repc = 0xffff;
+	/*
+	 * Unicode strings are always word aligned.
+	 */
+	if (mbc->chain_offset & 1) {
+		if (MBC_ROOM_FOR(mbc, sizeof (char)) == 0)
+			return (DECODE_NO_MORE_DATA);
+		mbc->chain_offset++;
+	}
 
-	max = MALLOC_QUANTUM;
-	*strpp = smb_srm_zalloc(sr, max);
-
-	ch = *strpp;
+	/*
+	 * Get the OtW data into a temporary buffer.
+	 * Free wcsbuf before return.
+	 */
+	wcslen = 0;
+	wcsmax = MALLOC_QUANTUM;
+	wcsbuf = smb_mem_alloc(wcsmax);
 	for (;;) {
-		while ((length + MTS_MB_CHAR_MAX) < max) {
-			if (repc <= 0)
-				goto done;
-			repc -= 2;
+		uint16_t	wchar;
 
-			if (mbc_marshal_get_short(mbc, &wchar) != 0)
-				return (DECODE_NO_MORE_DATA);
-
-			if (wchar == 0)	goto done;
+		if ((wcslen * 2) >= max_bytes)
+			break;
+		if (((wcslen * 2) + 4) >= wcsmax) {
+			wcsmax += MALLOC_QUANTUM;
+			wcsbuf = smb_mem_realloc(wcsbuf, wcsmax);
+		}
+		if (mbc_marshal_get_short(mbc, &wchar) != 0) {
+			rc = DECODE_NO_MORE_DATA;
+			goto out;
+		}
+		if (wchar == 0)
+			break;
+		/* Keep in little-endian form. */
+		LE_OUT16(wcsbuf + wcslen, wchar);
+		wcslen++;
+	}
+	wcsbuf[wcslen] = 0;
 
-			emitted = smb_wctomb(ch, wchar);
-			length += emitted;
-			ch += emitted;
-		}
-		max += MALLOC_QUANTUM;
-		*strpp = smb_srm_rezalloc(sr, *strpp, max);
-		ch = *strpp + length;
+	/*
+	 * Get the buffer we'll return and convert to UTF-8.
+	 * May take as much 4X number of wide chars.
+	 */
+	mbsmax = wcslen * MTS_MB_CUR_MAX;
+	mbs = smb_srm_zalloc(sr, mbsmax + 1);
+	ASSERT(mbs != NULL);
+	rlen = smb_wcstombs(mbs, wcsbuf, mbsmax);
+	if (rlen == (size_t)-1) {
+		rc = DECODE_NO_MORE_DATA;
+		goto out;
 	}
-done:	*ch = 0;
-	return (0);
+	if (rlen > mbsmax)
+		rlen = mbsmax;
+	mbs[rlen] = '\0';
+	*strpp = mbs;
+	rc = 0;
+
+out:
+	if (wcsbuf != NULL)
+		smb_mem_free(wcsbuf);
+	return (rc);
 }
 
 static int /*ARGSUSED*/
--- a/usr/src/uts/common/fs/smbsrv/smb_vops.c	Fri Oct 02 15:45:00 2015 -0400
+++ b/usr/src/uts/common/fs/smbsrv/smb_vops.c	Tue Nov 06 01:27:53 2018 -0500
@@ -1587,7 +1587,7 @@
 {
 	int v4_idx, numbytes, inc;
 	int space_left = buflen - 1; /* one byte reserved for null */
-	smb_wchar_t wc;
+	uint32_t wc;
 	char mbstring[MTS_MB_CHAR_MAX];
 	char *p, *src = name, *dst = buf;
 
@@ -1644,7 +1644,7 @@
 {
 	int v5_idx, numbytes;
 	int space_left = buflen - 1; /* one byte reserved for null */
-	smb_wchar_t wc;
+	uint32_t wc;
 	char mbstring[MTS_MB_CHAR_MAX];
 	char *src = name, *dst = buf;
 
--- a/usr/src/uts/common/smbsrv/string.h	Fri Oct 02 15:45:00 2015 -0400
+++ b/usr/src/uts/common/smbsrv/string.h	Tue Nov 06 01:27:53 2018 -0500
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2017 by Delphix. All rights reserved.
  */
 
@@ -85,7 +85,7 @@
 /*
  * Maximum number of bytes per multi-byte character.
  */
-#define	MTS_MB_CUR_MAX		3
+#define	MTS_MB_CUR_MAX		4
 #define	MTS_MB_CHAR_MAX		MTS_MB_CUR_MAX
 
 typedef	uint16_t smb_wchar_t;
@@ -118,8 +118,8 @@
 
 int smb_isupper(int);
 int smb_islower(int);
-int smb_toupper(int);
-int smb_tolower(int);
+uint32_t smb_toupper(uint32_t);
+uint32_t smb_tolower(uint32_t);
 char *smb_strupr(char *);
 char *smb_strlwr(char *);
 int smb_isstrupr(const char *);
@@ -130,8 +130,8 @@
 
 size_t smb_mbstowcs(smb_wchar_t *, const char *, size_t);
 size_t smb_wcstombs(char *, const smb_wchar_t *, size_t);
-int smb_mbtowc(smb_wchar_t *, const char *, size_t);
-int smb_wctomb(char *, smb_wchar_t);
+int smb_mbtowc(uint32_t *, const char *, size_t);
+int smb_wctomb(char *, uint32_t);
 
 size_t smb_wcequiv_strlen(const char *);
 size_t smb_sbequiv_strlen(const char *);
@@ -139,9 +139,6 @@
 int smb_oemtombs(char *, const uint8_t *, int);
 int smb_mbstooem(uint8_t *, const char *, int);
 
-int smb_stombs(char *, char *, int);
-int smb_mbstos(char *, const char *);
-
 size_t ucstooem(char *, const smb_wchar_t *, size_t, uint32_t);
 size_t oemtoucs(smb_wchar_t *, const char *, size_t, uint32_t);