summaryrefslogtreecommitdiff
path: root/usr/src/uts
diff options
context:
space:
mode:
authorthurlow <none@none>2008-02-13 19:51:22 -0800
committerthurlow <none@none>2008-02-13 19:51:22 -0800
commit4bff34e37def8a90f9194d81bc345c52ba20086a (patch)
tree7bf2710d9da099e3b07fea38e12788bfd565f3c5 /usr/src/uts
parenta916d99c7b27a531bf37c57f83b0b74120fd05bb (diff)
downloadillumos-joyent-4bff34e37def8a90f9194d81bc345c52ba20086a.tar.gz
PSARC 2005/695 CIFS Client on Solaris
PSARC 2007/303 pam_smb_login PSARC 2008/073 CIFS Client on Solaris - Updates 6651904 CIFS Client - PSARC 2005/695
Diffstat (limited to 'usr/src/uts')
-rw-r--r--usr/src/uts/common/Makefile.files8
-rw-r--r--usr/src/uts/common/Makefile.rules20
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/COPYRIGHT31
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/CREDITS11
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/README49
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/nsmb.conf28
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/offsets.in134
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c1294
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h481
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_crypt.c288
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c938
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c1372
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_osdep.h102
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c391
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h66
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c1408
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h199
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c1783
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h149
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c1179
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.c138
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h99
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c1221
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h91
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c513
-rw-r--r--usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c990
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h136
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c288
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_io.c206
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c489
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h301
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_rwlock.c252
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c3147
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c368
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h260
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c1001
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c915
-rw-r--r--usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c2498
-rw-r--r--usr/src/uts/common/netsmb/mchain.h204
-rw-r--r--usr/src/uts/common/netsmb/netbios.h160
-rw-r--r--usr/src/uts/common/netsmb/smb.h1522
-rw-r--r--usr/src/uts/common/netsmb/smb_dev.h420
-rw-r--r--usr/src/uts/common/os/policy.c21
-rw-r--r--usr/src/uts/common/os/vfs_conf.c4
-rw-r--r--usr/src/uts/common/sys/fs/smbfs_mount.h109
-rw-r--r--usr/src/uts/common/sys/mntent.h3
-rw-r--r--usr/src/uts/common/sys/policy.h3
-rw-r--r--usr/src/uts/intel/Makefile6
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared2
-rw-r--r--usr/src/uts/intel/nsmb/Makefile108
-rw-r--r--usr/src/uts/intel/nsmb/ioc_check.ref92
-rw-r--r--usr/src/uts/intel/os/minor_perm1
-rw-r--r--usr/src/uts/intel/os/name_to_major1
-rw-r--r--usr/src/uts/intel/smbfs/Makefile98
-rw-r--r--usr/src/uts/sparc/Makefile10
-rw-r--r--usr/src/uts/sparc/Makefile.sparc.shared3
-rw-r--r--usr/src/uts/sparc/nsmb/Makefile135
-rw-r--r--usr/src/uts/sparc/nsmb/inc.flg40
-rw-r--r--usr/src/uts/sparc/nsmb/ioc_check.ref92
-rw-r--r--usr/src/uts/sparc/os/minor_perm1
-rw-r--r--usr/src/uts/sparc/os/name_to_major1
-rw-r--r--usr/src/uts/sparc/smbfs/Makefile93
-rw-r--r--usr/src/uts/sparc/smbfs/inc.flg42
63 files changed, 26004 insertions, 11 deletions
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index c732c44f3a..7c26fb1f1a 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -1043,6 +1043,14 @@ UFS_OBJS += ufs_alloc.o ufs_bmap.o ufs_dir.o ufs_xattr.o \
lufs_log.o lufs_map.o lufs_top.o lufs_debug.o
VSCAN_OBJS += vscan_drv.o vscan_svc.o vscan_door.o
+NSMB_OBJS += smb_conn.o smb_crypt.o smb_dev.o smb_iod.o \
+ smb_rq.o smb_smb.o smb_tran.o smb_trantcp.o \
+ smb_usr.o smb_subrs.o subr_mchain.o smb_pass.o
+
+SMBFS_OBJS += smbfs_vfsops.o smbfs_vnops.o smbfs_node.o \
+ smbfs_client.o smbfs_io.o smbfs_smb.o \
+ smbfs_subr.o smbfs_subr2.o smbfs_rwlock.o
+
#
# LVM modules
#
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index 208b098b53..8a5ebdfc87 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -262,6 +262,14 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/sharefs/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/smbclnt/netsmb/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/smbclnt/smbfs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/sockfs/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1239,6 +1247,18 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/proc/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/sharefs/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+NSMBLINT = -erroff=E_FUNC_RET_ALWAYS_IGNOR2 -erroff=E_FUNC_RET_MAYBE_IGNORED2
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/smbclnt/netsmb/%.c
+ @($(LHEAD) $(LINT.c) $(NSMBLINT) $< $(LTAIL))
+
+$(LINTS_DIR)/smbfs_smb.ln: $(UTSBASE)/common/fs/smbclnt/smbfs/smbfs_smb.c
+ @($(LHEAD) $(LINT.c) $(NSMBLINT) \
+ $(UTSBASE)/common/fs/smbclnt/smbfs/smbfs_smb.c $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/smbclnt/smbfs/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/sockfs/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/COPYRIGHT b/usr/src/uts/common/fs/smbclnt/netsmb/COPYRIGHT
new file mode 100644
index 0000000000..2e2fa13fc4
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/COPYRIGHT
@@ -0,0 +1,31 @@
+#ident "%Z%%M% %I% %E% SMI"
+
+ Copyright (c) 2000, 2001 Boris Popov
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ This product includes software developed by Boris Popov.
+ 4. Neither the name of the author nor the names of any co-contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/CREDITS b/usr/src/uts/common/fs/smbclnt/netsmb/CREDITS
new file mode 100644
index 0000000000..31dbb784d9
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/CREDITS
@@ -0,0 +1,11 @@
+#ident "%Z%%M% %I% %E% SMI"
+
+ In the development process next sources were used:
+
+Various documents from the Microsoft ftp site.
+HTML docs published by Thursby Software.
+
+Special thanks to the Samba team for permission to use their source
+code as reference.
+
+Author - Boris Popov <bp@butya.kz>, <bp@freebsd.org>
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/README b/usr/src/uts/common/fs/smbclnt/netsmb/README
new file mode 100644
index 0000000000..c90f56cf7d
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/README
@@ -0,0 +1,49 @@
+
+
+ SMB/CIFS protocol and SMB/CIFS file system implementation
+ for FreeBSD, version 1.4.
+
+ This is native SMB/CIFS filesystem (smbfs for short) for FreeBSD.
+It is a complete, kernel side implementation of SMB requester and filesystem.
+
+ Supportted platforms:
+ FreeBSD 4.X
+
+ FreeBSD-current kernel module is included in the base source
+ tree.
+
+ I'm would be very grateful for any feedback, bug reports etc.
+
+ Supported SMB servers:
+ Samba
+ Windows 95/98/ME/2000/NT4.0 (SPs 4, 5, 6)
+ IBM LanManager
+ NetApp
+
+ An updated versions of this package can be retrieved from ftp server:
+
+ ftp://ftp.butya.kz/pub/smbfs/smbfs.tar.gz
+
+Author: Boris Popov <bp@freebsd.org>
+
+================================================================
+
+Additional notes from Sun Microsystems:
+
+This code (the OpenSolaris CIFS client) is derived from the
+Apple Darwin project, smb-217.2 code drop. See the file
+COPYRIGHT for copyright and redistribution terms.
+
+The Darwin code was reorganized as follows for Solaris:
+
+Darwin location: Solaris location: (usr/src/...)
+kernel/netsmb uts/common/netsmb (exported headers)
+kernel/netsmb uts/common/fs/smbclnt/netsmb
+kernel/fs/smbfs uts/common/fs/smbclnt/smbfs
+include/netsmb lib/libsmb/netsmb
+lib/smb lib/libsmb/smb
+mount_smbfs cmd/fs.d/smbclnt/mount
+smbutil cmd/fs.d/smbclnt/smbutil
+idl_compiler cmd/idlgen
+
+[ ident "%Z%%M% %I% %E% SMI" ]
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/nsmb.conf b/usr/src/uts/common/fs/smbclnt/netsmb/nsmb.conf
new file mode 100644
index 0000000000..91cbaae871
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/nsmb.conf
@@ -0,0 +1,28 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+name="nsmb" parent="pseudo";
+
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in b/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in
new file mode 100644
index 0000000000..6c39fa5a22
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in
@@ -0,0 +1,134 @@
+\
+\ 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 2007 Sun Microsystems, Inc. All rights reserved.
+\ Use is subject to license terms.
+\
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+\
+\ offsets.in: input file for the ctfstabs program,
+\ used to generate ioc_check.h - which verifies
+\ invariance of our ioctl data structures across
+\ 32-bit and 64-bit ABIs.
+
+#ifndef _GENASSYM
+#define _GENASSYM
+#endif
+
+#include <sys/types.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/socket_impl.h>
+#include <netsmb/smb.h>
+#include <netsmb/netbios.h>
+#include <netsmb/smb_dev.h>
+
+sockaddr_any SIZEOF_SOCKADDR_ANY
+
+sockaddr_in SIZEOF_SOCKADDR_IN
+
+sockaddr_nb SIZEOF_SOCKADDR_NB
+
+smbioc_ossn SIZEOF_SMBIOC_OSSN
+ ioc_server
+ ioc_local
+ ioc_localcs
+ ioc_servercs
+ ioc_srvname
+ ioc_user
+ ioc_workgroup
+ ioc_password IOC_SSN_PASSWD
+ ioc_opt IOC_SSN_OPT
+ ioc_timeout
+ ioc_retrycount
+ ioc_owner IOC_SSN_OWNER
+ ioc_group IOC_SSN_GROUP
+ ioc_mode IOC_SSN_MODE
+ ioc_rights IOC_SSN_RIGHTS
+ ioc_intoklen
+ ioc_outtoklen
+ _ioc_intok
+ _ioc_outtok
+
+smbioc_oshare SIZEOF_SMBIOC_OSHARE
+ ioc_share
+ ioc_password IOC_SH_PASSWD
+ ioc_opt IOC_SH_OPT
+ ioc_stype
+ ioc_owner IOC_SH_OWNER
+ ioc_group IOC_SH_GROUP
+ ioc_mode IOC_SH_MODE
+ ioc_rights IOC_SH_RIGHTS
+
+smbioc_rq SIZEOF_SMBIOC_RQ
+ ioc_cmd
+ ioc_twc
+ ioc_tbc
+ ioc_rpbufsz
+ ioc_rwc
+ ioc_rbc
+ ioc_errclass IOC_RQ_ERRCLASS
+ ioc_serror IOC_RQ_SERROR
+ ioc_error IOC_RQ_ERROR
+ _ioc_twords
+ _ioc_tbytes
+ _ioc_rpbuf
+
+smbioc_t2rq SIZEOF_SMBIOC_T2RQ
+ ioc_setup
+ ioc_setupcnt
+ ioc_name
+ ioc_tparamcnt
+ ioc_tdatacnt
+ ioc_rparamcnt
+ ioc_rdatacnt
+ ioc_errclass IOC_T2_ERRCLASS
+ ioc_serror IOC_T2_SERROR
+ ioc_error IOC_T2_ERROR
+ ioc_rpflags2
+ _ioc_tparam
+ _ioc_tdata
+ _ioc_rparam
+ _ioc_rdata
+
+smbioc_flags SIZEOF_SMBIOC_FLAGS
+ ioc_level
+ ioc_mask
+ ioc_flags
+
+smbioc_lookup SIZEOF_SMBIOC_LOOKUP
+ ioc_level IOC_LOOK_LEVEL
+ ioc_flags IOC_LOOK_FLAGS
+ ioc_sh
+ ioc_ssn
+
+smbioc_rw SIZEOF_SMBIOC_RW
+ ioc_fh
+ ioc_cnt
+ _ioc_offset
+ _ioc_base
+
+smbioc_pk SIZEOF_SMBIOC_PK
+ pk_dom
+ pk_usr
+ pk_pass
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c
new file mode 100644
index 0000000000..b0b50b80e1
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c
@@ -0,0 +1,1294 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_conn.c,v 1.27.166.1 2005/05/27 02:35:29 lindak Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Connection engine.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kmem.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/vnode.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/socketvar.h>
+#include <sys/cred.h>
+#include <sys/cred_impl.h>
+#include <netinet/in.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <sys/cmn_err.h>
+#include <sys/thread.h>
+#include <sys/atomic.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#include <sys/smb_iconv.h>
+#else
+#include <netsmb/smb_osdep.h>
+#endif
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_tran.h>
+#include <netsmb/smb_pass.h>
+
+static struct smb_connobj smb_vclist;
+static uint_t smb_vcnext = 0; /* next unique id for VC */
+
+void smb_co_init(struct smb_connobj *cp, int level, char *objname);
+void smb_co_done(struct smb_connobj *cp);
+void smb_co_hold(struct smb_connobj *cp);
+void smb_co_rele(struct smb_connobj *cp);
+void smb_co_kill(struct smb_connobj *cp);
+
+#ifdef APPLE
+static void smb_sm_lockvclist(void);
+static void smb_sm_unlockvclist(void);
+#endif
+
+static void smb_vc_free(struct smb_connobj *cp);
+static void smb_vc_gone(struct smb_connobj *cp);
+
+static void smb_share_free(struct smb_connobj *cp);
+static void smb_share_gone(struct smb_connobj *cp);
+
+/* smb_dup_sockaddr moved to smb_tran.c */
+
+int
+smb_sm_init(void)
+{
+ smb_co_init(&smb_vclist, SMBL_SM, "smbsm");
+ return (0);
+}
+
+int
+smb_sm_idle(void)
+{
+ int error = 0;
+ SMB_CO_LOCK(&smb_vclist);
+ if (smb_vclist.co_usecount > 1) {
+ SMBSDEBUG("%d connections still active\n",
+ smb_vclist.co_usecount - 1);
+ error = EBUSY;
+ }
+ SMB_CO_UNLOCK(&smb_vclist);
+ return (error);
+}
+
+void
+smb_sm_done(void)
+{
+ /*
+ * XXX Q4BP why are we not iterating on smb_vclist here?
+ * Because the caller has just called smb_sm_idle() to
+ * make sure we have no VCs before calling this.
+ */
+ smb_co_done(&smb_vclist);
+}
+
+/*
+ * Find a VC identified by the info in vcspec,
+ * and return it with a "hold", but not locked.
+ */
+/*ARGSUSED*/
+static int
+smb_sm_lookupvc(
+ struct smb_vcspec *vcspec,
+ struct smb_cred *scred,
+ struct smb_vc **vcpp)
+{
+ struct smb_connobj *co;
+ struct smb_vc *vcp;
+ zoneid_t zoneid = getzoneid();
+
+ ASSERT(MUTEX_HELD(&smb_vclist.co_lock));
+
+ /* var, head, next_field */
+ SLIST_FOREACH(co, &smb_vclist.co_children, co_next) {
+ vcp = CPTOVC(co);
+
+ /*
+ * Some things we can check without
+ * holding the lock (those that are
+ * set at creation and never change).
+ */
+
+ /* VCs in other zones are invisibile. */
+ if (vcp->vc_zoneid != zoneid)
+ continue;
+
+ /* Also segregate by owner. */
+ if (vcp->vc_uid != vcspec->owner)
+ continue;
+
+ /* XXX: we ignore the group. Remove vc_gid? */
+
+ /* server */
+ if (smb_cmp_sockaddr(vcp->vc_paddr, vcspec->sap))
+ continue;
+
+ /* domain+user */
+ if (strcmp(vcp->vc_domain, vcspec->domain))
+ continue;
+ if (strcmp(vcp->vc_username, vcspec->username))
+ continue;
+
+ SMB_VC_LOCK(vcp);
+
+ /* No new references allowed when _GONE is set */
+ if (vcp->vc_flags & SMBV_GONE)
+ goto unlock_continue;
+
+ if (vcp->vc_vopt & SMBVOPT_PRIVATE)
+ goto unlock_continue;
+
+ found:
+ /*
+ * Success! (Found one we can use)
+ * Return with it held, unlocked.
+ * In-line smb_vc_hold here.
+ */
+ co->co_usecount++;
+ SMB_VC_UNLOCK(vcp);
+ *vcpp = vcp;
+ return (0);
+
+ unlock_continue:
+ SMB_VC_UNLOCK(vcp);
+ /* keep looking. */
+ }
+
+ return (ENOENT);
+}
+
+
+int
+smb_sm_negotiate(
+ struct smb_vcspec *vcspec,
+ struct smb_cred *scred,
+ struct smb_vc **vcpp)
+{
+ struct smb_vc *vcp;
+ clock_t tmo;
+ int created, error;
+
+top:
+ *vcpp = vcp = NULL;
+
+ SMB_CO_LOCK(&smb_vclist);
+ error = smb_sm_lookupvc(vcspec, scred, &vcp);
+ if (error) {
+ /* The VC was not found. Create? */
+ if ((vcspec->optflags & SMBVOPT_CREATE) == 0) {
+ SMB_CO_UNLOCK(&smb_vclist);
+ return (error);
+ }
+ error = smb_vc_create(vcspec, scred, &vcp);
+ if (error) {
+ /* Could not create? Unusual. */
+ SMB_CO_UNLOCK(&smb_vclist);
+ return (error);
+ }
+ /* Note: co_usecount == 1 */
+ created = 1;
+ } else
+ created = 0;
+ SMB_CO_UNLOCK(&smb_vclist);
+
+ if (created) {
+ /*
+ * We have a NEW VC, held, but not locked.
+ */
+
+ SMBIODEBUG("vc_state=%d\n", vcp->vc_state);
+ switch (vcp->vc_state) {
+
+ case SMBIOD_ST_NOTCONN:
+ (void) smb_vc_setup(vcspec, scred, vcp, 0);
+ vcp->vc_genid++;
+ /* XXX: Save credentials of caller here? */
+ vcp->vc_state = SMBIOD_ST_RECONNECT;
+ /* FALLTHROUGH */
+
+ case SMBIOD_ST_RECONNECT:
+ error = smb_iod_connect(vcp);
+ if (error)
+ break;
+ vcp->vc_state = SMBIOD_ST_TRANACTIVE;
+ /* FALLTHROUGH */
+
+ case SMBIOD_ST_TRANACTIVE:
+ /* XXX: Just pass vcspec instead? */
+ vcp->vc_intok = vcspec->tok;
+ vcp->vc_intoklen = vcspec->toklen;
+ error = smb_smb_negotiate(vcp, &vcp->vc_scred);
+ vcp->vc_intok = NULL;
+ vcp->vc_intoklen = 0;
+ if (error)
+ break;
+ vcp->vc_state = SMBIOD_ST_NEGOACTIVE;
+ /* FALLTHROUGH */
+
+ case SMBIOD_ST_NEGOACTIVE:
+ case SMBIOD_ST_SSNSETUP:
+ case SMBIOD_ST_VCACTIVE:
+ /* We can (re)use this VC. */
+ error = 0;
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ SMB_VC_LOCK(vcp);
+ cv_broadcast(&vcp->vc_statechg);
+ SMB_VC_UNLOCK(vcp);
+
+ } else {
+ /*
+ * Found an existing VC. Reuse it, but first,
+ * wait for authentication to finish, etc.
+ * Note: We hold a reference on the VC.
+ */
+ error = 0;
+ SMB_VC_LOCK(vcp);
+ while (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
+ tmo = lbolt + SEC_TO_TICK(5);
+ tmo = cv_timedwait_sig(&vcp->vc_statechg,
+ &vcp->vc_lock, tmo);
+ if (tmo == 0) {
+ error = EINTR;
+ break;
+ }
+ if (vcp->vc_flags & SMBV_GONE)
+ break;
+ }
+ SMB_VC_UNLOCK(vcp);
+
+ /* Interrupted? */
+ if (error)
+ goto out;
+
+ /*
+ * The other guy failed authentication,
+ * or otherwise gave up on this VC.
+ * Drop reference, start over.
+ */
+ if (vcp->vc_flags & SMBV_GONE) {
+ smb_vc_rele(vcp);
+ goto top;
+ }
+
+ ASSERT(vcp->vc_state == SMBIOD_ST_VCACTIVE);
+ /* Success! */
+ }
+
+out:
+ if (error) {
+ /*
+ * Undo the hold from lookupvc,
+ * or destroy if from vc_create.
+ */
+ smb_vc_rele(vcp);
+ } else {
+ /* Return it held. */
+ *vcpp = vcp;
+ }
+
+ return (error);
+}
+
+
+int
+smb_sm_ssnsetup(
+ struct smb_vcspec *vcspec,
+ struct smb_cred *scred,
+ struct smb_vc *vcp)
+{
+ int error;
+
+ /*
+ * We have a VC, held, but not locked.
+ *
+ * Code from smb_iod_ssnsetup,
+ * with lots of rework.
+ */
+
+ SMBIODEBUG("vc_state=%d\n", vcp->vc_state);
+ switch (vcp->vc_state) {
+
+ case SMBIOD_ST_NEGOACTIVE:
+ /*
+ * This is the state we normally find.
+ * Calling _setup AGAIN to update the
+ * flags, security info, etc.
+ */
+ error = smb_vc_setup(vcspec, scred, vcp, 1);
+ if (error)
+ break;
+ vcp->vc_state = SMBIOD_ST_SSNSETUP;
+ /* FALLTHROUGH */
+
+ case SMBIOD_ST_SSNSETUP:
+ /* XXX: Just pass vcspec instead? */
+ vcp->vc_intok = vcspec->tok;
+ vcp->vc_intoklen = vcspec->toklen;
+ error = smb_smb_ssnsetup(vcp, &vcp->vc_scred);
+ vcp->vc_intok = NULL;
+ vcp->vc_intoklen = 0;
+ if (error)
+ break;
+ /* OK, start the reader thread... */
+ error = smb_iod_create(vcp);
+ if (error)
+ break;
+ vcp->vc_state = SMBIOD_ST_VCACTIVE;
+ /* FALLTHROUGH */
+
+ case SMBIOD_ST_VCACTIVE:
+ /* We can (re)use this VC. */
+ error = 0;
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ SMB_VC_LOCK(vcp);
+ cv_broadcast(&vcp->vc_statechg);
+ SMB_VC_UNLOCK(vcp);
+
+ return (error);
+}
+
+int
+smb_sm_tcon(
+ struct smb_sharespec *shspec,
+ struct smb_cred *scred,
+ struct smb_vc *vcp,
+ struct smb_share **sspp)
+{
+ struct smb_share *ssp;
+ int error;
+
+ *sspp = ssp = NULL;
+
+ if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
+ /*
+ * The wait for vc_state in smb_sm_negotiate
+ * _should_ get us a VC in the right state.
+ */
+ SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state);
+ return (ENOTCONN);
+ }
+
+ SMB_VC_LOCK(vcp);
+ error = smb_vc_lookupshare(vcp, shspec, scred, &ssp);
+ if (error) {
+ /* The share was not found. Create? */
+ if ((shspec->optflags & SMBVOPT_CREATE) == 0) {
+ SMB_VC_UNLOCK(vcp);
+ return (error);
+ }
+ error = smb_share_create(vcp, shspec, scred, &ssp);
+ if (error) {
+ /* Could not create? Unusual. */
+ SMB_VC_UNLOCK(vcp);
+ return (error);
+ }
+ /* Note: co_usecount == 1 */
+ }
+ SMB_VC_UNLOCK(vcp);
+
+ /*
+ * We have a share, held, but not locked.
+ * Make it connected...
+ */
+ SMB_SS_LOCK(ssp);
+ if (!smb_share_valid(ssp))
+ error = smb_share_tcon(ssp);
+ SMB_SS_UNLOCK(ssp);
+
+ if (error) {
+ /*
+ * Undo hold from lookupshare,
+ * or destroy if from _create.
+ */
+ smb_share_rele(ssp);
+ } else {
+ /* Return it held. */
+ *sspp = ssp;
+ }
+
+ return (error);
+}
+
+/*
+ * Common code for connection object
+ */
+/*ARGSUSED*/
+void
+smb_co_init(struct smb_connobj *cp, int level, char *objname)
+{
+
+ mutex_init(&cp->co_lock, objname, MUTEX_DRIVER, NULL);
+
+ cp->co_level = level;
+ cp->co_usecount = 1;
+ SLIST_INIT(&cp->co_children);
+}
+
+/*
+ * Called just before free of an object
+ * of which smb_connobj is a part, i.e.
+ * _vc_free, _share_free, also sm_done.
+ */
+void
+smb_co_done(struct smb_connobj *cp)
+{
+ ASSERT(SLIST_EMPTY(&cp->co_children));
+ mutex_destroy(&cp->co_lock);
+}
+
+static void
+smb_co_addchild(
+ struct smb_connobj *parent,
+ struct smb_connobj *child)
+{
+
+ /*
+ * Set the child's pointer to the parent.
+ * No references yet, so no need to lock.
+ */
+ ASSERT(child->co_usecount == 1);
+ child->co_parent = parent;
+
+ /*
+ * Add the child to the parent's list of
+ * children, and in-line smb_co_hold
+ */
+ ASSERT(MUTEX_HELD(&parent->co_lock));
+ parent->co_usecount++;
+ SLIST_INSERT_HEAD(&parent->co_children, child, co_next);
+}
+
+void
+smb_co_hold(struct smb_connobj *cp)
+{
+ SMB_CO_LOCK(cp);
+ cp->co_usecount++;
+ SMB_CO_UNLOCK(cp);
+}
+
+/*
+ * Called via smb_vc_rele, smb_share_rele
+ */
+void
+smb_co_rele(struct smb_connobj *co)
+{
+ struct smb_connobj *parent;
+ int old_flags;
+
+ SMB_CO_LOCK(co);
+ if (co->co_usecount > 1) {
+ co->co_usecount--;
+ SMB_CO_UNLOCK(co);
+ return;
+ }
+ ASSERT(co->co_usecount == 1);
+ co->co_usecount = 0;
+
+ /*
+ * This list of children should be empty now.
+ * Check this while we're still linked, so
+ * we have a better chance of debugging.
+ */
+ ASSERT(SLIST_EMPTY(&co->co_children));
+
+ /*
+ * OK, this element is going away.
+ *
+ * We need to drop the lock on this CO so we can take the
+ * parent CO lock. The _GONE flag prevents this CO from
+ * getting new references before we can unlink it from the
+ * parent list.
+ *
+ * The _GONE flag is also used to ensure that the co_gone
+ * function is called only once. Note that smb_co_kill may
+ * do this before we get here. If we find that the _GONE
+ * flag was not already set, then call the co_gone hook
+ * (smb_share_gone, smb_vc_gone) which will disconnect
+ * the share or the VC, respectively.
+ *
+ * Note the old: smb_co_gone(co, scred);
+ * is now in-line here.
+ */
+ old_flags = co->co_flags;
+ co->co_flags |= SMBO_GONE;
+ SMB_CO_UNLOCK(co);
+
+ if ((old_flags & SMBO_GONE) == 0 && co->co_gone)
+ co->co_gone(co);
+
+ /*
+ * If we have a parent (only smb_vclist does not)
+ * then unlink from parent's list of children.
+ * We have the only reference to the child.
+ */
+ parent = co->co_parent;
+ if (parent) {
+ SMB_CO_LOCK(parent);
+ ASSERT(SLIST_FIRST(&parent->co_children));
+ if (SLIST_FIRST(&parent->co_children)) {
+ SLIST_REMOVE(&parent->co_children, co,
+ smb_connobj, co_next);
+ }
+ SMB_CO_UNLOCK(parent);
+ }
+
+ /*
+ * Now it's safe to free the CO
+ */
+ if (co->co_free) {
+ co->co_free(co);
+ }
+
+ /*
+ * Finally, if the CO had a parent, decrement
+ * the parent's hold count for the lost child.
+ */
+ if (parent) {
+ /*
+ * Recursive call here (easier for debugging).
+ * Can only go two levels.
+ */
+ smb_co_rele(parent);
+ }
+}
+
+/*
+ * Do just the first part of what co_gone does,
+ * i.e. tree disconnect, or disconnect a VC.
+ * This is used to forcibly close things.
+ */
+void
+smb_co_kill(struct smb_connobj *co)
+{
+ int old_flags;
+
+ SMB_CO_LOCK(co);
+ old_flags = co->co_flags;
+ co->co_flags |= SMBO_GONE;
+ SMB_CO_UNLOCK(co);
+
+ /*
+ * Do the same "call only once" logic here as in
+ * smb_co_rele, though it's probably not possible
+ * for this to be called after smb_co_rele.
+ */
+ if ((old_flags & SMBO_GONE) == 0 && co->co_gone)
+ co->co_gone(co);
+
+ /* XXX: Walk list of children and kill those too? */
+}
+
+
+/*
+ * Session implementation
+ */
+
+/*
+ * This sets the fields that are allowed to change
+ * when doing a reconnect. Many others are set in
+ * smb_vc_create and never change afterwards.
+ * Don't want domain or user to change here.
+ */
+int
+smb_vc_setup(struct smb_vcspec *vcspec, struct smb_cred *scred,
+ struct smb_vc *vcp, int is_ss)
+{
+ int error, minauth;
+
+ /* Just save all the SMBVOPT_ options. */
+ vcp->vc_vopt = vcspec->optflags;
+
+ /* Cleared if nego response shows antique server! */
+ vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
+
+ /* XXX: Odd place for this. */
+ if (vcspec->optflags & SMBVOPT_EXT_SEC)
+ vcp->vc_hflags2 |= SMB_FLAGS2_EXT_SEC;
+
+ if (is_ss) {
+ /* Called from smb_sm_ssnsetup */
+
+ if (vcspec->optflags & SMBVOPT_USE_KEYCHAIN) {
+ /*
+ * Get p/w hashes from the keychain.
+ * The password in vcspec->pass is
+ * fiction, so don't store it.
+ */
+ error = smb_pkey_getpwh(vcp, scred->vc_ucred);
+ return (error);
+ }
+
+ /*
+ * Note: this can be called more than once
+ * for a given vcp, so free the old strings.
+ */
+ SMB_STRFREE(vcp->vc_pass);
+
+ /*
+ * Don't store the cleartext password
+ * unless the minauth value was changed
+ * to allow use of cleartext passwords.
+ * (By default, this is not allowed.)
+ */
+ minauth = vcspec->optflags & SMBVOPT_MINAUTH;
+ if (minauth == SMBVOPT_MINAUTH_NONE)
+ vcp->vc_pass = smb_strdup(vcspec->pass);
+
+ /* Compute LM and NTLM hashes. */
+ smb_oldlm_hash(vcspec->pass, vcp->vc_lmhash);
+ smb_ntlmv1hash(vcspec->pass, vcp->vc_nthash);
+ }
+
+ /* Success! */
+ error = 0;
+ return (error);
+}
+
+/*ARGSUSED*/
+int
+smb_vc_create(struct smb_vcspec *vcspec,
+ struct smb_cred *scred, struct smb_vc **vcpp)
+{
+ static char objtype[] = "smb_vc";
+ struct smb_vc *vcp;
+ int error = 0;
+
+ ASSERT(MUTEX_HELD(&smb_vclist.co_lock));
+
+ /*
+ * Checks for valid uid/gid are now in
+ * smb_usr_ioc2vcspec, so at this point
+ * we know the user has right to create
+ * with the uid/gid in the vcspec.
+ */
+
+ vcp = kmem_zalloc(sizeof (struct smb_vc), KM_SLEEP);
+
+ smb_co_init(VCTOCP(vcp), SMBL_VC, objtype);
+ vcp->vc_co.co_free = smb_vc_free;
+ vcp->vc_co.co_gone = smb_vc_gone;
+
+ cv_init(&vcp->vc_statechg, objtype, CV_DRIVER, NULL);
+ sema_init(&vcp->vc_sendlock, 1, objtype, SEMA_DRIVER, NULL);
+ rw_init(&vcp->iod_rqlock, objtype, RW_DRIVER, NULL);
+ cv_init(&vcp->iod_exit, objtype, CV_DRIVER, NULL);
+
+ vcp->vc_number = atomic_inc_uint_nv(&smb_vcnext);
+ vcp->vc_state = SMBIOD_ST_NOTCONN;
+ vcp->vc_timo = SMB_DEFRQTIMO;
+ /*
+ * I think SMB_UID_UNKNOWN is not the correct
+ * initial value for vc_smbuid. See the long
+ * comment in smb_iod_sendrq()
+ */
+ vcp->vc_smbuid = SMB_UID_UNKNOWN; /* XXX should be zero */
+ vcp->vc_tdesc = &smb_tran_nbtcp_desc;
+
+ /*
+ * These identify the connection.
+ */
+ vcp->vc_zoneid = getzoneid();
+ vcp->vc_uid = vcspec->owner;
+ vcp->vc_grp = vcspec->group;
+ vcp->vc_mode = vcspec->rights & SMBM_MASK;
+
+ vcp->vc_domain = smb_strdup(vcspec->domain);
+ vcp->vc_username = smb_strdup(vcspec->username);
+ vcp->vc_srvname = smb_strdup(vcspec->srvname);
+ vcp->vc_paddr = smb_dup_sockaddr(vcspec->sap);
+ vcp->vc_laddr = smb_dup_sockaddr(vcspec->lap);
+
+#ifdef NOICONVSUPPORT
+ /*
+ * REVISIT
+ */
+ error = iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower);
+ if (error)
+ goto errout;
+
+ error = iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper);
+ if (error)
+ goto errout;
+
+ if (vcspec->servercs[0]) {
+
+ error = iconv_open(vcspec->servercs, vcspec->localcs,
+ &vcp->vc_toserver);
+ if (error)
+ goto errout;
+
+ error = iconv_open(vcspec->localcs, vcspec->servercs,
+ &vcp->vc_tolocal);
+ if (error)
+ goto errout;
+ }
+#endif /* NOICONVSUPPORT */
+
+ /* This fills in vcp->vc_tdata */
+ if ((error = SMB_TRAN_CREATE(vcp, curproc)) != 0)
+ goto errout;
+
+ /* Success! */
+ smb_co_addchild(&smb_vclist, VCTOCP(vcp));
+ *vcpp = vcp;
+ return (0);
+
+errout:
+ /*
+ * This will destroy the new vc.
+ * See: smb_vc_free
+ */
+ smb_vc_rele(vcp);
+ return (error);
+}
+
+void
+smb_vc_hold(struct smb_vc *vcp)
+{
+ smb_co_hold(VCTOCP(vcp));
+}
+
+void
+smb_vc_rele(struct smb_vc *vcp)
+{
+ smb_co_rele(VCTOCP(vcp));
+}
+
+void
+smb_vc_kill(struct smb_vc *vcp)
+{
+ smb_co_kill(VCTOCP(vcp));
+}
+
+/*
+ * Normally called via smb_vc_rele()
+ * after co_usecount drops to zero.
+ * Also called via: smb_vc_kill()
+ *
+ * Shutdown the VC to this server,
+ * invalidate shares linked with it.
+ */
+/*ARGSUSED*/
+static void
+smb_vc_gone(struct smb_connobj *cp)
+{
+ struct smb_vc *vcp = CPTOVC(cp);
+
+ /*
+ * Was smb_vc_disconnect(vcp);
+ */
+ smb_iod_disconnect(vcp);
+
+ /* Note: smb_iod_destroy in vc_free */
+}
+
+static void
+smb_vc_free(struct smb_connobj *cp)
+{
+ struct smb_vc *vcp = CPTOVC(cp);
+
+ /*
+ * The VC has no more references, so
+ * no locks should be needed here.
+ * Make sure the IOD is gone.
+ */
+ smb_iod_destroy(vcp);
+
+ if (vcp->vc_tdata)
+ SMB_TRAN_DONE(vcp, curproc);
+
+ SMB_STRFREE(vcp->vc_username);
+ SMB_STRFREE(vcp->vc_srvname);
+ SMB_STRFREE(vcp->vc_pass);
+ SMB_STRFREE(vcp->vc_domain);
+ if (vcp->vc_paddr) {
+ smb_free_sockaddr(vcp->vc_paddr);
+ vcp->vc_paddr = NULL;
+ }
+ if (vcp->vc_laddr) {
+ smb_free_sockaddr(vcp->vc_laddr);
+ vcp->vc_laddr = NULL;
+ }
+
+/*
+ * We are not using the iconv routines here. So commenting them for now.
+ * REVISIT.
+ */
+#ifdef NOTYETDEFINED
+ if (vcp->vc_tolower)
+ iconv_close(vcp->vc_tolower);
+ if (vcp->vc_toupper)
+ iconv_close(vcp->vc_toupper);
+ if (vcp->vc_tolocal)
+ iconv_close(vcp->vc_tolocal);
+ if (vcp->vc_toserver)
+ iconv_close(vcp->vc_toserver);
+#endif
+ if (vcp->vc_intok)
+ kmem_free(vcp->vc_intok, vcp->vc_intoklen);
+ if (vcp->vc_outtok)
+ kmem_free(vcp->vc_outtok, vcp->vc_outtoklen);
+ if (vcp->vc_negtok)
+ kmem_free(vcp->vc_negtok, vcp->vc_negtoklen);
+
+ cv_destroy(&vcp->iod_exit);
+ rw_destroy(&vcp->iod_rqlock);
+ sema_destroy(&vcp->vc_sendlock);
+ cv_destroy(&vcp->vc_statechg);
+ smb_co_done(VCTOCP(vcp));
+ kmem_free(vcp, sizeof (*vcp));
+}
+
+
+/*
+ * Lookup share in the given VC. Share referenced and locked on return.
+ * VC expected to be locked on entry and will be left locked on exit.
+ */
+/*ARGSUSED*/
+int
+smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *shspec,
+ struct smb_cred *scred, struct smb_share **sspp)
+{
+ struct smb_connobj *co;
+ struct smb_share *ssp = NULL;
+
+ ASSERT(MUTEX_HELD(&vcp->vc_lock));
+
+ *sspp = NULL;
+
+ /* var, head, next_field */
+ SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) {
+ ssp = CPTOSS(co);
+
+ /* No new refs if _GONE is set. */
+ if (ssp->ss_flags & SMBS_GONE)
+ continue;
+
+ /* This has a hold, so no need to lock it. */
+ if (strcmp(ssp->ss_name, shspec->name) == 0)
+ goto found;
+ }
+ return (ENOENT);
+
+found:
+ /* Return it with a hold. */
+ smb_share_hold(ssp);
+ *sspp = ssp;
+ return (0);
+}
+
+
+static char smb_emptypass[] = "";
+
+const char *
+smb_vc_getpass(struct smb_vc *vcp)
+{
+ if (vcp->vc_pass)
+ return (vcp->vc_pass);
+ return (smb_emptypass);
+}
+
+uint16_t
+smb_vc_nextmid(struct smb_vc *vcp)
+{
+ uint16_t r;
+
+ r = atomic_inc_16_nv(&vcp->vc_mid);
+ return (r);
+}
+
+/*
+ * Get a pointer to the IP address suitable for passing to Trusted
+ * Extensions find_tpc() routine. Used by smbfs_mount_label_policy().
+ * Compare this code to nfs_mount_label_policy() if problems arise.
+ * Without support for direct CIFS-over-TCP, we should always see
+ * an AF_NETBIOS sockaddr here.
+ */
+void *
+smb_vc_getipaddr(struct smb_vc *vcp, int *ipvers)
+{
+ switch (vcp->vc_paddr->sa_family) {
+ case AF_NETBIOS: {
+ struct sockaddr_nb *snb;
+
+ *ipvers = IPV4_VERSION;
+ /*LINTED*/
+ snb = (struct sockaddr_nb *)vcp->vc_paddr;
+ return ((void *)&snb->snb_ipaddr);
+ }
+ case AF_INET: {
+ struct sockaddr_in *sin;
+
+ *ipvers = IPV4_VERSION;
+ /*LINTED*/
+ sin = (struct sockaddr_in *)vcp->vc_paddr;
+ return ((void *)&sin->sin_addr);
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 *sin6;
+
+ *ipvers = IPV6_VERSION;
+ /*LINTED*/
+ sin6 = (struct sockaddr_in6 *)vcp->vc_paddr;
+ return ((void *)&sin6->sin6_addr);
+ }
+ default:
+ SMBSDEBUG("invalid address family %d\n",
+ vcp->vc_paddr->sa_family);
+ *ipvers = 0;
+ return (NULL);
+ }
+}
+
+/*
+ * Share implementation
+ */
+/*
+ * Allocate share structure and attach it to the given VC
+ * Connection expected to be locked on entry. Share will be returned
+ * in locked state.
+ */
+/*ARGSUSED*/
+int
+smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec,
+ struct smb_cred *scred, struct smb_share **sspp)
+{
+ static char objtype[] = "smb_ss";
+ struct smb_share *ssp;
+
+ ASSERT(MUTEX_HELD(&vcp->vc_lock));
+
+ ssp = kmem_zalloc(sizeof (struct smb_share), KM_SLEEP);
+ smb_co_init(SSTOCP(ssp), SMBL_SHARE, objtype);
+ ssp->ss_co.co_free = smb_share_free;
+ ssp->ss_co.co_gone = smb_share_gone;
+
+ ssp->ss_name = smb_strdup(shspec->name);
+ ssp->ss_mount = NULL;
+ if (shspec->pass && shspec->pass[0])
+ ssp->ss_pass = smb_strdup(shspec->pass);
+ ssp->ss_type = shspec->stype;
+ ssp->ss_tid = SMB_TID_UNKNOWN;
+ ssp->ss_mode = shspec->rights & SMBM_MASK;
+ ssp->ss_fsname = NULL;
+ smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp));
+ *sspp = ssp;
+
+ return (0);
+}
+
+/*
+ * Normally called via smb_share_rele()
+ * after co_usecount drops to zero.
+ */
+static void
+smb_share_free(struct smb_connobj *cp)
+{
+ struct smb_share *ssp = CPTOSS(cp);
+
+ SMB_STRFREE(ssp->ss_name);
+ SMB_STRFREE(ssp->ss_pass);
+ SMB_STRFREE(ssp->ss_fsname);
+ smb_co_done(SSTOCP(ssp));
+ kmem_free(ssp, sizeof (*ssp));
+}
+
+/*
+ * Normally called via smb_share_rele()
+ * after co_usecount drops to zero.
+ * Also called via: smb_share_kill()
+ */
+static void
+smb_share_gone(struct smb_connobj *cp)
+{
+ struct smb_cred scred;
+ struct smb_share *ssp = CPTOSS(cp);
+
+ smb_credinit(&scred, curproc, NULL);
+ smb_iod_shutdown_share(ssp);
+ smb_smb_treedisconnect(ssp, &scred);
+ smb_credrele(&scred);
+}
+
+void
+smb_share_hold(struct smb_share *ssp)
+{
+ smb_co_hold(SSTOCP(ssp));
+}
+
+void
+smb_share_rele(struct smb_share *ssp)
+{
+ smb_co_rele(SSTOCP(ssp));
+}
+
+void
+smb_share_kill(struct smb_share *ssp)
+{
+ smb_co_kill(SSTOCP(ssp));
+}
+
+
+void
+smb_share_invalidate(struct smb_share *ssp)
+{
+ ssp->ss_tid = SMB_TID_UNKNOWN;
+}
+
+/*
+ * Returns NON-zero if the share is valid.
+ * Called with the share locked.
+ */
+int
+smb_share_valid(struct smb_share *ssp)
+{
+ struct smb_vc *vcp = SSTOVC(ssp);
+
+ ASSERT(MUTEX_HELD(&ssp->ss_lock));
+
+ if ((ssp->ss_flags & SMBS_CONNECTED) == 0)
+ return (0);
+
+ if (ssp->ss_tid == SMB_TID_UNKNOWN) {
+ SMBIODEBUG("found TID unknown\n");
+ ssp->ss_flags &= ~SMBS_CONNECTED;
+ }
+
+ if (ssp->ss_vcgenid != vcp->vc_genid) {
+ SMBIODEBUG("wrong genid\n");
+ ssp->ss_flags &= ~SMBS_CONNECTED;
+ }
+
+ return (ssp->ss_flags & SMBS_CONNECTED);
+}
+
+/*
+ * Connect (or reconnect) a share object.
+ * Called with the share locked.
+ */
+int
+smb_share_tcon(struct smb_share *ssp)
+{
+ struct smb_vc *vcp = SSTOVC(ssp);
+ clock_t tmo;
+ int error;
+
+ ASSERT(MUTEX_HELD(&ssp->ss_lock));
+
+ if (ssp->ss_flags & SMBS_CONNECTED) {
+ SMBIODEBUG("alread connected?");
+ return (0);
+ }
+
+ /*
+ * Wait for completion of any state changes
+ * that might be underway.
+ */
+ while (ssp->ss_flags & SMBS_RECONNECTING) {
+ ssp->ss_conn_waiters++;
+ tmo = cv_wait_sig(&ssp->ss_conn_done, &ssp->ss_lock);
+ ssp->ss_conn_waiters--;
+ if (tmo == 0) {
+ /* Interrupt! */
+ return (EINTR);
+ }
+ }
+
+ /* Did someone else do it for us? */
+ if (ssp->ss_flags & SMBS_CONNECTED)
+ return (0);
+
+ /*
+ * OK, we'll do the work.
+ */
+ ssp->ss_flags |= SMBS_RECONNECTING;
+
+ /* Drop the lock while doing the call. */
+ SMB_SS_UNLOCK(ssp);
+ error = smb_smb_treeconnect(ssp, &vcp->vc_scred);
+ SMB_SS_LOCK(ssp);
+
+ if (!error)
+ ssp->ss_flags |= SMBS_CONNECTED;
+ ssp->ss_flags &= ~SMBS_RECONNECTING;
+
+ /* They can all go ahead! */
+ if (ssp->ss_conn_waiters)
+ cv_broadcast(&ssp->ss_conn_done);
+
+ return (error);
+}
+
+const char *
+smb_share_getpass(struct smb_share *ssp)
+{
+ struct smb_vc *vcp;
+
+ if (ssp->ss_pass)
+ return (ssp->ss_pass);
+ vcp = SSTOVC(ssp);
+ if (vcp->vc_pass)
+ return (vcp->vc_pass);
+ return (smb_emptypass);
+}
+
+int
+smb_share_count(void)
+{
+ struct smb_connobj *covc, *coss;
+ struct smb_vc *vcp;
+ zoneid_t zoneid = getzoneid();
+ int nshares = 0;
+
+ SMB_CO_LOCK(&smb_vclist);
+ SLIST_FOREACH(covc, &smb_vclist.co_children, co_next) {
+ vcp = CPTOVC(covc);
+
+ /* VCs in other zones are invisibile. */
+ if (vcp->vc_zoneid != zoneid)
+ continue;
+
+ SMB_VC_LOCK(vcp);
+
+ /* var, head, next_field */
+ SLIST_FOREACH(coss, &(VCTOCP(vcp)->co_children), co_next) {
+ nshares++;
+ }
+
+ SMB_VC_UNLOCK(vcp);
+ }
+ SMB_CO_UNLOCK(&smb_vclist);
+
+ return (nshares);
+}
+
+/*
+ * Solaris zones support
+ */
+/*ARGSUSED*/
+void
+lingering_vc(struct smb_vc *vc)
+{
+ /* good place for a breakpoint */
+ DEBUG_ENTER("lingering VC");
+}
+
+/*
+ * On zone shutdown, kill any IOD threads still running in this zone.
+ */
+/* ARGSUSED */
+void
+nsmb_zone_shutdown(zoneid_t zoneid, void *data)
+{
+ struct smb_connobj *co;
+ struct smb_vc *vcp;
+
+ SMB_CO_LOCK(&smb_vclist);
+ SLIST_FOREACH(co, &smb_vclist.co_children, co_next) {
+ vcp = CPTOVC(co);
+
+ if (vcp->vc_zoneid != zoneid)
+ continue;
+
+ /*
+ * This will close the connection, and
+ * cause the IOD thread to terminate.
+ */
+ smb_vc_kill(vcp);
+ }
+ SMB_CO_UNLOCK(&smb_vclist);
+}
+
+/*
+ * On zone destroy, kill any IOD threads and free all resources they used.
+ */
+/* ARGSUSED */
+void
+nsmb_zone_destroy(zoneid_t zoneid, void *data)
+{
+ struct smb_connobj *co;
+ struct smb_vc *vcp;
+
+ /*
+ * We will repeat what should have already happened
+ * in zone_shutdown to make things go away.
+ *
+ * There should have been an smb_vc_rele call
+ * by now for all VCs in the zone. If not,
+ * there's probably more we needed to do in
+ * the shutdown call.
+ */
+
+ SMB_CO_LOCK(&smb_vclist);
+
+ if (smb_vclist.co_usecount > 1) {
+ SMBERROR("%d connections still active\n",
+ smb_vclist.co_usecount - 1);
+ }
+
+ /* var, head, next_field */
+ SLIST_FOREACH(co, &smb_vclist.co_children, co_next) {
+ vcp = CPTOVC(co);
+
+ if (vcp->vc_zoneid != zoneid)
+ continue;
+
+ /* Debugging */
+ lingering_vc(vcp);
+ }
+
+ SMB_CO_UNLOCK(&smb_vclist);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h
new file mode 100644
index 0000000000..8211c01d1b
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h
@@ -0,0 +1,481 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_conn.h,v 1.32.42.1 2005/05/27 02:35:29 lindak Exp $
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SMB_CONN_H
+#define _SMB_CONN_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/t_lock.h>
+#include <sys/queue.h> /* for SLIST below */
+#include <sys/uio.h>
+#include <netsmb/smb_dev.h>
+
+#ifndef _KERNEL
+#error "Not _KERNEL?"
+#endif
+
+/*
+ * Credentials of user/process for processing in the connection procedures
+ */
+typedef struct smb_cred {
+ pid_t vc_pid;
+ cred_t *vc_ucred;
+} smb_cred_t;
+
+/*
+ * Common object flags
+ */
+#define SMBO_GONE 0x1000000
+
+/*
+ * Bits in vc_flags (a.k.a. vc_co.co_flags)
+ * Many of these were duplicates of SMBVOPT_ flags
+ * and we now keep those too instead of merging
+ * them into vc_flags.
+ */
+
+#define SMBV_LONGNAMES 0x0004 /* conn configured to use long names */
+#define SMBV_ENCRYPT 0x0008 /* server demands encrypted password */
+#define SMBV_WIN95 0x0010 /* used to apply bugfixes for this OS */
+#define SMBV_NT4 0x0020 /* used when NT4 issues invalid resp */
+#define SMBV_RECONNECTING 0x0040 /* conn in process of reconnection */
+/* 0x0200 unused - was SMBV_FAILED */
+#define SMBV_UNICODE 0x0400 /* conn configured to use Unicode */
+#define SMBV_EXT_SEC 0x0800 /* conn to use extended security */
+
+/*
+ * Note: the common "obj" level uses this GONE flag by
+ * the name SMBO_GONE. Keep this alias as a reminder.
+ */
+#define SMBV_GONE SMBO_GONE
+
+/*
+ * bits in smb_share ss_flags (a.k.a. ss_co.co_flags)
+ */
+#define SMBS_RECONNECTING 0x0002
+#define SMBS_CONNECTED 0x0004
+#define SMBS_TCON_WAIT 0x0008
+#define SMBS_1980 0x0010
+/*
+ * ^ This partition can't handle dates before 1980. It's probably a FAT
+ * partition but could be some other ancient FS type
+ */
+#define SMBS_RESUMEKEYS 0x0010 /* must use resume keys */
+/*
+ * Note: the common "obj" level uses this GONE flag by
+ * the name SMBO_GONE. Keep this alias as a reminder.
+ */
+#define SMBS_GONE SMBO_GONE
+
+/*
+ * Negotiated protocol parameters
+ */
+struct smb_sopt {
+ int sv_proto;
+ int16_t sv_tz; /* offset in min relative to UTC */
+ uint32_t sv_maxtx; /* maximum transmit buf size */
+ uchar_t sv_sm; /* security mode */
+ uint16_t sv_maxmux; /* max number of outstanding rq's */
+ uint16_t sv_maxvcs; /* max number of VCs */
+ uint16_t sv_rawmode;
+ uint32_t sv_maxraw; /* maximum raw-buffer size */
+ uint32_t sv_skey; /* session key */
+ uint32_t sv_caps; /* capabilites SMB_CAP_ */
+};
+typedef struct smb_sopt smb_sopt_t;
+
+/*
+ * network IO daemon states
+ * really connection states.
+ */
+enum smbiod_state {
+ SMBIOD_ST_NOTCONN, /* no connect request was made */
+ SMBIOD_ST_RECONNECT, /* a [re]connect attempt is in progress */
+ SMBIOD_ST_TRANACTIVE, /* transport level is up */
+ SMBIOD_ST_NEGOACTIVE, /* completed negotiation */
+ SMBIOD_ST_SSNSETUP, /* started (a) session setup */
+ SMBIOD_ST_VCACTIVE, /* session established */
+ SMBIOD_ST_DEAD /* connection broken, transport is down */
+};
+
+
+/*
+ * Info structures
+ */
+#define SMB_INFO_NONE 0
+#define SMB_INFO_VC 2
+#define SMB_INFO_SHARE 3
+
+struct smb_vc_info {
+ int itype;
+ int usecount;
+ uid_t uid; /* user id of connection */
+ gid_t gid; /* group of connection */
+ mode_t mode; /* access mode */
+ int flags;
+ enum smbiod_state iodstate;
+ struct smb_sopt sopt;
+ char srvname[SMB_MAXSRVNAMELEN+1];
+ char vcname[128];
+};
+typedef struct smb_vc_info smb_vc_info_t;
+
+struct smb_share_info {
+ int itype;
+ int usecount;
+ ushort_t tid; /* TID */
+ int type; /* share type */
+ uid_t uid; /* user id of connection */
+ gid_t gid; /* group of connection */
+ mode_t mode; /* access mode */
+ int flags;
+ char sname[128];
+};
+typedef struct smb_share_info smb_share_info_t;
+
+struct smb_rq;
+/* This declares struct smb_rqhead */
+TAILQ_HEAD(smb_rqhead, smb_rq);
+
+#define SMB_NBTIMO 15
+#define SMB_DEFRQTIMO 30 /* 30 for oplock revoke/writeback */
+#define SMBWRTTIMO 60
+#define SMBSSNSETUPTIMO 60
+#define SMBNOREPLYWAIT (0)
+
+#define SMB_DIALECT(vcp) ((vcp)->vc_sopt.sv_proto)
+
+/*
+ * Connection object
+ */
+
+#define SMB_CO_LOCK(cp) mutex_enter(&(cp)->co_lock)
+#define SMB_CO_UNLOCK(cp) mutex_exit(&(cp)->co_lock)
+
+/*
+ * Common part of smb_vc, smb_share
+ * Locking: co_lock protects most
+ * fields in this struct, except
+ * as noted below:
+ */
+struct smb_connobj {
+ kmutex_t co_lock;
+ int co_level; /* SMBL_ */
+ int co_flags;
+ int co_usecount;
+
+ /* Note: must lock co_parent before child. */
+ struct smb_connobj *co_parent;
+
+ /* this.co_lock protects the co_children list */
+ SLIST_HEAD(, smb_connobj) co_children;
+
+ /*
+ * Linkage in parent's list of children.
+ * Must hold parent.co_lock to traverse.
+ */
+ SLIST_ENTRY(smb_connobj) co_next;
+
+ /* These two are set only at creation. */
+ void (*co_gone)(struct smb_connobj *);
+ void (*co_free)(struct smb_connobj *);
+};
+typedef struct smb_connobj smb_connobj_t;
+
+/*
+ * Virtual Circuit (session) to a server.
+ * This is the most (over)complicated part of SMB protocol.
+ * For the user security level (usl), each session with different remote
+ * user name has its own VC.
+ * It is unclear however, should share security level (ssl) allow additional
+ * VCs, because user name is not used and can be the same. On other hand,
+ * multiple VCs allows us to create separate sessions to server on a per
+ * user basis.
+ */
+
+typedef struct smb_vc {
+ struct smb_connobj vc_co;
+ enum smbiod_state vc_state;
+ kcondvar_t vc_statechg;
+ ksema_t vc_sendlock;
+
+ zoneid_t vc_zoneid;
+ char *vc_srvname;
+ struct sockaddr *vc_paddr; /* server addr */
+ struct sockaddr *vc_laddr; /* local addr, if any */
+ char *vc_domain; /* domain that defines username */
+ char *vc_username;
+ char *vc_pass; /* password for usl case */
+ uchar_t vc_lmhash[SMB_PWH_MAX];
+ uchar_t vc_nthash[SMB_PWH_MAX];
+
+ uint_t vc_timo; /* default request timeout */
+ int vc_maxvcs; /* maximum number of VC per conn */
+
+ void *vc_tolower; /* local charset */
+ void *vc_toupper; /* local charset */
+ void *vc_toserver; /* local charset to server one */
+ void *vc_tolocal; /* server charset to local one */
+ int vc_number; /* number of this VC from client side */
+ int vc_genid; /* "generation ID" of this VC */
+ uid_t vc_uid; /* user id of connection */
+ gid_t vc_grp; /* group of connection */
+ mode_t vc_mode; /* access mode */
+ uint16_t vc_smbuid; /* auth. session ID from server */
+
+ uint8_t vc_hflags; /* or'ed with flags in the smb header */
+ uint16_t vc_hflags2; /* or'ed with flags in the smb header */
+ void *vc_tdata; /* transport control block */
+ struct smb_tran_desc *vc_tdesc;
+ int vc_chlen; /* actual challenge length */
+ uchar_t vc_challenge[SMB_MAXCHALLENGELEN];
+ uint16_t vc_mid; /* multiplex id */
+ int vc_vopt; /* local options SMBVOPT_ */
+ struct smb_sopt vc_sopt; /* server options */
+ struct smb_cred vc_scred; /* used in reconnect procedure */
+ int vc_txmax; /* max tx/rx packet size */
+ int vc_rxmax; /* max readx data size */
+ int vc_wxmax; /* max writex data size */
+
+ /* Authentication tokens */
+ size_t vc_intoklen;
+ caddr_t vc_intok;
+ size_t vc_outtoklen;
+ caddr_t vc_outtok;
+ size_t vc_negtoklen;
+ caddr_t vc_negtok;
+
+ /*
+ * These members used to be in struct smbiod,
+ * which has been eliminated.
+ */
+ krwlock_t iod_rqlock; /* iod_rqlist */
+ struct smb_rqhead iod_rqlist; /* list of outstanding reqs */
+ struct _kthread *iod_thr; /* the IOD (reader) thread */
+ kcondvar_t iod_exit; /* IOD thread termination */
+ int iod_flags; /* see SMBIOD_* below */
+ int iod_newrq; /* send needed (iod_rqlock) */
+ int iod_muxfull; /* maxmux limit reached */
+ uint_t iod_rqwaiting; /* count of waiting requests */
+} smb_vc_t;
+
+#define vc_lock vc_co.co_lock
+#define vc_flags vc_co.co_flags
+#define vc_maxmux vc_sopt.sv_maxmux
+
+#define SMB_VC_LOCK(vcp) mutex_enter(&(vcp)->vc_lock)
+#define SMB_VC_UNLOCK(vcp) mutex_exit(&(vcp)->vc_lock)
+
+#define SMB_UNICODE_STRINGS(vcp) ((vcp)->vc_hflags2 & SMB_FLAGS2_UNICODE)
+
+/* Bits in iod_flags */
+#define SMBIOD_RUNNING 0x0001
+#define SMBIOD_SHUTDOWN 0x0002
+
+/*
+ * smb_share structure describes connection to the given SMB share (tree).
+ * Connection to share is always built on top of the VC.
+ */
+
+typedef struct smb_share {
+ struct smb_connobj ss_co;
+ kcondvar_t ss_conn_done; /* wait for reconnect */
+ int ss_conn_waiters;
+ char *ss_name;
+ char *ss_pass; /* share password, can be null */
+ char *ss_fsname;
+ void *ss_mount; /* used for smb up/down */
+ uint16_t ss_tid; /* TID */
+ int ss_type; /* share type */
+ mode_t ss_mode; /* access mode */
+ int ss_vcgenid; /* check VC generation ID */
+ uint32_t ss_maxfilenamelen;
+ int ss_sopt; /* local options SMBSOPT_ */
+} smb_share_t;
+
+#define ss_lock ss_co.co_lock
+#define ss_flags ss_co.co_flags
+
+#define SMB_SS_LOCK(ssp) mutex_enter(&(ssp)->ss_lock)
+#define SMB_SS_UNLOCK(ssp) mutex_exit(&(ssp)->ss_lock)
+
+#define CPTOVC(cp) ((struct smb_vc *)(cp))
+#define VCTOCP(vcp) (&(vcp)->vc_co)
+
+#define CPTOSS(cp) ((struct smb_share *)(cp))
+#define SSTOVC(ssp) CPTOVC(((ssp)->ss_co.co_parent))
+#define SSTOCP(ssp) (&(ssp)->ss_co)
+
+/*
+ * This is used internally to pass all the info about
+ * some VC that an ioctl caller is looking for.
+ */
+struct smb_vcspec {
+ char *srvname;
+ struct sockaddr *sap;
+ struct sockaddr *lap;
+ int optflags;
+ char *domain;
+ char *username;
+ char *pass;
+ uid_t owner;
+ gid_t group;
+ mode_t mode;
+ mode_t rights;
+ char *localcs;
+ char *servercs;
+ size_t toklen;
+ caddr_t tok;
+};
+typedef struct smb_vcspec smb_vcspec_t;
+
+/*
+ * This is used internally to pass all the info about
+ * some share that an ioctl caller is looking for.
+ */
+struct smb_sharespec {
+ char *name;
+ char *pass;
+ mode_t mode;
+ mode_t rights;
+ uid_t owner;
+ gid_t group;
+ int stype;
+ int optflags;
+};
+typedef struct smb_sharespec smb_sharespec_t;
+
+
+/*
+ * Call-back operations vector, so the netsmb module
+ * can notify smbfs about events affecting mounts.
+ * Installed in netsmb after smbfs loads.
+ */
+/* #define NEED_SMBFS_CALLBACKS 1 */
+#ifdef NEED_SMBFS_CALLBACKS
+typedef struct smb_fscb {
+ void (*fscb_dead)(smb_share_t *);
+ void (*fscb_down)(smb_share_t *);
+ void (*fscb_up)(smb_share_t *);
+} smb_fscb_t;
+/* Install the above vector, or pass NULL to clear it. */
+int smb_fscb_set(smb_fscb_t *);
+#endif /* NEED_SMBFS_CALLBACKS */
+
+/*
+ * IOD functions
+ */
+int smb_iod_create(struct smb_vc *vcp);
+int smb_iod_destroy(struct smb_vc *vcp);
+int smb_iod_connect(struct smb_vc *vcp);
+int smb_iod_disconnect(struct smb_vc *vcp);
+int smb_iod_addrq(struct smb_rq *rqp);
+int smb_iod_multirq(struct smb_rq *rqp);
+int smb_iod_waitrq(struct smb_rq *rqp);
+int smb_iod_removerq(struct smb_rq *rqp);
+void smb_iod_shutdown_share(struct smb_share *ssp);
+void smb_iod_notify_down(struct smb_vc *vcp);
+void smb_iod_notify_up(struct smb_vc *vcp);
+
+/*
+ * Session level functions
+ */
+int smb_sm_init(void);
+int smb_sm_idle(void);
+void smb_sm_done(void);
+
+int smb_sm_negotiate(struct smb_vcspec *vcspec,
+ struct smb_cred *scred, struct smb_vc **vcpp);
+int smb_sm_ssnsetup(struct smb_vcspec *vcspec,
+ struct smb_cred *scred, struct smb_vc *vcp);
+int smb_sm_tcon(struct smb_sharespec *shspec, struct smb_cred *scred,
+ struct smb_vc *vcp, struct smb_share **sspp);
+
+/*
+ * VC level functions
+ */
+int smb_vc_setup(struct smb_vcspec *vcspec, struct smb_cred *scred,
+ struct smb_vc *vcp, int is_ss);
+int smb_vc_create(struct smb_vcspec *vcspec,
+ struct smb_cred *scred, struct smb_vc **vcpp);
+int smb_vc_negotiate(struct smb_vc *vcp, struct smb_cred *scred);
+int smb_vc_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred);
+void smb_vc_hold(struct smb_vc *vcp);
+void smb_vc_rele(struct smb_vc *vcp);
+void smb_vc_kill(struct smb_vc *vcp);
+int smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *shspec,
+ struct smb_cred *scred, struct smb_share **sspp);
+const char *smb_vc_getpass(struct smb_vc *vcp);
+uint16_t smb_vc_nextmid(struct smb_vc *vcp);
+void *smb_vc_getipaddr(struct smb_vc *vcp, int *ipvers);
+
+/*
+ * share level functions
+ */
+int smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec,
+ struct smb_cred *scred, struct smb_share **sspp);
+
+void smb_share_hold(struct smb_share *ssp);
+void smb_share_rele(struct smb_share *ssp);
+void smb_share_kill(struct smb_share *ssp);
+
+void smb_share_invalidate(struct smb_share *ssp);
+int smb_share_tcon(struct smb_share *ssp);
+int smb_share_valid(struct smb_share *ssp);
+const char *smb_share_getpass(struct smb_share *ssp);
+int smb_share_count(void);
+
+/*
+ * SMB protocol level functions
+ */
+int smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred);
+int smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred);
+int smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred);
+int smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred);
+int smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred);
+int smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo);
+#ifdef APPLE
+int smb_smb_checkdir(struct smb_share *ssp, void *dnp,
+ char *name, int nmlen, struct smb_cred *scred);
+#endif
+int smb_rwuio(struct smb_share *ssp, uint16_t fid, uio_rw_t rw,
+ uio_t *uiop, struct smb_cred *scred, int timo);
+
+#endif /* _SMB_CONN_H */
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_crypt.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_crypt.c
new file mode 100644
index 0000000000..64057080b9
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_crypt.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_crypt.c,v 1.13 2005/01/26 23:50:50 lindak Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/md4.h>
+#include <sys/md5.h>
+#include <sys/des.h>
+#include <sys/kmem.h>
+#include <sys/crypto/api.h>
+#include <sys/crypto/common.h>
+#include <sys/cmn_err.h>
+
+#include <netsmb/smb_osdep.h>
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_dev.h>
+
+static uchar_t N8[] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
+
+static void
+smb_E(const uchar_t *key, uchar_t *data, uchar_t *dest)
+{
+ int rv;
+ uchar_t kk[8];
+ crypto_mechanism_t mech;
+ crypto_data_t d1, d2;
+ crypto_key_t keyt;
+
+
+ bzero(&d1, sizeof (crypto_data_t));
+ bzero(&d2, sizeof (crypto_data_t));
+ /*
+ * 'Key' here is the username - 7-bytes. Convert that to
+ * to a 8-byte string.
+ */
+ kk[0] = key[0] & 0xfe;
+ kk[1] = key[0] << 7 | (key[1] >> 1 & 0xfe);
+ kk[2] = key[1] << 6 | (key[2] >> 2 & 0xfe);
+ kk[3] = key[2] << 5 | (key[3] >> 3 & 0xfe);
+ kk[4] = key[3] << 4 | (key[4] >> 4 & 0xfe);
+ kk[5] = key[4] << 3 | (key[5] >> 5 & 0xfe);
+ kk[6] = key[5] << 2 | (key[6] >> 6 & 0xfe);
+ kk[7] = key[6] << 1;
+
+ keyt.ck_format = CRYPTO_KEY_RAW;
+ keyt.ck_length = 8 * 8;
+ keyt.ck_data = (void *)kk;
+
+ d1.cd_format = CRYPTO_DATA_RAW;
+ d1.cd_length = 8;
+ d1.cd_offset = 0;
+ d1.cd_raw.iov_len = 8;
+ d1.cd_raw.iov_base = (void *)data;
+
+ d2.cd_format = CRYPTO_DATA_RAW;
+ d2.cd_length = 8;
+ d2.cd_offset = 0;
+ d2.cd_raw.iov_len = 8;
+ d2.cd_raw.iov_base = (void *)dest;
+
+ mech.cm_type = crypto_mech2id(SUN_CKM_DES_ECB);
+ if (mech.cm_type == CRYPTO_MECH_INVALID)
+ cmn_err(CE_NOTE, "Invalid algorithm\n");
+ mech.cm_param = NULL;
+ mech.cm_param_len = 0;
+
+ rv = crypto_encrypt(&mech, &d1, &keyt, NULL, &d2, NULL);
+ if (rv != CRYPTO_SUCCESS)
+ SMBSDEBUG("crypto_encrypt failed.\n");
+}
+
+/*
+ * Compute the LM hash, which is used to compute the LM response.
+ */
+void
+smb_oldlm_hash(const char *apwd, uchar_t *lmhash)
+{
+ uchar_t P14[14+1];
+
+ /* Convert apwd to upper case, zero extend. */
+ bzero(P14, sizeof (P14));
+ smb_toupper(apwd, (char *)P14, 14);
+
+ /*
+ * lmhash = concat(Ex(P14, N8), zeros(5));
+ */
+ bzero(lmhash, 21);
+ smb_E(P14, N8, lmhash);
+ smb_E(P14 + 7, N8, lmhash + 8);
+}
+
+/*
+ * Compute an LM or NTLM response given the LM or NTLM hash and a
+ * challenge. Note: This now replaces smb_ntlmresponse which
+ * used to compute a different hash and then do the same
+ * response computation as found here. Now that the hash
+ * is computed by the caller, this is used for both.
+ */
+int
+smb_lmresponse(const uchar_t *hash, uchar_t *C8, uchar_t *RN)
+{
+
+ smb_E(hash, C8, RN);
+ smb_E(hash + 7, C8, RN + 8);
+ smb_E(hash + 14, C8, RN + 16);
+
+ return (0);
+}
+
+/*
+ * Compute the NTLMv1 hash, which is used to compute both NTLMv1 and
+ * NTLMv2 responses.
+ */
+void
+smb_ntlmv1hash(const char *apwd, uchar_t *v1hash)
+{
+ u_int16_t *unipwd;
+ MD4_CTX *ctxp;
+ size_t alen, unilen;
+
+ alen = strlen(apwd);
+ unipwd = kmem_alloc(alen * 2, KM_SLEEP);
+ /*
+ * v1hash = concat(MD4(U(apwd)), zeros(5));
+ */
+ unilen = smb_strtouni(unipwd, apwd, alen, UCONV_IGNORE_NULL);
+
+ ctxp = kmem_alloc(sizeof (MD4_CTX), KM_SLEEP);
+ MD4Init(ctxp);
+ MD4Update(ctxp, unipwd, unilen);
+ bzero(v1hash, 21);
+ MD4Final(v1hash, ctxp);
+
+ kmem_free(ctxp, sizeof (MD4_CTX));
+ kmem_free(unipwd, alen * 2);
+}
+
+/*
+ * Note: smb_ntlmresponse() is gone.
+ * Use: smb_lmresponse() instead.
+ */
+
+static void
+HMACT64(const uchar_t *key, size_t key_len, const uchar_t *data,
+ size_t data_len, uchar_t *digest)
+{
+ MD5_CTX context;
+ uchar_t k_ipad[64]; /* inner padding - key XORd with ipad */
+ uchar_t k_opad[64]; /* outer padding - key XORd with opad */
+ int i;
+
+ /* if key is longer than 64 bytes use only the first 64 bytes */
+ if (key_len > 64)
+ key_len = 64;
+
+ /*
+ * The HMAC-MD5 (and HMACT64) transform looks like:
+ *
+ * MD5(K XOR opad, MD5(K XOR ipad, data))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and data is the data being protected.
+ */
+
+ /* start out by storing key in pads */
+ bzero(k_ipad, sizeof (k_ipad));
+ bzero(k_opad, sizeof (k_opad));
+ bcopy(key, k_ipad, key_len);
+ bcopy(key, k_opad, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i = 0; i < 64; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+
+ /*
+ * perform inner MD5
+ */
+ MD5Init(&context); /* init context for 1st pass */
+ MD5Update(&context, k_ipad, 64); /* start with inner pad */
+ MD5Update(&context, data, data_len); /* then data of datagram */
+ MD5Final(digest, &context); /* finish up 1st pass */
+
+ /*
+ * perform outer MD5
+ */
+ MD5Init(&context); /* init context for 2nd pass */
+ MD5Update(&context, k_opad, 64); /* start with outer pad */
+ MD5Update(&context, digest, 16); /* then results of 1st hash */
+ MD5Final(digest, &context); /* finish up 2nd pass */
+}
+
+/*
+ * Compute an NTLMv2 response given the 21 byte NTLM(v1) hash,
+ * the user name, the destination workgroup/domain name,
+ * a challenge, and the blob.
+ */
+int
+smb_ntlmv2response(const uchar_t *v1hash, const uchar_t *user,
+ const uchar_t *destination, uchar_t *C8, const uchar_t *blob,
+ size_t bloblen, uchar_t **RN, size_t *RNlen)
+{
+ u_int16_t *uniuser, *unidest;
+ size_t uniuserlen, unidestlen;
+ uchar_t v2hash[16];
+ size_t len;
+ size_t datalen;
+ uchar_t *data, *data1;
+ size_t v2resplen;
+ uchar_t *v2resp;
+
+ /*
+ * v2hash=HMACT64(v1hash, 16, concat(upcase(user), upcase(destination))
+ * We assume that user and destination are supplied to us as
+ * upper-case UTF-8.
+ */
+ len = strlen((char *)user);
+ uniuser = kmem_alloc(len * sizeof (u_int16_t) + 1, KM_SLEEP);
+ uniuserlen = smb_strtouni(uniuser, (char *)user, len,
+ UCONV_IGNORE_NULL);
+ len = strlen((char *)destination);
+ unidest = kmem_alloc(len * sizeof (u_int16_t) + 1, KM_SLEEP);
+ unidestlen = smb_strtouni(unidest, (char *)destination, len,
+ UCONV_IGNORE_NULL);
+ datalen = uniuserlen + unidestlen;
+ data = kmem_alloc(datalen, KM_SLEEP);
+ bcopy(uniuser, data, uniuserlen);
+ bcopy(unidest, data + uniuserlen, unidestlen);
+ kmem_free(uniuser, strlen((char *)user) * sizeof (u_int16_t) + 1);
+ kmem_free(unidest, len * sizeof (u_int16_t) + 1);
+ HMACT64(v1hash, 16, data, datalen, v2hash);
+ kmem_free(data, datalen);
+
+ datalen = 8 + bloblen;
+ data1 = kmem_alloc(datalen, KM_SLEEP);
+ bcopy(C8, data1, 8);
+ bcopy(blob, data1 + 8, bloblen);
+ v2resplen = 16 + bloblen;
+ v2resp = kmem_alloc(v2resplen, KM_SLEEP);
+ HMACT64(v2hash, 16, data1, datalen, v2resp);
+ kmem_free(data1, datalen);
+ bcopy(blob, v2resp + 16, bloblen);
+ *RN = v2resp;
+ *RNlen = v2resplen;
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c
new file mode 100644
index 0000000000..6e40480724
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c
@@ -0,0 +1,938 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_dev.c,v 1.21 2004/12/13 00:25:18 lindak Exp $
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/sysmacros.h>
+#include <sys/uio.h>
+#include <sys/buf.h>
+#include <sys/modctl.h>
+#include <sys/open.h>
+#include <sys/file.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/cmn_err.h>
+#include <sys/stat.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/policy.h>
+#include <sys/zone.h>
+#include <sys/pathname.h>
+#include <sys/mount.h>
+#include <sys/sdt.h>
+#include <fs/fs_subr.h>
+#include <sys/modctl.h>
+#include <sys/devops.h>
+#include <sys/thread.h>
+#include <sys/mkdev.h>
+#include <sys/types.h>
+#include <sys/zone.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#else
+#include <netsmb/smb_osdep.h>
+#endif
+
+#include <netsmb/mchain.h> /* for "htoles()" */
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_dev.h>
+#include <netsmb/smb_pass.h>
+
+/* for version checks */
+const uint32_t nsmb_version = NSMB_VERSION;
+
+/*
+ * Userland code loops through minor #s 0 to 1023, looking for one which opens.
+ * Intially we create minor 0 and leave it for anyone. Minor zero will never
+ * actually get used - opening triggers creation of another (but private) minor,
+ * which userland code will get to and mark busy.
+ */
+#define SMBMINORS 1024
+static void *statep;
+static major_t nsmb_major;
+static minor_t nsmb_minor = 1;
+
+#define NSMB_MAX_MINOR (1 << 8)
+#define NSMB_MIN_MINOR (NSMB_MAX_MINOR + 1)
+
+#define ILP32 1
+#define LP64 2
+
+static kmutex_t dev_lck;
+
+/* Zone support */
+zone_key_t nsmb_zone_key;
+extern void nsmb_zone_shutdown(zoneid_t zoneid, void *data);
+extern void nsmb_zone_destroy(zoneid_t zoneid, void *data);
+
+/*
+ * cb_ops device operations.
+ */
+static int nsmb_open(dev_t *devp, int flag, int otyp, cred_t *credp);
+static int nsmb_close(dev_t dev, int flag, int otyp, cred_t *credp);
+static int nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
+ cred_t *credp, int *rvalp);
+/* smbfs cb_ops */
+static struct cb_ops nsmb_cbops = {
+ nsmb_open, /* open */
+ nsmb_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ nsmb_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* prop_op */
+ NULL, /* stream */
+ D_MP, /* cb_flag */
+ CB_REV, /* rev */
+ nodev, /* int (*cb_aread)() */
+ nodev /* int (*cb_awrite)() */
+};
+
+/*
+ * Device options
+ */
+static int nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+static int nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
+ void *arg, void **result);
+
+static struct dev_ops nsmb_ops = {
+ DEVO_REV, /* devo_rev, */
+ 0, /* refcnt */
+ nsmb_getinfo, /* info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ nsmb_attach, /* attach */
+ nsmb_detach, /* detach */
+ nodev, /* reset */
+ &nsmb_cbops, /* driver ops - devctl interfaces */
+ NULL, /* bus operations */
+ NULL /* power */
+};
+
+/*
+ * Module linkage information.
+ */
+
+static struct modldrv nsmb_modldrv = {
+ &mod_driverops, /* Driver module */
+ "SMBFS network driver v" NSMB_VER_STR,
+ &nsmb_ops /* Driver ops */
+};
+
+static struct modlinkage nsmb_modlinkage = {
+ MODREV_1,
+ (void *)&nsmb_modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ int error;
+
+ ddi_soft_state_init(&statep, sizeof (smb_dev_t), 1);
+
+ /* Can initialize some mutexes also. */
+ mutex_init(&dev_lck, NULL, MUTEX_DRIVER, NULL);
+ /*
+ * Create a major name and number.
+ */
+ nsmb_major = ddi_name_to_major(NSMB_NAME);
+ nsmb_minor = 0;
+
+ /* Connection data structures. */
+ (void) smb_sm_init();
+
+ /* Initialize password Key chain DB. */
+ smb_pkey_init();
+
+ zone_key_create(&nsmb_zone_key, NULL, nsmb_zone_shutdown,
+ nsmb_zone_destroy);
+
+ /*
+ * Install the module. Do this after other init,
+ * to prevent entrances before we're ready.
+ */
+ if ((error = mod_install((&nsmb_modlinkage))) != 0) {
+
+ /* Same as 2nd half of _fini */
+ (void) zone_key_delete(nsmb_zone_key);
+ smb_pkey_fini();
+ smb_sm_done();
+ mutex_destroy(&dev_lck);
+ ddi_soft_state_fini(&statep);
+
+ return (error);
+ }
+
+ return (0);
+}
+
+int
+_fini(void)
+{
+ int status;
+
+ /*
+ * Prevent unload if we have active VCs
+ * or stored passwords
+ */
+ if ((status = smb_sm_idle()) != 0)
+ return (status);
+ if ((status = smb_pkey_idle()) != 0)
+ return (status);
+
+ /*
+ * Remove the module. Do this before destroying things,
+ * to prevent new entrances while we're destorying.
+ */
+ if ((status = mod_remove(&nsmb_modlinkage)) != 0) {
+ return (status);
+ }
+
+ (void) zone_key_delete(nsmb_zone_key);
+
+ /* Destroy password Key chain DB. */
+ smb_pkey_fini();
+
+ smb_sm_done();
+
+ mutex_destroy(&dev_lck);
+ ddi_soft_state_fini(&statep);
+
+ return (status);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&nsmb_modlinkage, modinfop));
+}
+
+/*ARGSUSED*/
+static int
+nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
+{
+ int ret = DDI_SUCCESS;
+
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = 0;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = 0;
+ break;
+ default:
+ ret = DDI_FAILURE;
+ }
+ return (ret);
+}
+
+static int
+nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ smb_dev_t *sdp;
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+ /*
+ * only one instance - but we clone using the open routine
+ */
+ if (ddi_get_instance(dip) > 0)
+ return (DDI_FAILURE);
+
+ mutex_enter(&dev_lck);
+
+ /*
+ * This is the Zero'th minor device which is created.
+ */
+ if (ddi_soft_state_zalloc(statep, 0) == DDI_FAILURE) {
+ cmn_err(CE_WARN, "nsmb_attach: soft state alloc");
+ goto attach_failed;
+ }
+ if (ddi_create_minor_node(dip, "nsmb", S_IFCHR, 0, DDI_PSEUDO,
+ NULL) == DDI_FAILURE) {
+ cmn_err(CE_WARN, "nsmb_attach: create minor");
+ goto attach_failed;
+ }
+ if ((sdp = ddi_get_soft_state(statep, 0)) == NULL) {
+ cmn_err(CE_WARN, "nsmb_attach: get soft state");
+ ddi_remove_minor_node(dip, NULL);
+ goto attach_failed;
+ }
+
+ /*
+ * Need to see if this field is required.
+ * REVISIT
+ */
+ sdp->smb_dip = dip;
+ sdp->sd_seq = 0;
+ sdp->sd_opened = 1;
+
+ mutex_exit(&dev_lck);
+ ddi_report_dev(dip);
+ return (DDI_SUCCESS);
+
+attach_failed:
+ ddi_soft_state_free(statep, 0);
+ mutex_exit(&dev_lck);
+ return (DDI_FAILURE);
+}
+
+/*ARGSUSED*/
+static int
+nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+ if (ddi_get_instance(dip) > 0)
+ return (DDI_FAILURE);
+
+ ddi_soft_state_free(statep, 0);
+ ddi_remove_minor_node(dip, NULL);
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+nsmb_ioctl(dev_t dev,
+ int cmd,
+ intptr_t arg,
+ int mode,
+ cred_t *credp,
+ int *rvalp)
+{
+ smb_dev_t *sdp;
+ struct smb_vc *vcp = NULL;
+ struct smb_share *ssp = NULL;
+ struct smb_cred scred;
+ int err, error;
+ uid_t uid;
+
+ /* Free any+all of these at end of switch. */
+ smbioc_lookup_t *sioc = NULL;
+ smbioc_rq_t *srq = NULL;
+ smbioc_rw_t *rwrq = NULL;
+ smbioc_t2rq_t *strq = NULL;
+ smbioc_pk_t *pk = NULL;
+
+ sdp = ddi_get_soft_state(statep, getminor(dev));
+ if (sdp == NULL) {
+ return (DDI_FAILURE);
+ }
+ if ((sdp->sd_flags & NSMBFL_OPEN) == 0) {
+ return (EBADF);
+ }
+
+ /*
+ * Dont give access if the zone id is not as the same as we
+ * set in the nsmb_open or dont belong to the global zone.
+ * Check if the user belongs to this zone..
+ */
+ if (sdp->zoneid != getzoneid())
+ return (EIO);
+ if (cmd != SMBIOC_TDIS &&
+ zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN)
+ return (EIO);
+
+
+ error = 0;
+ smb_credinit(&scred, curproc, credp);
+ switch (cmd) {
+ case SMBIOC_GETVERS:
+ ddi_copyout(&nsmb_version, (void *)arg,
+ sizeof (nsmb_version), mode);
+ break;
+
+ case SMBIOC_REQUEST:
+ if (sdp->sd_share == NULL) {
+ error = ENOTCONN;
+ break;
+ }
+ srq = kmem_alloc(sizeof (*srq), KM_SLEEP);
+ if (ddi_copyin((void *) arg, srq,
+ sizeof (*srq), mode)) {
+ error = EFAULT;
+ break;
+ }
+ error = smb_usr_simplerequest(sdp->sd_share,
+ srq, &scred);
+ ddi_copyout(srq, (void *)arg,
+ SMBIOC_RQ_COPYOUT_SIZE, mode);
+ break;
+
+ case SMBIOC_T2RQ:
+ if (sdp->sd_share == NULL) {
+ error = ENOTCONN;
+ break;
+ }
+ strq = kmem_alloc(sizeof (*strq), KM_SLEEP);
+ if (ddi_copyin((void *)arg, strq,
+ sizeof (*strq), mode)) {
+ error = EFAULT;
+ break;
+ }
+ error = smb_usr_t2request(sdp->sd_share, strq, &scred);
+ ddi_copyout(strq, (void *)arg,
+ SMBIOC_T2RQ_COPYOUT_SIZE, mode);
+ break;
+
+ case SMBIOC_READ:
+ case SMBIOC_WRITE:
+ if ((ssp = sdp->sd_share) == NULL) {
+ error = ENOTCONN;
+ break;
+ }
+ rwrq = kmem_alloc(sizeof (*rwrq), KM_SLEEP);
+ if (ddi_copyin((void *)arg, rwrq,
+ sizeof (*rwrq), mode)) {
+ error = EFAULT;
+ break;
+ }
+ error = smb_usr_rw(ssp, rwrq, cmd, &scred);
+ ddi_copyout(rwrq, (void *)arg,
+ SMBIOC_RW_COPYOUT_SIZE, mode);
+ break;
+
+ case SMBIOC_NEGOTIATE:
+ /* Should be no VC (and no share) */
+ if (sdp->sd_vc || sdp->sd_share) {
+ error = EISCONN;
+ break;
+ }
+ sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP);
+ if (ddi_copyin((void *)arg, sioc,
+ sizeof (*sioc), mode)) {
+ error = EFAULT;
+ break;
+ }
+ vcp = NULL;
+ ssp = NULL;
+ error = smb_usr_negotiate(sioc, &scred, &vcp);
+ if (error)
+ break;
+ if (vcp) {
+ /*
+ * The VC has a hold from _negotiate
+ * which we keep until nsmb_close().
+ */
+ sdp->sd_level = SMBL_VC;
+ sdp->sd_vc = vcp;
+ /*
+ * If we just created this VC, and
+ * this minor is doing the setup,
+ * keep track of that fact here.
+ */
+ if (vcp->vc_state < SMBIOD_ST_VCACTIVE)
+ sdp->sd_flags |= NSMBFL_NEWVC;
+
+ }
+ /*
+ * Copyout the "out token" (security blob).
+ *
+ * This code used to be near the end of
+ * smb_usr_negotiate(). Moved the copyout
+ * calls here so we know the "mode"
+ */
+ if (vcp->vc_outtok) {
+ /*
+ * Note: will copyout sioc below
+ * including sioc.vc_outtoklen,
+ * so we no longer put the length
+ * at the start of the outtok data.
+ */
+ sioc->ioc_ssn.ioc_outtoklen =
+ vcp->vc_outtoklen;
+ err = ddi_copyout(
+ vcp->vc_outtok,
+ sioc->ioc_ssn.ioc_outtok,
+ vcp->vc_outtoklen, mode);
+ if (err) {
+ error = EFAULT;
+ break;
+ }
+ /*
+ * Save this blob in vc_negtok.
+ * We need it in case we have to
+ * reconnect.
+ *
+ * Set vc_negtok = vc_outtok
+ * but free vc_negtok first.
+ */
+ if (vcp->vc_negtok) {
+ kmem_free(
+ vcp->vc_negtok,
+ vcp->vc_negtoklen);
+ vcp->vc_negtok = NULL;
+ vcp->vc_negtoklen = 0;
+ }
+ vcp->vc_negtok = vcp->vc_outtok;
+ vcp->vc_negtoklen = vcp->vc_outtoklen;
+ vcp->vc_outtok = NULL;
+ vcp->vc_outtoklen = 0;
+ }
+ /*
+ * Added copyout here of (almost)
+ * the whole struct, even though
+ * the lib only needs _outtoklen.
+ * We may put other things in this
+ * struct that user-land needs.
+ */
+ err = ddi_copyout(sioc, (void *)arg,
+ SMBIOC_LOOK_COPYOUT_SIZE, mode);
+ if (err)
+ error = EFAULT;
+ break;
+
+ case SMBIOC_SSNSETUP:
+ /* Must have a VC, but no share. */
+ if (sdp->sd_share) {
+ error = EISCONN;
+ break;
+ }
+ if (!sdp->sd_vc) {
+ error = ENOTCONN;
+ break;
+ }
+ sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP);
+ if (ddi_copyin((void *)arg, sioc,
+ sizeof (*sioc), mode)) {
+ error = EFAULT;
+ break;
+ }
+ vcp = sdp->sd_vc;
+ ssp = NULL;
+ error = smb_usr_ssnsetup(sioc, &scred, vcp);
+ if (error)
+ break;
+ /*
+ * If this minor has finished ssn setup,
+ * turn off the NEWVC flag, otherwise we
+ * will kill this VC when we close.
+ */
+ if (vcp->vc_state == SMBIOD_ST_VCACTIVE)
+ sdp->sd_flags &= ~NSMBFL_NEWVC;
+ /*
+ * Copyout the "out token" (security blob).
+ *
+ * This code used to be near the end of
+ * smb_usr_ssnsetup(). Moved the copyout
+ * calls here so we know the "mode"
+ */
+ if (vcp->vc_outtok) {
+ /*
+ * Note: will copyout sioc below
+ * including sioc.vc_outtoklen,
+ * so we no longer put the length
+ * at the start of the outtok data.
+ */
+ sioc->ioc_ssn.ioc_outtoklen =
+ vcp->vc_outtoklen;
+ err = ddi_copyout(
+ vcp->vc_outtok,
+ sioc->ioc_ssn.ioc_outtok,
+ vcp->vc_outtoklen, mode);
+ if (err) {
+ error = EFAULT;
+ break;
+ }
+ /*
+ * Done with vc_outtok. Similar,
+ * but NOT the same as after the
+ * smb_usr_negotiate call above.
+ */
+ kmem_free(
+ vcp->vc_outtok,
+ vcp->vc_outtoklen);
+ vcp->vc_outtok = NULL;
+ vcp->vc_outtoklen = 0;
+ }
+ /* Added copyout here... (see above) */
+ err = ddi_copyout(sioc, (void *)arg,
+ SMBIOC_LOOK_COPYOUT_SIZE, mode);
+ if (err)
+ error = EFAULT;
+ break;
+
+ case SMBIOC_TCON:
+ /* Must have a VC, but no share. */
+ if (sdp->sd_share) {
+ error = EISCONN;
+ break;
+ }
+ if (!sdp->sd_vc) {
+ error = ENOTCONN;
+ break;
+ }
+ sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP);
+ if (ddi_copyin((void *)arg, sioc,
+ sizeof (*sioc), mode)) {
+ error = EFAULT;
+ break;
+ }
+ vcp = sdp->sd_vc;
+ ssp = NULL;
+ error = smb_usr_tcon(sioc, &scred, vcp, &ssp);
+ if (error)
+ break;
+ if (ssp) {
+ /*
+ * The share has a hold from _tcon
+ * which we keep until nsmb_close()
+ * or the SMBIOC_TDIS below.
+ */
+ sdp->sd_share = ssp;
+ sdp->sd_level = SMBL_SHARE;
+ }
+ /* No need for copyout here. */
+ break;
+
+ case SMBIOC_TDIS:
+ if (sdp->sd_share == NULL) {
+ error = ENOTCONN;
+ break;
+ }
+ smb_share_rele(sdp->sd_share);
+ sdp->sd_share = NULL;
+ sdp->sd_level = SMBL_VC;
+ break;
+ case SMBIOC_FLAGS2:
+ if (sdp->sd_share == NULL) {
+ error = ENOTCONN;
+ break;
+ }
+ if (!sdp->sd_vc) {
+ error = ENOTCONN;
+ break;
+ }
+ vcp = sdp->sd_vc;
+ /*
+ * Return the flags2 value.
+ */
+ ddi_copyout(&vcp->vc_hflags2, (void *)arg,
+ sizeof (u_int16_t), mode);
+ break;
+
+ case SMBIOC_PK_ADD:
+ pk = kmem_alloc(sizeof (*pk), KM_SLEEP);
+ if (ddi_copyin((void *)arg, pk,
+ sizeof (*pk), mode)) {
+ error = EFAULT;
+ break;
+ }
+ error = smb_pkey_add(pk, credp);
+ break;
+
+ case SMBIOC_PK_DEL:
+ pk = kmem_alloc(sizeof (*pk), KM_SLEEP);
+ if (ddi_copyin((void *)arg, pk,
+ sizeof (*pk), mode)) {
+ error = EFAULT;
+ break;
+ }
+ error = smb_pkey_del(pk, credp);
+ break;
+
+ case SMBIOC_PK_CHK:
+ pk = kmem_alloc(sizeof (*pk), KM_SLEEP);
+ if (ddi_copyin((void *)arg, pk,
+ sizeof (*pk), mode)) {
+ error = EFAULT;
+ break;
+ }
+ error = smb_pkey_check(pk, credp);
+ /*
+ * Note: Intentionally DO NOT copyout
+ * the pasword here. It can only be
+ * retrieved by internal calls. This
+ * ioctl only tells the caller if the
+ * keychain entry exists.
+ */
+ break;
+
+ case SMBIOC_PK_DEL_OWNER:
+ uid = crgetruid(credp);
+ error = smb_pkey_deluid(uid, credp);
+ break;
+
+ case SMBIOC_PK_DEL_EVERYONE:
+ uid = (uid_t)-1;
+ error = smb_pkey_deluid(uid, credp);
+ break;
+
+ default:
+ error = ENODEV;
+ }
+
+ /*
+ * Let's just do all the kmem_free stuff HERE,
+ * instead of at every switch break.
+ */
+
+ /* SMBIOC_REQUEST */
+ if (srq)
+ kmem_free(srq, sizeof (*srq));
+
+ /* SMBIOC_T2RQ */
+ if (strq)
+ kmem_free(strq, sizeof (*strq));
+
+ /* SMBIOC_READ */
+ /* SMBIOC_WRITE */
+ if (rwrq)
+ kmem_free(rwrq, sizeof (*rwrq));
+
+ /* SMBIOC_NEGOTIATE */
+ /* SMBIOC_SSNSETUP */
+ /* SMBIOC_TCON */
+ if (sioc) {
+ /*
+ * This data structure may contain
+ * cleartext passwords, so zap it.
+ */
+ bzero(sioc, sizeof (*sioc));
+ kmem_free(sioc, sizeof (*sioc));
+ }
+
+ /* SMBIOC_PK_... */
+ if (pk) {
+ /*
+ * This data structure may contain
+ * cleartext passwords, so zap it.
+ */
+ bzero(pk, sizeof (*pk));
+ kmem_free(pk, sizeof (*pk));
+ }
+
+ smb_credrele(&scred);
+
+ return (error);
+}
+
+/*ARGSUSED*/
+static int
+nsmb_open(dev_t *dev, int flags, int otyp, cred_t *cr)
+{
+ major_t new_major;
+ smb_dev_t *sdp, *sdv;
+
+ mutex_enter(&dev_lck);
+ for (; ; ) {
+ minor_t start = nsmb_minor;
+ do {
+ if (nsmb_minor >= MAXMIN32) {
+ if (nsmb_major == getmajor(*dev))
+ nsmb_minor = NSMB_MIN_MINOR;
+ else
+ nsmb_minor = 0;
+ } else {
+ nsmb_minor++;
+ }
+ sdv = ddi_get_soft_state(statep, nsmb_minor);
+ } while ((sdv != NULL) && (nsmb_minor != start));
+ if (nsmb_minor == start) {
+ /*
+ * The condition we need to solve here is all the
+ * MAXMIN32(~262000) minors numbers are reached. We
+ * need to create a new major number.
+ * zfs uses getudev() to create a new major number.
+ */
+ if ((new_major = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN,
+ "nsmb: Can't get unique major "
+ "device number.");
+ mutex_exit(&dev_lck);
+ return (-1);
+ }
+ nsmb_major = new_major;
+ nsmb_minor = 0;
+ } else {
+ break;
+ }
+ }
+
+ /*
+ * This is called by mount or open call.
+ * The open() routine is passed a pointer to a device number so
+ * that the driver can change the minor number. This allows
+ * drivers to dynamically create minor instances of the dev-
+ * ice. An example of this might be a pseudo-terminal driver
+ * that creates a new pseudo-terminal whenever it is opened.
+ * A driver that chooses the minor number dynamically, normally
+ * creates only one minor device node in attach(9E) with
+ * ddi_create_minor_node(9F) then changes the minor number com-
+ * ponent of *devp using makedevice(9F) and getmajor(9F) The
+ * driver needs to keep track of available minor numbers inter-
+ * nally.
+ * Stuff the structure smb_dev.
+ * return.
+ */
+
+ if (ddi_soft_state_zalloc(statep, nsmb_minor) == DDI_FAILURE) {
+ mutex_exit(&dev_lck);
+ return (ENXIO);
+ }
+ if ((sdp = ddi_get_soft_state(statep, nsmb_minor)) == NULL) {
+ mutex_exit(&dev_lck);
+ return (ENXIO);
+ }
+
+ sdp->sd_opened = 1;
+ sdp->sd_seq = nsmb_minor;
+ sdp->smb_cred = cr;
+ sdp->sd_flags |= NSMBFL_OPEN;
+ sdp->zoneid = crgetzoneid(cr);
+ mutex_exit(&dev_lck);
+
+ *dev = makedevice(nsmb_major, nsmb_minor);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+nsmb_close(dev_t dev, int flags, int otyp, cred_t *cr)
+{
+ struct smb_vc *vcp;
+ struct smb_share *ssp;
+ struct smb_cred scred;
+ minor_t inst = getminor(dev);
+ smb_dev_t *sdp;
+
+ mutex_enter(&dev_lck);
+ /*
+ * 1. Check the validity of the minor number.
+ * 2. Release any shares/vc associated with the connection.
+ * 3. Can close the minor number.
+ * 4. Deallocate any resources allocated in open() call.
+ */
+ smb_credinit(&scred, curproc, cr);
+
+ sdp = ddi_get_soft_state(statep, inst);
+
+ /*
+ * time to call ddi_get_soft_state()
+ */
+ ssp = sdp->sd_share;
+ if (ssp != NULL)
+ smb_share_rele(ssp);
+ vcp = sdp->sd_vc;
+ if (vcp != NULL) {
+ /*
+ * If this dev minor was doing session setup
+ * and failed to authenticate (or whatever)
+ * then we need to "kill" the VC here so any
+ * other threads waiting for the VC setup to
+ * finish will drop their references.
+ */
+ if (sdp->sd_flags & NSMBFL_NEWVC)
+ smb_vc_kill(vcp);
+ smb_vc_rele(vcp);
+ }
+ smb_credrele(&scred);
+
+ /*
+ * Free the instance
+ */
+ ddi_soft_state_free(statep, inst);
+ mutex_exit(&dev_lck);
+ return (0);
+}
+
+int
+smb_dev2share(int fd, struct smb_share **sspp)
+{
+ register vnode_t *vp;
+ smb_dev_t *sdp;
+ struct smb_share *ssp;
+ dev_t dev;
+ file_t *fp;
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+ vp = fp->f_vnode;
+ dev = vp->v_rdev;
+ if (dev == NULL) {
+ releasef(fd);
+ return (EBADF);
+ }
+ sdp = ddi_get_soft_state(statep, getminor(dev));
+ if (sdp == NULL) {
+ releasef(fd);
+ return (DDI_FAILURE);
+ }
+ ssp = sdp->sd_share;
+ if (ssp == NULL) {
+ releasef(fd);
+ return (ENOTCONN);
+ }
+ /*
+ * The share is already locked and referenced by the TCON ioctl
+ * We NULL to hand off share to caller (mount)
+ * This allows further ioctls against connection, for instance
+ * another tree connect and mount, in the automounter case
+ *
+ * We're effectively giving our reference to the mount.
+ *
+ * XXX: I'm not sure I like this. I'd rather see the ioctl
+ * caller do something explicit to give up this reference,
+ * (i.e. SMBIOC_TDIS above) and increment the hold here.
+ */
+ sdp->sd_share = NULL;
+ releasef(fd);
+ *sspp = ssp;
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c
new file mode 100644
index 0000000000..69d3fe25c4
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c
@@ -0,0 +1,1372 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_iod.c,v 1.32 2005/02/12 00:17:09 lindak Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef DEBUG
+/* See sys/queue.h */
+#define QUEUEDEBUG 1
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/atomic.h>
+#include <sys/proc.h>
+#include <sys/thread.h>
+#include <sys/kmem.h>
+#include <sys/unistd.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/types.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/time.h>
+#include <sys/class.h>
+#include <sys/disp.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/sdt.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#else
+#include <netsmb/smb_osdep.h>
+#endif
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_rq.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_tran.h>
+#include <netsmb/smb_trantcp.h>
+
+#ifdef NEED_SMBFS_CALLBACKS
+/*
+ * This is set/cleared when smbfs loads/unloads
+ * No locks should be necessary, because smbfs
+ * can't unload until all the mounts are gone.
+ */
+static smb_fscb_t *fscb;
+int
+smb_fscb_set(smb_fscb_t *cb)
+{
+ fscb = cb;
+ return (0);
+}
+#endif /* NEED_SMBFS_CALLBACKS */
+
+static void smb_iod_sendall(struct smb_vc *);
+static void smb_iod_recvall(struct smb_vc *);
+static void smb_iod_main(struct smb_vc *);
+
+
+#define SMBIOD_SLEEP_TIMO 2
+#define SMBIOD_PING_TIMO 60 /* seconds */
+
+/*
+ * After this many seconds we want an unresponded-to request to trigger
+ * some sort of UE (dialogue). If the connection hasn't responded at all
+ * in this many seconds then the dialogue is of the "connection isn't
+ * responding would you like to force unmount" variety. If the connection
+ * has been responding (to other requests that is) then we need a dialogue
+ * of the "operation is still pending do you want to cancel it" variety.
+ * At present this latter dialogue does not exist so we have no UE and
+ * just keep waiting for the slow operation.
+ */
+#define SMBUETIMEOUT 8 /* seconds */
+
+
+/* Lock Held version of the next function. */
+static inline void
+smb_iod_rqprocessed_LH(
+ struct smb_rq *rqp,
+ int error,
+ int flags)
+{
+ rqp->sr_flags |= flags;
+ rqp->sr_lerror = error;
+ rqp->sr_rpgen++;
+ rqp->sr_state = SMBRQ_NOTIFIED;
+ cv_broadcast(&rqp->sr_cond);
+}
+
+static void
+smb_iod_rqprocessed(
+ struct smb_rq *rqp,
+ int error,
+ int flags)
+{
+
+ SMBRQ_LOCK(rqp);
+ smb_iod_rqprocessed_LH(rqp, error, flags);
+ SMBRQ_UNLOCK(rqp);
+}
+
+static void
+smb_iod_invrq(struct smb_vc *vcp)
+{
+ struct smb_rq *rqp;
+
+ /*
+ * Invalidate all outstanding requests for this connection
+ */
+ rw_enter(&vcp->iod_rqlock, RW_READER);
+ TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) {
+ smb_iod_rqprocessed(rqp, ENOTCONN, SMBR_RESTART);
+ }
+ rw_exit(&vcp->iod_rqlock);
+}
+
+#ifdef SMBTP_UPCALL
+static void
+smb_iod_sockwakeup(struct smb_vc *vcp)
+{
+ /* note: called from socket upcall... */
+}
+#endif
+
+/*
+ * Called after we fail to send or recv.
+ * Called with no locks held.
+ */
+static void
+smb_iod_dead(struct smb_vc *vcp)
+{
+
+ SMB_VC_LOCK(vcp);
+ vcp->vc_state = SMBIOD_ST_DEAD;
+ cv_broadcast(&vcp->vc_statechg);
+
+#ifdef NEED_SMBFS_CALLBACKS
+ if (fscb != NULL) {
+ struct smb_connobj *co;
+ /*
+ * Walk the share list, notify...
+ * Was: smbfs_dead(...share->ss_mount);
+ * XXX: Ok to hold vc_lock here?
+ * XXX: More to do here?
+ */
+ SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) {
+ /* smbfs_dead() */
+ fscb->fscb_dead(CPTOSS(co));
+ }
+ }
+#endif /* NEED_SMBFS_CALLBACKS */
+
+ SMB_VC_UNLOCK(vcp);
+
+ smb_iod_invrq(vcp);
+}
+
+int
+smb_iod_connect(struct smb_vc *vcp)
+{
+ struct proc *p = curproc;
+ int error;
+
+ if (vcp->vc_state != SMBIOD_ST_RECONNECT)
+ return (EINVAL);
+
+ if (vcp->vc_laddr) {
+ error = SMB_TRAN_BIND(vcp, vcp->vc_laddr, p);
+ if (error)
+ goto errout;
+ }
+
+#ifdef SMBTP_SELECTID
+ SMB_TRAN_SETPARAM(vcp, SMBTP_SELECTID, vcp);
+#endif
+#ifdef SMBTP_UPCALL
+ SMB_TRAN_SETPARAM(vcp, SMBTP_UPCALL, (void *)smb_iod_sockwakeup);
+#endif
+
+ error = SMB_TRAN_CONNECT(vcp, vcp->vc_paddr, p);
+ if (error) {
+ SMBIODEBUG("connection to %s error %d\n",
+ vcp->vc_srvname, error);
+ goto errout;
+ }
+
+ /* Success! */
+ return (0);
+
+errout:
+
+ return (error);
+}
+
+/*
+ * Called by smb_vc_rele, smb_vc_kill
+ * Make the connection go away, and
+ * the IOD (reader) thread too!
+ */
+int
+smb_iod_disconnect(struct smb_vc *vcp)
+{
+
+ /*
+ * Let's be safe here and avoid doing any
+ * call across the network while trying to
+ * shut things down. If we just disconnect,
+ * the server will take care of the logoff.
+ */
+#if 0
+ if (vcp->vc_state == SMBIOD_ST_VCACTIVE) {
+ smb_smb_ssnclose(vcp, &vcp->vc_scred);
+ vcp->vc_state = SMBIOD_ST_TRANACTIVE;
+ }
+ vcp->vc_smbuid = SMB_UID_UNKNOWN;
+#endif
+
+ /*
+ * Used to call smb_iod_closetran here,
+ * which did both disconnect and close.
+ * We now do the close in smb_vc_free,
+ * so we always have a valid vc_tdata.
+ * Now just send the disconnect here.
+ * Extra disconnect calls are ignored.
+ */
+ SMB_TRAN_DISCONNECT(vcp, curproc);
+
+ /*
+ * If we have an IOD, let it handle the
+ * state change when it receives the ACK
+ * from the disconnect we just sent.
+ * Otherwise set the state here, i.e.
+ * after failing session setup.
+ */
+ SMB_VC_LOCK(vcp);
+ if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
+ vcp->vc_state = SMBIOD_ST_DEAD;
+ cv_broadcast(&vcp->vc_statechg);
+ }
+ SMB_VC_UNLOCK(vcp);
+
+ return (0);
+}
+
+/*
+ * Send one request.
+ *
+ * Called by _addrq (for internal requests)
+ * and by _sendall (via _addrq, _waitrq)
+ */
+static int
+smb_iod_sendrq(struct smb_rq *rqp)
+{
+ struct proc *p = curproc;
+ struct smb_vc *vcp = rqp->sr_vc;
+ struct smb_share *ssp = rqp->sr_share;
+ mblk_t *m;
+ int error;
+
+ ASSERT(vcp);
+ ASSERT(SEMA_HELD(&vcp->vc_sendlock));
+ ASSERT(RW_READ_HELD(&vcp->iod_rqlock));
+
+ /*
+ * Note: requests with sr_flags & SMBR_INTERNAL
+ * need to pass here with these states:
+ * SMBIOD_ST_TRANACTIVE: smb_negotiate
+ * SMBIOD_ST_NEGOACTIVE: smb_ssnsetup
+ */
+ SMBIODEBUG("vc_state = %d\n", vcp->vc_state);
+ switch (vcp->vc_state) {
+ case SMBIOD_ST_NOTCONN:
+ smb_iod_rqprocessed(rqp, ENOTCONN, 0);
+ return (0);
+ case SMBIOD_ST_DEAD:
+ /* This is what keeps the iod itself from sending more */
+ smb_iod_rqprocessed(rqp, ENOTCONN, 0);
+ return (0);
+ case SMBIOD_ST_RECONNECT:
+ return (0);
+ default:
+ break;
+ }
+
+ if (rqp->sr_sendcnt == 0) {
+
+ *rqp->sr_rquid = htoles(vcp->vc_smbuid);
+
+ /*
+ * XXX: Odd place for all this...
+ * Would expect these values in vc_smbuid
+ * and/or the request before we get here.
+ * I think most of this mess is due to having
+ * the initial UID set to SMB_UID_UKNOWN when
+ * it should have been initialized to zero!
+ * REVIST this later. XXX -gwr
+ *
+ * This is checking for the case where
+ * "vc_smbuid" was set to 0 in "smb_smb_ssnsetup()";
+ * that happens for requests that occur
+ * after that's done but before we get back the final
+ * session setup reply, where the latter is what
+ * gives us the UID. (There can be an arbitrary # of
+ * session setup packet exchanges to complete
+ * "extended security" authentication.)
+ *
+ * However, if the server gave us a UID of 0 in a
+ * Session Setup andX reply, and we then do a
+ * Tree Connect andX and get back a TID, we should
+ * use that TID, not 0, in subsequent references to
+ * that tree (e.g., in NetShareEnum RAP requests).
+ *
+ * So, for now, we forcibly zero out the TID only if we're
+ * doing extended security, as that's the only time
+ * that "vc_smbuid" should be explicitly zeroed.
+ *
+ * note we must and do use SMB_TID_UNKNOWN for SMB_COM_ECHO
+ */
+ if (!vcp->vc_smbuid &&
+ (vcp->vc_hflags2 & SMB_FLAGS2_EXT_SEC))
+ *rqp->sr_rqtid = htoles(0);
+ else
+ *rqp->sr_rqtid =
+ htoles(ssp ? ssp->ss_tid : SMB_TID_UNKNOWN);
+ mb_fixhdr(&rqp->sr_rq);
+ }
+ if (rqp->sr_sendcnt++ >= 60/SMBSBTIMO) { /* one minute */
+ smb_iod_rqprocessed(rqp, rqp->sr_lerror, SMBR_RESTART);
+ /*
+ * If all attempts to send a request failed, then
+ * something is seriously hosed.
+ */
+ return (ENOTCONN);
+ }
+
+ /*
+ * Replaced m_copym() with Solaris copymsg() which does the same
+ * work when we want to do a M_COPYALL.
+ * m = m_copym(rqp->sr_rq.mb_top, 0, M_COPYALL, 0);
+ */
+ m = copymsg(rqp->sr_rq.mb_top);
+
+#ifdef DTRACE_PROBE
+ DTRACE_PROBE2(smb_iod_sendrq,
+ (smb_rq_t *), rqp, (mblk_t *), m);
+#else
+ SMBIODEBUG("M:%04x, P:%04x, U:%04x, T:%04x\n", rqp->sr_mid, 0, 0, 0);
+#endif
+ m_dumpm(m);
+
+ error = rqp->sr_lerror = m ? SMB_TRAN_SEND(vcp, m, p) : ENOBUFS;
+ m = 0; /* consumed by SEND */
+ if (error == 0) {
+ SMBRQ_LOCK(rqp);
+ rqp->sr_flags |= SMBR_SENT;
+ rqp->sr_state = SMBRQ_SENT;
+ if (rqp->sr_flags & SMBR_SENDWAIT)
+ cv_broadcast(&rqp->sr_cond);
+ SMBRQ_UNLOCK(rqp);
+ return (0);
+ }
+ /*
+ * Check for fatal errors
+ */
+ if (SMB_TRAN_FATAL(vcp, error)) {
+ /*
+ * No further attempts should be made
+ */
+ SMBSDEBUG("TRAN_SEND returned fatal error %d\n", error);
+ return (ENOTCONN);
+ }
+ if (error)
+ SMBSDEBUG("TRAN_SEND returned non-fatal error %d\n", error);
+
+#ifdef APPLE
+ /* If proc waiting on rqp was signaled... */
+ if (smb_rq_intr(rqp))
+ smb_iod_rqprocessed(rqp, EINTR, 0);
+#endif
+
+ return (0);
+}
+
+static int
+smb_iod_recv1(struct smb_vc *vcp, mblk_t **mpp)
+{
+ struct proc *p = curproc;
+ mblk_t *m;
+ uchar_t *hp;
+ int error;
+
+top:
+ m = NULL;
+ error = SMB_TRAN_RECV(vcp, &m, p);
+ if (error == EAGAIN)
+ goto top;
+ if (error)
+ return (error);
+ ASSERT(m);
+
+ m = m_pullup(m, SMB_HDRLEN);
+ if (m == NULL) {
+ return (ENOSR);
+ }
+
+ /*
+ * Check the SMB header
+ */
+ hp = mtod(m, uchar_t *);
+ if (bcmp(hp, SMB_SIGNATURE, SMB_SIGLEN) != 0) {
+ m_freem(m);
+ return (EPROTO);
+ }
+
+ *mpp = m;
+ return (0);
+}
+
+/*
+ * Process incoming packets
+ *
+ * This is the "reader" loop, run by the IOD thread
+ * while in state SMBIOD_ST_VCACTIVE. The loop now
+ * simply blocks in the socket recv until either a
+ * message arrives, or a disconnect.
+ */
+static void
+smb_iod_recvall(struct smb_vc *vcp)
+{
+ struct smb_rq *rqp;
+ mblk_t *m;
+ uchar_t *hp;
+ ushort_t mid;
+ int error;
+ int etime_count = 0; /* for "server not responding", etc. */
+
+ for (;;) {
+
+ if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
+ SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state);
+ error = EIO;
+ break;
+ }
+
+ if (vcp->iod_flags & SMBIOD_SHUTDOWN) {
+ SMBIODEBUG("SHUTDOWN set\n");
+ error = EIO;
+ break;
+ }
+
+ m = NULL;
+ error = smb_iod_recv1(vcp, &m);
+
+ if ((error == ETIME) && vcp->iod_rqwaiting) {
+ /*
+ * Nothing received for 15 seconds,
+ * and we have requests waiting.
+ */
+ etime_count++;
+
+ /*
+ * Once, at 15 sec. notify callbacks
+ * and print the warning message.
+ */
+ if (etime_count == 1) {
+ smb_iod_notify_down(vcp);
+ zprintf(vcp->vc_zoneid,
+ "SMB server %s not responding\n",
+ vcp->vc_srvname);
+ }
+
+ /*
+ * At 30 sec. try sending an echo, and then
+ * once a minute thereafter. It's tricky to
+ * do a send from the IOD thread because
+ * we don't want to block here.
+ *
+ * Using tmo=SMBNOREPLYWAIT in the request
+ * so smb_rq_reply will skip smb_iod_waitrq.
+ * The smb_smb_echo call uses SMBR_INTERNAL
+ * to avoid calling smb_iod_sendall().
+ */
+ if ((etime_count & 3) == 2) {
+ smb_smb_echo(vcp, &vcp->vc_scred,
+ SMBNOREPLYWAIT);
+ }
+
+ continue;
+ } /* ETIME && iod_rqwaiting */
+
+ if (error == ETIME) {
+ /*
+ * If the IOD thread holds the last reference
+ * to this VC, disconnect, release, terminate.
+ * Usually can avoid the lock/unlock here.
+ * Note, in-line: _vc_kill ... _vc_gone
+ */
+ if (vcp->vc_co.co_usecount > 1)
+ continue;
+ SMB_VC_LOCK(vcp);
+ if (vcp->vc_co.co_usecount == 1 &&
+ (vcp->vc_flags & SMBV_GONE) == 0) {
+ vcp->vc_flags |= SMBV_GONE;
+ SMB_VC_UNLOCK(vcp);
+ smb_iod_disconnect(vcp);
+ continue; /* wait for ACK */
+ }
+ SMB_VC_UNLOCK(vcp);
+ continue;
+ } /* error == ETIME */
+
+ if (error) {
+ /*
+ * It's dangerous to continue here.
+ * (possible infinite loop!)
+ */
+ break;
+ }
+
+ /*
+ * Received something. Yea!
+ */
+ if (etime_count) {
+ etime_count = 0;
+
+ zprintf(vcp->vc_zoneid, "SMB server %s OK\n",
+ vcp->vc_srvname);
+
+ smb_iod_notify_up(vcp);
+ }
+
+ /*
+ * Have an SMB packet. The SMB header was
+ * checked in smb_iod_recv1().
+ * Find the request...
+ */
+ hp = mtod(m, uchar_t *);
+ /*LINTED*/
+ mid = SMB_HDRMID(hp);
+ SMBIODEBUG("mid %04x\n", (uint_t)mid);
+
+ rw_enter(&vcp->iod_rqlock, RW_READER);
+ TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) {
+
+ if (rqp->sr_mid != mid)
+ continue;
+
+ DTRACE_PROBE2(smb_iod_recvrq,
+ (smb_rq_t *), rqp, (mblk_t *), m);
+ m_dumpm(m);
+
+ SMBRQ_LOCK(rqp);
+ if (rqp->sr_rp.md_top == NULL) {
+ md_initm(&rqp->sr_rp, m);
+ } else {
+ if (rqp->sr_flags & SMBR_MULTIPACKET) {
+ md_append_record(&rqp->sr_rp, m);
+ } else {
+ SMBRQ_UNLOCK(rqp);
+ SMBSDEBUG("duplicate response %d "
+ "(ignored)\n", mid);
+ break;
+ }
+ }
+ smb_iod_rqprocessed_LH(rqp, 0, 0);
+ SMBRQ_UNLOCK(rqp);
+ break;
+ }
+
+ if (rqp == NULL) {
+ int cmd = SMB_HDRCMD(hp);
+
+ if (cmd != SMB_COM_ECHO)
+ SMBSDEBUG("drop resp: mid %d, cmd %d\n",
+ (uint_t)mid, cmd);
+/* smb_printrqlist(vcp); */
+ m_freem(m);
+ }
+ rw_exit(&vcp->iod_rqlock);
+
+ }
+#ifdef APPLE
+ /*
+ * check for interrupts
+ * On Solaris, handle in smb_iod_waitrq
+ */
+ rw_enter(&vcp->iod_rqlock, RW_READER);
+ TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) {
+ if (smb_sigintr(rqp->sr_cred->scr_vfsctx))
+ smb_iod_rqprocessed(rqp, EINTR, 0);
+ }
+ rw_exit(&vcp->iod_rqlock);
+#endif
+}
+
+/*
+ * Looks like we don't need these callbacks,
+ * but keep the code for now (for Apple).
+ */
+/*ARGSUSED*/
+void
+smb_iod_notify_down(struct smb_vc *vcp)
+{
+#ifdef NEED_SMBFS_CALLBACKS
+ struct smb_connobj *co;
+
+ if (fscb == NULL)
+ return;
+
+ /*
+ * Walk the share list, notify...
+ * Was: smbfs_down(...share->ss_mount);
+ * XXX: Ok to hold vc_lock here?
+ */
+ SMB_VC_LOCK(vcp);
+ SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) {
+ /* smbfs_down() */
+ fscb->fscb_down(CPTOSS(co));
+ }
+ SMB_VC_UNLOCK(vcp);
+#endif /* NEED_SMBFS_CALLBACKS */
+}
+
+/*ARGSUSED*/
+void
+smb_iod_notify_up(struct smb_vc *vcp)
+{
+#ifdef NEED_SMBFS_CALLBACKS
+ struct smb_connobj *co;
+
+ if (fscb == NULL)
+ return;
+
+ /*
+ * Walk the share list, notify...
+ * Was: smbfs_up(...share->ss_mount);
+ * XXX: Ok to hold vc_lock here?
+ */
+ SMB_VC_LOCK(vcp);
+ SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) {
+ /* smbfs_up() */
+ fscb->fscb_up(CPTOSS(co));
+ }
+ SMB_VC_UNLOCK(vcp);
+#endif /* NEED_SMBFS_CALLBACKS */
+}
+
+/*
+ * The IOD thread is now just a "reader",
+ * so no more smb_iod_request(). Yea!
+ */
+
+/*
+ * Place request in the queue, and send it now if possible.
+ * Called with no locks held.
+ */
+int
+smb_iod_addrq(struct smb_rq *rqp)
+{
+ struct smb_vc *vcp = rqp->sr_vc;
+ int error, save_newrq;
+
+ SMBIODEBUG("entry, mid=%d\n", rqp->sr_mid);
+
+ ASSERT(rqp->sr_cred);
+
+ /* This helps a little with debugging. */
+ rqp->sr_owner = curthread;
+
+ if (rqp->sr_flags & SMBR_INTERNAL) {
+ /*
+ * This is some kind of internal request,
+ * i.e. negotiate, session setup, echo...
+ * Allow vc_state < SMBIOD_ST_VCACTIVE, and
+ * always send directly from this thread.
+ * May be called by the IOD thread (echo).
+ * Note lock order: iod_rqlist, vc_sendlock
+ */
+ rw_enter(&vcp->iod_rqlock, RW_WRITER);
+ TAILQ_INSERT_HEAD(&vcp->iod_rqlist, rqp, sr_link);
+ rw_downgrade(&vcp->iod_rqlock);
+
+ /*
+ * Note: iod_sendrq expects vc_sendlock,
+ * so take that here, but carefully:
+ * Never block the IOD thread here.
+ */
+ if (curthread == vcp->iod_thr) {
+ if (sema_tryp(&vcp->vc_sendlock) == 0) {
+ SMBIODEBUG("sendlock busy\n");
+ error = EAGAIN;
+ } else {
+ /* Have vc_sendlock */
+ error = smb_iod_sendrq(rqp);
+ sema_v(&vcp->vc_sendlock);
+ }
+ } else {
+ sema_p(&vcp->vc_sendlock);
+ error = smb_iod_sendrq(rqp);
+ sema_v(&vcp->vc_sendlock);
+ }
+
+ rw_exit(&vcp->iod_rqlock);
+ if (error)
+ smb_iod_removerq(rqp);
+
+ return (error);
+ }
+
+ /*
+ * Normal request from the driver or smbfs.
+ * State should be correct after the check in
+ * smb_rq_enqueue(), but we dropped locks...
+ */
+ if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
+ SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state);
+ return (ENOTCONN);
+ }
+
+ rw_enter(&vcp->iod_rqlock, RW_WRITER);
+
+ TAILQ_INSERT_TAIL(&vcp->iod_rqlist, rqp, sr_link);
+
+ /* iod_rqlock/WRITER protects iod_newrq */
+ save_newrq = vcp->iod_newrq;
+ vcp->iod_newrq++;
+
+ rw_exit(&vcp->iod_rqlock);
+
+ /*
+ * Now send any requests that need to be sent,
+ * including the one we just put on the list.
+ * Only the thread that found iod_newrq==0
+ * needs to run the send loop.
+ */
+ if (save_newrq == 0)
+ smb_iod_sendall(vcp);
+
+ return (0);
+}
+
+/*
+ * Mark an SMBR_MULTIPACKET request as
+ * needing another send. Similar to the
+ * "normal" part of smb_iod_addrq.
+ */
+int
+smb_iod_multirq(struct smb_rq *rqp)
+{
+ struct smb_vc *vcp = rqp->sr_vc;
+ int save_newrq;
+
+ ASSERT(rqp->sr_flags & SMBR_MULTIPACKET);
+
+ if (rqp->sr_flags & SMBR_INTERNAL)
+ return (EINVAL);
+
+ if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
+ SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state);
+ return (ENOTCONN);
+ }
+
+ rw_enter(&vcp->iod_rqlock, RW_WRITER);
+
+ /* Already on iod_rqlist, just reset state. */
+ rqp->sr_state = SMBRQ_NOTSENT;
+
+ /* iod_rqlock/WRITER protects iod_newrq */
+ save_newrq = vcp->iod_newrq;
+ vcp->iod_newrq++;
+
+ rw_exit(&vcp->iod_rqlock);
+
+ /*
+ * Now send any requests that need to be sent,
+ * including the one we just marked NOTSENT.
+ * Only the thread that found iod_newrq==0
+ * needs to run the send loop.
+ */
+ if (save_newrq == 0)
+ smb_iod_sendall(vcp);
+
+ return (0);
+}
+
+
+int
+smb_iod_removerq(struct smb_rq *rqp)
+{
+ struct smb_vc *vcp = rqp->sr_vc;
+
+ SMBIODEBUG("entry, mid=%d\n", rqp->sr_mid);
+
+ rw_enter(&vcp->iod_rqlock, RW_WRITER);
+#ifdef QUEUEDEBUG
+ /*
+ * Make sure we have not already removed it.
+ * See sys/queue.h QUEUEDEBUG_TAILQ_POSTREMOVE
+ * XXX: Don't like the constant 1 here...
+ */
+ ASSERT(rqp->sr_link.tqe_next != (void *)1L);
+#endif
+ TAILQ_REMOVE(&vcp->iod_rqlist, rqp, sr_link);
+ rw_exit(&vcp->iod_rqlock);
+
+ return (0);
+}
+
+
+/*
+ * Internal version of smb_iod_waitrq.
+ *
+ * This is used when there is no reader thread,
+ * so we have to do the recv here. The request
+ * must have the SMBR_INTERNAL flag set.
+ */
+static int
+smb_iod_waitrq_internal(struct smb_rq *rqp)
+{
+ struct smb_vc *vcp = rqp->sr_vc;
+ mblk_t *m;
+ uchar_t *hp;
+ int error;
+ uint16_t mid;
+ uint8_t cmd;
+
+ /* Make sure it's an internal request. */
+ if ((rqp->sr_flags & SMBR_INTERNAL) == 0) {
+ SMBIODEBUG("not internal\n");
+ return (EINVAL);
+ }
+
+ /* Only simple requests allowed. */
+ if (rqp->sr_flags & SMBR_MULTIPACKET) {
+ SMBIODEBUG("multipacket\n");
+ return (EINVAL);
+ }
+
+ /* Should not already have a response. */
+ if (rqp->sr_rp.md_top) {
+ DEBUG_ENTER("smb_iod_waitrq again?\n");
+ return (0);
+ }
+
+ /*
+ * The message recv loop. Terminates when we
+ * receive the message we're looking for.
+ * Drop others, with complaints.
+ * Scaled-down version of smb_iod_recvall
+ */
+ for (;;) {
+ m = NULL;
+ error = smb_iod_recv1(vcp, &m);
+ if (error) {
+ /*
+ * It's dangerous to continue here.
+ * (possible infinite loop!)
+ */
+#if 0
+ if (SMB_TRAN_FATAL(vcp, error)) {
+ return (error);
+ }
+ continue;
+#endif
+ return (error);
+ }
+
+ hp = mtod(m, uchar_t *);
+ cmd = SMB_HDRCMD(hp);
+ /*LINTED*/
+ mid = SMB_HDRMID(hp);
+
+ SMBIODEBUG("cmd 0x%02x mid %04x\n",
+ (uint_t)cmd, (uint_t)mid);
+ m_dumpm(m);
+
+ /*
+ * Normally, the MID will match.
+ * For internal requests, also
+ * match on the cmd to be safe.
+ */
+ if (mid == rqp->sr_mid)
+ break;
+ if (cmd == rqp->sr_cmd) {
+ SMBIODEBUG("cmd match but not mid!\n");
+ break;
+ }
+
+ SMBIODEBUG("drop nomatch\n");
+ m_freem(m);
+ }
+
+ /*
+ * Have the response we were waiting for.
+ * Simplified version of the code from
+ * smb_iod_recvall
+ */
+ SMBRQ_LOCK(rqp);
+ if (rqp->sr_rp.md_top == NULL) {
+ md_initm(&rqp->sr_rp, m);
+ } else {
+ SMBIODEBUG("drop duplicate\n");
+ m_freem(m);
+ }
+ SMBRQ_UNLOCK(rqp);
+
+ return (0);
+}
+
+
+/*
+ * Wait for a request to complete.
+ *
+ * For internal requests, see smb_iod_waitrq_internal.
+ * For normal requests, we need to deal with
+ * ioc_muxcnt dropping below vc_maxmux by
+ * making arrangements to send more...
+ */
+int
+smb_iod_waitrq(struct smb_rq *rqp)
+{
+ struct smb_vc *vcp = rqp->sr_vc;
+ clock_t tr, tmo1, tmo2;
+ int error, rc;
+
+ SMBIODEBUG("entry, cmd=0x%02x mid=0x%04x\n",
+ (uint_t)rqp->sr_cmd, (uint_t)rqp->sr_mid);
+
+ if (rqp->sr_flags & SMBR_INTERNAL) {
+ ASSERT((rqp->sr_flags & SMBR_MULTIPACKET) == 0);
+ error = smb_iod_waitrq_internal(rqp);
+ smb_iod_removerq(rqp);
+ return (error);
+ }
+
+ /*
+ * Make sure this is NOT the IOD thread,
+ * or the wait below will always timeout.
+ */
+ ASSERT(curthread != vcp->iod_thr);
+
+ atomic_inc_uint(&vcp->iod_rqwaiting);
+ SMBRQ_LOCK(rqp);
+
+ /*
+ * First, wait for the request to be sent. Normally the send
+ * has already happened by the time we get here. However, if
+ * we have more than maxmux entries in the request list, our
+ * request may not be sent until other requests complete.
+ * The wait in this case is due to local I/O demands, so
+ * we don't want the server response timeout to apply.
+ *
+ * If a request is allowed to interrupt this wait, then the
+ * request is cancelled and never sent OTW. Some kinds of
+ * requests should never be cancelled (i.e. close) and those
+ * are marked SMBR_NOINTR_SEND so they either go eventually,
+ * or a connection close will terminate them with ENOTCONN.
+ */
+ while (rqp->sr_state == SMBRQ_NOTSENT) {
+ rqp->sr_flags |= SMBR_SENDWAIT;
+ if (rqp->sr_flags & SMBR_NOINTR_SEND) {
+ cv_wait(&rqp->sr_cond, &rqp->sr_lock);
+ rc = 1;
+ } else
+ rc = cv_wait_sig(&rqp->sr_cond, &rqp->sr_lock);
+ rqp->sr_flags &= ~SMBR_SENDWAIT;
+ if (rc == 0) {
+ SMBIODEBUG("EINTR in sendwait, mid=%u\n", rqp->sr_mid);
+ error = EINTR;
+ goto out;
+ }
+ }
+
+ /*
+ * The request has been sent. Now wait for the response,
+ * with the timeout specified for this request.
+ * Compute all the deadlines now, so we effectively
+ * start the timer(s) after the request is sent.
+ */
+ if (smb_timo_notice && (smb_timo_notice < rqp->sr_timo))
+ tmo1 = lbolt + SEC_TO_TICK(smb_timo_notice);
+ else
+ tmo1 = 0;
+ tmo2 = lbolt + SEC_TO_TICK(rqp->sr_timo);
+
+ /*
+ * As above, we don't want to allow interrupt for some
+ * requests like open, because we could miss a succesful
+ * response and therefore "leak" a FID. Such requests
+ * are marked SMBR_NOINTR_RECV to prevent that.
+ *
+ * If "slow server" warnings are enabled, wait first
+ * for the "notice" timeout, and warn if expired.
+ */
+ if (tmo1 && rqp->sr_rpgen == rqp->sr_rplast) {
+ if (rqp->sr_flags & SMBR_NOINTR_RECV)
+ tr = cv_timedwait(&rqp->sr_cond,
+ &rqp->sr_lock, tmo1);
+ else
+ tr = cv_timedwait_sig(&rqp->sr_cond,
+ &rqp->sr_lock, tmo1);
+ if (tr == 0) {
+ error = EINTR;
+ goto out;
+ }
+ if (tr < 0) {
+#ifdef DTRACE_PROBE
+ DTRACE_PROBE1(smb_iod_waitrq1,
+ (smb_rq_t *), rqp);
+#endif
+#ifdef NOT_YET
+ /* Want this to go ONLY to the user. */
+ uprintf("SMB server %s has not responded"
+ " to request %d after %d seconds..."
+ " (still waiting).\n", vcp->vc_srvname,
+ rqp->sr_mid, smb_timo_notice);
+#endif
+ }
+ }
+
+ /*
+ * Keep waiting until tmo2 is expired.
+ */
+ while (rqp->sr_rpgen == rqp->sr_rplast) {
+ if (rqp->sr_flags & SMBR_NOINTR_RECV)
+ tr = cv_timedwait(&rqp->sr_cond,
+ &rqp->sr_lock, tmo2);
+ else
+ tr = cv_timedwait_sig(&rqp->sr_cond,
+ &rqp->sr_lock, tmo2);
+ if (tr == 0) {
+ error = EINTR;
+ goto out;
+ }
+ if (tr < 0) {
+#ifdef DTRACE_PROBE
+ DTRACE_PROBE1(smb_iod_waitrq2,
+ (smb_rq_t *), rqp);
+#endif
+#ifdef NOT_YET
+ /* Want this to go ONLY to the user. */
+ uprintf("SMB server %s has not responded"
+ " to request %d after %d seconds..."
+ " (giving up).\n", vcp->vc_srvname,
+ rqp->sr_mid, rqp->sr_timo);
+#endif
+ error = ETIME;
+ goto out;
+ }
+ /* got wakeup */
+ }
+ error = rqp->sr_lerror;
+ rqp->sr_rplast++;
+
+out:
+ SMBRQ_UNLOCK(rqp);
+ atomic_dec_uint(&vcp->iod_rqwaiting);
+
+ /*
+ * MULTIPACKET request must stay in the list.
+ * They may need additional responses.
+ */
+ if ((rqp->sr_flags & SMBR_MULTIPACKET) == 0)
+ smb_iod_removerq(rqp);
+
+ /*
+ * Some request has been completed.
+ * If we reached the mux limit,
+ * re-run the send loop...
+ */
+ if (vcp->iod_muxfull)
+ smb_iod_sendall(vcp);
+
+ return (error);
+}
+
+/*
+ * Shutdown all outstanding I/O requests on the specified share with
+ * ENXIO; used when unmounting a share. (There shouldn't be any for a
+ * non-forced unmount; if this is a forced unmount, we have to shutdown
+ * the requests as part of the unmount process.)
+ */
+void
+smb_iod_shutdown_share(struct smb_share *ssp)
+{
+ struct smb_vc *vcp = SSTOVC(ssp);
+ struct smb_rq *rqp;
+
+ /*
+ * Loop through the list of requests and shutdown the ones
+ * that are for the specified share.
+ */
+ rw_enter(&vcp->iod_rqlock, RW_READER);
+ TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) {
+ if (rqp->sr_state != SMBRQ_NOTIFIED && rqp->sr_share == ssp)
+ smb_iod_rqprocessed(rqp, EIO, 0);
+ }
+ rw_exit(&vcp->iod_rqlock);
+}
+
+/*
+ * Send all requests that need sending.
+ * Called from _addrq, _multirq, _waitrq
+ */
+static void
+smb_iod_sendall(struct smb_vc *vcp)
+{
+ struct smb_rq *rqp;
+ int error, save_newrq, muxcnt;
+
+ /*
+ * Clear "newrq" to make sure threads adding
+ * new requests will run this function again.
+ */
+ rw_enter(&vcp->iod_rqlock, RW_WRITER);
+ save_newrq = vcp->iod_newrq;
+ vcp->iod_newrq = 0;
+
+ /*
+ * We only read iod_rqlist, so downgrade rwlock.
+ * This allows the IOD to handle responses while
+ * some requesting thread may be blocked in send.
+ */
+ rw_downgrade(&vcp->iod_rqlock);
+
+ /* Expect to find about this many requests. */
+ SMBIODEBUG("top, save_newrq=%d\n", save_newrq);
+
+ /*
+ * Serialize to prevent multiple senders.
+ * Note lock order: iod_rqlock, vc_sendlock
+ */
+ sema_p(&vcp->vc_sendlock);
+
+ /*
+ * Walk the list of requests and send when possible.
+ * We avoid having more than vc_maxmux requests
+ * outstanding to the server by traversing only
+ * vc_maxmux entries into this list. Simple!
+ */
+ ASSERT(vcp->vc_maxmux > 0);
+ error = muxcnt = 0;
+ TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) {
+
+ if (vcp->vc_state == SMBIOD_ST_DEAD) {
+ error = ENOTCONN; /* stop everything! */
+ break;
+ }
+
+ if (rqp->sr_state == SMBRQ_NOTSENT) {
+ error = smb_iod_sendrq(rqp);
+ if (error)
+ break;
+ }
+
+ if (++muxcnt == vcp->vc_maxmux) {
+ SMBIODEBUG("muxcnt == vc_maxmux\n");
+ break;
+ }
+
+ }
+
+ /*
+ * If we have vc_maxmux requests outstanding,
+ * arrange for _waitrq to call _sendall as
+ * requests are completed.
+ */
+ vcp->iod_muxfull =
+ (muxcnt < vcp->vc_maxmux) ? 0 : 1;
+
+ sema_v(&vcp->vc_sendlock);
+ rw_exit(&vcp->iod_rqlock);
+
+ if (error == ENOTCONN)
+ smb_iod_dead(vcp);
+
+}
+
+
+/*
+ * "main" function for smbiod daemon thread
+ */
+void
+smb_iod_main(struct smb_vc *vcp)
+{
+ kthread_t *thr = curthread;
+
+ SMBIODEBUG("entry\n");
+
+ SMBIODEBUG("Running, thr=0x%p\n", thr);
+
+ /*
+ * Prevent race with thread that created us.
+ * After we get this lock iod_thr is set.
+ */
+ SMB_VC_LOCK(vcp);
+ ASSERT(thr == vcp->iod_thr);
+
+ /* Redundant with iod_thr, but may help debugging. */
+ vcp->iod_flags |= SMBIOD_RUNNING;
+ SMB_VC_UNLOCK(vcp);
+
+ /*
+ * OK, this is a new reader thread.
+ * In case of reconnect, tell any
+ * old requests they can restart.
+ */
+ smb_iod_invrq(vcp);
+
+ /*
+ * Run the "reader" loop.
+ */
+ smb_iod_recvall(vcp);
+
+ /*
+ * The reader loop function returns only when
+ * there's been a fatal error on the connection.
+ */
+ smb_iod_dead(vcp);
+
+ /*
+ * The reader thread is going away. Clear iod_thr,
+ * and wake up anybody waiting for us to quit.
+ */
+ SMB_VC_LOCK(vcp);
+ vcp->iod_flags &= ~SMBIOD_RUNNING;
+ vcp->iod_thr = NULL;
+ cv_broadcast(&vcp->iod_exit);
+ SMB_VC_UNLOCK(vcp);
+
+ /*
+ * This hold was taken in smb_iod_create()
+ * when this thread was created.
+ */
+ smb_vc_rele(vcp);
+
+ SMBIODEBUG("Exiting, p=0x%p\n", curproc);
+ zthread_exit();
+}
+
+/*
+ * Create the reader thread.
+ *
+ * This happens when we are just about to
+ * enter vc_state = SMBIOD_ST_VCACTIVE;
+ * See smb_sm_ssnsetup()
+ */
+int
+smb_iod_create(struct smb_vc *vcp)
+{
+ kthread_t *thr = NULL;
+ int error;
+
+ /*
+ * Take a hold on the VC for the IOD thread.
+ * This hold will be released when the IOD
+ * thread terminates. (or on error below)
+ */
+ smb_vc_hold(vcp);
+
+ SMB_VC_LOCK(vcp);
+
+ if (vcp->iod_thr != NULL) {
+ SMBIODEBUG("aready have an IOD?");
+ error = EIO;
+ goto out;
+ }
+
+ /*
+ * Darwin code used: IOCreateThread(...)
+ * In Solaris, we use...
+ */
+ thr = zthread_create(
+ NULL, /* stack */
+ 0, /* stack size (default) */
+ smb_iod_main, /* entry func... */
+ vcp, /* ... and arg */
+ 0, /* len (of what?) */
+ minclsyspri); /* priority */
+ if (thr == NULL) {
+ SMBERROR("can't start smbiod\n");
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* Success! */
+ error = 0;
+ vcp->iod_thr = thr;
+
+out:
+ SMB_VC_UNLOCK(vcp);
+
+ if (error)
+ smb_vc_rele(vcp);
+
+ return (error);
+}
+
+/*
+ * Called from smb_vc_free to do any
+ * cleanup of our IOD (reader) thread.
+ */
+int
+smb_iod_destroy(struct smb_vc *vcp)
+{
+ clock_t tmo;
+
+ /*
+ * Let's try to make sure the IOD thread
+ * goes away, by waiting for it to exit.
+ * Normally, it's gone by now.
+ *
+ * Only wait for a second, because we're in the
+ * teardown path and don't want to get stuck here.
+ * Should not take long, or things are hosed...
+ */
+ SMB_VC_LOCK(vcp);
+ if (vcp->iod_thr) {
+ vcp->iod_flags |= SMBIOD_SHUTDOWN;
+ tmo = lbolt + hz;
+ tmo = cv_timedwait(&vcp->iod_exit, &vcp->vc_lock, tmo);
+ if (tmo == -1) {
+ SMBERROR("IOD thread for %s did not exit?\n",
+ vcp->vc_srvname);
+ }
+ }
+ if (vcp->iod_thr) {
+ /* This should not happen. */
+ SMBIODEBUG("IOD thread did not exit!\n");
+ /* Try harder? */
+ tsignal(vcp->iod_thr, SIGKILL);
+ }
+ SMB_VC_UNLOCK(vcp);
+
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_osdep.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_osdep.h
new file mode 100644
index 0000000000..712acb6f3b
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_osdep.h
@@ -0,0 +1,102 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Code corresponding to smb_apple.h
+ * XXX: Could merge this into smb_subr.h
+ * as long as that doesn't break smbfs
+ */
+
+#ifndef _NETSMB_SMB_OSDEP_H_
+#define _NETSMB_SMB_OSDEP_H_
+
+#ifndef PRIVSYM
+#define PRIVSYM
+#endif
+
+#ifndef min
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define CAST_DOWN(type, addr) (((type)((uintptr_t)(addr))))
+#define USER_ADDR_NULL ((user_addr_t)0)
+#define CAST_USER_ADDR_T(a_ptr) ((user_addr_t)(a_ptr))
+
+/*
+ * flags to (BSD) malloc
+ */
+#define M_WAITOK 0x0000
+#define M_NOWAIT 0x0001
+#define M_ZERO 0x0004 /* bzero the allocation */
+
+/* Iconv stuff */
+
+/*
+ * Some UTF Related stuff. Will be deleting this once compiled and using
+ * ienup's code.
+ */
+/*
+ * UTF-8 encode/decode flags
+ */
+#define UTF_REVERSE_ENDIAN 0x01 /* reverse UCS-2 byte order */
+#define UTF_NO_NULL_TERM 0x02 /* do not add null termination */
+#define UTF_DECOMPOSED 0x04 /* generate fully decomposed UCS-2 */
+#define UTF_PRECOMPOSED 0x08 /* generate precomposed UCS-2 */
+
+/*
+ * These are actually included in sunddi.h. I am getting compilation
+ * errors right now. Adding the induvidual defines here again from sunddi.h
+ * Unicode encoding conversion functions and their macros.
+ */
+#define UCONV_IN_BIG_ENDIAN 0x0001
+#define UCONV_OUT_BIG_ENDIAN 0x0002
+#define UCONV_IN_SYSTEM_ENDIAN 0x0004
+#define UCONV_OUT_SYSTEM_ENDIAN 0x0008
+#define UCONV_IN_LITTLE_ENDIAN 0x0010
+#define UCONV_OUT_LITTLE_ENDIAN 0x0020
+#define UCONV_IGNORE_NULL 0x0040
+#define UCONV_IN_ACCEPT_BOM 0x0080
+#define UCONV_OUT_EMIT_BOM 0x0100
+
+extern int uconv_u8tou16(const uchar_t *, size_t *, uint16_t *, size_t *, int);
+
+/* Legacy type names for Solaris. */
+typedef uint64_t u_int64_t;
+typedef uint32_t u_int32_t;
+typedef uint16_t u_int16_t;
+typedef uint8_t u_int8_t;
+
+typedef const char * c_caddr_t;
+typedef uint64_t user_addr_t;
+
+/*
+ * Time related calls.
+ */
+
+/* BEGIN CSTYLED */
+#define timespeccmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
+ ((tvp)->tv_nsec cmp (uvp)->tv_nsec) : \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
+/* END CSTYLED */
+
+#define timespecadd(vvp, uvp) \
+ { \
+ (vvp)->tv_sec += (uvp)->tv_sec; \
+ (vvp)->tv_nsec += (uvp)->tv_nsec; \
+ if ((vvp)->tv_nsec >= 1000000000) { \
+ (vvp)->tv_sec++; \
+ (vvp)->tv_nsec -= 1000000000; \
+ } \
+ }
+
+#define timespecsub(vvp, uvp) \
+ { \
+ (vvp)->tv_sec -= (uvp)->tv_sec; \
+ (vvp)->tv_nsec -= (uvp)->tv_nsec; \
+ if ((vvp)->tv_nsec < 0) { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_nsec += 1000000000; \
+ } \
+ }
+
+#endif /* _NETSMB_SMB_OSDEP_H_ */
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c
new file mode 100644
index 0000000000..c78c0e61b1
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c
@@ -0,0 +1,391 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Password Keychain storage mechanism.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/sysmacros.h>
+#include <sys/uio.h>
+#include <sys/buf.h>
+#include <sys/modctl.h>
+#include <sys/open.h>
+#include <sys/file.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/cmn_err.h>
+#include <sys/stat.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/policy.h>
+#include <sys/zone.h>
+#include <sys/pathname.h>
+#include <sys/mount.h>
+#include <sys/sdt.h>
+#include <fs/fs_subr.h>
+#include <sys/devops.h>
+#include <sys/thread.h>
+#include <sys/mkdev.h>
+#include <sys/avl.h>
+#include <sys/avl_impl.h>
+
+#include <netsmb/smb_osdep.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_dev.h>
+#include <netsmb/smb_pass.h>
+
+/*
+ * The smb_ptd is a cache of Uid's, User names, passwords and domain names.
+ * It will be used for storing the password information for a user and will
+ * be used to for connections without entering the pasword again if its
+ * already keyed in by the user. Its a kind of Key-Chain mechanism
+ * implemented by Apple folks.
+ */
+
+/*
+ * Information stored in the nodes:
+ * UID: Uid of the person who initiated the login request.
+ * ZoneID: ZoneID of the zone from where the login request is initiated.
+ * Username: Username in the CIFS server.
+ * Srvdom: Domain name/ Server name of the CIFS server.
+ * Password: Password of the user.
+ * For more information, see smb_pass.h and sys/avl.h
+ */
+
+/*
+ * Information retrieved from the node.
+ * Node/password information can only be retrived with a call
+ * to smb_pkey_getpw(). Password never gets copied to the userspace.
+ * It will be copied to the Kernel data structure smbioc_ossn->ioc_password
+ * when needed for doing the "Session Setup". All other calls will return
+ * either a success or a failure.
+ */
+
+avl_tree_t smb_ptd; /* AVL password tree descriptor */
+unsigned int smb_list_len = 0; /* No. of elements in the tree. */
+kmutex_t smb_ptd_lock; /* Mutex lock for controlled access */
+
+/*
+ * This routine is called by AVL tree calls when they want to find a
+ * node, find the next position in the tree to add or for deletion.
+ * Compare nodes from the tree to find the actual node based on
+ * uid/zoneid/username/domainname.
+ */
+int
+smb_pkey_cmp(const void *a, const void *b)
+{
+ const smb_passid_t *pa = (smb_passid_t *)a;
+ const smb_passid_t *pb = (smb_passid_t *)b;
+ int duser, dsrv;
+
+ ASSERT(MUTEX_HELD(&smb_ptd_lock));
+
+ /*
+ * The nodes are added sorted on the uid/zoneid/domainname/username
+ * We will do this:
+ * Compare uid's. The owner who stored the node gets access.
+ * Then zoneid to check if the access is from the same zone.
+ * Compare usernames.
+ * If the above are same, then compare domain/server names.
+ */
+ if (pa->uid < pb->uid)
+ return (-1);
+ if (pa->uid > pb->uid)
+ return (+1);
+ if (pa->zoneid < pb->zoneid)
+ return (-1);
+ if (pa->zoneid > pb->zoneid)
+ return (+1);
+ dsrv = strcasecmp(pa->srvdom, pb->srvdom);
+ if (dsrv < 0)
+ return (-1);
+ if (dsrv > 0)
+ return (+1);
+ duser = strcasecmp(pa->username, pb->username);
+ if (duser < 0)
+ return (-1);
+ if (duser > 0)
+ return (+1);
+ return (0);
+}
+
+/*
+ * Initialization of the code that deals with uid and passwords.
+ */
+void
+smb_pkey_init()
+{
+ avl_create(&smb_ptd,
+ smb_pkey_cmp,
+ sizeof (smb_passid_t),
+ offsetof(smb_passid_t,
+ cpnode));
+ mutex_init(&smb_ptd_lock, NULL, MUTEX_DEFAULT, NULL);
+}
+
+/*
+ * Destroy the full AVL tree.
+ */
+void
+smb_pkey_fini()
+{
+ smb_pkey_deluid((uid_t)-1, CRED());
+ avl_destroy(&smb_ptd);
+ mutex_destroy(&smb_ptd_lock);
+}
+
+/*
+ * Driver unload calls this to ask if we
+ * have any stored passwords
+ */
+int
+smb_pkey_idle()
+{
+ int n;
+
+ mutex_enter(&smb_ptd_lock);
+ n = avl_numnodes(&smb_ptd);
+ mutex_exit(&smb_ptd_lock);
+
+ return ((n) ? EBUSY : 0);
+}
+
+int
+smb_node_delete(smb_passid_t *tmp)
+{
+ ASSERT(MUTEX_HELD(&smb_ptd_lock));
+ avl_remove(&smb_ptd, tmp);
+ smb_strfree(tmp->srvdom);
+ smb_strfree(tmp->username);
+ kmem_free(tmp, sizeof (*tmp));
+ return (0);
+}
+
+
+/*
+ * Remove a node from the AVL tree identified by cpid.
+ */
+int
+smb_pkey_del(smbioc_pk_t *pk, cred_t *cr)
+{
+ avl_index_t where;
+ smb_passid_t buf, *cpid, *tmp;
+ uid_t uid;
+
+ tmp = &buf;
+ uid = pk->pk_uid;
+ if (uid == (uid_t)-1)
+ uid = crgetruid(cr);
+ else {
+ if (secpolicy_smbfs_login(cr, uid))
+ return (EPERM);
+ }
+ tmp->uid = uid;
+ tmp->zoneid = getzoneid();
+ tmp->srvdom = pk->pk_dom;
+ tmp->username = pk->pk_usr;
+
+ mutex_enter(&smb_ptd_lock);
+ if ((cpid = (smb_passid_t *)avl_find(&smb_ptd,
+ tmp, &where)) != NULL) {
+ smb_node_delete(cpid);
+ }
+ mutex_exit(&smb_ptd_lock);
+
+ return (0);
+}
+
+/*
+ * Delete the entries owned by a particular user
+ * based on uid. We go through all the nodes and
+ * delete the nodes whereever the uid matches.
+ *
+ * Also implements "delete all" when uid == -1.
+ *
+ * You must have privilege to use any uid other
+ * than your real uid.
+ */
+int
+smb_pkey_deluid(uid_t ioc_uid, cred_t *cr)
+{
+ smb_passid_t *cpid, *tmp;
+
+ if (secpolicy_smbfs_login(cr, ioc_uid))
+ return (EPERM);
+
+ mutex_enter(&smb_ptd_lock);
+ for (tmp = avl_first(&smb_ptd); tmp != NULL;
+ tmp = cpid) {
+ cpid = AVL_NEXT(&smb_ptd, tmp);
+ if (ioc_uid == (uid_t)-1 ||
+ ioc_uid == tmp->uid) {
+ /*
+ * Delete the node.
+ */
+ smb_node_delete(tmp);
+ }
+ }
+ mutex_exit(&smb_ptd_lock);
+
+ return (0);
+}
+
+/*
+ * Add entry or modify existing.
+ * Check for existing entry..
+ * If present, delete.
+ * Now, add the new entry.
+ */
+int
+smb_pkey_add(smbioc_pk_t *pk, cred_t *cr)
+{
+ avl_tree_t *t = &smb_ptd;
+ avl_index_t where;
+ smb_passid_t *tmp, *cpid;
+ int ret;
+ uid_t uid;
+
+ uid = pk->pk_uid;
+ if (uid == (uid_t)-1)
+ uid = crgetruid(cr);
+ else {
+ if (secpolicy_smbfs_login(cr, uid))
+ return (EPERM);
+ }
+ cpid = kmem_zalloc(sizeof (smb_passid_t), KM_SLEEP);
+ cpid->uid = uid;
+ cpid->zoneid = getzoneid();
+ cpid->srvdom = smb_strdup(pk->pk_dom);
+ cpid->username = smb_strdup(pk->pk_usr);
+ smb_oldlm_hash(pk->pk_pass, cpid->lmhash);
+ smb_ntlmv1hash(pk->pk_pass, cpid->nthash);
+
+ /*
+ * XXX: Instead of calling smb_pkey_check here,
+ * should call avl_find directly, and hold the
+ * lock across: avl_find, avl_remove, avl_insert.
+ */
+
+ /* If it already exists, delete it. */
+ ret = smb_pkey_check(pk, cr);
+ if (ret == 0) {
+ smb_pkey_del(pk, cr);
+ }
+
+ mutex_enter(&smb_ptd_lock);
+ tmp = (smb_passid_t *)avl_find(t, cpid, &where);
+ if (tmp == NULL) {
+ avl_insert(t, cpid, where);
+ } else {
+ smb_strfree(cpid->srvdom);
+ smb_strfree(cpid->username);
+ kmem_free(cpid, sizeof (smb_passid_t));
+ }
+ mutex_exit(&smb_ptd_lock);
+
+ return (0);
+}
+
+/*
+ * Determine if a node with uid,zoneid, uname & dname exists in the tree
+ * given the information. Does NOT return the stored password.
+ */
+int
+smb_pkey_check(smbioc_pk_t *pk, cred_t *cr)
+{
+ avl_tree_t *t = &smb_ptd;
+ avl_index_t where;
+ smb_passid_t *tmp, *cpid;
+ int error = ENOENT;
+ uid_t uid;
+
+ uid = pk->pk_uid;
+ if (uid == (uid_t)-1)
+ uid = crgetruid(cr);
+ else {
+ if (secpolicy_smbfs_login(cr, uid))
+ return (EPERM);
+ }
+ cpid = kmem_alloc(sizeof (smb_passid_t), KM_SLEEP);
+ cpid->uid = uid;
+ cpid->zoneid = getzoneid();
+ cpid->srvdom = pk->pk_dom;
+ cpid->username = pk->pk_usr;
+
+ mutex_enter(&smb_ptd_lock);
+ tmp = (smb_passid_t *)avl_find(t, cpid, &where);
+ if (tmp != NULL)
+ error = 0;
+ mutex_exit(&smb_ptd_lock);
+
+ kmem_free(cpid, sizeof (smb_passid_t));
+ return (error);
+}
+
+/*
+ * Interface function between the keychain mechanism and SMB password
+ * handling during Session Setup. Internal form of smb_pkey_check().
+ * Copies the password hashes into the VC.
+ */
+int
+smb_pkey_getpwh(struct smb_vc *vcp, cred_t *cr)
+{
+ avl_tree_t *t = &smb_ptd;
+ avl_index_t where;
+ smb_passid_t *tmp, *cpid;
+ int error = ENOENT;
+
+ cpid = kmem_alloc(sizeof (smb_passid_t), KM_SLEEP);
+ cpid->uid = crgetruid(cr);
+ cpid->zoneid = getzoneid();
+ cpid->username = vcp->vc_username;
+
+ if (vcp->vc_vopt & SMBVOPT_KC_DOMAIN)
+ cpid->srvdom = vcp->vc_domain;
+ else
+ cpid->srvdom = vcp->vc_srvname;
+
+ mutex_enter(&smb_ptd_lock);
+ tmp = (smb_passid_t *)avl_find(t, cpid, &where);
+ if (tmp != NULL) {
+ bcopy(tmp->lmhash, vcp->vc_lmhash, SMB_PWH_MAX);
+ bcopy(tmp->nthash, vcp->vc_nthash, SMB_PWH_MAX);
+ error = 0;
+ }
+ mutex_exit(&smb_ptd_lock);
+
+ kmem_free(cpid, sizeof (smb_passid_t));
+ return (error);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h
new file mode 100644
index 0000000000..d4147798ba
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h
@@ -0,0 +1,66 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SMB_PASS_H
+#define _SMB_PASS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Password keychains interface
+ */
+
+#include <sys/avl.h>
+#include <netsmb/smb_dev.h>
+
+/*
+ * Here just so our mdb module can use it.
+ * Otherwise could be private to smb_pass.c
+ */
+typedef struct smb_passid {
+ avl_node_t cpnode; /* Next Node information */
+ uid_t uid; /* User id */
+ zoneid_t zoneid; /* Future Use */
+ char *srvdom; /* Windows Domain (or server) */
+ char *username; /* Windows User name */
+ uchar_t lmhash[SMB_PWH_MAX];
+ uchar_t nthash[SMB_PWH_MAX];
+} smb_passid_t;
+
+/* Called from smb_dev.c */
+void smb_pkey_init(void);
+void smb_pkey_fini(void);
+int smb_pkey_idle(void);
+int smb_pkey_add(smbioc_pk_t *pk, cred_t *cr);
+int smb_pkey_del(smbioc_pk_t *pk, cred_t *cr);
+int smb_pkey_check(smbioc_pk_t *pk, cred_t *cr);
+int smb_pkey_deluid(uid_t uid, cred_t *cr);
+
+/* Called from smb_usr.c */
+struct smb_vc;
+int smb_pkey_getpwh(struct smb_vc *vcp, cred_t *cr);
+
+#endif /* _SMB_PASS_H */
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c
new file mode 100644
index 0000000000..1115e6fc69
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c
@@ -0,0 +1,1408 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_rq.c,v 1.29 2005/02/11 01:44:17 lindak Exp $
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kmem.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/socket.h>
+#include <sys/mount.h>
+#include <sys/cmn_err.h>
+#include <sys/sdt.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#else
+#include <netsmb/smb_osdep.h>
+#endif
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_tran.h>
+#include <netsmb/smb_rq.h>
+
+static int smb_rq_reply(struct smb_rq *rqp);
+static int smb_rq_enqueue(struct smb_rq *rqp);
+static int smb_rq_getenv(struct smb_connobj *layer,
+ struct smb_vc **vcpp, struct smb_share **sspp);
+static int smb_rq_new(struct smb_rq *rqp, uchar_t cmd);
+static int smb_t2_reply(struct smb_t2rq *t2p);
+static int smb_nt_reply(struct smb_ntrq *ntp);
+
+
+
+int
+smb_rq_alloc(struct smb_connobj *layer, uchar_t cmd, struct smb_cred *scred,
+ struct smb_rq **rqpp)
+{
+ struct smb_rq *rqp;
+ int error;
+
+ rqp = (struct smb_rq *)kmem_alloc(sizeof (struct smb_rq), KM_SLEEP);
+ if (rqp == NULL)
+ return (ENOMEM);
+ error = smb_rq_init(rqp, layer, cmd, scred);
+ if (error) {
+ smb_rq_done(rqp);
+ return (error);
+ }
+ rqp->sr_flags |= SMBR_ALLOCED;
+ *rqpp = rqp;
+ return (0);
+}
+
+
+int
+smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, uchar_t cmd,
+ struct smb_cred *scred)
+{
+ int error;
+
+ bzero(rqp, sizeof (*rqp));
+ mutex_init(&rqp->sr_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&rqp->sr_cond, NULL, CV_DEFAULT, NULL);
+
+ error = smb_rq_getenv(layer, &rqp->sr_vc, &rqp->sr_share);
+ if (error)
+ return (error);
+
+ rqp->sr_rexmit = SMBMAXRESTARTS;
+ rqp->sr_cred = scred; /* XXX no ref hold */
+ rqp->sr_mid = smb_vc_nextmid(rqp->sr_vc);
+ error = smb_rq_new(rqp, cmd);
+ if (!error) {
+ rqp->sr_flags |= SMBR_VCREF;
+ smb_vc_hold(rqp->sr_vc);
+ }
+ return (error);
+}
+
+static int
+smb_rq_new(struct smb_rq *rqp, uchar_t cmd)
+{
+ struct smb_vc *vcp = rqp->sr_vc;
+ struct mbchain *mbp = &rqp->sr_rq;
+ int error;
+ static char tzero[12];
+ caddr_t ptr;
+ pid_t pid;
+
+ ASSERT(rqp != NULL);
+ ASSERT(rqp->sr_cred != NULL);
+ pid = rqp->sr_cred->vc_pid;
+ rqp->sr_sendcnt = 0;
+ rqp->sr_cmd = cmd;
+ mb_done(mbp);
+ md_done(&rqp->sr_rp);
+ error = mb_init(mbp);
+ if (error)
+ return (error);
+ mb_put_mem(mbp, SMB_SIGNATURE, SMB_SIGLEN, MB_MSYSTEM);
+ mb_put_uint8(mbp, cmd);
+ mb_put_uint32le(mbp, 0);
+ mb_put_uint8(mbp, vcp->vc_hflags);
+ if (cmd == SMB_COM_TRANSACTION || cmd == SMB_COM_TRANSACTION_SECONDARY)
+ mb_put_uint16le(mbp, (vcp->vc_hflags2 & ~SMB_FLAGS2_UNICODE));
+ else
+ mb_put_uint16le(mbp, vcp->vc_hflags2);
+ mb_put_mem(mbp, tzero, 12, MB_MSYSTEM);
+ ptr = mb_reserve(mbp, sizeof (u_int16_t));
+ /*LINTED*/
+ ASSERT(ptr == (caddr_t)((u_int16_t *)ptr));
+ /*LINTED*/
+ rqp->sr_rqtid = (u_int16_t *)ptr;
+ mb_put_uint16le(mbp, (u_int16_t)(pid));
+ ptr = mb_reserve(mbp, sizeof (u_int16_t));
+ /*LINTED*/
+ ASSERT(ptr == (caddr_t)((u_int16_t *)ptr));
+ /*LINTED*/
+ rqp->sr_rquid = (u_int16_t *)ptr;
+ mb_put_uint16le(mbp, rqp->sr_mid);
+ return (0);
+}
+
+void
+smb_rq_done(struct smb_rq *rqp)
+{
+ /* No locks. Last ref. here. */
+ if (rqp->sr_flags & SMBR_VCREF) {
+ rqp->sr_flags &= ~SMBR_VCREF;
+ smb_vc_rele(rqp->sr_vc);
+ }
+ mb_done(&rqp->sr_rq);
+ md_done(&rqp->sr_rp);
+ mutex_destroy(&rqp->sr_lock);
+ cv_destroy(&rqp->sr_cond);
+ if (rqp->sr_flags & SMBR_ALLOCED)
+ kmem_free(rqp, sizeof (*rqp));
+}
+
+/*
+ * Simple request-reply exchange
+ */
+int
+smb_rq_simple_timed(struct smb_rq *rqp, int timeout)
+{
+ int error = EINVAL;
+
+ for (; ; ) {
+ /*
+ * Don't send any new requests if force unmount is underway.
+ * This check was moved into smb_rq_enqueue.
+ */
+ rqp->sr_flags &= ~SMBR_RESTART;
+ rqp->sr_timo = timeout; /* in seconds */
+ rqp->sr_state = SMBRQ_NOTSENT;
+ error = smb_rq_enqueue(rqp);
+ if (error) {
+ break;
+ }
+ error = smb_rq_reply(rqp);
+ if (!error)
+ break;
+ if ((rqp->sr_flags & (SMBR_RESTART | SMBR_NORESTART)) !=
+ SMBR_RESTART)
+ break;
+ if (rqp->sr_rexmit <= 0)
+ break;
+ SMBRQ_LOCK(rqp);
+ if (rqp->sr_share && rqp->sr_share->ss_mount) {
+ cv_timedwait(&rqp->sr_cond, &(rqp)->sr_lock,
+ lbolt + (hz * SMB_RCNDELAY));
+
+ } else {
+ delay(lbolt + (hz * SMB_RCNDELAY));
+ }
+ SMBRQ_UNLOCK(rqp);
+ rqp->sr_rexmit--;
+#ifdef XXX
+ timeout *= 2;
+#endif
+ }
+ return (error);
+}
+
+
+int
+smb_rq_simple(struct smb_rq *rqp)
+{
+ return (smb_rq_simple_timed(rqp, smb_timo_default));
+}
+
+static int
+smb_rq_enqueue(struct smb_rq *rqp)
+{
+ struct smb_vc *vcp = rqp->sr_vc;
+ struct smb_share *ssp = rqp->sr_share;
+ int error = 0;
+
+ /*
+ * Unfortunate special case needed for
+ * tree disconnect, which needs sr_share
+ * but should skip the reconnect check.
+ */
+ if (rqp->sr_cmd == SMB_COM_TREE_DISCONNECT)
+ ssp = NULL;
+
+ /*
+ * If this is an "internal" request, bypass any
+ * wait for connection state changes, etc.
+ * This request is making those changes.
+ */
+ if (rqp->sr_flags & SMBR_INTERNAL) {
+ ASSERT(ssp == NULL);
+ goto just_doit;
+ }
+
+ /*
+ * Wait for VC reconnect to finish...
+ * XXX: Deal with reconnect later.
+ * Just bail out for now.
+ *
+ * MacOS might check vfs_isforce() here.
+ */
+ if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
+ SMBSDEBUG("bad vc_state=%d\n", vcp->vc_state);
+ return (ENOTCONN);
+ }
+
+ /*
+ * If this request has a "share" object:
+ * 1: Deny access if share is _GONE (unmounted)
+ * 2: Wait for state changes in that object,
+ * Initiate share (re)connect if needed.
+ * XXX: Not really doing 2 yet.
+ */
+ if (ssp) {
+ if (ssp->ss_flags & SMBS_GONE)
+ return (ENOTCONN);
+ SMB_SS_LOCK(ssp);
+ if (!smb_share_valid(ssp)) {
+ error = smb_share_tcon(ssp);
+ }
+ SMB_SS_UNLOCK(ssp);
+ }
+
+ if (!error) {
+ just_doit:
+ error = smb_iod_addrq(rqp);
+ }
+
+ return (error);
+}
+
+/*
+ * Mark location of the word count, which is filled in later by
+ * smb_rw_wend(). Also initialize the counter that it uses
+ * to figure out what value to fill in.
+ *
+ * Note that the word count happens to be 8-bit.
+ */
+void
+smb_rq_wstart(struct smb_rq *rqp)
+{
+ rqp->sr_wcount = mb_reserve(&rqp->sr_rq, sizeof (uint8_t));
+ rqp->sr_rq.mb_count = 0;
+}
+
+void
+smb_rq_wend(struct smb_rq *rqp)
+{
+ uint_t wcnt;
+
+ if (rqp->sr_wcount == NULL) {
+ SMBSDEBUG("no wcount\n");
+ return;
+ }
+ wcnt = rqp->sr_rq.mb_count;
+ if (wcnt > 0x1ff)
+ SMBSDEBUG("word count too large (%d)\n", wcnt);
+ if (wcnt & 1)
+ SMBSDEBUG("odd word count\n");
+ /* Fill in the word count (8-bits) */
+ *rqp->sr_wcount = (wcnt >> 1);
+}
+
+/*
+ * Mark location of the byte count, which is filled in later by
+ * smb_rw_bend(). Also initialize the counter that it uses
+ * to figure out what value to fill in.
+ *
+ * Note that the byte count happens to be 16-bit.
+ */
+void
+smb_rq_bstart(struct smb_rq *rqp)
+{
+ rqp->sr_bcount = mb_reserve(&rqp->sr_rq, sizeof (uint16_t));
+ rqp->sr_rq.mb_count = 0;
+}
+
+void
+smb_rq_bend(struct smb_rq *rqp)
+{
+ uint_t bcnt;
+
+ if (rqp->sr_bcount == NULL) {
+ SMBSDEBUG("no bcount\n");
+ return;
+ }
+ bcnt = rqp->sr_rq.mb_count;
+ if (bcnt > 0xffff)
+ SMBSDEBUG("byte count too large (%d)\n", bcnt);
+ /*
+ * Fill in the byte count (16-bits)
+ * The pointer is char * type due to
+ * typical off-by-one alignment.
+ */
+ rqp->sr_bcount[0] = bcnt & 0xFF;
+ rqp->sr_bcount[1] = (bcnt >> 8);
+}
+
+int
+smb_rq_intr(struct smb_rq *rqp)
+{
+ if (rqp->sr_flags & SMBR_INTR)
+ return (EINTR);
+
+ return (0);
+#ifdef APPLE
+ return (smb_sigintr(rqp->sr_cred->scr_vfsctx));
+#endif
+}
+
+int
+smb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp)
+{
+ *mbpp = &rqp->sr_rq;
+ return (0);
+}
+
+int
+smb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp)
+{
+ *mbpp = &rqp->sr_rp;
+ return (0);
+}
+
+static int
+smb_rq_getenv(struct smb_connobj *co,
+ struct smb_vc **vcpp, struct smb_share **sspp)
+{
+ struct smb_vc *vcp = NULL;
+ struct smb_share *ssp = NULL;
+ int error = 0;
+
+ if (co->co_flags & SMBO_GONE) {
+ SMBSDEBUG("zombie CO\n");
+ error = EINVAL;
+ goto out;
+ }
+
+ switch (co->co_level) {
+ case SMBL_VC:
+ vcp = CPTOVC(co);
+ if (co->co_parent == NULL) {
+ SMBSDEBUG("zombie VC %s\n", vcp->vc_srvname);
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ case SMBL_SHARE:
+ ssp = CPTOSS(co);
+ if (co->co_parent == NULL) {
+ SMBSDEBUG("zombie share %s\n", ssp->ss_name);
+ error = EINVAL;
+ break;
+ }
+ error = smb_rq_getenv(co->co_parent, &vcp, NULL);
+ break;
+ default:
+ SMBSDEBUG("invalid level %d passed\n", co->co_level);
+ error = EINVAL;
+ }
+
+out:
+ if (!error) {
+ if (vcpp)
+ *vcpp = vcp;
+ if (sspp)
+ *sspp = ssp;
+ }
+
+ return (error);
+}
+
+/*
+ * Wait for reply on the request
+ */
+static int
+smb_rq_reply(struct smb_rq *rqp)
+{
+ struct mdchain *mdp = &rqp->sr_rp;
+ u_int32_t tdw;
+ u_int8_t tb;
+ int error, rperror = 0;
+
+ if (rqp->sr_timo == SMBNOREPLYWAIT)
+ return (smb_iod_removerq(rqp));
+
+ error = smb_iod_waitrq(rqp);
+ if (error)
+ return (error);
+ error = md_get_uint32(mdp, &tdw);
+ if (error)
+ return (error);
+ error = md_get_uint8(mdp, &tb);
+ error = md_get_uint32le(mdp, &rqp->sr_error);
+ error = md_get_uint8(mdp, &rqp->sr_rpflags);
+ error = md_get_uint16le(mdp, &rqp->sr_rpflags2);
+ if (rqp->sr_rpflags2 & SMB_FLAGS2_ERR_STATUS) {
+ /*
+ * Do a special check for STATUS_BUFFER_OVERFLOW;
+ * it's not an error.
+ */
+ if (rqp->sr_error == NT_STATUS_BUFFER_OVERFLOW) {
+ /*
+ * Don't report it as an error to our caller;
+ * they can look at rqp->sr_error if they
+ * need to know whether we got a
+ * STATUS_BUFFER_OVERFLOW.
+ * XXX - should we do that for all errors
+ * where (error & 0xC0000000) is 0x80000000,
+ * i.e. all warnings?
+ */
+ rperror = 0;
+ } else
+ rperror = smb_maperr32(rqp->sr_error);
+ } else {
+ rqp->sr_errclass = rqp->sr_error & 0xff;
+ rqp->sr_serror = rqp->sr_error >> 16;
+ rperror = smb_maperror(rqp->sr_errclass, rqp->sr_serror);
+ }
+ if (rperror == EMOREDATA) {
+ rperror = E2BIG;
+ rqp->sr_flags |= SMBR_MOREDATA;
+ } else
+ rqp->sr_flags &= ~SMBR_MOREDATA;
+
+ error = md_get_uint32(mdp, &tdw);
+ error = md_get_uint32(mdp, &tdw);
+ error = md_get_uint32(mdp, &tdw);
+
+ error = md_get_uint16le(mdp, &rqp->sr_rptid);
+ error = md_get_uint16le(mdp, &rqp->sr_rppid);
+ error = md_get_uint16le(mdp, &rqp->sr_rpuid);
+ error = md_get_uint16le(mdp, &rqp->sr_rpmid);
+
+ SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x, E: %d:%d\n",
+ rqp->sr_rpmid, rqp->sr_rppid, rqp->sr_rpuid, rqp->sr_rptid,
+ rqp->sr_errclass, rqp->sr_serror);
+
+ return ((error) ? error : rperror);
+}
+
+
+#define ALIGN4(a) (((a) + 3) & ~3)
+
+/*
+ * TRANS2 request implementation
+ * TRANS implementation is in the "t2" routines
+ * NT_TRANSACTION implementation is the separate "nt" stuff
+ */
+int
+smb_t2_alloc(struct smb_connobj *layer, ushort_t setup, struct smb_cred *scred,
+ struct smb_t2rq **t2pp)
+{
+ struct smb_t2rq *t2p;
+ int error;
+
+ t2p = (struct smb_t2rq *)kmem_alloc(sizeof (*t2p), KM_SLEEP);
+ if (t2p == NULL)
+ return (ENOMEM);
+ error = smb_t2_init(t2p, layer, &setup, 1, scred);
+ mutex_init(&t2p->t2_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&t2p->t2_cond, NULL, CV_DEFAULT, NULL);
+ t2p->t2_flags |= SMBT2_ALLOCED;
+ if (error) {
+ smb_t2_done(t2p);
+ return (error);
+ }
+ *t2pp = t2p;
+ return (0);
+}
+
+int
+smb_nt_alloc(struct smb_connobj *layer, ushort_t fn, struct smb_cred *scred,
+ struct smb_ntrq **ntpp)
+{
+ struct smb_ntrq *ntp;
+ int error;
+
+ ntp = (struct smb_ntrq *)kmem_alloc(sizeof (*ntp), KM_SLEEP);
+ if (ntp == NULL)
+ return (ENOMEM);
+ error = smb_nt_init(ntp, layer, fn, scred);
+ mutex_init(&ntp->nt_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&ntp->nt_cond, NULL, CV_DEFAULT, NULL);
+ ntp->nt_flags |= SMBT2_ALLOCED;
+ if (error) {
+ smb_nt_done(ntp);
+ return (error);
+ }
+ *ntpp = ntp;
+ return (0);
+}
+
+int
+smb_t2_init(struct smb_t2rq *t2p, struct smb_connobj *source, ushort_t *setup,
+ int setupcnt, struct smb_cred *scred)
+{
+ int i;
+ int error;
+
+ bzero(t2p, sizeof (*t2p));
+ t2p->t2_source = source;
+ t2p->t2_setupcount = (u_int16_t)setupcnt;
+ t2p->t2_setupdata = t2p->t2_setup;
+ for (i = 0; i < setupcnt; i++)
+ t2p->t2_setup[i] = setup[i];
+ t2p->t2_fid = 0xffff;
+ t2p->t2_cred = scred;
+ t2p->t2_share = (source->co_level == SMBL_SHARE ?
+ CPTOSS(source) : NULL); /* for smb up/down */
+ error = smb_rq_getenv(source, &t2p->t2_vc, NULL);
+ if (error)
+ return (error);
+ return (0);
+}
+
+int
+smb_nt_init(struct smb_ntrq *ntp, struct smb_connobj *source, ushort_t fn,
+ struct smb_cred *scred)
+{
+ int error;
+
+ bzero(ntp, sizeof (*ntp));
+ ntp->nt_source = source;
+ ntp->nt_function = fn;
+ ntp->nt_cred = scred;
+ ntp->nt_share = (source->co_level == SMBL_SHARE ?
+ CPTOSS(source) : NULL); /* for smb up/down */
+ error = smb_rq_getenv(source, &ntp->nt_vc, NULL);
+ if (error)
+ return (error);
+ return (0);
+}
+
+void
+smb_t2_done(struct smb_t2rq *t2p)
+{
+ mb_done(&t2p->t2_tparam);
+ mb_done(&t2p->t2_tdata);
+ md_done(&t2p->t2_rparam);
+ md_done(&t2p->t2_rdata);
+ mutex_destroy(&t2p->t2_lock);
+ cv_destroy(&t2p->t2_cond);
+#ifdef NOTYETRESOLVED
+ if (t2p->t2_flags & SMBT2_ALLOCED)
+ kmem_free(t2p, sizeof (*t2p));
+#endif
+ if (t2p) {
+ kmem_free(t2p, sizeof (*t2p));
+ }
+}
+
+u_int32_t
+smb_t2_err(struct smb_t2rq *t2p)
+{
+ /* mask off "severity" and the "component" bit */
+ return (t2p->t2_sr_error & ~(0xe0000000));
+}
+
+void
+smb_nt_done(struct smb_ntrq *ntp)
+{
+ mb_done(&ntp->nt_tsetup);
+ mb_done(&ntp->nt_tparam);
+ mb_done(&ntp->nt_tdata);
+ md_done(&ntp->nt_rparam);
+ md_done(&ntp->nt_rdata);
+ cv_destroy(&ntp->nt_cond);
+ mutex_destroy(&ntp->nt_lock);
+ if (ntp->nt_flags & SMBT2_ALLOCED)
+ kmem_free(ntp, sizeof (*ntp));
+}
+
+/*
+ * Extract data [offset,count] from mtop and add to mdp.
+ */
+static int
+smb_t2_placedata(mblk_t *mtop, u_int16_t offset, u_int16_t count,
+ struct mdchain *mdp)
+{
+ mblk_t *n;
+
+ n = m_copym(mtop, offset, count, M_WAITOK);
+ if (n == NULL)
+ return (EBADRPC);
+
+ if (mdp->md_top == NULL) {
+ md_initm(mdp, n);
+ } else
+ m_cat(mdp->md_top, n);
+
+ return (0);
+}
+
+static int
+smb_t2_reply(struct smb_t2rq *t2p)
+{
+ struct mdchain *mdp;
+ struct smb_rq *rqp = t2p->t2_rq;
+ int error, error2, totpgot, totdgot;
+ u_int16_t totpcount, totdcount, pcount, poff, doff, pdisp, ddisp;
+ u_int16_t tmp, bc, dcount;
+ u_int8_t wc;
+
+ t2p->t2_flags &= ~SMBT2_MOREDATA;
+
+ error = smb_rq_reply(rqp);
+ if (rqp->sr_flags & SMBR_MOREDATA)
+ t2p->t2_flags |= SMBT2_MOREDATA;
+ t2p->t2_sr_errclass = rqp->sr_errclass;
+ t2p->t2_sr_serror = rqp->sr_serror;
+ t2p->t2_sr_error = rqp->sr_error;
+ t2p->t2_sr_rpflags2 = rqp->sr_rpflags2;
+ if (error && !(rqp->sr_flags & SMBR_MOREDATA))
+ return (error);
+ /*
+ * Now we have to get all subseqent responses, if any.
+ * The CIFS specification says that they can be misordered,
+ * which is weird.
+ * TODO: timo
+ */
+ totpgot = totdgot = 0;
+ totpcount = totdcount = 0xffff;
+ mdp = &rqp->sr_rp;
+ for (;;) {
+ DTRACE_PROBE2(smb_trans_reply,
+ (smb_rq_t *), rqp, (mblk_t *), mdp->md_top);
+ m_dumpm(mdp->md_top);
+
+ if ((error2 = md_get_uint8(mdp, &wc)) != 0)
+ break;
+ if (wc < 10) {
+ error2 = ENOENT;
+ break;
+ }
+ if ((error2 = md_get_uint16le(mdp, &tmp)) != 0)
+ break;
+ if (totpcount > tmp)
+ totpcount = tmp;
+ if ((error2 = md_get_uint16le(mdp, &tmp)) != 0)
+ break;
+ if (totdcount > tmp)
+ totdcount = tmp;
+ if ((error2 = md_get_uint16le(mdp, &tmp)) != 0 || /* reserved */
+ (error2 = md_get_uint16le(mdp, &pcount)) != 0 ||
+ (error2 = md_get_uint16le(mdp, &poff)) != 0 ||
+ (error2 = md_get_uint16le(mdp, &pdisp)) != 0)
+ break;
+ if (pcount != 0 && pdisp != totpgot) {
+ SMBSDEBUG("Can't handle misordered parameters %d:%d\n",
+ pdisp, totpgot);
+ error2 = EINVAL;
+ break;
+ }
+ if ((error2 = md_get_uint16le(mdp, &dcount)) != 0 ||
+ (error2 = md_get_uint16le(mdp, &doff)) != 0 ||
+ (error2 = md_get_uint16le(mdp, &ddisp)) != 0)
+ break;
+ if (dcount != 0 && ddisp != totdgot) {
+ SMBSDEBUG("Can't handle misordered data: dcount %d\n",
+ dcount);
+ error2 = EINVAL;
+ break;
+ }
+
+ /* XXX: Skip setup words? We don't save them? */
+ md_get_uint8(mdp, &wc); /* SetupCount */
+ md_get_uint8(mdp, NULL); /* Reserved2 */
+ tmp = wc;
+ while (tmp--)
+ md_get_uint16(mdp, NULL);
+
+ if ((error2 = md_get_uint16le(mdp, &bc)) != 0)
+ break;
+
+ /*
+ * There are pad bytes here, and the poff value
+ * indicates where the next data are found.
+ * No need to guess at the padding size.
+ */
+ if (pcount) {
+ error2 = smb_t2_placedata(mdp->md_top, poff,
+ pcount, &t2p->t2_rparam);
+ if (error2)
+ break;
+ }
+ totpgot += pcount;
+
+ if (dcount) {
+ error2 = smb_t2_placedata(mdp->md_top, doff,
+ dcount, &t2p->t2_rdata);
+ if (error2)
+ break;
+ }
+ totdgot += dcount;
+
+ if (totpgot >= totpcount && totdgot >= totdcount) {
+ error2 = 0;
+ t2p->t2_flags |= SMBT2_ALLRECV;
+ break;
+ }
+ /*
+ * We're done with this reply, look for the next one.
+ */
+ SMBRQ_LOCK(rqp);
+ md_next_record(&rqp->sr_rp);
+ SMBRQ_UNLOCK(rqp);
+ error2 = smb_rq_reply(rqp);
+ if (rqp->sr_flags & SMBR_MOREDATA)
+ t2p->t2_flags |= SMBT2_MOREDATA;
+ if (!error2)
+ continue;
+ t2p->t2_sr_errclass = rqp->sr_errclass;
+ t2p->t2_sr_serror = rqp->sr_serror;
+ t2p->t2_sr_error = rqp->sr_error;
+ t2p->t2_sr_rpflags2 = rqp->sr_rpflags2;
+ error = error2;
+ if (!(rqp->sr_flags & SMBR_MOREDATA))
+ break;
+ }
+ return (error ? error : error2);
+}
+
+static int
+smb_nt_reply(struct smb_ntrq *ntp)
+{
+ struct mdchain *mdp;
+ struct smb_rq *rqp = ntp->nt_rq;
+ int error, error2;
+ u_int32_t totpcount, totdcount, pcount, poff, doff, pdisp, ddisp;
+ u_int32_t tmp, dcount, totpgot, totdgot;
+ u_int16_t bc;
+ u_int8_t wc;
+
+ ntp->nt_flags &= ~SMBT2_MOREDATA;
+
+ error = smb_rq_reply(rqp);
+ if (rqp->sr_flags & SMBR_MOREDATA)
+ ntp->nt_flags |= SMBT2_MOREDATA;
+ ntp->nt_sr_error = rqp->sr_error;
+ ntp->nt_sr_rpflags2 = rqp->sr_rpflags2;
+ if (error && !(rqp->sr_flags & SMBR_MOREDATA))
+ return (error);
+ /*
+ * Now we have to get all subseqent responses. The CIFS specification
+ * says that they can be misordered which is weird.
+ * TODO: timo
+ */
+ totpgot = totdgot = 0;
+ totpcount = totdcount = 0xffffffff;
+ mdp = &rqp->sr_rp;
+ for (;;) {
+ DTRACE_PROBE2(smb_trans_reply,
+ (smb_rq_t *), rqp, (mblk_t *), mdp->md_top);
+ m_dumpm(mdp->md_top);
+
+ if ((error2 = md_get_uint8(mdp, &wc)) != 0)
+ break;
+ if (wc < 18) {
+ error2 = ENOENT;
+ break;
+ }
+ md_get_mem(mdp, NULL, 3, MB_MSYSTEM); /* reserved */
+ if ((error2 = md_get_uint32le(mdp, &tmp)) != 0)
+ break;
+ if (totpcount > tmp)
+ totpcount = tmp;
+ if ((error2 = md_get_uint32le(mdp, &tmp)) != 0)
+ break;
+ if (totdcount > tmp)
+ totdcount = tmp;
+ if ((error2 = md_get_uint32le(mdp, &pcount)) != 0 ||
+ (error2 = md_get_uint32le(mdp, &poff)) != 0 ||
+ (error2 = md_get_uint32le(mdp, &pdisp)) != 0)
+ break;
+ if (pcount != 0 && pdisp != totpgot) {
+ SMBSDEBUG("Can't handle misordered parameters %d:%d\n",
+ pdisp, totpgot);
+ error2 = EINVAL;
+ break;
+ }
+ if ((error2 = md_get_uint32le(mdp, &dcount)) != 0 ||
+ (error2 = md_get_uint32le(mdp, &doff)) != 0 ||
+ (error2 = md_get_uint32le(mdp, &ddisp)) != 0)
+ break;
+ if (dcount != 0 && ddisp != totdgot) {
+ SMBSDEBUG("Can't handle misordered data: dcount %d\n",
+ dcount);
+ error2 = EINVAL;
+ break;
+ }
+
+ /* XXX: Skip setup words? We don't save them? */
+ md_get_uint8(mdp, &wc); /* SetupCount */
+ tmp = wc;
+ while (tmp--)
+ md_get_uint16(mdp, NULL);
+
+ if ((error2 = md_get_uint16le(mdp, &bc)) != 0)
+ break;
+
+ /*
+ * There are pad bytes here, and the poff value
+ * indicates where the next data are found.
+ * No need to guess at the padding size.
+ */
+ if (pcount) {
+ error2 = smb_t2_placedata(mdp->md_top, poff, pcount,
+ &ntp->nt_rparam);
+ if (error2)
+ break;
+ }
+ totpgot += pcount;
+
+ if (dcount) {
+ error2 = smb_t2_placedata(mdp->md_top, doff, dcount,
+ &ntp->nt_rdata);
+ if (error2)
+ break;
+ }
+ totdgot += dcount;
+
+ if (totpgot >= totpcount && totdgot >= totdcount) {
+ error2 = 0;
+ ntp->nt_flags |= SMBT2_ALLRECV;
+ break;
+ }
+ /*
+ * We're done with this reply, look for the next one.
+ */
+ SMBRQ_LOCK(rqp);
+ md_next_record(&rqp->sr_rp);
+ SMBRQ_UNLOCK(rqp);
+ error2 = smb_rq_reply(rqp);
+ if (rqp->sr_flags & SMBR_MOREDATA)
+ ntp->nt_flags |= SMBT2_MOREDATA;
+ if (!error2)
+ continue;
+ ntp->nt_sr_error = rqp->sr_error;
+ ntp->nt_sr_rpflags2 = rqp->sr_rpflags2;
+ error = error2;
+ if (!(rqp->sr_flags & SMBR_MOREDATA))
+ break;
+ }
+ return (error ? error : error2);
+}
+
+int md_get_mbuf(struct mdchain *mdp, int size, mblk_t **ret);
+int mb_put_mbuf(struct mbchain *mbp, mblk_t *m);
+
+/*
+ * Perform a full round of TRANS2 request
+ */
+static int
+smb_t2_request_int(struct smb_t2rq *t2p)
+{
+ struct smb_vc *vcp = t2p->t2_vc;
+ struct smb_cred *scred = t2p->t2_cred;
+ struct mbchain *mbp;
+ struct mdchain *mdp, mbparam, mbdata;
+ mblk_t *m;
+ struct smb_rq *rqp;
+ int totpcount, leftpcount, totdcount, leftdcount, len, txmax, i;
+ int error, doff, poff, txdcount, txpcount, nmlen;
+
+ m = t2p->t2_tparam.mb_top;
+ if (m) {
+ md_initm(&mbparam, m); /* do not free it! */
+ totpcount = m_fixhdr(m);
+ if (totpcount > 0xffff) /* maxvalue for ushort_t */
+ return (EINVAL);
+ } else
+ totpcount = 0;
+ m = t2p->t2_tdata.mb_top;
+ if (m) {
+ md_initm(&mbdata, m); /* do not free it! */
+ totdcount = m_fixhdr(m);
+ if (totdcount > 0xffff)
+ return (EINVAL);
+ } else
+ totdcount = 0;
+ leftdcount = totdcount;
+ leftpcount = totpcount;
+ txmax = vcp->vc_txmax;
+ error = smb_rq_alloc(t2p->t2_source, t2p->t_name[0] ?
+ SMB_COM_TRANSACTION : SMB_COM_TRANSACTION2, scred, &rqp);
+ if (error)
+ return (error);
+ rqp->sr_timo = smb_timo_default;
+ rqp->sr_flags |= SMBR_MULTIPACKET;
+ t2p->t2_rq = rqp;
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, totpcount);
+ mb_put_uint16le(mbp, totdcount);
+ mb_put_uint16le(mbp, t2p->t2_maxpcount);
+ mb_put_uint16le(mbp, t2p->t2_maxdcount);
+ mb_put_uint8(mbp, t2p->t2_maxscount);
+ mb_put_uint8(mbp, 0); /* reserved */
+ mb_put_uint16le(mbp, 0); /* flags */
+ mb_put_uint32le(mbp, 0); /* Timeout */
+ mb_put_uint16le(mbp, 0); /* reserved 2 */
+ len = mb_fixhdr(mbp);
+
+ /*
+ * now we have known packet size as
+ * ALIGN4(len + 5 * 2 + setupcount * 2 + 2 + strlen(name) + 1),
+ * and need to decide which parts should go into the first request
+ */
+ nmlen = t2p->t_name ? strlen(t2p->t_name) : 0;
+ len = ALIGN4(len + 5 * 2 + t2p->t2_setupcount * 2 + 2 + nmlen + 1);
+ if (len + leftpcount > txmax) {
+ txpcount = min(leftpcount, txmax - len);
+ poff = len;
+ txdcount = 0;
+ doff = 0;
+ } else {
+ txpcount = leftpcount;
+ poff = txpcount ? len : 0;
+ /*
+ * Other client traffic seems to "ALIGN2" here. The extra
+ * 2 byte pad we use has no observed downside and may be
+ * required for some old servers(?)
+ */
+ len = ALIGN4(len + txpcount);
+ txdcount = min(leftdcount, txmax - len);
+ doff = txdcount ? len : 0;
+ }
+ leftpcount -= txpcount;
+ leftdcount -= txdcount;
+ mb_put_uint16le(mbp, txpcount);
+ mb_put_uint16le(mbp, poff);
+ mb_put_uint16le(mbp, txdcount);
+ mb_put_uint16le(mbp, doff);
+ mb_put_uint8(mbp, t2p->t2_setupcount);
+ mb_put_uint8(mbp, 0);
+ for (i = 0; i < t2p->t2_setupcount; i++) {
+ mb_put_uint16le(mbp, t2p->t2_setupdata[i]);
+ }
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ /* TDUNICODE */
+ if (t2p->t_name)
+ mb_put_mem(mbp, t2p->t_name, nmlen, MB_MSYSTEM);
+ mb_put_uint8(mbp, 0); /* terminating zero */
+ len = mb_fixhdr(mbp);
+ if (txpcount) {
+ mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
+ error = md_get_mbuf(&mbparam, txpcount, &m);
+ SMBSDEBUG("%d:%d:%d\n", error, txpcount, txmax);
+ if (error)
+ goto freerq;
+ mb_put_mbuf(mbp, m);
+ }
+ len = mb_fixhdr(mbp);
+ if (txdcount) {
+ mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
+ error = md_get_mbuf(&mbdata, txdcount, &m);
+ if (error)
+ goto freerq;
+ mb_put_mbuf(mbp, m);
+ }
+ smb_rq_bend(rqp); /* incredible, but thats it... */
+ error = smb_rq_enqueue(rqp);
+ if (error)
+ goto freerq;
+ if (leftpcount || leftdcount) {
+ error = smb_rq_reply(rqp);
+ if (error)
+ goto bad;
+ /*
+ * this is an interim response, ignore it.
+ */
+ SMBRQ_LOCK(rqp);
+ md_next_record(&rqp->sr_rp);
+ SMBRQ_UNLOCK(rqp);
+ }
+ while (leftpcount || leftdcount) {
+ error = smb_rq_new(rqp, t2p->t_name ?
+ SMB_COM_TRANSACTION_SECONDARY :
+ SMB_COM_TRANSACTION2_SECONDARY);
+ if (error)
+ goto bad;
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, totpcount);
+ mb_put_uint16le(mbp, totdcount);
+ len = mb_fixhdr(mbp);
+ /*
+ * now we have known packet size as
+ * ALIGN4(len + 7 * 2 + 2) for T2 request, and -2 for T one,
+ * and need to decide which parts should go into request
+ */
+ len = ALIGN4(len + 6 * 2 + 2);
+ if (t2p->t_name == NULL)
+ len += 2;
+ if (len + leftpcount > txmax) {
+ txpcount = min(leftpcount, txmax - len);
+ poff = len;
+ txdcount = 0;
+ doff = 0;
+ } else {
+ txpcount = leftpcount;
+ poff = txpcount ? len : 0;
+ len = ALIGN4(len + txpcount);
+ txdcount = min(leftdcount, txmax - len);
+ doff = txdcount ? len : 0;
+ }
+ mb_put_uint16le(mbp, txpcount);
+ mb_put_uint16le(mbp, poff);
+ mb_put_uint16le(mbp, totpcount - leftpcount);
+ mb_put_uint16le(mbp, txdcount);
+ mb_put_uint16le(mbp, doff);
+ mb_put_uint16le(mbp, totdcount - leftdcount);
+ leftpcount -= txpcount;
+ leftdcount -= txdcount;
+ if (t2p->t_name == NULL)
+ mb_put_uint16le(mbp, t2p->t2_fid);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, 0); /* name */
+ len = mb_fixhdr(mbp);
+ if (txpcount) {
+ mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
+ error = md_get_mbuf(&mbparam, txpcount, &m);
+ if (error)
+ goto bad;
+ mb_put_mbuf(mbp, m);
+ }
+ len = mb_fixhdr(mbp);
+ if (txdcount) {
+ mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
+ error = md_get_mbuf(&mbdata, txdcount, &m);
+ if (error)
+ goto bad;
+ mb_put_mbuf(mbp, m);
+ }
+ smb_rq_bend(rqp);
+ error = smb_iod_multirq(rqp);
+ if (error)
+ goto bad;
+ } /* while left params or data */
+ error = smb_t2_reply(t2p);
+ if (error && !(t2p->t2_flags & SMBT2_MOREDATA))
+ goto bad;
+ mdp = &t2p->t2_rdata;
+ if (mdp->md_top) {
+ m_fixhdr(mdp->md_top);
+ md_initm(mdp, mdp->md_top);
+ }
+ mdp = &t2p->t2_rparam;
+ if (mdp->md_top) {
+ m_fixhdr(mdp->md_top);
+ md_initm(mdp, mdp->md_top);
+ }
+bad:
+ smb_iod_removerq(rqp);
+freerq:
+ if (error && !(t2p->t2_flags & SMBT2_MOREDATA)) {
+ if (rqp->sr_flags & SMBR_RESTART)
+ t2p->t2_flags |= SMBT2_RESTART;
+ md_done(&t2p->t2_rparam);
+ md_done(&t2p->t2_rdata);
+ }
+ smb_rq_done(rqp);
+ return (error);
+}
+
+
+/*
+ * Perform a full round of NT_TRANSACTION request
+ */
+static int
+smb_nt_request_int(struct smb_ntrq *ntp)
+{
+ struct smb_vc *vcp = ntp->nt_vc;
+ struct smb_cred *scred = ntp->nt_cred;
+ struct mbchain *mbp;
+ struct mdchain *mdp, mbsetup, mbparam, mbdata;
+ mblk_t *m;
+ struct smb_rq *rqp;
+ int totpcount, leftpcount, totdcount, leftdcount, len, txmax;
+ int error, doff, poff, txdcount, txpcount;
+ int totscount;
+
+ m = ntp->nt_tsetup.mb_top;
+ if (m) {
+ md_initm(&mbsetup, m); /* do not free it! */
+ totscount = m_fixhdr(m);
+ if (totscount > 2 * 0xff)
+ return (EINVAL);
+ } else
+ totscount = 0;
+ m = ntp->nt_tparam.mb_top;
+ if (m) {
+ md_initm(&mbparam, m); /* do not free it! */
+ totpcount = m_fixhdr(m);
+ if (totpcount > 0x7fffffff)
+ return (EINVAL);
+ } else
+ totpcount = 0;
+ m = ntp->nt_tdata.mb_top;
+ if (m) {
+ md_initm(&mbdata, m); /* do not free it! */
+ totdcount = m_fixhdr(m);
+ if (totdcount > 0x7fffffff)
+ return (EINVAL);
+ } else
+ totdcount = 0;
+ leftdcount = totdcount;
+ leftpcount = totpcount;
+ txmax = vcp->vc_txmax;
+ error = smb_rq_alloc(ntp->nt_source, SMB_COM_NT_TRANSACT, scred, &rqp);
+ if (error)
+ return (error);
+ rqp->sr_timo = smb_timo_default;
+ rqp->sr_flags |= SMBR_MULTIPACKET;
+ ntp->nt_rq = rqp;
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ mb_put_uint8(mbp, ntp->nt_maxscount);
+ mb_put_uint16le(mbp, 0); /* reserved (flags?) */
+ mb_put_uint32le(mbp, totpcount);
+ mb_put_uint32le(mbp, totdcount);
+ mb_put_uint32le(mbp, ntp->nt_maxpcount);
+ mb_put_uint32le(mbp, ntp->nt_maxdcount);
+ len = mb_fixhdr(mbp);
+ /*
+ * now we have known packet size as
+ * ALIGN4(len + 4 * 4 + 1 + 2 + ((totscount+1)&~1) + 2),
+ * and need to decide which parts should go into the first request
+ */
+ len = ALIGN4(len + 4 * 4 + 1 + 2 + ((totscount+1)&~1) + 2);
+ if (len + leftpcount > txmax) {
+ txpcount = min(leftpcount, txmax - len);
+ poff = len;
+ txdcount = 0;
+ doff = 0;
+ } else {
+ txpcount = leftpcount;
+ poff = txpcount ? len : 0;
+ len = ALIGN4(len + txpcount);
+ txdcount = min(leftdcount, txmax - len);
+ doff = txdcount ? len : 0;
+ }
+ leftpcount -= txpcount;
+ leftdcount -= txdcount;
+ mb_put_uint32le(mbp, txpcount);
+ mb_put_uint32le(mbp, poff);
+ mb_put_uint32le(mbp, txdcount);
+ mb_put_uint32le(mbp, doff);
+ mb_put_uint8(mbp, (totscount+1)/2);
+ mb_put_uint16le(mbp, ntp->nt_function);
+ if (totscount) {
+ error = md_get_mbuf(&mbsetup, totscount, &m);
+ SMBSDEBUG("%d:%d:%d\n", error, totscount, txmax);
+ if (error)
+ goto freerq;
+ mb_put_mbuf(mbp, m);
+ if (totscount & 1)
+ mb_put_uint8(mbp, 0); /* setup is in words */
+ }
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ len = mb_fixhdr(mbp);
+ if (txpcount) {
+ mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
+ error = md_get_mbuf(&mbparam, txpcount, &m);
+ SMBSDEBUG("%d:%d:%d\n", error, txpcount, txmax);
+ if (error)
+ goto freerq;
+ mb_put_mbuf(mbp, m);
+ }
+ len = mb_fixhdr(mbp);
+ if (txdcount) {
+ mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
+ error = md_get_mbuf(&mbdata, txdcount, &m);
+ if (error)
+ goto freerq;
+ mb_put_mbuf(mbp, m);
+ }
+ smb_rq_bend(rqp); /* incredible, but thats it... */
+ error = smb_rq_enqueue(rqp);
+ if (error)
+ goto freerq;
+ if (leftpcount || leftdcount) {
+ error = smb_rq_reply(rqp);
+ if (error)
+ goto bad;
+ /*
+ * this is an interim response, ignore it.
+ */
+ SMBRQ_LOCK(rqp);
+ md_next_record(&rqp->sr_rp);
+ SMBRQ_UNLOCK(rqp);
+ }
+ while (leftpcount || leftdcount) {
+ error = smb_rq_new(rqp, SMB_COM_NT_TRANSACT_SECONDARY);
+ if (error)
+ goto bad;
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ mb_put_mem(mbp, NULL, 3, MB_MZERO);
+ mb_put_uint32le(mbp, totpcount);
+ mb_put_uint32le(mbp, totdcount);
+ len = mb_fixhdr(mbp);
+ /*
+ * now we have known packet size as
+ * ALIGN4(len + 6 * 4 + 2)
+ * and need to decide which parts should go into request
+ */
+ len = ALIGN4(len + 6 * 4 + 2);
+ if (len + leftpcount > txmax) {
+ txpcount = min(leftpcount, txmax - len);
+ poff = len;
+ txdcount = 0;
+ doff = 0;
+ } else {
+ txpcount = leftpcount;
+ poff = txpcount ? len : 0;
+ len = ALIGN4(len + txpcount);
+ txdcount = min(leftdcount, txmax - len);
+ doff = txdcount ? len : 0;
+ }
+ mb_put_uint32le(mbp, txpcount);
+ mb_put_uint32le(mbp, poff);
+ mb_put_uint32le(mbp, totpcount - leftpcount);
+ mb_put_uint32le(mbp, txdcount);
+ mb_put_uint32le(mbp, doff);
+ mb_put_uint32le(mbp, totdcount - leftdcount);
+ leftpcount -= txpcount;
+ leftdcount -= txdcount;
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ len = mb_fixhdr(mbp);
+ if (txpcount) {
+ mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
+ error = md_get_mbuf(&mbparam, txpcount, &m);
+ if (error)
+ goto bad;
+ mb_put_mbuf(mbp, m);
+ }
+ len = mb_fixhdr(mbp);
+ if (txdcount) {
+ mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
+ error = md_get_mbuf(&mbdata, txdcount, &m);
+ if (error)
+ goto bad;
+ mb_put_mbuf(mbp, m);
+ }
+ smb_rq_bend(rqp);
+ error = smb_iod_multirq(rqp);
+ if (error)
+ goto bad;
+ } /* while left params or data */
+ error = smb_nt_reply(ntp);
+ if (error && !(ntp->nt_flags & SMBT2_MOREDATA))
+ goto bad;
+ mdp = &ntp->nt_rdata;
+ if (mdp->md_top) {
+ m_fixhdr(mdp->md_top);
+ md_initm(mdp, mdp->md_top);
+ }
+ mdp = &ntp->nt_rparam;
+ if (mdp->md_top) {
+ m_fixhdr(mdp->md_top);
+ md_initm(mdp, mdp->md_top);
+ }
+bad:
+ smb_iod_removerq(rqp);
+freerq:
+ if (error && !(ntp->nt_flags & SMBT2_MOREDATA)) {
+ if (rqp->sr_flags & SMBR_RESTART)
+ ntp->nt_flags |= SMBT2_RESTART;
+ md_done(&ntp->nt_rparam);
+ md_done(&ntp->nt_rdata);
+ }
+ smb_rq_done(rqp);
+ return (error);
+}
+
+int
+smb_t2_request(struct smb_t2rq *t2p)
+{
+ int error = EINVAL, i;
+
+ for (i = 0; ; ) {
+ /*
+ * Don't send any new requests if force unmount is underway.
+ * This check was moved into smb_rq_enqueue, called by
+ * smb_t2_request_int()
+ */
+ t2p->t2_flags &= ~SMBT2_RESTART;
+ error = smb_t2_request_int(t2p);
+ if (!error)
+ break;
+ if ((t2p->t2_flags & (SMBT2_RESTART | SMBT2_NORESTART)) !=
+ SMBT2_RESTART)
+ break;
+ if (++i > SMBMAXRESTARTS)
+ break;
+ mutex_enter(&(t2p)->t2_lock);
+ if (t2p->t2_share && t2p->t2_share->ss_mount) {
+ cv_timedwait(&t2p->t2_cond, &(t2p)->t2_lock,
+ lbolt + (hz * SMB_RCNDELAY));
+ } else {
+ delay(lbolt + (hz * SMB_RCNDELAY));
+ }
+ mutex_exit(&(t2p)->t2_lock);
+ }
+ return (error);
+}
+
+
+int
+smb_nt_request(struct smb_ntrq *ntp)
+{
+ int error = EINVAL, i;
+
+ for (i = 0; ; ) {
+ /*
+ * Don't send any new requests if force unmount is underway.
+ * This check was moved into smb_rq_enqueue, called by
+ * smb_nt_request_int()
+ */
+ ntp->nt_flags &= ~SMBT2_RESTART;
+ error = smb_nt_request_int(ntp);
+ if (!error)
+ break;
+ if ((ntp->nt_flags & (SMBT2_RESTART | SMBT2_NORESTART)) !=
+ SMBT2_RESTART)
+ break;
+ if (++i > SMBMAXRESTARTS)
+ break;
+ mutex_enter(&(ntp)->nt_lock);
+ if (ntp->nt_share && ntp->nt_share->ss_mount) {
+ cv_timedwait(&ntp->nt_cond, &(ntp)->nt_lock,
+ lbolt + (hz * SMB_RCNDELAY));
+
+ } else {
+ delay(lbolt + (hz * SMB_RCNDELAY));
+ }
+ mutex_exit(&(ntp)->nt_lock);
+ }
+ return (error);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h
new file mode 100644
index 0000000000..2e90252405
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_rq.h,v 1.9 2005/01/22 22:20:58 lindak Exp $
+ */
+#ifndef _NETSMB_SMB_RQ_H_
+#define _NETSMB_SMB_RQ_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <netsmb/mchain.h>
+#include <sys/queue.h>
+
+#define SMBR_ALLOCED 0x0001 /* structure was malloced */
+#define SMBR_SENT 0x0002 /* request successfully transmitted */
+#define SMBR_REXMIT 0x0004 /* request should be retransmitted */
+#define SMBR_INTR 0x0008 /* request interrupted */
+#define SMBR_RESTART 0x0010 /* req should be repeated if possible */
+#define SMBR_NORESTART 0x0020 /* request is not restartable */
+#define SMBR_MULTIPACKET 0x0040 /* multiple pkts can be sent/received */
+#define SMBR_INTERNAL 0x0080 /* request is internal to netsmb */
+#define SMBR_NOINTR_SEND 0x0100 /* no interrupt in send wait */
+#define SMBR_NOINTR_RECV 0x0200 /* no interrupt in recv wait */
+#define SMBR_SENDWAIT 0x0400 /* waiting for send to complete */
+#define SMBR_VCREF 0x4000 /* took vc reference */
+#define SMBR_MOREDATA 0x8000 /* our buffer was too small */
+
+#define SMBT2_ALLSENT 0x0001 /* all data and params are sent */
+#define SMBT2_ALLRECV 0x0002 /* all data and params are received */
+#define SMBT2_ALLOCED 0x0004
+#define SMBT2_RESTART 0x0008
+#define SMBT2_NORESTART 0x0010
+#define SMBT2_MOREDATA 0x8000 /* our buffer was too small */
+
+#define SMBRQ_LOCK(rqp) mutex_enter(&(rqp)->sr_lock)
+#define SMBRQ_UNLOCK(rqp) mutex_exit(&(rqp)->sr_lock)
+
+
+enum smbrq_state {
+ SMBRQ_NOTSENT, /* rq have data to send */
+ SMBRQ_SENT, /* send procedure completed */
+ SMBRQ_REPLYRECEIVED,
+ SMBRQ_NOTIFIED /* owner notified about completion */
+};
+
+struct smb_vc;
+
+struct smb_rq {
+ TAILQ_ENTRY(smb_rq) sr_link;
+ kmutex_t sr_lock;
+ kcondvar_t sr_cond;
+ enum smbrq_state sr_state;
+ struct smb_vc *sr_vc;
+ struct smb_share *sr_share;
+ struct _kthread *sr_owner;
+ ushort_t sr_mid;
+ struct mbchain sr_rq;
+ uchar_t sr_cmd;
+ uint8_t sr_rqflags;
+ uint16_t sr_rqflags2;
+ uchar_t *sr_wcount;
+ uchar_t *sr_bcount;
+ struct mdchain sr_rp;
+ int sr_rpgen;
+ int sr_rplast;
+ int sr_flags; /* SMBR_* */
+ int sr_rpsize;
+ struct smb_cred *sr_cred;
+ int sr_timo;
+ int sr_rexmit; /* how many more retries. dflt 0 */
+ int sr_sendcnt;
+ struct timespec sr_timesent;
+ int sr_lerror;
+ uint16_t *sr_rqtid;
+ uint16_t *sr_rquid;
+ uint8_t sr_errclass;
+ uint16_t sr_serror;
+ uint32_t sr_error;
+ uint8_t sr_rpflags;
+ uint16_t sr_rpflags2;
+ uint16_t sr_rptid;
+ uint16_t sr_rppid;
+ uint16_t sr_rpuid;
+ uint16_t sr_rpmid;
+};
+typedef struct smb_rq smb_rq_t;
+
+struct smb_t2rq {
+ kmutex_t t2_lock;
+ kcondvar_t t2_cond;
+ uint16_t t2_setupcount;
+ uint16_t *t2_setupdata;
+ uint16_t t2_setup[SMB_MAXSETUPWORDS];
+ uint8_t t2_maxscount; /* max setup words to return */
+ uint16_t t2_maxpcount; /* max param bytes to return */
+ uint16_t t2_maxdcount; /* max data bytes to return */
+ uint16_t t2_fid; /* for T2 request */
+ char t_name[128]; /* for T, should be zero for T2 */
+ int t2_flags; /* SMBT2_ */
+ struct mbchain t2_tparam; /* parameters to transmit */
+ struct mbchain t2_tdata; /* data to transmit */
+ struct mdchain t2_rparam; /* received paramters */
+ struct mdchain t2_rdata; /* received data */
+ struct smb_cred *t2_cred;
+ struct smb_connobj *t2_source;
+ struct smb_rq *t2_rq;
+ struct smb_vc *t2_vc;
+ struct smb_share *t2_share; /* for smb up/down */
+ /* unmapped windows error detail */
+ uint8_t t2_sr_errclass;
+ uint16_t t2_sr_serror;
+ uint32_t t2_sr_error;
+ uint16_t t2_sr_rpflags2;
+};
+typedef struct smb_t2rq smb_t2rq_t;
+
+struct smb_ntrq {
+ kmutex_t nt_lock;
+ kcondvar_t nt_cond;
+ uint16_t nt_function;
+ uint8_t nt_maxscount; /* max setup words to return */
+ uint32_t nt_maxpcount; /* max param bytes to return */
+ uint32_t nt_maxdcount; /* max data bytes to return */
+ int nt_flags; /* SMBT2_ */
+ struct mbchain nt_tsetup; /* setup to transmit */
+ struct mbchain nt_tparam; /* parameters to transmit */
+ struct mbchain nt_tdata; /* data to transmit */
+ struct mdchain nt_rparam; /* received paramters */
+ struct mdchain nt_rdata; /* received data */
+ struct smb_cred *nt_cred;
+ struct smb_connobj *nt_source;
+ struct smb_rq *nt_rq;
+ struct smb_vc *nt_vc;
+ struct smb_share *nt_share; /* for smb up/down */
+ /* unmapped windows error details */
+ uint32_t nt_sr_error;
+ uint16_t nt_sr_rpflags2;
+};
+typedef struct smb_ntrq smb_ntrq_t;
+
+int smb_rq_alloc(struct smb_connobj *layer, uchar_t cmd,
+ struct smb_cred *scred, struct smb_rq **rqpp);
+int smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer,
+ uchar_t cmd, struct smb_cred *scred);
+void smb_rq_done(struct smb_rq *rqp);
+int smb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp);
+int smb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp);
+void smb_rq_wstart(struct smb_rq *rqp);
+void smb_rq_wend(struct smb_rq *rqp);
+void smb_rq_bstart(struct smb_rq *rqp);
+void smb_rq_bend(struct smb_rq *rqp);
+int smb_rq_intr(struct smb_rq *rqp);
+int smb_rq_simple(struct smb_rq *rqp);
+int smb_rq_simple_timed(struct smb_rq *rqp, int timeout);
+
+int smb_t2_alloc(struct smb_connobj *layer, ushort_t setup,
+ struct smb_cred *scred, struct smb_t2rq **rqpp);
+int smb_t2_init(struct smb_t2rq *rqp, struct smb_connobj *layer,
+ ushort_t *setup, int setupcnt, struct smb_cred *scred);
+void smb_t2_done(struct smb_t2rq *t2p);
+int smb_t2_request(struct smb_t2rq *t2p);
+uint32_t smb_t2_err(struct smb_t2rq *t2p);
+
+int smb_nt_alloc(struct smb_connobj *layer, ushort_t fn,
+ struct smb_cred *scred, struct smb_ntrq **rqpp);
+int smb_nt_init(struct smb_ntrq *rqp, struct smb_connobj *layer,
+ ushort_t fn, struct smb_cred *scred);
+void smb_nt_done(struct smb_ntrq *ntp);
+int smb_nt_request(struct smb_ntrq *ntp);
+
+#endif /* _NETSMB_SMB_RQ_H_ */
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c
new file mode 100644
index 0000000000..5f54b7c38f
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c
@@ -0,0 +1,1783 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_smb.c,v 1.35.100.2 2005/06/02 00:55:39 lindak Exp $
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * various SMB requests. Most of the routines merely packs data into mbufs.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kmem.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/random.h>
+#include <sys/note.h>
+#include <sys/cmn_err.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#include <sys/utfconv.h>
+#else
+#include <netsmb/smb_osdep.h>
+#endif
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_rq.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_tran.h>
+
+/*
+ * Largest size to use with LARGE_READ/LARGE_WRITE.
+ * Specs say up to 64k data bytes, but Windows traffic
+ * uses 60k... no doubt for some good reason.
+ * (Probably to keep 4k block alignment.)
+ * XXX: Move to smb.h maybe?
+ */
+#define SMB_MAX_LARGE_RW_SIZE (60*1024)
+
+/*
+ * Default timeout values, all in seconds.
+ * Make these tunable (only via mdb for now).
+ */
+int smb_timo_notice = 15;
+int smb_timo_default = 30; /* was SMB_DEFRQTIMO */
+int smb_timo_open = 45;
+int smb_timo_read = 45;
+int smb_timo_write = 60; /* was SMBWRTTIMO */
+int smb_timo_append = 90;
+
+static int smb_smb_read(struct smb_share *ssp, u_int16_t fid,
+ int *len, int *rresid, uio_t *uiop, struct smb_cred *scred, int timo);
+static int smb_smb_write(struct smb_share *ssp, u_int16_t fid,
+ int *len, int *rresid, uio_t *uiop, struct smb_cred *scred, int timo);
+
+struct smb_dialect {
+ int d_id;
+ const char *d_name;
+};
+
+smb_unichar smb_unieol = 0;
+
+static struct smb_dialect smb_dialects[] = {
+ {SMB_DIALECT_CORE, "PC NETWORK PROGRAM 1.0"},
+ {SMB_DIALECT_LANMAN1_0, "LANMAN1.0"},
+ {SMB_DIALECT_LANMAN2_0, "LM1.2X002"},
+ {SMB_DIALECT_LANMAN2_1, "LANMAN2.1"},
+ {SMB_DIALECT_NTLM0_12, "NT LM 0.12"},
+ {-1, NULL}
+};
+
+#define SMB_DIALECT_MAX \
+ (sizeof (smb_dialects) / sizeof (struct smb_dialect) - 2)
+
+/*
+ * Number of seconds between 1970 and 1601 year
+ */
+const u_int64_t DIFF1970TO1601 = 11644473600ULL;
+
+void
+smb_time_local2server(struct timespec *tsp, int tzoff, long *seconds)
+{
+ /*
+ * XXX - what if we connected to the server when it was in
+ * daylight savings/summer time and we've subsequently switched
+ * to standard time, or vice versa, so that the time zone
+ * offset we got from the server is now wrong?
+ */
+ *seconds = tsp->tv_sec - tzoff * 60;
+ /* - tz.tz_minuteswest * 60 - (wall_cmos_clock ? adjkerntz : 0) */
+}
+
+void
+smb_time_server2local(ulong_t seconds, int tzoff, struct timespec *tsp)
+{
+ /*
+ * XXX - what if we connected to the server when it was in
+ * daylight savings/summer time and we've subsequently switched
+ * to standard time, or vice versa, so that the time zone
+ * offset we got from the server is now wrong?
+ */
+ tsp->tv_sec = seconds + tzoff * 60;
+ /* + tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0); */
+ tsp->tv_nsec = 0;
+}
+
+/*
+ * Time from server comes as UTC, so no need to use tz
+ */
+/*ARGSUSED*/
+void
+smb_time_NT2local(u_int64_t nsec, int tzoff, struct timespec *tsp)
+{
+ smb_time_server2local(nsec / 10000000 - DIFF1970TO1601, 0, tsp);
+}
+
+/*ARGSUSED*/
+void
+smb_time_local2NT(struct timespec *tsp, int tzoff, u_int64_t *nsec)
+{
+ long seconds;
+
+ smb_time_local2server(tsp, 0, &seconds);
+ *nsec = (((u_int64_t)(seconds) & ~1) + DIFF1970TO1601) *
+ (u_int64_t)10000000;
+}
+
+#if defined(NOICONVSUPPORT) || defined(lint)
+extern int iconv_open(const char *to, const char *from, void **handle);
+extern int iconv_close(void *handle);
+#endif
+
+int
+smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
+{
+ struct smb_dialect *dp;
+ struct smb_sopt *sp = NULL;
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ u_int8_t wc, stime[8], sblen;
+ u_int16_t dindex, tw, tw1, swlen, bc;
+ int error;
+ int unicode = 0;
+ char *servercs;
+ void *servercshandle = NULL;
+ void *localcshandle = NULL;
+ u_int16_t toklen;
+
+ vcp->vc_hflags = SMB_FLAGS_CASELESS; /* XXX on Unix? */
+ /*
+ * Make sure SMB_FLAGS2_UNICODE is "off" so mb_put_dstring
+ * marshalls the dialect strings in plain ascii.
+ */
+ vcp->vc_hflags2 &= ~SMB_FLAGS2_UNICODE;
+ vcp->vc_hflags2 |= SMB_FLAGS2_ERR_STATUS;
+
+ SMB_VC_LOCK(vcp);
+ vcp->vc_flags &= ~(SMBV_ENCRYPT);
+ SMB_VC_UNLOCK(vcp);
+
+ sp = &vcp->vc_sopt;
+ bzero(sp, sizeof (struct smb_sopt));
+ error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ for (dp = smb_dialects; dp->d_id != -1; dp++) {
+ mb_put_uint8(mbp, SMB_DT_DIALECT);
+ smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE);
+ }
+ smb_rq_bend(rqp);
+
+ /*
+ * This request should not wait for
+ * connection state changes, etc.
+ */
+ rqp->sr_flags |= SMBR_INTERNAL;
+ error = smb_rq_simple(rqp);
+ SMBSDEBUG("%d\n", error);
+ if (error)
+ goto bad;
+
+ smb_rq_getreply(rqp, &mdp);
+ do {
+ error = md_get_uint8(mdp, &wc);
+ if (error)
+ break;
+ error = md_get_uint16le(mdp, &dindex);
+ if (error)
+ break;
+ error = EBADRPC;
+ if (dindex > SMB_DIALECT_MAX) {
+ SMBERROR(
+ "Don't know how to talk with server %s (%d)\n",
+ vcp->vc_srvname, dindex);
+ break;
+ }
+ dp = smb_dialects + dindex;
+ if (dindex < SMB_DIALECT_MAX) {
+ SMBERROR(
+ "Server %s negotiated old dialect (%s)\n",
+ vcp->vc_srvname, dp->d_name);
+ }
+ sp->sv_proto = dp->d_id;
+ SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc);
+ if (dp->d_id >= SMB_DIALECT_NTLM0_12) {
+ if (wc != 17)
+ break;
+ md_get_uint8(mdp, &sp->sv_sm);
+ md_get_uint16le(mdp, &sp->sv_maxmux);
+ md_get_uint16le(mdp, &sp->sv_maxvcs);
+ md_get_uint32le(mdp, &sp->sv_maxtx);
+ md_get_uint32le(mdp, &sp->sv_maxraw);
+ md_get_uint32le(mdp, &sp->sv_skey);
+ md_get_uint32le(mdp, &sp->sv_caps);
+ md_get_mem(mdp, (char *)stime, 8, MB_MSYSTEM);
+ md_get_uint16le(mdp, (u_int16_t *)&sp->sv_tz);
+ md_get_uint8(mdp, &sblen);
+ error = md_get_uint16le(mdp, &bc);
+ if (error)
+ break;
+ if (sp->sv_sm & SMB_SM_SIGS_REQUIRE)
+ SMBERROR("server configuration requires "
+ "packet signing, which we dont support: "
+ "sp->sv_sm %d\n", sp->sv_sm);
+ if (sp->sv_caps & SMB_CAP_UNICODE) {
+ SMB_VC_LOCK(vcp);
+ vcp->vc_flags |= SMBV_UNICODE;
+ SMB_VC_UNLOCK(vcp);
+ unicode = 1;
+ }
+ if (!(sp->sv_caps & SMB_CAP_STATUS32)) {
+ /*
+ * They don't do NT error codes.
+ *
+ * If we send requests with
+ * SMB_FLAGS2_ERR_STATUS set in
+ * Flags2, Windows 98, at least,
+ * appears to send replies with that
+ * bit set even though it sends back
+ * DOS error codes. (They probably
+ * just use the request header as
+ * a template for the reply header,
+ * and don't bother clearing that bit.)
+ *
+ * Therefore, we clear that bit in
+ * our vc_hflags2 field.
+ */
+ vcp->vc_hflags2 &= ~SMB_FLAGS2_ERR_STATUS;
+ }
+ if (dp->d_id == SMB_DIALECT_NTLM0_12 &&
+ sp->sv_maxtx < 4096 &&
+ (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) {
+ SMB_VC_LOCK(vcp);
+ vcp->vc_flags |= SMBV_WIN95;
+ SMB_VC_UNLOCK(vcp);
+ SMBSDEBUG("Win95 detected\n");
+ }
+
+ /*
+ * 3 cases here:
+ *
+ * 1) Extended security.
+ * Read bc bytes below for security blob.
+ * Note that we DON'T put the Caps flag in outtok.
+ * outtoklen = bc
+ *
+ * 2) No extended security, have challenge data and
+ * possibly a domain name (which might be zero
+ * bytes long, meaning "missing").
+ * Copy challenge stuff to vcp->vc_ch (sblen bytes),
+ * then copy Cap flags and domain name (bc-sblen
+ * bytes) to outtok.
+ * outtoklen = bc-sblen+4, where the 4 is for the
+ * Caps flag.
+ *
+ * 3) No extended security, no challenge data, just
+ * possibly a domain name.
+ * Copy Capsflags and domain name (bc) to outtok.
+ * outtoklen = bc+4, where 4 is for the Caps flag
+ */
+
+ /*
+ * Sanity check: make sure the challenge length
+ * isn't bigger than the byte count.
+ */
+ if (sblen > bc) {
+ error = EBADRPC;
+ break;
+ }
+ toklen = bc;
+
+ if (sblen && sblen <= SMB_MAXCHALLENGELEN &&
+ sp->sv_sm & SMB_SM_ENCRYPT) {
+ error = md_get_mem(mdp,
+ (char *)vcp->vc_challenge,
+ sblen, MB_MSYSTEM);
+ if (error)
+ break;
+ vcp->vc_chlen = sblen;
+ toklen -= sblen;
+
+ SMB_VC_LOCK(vcp);
+ vcp->vc_flags |= SMBV_ENCRYPT;
+ SMB_VC_UNLOCK(vcp);
+ }
+
+ /*
+ * For servers that don't support unicode
+ * there are 2 things we could do:
+ * 1) Pass the server Caps flags up to the
+ * user level so the logic up there will
+ * know whether the domain name is unicode
+ * (this is what I did).
+ * 2) Try to convert the non-unicode string
+ * to unicode. This doubles the length of
+ * the outtok buffer and would be guessing that
+ * the string was single-byte ascii, and that
+ * might be wrong. Why ask for trouble?
+ */
+
+ /* Warning: NetApp may omit the GUID */
+
+ if (!(sp->sv_caps & SMB_CAP_EXT_SECURITY)) {
+ /*
+ * No extended security.
+ * Stick domain name, if present,
+ * and caps in outtok.
+ */
+ toklen = toklen + 4; /* space for Caps flags */
+ vcp->vc_outtoklen = toklen;
+ vcp->vc_outtok = kmem_alloc(toklen, KM_SLEEP);
+ /* first store server capability bits */
+ /*LINTED*/
+ ASSERT(vcp->vc_outtok ==
+ (caddr_t)(((u_int32_t *)vcp->vc_outtok)));
+ /*LINTED*/
+ *(u_int32_t *)(vcp->vc_outtok) = sp->sv_caps;
+
+ /*
+ * Then store the domain name if present;
+ * be sure to subtract 4 from the length
+ * for the Caps flag.
+ */
+ if (toklen > 4) {
+ error = md_get_mem(mdp,
+ vcp->vc_outtok+4, toklen-4,
+ MB_MSYSTEM);
+ }
+ } else {
+ /*
+ * Extended security.
+ * Stick the rest of the buffer in outtok.
+ */
+ vcp->vc_outtoklen = toklen;
+ vcp->vc_outtok = kmem_alloc(toklen, KM_SLEEP);
+ error = md_get_mem(mdp, vcp->vc_outtok, toklen,
+ MB_MSYSTEM);
+ }
+ break;
+ }
+ vcp->vc_hflags2 &= ~(SMB_FLAGS2_EXT_SEC|SMB_FLAGS2_DFS|
+ SMB_FLAGS2_ERR_STATUS|SMB_FLAGS2_UNICODE);
+ if (dp->d_id > SMB_DIALECT_CORE) {
+ md_get_uint16le(mdp, &tw);
+ sp->sv_sm = (uchar_t)tw;
+ md_get_uint16le(mdp, &tw);
+ sp->sv_maxtx = tw;
+ md_get_uint16le(mdp, &sp->sv_maxmux);
+ md_get_uint16le(mdp, &sp->sv_maxvcs);
+ md_get_uint16le(mdp, &tw); /* rawmode */
+ md_get_uint32le(mdp, &sp->sv_skey);
+ if (wc == 13) { /* >= LANMAN1 */
+ md_get_uint16(mdp, &tw); /* time */
+ md_get_uint16(mdp, &tw1); /* date */
+ md_get_uint16le(mdp, (u_int16_t *)&sp->sv_tz);
+ md_get_uint16le(mdp, &swlen);
+ if (swlen > SMB_MAXCHALLENGELEN)
+ break;
+ md_get_uint16(mdp, NULL); /* mbz */
+ if (md_get_uint16le(mdp, &bc) != 0)
+ break;
+ if (bc < swlen)
+ break;
+ if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
+ error = md_get_mem(mdp,
+ (char *)vcp->vc_challenge,
+ swlen, MB_MSYSTEM);
+ if (error)
+ break;
+ vcp->vc_chlen = swlen;
+
+ SMB_VC_LOCK(vcp);
+ vcp->vc_flags |= SMBV_ENCRYPT;
+ SMB_VC_UNLOCK(vcp);
+ }
+ }
+ } else { /* an old CORE protocol */
+ vcp->vc_hflags2 &= ~SMB_FLAGS2_KNOWS_LONG_NAMES;
+ sp->sv_maxmux = 1;
+ }
+ error = 0;
+ /*LINTED*/
+ } while (0);
+ if (error == 0) {
+ uint32_t x;
+
+ /*
+ * Maximum outstanding requests.
+ */
+ if (vcp->vc_maxmux < 1)
+ vcp->vc_maxmux = 1;
+
+ /*
+ * Max VCs between server and client.
+ * We only use one.
+ */
+ vcp->vc_maxvcs = sp->sv_maxvcs;
+ if (vcp->vc_maxvcs < 1)
+ vcp->vc_maxvcs = 1;
+
+ /*
+ * Maximum transfer size.
+ * Sanity checks:
+ *
+ * Spec. says lower limit is 1024. OK.
+ *
+ * Let's be conservative about an upper limit here.
+ * Win2k uses 16644 (and others) so 32k should be a
+ * reasonable sanity limit for this value.
+ *
+ * Note that this limit does NOT affect READX/WRITEX
+ * with CAP_LARGE_xxx, which we nearly always use.
+ */
+ vcp->vc_txmax = sp->sv_maxtx;
+ if (vcp->vc_txmax < 1024)
+ vcp->vc_txmax = 1024;
+ if (vcp->vc_txmax > 0x8000)
+ vcp->vc_txmax = 0x8000;
+
+ /*
+ * Max read/write sizes, WITHOUT overhead.
+ * This is just the payload size, so we must
+ * leave room for the SMB headers, etc.
+ *
+ * With CAP_LARGE_xxx, always use 60k.
+ * Otherwise use the vc_txmax value, but
+ * reduced and rounded down. Tricky bit:
+ *
+ * Servers typically give us a value that's
+ * some nice "round" number, i.e 0x4000 plus
+ * some overhead, i.e. Win2k: 16644==0x4104
+ * Subtract for the SMB header (32) and the
+ * SMB command word and byte vectors (34?),
+ * then round down to a 512 byte multiple.
+ */
+ x = (vcp->vc_txmax - 68) & 0xFE00;
+ if (sp->sv_caps & SMB_CAP_LARGE_READX)
+ vcp->vc_rxmax = SMB_MAX_LARGE_RW_SIZE;
+ else
+ vcp->vc_rxmax = x;
+ if (sp->sv_caps & SMB_CAP_LARGE_WRITEX)
+ vcp->vc_wxmax = SMB_MAX_LARGE_RW_SIZE;
+ else
+ vcp->vc_wxmax = x;
+
+ SMBSDEBUG("TZ = %d\n", sp->sv_tz);
+ SMBSDEBUG("CAPS = %x\n", sp->sv_caps);
+
+ SMBSDEBUG("maxmux = %d\n", vcp->vc_maxmux);
+ SMBSDEBUG("maxvcs = %d\n", vcp->vc_maxvcs);
+ SMBSDEBUG("txmax = %d\n", vcp->vc_txmax);
+ SMBSDEBUG("rxmax = %d\n", vcp->vc_rxmax);
+ SMBSDEBUG("wxmax = %d\n", vcp->vc_wxmax);
+ }
+
+ /*
+ * If the server supports Unicode, set up to use Unicode
+ * when talking to them. Othewise, use code page 437.
+ */
+ if (unicode)
+ servercs = "ucs-2";
+ else {
+ /*
+ * todo: if we can't determine the server's encoding, we
+ * need to try a best-guess here.
+ */
+ servercs = "cp437";
+ }
+#if defined(NOICONVSUPPORT) || defined(lint)
+ /*
+ * REVISIT
+ */
+ error = iconv_open(servercs, "utf-8", &servercshandle);
+ if (error != 0)
+ goto bad;
+ error = iconv_open("utf-8", servercs, &localcshandle);
+ if (error != 0) {
+ iconv_close(servercshandle);
+ goto bad;
+ }
+ if (vcp->vc_toserver)
+ iconv_close(vcp->vc_toserver);
+ if (vcp->vc_tolocal)
+ iconv_close(vcp->vc_tolocal);
+ vcp->vc_toserver = servercshandle;
+ vcp->vc_tolocal = localcshandle;
+#endif
+ if (unicode)
+ vcp->vc_hflags2 |= SMB_FLAGS2_UNICODE;
+bad:
+ smb_rq_done(rqp);
+ return (error);
+}
+
+static void
+get_ascii_password(struct smb_vc *vcp, int upper, char *pbuf)
+{
+ const char *pw = smb_vc_getpass(vcp);
+ if (upper)
+ smb_toupper(pw, pbuf, SMB_MAXPASSWORDLEN);
+ else
+ strncpy(pbuf, pw, SMB_MAXPASSWORDLEN);
+ pbuf[SMB_MAXPASSWORDLEN] = '\0';
+}
+
+#ifdef APPLE
+static void
+get_unicode_password(struct smb_vc *vcp, char *pbuf)
+{
+ strncpy(pbuf, smb_vc_getpass(vcp), SMB_MAXPASSWORDLEN);
+ pbuf[SMB_MAXPASSWORDLEN] = '\0';
+}
+#endif
+
+/*ARGSUSED*/
+static uchar_t *
+add_name_to_blob(uchar_t *blobnames, struct smb_vc *vcp, const uchar_t *name,
+ size_t namelen, int nametype, int uppercase)
+{
+ struct ntlmv2_namehdr namehdr;
+ char *namebuf;
+ u_int16_t *uninamebuf;
+ size_t uninamelen;
+
+ if (name != NULL) {
+ uninamebuf = kmem_alloc(2 * namelen, KM_SLEEP);
+ if (uppercase) {
+ namebuf = kmem_alloc(namelen + 1, KM_SLEEP);
+ smb_toupper((const char *)name, namebuf, namelen);
+ namebuf[namelen] = '\0';
+ uninamelen = smb_strtouni(uninamebuf, namebuf, namelen,
+ UCONV_IGNORE_NULL);
+ kmem_free(namebuf, namelen + 1);
+ } else {
+ uninamelen = smb_strtouni(uninamebuf, (char *)name,
+ namelen, UCONV_IGNORE_NULL);
+ }
+ } else {
+ uninamelen = 0;
+ uninamebuf = NULL;
+ }
+ namehdr.type = htoles(nametype);
+ namehdr.len = htoles(uninamelen);
+ bcopy(&namehdr, blobnames, sizeof (namehdr));
+ blobnames += sizeof (namehdr);
+ if (uninamebuf != NULL) {
+ bcopy(uninamebuf, blobnames, uninamelen);
+ blobnames += uninamelen;
+ kmem_free(uninamebuf, namelen * 2);
+ }
+ return (blobnames);
+}
+
+static uchar_t *
+make_ntlmv2_blob(struct smb_vc *vcp, u_int64_t client_nonce, size_t *bloblen)
+{
+ uchar_t *blob;
+ size_t blobsize;
+ size_t domainlen, srvlen;
+ struct ntlmv2_blobhdr *blobhdr;
+ struct timespec now;
+ u_int64_t timestamp;
+ uchar_t *blobnames;
+ ptrdiff_t diff;
+
+ /*
+ * XXX - the information at
+ *
+ * http://davenport.sourceforge.net/ntlm.html#theNtlmv2Response
+ *
+ * says that the "target information" comes from the Type 2 message,
+ * but, as we're not doing NTLMSSP, we don't have that.
+ *
+ * Should we use the names from the NegProt response? Can we trust
+ * the NegProt response? (I've seen captures where the primary
+ * domain name has an extra byte in front of it.)
+ *
+ * For now, we don't trust it - we use vcp->vc_domain and
+ * vcp->vc_srvname, instead. We upper-case them and convert
+ * them to Unicode, as that's what's supposed to be in the blob.
+ */
+ domainlen = strlen(vcp->vc_domain);
+ srvlen = strlen(vcp->vc_srvname);
+ blobsize = sizeof (struct ntlmv2_blobhdr)
+ + 3*sizeof (struct ntlmv2_namehdr) + 4 + 2*domainlen + 2*srvlen;
+ blob = kmem_zalloc(blobsize, KM_SLEEP);
+ /*LINTED*/
+ ASSERT(blob == (uchar_t *)((struct ntlmv2_blobhdr *)blob));
+ /*LINTED*/
+ blobhdr = (struct ntlmv2_blobhdr *)blob;
+ blobhdr->header = htolel(0x00000101);
+ gethrestime(&now);
+ smb_time_local2NT(&now, 0, &timestamp);
+ blobhdr->timestamp = htoleq(timestamp);
+ blobhdr->client_nonce = client_nonce;
+ blobnames = blob + sizeof (struct ntlmv2_blobhdr);
+ blobnames = add_name_to_blob(blobnames, vcp, (uchar_t *)vcp->vc_domain,
+ domainlen, NAMETYPE_DOMAIN_NB, 1);
+ blobnames = add_name_to_blob(blobnames, vcp, (uchar_t *)vcp->vc_srvname,
+ srvlen, NAMETYPE_MACHINE_NB, 1);
+ blobnames = add_name_to_blob(blobnames, vcp, NULL, 0, NAMETYPE_EOL, 0);
+ diff = (intptr_t)blobnames - (intptr_t)blob;
+ ASSERT(diff == (ptrdiff_t)((size_t)diff));
+ *bloblen = (size_t)diff;
+ return (blob);
+}
+
+/*
+ * See radar 4134676. This define helps us avoid how a certain old server
+ * grants limited Guest access when we try NTLMv2, but works fine with NTLM.
+ * The fingerprint we are looking for here is DOS error codes and no-Unicode.
+ * Note XP grants Guest access but uses Unicode and NT error codes.
+ */
+#define smb_antique(rqp) (!((rqp)->sr_rpflags2 & SMB_FLAGS2_ERR_STATUS) && \
+ !((rqp)->sr_rpflags2 & SMB_FLAGS2_UNICODE))
+
+/*
+ * When not doing Kerberos, we can try, in order:
+ *
+ * NTLMv2
+ * NTLM with the ASCII password not upper-cased
+ * NTLM with the ASCII password upper-cased
+ *
+ * if the server supports encrypted passwords, or
+ *
+ * plain-text with the ASCII password not upper-cased
+ * plain-text with the ASCII password upper-cased
+ *
+ * if it doesn't.
+ */
+#define STATE_NTLMV2 0
+#define STATE_NOUCPW 1
+#define STATE_UCPW 2
+
+int
+smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
+{
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ u_int8_t wc;
+ int minauth;
+ smb_uniptr unipp = NULL, ntencpass = NULL;
+ char *pp = NULL, *up = NULL, *ucup = NULL, *ucdp = NULL;
+ char *pbuf = NULL;
+ char *encpass = NULL;
+ int error = 0;
+ size_t plen = 0, uniplen = 0, uniplen2 = 0, tmplen;
+ size_t ucup_sl = 0, ucdp_sl = 0;
+ int state;
+ size_t ntlmv2_bloblen;
+ uchar_t *ntlmv2_blob;
+ u_int64_t client_nonce;
+ u_int32_t caps;
+ u_int16_t bl; /* BLOB length */
+ u_int16_t saveflags2 = vcp->vc_hflags2;
+ void * savetoserver = vcp->vc_toserver;
+ u_int16_t action;
+ int declinedguest = 0;
+ static const char NativeOS[] = "Solaris";
+ static const char LanMan[] = "NETSMB";
+ /*
+ * Most of the "capability" bits we offer should be copied
+ * from those offered by the server, with a mask applied.
+ * This is the mask of capabilies copied from the server.
+ * Some others get special handling below.
+ */
+ static const uint32_t caps_mask =
+ SMB_CAP_UNICODE |
+ SMB_CAP_LARGE_FILES |
+ SMB_CAP_NT_SMBS |
+ SMB_CAP_STATUS32 |
+ SMB_CAP_LARGE_READX |
+ SMB_CAP_LARGE_WRITEX;
+
+ caps = vcp->vc_sopt.sv_caps & caps_mask;
+
+ /* No unicode unless server supports and encryption on */
+ if (!((vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) &&
+ (vcp->vc_flags & SMBV_UNICODE))) {
+ vcp->vc_hflags2 &= 0xffff - SMB_FLAGS2_UNICODE;
+ vcp->vc_toserver = 0;
+ }
+
+ minauth = vcp->vc_vopt & SMBVOPT_MINAUTH;
+ if (vcp->vc_intok) {
+ if (vcp->vc_intoklen > 65536 ||
+ !(vcp->vc_hflags2 & SMB_FLAGS2_EXT_SEC) ||
+ SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) {
+ error = EINVAL;
+ goto ssn_exit;
+ }
+ vcp->vc_smbuid = 0;
+ }
+
+ /*
+ * Try only plain text passwords.
+ */
+ if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
+ state = STATE_NTLMV2; /* try NTLMv2 first */
+ } else {
+ state = STATE_NOUCPW; /* try plain-text mixed-case first */
+ }
+again:
+
+ if (!vcp->vc_intok)
+ vcp->vc_smbuid = SMB_UID_UNKNOWN;
+
+ if (!vcp->vc_intok) {
+ /*
+ * We're not doing extended security, which, for
+ * now, means we're not doing Kerberos.
+ * Fail if the minimum authentication level is
+ * Kerberos.
+ */
+ if (minauth >= SMBVOPT_MINAUTH_KERBEROS) {
+ error = EAUTH;
+ goto ssn_exit;
+ }
+ if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
+ /*
+ * Server wants encrypted passwords.
+ */
+ if (state > STATE_NTLMV2) {
+ /*
+ * We tried NTLMv2 in STATE_NTLMV2.
+ * Shall we allow fallback? (to NTLM)
+ */
+ if (minauth >= SMBVOPT_MINAUTH_NTLMV2) {
+ error = EAUTH;
+ goto ssn_exit;
+ }
+ }
+ if (state > STATE_NOUCPW) {
+ /*
+ * We tried NTLM in STATE_NOUCPW.
+ * No need to try it again.
+ */
+ error = EAUTH;
+ goto ssn_exit;
+ }
+ } else {
+ /*
+ * Plain-text passwords.
+ * Fail if the minimum authentication level is
+ * LM or better.
+ */
+ if (minauth > SMBVOPT_MINAUTH_NTLM) {
+ error = EAUTH;
+ goto ssn_exit;
+ }
+ }
+ }
+
+ error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX,
+ scred, &rqp);
+ if (error)
+ goto ssn_exit;
+
+ /*
+ * Domain name must be upper-case, as that's what's used
+ * when computing LMv2 and NTLMv2 responses - and, for NTLMv2,
+ * the domain name in the request has to be upper-cased as well.
+ * (That appears not to be the case for the user name. Go
+ * figure.)
+ *
+ * don't need to uppercase domain string. It's already uppercase UTF-8.
+ */
+
+ ucdp_sl = strlen(vcp->vc_domain);
+ ucdp = kmem_zalloc(ucdp_sl + 1, KM_SLEEP);
+ memcpy(ucdp, vcp->vc_domain, ucdp_sl + 1);
+
+ if (vcp->vc_intok) {
+ caps |= SMB_CAP_EXT_SECURITY;
+ } else if (!(vcp->vc_sopt.sv_sm & SMB_SM_USER)) {
+ /*
+ * In the share security mode password will be used
+ * only in the tree authentication
+ */
+ pp = "";
+ plen = 1;
+ unipp = &smb_unieol;
+ uniplen = sizeof (smb_unieol);
+ } else {
+ pbuf = kmem_alloc(SMB_MAXPASSWORDLEN + 1, KM_SLEEP);
+ if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
+ if (state == STATE_NTLMV2) {
+ /*
+ * Compute the LMv2 and NTLMv2 responses,
+ * derived from the challenge, the user name,
+ * the domain/workgroup into which we're
+ * logging, and the Unicode password.
+ */
+
+ /*
+ * Construct the client nonce by getting
+ * a bunch of random data.
+ */
+ (void) random_get_pseudo_bytes((void *)
+ &client_nonce, sizeof (client_nonce));
+
+ /*
+ * Convert the user name to upper-case, as
+ * that's what's used when computing LMv2
+ * and NTLMv2 responses.
+ */
+ ucup_sl = strlen(vcp->vc_username);
+ ucup = kmem_alloc(ucup_sl + 1, KM_SLEEP);
+ smb_toupper((const char *)vcp->vc_username,
+ ucup, ucup_sl);
+ ucup[ucup_sl] = '\0';
+
+ /*
+ * Compute the LMv2 response, derived
+ * from the server challenge, the
+ * user name, the domain/workgroup
+ * into which we're logging, the
+ * client nonce, and the NT hash.
+ */
+ smb_ntlmv2response(vcp->vc_nthash,
+ (uchar_t *)ucup, (uchar_t *)ucdp,
+ vcp->vc_challenge,
+ (uchar_t *)&client_nonce, 8,
+ (uchar_t **)&encpass, &plen);
+ pp = encpass;
+
+ /*
+ * Construct the blob.
+ */
+ ntlmv2_blob = make_ntlmv2_blob(vcp,
+ client_nonce, &ntlmv2_bloblen);
+
+ /*
+ * Compute the NTLMv2 response, derived
+ * from the server challenge, the
+ * user name, the domain/workgroup
+ * into which we're logging, the
+ * blob, and the NT hash.
+ */
+ smb_ntlmv2response(vcp->vc_nthash,
+ (uchar_t *)ucup, (uchar_t *)ucdp,
+ vcp->vc_challenge,
+ ntlmv2_blob, ntlmv2_bloblen,
+ (uchar_t **)&ntencpass, &uniplen);
+ uniplen2 = uniplen;
+ unipp = ntencpass;
+ tmplen = plen;
+
+ kmem_free(ucup, ucup_sl + 1);
+ kmem_free((char *)ntlmv2_blob,
+ sizeof (struct ntlmv2_blobhdr) +
+ 3 * sizeof (struct ntlmv2_namehdr) +
+ 4 +
+ 2 * strlen(vcp->vc_domain) +
+ 2 * strlen(vcp->vc_srvname));
+ } else {
+ plen = 24;
+ encpass = kmem_zalloc(plen, KM_SLEEP);
+ /*
+ * Compute the LM response, derived
+ * from the challenge and the ASCII
+ * password.
+ */
+ if (minauth < SMBVOPT_MINAUTH_NTLM) {
+ smb_lmresponse(vcp->vc_lmhash,
+ vcp->vc_challenge,
+ (uchar_t *)encpass);
+ }
+ pp = encpass;
+
+ /*
+ * Compute the NTLM response, derived from
+ * the challenge and the NT hash.
+ */
+ uniplen = 24;
+ uniplen2 = uniplen;
+ ntencpass = kmem_alloc(uniplen, KM_SLEEP);
+ smb_lmresponse(vcp->vc_nthash,
+ vcp->vc_challenge,
+ (uchar_t *)ntencpass);
+ unipp = ntencpass;
+ }
+ } else {
+ /*
+ * We try w/o uppercasing first so Samba mixed case
+ * passwords work. If that fails, we come back and
+ * try uppercasing to satisfy OS/2 and Windows for
+ * Workgroups.
+ */
+ get_ascii_password(vcp, (state == STATE_UCPW), pbuf);
+ plen = strlen(pbuf) + 1;
+ pp = pbuf;
+ uniplen = plen * 2;
+ uniplen2 = uniplen;
+ ntencpass = kmem_alloc(uniplen, KM_SLEEP);
+ (void) smb_strtouni(ntencpass, smb_vc_getpass(vcp),
+ 0, 0);
+ plen--;
+ /*
+ * The uniplen is zeroed because Samba cannot deal
+ * with this 2nd cleartext password. This Samba
+ * "bug" is actually a workaround for problems in
+ * Microsoft clients.
+ */
+ uniplen = 0; /* -= 2 */
+ unipp = ntencpass;
+ }
+ }
+ smb_rq_wstart(rqp);
+ mbp = &rqp->sr_rq;
+ up = vcp->vc_username;
+ /*
+ * If userid is null we are attempting anonymous browse login
+ * so passwords must be zero length.
+ */
+ if (*up == '\0') {
+ plen = uniplen = 0;
+ }
+ mb_put_uint8(mbp, 0xff);
+ mb_put_uint8(mbp, 0);
+ mb_put_uint16le(mbp, 0);
+ mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx);
+ mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux);
+ mb_put_uint16le(mbp, vcp->vc_number);
+ mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey);
+ if ((SMB_DIALECT(vcp)) < SMB_DIALECT_NTLM0_12) {
+ mb_put_uint16le(mbp, plen);
+ mb_put_uint32le(mbp, 0);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
+ smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* user */
+ smb_put_dstring(mbp, vcp, ucdp, SMB_CS_NONE); /* domain */
+ } else {
+ if (vcp->vc_intok) {
+ mb_put_uint16le(mbp, vcp->vc_intoklen);
+ mb_put_uint32le(mbp, 0); /* reserved */
+ mb_put_uint32le(mbp, caps); /* my caps */
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_mem(mbp, vcp->vc_intok, vcp->vc_intoklen,
+ MB_MSYSTEM); /* security blob */
+ } else {
+ mb_put_uint16le(mbp, plen);
+ mb_put_uint16le(mbp, uniplen);
+ mb_put_uint32le(mbp, 0); /* reserved */
+ mb_put_uint32le(mbp, caps); /* my caps */
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_mem(mbp, pp, plen, MB_MSYSTEM); /* password */
+ mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM);
+ smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* user */
+ smb_put_dstring(mbp, vcp, ucdp, SMB_CS_NONE); /* dom */
+ }
+ }
+ smb_put_dstring(mbp, vcp, NativeOS, SMB_CS_NONE); /* OS */
+ smb_put_dstring(mbp, vcp, LanMan, SMB_CS_NONE); /* LAN Mgr */
+ smb_rq_bend(rqp);
+ if (ntencpass) {
+ kmem_free(ntencpass, uniplen2);
+ ntencpass = NULL;
+ }
+ if (encpass) {
+ kmem_free(encpass, 24);
+ encpass = NULL;
+ }
+ if (ucdp) {
+ kmem_free(ucdp, ucdp_sl + 1);
+ ucdp = NULL;
+ }
+
+ /*
+ * This request should not wait for
+ * connection state changes, etc.
+ */
+ rqp->sr_flags |= SMBR_INTERNAL;
+ error = smb_rq_simple_timed(rqp, SMBSSNSETUPTIMO);
+ SMBSDEBUG("%d\n", error);
+ if (error) {
+ if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess)
+ error = EAUTH;
+ if (!(rqp->sr_errclass == ERRDOS &&
+ rqp->sr_serror == ERRmoredata))
+ goto bad;
+ }
+ vcp->vc_smbuid = rqp->sr_rpuid;
+ smb_rq_getreply(rqp, &mdp);
+ do {
+ error = md_get_uint8(mdp, &wc);
+ if (error)
+ break;
+ error = EBADRPC;
+ if (vcp->vc_intok) {
+ if (wc != 4)
+ break;
+ } else if (wc != 3)
+ break;
+ md_get_uint8(mdp, NULL); /* secondary cmd */
+ md_get_uint8(mdp, NULL); /* mbz */
+ md_get_uint16le(mdp, NULL); /* andxoffset */
+ md_get_uint16le(mdp, &action); /* action */
+ if (vcp->vc_intok)
+ md_get_uint16le(mdp, &bl); /* ext security */
+ md_get_uint16le(mdp, NULL); /* byte count */
+ if (vcp->vc_intok) {
+ vcp->vc_outtoklen = bl;
+ vcp->vc_outtok = kmem_alloc(bl, KM_SLEEP);
+ error = md_get_mem(mdp, vcp->vc_outtok, bl, MB_MSYSTEM);
+ if (error)
+ break;
+ }
+ /* server OS, LANMGR, & Domain here */
+ error = 0;
+ /*LINTED*/
+ } while (0);
+bad:
+ if (encpass) {
+ kmem_free(encpass, tmplen);
+ encpass = NULL;
+ }
+ if (pbuf) {
+ kmem_free(pbuf, SMB_MAXPASSWORDLEN + 1);
+ pbuf = NULL;
+ }
+ if (vcp->vc_sopt.sv_sm & SMB_SM_USER && !vcp->vc_intok &&
+ (error || (*up != '\0' && action & SMB_ACT_GUEST &&
+ state == STATE_NTLMV2 && smb_antique(rqp)))) {
+ /*
+ * We're doing user-level authentication (so we are actually
+ * sending authentication stuff over the wire), and we're
+ * not doing extended security, and the stuff we tried
+ * failed (or we we're trying to login a real user but
+ * got granted guest access instead.)
+ */
+ if (!error)
+ declinedguest = 1;
+ /*
+ * Should we try the next type of authentication?
+ */
+ if (state < STATE_UCPW) {
+ /*
+ * Yes, we still have more to try.
+ */
+ state++;
+ smb_rq_done(rqp);
+ goto again;
+ }
+ }
+ smb_rq_done(rqp);
+
+ssn_exit:
+ if (error && declinedguest)
+ SMBERROR("we declined ntlmv2 guest access. errno will be %d\n",
+ error);
+ /* Restore things we changed and return */
+ vcp->vc_hflags2 = saveflags2;
+ vcp->vc_toserver = savetoserver;
+ return (error);
+}
+
+int
+smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred)
+{
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ int error;
+
+ if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
+ return (0);
+
+ error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp);
+ if (error)
+ return (error);
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ mb_put_uint8(mbp, 0xff);
+ mb_put_uint8(mbp, 0);
+ mb_put_uint16le(mbp, 0);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ smb_rq_bend(rqp);
+ /*
+ * Run this with a relatively short timeout.
+ * We don't really care about the result,
+ * as we're just trying to play nice and
+ * "say goodbye" before we hangup.
+ * XXX: Add SMBLOGOFFTIMO somewhere?
+ */
+ error = smb_rq_simple_timed(rqp, 5);
+ SMBSDEBUG("%d\n", error);
+ smb_rq_done(rqp);
+ return (error);
+}
+
+static char smb_any_share[] = "?????";
+
+static char *
+smb_share_typename(int stype)
+{
+ char *pp;
+
+ switch (stype) {
+ case STYPE_DISKTREE:
+ pp = "A:";
+ break;
+ case STYPE_PRINTQ:
+ pp = smb_any_share; /* can't use LPT: here... */
+ break;
+ case STYPE_DEVICE:
+ pp = "COMM";
+ break;
+ case STYPE_IPC:
+ pp = "IPC";
+ break;
+ default:
+ pp = smb_any_share;
+ break;
+ }
+ return (pp);
+}
+
+int
+smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
+{
+ struct smb_vc *vcp;
+ struct smb_rq rq, *rqp = &rq;
+ struct mbchain *mbp;
+ char *pp, *pbuf, *encpass;
+ const char *pw;
+ uchar_t hash[SMB_PWH_MAX];
+ int error, plen, caseopt;
+ int upper = 0;
+
+again:
+ vcp = SSTOVC(ssp);
+
+ /*
+ * Make this a "VC-level" request, so it will have
+ * rqp->sr_share == NULL, and smb_iod_sendrq()
+ * will send it with TID = SMB_TID_UNKNOWN
+ *
+ * This also serves to bypass the wait for
+ * share state changes, which this call is
+ * trying to carry out.
+ *
+ * No longer need to set ssp->ss_tid
+ * here, but it's harmless enough.
+ */
+ ssp->ss_tid = SMB_TID_UNKNOWN;
+ error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_CONNECT_ANDX,
+ scred, &rqp);
+ if (error)
+ return (error);
+ caseopt = SMB_CS_NONE;
+ if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
+ plen = 1;
+ pp = "";
+ pbuf = NULL;
+ encpass = NULL;
+ } else {
+ pbuf = kmem_alloc(SMB_MAXPASSWORDLEN + 1, KM_SLEEP);
+ encpass = kmem_alloc(24, KM_SLEEP);
+ pw = smb_share_getpass(ssp);
+ /*
+ * We try w/o uppercasing first so Samba mixed case
+ * passwords work. If that fails we come back and try
+ * uppercasing to satisfy OS/2 and Windows for Workgroups.
+ */
+ if (upper++) {
+ smb_toupper(pw, pbuf, SMB_MAXPASSWORDLEN);
+ smb_oldlm_hash(pw, hash);
+ } else {
+ strncpy(pbuf, pw, SMB_MAXPASSWORDLEN);
+ smb_ntlmv1hash(pw, hash);
+ }
+ pbuf[SMB_MAXPASSWORDLEN] = '\0';
+
+#ifdef NOICONVSUPPORT
+ /*
+ * We need to convert here to the server codeset.
+ * Initially we will send the same stuff and see what happens
+ * witout the conversion. REVISIT.
+ */
+ iconv_convstr(vcp->vc_toserver, pbuf, pbuf, SMB_MAXPASSWORDLEN);
+#endif
+ if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
+ plen = 24;
+ smb_lmresponse(hash,
+ vcp->vc_challenge,
+ (uchar_t *)encpass);
+ pp = encpass;
+ } else {
+ plen = strlen(pbuf) + 1;
+ pp = pbuf;
+ }
+ }
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ mb_put_uint8(mbp, 0xff);
+ mb_put_uint8(mbp, 0);
+ mb_put_uint16le(mbp, 0);
+ mb_put_uint16le(mbp, 0); /* Flags */
+ mb_put_uint16le(mbp, plen);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ error = mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
+ if (error) {
+ SMBSDEBUG("error %d from mb_put_mem for pp\n", error);
+ goto bad;
+ }
+ smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt, NULL);
+ pp = vcp->vc_srvname;
+ error = smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt, NULL);
+ if (error) {
+ SMBSDEBUG("error %d from smb_put_dmem for srvname\n", error);
+ goto bad;
+ }
+ smb_put_dmem(mbp, vcp, "\\", 1, caseopt, NULL);
+ pp = ssp->ss_name;
+ error = smb_put_dstring(mbp, vcp, pp, caseopt);
+ if (error) {
+ SMBSDEBUG("error %d from smb_put_dstring for ss_name\n", error);
+ goto bad;
+ }
+ /* The type name is always ASCII */
+ pp = smb_share_typename(ssp->ss_type);
+ error = mb_put_mem(mbp, pp, strlen(pp) + 1, MB_MSYSTEM);
+ if (error) {
+ SMBSDEBUG("error %d from mb_put_mem for ss_type\n", error);
+ goto bad;
+ }
+ smb_rq_bend(rqp);
+ /*
+ * Don't want to risk missing a successful
+ * tree connect response.
+ */
+ rqp->sr_flags |= SMBR_NOINTR_RECV;
+ error = smb_rq_simple(rqp);
+ SMBSDEBUG("%d\n", error);
+ if (error)
+ goto bad;
+
+ /* Success! */
+ SMB_SS_LOCK(ssp);
+ ssp->ss_tid = rqp->sr_rptid;
+ ssp->ss_vcgenid = vcp->vc_genid;
+ ssp->ss_flags |= SMBS_CONNECTED;
+ SMB_SS_UNLOCK(ssp);
+
+bad:
+ if (encpass)
+ kmem_free(encpass, 24);
+ if (pbuf)
+ kmem_free(pbuf, SMB_MAXPASSWORDLEN + 1);
+ smb_rq_done(rqp);
+ if (error && upper == 1)
+ goto again;
+ return (error);
+}
+
+int
+smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
+{
+ struct smb_vc *vcp;
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ int error;
+
+ if (ssp->ss_tid == SMB_TID_UNKNOWN)
+ return (0);
+
+ /*
+ * Build this as a "VC-level" request, so it will
+ * avoid testing the _GONE flag on the share,
+ * which has already been set at this point.
+ * Add the share pointer "by hand" below, so
+ * smb_iod_sendrq will plug in the TID.
+ */
+ vcp = SSTOVC(ssp);
+ error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
+ if (error)
+ return (error);
+ rqp->sr_share = ssp; /* by hand */
+ mbp = &rqp->sr_rq;
+#ifdef lint
+ mbp = mbp;
+#endif
+ smb_rq_wstart(rqp);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ smb_rq_bend(rqp);
+
+ /*
+ * Run this with a relatively short timeout. (5 sec.)
+ * We don't really care about the result here, but we
+ * do need to make sure we send this out, or we could
+ * "leak" active tree IDs on interrupt or timeout.
+ * The NOINTR_SEND flag makes this request immune to
+ * interrupt or timeout until the send is done.
+ */
+ rqp->sr_flags |= SMBR_NOINTR_SEND;
+ error = smb_rq_simple_timed(rqp, 5);
+ SMBSDEBUG("%d\n", error);
+ smb_rq_done(rqp);
+ ssp->ss_tid = SMB_TID_UNKNOWN;
+ return (error);
+}
+
+static int
+smb_smb_readx(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
+ uio_t *uiop, struct smb_cred *scred, int timo)
+{
+ struct smb_vc *vcp = SSTOVC(ssp);
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ u_int8_t wc;
+ int error;
+ u_int16_t residhi, residlo, off, doff;
+ u_int32_t resid;
+
+ if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX) == 0) {
+ /* Fall back to the old cmd. */
+ return (smb_smb_read(ssp, fid, len, rresid, uiop,
+ scred, timo));
+ }
+ if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES) == 0) {
+ /* Have ReadX but not large files? */
+ if ((uiop->uio_loffset + *len) > UINT32_MAX)
+ return (EFBIG);
+ }
+ *len = min(*len, vcp->vc_rxmax);
+
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_uint8(mbp, 0xff); /* no secondary command */
+ mb_put_uint8(mbp, 0); /* MBZ */
+ mb_put_uint16le(mbp, 0); /* offset to secondary */
+ mb_put_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM);
+ mb_put_uint32le(mbp, (u_int32_t)(uiop->uio_offset));
+ mb_put_uint16le(mbp, (u_int16_t)*len); /* MaxCount */
+ mb_put_uint16le(mbp, (u_int16_t)*len); /* MinCount */
+ /* (only indicates blocking) */
+ mb_put_uint32le(mbp, (unsigned)*len >> 16); /* MaxCountHigh */
+ mb_put_uint16le(mbp, (u_int16_t)*len); /* Remaining ("obsolete") */
+ mb_put_uint32le(mbp, (u_int32_t)((uiop->uio_loffset) >> 32));
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ smb_rq_bend(rqp);
+ do {
+ if (timo == 0)
+ timo = smb_timo_read;
+ error = smb_rq_simple_timed(rqp, timo);
+ if (error)
+ break;
+ smb_rq_getreply(rqp, &mdp);
+ off = SMB_HDRLEN;
+ md_get_uint8(mdp, &wc);
+ off++;
+ if (wc != 12) {
+ error = EBADRPC;
+ break;
+ }
+ md_get_uint8(mdp, NULL);
+ off++;
+ md_get_uint8(mdp, NULL);
+ off++;
+ md_get_uint16le(mdp, NULL);
+ off += 2;
+ md_get_uint16le(mdp, NULL);
+ off += 2;
+ md_get_uint16le(mdp, NULL); /* data compaction mode */
+ off += 2;
+ md_get_uint16le(mdp, NULL);
+ off += 2;
+ md_get_uint16le(mdp, &residlo);
+ off += 2;
+ md_get_uint16le(mdp, &doff); /* data offset */
+ off += 2;
+ md_get_uint16le(mdp, &residhi);
+ off += 2;
+ resid = (residhi << 16) | residlo;
+ md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
+ off += 4*2;
+ md_get_uint16le(mdp, NULL); /* ByteCount */
+ off += 2;
+ if (doff > off) /* pad byte(s)? */
+ md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM);
+ if (resid == 0) {
+ *rresid = resid;
+ break;
+ }
+ error = md_get_uio(mdp, uiop, resid);
+ if (error)
+ break;
+ *rresid = resid;
+ /*LINTED*/
+ } while (0);
+ smb_rq_done(rqp);
+ return (error);
+}
+
+static int
+smb_smb_writex(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
+ uio_t *uiop, struct smb_cred *scred, int timo)
+{
+ struct smb_vc *vcp = SSTOVC(ssp);
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ int error;
+ u_int8_t wc;
+ u_int16_t resid;
+
+ if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX) == 0) {
+ /* Fall back to the old cmd. */
+ return (smb_smb_write(ssp, fid, len, rresid, uiop,
+ scred, timo));
+ }
+ if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES) == 0) {
+ /* Have WriteX but not large files? */
+ if ((uiop->uio_loffset + *len) > UINT32_MAX)
+ return (EFBIG);
+ }
+ *len = min(*len, vcp->vc_wxmax);
+
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_uint8(mbp, 0xff); /* no secondary command */
+ mb_put_uint8(mbp, 0); /* MBZ */
+ mb_put_uint16le(mbp, 0); /* offset to secondary */
+ mb_put_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM);
+ mb_put_uint32le(mbp, (u_int32_t)(uiop->uio_offset));
+ mb_put_uint32le(mbp, 0); /* MBZ (timeout) */
+ mb_put_uint16le(mbp, 0); /* !write-thru */
+ mb_put_uint16le(mbp, 0);
+ mb_put_uint16le(mbp, (u_int16_t)((unsigned)*len >> 16));
+ mb_put_uint16le(mbp, (u_int16_t)*len);
+ mb_put_uint16le(mbp, 64); /* data offset from header start */
+ mb_put_uint32le(mbp, (u_int32_t)((uiop->uio_loffset) >> 32));
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ do {
+ mb_put_uint8(mbp, 0xee); /* mimic xp pad byte! */
+ error = mb_put_uio(mbp, uiop, *len);
+ if (error)
+ break;
+ smb_rq_bend(rqp);
+ if (timo == 0)
+ timo = smb_timo_write;
+ error = smb_rq_simple_timed(rqp, timo);
+ if (error)
+ break;
+ smb_rq_getreply(rqp, &mdp);
+ md_get_uint8(mdp, &wc);
+ if (wc != 6) {
+ error = EBADRPC;
+ break;
+ }
+ md_get_uint8(mdp, NULL);
+ md_get_uint8(mdp, NULL);
+ md_get_uint16le(mdp, NULL);
+ md_get_uint16le(mdp, &resid); /* actually is # written */
+ *rresid = resid;
+ /*
+ * if LARGE_WRITEX then there's one more bit of # written
+ */
+ if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX)) {
+ md_get_uint16le(mdp, NULL);
+ md_get_uint16le(mdp, &resid);
+ *rresid |= (int)(resid & 1) << 16;
+ }
+ /*LINTED*/
+ } while (0);
+
+ smb_rq_done(rqp);
+ return (error);
+}
+
+static int
+smb_smb_read(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
+ uio_t *uiop, struct smb_cred *scred, int timo)
+{
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ u_int16_t resid, bc;
+ u_int8_t wc;
+ int error, rlen;
+
+ /* This cmd is limited to 32-bit offsets. */
+ if ((uiop->uio_loffset + *len) > UINT32_MAX)
+ return (EFBIG);
+ *len = rlen = min(*len, SSTOVC(ssp)->vc_rxmax);
+
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM);
+ mb_put_uint16le(mbp, (u_int16_t)rlen);
+ mb_put_uint32le(mbp, (u_int32_t)uiop->uio_offset);
+ mb_put_uint16le(mbp, (u_int16_t)min(uiop->uio_resid, 0xffff));
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ smb_rq_bend(rqp);
+ do {
+ if (timo == 0)
+ timo = smb_timo_read;
+ error = smb_rq_simple_timed(rqp, timo);
+ if (error)
+ break;
+ smb_rq_getreply(rqp, &mdp);
+ md_get_uint8(mdp, &wc);
+ if (wc != 5) {
+ error = EBADRPC;
+ break;
+ }
+ md_get_uint16le(mdp, &resid);
+ md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
+ md_get_uint16le(mdp, &bc);
+ md_get_uint8(mdp, NULL); /* ignore buffer type */
+ md_get_uint16le(mdp, &resid);
+ if (resid == 0) {
+ *rresid = resid;
+ break;
+ }
+ error = md_get_uio(mdp, uiop, resid);
+ if (error)
+ break;
+ *rresid = resid;
+ /*LINTED*/
+ } while (0);
+ smb_rq_done(rqp);
+ return (error);
+}
+
+static int
+smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
+ uio_t *uiop, struct smb_cred *scred, int timo)
+{
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ u_int16_t resid;
+ u_int8_t wc;
+ int error;
+
+ /* This cmd is limited to 32-bit offsets. */
+ if ((uiop->uio_loffset + *len) > UINT32_MAX)
+ return (EFBIG);
+ *len = resid = min(*len, SSTOVC(ssp)->vc_wxmax);
+
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM);
+ mb_put_uint16le(mbp, resid);
+ mb_put_uint32le(mbp, (u_int32_t)uiop->uio_offset);
+ mb_put_uint16le(mbp, (u_int16_t)min(uiop->uio_resid, 0xffff));
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_DATA);
+ mb_put_uint16le(mbp, resid);
+ do {
+ error = mb_put_uio(mbp, uiop, resid);
+ if (error)
+ break;
+ smb_rq_bend(rqp);
+ if (timo == 0)
+ timo = smb_timo_write;
+ error = smb_rq_simple_timed(rqp, timo);
+ if (error)
+ break;
+ smb_rq_getreply(rqp, &mdp);
+ md_get_uint8(mdp, &wc);
+ if (wc != 1) {
+ error = EBADRPC;
+ break;
+ }
+ md_get_uint16le(mdp, &resid);
+ *rresid = resid;
+ /*LINTED*/
+ } while (0);
+ smb_rq_done(rqp);
+ return (error);
+}
+
+/*
+ * Common function for read/write with UIO.
+ * Called by netsmb smb_usr_rw,
+ * smbfs_readvnode, smbfs_writevnode
+ */
+int
+smb_rwuio(struct smb_share *ssp, u_int16_t fid, uio_rw_t rw,
+ uio_t *uiop, struct smb_cred *scred, int timo)
+{
+ ssize_t old_resid, tsize;
+ offset_t old_offset;
+ int len, resid;
+ int error = 0;
+
+ old_offset = uiop->uio_loffset;
+ old_resid = tsize = uiop->uio_resid;
+
+ while (tsize > 0) {
+ /* Lint: tsize may be 64-bits */
+ len = SMB_MAX_LARGE_RW_SIZE;
+ if (len > tsize)
+ len = (int)tsize;
+
+ if (rw == UIO_READ)
+ error = smb_smb_readx(ssp, fid, &len, &resid, uiop,
+ scred, timo);
+ else
+ error = smb_smb_writex(ssp, fid, &len, &resid, uiop,
+ scred, timo);
+ if (error)
+ break;
+
+ if (resid < len) {
+ error = EIO;
+ break;
+ }
+
+ tsize -= resid;
+ timo = 0; /* only first write is special */
+ }
+
+ if (error) {
+ /*
+ * Errors can happen in copyin/copyout, the rpc, etc. so
+ * they imply resid is unreliable. The only safe thing is
+ * to pretend zero bytes made it. We needn't restore the
+ * iovs because callers don't depend on them in error
+ * paths - uio_resid and uio_offset are what matter.
+ */
+ uiop->uio_loffset = old_offset;
+ uiop->uio_resid = old_resid;
+ }
+
+ return (error);
+}
+
+
+static u_int32_t smbechoes = 0;
+
+int
+smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo)
+{
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ int error;
+
+ error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
+ if (error)
+ return (error);
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, 1); /* echo count */
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint32le(mbp, atomic_inc_32_nv(&smbechoes));
+ smb_rq_bend(rqp);
+ /*
+ * Note: the IOD calls this, so
+ * this request must not wait for
+ * connection state changes, etc.
+ */
+ rqp->sr_flags |= SMBR_INTERNAL;
+ error = smb_rq_simple_timed(rqp, timo);
+ SMBSDEBUG("%d\n", error);
+ smb_rq_done(rqp);
+ return (error);
+}
+
+#ifdef APPLE
+int
+smb_smb_checkdir(struct smb_share *ssp, void *dnp, char *name,
+ int nmlen, struct smb_cred *scred)
+{
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ int error;
+
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_CHECK_DIRECTORY, scred, &rqp);
+ if (error)
+ return (error);
+
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ /*
+ * All we need to do is marshall the path: "\\"
+ * (the root of the share) into this request.
+ * We essentially in-line smbfs_fullpath() here,
+ * except no mb_put_padbyte (already aligned).
+ */
+ smb_put_dstring(mbp, SSTOVC(ssp), "\\", SMB_CS_NONE);
+ smb_rq_bend(rqp);
+
+ error = smb_rq_simple(rqp);
+ SMBSDEBUG("%d\n", error);
+ smb_rq_done(rqp);
+
+ return (error);
+}
+#endif /* APPLE */
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h
new file mode 100644
index 0000000000..eb5895f702
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_subr.h,v 1.13 2004/09/14 22:59:08 lindak Exp $
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NETSMB_SMB_SUBR_H_
+#define _NETSMB_SMB_SUBR_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/cmn_err.h>
+#include <sys/lock.h>
+#include <sys/note.h>
+
+/* Helper function for SMBERROR */
+/*PRINTFLIKE3*/
+extern void smb_errmsg(int, const char *, const char *, ...)
+ __KPRINTFLIKE(3);
+void m_dumpm(mblk_t *m);
+
+/*
+ * Let's use C99 standard variadic macros!
+ * Also the C99 __func__ (function name) feature.
+ */
+#define SMBERROR(...) \
+ smb_errmsg(CE_NOTE, __func__, __VA_ARGS__)
+#define SMBPANIC(...) \
+ smb_errmsg(CE_PANIC, __func__, __VA_ARGS__)
+#define SMBSDEBUG(...) \
+ smb_errmsg(CE_CONT, __func__, __VA_ARGS__)
+#define SMBIODEBUG(...) \
+ smb_errmsg(CE_CONT, __func__, __VA_ARGS__)
+#define NBDEBUG(...) \
+ smb_errmsg(CE_CONT, __func__, __VA_ARGS__)
+
+#if defined(DEBUG) || defined(lint)
+
+#define DEBUG_ENTER(str) debug_enter(str)
+
+#else /* DEBUG or lint */
+
+#define DEBUG_ENTER(str) ((void)0)
+
+#endif /* DEBUG or lint */
+
+#define SMB_SIGMASK \
+ (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \
+ sigmask(SIGHUP)|sigmask(SIGQUIT))
+
+#define SMB_STRFREE(p) do { \
+ if (p) \
+ smb_strfree(p); \
+ _NOTE(CONSTCOND) \
+} while (0)
+
+typedef uint16_t smb_unichar;
+typedef smb_unichar *smb_uniptr;
+
+extern smb_unichar smb_unieol;
+
+struct mbchain;
+struct smb_rq;
+struct smb_vc;
+
+/*
+ * Tunable timeout values. See: smb_smb.c
+ */
+extern int smb_timo_notice;
+extern int smb_timo_default;
+extern int smb_timo_open;
+extern int smb_timo_read;
+extern int smb_timo_write;
+extern int smb_timo_append;
+
+#define EMOREDATA (0x7fff)
+
+#ifdef APPLE
+void smb_scred_init(struct smb_cred *scred, vfs_context_t vfsctx);
+int smb_sigintr(vfs_context_t);
+#endif
+void smb_credinit(struct smb_cred *scred, struct proc *p, cred_t *cr);
+void smb_credrele(struct smb_cred *scred);
+char *smb_strdup(const char *s);
+void *smb_memdup(const void *umem, int len);
+char *smb_strdupin(char *s, int maxlen);
+void *smb_memdupin(void *umem, int len);
+size_t smb_strtouni(uint16_t *dst, const char *src, size_t inlen, int flags);
+void smb_strfree(char *s);
+void smb_memfree(void *s);
+void *smb_zmalloc(unsigned long size);
+
+void smb_oldlm_hash(const char *apwd, uchar_t *hash);
+void smb_ntlmv1hash(const char *apwd, uchar_t *hash);
+
+int smb_lmresponse(const uchar_t *hash, uchar_t *C8, uchar_t *RN);
+int smb_ntlmresponse(const uchar_t *hash, uchar_t *C8, uchar_t *RN);
+int smb_ntlmv2response(const uchar_t *hash, const uchar_t *user,
+ const uchar_t *destination, uchar_t *C8, const uchar_t *blob,
+ size_t bloblen, uchar_t **RN, size_t *RNlen);
+int smb_maperror(int eclass, int eno);
+uint32_t smb_maperr32(uint32_t eno);
+int smb_put_dmem(struct mbchain *mbp, struct smb_vc *vcp,
+ const char *src, int len, int caseopt, int *lenp);
+int smb_put_dstring(struct mbchain *mbp, struct smb_vc *vcp,
+ const char *src, int caseopt);
+int smb_put_string(struct smb_rq *rqp, const char *src);
+int smb_put_asunistring(struct smb_rq *rqp, const char *src);
+int smb_checksmp(void);
+
+int smb_cmp_sockaddr(struct sockaddr *, struct sockaddr *);
+struct sockaddr *smb_dup_sockaddr(struct sockaddr *sa);
+void smb_free_sockaddr(struct sockaddr *sa);
+int smb_toupper(const char *, char *, size_t);
+
+#endif /* !_NETSMB_SMB_SUBR_H_ */
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c
new file mode 100644
index 0000000000..41341a0308
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c
@@ -0,0 +1,1179 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_subr.c,v 1.27.108.1 2005/06/02 00:55:39 lindak Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kmem.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/socket.h>
+#include <sys/isa_defs.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/sunddi.h>
+#include <sys/cmn_err.h>
+#include <sys/sdt.h>
+#include <sys/priv.h>
+#include <sys/u8_textprep.h>
+
+#include <netsmb/smb_osdep.h>
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_rq.h>
+#include <netsmb/smb_subr.h>
+
+/*
+ * XXX:This conversion might not be fully MS-Compatible
+ * for calculating hashes. The output length may differ
+ * for some locales and needs to be handled from where
+ * the call is made.
+ */
+int
+smb_toupper(const char *inbuf, char *outbuf, size_t outlen)
+{
+ int err = 0;
+ size_t inlen, inrem, outrem;
+
+ inrem = inlen = strlen(inbuf);
+ outrem = outlen;
+ (void) u8_textprep_str((char *)inbuf, &inrem, outbuf, &outrem,
+ U8_TEXTPREP_TOUPPER, U8_UNICODE_LATEST, &err);
+ /* inrem, outrem are bytes unused, remaining */
+ if (inrem) {
+ SMBSDEBUG("input %d remains: %s\n", (int)inrem, inbuf);
+ inlen -= inrem;
+ }
+ if (outrem) {
+ outlen -= outrem;
+ outbuf[outlen] = '\0';
+ }
+ if (outlen > inlen) {
+ SMBSDEBUG("outlen > inlen! (%d > %d)\n",
+ (int)outlen, (int)inlen);
+ /* Truncate to inlen here? */
+ }
+
+ return (err);
+}
+
+void
+smb_credinit(struct smb_cred *scred, struct proc *p, cred_t *icr)
+{
+ scred->vc_pid = p->p_pidp->pid_id;
+ if (!icr)
+ icr = p->p_cred;
+ if (is_system_labeled()) {
+ icr = crdup(icr);
+ (void) setpflags(NET_MAC_AWARE, 1, icr);
+ } else {
+ crhold(icr);
+ }
+ scred->vc_ucred = icr;
+}
+
+void
+smb_credrele(struct smb_cred *scred)
+{
+ crfree(scred->vc_ucred);
+ scred->vc_ucred = NULL;
+}
+
+#ifdef APPLE
+/*ARGSUSED*/
+int
+smb_sigintr(vfs_context_t vfsctx)
+{
+ /*
+ * I cannot find something to match vfs_context_issignal.
+ * It calls proc_pendingsignals() in Darwin code.
+ */
+ if (vfsctx && vfs_context_issignal(vfsctx, SMB_SIGMASK))
+ return (EINTR);
+ return (0);
+}
+#endif
+
+char *
+smb_strdup(const char *s)
+{
+ char *p;
+ int len;
+
+ len = s ? strlen(s) + 1 : 1;
+ p = kmem_alloc(len, KM_SLEEP);
+ if (s)
+ bcopy(s, p, len);
+ else
+ *p = 0;
+ return (p);
+}
+
+/*
+ * duplicate string from a user space.
+ */
+char *
+smb_strdupin(char *s, int maxlen)
+{
+ char *p, bt;
+ int len = 0;
+
+ for (p = s; ; p++) {
+ if (copyin(p, &bt, 1))
+ return (NULL);
+ len++;
+ if (maxlen && len > maxlen)
+ return (NULL);
+ if (bt == 0)
+ break;
+ }
+ p = kmem_alloc(len, KM_SLEEP);
+ copyin(s, p, len);
+ return (p);
+}
+
+/*
+ * duplicate memory block from a user space.
+ */
+void *
+smb_memdupin(void *umem, int len)
+{
+ char *p;
+
+ if (len > 32 * 1024)
+ return (NULL);
+ p = kmem_alloc(len, KM_SLEEP);
+ if (copyin(umem, p, len) == 0)
+ return (p);
+ kmem_free(p, len);
+ return (NULL);
+}
+
+/*
+ * duplicate memory block in the kernel space.
+ */
+void *
+smb_memdup(const void *umem, int len)
+{
+ char *p;
+
+ if (len > 32 * 1024)
+ return (NULL);
+ p = kmem_alloc(len, KM_SLEEP);
+ if (p == NULL)
+ return (NULL);
+ bcopy(umem, p, len);
+ return (p);
+}
+
+void
+smb_strfree(char *s)
+{
+ kmem_free(s, strlen(s) + 1);
+}
+
+void
+smb_memfree(void *s)
+{
+ kmem_free(s, strlen(s));
+}
+
+void *
+smb_zmalloc(unsigned long size)
+{
+ void *p = kmem_zalloc(size, KM_SLEEP);
+ return (p);
+}
+
+size_t
+smb_strtouni(u_int16_t *dst, const char *src, size_t inlen, int flags)
+{
+ size_t outlen = 0;
+
+ if (!inlen)
+ inlen = strlen(src);
+
+ /* Force output format to little-endian. */
+ flags &= ~UCONV_OUT_BIG_ENDIAN;
+ flags |= UCONV_OUT_LITTLE_ENDIAN;
+
+ outlen = inlen * 2;
+ if (uconv_u8tou16((uchar_t *)src, &inlen, dst, &outlen, flags) != 0) {
+ outlen = 0;
+ }
+ return (outlen * 2);
+}
+
+/*
+ * Helper for the SMBERROR macro, etc.
+ * This is also a good place for a breakpoint
+ * or a dtrace probe, i.e. fbt:nsmb:smb_errmsg
+ */
+void
+smb_errmsg(int cel, const char *func_name, const char *fmt, ...)
+{
+ va_list adx;
+ char buf[100];
+
+ va_start(adx, fmt);
+ if (cel == CE_CONT) {
+ /*
+ * This is one of our xxxDEBUG macros.
+ * Don't bother to log these, but just
+ * fire a dtrace probe with the message.
+ */
+ vsnprintf(buf, sizeof (buf), fmt, adx);
+ DTRACE_PROBE2(debugmsg2,
+ (char *), func_name,
+ (char *), buf);
+ } else {
+ /*
+ * This is one of our xxxERROR macros.
+ * Add a prefix to the fmt string,
+ * then let vcmn_err do the args.
+ */
+ snprintf(buf, sizeof (buf), "?%s: %s", func_name, fmt);
+ DTRACE_PROBE3(debugmsg3,
+ (char *), func_name,
+ (char *), buf,
+ va_list, adx);
+ vcmn_err(cel, buf, adx);
+ }
+ va_end(adx);
+}
+
+#if 1 /* def SMB_SOCKETDATA_DEBUG */
+void
+m_dumpm(mblk_t *m)
+{
+ int len, seg;
+
+ len = msgdsize(m);
+ DTRACE_PROBE2(dsize, int, len, (mblk_t *), m);
+
+ for (seg = 0; m; seg++) {
+ DTRACE_PROBE2(mblk, int, seg, (mblk_t *), m);
+ m = m->b_cont;
+ }
+}
+#endif
+
+/* all these need review XXX */
+#ifndef EPROTO
+#define EPROTO ECONNABORTED
+#endif
+#ifndef ELIBACC
+#define ELIBACC ENOENT
+#endif
+#ifndef ENODATA
+#define ENODATA EINVAL
+#endif
+#ifndef ENOTUNIQ
+#define ENOTUNIQ EADDRINUSE
+#endif
+#ifndef ECOMM
+#define ECOMM EIO
+#endif
+#ifndef ENOMEDIUM
+#define ENOMEDIUM EIO
+#endif
+#ifndef ETIME
+#define ETIME ETIMEDOUT
+#endif
+
+static struct {
+ unsigned nterr;
+ unsigned errno;
+} nt2errno[] = {
+ {NT_STATUS_ACCESS_DENIED, EACCES},
+ {NT_STATUS_ACCESS_VIOLATION, EACCES},
+ {NT_STATUS_ACCOUNT_DISABLED, EACCES},
+ {NT_STATUS_ACCOUNT_RESTRICTION, EACCES},
+ {NT_STATUS_ADDRESS_ALREADY_EXISTS, EADDRINUSE},
+ {NT_STATUS_BAD_NETWORK_NAME, ENOENT},
+ {NT_STATUS_BUFFER_TOO_SMALL, EMOREDATA},
+ {NT_STATUS_CANNOT_DELETE, EACCES},
+ {NT_STATUS_CONFLICTING_ADDRESSES, EADDRINUSE},
+ {NT_STATUS_CONNECTION_ABORTED, ECONNABORTED},
+ {NT_STATUS_CONNECTION_DISCONNECTED, ECONNABORTED},
+ {NT_STATUS_CONNECTION_REFUSED, ECONNREFUSED},
+ {NT_STATUS_CONNECTION_RESET, ENETRESET},
+ {NT_STATUS_DEVICE_DOES_NOT_EXIST, ENODEV},
+ {NT_STATUS_DEVICE_PROTOCOL_ERROR, EPROTO},
+ {NT_STATUS_DIRECTORY_NOT_EMPTY, ENOTEMPTY},
+ {NT_STATUS_DISK_FULL, ENOSPC},
+ {NT_STATUS_DLL_NOT_FOUND, ELIBACC},
+ {NT_STATUS_END_OF_FILE, ENODATA},
+ {NT_STATUS_FILE_IS_A_DIRECTORY, EISDIR},
+ {NT_STATUS_FLOAT_INEXACT_RESULT, ERANGE},
+ {NT_STATUS_FLOAT_OVERFLOW, ERANGE},
+ {NT_STATUS_FLOAT_UNDERFLOW, ERANGE},
+ {NT_STATUS_HOST_UNREACHABLE, EHOSTUNREACH},
+ {NT_STATUS_ILL_FORMED_PASSWORD, EACCES},
+ {NT_STATUS_INTEGER_OVERFLOW, ERANGE},
+ {NT_STATUS_INVALID_HANDLE, EBADF},
+ {NT_STATUS_INVALID_LOGON_HOURS, EACCES},
+ {NT_STATUS_INVALID_PARAMETER, EINVAL},
+ {NT_STATUS_INVALID_PIPE_STATE, EPIPE},
+ {NT_STATUS_INVALID_WORKSTATION, EACCES},
+ {NT_STATUS_IN_PAGE_ERROR, EFAULT},
+ {NT_STATUS_IO_TIMEOUT, ETIMEDOUT},
+ {NT_STATUS_IP_ADDRESS_CONFLICT1, ENOTUNIQ},
+ {NT_STATUS_IP_ADDRESS_CONFLICT2, ENOTUNIQ},
+ {NT_STATUS_LICENSE_QUOTA_EXCEEDED, EDQUOT},
+ {NT_STATUS_LOGON_FAILURE, EACCES},
+ {NT_STATUS_MEDIA_WRITE_PROTECTED, EROFS},
+ {NT_STATUS_MEMORY_NOT_ALLOCATED, EFAULT},
+ {NT_STATUS_NAME_TOO_LONG, ENAMETOOLONG},
+ {NT_STATUS_NETWORK_ACCESS_DENIED, EACCES},
+ {NT_STATUS_NETWORK_BUSY, EBUSY},
+ {NT_STATUS_NETWORK_UNREACHABLE, ENETUNREACH},
+ {NT_STATUS_NET_WRITE_FAULT, ECOMM},
+ {NT_STATUS_NONEXISTENT_SECTOR, ESPIPE},
+ {NT_STATUS_NOT_A_DIRECTORY, ENOTDIR},
+ {NT_STATUS_NOT_IMPLEMENTED, ENOSYS},
+ {NT_STATUS_NOT_MAPPED_VIEW, EINVAL},
+ {NT_STATUS_NOT_SUPPORTED, ENOSYS},
+ {NT_STATUS_NO_MEDIA, ENOMEDIUM},
+ {NT_STATUS_NO_MEDIA_IN_DEVICE, ENOMEDIUM},
+ {NT_STATUS_NO_MEMORY, ENOMEM},
+ {NT_STATUS_NO_SUCH_DEVICE, ENODEV},
+ {NT_STATUS_NO_SUCH_FILE, ENOENT},
+ {NT_STATUS_OBJECT_NAME_COLLISION, EEXIST},
+ {NT_STATUS_OBJECT_NAME_NOT_FOUND, ENOENT},
+ {NT_STATUS_OBJECT_PATH_INVALID, ENOTDIR},
+ {NT_STATUS_PAGEFILE_QUOTA, EDQUOT},
+ {NT_STATUS_PASSWORD_EXPIRED, EACCES},
+ {NT_STATUS_PASSWORD_RESTRICTION, EACCES},
+ {NT_STATUS_PATH_NOT_COVERED, ENOENT},
+ {NT_STATUS_PIPE_BROKEN, EPIPE},
+ {NT_STATUS_PIPE_BUSY, EPIPE},
+ {NT_STATUS_PIPE_CONNECTED, EISCONN},
+ {NT_STATUS_PIPE_DISCONNECTED, EPIPE},
+ {NT_STATUS_PIPE_NOT_AVAILABLE, ENOSYS},
+ {NT_STATUS_PORT_CONNECTION_REFUSED, ECONNREFUSED},
+ {NT_STATUS_PORT_MESSAGE_TOO_LONG, EMSGSIZE},
+ {NT_STATUS_PORT_UNREACHABLE, EHOSTUNREACH},
+ {NT_STATUS_PROTOCOL_UNREACHABLE, ENOPROTOOPT},
+ {NT_STATUS_QUOTA_EXCEEDED, EDQUOT},
+ {NT_STATUS_REGISTRY_QUOTA_LIMIT, EDQUOT},
+ {NT_STATUS_REMOTE_DISCONNECT, ESHUTDOWN},
+ {NT_STATUS_REMOTE_NOT_LISTENING, ECONNREFUSED},
+ {NT_STATUS_REQUEST_NOT_ACCEPTED, EACCES},
+ {NT_STATUS_RETRY, EAGAIN},
+ {NT_STATUS_SHARING_VIOLATION, EBUSY},
+ {NT_STATUS_TIMER_NOT_CANCELED, ETIME},
+ {NT_STATUS_TOO_MANY_LINKS, EMLINK},
+ {NT_STATUS_TOO_MANY_OPENED_FILES, EMFILE},
+ {NT_STATUS_UNABLE_TO_FREE_VM, EADDRINUSE},
+ {NT_STATUS_UNSUCCESSFUL, EINVAL},
+ {NT_STATUS_WRONG_PASSWORD, EACCES},
+ {0, 0}
+};
+
+static struct {
+ unsigned dclass;
+ unsigned derr;
+ unsigned nterr;
+} nt2doserr[] = {
+ {ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL},
+ {ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRDOS, ERRbadlength, NT_STATUS_INFO_LENGTH_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA},
+ {ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK},
+ {ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_CID},
+ {ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER},
+ {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE},
+ {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE},
+ {ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST},
+ {ERRDOS, ERRhandleeof, NT_STATUS_END_OF_FILE},
+ {ERRDOS, ERRwrongdisk, NT_STATUS_WRONG_VOLUME},
+ {ERRDOS, ERRnotready, NT_STATUS_NO_MEDIA_IN_DEVICE},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA},
+ {ERRDOS, ERRsectornotfound, NT_STATUS_NONEXISTENT_SECTOR},
+ {ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY},
+ {ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES},
+ {ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_UNABLE_TO_FREE_VM},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_UNABLE_TO_DELETE_SECTION},
+ {ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION},
+ {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE},
+ {ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION},
+ {ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED},
+ {ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL},
+ {ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNWIND},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET},
+ {ERRDOS, 158, NT_STATUS_NOT_LOCKED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR},
+ {ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM},
+ {ERRDOS, 487, NT_STATUS_NOT_COMMITTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES},
+ {ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_MIX},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR},
+ {ERRDOS, ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND},
+ {ERRDOS, 183, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE},
+ {ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED},
+ {ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID},
+ {ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND},
+ {ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD},
+ {ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN},
+ {ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR},
+ {ERRDOS, ERRcrc, NT_STATUS_DATA_ERROR},
+ {ERRDOS, ERRcrc, NT_STATUS_CRC_ERROR},
+ {ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG},
+ {ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED},
+ {ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE},
+ {ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PAGE_PROTECTION},
+ {ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED},
+ {ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_PORT_ALREADY_SET},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_SECTION_NOT_IMAGE},
+ {ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_BAD_WORKING_SET_LIMIT},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INCOMPATIBLE_FILE_MAP},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_SECTION_PROTECTION},
+ {ERRDOS, 282, NT_STATUS_EAS_NOT_SUPPORTED},
+ {ERRDOS, 255, NT_STATUS_EA_TOO_LARGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR},
+ {ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT},
+ {ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_DELETE_PENDING},
+ {ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION},
+ {ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY},
+ {ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME},
+ {ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER},
+ {ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN},
+ {ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD},
+ {ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION},
+ {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION},
+ {ERRSRV, 2241, NT_STATUS_INVALID_LOGON_HOURS},
+ {ERRSRV, 2240, NT_STATUS_INVALID_WORKSTATION},
+ {ERRSRV, 2242, NT_STATUS_PASSWORD_EXPIRED},
+ {ERRSRV, 2239, NT_STATUS_ACCOUNT_DISABLED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR},
+ {ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL},
+ {ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED},
+ {ERRDOS, 112, NT_STATUS_DISK_FULL},
+ {ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED},
+ {ERRDOS, ERRtoomanynames, NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+ {ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY},
+ {ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED},
+ {ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL},
+ {ERRDOS, ERRoutofmem, NT_STATUS_SECTION_NOT_EXTENDED},
+ {ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO},
+ {ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION},
+ {ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES},
+ {ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID},
+ {ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED},
+ {ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND},
+ {ERRDOS, ERRcrc, NT_STATUS_DEVICE_DATA_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED},
+ {ERRDOS, ERRnotready, NT_STATUS_DEVICE_POWER_FAILURE},
+ {ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE},
+ {ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA},
+ {ERRDOS, ERRwriteprotect, NT_STATUS_MEDIA_WRITE_PROTECTED},
+ {ERRDOS, ERRnotready, NT_STATUS_DEVICE_NOT_READY},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_BAD_MASTER_BOOT_RECORD},
+ {ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT},
+ {ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE},
+ {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE},
+ {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE},
+ {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY},
+ {ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION},
+ {ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED},
+ {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING},
+ {ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING},
+ {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE},
+ {ERRDOS, 121, NT_STATUS_IO_TIMEOUT},
+ {ERRDOS, ERRhandleeof, NT_STATUS_FILE_FORCED_CLOSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED},
+ {ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET},
+ {ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY},
+ {ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED},
+ {ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING},
+ {ERRDOS, 52, NT_STATUS_DUPLICATE_NAME},
+ {ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH},
+ {ERRDOS, 54, NT_STATUS_NETWORK_BUSY},
+ {ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST},
+ {ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS},
+ {ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR},
+ {ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE},
+ {ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+ {ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER},
+ {ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL},
+ {ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE},
+ {ERRDOS, 63, NT_STATUS_PRINT_CANCELLED},
+ {ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED},
+ {ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES},
+ {ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS},
+ {ERRDOS, 70, NT_STATUS_SHARING_PAUSED},
+ {ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED},
+ {ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT},
+ {ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED},
+ {ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT},
+ {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED},
+ {ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED},
+ {ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_1},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_2},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_3},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_4},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_5},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_6},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_7},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_8},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_9},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_10},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_11},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_12},
+ {ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE},
+ {ERRDOS, 203, NT_STATUS_VARIABLE_NOT_FOUND},
+ {ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR},
+ {ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION},
+ {ERRDOS, 206, NT_STATUS_NAME_TOO_LONG},
+ {ERRDOS, 2401, NT_STATUS_FILES_OPEN},
+ {ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE},
+ {ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND},
+ {ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_LDT},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT},
+ {ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO},
+ {ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANCELLED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME},
+ {ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT},
+ {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP},
+ {ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS},
+ {ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED},
+ {ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT},
+ {ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC},
+ {ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED},
+ {ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED},
+ {ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND},
+ {ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT},
+ {ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT},
+ {ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT},
+ {ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES},
+ {ERRDOS, 59, NT_STATUS_LINK_FAILED},
+ {ERRDOS, 59, NT_STATUS_LINK_TIMEOUT},
+ {ERRDOS, 59, NT_STATUS_INVALID_CONNECTION},
+ {ERRDOS, 59, NT_STATUS_INVALID_ADDRESS},
+ {ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE},
+ {ERRDOS, 124, NT_STATUS_INVALID_LEVEL},
+ {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT},
+ {ERRDOS, 109, NT_STATUS_PIPE_BROKEN},
+ {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT},
+ {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME},
+ {ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS},
+ {ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS},
+ {ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS},
+ {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY},
+ {ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING},
+ {ERRHRD, ERRgeneral, NT_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT},
+ {ERRHRD, ERRgeneral, NT_STATUS_16F},
+ {ERRHRD, ERRgeneral, NT_STATUS_170},
+ {ERRHRD, ERRgeneral, NT_STATUS_171},
+ {ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA},
+ {ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA},
+ {ERRHRD, ERRgeneral, NT_STATUS_179},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER},
+ {ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS},
+ {ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN},
+ {ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE},
+ {ERRDOS, ERRinvalidparam, NT_STATUS_DEVICE_CONFIGURATION_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR},
+ {ERRDOS, ERRbadcmd, NT_STATUS_INVALID_DEVICE_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL},
+ {ERRDOS, ERRwriteprotect, NT_STATUS_TOO_LATE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT},
+ {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT},
+ {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START},
+ {ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED},
+ {ERRDOS, ERRinvgroup, NT_STATUS_NETLOGON_NOT_STARTED},
+ {ERRSRV, 2239, NT_STATUS_ACCOUNT_EXPIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK},
+ {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT},
+ {ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT},
+ {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED},
+ {ERRDOS, ERRnoaccess,
+ NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT},
+ {ERRDOS, ERRnoaccess,
+ NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT},
+ {ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY},
+ {ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND},
+ {ERRDOS, ERRnomem, NT_STATUS_INSUFF_SERVER_RESOURCES},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES},
+ {ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS},
+ {ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED},
+ {ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED},
+ {ERRDOS, 64, NT_STATUS_CONNECTION_RESET},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_NODES},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE},
+ {ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION},
+ {ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT},
+ {ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT},
+ {ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA},
+ {ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID},
+ {ERRSRV, 2242, NT_STATUS_PASSWORD_MUST_CHANGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM},
+ {ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ},
+ {ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK},
+ {ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID},
+ {ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_RETRY},
+ {ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND},
+ {ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT},
+ {ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT},
+ {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER},
+ {ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION},
+ {ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH},
+ {ERRHRD, ERRgeneral, 0x000024a},
+ {ERRHRD, ERRgeneral, 0x000024b},
+ {ERRHRD, ERRgeneral, 0x000024c},
+ {ERRHRD, ERRgeneral, 0x000024d},
+ {ERRHRD, ERRgeneral, 0x000024e},
+ {ERRHRD, ERRgeneral, 0x000024f},
+ {ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT},
+ {ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST},
+ {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1},
+ {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2},
+ {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT},
+ {ERRSRV, ERRbadtype, NT_STATUS_PATH_NOT_COVERED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE},
+ {ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT},
+ {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT},
+ {ERRHRD, ERRgeneral, 0x000025d},
+ {ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH},
+ {ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND},
+ {ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND},
+ {ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LINKS},
+ {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE},
+ {ERRDOS, ERRnotready, NT_STATUS_VOLUME_DISMOUNTED},
+ {ERRDOS, 161, NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT},
+ {ERRDOS, ERRnoaccess, NT_STATUS_ENCRYPTION_FAILED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_DECRYPTION_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_RANGE_NOT_FOUND},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_RECOVERY_POLICY},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_EFS},
+ {ERRDOS, ERRnoaccess, NT_STATUS_WRONG_EFS},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_USER_KEYS},
+ {ERRDOS, ERRbadfunc, NT_STATUS_VOLUME_NOT_UPGRADED},
+};
+
+u_int32_t
+smb_maperr32(u_int32_t eno)
+{
+ int i;
+ unsigned orig = eno;
+
+ /*
+ * Hi two bits are "severity". Ignore "success" (0) and
+ * "informational" (1) values.
+ */
+ if (!(eno & 0x80000000))
+ return (0);
+ /* mask off "severity" and the "component" bit */
+ eno &= ~(0xe0000000);
+
+ /* first try direct map to unix */
+ for (i = 0; nt2errno[i].errno; i++)
+ if (nt2errno[i].nterr == eno)
+ return (nt2errno[i].errno);
+ SMBERROR("no direct map for 32 bit server error (0x%x)\n", orig);
+
+ /* ok, then try mapping to dos to unix */
+ for (i = 0; nt2doserr[i].derr; i++)
+ if (nt2doserr[i].nterr == eno)
+ return (smb_maperror(nt2doserr[i].dclass,
+ nt2doserr[i].derr));
+ return (smb_maperror(ERRHRD, ERRgeneral));
+}
+
+
+int
+smb_maperror(int eclass, int eno)
+{
+ if (eclass == 0 && eno == 0)
+ return (0);
+ switch (eclass) {
+ case ERRDOS:
+ switch (eno) {
+ case ERRbadfunc:
+ case ERRbadenv:
+ case ERRbadformat:
+ case ERRremcd:
+ case ERRrmuns:
+ return (EINVAL);
+ case ERRbadfile:
+ case ERRbadpath:
+ case ERRnoipc:
+ case ERRnosuchshare:
+ return (ENOENT);
+ case ERRnofids:
+ return (EMFILE);
+ case ERRnoaccess:
+ /*
+ * XXX CSM Reported on samba-technical 12/7/2002
+ *
+ * There is a case for which server(s) return
+ * ERRnoaccess but should return ERRdiskfull: When
+ * the offset for a write is exactly the server
+ * file size limit then Samba (at least) thinks
+ * the reason for zero bytes having been written
+ * must have been "access denied" from the local
+ * filesystem. This cannot be easily worked
+ * around since the server behaviour is
+ * indistinguishable from actual access denied.
+ * An incomplete workaround: attempt a 2 byte write
+ * from "offset-1". (That may require reading at
+ * offset-1 first.) The flaw is that reading or
+ * writing at offset-1 could cause an
+ * unrelated error (due to a byte range lock
+ * for instance) and we can't presume the
+ * order servers check errors in.
+ */
+ case ERRbadaccess:
+ return (EACCES);
+ case ERRbadshare:
+ return (EBUSY);
+ case ERRbadfid:
+ return (EBADF);
+ case ERRbadmcb:
+ return (EIO);
+ case ERRnomem:
+ return (ENOMEM); /* actually remote no mem... */
+ case ERRbadmem:
+ return (EFAULT);
+ case ERRbaddata:
+ return (E2BIG);
+ case ERRbaddrive:
+ case ERRnotready: /* nt */
+ return (ENXIO);
+ case ERRdiffdevice:
+ return (EXDEV);
+ case ERRnofiles:
+ return (0); /* eeof ? */
+ case ERRlock:
+ return (EDEADLK);
+ case ERRfilexists:
+ return (EEXIST);
+ case ERRinvalidname: /* samba maps as noent */
+ return (ENOENT);
+ case ERRdirnotempty: /* samba */
+ return (ENOTEMPTY);
+ case ERRnotlocked:
+ return (0); /* 0 since bsd unlocks on any close */
+ case ERRrename:
+ return (EEXIST);
+ case ERRmoredata:
+ return (EMOREDATA);
+ }
+ break;
+ case ERRSRV:
+ switch (eno) {
+ case ERRerror:
+ return (EINVAL);
+ case ERRbadpw:
+ return (EAUTH);
+ case ERRaccess:
+ case ERRbaduid:
+ return (EACCES);
+ case ERRinvnid:
+ return (ENETRESET);
+ case ERRinvnetname:
+ SMBERROR("NetBIOS name is invalid: %d\n",
+ ERRinvnetname);
+ return (EAUTH);
+ case ERRbadtype: /* reserved and returned */
+ return (EIO);
+ case ERRacctexpired: /* NT: account exists but disabled */
+ return (EPERM);
+ }
+ break;
+ case ERRHRD:
+ switch (eno) {
+ case ERRnowrite:
+ return (EROFS);
+ case ERRbadunit:
+ return (ENODEV);
+ case ERRbadreq:
+ return (EBADRPC);
+ case ERRbadshare:
+ return (ETXTBSY);
+ case ERRlock:
+ return (EDEADLK);
+ case ERRdiskfull:
+ return (EFBIG);
+ case ERRnotready:
+ case ERRbadcmd:
+ case ERRdata:
+ case ERRgeneral:
+ return (EIO);
+ default:
+ SMBERROR("Unmapped DOS error %d:%d\n", eclass, eno);
+ return (EIO);
+ }
+ }
+ SMBERROR("Unmapped DOS error %d:%d\n", eclass, eno);
+ return (EBADRPC);
+}
+
+#if defined(NOICONVSUPPORT) || defined(lint)
+extern int iconv_conv(void *handle, const char **inbuf,
+ size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
+#endif
+
+#define SMALL_CONV 256
+
+/*ARGSUSED*/
+int
+smb_put_dmem(struct mbchain *mbp, struct smb_vc *vcp, const char *src,
+ int size, int caseopt, int *lenp)
+{
+ uint16_t convbuf[SMALL_CONV];
+ uint16_t *cbuf;
+ size_t cbufalloc, inlen, outlen;
+ int error;
+
+ if (size <= 0)
+ return (0);
+
+ /*
+ * Handle the easy case (non-unicode).
+ * XXX: Technically, we should convert
+ * the string to OEM codeset first...
+ * Modern servers all use Unicode, so
+ * this is good enough.
+ */
+ if (SMB_UNICODE_STRINGS(vcp) == 0) {
+ error = mb_put_mem(mbp, src, size, MB_MSYSTEM);
+ if (!error && lenp)
+ *lenp += size;
+ return (error);
+ }
+
+ /*
+ * Convert to UCS-2 (really UTF-16).
+ * Use stack buffer if the string is
+ * small enough, else allocate.
+ */
+ if (size <= SMALL_CONV) {
+ cbufalloc = 0;
+ outlen = SMALL_CONV;
+ cbuf = convbuf;
+ } else {
+ outlen = size; /* in utf-16 characters */
+ cbufalloc = outlen * 2;
+ cbuf = kmem_alloc(cbufalloc, KM_SLEEP);
+ }
+
+ inlen = size;
+ error = uconv_u8tou16((uchar_t *)src, &inlen, cbuf, &outlen,
+ UCONV_OUT_LITTLE_ENDIAN | UCONV_IGNORE_NULL);
+ outlen *= 2; /* convert to bytes */
+
+ if (!error) {
+ (void) mb_put_padbyte(mbp); /* align */
+ error = mb_put_mem(mbp, (char *)cbuf, outlen, MB_MSYSTEM);
+ }
+ if (!error && lenp)
+ *lenp += outlen;
+
+ if (cbufalloc)
+ kmem_free(cbuf, cbufalloc);
+
+ return (error);
+}
+
+int
+smb_put_dstring(struct mbchain *mbp, struct smb_vc *vcp, const char *src,
+ int caseopt)
+{
+ int error, len;
+
+ /*
+ * Let smb_put_dmem put both the string
+ * and the terminating null.
+ */
+ len = strlen(src) + 1;
+ error = smb_put_dmem(mbp, vcp, src, len, caseopt, NULL);
+ if (error)
+ return (error);
+
+ return (error);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.c
new file mode 100644
index 0000000000..7a646b48c7
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Selected code from smb_conn.c
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Helper functions for smb_trantcp.c
+ * (and maybe future transports)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+/* Like smb_dev.h, this knows about all our sockaddr formats. */
+#include <netsmb/netbios.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#else
+#include <netsmb/smb_osdep.h>
+#endif
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_tran.h>
+
+/*
+ * Return the length of a sockaddr structure.
+ * Only needs to handle the address formats
+ * used by smb_dup_sockaddr.
+ */
+static size_t
+SA_LEN(struct sockaddr *sa)
+{
+ size_t len;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ len = sizeof (struct sockaddr_in);
+ break;
+ case AF_INET6:
+ len = sizeof (struct sockaddr_in6);
+ break;
+ case AF_NETBIOS:
+ len = sizeof (struct sockaddr_nb);
+ break;
+ default:
+ SMBSDEBUG("invalid address family %d\n", sa->sa_family);
+ len = sizeof (struct sockaddr);
+ break;
+ }
+
+ return (len);
+}
+
+/*
+ * Compare two sockaddr contents
+ * Return zero if identical.
+ */
+int
+smb_cmp_sockaddr(struct sockaddr *a1, struct sockaddr *a2)
+{
+ size_t l1, l2;
+
+ l1 = SA_LEN(a1);
+ l2 = SA_LEN(a2);
+
+ if (l1 != l2)
+ return (-1);
+
+ return (bcmp(a1, a2, l1));
+}
+
+/*
+ * Copy a socket address of varying size.
+ */
+struct sockaddr *
+smb_dup_sockaddr(struct sockaddr *sa)
+{
+ struct sockaddr *sa2;
+ size_t len;
+
+ /* Get the length (varies per family) */
+ len = SA_LEN(sa);
+
+ sa2 = kmem_alloc(len, KM_SLEEP);
+ if (sa2)
+ bcopy(sa, sa2, len);
+
+ return (sa2);
+}
+
+void
+smb_free_sockaddr(struct sockaddr *sa)
+{
+ size_t len;
+
+ /* Get the length (varies per family) */
+ len = SA_LEN(sa);
+
+ kmem_free(sa, len);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h
new file mode 100644
index 0000000000..d82e3d862b
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_tran.h,v 1.2 2001/12/21 02:41:30 conrad Exp $
+ */
+
+#ifndef _NETSMB_SMB_TRAN_H_
+#define _NETSMB_SMB_TRAN_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/socket.h>
+
+/*
+ * Known transports
+ */
+#define SMBT_NBTCP 1
+
+/*
+ * Transport parameters
+ */
+#define SMBTP_SNDSZ 1 /* R - int */
+#define SMBTP_RCVSZ 2 /* R - int */
+#define SMBTP_TIMEOUT 3 /* RW - struct timespec */
+#ifndef __sun
+#define SMBTP_SELECTID 4 /* RW - (void *) */
+#define SMBTP_UPCALL 5 /* RW - (* void)(void *) */
+#endif
+
+struct smb_tran_ops;
+
+struct smb_tran_desc {
+ sa_family_t tr_type;
+ int (*tr_create)(struct smb_vc *vcp, struct proc *p);
+ int (*tr_done)(struct smb_vc *vcp, struct proc *p);
+ int (*tr_bind)(struct smb_vc *vcp, struct sockaddr *sap,
+ struct proc *p);
+ int (*tr_connect)(struct smb_vc *vcp, struct sockaddr *sap,
+ struct proc *p);
+ int (*tr_disconnect)(struct smb_vc *vcp, struct proc *p);
+ int (*tr_send)(struct smb_vc *vcp, mblk_t *m0, struct proc *p);
+ int (*tr_recv)(struct smb_vc *vcp, mblk_t **mpp, struct proc *p);
+ int (*tr_poll)(struct smb_vc *vcp, int ticks, struct proc *p);
+ int (*tr_getparam)(struct smb_vc *vcp, int param, void *data);
+ int (*tr_setparam)(struct smb_vc *vcp, int param, void *data);
+ int (*tr_fatal)(struct smb_vc *vcp, int error);
+#ifdef notyet
+ int (*tr_cmpaddr)(void *addr1, void *addr2);
+#endif
+ LIST_ENTRY(smb_tran_desc) tr_link;
+};
+typedef struct smb_tran_desc smb_tran_desc_t;
+
+#define SMB_TRAN_CREATE(vcp, p) (vcp)->vc_tdesc->tr_create(vcp, p)
+#define SMB_TRAN_DONE(vcp, p) (vcp)->vc_tdesc->tr_done(vcp, p)
+#define SMB_TRAN_BIND(vcp, sap, p) (vcp)->vc_tdesc->tr_bind(vcp, sap, p)
+#define SMB_TRAN_CONNECT(vcp, sap, p) (vcp)->vc_tdesc->tr_connect(vcp, sap, p)
+#define SMB_TRAN_DISCONNECT(vcp, p) (vcp)->vc_tdesc->tr_disconnect(vcp, p)
+#define SMB_TRAN_SEND(vcp, m0, p) (vcp)->vc_tdesc->tr_send(vcp, m0, p)
+#define SMB_TRAN_RECV(vcp, m, p) (vcp)->vc_tdesc->tr_recv(vcp, m, p)
+#define SMB_TRAN_POLL(vcp, t, p) (vcp)->vc_tdesc->tr_poll(vcp, t, p)
+#define SMB_TRAN_GETPARAM(vcp, par, data) \
+ (vcp)->vc_tdesc->tr_getparam(vcp, par, data)
+#define SMB_TRAN_SETPARAM(vcp, par, data) \
+ (vcp)->vc_tdesc->tr_setparam(vcp, par, data)
+#define SMB_TRAN_FATAL(vcp, error) (vcp)->vc_tdesc->tr_fatal(vcp, error)
+
+/* Ops vectors for each transport. */
+extern struct smb_tran_desc smb_tran_nbtcp_desc;
+
+#endif /* _NETSMB_SMB_TRAN_H_ */
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c
new file mode 100644
index 0000000000..4543c65935
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c
@@ -0,0 +1,1221 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_trantcp.c,v 1.39 2005/03/02 01:27:44 lindak Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/autoconf.h>
+#include <sys/sysmacros.h>
+#include <sys/sunddi.h>
+#include <sys/kmem.h>
+#include <sys/proc.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/stream.h>
+#include <sys/strsubr.h>
+#include <sys/strsun.h>
+#include <sys/stropts.h>
+#include <sys/cmn_err.h>
+#include <sys/tihdr.h>
+#include <sys/tiuser.h>
+#include <sys/t_kuser.h>
+#include <sys/priv.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#else
+#include <netsmb/smb_osdep.h>
+#endif
+
+#include <netsmb/mchain.h>
+#include <netsmb/netbios.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_tran.h>
+#include <netsmb/smb_trantcp.h>
+
+/*
+ * SMB messages are up to 64K.
+ * Let's leave room for two.
+ */
+static int smb_tcpsndbuf = 0x20000;
+static int smb_tcprcvbuf = 0x20000;
+
+static dev_t smb_tcp_dev;
+
+static int nbssn_recv(struct nbpcb *nbp, mblk_t **mpp, int *lenp,
+ uint8_t *rpcodep, struct proc *p);
+static int nb_disconnect(struct nbpcb *nbp);
+
+static int
+nb_wait_ack(TIUSER *tiptr, t_scalar_t ack_prim, int fmode)
+{
+ int msgsz;
+ union T_primitives *pptr;
+ mblk_t *bp;
+ ptrdiff_t diff;
+ int error;
+
+ /*
+ * wait for ack
+ */
+ bp = NULL;
+ if ((error = tli_recv(tiptr, &bp, fmode)) != 0)
+ return (error);
+
+ /*LINTED*/
+ diff = MBLKL(bp);
+ ASSERT(diff == (ptrdiff_t)((int)diff));
+ msgsz = (int)diff;
+
+ if (msgsz < sizeof (int)) {
+ freemsg(bp);
+ return (EPROTO);
+ }
+
+ /*LINTED*/
+ pptr = (union T_primitives *)bp->b_rptr;
+ if (pptr->type == ack_prim)
+ error = 0; /* Success */
+ else if (pptr->type == T_ERROR_ACK) {
+ if (pptr->error_ack.TLI_error == TSYSERR)
+ error = pptr->error_ack.UNIX_error;
+ else
+ error = t_tlitosyserr(pptr->error_ack.TLI_error);
+ } else
+ error = EPROTO;
+
+ freemsg(bp);
+ return (error);
+}
+
+/*
+ * Internal set sockopt for int-sized options.
+ * Is there a common Solaris function for this?
+ * Code from uts/common/rpc/clnt_cots.c
+ */
+static int
+nb_setsockopt_int(TIUSER *tiptr, int level, int name, int val)
+{
+ int fmode;
+ mblk_t *mp;
+ struct opthdr *opt;
+ struct T_optmgmt_req *tor;
+ int *valp;
+ int error, mlen;
+
+ mlen = (sizeof (struct T_optmgmt_req) +
+ sizeof (struct opthdr) + sizeof (int));
+ if (!(mp = allocb_wait(mlen, BPRI_LO, STR_NOSIG, &error)))
+ return (error);
+
+ mp->b_datap->db_type = M_PROTO;
+ /*LINTED*/
+ tor = (struct T_optmgmt_req *)mp->b_wptr;
+ tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
+ tor->MGMT_flags = T_NEGOTIATE;
+ tor->OPT_length = sizeof (struct opthdr) + sizeof (int);
+ tor->OPT_offset = sizeof (struct T_optmgmt_req);
+ mp->b_wptr += sizeof (struct T_optmgmt_req);
+
+ /*LINTED*/
+ opt = (struct opthdr *)mp->b_wptr;
+ opt->level = level;
+ opt->name = name;
+ opt->len = sizeof (int);
+ mp->b_wptr += sizeof (struct opthdr);
+
+ /* LINTED */
+ valp = (int *)mp->b_wptr;
+ *valp = val;
+ mp->b_wptr += sizeof (int);
+
+ fmode = tiptr->fp->f_flag;
+ if ((error = tli_send(tiptr, mp, fmode)) != 0)
+ return (error);
+
+ fmode = 0; /* need to block */
+ error = nb_wait_ack(tiptr, T_OPTMGMT_ACK, fmode);
+ return (error);
+}
+
+/*
+ * Get mblks into *mpp until the data length is at least mlen.
+ * Note that *mpp may already contain a fragment.
+ *
+ * If we ever have to wait more than 15 sec. to read a message,
+ * return ETIME. (Caller will declare the VD dead.)
+ */
+static int
+nb_getmsg_mlen(struct nbpcb *nbp, mblk_t **mpp, size_t mlen)
+{
+ mblk_t *im, *tm;
+ union T_primitives *pptr;
+ size_t dlen;
+ int events, fmode, timo, waitflg;
+ int error = 0;
+
+ /*
+ * Get the first message (fragment) if
+ * we don't already have a left-over.
+ */
+ dlen = msgdsize(*mpp); /* *mpp==null is OK */
+ while (dlen < mlen) {
+
+ /*
+ * I think we still want this to return ETIME
+ * if nothing arrives for SMB_NBTIMO (15) sec.
+ * so we can report "server not responding".
+ * We _could_ just block here now that our
+ * IOD is just a reader.
+ */
+#if 1
+ /* Wait with timeout... */
+ events = 0;
+ waitflg = READWAIT;
+ timo = SEC_TO_TICK(SMB_NBTIMO);
+ error = t_kspoll(nbp->nbp_tiptr, timo, waitflg, &events);
+ if (!error && !events)
+ error = ETIME;
+ if (error)
+ break;
+ /* file mode for recv is: */
+ fmode = FNDELAY; /* non-blocking */
+#else
+ fmode = 0; /* normal (blocking) */
+#endif
+
+ /* Get some more... */
+ tm = NULL;
+ error = tli_recv(nbp->nbp_tiptr, &tm, fmode);
+ if (error == EAGAIN)
+ continue;
+ if (error)
+ break;
+
+ /*
+ * Normally get M_DATA messages here,
+ * but have to check for other types.
+ */
+ switch (tm->b_datap->db_type) {
+ case M_DATA:
+ break;
+ case M_PROTO:
+ case M_PCPROTO:
+ /*LINTED*/
+ pptr = (union T_primitives *)tm->b_rptr;
+ switch (pptr->type) {
+ case T_DATA_IND:
+ /* remove 1st mblk, keep the rest. */
+ im = tm->b_cont;
+ tm->b_cont = NULL;
+ freeb(tm);
+ tm = im;
+ break;
+ case T_DISCON_IND:
+ /* Peer disconnected. */
+ NBDEBUG("T_DISCON_IND: reason=%d",
+ pptr->discon_ind.DISCON_reason);
+ goto discon;
+ case T_ORDREL_IND:
+ /* Peer disconnecting. */
+ NBDEBUG("T_ORDREL_IND");
+ goto discon;
+ case T_OK_ACK:
+ switch (pptr->ok_ack.CORRECT_prim) {
+ case T_DISCON_REQ:
+ NBDEBUG("T_OK_ACK/T_DISCON_REQ");
+ goto discon;
+ default:
+ NBDEBUG("T_OK_ACK/prim=%d",
+ pptr->ok_ack.CORRECT_prim);
+ goto discon;
+ }
+ default:
+ NBDEBUG("M_PROTO/type=%d", pptr->type);
+ goto discon;
+ }
+ break; /* M_PROTO, M_PCPROTO */
+
+ default:
+ NBDEBUG("unexpected msg type=%d",
+ tm->b_datap->db_type);
+ /*FALLTHROUGH*/
+discon:
+ /*
+ * The connection is no longer usable.
+ * Drop this message and disconnect.
+ *
+ * Note: nb_disconnect only does t_snddis
+ * on the first call, but does important
+ * cleanup and state change on any call.
+ */
+ freemsg(tm);
+ nb_disconnect(nbp);
+ return (ENOTCONN);
+ }
+
+ /*
+ * If we have a data message, append it to
+ * the previous chunk(s) and update dlen
+ */
+ if (!tm)
+ continue;
+ if (*mpp == NULL) {
+ *mpp = tm;
+ } else {
+ /* Append */
+ for (im = *mpp; im->b_cont; im = im->b_cont)
+ ;
+ im->b_cont = tm;
+ }
+ dlen += msgdsize(tm);
+ }
+
+ return (error);
+}
+
+/*
+ * Send a T_DISCON_REQ (disconnect)
+ */
+static int
+nb_snddis(TIUSER *tiptr)
+{
+ mblk_t *mp;
+ struct T_discon_req *dreq;
+ int error, fmode, mlen;
+
+ mlen = sizeof (struct T_discon_req);
+ if (!(mp = allocb_wait(mlen, BPRI_LO, STR_NOSIG, &error)))
+ return (error);
+
+ mp->b_datap->db_type = M_PROTO;
+ /*LINTED*/
+ dreq = (struct T_discon_req *)mp->b_wptr;
+ dreq->PRIM_type = T_DISCON_REQ;
+ dreq->SEQ_number = -1;
+ mp->b_wptr += sizeof (struct T_discon_req);
+
+ fmode = tiptr->fp->f_flag;
+ if ((error = tli_send(tiptr, mp, fmode)) != 0)
+ return (error);
+
+#if 0 /* Now letting the IOD recv this. */
+ fmode = 0; /* need to block */
+ error = nb_wait_ack(tiptr, T_OK_ACK, fmode);
+#endif
+ return (error);
+}
+
+#ifdef APPLE
+static int
+nb_intr(struct nbpcb *nbp, struct proc *p)
+{
+ return (0);
+}
+#endif
+
+/*
+ * Stuff the NetBIOS header into space already prepended.
+ */
+static int
+nb_sethdr(mblk_t *m, uint8_t type, uint32_t len)
+{
+ uint32_t *p;
+
+ len &= 0x1FFFF;
+ len |= (type << 24);
+
+ /*LINTED*/
+ p = (uint32_t *)m->b_rptr;
+ *p = htonl(len);
+ return (0);
+}
+
+/*
+ * Note: Moved name encoding into here.
+ */
+static int
+nb_put_name(struct mbchain *mbp, struct sockaddr_nb *snb)
+{
+ int i, len;
+ uchar_t ch, *p;
+
+ /*
+ * Do the NetBIOS "first-level encoding" here.
+ * (RFC1002 explains this wierdness...)
+ * See similar code in smbfs library:
+ * lib/libsmbfs/smb/nb_name.c
+ *
+ * Here is what we marshall:
+ * uint8_t NAME_LENGTH (always 32)
+ * uint8_t ENCODED_NAME[32]
+ * uint8_t SCOPE_LENGTH
+ * XXX Scope should follow here, then another null,
+ * if and when we support NetBIOS scopes.
+ */
+ len = 1 + (2 * NB_NAMELEN) + 1;
+
+ p = mb_reserve(mbp, len);
+ if (!p)
+ return (ENOSR);
+
+ /* NAME_LENGTH */
+ *p++ = (2 * NB_NAMELEN);
+
+ /* ENCODED_NAME */
+ for (i = 0; i < NB_NAMELEN; i++) {
+ ch = (uchar_t)snb->snb_name[i];
+ *p++ = 'A' + ((ch >> 4) & 0xF);
+ *p++ = 'A' + ((ch) & 0xF);
+ }
+
+ /* SCOPE_LENGTH */
+ *p++ = 0;
+
+ return (0);
+}
+
+static int
+nb_tcpopen(struct nbpcb *nbp, struct proc *p)
+{
+ TIUSER *tiptr;
+ int err, oflags = FREAD|FWRITE;
+ cred_t *cr = p->p_cred;
+
+ if (!smb_tcp_dev) {
+ smb_tcp_dev = makedevice(
+ clone_major, ddi_name_to_major("tcp"));
+ }
+
+ /*
+ * This magic arranges for our network endpoint
+ * to have the right "label" for operation in a
+ * "trusted extensions" environment.
+ */
+ if (is_system_labeled()) {
+ cr = crdup(cr);
+ (void) setpflags(NET_MAC_AWARE, 1, cr);
+ } else {
+ crhold(cr);
+ }
+ err = t_kopen(NULL, smb_tcp_dev, oflags, &tiptr, cr);
+ crfree(cr);
+ if (err)
+ return (err);
+
+ /* Note: I_PUSH "timod" is done by t_kopen */
+
+ /* Save the TPI handle we use everywhere. */
+ nbp->nbp_tiptr = tiptr;
+
+ /*
+ * Internal ktli calls need the "fmode" flags
+ * from the t_kopen call. XXX: Not sure if the
+ * flags have the right bits set, or if we
+ * always want the same block/non-block flags.
+ * XXX: Look into this...
+ */
+ nbp->nbp_fmode = tiptr->fp->f_flag;
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+nb_connect_in(struct nbpcb *nbp, struct sockaddr_in *to, struct proc *p)
+{
+ int error;
+ TIUSER *tiptr = NULL;
+ struct t_call call;
+
+ tiptr = nbp->nbp_tiptr;
+ if (tiptr == NULL)
+ return (EBADF);
+ if (nbp->nbp_flags & NBF_CONNECTED)
+ return (EISCONN);
+
+ /*
+ * Set various socket/TCP options.
+ * Failures here are not fatal -
+ * just log a complaint.
+ *
+ * We don't need these two:
+ * SO_RCVTIMEO, SO_SNDTIMEO
+ */
+
+ error = nb_setsockopt_int(tiptr, SOL_SOCKET, SO_SNDBUF,
+ nbp->nbp_sndbuf);
+ if (error)
+ NBDEBUG("nb_connect_in: set SO_SNDBUF");
+
+ error = nb_setsockopt_int(tiptr, SOL_SOCKET, SO_RCVBUF,
+ nbp->nbp_rcvbuf);
+ if (error)
+ NBDEBUG("nb_connect_in: set SO_RCVBUF");
+
+ error = nb_setsockopt_int(tiptr, SOL_SOCKET, SO_KEEPALIVE, 1);
+ if (error)
+ NBDEBUG("nb_connect_in: set SO_KEEPALIVE");
+
+ error = nb_setsockopt_int(tiptr, IPPROTO_TCP, TCP_NODELAY, 1);
+ if (error)
+ NBDEBUG("nb_connect_in: set TCP_NODELAY");
+
+ /* Do local bind (any address) */
+ if ((error = t_kbind(tiptr, NULL, NULL)) != 0) {
+ NBDEBUG("nb_connect_in: bind local");
+ return (error);
+ }
+
+ /*
+ * Setup (snd)call address (connect to).
+ * Just pass NULL for the (rcv)call.
+ */
+ bzero(&call, sizeof (call));
+ call.addr.len = sizeof (*to);
+ call.addr.buf = (char *)to;
+ /* call.opt - none */
+ /* call.udata -- XXX: Should put NB session req here! */
+
+ /* Send the connect, wait... */
+ error = t_kconnect(tiptr, &call, NULL);
+ if (error) {
+ NBDEBUG("nb_connect_in: connect %d error", error);
+ /*
+ * XXX: t_kconnect returning EPROTO here instead of ETIMEDOUT
+ * here. Temporarily return ETIMEDOUT error if we get EPROTO.
+ */
+ if (error == EPROTO)
+ error = ETIMEDOUT;
+ } else {
+ mutex_enter(&nbp->nbp_lock);
+ nbp->nbp_flags |= NBF_CONNECTED;
+ mutex_exit(&nbp->nbp_lock);
+ }
+
+ return (error);
+}
+
+static int
+nbssn_rq_request(struct nbpcb *nbp, struct proc *p)
+{
+ struct mbchain mb, *mbp = &mb;
+ struct mdchain md, *mdp = &md;
+ mblk_t *m0;
+ struct sockaddr_in sin;
+ ushort_t port;
+ uint8_t rpcode;
+ int error, rplen;
+
+ error = mb_init(mbp);
+ if (error)
+ return (error);
+
+ /*
+ * Put a zero for the 4-byte NetBIOS header,
+ * then let nb_sethdr() overwrite it.
+ */
+ mb_put_uint32le(mbp, 0);
+ nb_put_name(mbp, nbp->nbp_paddr);
+ nb_put_name(mbp, nbp->nbp_laddr);
+ nb_sethdr(mbp->mb_top, NB_SSN_REQUEST, mb_fixhdr(mbp) - 4);
+
+ m0 = mb_detach(mbp);
+ error = tli_send(nbp->nbp_tiptr, m0, nbp->nbp_fmode);
+ m0 = NULL; /* Note: _always_ consumed by tli_send */
+ mb_done(mbp);
+ if (error)
+ return (error);
+
+ nbp->nbp_state = NBST_RQSENT;
+ error = nbssn_recv(nbp, &m0, &rplen, &rpcode, p);
+ if (error == EWOULDBLOCK) { /* Timeout */
+ NBDEBUG("initial request timeout\n");
+ return (ETIMEDOUT);
+ }
+ if (error) {
+ NBDEBUG("recv() error %d\n", error);
+ return (error);
+ }
+ /*
+ * Process NETBIOS reply
+ */
+ if (m0)
+ md_initm(mdp, m0);
+
+ error = 0;
+ if (rpcode == NB_SSN_POSRESP) {
+ mutex_enter(&nbp->nbp_lock);
+ nbp->nbp_state = NBST_SESSION;
+ mutex_exit(&nbp->nbp_lock);
+ goto out;
+ }
+ if (rpcode != NB_SSN_RTGRESP) {
+ error = ECONNABORTED;
+ goto out;
+ }
+ if (rplen != 6) {
+ error = ECONNABORTED;
+ goto out;
+ }
+ md_get_mem(mdp, (caddr_t)&sin.sin_addr, 4, MB_MSYSTEM);
+ md_get_uint16(mdp, &port);
+ sin.sin_port = port;
+ nbp->nbp_state = NBST_RETARGET;
+ nb_disconnect(nbp);
+ error = nb_connect_in(nbp, &sin, p);
+ if (!error)
+ error = nbssn_rq_request(nbp, p);
+ if (error) {
+ nb_disconnect(nbp);
+ }
+
+out:
+ if (m0)
+ md_done(mdp);
+ return (error);
+}
+
+/*
+ * Wait for up to 15 sec. for the next packet.
+ * Often return ETIME and do nothing else.
+ * When a packet header is available, check
+ * the header and get the length, but don't
+ * consume it. No side effects here except
+ * for the pullupmsg call.
+ */
+static int
+nbssn_peekhdr(struct nbpcb *nbp, size_t *lenp, uint8_t *rpcodep)
+{
+ uint32_t len, *hdr;
+ int error;
+
+ /*
+ * Get the first message (fragment) if
+ * we don't already have a left-over.
+ */
+ error = nb_getmsg_mlen(nbp, &nbp->nbp_frag, sizeof (len));
+ if (error)
+ return (error);
+
+ if (!pullupmsg(nbp->nbp_frag, sizeof (len)))
+ return (ENOSR);
+
+ /*
+ * Check the NetBIOS header.
+ * (NOT consumed here)
+ */
+ /*LINTED*/
+ hdr = (uint32_t *)nbp->nbp_frag->b_rptr;
+
+ len = ntohl(*hdr);
+ if ((len >> 16) & 0xFE) {
+ NBDEBUG("bad nb header received 0x%x (MBZ flag set)\n", len);
+ return (EPIPE);
+ }
+ *rpcodep = (len >> 24) & 0xFF;
+ switch (*rpcodep) {
+ case NB_SSN_MESSAGE:
+ case NB_SSN_REQUEST:
+ case NB_SSN_POSRESP:
+ case NB_SSN_NEGRESP:
+ case NB_SSN_RTGRESP:
+ case NB_SSN_KEEPALIVE:
+ break;
+ default:
+ NBDEBUG("bad nb header received 0x%x (bogus type)\n", len);
+ return (EPIPE);
+ }
+ len &= 0x1ffff;
+ if (len > SMB_MAXPKTLEN) {
+ NBDEBUG("packet too long (%d)\n", len);
+ return (EFBIG);
+ }
+ *lenp = len;
+ return (0);
+}
+
+/*
+ * Receive a NetBIOS message. This may block to wait for the entire
+ * message to arrive. The caller knows there is (or should be) a
+ * message to be read. When we receive and drop a keepalive or
+ * zero-length message, return EAGAIN so the caller knows that
+ * something was received. This avoids false triggering of the
+ * "server not responding" state machine.
+ */
+/*ARGSUSED*/
+static int
+nbssn_recv(struct nbpcb *nbp, mblk_t **mpp, int *lenp,
+ uint8_t *rpcodep, struct proc *p)
+{
+ TIUSER *tiptr = nbp->nbp_tiptr;
+ mblk_t *m0;
+ uint8_t rpcode;
+ int error;
+ size_t rlen, len;
+
+ /* We should be the only reader. */
+ ASSERT(nbp->nbp_flags & NBF_RECVLOCK);
+
+ if (tiptr == NULL)
+ return (EBADF);
+ if (mpp) {
+ if (*mpp) {
+ NBDEBUG("*mpp not 0 - leak?");
+ }
+ *mpp = NULL;
+ }
+ m0 = NULL;
+
+ /*
+ * Get the NetBIOS header (not consumed yet)
+ */
+ error = nbssn_peekhdr(nbp, &len, &rpcode);
+ if (error) {
+ if (error != ETIME)
+ NBDEBUG("peekhdr, error=%d\n", error);
+ return (error);
+ }
+ NBDEBUG("Have pkt, type=0x%x len=0x%x\n",
+ (int)rpcode, (int)len);
+
+ /*
+ * Block here waiting for the whole packet to arrive.
+ * If we get a timeout, return without side effects.
+ * The data length we wait for here includes both the
+ * NetBIOS header and the payload.
+ */
+ error = nb_getmsg_mlen(nbp, &nbp->nbp_frag, len + 4);
+ if (error) {
+ NBDEBUG("getmsg(body), error=%d\n", error);
+ return (error);
+ }
+
+ /*
+ * We now have an entire NetBIOS message.
+ * Trim off the NetBIOS header and consume it.
+ * Note: _peekhdr has done pullupmsg for us,
+ * so we know it's safe to advance b_rptr.
+ */
+ m0 = nbp->nbp_frag;
+ m0->b_rptr += 4;
+
+ /*
+ * There may be more data after the message
+ * we're about to return, in which case we
+ * split it and leave the remainder.
+ */
+ rlen = msgdsize(m0);
+ ASSERT(rlen >= len);
+ nbp->nbp_frag = NULL;
+ if (rlen > len)
+ nbp->nbp_frag = m_split(m0, len, 1);
+
+ if (nbp->nbp_state != NBST_SESSION) {
+ /*
+ * No session is established.
+ * Return whatever packet we got.
+ */
+ goto out;
+ }
+
+ /*
+ * A session is established; the only packets
+ * we should see are session message and
+ * keep-alive packets. Drop anything else.
+ */
+ switch (rpcode) {
+
+ case NB_SSN_KEEPALIVE:
+ /*
+ * It's a keepalive. Discard any data in it
+ * (there's not supposed to be any, but that
+ * doesn't mean some server won't send some)
+ */
+ if (len)
+ NBDEBUG("Keepalive with data %d\n", (int)len);
+ error = EAGAIN;
+ break;
+
+ case NB_SSN_MESSAGE:
+ /*
+ * Session message. Does it have any data?
+ */
+ if (len == 0) {
+ /*
+ * No data - treat as keepalive (drop).
+ */
+ error = EAGAIN;
+ break;
+ }
+ /*
+ * Yes, has data. Return it.
+ */
+ error = 0;
+ break;
+
+ default:
+ /*
+ * Drop anything else.
+ */
+ NBDEBUG("non-session packet %x\n", rpcode);
+ error = EAGAIN;
+ break;
+ }
+
+out:
+ if (error) {
+ if (m0)
+ m_freem(m0);
+ return (error);
+ }
+ if (mpp)
+ *mpp = m0;
+ else
+ m_freem(m0);
+ *lenp = (int)len;
+ *rpcodep = rpcode;
+ return (0);
+}
+
+/*
+ * SMB transport interface
+ */
+static int
+smb_nbst_create(struct smb_vc *vcp, struct proc *p)
+{
+ struct nbpcb *nbp;
+ int error;
+
+ nbp = kmem_zalloc(sizeof (struct nbpcb), KM_SLEEP);
+
+ /*
+ * We don't keep reference counts or otherwise
+ * prevent nbp->nbp_tiptr from going away, so
+ * do the TLI open here and keep it until the
+ * last ref calls smb_nbst_done.
+ * This does t_kopen (open endpoint)
+ */
+ error = nb_tcpopen(nbp, p);
+ if (error) {
+ kmem_free(nbp, sizeof (*nbp));
+ return (error);
+ }
+
+ nbp->nbp_timo.tv_sec = SMB_NBTIMO;
+ nbp->nbp_state = NBST_CLOSED; /* really IDLE */
+ nbp->nbp_vc = vcp;
+ nbp->nbp_sndbuf = smb_tcpsndbuf;
+ nbp->nbp_rcvbuf = smb_tcprcvbuf;
+ mutex_init(&nbp->nbp_lock, NULL, MUTEX_DRIVER, NULL);
+ vcp->vc_tdata = nbp;
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+smb_nbst_done(struct smb_vc *vcp, struct proc *p)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+
+ if (nbp == NULL)
+ return (ENOTCONN);
+ vcp->vc_tdata = NULL;
+
+ /*
+ * Don't really need to disconnect here,
+ * because the close following will do it.
+ * But it's harmless.
+ */
+ if (nbp->nbp_flags & NBF_CONNECTED)
+ nb_disconnect(nbp);
+ if (nbp->nbp_tiptr)
+ t_kclose(nbp->nbp_tiptr, 1);
+ if (nbp->nbp_laddr)
+ smb_free_sockaddr((struct sockaddr *)nbp->nbp_laddr);
+ if (nbp->nbp_paddr)
+ smb_free_sockaddr((struct sockaddr *)nbp->nbp_paddr);
+ mutex_destroy(&nbp->nbp_lock);
+ kmem_free(nbp, sizeof (*nbp));
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+ struct sockaddr_nb *snb;
+ int error;
+
+ NBDEBUG("\n");
+ error = EINVAL;
+
+ if (nbp->nbp_flags & NBF_LOCADDR)
+ goto out;
+
+ /*
+ * Null name is an "anonymous" (NULL) bind request.
+ * (Let the transport pick a local name.)
+ * This transport does not support NULL bind.
+ */
+ if (sap == NULL)
+ goto out;
+
+ /*LINTED*/
+ snb = (struct sockaddr_nb *)smb_dup_sockaddr(sap);
+ if (snb == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ mutex_enter(&nbp->nbp_lock);
+ nbp->nbp_laddr = snb;
+ nbp->nbp_flags |= NBF_LOCADDR;
+ mutex_exit(&nbp->nbp_lock);
+ error = 0;
+
+out:
+ return (error);
+}
+
+static int
+smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+ struct sockaddr_in sin;
+ struct sockaddr_nb *snb;
+ struct timespec ts1, ts2;
+ int error;
+
+ NBDEBUG("\n");
+ if (nbp->nbp_tiptr == NULL)
+ return (EBADF);
+ if (nbp->nbp_laddr == NULL)
+ return (EINVAL);
+
+ /*
+ * Note: nbssn_rq_request() will call nbssn_recv(),
+ * so set the RECVLOCK flag here. Otherwise we'll
+ * hit an ASSERT for this flag in nbssn_recv().
+ */
+ mutex_enter(&nbp->nbp_lock);
+ if (nbp->nbp_flags & NBF_RECVLOCK) {
+ NBDEBUG("attempt to reenter session layer!\n");
+ mutex_exit(&nbp->nbp_lock);
+ return (EWOULDBLOCK);
+ }
+ nbp->nbp_flags |= NBF_RECVLOCK;
+ mutex_exit(&nbp->nbp_lock);
+
+ /*LINTED*/
+ snb = (struct sockaddr_nb *)smb_dup_sockaddr(sap);
+ if (snb == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ if (nbp->nbp_paddr)
+ smb_free_sockaddr((struct sockaddr *)nbp->nbp_paddr);
+ nbp->nbp_paddr = snb;
+
+ /* Setup the remote IP address. */
+ bzero(&sin, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(SMB_TCP_PORT);
+ sin.sin_addr.s_addr = snb->snb_ipaddr;
+
+ /*
+ * For our general timeout we use the greater of
+ * the default (15 sec) and 4 times the time it
+ * took for the first round trip. We used to use
+ * just the latter, but sometimes if the first
+ * round trip is very fast the subsequent 4 sec
+ * timeouts are simply too short.
+ */
+ gethrestime(&ts1);
+ error = nb_connect_in(nbp, &sin, p);
+ if (error)
+ goto out;
+ gethrestime(&ts2);
+ timespecsub(&ts2, &ts1);
+ timespecadd(&ts2, &ts2);
+ timespecadd(&ts2, &ts2); /* * 4 */
+ /*CSTYLED*/
+ if (timespeccmp(&ts2, (&(nbp->nbp_timo)), >))
+ nbp->nbp_timo = ts2;
+ error = nbssn_rq_request(nbp, p);
+ if (error)
+ nb_disconnect(nbp);
+out:
+ mutex_enter(&nbp->nbp_lock);
+ nbp->nbp_flags &= ~NBF_RECVLOCK;
+ mutex_exit(&nbp->nbp_lock);
+
+ return (error);
+}
+
+/*ARGSUSED*/
+static int
+smb_nbst_disconnect(struct smb_vc *vcp, struct proc *p)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+
+ if (nbp == NULL)
+ return (ENOTCONN);
+
+ return (nb_disconnect(nbp));
+}
+
+static int
+nb_disconnect(struct nbpcb *nbp)
+{
+ TIUSER *tiptr;
+ int save_flags;
+
+ tiptr = nbp->nbp_tiptr;
+ if (tiptr == NULL)
+ return (EBADF);
+
+ mutex_enter(&nbp->nbp_lock);
+ save_flags = nbp->nbp_flags;
+ nbp->nbp_flags &= ~NBF_CONNECTED;
+ if (nbp->nbp_frag) {
+ freemsg(nbp->nbp_frag);
+ nbp->nbp_frag = NULL;
+ }
+ mutex_exit(&nbp->nbp_lock);
+
+ if (save_flags & NBF_CONNECTED)
+ nb_snddis(tiptr);
+
+ if (nbp->nbp_state != NBST_RETARGET) {
+ nbp->nbp_state = NBST_CLOSED; /* really IDLE */
+ }
+ return (0);
+}
+
+/*
+ * Always consume the message.
+ * (On error too!)
+ */
+/*ARGSUSED*/
+static int
+smb_nbst_send(struct smb_vc *vcp, mblk_t *m, struct proc *p)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+ ptrdiff_t diff;
+ uint32_t mlen;
+ int error;
+
+ if (nbp == NULL || nbp->nbp_tiptr == NULL) {
+ error = EBADF;
+ goto errout;
+ }
+
+ /*
+ * Get the message length, which
+ * does NOT include the NetBIOS header
+ */
+ mlen = msgdsize(m);
+
+ /*
+ * Normally, mb_init() will have left space
+ * for us to prepend the NetBIOS header in
+ * the data block of the first mblk.
+ * However, we have to check in case other
+ * code did not leave this space, or if the
+ * message is from dupmsg (db_ref > 1)
+ *
+ * If don't find room in the first data block,
+ * we have to allocb a new message and link it
+ * on the front of the chain. We try not to
+ * do this becuase it's less efficient. Also,
+ * some network drivers will apparently send
+ * each mblk in the chain as separate frames.
+ * (That's arguably a driver bug.)
+ */
+
+ /* LINTED */
+ diff = MBLKHEAD(m);
+ if (diff == 4 && DB_REF(m) == 1) {
+ /* We can use the first dblk. */
+ m->b_rptr -= 4;
+ } else {
+ /* Link a new mblk on the head. */
+ mblk_t *m0;
+
+ /* M_PREPEND */
+ m0 = allocb_wait(4, BPRI_LO, STR_NOSIG, &error);
+ if (!m0)
+ goto errout;
+
+ m0->b_wptr += 4;
+ m0->b_cont = m;
+ m = m0;
+ }
+
+ nb_sethdr(m, NB_SSN_MESSAGE, mlen);
+ error = tli_send(nbp->nbp_tiptr, m, 0);
+ return (error);
+
+errout:
+ if (m)
+ m_freem(m);
+ return (error);
+}
+
+
+static int
+smb_nbst_recv(struct smb_vc *vcp, mblk_t **mpp, struct proc *p)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+ uint8_t rpcode;
+ int error, rplen;
+
+ mutex_enter(&nbp->nbp_lock);
+ if (nbp->nbp_flags & NBF_RECVLOCK) {
+ NBDEBUG("attempt to reenter session layer!\n");
+ mutex_exit(&nbp->nbp_lock);
+ return (EWOULDBLOCK);
+ }
+ nbp->nbp_flags |= NBF_RECVLOCK;
+ mutex_exit(&nbp->nbp_lock);
+ error = nbssn_recv(nbp, mpp, &rplen, &rpcode, p);
+ mutex_enter(&nbp->nbp_lock);
+ nbp->nbp_flags &= ~NBF_RECVLOCK;
+ mutex_exit(&nbp->nbp_lock);
+ return (error);
+}
+
+/*
+ * Wait for up to "ticks" clock ticks for input on vcp.
+ * Returns zero if input is available, otherwise ETIME
+ * indicating time expired, or other error codes.
+ */
+/*ARGSUSED*/
+static int
+smb_nbst_poll(struct smb_vc *vcp, int ticks, struct proc *p)
+{
+ int error;
+ int events = 0;
+ int waitflg = READWAIT;
+ struct nbpcb *nbp = vcp->vc_tdata;
+
+ error = t_kspoll(nbp->nbp_tiptr, ticks, waitflg, &events);
+ if (!error && !events)
+ error = ETIME;
+
+ return (error);
+}
+
+static int
+smb_nbst_getparam(struct smb_vc *vcp, int param, void *data)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+
+ switch (param) {
+ case SMBTP_SNDSZ:
+ *(int *)data = nbp->nbp_sndbuf;
+ break;
+ case SMBTP_RCVSZ:
+ *(int *)data = nbp->nbp_rcvbuf;
+ break;
+ case SMBTP_TIMEOUT:
+ *(struct timespec *)data = nbp->nbp_timo;
+ break;
+#ifdef SMBTP_SELECTID
+ case SMBTP_SELECTID:
+ *(void **)data = nbp->nbp_selectid;
+ break;
+#endif
+#ifdef SMBTP_UPCALL
+ case SMBTP_UPCALL:
+ *(void **)data = nbp->nbp_upcall;
+ break;
+#endif
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+smb_nbst_setparam(struct smb_vc *vcp, int param, void *data)
+{
+ return (EINVAL);
+}
+
+/*
+ * Check for fatal errors
+ */
+/*ARGSUSED*/
+static int
+smb_nbst_fatal(struct smb_vc *vcp, int error)
+{
+ switch (error) {
+ case ENOTCONN:
+ case ENETRESET:
+ case ECONNABORTED:
+ case EPIPE:
+ return (1);
+ }
+ return (0);
+}
+
+
+struct smb_tran_desc smb_tran_nbtcp_desc = {
+ SMBT_NBTCP,
+ smb_nbst_create,
+ smb_nbst_done,
+ smb_nbst_bind,
+ smb_nbst_connect,
+ smb_nbst_disconnect,
+ smb_nbst_send,
+ smb_nbst_recv,
+ smb_nbst_poll,
+ smb_nbst_getparam,
+ smb_nbst_setparam,
+ smb_nbst_fatal,
+ {NULL, NULL}
+};
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h
new file mode 100644
index 0000000000..80ebdc8dd9
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_trantcp.h,v 1.8 2004/08/03 23:50:01 lindak Exp $
+ */
+#ifndef _NETSMB_SMB_TRANTCP_H_
+#define _NETSMB_SMB_TRANTCP_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+enum nbstate {
+ NBST_CLOSED,
+ NBST_RQSENT,
+ NBST_SESSION,
+ NBST_RETARGET,
+ NBST_REFUSED
+};
+
+
+/*
+ * socket specific data
+ */
+struct nbpcb {
+ struct smb_vc *nbp_vc;
+ struct tiuser *nbp_tiptr; /* KTLI transport handle... */
+ ushort_t nbp_fmode;
+ mblk_t *nbp_frag; /* left-over from last recv */
+
+ struct sockaddr_nb *nbp_laddr; /* local address */
+ struct sockaddr_nb *nbp_paddr; /* peer address */
+
+ int nbp_flags;
+#define NBF_LOCADDR 0x0001 /* has local addr */
+#define NBF_CONNECTED 0x0002
+#define NBF_RECVLOCK 0x0004
+#define NBF_UPCALLED 0x0010
+
+ enum nbstate nbp_state;
+ struct timespec nbp_timo;
+ int nbp_sndbuf;
+ int nbp_rcvbuf;
+ void *nbp_selectid;
+ kmutex_t nbp_lock;
+};
+typedef struct nbpcb nbpcb_t;
+
+/*
+ * Nominal space allocated per a NETBIOS socket.
+ */
+#define NB_SNDQ (10 * 1024)
+#define NB_RCVQ (20 * 1024)
+
+/*
+ * TCP slowstart presents a problem in conjunction with large
+ * reads. To ensure a steady stream of ACKs while reading using
+ * large transaction sizes, we call soreceive() with a smaller
+ * buffer size. See nbssn_recv().
+ */
+#define NB_SORECEIVE_CHUNK (8 * 1024)
+
+#define SMBSBTIMO 15 /* seconds for sockbuf timeouts */
+
+#endif /* !_NETSMB_SMB_TRANTCP_H_ */
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c
new file mode 100644
index 0000000000..e8af12ba20
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_usr.c,v 1.15 2004/12/13 00:25:18 lindak Exp $
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/kmem.h>
+#include <sys/systm.h>
+#include <sys/policy.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/cmn_err.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#include <sys/smb_iconv.h>
+#else
+#include <netsmb/smb_osdep.h>
+#endif
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_rq.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_dev.h>
+
+/*
+ * helpers for nsmb device. Can be moved to the smb_dev.c file.
+ */
+static void smb_usr_vcspec_free(struct smb_vcspec *spec);
+
+/*
+ * Moved the access checks here, just becuase
+ * this was a more convenient place to do it
+ * than in every function calling this.
+ */
+static int
+smb_usr_ioc2vcspec(struct smbioc_ossn *dp, struct smb_vcspec *spec)
+{
+ cred_t *cr = CRED();
+ uid_t realuid;
+
+ /*
+ * Only superuser can specify a UID or GID.
+ */
+ realuid = crgetruid(cr);
+ if (dp->ioc_owner == SMBM_ANY_OWNER)
+ spec->owner = realuid;
+ else {
+ /*
+ * Do we have the privilege to create with the
+ * specified uid? (does uid == cr->cr_uid, etc.)
+ * MacOS would want suser(), or similar here.
+ */
+ if (secpolicy_vnode_owner(cr, dp->ioc_owner))
+ return (EPERM);
+ spec->owner = dp->ioc_owner;
+ }
+ if (dp->ioc_group == SMBM_ANY_GROUP)
+ spec->group = crgetgid(cr);
+ else {
+ /*
+ * Do we have the privilege to create with the
+ * specified gid? (one of our groups?)
+ */
+ if (groupmember(dp->ioc_group, cr) ||
+ secpolicy_vnode_create_gid(cr) == 0)
+ spec->group = dp->ioc_group;
+ else
+ return (EPERM);
+ }
+
+ /*
+ * Valid codesets? XXX
+ */
+ if (dp->ioc_localcs[0] == 0) {
+ spec->localcs = "ISO8859-1";
+#ifdef NOTYETRESOLVED
+ SMBERROR("no local charset ? dp->ioc_localcs[0]: %d\n",
+ dp->ioc_localcs[0]);
+ return (EINVAL);
+#endif
+ } else
+ spec->localcs = spec->localcs;
+
+ /*
+ * Check for valid sa_family.
+ * XXX: Just NetBIOS for now.
+ */
+ if (dp->ioc_server.sa.sa_family != AF_NETBIOS)
+ return (EINVAL);
+ spec->sap = &dp->ioc_server.sa;
+
+ if (dp->ioc_local.sa.sa_family) {
+ /* If specified, local AF must be the same. */
+ if (dp->ioc_local.sa.sa_family !=
+ dp->ioc_server.sa.sa_family)
+ return (EINVAL);
+ spec->lap = &dp->ioc_local.sa;
+ }
+
+ if (dp->ioc_intok) {
+ spec->tok = smb_memdupin(dp->ioc_intok, dp->ioc_intoklen);
+ if (spec->tok == NULL)
+ return (EFAULT);
+ spec->toklen = dp->ioc_intoklen;
+ }
+
+ spec->srvname = dp->ioc_srvname;
+ spec->pass = dp->ioc_password;
+ spec->domain = dp->ioc_workgroup;
+ spec->username = dp->ioc_user;
+ spec->mode = dp->ioc_mode;
+ spec->rights = dp->ioc_rights;
+ spec->servercs = dp->ioc_servercs;
+ spec->optflags = dp->ioc_opt;
+
+ return (0);
+}
+
+static void
+smb_usr_shspec_free(struct smb_sharespec *sspec)
+{
+ kmem_free(sspec, sizeof (struct smb_sharespec));
+}
+
+static void
+smb_usr_vcspec_free(struct smb_vcspec *spec)
+{
+
+ if (spec->tok) {
+ kmem_free(spec->tok, spec->toklen);
+ }
+ kmem_free(spec, sizeof (*spec));
+}
+
+static int
+smb_usr_ioc2sharespec(struct smbioc_oshare *dp, struct smb_sharespec *spec)
+{
+ bzero(spec, sizeof (*spec));
+ spec->name = dp->ioc_share;
+ spec->pass = dp->ioc_password;
+ spec->mode = dp->ioc_mode;
+ spec->rights = dp->ioc_rights;
+ spec->owner = dp->ioc_owner;
+ spec->group = dp->ioc_group;
+ spec->stype = dp->ioc_stype;
+ spec->optflags = dp->ioc_opt;
+ return (0);
+}
+
+int
+smb_usr_negotiate(struct smbioc_lookup *dp, struct smb_cred *scred,
+ struct smb_vc **vcpp)
+{
+ struct smb_vc *vcp = NULL;
+ struct smb_vcspec *vspec = NULL;
+ struct smb_sharespec *sspecp = NULL;
+ int error = 0;
+
+ if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
+ return (EINVAL);
+ vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP);
+ error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec);
+ if (error)
+ return (error);
+ if (dp->ioc_flags & SMBLK_CREATE)
+ vspec->optflags |= SMBVOPT_CREATE;
+ if (dp->ioc_level >= SMBL_SHARE) {
+ sspecp = kmem_alloc(sizeof (*sspecp), KM_SLEEP);
+ error = smb_usr_ioc2sharespec(&dp->ioc_sh, sspecp);
+ if (error)
+ goto out;
+ }
+ error = smb_sm_negotiate(vspec, scred, &vcp);
+ if (error == 0) {
+ *vcpp = vcp;
+ /*
+ * Used to copyout ioc_outtok, outtoklen here,
+ * but that's now in smb_dev. (our caller)
+ *
+ * If this call asked for extended security and
+ * the server does not support it, clear the
+ * flag so the caller knows this.
+ *
+ * XXX: Should just add sv_caps to ioc_ssn,
+ * set the new sv_caps field here, and let
+ * let the copyout of ioc_ssn handle it.
+ */
+ if (!(vcp->vc_sopt.sv_caps & SMB_CAP_EXT_SECURITY) &&
+ (dp->ioc_ssn.ioc_opt & SMBVOPT_EXT_SEC)) {
+ dp->ioc_ssn.ioc_opt &= ~SMBVOPT_EXT_SEC;
+ SMBSDEBUG("turned off extended security");
+ }
+ }
+out:
+ smb_usr_vcspec_free(vspec);
+ smb_usr_shspec_free(sspecp);
+ return (error);
+}
+
+int
+smb_usr_ssnsetup(struct smbioc_lookup *dp, struct smb_cred *scred,
+ struct smb_vc *vcp)
+{
+ struct smb_vcspec *vspec = NULL;
+ int error;
+
+ if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
+ return (EINVAL);
+
+ vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP);
+ error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec);
+ if (error)
+ goto out;
+
+ error = smb_sm_ssnsetup(vspec, scred, vcp);
+ /*
+ * Moved the copyout of ioc_outtok to
+ * smb_dev.c (our caller)
+ */
+
+out:
+ smb_usr_vcspec_free(vspec);
+ return (error);
+}
+
+
+int
+smb_usr_tcon(struct smbioc_lookup *dp, struct smb_cred *scred,
+ struct smb_vc *vcp, struct smb_share **sspp)
+{
+ struct smb_sharespec *sspecp = NULL;
+ int error;
+
+ if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
+ return (EINVAL);
+
+ if (dp->ioc_level >= SMBL_SHARE) {
+ sspecp = kmem_alloc(sizeof (*sspecp), KM_SLEEP);
+ error = smb_usr_ioc2sharespec(&dp->ioc_sh, sspecp);
+ if (error)
+ goto out;
+ }
+ error = smb_sm_tcon(sspecp, scred, vcp, sspp);
+
+out:
+ if (sspecp)
+ smb_usr_shspec_free(sspecp);
+
+ return (error);
+}
+
+/*
+ * Connect to the resource specified by smbioc_ossn structure.
+ * It may either find an existing connection or try to establish a new one.
+ * If no errors occured smb_vc returned locked and referenced.
+ */
+
+int
+smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *dp,
+ struct smb_cred *scred)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ char *p;
+ size_t wc2;
+ u_int8_t wc;
+ u_int16_t bc;
+ int error;
+
+ switch (dp->ioc_cmd) {
+ case SMB_COM_TRANSACTION2:
+ case SMB_COM_TRANSACTION2_SECONDARY:
+ case SMB_COM_CLOSE_AND_TREE_DISC:
+ case SMB_COM_TREE_CONNECT:
+ case SMB_COM_TREE_DISCONNECT:
+ case SMB_COM_NEGOTIATE:
+ case SMB_COM_SESSION_SETUP_ANDX:
+ case SMB_COM_LOGOFF_ANDX:
+ case SMB_COM_TREE_CONNECT_ANDX:
+ return (EPERM);
+ }
+ error = smb_rq_init(rqp, SSTOCP(ssp), dp->ioc_cmd, scred);
+ if (error)
+ return (error);
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ error = mb_put_mem(mbp, dp->ioc_twords,
+ dp->ioc_twc * 2, MB_MUSER);
+ if (error)
+ goto bad;
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ error = mb_put_mem(mbp, dp->ioc_tbytes,
+ dp->ioc_tbc, MB_MUSER);
+ if (error)
+ goto bad;
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ if (error)
+ goto bad;
+ mdp = &rqp->sr_rp;
+ md_get_uint8(mdp, &wc);
+ dp->ioc_rwc = wc;
+ wc2 = wc * 2;
+ if (wc2 > dp->ioc_rpbufsz) {
+ error = EBADRPC;
+ goto bad;
+ }
+ error = md_get_mem(mdp, dp->ioc_rpbuf, wc2, MB_MUSER);
+ if (error)
+ goto bad;
+ md_get_uint16le(mdp, &bc);
+ if ((wc2 + bc) > dp->ioc_rpbufsz) {
+ error = EBADRPC;
+ goto bad;
+ }
+ dp->ioc_rbc = bc;
+ p = dp->ioc_rpbuf;
+ error = md_get_mem(mdp, p + wc2, bc, MB_MUSER);
+bad:
+ dp->ioc_errclass = rqp->sr_errclass;
+ dp->ioc_serror = rqp->sr_serror;
+ dp->ioc_error = rqp->sr_error;
+ smb_rq_done(rqp);
+ return (error);
+
+}
+
+static int
+smb_cpdatain(struct mbchain *mbp, int len, char *data)
+{
+ int error;
+
+ if (len == 0)
+ return (0);
+ error = mb_init(mbp);
+ if (error)
+ return (error);
+ return (mb_put_mem(mbp, data, len, MB_MUSER));
+}
+
+int
+smb_usr_t2request(struct smb_share *ssp, smbioc_t2rq_t *dp,
+ struct smb_cred *scred)
+{
+ struct smb_t2rq t2, *t2p = &t2;
+ struct mdchain *mdp;
+ int error, len;
+
+ if (dp->ioc_setupcnt > SMB_MAXSETUPWORDS)
+ return (EINVAL);
+
+ t2p = (struct smb_t2rq *)kmem_alloc(sizeof (struct smb_t2rq), KM_SLEEP);
+ if (t2p == NULL)
+ return (ENOMEM);
+ error = smb_t2_init(t2p, SSTOCP(ssp), dp->ioc_setup, dp->ioc_setupcnt,
+ scred);
+ if (error)
+ return (error);
+ len = t2p->t2_setupcount = dp->ioc_setupcnt;
+ if (len > 1)
+ t2p->t2_setupdata = dp->ioc_setup;
+ if (dp->ioc_name) {
+ bcopy(dp->ioc_name, t2p->t_name, 128);
+ if (t2p->t_name == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
+ }
+ t2p->t2_maxscount = 0;
+ t2p->t2_maxpcount = dp->ioc_rparamcnt;
+ t2p->t2_maxdcount = dp->ioc_rdatacnt;
+ error = smb_cpdatain(&t2p->t2_tparam, dp->ioc_tparamcnt,
+ dp->ioc_tparam);
+ if (error)
+ goto bad;
+ error = smb_cpdatain(&t2p->t2_tdata,
+ dp->ioc_tdatacnt, dp->ioc_tdata);
+ if (error)
+ goto bad;
+ error = smb_t2_request(t2p);
+ dp->ioc_errclass = t2p->t2_sr_errclass;
+ dp->ioc_serror = t2p->t2_sr_serror;
+ dp->ioc_error = t2p->t2_sr_error;
+ dp->ioc_rpflags2 = t2p->t2_sr_rpflags2;
+ if (error)
+ goto bad;
+ mdp = &t2p->t2_rparam;
+ if (mdp->md_top) {
+ mblk_t *m = mdp->md_top;
+#ifdef lint
+ m = m;
+#endif
+ len = m_fixhdr(mdp->md_top);
+ if (len > dp->ioc_rparamcnt) {
+ error = EMSGSIZE;
+ goto bad;
+ }
+ dp->ioc_rparamcnt = (ushort_t)len;
+ error = md_get_mem(mdp, dp->ioc_rparam,
+ len, MB_MUSER);
+ if (error) {
+ goto bad;
+ }
+ } else
+ dp->ioc_rparamcnt = 0;
+ mdp = &t2p->t2_rdata;
+ if (mdp->md_top) {
+ mblk_t *m = mdp->md_top;
+#ifdef lint
+ m = m;
+#endif
+ len = m_fixhdr(mdp->md_top);
+ if (len > dp->ioc_rdatacnt) {
+ error = EMSGSIZE;
+ goto bad;
+ }
+ dp->ioc_rdatacnt = (ushort_t)len;
+ error = md_get_mem(mdp, dp->ioc_rdata,
+ len, MB_MUSER);
+ if (error) {
+ goto bad;
+ }
+ } else
+ dp->ioc_rdatacnt = 0;
+bad:
+ smb_t2_done(t2p);
+ return (error);
+}
+
+/*
+ * Helper for nsmb_ioctl cases
+ * SMBIOC_READ, SMBIOC_WRITE
+ */
+int
+smb_usr_rw(struct smb_share *ssp, smbioc_rw_t *rwrq,
+ int cmd, struct smb_cred *scred)
+{
+ struct iovec aiov[1];
+ struct uio auio;
+ u_int16_t fh;
+ int error;
+ uio_rw_t rw;
+
+ switch (cmd) {
+ case SMBIOC_READ:
+ rw = UIO_READ;
+ break;
+ case SMBIOC_WRITE:
+ rw = UIO_WRITE;
+ break;
+ default:
+ return (ENODEV);
+ }
+
+ fh = htoles(rwrq->ioc_fh);
+
+ aiov[0].iov_base = rwrq->ioc_base;
+ aiov[0].iov_len = (size_t)rwrq->ioc_cnt;
+
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = rwrq->ioc_offset;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_fmode = 0;
+ auio.uio_resid = (size_t)rwrq->ioc_cnt;
+
+ error = smb_rwuio(ssp, fh, rw, &auio, scred, 0);
+
+ /*
+ * On return ioc_cnt holds the
+ * number of bytes transferred.
+ */
+ rwrq->ioc_cnt -= auio.uio_resid;
+
+ return (error);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c b/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c
new file mode 100644
index 0000000000..8ead47d2a5
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c
@@ -0,0 +1,990 @@
+/*
+ * Copyright (c) 2000, 2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/kern/subr_mchain.c,v 1.1 2001/02/24 15:44:29 bp Exp $
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/strsubr.h>
+#include <sys/cmn_err.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#else
+#include <netsmb/smb_osdep.h>
+#endif
+
+#include <netsmb/mchain.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+
+/* BEGIN CSTYLED */
+/*
+ * BSD-style mbufs, vs SysV-style mblks:
+ * One big difference: the mbuf payload is:
+ * m_data ... (m_data + m_len)
+ * In Unix STREAMS, the mblk payload is:
+ * b_rptr ... b_wptr
+ *
+ * Here are some handy conversion notes:
+ *
+ * struct mbuf struct mblk
+ * m->m_next m->b_cont
+ * m->m_nextpkt m->b_next
+ * m->m_data m->b_rptr
+ * m->m_len MBLKL(m)
+ * m->m_dat[] m->b_datap->db_base
+ * &m->m_dat[MLEN] m->b_datap->db_lim
+ * M_TRAILINGSPACE(m) MBLKTAIL(m)
+ * m_freem(m) freemsg(m)
+ *
+ * Note that mbufs chains also have a special "packet" header,
+ * which has the length of the whole message. In STREAMS one
+ * typically just calls msgdsize(m) to get that.
+ */
+/* END CSTYLED */
+
+
+/*
+ *
+ * MODULE_VERSION(libmchain, 1);
+ */
+
+#ifdef __GNUC__
+#define MBERROR(format, args...) printf("%s(%d): "format, \
+ __FUNCTION__, __LINE__, ## args)
+#define MBPANIC(format, args...) printf("%s(%d): "format, \
+ __FUNCTION__, __LINE__, ## args)
+#else
+#define MBERROR(...) \
+ smb_errmsg(CE_NOTE, __func__, __VA_ARGS__)
+#define MBPANIC(...) \
+ smb_errmsg(CE_PANIC, __func__, __VA_ARGS__)
+#endif
+
+/*
+ * MLEN: The smallest mblk we'll allocate.
+ *
+ * There's more to MLEN than you might think.
+ * Some ethernet drivers may send each mblk as a
+ * separate frame, so we want MLEN at least 1K.
+ * We could have used 1K here, but that might
+ * hurt transports that support larger frames.
+ * 4K fits nicely in 3 Ethernet frames (3 * 1500)
+ * leaving about 500 bytes for protocol headers.
+ *
+ * XXX: Would Ethernet drivers be happier
+ * (more efficient) if we used 1K here?
+ */
+#define MLEN 4096
+
+
+/*
+ * Some UIO routines.
+ * Taken from Darwin Sourcecs.
+ */
+
+/*
+ * uio_isuserspace - return non zero value if the address space
+ * flag is for a user address space (could be 32 or 64 bit).
+ */
+int
+uio_isuserspace(uio_t *a_uio)
+{
+ if (a_uio->uio_segflg == UIO_USERSPACE) {
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * uio_curriovbase - return the base address of the current iovec associated
+ * with the given uio_t. May return 0.
+ */
+caddr_t
+uio_curriovbase(uio_t *a_uio)
+{
+ if (a_uio->uio_iovcnt < 1) {
+ return (0);
+ }
+ return ((caddr_t)((uintptr_t)a_uio->uio_iov->iov_base));
+}
+
+/*
+ * uio_curriovlen - return the length value of the current iovec associated
+ * with the given uio_t.
+ */
+size_t
+uio_curriovlen(uio_t *a_uio)
+{
+ if (a_uio->uio_iovcnt < 1) {
+ return (0);
+ }
+ return ((size_t)a_uio->uio_iov->iov_len);
+}
+
+
+/*
+ * uio_update - update the given uio_t for a_count of completed IO.
+ * This call decrements the current iovec length and residual IO value
+ * and increments the current iovec base address and offset value.
+ * If the current iovec length is 0 then advance to the next
+ * iovec (if any).
+ * If the a_count passed in is 0, than only do the advancement
+ * over any 0 length iovec's.
+ */
+void
+uio_update(uio_t *a_uio, size_t a_count)
+{
+ if (a_uio->uio_iovcnt < 1) {
+ return;
+ }
+
+ /*
+ * if a_count == 0, then we are asking to skip over
+ * any empty iovs
+ */
+ if (a_count) {
+ if (a_count > a_uio->uio_iov->iov_len) {
+ a_uio->uio_iov->iov_base += a_uio->uio_iov->iov_len;
+ a_uio->uio_iov->iov_len = 0;
+ } else {
+ a_uio->uio_iov->iov_base += a_count;
+ a_uio->uio_iov->iov_len -= a_count;
+ }
+ if (a_uio->uio_resid < 0) {
+ a_uio->uio_resid = 0;
+ }
+ if (a_count > (size_t)a_uio->uio_resid) {
+ a_uio->uio_offset += a_uio->uio_resid;
+ a_uio->uio_resid = 0;
+ } else {
+ a_uio->uio_offset += a_count;
+ a_uio->uio_resid -= a_count;
+ }
+ }
+ /*
+ * advance to next iovec if current one is totally consumed
+ */
+ while (a_uio->uio_iovcnt > 0 && a_uio->uio_iov->iov_len == 0) {
+ a_uio->uio_iovcnt--;
+ if (a_uio->uio_iovcnt > 0) {
+ a_uio->uio_iov++;
+ }
+ }
+}
+
+
+/*ARGSUSED*/
+mblk_t *
+m_getblk(int size, int type)
+{
+ mblk_t *mblk;
+ int error;
+
+ /* Make size at least MLEN. */
+ if (size < MLEN)
+ size = MLEN;
+ mblk = allocb_wait(size, BPRI_LO, STR_NOSIG, &error);
+ ASSERT(mblk);
+ return (mblk);
+}
+
+void
+mb_done(struct mbchain *mbp)
+{
+ if (mbp->mb_top) {
+ freemsg(mbp->mb_top);
+ mbp->mb_top = NULL;
+ }
+ /* Avoid dangling references */
+ mbp->mb_cur = NULL;
+}
+
+unsigned int
+m_length(mblk_t *mblk)
+{
+ uint64_t diff;
+
+ diff = (uintptr_t)mblk->b_datap->db_lim -
+ (uintptr_t)mblk->b_datap->db_base;
+ ASSERT(diff == (uint64_t)((unsigned int)diff));
+ return ((unsigned int)diff);
+}
+
+void
+mb_initm(struct mbchain *mbp, mblk_t *m)
+{
+ bzero(mbp, sizeof (*mbp));
+ mbp->mb_top = mbp->mb_cur = m;
+}
+
+
+int
+mb_init(struct mbchain *mbp)
+{
+ mblk_t *mblk;
+
+ mblk = m_getblk(MLEN, 1);
+ if (mblk == NULL) {
+ return (ENOSR);
+ }
+
+ /*
+ * Leave room in this first mblk so we can
+ * prepend a 4-byte NetBIOS header.
+ * See smb_nbst_send()
+ */
+ mblk->b_wptr += 4;
+ mblk->b_rptr = mblk->b_wptr;
+
+ mb_initm(mbp, mblk);
+ return (0);
+}
+
+
+/*
+ * mb_detach() function returns the value of mbp->mb_top field
+ * and sets its * value to NULL.
+ */
+
+mblk_t *
+mb_detach(struct mbchain *mbp)
+{
+ mblk_t *m;
+
+ m = mbp->mb_top;
+ mbp->mb_top = mbp->mb_cur = NULL;
+ return (m);
+}
+
+/*
+ * Returns the length of the mblk_t data.
+ *
+ */
+int
+m_fixhdr(mblk_t *m0)
+{
+ size_t dsz;
+
+ dsz = msgdsize(m0);
+ return ((int)dsz);
+}
+
+/*
+ * BSD code set the message header length here, and
+ * returned the length. We don't have that field, so
+ * just return the message length.
+ */
+int
+mb_fixhdr(struct mbchain *mbp)
+{
+ return (m_fixhdr(mbp->mb_top));
+}
+
+
+/*
+ * Check if object of size 'size' fit to the current position and
+ * allocate new mbuf if not. Advance pointers and increase len. of mbuf(s).
+ * Return pointer to the object placeholder or NULL if any error occured.
+ * Note: size should be <= MLEN
+ */
+void *
+mb_reserve(struct mbchain *mbp, int size)
+{
+ mblk_t *m, *mn;
+ void *bpos;
+
+ m = mbp->mb_cur;
+ /*
+ * If the requested size is more than the space left.
+ * Allocate and appenad a new mblk.
+ */
+ /*LINTED*/
+ if (MBLKTAIL(m) < size) {
+ mn = m_getblk(size, 1);
+ if (mn == NULL)
+ return (NULL);
+ mbp->mb_cur = m->b_cont = mn;
+ m = mn;
+ }
+ /*
+ * If 'size' bytes fits into the buffer, then
+ * 1. increment the write pointer to the size.
+ * 2. return the position from where the memory is reserved.
+ */
+ bpos = m->b_wptr;
+ m->b_wptr += size;
+ mbp->mb_count += size;
+ return (bpos);
+}
+
+/*
+ * All mb_put_*() functions perform an actual copy of the data into mbuf
+ * chain. Functions which have le or be suffixes will perform conversion to
+ * the little- or big-endian data formats.
+ * XXX: Assumes total data length in previous mblks is EVEN.
+ * XXX: Might need to compute the offset from mb_top instead.
+ */
+int
+mb_put_padbyte(struct mbchain *mbp)
+{
+ caddr_t dst;
+ char x = 0;
+
+ dst = (caddr_t)mbp->mb_cur->b_wptr;
+
+ /* only add padding if address is odd */
+ if ((long)dst & 1)
+ return (mb_put_mem(mbp, (caddr_t)&x, 1, MB_MSYSTEM));
+ else
+ return (0);
+}
+
+int
+mb_put_uint8(struct mbchain *mbp, u_int8_t x)
+{
+ return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
+}
+
+int
+mb_put_uint16be(struct mbchain *mbp, u_int16_t x)
+{
+ x = htobes(x);
+ return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
+}
+
+int
+mb_put_uint16le(struct mbchain *mbp, u_int16_t x)
+{
+ x = htoles(x);
+ return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
+}
+
+int
+mb_put_uint32be(struct mbchain *mbp, u_int32_t x)
+{
+ x = htobel(x);
+ return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
+}
+
+int
+mb_put_uint32le(struct mbchain *mbp, u_int32_t x)
+{
+ x = htolel(x);
+ return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
+}
+
+int
+mb_put_uint64be(struct mbchain *mbp, u_int64_t x)
+{
+ x = htobeq(x);
+ return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
+}
+
+int
+mb_put_uint64le(struct mbchain *mbp, u_int64_t x)
+{
+ x = htoleq(x);
+ return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM));
+}
+
+/*
+ * mb_put_mem() function copies size bytes of data specified by the source
+ * argument to an mbuf chain. The type argument specifies the method used
+ * to perform a copy
+ */
+int
+mb_put_mem(struct mbchain *mbp, c_caddr_t source, int size, int type)
+{
+ mblk_t *m, *n;
+ caddr_t dst;
+ c_caddr_t src;
+ int cplen, error, mleft, count;
+ uint64_t diff;
+
+ m = mbp->mb_cur;
+
+ /*LINTED*/
+ diff = MBLKTAIL(m);
+ ASSERT(diff == (uint64_t)((int)diff));
+ mleft = (int)diff;
+
+ while (size > 0) {
+ if (mleft == 0) {
+ if (m->b_cont == NULL) {
+ /*
+ * Changed m_getm() to m_getblk()
+ * with the requested size, so we
+ * don't need m_getm() anymore.
+ */
+ n = m_getblk(size, 1);
+ if (n == NULL)
+ return (ENOBUFS);
+ m->b_cont = n;
+ }
+ m = m->b_cont;
+ /*LINTED*/
+ diff = MBLKTAIL(m);
+ ASSERT(diff == (uint64_t)((int)diff));
+ mleft = (int)diff;
+ continue;
+ }
+ cplen = mleft > size ? size : mleft;
+ dst = (caddr_t)m->b_wptr;
+ switch (type) {
+ case MB_MINLINE:
+ for (src = source, count = cplen; count; count--)
+ *dst++ = *src++;
+ break;
+ case MB_MSYSTEM:
+ /*
+ * Try copying the raw bytes instead of using bcopy()
+ */
+ bcopy(source, dst, cplen);
+ break;
+ case MB_MUSER:
+ error = copyin((void *)source, dst, cplen);
+ if (error)
+ return (error);
+ break;
+ case MB_MZERO:
+ bzero(dst, cplen);
+ break;
+ }
+ size -= cplen;
+ source += cplen;
+ mleft -= cplen;
+ m->b_wptr += cplen;
+ mbp->mb_count += cplen;
+ }
+ mbp->mb_cur = m;
+ return (0);
+}
+
+/*
+ * Append an mblk to the chain.
+ */
+int
+mb_put_mbuf(struct mbchain *mbp, mblk_t *m)
+{
+ mblk_t *mb;
+
+ /* See: linkb(9f) */
+ for (mb = mbp->mb_cur; mb->b_cont; mb = mb->b_cont)
+ ;
+ mb->b_cont = m;
+ mbp->mb_cur = m;
+ mbp->mb_count += msgdsize(m);
+
+ return (0);
+}
+
+/*
+ * copies a uio scatter/gather list to an mbuf chain.
+ */
+int
+mb_put_uio(struct mbchain *mbp, uio_t *uiop, int size)
+{
+ int left;
+ int mtype, error;
+
+ mtype = (uio_isuserspace(uiop) ? MB_MUSER : MB_MSYSTEM);
+
+ while (size > 0 && uiop->uio_resid) {
+ if (uiop->uio_iovcnt <= 0 || uio_curriovbase(uiop) ==
+ USER_ADDR_NULL)
+ return (EFBIG);
+ left = uio_curriovlen(uiop);
+ if (left > size)
+ left = size;
+ error = mb_put_mem(mbp, CAST_DOWN(caddr_t,
+ uio_curriovbase(uiop)), left, mtype);
+ if (error)
+ return (error);
+ uio_update(uiop, left);
+ size -= left;
+ }
+ return (0);
+}
+
+/*
+ * Routines for fetching data from an mbuf chain
+ */
+int
+md_init(struct mdchain *mdp)
+{
+ mblk_t *m;
+
+ m = m_getblk(MLEN, 1);
+ if (m == NULL)
+ return (ENOBUFS);
+ md_initm(mdp, m);
+ return (0);
+}
+
+void
+md_initm(struct mdchain *mdp, mblk_t *m)
+{
+ bzero(mdp, sizeof (*mdp));
+ mdp->md_top = mdp->md_cur = m;
+ mdp->md_pos = m->b_rptr;
+}
+
+void
+md_done(struct mdchain *mdp)
+{
+ mblk_t *m;
+
+ /*
+ * Deal with the fact that we can error out of
+ * smb_t2_reply or smb_nt_reply without using up
+ * all the "records" added by md_append_record().
+ */
+ while ((m = mdp->md_top) != NULL) {
+ mdp->md_top = m->b_next;
+ m->b_next = NULL;
+ freemsg(m);
+ }
+ /* Avoid dangling references */
+ mdp->md_cur = NULL;
+ mdp->md_pos = NULL;
+}
+
+/*
+ * Append a new message (separate mbuf chain).
+ * It is caller responsibility to prevent
+ * multiple calls to fetch/record routines.
+ * XXX: Note (mis)use of mblk->b_next here.
+ */
+void
+md_append_record(struct mdchain *mdp, mblk_t *top)
+{
+ mblk_t *m;
+
+ top->b_next = NULL;
+ if (mdp->md_top == NULL) {
+ md_initm(mdp, top);
+ return;
+ }
+ m = mdp->md_top;
+ /* Get to last message (not b_cont chain) */
+ while (m->b_next)
+ m = m->b_next;
+ m->b_next = top;
+}
+
+/*
+ * Advance mdp->md_top to the next message.
+ * XXX: Note (mis)use of mblk->b_next here.
+ */
+int
+md_next_record(struct mdchain *mdp)
+{
+ mblk_t *m;
+
+ if (mdp->md_top == NULL)
+ return (ENOENT);
+ /* Get to next message (not b_cont chain) */
+ m = mdp->md_top->b_next;
+ mdp->md_top->b_next = NULL;
+ md_done(mdp);
+ if (m == NULL)
+ return (ENOENT);
+ md_initm(mdp, m);
+ return (0);
+}
+
+int
+md_get_uint8(struct mdchain *mdp, u_int8_t *x)
+{
+ return (md_get_mem(mdp, (char *)x, 1, MB_MINLINE));
+}
+
+int
+md_get_uint16(struct mdchain *mdp, u_int16_t *x)
+{
+ return (md_get_mem(mdp, (char *)x, 2, MB_MINLINE));
+}
+
+int
+md_get_uint16le(struct mdchain *mdp, u_int16_t *x)
+{
+ u_int16_t v;
+ int error = md_get_uint16(mdp, &v);
+
+ if (x)
+ *x = letohs(v);
+ return (error);
+}
+
+int
+md_get_uint16be(struct mdchain *mdp, u_int16_t *x) {
+ u_int16_t v;
+ int error = md_get_uint16(mdp, &v);
+
+ if (x)
+ *x = betohs(v);
+ return (error);
+}
+
+int
+md_get_uint32(struct mdchain *mdp, u_int32_t *x)
+{
+ return (md_get_mem(mdp, (caddr_t)x, 4, MB_MINLINE));
+}
+
+int
+md_get_uint32be(struct mdchain *mdp, u_int32_t *x)
+{
+ u_int32_t v;
+ int error;
+
+ error = md_get_uint32(mdp, &v);
+ if (x)
+ *x = betohl(v);
+ return (error);
+}
+
+int
+md_get_uint32le(struct mdchain *mdp, u_int32_t *x)
+{
+ u_int32_t v;
+ int error;
+
+ error = md_get_uint32(mdp, &v);
+ if (x)
+ *x = letohl(v);
+ return (error);
+}
+
+int
+md_get_uint64(struct mdchain *mdp, u_int64_t *x)
+{
+ return (md_get_mem(mdp, (caddr_t)x, 8, MB_MINLINE));
+}
+
+int
+md_get_uint64be(struct mdchain *mdp, u_int64_t *x)
+{
+ u_int64_t v;
+ int error;
+
+ error = md_get_uint64(mdp, &v);
+ if (x)
+ *x = betohq(v);
+ return (error);
+}
+
+int
+md_get_uint64le(struct mdchain *mdp, u_int64_t *x)
+{
+ u_int64_t v;
+ int error;
+
+ error = md_get_uint64(mdp, &v);
+ if (x)
+ *x = letohq(v);
+ return (error);
+}
+
+int
+md_get_mem(struct mdchain *mdp, caddr_t target, int size, int type)
+{
+ mblk_t *m = mdp->md_cur;
+ int error;
+ int count;
+ unsigned char *s;
+ uint64_t diff;
+
+ while (size > 0) {
+ if (m == NULL) {
+ SMBSDEBUG("incomplete copy\n");
+ return (EBADRPC);
+ }
+
+ /*
+ * Offset in the current MBUF.
+ */
+ s = mdp->md_pos;
+ ASSERT((m->b_rptr <= s) && (s <= m->b_wptr));
+
+ /* Data remaining. */
+ diff = (uintptr_t)m->b_wptr - (uintptr_t)s;
+ ASSERT(diff == (uint64_t)((int)diff));
+ count = (int)diff;
+
+ /*
+ * Check if the no. of bytes remaining is less than
+ * the bytes requested.
+ */
+ if (count == 0) {
+ m = m->b_cont;
+ if (m) {
+ mdp->md_cur = m;
+ mdp->md_pos = s = m->b_rptr;
+ }
+ continue;
+ }
+ if (count > size)
+ count = size;
+ size -= count;
+ mdp->md_pos += count;
+ if (target == NULL)
+ continue;
+ switch (type) {
+ case MB_MUSER:
+ error = copyout(s, (void *)target, count);
+ if (error)
+ return (error);
+ break;
+ case MB_MSYSTEM:
+ bcopy(s, target, count);
+ break;
+ case MB_MINLINE:
+ while (count--)
+ *target++ = *s++;
+ continue;
+ }
+ target += count;
+ }
+ return (0);
+}
+
+/*
+ * Get the next SIZE bytes as a separate mblk.
+ */
+int
+md_get_mbuf(struct mdchain *mdp, int size, mblk_t **ret)
+{
+ mblk_t *m, *rm;
+
+ unsigned char *s;
+ uint64_t diff;
+ int off;
+
+ /*
+ * Offset in the current MBUF.
+ */
+ m = mdp->md_cur;
+ s = mdp->md_pos;
+ ASSERT((m->b_rptr <= s) && (s <= m->b_wptr));
+ diff = (uintptr_t)s - (uintptr_t)m->b_rptr;
+ ASSERT(diff == (uint64_t)((int)diff));
+ off = (int)diff;
+
+ rm = m_copym(m, off, size, M_WAITOK);
+ if (rm == NULL)
+ return (EBADRPC);
+
+ *ret = rm;
+ return (0);
+}
+
+int
+md_get_uio(struct mdchain *mdp, uio_t *uiop, int size)
+{
+ size_t left;
+ int mtype, error;
+
+ mtype = (uio_isuserspace(uiop) ? MB_MUSER : MB_MSYSTEM);
+ while (size > 0 && uiop->uio_resid) {
+ if (uiop->uio_iovcnt <= 0 ||
+ uio_curriovbase(uiop) == USER_ADDR_NULL)
+ return (EFBIG);
+ left = uio_curriovlen(uiop);
+ if (left > size)
+ left = size;
+ error = md_get_mem(mdp, CAST_DOWN(caddr_t,
+ uio_curriovbase(uiop)), left, mtype);
+ if (error)
+ return (error);
+ uio_update(uiop, left);
+ size -= left;
+ }
+ return (0);
+}
+
+/*
+ * Additions for Solaris
+ */
+
+/*
+ * concatenate mblk chain n to m.
+ * go till end of data in m.
+ * then add the link of b_cont to n.
+ * See: linkb(9f)
+ */
+
+void m_cat(
+ mblk_t *m,
+ mblk_t *n)
+{
+ if (!n)
+ return;
+ while (m->b_cont) {
+ m = m->b_cont;
+ }
+ m->b_cont = n;
+}
+
+/*ARGSUSED*/
+mblk_t *
+m_copym(mblk_t *m, int off, int len, int wait)
+{
+ mblk_t *n;
+ size_t dsz;
+ ssize_t adj;
+
+ dsz = msgdsize(m);
+ if (len == M_COPYALL) {
+ if (off > dsz)
+ return (0);
+ } else {
+ if ((off + len) > dsz)
+ return (0);
+ }
+
+ if ((n = dupmsg(m)) == NULL)
+ return (0);
+
+ /* trim from head */
+ adj = off;
+ if (!adjmsg(n, adj)) {
+ freemsg(n);
+ return (0);
+ }
+
+ /* trim from tail */
+ if (len != M_COPYALL) {
+ dsz = msgdsize(n);
+ ASSERT(len <= dsz);
+ if (len < dsz) {
+ adj = (ssize_t)len - (ssize_t)dsz;
+ ASSERT(adj < 0);
+ adjmsg(n, adj);
+ }
+ }
+
+ return (n);
+}
+
+/*
+ * Get "rqlen" contiguous bytes into the first mblk of a chain.
+ */
+mblk_t *
+m_pullup(
+ mblk_t *m,
+ int rqlen)
+{
+ ptrdiff_t diff;
+
+ /*LINTED*/
+ diff = MBLKL(m);
+ ASSERT(diff == (ptrdiff_t)((int)diff));
+ if ((int)diff < rqlen) {
+ /* This should be rare. */
+ if (!pullupmsg(m, rqlen)) {
+ SMBSDEBUG("pullupmsg failed!\n");
+ freemsg(m);
+ return (NULL);
+ }
+ }
+ return (m);
+}
+
+
+/*
+ * m_split : split the mblk from the offset(len0) to the end.
+ * Partition an mbuf chain in two pieces, returning the tail --
+ * all but the first len0 bytes. In case of failure, it returns NULL and
+ * attempts to restore the chain to its original state.
+ * Similar to dupmsg() + adjmsg() on Solaris.
+ */
+/*ARGSUSED*/
+mblk_t *
+m_split(
+ mblk_t *m0,
+ int len0,
+ int wait)
+{
+ mblk_t *m, *n;
+ int mbl, len = len0;
+ ptrdiff_t diff;
+
+#if 0 /* If life were simple, this would be: */
+ for (m = m0; m && len > MBLKL(m); m = m->b_cont)
+ len -= MBLKL(m);
+#else /* but with LP64 and picky lint we have: */
+ for (m = m0; m; m = m->b_cont) {
+ /*LINTED*/
+ diff = MBLKL(m);
+ ASSERT(diff == (ptrdiff_t)((int)diff));
+ mbl = (int)diff;
+ if (len <= mbl)
+ break;
+ len -= mbl;
+ }
+#endif
+
+ if (m == 0)
+ return (0);
+
+ /* This is the one to split (dupb, adjust) */
+ if ((n = dupb(m)) == 0)
+ return (0);
+
+ /*LINTED*/
+ ASSERT(len <= MBLKL(m));
+
+ m->b_wptr = m->b_rptr + len;
+ n->b_rptr += len;
+
+ /* Move any b_cont (tail) to the new head. */
+ n->b_cont = m->b_cont;
+ m->b_cont = NULL;
+
+ return (n);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h
new file mode 100644
index 0000000000..0a2e78c103
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smbfs.h,v 1.30.100.1 2005/05/27 02:35:28 lindak Exp $
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SMBFS_SMBFS_H
+#define _SMBFS_SMBFS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * FS-specific VFS structures for smbfs.
+ * (per-mount stuff, etc.)
+ *
+ * This file used to have mount args stuff,
+ * but that's now in sys/fs/smbfs_mount.h
+ */
+
+#include <sys/list.h>
+#include <sys/vfs.h>
+#include <sys/fs/smbfs_mount.h>
+
+
+/*
+ * SM_MAX_STATFSTIME is the maximum time to cache statvfs data. Since this
+ * should be a fast call on the server, the time the data cached is short.
+ * That lets the cache handle bursts of statvfs() requests without generating
+ * lots of network traffic.
+ */
+#define SM_MAX_STATFSTIME 2
+
+/* Mask values for smbmount structure sm_status field */
+#define SM_STATUS_STATFS_BUSY 0x00000001 /* statvfs is in progress */
+#define SM_STATUS_STATFS_WANT 0x00000002 /* statvfs wakeup is wanted */
+#define SM_STATUS_TIMEO 0x00000004 /* this mount is not responding */
+#define SM_STATUS_DEAD 0x00000010 /* connection gone - unmount this */
+
+extern const struct fs_operation_def smbfs_vnodeops_template[];
+extern struct vnodeops *smbfs_vnodeops;
+
+struct smbnode;
+struct smb_share;
+
+/*
+ * The values for smi_flags.
+ */
+#define SMI_INT 0x01 /* interrupts allowed */
+#define SMI_DEAD 0x02 /* zone shutting down */
+#define SMI_LLOCK 0x80 /* local locking only */
+
+/*
+ * Corresponds to Darwin: struct smbmount
+ */
+typedef struct smbmntinfo {
+ struct vfs *smi_vfsp; /* mount back pointer to vfs */
+ struct smbnode *smi_root; /* the root node */
+ struct smb_share *smi_share; /* netsmb SMB share conn data */
+ kmutex_t smi_lock; /* mutex for flags, etc. */
+ uint32_t smi_flags; /* NFS-derived flag bits */
+ uint32_t smi_fsattr; /* acls & streams opts */
+ uint32_t smi_status; /* status bits for this mount */
+ hrtime_t smi_statfstime; /* sm_statvfsbuf cache time */
+ statvfs64_t smi_statvfsbuf; /* cached statvfs data */
+ kcondvar_t smi_statvfs_cv;
+
+ /*
+ * Kstat statistics
+ */
+ struct kstat *smi_io_kstats;
+ struct kstat *smi_ro_kstats;
+
+ /*
+ * Zones support.
+ */
+ struct zone *smi_zone; /* Zone mounted in */
+ list_node_t smi_zone_node; /* Link to per-zone smi list */
+ /* Lock for the list is: smi_globals_t -> smg_lock */
+
+ /*
+ * Copy of the args from mount.
+ */
+ struct smbfs_args smi_args;
+} smbmntinfo_t;
+
+typedef struct smbfattr {
+ int fa_attr;
+ len_t fa_size;
+ struct timespec fa_atime;
+ struct timespec fa_ctime;
+ struct timespec fa_mtime;
+ ino64_t fa_ino;
+ struct timespec fa_reqtime;
+} smbfattr_t;
+
+/*
+ * vnode pointer to mount info
+ */
+#define VTOSMI(vp) ((smbmntinfo_t *)(((vp)->v_vfsp)->vfs_data))
+#define VFTOSMI(vfsp) ((smbmntinfo_t *)((vfsp)->vfs_data))
+#define SMBINTR(vp) (VTOSMI(vp)->smi_flags & SMI_INT)
+
+#endif /* _SMBFS_SMBFS_H */
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c
new file mode 100644
index 0000000000..368d5e2487
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c
@@ -0,0 +1,288 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/t_lock.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/errno.h>
+#include <sys/buf.h>
+#include <sys/stat.h>
+#include <sys/cred.h>
+#include <sys/kmem.h>
+#include <sys/debug.h>
+#include <sys/dnlc.h>
+#include <sys/vmsystm.h>
+#include <sys/flock.h>
+#include <sys/share.h>
+#include <sys/cmn_err.h>
+#include <sys/tiuser.h>
+#include <sys/sysmacros.h>
+#include <sys/callb.h>
+#include <sys/acl.h>
+#include <sys/kstat.h>
+#include <sys/signal.h>
+#include <sys/list.h>
+#include <sys/zone.h>
+
+#include <netsmb/smb_conn.h>
+
+#include <smbfs/smbfs.h>
+#include <smbfs/smbfs_node.h>
+#include <smbfs/smbfs_subr.h>
+
+#include <vm/hat.h>
+#include <vm/as.h>
+#include <vm/page.h>
+#include <vm/pvn.h>
+#include <vm/seg.h>
+#include <vm/seg_map.h>
+#include <vm/seg_vn.h>
+
+/*
+ * The following code provide zone support in order to perform an action
+ * for each smbfs mount in a zone. This is also where we would add
+ * per-zone globals and kernel threads for the smbfs module (since
+ * they must be terminated by the shutdown callback).
+ */
+
+struct smi_globals {
+ kmutex_t smg_lock; /* lock protecting smg_list */
+ list_t smg_list; /* list of SMBFS mounts in zone */
+ boolean_t smg_destructor_called;
+};
+typedef struct smi_globals smi_globals_t;
+
+static zone_key_t smi_list_key;
+
+/* ARGSUSED */
+static void *
+smbfs_zone_init(zoneid_t zoneid)
+{
+ smi_globals_t *smg;
+
+ smg = kmem_alloc(sizeof (*smg), KM_SLEEP);
+ mutex_init(&smg->smg_lock, NULL, MUTEX_DEFAULT, NULL);
+ list_create(&smg->smg_list, sizeof (smbmntinfo_t),
+ offsetof(smbmntinfo_t, smi_zone_node));
+ smg->smg_destructor_called = B_FALSE;
+ return (smg);
+}
+
+/*
+ * Callback routine to tell all SMBFS mounts in the zone to stop creating new
+ * threads. Existing threads should exit.
+ */
+/* ARGSUSED */
+static void
+smbfs_zone_shutdown(zoneid_t zoneid, void *data)
+{
+ smi_globals_t *smg = data;
+ smbmntinfo_t *smi;
+
+ ASSERT(smg != NULL);
+again:
+ mutex_enter(&smg->smg_lock);
+ for (smi = list_head(&smg->smg_list); smi != NULL;
+ smi = list_next(&smg->smg_list, smi)) {
+
+ /*
+ * If we've done the shutdown work for this FS, skip.
+ * Once we go off the end of the list, we're done.
+ */
+ if (smi->smi_flags & SMI_DEAD)
+ continue;
+
+ /*
+ * We will do work, so not done. Get a hold on the FS.
+ */
+ VFS_HOLD(smi->smi_vfsp);
+
+ /*
+ * purge the DNLC for this filesystem
+ */
+ (void) dnlc_purge_vfsp(smi->smi_vfsp, 0);
+
+ mutex_enter(&smi->smi_lock);
+ smi->smi_flags |= SMI_DEAD;
+ mutex_exit(&smi->smi_lock);
+
+ /*
+ * Drop lock and release FS, which may change list, then repeat.
+ * We're done when every mi has been done or the list is empty.
+ */
+ mutex_exit(&smg->smg_lock);
+ VFS_RELE(smi->smi_vfsp);
+ goto again;
+ }
+ mutex_exit(&smg->smg_lock);
+}
+
+static void
+smbfs_zone_free_globals(smi_globals_t *smg)
+{
+ list_destroy(&smg->smg_list); /* makes sure the list is empty */
+ mutex_destroy(&smg->smg_lock);
+ kmem_free(smg, sizeof (*smg));
+
+}
+
+/* ARGSUSED */
+static void
+smbfs_zone_destroy(zoneid_t zoneid, void *data)
+{
+ smi_globals_t *smg = data;
+
+ ASSERT(smg != NULL);
+ mutex_enter(&smg->smg_lock);
+ if (list_head(&smg->smg_list) != NULL) {
+ /* Still waiting for VFS_FREEVFS() */
+ smg->smg_destructor_called = B_TRUE;
+ mutex_exit(&smg->smg_lock);
+ return;
+ }
+ smbfs_zone_free_globals(smg);
+}
+
+/*
+ * Add an SMBFS mount to the per-zone list of SMBFS mounts.
+ */
+void
+smbfs_zonelist_add(smbmntinfo_t *smi)
+{
+ smi_globals_t *smg;
+
+ smg = zone_getspecific(smi_list_key, smi->smi_zone);
+ mutex_enter(&smg->smg_lock);
+ list_insert_head(&smg->smg_list, smi);
+ mutex_exit(&smg->smg_lock);
+}
+
+/*
+ * Remove an SMBFS mount from the per-zone list of SMBFS mounts.
+ */
+void
+smbfs_zonelist_remove(smbmntinfo_t *smi)
+{
+ smi_globals_t *smg;
+
+ smg = zone_getspecific(smi_list_key, smi->smi_zone);
+ mutex_enter(&smg->smg_lock);
+ list_remove(&smg->smg_list, smi);
+ /*
+ * We can be called asynchronously by VFS_FREEVFS() after the zone
+ * shutdown/destroy callbacks have executed; if so, clean up the zone's
+ * smi_globals.
+ */
+ if (list_head(&smg->smg_list) == NULL &&
+ smg->smg_destructor_called == B_TRUE) {
+ smbfs_zone_free_globals(smg);
+ return;
+ }
+ mutex_exit(&smg->smg_lock);
+}
+
+
+#ifdef NEED_SMBFS_CALLBACKS
+/*
+ * Call-back hooks for netsmb, in case we want them.
+ * Apple's VFS wants them. We may not need them.
+ *
+ * I thought I could use the "dead" callback from netsmb
+ * to set the SMI_DEAD flag, but that looks like it will
+ * interfere with the zone shutdown mechanisms.
+ */
+static void smbfs_dead(smb_share_t *ssp)
+{
+#if 0 /* see above */
+ smbmntinfo_t *smi = ssp->ss_mount;
+ if (smi) {
+ mutex_enter(&smi->smi_lock);
+ smi->smi_flags |= SMI_DEAD;
+ mutex_exit(&smi->smi_lock);
+ }
+#endif
+}
+
+static void smbfs_down(smb_share_t *ss)
+{
+ /* no-op */
+}
+
+static void smbfs_up(smb_share_t *ss)
+{
+ /* no-op */
+}
+
+smb_fscb_t smbfs_cb = {
+ .fscb_dead = smbfs_dead,
+ .fscb_down = smbfs_down,
+ .fscb_up = smbfs_up };
+
+#endif /* NEED_SMBFS_CALLBACKS */
+
+/*
+ * SMBFS Client initialization routine. This routine should only be called
+ * once. It performs the following tasks:
+ * - Initalize all global locks
+ * - Call sub-initialization routines (localize access to variables)
+ */
+int
+smbfs_clntinit(void)
+{
+ int error;
+
+ error = smbfs_subrinit();
+ if (error)
+ return (error);
+ zone_key_create(&smi_list_key, smbfs_zone_init, smbfs_zone_shutdown,
+ smbfs_zone_destroy);
+#ifdef NEED_SMBFS_CALLBACKS
+ smb_fscb_set(&smbfs_cb);
+#endif /* NEED_SMBFS_CALLBACKS */
+ return (0);
+}
+
+/*
+ * This routine is called when the modunload is called. This will cleanup
+ * the previously allocated/initialized nodes.
+ */
+void
+smbfs_clntfini(void)
+{
+#ifdef NEED_SMBFS_CALLBACKS
+ smb_fscb_set(NULL);
+#endif /* NEED_SMBFS_CALLBACKS */
+ (void) zone_key_delete(smi_list_key);
+ smbfs_subrfini();
+}
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_io.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_io.c
new file mode 100644
index 0000000000..b813026f5e
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_io.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smbfs_io.c,v 1.41.38.1 2005/05/27 02:35:28 lindak Exp $
+ *
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <sys/proc.h>
+#include <sys/fcntl.h>
+#include <sys/mount.h>
+#include <sys/dirent.h>
+#include <sys/syslog.h>
+#include <sys/file.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#else
+#include <netsmb/smb_osdep.h>
+#endif
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+
+#include <smbfs/smbfs.h>
+#include <smbfs/smbfs_node.h>
+#include <smbfs/smbfs_subr.h>
+
+/* XXX: This file should go away, after more work in _vnops.c */
+
+int
+smbfs_readvnode(vnode_t *vp, uio_t *uiop, cred_t *cr,
+ struct vattr *vap)
+{
+ smbmntinfo_t *smp = VTOSMI(vp);
+ struct smbnode *np = VTOSMB(vp);
+ struct smb_cred scred;
+ int error;
+ int requestsize;
+ size_t remainder;
+
+ /* shared lock for n_fid use in smb_rwuio */
+ ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER));
+
+ if (vp->v_type != VREG) {
+ SMBVDEBUG("only VREG supported\n");
+ return (EIO);
+ }
+ if (uiop->uio_resid == 0)
+ return (0);
+ if (uiop->uio_loffset < 0)
+ return (EINVAL);
+#ifdef NOT_YET
+ if ((uiop->uio_loffset + uiop->uio_resid) > smp->nm_maxfilesize)
+ return (EFBIG);
+#endif
+
+ smb_credinit(&scred, curproc, cr);
+
+ /* XXX: Update n_size maybe? */
+ (void) smbfs_smb_flush(np, &scred);
+
+ if (uiop->uio_loffset >= vap->va_size) {
+ /* if offset is beyond EOF, read nothing */
+ error = 0;
+ goto out;
+ }
+
+ /* pin requestsize to EOF */
+ requestsize = min(uiop->uio_resid,
+ (vap->va_size - uiop->uio_loffset));
+
+ /* subtract requestSize from uio_resid and save remainder */
+ remainder = uiop->uio_resid - requestsize;
+
+ /* adjust size of read */
+ uiop->uio_resid = requestsize;
+
+ error = smb_rwuio(smp->smi_share, np->n_fid, UIO_READ, uiop,
+ &scred, smb_timo_read);
+
+ /* set remaining uio_resid */
+ uiop->uio_resid = uiop->uio_resid + remainder;
+
+out:
+ smb_credrele(&scred);
+
+ return (error);
+}
+
+int
+smbfs_writevnode(vnode_t *vp, uio_t *uiop,
+ cred_t *cr, int ioflag, int timo)
+{
+ smbmntinfo_t *smp = VTOSMI(vp);
+ struct smbnode *np = VTOSMB(vp);
+ struct smb_cred scred;
+ int error = 0;
+
+ /* shared lock for n_fid use in smb_rwuio */
+ ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER));
+
+ if (vp->v_type != VREG) {
+ SMBVDEBUG("only VREG supported\n");
+ return (EIO);
+ }
+ SMBVDEBUG("ofs=%lld,resid=%d\n", uiop->uio_loffset,
+ (int)uiop->uio_resid);
+ if (uiop->uio_loffset < 0)
+ return (EINVAL);
+#ifdef NOT_YET
+ if (uiop->uio_loffset + uiop->uio_resid > smp->nm_maxfilesize)
+ return (EFBIG);
+#endif
+ if (ioflag & (FAPPEND | FSYNC)) {
+ if (np->n_flag & NMODIFIED) {
+ smbfs_attr_cacheremove(np);
+ /* XXX: smbfs_vinvalbuf? */
+ }
+ if (ioflag & FAPPEND) {
+ struct vattr vattr;
+ /*
+ * File size can be changed by another client
+ */
+ error = smbfsgetattr(vp, &vattr, cr);
+ if (error)
+ return (error);
+ mutex_enter(&np->r_statelock);
+ uiop->uio_loffset = np->n_size;
+ mutex_exit(&np->r_statelock);
+ }
+ }
+ if (uiop->uio_resid == 0)
+ return (0);
+
+ smb_credinit(&scred, curproc, cr);
+
+ /*
+ * Darwin had code here to zero-extend using
+ * smb_write requests. Not needed.
+ *
+ * Use a longer timeout when appending.
+ * This ignores the passed-in timo value,
+ * but that was just a constant anyway.
+ * XXX: remove passed in timo arg later.
+ */
+ timo = smb_timo_write;
+ if ((uiop->uio_loffset + uiop->uio_resid) > np->n_size)
+ timo = smb_timo_append;
+ error = smb_rwuio(smp->smi_share, np->n_fid, UIO_WRITE, uiop,
+ &scred, timo);
+
+ mutex_enter(&np->r_statelock);
+ np->n_flag |= (NFLUSHWIRE | NATTRCHANGED);
+ mutex_exit(&np->r_statelock);
+
+ smb_credrele(&scred);
+
+ SMBVDEBUG("after: ofs=%lld,resid=%d\n", uiop->uio_loffset,
+ (int)uiop->uio_resid);
+ if (!error) {
+ mutex_enter(&np->r_statelock);
+ if (uiop->uio_loffset > (offset_t)np->n_size)
+ np->n_size = (len_t)uiop->uio_loffset;
+ mutex_exit(&np->r_statelock);
+ }
+ return (error);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c
new file mode 100644
index 0000000000..6a2b184f47
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smbfs_node.c,v 1.54.52.1 2005/05/27 02:35:28 lindak Exp $
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/cred.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/kmem.h>
+#include <sys/stat.h>
+#include <sys/atomic.h>
+#include <sys/cmn_err.h>
+#include <sys/sysmacros.h>
+#include <sys/bitmap.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#else
+#include <netsmb/smb_osdep.h>
+#endif
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+
+#include <smbfs/smbfs.h>
+#include <smbfs/smbfs_node.h>
+#include <smbfs/smbfs_subr.h>
+
+#if defined(DEBUG) || defined(lint)
+#define SMBFS_NAME_DEBUG
+#endif
+
+/*
+ * Lack of inode numbers leads us to the problem of generating them.
+ * Partially this problem can be solved by having a dir/file cache
+ * with inode numbers generated from the incremented by one counter.
+ * However this way will require too much kernel memory, gives all
+ * sorts of locking and consistency problems, not to mentinon counter
+ * overflows. So, I'm decided to use a hash function to generate
+ * pseudo random (and [often?] unique) inode numbers.
+ */
+
+/* Magic constants for name hashing. */
+#define FNV_32_PRIME ((uint32_t)0x01000193UL)
+#define FNV1_32_INIT ((uint32_t)33554467UL)
+
+uint32_t
+smbfs_hash3(uint32_t ival, const char *name, int nmlen)
+{
+ uint32_t v;
+
+ for (v = ival; nmlen; name++, nmlen--) {
+ v *= FNV_32_PRIME;
+ v ^= (uint32_t)*name;
+ }
+ return (v);
+}
+
+uint32_t
+smbfs_hash(const char *name, int nmlen)
+{
+ uint32_t v;
+
+ v = smbfs_hash3(FNV1_32_INIT, name, nmlen);
+ return (v);
+}
+
+/*
+ * This is basically a hash of the full path name, but
+ * computed without having the full path contiguously.
+ * The path building logic needs to match what
+ * smbfs_fullpath does.
+ *
+ * Note that smbfs_make_node computes inode numbers by
+ * calling smbfs_hash on the full path name. This will
+ * compute the same result given the directory path and
+ * the last component separately.
+ */
+uint32_t
+smbfs_getino(struct smbnode *dnp, const char *name, int nmlen)
+{
+ uint32_t ino;
+
+ /* Start with directory hash */
+ ino = (uint32_t)dnp->n_ino;
+
+ /*
+ * If not the root, hash a slash.
+ */
+ if (dnp->n_rplen > 1)
+ ino = smbfs_hash3(ino, "\\", 1);
+
+ /* Now hash this component. */
+ ino = smbfs_hash3(ino, name, nmlen);
+
+ return (ino);
+}
+
+#define CHAR_FC '\374' /* 0xFC */
+#define CHAR_FE '\376' /* 0xFE */
+char *
+smbfs_name_alloc(const char *name, int nmlen)
+{
+ char *cp;
+ size_t alen;
+
+#ifdef SMBFS_NAME_DEBUG
+ /*
+ * Note: The passed length is strlen(name),
+ * and does NOT include the terminating nul.
+ * Allocated space holds: (in order)
+ * (int)strlen
+ * char 0xFC (1st marker)
+ * copy of string
+ * terminating null
+ * char 0xFE (2nd marker)
+ */
+ alen = sizeof (int) + 1 + nmlen + 1 + 1;
+ cp = kmem_alloc(alen, KM_SLEEP);
+ /*LINTED*/
+ *(int *)cp = nmlen;
+ cp += sizeof (int);
+ cp[0] = CHAR_FC;
+ cp++;
+ bcopy(name, cp, nmlen);
+ cp[nmlen] = 0;
+ cp[nmlen + 1] = CHAR_FE;
+#else
+ alen = nmlen + 1; /* Passed length does NOT include the nul. */
+ cp = kmem_alloc(alen, KM_SLEEP);
+ bcopy(name, cp, nmlen);
+ cp[nmlen] = 0;
+#endif
+ return (cp);
+}
+
+/*
+ * Note: Passed length does NOT include the nul,
+ * the same as with smbfs_name_alloc().
+ */
+void
+smbfs_name_free(const char *name, int nmlen)
+{
+ size_t alen;
+#ifdef SMBFS_NAME_DEBUG
+ int lnmlen;
+ char *cp;
+
+ /*
+ * See comment in smbfs_name_alloc
+ * about the layout of this memory.
+ */
+ alen = sizeof (int) + 1 + nmlen + 1 + 1;
+ cp = (char *)name;
+ cp--;
+ if (*cp != CHAR_FC) {
+ debug_enter("smbfs_name_free: name[-1] != 0xFC");
+ }
+ cp -= sizeof (int);
+ /*LINTED*/
+ lnmlen = *(int *)cp;
+ if (lnmlen != nmlen) {
+ debug_enter("smbfs_name_free: name[-5] != nmlen");
+ }
+ if (name[nmlen + 1] != CHAR_FE) {
+ debug_enter("smbfs_name_free: name[nmlen+1] != 0xFE");
+ }
+ kmem_free(cp, alen);
+#else
+ alen = nmlen + 1;
+ kmem_free((char *)name, alen);
+#endif
+}
+
+/*
+ * smbfs_nget()
+ *
+ * NOTES:
+ *
+ * It would be nice to be able to pass in a flag when the caller is sure
+ * that the node does not exist and should just be allocated.
+ */
+int
+smbfs_nget(vnode_t *dvp, const char *name, int nmlen,
+ struct smbfattr *fap, vnode_t **vpp)
+{
+ struct smbnode *dnp = VTOSMB(dvp);
+ vnode_t *vp;
+
+ *vpp = NULL;
+
+ /* Don't expect "." or ".." here anymore. */
+ if ((nmlen == 1 && name[0] == '.') ||
+ (nmlen == 2 && name[0] == '.' && name[1] == '.')) {
+ DEBUG_ENTER("smbfs_nget: name is '.' or '..'");
+ return (EINVAL);
+ }
+
+ /* The real work is in this call... */
+ vp = smbfs_make_node(dvp->v_vfsp,
+ dnp->n_rpath, dnp->n_rplen,
+ name, nmlen, fap);
+
+ /*
+ * We always have a vp now, because
+ * smbfs_make_node / make_smbnode
+ * calls kmem_alloc with KM_SLEEP.
+ */
+ ASSERT(vp);
+
+#ifdef NOT_YET
+ /* update the attr_cache info if the file is clean */
+ if (fap && !(VTOSMB(vp)->n_flag & NFLUSHWIRE))
+ smbfs_attr_cacheenter(vp, fap);
+ if (dvp && makeentry) {
+ /* add entry to DNLC */
+ cache_enter(dvp, vp, &cn);
+ }
+#endif /* NOT_YET */
+
+ /* BSD symlink hack removed (smb_symmagic) */
+
+#ifdef NOT_YET
+ smbfs_attr_cacheenter(vp, fap); /* update the attr_cache info */
+#endif /* NOT_YET */
+
+ *vpp = vp;
+
+ return (0);
+}
+
+/*
+ * routines to maintain vnode attributes cache
+ * smbfs_attr_cacheenter: unpack np.i to vnode_vattr structure
+ *
+ * Note that some SMB servers do not exhibit POSIX behaviour
+ * with regard to the mtime on directories. To work around
+ * this, we never allow the mtime on a directory to go backwards,
+ * and bump it forwards elsewhere to simulate the correct
+ * behaviour.
+ */
+void
+smbfs_attr_cacheenter(vnode_t *vp, struct smbfattr *fap)
+{
+ struct smbnode *np = VTOSMB(vp);
+ int vtype;
+ struct timespec ts;
+
+ mutex_enter(&np->r_statelock);
+
+ vtype = vp->v_type;
+ if (vtype == VREG) {
+ if (np->n_size != fap->fa_size) {
+ /*
+ * Had Darwin ubc_sync_range call here,
+ * invalidating the truncated range.
+ * XXX: Solaris equivalent?
+ */
+ SMBVDEBUG("Update size?\n");
+ }
+ np->n_size = fap->fa_size;
+ } else if (vtype == VDIR) {
+ np->n_size = 16384; /* XXX should be a better way ... */
+ /*
+ * Don't allow mtime to go backwards.
+ * Yes this has its flaws. Better ideas are welcome!
+ */
+ /*CSTYLED*/
+ if (timespeccmp(&fap->fa_mtime, &np->n_mtime, <))
+ fap->fa_mtime = np->n_mtime;
+ } else if (vtype != VLNK)
+ goto out;
+
+ np->n_atime = fap->fa_atime;
+ np->n_ctime = fap->fa_ctime;
+ np->n_mtime = fap->fa_mtime;
+ np->n_dosattr = fap->fa_attr;
+
+ np->n_flag &= ~NATTRCHANGED;
+ gethrestime(&ts);
+ np->n_attrage = ts.tv_sec;
+
+out:
+ mutex_exit(&np->r_statelock);
+}
+
+int
+smbfs_attr_cachelookup(vnode_t *vp, struct vattr *vap)
+{
+ struct smbnode *np = VTOSMB(vp);
+ struct smbmntinfo *smi = VTOSMI(vp);
+ time_t attrtimeo;
+ struct timespec ts, *stime;
+ mode_t type;
+
+ /*
+ * Determine attrtimeo. It will be something between SMB_MINATTRTIMO and
+ * SMB_MAXATTRTIMO where recently modified files have a short timeout
+ * and files that haven't been modified in a long time have a long
+ * timeout. This is the same algorithm used by NFS.
+ */
+ gethrestime(&ts);
+ stime = &np->r_mtime;
+ attrtimeo = (ts.tv_sec - stime->tv_sec) / 10;
+ if (attrtimeo < SMB_MINATTRTIMO) {
+ attrtimeo = SMB_MINATTRTIMO;
+ } else if (attrtimeo > SMB_MAXATTRTIMO)
+ attrtimeo = SMB_MAXATTRTIMO;
+ /* has too much time passed? */
+ stime = (struct timespec *)&np->r_attrtime;
+ if ((ts.tv_sec - stime->tv_sec) > attrtimeo)
+ return (ENOENT);
+
+ if (!vap)
+ return (0);
+
+ switch (vp->v_type) {
+ case VREG:
+ type = S_IFREG;
+ break;
+ case VLNK:
+ type = S_IFLNK;
+ break;
+ case VDIR:
+ type = S_IFDIR;
+ break;
+ default:
+ SMBSDEBUG("unknown vnode_vtype %d\n", vp->v_type);
+ return (EINVAL);
+ }
+
+ mutex_enter(&np->r_statelock);
+
+ if (!(np->n_flag & NGOTIDS)) {
+ np->n_mode = type;
+#ifdef APPLE
+ if (smi->smi_fsattr & FILE_PERSISTENT_ACLS) {
+ /* XXX: Can this block? Drop r_statelock? */
+ if (!smbfs_getids(np, scredp)) {
+ np->n_flag |= NGOTIDS;
+ np->n_mode |= ACCESSPERMS; /* 0777 */
+ }
+ }
+#endif /* APPLE */
+ if (!(np->n_flag & NGOTIDS)) {
+ np->n_flag |= NGOTIDS;
+ np->n_uid = smi->smi_args.uid;
+ np->n_gid = smi->smi_args.gid;
+ }
+ }
+
+ if (vap->va_mask & AT_TYPE)
+ vap->va_type = vp->v_type;
+ if (vap->va_mask & AT_MODE) {
+ np->n_mode = 0;
+ if (vp->v_type == VDIR)
+ np->n_mode |= smi->smi_args.dir_mode;
+ else /* symlink and regular file */
+ np->n_mode |= smi->smi_args.file_mode;
+ vap->va_mode = np->n_mode;
+ }
+ if (vap->va_mask & AT_SIZE)
+ vap->va_size = np->n_size;
+ if (vap->va_mask & AT_NODEID)
+ vap->va_nodeid = np->n_ino;
+ if (vap->va_mask & AT_ATIME)
+ vap->va_atime = np->n_atime;
+ if (vap->va_mask & AT_CTIME)
+ vap->va_ctime = np->n_ctime;
+ if (vap->va_mask & AT_MTIME)
+ vap->va_mtime = np->n_mtime;
+ vap->va_nlink = 1;
+ vap->va_uid = np->n_uid;
+ vap->va_gid = np->n_gid;
+ vap->va_fsid = vp->v_vfsp->vfs_dev;
+ vap->va_rdev = 0;
+ vap->va_blksize = MAXBSIZE;
+ vap->va_nblocks = (fsblkcnt64_t)btod(np->n_size);
+ vap->va_seq = 0;
+
+ mutex_exit(&np->r_statelock);
+
+ return (0);
+}
+
+/*
+ * Some SMB servers don't exhibit POSIX behaviour with regard to
+ * updating the directory mtime when the directory's contents
+ * change.
+ *
+ * We force the issue here by updating our cached copy of the mtime
+ * whenever we perform such an action ourselves, and then mark the
+ * cache invalid. Subsequently when the invalidated cache entry is
+ * updated, we disallow an update that would move the mtime backwards.
+ *
+ * This preserves correct or near-correct behaviour with a
+ * compliant server, and gives near-correct behaviour with
+ * a non-compliant server in the most common case (we are the
+ * only client changing the directory).
+ *
+ * There are also complications if a server's time is ahead
+ * of our own. We must 'touch' a directory when it is first
+ * created, to ensure that the timestamp starts out sane,
+ * however it may have a timestamp well ahead of the 'touch'
+ * point which will be returned and cached the first time the
+ * directory's attributes are fetched. Subsequently, the
+ * directory's mtime will not appear to us to change at all
+ * until our local time catches up to the server.
+ *
+ * Thus, any time a directory is 'touched', the saved timestamp
+ * must advance at least far enough forwards to be visible to
+ * the stat(2) interface.
+ *
+ * XXX note that better behaviour with non-compliant servers
+ * could be obtained by bumping the mtime forwards when
+ * an update for an invalidated entry returns a nonsensical
+ * mtime.
+ */
+
+void
+smbfs_attr_touchdir(struct smbnode *dnp)
+{
+ struct timespec ts, ta;
+
+ mutex_enter(&dnp->r_statelock);
+
+ /*
+ * XXX - not sure about this...
+ * Creep the saved time forwards far enough that
+ * layers above the kernel will notice.
+ */
+ ta.tv_sec = 1;
+ ta.tv_nsec = 0;
+ timespecadd(&dnp->n_mtime, &ta);
+ /*
+ * If the current time is later than the updated
+ * saved time, apply it instead.
+ */
+ gethrestime(&ts);
+ /*CSTYLED*/
+ if (timespeccmp(&dnp->n_mtime, &ts, <))
+ dnp->n_mtime = ts;
+ /*
+ * Invalidate the cache, so that we go to the wire
+ * to check that the server doesn't have a better
+ * timestamp next time we care.
+ */
+ smbfs_attr_cacheremove(dnp);
+ mutex_exit(&dnp->r_statelock);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h
new file mode 100644
index 0000000000..e05a0b4a5d
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smbfs_node.h,v 1.31.52.1 2005/05/27 02:35:28 lindak Exp $
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _FS_SMBFS_NODE_H_
+#define _FS_SMBFS_NODE_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Much code copied into here from Sun NFS.
+ */
+
+#include <sys/avl.h>
+#include <sys/list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct rddir_cache {
+ lloff_t _cookie; /* cookie used to find this cache entry */
+ lloff_t _ncookie; /* cookie used to find the next cache entry */
+ char *entries; /* buffer containing dirent entries */
+ int eof; /* EOF reached after this request */
+ int entlen; /* size of dirent entries in buf */
+ int buflen; /* size of the buffer used to store entries */
+ int flags; /* control flags, see below */
+ kcondvar_t cv; /* cv for blocking */
+ int error; /* error from RPC operation */
+ kmutex_t lock;
+ uint_t count; /* reference count */
+ avl_node_t tree; /* AVL tree links */
+} rddir_cache;
+
+#define smbfs_cookie _cookie._p._l
+#define smbfs_ncookie _ncookie._p._l
+#define smbfs3_cookie _cookie._f
+#define smbfs3_ncookie _ncookie._f
+
+#define RDDIR 0x1 /* readdir operation in progress */
+#define RDDIRWAIT 0x2 /* waiting on readdir in progress */
+#define RDDIRREQ 0x4 /* a new readdir is required */
+#define RDDIRCACHED 0x8 /* entry is in the cache */
+
+#define HAVE_RDDIR_CACHE(rp) (avl_numnodes(&(rp)->r_dir) > 0)
+
+/*
+ * A homegrown reader/writer lock implementation. It addresses
+ * two requirements not addressed by the system primitives. They
+ * are that the `enter" operation is optionally interruptible and
+ * that that they can be re`enter'ed by writers without deadlock.
+ */
+typedef struct smbfs_rwlock {
+ int count;
+ int waiters;
+ kthread_t *owner;
+ kmutex_t lock;
+ kcondvar_t cv;
+} smbfs_rwlock_t;
+
+/*
+ * The format of the hash bucket used to lookup smbnodes from a file handle.
+ */
+typedef struct rhashq {
+ struct smbnode *r_hashf;
+ struct smbnode *r_hashb;
+ krwlock_t r_lock;
+} rhashq_t;
+
+/*
+ * Remote file information structure.
+ *
+ * The smbnode is the "inode" for remote files. It contains all the
+ * information necessary to handle remote file on the client side.
+ *
+ * Note on file sizes: we keep two file sizes in the smbnode: the size
+ * according to the client (r_size) and the size according to the server
+ * (r_attr.va_size). They can differ because we modify r_size during a
+ * write system call (smbfs_rdwr), before the write request goes over the
+ * wire (before the file is actually modified on the server). If an OTW
+ * request occurs before the cached data is written to the server the file
+ * size returned from the server (r_attr.va_size) may not match r_size.
+ * r_size is the one we use, in general. r_attr.va_size is only used to
+ * determine whether or not our cached data is valid.
+ *
+ * Each smbnode has 3 locks associated with it (not including the smbnode
+ * hash table and free list locks):
+ *
+ * r_rwlock: Serializes smbfs_write and smbfs_setattr requests
+ * and allows smbfs_read requests to proceed in parallel.
+ * Serializes reads/updates to directories.
+ *
+ * r_lkserlock: Serializes lock requests with map, write, and
+ * readahead operations.
+ *
+ * r_statelock: Protects all fields in the smbnode except for
+ * those listed below. This lock is intented
+ * to be held for relatively short periods of
+ * time (not accross entire putpage operations,
+ * for example).
+ *
+ * The following members are protected by the mutex rpfreelist_lock:
+ * r_freef
+ * r_freeb
+ *
+ * The following members are protected by the hash bucket rwlock:
+ * r_hashf
+ * r_hashb
+ *
+ * Note: r_modaddr is only accessed when the r_statelock mutex is held.
+ * Its value is also controlled via r_rwlock. It is assumed that
+ * there will be only 1 writer active at a time, so it safe to
+ * set r_modaddr and release r_statelock as long as the r_rwlock
+ * writer lock is held.
+ *
+ * 64-bit offsets: the code formerly assumed that atomic reads of
+ * r_size were safe and reliable; on 32-bit architectures, this is
+ * not true since an intervening bus cycle from another processor
+ * could update half of the size field. The r_statelock must now
+ * be held whenever any kind of access of r_size is made.
+ *
+ * Lock ordering:
+ * r_rwlock > r_lkserlock > r_statelock
+ */
+struct exportinfo; /* defined in smbfs/export.h */
+struct failinfo; /* defined in smbfs/smbfs_clnt.h */
+struct mntinfo; /* defined in smbfs/smbfs_clnt.h */
+
+#ifdef _KERNEL
+/* Bits for smbnode.n_flag */
+#define NFLUSHINPROG 0x00001
+#define NFLUSHWANT 0x00002 /* they should gone ... */
+#define NMODIFIED 0x00004 /* bogus, until async IO implemented */
+#define NREFPARENT 0x00010 /* node holds parent from recycling */
+#define NGOTIDS 0x00020
+#define NRDIRSERIAL 0x00080 /* serialize readdir operation */
+#define NISMAPPED 0x00800
+#define NFLUSHWIRE 0x01000
+#define NATTRCHANGED 0x02000 /* use smbfs_attr_cacheremove at close */
+#define NALLOC 0x04000 /* being created */
+#define NWALLOC 0x08000 /* awaiting creation */
+
+typedef struct smbnode {
+ /* from Sun NFS struct rnode (XXX: cleanup needed) */
+ /* the hash fields must be first to match the rhashq_t */
+ /* Lock for the hash queue is: np->r_hashq->r_lock */
+ struct smbnode *r_hashf; /* hash queue forward pointer */
+ struct smbnode *r_hashb; /* hash queue back pointer */
+ /* Lock for the free list is: smbfreelist_lock */
+ struct smbnode *r_freef; /* free list forward pointer */
+ struct smbnode *r_freeb; /* free list back pointer */
+ rhashq_t *r_hashq; /* pointer to the hash bucket */
+ vnode_t *r_vnode; /* vnode for remote file */
+ smbfs_rwlock_t r_rwlock; /* serializes write/setattr requests */
+ smbfs_rwlock_t r_lkserlock; /* serialize lock with other ops */
+ kmutex_t r_statelock; /* protects (most of) smbnode fields */
+ u_offset_t r_nextr; /* next byte read offset (read-ahead) */
+ cred_t *r_cred; /* current credentials */
+ len_t r_size; /* client's view of file size */
+ struct vattr r_attr; /* cached vnode attributes */
+ hrtime_t r_attrtime; /* time attributes become invalid */
+ long r_mapcnt; /* count of mmapped pages */
+ uint_t r_count; /* # of refs not reflect in v_count */
+ uint_t r_awcount; /* # of outstanding async write */
+ uint_t r_gcount; /* getattrs waiting to flush pages */
+ ushort_t r_flags; /* flags, see below */
+ short r_error; /* async write error */
+ kcondvar_t r_cv; /* condvar for blocked threads */
+ avl_tree_t r_dir; /* cache of readdir responses */
+ rddir_cache *r_direof; /* pointer to the EOF entry */
+ kthread_t *r_serial; /* id of purging thread */
+ list_t r_indelmap; /* list of delmap callers */
+ /*
+ * Members derived from Darwin struct smbnode.
+ * Note: n_parent node pointer removed because it
+ * caused unwanted "holds" on nodes in our cache.
+ * Now keeping just the full remote path instead,
+ * in server form, relative to the share root.
+ */
+ char *n_rpath;
+ int n_rplen;
+ uint32_t n_flag;
+ smbmntinfo_t *n_mount;
+ ino64_t n_ino;
+ /* Lock for the next 7 is r_lkserlock */
+ int n_dirrefs;
+ struct smbfs_fctx *n_dirseq; /* ff context */
+ long n_dirofs; /* last ff offset */
+ long n_direof; /* End of dir. offset. */
+ int n_fidrefs;
+ uint16_t n_fid; /* file handle */
+ uint32_t n_rights; /* granted rights */
+ /* Lock for the rest is r_statelock */
+ uid_t n_uid;
+ gid_t n_gid;
+ mode_t n_mode;
+ timestruc_t r_atime;
+ timestruc_t r_ctime;
+ timestruc_t r_mtime;
+ int n_dosattr;
+ /*
+ * XXX: Maybe use this instead:
+ * #define n_atime r_attr.va_atime
+ * etc.
+ */
+#define n_size r_size
+#define n_atime r_atime
+#define n_ctime r_ctime
+#define n_mtime r_mtime
+#define n_attrage r_attrtime
+} smbnode_t;
+#endif /* _KERNEL */
+
+/*
+ * Flags
+ */
+#define RREADDIRPLUS 0x1 /* issue a READDIRPLUS instead of READDIR */
+#define RDIRTY 0x2 /* dirty pages from write operation */
+#define RSTALE 0x4 /* file handle is stale */
+#define RMODINPROGRESS 0x8 /* page modification happening */
+#define RTRUNCATE 0x10 /* truncating, don't commit */
+#define RHAVEVERF 0x20 /* have a write verifier to compare against */
+#define RCOMMIT 0x40 /* commit in progress */
+#define RCOMMITWAIT 0x80 /* someone is waiting to do a commit */
+#define RHASHED 0x100 /* smbnode is in hash queues */
+#define ROUTOFSPACE 0x200 /* an out of space error has happened */
+#define RDIRECTIO 0x400 /* bypass the buffer cache */
+#define RLOOKUP 0x800 /* a lookup has been performed */
+#define RWRITEATTR 0x1000 /* attributes came from WRITE */
+#define RINDNLCPURGE 0x2000 /* in the process of purging DNLC references */
+#define RDELMAPLIST 0x4000 /* delmap callers tracking for as callback */
+
+/*
+ * Convert between vnode and smbnode
+ */
+#define VTOSMB(vp) ((smbnode_t *)((vp)->v_data))
+#define SMBTOV(np) ((np)->r_vnode)
+
+/* Attribute cache timeouts in seconds */
+#define SMB_MINATTRTIMO 2
+#define SMB_MAXATTRTIMO 30
+
+/*
+ * Function definitions.
+ */
+struct smb_cred;
+int smbfs_nget(vnode_t *dvp, const char *name, int nmlen,
+ struct smbfattr *fap, vnode_t **vpp);
+void smbfs_attr_cacheenter(vnode_t *vp, struct smbfattr *fap);
+int smbfs_attr_cachelookup(vnode_t *vp, struct vattr *va);
+void smbfs_attr_touchdir(struct smbnode *dnp);
+char *smbfs_name_alloc(const char *name, int nmlen);
+void smbfs_name_free(const char *name, int nmlen);
+uint32_t smbfs_hash(const char *name, int nmlen);
+uint32_t smbfs_hash3(uint32_t ival, const char *name, int nmlen);
+uint32_t smbfs_getino(struct smbnode *dnp, const char *name, int nmlen);
+int smb_check_table(struct vfs *vfsp, smbnode_t *srp);
+
+#define smbfs_attr_cacheremove(np) (np)->n_attrage = 0
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FS_SMBFS_NODE_H_ */
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_rwlock.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_rwlock.c
new file mode 100644
index 0000000000..d9f4ae70aa
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_rwlock.c
@@ -0,0 +1,252 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * A homegrown reader/writer lock implementation. It addresses
+ * two requirements not addressed by the system primitives. They
+ * are that the `enter" operation is optionally interruptible and
+ * that that they can be re`enter'ed by writers without deadlock.
+ *
+ * All of this was borrowed from NFS.
+ * See: uts/common/fs/nfs/nfs_subr.c
+ *
+ * XXX: Could we make this serve our needs instead?
+ * See: uts/common/os/rwstlock.c
+ * (and then use it for NFS too)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+
+#include <smbfs/smbfs.h>
+#include <smbfs/smbfs_node.h>
+#include <smbfs/smbfs_subr.h>
+
+
+/*
+ * Only can return non-zero if intr != 0.
+ */
+int
+smbfs_rw_enter_sig(smbfs_rwlock_t *l, krw_t rw, int intr)
+{
+
+ mutex_enter(&l->lock);
+
+ /*
+ * If this is a nested enter, then allow it. There
+ * must be as many exits as enters through.
+ */
+ if (l->owner == curthread) {
+ /* lock is held for writing by current thread */
+ ASSERT(rw == RW_READER || rw == RW_WRITER);
+ l->count--;
+ } else if (rw == RW_READER) {
+ /*
+ * While there is a writer active or writers waiting,
+ * then wait for them to finish up and move on. Then,
+ * increment the count to indicate that a reader is
+ * active.
+ */
+ while (l->count < 0 || l->waiters > 0) {
+ if (intr) {
+ klwp_t *lwp = ttolwp(curthread);
+
+ if (lwp != NULL)
+ lwp->lwp_nostop++;
+ if (!cv_wait_sig(&l->cv, &l->lock)) {
+ if (lwp != NULL)
+ lwp->lwp_nostop--;
+ mutex_exit(&l->lock);
+ return (EINTR);
+ }
+ if (lwp != NULL)
+ lwp->lwp_nostop--;
+ } else
+ cv_wait(&l->cv, &l->lock);
+ }
+ ASSERT(l->count < INT_MAX);
+#ifdef SMBDEBUG
+ if ((l->count % 10000) == 9999)
+ cmn_err(CE_WARN, "smbfs_rw_enter_sig: count %d on"
+ "rwlock @ %p\n", l->count, (void *)&l);
+#endif
+ l->count++;
+ } else {
+ ASSERT(rw == RW_WRITER);
+ /*
+ * While there are readers active or a writer
+ * active, then wait for all of the readers
+ * to finish or for the writer to finish.
+ * Then, set the owner field to curthread and
+ * decrement count to indicate that a writer
+ * is active.
+ */
+ while (l->count > 0 || l->owner != NULL) {
+ l->waiters++;
+ if (intr) {
+ klwp_t *lwp = ttolwp(curthread);
+
+ if (lwp != NULL)
+ lwp->lwp_nostop++;
+ if (!cv_wait_sig(&l->cv, &l->lock)) {
+ if (lwp != NULL)
+ lwp->lwp_nostop--;
+ l->waiters--;
+ cv_broadcast(&l->cv);
+ mutex_exit(&l->lock);
+ return (EINTR);
+ }
+ if (lwp != NULL)
+ lwp->lwp_nostop--;
+ } else
+ cv_wait(&l->cv, &l->lock);
+ l->waiters--;
+ }
+ l->owner = curthread;
+ l->count--;
+ }
+
+ mutex_exit(&l->lock);
+
+ return (0);
+}
+
+/*
+ * If the lock is available, obtain it and return non-zero. If there is
+ * already a conflicting lock, return 0 immediately.
+ */
+
+int
+smbfs_rw_tryenter(smbfs_rwlock_t *l, krw_t rw)
+{
+ mutex_enter(&l->lock);
+
+ /*
+ * If this is a nested enter, then allow it. There
+ * must be as many exits as enters through.
+ */
+ if (l->owner == curthread) {
+ /* lock is held for writing by current thread */
+ ASSERT(rw == RW_READER || rw == RW_WRITER);
+ l->count--;
+ } else if (rw == RW_READER) {
+ /*
+ * If there is a writer active or writers waiting, deny the
+ * lock. Otherwise, bump the count of readers.
+ */
+ if (l->count < 0 || l->waiters > 0) {
+ mutex_exit(&l->lock);
+ return (0);
+ }
+ l->count++;
+ } else {
+ ASSERT(rw == RW_WRITER);
+ /*
+ * If there are readers active or a writer active, deny the
+ * lock. Otherwise, set the owner field to curthread and
+ * decrement count to indicate that a writer is active.
+ */
+ if (l->count > 0 || l->owner != NULL) {
+ mutex_exit(&l->lock);
+ return (0);
+ }
+ l->owner = curthread;
+ l->count--;
+ }
+
+ mutex_exit(&l->lock);
+
+ return (1);
+}
+
+void
+smbfs_rw_exit(smbfs_rwlock_t *l)
+{
+
+ mutex_enter(&l->lock);
+ /*
+ * If this is releasing a writer lock, then increment count to
+ * indicate that there is one less writer active. If this was
+ * the last of possibly nested writer locks, then clear the owner
+ * field as well to indicate that there is no writer active
+ * and wakeup any possible waiting writers or readers.
+ *
+ * If releasing a reader lock, then just decrement count to
+ * indicate that there is one less reader active. If this was
+ * the last active reader and there are writer(s) waiting,
+ * then wake up the first.
+ */
+ if (l->owner != NULL) {
+ ASSERT(l->owner == curthread);
+ l->count++;
+ if (l->count == 0) {
+ l->owner = NULL;
+ cv_broadcast(&l->cv);
+ }
+ } else {
+ ASSERT(l->count > 0);
+ l->count--;
+ if (l->count == 0 && l->waiters > 0)
+ cv_broadcast(&l->cv);
+ }
+ mutex_exit(&l->lock);
+}
+
+int
+smbfs_rw_lock_held(smbfs_rwlock_t *l, krw_t rw)
+{
+
+ if (rw == RW_READER)
+ return (l->count > 0);
+ ASSERT(rw == RW_WRITER);
+ return (l->count < 0);
+}
+
+/* ARGSUSED */
+void
+smbfs_rw_init(smbfs_rwlock_t *l, char *name, krw_type_t type, void *arg)
+{
+
+ l->count = 0;
+ l->waiters = 0;
+ l->owner = NULL;
+ mutex_init(&l->lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&l->cv, NULL, CV_DEFAULT, NULL);
+}
+
+void
+smbfs_rw_destroy(smbfs_rwlock_t *l)
+{
+
+ mutex_destroy(&l->lock);
+ cv_destroy(&l->cv);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c
new file mode 100644
index 0000000000..789bfdfaff
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c
@@ -0,0 +1,3147 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smbfs_smb.c,v 1.73.38.1 2005/05/27 02:35:28 lindak Exp $
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/cmn_err.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#include <sys/utfconv.h>
+#else
+#include <netsmb/smb_osdep.h>
+#endif
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_rq.h>
+
+#include <smbfs/smbfs.h>
+#include <smbfs/smbfs_node.h>
+#include <smbfs/smbfs_subr.h>
+
+/*
+ * Local functions.
+ * Not static, to aid debugging.
+ */
+
+int smbfs_smb_qfileinfo(struct smbnode *np, struct smbfattr *fap,
+ struct smb_cred *scrp, short infolevel);
+int smbfs_smb_qpathinfo(struct smbnode *np, struct smbfattr *fap,
+ struct smb_cred *scrp, short infolevel);
+int smbfs_smb_query_info(struct smbnode *np, const char *name, int nmlen,
+ struct smbfattr *fap, struct smb_cred *scrp);
+
+int smbfs_smb_statfsLM1(struct smb_share *ssp,
+ statvfs64_t *sbp, struct smb_cred *scrp);
+int smbfs_smb_statfsLM2(struct smb_share *ssp,
+ statvfs64_t *sbp, struct smb_cred *scrp);
+
+int smbfs_smb_setftime1(struct smbnode *np, uint16_t fid,
+ struct timespec *mtime, struct timespec *atime,
+ struct smb_cred *scrp);
+int smbfs_smb_setfattrNT(struct smbnode *np, uint16_t fid,
+ uint32_t attr, struct timespec *mtime, struct timespec *atime,
+ struct smb_cred *scrp);
+
+int smbfs_smb_setpattr1(struct smbnode *np,
+ const char *name, int len, uint32_t attr,
+ struct timespec *mtime, struct smb_cred *scrp);
+int smbfs_smb_setpattr2(struct smbnode *np, uint32_t attr,
+ struct timespec *mtime, struct timespec *atime,
+ struct smb_cred *scrp);
+int smbfs_smb_setpattrNT(struct smbnode *np, uint32_t attr,
+ struct timespec *mtime, struct timespec *atime,
+ struct smb_cred *scrp);
+
+
+/*
+ * Todo: locking over-the-wire
+ */
+#ifdef APPLE
+
+static int
+smbfs_smb_lockandx(struct smbnode *np, int op, uint32_t pid,
+ offset_t start, uint64_t len, int largelock,
+ struct smb_cred *scrp, uint32_t timeout)
+{
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_rq rq, *rqp = &rq;
+ struct mbchain *mbp;
+ uint8_t ltype = 0;
+ int error;
+
+ /* Shared lock for n_fid use below. */
+ ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER));
+
+ if (op == SMB_LOCK_SHARED)
+ ltype |= SMB_LOCKING_ANDX_SHARED_LOCK;
+ if (largelock)
+ ltype |= SMB_LOCKING_ANDX_LARGE_FILES;
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_LOCKING_ANDX, scrp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_uint8(mbp, 0xff); /* secondary command */
+ mb_put_uint8(mbp, 0); /* MBZ */
+ mb_put_uint16le(mbp, 0);
+ mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM);
+ mb_put_uint8(mbp, ltype); /* locktype */
+ mb_put_uint8(mbp, 0); /* oplocklevel - 0 seems is NO_OPLOCK */
+ mb_put_uint32le(mbp, timeout); /* 0 nowait, -1 infinite wait */
+ mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 1 : 0);
+ mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 0 : 1);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint16le(mbp, pid);
+ if (!largelock) {
+ mb_put_uint32le(mbp, start);
+ mb_put_uint32le(mbp, len);
+ } else {
+ mb_put_uint16le(mbp, 0); /* pad */
+ mb_put_uint32le(mbp, start >> 32); /* OffsetHigh */
+ mb_put_uint32le(mbp, start & 0xffffffff); /* OffsetLow */
+ mb_put_uint32le(mbp, len >> 32); /* LengthHigh */
+ mb_put_uint32le(mbp, len & 0xffffffff); /* LengthLow */
+ }
+ smb_rq_bend(rqp);
+ /*
+ * Don't want to risk missing a successful
+ * unlock send or lock response, or we could
+ * lose track of an outstanding lock.
+ */
+ if (op == SMB_LOCK_RELEASE)
+ rqp->sr_flags |= SMBR_NOINTR_SEND;
+ else
+ rqp->sr_flags |= SMBR_NOINTR_RECV;
+
+ error = smb_rq_simple(rqp);
+ smb_rq_done(rqp);
+ return (error);
+}
+
+int
+smbfs_smb_lock(struct smbnode *np, int op, caddr_t id,
+ offset_t start, uint64_t len, int largelock,
+ struct smb_cred *scrp, uint32_t timeout)
+{
+ struct smb_share *ssp = np->n_mount->smi_share;
+
+ if (SMB_DIALECT(SSTOVC(ssp)) < SMB_DIALECT_LANMAN1_0)
+ /*
+ * TODO: use LOCK_BYTE_RANGE here.
+ */
+ return (EINVAL);
+ else
+ return (smbfs_smb_lockandx(np, op, (uint32_t)id, start, len,
+ largelock, scrp, timeout));
+}
+
+#endif /* APPLE */
+
+/*
+ * Helper for smbfs_getattr
+ * Something like nfs_getattr_otw
+ */
+int
+smbfs_smb_getfattr(
+ struct smbnode *np,
+ struct smbfattr *fap,
+ struct smb_cred *scrp)
+{
+ int error;
+
+ /*
+ * This lock is really only necessary for qfileinfo,
+ * but hopefully we use that most of the time.
+ * Lock may be writer (via open) or reader.
+ */
+ ASSERT(np->r_lkserlock.count != 0);
+
+ if (np->n_fidrefs)
+ error = smbfs_smb_qfileinfo(np, fap, scrp, 0);
+ else
+ error = smbfs_smb_qpathinfo(np, fap, scrp, 0);
+
+ if (error == EINVAL) {
+ /* fallback */
+ error = smbfs_smb_query_info(np, NULL, 0, fap, scrp);
+ }
+
+#if 0 /* Moved this part to caller. */
+ if (!error && fap->fa_mtime.tv_sec == 0)
+ smbfs_attr_touchdir(dnp);
+#endif
+
+ return (error);
+}
+
+
+/*
+ * Nearly identical to smbfs_smb_qfileinfo (below).
+ * Please keep them in sync.
+ */
+int
+smbfs_smb_qpathinfo(struct smbnode *np, struct smbfattr *fap,
+ struct smb_cred *scrp, short infolevel)
+{
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ struct smb_t2rq *t2p;
+ int error, svtz, timesok = 1;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ uint16_t date, time, wattr;
+ uint64_t llongint, lsize;
+ uint32_t size, dattr;
+
+top:
+ error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_PATH_INFORMATION,
+ scrp, &t2p);
+ if (error)
+ return (error);
+ mbp = &t2p->t2_tparam;
+ mb_init(mbp);
+ if (!infolevel) {
+ if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12)
+ infolevel = SMB_QFILEINFO_STANDARD;
+ else
+ infolevel = SMB_QFILEINFO_ALL_INFO;
+ }
+ mb_put_uint16le(mbp, infolevel);
+ mb_put_uint32le(mbp, 0);
+ /* mb_put_uint8(mbp, SMB_DT_ASCII); specs are wrong */
+ error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\');
+ if (error) {
+ smb_t2_done(t2p);
+ return (error);
+ }
+ t2p->t2_maxpcount = 2;
+ t2p->t2_maxdcount = vcp->vc_txmax;
+ error = smb_t2_request(t2p);
+ if (error) {
+ smb_t2_done(t2p);
+ /* Invalid info level? Try fallback. */
+ if (error == EINVAL &&
+ infolevel == SMB_QFILEINFO_ALL_INFO) {
+ infolevel = SMB_QFILEINFO_STANDARD;
+ goto top;
+ }
+ return (error);
+ }
+ mdp = &t2p->t2_rdata;
+ svtz = vcp->vc_sopt.sv_tz;
+ switch (infolevel) {
+ case SMB_QFILEINFO_STANDARD:
+ timesok = 0;
+ md_get_uint16le(mdp, NULL);
+ md_get_uint16le(mdp, NULL); /* creation time */
+ md_get_uint16le(mdp, &date);
+ md_get_uint16le(mdp, &time); /* access time */
+ if (date || time) {
+ timesok++;
+ smb_dos2unixtime(date, time, 0, svtz, &fap->fa_atime);
+ }
+ md_get_uint16le(mdp, &date);
+ md_get_uint16le(mdp, &time); /* modify time */
+ if (date || time) {
+ timesok++;
+ smb_dos2unixtime(date, time, 0, svtz, &fap->fa_mtime);
+ }
+ md_get_uint32le(mdp, &size);
+ fap->fa_size = size;
+ md_get_uint32(mdp, NULL); /* allocation size */
+ md_get_uint16le(mdp, &wattr);
+ fap->fa_attr = wattr;
+ break;
+ case SMB_QFILEINFO_ALL_INFO:
+ timesok = 0;
+ /* creation time (discard) */
+ md_get_uint64(mdp, NULL);
+ /* last access time */
+ md_get_uint64le(mdp, &llongint);
+ if (llongint) {
+ timesok++;
+ smb_time_NT2local(llongint, svtz, &fap->fa_atime);
+ }
+ /* last write time */
+ md_get_uint64le(mdp, &llongint);
+ if (llongint) {
+ timesok++;
+ smb_time_NT2local(llongint, svtz, &fap->fa_mtime);
+ }
+ /* last change time */
+ md_get_uint64le(mdp, &llongint);
+ if (llongint) {
+ timesok++;
+ smb_time_NT2local(llongint, svtz, &fap->fa_ctime);
+ }
+ /* attributes */
+ md_get_uint32le(mdp, &dattr);
+ fap->fa_attr = dattr;
+ /*
+ * 4-Byte alignment - discard
+ * Specs doesn't talk about this.
+ */
+ md_get_uint32le(mdp, NULL);
+ /* allocation size (discard) */
+ md_get_uint64le(mdp, NULL);
+ /* File size */
+ md_get_uint64le(mdp, &lsize);
+ fap->fa_size = lsize;
+ break;
+ default:
+ SMBVDEBUG("unexpected info level %d\n", infolevel);
+ error = EINVAL;
+ }
+ smb_t2_done(t2p);
+ /*
+ * if all times are zero (observed with FAT on NT4SP6)
+ * then fall back to older info level
+ */
+ if (!timesok) {
+ if (infolevel == SMB_QFILEINFO_ALL_INFO) {
+ infolevel = SMB_QFILEINFO_STANDARD;
+ goto top;
+ }
+ error = EINVAL;
+ }
+ return (error);
+}
+
+/*
+ * Nearly identical to smbfs_smb_qpathinfo (above).
+ * Please keep them in sync.
+ */
+int
+smbfs_smb_qfileinfo(struct smbnode *np, struct smbfattr *fap,
+ struct smb_cred *scrp, short infolevel)
+{
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ struct smb_t2rq *t2p;
+ int error, svtz, timesok = 1;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ uint16_t date, time, wattr;
+ uint64_t llongint, lsize;
+ uint32_t size, dattr;
+
+ /*
+ * Shared lock for n_fid use below.
+ * See smbfs_smb_getfattr()
+ */
+ ASSERT(np->r_lkserlock.count != 0);
+
+ if (np->n_fid == SMB_FID_UNUSED)
+ return (EBADF);
+
+top:
+ error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_FILE_INFORMATION,
+ scrp, &t2p);
+ if (error)
+ return (error);
+ mbp = &t2p->t2_tparam;
+ mb_init(mbp);
+ if (!infolevel) {
+ if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12)
+ infolevel = SMB_QFILEINFO_STANDARD;
+ else
+ infolevel = SMB_QFILEINFO_ALL_INFO;
+ }
+ mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM);
+ mb_put_uint16le(mbp, infolevel);
+ t2p->t2_maxpcount = 2;
+ t2p->t2_maxdcount = vcp->vc_txmax;
+ error = smb_t2_request(t2p);
+ if (error) {
+ smb_t2_done(t2p);
+ /* Invalid info level? Try fallback. */
+ if (error == EINVAL &&
+ infolevel == SMB_QFILEINFO_ALL_INFO) {
+ infolevel = SMB_QFILEINFO_STANDARD;
+ goto top;
+ }
+ return (error);
+ }
+ mdp = &t2p->t2_rdata;
+ svtz = vcp->vc_sopt.sv_tz;
+ switch (infolevel) {
+ case SMB_QFILEINFO_STANDARD:
+ timesok = 0;
+ md_get_uint16le(mdp, NULL);
+ md_get_uint16le(mdp, NULL); /* creation time */
+ md_get_uint16le(mdp, &date);
+ md_get_uint16le(mdp, &time); /* access time */
+ if (date || time) {
+ timesok++;
+ smb_dos2unixtime(date, time, 0, svtz, &fap->fa_atime);
+ }
+ md_get_uint16le(mdp, &date);
+ md_get_uint16le(mdp, &time); /* modify time */
+ if (date || time) {
+ timesok++;
+ smb_dos2unixtime(date, time, 0, svtz, &fap->fa_mtime);
+ }
+ md_get_uint32le(mdp, &size);
+ fap->fa_size = size;
+ md_get_uint32(mdp, NULL); /* allocation size */
+ md_get_uint16le(mdp, &wattr);
+ fap->fa_attr = wattr;
+ break;
+ case SMB_QFILEINFO_ALL_INFO:
+ timesok = 0;
+ /* creation time (discard) */
+ md_get_uint64(mdp, NULL);
+ /* last access time */
+ md_get_uint64le(mdp, &llongint);
+ if (llongint) {
+ timesok++;
+ smb_time_NT2local(llongint, svtz, &fap->fa_atime);
+ }
+ /* last write time */
+ md_get_uint64le(mdp, &llongint);
+ if (llongint) {
+ timesok++;
+ smb_time_NT2local(llongint, svtz, &fap->fa_mtime);
+ }
+ /* last change time */
+ md_get_uint64le(mdp, &llongint);
+ if (llongint) {
+ timesok++;
+ smb_time_NT2local(llongint, svtz, &fap->fa_ctime);
+ }
+ /* attributes */
+ md_get_uint32le(mdp, &dattr);
+ fap->fa_attr = dattr;
+ /*
+ * 4-Byte alignment - discard
+ * Specs doesn't talk about this.
+ */
+ md_get_uint32le(mdp, NULL);
+ /* allocation size (discard) */
+ md_get_uint64le(mdp, NULL);
+ /* File size */
+ md_get_uint64le(mdp, &lsize);
+ fap->fa_size = lsize;
+ break;
+ default:
+ SMBVDEBUG("unexpected info level %d\n", infolevel);
+ error = EINVAL;
+ }
+ smb_t2_done(t2p);
+ /*
+ * if all times are zero (observed with FAT on NT4SP6)
+ * then fall back to older info level
+ */
+ if (!timesok) {
+ if (infolevel == SMB_QFILEINFO_ALL_INFO) {
+ infolevel = SMB_QFILEINFO_STANDARD;
+ goto top;
+ }
+ error = EINVAL;
+ }
+ return (error);
+}
+
+/*
+ * Support functions for _qstreaminfo
+ * Todo: show NT file streams as
+ * Solaris named attributes.
+ */
+#ifdef APPLE
+
+static char *
+sfm2xattr(char *sfm)
+{
+ if (!strncasecmp(sfm, SFM_RESOURCEFORK_NAME,
+ sizeof (SFM_RESOURCEFORK_NAME)))
+ return (XATTR_RESOURCEFORK_NAME);
+ if (!strncasecmp(sfm, SFM_FINDERINFO_NAME,
+ sizeof (SFM_FINDERINFO_NAME)))
+ return (XATTR_FINDERINFO_NAME);
+ return (NULL);
+}
+
+static int
+smbfs_smb_undollardata(struct smbnode *np, struct smbfs_fctx *ctx)
+{
+ char *cp;
+ int len = strlen(SMB_DATASTREAM);
+
+ if (!ctx->f_name) /* sanity check */
+ goto bad;
+ if (ctx->f_nmlen < len + 1) /* "::$DATA" at a minimum */
+ goto bad;
+ if (*ctx->f_name != ':') /* leading colon - "always" */
+ goto bad;
+ cp = &ctx->f_name[ctx->f_nmlen - len]; /* point to 2nd colon */
+ if (bcmp(cp, SMB_DATASTREAM, len))
+ goto bad;
+ if (ctx->f_nmlen == len + 1) /* merely the data fork? */
+ return (0); /* skip it */
+ /*
+ * XXX here we should be calling KPI to validate the stream name
+ */
+ if (ctx->f_nmlen >= 18 &&
+ !(bcmp(ctx->f_name, ":com.apple.system.", 18) == 0))
+ return (0); /* skip protected system attrs */
+ if (ctx->f_nmlen - len > XATTR_MAXNAMELEN + 1)
+ goto bad; /* mustnt return more than 128 bytes */
+ /*
+ * Un-count a colon and the $DATA, then the
+ * 2nd colon is replaced by a terminating null.
+ */
+ ctx->f_nmlen -= len;
+ *cp = '\0';
+ return (1);
+bad:
+ SMBSDEBUG("file \"%.*s\" has bad stream \"%.*s\"\n",
+ np->n_nmlen, np->n_name, ctx->f_nmlen, ctx->f_name);
+ return (0); /* skip it */
+}
+
+PRIVSYM int
+smbfs_smb_qstreaminfo(struct smbnode *np, struct smb_cred *scrp,
+ uio_t uio, size_t *sizep)
+{
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ struct smb_t2rq *t2p;
+ int error;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ uint32_t next, nlen, used;
+ struct smbfs_fctx ctx;
+
+ *sizep = 0;
+ ctx.f_ssp = ssp;
+ ctx.f_name = NULL;
+
+ error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_PATH_INFORMATION,
+ scrp, &t2p);
+ if (error)
+ return (error);
+ mbp = &t2p->t2_tparam;
+ mb_init(mbp);
+ /*
+ * SMB_QFILEINFO_STREAM_INFORMATION is an option to consider
+ * here. Samba declined to support the older info level with
+ * a comment claiming doing so caused a BSOD.
+ */
+ mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFO);
+ mb_put_uint32le(mbp, 0);
+ /* mb_put_uint8(mbp, SMB_DT_ASCII); specs are wrong */
+ error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\');
+ if (error)
+ goto out;
+ t2p->t2_maxpcount = 2;
+ t2p->t2_maxdcount = vcp->vc_txmax;
+ error = smb_t2_request(t2p);
+ if (error) {
+ if (smb_t2_err(t2p) == NT_STATUS_INVALID_PARAMETER)
+ error = ENOTSUP;
+ goto out;
+ }
+ mdp = &t2p->t2_rdata;
+ /*
+ * On a directory Windows is likely to return a zero data count.
+ * Check for that now to avoid EBADRPC from md_get_uint32le
+ */
+ if (mdp->md_cur == NULL)
+ goto out;
+ do {
+ if ((error = md_get_uint32le(mdp, &next)))
+ goto out;
+ if ((error = md_get_uint32le(mdp, &nlen))) /* name length */
+ goto out;
+ if ((error = md_get_uint64le(mdp, NULL))) /* stream size */
+ goto out;
+ if ((error = md_get_uint64le(mdp, NULL))) /* allocated size */
+ goto out;
+ /*
+ * Sanity check to limit DoS or buffer overrun attempts.
+ * The arbitrary 16384 is sufficient for all legit packets.
+ */
+ if (nlen > 16384) {
+ SMBVDEBUG("huge name length in packet!\n");
+ error = EBADRPC;
+ goto out;
+ }
+ ctx.f_name = kmem_zalloc(nlen, KM_SLEEP);
+ ctx.f_namesz = nlen;
+ if ((error = md_get_mem(mdp, ctx.f_name, nlen, MB_MSYSTEM)))
+ goto out;
+ /*
+ * skip pad bytes and/or tail of overlong name
+ */
+ used = 4 + 4 + 8 + 8 + nlen;
+ if (next && next > used) {
+ if (next - used > 16384) {
+ SMBVDEBUG("huge offset in packet!\n");
+ error = EBADRPC;
+ goto out;
+ }
+ md_get_mem(mdp, NULL, next - used, MB_MSYSTEM);
+ }
+ /* ignore a trailing null, not that we expect them */
+ if (SMB_UNICODE_STRINGS(vcp)) {
+ if (nlen > 1 && !ctx.f_name[nlen - 1] &&
+ !ctx.f_name[nlen - 2])
+ nlen -= 2;
+ } else {
+ if (nlen && !ctx.f_name[nlen - 1])
+ nlen -= 1;
+ }
+ ctx.f_nmlen = nlen;
+ smbfs_fname_tolocal(&ctx); /* converts from UCS2LE */
+ /*
+ * We should now have a name in the form
+ * : <foo> :$DATA
+ * Where <foo> is UTF-8 w/o null termination
+ * If it isn't in that form we want to LOG it and skip it.
+ * Note we want to skip w/o logging the "data fork" entry,
+ * which is simply ::$DATA
+ * Otherwise we want to uiomove out <foo> with a null added.
+ */
+ if (smbfs_smb_undollardata(np, &ctx)) {
+ char *s;
+
+ /* the "+ 1" skips over the leading colon */
+ s = sfm2xattr(ctx.f_name + 1);
+#ifndef DUAL_EAS /* XXX */
+ /*
+ * In Tiger Carbon still accesses dot-underscore files directly, so...
+ * For Tiger we preserve the SFM/Thursby AFP_* stream names rather
+ * than mapping them to com.apple.*. This means our copy engines
+ * will preserve SFM/Thursby resource-fork and finder-info.
+ */
+ s = NULL;
+#endif
+ if (s)
+ ctx.f_nmlen = strlen(s) + 1;
+ else
+ s = ctx.f_name + 1;
+ if (uio)
+ uiomove(s, ctx.f_nmlen, uio);
+ else
+ *sizep += ctx.f_nmlen;
+ }
+ kmem_free(ctx.f_name, ctx.f_namesz);
+ ctx.f_name = NULL;
+ } while (next && !error);
+out:
+ if (ctx.f_name)
+ kmem_free(ctx.f_name, ctx.f_namesz);
+ smb_t2_done(t2p);
+ return (error);
+}
+
+#endif /* APPLE */
+
+int
+smbfs_smb_qfsattr(struct smb_share *ssp, uint32_t *attrp,
+ struct smb_cred *scrp)
+{
+ struct smb_t2rq *t2p;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ uint32_t nlen;
+ int error;
+ char *fs_name; /* will malloc whatever the size is */
+ struct smbfs_fctx ctx;
+
+ error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_FS_INFORMATION,
+ scrp, &t2p);
+ if (error)
+ return (error);
+ mbp = &t2p->t2_tparam;
+ mb_init(mbp);
+ mb_put_uint16le(mbp, SMB_QFS_ATTRIBUTE_INFO);
+ t2p->t2_maxpcount = 4;
+ t2p->t2_maxdcount = 4 * 3 + 512;
+ error = smb_t2_request(t2p);
+ if (error) {
+ smb_t2_done(t2p);
+ return (error);
+ }
+ mdp = &t2p->t2_rdata;
+ md_get_uint32le(mdp, attrp);
+ md_get_uint32le(mdp, &ssp->ss_maxfilenamelen);
+ md_get_uint32le(mdp, &nlen); /* fs name length */
+ if (ssp->ss_fsname == NULL && nlen) {
+ ctx.f_ssp = ssp;
+ ctx.f_name = kmem_alloc(nlen, KM_SLEEP);
+ ctx.f_namesz = nlen;
+ md_get_mem(mdp, ctx.f_name, nlen, MB_MSYSTEM);
+ ctx.f_nmlen = nlen;
+ smbfs_fname_tolocal(&ctx);
+ fs_name = kmem_alloc(ctx.f_nmlen+1, KM_SLEEP);
+ bcopy(ctx.f_name, fs_name, ctx.f_nmlen);
+ fs_name[ctx.f_nmlen] = '\0';
+ ssp->ss_fsname = fs_name;
+ kmem_free(ctx.f_name, ctx.f_namesz);
+ /*
+ * If fs_name isn't NTFS they probably require resume keys.
+ * This is another example of the client trying to fix a server
+ * bug. This code uses the logic created by PR-3983209. See
+ * long block comment in smbfs_smb_findnextLM2.
+ */
+ if (strcmp(fs_name, "NTFS")) {
+ SMB_SS_LOCK(ssp);
+ ssp->ss_flags |= SMBS_RESUMEKEYS;
+ SMB_SS_UNLOCK(ssp);
+ }
+ SMBVDEBUG("(fyi) share '%s', attr 0x%x, maxfilename %d\n",
+ ssp->ss_fsname, *attrp, ssp->ss_maxfilenamelen);
+ }
+ smb_t2_done(t2p);
+ return (0);
+}
+
+int
+smbfs_smb_statfs(struct smb_share *ssp, statvfs64_t *sbp,
+ struct smb_cred *scp)
+{
+ int error;
+
+ if (SMB_DIALECT(SSTOVC(ssp)) >= SMB_DIALECT_LANMAN2_0)
+ error = smbfs_smb_statfsLM2(ssp, sbp, scp);
+ else
+ error = smbfs_smb_statfsLM1(ssp, sbp, scp);
+
+ return (error);
+}
+
+int
+smbfs_smb_statfsLM2(struct smb_share *ssp, statvfs64_t *sbp,
+ struct smb_cred *scrp)
+{
+ struct smb_t2rq *t2p;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ uint16_t bsize;
+ uint32_t units, bpu, funits;
+ uint64_t s, t, f;
+ int error;
+
+ error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_FS_INFORMATION,
+ scrp, &t2p);
+ if (error)
+ return (error);
+ mbp = &t2p->t2_tparam;
+ mb_init(mbp);
+ mb_put_uint16le(mbp, SMB_QFS_ALLOCATION);
+ t2p->t2_maxpcount = 4;
+ t2p->t2_maxdcount = 4 * 4 + 2;
+ error = smb_t2_request(t2p);
+ if (error) {
+ smb_t2_done(t2p);
+ return (error);
+ }
+ mdp = &t2p->t2_rdata;
+ md_get_uint32(mdp, NULL); /* fs id */
+ md_get_uint32le(mdp, &bpu);
+ md_get_uint32le(mdp, &units);
+ md_get_uint32le(mdp, &funits);
+ md_get_uint16le(mdp, &bsize);
+ s = bsize;
+ s *= bpu;
+ t = units;
+ f = funits;
+ /*
+ * Don't allow over-large blocksizes as they determine
+ * Finder List-view size granularities. On the other
+ * hand, we mustn't let the block count overflow the
+ * 31 bits available.
+ */
+ while (s > 16 * 1024) {
+ if (t > LONG_MAX)
+ break;
+ s /= 2;
+ t *= 2;
+ f *= 2;
+ }
+ while (t > LONG_MAX) {
+ t /= 2;
+ f /= 2;
+ s *= 2;
+ }
+ sbp->f_bsize = (ulong_t)s; /* file system block size */
+ sbp->f_blocks = t; /* total data blocks in file system */
+ sbp->f_bfree = f; /* free blocks in fs */
+ sbp->f_bavail = f; /* free blocks avail to non-superuser */
+ sbp->f_files = (-1); /* total file nodes in file system */
+ sbp->f_ffree = (-1); /* free file nodes in fs */
+ smb_t2_done(t2p);
+ return (0);
+}
+
+int
+smbfs_smb_statfsLM1(struct smb_share *ssp, statvfs64_t *sbp,
+ struct smb_cred *scrp)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct mdchain *mdp;
+ uint16_t units, bpu, bsize, funits;
+ uint64_t s, t, f;
+ int error;
+
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_QUERY_INFORMATION_DISK,
+ scrp);
+ if (error)
+ return (error);
+ smb_rq_wstart(rqp);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ if (error) {
+ smb_rq_done(rqp);
+ return (error);
+ }
+ smb_rq_getreply(rqp, &mdp);
+ md_get_uint16le(mdp, &units);
+ md_get_uint16le(mdp, &bpu);
+ md_get_uint16le(mdp, &bsize);
+ md_get_uint16le(mdp, &funits);
+ s = bsize;
+ s *= bpu;
+ t = units;
+ f = funits;
+ /*
+ * Don't allow over-large blocksizes as they determine
+ * Finder List-view size granularities. On the other
+ * hand, we mustn't let the block count overflow the
+ * 31 bits available.
+ */
+ while (s > 16 * 1024) {
+ if (t > LONG_MAX)
+ break;
+ s /= 2;
+ t *= 2;
+ f *= 2;
+ }
+ while (t > LONG_MAX) {
+ t /= 2;
+ f /= 2;
+ s *= 2;
+ }
+ sbp->f_bsize = (ulong_t)s; /* file system block size */
+ sbp->f_blocks = t; /* total data blocks in file system */
+ sbp->f_bfree = f; /* free blocks in fs */
+ sbp->f_bavail = f; /* free blocks avail to non-superuser */
+ sbp->f_files = (-1); /* total file nodes in file system */
+ sbp->f_ffree = (-1); /* free file nodes in fs */
+ smb_rq_done(rqp);
+ return (0);
+}
+
+int
+smbfs_smb_seteof(struct smb_share *ssp, uint16_t fid, uint64_t newsize,
+ struct smb_cred *scrp)
+{
+ struct smb_t2rq *t2p;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ struct mbchain *mbp;
+ int error;
+
+ error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION,
+ scrp, &t2p);
+ if (error)
+ return (error);
+ mbp = &t2p->t2_tparam;
+ mb_init(mbp);
+ mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM);
+ if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU)
+ mb_put_uint16le(mbp, SMB_SFILEINFO_END_OF_FILE_INFORMATION);
+ else
+ mb_put_uint16le(mbp, SMB_SFILEINFO_END_OF_FILE_INFO);
+ mb_put_uint32le(mbp, 0); /* XXX should be 16 not 32(?) */
+ mbp = &t2p->t2_tdata;
+ mb_init(mbp);
+ mb_put_uint64le(mbp, newsize);
+ mb_put_uint32le(mbp, 0); /* padding */
+ mb_put_uint16le(mbp, 0);
+ t2p->t2_maxpcount = 2;
+ t2p->t2_maxdcount = 0;
+ error = smb_t2_request(t2p);
+ smb_t2_done(t2p);
+ return (error);
+}
+
+/*ARGSUSED*/
+int
+smbfs_smb_t2rename(struct smbnode *np, struct smbnode *tdnp,
+ const char *tname, int tnmlen, struct smb_cred *scrp, int overwrite)
+{
+ struct smb_t2rq *t2p;
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ struct mbchain *mbp;
+ int32_t *ucslenp;
+ int error, cerror;
+ uint16_t fid = 0;
+
+ /* Shared lock for n_fid use below. */
+ ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER));
+
+ if (!(vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU))
+ return (ENOTSUP);
+ error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION,
+ scrp, &t2p);
+ if (error)
+ return (error);
+ if (tdnp) {
+ error = smbfs_smb_tmpopen(tdnp, SA_RIGHT_FILE_READ_DATA, scrp,
+ &fid);
+ if (error)
+ goto exit;
+ }
+ mbp = &t2p->t2_tparam;
+ mb_init(mbp);
+ mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM);
+ mb_put_uint16le(mbp, SMB_SFILEINFO_RENAME_INFORMATION);
+ mb_put_uint16le(mbp, 0); /* reserved, nowadays */
+ mbp = &t2p->t2_tdata;
+ mb_init(mbp);
+ mb_put_uint32le(mbp, overwrite);
+ mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); /* base for tname */
+ mb_put_uint16le(mbp, 0); /* part of a 32bit fid? */
+ ucslenp = (int32_t *)mb_reserve(mbp, sizeof (int32_t));
+ mbp->mb_count = 0;
+ error = smb_put_dstring(mbp, vcp, tname, SMB_CS_NONE);
+ if (error)
+ goto exit;
+ mbp->mb_count--; /* don't count the null */
+ *ucslenp = htolel(mbp->mb_count);
+ t2p->t2_maxpcount = 2;
+ t2p->t2_maxdcount = 0;
+ error = smb_t2_request(t2p);
+exit:
+ if (fid) {
+ cerror = smbfs_smb_tmpclose(tdnp, fid, scrp);
+ if (cerror)
+ SMBERROR("error %d closing fid %d\n", cerror, fid);
+ }
+ smb_t2_done(t2p);
+ return (error);
+}
+
+int
+smbfs_smb_flush(struct smbnode *np, struct smb_cred *scrp)
+{
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_rq rq, *rqp = &rq;
+ struct mbchain *mbp;
+ int error;
+
+ /* Shared lock for n_fid use below. */
+ ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER));
+
+ if (!(np->n_flag & NFLUSHWIRE))
+ return (0);
+ if (np->r_count == 0)
+ return (0); /* not open */
+ if (np->r_vnode->v_type != VREG)
+ return (0); /* not a file */
+
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_FLUSH, scrp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ smb_rq_done(rqp);
+ if (!error) {
+ mutex_enter(&np->r_statelock);
+ np->n_flag &= ~NFLUSHWIRE;
+ mutex_exit(&np->r_statelock);
+ }
+ return (error);
+}
+
+int
+smbfs_smb_setfsize(struct smbnode *np, uint16_t fid, uint64_t newsize,
+ struct smb_cred *scrp)
+{
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_rq rq, *rqp = &rq;
+ struct mbchain *mbp;
+ int error;
+
+ /*
+ * This call knows about 64-bit offsets.
+ */
+ error = smbfs_smb_seteof(ssp, fid, newsize, scrp);
+ if (!error) {
+ mutex_enter(&np->r_statelock);
+ np->n_flag |= (NFLUSHWIRE | NATTRCHANGED);
+ mutex_exit(&np->r_statelock);
+ return (0);
+ }
+
+ /*
+ * If we have SMB_CAP_LARGE_FILES, the above
+ * should have worked. XXX: Don't fallback?
+ * XXX: Or fallback on specific errors?
+ */
+ if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES) {
+ SMBVDEBUG("Have CAP_LARGE but _seteof error=%d\n", error);
+ }
+
+ /*
+ * OK, so fallback to SMB_COM_WRITE, but note:
+ * it only supports 32-bit file offsets.
+ */
+ if (newsize > UINT32_MAX)
+ return (EFBIG);
+
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_WRITE, scrp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM);
+ mb_put_uint16le(mbp, 0);
+ mb_put_uint32le(mbp, newsize);
+ mb_put_uint16le(mbp, 0);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_DATA);
+ mb_put_uint16le(mbp, 0);
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ smb_rq_done(rqp);
+ mutex_enter(&np->r_statelock);
+ np->n_flag |= (NFLUSHWIRE | NATTRCHANGED);
+ mutex_exit(&np->r_statelock);
+ return (error);
+}
+
+int
+smbfs_smb_query_info(struct smbnode *np, const char *name, int nmlen,
+ struct smbfattr *fap, struct smb_cred *scrp)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ uint8_t wc;
+ int error;
+ uint16_t wattr;
+ uint32_t longint;
+
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_QUERY_INFORMATION, scrp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ do {
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), np,
+ name, &nmlen, '\\');
+ if (error)
+ break;
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ if (error)
+ break;
+ smb_rq_getreply(rqp, &mdp);
+ if (md_get_uint8(mdp, &wc) != 0 || wc != 10) {
+ error = EBADRPC;
+ break;
+ }
+ md_get_uint16le(mdp, &wattr);
+ fap->fa_attr = wattr;
+ /*
+ * Be careful using the time returned here, as
+ * with FAT on NT4SP6, at least, the time returned is low
+ * 32 bits of 100s of nanoseconds (since 1601) so it rolls
+ * over about every seven minutes!
+ */
+ md_get_uint32le(mdp, &longint); /* specs: secs since 1970 */
+ if (longint) /* avoid bogus zero returns */
+ smb_time_server2local(longint,
+ SSTOVC(ssp)->vc_sopt.sv_tz, &fap->fa_mtime);
+ md_get_uint32le(mdp, &longint);
+ fap->fa_size = longint;
+ /*LINTED*/
+ } while (0);
+ smb_rq_done(rqp);
+ return (error);
+}
+
+int
+smbfs_smb_setpattr(struct smbnode *np, uint32_t attr,
+ struct timespec *mtime, struct timespec *atime,
+ struct smb_cred *scrp)
+{
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ int error;
+
+ /*
+ * This is the logic that was in smbfs_vnops.c
+ */
+ if ((vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) &&
+ (vcp->vc_flags & SMBV_NT4) == 0) {
+ /*
+ * NT4 doesn't understand "NT" style SMBs;
+ * for NT4 we use the old SET_PATH_INFO
+ * XXX Actually, I think the issue is that
+ * NT requires an open handle for this.
+ */
+ error = smbfs_smb_setpattrNT(np,
+ attr, mtime, atime, scrp);
+ if (error != EBADRPC)
+ return (error);
+
+ /* NT4 response, remember */
+ SMB_VC_LOCK(vcp);
+ vcp->vc_flags |= SMBV_NT4;
+ SMB_VC_UNLOCK(vcp);
+ }
+
+ if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) {
+ error = smbfs_smb_setpattr2(np,
+ attr, mtime, atime, scrp);
+ } else {
+ error = smbfs_smb_setpattr1(np, NULL, 0,
+ attr, mtime, scrp);
+ }
+
+ return (error);
+}
+
+/*
+ * Set DOS file attributes. mtime should be NULL for dialects above lm10
+ */
+int
+smbfs_smb_setpattr1(struct smbnode *np, const char *name, int len,
+ uint32_t attr, struct timespec *mtime,
+ struct smb_cred *scrp)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct mbchain *mbp;
+ long time;
+ int error, svtz;
+
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION, scrp);
+ if (error)
+ return (error);
+ svtz = SSTOVC(ssp)->vc_sopt.sv_tz;
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, (uint16_t)attr);
+ if (mtime) {
+ smb_time_local2server(mtime, svtz, &time);
+ } else
+ time = 0;
+ mb_put_uint32le(mbp, time); /* mtime */
+ mb_put_mem(mbp, NULL, 5 * 2, MB_MZERO);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ do {
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), np, name, &len, '\\');
+ if (error)
+ break;
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ if (SMB_UNICODE_STRINGS(SSTOVC(ssp))) {
+ mb_put_padbyte(mbp);
+ mb_put_uint8(mbp, 0); /* 1st byte NULL Unicode char */
+ }
+ mb_put_uint8(mbp, 0);
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ if (error)
+ break;
+ /*LINTED*/
+ } while (0);
+ smb_rq_done(rqp);
+ return (error);
+}
+
+int
+smbfs_smb_hideit(struct smbnode *np, const char *name, int len,
+ struct smb_cred *scrp)
+{
+ struct smbfattr fa;
+ int error;
+ uint32_t attr;
+
+ error = smbfs_smb_query_info(np, name, len, &fa, scrp);
+ attr = fa.fa_attr;
+ if (!error && !(attr & SMB_FA_HIDDEN)) {
+ attr |= SMB_FA_HIDDEN;
+ error = smbfs_smb_setpattr1(np, name, len, attr, NULL, scrp);
+ }
+ return (error);
+}
+
+
+int
+smbfs_smb_unhideit(struct smbnode *np, const char *name, int len,
+ struct smb_cred *scrp)
+{
+ struct smbfattr fa;
+ uint32_t attr;
+ int error;
+
+ error = smbfs_smb_query_info(np, name, len, &fa, scrp);
+ attr = fa.fa_attr;
+ if (!error && (attr & SMB_FA_HIDDEN)) {
+ attr &= ~SMB_FA_HIDDEN;
+ error = smbfs_smb_setpattr1(np, name, len, attr, NULL, scrp);
+ }
+ return (error);
+}
+
+/*
+ * Note, win95 doesn't support this call.
+ */
+int
+smbfs_smb_setpattr2(struct smbnode *np, uint32_t attr,
+ struct timespec *mtime, struct timespec *atime,
+ struct smb_cred *scrp)
+{
+ struct smb_t2rq *t2p;
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ struct mbchain *mbp;
+ uint16_t date, time;
+ int error, tzoff;
+
+ error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION,
+ scrp, &t2p);
+ if (error)
+ return (error);
+ mbp = &t2p->t2_tparam;
+ mb_init(mbp);
+ mb_put_uint16le(mbp, SMB_SFILEINFO_STANDARD);
+ mb_put_uint32le(mbp, 0); /* MBZ */
+ /* mb_put_uint8(mbp, SMB_DT_ASCII); specs incorrect */
+ error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\');
+ if (error) {
+ smb_t2_done(t2p);
+ return (error);
+ }
+ tzoff = vcp->vc_sopt.sv_tz;
+ mbp = &t2p->t2_tdata;
+ mb_init(mbp);
+ mb_put_uint32le(mbp, 0); /* creation time */
+ if (atime)
+ smb_time_unix2dos(atime, tzoff, &date, &time, NULL);
+ else
+ time = date = 0;
+ mb_put_uint16le(mbp, date);
+ mb_put_uint16le(mbp, time);
+ if (mtime)
+ smb_time_unix2dos(mtime, tzoff, &date, &time, NULL);
+ else
+ time = date = 0;
+ mb_put_uint16le(mbp, date);
+ mb_put_uint16le(mbp, time);
+ mb_put_uint32le(mbp, 0); /* file size */
+ mb_put_uint32le(mbp, 0); /* allocation unit size */
+ mb_put_uint16le(mbp, attr); /* DOS attr */
+ mb_put_uint32le(mbp, 0); /* EA size */
+ t2p->t2_maxpcount = 5 * 2;
+ t2p->t2_maxdcount = vcp->vc_txmax;
+ error = smb_t2_request(t2p);
+ smb_t2_done(t2p);
+ return (error);
+}
+
+/*
+ * *BASIC_INFO works with Samba, but Win2K servers say it is an
+ * invalid information level on a SET_PATH_INFO. Note Win2K does
+ * support *BASIC_INFO on a SET_FILE_INFO, and they support the
+ * equivalent *BASIC_INFORMATION on SET_PATH_INFO. Go figure.
+ */
+int
+smbfs_smb_setpattrNT(struct smbnode *np, uint32_t attr,
+ struct timespec *mtime, struct timespec *atime,
+ struct smb_cred *scrp)
+{
+ struct smb_t2rq *t2p;
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ struct mbchain *mbp;
+ uint64_t tm;
+ int error, tzoff;
+ /* 64 bit value for Jan 1 1980 */
+ PRIVSYM uint64_t DIFF1980TO1601 = 11960035200ULL*10000000ULL;
+
+ error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION,
+ scrp, &t2p);
+ if (error)
+ return (error);
+ mbp = &t2p->t2_tparam;
+ mb_init(mbp);
+ if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU)
+ mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFORMATION);
+ else
+ mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFO);
+ mb_put_uint32le(mbp, 0); /* MBZ */
+ /* mb_put_uint8(mbp, SMB_DT_ASCII); specs incorrect */
+ error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\');
+ if (error) {
+ smb_t2_done(t2p);
+ return (error);
+ }
+ tzoff = vcp->vc_sopt.sv_tz;
+
+ /* do we know it won't support dates < 1980? */
+ if (!(ssp->ss_flags & SMBS_1980)) {
+ mbp = &t2p->t2_tdata;
+ mb_init(mbp);
+ mb_put_uint64le(mbp, 0); /* creation time */
+ if (atime) {
+ smb_time_local2NT(atime, tzoff, &tm);
+ } else
+ tm = 0;
+ mb_put_uint64le(mbp, tm); /* access time */
+ if (mtime) {
+ smb_time_local2NT(mtime, tzoff, &tm);
+ } else
+ tm = 0;
+ mb_put_uint64le(mbp, tm); /* last write time */
+ mb_put_uint64le(mbp, tm); /* change time */
+ mb_put_uint32le(mbp, attr); /* attr */
+ mb_put_uint32le(mbp, 0); /* undocumented padding */
+ t2p->t2_maxpcount = 24;
+ t2p->t2_maxdcount = 56;
+ error = smb_t2_request(t2p);
+ }
+ /*
+ * "invalid argument" error probably means it's a
+ * FAT drive that doesn't accept dates earlier
+ * than 1980, so adjust dates and retry. If the
+ * 1980 flag is on we fell thru the if {} above
+ */
+ if ((ssp->ss_flags & SMBS_1980) || (error == EINVAL)) {
+ mbp = &t2p->t2_tdata;
+ mb_init(mbp);
+ mb_put_uint64le(mbp, 0); /* creation time */
+ if (atime) {
+ smb_time_local2NT(atime, tzoff, &tm);
+ if (tm < DIFF1980TO1601)
+ tm = DIFF1980TO1601;
+ } else
+ tm = 0;
+ mb_put_uint64le(mbp, tm); /* access time */
+ if (mtime) {
+ smb_time_local2NT(mtime, tzoff, &tm);
+ if (tm < DIFF1980TO1601)
+ tm = DIFF1980TO1601;
+ } else
+ tm = 0;
+ mb_put_uint64le(mbp, tm); /* last write time */
+ mb_put_uint64le(mbp, tm); /* change time */
+ mb_put_uint32le(mbp, attr); /* attr */
+ mb_put_uint32le(mbp, 0); /* undocumented padding */
+ t2p->t2_maxpcount = 24;
+ t2p->t2_maxdcount = 56;
+ error = smb_t2_request(t2p);
+
+ /* if this worked set flag to do the right thing next time */
+ if (!(error)) {
+ SMB_SS_LOCK(ssp);
+ ssp->ss_flags |= SMBS_1980;
+ SMB_SS_UNLOCK(ssp);
+ }
+ }
+ smb_t2_done(t2p);
+ return (error);
+}
+
+int
+smbfs_smb_setfattr(struct smbnode *np, uint16_t fid,
+ uint32_t attr, struct timespec *mtime,
+ struct timespec *atime, struct smb_cred *scrp)
+{
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ int error;
+
+ /*
+ * This is the logic that was in smbfs_vnops.c
+ * Might not be quite right for older dialects.
+ * (XXX: What about the DOS attributes?)
+ */
+ if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS)
+ error = smbfs_smb_setfattrNT(np, fid,
+ np->n_dosattr, mtime, atime, scrp);
+ else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0)
+ error = smbfs_smb_setftime1(np, fid,
+ mtime, atime, scrp);
+ else
+ error = smbfs_smb_setpattr1(np, NULL, 0,
+ attr, mtime, scrp);
+
+ return (error);
+}
+
+/*
+ * Set file atime and mtime. Isn't supported by core dialect.
+ */
+int
+smbfs_smb_setftime1(
+ struct smbnode *np,
+ uint16_t fid,
+ struct timespec *mtime,
+ struct timespec *atime,
+ struct smb_cred *scrp)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct mbchain *mbp;
+ uint16_t date, time;
+ int error, tzoff;
+
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION2, scrp);
+ if (error)
+ return (error);
+ tzoff = SSTOVC(ssp)->vc_sopt.sv_tz;
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM);
+ mb_put_uint32le(mbp, 0); /* creation time */
+
+ if (atime)
+ smb_time_unix2dos(atime, tzoff, &date, &time, NULL);
+ else
+ time = date = 0;
+ mb_put_uint16le(mbp, date);
+ mb_put_uint16le(mbp, time);
+ if (mtime)
+ smb_time_unix2dos(mtime, tzoff, &date, &time, NULL);
+ else
+ time = date = 0;
+ mb_put_uint16le(mbp, date);
+ mb_put_uint16le(mbp, time);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ SMBVDEBUG("%d\n", error);
+ smb_rq_done(rqp);
+ return (error);
+}
+
+/*
+ * Set DOS file attributes.
+ * Looks like this call can be used only if CAP_NT_SMBS bit is on.
+ */
+int
+smbfs_smb_setfattrNT(struct smbnode *np, uint16_t fid,
+ uint32_t attr, struct timespec *mtime,
+ struct timespec *atime, struct smb_cred *scrp)
+{
+ struct smb_t2rq *t2p;
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ struct mbchain *mbp;
+ uint64_t tm;
+ int error, svtz;
+
+ error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION,
+ scrp, &t2p);
+ if (error)
+ return (error);
+ svtz = SSTOVC(ssp)->vc_sopt.sv_tz;
+ mbp = &t2p->t2_tparam;
+ mb_init(mbp);
+ mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM);
+ if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU)
+ mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFORMATION);
+ else
+ mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFO);
+ mb_put_uint32le(mbp, 0); /* XXX should be 16 not 32(?) */
+ mbp = &t2p->t2_tdata;
+ mb_init(mbp);
+ mb_put_uint64le(mbp, 0); /* creation time */
+ if (atime) {
+ smb_time_local2NT(atime, svtz, &tm);
+ } else
+ tm = 0;
+ mb_put_uint64le(mbp, tm); /* access time */
+ if (mtime) {
+ smb_time_local2NT(mtime, svtz, &tm);
+ } else
+ tm = 0;
+ mb_put_uint64le(mbp, tm); /* last write time */
+ mb_put_uint64le(mbp, tm); /* change time */
+ mb_put_uint32le(mbp, attr);
+ mb_put_uint32le(mbp, 0); /* padding */
+ t2p->t2_maxpcount = 2;
+ t2p->t2_maxdcount = 0;
+ error = smb_t2_request(t2p);
+ smb_t2_done(t2p);
+ return (error);
+}
+
+/*
+ * Modern create/open of file or directory.
+ *
+ * If disp is ..._DISP_OPEN, or ...DISP_OPEN_IF, or...
+ * then this is an open attempt, and:
+ * If xattr then name is the stream to be opened at np,
+ * Else np should be opened.
+ * ...we won't touch *fidp,
+ * ...we will set or clear *attrcacheupdated.
+ * Else this is a creation attempt, and:
+ * If xattr then name is the stream to create at np,
+ * Else name is the thing to create under directory np.
+ * ...we will return *fidp,
+ * ...we won't touch *attrcacheupdated.
+ *
+ * Note, We use: disp = ...OPEN_IF, ...OVERWRITE_IF, etc.
+ * now too, which may or may not create a new object.
+ */
+int
+smbfs_smb_ntcreatex(struct smbnode *np, uint32_t rights,
+ struct smb_cred *scrp, enum vtype vt,
+ int *attrcacheupdated, uint16_t *fidp,
+ const char *name, int nmlen, uint32_t disp, int xattr,
+ len_t *sizep, uint32_t *rightsp)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ struct smbfattr fap;
+ uint8_t wc;
+ uint32_t longint, createact, createopt, efa;
+ uint64_t llongint;
+ int error;
+ uint16_t fid, *namelenp;
+
+ /*
+ * Set the File attributes and Create options.
+ * WinXP uses EFA_NORMAL in all of these cases.
+ */
+ createopt = (vt == VDIR) ?
+ NTCREATEX_OPTIONS_DIRECTORY :
+ NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
+ efa = SMB_EFA_NORMAL;
+ if (disp != NTCREATEX_DISP_OPEN && !xattr) {
+ if (name && *name == '.')
+ efa = SMB_EFA_HIDDEN;
+ }
+
+ gethrestime(&fap.fa_reqtime);
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_NT_CREATE_ANDX, scrp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_uint8(mbp, 0xff); /* secondary command */
+ mb_put_uint8(mbp, 0); /* MBZ */
+ mb_put_uint16le(mbp, 0); /* offset to next command (none) */
+ mb_put_uint8(mbp, 0); /* MBZ */
+ namelenp = (uint16_t *)mb_reserve(mbp, sizeof (uint16_t));
+ /*
+ * XP to a W2K Server does not use NTCREATEX_FLAGS_OPEN_DIRECTORY
+ * for creating nor for opening a directory. Samba ignores the bit.
+ */
+#if 0 /* causes sharing violation when making dir on W2K! */
+ mb_put_uint32le(mbp, vt == VDIR ? NTCREATEX_FLAGS_OPEN_DIRECTORY : 0);
+#else
+ mb_put_uint32le(mbp, 0); /* NTCREATEX_FLAGS_* */
+#endif
+ mb_put_uint32le(mbp, 0); /* FID - basis for path if not root */
+ mb_put_uint32le(mbp, rights);
+ mb_put_uint64le(mbp, 0); /* "initial allocation size" */
+ mb_put_uint32le(mbp, efa);
+ mb_put_uint32le(mbp, NTCREATEX_SHARE_ACCESS_ALL);
+ mb_put_uint32le(mbp, disp);
+ mb_put_uint32le(mbp, createopt);
+ mb_put_uint32le(mbp, NTCREATEX_IMPERSONATION_IMPERSONATION); /* (?) */
+ mb_put_uint8(mbp, 0); /* security flags (?) */
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ do {
+ if (name == NULL)
+ nmlen = 0;
+ error = smbfs_fullpath(mbp, vcp, np, name, &nmlen,
+ xattr ? ':' : '\\');
+ if (error)
+ break;
+ *namelenp = htoles(nmlen); /* includes null */
+ smb_rq_bend(rqp);
+ /*
+ * Don't want to risk missing a successful
+ * open response, or we could "leak" FIDs.
+ */
+ rqp->sr_flags |= SMBR_NOINTR_RECV;
+ error = smb_rq_simple_timed(rqp, smb_timo_open);
+ if (error)
+ break;
+ smb_rq_getreply(rqp, &mdp);
+ /*
+ * spec says 26 for word count, but 34 words are defined
+ * and observed from win2000
+ */
+ if (md_get_uint8(mdp, &wc) != 0 ||
+ (wc != 26 && wc != 34 && wc != 42)) {
+ error = EBADRPC;
+ break;
+ }
+ md_get_uint8(mdp, NULL); /* secondary cmd */
+ md_get_uint8(mdp, NULL); /* mbz */
+ md_get_uint16le(mdp, NULL); /* andxoffset */
+ md_get_uint8(mdp, NULL); /* oplock lvl granted */
+ md_get_uint16(mdp, &fid); /* yes, leaving it LE */
+ md_get_uint32le(mdp, &createact); /* create_action */
+ md_get_uint64le(mdp, &llongint); /* creation time */
+ md_get_uint64le(mdp, &llongint); /* access time */
+ if (llongint) /* avoid bogus 0 time (on FAT roots) */
+ smb_time_NT2local(llongint, vcp->vc_sopt.sv_tz,
+ &fap.fa_atime);
+ md_get_uint64le(mdp, &llongint); /* write time */
+ if (llongint) /* avoid bogus 0 time (on FAT roots) */
+ smb_time_NT2local(llongint, vcp->vc_sopt.sv_tz,
+ &fap.fa_mtime);
+ md_get_uint64le(mdp, &llongint); /* change time */
+ if (llongint) /* avoid bogus 0 time (on FAT roots) */
+ smb_time_NT2local(llongint, vcp->vc_sopt.sv_tz,
+ &fap.fa_ctime);
+ md_get_uint32le(mdp, &longint); /* attributes */
+ fap.fa_attr = longint;
+ md_get_uint64le(mdp, NULL); /* allocation size */
+ md_get_uint64le(mdp, &llongint); /* EOF */
+ fap.fa_size = llongint;
+ if (sizep)
+ *sizep = fap.fa_size;
+ md_get_uint16le(mdp, NULL); /* file type */
+ md_get_uint16le(mdp, NULL); /* device state */
+ md_get_uint8(mdp, NULL); /* directory (boolean) */
+ /*LINTED*/
+ } while (0);
+ smb_rq_done(rqp);
+ if (error)
+ return (error);
+ if (fidp)
+ *fidp = fid;
+ if (rightsp)
+ *rightsp = rights;
+ /*
+ * Is it possible that we have cached attributes?
+ * Assume "not cached" if we created the object.
+ */
+ if (createact == NTCREATEX_ACTION_CREATED || xattr)
+ goto uncached;
+ if (attrcacheupdated)
+ *attrcacheupdated = 0;
+ /*
+ * Update the cached attributes if they are still valid
+ * in the cache and if nothing has changed.
+ */
+ if (np->r_vnode == NULL)
+ goto uncached;
+ if (smbfs_attr_cachelookup(np->r_vnode, NULL) != 0)
+ goto uncached; /* the cached attributes are not valid */
+ if (fap.fa_size != np->n_size)
+ goto uncached; /* the size is different */
+ if (fap.fa_attr != np->n_dosattr)
+ goto uncached; /* the attrs are different */
+ /*
+ * fap.fa_mtime is in two second increments while np->n_mtime
+ * may be in one second increments, so comparing the times is
+ * somewhat sloppy.
+ *
+ * XXX: true fap.fa_mtime resolution must depend upon server's
+ * local filesystem and is thus indeterminate... XXX ...TBD how that
+ * affects this code... note wire resolution here is 100ns versus
+ * 1sec down in smbfs_smb_oldopen(SMB_COM_OPEN)
+ */
+ if (fap.fa_mtime.tv_sec != np->n_mtime.tv_sec &&
+ fap.fa_mtime.tv_sec != np->n_mtime.tv_sec - 1 &&
+ fap.fa_mtime.tv_sec != np->n_mtime.tv_sec + 1)
+ goto uncached; /* the mod time is different */
+
+ fap.fa_mtime.tv_sec = np->n_mtime.tv_sec; /* keep higher res time */
+ smbfs_attr_cacheenter(np->r_vnode, &fap);
+ if (attrcacheupdated)
+ *attrcacheupdated = 1;
+uncached:
+ return (0);
+}
+
+static uint32_t
+smb_mode2rights(int mode)
+{
+ mode = mode & SMB_AM_OPENMODE;
+
+ switch (mode) {
+ case SMB_AM_OPENREAD:
+ return (GENERIC_RIGHT_READ_ACCESS);
+ case SMB_AM_OPENWRITE:
+ return (GENERIC_RIGHT_WRITE_ACCESS);
+ case SMB_AM_OPENRW:
+ return (GENERIC_RIGHT_ALL_ACCESS);
+ case SMB_AM_OPENEXEC:
+ return (GENERIC_RIGHT_EXECUTE_ACCESS);
+ }
+ return (0);
+}
+
+static int
+smb_rights2mode(uint32_t rights)
+{
+ int accmode = SMB_AM_OPENEXEC; /* our fallback */
+
+ if (rights & (SA_RIGHT_FILE_APPEND_DATA | SA_RIGHT_FILE_DELETE_CHILD |
+ SA_RIGHT_FILE_WRITE_EA | SA_RIGHT_FILE_WRITE_ATTRIBUTES |
+ SA_RIGHT_FILE_WRITE_DATA | STD_RIGHT_WRITE_OWNER_ACCESS |
+ STD_RIGHT_DELETE_ACCESS | STD_RIGHT_WRITE_DAC_ACCESS |
+ GENERIC_RIGHT_ALL_ACCESS | GENERIC_RIGHT_WRITE_ACCESS))
+ accmode = SMB_AM_OPENWRITE;
+ if (rights & (SA_RIGHT_FILE_READ_DATA | SA_RIGHT_FILE_READ_ATTRIBUTES |
+ SA_RIGHT_FILE_READ_EA | STD_RIGHT_READ_CONTROL_ACCESS |
+ GENERIC_RIGHT_ALL_ACCESS | GENERIC_RIGHT_READ_ACCESS))
+ accmode = (accmode == SMB_AM_OPENEXEC) ? SMB_AM_OPENREAD
+ : SMB_AM_OPENRW;
+ return (accmode);
+}
+
+static int
+smbfs_smb_oldopen(struct smbnode *np, int accmode, struct smb_cred *scrp,
+ int *attrcacheupdated, uint16_t *fidp, const char *name,
+ int nmlen, int xattr, len_t *sizep, uint32_t *rightsp)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ struct smbfattr fap;
+ uint8_t wc;
+ uint16_t fid, wattr, grantedmode;
+ uint32_t longint;
+ int error;
+
+ /*
+ * Use DENYNONE to give unixy semantics of permitting
+ * everything not forbidden by permissions. Ie denial
+ * is up to server with clients/openers needing to use
+ * advisory locks for further control.
+ */
+ accmode |= SMB_SM_DENYNONE;
+
+ gethrestime(&fap.fa_reqtime);
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_OPEN, scrp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, accmode);
+ mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_RDONLY |
+ SMB_FA_DIR);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ do {
+ error = smbfs_fullpath(mbp, vcp, np, name, &nmlen,
+ xattr ? ':' : '\\');
+ if (error)
+ break;
+ smb_rq_bend(rqp);
+ /*
+ * Don't want to risk missing a successful
+ * open response, or we could "leak" FIDs.
+ */
+ rqp->sr_flags |= SMBR_NOINTR_RECV;
+ error = smb_rq_simple_timed(rqp, smb_timo_open);
+ if (error)
+ break;
+ smb_rq_getreply(rqp, &mdp);
+ /*
+ * 8/2002 a DAVE server returned wc of 15 so we ignore that.
+ * (the actual packet length and data was correct)
+ */
+ if (md_get_uint8(mdp, &wc) != 0 || (wc != 7 && wc != 15)) {
+ error = EBADRPC;
+ break;
+ }
+ md_get_uint16(mdp, &fid); /* yes, we leave it LE */
+ md_get_uint16le(mdp, &wattr);
+ fap.fa_attr = wattr;
+ /*
+ * Be careful using the time returned here, as
+ * with FAT on NT4SP6, at least, the time returned is low
+ * 32 bits of 100s of nanoseconds (since 1601) so it rolls
+ * over about every seven minutes!
+ */
+ md_get_uint32le(mdp, &longint); /* specs: secs since 1970 */
+ if (longint) /* avoid bogus zero returns */
+ smb_time_server2local(longint, vcp->vc_sopt.sv_tz,
+ &fap.fa_mtime);
+ md_get_uint32le(mdp, &longint);
+ fap.fa_size = longint;
+ if (sizep)
+ *sizep = fap.fa_size;
+ md_get_uint16le(mdp, &grantedmode);
+ /*LINTED*/
+ } while (0);
+ smb_rq_done(rqp);
+ if (error)
+ return (error);
+ if (fidp)
+ *fidp = fid;
+ if (xattr)
+ goto uncached;
+ if (rightsp)
+ *rightsp = smb_mode2rights(grantedmode);
+ if (attrcacheupdated)
+ *attrcacheupdated = 0;
+ /*
+ * Update the cached attributes if they are still valid
+ * in the cache and if nothing has changed.
+ * Note that this won't ever update if the file size is
+ * greater than the 32-bits returned by SMB_COM_OPEN.
+ * For 64-bit file sizes, SMB_COM_NT_CREATE_ANDX must
+ * be used instead of SMB_COM_OPEN.
+ */
+ if (np->r_vnode == NULL)
+ goto uncached;
+ if (smbfs_attr_cachelookup(np->r_vnode, NULL) != 0)
+ goto uncached; /* the cached attributes are not valid */
+ if (fap.fa_size != np->n_size)
+ goto uncached; /* the size is different */
+ if (fap.fa_attr != np->n_dosattr)
+ goto uncached; /* the attrs are different */
+ /*
+ * fap.fa_mtime is in two second increments while np->n_mtime
+ * may be in one second increments, so comparing the times is
+ * somewhat sloppy.
+ */
+ if (fap.fa_mtime.tv_sec != np->n_mtime.tv_sec &&
+ fap.fa_mtime.tv_sec != np->n_mtime.tv_sec - 1 &&
+ fap.fa_mtime.tv_sec != np->n_mtime.tv_sec + 1)
+ goto uncached; /* the mod time is different */
+
+ fap.fa_mtime.tv_sec = np->n_mtime.tv_sec; /* keep higher res time */
+ smbfs_attr_cacheenter(np->r_vnode, &fap);
+ if (attrcacheupdated)
+ *attrcacheupdated = 1;
+uncached:
+ return (0);
+}
+
+int
+smbfs_smb_tmpopen(struct smbnode *np, uint32_t rights, struct smb_cred *scrp,
+ uint16_t *fidp)
+{
+ struct smb_vc *vcp = SSTOVC(np->n_mount->smi_share);
+ enum vtype vt = VREG;
+ int error;
+
+ /* Shared lock for n_fid use below. */
+ ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER));
+
+ mutex_enter(&np->r_statelock);
+ if (np->n_fidrefs && (rights & np->n_rights) == rights) {
+ np->n_fidrefs++;
+ *fidp = np->n_fid;
+ mutex_exit(&np->r_statelock);
+ return (0);
+ }
+ mutex_exit(&np->r_statelock);
+
+ if (!(vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS)) {
+ int mode = smb_rights2mode(rights);
+ error = smbfs_smb_oldopen(np, mode, scrp,
+ NULL, fidp, NULL, 0, 0, NULL, NULL);
+ } else {
+ if (SMBTOV(np))
+ vt = SMBTOV(np)->v_type;
+ error = smbfs_smb_ntcreatex(np, rights, scrp, vt,
+ NULL, fidp, NULL, 0, NTCREATEX_DISP_OPEN, 0,
+ NULL, NULL);
+ }
+
+ if (*fidp == np->n_fid) {
+ /*
+ * Oh no, the server gave us the same FID again?
+ * This will cause us to close np->n_fid early!
+ */
+ SMBVDEBUG("duplicate fid: 0x%x\n", *fidp);
+ }
+
+ return (error);
+}
+
+int
+smbfs_smb_tmpclose(struct smbnode *np, uint16_t fid, struct smb_cred *scrp)
+{
+ struct smb_share *ssp = np->n_mount->smi_share;
+ int error = 0;
+ uint16_t oldfid = SMB_FID_UNUSED;
+
+ /* Shared lock for n_fid use below. */
+ ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER));
+
+ mutex_enter(&np->r_statelock);
+ if (fid == np->n_fid) {
+ ASSERT(np->n_fidrefs > 0);
+ if (--np->n_fidrefs == 0) {
+ /*
+ * Don't expect to find the last reference
+ * here in tmpclose. Hard to deal with as
+ * we don't have r_lkserlock exclusive.
+ * Will close oldfid below.
+ */
+ oldfid = np->n_fid;
+ np->n_fid = SMB_FID_UNUSED;
+ }
+ } else {
+ /* Will close the passed fid. */
+ oldfid = fid;
+ }
+ mutex_exit(&np->r_statelock);
+
+ if (oldfid != SMB_FID_UNUSED)
+ error = smbfs_smb_close(ssp, oldfid, NULL, scrp);
+
+ return (error);
+}
+
+int
+smbfs_smb_open(struct smbnode *np, uint32_t rights, struct smb_cred *scrp,
+ int *attrcacheupdated, uint16_t *fidp, const char *name,
+ int nmlen, int xattr, len_t *sizep, uint32_t *rightsp)
+{
+ int error;
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct smb_vc *vcp = SSTOVC(ssp);
+ enum vtype vt = VREG;
+
+ if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) {
+ if (SMBTOV(np))
+ vt = SMBTOV(np)->v_type;
+ error = smbfs_smb_ntcreatex(np, rights, scrp, vt,
+ attrcacheupdated, fidp, name, nmlen,
+ NTCREATEX_DISP_OPEN, xattr, sizep, rightsp);
+ } else {
+ error = smbfs_smb_oldopen(np, smb_rights2mode(rights), scrp,
+ attrcacheupdated, fidp, name, nmlen, xattr, sizep, rightsp);
+ }
+#if 0 /* let caller do this */
+ if (!error && !name)
+ np->n_fidrefs++;
+#endif
+ return (error);
+}
+
+int
+smbfs_smb_close(struct smb_share *ssp, uint16_t fid, struct timespec *mtime,
+ struct smb_cred *scrp)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct mbchain *mbp;
+ long time;
+ int error;
+
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CLOSE, scrp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM);
+ if (mtime) {
+ smb_time_local2server(mtime, SSTOVC(ssp)->vc_sopt.sv_tz, &time);
+ } else
+ time = 0;
+ mb_put_uint32le(mbp, time);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ smb_rq_bend(rqp);
+
+ /*
+ * We don't really care about the result here, but we
+ * do need to make sure we send this out, or we could
+ * "leak" open file handles on interrupt or timeout.
+ * The NOINTR_SEND flag makes this request immune to
+ * interrupt or timeout until the send is done.
+ */
+ rqp->sr_flags |= SMBR_NOINTR_SEND;
+ error = smb_rq_simple(rqp);
+ smb_rq_done(rqp);
+ /*
+ * ENOTCONN isn't interesting - if the connection is closed,
+ * so are all our FIDs - and EIO is also not interesting,
+ * as it means a forced unmount was done. (was ENXIO)
+ * Also ETIME, which means we sent the request but gave up
+ * waiting before the response came back.
+ *
+ * Don't clog up the system log with warnings about these
+ * uninteresting failures on closes.
+ */
+ switch (error) {
+ case ENOTCONN:
+ case ENXIO:
+ case EIO:
+ case ETIME:
+ error = 0;
+ }
+ return (error);
+}
+
+static int
+smbfs_smb_oldcreate(struct smbnode *dnp, const char *name, int nmlen,
+ struct smb_cred *scrp, uint16_t *fidp, int xattr)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct smb_share *ssp = dnp->n_mount->smi_share;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ struct timespec ctime;
+ uint8_t wc;
+ long tm;
+ int error;
+ uint16_t attr = SMB_FA_ARCHIVE;
+
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE, scrp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ if (name && *name == '.')
+ attr |= SMB_FA_HIDDEN;
+ mb_put_uint16le(mbp, attr); /* attributes */
+ gethrestime(&ctime);
+ smb_time_local2server(&ctime, SSTOVC(ssp)->vc_sopt.sv_tz, &tm);
+ mb_put_uint32le(mbp, tm);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, &nmlen,
+ xattr ? ':' : '\\');
+ if (!error) {
+ smb_rq_bend(rqp);
+ /*
+ * Don't want to risk missing a successful
+ * open response, or we could "leak" FIDs.
+ */
+ rqp->sr_flags |= SMBR_NOINTR_RECV;
+ error = smb_rq_simple_timed(rqp, smb_timo_open);
+ if (!error) {
+ smb_rq_getreply(rqp, &mdp);
+ md_get_uint8(mdp, &wc);
+ if (wc == 1)
+ md_get_uint16(mdp, fidp);
+ else
+ error = EBADRPC;
+ }
+ }
+ smb_rq_done(rqp);
+ return (error);
+}
+
+int
+smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen,
+ struct smb_cred *scrp, uint16_t *fidp, uint32_t disp, int xattr)
+{
+ struct smb_vc *vcp = SSTOVC(dnp->n_mount->smi_share);
+
+ /*
+ * At present the only access we might need is to WRITE data,
+ * and that only if we are creating a "symlink". When/if the
+ * access needed gets more complex it should made a parameter
+ * and be set upstream.
+ */
+ if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) {
+ return (smbfs_smb_ntcreatex(dnp, SA_RIGHT_FILE_WRITE_DATA,
+ scrp, VREG, NULL, fidp, name, nmlen, disp, xattr,
+ NULL, NULL));
+ } else
+ return (smbfs_smb_oldcreate(dnp, name, nmlen, scrp, fidp,
+ xattr));
+}
+
+int
+smbfs_smb_delete(struct smbnode *np, struct smb_cred *scrp, const char *name,
+ int nmlen, int xattr)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct mbchain *mbp;
+ int error;
+
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE, scrp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), np, name, &nmlen,
+ xattr ? ':' : '\\');
+ if (!error) {
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ }
+ smb_rq_done(rqp);
+ return (error);
+}
+
+int
+smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp,
+ const char *tname, int tnmlen, struct smb_cred *scrp)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct smb_share *ssp = src->n_mount->smi_share;
+ struct mbchain *mbp;
+ int error;
+ uint16_t fa;
+
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_RENAME, scrp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ /* freebsd bug: Let directories be renamed - Win98 requires DIR bit */
+ fa = (SMBTOV(src)->v_type == VDIR) ? SMB_FA_DIR : 0;
+ fa |= SMB_FA_SYSTEM | SMB_FA_HIDDEN;
+ mb_put_uint16le(mbp, fa);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ do {
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, NULL, '\\');
+ if (error)
+ break;
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, &tnmlen,
+ '\\');
+ if (error)
+ break;
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ /*LINTED*/
+ } while (0);
+ smb_rq_done(rqp);
+ return (error);
+}
+
+int
+smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp,
+ const char *tname, int tnmlen, uint16_t flags, struct smb_cred *scrp)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct smb_share *ssp = src->n_mount->smi_share;
+ struct mbchain *mbp;
+ int error;
+
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_MOVE, scrp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, SMB_TID_UNKNOWN);
+ mb_put_uint16le(mbp, 0x20); /* delete target file */
+ mb_put_uint16le(mbp, flags);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ do {
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, NULL, '\\');
+ if (error)
+ break;
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, &tnmlen,
+ '\\');
+ if (error)
+ break;
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ /*LINTED*/
+ } while (0);
+ smb_rq_done(rqp);
+ return (error);
+}
+
+static int
+smbfs_smb_oldmkdir(struct smbnode *dnp, const char *name, int len,
+ struct smb_cred *scrp)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct smb_share *ssp = dnp->n_mount->smi_share;
+ struct mbchain *mbp;
+ int error;
+
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE_DIRECTORY, scrp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, &len, '\\');
+ if (!error) {
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ }
+ smb_rq_done(rqp);
+ return (error);
+}
+
+int
+smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len,
+ struct smb_cred *scrp)
+{
+ struct smb_share *ssp = dnp->n_mount->smi_share;
+ uint16_t fid;
+ int error;
+
+ /*
+ * We ask for SA_RIGHT_FILE_READ_DATA not because we need it, but
+ * just to be asking for something. The rights==0 case could
+ * easily be broken on some old or unusual servers.
+ */
+ if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) {
+ error = smbfs_smb_ntcreatex(dnp, SA_RIGHT_FILE_READ_DATA,
+ scrp, VDIR, NULL, &fid, name, len,
+ NTCREATEX_DISP_CREATE, 0, NULL, NULL);
+ if (error)
+ return (error);
+ error = smbfs_smb_close(ssp, fid, NULL, scrp);
+ if (error)
+ SMBERROR("error %d closing fid %d\n", error, fid);
+ return (0);
+ } else
+ return (smbfs_smb_oldmkdir(dnp, name, len, scrp));
+}
+
+int
+smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scrp)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct smb_share *ssp = np->n_mount->smi_share;
+ struct mbchain *mbp;
+ int error;
+
+ error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE_DIRECTORY, scrp);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_ASCII);
+ error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, NULL, '\\');
+ if (!error) {
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ }
+ smb_rq_done(rqp);
+ return (error);
+}
+
+static int
+smbfs_smb_search(struct smbfs_fctx *ctx)
+{
+ struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ uint8_t wc, bt;
+ uint16_t ec, dlen, bc;
+ int len, maxent, error, iseof = 0;
+
+ maxent = min(ctx->f_left,
+ (vcp->vc_txmax - SMB_HDRLEN - 2*2) / SMB_DENTRYLEN);
+ if (ctx->f_rq) {
+ smb_rq_done(ctx->f_rq);
+ ctx->f_rq = NULL;
+ }
+ error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB_COM_SEARCH,
+ ctx->f_scred, &rqp);
+ if (error)
+ return (error);
+ ctx->f_rq = rqp;
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, maxent); /* max entries to return */
+ mb_put_uint16le(mbp, ctx->f_attrmask);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_ASCII); /* buffer format */
+ if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
+ len = ctx->f_wclen;
+ error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard,
+ &len, '\\');
+ if (error)
+ return (error);
+ mb_put_uint8(mbp, SMB_DT_VARIABLE);
+ mb_put_uint16le(mbp, 0); /* context length */
+ ctx->f_flags &= ~SMBFS_RDD_FINDFIRST;
+ } else {
+ if (SMB_UNICODE_STRINGS(vcp)) {
+ mb_put_padbyte(mbp);
+ mb_put_uint8(mbp, 0);
+ }
+ mb_put_uint8(mbp, 0);
+ mb_put_uint8(mbp, SMB_DT_VARIABLE);
+ mb_put_uint16le(mbp, SMB_SKEYLEN);
+ mb_put_mem(mbp, (char *)ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM);
+ }
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnofiles) {
+ error = 0;
+ iseof = 1;
+ ctx->f_flags |= SMBFS_RDD_EOF;
+ } else if (error)
+ return (error);
+ smb_rq_getreply(rqp, &mdp);
+ md_get_uint8(mdp, &wc);
+ if (wc != 1)
+ return (iseof ? ENOENT : EBADRPC);
+ md_get_uint16le(mdp, &ec);
+ if (ec == 0)
+ return (ENOENT);
+ ctx->f_ecnt = ec;
+ md_get_uint16le(mdp, &bc);
+ if (bc < 3)
+ return (EBADRPC);
+ bc -= 3;
+ md_get_uint8(mdp, &bt);
+ if (bt != SMB_DT_VARIABLE)
+ return (EBADRPC);
+ md_get_uint16le(mdp, &dlen);
+ if (dlen != bc || dlen % SMB_DENTRYLEN != 0)
+ return (EBADRPC);
+ return (0);
+}
+
+
+/*ARGSUSED*/
+static int
+smbfs_smb_findopenLM1(struct smbfs_fctx *ctx, struct smbnode *dnp,
+ const char *wildcard, int wclen, uint16_t attr, struct smb_cred *scrp)
+{
+ /* #pragma unused(dnp, scrp) */
+ ctx->f_attrmask = attr;
+ if (wildcard) {
+ if (wclen == 1 && wildcard[0] == '*') {
+ ctx->f_wildcard = "*.*";
+ ctx->f_wclen = 3;
+ } else {
+ ctx->f_wildcard = wildcard;
+ ctx->f_wclen = wclen;
+ }
+ } else {
+ ctx->f_wildcard = NULL;
+ ctx->f_wclen = 0;
+ }
+ ctx->f_name = (char *)ctx->f_fname;
+ ctx->f_namesz = 0;
+ return (0);
+}
+
+static int
+smbfs_smb_findnextLM1(struct smbfs_fctx *ctx, uint16_t limit)
+{
+ struct mdchain *mdp;
+ struct smb_rq *rqp;
+ char *cp;
+ uint8_t battr;
+ uint16_t date, time;
+ uint32_t size;
+ int error;
+ struct timespec ts;
+
+ if (ctx->f_ecnt == 0) {
+ if (ctx->f_flags & SMBFS_RDD_EOF)
+ return (ENOENT);
+ ctx->f_left = ctx->f_limit = limit;
+ gethrestime(&ts);
+ error = smbfs_smb_search(ctx);
+ if (error)
+ return (error);
+ ctx->f_attr.fa_reqtime = ts;
+ }
+ rqp = ctx->f_rq;
+ smb_rq_getreply(rqp, &mdp);
+ md_get_mem(mdp, (char *)ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM);
+ md_get_uint8(mdp, &battr);
+ md_get_uint16le(mdp, &time);
+ md_get_uint16le(mdp, &date);
+ md_get_uint32le(mdp, &size);
+ cp = ctx->f_name;
+ md_get_mem(mdp, cp, sizeof (ctx->f_fname), MB_MSYSTEM);
+ cp[sizeof (ctx->f_fname) - 1] = 0;
+ cp += strlen(cp) - 1;
+ while (*cp == ' ' && cp >= ctx->f_name)
+ *cp-- = 0;
+ ctx->f_attr.fa_attr = battr;
+ smb_dos2unixtime(date, time, 0, rqp->sr_vc->vc_sopt.sv_tz,
+ &ctx->f_attr.fa_mtime);
+ ctx->f_attr.fa_size = size;
+ ctx->f_nmlen = strlen(ctx->f_name);
+ ctx->f_ecnt--;
+ ctx->f_left--;
+ return (0);
+}
+
+static int
+smbfs_smb_findcloseLM1(struct smbfs_fctx *ctx)
+{
+ if (ctx->f_rq)
+ smb_rq_done(ctx->f_rq);
+ return (0);
+}
+
+/*
+ * TRANS2_FIND_FIRST2/NEXT2, used for NT LM12 dialect
+ */
+static int
+smbfs_smb_trans2find2(struct smbfs_fctx *ctx)
+{
+ struct smb_t2rq *t2p;
+ struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ uint16_t tw, flags;
+ int len, error;
+
+ if (ctx->f_t2) {
+ smb_t2_done(ctx->f_t2);
+ ctx->f_t2 = NULL;
+ }
+ ctx->f_flags &= ~SMBFS_RDD_GOTRNAME;
+ flags = FIND2_RETURN_RESUME_KEYS | FIND2_CLOSE_ON_EOS;
+ if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
+ flags |= FIND2_CLOSE_AFTER_REQUEST;
+ ctx->f_flags |= SMBFS_RDD_NOCLOSE;
+ }
+ if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
+ error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_FIRST2,
+ ctx->f_scred, &t2p);
+ if (error)
+ return (error);
+ ctx->f_t2 = t2p;
+ mbp = &t2p->t2_tparam;
+ mb_init(mbp);
+ mb_put_uint16le(mbp, ctx->f_attrmask);
+ mb_put_uint16le(mbp, ctx->f_limit);
+ mb_put_uint16le(mbp, flags);
+ mb_put_uint16le(mbp, ctx->f_infolevel);
+ mb_put_uint32le(mbp, 0);
+ /* mb_put_uint8(mbp, SMB_DT_ASCII); specs? hah! */
+ len = ctx->f_wclen;
+ error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard,
+ &len, '\\');
+ if (error)
+ return (error);
+ } else {
+ error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_NEXT2,
+ ctx->f_scred, &t2p);
+ if (error)
+ return (error);
+ ctx->f_t2 = t2p;
+ mbp = &t2p->t2_tparam;
+ mb_init(mbp);
+ mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM);
+ mb_put_uint16le(mbp, ctx->f_limit);
+ mb_put_uint16le(mbp, ctx->f_infolevel);
+ if (ctx->f_ssp->ss_flags & SMBS_RESUMEKEYS) {
+ mb_put_uint32le(mbp, ctx->f_rkey);
+ } else
+ mb_put_uint32le(mbp, 0);
+ mb_put_uint16le(mbp, flags);
+ if (ctx->f_rname) {
+ /* resume file name */
+ mb_put_mem(mbp, ctx->f_rname, ctx->f_rnamelen,
+ MB_MSYSTEM);
+ }
+ /* Add trailing null - 1 byte if ASCII, 2 if Unicode */
+ if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp)))
+ mb_put_uint8(mbp, 0); /* 1st byte NULL Unicode char */
+ mb_put_uint8(mbp, 0);
+#if 0
+ struct timespec ts;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 200 * 1000 * 1000; /* 200ms */
+ if (vcp->vc_flags & SMBC_WIN95) {
+ /*
+ * some implementations suggests to sleep here
+ * for 200ms, due to the bug in the Win95.
+ * I've didn't notice any problem, but put code
+ * for it.
+ */
+ msleep(&flags, 0, PVFS, "fix95", &ts);
+ }
+#endif
+ }
+ t2p->t2_maxpcount = 5 * 2;
+ t2p->t2_maxdcount = vcp->vc_txmax;
+ error = smb_t2_request(t2p);
+ if (error)
+ return (error);
+ mdp = &t2p->t2_rparam;
+ if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
+ if ((error = md_get_uint16(mdp, &ctx->f_Sid)) != 0)
+ return (error);
+ ctx->f_flags &= ~SMBFS_RDD_FINDFIRST;
+ }
+ if ((error = md_get_uint16le(mdp, &tw)) != 0)
+ return (error);
+ ctx->f_ecnt = tw; /* search count - # entries returned */
+ if ((error = md_get_uint16le(mdp, &tw)) != 0)
+ return (error);
+ /*
+ * tw now is the "end of search" flag. against an XP server tw
+ * comes back zero when the prior find_next returned exactly
+ * the number of entries requested. in which case we'd try again
+ * but the search has in fact been closed so an EBADF results. our
+ * circumvention is to check here for a zero search count.
+ */
+ if (tw || ctx->f_ecnt == 0)
+ ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE;
+ if ((error = md_get_uint16le(mdp, &tw)) != 0)
+ return (error);
+ if ((error = md_get_uint16le(mdp, &tw)) != 0)
+ return (error);
+ if (ctx->f_ecnt == 0)
+ return (ENOENT);
+ ctx->f_rnameofs = tw;
+ mdp = &t2p->t2_rdata;
+ if (mdp->md_top == NULL) {
+ SMBVDEBUG("ecnt = %d, but data is NULL\n", ctx->f_ecnt);
+ return (ENOENT);
+ }
+#ifdef APPLE
+ if (mdp->md_top->m_len == 0) {
+ printf("bug: ecnt = %d, but m_len = 0 and m_next = %p "
+ "(please report)\n", ctx->f_ecnt, mbp->mb_top->m_next);
+ return (ENOENT);
+ }
+#endif
+ ctx->f_eofs = 0;
+ return (0);
+}
+
+static int
+smbfs_smb_findclose2(struct smbfs_fctx *ctx)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct mbchain *mbp;
+ int error;
+
+ error = smb_rq_init(rqp, SSTOCP(ctx->f_ssp), SMB_COM_FIND_CLOSE2,
+ ctx->f_scred);
+ if (error)
+ return (error);
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ smb_rq_bend(rqp);
+ /* Ditto comments at _smb_close */
+ rqp->sr_flags |= SMBR_NOINTR_SEND;
+ error = smb_rq_simple(rqp);
+ smb_rq_done(rqp);
+ return (error);
+}
+
+/*ARGSUSED*/
+static int
+smbfs_smb_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp,
+ const char *wildcard, int wclen, uint16_t attr, struct smb_cred *scrp)
+{
+ ctx->f_namesz = SMB_MAXFNAMELEN;
+ if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp)))
+ ctx->f_namesz *= 2;
+ ctx->f_name = kmem_zalloc(ctx->f_namesz, KM_SLEEP);
+ ctx->f_infolevel = SMB_DIALECT(SSTOVC(ctx->f_ssp))
+ < SMB_DIALECT_NTLM0_12 ? SMB_FIND_STANDARD :
+ SMB_FIND_BOTH_DIRECTORY_INFO;
+ ctx->f_attrmask = attr;
+ ctx->f_wildcard = wildcard;
+ ctx->f_wclen = wclen;
+ return (0);
+}
+
+static int
+smbfs_smb_findnextLM2(struct smbfs_fctx *ctx, uint16_t limit)
+{
+ struct mdchain *mdp;
+ struct smb_t2rq *t2p;
+ char *cp;
+ uint8_t tb;
+ uint16_t date, time, wattr;
+ uint32_t size, next, dattr, resumekey = 0;
+ uint64_t llongint;
+ int error, svtz, cnt, fxsz, nmlen, recsz, otw;
+ struct timespec ts;
+
+again:
+ otw = 0; /* nothing sent Over The Wire (yet) */
+ if (ctx->f_ecnt == 0) {
+ if (ctx->f_flags & SMBFS_RDD_EOF)
+ return (ENOENT);
+ ctx->f_left = ctx->f_limit = limit;
+ gethrestime(&ts);
+ error = smbfs_smb_trans2find2(ctx);
+ if (error)
+ return (error);
+ ctx->f_attr.fa_reqtime = ts;
+ ctx->f_otws++;
+ otw = 1;
+ }
+ t2p = ctx->f_t2;
+ mdp = &t2p->t2_rdata;
+ svtz = SSTOVC(ctx->f_ssp)->vc_sopt.sv_tz;
+ switch (ctx->f_infolevel) {
+ case SMB_FIND_STANDARD:
+ next = 0;
+ fxsz = 0;
+ md_get_uint16le(mdp, &date);
+ md_get_uint16le(mdp, &time); /* creation time */
+ md_get_uint16le(mdp, &date);
+ md_get_uint16le(mdp, &time); /* access time */
+ smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_atime);
+ md_get_uint16le(mdp, &date);
+ md_get_uint16le(mdp, &time); /* modify time */
+ smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_mtime);
+ md_get_uint32le(mdp, &size);
+ ctx->f_attr.fa_size = size;
+ md_get_uint32(mdp, NULL); /* allocation size */
+ md_get_uint16le(mdp, &wattr);
+ ctx->f_attr.fa_attr = wattr;
+ md_get_uint8(mdp, &tb);
+ size = nmlen = tb;
+ fxsz = 23;
+ recsz = next = 24 + nmlen; /* docs misses zero byte @end */
+ break;
+ case SMB_FIND_DIRECTORY_INFO:
+ case SMB_FIND_BOTH_DIRECTORY_INFO:
+ md_get_uint32le(mdp, &next);
+ md_get_uint32le(mdp, &resumekey); /* file index (resume key) */
+ md_get_uint64(mdp, NULL); /* creation time */
+ md_get_uint64le(mdp, &llongint);
+ smb_time_NT2local(llongint, svtz, &ctx->f_attr.fa_atime);
+ md_get_uint64le(mdp, &llongint);
+ smb_time_NT2local(llongint, svtz, &ctx->f_attr.fa_mtime);
+ md_get_uint64le(mdp, &llongint);
+ smb_time_NT2local(llongint, svtz, &ctx->f_attr.fa_ctime);
+ md_get_uint64le(mdp, &llongint); /* file size */
+ ctx->f_attr.fa_size = llongint;
+ md_get_uint64(mdp, NULL); /* real size (should use) */
+ /* freebsd bug: fa_attr endian bug */
+ md_get_uint32le(mdp, &dattr); /* extended file attributes */
+ ctx->f_attr.fa_attr = dattr;
+ md_get_uint32le(mdp, &size); /* name len */
+ fxsz = 64; /* size ofinfo up to filename */
+ if (ctx->f_infolevel == SMB_FIND_BOTH_DIRECTORY_INFO) {
+ /*
+ * Skip EaSize(4 bytes), a byte of ShortNameLength,
+ * a reserved byte, and ShortName(8.3 means 24 bytes,
+ * as Leach defined it to always be Unicode)
+ */
+ md_get_mem(mdp, NULL, 30, MB_MSYSTEM);
+ fxsz += 30;
+ }
+ recsz = next ? next : fxsz + size;
+ break;
+ default:
+ SMBVDEBUG("unexpected info level %d\n", ctx->f_infolevel);
+ return (EINVAL);
+ }
+ if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp)))
+ nmlen = min(size, SMB_MAXFNAMELEN * 2);
+ else
+ nmlen = min(size, SMB_MAXFNAMELEN);
+ if (ctx->f_name)
+ kmem_free(ctx->f_name, ctx->f_namesz);
+ cp = ctx->f_name = kmem_alloc(nmlen, KM_SLEEP);
+ ctx->f_namesz = nmlen;
+ error = md_get_mem(mdp, cp, nmlen, MB_MSYSTEM);
+ if (error)
+ return (error);
+ if (next) {
+ cnt = next - nmlen - fxsz;
+ if (cnt > 0)
+ md_get_mem(mdp, NULL, cnt, MB_MSYSTEM);
+ else if (cnt < 0) {
+ SMBVDEBUG("out of sync\n");
+ return (EBADRPC);
+ }
+ }
+ /* Don't count any trailing null in the name. */
+ if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) {
+ if (nmlen > 1 && cp[nmlen - 1] == 0 && cp[nmlen - 2] == 0)
+ nmlen -= 2;
+ } else {
+ if (nmlen && cp[nmlen - 1] == 0)
+ nmlen--;
+ }
+ if (nmlen == 0)
+ return (EBADRPC);
+
+ /*
+ * Ref radar 3983209. On a find-next we expect a server will
+ * 1) if the continue bit is set, use the server's idea of current loc,
+ * 2) else if the resume key is non-zero, use that location,
+ * 3) else if the resume name is set, use that location,
+ * 4) else use the server's idea of current location.
+ *
+ * Current NetApps don't do that. If we send no continue bit, a zero
+ * resume key, and a resume name, the NetApp ignores the resume name
+ * and acts on the (zero) resume key, sending back the start of the
+ * directory again. Panther doesn't expose the netapp bug; Panther used
+ * the continue bit, but that was changed for 2866172. Win2000 as a
+ * client also relies upon the resume name, but they request a very
+ * large number of files, so the bug would be seen only with very
+ * large directories.
+ *
+ * Our fix is to notice if the second OTW op (the first find-next)
+ * returns, in the first filename, the same filename we got back
+ * at the start of the first OTW (the find-first). In that case
+ * we've detected the server bug and set SMBS_RESUMEKEYS, causing us
+ * to send non-zero resume keys henceforth.
+ *
+ * Caveat: if there's a netapp so old it doesn't negotiate NTLM 0.12
+ * then we get no resume keys so f_rkey stays zero and this "fix"
+ * changes nothing.
+ *
+ * Note due to a similar problem (4051871) we also set SMBS_RESUMEKEYS
+ * for FAT volumes, at mount time.
+ */
+ if (otw && !(ctx->f_ssp->ss_flags & SMBS_RESUMEKEYS)) {
+ if (ctx->f_otws == 1) {
+ ctx->f_firstnmlen = nmlen;
+ ctx->f_firstnm = kmem_alloc(nmlen, KM_SLEEP);
+ bcopy(ctx->f_name, ctx->f_firstnm, nmlen);
+ } else if (ctx->f_otws == 2 && nmlen == ctx->f_firstnmlen &&
+ !(bcmp(ctx->f_name, ctx->f_firstnm, nmlen) == 0)) {
+ struct smb_share *ssp = ctx->f_ssp;
+ SMBERROR(
+ "server resume_name bug seen; using resume keys\n");
+ SMB_SS_LOCK(ssp);
+ ssp->ss_flags |= SMBS_RESUMEKEYS;
+ SMB_SS_UNLOCK(ssp);
+ ctx->f_ecnt = 0;
+ goto again; /* must redo last otw op! */
+ }
+ }
+ ctx->f_rkey = resumekey;
+
+ next = ctx->f_eofs + recsz;
+ if (ctx->f_rnameofs &&
+ (ctx->f_flags & SMBFS_RDD_GOTRNAME) == 0 &&
+ (ctx->f_rnameofs >= ctx->f_eofs &&
+ ctx->f_rnameofs < (int)next)) {
+ /*
+ * Server needs a resume filename.
+ */
+ if (ctx->f_rnamelen != nmlen) {
+ if (ctx->f_rname)
+ kmem_free(ctx->f_rname, ctx->f_rnamelen);
+ ctx->f_rname = kmem_alloc(nmlen, KM_SLEEP);
+ ctx->f_rnamelen = nmlen;
+ }
+ bcopy(ctx->f_name, ctx->f_rname, nmlen);
+ ctx->f_flags |= SMBFS_RDD_GOTRNAME;
+ }
+ ctx->f_nmlen = nmlen;
+ ctx->f_eofs = next;
+ ctx->f_ecnt--;
+ ctx->f_left--;
+ return (0);
+}
+
+static int
+smbfs_smb_findcloseLM2(struct smbfs_fctx *ctx)
+{
+ if (ctx->f_name)
+ kmem_free(ctx->f_name, ctx->f_namesz);
+ if (ctx->f_t2)
+ smb_t2_done(ctx->f_t2);
+ if ((ctx->f_flags & SMBFS_RDD_NOCLOSE) == 0)
+ smbfs_smb_findclose2(ctx);
+ return (0);
+}
+
+int
+smbfs_smb_findopen(struct smbnode *dnp, const char *wildcard, int wclen,
+ int attr, struct smb_cred *scrp,
+ struct smbfs_fctx **ctxpp)
+{
+ struct smbfs_fctx *ctx;
+ int error;
+
+ ctx = kmem_alloc(sizeof (*ctx), KM_SLEEP);
+ if (ctx == NULL)
+ return (ENOMEM);
+ bzero(ctx, sizeof (*ctx));
+ if (dnp->n_mount->smi_share) {
+ ctx->f_ssp = dnp->n_mount->smi_share;
+ }
+ ctx->f_dnp = dnp;
+ ctx->f_flags = SMBFS_RDD_FINDFIRST;
+ ctx->f_scred = scrp;
+ if (SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_LANMAN2_0 ||
+ (dnp->n_mount->smi_args.flags & SMBFS_MOUNT_NO_LONG)) {
+ ctx->f_flags |= SMBFS_RDD_USESEARCH;
+ error = smbfs_smb_findopenLM1(ctx, dnp, wildcard, wclen,
+ attr, scrp);
+ } else
+ error = smbfs_smb_findopenLM2(ctx, dnp, wildcard, wclen,
+ attr, scrp);
+ if (error)
+ smbfs_smb_findclose(ctx, scrp);
+ else
+ *ctxpp = ctx;
+ return (error);
+}
+
+int
+smbfs_smb_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scrp)
+{
+ int error;
+
+ /*
+ * Note: "limit" (maxcount) needs to fit in a short!
+ *
+ * smb_lookup always uses 1, which is OK (no wildcards).
+ * Otherwise, this is smbfs_readdir, and we want to force
+ * limit to be in the range 3 to 1000. The low limit (3)
+ * is so we can always give the caller one "real" entry
+ * (something other than "." or "..") The high limit is
+ * just tuning. WinNT used 512, Win2k 1366. We use 1000.
+ *
+ * XXX: Move the [skip . ..] gunk to our caller (readdir).
+ */
+ if ((ctx->f_flags & SMBFS_RDD_FINDSINGLE) == 0) {
+ if (limit < 3)
+ limit = 3;
+ if (limit > 1000)
+ limit = 1000;
+ }
+
+ ctx->f_scred = scrp;
+ for (;;) {
+ if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
+ error = smbfs_smb_findnextLM1(ctx, (uint16_t)limit);
+ } else
+ error = smbfs_smb_findnextLM2(ctx, (uint16_t)limit);
+ if (error)
+ return (error);
+ if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) {
+ /*LINTED*/
+ uint16_t *up = (uint16_t *)ctx->f_name;
+
+ /* Do comparisons on UCS-2LE characters */
+ if ((ctx->f_nmlen == 2 && up[0] == htoles('.')) ||
+ (ctx->f_nmlen == 4 && up[0] == htoles('.') &&
+ up[1] == htoles('.')))
+ continue;
+ } else {
+ if ((ctx->f_nmlen == 1 && ctx->f_name[0] == '.') ||
+ (ctx->f_nmlen == 2 && ctx->f_name[0] == '.' &&
+ ctx->f_name[1] == '.'))
+ continue;
+ }
+ break;
+ }
+ smbfs_fname_tolocal(ctx);
+ ctx->f_attr.fa_ino = smbfs_getino(ctx->f_dnp, ctx->f_name,
+ ctx->f_nmlen);
+ return (0);
+}
+
+
+int
+smbfs_smb_findclose(struct smbfs_fctx *ctx, struct smb_cred *scrp)
+{
+ ctx->f_scred = scrp;
+ if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
+ smbfs_smb_findcloseLM1(ctx);
+ } else
+ smbfs_smb_findcloseLM2(ctx);
+ if (ctx->f_rname)
+ kmem_free(ctx->f_rname, ctx->f_rnamelen);
+ if (ctx->f_firstnm)
+ kmem_free(ctx->f_firstnm, ctx->f_firstnmlen);
+ kmem_free(ctx, sizeof (*ctx));
+ return (0);
+}
+
+
+int
+smbfs_smb_lookup(struct smbnode *dnp, const char **namep, int *nmlenp,
+ struct smbfattr *fap, struct smb_cred *scrp)
+{
+ struct smbfs_fctx *ctx;
+ int error, intr;
+ const char *name = (namep ? *namep : NULL);
+ int nmlen = (nmlenp ? *nmlenp : 0);
+
+ /* This is no longer called with a null dnp */
+ ASSERT(dnp);
+
+ /*
+ * Should not get here with "" anymore.
+ */
+ if (!name || !nmlen) {
+ DEBUG_ENTER("smbfs_smb_lookup: name is NULL");
+ return (EINVAL);
+ }
+
+ /*
+ * Should not get here with "." or ".." anymore.
+ */
+ if ((nmlen == 1 && name[0] == '.') ||
+ (nmlen == 2 && name[0] == '.' && name[1] == '.')) {
+ DEBUG_ENTER("smbfs_smb_lookup: name is '.' or '..'");
+ return (EINVAL);
+ }
+
+ /*
+ * Shared lock for n_fid use (smb_flush).
+ */
+ intr = dnp->n_mount->smi_flags & SMI_INT;
+ if (smbfs_rw_enter_sig(&dnp->r_lkserlock, RW_READER, intr))
+ return (EINTR);
+
+ bzero(fap, sizeof (*fap));
+
+ /*
+ * This hides a server bug observable in Win98:
+ * size changes may not show until a CLOSE or a FLUSH op
+ * XXX: Make this conditional on !NTSMBs
+ */
+ error = smbfs_smb_flush(dnp, scrp);
+ if (error)
+ goto out;
+ error = smbfs_smb_findopen(dnp, name, nmlen,
+ SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, scrp, &ctx);
+ if (error)
+ goto out;
+ ctx->f_flags |= SMBFS_RDD_FINDSINGLE;
+ error = smbfs_smb_findnext(ctx, 1, scrp);
+ if (error == 0) {
+ *fap = ctx->f_attr;
+ if (name == NULL)
+ fap->fa_ino = dnp->n_ino;
+ if (namep)
+ *namep = (const char *)smbfs_name_alloc(
+ ctx->f_name, ctx->f_nmlen);
+ if (nmlenp)
+ *nmlenp = ctx->f_nmlen;
+ }
+ smbfs_smb_findclose(ctx, scrp);
+
+out:
+ smbfs_rw_exit(&dnp->r_lkserlock);
+ return (error);
+}
+
+/*
+ * Support functions for get/set security
+ */
+#ifdef APPLE
+
+int
+smbfs_smb_getsec_int(struct smb_share *ssp, uint16_t fid,
+ struct smb_cred *scrp, uint32_t selector,
+ struct ntsecdesc **res, int *reslen)
+{
+ struct smb_ntrq *ntp;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ int error, len;
+
+ error = smb_nt_alloc(SSTOCP(ssp), NT_TRANSACT_QUERY_SECURITY_DESC,
+ scrp, &ntp);
+ if (error)
+ return (error);
+ mbp = &ntp->nt_tparam;
+ mb_init(mbp);
+ mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM);
+ mb_put_uint16le(mbp, 0); /* reserved */
+ mb_put_uint32le(mbp, selector);
+ ntp->nt_maxpcount = 4;
+ ntp->nt_maxdcount = *reslen;
+ error = smb_nt_request(ntp);
+ if (error && !(ntp->nt_flags & SMBT2_MOREDATA))
+ goto done;
+ *res = NULL;
+ /*
+ * if there's more data than we said we could receive, here
+ * is where we pick up the length of it
+ */
+ mdp = &ntp->nt_rparam;
+ md_get_uint32le(mdp, reslen);
+
+ mdp = &ntp->nt_rdata;
+ if (mdp->md_top) { /* XXX md_cur safer than md_top */
+ len = m_fixhdr(mdp->md_top);
+ /*
+ * The following "if (len < *reslen)" handles a Windows bug
+ * observed when the underlying filesystem is FAT32. In that
+ * case a 32 byte security descriptor comes back (S-1-1-0, ie
+ * "Everyone") but the Parameter Block claims 44 is the length
+ * of the security descriptor. (The Data Block length
+ * claimed is 32. This server bug was reported against NT
+ * first and I've personally observed it with W2K.
+ */
+ if (len < *reslen)
+ *reslen = len;
+ if (len == *reslen) {
+ MALLOC(*res, struct ntsecdesc *, len, M_TEMP, M_WAITOK);
+ md_get_mem(mdp, (caddr_t)*res, len, MB_MSYSTEM);
+ } else if (len > *reslen)
+ SMBVDEBUG("len %d *reslen %d fid 0x%x\n", len, *reslen,
+ letohs(fid));
+ } else
+ SMBVDEBUG("null md_top? fid 0x%x\n", letohs(fid));
+done:
+ smb_nt_done(ntp);
+ return (error);
+}
+
+int
+smbfs_smb_getsec(struct smb_share *ssp, uint16_t fid, struct smb_cred *scrp,
+ uint32_t selector, struct ntsecdesc **res)
+{
+ int error, olen, seclen;
+
+ olen = seclen = 500; /* "overlarge" values => server errors */
+ error = smbfs_smb_getsec_int(ssp, fid, scrp, selector, res, &seclen);
+ if (error && seclen > olen)
+ error = smbfs_smb_getsec_int(ssp, fid, scrp, selector, res,
+ &seclen);
+ return (error);
+}
+
+int
+smbfs_smb_setsec(struct smb_share *ssp, uint16_t fid, struct smb_cred *scrp,
+ uint32_t selector, uint16_t flags, struct ntsid *owner,
+ struct ntsid *group, struct ntacl *sacl, struct ntacl *dacl)
+{
+ struct smb_ntrq *ntp;
+ struct mbchain *mbp;
+ int error, off;
+ struct ntsecdesc ntsd;
+
+ error = smb_nt_alloc(SSTOCP(ssp), NT_TRANSACT_SET_SECURITY_DESC,
+ scrp, &ntp);
+ if (error)
+ return (error);
+ mbp = &ntp->nt_tparam;
+ mb_init(mbp);
+ mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM);
+ mb_put_uint16le(mbp, 0); /* reserved */
+ mb_put_uint32le(mbp, selector);
+ mbp = &ntp->nt_tdata;
+ mb_init(mbp);
+ bzero(&ntsd, sizeof (ntsd));
+ wset_sdrevision(&ntsd);
+ /*
+ * A note about flags ("SECURITY_DESCRIPTOR_CONTROL" in MSDN)
+ * We set here only those bits we can be sure must be set. The rest
+ * are up to the caller. In particular, the caller may intentionally
+ * set an acl PRESENT bit while giving us a null pointer for the
+ * acl - that sets a null acl, giving access to everyone. Note also
+ * that the AUTO_INHERITED bits should probably always be set unless
+ * the server is NT.
+ */
+ flags |= SD_SELF_RELATIVE;
+ off = sizeof (ntsd);
+ if (owner) {
+ wset_sdowneroff(&ntsd, off);
+ off += sidlen(owner);
+ }
+ if (group) {
+ wset_sdgroupoff(&ntsd, off);
+ off += sidlen(group);
+ }
+ if (sacl) {
+ flags |= SD_SACL_PRESENT;
+ wset_sdsacloff(&ntsd, off);
+ off += acllen(sacl);
+ }
+ if (dacl) {
+ flags |= SD_DACL_PRESENT;
+ wset_sddacloff(&ntsd, off);
+ }
+ wset_sdflags(&ntsd, flags);
+ mb_put_mem(mbp, (caddr_t)&ntsd, sizeof (ntsd), MB_MSYSTEM);
+ if (owner)
+ mb_put_mem(mbp, (caddr_t)owner, sidlen(owner), MB_MSYSTEM);
+ if (group)
+ mb_put_mem(mbp, (caddr_t)group, sidlen(group), MB_MSYSTEM);
+ if (sacl)
+ mb_put_mem(mbp, (caddr_t)sacl, acllen(sacl), MB_MSYSTEM);
+ if (dacl)
+ mb_put_mem(mbp, (caddr_t)dacl, acllen(dacl), MB_MSYSTEM);
+ ntp->nt_maxpcount = 0;
+ ntp->nt_maxdcount = 0;
+ error = smb_nt_request(ntp);
+ smb_nt_done(ntp);
+ return (error);
+}
+
+#endif /* APPLE */
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c
new file mode 100644
index 0000000000..5ef9b5439f
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smbfs_subr.c,v 1.18 2005/02/02 00:22:23 lindak Exp $
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/sunddi.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#include <sys/utfconv.h>
+#include <sys/smb_iconv.h>
+#else /* APPLE */
+#include <netsmb/smb_osdep.h>
+#endif /* APPLE */
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_rq.h>
+
+#include <smbfs/smbfs.h>
+#include <smbfs/smbfs_node.h>
+#include <smbfs/smbfs_subr.h>
+
+#ifdef APPLE
+MALLOC_DEFINE(M_SMBFSDATA, "SMBFS data", "SMBFS private data");
+#endif /* APPLE */
+
+/*
+ * Time & date conversion routines taken from msdosfs. Although leap
+ * year calculation is bogus, it's sufficient before 2100 :)
+ */
+/*
+ * This is the format of the contents of the deTime field in the direntry
+ * structure.
+ * We don't use bitfields because we don't know how compilers for
+ * arbitrary machines will lay them out.
+ */
+#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */
+#define DT_2SECONDS_SHIFT 0
+#define DT_MINUTES_MASK 0x7E0 /* minutes */
+#define DT_MINUTES_SHIFT 5
+#define DT_HOURS_MASK 0xF800 /* hours */
+#define DT_HOURS_SHIFT 11
+
+/*
+ * This is the format of the contents of the deDate field in the direntry
+ * structure.
+ */
+#define DD_DAY_MASK 0x1F /* day of month */
+#define DD_DAY_SHIFT 0
+#define DD_MONTH_MASK 0x1E0 /* month */
+#define DD_MONTH_SHIFT 5
+#define DD_YEAR_MASK 0xFE00 /* year - 1980 */
+#define DD_YEAR_SHIFT 9
+/*
+ * Total number of days that have passed for each month in a regular year.
+ */
+static ushort_t regyear[] = {
+ 31, 59, 90, 120, 151, 181,
+ 212, 243, 273, 304, 334, 365
+};
+
+/*
+ * Total number of days that have passed for each month in a leap year.
+ */
+static ushort_t leapyear[] = {
+ 31, 60, 91, 121, 152, 182,
+ 213, 244, 274, 305, 335, 366
+};
+
+/*
+ * Variables used to remember parts of the last time conversion. Maybe we
+ * can avoid a full conversion.
+ */
+static ulong_t lasttime;
+static ulong_t lastday;
+static ushort_t lastddate;
+static ushort_t lastdtime;
+
+#ifdef APPLE
+PRIVSYM int wall_cmos_clock = 0; /* XXX */
+PRIVSYM int adjkerntz = 0; /* XXX */
+#endif /* APPLE */
+
+void
+smb_time_unix2dos(struct timespec *tsp, int tzoff, u_int16_t *ddp,
+ u_int16_t *dtp, u_int8_t *dhp)
+{
+ long t;
+ ulong_t days, year, month, inc;
+ ushort_t *months;
+
+ /*
+ * If the time from the last conversion is the same as now, then
+ * skip the computations and use the saved result.
+ */
+ smb_time_local2server(tsp, tzoff, &t);
+ t &= ~1;
+ if (lasttime != t) {
+ lasttime = t;
+ if (t < 0) {
+ /*
+ * This is before 1970, so it's before 1980,
+ * and can't be represented as a DOS time.
+ * Just represent it as the DOS epoch.
+ */
+ lastdtime = 0;
+ lastddate = (1 << DD_DAY_SHIFT)
+ + (1 << DD_MONTH_SHIFT)
+ + ((1980 - 1980) << DD_YEAR_SHIFT);
+ } else {
+ lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT)
+ + (((t / 60) % 60) << DT_MINUTES_SHIFT)
+ + (((t / 3600) % 24) << DT_HOURS_SHIFT);
+
+ /*
+ * If the number of days since 1970 is the same as
+ * the last time we did the computation then skip
+ * all this leap year and month stuff.
+ */
+ days = t / (24 * 60 * 60);
+ if (days != lastday) {
+ lastday = days;
+ for (year = 1970; ; year++) {
+ /*
+ * XXX - works in 2000, but won't
+ * work in 2100.
+ */
+ inc = year & 0x03 ? 365 : 366;
+ if (days < inc)
+ break;
+ days -= inc;
+ }
+ /*
+ * XXX - works in 2000, but won't work in 2100.
+ */
+ months = year & 0x03 ? regyear : leapyear;
+ for (month = 0; days >= months[month]; month++)
+ ;
+ if (month > 0)
+ days -= months[month - 1];
+ lastddate = ((days + 1) << DD_DAY_SHIFT)
+ + ((month + 1) << DD_MONTH_SHIFT);
+ /*
+ * Remember DOS's idea of time is relative
+ * to 1980, but UN*X's is relative to 1970.
+ * If somehow we get a time before 1980 then
+ * don't give totally crazy results.
+ */
+ if (year > 1980)
+ lastddate += (year - 1980) <<
+ DD_YEAR_SHIFT;
+ }
+ }
+ }
+ if (dtp)
+ *dtp = lastdtime;
+ if (dhp)
+ *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000;
+
+ *ddp = lastddate;
+}
+
+/*
+ * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that
+ * interval there were 8 regular years and 2 leap years.
+ */
+#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60))
+
+static ushort_t lastdosdate;
+static ulong_t lastseconds;
+
+void
+smb_dos2unixtime(uint_t dd, uint_t dt, uint_t dh, int tzoff,
+ struct timespec *tsp)
+{
+ ulong_t seconds;
+ ulong_t month;
+ ulong_t year;
+ ulong_t days;
+ ushort_t *months;
+
+ if (dd == 0) {
+ tsp->tv_sec = 0;
+ tsp->tv_nsec = 0;
+ return;
+ }
+ seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1)
+ + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60
+ + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600
+ + dh / 100;
+ /*
+ * If the year, month, and day from the last conversion are the
+ * same then use the saved value.
+ */
+ if (lastdosdate != dd) {
+ lastdosdate = (ushort_t)dd;
+ days = 0;
+ year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT;
+ days = year * 365;
+ days += year / 4 + 1; /* add in leap days */
+ /*
+ * XXX - works in 2000, but won't work in 2100.
+ */
+ if ((year & 0x03) == 0)
+ days--; /* if year is a leap year */
+ months = year & 0x03 ? regyear : leapyear;
+ month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT;
+ if (month < 1 || month > 12) {
+ month = 1;
+ }
+ if (month > 1)
+ days += months[month - 2];
+ days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1;
+ lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980;
+ }
+ smb_time_server2local(seconds + lastseconds, tzoff, tsp);
+ tsp->tv_nsec = (dh % 100) * 10000000;
+}
+
+/*
+ * In the Darwin code, this function used to compute the full path
+ * by following the chain of n_parent pointers back to the root.
+ * In the Solaris port we found the n_parent pointers inconvenient
+ * because they hold parent nodes busy. We now keep the full path
+ * in every node, so this function need only marshall the directory
+ * path, and (if provided) the separator and last component name.
+ *
+ * Note that this logic must match that in smbfs_getino
+ */
+int
+smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp,
+ const char *name, int *lenp, u_int8_t sep)
+{
+ int caseopt = SMB_CS_NONE;
+ int error, len = 0;
+ int unicode = (SMB_UNICODE_STRINGS(vcp)) ? 1 : 0;
+
+ if (SMB_DIALECT(vcp) < SMB_DIALECT_LANMAN1_0)
+ caseopt |= SMB_CS_UPPER;
+
+ if (lenp) {
+ len = *lenp;
+ *lenp = 0;
+ }
+ if (unicode) {
+ error = mb_put_padbyte(mbp);
+ if (error)
+ return (error);
+ }
+ error = smb_put_dmem(mbp, vcp,
+ dnp->n_rpath, dnp->n_rplen,
+ caseopt, lenp);
+ if (name) {
+ /* If not at root, put separator */
+ if (dnp->n_rplen > 1) {
+ if (unicode)
+ error = mb_put_uint16le(mbp, sep);
+ else
+ error = mb_put_uint8(mbp, sep);
+ if (!error && lenp)
+ *lenp += (unicode + 1);
+ if (error)
+ return (error);
+ }
+ /* Put the name */
+ error = smb_put_dmem(mbp, vcp,
+ name, len, caseopt, lenp);
+ if (error)
+ return (error);
+ }
+ /* Put NULL termination. */
+ if (unicode)
+ error = mb_put_uint16le(mbp, 0);
+ else
+ error = mb_put_uint8(mbp, 0);
+ if (!error && lenp)
+ *lenp += (unicode + 1);
+
+ return (error);
+}
+
+void
+smbfs_fname_tolocal(struct smbfs_fctx *ctx)
+{
+ int length;
+ struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
+ uchar_t *dst;
+ const ushort_t *src;
+ size_t inlen, outlen;
+ int flags = 0;
+
+ if (ctx->f_nmlen == 0)
+ return;
+
+ /* XXX: This is temporary, right? Need iconv... */
+ if (!SMB_UNICODE_STRINGS(vcp))
+ return;
+
+ /*
+ * In Unix, the UTF-8 name can be larger and
+ * in-place conversions are not supported.
+ * Note: 3,9 are the maximum UTF-8 expansion
+ * factors when converting strings from UTF-16
+ * XXX: This was removed. REVISIT
+ */
+ if (SMB_UNICODE_STRINGS(vcp))
+ length = ctx->f_nmlen * 9; /* why 9 */
+ else
+ length = ctx->f_nmlen * 3; /* why 3 */
+ length = min(length, SMB_MAXFNAMELEN);
+
+ dst = kmem_zalloc(length, KM_SLEEP);
+ outlen = length;
+ /*LINTED*/
+ src = (const ushort_t *)ctx->f_name;
+ inlen = ctx->f_nmlen / 2; /* need number of UCS-2 characters */
+ flags |= UCONV_IN_LITTLE_ENDIAN;
+
+ if (uconv_u16tou8(src, &inlen, dst, &outlen, flags) == 0) {
+ kmem_free(ctx->f_name, ctx->f_namesz);
+ ctx->f_name = (char *)dst;
+ ctx->f_namesz = length;
+ ctx->f_nmlen = (int)outlen;
+ } else
+ kmem_free(dst, length);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h
new file mode 100644
index 0000000000..18641420f0
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smbfs_subr.h,v 1.25 2005/03/17 01:23:40 lindak Exp $
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _FS_SMBFS_SMBFS_SUBR_H_
+#define _FS_SMBFS_SMBFS_SUBR_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* This defines terms used in the error messages */
+#include <sys/cmn_err.h>
+
+#if defined(DEBUG) || defined(lint)
+#define SMB_VNODE_DEBUG 1
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+/*
+ * Let's use C99 standard variadic macros!
+ * Also the C99 __func__ (function name) feature.
+ */
+#define SMBFSERR(...) \
+ smb_errmsg(CE_NOTE, __func__, __VA_ARGS__)
+#define SMBVDEBUG(...) \
+ smb_errmsg(CE_CONT, __func__, __VA_ARGS__)
+
+/*
+ * Possible lock commands
+ */
+#define SMB_LOCK_EXCL 0
+#define SMB_LOCK_SHARED 1
+#define SMB_LOCK_RELEASE 2
+
+struct mbchain;
+struct smb_cred;
+struct smb_vc;
+struct statvfs;
+struct timespec;
+
+
+/*
+ * Context to perform findfirst/findnext/findclose operations
+ */
+#define SMBFS_RDD_FINDFIRST 0x01
+#define SMBFS_RDD_EOF 0x02
+#define SMBFS_RDD_FINDSINGLE 0x04
+#define SMBFS_RDD_USESEARCH 0x08
+#define SMBFS_RDD_NOCLOSE 0x10
+#define SMBFS_RDD_GOTRNAME 0x1000
+
+/*
+ * Search context supplied by server
+ */
+#define SMB_SKEYLEN 21 /* search context */
+#define SMB_DENTRYLEN (SMB_SKEYLEN + 22) /* entire entry */
+
+struct smbfs_fctx {
+ /*
+ * Setable values
+ */
+ int f_flags; /* SMBFS_RDD_ */
+ /*
+ * Return values
+ */
+ struct smbfattr f_attr; /* current attributes */
+ char *f_name; /* current file name */
+ int f_nmlen; /* name len */
+ int f_namesz; /* memory allocated */
+ /*
+ * Internal variables
+ */
+ uint16_t f_limit; /* maximum number of entries */
+ uint16_t f_attrmask; /* SMB_FA_ */
+ int f_wclen;
+ const char *f_wildcard;
+ struct smbnode *f_dnp;
+ struct smb_cred *f_scred;
+ struct smb_share *f_ssp;
+ union {
+ struct smb_rq *uf_rq;
+ struct smb_t2rq *uf_t2;
+ } f_urq;
+ int f_left; /* entries left */
+ int f_ecnt; /* entries left in current response */
+ int f_eofs; /* entry offset in data block */
+ uchar_t f_skey[SMB_SKEYLEN]; /* server side search context */
+ uchar_t f_fname[8 + 1 + 3 + 1]; /* for 8.3 filenames */
+ uint16_t f_Sid; /* Search handle (like a FID) */
+ uint16_t f_infolevel;
+ int f_rnamelen;
+ char *f_rname; /* resume name */
+ int f_rnameofs;
+ int f_otws; /* # over-the-wire ops so far */
+ char *f_firstnm; /* first filename we got back */
+ int f_firstnmlen;
+ int f_rkey; /* resume key */
+};
+typedef struct smbfs_fctx smbfs_fctx_t;
+
+#define f_rq f_urq.uf_rq
+#define f_t2 f_urq.uf_t2
+
+
+/*
+ * smb level
+ */
+int smbfs_smb_lock(struct smbnode *np, int op, caddr_t id,
+ offset_t start, uint64_t len, int largelock,
+ struct smb_cred *scrp, uint32_t timeout);
+int smbfs_smb_qfsattr(struct smb_share *ssp, uint32_t *attrp,
+ struct smb_cred *scrp);
+int smbfs_smb_statfs(struct smb_share *ssp, statvfs64_t *sbp,
+ struct smb_cred *scrp);
+int smbfs_smb_setfsize(struct smbnode *np, uint16_t fid, uint64_t newsize,
+ struct smb_cred *scrp);
+
+int smbfs_smb_getfattr(struct smbnode *np, struct smbfattr *fap,
+ struct smb_cred *scrp);
+
+int smbfs_smb_setfattr(struct smbnode *np, uint16_t fid,
+ uint32_t attr, struct timespec *mtime, struct timespec *atime,
+ struct smb_cred *scrp);
+
+int smbfs_smb_setpattr(struct smbnode *np,
+ uint32_t attr, struct timespec *mtime, struct timespec *atime,
+ struct smb_cred *scrp);
+
+int smbfs_smb_open(struct smbnode *np, uint32_t rights, struct smb_cred *scrp,
+ int *attrcacheupdated, uint16_t *fidp, const char *name, int nmlen,
+ int xattr, len_t *sizep, uint32_t *rightsp);
+int smbfs_smb_tmpopen(struct smbnode *np, uint32_t rights,
+ struct smb_cred *scrp, uint16_t *fidp);
+int smbfs_smb_close(struct smb_share *ssp, uint16_t fid,
+ struct timespec *mtime, struct smb_cred *scrp);
+int smbfs_smb_tmpclose(struct smbnode *ssp, uint16_t fid,
+ struct smb_cred *scrp);
+int smbfs_smb_create(struct smbnode *dnp, const char *name, int len,
+ struct smb_cred *scrp, uint16_t *fidp, uint32_t disp, int xattr);
+int smbfs_smb_delete(struct smbnode *np, struct smb_cred *scrp,
+ const char *name, int len, int xattr);
+int smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp,
+ const char *tname, int tnmlen, struct smb_cred *scrp);
+int smbfs_smb_t2rename(struct smbnode *np, struct smbnode *tdnp,
+ const char *tname, int tnmlen, struct smb_cred *scrp, int overwrite);
+int smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp,
+ const char *tname, int tnmlen, uint16_t flags, struct smb_cred *scrp);
+int smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len,
+ struct smb_cred *scrp);
+int smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scrp);
+int smbfs_smb_findopen(struct smbnode *dnp, const char *wildcard, int wclen,
+ int attr, struct smb_cred *scrp, struct smbfs_fctx **ctxpp);
+int smbfs_smb_findnext(struct smbfs_fctx *ctx, int limit,
+ struct smb_cred *scrp);
+int smbfs_smb_findclose(struct smbfs_fctx *ctx, struct smb_cred *scrp);
+int smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp,
+ struct smbnode *dnp, const char *name, int *nmlenp, uint8_t sep);
+int smbfs_smb_lookup(struct smbnode *dnp, const char **namep, int *nmlenp,
+ struct smbfattr *fap, struct smb_cred *scrp);
+int smbfs_smb_hideit(struct smbnode *np, const char *name, int len,
+ struct smb_cred *scrp);
+int smbfs_smb_unhideit(struct smbnode *np, const char *name, int len,
+ struct smb_cred *scrp);
+int smbfs_smb_flush(struct smbnode *np, struct smb_cred *scrp);
+int smbfs_0extend(vnode_t *vp, uint16_t fid, len_t from, len_t to,
+ struct smb_cred *scredp, int timo);
+
+#ifdef NOT_YET
+int smbfs_smb_getsec(struct smb_share *ssp, uint16_t fid,
+ struct smb_cred *scrp, uint32_t selector, struct ntsecdesc **res);
+int smbfs_smb_setsec(struct smb_share *ssp, uint16_t fid,
+ struct smb_cred *scrp, uint32_t selector, uint16_t flags,
+ struct ntsid *owner, struct ntsid *group, struct ntacl *sacl,
+ struct ntacl *dacl);
+int smbfs_smb_qstreaminfo(struct smbnode *np, struct smb_cred *scrp,
+ uio_t uio, size_t *sizep);
+#endif /* NOT_YET */
+
+void smbfs_fname_tolocal(struct smbfs_fctx *ctx);
+
+void smb_time_local2server(struct timespec *tsp, int tzoff, long *seconds);
+void smb_time_server2local(ulong_t seconds, int tzoff, struct timespec *tsp);
+void smb_time_NT2local(uint64_t nsec, int tzoff, struct timespec *tsp);
+void smb_time_local2NT(struct timespec *tsp, int tzoff, uint64_t *nsec);
+void smb_time_unix2dos(struct timespec *tsp, int tzoff, uint16_t *ddp,
+ uint16_t *dtp, uint8_t *dhp);
+void smb_dos2unixtime(uint_t dd, uint_t dt, uint_t dh, int tzoff,
+ struct timespec *tsp);
+
+/* Stuff borrowed from NFS (and then hacked) */
+vnode_t *smbfs_make_node(vfs_t *vfsp,
+ const char *dir, int dirlen,
+ const char *name, int nmlen,
+ struct smbfattr *fap);
+void smb_addfree(smbnode_t *sp);
+void smb_addhash(smbnode_t *sp);
+void smb_rmhash(smbnode_t *);
+
+int smbfs_subrinit(void);
+void smbfs_subrfini(void);
+int smbfs_clntinit(void);
+void smbfs_clntfini(void);
+void smbfs_zonelist_add(smbmntinfo_t *smi);
+void smbfs_zonelist_remove(smbmntinfo_t *smi);
+void smbfs_destroy_table(struct vfs *vfsp);
+int smbfs_readvnode(vnode_t *, uio_t *, cred_t *, struct vattr *);
+int smbfs_writevnode(vnode_t *vp, uio_t *uiop, cred_t *cr,
+ int ioflag, int timo);
+int smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr);
+
+/* For Solaris, interruptible rwlock */
+int smbfs_rw_enter_sig(smbfs_rwlock_t *l, krw_t rw, int intr);
+int smbfs_rw_tryenter(smbfs_rwlock_t *l, krw_t rw);
+void smbfs_rw_exit(smbfs_rwlock_t *l);
+int smbfs_rw_lock_held(smbfs_rwlock_t *l, krw_t rw);
+void smbfs_rw_init(smbfs_rwlock_t *l, char *name, krw_type_t type, void *arg);
+void smbfs_rw_destroy(smbfs_rwlock_t *l);
+
+#endif /* !_FS_SMBFS_SMBFS_SUBR_H_ */
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c
new file mode 100644
index 0000000000..c7ae9131a1
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c
@@ -0,0 +1,1001 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Node hash implementation borrowed from NFS.
+ * See: uts/common/fs/nfs/nfs_subr.c
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/bitmap.h>
+#include <sys/dnlc.h>
+#include <sys/kmem.h>
+#include <sys/sunddi.h>
+
+#ifdef APPLE
+#include <sys/smb_apple.h>
+#include <sys/utfconv.h>
+#include <sys/smb_iconv.h>
+#else
+#include <netsmb/smb_osdep.h>
+#endif
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_rq.h>
+
+#include <smbfs/smbfs.h>
+#include <smbfs/smbfs_node.h>
+#include <smbfs/smbfs_subr.h>
+
+/*
+ * The hash queues for the access to active and cached smbnodes
+ * are organized as doubly linked lists. A reader/writer lock
+ * for each hash bucket is used to control access and to synchronize
+ * lookups, additions, and deletions from the hash queue.
+ *
+ * The smbnode freelist is organized as a doubly linked list with
+ * a head pointer. Additions and deletions are synchronized via
+ * a single mutex.
+ *
+ * In order to add an smbnode to the free list, it must be hashed into
+ * a hash queue and the exclusive lock to the hash queue be held.
+ * If an smbnode is not hashed into a hash queue, then it is destroyed
+ * because it represents no valuable information that can be reused
+ * about the file. The exclusive lock to the hash queue must be
+ * held in order to prevent a lookup in the hash queue from finding
+ * the smbnode and using it and assuming that the smbnode is not on the
+ * freelist. The lookup in the hash queue will have the hash queue
+ * locked, either exclusive or shared.
+ *
+ * The vnode reference count for each smbnode is not allowed to drop
+ * below 1. This prevents external entities, such as the VM
+ * subsystem, from acquiring references to vnodes already on the
+ * freelist and then trying to place them back on the freelist
+ * when their reference is released. This means that the when an
+ * smbnode is looked up in the hash queues, then either the smbnode
+ * is removed from the freelist and that reference is tranfered to
+ * the new reference or the vnode reference count must be incremented
+ * accordingly. The mutex for the freelist must be held in order to
+ * accurately test to see if the smbnode is on the freelist or not.
+ * The hash queue lock might be held shared and it is possible that
+ * two different threads may race to remove the smbnode from the
+ * freelist. This race can be resolved by holding the mutex for the
+ * freelist. Please note that the mutex for the freelist does not
+ * need to held if the smbnode is not on the freelist. It can not be
+ * placed on the freelist due to the requirement that the thread
+ * putting the smbnode on the freelist must hold the exclusive lock
+ * to the hash queue and the thread doing the lookup in the hash
+ * queue is holding either a shared or exclusive lock to the hash
+ * queue.
+ *
+ * The lock ordering is:
+ *
+ * hash bucket lock -> vnode lock
+ * hash bucket lock -> freelist lock
+ */
+static rhashq_t *smbtable;
+
+static kmutex_t smbfreelist_lock;
+static smbnode_t *smbfreelist = NULL;
+static ulong_t smbnodenew = 0;
+long nsmbnode = 0;
+
+static int smbtablesize;
+static int smbtablemask;
+static int smbhashlen = 4;
+
+static struct kmem_cache *smbnode_cache;
+
+/*
+ * Mutex to protect the following variables:
+ * smbfs_major
+ * smbfs_minor
+ */
+kmutex_t smbfs_minor_lock;
+int smbfs_major;
+int smbfs_minor;
+
+/*
+ * Local functions.
+ * Not static, to aid debugging.
+ */
+void smb_rmfree(smbnode_t *);
+void smbinactive(smbnode_t *);
+void smb_rmhash_locked(smbnode_t *);
+void smb_destroy_node(smbnode_t *);
+void smbfs_kmem_reclaim(void *cdrarg);
+
+smbnode_t *smbhashfind(struct vfs *, const char *, int, rhashq_t *);
+static vnode_t *make_smbnode(vfs_t *, char *, int, rhashq_t *, int *);
+
+
+/*
+ * Free the resources associated with an smbnode.
+ * Note: This is different from smbfs_inactive
+ *
+ * NFS: nfs_subr.c:rinactive
+ */
+void
+smbinactive(smbnode_t *np)
+{
+
+ if (np->n_rpath) {
+ kmem_free(np->n_rpath, np->n_rplen + 1);
+ np->n_rpath = NULL;
+ }
+}
+
+/*
+ * Return a vnode for the given CIFS directory and filename.
+ * If no smbnode exists for this fhandle, create one and put it
+ * into the hash queues. If the smbnode for this fhandle
+ * already exists, return it.
+ *
+ * Note: make_smbnode() may upgrade the hash bucket lock to exclusive.
+ *
+ * NFS: nfs_subr.c:makenfsnode
+ */
+vnode_t *
+smbfs_make_node(
+ vfs_t *vfsp,
+ const char *dir,
+ int dirlen,
+ const char *name,
+ int nmlen,
+ struct smbfattr *fap)
+{
+ char *rpath;
+ int rplen, idx;
+ uint32_t hash;
+ rhashq_t *rhtp;
+ smbnode_t *np;
+ vnode_t *vp;
+#ifdef NOT_YET
+ vattr_t va;
+#endif
+ int newnode;
+
+ /*
+ * Build the full path name in allocated memory
+ * so we have it for lookup, etc.
+ *
+ * ToDo: Would prefer to allocate a remote path
+ * only when we will create a new node.
+ */
+ rplen = dirlen;
+ if (name) {
+ /* If not at root, we'll add a slash. */
+ if (dirlen > 1)
+ rplen++;
+ rplen += nmlen;
+ }
+ rpath = kmem_alloc(rplen + 1, KM_SLEEP);
+
+ bcopy(dir, rpath, dirlen);
+ if (name) {
+ if (dirlen > 1)
+ rpath[dirlen++] = '\\';
+ bcopy(name, &rpath[dirlen], nmlen);
+ }
+ rpath[rplen] = 0;
+
+ hash = smbfs_hash(rpath, rplen);
+ idx = hash & smbtablemask;
+ rhtp = &smbtable[idx];
+ rw_enter(&rhtp->r_lock, RW_READER);
+
+ vp = make_smbnode(vfsp, rpath, rplen, rhtp, &newnode);
+ np = VTOSMB(vp);
+ np->n_ino = hash; /* Equivalent to: smbfs_getino() */
+
+ /*
+ * Note: make_smbnode keeps a reference to rpath in
+ * new nodes it creates, so only free when we found
+ * an existing node.
+ */
+ if (!newnode) {
+ kmem_free(rpath, rplen + 1);
+ rpath = NULL;
+ }
+
+ if (fap == NULL) {
+#ifdef NOT_YET
+ if (newnode) {
+ PURGE_ATTRCACHE(vp);
+ }
+#endif
+ rw_exit(&rhtp->r_lock);
+ return (vp);
+ }
+
+ /* Have SMB attributes. */
+ vp->v_type = (fap->fa_attr & SMB_FA_DIR) ? VDIR : VREG;
+ /* XXX: np->n_ino = fap->fa_ino; see above */
+ np->r_size = fap->fa_size;
+ /* XXX: np->r_attr = *fap here instead? */
+ np->r_atime = fap->fa_atime;
+ np->r_ctime = fap->fa_mtime;
+ np->r_mtime = fap->fa_ctime;
+
+#ifdef NOT_YET
+ if (!newnode) {
+ rw_exit(&rhtp->r_lock);
+ (void) nfs_cache_fattr(vp, attr, &va, t, cr);
+ } else {
+ if (attr->na_type < NFNON || attr->na_type > NFSOC)
+ vp->v_type = VBAD;
+ else
+ vp->v_type = n2v_type(attr);
+ vp->v_rdev = makedevice(attr->rdev.specdata1,
+ attr->rdev.specdata2);
+ nfs_attrcache(vp, attr, t);
+ rw_exit(&rhtp->r_lock);
+ }
+#else
+ rw_exit(&rhtp->r_lock);
+#endif
+
+ return (vp);
+}
+
+/*
+ * NFS: nfs_subr.c:rtablehash
+ * We use smbfs_hash().
+ */
+
+/*
+ * Find or create an smbnode.
+ * NFS: nfs_subr.c:make_rnode
+ */
+static vnode_t *
+make_smbnode(
+ vfs_t *vfsp,
+ char *rpath,
+ int rplen,
+ rhashq_t *rhtp,
+ int *newnode)
+{
+ smbnode_t *np;
+ smbnode_t *tnp;
+ vnode_t *vp;
+ smbmntinfo_t *mi;
+
+ ASSERT(RW_READ_HELD(&rhtp->r_lock));
+
+ mi = VFTOSMI(vfsp);
+
+start:
+ np = smbhashfind(vfsp, rpath, rplen, rhtp);
+ if (np != NULL) {
+ vp = SMBTOV(np);
+ *newnode = 0;
+ return (vp);
+ }
+
+ /* Note: will retake this lock below. */
+ rw_exit(&rhtp->r_lock);
+
+ /*
+ * see if we can find something on the freelist
+ */
+ mutex_enter(&smbfreelist_lock);
+ if (smbfreelist != NULL && smbnodenew >= nsmbnode) {
+ np = smbfreelist;
+ smb_rmfree(np);
+ mutex_exit(&smbfreelist_lock);
+
+ vp = SMBTOV(np);
+
+ if (np->r_flags & RHASHED) {
+ rw_enter(&np->r_hashq->r_lock, RW_WRITER);
+ mutex_enter(&vp->v_lock);
+ if (vp->v_count > 1) {
+ vp->v_count--;
+ mutex_exit(&vp->v_lock);
+ rw_exit(&np->r_hashq->r_lock);
+ rw_enter(&rhtp->r_lock, RW_READER);
+ goto start;
+ }
+ mutex_exit(&vp->v_lock);
+ smb_rmhash_locked(np);
+ rw_exit(&np->r_hashq->r_lock);
+ }
+
+ smbinactive(np);
+
+ mutex_enter(&vp->v_lock);
+ if (vp->v_count > 1) {
+ vp->v_count--;
+ mutex_exit(&vp->v_lock);
+ rw_enter(&rhtp->r_lock, RW_READER);
+ goto start;
+ }
+ mutex_exit(&vp->v_lock);
+ vn_invalid(vp);
+ /*
+ * destroy old locks before bzero'ing and
+ * recreating the locks below.
+ */
+ smbfs_rw_destroy(&np->r_rwlock);
+ smbfs_rw_destroy(&np->r_lkserlock);
+ mutex_destroy(&np->r_statelock);
+ cv_destroy(&np->r_cv);
+ /*
+ * Make sure that if smbnode is recycled then
+ * VFS count is decremented properly before
+ * reuse.
+ */
+ VFS_RELE(vp->v_vfsp);
+ vn_reinit(vp);
+ } else {
+ /*
+ * allocate and initialize a new smbnode
+ */
+ vnode_t *new_vp;
+
+ mutex_exit(&smbfreelist_lock);
+
+ np = kmem_cache_alloc(smbnode_cache, KM_SLEEP);
+ new_vp = vn_alloc(KM_SLEEP);
+
+ atomic_add_long((ulong_t *)&smbnodenew, 1);
+ vp = new_vp;
+ }
+
+ /* Initialize smbnode_t */
+ bzero(np, sizeof (*np));
+
+ smbfs_rw_init(&np->r_rwlock, NULL, RW_DEFAULT, NULL);
+ smbfs_rw_init(&np->r_lkserlock, NULL, RW_DEFAULT, NULL);
+ mutex_init(&np->r_statelock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&np->r_cv, NULL, CV_DEFAULT, NULL);
+ /* cv_init(&np->r_commit.c_cv, NULL, CV_DEFAULT, NULL); */
+
+ np->r_vnode = vp;
+ np->n_mount = mi;
+ np->r_hashq = rhtp;
+ np->n_direof = -1;
+ np->n_fid = SMB_FID_UNUSED;
+ np->n_uid = UID_NOBODY;
+ np->n_gid = GID_NOBODY;
+ /* XXX: make attributes stale? */
+
+#if 0 /* XXX dircache */
+ /*
+ * We don't know if it's a directory yet.
+ * Let the caller do this? XXX
+ */
+ avl_create(&np->r_dir, compar, sizeof (rddir_cache),
+ offsetof(rddir_cache, tree));
+#endif
+
+ /* Now fill in the vnode. */
+ vn_setops(vp, smbfs_vnodeops);
+ vp->v_data = (caddr_t)np;
+ VFS_HOLD(vfsp);
+ vp->v_vfsp = vfsp;
+ vp->v_type = VNON;
+
+ /*
+ * There is a race condition if someone else
+ * alloc's the smbnode while no locks are held, so we
+ * check again and recover if found.
+ */
+ rw_enter(&rhtp->r_lock, RW_WRITER);
+ tnp = smbhashfind(vfsp, rpath, rplen, rhtp);
+ if (tnp != NULL) {
+ vp = SMBTOV(tnp);
+ *newnode = 0;
+ rw_exit(&rhtp->r_lock);
+ /* The node we were building goes on the free list. */
+ smb_addfree(np);
+ rw_enter(&rhtp->r_lock, RW_READER);
+ return (vp);
+ }
+
+ /*
+ * Hash search identifies nodes by the full pathname,
+ * so store that before linking in the hash list.
+ * Note: caller allocates the rpath, and knows
+ * about this reference when *newnode is set.
+ */
+ np->n_rpath = rpath;
+ np->n_rplen = rplen;
+
+ smb_addhash(np);
+ *newnode = 1;
+ return (vp);
+}
+
+/*
+ * smb_addfree
+ * Put a smbnode on the free list.
+ *
+ * Normally called by smbfs_inactive, but also
+ * called in here during cleanup operations.
+ *
+ * Smbnodes which were allocated above and beyond the normal limit
+ * are immediately freed.
+ *
+ * NFS: nfs_subr.c:rp_addfree
+ */
+void
+smb_addfree(smbnode_t *np)
+{
+ vnode_t *vp;
+ struct vfs *vfsp;
+
+ vp = SMBTOV(np);
+ ASSERT(vp->v_count >= 1);
+ ASSERT(np->r_freef == NULL && np->r_freeb == NULL);
+
+ /*
+ * If we have too many smbnodes allocated and there are no
+ * references to this smbnode, or if the smbnode is no longer
+ * accessible by it does not reside in the hash queues,
+ * or if an i/o error occurred while writing to the file,
+ * then just free it instead of putting it on the smbnode
+ * freelist.
+ */
+ vfsp = vp->v_vfsp;
+ if (((smbnodenew > nsmbnode || !(np->r_flags & RHASHED) ||
+ np->r_error || (vfsp->vfs_flag & VFS_UNMOUNTED)) &&
+ np->r_count == 0)) {
+ if (np->r_flags & RHASHED) {
+ rw_enter(&np->r_hashq->r_lock, RW_WRITER);
+ mutex_enter(&vp->v_lock);
+ if (vp->v_count > 1) {
+ vp->v_count--;
+ mutex_exit(&vp->v_lock);
+ rw_exit(&np->r_hashq->r_lock);
+ return;
+ /*
+ * Will get another call later,
+ * via smbfs_inactive.
+ */
+ }
+ mutex_exit(&vp->v_lock);
+ smb_rmhash_locked(np);
+ rw_exit(&np->r_hashq->r_lock);
+ }
+
+ smbinactive(np);
+
+ /*
+ * Recheck the vnode reference count. We need to
+ * make sure that another reference has not been
+ * acquired while we were not holding v_lock. The
+ * smbnode is not in the smbnode hash queues, so the
+ * only way for a reference to have been acquired
+ * is for a VOP_PUTPAGE because the smbnode was marked
+ * with RDIRTY or for a modified page. This
+ * reference may have been acquired before our call
+ * to smbinactive. The i/o may have been completed,
+ * thus allowing smbinactive to complete, but the
+ * reference to the vnode may not have been released
+ * yet. In any case, the smbnode can not be destroyed
+ * until the other references to this vnode have been
+ * released. The other references will take care of
+ * either destroying the smbnode or placing it on the
+ * smbnode freelist. If there are no other references,
+ * then the smbnode may be safely destroyed.
+ */
+ mutex_enter(&vp->v_lock);
+ if (vp->v_count > 1) {
+ vp->v_count--;
+ mutex_exit(&vp->v_lock);
+ return;
+ }
+ mutex_exit(&vp->v_lock);
+
+ smb_destroy_node(np);
+ return;
+ }
+ /*
+ * Lock the hash queue and then recheck the reference count
+ * to ensure that no other threads have acquired a reference
+ * to indicate that the smbnode should not be placed on the
+ * freelist. If another reference has been acquired, then
+ * just release this one and let the other thread complete
+ * the processing of adding this smbnode to the freelist.
+ */
+ rw_enter(&np->r_hashq->r_lock, RW_WRITER);
+
+ mutex_enter(&vp->v_lock);
+ if (vp->v_count > 1) {
+ vp->v_count--;
+ mutex_exit(&vp->v_lock);
+ rw_exit(&np->r_hashq->r_lock);
+ return;
+ }
+ mutex_exit(&vp->v_lock);
+
+ /*
+ * If there is no cached data or metadata for this file, then
+ * put the smbnode on the front of the freelist so that it will
+ * be reused before other smbnodes which may have cached data or
+ * metadata associated with them.
+ */
+ mutex_enter(&smbfreelist_lock);
+ if (smbfreelist == NULL) {
+ np->r_freef = np;
+ np->r_freeb = np;
+ smbfreelist = np;
+ } else {
+ np->r_freef = smbfreelist;
+ np->r_freeb = smbfreelist->r_freeb;
+ smbfreelist->r_freeb->r_freef = np;
+ smbfreelist->r_freeb = np;
+ }
+ mutex_exit(&smbfreelist_lock);
+
+ rw_exit(&np->r_hashq->r_lock);
+}
+
+/*
+ * Remove an smbnode from the free list.
+ *
+ * The caller must be holding smbfreelist_lock and the smbnode
+ * must be on the freelist.
+ *
+ * NFS: nfs_subr.c:rp_rmfree
+ */
+void
+smb_rmfree(smbnode_t *np)
+{
+
+ ASSERT(MUTEX_HELD(&smbfreelist_lock));
+ ASSERT(np->r_freef != NULL && np->r_freeb != NULL);
+
+ if (np == smbfreelist) {
+ smbfreelist = np->r_freef;
+ if (np == smbfreelist)
+ smbfreelist = NULL;
+ }
+
+ np->r_freeb->r_freef = np->r_freef;
+ np->r_freef->r_freeb = np->r_freeb;
+
+ np->r_freef = np->r_freeb = NULL;
+}
+
+/*
+ * Put a smbnode in the hash table.
+ *
+ * The caller must be holding the exclusive hash queue lock.
+ *
+ * NFS: nfs_subr.c:rp_addhash
+ */
+void
+smb_addhash(smbnode_t *np)
+{
+
+ ASSERT(RW_WRITE_HELD(&np->r_hashq->r_lock));
+ ASSERT(!(np->r_flags & RHASHED));
+
+ np->r_hashf = np->r_hashq->r_hashf;
+ np->r_hashq->r_hashf = np;
+ np->r_hashb = (smbnode_t *)np->r_hashq;
+ np->r_hashf->r_hashb = np;
+
+ mutex_enter(&np->r_statelock);
+ np->r_flags |= RHASHED;
+ mutex_exit(&np->r_statelock);
+}
+
+/*
+ * Remove a smbnode from the hash table.
+ *
+ * The caller must be holding the hash queue lock.
+ *
+ * NFS: nfs_subr.c:rp_rmhash_locked
+ */
+void
+smb_rmhash_locked(smbnode_t *np)
+{
+
+ ASSERT(RW_WRITE_HELD(&np->r_hashq->r_lock));
+ ASSERT(np->r_flags & RHASHED);
+
+ np->r_hashb->r_hashf = np->r_hashf;
+ np->r_hashf->r_hashb = np->r_hashb;
+
+ mutex_enter(&np->r_statelock);
+ np->r_flags &= ~RHASHED;
+ mutex_exit(&np->r_statelock);
+}
+
+/*
+ * Remove a smbnode from the hash table.
+ *
+ * The caller must not be holding the hash queue lock.
+ */
+void
+smb_rmhash(smbnode_t *np)
+{
+
+ rw_enter(&np->r_hashq->r_lock, RW_WRITER);
+ smb_rmhash_locked(np);
+ rw_exit(&np->r_hashq->r_lock);
+}
+
+/*
+ * Lookup a smbnode by fhandle.
+ *
+ * The caller must be holding the hash queue lock, either shared or exclusive.
+ * XXX: make static?
+ *
+ * NFS: nfs_subr.c:rfind
+ */
+smbnode_t *
+smbhashfind(
+ struct vfs *vfsp,
+ const char *rpath,
+ int rplen,
+ rhashq_t *rhtp)
+{
+ smbnode_t *np;
+ vnode_t *vp;
+
+ ASSERT(RW_LOCK_HELD(&rhtp->r_lock));
+
+ for (np = rhtp->r_hashf; np != (smbnode_t *)rhtp; np = np->r_hashf) {
+ vp = SMBTOV(np);
+ if (vp->v_vfsp == vfsp &&
+ np->n_rplen == rplen &&
+ bcmp(np->n_rpath, rpath, rplen) == 0) {
+ /*
+ * remove smbnode from free list, if necessary.
+ */
+ if (np->r_freef != NULL) {
+ mutex_enter(&smbfreelist_lock);
+ /*
+ * If the smbnode is on the freelist,
+ * then remove it and use that reference
+ * as the new reference. Otherwise,
+ * need to increment the reference count.
+ */
+ if (np->r_freef != NULL) {
+ smb_rmfree(np);
+ mutex_exit(&smbfreelist_lock);
+ } else {
+ mutex_exit(&smbfreelist_lock);
+ VN_HOLD(vp);
+ }
+ } else
+ VN_HOLD(vp);
+ return (np);
+ }
+ }
+ return (NULL);
+}
+
+#ifdef SMB_VNODE_DEBUG
+int smb_check_table_debug = 1;
+#else /* SMB_VNODE_DEBUG */
+int smb_check_table_debug = 0;
+#endif /* SMB_VNODE_DEBUG */
+
+
+/*
+ * Return 1 if there is a active vnode belonging to this vfs in the
+ * smbtable cache.
+ *
+ * Several of these checks are done without holding the usual
+ * locks. This is safe because destroy_smbtable(), smb_addfree(),
+ * etc. will redo the necessary checks before actually destroying
+ * any smbnodes.
+ *
+ * NFS: nfs_subr.c:check_rtable
+ *
+ * Debugging changes here relative to NFS.
+ * Relatively harmless, so left 'em in.
+ */
+int
+smb_check_table(struct vfs *vfsp, smbnode_t *rtnp)
+{
+ smbnode_t *np;
+ vnode_t *vp;
+ int index;
+ int busycnt = 0;
+
+ for (index = 0; index < smbtablesize; index++) {
+ rw_enter(&smbtable[index].r_lock, RW_READER);
+ for (np = smbtable[index].r_hashf;
+ np != (smbnode_t *)(&smbtable[index]);
+ np = np->r_hashf) {
+ if (np == rtnp)
+ continue; /* skip the root */
+ vp = SMBTOV(np);
+ if (vp->v_vfsp != vfsp)
+ continue; /* skip other mount */
+
+ /* Now the 'busy' checks: */
+ /* Not on the free list? */
+ if (np->r_freef == NULL) {
+ SMBVDEBUG("!r_freef: node=0x%p, v_path=%s\n",
+ (void *)np, vp->v_path);
+ busycnt++;
+ }
+
+ /* Has dirty pages? */
+ if (vn_has_cached_data(vp) &&
+ (np->r_flags & RDIRTY)) {
+ SMBVDEBUG("is dirty: node=0x%p, v_path=%s\n",
+ (void *)np, vp->v_path);
+ busycnt++;
+ }
+
+ /* Other refs? (not reflected in v_count) */
+ if (np->r_count > 0) {
+ SMBVDEBUG("+r_count: node=0x%p, v_path=%s\n",
+ (void *)np, vp->v_path);
+ busycnt++;
+ }
+
+ if (busycnt && !smb_check_table_debug)
+ break;
+
+ }
+ rw_exit(&smbtable[index].r_lock);
+ }
+ return (busycnt);
+}
+
+/*
+ * Destroy inactive vnodes from the hash queues which belong to this
+ * vfs. It is essential that we destroy all inactive vnodes during a
+ * forced unmount as well as during a normal unmount.
+ *
+ * NFS: nfs_subr.c:destroy_rtable
+ */
+void
+smbfs_destroy_table(struct vfs *vfsp)
+{
+ int index;
+ smbnode_t *np;
+ smbnode_t *rlist;
+ smbnode_t *r_hashf;
+ vnode_t *vp;
+
+ rlist = NULL;
+
+ for (index = 0; index < smbtablesize; index++) {
+ rw_enter(&smbtable[index].r_lock, RW_WRITER);
+ for (np = smbtable[index].r_hashf;
+ np != (smbnode_t *)(&smbtable[index]);
+ np = r_hashf) {
+ /* save the hash pointer before destroying */
+ r_hashf = np->r_hashf;
+ vp = SMBTOV(np);
+ if (vp->v_vfsp == vfsp) {
+ mutex_enter(&smbfreelist_lock);
+ if (np->r_freef != NULL) {
+ smb_rmfree(np);
+ mutex_exit(&smbfreelist_lock);
+ smb_rmhash_locked(np);
+ np->r_hashf = rlist;
+ rlist = np;
+ } else
+ mutex_exit(&smbfreelist_lock);
+ }
+ }
+ rw_exit(&smbtable[index].r_lock);
+ }
+
+ for (np = rlist; np != NULL; np = rlist) {
+ rlist = np->r_hashf;
+ /*
+ * This call to smb_addfree will end up destroying the
+ * smbnode, but in a safe way with the appropriate set
+ * of checks done.
+ */
+ smb_addfree(np);
+ }
+
+}
+
+/*
+ * This routine destroys all the resources associated with the smbnode
+ * and then the smbnode itself.
+ *
+ * NFS: nfs_subr.c:destroy_rnode
+ */
+void
+smb_destroy_node(smbnode_t *np)
+{
+ vnode_t *vp;
+ vfs_t *vfsp;
+
+ vp = SMBTOV(np);
+ vfsp = vp->v_vfsp;
+
+ ASSERT(vp->v_count == 1);
+ ASSERT(np->r_count == 0);
+ ASSERT(np->r_mapcnt == 0);
+ ASSERT(!(np->r_flags & RHASHED));
+ ASSERT(np->r_freef == NULL && np->r_freeb == NULL);
+ atomic_add_long((ulong_t *)&smbnodenew, -1);
+ vn_invalid(vp);
+ vn_free(vp);
+ kmem_cache_free(smbnode_cache, np);
+ VFS_RELE(vfsp);
+}
+
+/* rflush? */
+/* access cache */
+/* client handles */
+
+/*
+ * initialize resources that are used by smbfs_subr.c
+ * this is called from the _init() routine (by the way of smbfs_clntinit())
+ *
+ * allocate and initialze smbfs hash table
+ * NFS: nfs_subr.c:nfs_subrinit
+ */
+int
+smbfs_subrinit(void)
+{
+ int i;
+ ulong_t nsmbnode_max;
+
+ /*
+ * Allocate and initialize the smbnode hash queues
+ */
+ if (nsmbnode <= 0)
+ nsmbnode = ncsize; /* dnlc.h */
+ nsmbnode_max = (ulong_t)((kmem_maxavail() >> 2) /
+ sizeof (struct smbnode));
+ if (nsmbnode > nsmbnode_max || (nsmbnode == 0 && ncsize == 0)) {
+ zcmn_err(GLOBAL_ZONEID, CE_NOTE,
+ "setting nsmbnode to max value of %ld", nsmbnode_max);
+ nsmbnode = nsmbnode_max;
+ }
+
+ smbtablesize = 1 << highbit(nsmbnode / smbhashlen);
+ smbtablemask = smbtablesize - 1;
+ smbtable = kmem_alloc(smbtablesize * sizeof (*smbtable), KM_SLEEP);
+ for (i = 0; i < smbtablesize; i++) {
+ smbtable[i].r_hashf = (smbnode_t *)(&smbtable[i]);
+ smbtable[i].r_hashb = (smbnode_t *)(&smbtable[i]);
+ rw_init(&smbtable[i].r_lock, NULL, RW_DEFAULT, NULL);
+ }
+ smbnode_cache = kmem_cache_create("smbnode_cache", sizeof (smbnode_t),
+ 0, NULL, NULL, smbfs_kmem_reclaim, NULL, NULL, 0);
+
+ /*
+ * Initialize the various mutexes and reader/writer locks
+ */
+ mutex_init(&smbfreelist_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&smbfs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ /*
+ * Assign unique major number for all smbfs mounts
+ */
+ if ((smbfs_major = getudev()) == -1) {
+ zcmn_err(GLOBAL_ZONEID, CE_WARN,
+ "smbfs: init: can't get unique device number");
+ smbfs_major = 0;
+ }
+ smbfs_minor = 0;
+
+ return (0);
+}
+
+/*
+ * free smbfs hash table, etc.
+ * NFS: nfs_subr.c:nfs_subrfini
+ */
+void
+smbfs_subrfini(void)
+{
+ int i;
+
+ /*
+ * Deallocate the smbnode hash queues
+ */
+ kmem_cache_destroy(smbnode_cache);
+
+ for (i = 0; i < smbtablesize; i++)
+ rw_destroy(&smbtable[i].r_lock);
+ kmem_free(smbtable, smbtablesize * sizeof (*smbtable));
+
+ /*
+ * Destroy the various mutexes and reader/writer locks
+ */
+ mutex_destroy(&smbfreelist_lock);
+ mutex_destroy(&smbfs_minor_lock);
+}
+
+/* rddir_cache ? */
+
+/*
+ * Support functions for smbfs_kmem_reclaim
+ */
+
+static int
+smbfs_node_reclaim(void)
+{
+ int freed;
+ smbnode_t *np;
+ vnode_t *vp;
+
+ freed = 0;
+ mutex_enter(&smbfreelist_lock);
+ while ((np = smbfreelist) != NULL) {
+ smb_rmfree(np);
+ mutex_exit(&smbfreelist_lock);
+ if (np->r_flags & RHASHED) {
+ vp = SMBTOV(np);
+ rw_enter(&np->r_hashq->r_lock, RW_WRITER);
+ mutex_enter(&vp->v_lock);
+ if (vp->v_count > 1) {
+ vp->v_count--;
+ mutex_exit(&vp->v_lock);
+ rw_exit(&np->r_hashq->r_lock);
+ mutex_enter(&smbfreelist_lock);
+ continue;
+ }
+ mutex_exit(&vp->v_lock);
+ smb_rmhash_locked(np);
+ rw_exit(&np->r_hashq->r_lock);
+ }
+ /*
+ * This call to smb_addfree will end up destroying the
+ * smbnode, but in a safe way with the appropriate set
+ * of checks done.
+ */
+ smb_addfree(np);
+ mutex_enter(&smbfreelist_lock);
+ }
+ mutex_exit(&smbfreelist_lock);
+ return (freed);
+}
+
+/*
+ * Called by kmem_cache_alloc ask us if we could
+ * "Please give back some memory!"
+ *
+ * Todo: dump nodes from the free list?
+ */
+/*ARGSUSED*/
+void
+smbfs_kmem_reclaim(void *cdrarg)
+{
+ (void) smbfs_node_reclaim();
+}
+
+/* nfs failover stuff */
+/* nfs_rw_xxx - see smbfs_rwlock.c */
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c
new file mode 100644
index 0000000000..f391dedd41
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c
@@ -0,0 +1,915 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smbfs_vfsops.c,v 1.73.64.1 2005/05/27 02:35:28 lindak Exp $
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/systm.h>
+#include <sys/cred.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <fs/fs_subr.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/mkdev.h>
+#include <sys/mount.h>
+#include <sys/statvfs.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+#include <sys/cmn_err.h>
+#include <sys/modctl.h>
+#include <sys/policy.h>
+#include <sys/atomic.h>
+#include <sys/zone.h>
+#include <sys/vfs_opreg.h>
+#include <sys/mntent.h>
+#include <sys/priv.h>
+#include <sys/tsol/label.h>
+#include <sys/tsol/tndb.h>
+#include <inet/ip.h>
+
+#include <netsmb/smb_osdep.h>
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_dev.h>
+
+#include <smbfs/smbfs.h>
+#include <smbfs/smbfs_node.h>
+#include <smbfs/smbfs_subr.h>
+
+/*
+ * Local functions definitions.
+ */
+int smbfsinit(int fstyp, char *name);
+void smbfsfini();
+static int smbfs_mount_label_policy(vfs_t *, void *, int, cred_t *);
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ "smbfs", /* type name string */
+ smbfsinit, /* init routine */
+ VSW_NOTZONESAFE, /* flags */
+ NULL /* mount options table prototype */
+};
+
+static struct modlfs modlfs = {
+ &mod_fsops,
+ "SMBFS filesystem v" SMBFS_VER_STR,
+ &vfw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlfs, NULL
+};
+
+/*
+ * Mutex to protect the following variables:
+ * smbfs_major
+ * smbfs_minor
+ */
+extern kmutex_t smbfs_minor_lock;
+extern int smbfs_major;
+extern int smbfs_minor;
+
+/*
+ * Prevent unloads while we have mounts
+ */
+uint32_t smbfs_mountcount;
+
+/*
+ * smbfs vfs operations.
+ */
+static int smbfs_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *);
+static int smbfs_unmount(vfs_t *, int, cred_t *);
+static int smbfs_root(vfs_t *, vnode_t **);
+static int smbfs_statvfs(vfs_t *, statvfs64_t *);
+static int smbfs_sync(vfs_t *, short, cred_t *);
+static void smbfs_freevfs(vfs_t *);
+
+/*
+ * Module loading
+ */
+
+/*
+ * This routine is invoked automatically when the kernel module
+ * containing this routine is loaded. This allows module specific
+ * initialization to be done when the module is loaded.
+ */
+int
+_init(void)
+{
+ int status;
+
+ /*
+ * Check compiled-in version of "nsmb"
+ * that we're linked with. (paranoid)
+ */
+ if (nsmb_version != NSMB_VERSION) {
+ cmn_err(CE_WARN, "_init: nsmb version mismatch");
+ return (ENOTTY);
+ }
+
+ smbfs_mountcount = 0;
+
+ if ((status = smbfs_clntinit()) != 0) {
+ cmn_err(CE_WARN, "_init: smbfs_clntinit failed");
+ return (status);
+ }
+
+ status = mod_install((struct modlinkage *)&modlinkage);
+ return (status);
+}
+
+/*
+ * Free kernel module resources that were allocated in _init
+ * and remove the linkage information into the kernel
+ */
+int
+_fini(void)
+{
+ int error;
+
+ /*
+ * If a forcedly unmounted instance is still hanging around,
+ * we cannot allow the module to be unloaded because that would
+ * cause panics once the VFS framework decides it's time to call
+ * into VFS_FREEVFS().
+ */
+ if (smbfs_mountcount)
+ return (EBUSY);
+
+ error = mod_remove(&modlinkage);
+ if (error)
+ return (error);
+
+ /*
+ * Free the allocated smbnodes, etc.
+ */
+ smbfs_clntfini();
+
+ /*
+ * Free the ops vectors
+ */
+ smbfsfini();
+ return (0);
+}
+
+/*
+ * Return information about the module
+ */
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info((struct modlinkage *)&modlinkage, modinfop));
+}
+
+/*
+ * Initialize the vfs structure
+ */
+
+int smbfsfstyp;
+vfsops_t *smbfs_vfsops = NULL;
+
+static const fs_operation_def_t smbfs_vfsops_template[] = {
+ { VFSNAME_MOUNT, { .vfs_mount = smbfs_mount } },
+ { VFSNAME_UNMOUNT, { .vfs_unmount = smbfs_unmount } },
+ { VFSNAME_ROOT, { .vfs_root = smbfs_root } },
+ { VFSNAME_STATVFS, { .vfs_statvfs = smbfs_statvfs } },
+ { VFSNAME_SYNC, { .vfs_sync = smbfs_sync } },
+ { VFSNAME_VGET, { .error = fs_nosys } },
+ { VFSNAME_MOUNTROOT, { .error = fs_nosys } },
+ { VFSNAME_FREEVFS, { .vfs_freevfs = smbfs_freevfs } },
+ { NULL, NULL }
+};
+
+int
+smbfsinit(int fstyp, char *name)
+{
+ int error;
+
+ error = vfs_setfsops(fstyp, smbfs_vfsops_template, &smbfs_vfsops);
+ if (error != 0) {
+ zcmn_err(GLOBAL_ZONEID, CE_WARN,
+ "smbfsinit: bad vfs ops template");
+ return (error);
+ }
+
+ error = vn_make_ops(name, smbfs_vnodeops_template, &smbfs_vnodeops);
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstyp);
+ zcmn_err(GLOBAL_ZONEID, CE_WARN,
+ "smbfsinit: bad vnode ops template");
+ return (error);
+ }
+
+ smbfsfstyp = fstyp;
+
+ return (0);
+}
+
+void
+smbfsfini()
+{
+ if (smbfs_vfsops) {
+ (void) vfs_freevfsops_by_type(smbfsfstyp);
+ smbfs_vfsops = NULL;
+ }
+ if (smbfs_vnodeops) {
+ vn_freevnodeops(smbfs_vnodeops);
+ smbfs_vnodeops = NULL;
+ }
+}
+
+void
+smbfs_free_smi(smbmntinfo_t *smi)
+{
+ if (smi) {
+ smbfs_zonelist_remove(smi);
+ kmem_free(smi, sizeof (smbmntinfo_t));
+ }
+}
+
+/*
+ * smbfs mount vfsop
+ * Set up mount info record and attach it to vfs struct.
+ */
+static int
+smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+ char *data = uap->dataptr;
+ int error;
+ vnode_t *rtvp = NULL; /* root of this fs */
+ smbmntinfo_t *smi = NULL;
+ dev_t smbfs_dev;
+ int version;
+ int devfd;
+ zone_t *zone = curproc->p_zone;
+ zone_t *mntzone = NULL;
+ smb_share_t *ssp = NULL;
+ smb_cred_t scred;
+
+ STRUCT_DECL(smbfs_args, args); /* smbfs mount arguments */
+
+ if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
+ return (error);
+
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ /*
+ * get arguments
+ *
+ * uap->datalen might be different from sizeof (args)
+ * in a compatible situation.
+ */
+ STRUCT_INIT(args, get_udatamodel());
+ bzero(STRUCT_BUF(args), SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE));
+ if (copyin(data, STRUCT_BUF(args), MIN(uap->datalen,
+ SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE))))
+ return (EFAULT);
+
+ /*
+ * Check mount program version
+ */
+ version = STRUCT_FGET(args, version);
+ if (version != SMBFS_VERSION) {
+ cmn_err(CE_WARN, "mount version mismatch:"
+ " kernel=%d, mount=%d\n",
+ SMBFS_VERSION, version);
+ return (EINVAL);
+ }
+
+ if (uap->flags & MS_REMOUNT) {
+ cmn_err(CE_WARN, "MS_REMOUNT not implemented");
+ return (ENOTSUP);
+ }
+
+ /*
+ * Check for busy
+ */
+ mutex_enter(&mvp->v_lock);
+ if (!(uap->flags & MS_OVERLAY) &&
+ (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+ /*
+ * Get the "share" from the netsmb driver (ssp).
+ * It is returned with a "ref" (hold) for us.
+ * Release this hold: at errout below, or in
+ * smbfs_freevfs().
+ */
+ devfd = STRUCT_FGET(args, devfd);
+ error = smb_dev2share(devfd, &ssp);
+ if (error) {
+ cmn_err(CE_WARN, "invalid device handle %d (%d)\n",
+ devfd, error);
+ return (error);
+ }
+
+ /*
+ * We don't have data structures to support multiple mounts of
+ * the same share object by the same owner, so don't allow it.
+ */
+ if (ssp->ss_mount != NULL) {
+ smb_share_rele(ssp);
+ return (EBUSY);
+ }
+
+ smb_credinit(&scred, curproc, cr);
+
+ /*
+ * Use "goto errout" from here on.
+ * See: ssp, smi, rtvp, mntzone
+ */
+
+ /*
+ * Determine the zone we're being mounted into.
+ */
+ zone_hold(mntzone = zone); /* start with this assumption */
+ if (getzoneid() == GLOBAL_ZONEID) {
+ zone_rele(mntzone);
+ mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
+ ASSERT(mntzone != NULL);
+ if (mntzone != zone) {
+ error = EBUSY;
+ goto errout;
+ }
+ }
+
+ /*
+ * Stop the mount from going any further if the zone is going away.
+ */
+ if (zone_status_get(mntzone) >= ZONE_IS_SHUTTING_DOWN) {
+ error = EBUSY;
+ goto errout;
+ }
+
+ /*
+ * On a Trusted Extensions client, we may have to force read-only
+ * for read-down mounts.
+ */
+ if (is_system_labeled()) {
+ void *addr;
+ int ipvers = 0;
+ struct smb_vc *vcp;
+
+ vcp = SSTOVC(ssp);
+ addr = smb_vc_getipaddr(vcp, &ipvers);
+ error = smbfs_mount_label_policy(vfsp, addr, ipvers, cr);
+
+ if (error > 0)
+ goto errout;
+
+ if (error == -1) {
+ /* change mount to read-only to prevent write-down */
+ vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
+ }
+ }
+
+ /*
+ * Get root vnode.
+ */
+proceed:
+
+ /*
+ * Create a mount record and link it to the vfs struct.
+ * Compare with NFS: nfsrootvp()
+ */
+ smi = kmem_zalloc(sizeof (smbmntinfo_t), KM_SLEEP);
+
+ smi->smi_share = ssp;
+ ssp->ss_mount = smi;
+ smi->smi_zone = mntzone;
+
+ /*
+ * XXX If not root, get uid/gid from the covered vnode.
+ */
+ smi->smi_args.dir_mode = STRUCT_FGET(args, dir_mode);
+ smi->smi_args.file_mode = STRUCT_FGET(args, file_mode);
+ smi->smi_args.uid = STRUCT_FGET(args, uid);
+ smi->smi_args.gid = STRUCT_FGET(args, gid);
+
+ error = smbfs_smb_qfsattr(ssp, &smi->smi_fsattr, &scred);
+ if (error) {
+ SMBVDEBUG("smbfs_smb_qfsattr error %d\n", error);
+ }
+
+#ifdef NOT_YET
+ /* Once acls are implemented, remove the ifdefs */
+ else if (smbfs_aclsflunksniff(smi, &scred)) {
+ mutex_enter(&smi->smi_lock);
+ smi->smi_fsattr &= ~FILE_PERSISTENT_ACLS;
+ mutex_exit(&smi->smi_lock);
+ }
+#endif /* NOT_YET */
+
+ /*
+ * Assign a unique device id to the mount
+ */
+ mutex_enter(&smbfs_minor_lock);
+ do {
+ smbfs_minor = (smbfs_minor + 1) & MAXMIN32;
+ smbfs_dev = makedevice(smbfs_major, smbfs_minor);
+ } while (vfs_devismounted(smbfs_dev));
+ mutex_exit(&smbfs_minor_lock);
+
+ vfsp->vfs_dev = smbfs_dev;
+ vfs_make_fsid(&vfsp->vfs_fsid, smbfs_dev, smbfsfstyp);
+ vfsp->vfs_data = (caddr_t)smi;
+ vfsp->vfs_fstype = smbfsfstyp;
+ vfsp->vfs_bsize = MAXBSIZE;
+ vfsp->vfs_bcount = 0;
+
+ smi->smi_flags = SMI_INT | SMI_LLOCK;
+ smi->smi_vfsp = vfsp;
+ smbfs_zonelist_add(smi);
+
+ /*
+ * Create the root vnode, which we need in unmount
+ * for the call to smb_check_table(), etc.
+ */
+ rtvp = smbfs_make_node(vfsp, "\\", 1, NULL, 0, NULL);
+ if (!rtvp) {
+ cmn_err(CE_WARN, "smbfs_mount: make_node failed\n");
+ return (ENOENT);
+ }
+ rtvp->v_type = VDIR;
+ rtvp->v_flag |= VROOT;
+
+ /*
+ * Could get attributes here, but that can wait
+ * until someone does a getattr call.
+ *
+ * NFS does other stuff here too:
+ * async worker threads
+ * init kstats
+ *
+ * End of code from NFS nfsrootvp()
+ */
+
+ smb_credrele(&scred);
+
+ smi->smi_root = VTOSMB(rtvp);
+
+ atomic_inc_32(&smbfs_mountcount);
+
+ return (0);
+
+errout:
+
+ ASSERT(rtvp == NULL);
+
+ vfsp->vfs_data = NULL;
+ if (smi)
+ smbfs_free_smi(smi);
+
+ if (mntzone != NULL)
+ zone_rele(mntzone);
+
+ if (ssp)
+ smb_share_rele(ssp);
+
+ smb_credrele(&scred);
+
+ /* args, if we allocated */
+
+ return (error);
+}
+
+/*
+ * vfs operations
+ */
+static int
+smbfs_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+ smbmntinfo_t *smi;
+ smbnode_t *rtnp;
+
+ smi = VFTOSMI(vfsp);
+
+ if (secpolicy_fs_unmount(cr, vfsp) != 0)
+ return (EPERM);
+
+ if ((flag & MS_FORCE) == 0) {
+#ifdef APPLE
+ smbfs_rflush(vfsp, cr);
+#endif
+
+ /*
+ * If there are any active vnodes on this file system,
+ * (other than the root vnode) then the file system is
+ * busy and can't be umounted.
+ */
+ if (smb_check_table(vfsp, smi->smi_root))
+ return (EBUSY);
+
+ /*
+ * We normally hold a ref to the root vnode, so
+ * check for references beyond the one we expect:
+ * smbmntinfo_t -> smi_root
+ * Note that NFS does not hold the root vnode.
+ */
+ if (smi->smi_root &&
+ smi->smi_root->r_vnode->v_count > 1)
+ return (EBUSY);
+ }
+
+ /*
+ * common code for both forced and non-forced
+ *
+ * Setting VFS_UNMOUNTED prevents new operations.
+ * Operations already underway may continue,
+ * but not for long.
+ */
+ vfsp->vfs_flag |= VFS_UNMOUNTED;
+
+ /*
+ * Shutdown any outstanding I/O requests on this share,
+ * and force a tree disconnect. The share object will
+ * continue to hang around until smb_share_rele().
+ * This should also cause most active nodes to be
+ * released as their operations fail with EIO.
+ */
+ smb_share_kill(smi->smi_share);
+
+ /*
+ * If we hold the root VP (and we normally do)
+ * then it's safe to release it now.
+ */
+ if (smi->smi_root) {
+ rtnp = smi->smi_root;
+ smi->smi_root = NULL;
+ VN_RELE(rtnp->r_vnode); /* release root vnode */
+ }
+
+ /*
+ * Remove all nodes from the node hash tables.
+ * This (indirectly) calls: smb_addfree, smbinactive,
+ * which will try to flush dirty pages, etc. so
+ * don't destroy the underlying share just yet.
+ *
+ * Also, with a forced unmount, some nodes may
+ * remain active, and those will get cleaned up
+ * after their last vn_rele.
+ */
+ smbfs_destroy_table(vfsp);
+
+ /*
+ * Delete our kstats...
+ *
+ * Doing it here, rather than waiting until
+ * smbfs_freevfs so these are not visible
+ * after the unmount.
+ */
+ if (smi->smi_io_kstats) {
+ kstat_delete(smi->smi_io_kstats);
+ smi->smi_io_kstats = NULL;
+ }
+ if (smi->smi_ro_kstats) {
+ kstat_delete(smi->smi_ro_kstats);
+ smi->smi_ro_kstats = NULL;
+ }
+
+ /*
+ * Note: the smb_share_rele()
+ * happens in smbfs_freevfs()
+ */
+
+ return (0);
+}
+
+
+/*
+ * find root of smbfs
+ */
+static int
+smbfs_root(vfs_t *vfsp, vnode_t **vpp)
+{
+ smbmntinfo_t *smi;
+ vnode_t *vp;
+
+ smi = VFTOSMI(vfsp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EPERM);
+
+ if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ /*
+ * The root vp is created in mount and held
+ * until unmount, so this is paranoia.
+ */
+ if (smi->smi_root == NULL)
+ return (EIO);
+
+ /* Just take a reference and return it. */
+ vp = SMBTOV(smi->smi_root);
+ VN_HOLD(vp);
+ *vpp = vp;
+
+ return (0);
+}
+
+/*
+ * Get file system statistics.
+ */
+static int
+smbfs_statvfs(vfs_t *vfsp, statvfs64_t *sbp)
+{
+ int error;
+ smbmntinfo_t *smi = VFTOSMI(vfsp);
+ smb_share_t *ssp = smi->smi_share;
+ statvfs64_t stvfs;
+ hrtime_t now;
+ smb_cred_t scred;
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EPERM);
+
+ if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ mutex_enter(&smi->smi_lock);
+
+ /*
+ * Use cached result if still valid.
+ */
+recheck:
+ now = gethrtime();
+ if (now < smi->smi_statfstime) {
+ goto cache_hit;
+ }
+
+ /*
+ * FS attributes are stale, so someone
+ * needs to do an OTW call to get them.
+ * Serialize here so only one thread
+ * does the OTW call.
+ */
+ if (smi->smi_status & SM_STATUS_STATFS_BUSY) {
+ smi->smi_status |= SM_STATUS_STATFS_WANT;
+ if (!cv_wait_sig(&smi->smi_statvfs_cv, &smi->smi_lock)) {
+ mutex_exit(&smi->smi_lock);
+ return (EINTR);
+ }
+ /* Hope status is valid now. */
+ goto recheck;
+ }
+ smi->smi_status |= SM_STATUS_STATFS_BUSY;
+ mutex_exit(&smi->smi_lock);
+
+ /*
+ * Do the OTW call. Note: lock NOT held.
+ */
+ smb_credinit(&scred, curproc, NULL);
+ bzero(&stvfs, sizeof (stvfs));
+ error = smbfs_smb_statfs(ssp, &stvfs, &scred);
+ smb_credrele(&scred);
+
+ mutex_enter(&smi->smi_lock);
+ if (smi->smi_status & SM_STATUS_STATFS_WANT)
+ cv_broadcast(&smi->smi_statvfs_cv);
+ smi->smi_status &= ~(SM_STATUS_STATFS_BUSY | SM_STATUS_STATFS_WANT);
+
+ if (error) {
+ SMBVDEBUG("statfs error=%d\n", error);
+ mutex_exit(&smi->smi_lock);
+ return (error);
+ }
+
+ /*
+ * Set a few things the OTW call didn't get.
+ */
+ stvfs.f_frsize = stvfs.f_bsize;
+ stvfs.f_favail = stvfs.f_ffree;
+ stvfs.f_fsid = (unsigned long)vfsp->vfs_fsid.val[0];
+ strncpy(stvfs.f_basetype, vfw.name, FSTYPSZ);
+ stvfs.f_flag = vf_to_stf(vfsp->vfs_flag);
+ stvfs.f_namemax = (uint32_t)MAXNAMELEN - 1;
+
+ /*
+ * Save the result, update lifetime
+ */
+ now = gethrtime();
+ smi->smi_statfstime = now +
+ (SM_MAX_STATFSTIME * (hrtime_t)NANOSEC);
+ smi->smi_statvfsbuf = stvfs; /* struct assign! */
+
+ /*
+ * Copy the statvfs data to caller's buf.
+ * Note: struct assignment
+ */
+cache_hit:
+ *sbp = smi->smi_statvfsbuf;
+ mutex_exit(&smi->smi_lock);
+ return (error);
+}
+
+static kmutex_t smbfs_syncbusy;
+
+/*
+ * Flush dirty smbfs files for file system vfsp.
+ * If vfsp == NULL, all smbfs files are flushed.
+ */
+/*ARGSUSED*/
+static int
+smbfs_sync(vfs_t *vfsp, short flag, cred_t *cr)
+{
+ /*
+ * Cross-zone calls are OK here, since this translates to a
+ * VOP_PUTPAGE(B_ASYNC), which gets picked up by the right zone.
+ */
+#ifdef APPLE
+ if (!(flag & SYNC_ATTR) && mutex_tryenter(&smbfs_syncbusy) != 0) {
+ smbfs_rflush(vfsp, cr);
+ mutex_exit(&smbfs_syncbusy);
+ }
+#endif /* APPLE */
+ return (0);
+}
+
+/*
+ * Initialization routine for VFS routines. Should only be called once
+ */
+int
+smbfs_vfsinit(void)
+{
+ mutex_init(&smbfs_syncbusy, NULL, MUTEX_DEFAULT, NULL);
+ return (0);
+}
+
+/*
+ * Shutdown routine for VFS routines. Should only be called once
+ */
+void
+smbfs_vfsfini(void)
+{
+ mutex_destroy(&smbfs_syncbusy);
+}
+
+void
+smbfs_freevfs(vfs_t *vfsp)
+{
+ smbmntinfo_t *smi;
+ smb_share_t *ssp;
+
+ /* free up the resources */
+ smi = VFTOSMI(vfsp);
+
+ /*
+ * By this time we should have already deleted the
+ * smi kstats in the unmount code. If they are still around
+ * something is wrong
+ */
+ ASSERT(smi->smi_io_kstats == NULL);
+
+ /*
+ * Drop our reference to the share.
+ * This usually leads to VC close.
+ */
+ ssp = smi->smi_share;
+ smi->smi_share = NULL;
+ ssp->ss_mount = NULL;
+
+ smb_share_rele(ssp);
+
+ zone_rele(smi->smi_zone);
+
+ smbfs_free_smi(smi);
+
+ /*
+ * Allow _fini() to succeed now, if so desired.
+ */
+ atomic_dec_32(&smbfs_mountcount);
+}
+
+/*
+ * smbfs_mount_label_policy:
+ * Determine whether the mount is allowed according to MAC check,
+ * by comparing (where appropriate) label of the remote server
+ * against the label of the zone being mounted into.
+ *
+ * Returns:
+ * 0 : access allowed
+ * -1 : read-only access allowed (i.e., read-down)
+ * >0 : error code, such as EACCES
+ *
+ * NB:
+ * NFS supports Cipso labels by parsing the vfs_resource
+ * to see what the Solaris server global zone has shared.
+ * We can't support that for CIFS since resource names
+ * contain share names, not paths.
+ */
+static int
+smbfs_mount_label_policy(vfs_t *vfsp, void *ipaddr, int addr_type, cred_t *cr)
+{
+ bslabel_t *server_sl, *mntlabel;
+ zone_t *mntzone = NULL;
+ ts_label_t *zlabel;
+ tsol_tpc_t *tp;
+ ts_label_t *tsl = NULL;
+ int retv;
+
+ /*
+ * Get the zone's label. Each zone on a labeled system has a label.
+ */
+ mntzone = zone_find_by_any_path(refstr_value(vfsp->vfs_mntpt), B_FALSE);
+ zlabel = mntzone->zone_slabel;
+ ASSERT(zlabel != NULL);
+ label_hold(zlabel);
+
+ retv = EACCES; /* assume the worst */
+
+ /*
+ * Next, get the assigned label of the remote server.
+ */
+ tp = find_tpc(ipaddr, addr_type, B_FALSE);
+ if (tp == NULL)
+ goto out; /* error getting host entry */
+
+ if (tp->tpc_tp.tp_doi != zlabel->tsl_doi)
+ goto rel_tpc; /* invalid domain */
+ if ((tp->tpc_tp.host_type != UNLABELED))
+ goto rel_tpc; /* invalid hosttype */
+
+ server_sl = &tp->tpc_tp.tp_def_label;
+ mntlabel = label2bslabel(zlabel);
+
+ /*
+ * Now compare labels to complete the MAC check. If the labels
+ * are equal or if the requestor is in the global zone and has
+ * NET_MAC_AWARE, then allow read-write access. (Except for
+ * mounts into the global zone itself; restrict these to
+ * read-only.)
+ *
+ * If the requestor is in some other zone, but his label
+ * dominates the server, then allow read-down.
+ *
+ * Otherwise, access is denied.
+ */
+ if (blequal(mntlabel, server_sl) ||
+ (crgetzoneid(cr) == GLOBAL_ZONEID &&
+ getpflags(NET_MAC_AWARE, cr) != 0)) {
+ if ((mntzone == global_zone) ||
+ !blequal(mntlabel, server_sl))
+ retv = -1; /* read-only */
+ else
+ retv = 0; /* access OK */
+ } else if (bldominates(mntlabel, server_sl)) {
+ retv = -1; /* read-only */
+ } else {
+ retv = EACCES;
+ }
+
+ if (tsl != NULL)
+ label_rele(tsl);
+
+rel_tpc:
+ /*LINTED*/
+ TPC_RELE(tp);
+out:
+ if (mntzone)
+ zone_rele(mntzone);
+ label_rele(zlabel);
+ return (retv);
+}
diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c
new file mode 100644
index 0000000000..63754da4f4
--- /dev/null
+++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c
@@ -0,0 +1,2498 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smbfs_vnops.c,v 1.128.36.1 2005/05/27 02:35:28 lindak Exp $
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/systm.h>
+#include <sys/cred.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/uio.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/dnlc.h>
+#include <sys/vfs_opreg.h>
+#include <sys/policy.h>
+
+#include <netsmb/smb_osdep.h>
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+
+#include <smbfs/smbfs.h>
+#include <smbfs/smbfs_node.h>
+#include <smbfs/smbfs_subr.h>
+
+#include <fs/fs_subr.h>
+
+/*
+ * These characters are illegal in NTFS file names.
+ * ref: http://support.microsoft.com/kb/147438
+ */
+static const char illegal_chars[] = {
+ '\\', /* back slash */
+ '/', /* slash */
+ ':', /* colon */
+ '*', /* asterisk */
+ '?', /* question mark */
+ '"', /* double quote */
+ '<', /* less than sign */
+ '>', /* greater than sign */
+ '|', /* vertical bar */
+ 0
+};
+
+/*
+ * Turning this on causes nodes to be created in the cache
+ * during directory listings. The "fast" claim is debatable,
+ * and the effects on the cache can be undesirable.
+ */
+
+/* local static function defines */
+
+static int smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr,
+ int dnlc, caller_context_t *);
+static int smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm,
+ cred_t *cr, caller_context_t *);
+static int smbfssetattr(vnode_t *, struct vattr *, int, cred_t *);
+static int smbfs_accessx(void *, int, cred_t *);
+static int smbfs_readvdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp,
+ caller_context_t *);
+/*
+ * These are the vnode ops routines which implement the vnode interface to
+ * the networked file system. These routines just take their parameters,
+ * make them look networkish by putting the right info into interface structs,
+ * and then calling the appropriate remote routine(s) to do the work.
+ *
+ * Note on directory name lookup cacheing: If we detect a stale fhandle,
+ * we purge the directory cache relative to that vnode. This way, the
+ * user won't get burned by the cache repeatedly. See <smbfs/smbnode.h> for
+ * more details on smbnode locking.
+ */
+
+static int smbfs_open(vnode_t **, int, cred_t *, caller_context_t *);
+static int smbfs_close(vnode_t *, int, int, offset_t, cred_t *,
+ caller_context_t *);
+static int smbfs_read(vnode_t *, struct uio *, int, cred_t *,
+ caller_context_t *);
+static int smbfs_write(vnode_t *, struct uio *, int, cred_t *,
+ caller_context_t *);
+static int smbfs_getattr(vnode_t *, struct vattr *, int, cred_t *,
+ caller_context_t *);
+static int smbfs_setattr(vnode_t *, struct vattr *, int, cred_t *,
+ caller_context_t *);
+static int smbfs_access(vnode_t *, int, int, cred_t *, caller_context_t *);
+static int smbfs_fsync(vnode_t *, int, cred_t *, caller_context_t *);
+static void smbfs_inactive(vnode_t *, cred_t *, caller_context_t *);
+static int smbfs_lookup(vnode_t *, char *, vnode_t **, struct pathname *,
+ int, vnode_t *, cred_t *, caller_context_t *,
+ int *, pathname_t *);
+static int smbfs_create(vnode_t *, char *, struct vattr *, enum vcexcl,
+ int, vnode_t **, cred_t *, int, caller_context_t *,
+ vsecattr_t *);
+static int smbfs_remove(vnode_t *, char *, cred_t *, caller_context_t *,
+ int);
+static int smbfs_rename(vnode_t *, char *, vnode_t *, char *, cred_t *,
+ caller_context_t *, int);
+static int smbfs_mkdir(vnode_t *, char *, struct vattr *, vnode_t **,
+ cred_t *, caller_context_t *, int, vsecattr_t *);
+static int smbfs_rmdir(vnode_t *, char *, vnode_t *, cred_t *,
+ caller_context_t *, int);
+static int smbfs_readdir(vnode_t *, struct uio *, cred_t *, int *,
+ caller_context_t *, int);
+static int smbfs_rwlock(vnode_t *, int, caller_context_t *);
+static void smbfs_rwunlock(vnode_t *, int, caller_context_t *);
+static int smbfs_seek(vnode_t *, offset_t, offset_t *, caller_context_t *);
+static int smbfs_frlock(vnode_t *, int, struct flock64 *, int, offset_t,
+ struct flk_callback *, cred_t *, caller_context_t *);
+static int smbfs_space(vnode_t *, int, struct flock64 *, int, offset_t,
+ cred_t *, caller_context_t *);
+static int smbfs_pathconf(vnode_t *, int, ulong_t *, cred_t *,
+ caller_context_t *);
+static int smbfs_shrlock(vnode_t *, int, struct shrlock *, int, cred_t *,
+ caller_context_t *);
+
+/* Dummy function to use until correct function is ported in */
+int noop_vnodeop() {
+ return (0);
+}
+
+struct vnodeops *smbfs_vnodeops = NULL;
+
+/*
+ * Most unimplemented ops will return ENOSYS because of fs_nosys().
+ * The only ops where that won't work are ACCESS (due to open(2)
+ * failures) and GETSECATTR (due to acl(2) failures).
+ */
+const fs_operation_def_t smbfs_vnodeops_template[] = {
+ { VOPNAME_OPEN, { .vop_open = smbfs_open } },
+ { VOPNAME_CLOSE, { .vop_close = smbfs_close } },
+ { VOPNAME_READ, { .vop_read = smbfs_read } },
+ { VOPNAME_WRITE, { .vop_write = smbfs_write } },
+ { VOPNAME_IOCTL, { .error = fs_nosys } }, /* smbfs_ioctl, */
+ { VOPNAME_GETATTR, { .vop_getattr = smbfs_getattr } },
+ { VOPNAME_SETATTR, { .vop_setattr = smbfs_setattr } },
+ { VOPNAME_ACCESS, { .vop_access = smbfs_access } },
+ { VOPNAME_LOOKUP, { .vop_lookup = smbfs_lookup } },
+ { VOPNAME_CREATE, { .vop_create = smbfs_create } },
+ { VOPNAME_REMOVE, { .vop_remove = smbfs_remove } },
+ { VOPNAME_LINK, { .error = fs_nosys } }, /* smbfs_link, */
+ { VOPNAME_RENAME, { .vop_rename = smbfs_rename } },
+ { VOPNAME_MKDIR, { .vop_mkdir = smbfs_mkdir } },
+ { VOPNAME_RMDIR, { .vop_rmdir = smbfs_rmdir } },
+ { VOPNAME_READDIR, { .vop_readdir = smbfs_readdir } },
+ { VOPNAME_SYMLINK, { .error = fs_nosys } }, /* smbfs_symlink, */
+ { VOPNAME_READLINK, { .error = fs_nosys } }, /* smbfs_readlink, */
+ { VOPNAME_FSYNC, { .vop_fsync = smbfs_fsync } },
+ { VOPNAME_INACTIVE, { .vop_inactive = smbfs_inactive } },
+ { VOPNAME_FID, { .error = fs_nosys } }, /* smbfs_fid, */
+ { VOPNAME_RWLOCK, { .vop_rwlock = smbfs_rwlock } },
+ { VOPNAME_RWUNLOCK, { .vop_rwunlock = smbfs_rwunlock } },
+ { VOPNAME_SEEK, { .vop_seek = smbfs_seek } },
+ { VOPNAME_FRLOCK, { .vop_frlock = smbfs_frlock } },
+ { VOPNAME_SPACE, { .vop_space = smbfs_space } },
+ { VOPNAME_REALVP, { .error = fs_nosys } }, /* smbfs_realvp, */
+ { VOPNAME_GETPAGE, { .error = fs_nosys } }, /* smbfs_getpage, */
+ { VOPNAME_PUTPAGE, { .error = fs_nosys } }, /* smbfs_putpage, */
+ { VOPNAME_MAP, { .error = fs_nosys } }, /* smbfs_map, */
+ { VOPNAME_ADDMAP, { .error = fs_nosys } }, /* smbfs_addmap, */
+ { VOPNAME_DELMAP, { .error = fs_nosys } }, /* smbfs_delmap, */
+ { VOPNAME_DUMP, { .error = fs_nosys } }, /* smbfs_dump, */
+ { VOPNAME_PATHCONF, { .vop_pathconf = smbfs_pathconf } },
+ { VOPNAME_PAGEIO, { .error = fs_nosys } }, /* smbfs_pageio, */
+ { VOPNAME_SETSECATTR, { .error = fs_nosys } }, /* smbfs_setsecattr, */
+ { VOPNAME_GETSECATTR, { .error = noop_vnodeop } },
+ /* smbfs_getsecattr, */
+ { VOPNAME_SHRLOCK, { .vop_shrlock = smbfs_shrlock } },
+ { NULL, NULL }
+};
+
+/*
+ * XXX
+ * When new and relevant functionality is enabled, we should be
+ * calling vfs_set_feature() to inform callers that pieces of
+ * functionality are available, per PSARC 2007/227, e.g.
+ *
+ * VFSFT_XVATTR Supports xvattr for attrs
+ * VFSFT_CASEINSENSITIVE Supports case-insensitive
+ * VFSFT_NOCASESENSITIVE NOT case-sensitive
+ * VFSFT_DIRENTFLAGS Supports dirent flags
+ * VFSFT_ACLONCREATE Supports ACL on create
+ * VFSFT_ACEMASKONACCESS Can use ACEMASK for access
+ */
+/* ARGSUSED */
+static int
+smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
+{
+ struct vattr va;
+ smbnode_t *np;
+ vnode_t *vp;
+ u_int32_t rights, rightsrcvd;
+ u_int16_t fid, oldfid;
+ struct smb_cred scred;
+ smbmntinfo_t *smi;
+ cred_t *oldcr;
+ int attrcacheupdated = 0;
+ int tmperror;
+ int error = 0;
+
+ vp = *vpp;
+ np = VTOSMB(vp);
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ if (vp->v_type != VREG && vp->v_type != VDIR) { /* XXX VLNK? */
+ SMBVDEBUG("open eacces vtype=%d\n", vp->v_type);
+ return (EACCES);
+ }
+
+ /*
+ * Get exclusive access to n_fid and related stuff.
+ * No returns after this until out.
+ */
+ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, SMBINTR(vp)))
+ return (EINTR);
+ smb_credinit(&scred, curproc, cr);
+
+ /*
+ * Directory open is easy.
+ */
+ if (vp->v_type == VDIR) {
+ np->n_dirrefs++;
+ goto have_fid;
+ }
+
+ /*
+ * If caller specified O_TRUNC/FTRUNC, then be sure to set
+ * FWRITE (to drive successful setattr(size=0) after open)
+ */
+ if (flag & FTRUNC)
+ flag |= FWRITE;
+
+ /*
+ * If we already have it open, check to see if current rights
+ * are sufficient for this open.
+ */
+ if (np->n_fidrefs) {
+ int upgrade = 0;
+
+ /* BEGIN CSTYLED */
+ if ((flag & FWRITE) &&
+ !(np->n_rights & (SA_RIGHT_FILE_WRITE_DATA |
+ GENERIC_RIGHT_ALL_ACCESS |
+ GENERIC_RIGHT_WRITE_ACCESS)))
+ upgrade = 1;
+ if ((flag & FREAD) &&
+ !(np->n_rights & (SA_RIGHT_FILE_READ_DATA |
+ GENERIC_RIGHT_ALL_ACCESS |
+ GENERIC_RIGHT_READ_ACCESS)))
+ upgrade = 1;
+ /* END CSTYLED */
+ if (!upgrade) {
+ /*
+ * the existing open is good enough
+ */
+ np->n_fidrefs++;
+ goto have_fid;
+ }
+ }
+ rights = np->n_fidrefs ? np->n_rights : 0;
+
+ /*
+ * we always ask for READ_CONTROL so we can always get the
+ * owner/group IDs to satisfy a stat.
+ * XXX: verify that works with "drop boxes"
+ */
+ rights |= STD_RIGHT_READ_CONTROL_ACCESS;
+ if ((flag & FREAD))
+ rights |= SA_RIGHT_FILE_READ_DATA;
+ if ((flag & FWRITE))
+ rights |= SA_RIGHT_FILE_APPEND_DATA | SA_RIGHT_FILE_WRITE_DATA;
+
+ /* XXX: open gets the current size, but we don't use it. */
+ error = smbfs_smb_open(np, rights, &scred, &attrcacheupdated, &fid,
+ NULL, 0, 0, NULL, &rightsrcvd);
+ if (error)
+ goto out;
+
+ /*
+ * We have a new FID and access rights.
+ */
+ oldfid = np->n_fid;
+ np->n_fid = fid;
+ np->n_rights = rightsrcvd;
+ np->n_fidrefs++;
+ if (np->n_fidrefs > 1) {
+ /*
+ * We already had it open (presumably because
+ * it was open with insufficient rights.)
+ * Close old wire-open.
+ */
+ tmperror = smbfs_smb_close(smi->smi_share,
+ oldfid, &np->n_mtime, &scred);
+ if (tmperror)
+ SMBVDEBUG("error %d closing %s\n",
+ tmperror, np->n_rpath);
+ }
+
+ /*
+ * This thread did the open.
+ * Save our credentials too.
+ */
+ mutex_enter(&np->r_statelock);
+ oldcr = np->r_cred;
+ np->r_cred = cr;
+ crhold(cr);
+ if (oldcr)
+ crfree(oldcr);
+ mutex_exit(&np->r_statelock);
+
+have_fid:
+ /* Get attributes (maybe). */
+
+
+ /* Darwin (derived) code. */
+
+ va.va_mask = AT_MTIME;
+ if (np->n_flag & NMODIFIED)
+ smbfs_attr_cacheremove(np);
+
+ /*
+ * Try to get attributes, but don't bail on error.
+ * We already hold r_lkserlock/reader so note:
+ * this call will recursively take r_lkserlock.
+ */
+ tmperror = smbfsgetattr(vp, &va, cr);
+ if (tmperror)
+ SMBERROR("getattr failed, error=%d", tmperror);
+ else
+ np->n_mtime.tv_sec = va.va_mtime.tv_sec;
+
+out:
+ smb_credrele(&scred);
+ smbfs_rw_exit(&np->r_lkserlock);
+ return (error);
+}
+
+/*ARGSUSED*/
+static int
+smbfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+ caller_context_t *ct)
+{
+ smbnode_t *np;
+ int error = 0;
+ struct smb_cred scred;
+
+ np = VTOSMB(vp);
+
+ /*
+ * Don't "bail out" for VFS_UNMOUNTED here,
+ * as we want to do cleanup, etc.
+ */
+
+ /*
+ * zone_enter(2) prevents processes from changing zones with SMBFS files
+ * open; if we happen to get here from the wrong zone we can't do
+ * anything over the wire.
+ */
+ if (VTOSMI(vp)->smi_zone != curproc->p_zone) {
+ /*
+ * We could attempt to clean up locks, except we're sure
+ * that the current process didn't acquire any locks on
+ * the file: any attempt to lock a file belong to another zone
+ * will fail, and one can't lock an SMBFS file and then change
+ * zones, as that fails too.
+ *
+ * Returning an error here is the sane thing to do. A
+ * subsequent call to VN_RELE() which translates to a
+ * smbfs_inactive() will clean up state: if the zone of the
+ * vnode's origin is still alive and kicking, an async worker
+ * thread will handle the request (from the correct zone), and
+ * everything (minus the final smbfs_getattr_otw() call) should
+ * be OK. If the zone is going away smbfs_async_inactive() will
+ * throw away cached pages inline.
+ */
+ return (EIO);
+ }
+
+ /*
+ * If we are using local locking for this filesystem, then
+ * release all of the SYSV style record locks. Otherwise,
+ * we are doing network locking and we need to release all
+ * of the network locks. All of the locks held by this
+ * process on this file are released no matter what the
+ * incoming reference count is.
+ */
+ if (VTOSMI(vp)->smi_flags & SMI_LLOCK) {
+ cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
+ cleanshares(vp, ttoproc(curthread)->p_pid);
+ }
+
+ if (count > 1)
+ return (0);
+ /*
+ * OK, do "last close" stuff.
+ */
+
+
+ /*
+ * Do the CIFS close.
+ * Darwin code
+ */
+
+ /*
+ * Exclusive lock for modifying n_fid stuff.
+ * Don't want this one ever interruptible.
+ */
+ (void) smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, 0);
+ smb_credinit(&scred, curproc, cr);
+
+ error = 0;
+ if (vp->v_type == VDIR) {
+ struct smbfs_fctx *fctx;
+ ASSERT(np->n_dirrefs > 0);
+ if (--np->n_dirrefs)
+ goto out;
+ if ((fctx = np->n_dirseq) != NULL) {
+ np->n_dirseq = NULL;
+ error = smbfs_smb_findclose(fctx, &scred);
+ }
+ } else {
+ uint16_t ofid;
+ ASSERT(np->n_fidrefs > 0);
+ if (--np->n_fidrefs)
+ goto out;
+ if ((ofid = np->n_fid) != SMB_FID_UNUSED) {
+ np->n_fid = SMB_FID_UNUSED;
+ error = smbfs_smb_close(np->n_mount->smi_share,
+ ofid, NULL, &scred);
+ }
+ }
+ if (error) {
+ SMBERROR("error %d closing %s\n",
+ error, np->n_rpath);
+ }
+
+ if (np->n_flag & NATTRCHANGED)
+ smbfs_attr_cacheremove(np);
+
+out:
+ smb_credrele(&scred);
+ smbfs_rw_exit(&np->r_lkserlock);
+
+ /* don't return any errors */
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+smbfs_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ int error;
+ struct vattr va;
+ smbmntinfo_t *smi;
+ smbnode_t *np;
+ /* u_offset_t off; */
+ /* offset_t diff; */
+
+ np = VTOSMB(vp);
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ ASSERT(smbfs_rw_lock_held(&np->r_rwlock, RW_READER));
+
+ if (vp->v_type != VREG)
+ return (EISDIR);
+
+ if (uiop->uio_resid == 0)
+ return (0);
+
+ /*
+ * Like NFS3, just check for 63-bit overflow.
+ * Our SMB layer takes care to return EFBIG
+ * when it has to fallback to a 32-bit call.
+ */
+ if (uiop->uio_loffset < 0 ||
+ uiop->uio_loffset + uiop->uio_resid < 0)
+ return (EINVAL);
+
+ /* Shared lock for n_fid use in smbfs_readvnode */
+ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
+ return (EINTR);
+
+ /* get vnode attributes from server */
+ va.va_mask = AT_SIZE | AT_MTIME;
+ if (error = smbfsgetattr(vp, &va, cr))
+ goto out;
+
+ /* should probably update mtime with mtime from server here */
+
+ /*
+ * Darwin had a loop here that handled paging stuff.
+ * Solaris does paging differently, so no loop needed.
+ */
+ error = smbfs_readvnode(vp, uiop, cr, &va);
+
+out:
+ smbfs_rw_exit(&np->r_lkserlock);
+ return (error);
+
+}
+
+
+/* ARGSUSED */
+static int
+smbfs_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ int error;
+ smbmntinfo_t *smi;
+ smbnode_t *np;
+ int timo = SMBWRTTIMO;
+
+ np = VTOSMB(vp);
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ ASSERT(smbfs_rw_lock_held(&np->r_rwlock, RW_WRITER));
+
+ if (vp->v_type != VREG)
+ return (EISDIR);
+
+ if (uiop->uio_resid == 0)
+ return (0);
+
+ /* Shared lock for n_fid use in smbfs_writevnode */
+ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
+ return (EINTR);
+
+
+ /*
+ * Darwin had a loop here that handled paging stuff.
+ * Solaris does paging differently, so no loop needed.
+ */
+ error = smbfs_writevnode(vp, uiop, cr, ioflag, timo);
+
+ smbfs_rw_exit(&np->r_lkserlock);
+ return (error);
+
+}
+
+
+/*
+ * Return either cached or remote attributes. If get remote attr
+ * use them to check and invalidate caches, then cache the new attributes.
+ *
+ * XXX
+ * This op should eventually support PSARC 2007/315, Extensible Attribute
+ * Interfaces, for richer metadata.
+ */
+/* ARGSUSED */
+static int
+smbfs_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ smbnode_t *np;
+ smbmntinfo_t *smi;
+
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ /*
+ * If it has been specified that the return value will
+ * just be used as a hint, and we are only being asked
+ * for size, fsid or rdevid, then return the client's
+ * notion of these values without checking to make sure
+ * that the attribute cache is up to date.
+ * The whole point is to avoid an over the wire GETATTR
+ * call.
+ */
+ np = VTOSMB(vp);
+ if (flags & ATTR_HINT) {
+ if (vap->va_mask ==
+ (vap->va_mask & (AT_SIZE | AT_FSID | AT_RDEV))) {
+ mutex_enter(&np->r_statelock);
+ if (vap->va_mask | AT_SIZE)
+ vap->va_size = np->r_size;
+ if (vap->va_mask | AT_FSID)
+ vap->va_fsid = np->r_attr.va_fsid;
+ if (vap->va_mask | AT_RDEV)
+ vap->va_rdev = np->r_attr.va_rdev;
+ mutex_exit(&np->r_statelock);
+ return (0);
+ }
+ }
+
+
+ return (smbfsgetattr(vp, vap, cr));
+}
+
+/*
+ * Mostly from Darwin smbfs_getattr()
+ */
+int
+smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr)
+{
+ int error;
+ smbnode_t *np;
+ struct smb_cred scred;
+ struct smbfattr fattr;
+
+ ASSERT(curproc->p_zone == VTOSMI(vp)->smi_zone);
+
+ np = VTOSMB(vp);
+
+ /*
+ * If we've got cached attributes, we're done, otherwise go
+ * to the server to get attributes, which will update the cache
+ * in the process.
+ *
+ * This section from Darwin smbfs_getattr,
+ * but then modified a lot.
+ */
+ error = smbfs_attr_cachelookup(vp, vap);
+ if (error != ENOENT)
+ return (error);
+
+ /* Shared lock for (possible) n_fid use. */
+ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
+ return (EINTR);
+ smb_credinit(&scred, curproc, cr);
+
+ error = smbfs_smb_getfattr(np, &fattr, &scred);
+
+ smb_credrele(&scred);
+ smbfs_rw_exit(&np->r_lkserlock);
+
+ if (!error) {
+ smbfs_attr_cacheenter(vp, &fattr);
+ error = smbfs_attr_cachelookup(vp, vap);
+ }
+ return (error);
+}
+
+/*
+ * XXX
+ * This op should eventually support PSARC 2007/315, Extensible Attribute
+ * Interfaces, for richer metadata.
+ */
+/*ARGSUSED4*/
+static int
+smbfs_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ int error;
+ uint_t mask;
+ struct vattr oldva;
+ smbmntinfo_t *smi;
+
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ mask = vap->va_mask;
+ if (mask & AT_NOSET)
+ return (EINVAL);
+
+ oldva.va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID;
+ error = smbfsgetattr(vp, &oldva, cr);
+ if (error)
+ return (error);
+
+ error = secpolicy_vnode_setattr(cr, vp, vap, &oldva, flags,
+ smbfs_accessx, vp);
+ if (error)
+ return (error);
+
+ return (smbfssetattr(vp, vap, flags, cr));
+}
+
+/*
+ * Mostly from Darwin smbfs_setattr()
+ * but then modified a lot.
+ */
+/* ARGSUSED */
+static int
+smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr)
+{
+ int error = 0;
+ smbnode_t *np = VTOSMB(vp);
+ smbmntinfo_t *smi = VTOSMI(vp);
+ uint_t mask = vap->va_mask;
+ struct timespec *mtime, *atime;
+ struct smb_cred scred;
+ int cerror, modified = 0;
+ unsigned short fid;
+ int have_fid = 0;
+ uint32_t rights = 0;
+
+ ASSERT(curproc->p_zone == smi->smi_zone);
+
+ /*
+ * If our caller is trying to set multiple attributes, they
+ * can make no assumption about what order they are done in.
+ * Here we try to do them in order of decreasing likelihood
+ * of failure, just to minimize the chance we'll wind up
+ * with a partially complete request.
+ */
+
+ /* Shared lock for (possible) n_fid use. */
+ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
+ return (EINTR);
+ smb_credinit(&scred, curproc, cr);
+
+ /*
+ * Will we need an open handle for this setattr?
+ * If so, what rights will we need?
+ */
+ if (mask & (AT_ATIME | AT_MTIME)) {
+ rights |=
+ SA_RIGHT_FILE_WRITE_ATTRIBUTES |
+ GENERIC_RIGHT_ALL_ACCESS |
+ GENERIC_RIGHT_WRITE_ACCESS;
+ }
+ if (mask & AT_SIZE) {
+ rights |=
+ SA_RIGHT_FILE_WRITE_DATA |
+ SA_RIGHT_FILE_APPEND_DATA;
+ /*
+ * Only SIZE requires a handle.
+ * XXX May be more reliable to just
+ * always get the file handle here.
+ */
+ error = smbfs_smb_tmpopen(np, rights, &scred, &fid);
+ if (error) {
+ SMBVDEBUG("error %d opening %s\n",
+ error, np->n_rpath);
+ goto out;
+ }
+ have_fid = 1;
+ }
+
+
+ /*
+ * If the server supports the UNIX extensions, right here is where
+ * we'd support changes to uid, gid, mode, and possibly va_flags.
+ * For now we claim to have made any such changes.
+ */
+
+ if (mask & AT_SIZE) {
+ /*
+ * If the new file size is less than what the client sees as
+ * the file size, then just change the size and invalidate
+ * the pages.
+ * I am commenting this code at present because the function
+ * smbfs_putapage() is not yet implemented.
+ */
+
+ /*
+ * Set the file size to vap->va_size.
+ */
+ ASSERT(have_fid);
+ error = smbfs_smb_setfsize(np, fid, vap->va_size, &scred);
+ if (error) {
+ SMBVDEBUG("setsize error %d file %s\n",
+ error, np->n_rpath);
+ } else {
+ /*
+ * Darwin had code here to zero-extend.
+ * Tests indicate the server will zero-fill,
+ * so looks like we don't need to do this.
+ * Good thing, as this could take forever.
+ */
+ mutex_enter(&np->r_statelock);
+ np->r_size = vap->va_size;
+ mutex_exit(&np->r_statelock);
+ modified = 1;
+ }
+ }
+
+ /*
+ * XXX: When Solaris has create_time, set that too.
+ * Note: create_time is different from ctime.
+ */
+ mtime = ((mask & AT_MTIME) ? &vap->va_mtime : 0);
+ atime = ((mask & AT_ATIME) ? &vap->va_atime : 0);
+
+ if (mtime || atime) {
+ /*
+ * If file is opened with write-attributes capability,
+ * we use handle-based calls. If not, we use path-based ones.
+ */
+ if (have_fid) {
+ error = smbfs_smb_setfattr(np, fid,
+ np->n_dosattr, mtime, atime, &scred);
+ } else {
+ error = smbfs_smb_setpattr(np,
+ np->n_dosattr, mtime, atime, &scred);
+ }
+ if (error) {
+ SMBVDEBUG("set times error %d file %s\n",
+ error, np->n_rpath);
+ } else {
+ /* XXX: set np->n_mtime, etc? */
+ modified = 1;
+ }
+ }
+
+out:
+ if (modified) {
+ /*
+ * Invalidate attribute cache in case if server doesn't set
+ * required attributes.
+ */
+ smbfs_attr_cacheremove(np);
+ /*
+ * XXX Darwin called _getattr here to
+ * update the mtime. Should we?
+ */
+ }
+
+ if (have_fid) {
+ cerror = smbfs_smb_tmpclose(np, fid, &scred);
+ if (cerror)
+ SMBERROR("error %d closing %s\n",
+ cerror, np->n_rpath);
+ }
+
+ smb_credrele(&scred);
+ smbfs_rw_exit(&np->r_lkserlock);
+
+ return (error);
+}
+
+/*
+ * smbfs_access_rwx()
+ * Common function for smbfs_access, etc.
+ *
+ * The security model implemented by the FS is unusual
+ * due to our "single user mounts" restriction.
+ *
+ * All access under a given mount point uses the CIFS
+ * credentials established by the owner of the mount.
+ * The Unix uid/gid/mode information is not (easily)
+ * provided by CIFS, and is instead fabricated using
+ * settings held in the mount structure.
+ *
+ * Most access checking is handled by the CIFS server,
+ * but we need sufficient Unix access checks here to
+ * prevent other local Unix users from having access
+ * to objects under this mount that the uid/gid/mode
+ * settings in the mount would not allow.
+ *
+ * With this model, there is a case where we need the
+ * ability to do an access check before we have the
+ * vnode for an object. This function takes advantage
+ * of the fact that the uid/gid/mode is per mount, and
+ * avoids the need for a vnode.
+ *
+ * We still (sort of) need a vnode when we call
+ * secpolicy_vnode_access, but that only uses
+ * the vtype field, so we can use a pair of fake
+ * vnodes that have only v_type filled in.
+ *
+ * XXX: Later, add a new secpolicy_vtype_access()
+ * that takes the vtype instead of a vnode, and
+ * get rid of the tmpl_vxxx fake vnodes below.
+ */
+static int
+smbfs_access_rwx(vfs_t *vfsp, int vtype, int mode, cred_t *cr)
+{
+ /* See the secpolicy call below. */
+ static const vnode_t tmpl_vdir = { .v_type = VDIR };
+ static const vnode_t tmpl_vreg = { .v_type = VREG };
+ vattr_t va;
+ vnode_t *tvp;
+ struct smbmntinfo *smi = VFTOSMI(vfsp);
+ int shift = 0;
+
+ /*
+ * Build our (fabricated) vnode attributes.
+ * XXX: Could make these templates in the
+ * per-mount struct and use them here.
+ */
+ bzero(&va, sizeof (va));
+ va.va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID;
+ va.va_type = vtype;
+ va.va_mode = (vtype == VDIR) ?
+ smi->smi_args.dir_mode :
+ smi->smi_args.file_mode;
+ va.va_uid = smi->smi_args.uid;
+ va.va_gid = smi->smi_args.gid;
+
+ /*
+ * Disallow write attempts on read-only file systems,
+ * unless the file is a device or fifo node. Note:
+ * Inline vn_is_readonly and IS_DEVVP here because
+ * we may not have a vnode ptr. Original expr. was:
+ * (mode & VWRITE) && vn_is_readonly(vp) && !IS_DEVVP(vp))
+ */
+ if ((mode & VWRITE) &&
+ (vfsp->vfs_flag & VFS_RDONLY) &&
+ !(vtype == VCHR || vtype == VBLK || vtype == VFIFO))
+ return (EROFS);
+
+ /*
+ * Disallow attempts to access mandatory lock files.
+ * Similarly, expand MANDLOCK here.
+ * XXX: not sure we need this.
+ */
+ if ((mode & (VWRITE | VREAD | VEXEC)) &&
+ va.va_type == VREG && MANDMODE(va.va_mode))
+ return (EACCES);
+
+ /*
+ * Access check is based on only
+ * one of owner, group, public.
+ * If not owner, then check group.
+ * If not a member of the group,
+ * then check public access.
+ */
+ if (crgetuid(cr) != va.va_uid) {
+ shift += 3;
+ if (!groupmember(va.va_gid, cr))
+ shift += 3;
+ }
+ mode &= ~(va.va_mode << shift);
+ if (mode == 0)
+ return (0);
+
+ /*
+ * We need a vnode for secpolicy_vnode_access,
+ * but the only thing it looks at is v_type,
+ * so pass one of the templates above.
+ */
+ tvp = (va.va_type == VDIR) ?
+ (vnode_t *)&tmpl_vdir :
+ (vnode_t *)&tmpl_vreg;
+ return (secpolicy_vnode_access(cr, tvp, va.va_uid, mode));
+}
+
+/*
+ * See smbfs_setattr
+ */
+static int
+smbfs_accessx(void *arg, int mode, cred_t *cr)
+{
+ vnode_t *vp = arg;
+ /*
+ * Note: The caller has checked the current zone,
+ * the SMI_DEAD and VFS_UNMOUNTED flags, etc.
+ */
+ return (smbfs_access_rwx(vp->v_vfsp, vp->v_type, mode, cr));
+}
+
+/*
+ * XXX
+ * This op should support PSARC 2007/403, Modified Access Checks for CIFS
+ */
+/* ARGSUSED */
+static int
+smbfs_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
+{
+ vfs_t *vfsp;
+ smbmntinfo_t *smi;
+
+ vfsp = vp->v_vfsp;
+ smi = VFTOSMI(vfsp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ return (smbfs_access_rwx(vfsp, vp->v_type, mode, cr));
+}
+
+
+/*
+ * Flush local dirty pages to stable storage on the server.
+ *
+ * If FNODSYNC is specified, then there is nothing to do because
+ * metadata changes are not cached on the client before being
+ * sent to the server.
+ *
+ * Currently, this is a no-op since we don't cache data, either.
+ */
+/* ARGSUSED */
+static int
+smbfs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct)
+{
+ int error = 0;
+ smbmntinfo_t *smi;
+
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ if ((syncflag & FNODSYNC) || IS_SWAPVP(vp))
+ return (0);
+
+ return (error);
+}
+
+/*
+ * Last reference to vnode went away.
+ */
+/* ARGSUSED */
+static void
+smbfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+{
+ smbnode_t *np;
+
+ /*
+ * Don't "bail out" for VFS_UNMOUNTED here,
+ * as we want to do cleanup, etc.
+ * See also pcfs_inactive
+ */
+
+ np = VTOSMB(vp);
+
+ /*
+ * If this is coming from the wrong zone, we let someone in the right
+ * zone take care of it asynchronously. We can get here due to
+ * VN_RELE() being called from pageout() or fsflush(). This call may
+ * potentially turn into an expensive no-op if, for instance, v_count
+ * gets incremented in the meantime, but it's still correct.
+ */
+
+ /*
+ * Some paranoia from the Darwin code:
+ * Make sure the FID was closed.
+ * If we see this, it's a bug!
+ *
+ * No rw_enter here, as this should be the
+ * last ref, and we're just looking...
+ */
+ if (np->n_fidrefs > 0) {
+ SMBVDEBUG("opencount %d fid %d file %s\n",
+ np->n_fidrefs, np->n_fid, np->n_rpath);
+ }
+ if (np->n_dirrefs > 0) {
+ uint_t fid = (np->n_dirseq) ?
+ np->n_dirseq->f_Sid : 0;
+ SMBVDEBUG("opencount %d fid %d dir %s\n",
+ np->n_dirrefs, fid, np->n_rpath);
+ }
+
+ smb_addfree(np);
+}
+
+/*
+ * Remote file system operations having to do with directory manipulation.
+ */
+/* ARGSUSED */
+static int
+smbfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp)
+{
+ int error;
+ smbnode_t *dnp;
+ smbmntinfo_t *smi;
+
+ smi = VTOSMI(dvp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EPERM);
+
+ if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ dnp = VTOSMB(dvp);
+ if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_READER, SMBINTR(dvp))) {
+ error = EINTR;
+ goto out;
+ }
+
+ error = smbfslookup(dvp, nm, vpp, cr, 1, ct);
+
+ smbfs_rw_exit(&dnp->r_rwlock);
+
+out:
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc,
+ caller_context_t *ct)
+{
+ int error;
+ int supplen; /* supported length */
+ vnode_t *vp;
+ smbnode_t *dnp;
+ smbmntinfo_t *smi;
+ /* struct smb_vc *vcp; */
+ const char *name = (const char *)nm;
+ int nmlen = strlen(nm);
+ int rplen;
+ struct smb_cred scred;
+ struct smbfattr fa;
+
+ smi = VTOSMI(dvp);
+ dnp = VTOSMB(dvp);
+
+ ASSERT(curproc->p_zone == smi->smi_zone);
+
+#ifdef NOT_YET
+ vcp = SSTOVC(smi->smi_share);
+
+ /* XXX: Should compute this once and store it in smbmntinfo_t */
+ supplen = (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) ? 255 : 12;
+#else
+ supplen = 255;
+#endif
+
+ /*
+ * RWlock must be held, either reader or writer.
+ * XXX: Can we check without looking directly
+ * inside the struct smbfs_rwlock_t?
+ */
+ ASSERT(dnp->r_rwlock.count != 0);
+
+ /*
+ * If lookup is for "", just return dvp. Don't need
+ * to send it over the wire, look it up in the dnlc,
+ * or perform any access checks.
+ */
+ if (nmlen == 0) {
+ VN_HOLD(dvp);
+ *vpp = dvp;
+ return (0);
+ }
+
+ /* if the name is longer that what is supported, return an error */
+ if (nmlen > supplen)
+ return (ENAMETOOLONG);
+
+ /*
+ * Avoid surprises with characters that are
+ * illegal in Windows file names.
+ * Todo: CATIA mappings XXX
+ */
+ if (strpbrk(nm, illegal_chars))
+ return (EINVAL);
+
+ /* if the dvp is not a directory, return an error */
+ if (dvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ /* Need search permission in the directory. */
+ error = smbfs_access(dvp, VEXEC, 0, cr, ct);
+ if (error)
+ return (error);
+
+ /*
+ * If lookup is for ".", just return dvp. Don't need
+ * to send it over the wire or look it up in the dnlc,
+ * just need to check access (done above).
+ */
+ if (nmlen == 1 && name[0] == '.') {
+ VN_HOLD(dvp);
+ *vpp = dvp;
+ return (0);
+ }
+
+#ifdef NOT_YET
+ if (dnlc) {
+ /*
+ * NOTE: search the dnlc here
+ */
+ }
+#endif
+
+ /*
+ * Handle lookup of ".." which is quite tricky,
+ * because the protocol gives us little help.
+ *
+ * We keep full pathnames (as seen on the server)
+ * so we can just trim off the last component to
+ * get the full pathname of the parent. Note:
+ * We don't actually copy and modify, but just
+ * compute the trimmed length and pass that with
+ * the current dir path (not null terminated).
+ *
+ * We don't go over-the-wire to get attributes
+ * for ".." because we know it's a directory,
+ * and we can just leave the rest "stale"
+ * until someone does a getattr.
+ */
+ if (nmlen == 2 && name[0] == '.' && name[1] == '.') {
+ if (dvp->v_flag & VROOT) {
+ /*
+ * Already at the root. This can happen
+ * with directory listings at the root,
+ * which lookup "." and ".." to get the
+ * inode numbers. Let ".." be the same
+ * as "." in the FS root.
+ */
+ VN_HOLD(dvp);
+ *vpp = dvp;
+ return (0);
+ }
+
+ /*
+ * Find the parent path length.
+ */
+ rplen = dnp->n_rplen;
+ ASSERT(rplen > 0);
+ while (--rplen >= 0) {
+ if (dnp->n_rpath[rplen] == '\\')
+ break;
+ }
+ if (rplen == 0) {
+ /* Found our way to the root. */
+ vp = SMBTOV(smi->smi_root);
+ VN_HOLD(vp);
+ *vpp = vp;
+ return (0);
+ }
+ vp = smbfs_make_node(dvp->v_vfsp,
+ dnp->n_rpath, rplen,
+ NULL, 0, NULL);
+ if (vp == NULL) {
+ return (ENOENT);
+ }
+ vp->v_type = VDIR;
+
+ /* Success! */
+ *vpp = vp;
+ return (0);
+ }
+
+ /*
+ * Normal lookup of a child node.
+ * Note we handled "." and ".." above.
+ *
+ * First, go over-the-wire to get the
+ * node type (and attributes).
+ */
+ smb_credinit(&scred, curproc, cr);
+ /* Note: this can allocate a new "name" */
+ error = smbfs_smb_lookup(dnp, &name, &nmlen, &fa, &scred);
+ smb_credrele(&scred);
+ if (error)
+ goto out;
+
+ /*
+ * Find or create the node.
+ */
+ error = smbfs_nget(dvp, name, nmlen, &fa, &vp);
+ if (error)
+ goto out;
+
+ /* Success! */
+ *vpp = vp;
+
+out:
+ /* smbfs_smb_lookup may have allocated name. */
+ if (name != nm)
+ smbfs_name_free(name, nmlen);
+
+ return (error);
+}
+
+/*
+ * XXX
+ * vsecattr_t is new to build 77, and we need to eventually support
+ * it in order to create an ACL when an object is created.
+ *
+ * This op should support the new FIGNORECASE flag for case-insensitive
+ * lookups, per PSARC 2007/244.
+ */
+/* ARGSUSED */
+static int
+smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
+ int mode, vnode_t **vpp, cred_t *cr, int lfaware, caller_context_t *ct,
+ vsecattr_t *vsecp)
+{
+ int error;
+ int cerror;
+ vfs_t *vfsp;
+ vnode_t *vp;
+#ifdef NOT_YET
+ smbnode_t *np;
+#endif
+ smbnode_t *dnp;
+ smbmntinfo_t *smi;
+ struct vattr vattr;
+ struct smbfattr fattr;
+ struct smb_cred scred;
+ const char *name = (const char *)nm;
+ int nmlen = strlen(nm);
+ uint32_t disp;
+ uint16_t fid;
+
+ vfsp = dvp->v_vfsp;
+ smi = VFTOSMI(vfsp);
+ dnp = VTOSMB(dvp);
+ vp = NULL;
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EPERM);
+
+ if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ /*
+ * Note: this may break mknod(2) calls to create a directory,
+ * but that's obscure use. Some other filesystems do this.
+ * XXX: Later, redirect VDIR type here to _mkdir.
+ */
+ if (va->va_type != VREG)
+ return (EINVAL);
+
+ /*
+ * If the pathname is "", just use dvp, no checks.
+ * Do this outside of the rwlock (like zfs).
+ */
+ if (nmlen == 0) {
+ VN_HOLD(dvp);
+ *vpp = dvp;
+ return (0);
+ }
+
+ /* Don't allow "." or ".." through here. */
+ if ((nmlen == 1 && name[0] == '.') ||
+ (nmlen == 2 && name[0] == '.' && name[1] == '.'))
+ return (EISDIR);
+
+ /*
+ * We make a copy of the attributes because the caller does not
+ * expect us to change what va points to.
+ */
+ vattr = *va;
+
+ if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp)))
+ return (EINTR);
+ smb_credinit(&scred, curproc, cr);
+
+ /*
+ * XXX: Do we need r_lkserlock too?
+ * No use of any shared fid or fctx...
+ */
+
+ /*
+ * NFS needs to go over the wire, just to be sure whether the
+ * file exists or not. Using the DNLC can be dangerous in
+ * this case when making a decision regarding existence.
+ *
+ * The SMB protocol does NOT really need to go OTW here
+ * thanks to the expressive NTCREATE disposition values.
+ * Unfortunately, to do Unix access checks correctly,
+ * we need to know if the object already exists.
+ * When the object does not exist, we need VWRITE on
+ * the directory. Note: smbfslookup() checks VEXEC.
+ */
+ error = smbfslookup(dvp, nm, &vp, cr, 0, ct);
+ if (error == 0) {
+ /*
+ * file already exists
+ */
+ if (exclusive == EXCL) {
+ error = EEXIST;
+ goto out;
+ }
+ /*
+ * Verify requested access.
+ */
+ error = smbfs_access(vp, mode, 0, cr, ct);
+ if (error)
+ goto out;
+
+ /*
+ * Truncate (if requested).
+ */
+ if ((vattr.va_mask & AT_SIZE) && vattr.va_size == 0) {
+ vattr.va_mask = AT_SIZE;
+ error = smbfssetattr(vp, &vattr, 0, cr);
+ if (error)
+ goto out;
+ }
+ /* Success! */
+#ifdef NOT_YET
+ vnevent_create(vp, ct);
+#endif
+ *vpp = vp;
+ goto out;
+ }
+
+ /*
+ * The file did not exist. Need VWRITE in the directory.
+ */
+ error = smbfs_access(dvp, VWRITE, 0, cr, ct);
+ if (error)
+ goto out;
+
+ /*
+ * Now things get tricky. We also need to check the
+ * requested open mode against the file we may create.
+ * See comments at smbfs_access_rwx
+ */
+ error = smbfs_access_rwx(vfsp, VREG, mode, cr);
+ if (error)
+ goto out;
+
+#ifdef NOT_YET
+ /* remove the entry from the negative entry from the dnlc */
+ dnlc_remove(dvp, name);
+#endif
+
+ /*
+ * Now the code derived from Darwin,
+ * but with greater use of NT_CREATE
+ * disposition options. Much changed.
+ *
+ * Create (or open) a new child node.
+ * Note we handled "." and ".." above.
+ */
+
+ if (exclusive == EXCL)
+ disp = NTCREATEX_DISP_CREATE;
+ else {
+ /* Truncate regular files if requested. */
+ if ((va->va_type == VREG) &&
+ (va->va_mask & AT_SIZE) &&
+ (va->va_size == 0))
+ disp = NTCREATEX_DISP_OVERWRITE_IF;
+ else
+ disp = NTCREATEX_DISP_OPEN_IF;
+ }
+ error = smbfs_smb_create(dnp, name, nmlen, &scred, &fid, disp, 0);
+ if (error)
+ goto out;
+
+ /*
+ * XXX: Missing some code here to deal with
+ * the case where we opened an existing file,
+ * it's size is larger than 32-bits, and we're
+ * setting the size from a process that's not
+ * aware of large file offsets. i.e.
+ * from the NFS3 code:
+ */
+#if NOT_YET /* XXX */
+ if ((vattr.va_mask & AT_SIZE) &&
+ vp->v_type == VREG) {
+ np = VTOSMB(vp);
+ /*
+ * Check here for large file handled
+ * by LF-unaware process (as
+ * ufs_create() does)
+ */
+ if (!(lfaware & FOFFMAX)) {
+ mutex_enter(&np->r_statelock);
+ if (np->r_size > MAXOFF32_T)
+ error = EOVERFLOW;
+ mutex_exit(&np->r_statelock);
+ }
+ if (!error) {
+ vattr.va_mask = AT_SIZE;
+ error = smbfssetattr(vp,
+ &vattr, 0, cr);
+ }
+ }
+#endif /* XXX */
+ /*
+ * Should use the fid to get/set the size
+ * while we have it opened here. See above.
+ */
+
+ cerror = smbfs_smb_close(smi->smi_share, fid, NULL, &scred);
+ if (cerror)
+ SMBERROR("error %d closing %s\\%s\n",
+ cerror, dnp->n_rpath, name);
+
+ /*
+ * In the open case, the name may differ a little
+ * from what we passed to create (case, etc.)
+ * so call lookup to get the (opened) name.
+ *
+ * XXX: Could avoid this extra lookup if the
+ * "createact" result from NT_CREATE says we
+ * created the object.
+ */
+ error = smbfs_smb_lookup(dnp, &name, &nmlen, &fattr, &scred);
+ if (error)
+ goto out;
+
+ /* update attr and directory cache */
+ smbfs_attr_touchdir(dnp);
+
+ error = smbfs_nget(dvp, name, nmlen, &fattr, &vp);
+ if (error)
+ goto out;
+
+#ifdef NOT_YET
+ dnlc_update(dvp, name, vp);
+ /* XXX invalidate pages if we truncated? */
+#endif
+
+ /* Success! */
+ *vpp = vp;
+ error = 0;
+
+out:
+ smb_credrele(&scred);
+ if (name != nm)
+ smbfs_name_free(name, nmlen);
+ smbfs_rw_exit(&dnp->r_rwlock);
+ return (error);
+}
+
+/*
+ * XXX
+ * This op should support the new FIGNORECASE flag for case-insensitive
+ * lookups, per PSARC 2007/244.
+ */
+/* ARGSUSED */
+static int
+smbfs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
+ int flags)
+{
+ int error;
+ vnode_t *vp;
+ smbnode_t *np;
+ smbnode_t *dnp;
+ struct smb_cred scred;
+ /* enum smbfsstat status; */
+ smbmntinfo_t *smi;
+
+ smi = VTOSMI(dvp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EPERM);
+
+ if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ dnp = VTOSMB(dvp);
+ if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp)))
+ return (EINTR);
+
+ /*
+ * Verify access to the dirctory.
+ */
+ error = smbfs_access(dvp, VWRITE|VEXEC, 0, cr, ct);
+ if (error)
+ goto out;
+
+ /*
+ * NOTE: the darwin code gets the "vp" passed in so it looks
+ * like the "vp" has probably been "lookup"ed by the VFS layer.
+ * It looks like we will need to lookup the vp to check the
+ * caches and check if the object being deleted is a directory.
+ */
+ error = smbfslookup(dvp, nm, &vp, cr, 0, ct);
+ if (error)
+ goto out;
+
+ /* Never allow link/unlink directories on CIFS. */
+ if (vp->v_type == VDIR) {
+ VN_RELE(vp);
+ error = EPERM;
+ goto out;
+ }
+
+#ifdef NOT_YET
+ /*
+ * First just remove the entry from the name cache, as it
+ * is most likely the only entry for this vp.
+ */
+ dnlc_remove(dvp, nm);
+
+ /*
+ * If the file has a v_count > 1 then there may be more than one
+ * entry in the name cache due multiple links or an open file,
+ * but we don't have the real reference count so flush all
+ * possible entries.
+ */
+ if (vp->v_count > 1)
+ dnlc_purge_vp(vp);
+#endif /* NOT_YET */
+
+ /*
+ * Now we have the real reference count on the vnode
+ */
+ np = VTOSMB(vp);
+ mutex_enter(&np->r_statelock);
+ if (vp->v_count > 1) {
+ /*
+ * NFS does a rename on remove here.
+ * Probably not applicable for SMB.
+ * Like Darwin, just return EBUSY.
+ *
+ * XXX: Todo - Ask the server to set the
+ * set the delete-on-close flag.
+ */
+ mutex_exit(&np->r_statelock);
+ error = EBUSY;
+ goto out;
+ } else {
+ mutex_exit(&np->r_statelock);
+
+ smb_credinit(&scred, curproc, cr);
+ error = smbfs_smb_delete(np, &scred, NULL, 0, 0);
+ smb_credrele(&scred);
+
+ }
+
+ VN_RELE(vp);
+
+out:
+ smbfs_rw_exit(&dnp->r_rwlock);
+
+ return (error);
+}
+
+
+/*
+ * XXX
+ * This op should support the new FIGNORECASE flag for case-insensitive
+ * lookups, per PSARC 2007/244.
+ */
+/* ARGSUSED */
+static int
+smbfs_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
+ caller_context_t *ct, int flags)
+{
+ /* vnode_t *realvp; */
+
+ if (curproc->p_zone != VTOSMI(odvp)->smi_zone ||
+ curproc->p_zone != VTOSMI(ndvp)->smi_zone)
+ return (EPERM);
+
+ if (VTOSMI(odvp)->smi_flags & SMI_DEAD ||
+ VTOSMI(ndvp)->smi_flags & SMI_DEAD ||
+ odvp->v_vfsp->vfs_flag & VFS_UNMOUNTED ||
+ ndvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ return (smbfsrename(odvp, onm, ndvp, nnm, cr, ct));
+}
+
+/*
+ * smbfsrename does the real work of renaming in SMBFS
+ */
+/* ARGSUSED */
+static int
+smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
+ caller_context_t *ct)
+{
+ int error;
+ int nvp_locked = 0;
+ vnode_t *nvp = NULL;
+ vnode_t *ovp = NULL;
+ smbnode_t *onp;
+ smbnode_t *odnp;
+ smbnode_t *ndnp;
+ struct smb_cred scred;
+ /* enum smbfsstat status; */
+
+ ASSERT(curproc->p_zone == VTOSMI(odvp)->smi_zone);
+
+ if (strcmp(onm, ".") == 0 || strcmp(onm, "..") == 0 ||
+ strcmp(nnm, ".") == 0 || strcmp(nnm, "..") == 0)
+ return (EINVAL);
+
+ /*
+ * Check that everything is on the same filesystem.
+ * vn_rename checks the fsid's, but in case we don't
+ * fill those in correctly, check here too.
+ */
+ if (odvp->v_vfsp != ndvp->v_vfsp)
+ return (EXDEV);
+
+ odnp = VTOSMB(odvp);
+ ndnp = VTOSMB(ndvp);
+
+ /*
+ * Avoid deadlock here on old vs new directory nodes
+ * by always taking the locks in order of address.
+ * The order is arbitrary, but must be consistent.
+ */
+ if (odnp < ndnp) {
+ if (smbfs_rw_enter_sig(&odnp->r_rwlock, RW_WRITER,
+ SMBINTR(odvp)))
+ return (EINTR);
+ if (smbfs_rw_enter_sig(&ndnp->r_rwlock, RW_WRITER,
+ SMBINTR(ndvp))) {
+ smbfs_rw_exit(&odnp->r_rwlock);
+ return (EINTR);
+ }
+ } else {
+ if (smbfs_rw_enter_sig(&ndnp->r_rwlock, RW_WRITER,
+ SMBINTR(ndvp)))
+ return (EINTR);
+ if (smbfs_rw_enter_sig(&odnp->r_rwlock, RW_WRITER,
+ SMBINTR(odvp))) {
+ smbfs_rw_exit(&ndnp->r_rwlock);
+ return (EINTR);
+ }
+ }
+ /*
+ * No returns after this point (goto out)
+ */
+
+ /*
+ * Need write access on source and target.
+ * Server takes care of most checks.
+ */
+ error = smbfs_access(odvp, VWRITE|VEXEC, 0, cr, ct);
+ if (error)
+ goto out;
+ if (odvp != ndvp) {
+ error = smbfs_access(ndvp, VWRITE, 0, cr, ct);
+ if (error)
+ goto out;
+ }
+
+ /*
+ * Lookup the source name. Must already exist.
+ */
+ error = smbfslookup(odvp, onm, &ovp, cr, 0, ct);
+ if (error)
+ goto out;
+
+ /*
+ * Lookup the target file. If it exists, it needs to be
+ * checked to see whether it is a mount point and whether
+ * it is active (open).
+ */
+ error = smbfslookup(ndvp, nnm, &nvp, cr, 0, ct);
+ if (!error) {
+ /*
+ * Target (nvp) already exists. Check that it
+ * has the same type as the source. The server
+ * will check this also, (and more reliably) but
+ * this lets us return the correct error codes.
+ */
+ if (ovp->v_type == VDIR) {
+ if (nvp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto out;
+ }
+ } else {
+ if (nvp->v_type == VDIR) {
+ error = EISDIR;
+ goto out;
+ }
+ }
+
+ /*
+ * POSIX dictates that when the source and target
+ * entries refer to the same file object, rename
+ * must do nothing and exit without error.
+ */
+ if (ovp == nvp) {
+ error = 0;
+ goto out;
+ }
+
+ /*
+ * Also must ensure the target is not a mount point,
+ * and keep mount/umount away until we're done.
+ */
+ if (vn_vfsrlock(nvp)) {
+ error = EBUSY;
+ goto out;
+ }
+ nvp_locked = 1;
+ if (vn_mountedvfs(nvp) != NULL) {
+ error = EBUSY;
+ goto out;
+ }
+
+#ifdef NOT_YET
+ /*
+ * Purge the name cache of all references to this vnode
+ * so that we can check the reference count to infer
+ * whether it is active or not.
+ */
+ /*
+ * First just remove the entry from the name cache, as it
+ * is most likely the only entry for this vp.
+ */
+ dnlc_remove(ndvp, nnm);
+ /*
+ * If the file has a v_count > 1 then there may be more
+ * than one entry in the name cache due multiple links
+ * or an open file, but we don't have the real reference
+ * count so flush all possible entries.
+ */
+ if (nvp->v_count > 1)
+ dnlc_purge_vp(nvp);
+#endif
+
+ if (nvp->v_count > 1 && nvp->v_type != VDIR) {
+ /*
+ * The target file exists, is not the same as
+ * the source file, and is active. Other FS
+ * implementations unlink the target here.
+ * For SMB, we don't assume we can remove an
+ * open file. Return an error instead.
+ * Darwin returned an error here too.
+ */
+ error = EEXIST;
+ goto out;
+ }
+ } /* nvp */
+
+#ifdef NOT_YET
+ dnlc_remove(odvp, onm);
+ dnlc_remove(ndvp, nnm);
+#endif
+
+ onp = VTOSMB(ovp);
+ smb_credinit(&scred, curproc, cr);
+ error = smbfs_smb_rename(onp, ndnp, nnm, strlen(nnm), &scred);
+ smb_credrele(&scred);
+
+
+out:
+ if (nvp) {
+ if (nvp_locked)
+ vn_vfsunlock(nvp);
+ VN_RELE(nvp);
+ }
+ if (ovp)
+ VN_RELE(ovp);
+
+ smbfs_rw_exit(&odnp->r_rwlock);
+ smbfs_rw_exit(&ndnp->r_rwlock);
+
+ return (error);
+}
+
+/*
+ * XXX
+ * vsecattr_t is new to build 77, and we need to eventually support
+ * it in order to create an ACL when an object is created.
+ *
+ * This op should support the new FIGNORECASE flag for case-insensitive
+ * lookups, per PSARC 2007/244.
+ */
+/* ARGSUSED */
+static int
+smbfs_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp,
+ cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
+{
+ vnode_t *vp;
+ struct smbnode *dnp = VTOSMB(dvp);
+ struct smbmntinfo *smi = VTOSMI(dvp);
+ struct smb_cred scred;
+ struct smbfattr fattr;
+ const char *name = (const char *) nm;
+ int nmlen = strlen(name);
+ int error, hiderr;
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EPERM);
+
+ if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ if ((nmlen == 1 && name[0] == '.') ||
+ (nmlen == 2 && name[0] == '.' && name[1] == '.'))
+ return (EEXIST);
+
+ if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp)))
+ return (EINTR);
+ smb_credinit(&scred, curproc, cr);
+
+ /*
+ * XXX: Do we need r_lkserlock too?
+ * No use of any shared fid or fctx...
+ */
+
+ /*
+ * Require write access in the containing directory.
+ */
+ error = smbfs_access(dvp, VWRITE, 0, cr, ct);
+ if (error)
+ goto out;
+
+ error = smbfs_smb_mkdir(dnp, name, nmlen, &scred);
+ if (error)
+ goto out;
+
+ error = smbfs_smb_lookup(dnp, &name, &nmlen, &fattr, &scred);
+ if (error)
+ goto out;
+
+ smbfs_attr_touchdir(dnp);
+
+ error = smbfs_nget(dvp, name, nmlen, &fattr, &vp);
+ if (error)
+ goto out;
+
+#ifdef NOT_YET
+ dnlc_update(dvp, name, vp);
+#endif
+
+ if (name[0] == '.')
+ if ((hiderr = smbfs_smb_hideit(VTOSMB(vp), NULL, 0, &scred)))
+ SMBVDEBUG("hide failure %d\n", hiderr);
+
+ /* Success! */
+ *vpp = vp;
+ error = 0;
+out:
+ smb_credrele(&scred);
+ smbfs_rw_exit(&dnp->r_rwlock);
+
+ if (name != nm)
+ smbfs_name_free(name, nmlen);
+
+ return (error);
+}
+
+/*
+ * XXX
+ * This op should support the new FIGNORECASE flag for case-insensitive
+ * lookups, per PSARC 2007/244.
+ */
+/* ARGSUSED */
+static int
+smbfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
+ caller_context_t *ct, int flags)
+{
+ vnode_t *vp = NULL;
+ int vp_locked = 0;
+ struct smbmntinfo *smi = VTOSMI(dvp);
+ struct smbnode *dnp = VTOSMB(dvp);
+ struct smbnode *np;
+ struct smb_cred scred;
+ int error;
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EPERM);
+
+ if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp)))
+ return (EINTR);
+ smb_credinit(&scred, curproc, cr);
+
+ /*
+ * Require w/x access in the containing directory.
+ * Server handles all other access checks.
+ */
+ error = smbfs_access(dvp, VEXEC|VWRITE, 0, cr, ct);
+ if (error)
+ goto out;
+
+ /*
+ * First lookup the entry to be removed.
+ */
+ error = smbfslookup(dvp, nm, &vp, cr, 0, ct);
+ if (error)
+ goto out;
+ np = VTOSMB(vp);
+
+ /*
+ * Disallow rmdir of "." or current dir, or the FS root.
+ * Also make sure it's a directory, not a mount point,
+ * and lock to keep mount/umount away until we're done.
+ */
+ if ((vp == dvp) || (vp == cdir) || (vp->v_flag & VROOT)) {
+ error = EINVAL;
+ goto out;
+ }
+ if (vp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto out;
+ }
+ if (vn_vfsrlock(vp)) {
+ error = EBUSY;
+ goto out;
+ }
+ vp_locked = 1;
+ if (vn_mountedvfs(vp) != NULL) {
+ error = EBUSY;
+ goto out;
+ }
+
+ error = smbfs_smb_rmdir(np, &scred);
+ if (error)
+ goto out;
+
+ mutex_enter(&np->r_statelock);
+ dnp->n_flag |= NMODIFIED;
+ mutex_exit(&np->r_statelock);
+ smbfs_attr_touchdir(dnp);
+#ifdef NOT_YET
+ dnlc_remove(dvp, nm);
+ dnlc_purge_vp(vp);
+#endif
+ smb_rmhash(np);
+
+out:
+ if (vp) {
+ if (vp_locked)
+ vn_vfsunlock(vp);
+ VN_RELE(vp);
+ }
+ smb_credrele(&scred);
+ smbfs_rw_exit(&dnp->r_rwlock);
+
+ return (error);
+}
+
+
+/* ARGSUSED */
+static int
+smbfs_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ struct smbnode *np = VTOSMB(vp);
+ int error = 0;
+ smbmntinfo_t *smi;
+
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ /*
+ * Require read access in the directory.
+ */
+ error = smbfs_access(vp, VREAD, 0, cr, ct);
+ if (error)
+ return (error);
+
+ ASSERT(smbfs_rw_lock_held(&np->r_rwlock, RW_READER));
+
+ /*
+ * XXX: Todo readdir cache here
+ * Note: NFS code is just below this.
+ *
+ * I am serializing the entire readdir opreation
+ * now since we have not yet implemented readdir
+ * cache. This fix needs to be revisited once
+ * we implement readdir cache.
+ */
+ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, SMBINTR(vp)))
+ return (EINTR);
+
+ error = smbfs_readvdir(vp, uiop, cr, eofp, ct);
+
+ smbfs_rw_exit(&np->r_lkserlock);
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+smbfs_readvdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp,
+ caller_context_t *ct)
+{
+ size_t dbufsiz;
+ struct dirent64 *dp;
+ struct smb_cred scred;
+ vnode_t *newvp;
+ struct smbnode *np = VTOSMB(vp);
+ int nmlen, reclen, error = 0;
+ long offset, limit;
+ struct smbfs_fctx *ctx;
+
+ ASSERT(curproc->p_zone == VTOSMI(vp)->smi_zone);
+
+ /* Make sure we serialize for n_dirseq use. */
+ ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_WRITER));
+
+ /* Min size is DIRENT64_RECLEN(256) rounded up. */
+ if (uio->uio_resid < 512 || uio->uio_offset < 0)
+ return (EINVAL);
+
+ /*
+ * This dnlc_purge_vp ensures that name cache for this dir will be
+ * current - it'll only have the items for which the smbfs_nget
+ * MAKEENTRY happened.
+ */
+#ifdef NOT_YET
+ if (smbfs_fastlookup)
+ dnlc_purge_vp(vp);
+#endif
+ SMBVDEBUG("dirname='%s'\n", np->n_rpath);
+ smb_credinit(&scred, curproc, cr);
+ dbufsiz = DIRENT64_RECLEN(MAXNAMELEN);
+ dp = kmem_alloc(dbufsiz, KM_SLEEP);
+
+ offset = uio->uio_offset; /* NB: "cookie" */
+ limit = uio->uio_resid / DIRENT64_RECLEN(1);
+ SMBVDEBUG("offset=0x%ld, limit=0x%ld\n", offset, limit);
+
+ if (offset == 0) {
+ /* Don't know EOF until findclose */
+ np->n_direof = -1;
+ } else if (offset == np->n_direof) {
+ /* Arrived at end of directory. */
+ goto out;
+ }
+
+ /*
+ * Generate the "." and ".." entries here so we can
+ * (1) make sure they appear (but only once), and
+ * (2) deal with getting their I numbers which the
+ * findnext below does only for normal names.
+ */
+ while (limit && offset < 2) {
+ limit--;
+ reclen = DIRENT64_RECLEN(offset + 1);
+ bzero(dp, reclen);
+ /*LINTED*/
+ dp->d_reclen = reclen;
+ /* Tricky: offset 0 is ".", offset 1 is ".." */
+ dp->d_name[0] = '.';
+ dp->d_name[1] = '.';
+ dp->d_name[offset + 1] = '\0';
+ /*
+ * Want the real I-numbers for the "." and ".."
+ * entries. For these two names, we know that
+ * smbfslookup can do this all locally.
+ */
+ error = smbfslookup(vp, dp->d_name, &newvp, cr, 1, ct);
+ if (error) {
+ dp->d_ino = np->n_ino + offset; /* fiction */
+ } else {
+ dp->d_ino = VTOSMB(newvp)->n_ino;
+ VN_RELE(newvp);
+ }
+ dp->d_off = offset + 1; /* see d_off below */
+ error = uiomove(dp, dp->d_reclen, UIO_READ, uio);
+ if (error)
+ goto out;
+ uio->uio_offset = ++offset;
+ }
+ if (limit == 0)
+ goto out;
+ if (offset != np->n_dirofs || np->n_dirseq == NULL) {
+ SMBVDEBUG("Reopening search %ld:%ld\n", offset, np->n_dirofs);
+ if (np->n_dirseq) {
+ (void) smbfs_smb_findclose(np->n_dirseq, &scred);
+ np->n_dirseq = NULL;
+ }
+ np->n_dirofs = 2;
+ error = smbfs_smb_findopen(np, "*", 1,
+ SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR,
+ &scred, &ctx);
+ if (error) {
+ SMBVDEBUG("can not open search, error = %d", error);
+ goto out;
+ }
+ np->n_dirseq = ctx;
+ } else
+ ctx = np->n_dirseq;
+ while (np->n_dirofs < offset) {
+ if (smbfs_smb_findnext(ctx, offset - np->n_dirofs++,
+ &scred) != 0) {
+ (void) smbfs_smb_findclose(np->n_dirseq, &scred);
+ np->n_dirseq = NULL;
+ np->n_direof = np->n_dirofs;
+ np->n_dirofs = 0;
+ *eofp = 1;
+ error = 0;
+ goto out;
+ }
+ }
+ error = 0;
+ for (; limit; limit--) {
+ error = smbfs_smb_findnext(ctx, limit, &scred);
+ if (error) {
+ if (error == EBADRPC)
+ error = ENOENT;
+ (void) smbfs_smb_findclose(np->n_dirseq, &scred);
+ np->n_dirseq = NULL;
+ np->n_direof = np->n_dirofs;
+ np->n_dirofs = 0;
+ *eofp = 1;
+ error = 0;
+ break;
+ }
+ np->n_dirofs++;
+ /* Sanity check the name length. */
+ nmlen = ctx->f_nmlen;
+ if (nmlen > (MAXNAMELEN - 1)) {
+ nmlen = MAXNAMELEN - 1;
+ SMBVDEBUG("Truncating name: %s\n", ctx->f_name);
+ }
+ reclen = DIRENT64_RECLEN(nmlen);
+ if (uio->uio_resid < reclen)
+ break;
+ bzero(dp, reclen);
+ /*LINTED*/
+ dp->d_reclen = reclen;
+ dp->d_ino = ctx->f_attr.fa_ino;
+ /*
+ * Note: d_off is the offset that a user-level program
+ * should seek to for reading the _next_ directory entry.
+ * See libc: readdir, telldir, seekdir
+ */
+ dp->d_off = offset + 1;
+ bcopy(ctx->f_name, dp->d_name, nmlen);
+ dp->d_name[nmlen] = '\0';
+#ifdef NOT_YET
+ if (smbfs_fastlookup) {
+ if (smbfs_nget(vp, ctx->f_name,
+ ctx->f_nmlen, &ctx->f_attr, &newvp) == 0)
+ VN_RELE(newvp);
+ }
+#endif /* NOT_YET */
+ error = uiomove(dp, dp->d_reclen, UIO_READ, uio);
+ if (error)
+ break;
+ uio->uio_offset = ++offset;
+ }
+ if (error == ENOENT)
+ error = 0;
+out:
+ kmem_free(dp, dbufsiz);
+ smb_credrele(&scred);
+ return (error);
+}
+
+
+/*
+ * The pair of functions VOP_RWLOCK, VOP_RWUNLOCK
+ * are optional functions that are called by:
+ * getdents, before/after VOP_READDIR
+ * pread, before/after ... VOP_READ
+ * pwrite, before/after ... VOP_WRITE
+ * (other places)
+ *
+ * Careful here: None of the above check for any
+ * error returns from VOP_RWLOCK / VOP_RWUNLOCK!
+ * In fact, the return value from _rwlock is NOT
+ * an error code, but V_WRITELOCK_TRUE / _FALSE.
+ *
+ * Therefore, it's up to _this_ code to make sure
+ * the lock state remains balanced, which means
+ * we can't "bail out" on interrupts, etc.
+ */
+
+/* ARGSUSED2 */
+static int
+smbfs_rwlock(vnode_t *vp, int write_lock, caller_context_t *ctp)
+{
+ smbnode_t *np = VTOSMB(vp);
+
+ if (!write_lock) {
+ (void) smbfs_rw_enter_sig(&np->r_rwlock, RW_READER, FALSE);
+ return (V_WRITELOCK_FALSE);
+ }
+
+
+ (void) smbfs_rw_enter_sig(&np->r_rwlock, RW_WRITER, FALSE);
+ return (V_WRITELOCK_TRUE);
+}
+
+/* ARGSUSED */
+static void
+smbfs_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ctp)
+{
+ smbnode_t *np = VTOSMB(vp);
+
+ smbfs_rw_exit(&np->r_rwlock);
+}
+
+
+/* ARGSUSED */
+static int
+smbfs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct)
+{
+ smbmntinfo_t *smi;
+
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EPERM);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ /*
+ * Because we stuff the readdir cookie into the offset field
+ * someone may attempt to do an lseek with the cookie which
+ * we want to succeed.
+ */
+ if (vp->v_type == VDIR)
+ return (0);
+
+ /* Like NFS3, just check for 63-bit overflow. */
+ if (*noffp < 0)
+ return (EINVAL);
+
+ return (0);
+}
+
+
+/*
+ * XXX
+ * This op may need to support PSARC 2007/440, nbmand changes for CIFS Service.
+ */
+static int
+smbfs_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
+ offset_t offset, struct flk_callback *flk_cbp, cred_t *cr,
+ caller_context_t *ct)
+{
+ if (curproc->p_zone != VTOSMI(vp)->smi_zone)
+ return (EIO);
+
+ if (VTOSMI(vp)->smi_flags & SMI_LLOCK)
+ return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct));
+ else
+ return (ENOSYS);
+}
+
+/*
+ * Free storage space associated with the specified vnode. The portion
+ * to be freed is specified by bfp->l_start and bfp->l_len (already
+ * normalized to a "whence" of 0).
+ *
+ * Called by fcntl(fd, F_FREESP, lkp) for libc:ftruncate, etc.
+ */
+/* ARGSUSED */
+static int
+smbfs_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
+ offset_t offset, cred_t *cr, caller_context_t *ct)
+{
+ int error;
+ smbmntinfo_t *smi;
+
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ ASSERT(vp->v_type == VREG);
+ if (cmd != F_FREESP)
+ return (EINVAL);
+
+ /*
+ * Like NFS3, no 32-bit offset checks here.
+ * Our SMB layer takes care to return EFBIG
+ * when it has to fallback to a 32-bit call.
+ */
+
+ error = convoff(vp, bfp, 0, offset);
+ if (!error) {
+ ASSERT(bfp->l_start >= 0);
+ if (bfp->l_len == 0) {
+ struct vattr va;
+
+ /*
+ * ftruncate should not change the ctime and
+ * mtime if we truncate the file to its
+ * previous size.
+ */
+ va.va_mask = AT_SIZE;
+ error = smbfsgetattr(vp, &va, cr);
+ if (error || va.va_size == bfp->l_start)
+ return (error);
+ va.va_mask = AT_SIZE;
+ va.va_size = bfp->l_start;
+ error = smbfssetattr(vp, &va, 0, cr);
+ } else
+ error = EINVAL;
+ }
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+smbfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
+ caller_context_t *ct)
+{
+ smbmntinfo_t *smi;
+ struct smb_share *ssp;
+
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ switch (cmd) {
+ case _PC_FILESIZEBITS:
+ ssp = smi->smi_share;
+ if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES)
+ *valp = 64;
+ else
+ *valp = 32;
+ break;
+
+ case _PC_LINK_MAX:
+ /* We only ever report one link to an object */
+ *valp = 1;
+ break;
+
+ case _PC_SYMLINK_MAX: /* No symlinks until we do Unix extensions */
+ case _PC_ACL_ENABLED: /* No ACLs yet - see FILE_PERSISTENT_ACLS bit */
+ case _PC_XATTR_EXISTS: /* No xattrs yet */
+ *valp = 0;
+ break;
+
+ default:
+ return (fs_pathconf(vp, cmd, valp, cr, ct));
+ }
+ return (0);
+}
+
+
+
+/*
+ * XXX
+ * This op should eventually support PSARC 2007/268.
+ */
+static int
+smbfs_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr,
+ caller_context_t *ct)
+{
+ if (curproc->p_zone != VTOSMI(vp)->smi_zone)
+ return (EIO);
+
+ if (VTOSMI(vp)->smi_flags & SMI_LLOCK)
+ return (fs_shrlock(vp, cmd, shr, flag, cr, ct));
+ else
+ return (ENOSYS);
+}
diff --git a/usr/src/uts/common/netsmb/mchain.h b/usr/src/uts/common/netsmb/mchain.h
new file mode 100644
index 0000000000..e6fb362a9c
--- /dev/null
+++ b/usr/src/uts/common/netsmb/mchain.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2000, 2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/sys/mchain.h,v 1.1 2001/02/24 15:44:30 bp Exp $
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MCHAIN_H_
+#define _MCHAIN_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/isa_defs.h>
+#include <sys/byteorder.h>
+
+#ifdef _LITTLE_ENDIAN
+
+/* little-endian values on little-endian */
+#define htoles(x) ((uint16_t)(x))
+#define letohs(x) ((uint16_t)(x))
+#define htolel(x) ((uint32_t)(x))
+#define letohl(x) ((uint32_t)(x))
+#define htoleq(x) ((uint64_t)(x))
+#define letohq(x) ((uint64_t)(x))
+
+/*
+ * big-endian values on little-endian (swap)
+ *
+ * Use the BSWAP macros because they're fastest, and they're
+ * available in all environments where we use this header.
+ */
+#define htobes(x) BSWAP_16(x)
+#define betohs(x) BSWAP_16(x)
+#define htobel(x) BSWAP_32(x)
+#define betohl(x) BSWAP_32(x)
+#define htobeq(x) BSWAP_64(x)
+#define betohq(x) BSWAP_64(x)
+
+#else /* (BYTE_ORDER == LITTLE_ENDIAN) */
+
+/* little-endian values on big-endian (swap) */
+#define letohs(x) BSWAP_16(x)
+#define htoles(x) BSWAP_16(x)
+#define letohl(x) BSWAP_32(x)
+#define htolel(x) BSWAP_32(x)
+#define letohq(x) BSWAP_64(x)
+#define htoleq(x) BSWAP_64(x)
+
+/* big-endian values on big-endian */
+#define htobes(x) ((uint16_t)(x))
+#define betohs(x) ((uint16_t)(x))
+#define htobel(x) ((uint32_t)(x))
+#define betohl(x) ((uint32_t)(x))
+#define htobeq(x) ((uint64_t)(x))
+#define betohq(x) ((uint64_t)(x))
+#endif /* (BYTE_ORDER == LITTLE_ENDIAN) */
+
+
+#ifdef _KERNEL
+
+/* BEGIN CSTYLED */
+/*
+ * BSD-style mbufs, vs SysV-style mblks:
+ * One big difference: the mbuf payload is:
+ * m_data ... (m_data + m_len)
+ * In Unix STREAMS, the mblk payload is:
+ * b_rptr ... b_wptr
+ *
+ * Here are some handy conversion notes:
+ *
+ * struct mbuf struct mblk
+ * m->m_next m->b_cont
+ * m->m_nextpkt m->b_next
+ * m->m_data m->b_rptr
+ * m->m_len MBLKL(m)
+ * m->m_dat[] m->b_datap->db_base
+ * &m->m_dat[MLEN] m->b_datap->db_lim
+ * M_TRAILINGSPACE(m) MBLKTAIL(m)
+ * m_freem(m) freemsg(m)
+ *
+ * Note that mbufs chains also have a special "packet" header,
+ * which has the length of the whole message. In STREAMS one
+ * typically just calls msgdsize(m) to get that.
+ */
+/* END CSTYLED */
+
+#include <sys/stream.h> /* mblk_t */
+
+/*
+ * Type of copy for mb_{put|get}_mem()
+ */
+#define MB_MSYSTEM 0 /* use bcopy() */
+#define MB_MUSER 1 /* use copyin()/copyout() */
+#define MB_MINLINE 2 /* use an inline copy loop */
+#define MB_MZERO 3 /* bzero(), mb_put_mem only */
+#define MB_MCUSTOM 4 /* use an user defined function */
+
+struct mbchain {
+ mblk_t *mb_top;
+ mblk_t *mb_cur;
+ uint_t mb_count;
+};
+typedef struct mbchain mbchain_t;
+
+struct mdchain {
+ mblk_t *md_top; /* head of mblk chain */
+ mblk_t *md_cur; /* current mblk */
+ uchar_t *md_pos; /* offset in the current mblk */
+};
+typedef struct mdchain mdchain_t;
+
+int m_fixhdr(mblk_t *m);
+
+int mb_init(struct mbchain *mbp);
+void mb_initm(struct mbchain *mbp, mblk_t *m);
+void mb_done(struct mbchain *mbp);
+mblk_t *mb_detach(struct mbchain *mbp);
+int mb_fixhdr(struct mbchain *mbp);
+void *mb_reserve(struct mbchain *mbp, int size);
+
+int mb_put_padbyte(struct mbchain *mbp);
+int mb_put_uint8(struct mbchain *mbp, uint8_t x);
+int mb_put_uint16be(struct mbchain *mbp, uint16_t x);
+int mb_put_uint16le(struct mbchain *mbp, uint16_t x);
+int mb_put_uint32be(struct mbchain *mbp, uint32_t x);
+int mb_put_uint32le(struct mbchain *mbp, uint32_t x);
+int mb_put_uint64be(struct mbchain *mbp, uint64_t x);
+int mb_put_uint64le(struct mbchain *mbp, uint64_t x);
+int mb_put_mem(struct mbchain *mbp, const char *src, int size, int type);
+
+int mb_put_mblk(struct mbchain *mbp, mblk_t *m);
+int mb_put_uio(struct mbchain *mbp, uio_t *uiop, int size);
+
+int md_init(struct mdchain *mdp);
+void md_initm(struct mdchain *mbp, mblk_t *m);
+void md_done(struct mdchain *mdp);
+void md_append_record(struct mdchain *mdp, mblk_t *top);
+int md_next_record(struct mdchain *mdp);
+int md_get_uint8(struct mdchain *mdp, uint8_t *x);
+int md_get_uint16(struct mdchain *mdp, uint16_t *x);
+int md_get_uint16le(struct mdchain *mdp, uint16_t *x);
+int md_get_uint16be(struct mdchain *mdp, uint16_t *x);
+int md_get_uint32(struct mdchain *mdp, uint32_t *x);
+int md_get_uint32be(struct mdchain *mdp, uint32_t *x);
+int md_get_uint32le(struct mdchain *mdp, uint32_t *x);
+int md_get_uint64(struct mdchain *mdp, uint64_t *x);
+int md_get_uint64be(struct mdchain *mdp, uint64_t *x);
+int md_get_uint64le(struct mdchain *mdp, uint64_t *x);
+int md_get_mem(struct mdchain *mdp, caddr_t target, int size, int type);
+int md_get_mblk(struct mdchain *mdp, int size, mblk_t **m);
+int md_get_uio(struct mdchain *mdp, uio_t *uiop, int size);
+
+/*
+ * Additions for Solaris to replace things that came from
+ * <sys/mbuf.h> in the Darwin code. These are mostly just
+ * wrappers for streams functions. See: subr_mchain.c
+ */
+
+#define mtod(m, t) ((t)((m)->b_rptr))
+
+/* length to m_copym to copy all */
+#define M_COPYALL -1
+
+mblk_t *m_copym(mblk_t *, int, int, int);
+mblk_t *m_pullup(mblk_t *, int);
+mblk_t *m_split(mblk_t *, int, int);
+void m_cat(mblk_t *, mblk_t *);
+#define m_freem(x) freemsg(x)
+mblk_t *m_getblk(int, int);
+
+#endif /* ifdef _KERNEL */
+#endif /* !_MCHAIN_H_ */
diff --git a/usr/src/uts/common/netsmb/netbios.h b/usr/src/uts/common/netsmb/netbios.h
new file mode 100644
index 0000000000..b61f19c2dd
--- /dev/null
+++ b/usr/src/uts/common/netsmb/netbios.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: netbios.h,v 1.5 2004/03/19 01:49:45 lindak Exp $
+ */
+
+#ifndef _NETSMB_NETBIOS_H_
+#define _NETSMB_NETBIOS_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef _NETINET_IN_H_
+#include <netinet/in.h>
+#endif
+
+/*
+ * This is a fake address family number, used to
+ * recognize our fake sockaddr_nb objects.
+ * This is never handed to bind or connect.
+ */
+#ifndef AF_NETBIOS
+#define AF_NETBIOS (AF_MAX+2)
+#endif
+
+#define PF_NETBIOS AF_NETBIOS
+
+/*
+ * NetBIOS port numbers by the names used in the Darwin code.
+ * XXX: Change the code to use IPPORT_xxx from in.h directly.
+ * XXX: Add IPPORT_SMB_OVER_TCP or some such (port 445)
+ */
+#define NBNS_UDP_PORT IPPORT_NETBIOS_NS /* 137 */
+#define SMB_TCP_PORT IPPORT_NETBIOS_SSN /* 139 */
+
+#define NBPROTO_TCPSSN 1 /* NETBIOS session over TCP */
+
+#define NB_NAMELEN 16
+#define NB_ENCNAMELEN NB_NAMELEN * 2
+#define NB_MAXLABLEN 63
+
+#define NB_MINSALEN (sizeof (struct sockaddr_nb))
+
+/*
+ * name types
+ */
+#define NBT_WKSTA 0x00
+#define NBT_CLIENT 0x03
+#define NBT_RASSRVR 0x06
+#define NBT_DMB 0x1B
+#define NBT_IP 0x1C
+#define NBT_MB 0x1D
+#define NBT_BS 0x1E
+#define NBT_NETDDE 0x1F
+#define NBT_SERVER 0x20
+#define NBT_RASCLNT 0x21
+#define NBT_NMAGENT 0xBE
+#define NBT_NMUTIL 0xBF
+
+/*
+ * Session packet types
+ */
+#define NB_SSN_MESSAGE 0x0
+#define NB_SSN_REQUEST 0x81
+#define NB_SSN_POSRESP 0x82
+#define NB_SSN_NEGRESP 0x83
+#define NB_SSN_RTGRESP 0x84
+#define NB_SSN_KEEPALIVE 0x85
+
+/*
+ * resolver: Opcodes
+ */
+#define NBNS_OPCODE_QUERY 0x00
+#define NBNS_OPCODE_REGISTER 0x05
+#define NBNS_OPCODE_RELEASE 0x06
+#define NBNS_OPCODE_WACK 0x07
+#define NBNS_OPCODE_REFRESH 0x08
+#define NBNS_OPCODE_RESPONSE 0x10 /* or'ed with other opcodes */
+
+/*
+ * resolver: NM_FLAGS
+ */
+#define NBNS_NMFLAG_BCAST 0x01
+#define NBNS_NMFLAG_RA 0x08 /* recursion available */
+#define NBNS_NMFLAG_RD 0x10 /* recursion desired */
+#define NBNS_NMFLAG_TC 0x20 /* truncation occured */
+#define NBNS_NMFLAG_AA 0x40 /* authoritative answer */
+
+/*
+ * resolver: Question types
+ */
+#define NBNS_QUESTION_TYPE_NB 0x0020
+#define NBNS_QUESTION_TYPE_NBSTAT 0x0021
+
+/*
+ * resolver: Question class
+ */
+#define NBNS_QUESTION_CLASS_IN 0x0001
+
+/*
+ * resolver: Limits
+ */
+#define NBNS_MAXREDIRECTS 3 /* max number of accepted redirects */
+#define NBDG_MAXSIZE 576 /* maximum nbns datagram size */
+
+/*
+ * NETBIOS addressing
+ */
+
+struct nb_name {
+ uint_t nn_type;
+ char nn_name[NB_NAMELEN];
+ char *nn_scope;
+};
+typedef struct nb_name nb_name_t;
+
+/*
+ * Our private NetBIOS socket address format.
+ * Note that it's LARGER than sockaddr.
+ *
+ * XXX: Also note that the library code is sloppy about
+ * casting this to sockaddr_in so let's keep snb_ipaddr
+ * at the same offset, at least until that's fixed.
+ */
+struct sockaddr_nb {
+ sa_family_t snb_family; /* address family */
+ uint16_t snb_flags; /* NBNS_GROUPFLG, etc. */
+ uint32_t snb_ipaddr; /* always IPv4 */
+ char snb_name[NB_NAMELEN]; /* NOT encoded */
+};
+typedef struct sockaddr_nb sockaddr_nb_t;
+
+#endif /* !_NETSMB_NETBIOS_H_ */
diff --git a/usr/src/uts/common/netsmb/smb.h b/usr/src/uts/common/netsmb/smb.h
new file mode 100644
index 0000000000..40c3522cfc
--- /dev/null
+++ b/usr/src/uts/common/netsmb/smb.h
@@ -0,0 +1,1522 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Now many of these defines are from samba4 code, by Andrew Tridgell.
+ * (Permission given to Conrad Minshall at CIFS plugfest Aug 13 2003.)
+ * (Note the main decision was whether to use defines found in MS includes
+ * and web pages, versus Samba, and the deciding factor is which developers
+ * are more likely to be looking at this code base.)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb.h,v 1.36.90.1 2005/05/27 02:35:29 lindak Exp $
+ */
+
+/*
+ * Common definintions and structures for SMB/CIFS protocol
+ */
+
+#ifndef _NETSMB_SMB_H_
+#define _NETSMB_SMB_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file should be purely SMB protocol definition stuff.
+ * (Please don't make it a catch-all:)
+ */
+
+/*
+ * SMB dialects that we have to deal with.
+ */
+enum smb_dialects {
+ SMB_DIALECT_NONE,
+ SMB_DIALECT_CORE, /* PC NETWORK PROGRAM 1.0, PCLAN1.0 */
+ SMB_DIALECT_COREPLUS, /* MICROSOFT NETWORKS 1.03 */
+ SMB_DIALECT_LANMAN1_0, /* MICROSOFT NETWORKS 3.0, LANMAN1.0 */
+ SMB_DIALECT_LANMAN2_0, /* LM1.2X002, DOS LM1.2X002, Samba */
+ SMB_DIALECT_LANMAN2_1, /* DOS LANMAN2.1, LANMAN2.1 */
+ SMB_DIALECT_NTLM0_12 /* NT LM 0.12, Windows for Workgroups */
+ /* 3.1a, * NT LANMAN 1.0 */
+};
+
+/*
+ * Formats of data/string buffers
+ */
+#define SMB_DT_DATA 1
+#define SMB_DT_DIALECT 2
+#define SMB_DT_PATHNAME 3
+#define SMB_DT_ASCII 4
+#define SMB_DT_VARIABLE 5
+
+/*
+ * SMB header
+ */
+#define SMB_SIGNATURE "\xFFSMB"
+#define SMB_SIGLEN 4
+#define SMB_HDRCMD(p) (*((uchar_t *)(p) + SMB_SIGLEN))
+#define SMB_HDRMID(p) (letohs(*(ushort_t *)((uchar_t *)(p) + 30)))
+#define SMB_HDRLEN 32
+/*
+ * bits in the smb_flags field
+ */
+#define SMB_FLAGS_SUPPORT_LOCKREAD 0x01
+#define SMB_FLAGS_CLIENT_BUF_AVAIL 0x02
+#define SMB_FLAGS_CASELESS 0x08
+#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10
+#define SMB_FLAGS_REQUEST_OPLOCK 0x20
+#define SMB_FLAGS_REQUEST_BATCH_OPLOCK 0x40
+#define SMB_FLAGS_SERVER_RESP 0x80
+
+/*
+ * bits in the smb_flags2 field
+ */
+#define SMB_FLAGS2_KNOWS_LONG_NAMES 0x0001
+#define SMB_FLAGS2_KNOWS_EAS 0x0002 /* client know about EAs */
+#define SMB_FLAGS2_SECURITY_SIGNATURE 0x0004 /* check SMB integrity */
+#define SMB_FLAGS2_IS_LONG_NAME 0x0040 /* any path name is long name */
+#define SMB_FLAGS2_EXT_SEC 0x0800 /* client aware of Extended */
+ /* Security negotiation */
+#define SMB_FLAGS2_DFS 0x1000 /* resolve paths in DFS */
+#define SMB_FLAGS2_PAGING_IO 0x2000 /* for exec */
+#define SMB_FLAGS2_ERR_STATUS 0x4000 /* 1 - status.status */
+#define SMB_FLAGS2_UNICODE 0x8000 /* use Unicode for strings */
+
+#define SMB_UID_UNKNOWN 0xffff
+#define SMB_TID_UNKNOWN 0xffff
+#define SMB_FID_UNUSED 0xffff
+
+/*
+ * Security mode bits
+ */
+#define SMB_SM_USER 0x01 /* server in the user security mode */
+#define SMB_SM_ENCRYPT 0x02 /* use challenge/responce */
+#define SMB_SM_SIGS 0x04
+#define SMB_SM_SIGS_REQUIRE 0x08
+
+/*
+ * Action bits in session setup reply
+ */
+#define SMB_ACT_GUEST 0x01
+
+/*
+ * NTLM capabilities
+ */
+#define SMB_CAP_RAW_MODE 0x0001
+#define SMB_CAP_MPX_MODE 0x0002
+#define SMB_CAP_UNICODE 0x0004
+#define SMB_CAP_LARGE_FILES 0x0008 /* 64 bit offsets supported */
+#define SMB_CAP_NT_SMBS 0x0010
+#define SMB_CAP_RPC_REMOTE_APIS 0x0020
+#define SMB_CAP_STATUS32 0x0040
+#define SMB_CAP_LEVEL_II_OPLOCKS 0x0080
+#define SMB_CAP_LOCK_AND_READ 0x0100
+#define SMB_CAP_NT_FIND 0x0200
+#define SMB_CAP_DFS 0x1000
+#define SMB_CAP_INFOLEVEL_PASSTHRU 0x2000
+#define SMB_CAP_LARGE_READX 0x4000
+#define SMB_CAP_LARGE_WRITEX 0x8000
+#define SMB_CAP_UNIX 0x00800000
+#define SMB_CAP_BULK_TRANSFER 0x20000000
+#define SMB_CAP_COMPRESSED_DATA 0x40000000
+#define SMB_CAP_EXT_SECURITY 0x80000000
+
+/*
+ * File attributes
+ */
+#define SMB_FA_RDONLY 0x01
+#define SMB_FA_HIDDEN 0x02
+#define SMB_FA_SYSTEM 0x04
+#define SMB_FA_VOLUME 0x08
+#define SMB_FA_DIR 0x10
+#define SMB_FA_ARCHIVE 0x20
+
+/*
+ * Extended file attributes
+ */
+#define SMB_EFA_RDONLY 0x00000001
+#define SMB_EFA_HIDDEN 0x00000002
+#define SMB_EFA_SYSTEM 0x00000004
+#define SMB_EFA_VOLUME 0x00000008
+#define SMB_EFA_DIRECTORY 0x00000010
+#define SMB_EFA_ARCHIVE 0x00000020
+#define SMB_EFA_DEVICE 0x00000040
+#define SMB_EFA_NORMAL 0x00000080
+#define SMB_EFA_TEMPORARY 0x00000100
+#define SMB_EFA_SPARSE 0x00000200
+#define SMB_EFA_REPARSE_POINT 0x00000400
+#define SMB_EFA_COMPRESSED 0x00000800
+#define SMB_EFA_OFFLINE 0x00001000
+#define SMB_EFA_NONINDEXED 0x00002000
+#define SMB_EFA_ENCRYPTED 0x00004000
+#define SMB_EFA_POSIX_SEMANTICS 0x01000000
+#define SMB_EFA_BACKUP_SEMANTICS 0x02000000
+#define SMB_EFA_DELETE_ON_CLOSE 0x04000000
+#define SMB_EFA_SEQUENTIAL_SCAN 0x08000000
+#define SMB_EFA_RANDOM_ACCESS 0x10000000
+#define SMB_EFA_NO_BUFFERING 0x20000000
+#define SMB_EFA_WRITE_THROUGH 0x80000000
+
+/*
+ * Access Mode Encoding
+ */
+#define SMB_AM_OPENREAD 0x0000
+#define SMB_AM_OPENWRITE 0x0001
+#define SMB_AM_OPENRW 0x0002
+#define SMB_AM_OPENEXEC 0x0003
+#define SMB_AM_OPENMODE 0x0003 /* mask for access mode bits */
+#define SMB_SM_COMPAT 0x0000
+#define SMB_SM_EXCLUSIVE 0x0010
+#define SMB_SM_DENYWRITE 0x0020
+#define SMB_SM_DENYREADEXEC 0x0030
+#define SMB_SM_DENYNONE 0x0040
+
+/* NT_CREATE_ANDX flags */
+#define NTCREATEX_FLAGS_REQUEST_OPLOCK 0x02
+#define NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK 0x04
+#define NTCREATEX_FLAGS_OPEN_DIRECTORY 0x08
+#define NTCREATEX_FLAGS_EXTENDED 0x10
+
+/* NT_CREATE_ANDX share_access (share mode) */
+#define NTCREATEX_SHARE_ACCESS_NONE 0
+#define NTCREATEX_SHARE_ACCESS_READ 1
+#define NTCREATEX_SHARE_ACCESS_WRITE 2
+#define NTCREATEX_SHARE_ACCESS_DELETE 4
+#define NTCREATEX_SHARE_ACCESS_ALL 7
+
+/* NT_CREATE_ANDX open_disposition */
+#define NTCREATEX_DISP_SUPERSEDE 0 /* if file exists supersede it */
+#define NTCREATEX_DISP_OPEN 1 /* exists ? open it : fail */
+#define NTCREATEX_DISP_CREATE 2 /* exists ? fail : create it */
+#define NTCREATEX_DISP_OPEN_IF 3 /* exists ? open it : create it */
+#define NTCREATEX_DISP_OVERWRITE 4 /* exists ? overwrite : fail */
+#define NTCREATEX_DISP_OVERWRITE_IF 5 /* exists ? overwrite : create */
+
+/* NT_CREATE_ANDX create_options */
+#define NTCREATEX_OPTIONS_DIRECTORY 0x0001
+#define NTCREATEX_OPTIONS_WRITE_THROUGH 0x0002
+#define NTCREATEX_OPTIONS_SEQUENTIAL_ONLY 0x0004
+#define NTCREATEX_OPTIONS_SYNC_ALERT 0x0010
+#define NTCREATEX_OPTIONS_ASYNC_ALERT 0x0020
+#define NTCREATEX_OPTIONS_NON_DIRECTORY_FILE 0x0040
+#define NTCREATEX_OPTIONS_NO_EA_KNOWLEDGE 0x0200
+#define NTCREATEX_OPTIONS_EIGHT_DOT_THREE_ONLY 0x0400
+#define NTCREATEX_OPTIONS_RANDOM_ACCESS 0x0800
+#define NTCREATEX_OPTIONS_DELETE_ON_CLOSE 0x1000
+#define NTCREATEX_OPTIONS_OPEN_BY_FILE_ID 0x2000
+
+/* NT_CREATE_ANDX "impersonation" */
+#define NTCREATEX_IMPERSONATION_ANONYMOUS 0
+#define NTCREATEX_IMPERSONATION_IDENTIFICATION 1
+#define NTCREATEX_IMPERSONATION_IMPERSONATION 2
+#define NTCREATEX_IMPERSONATION_DELEGATION 3
+
+/* NT_CREATE_ANDX security flags */
+#define NTCREATEX_SECURITY_DYNAMIC 1
+#define NTCREATEX_SECURITY_ALL 2
+
+/* NT_CREATE_ANDX create_action in reply */
+#define NTCREATEX_ACTION_EXISTED 1
+#define NTCREATEX_ACTION_CREATED 2
+#define NTCREATEX_ACTION_TRUNCATED 3
+
+/* SMB_TRANS2_FIND_FIRST2/SMB_TRANS2_FIND_NEXT2 flags */
+#define FIND2_CLOSE_AFTER_REQUEST 0x0001
+#define FIND2_CLOSE_ON_EOS 0x0002
+#define FIND2_RETURN_RESUME_KEYS 0x0004
+#define FIND2_CONTINUE_SEARCH 0x0008
+#define FIND2_BACKUP_INTENT 0x0010
+
+/*
+ * SMB commands
+ */
+#define SMB_COM_CREATE_DIRECTORY 0x00
+#define SMB_COM_DELETE_DIRECTORY 0x01
+#define SMB_COM_OPEN 0x02
+#define SMB_COM_CREATE 0x03
+#define SMB_COM_CLOSE 0x04
+#define SMB_COM_FLUSH 0x05
+#define SMB_COM_DELETE 0x06
+#define SMB_COM_RENAME 0x07
+#define SMB_COM_QUERY_INFORMATION 0x08
+#define SMB_COM_SET_INFORMATION 0x09
+#define SMB_COM_READ 0x0A
+#define SMB_COM_WRITE 0x0B
+#define SMB_COM_LOCK_BYTE_RANGE 0x0C
+#define SMB_COM_UNLOCK_BYTE_RANGE 0x0D
+#define SMB_COM_CREATE_TEMPORARY 0x0E
+#define SMB_COM_CREATE_NEW 0x0F
+#define SMB_COM_CHECK_DIRECTORY 0x10
+#define SMB_COM_PROCESS_EXIT 0x11
+#define SMB_COM_SEEK 0x12
+#define SMB_COM_LOCK_AND_READ 0x13
+#define SMB_COM_WRITE_AND_UNLOCK 0x14
+#define SMB_COM_READ_RAW 0x1A
+#define SMB_COM_READ_MPX 0x1B
+#define SMB_COM_READ_MPX_SECONDARY 0x1C
+#define SMB_COM_WRITE_RAW 0x1D
+#define SMB_COM_WRITE_MPX 0x1E
+#define SMB_COM_WRITE_COMPLETE 0x20
+#define SMB_COM_SET_INFORMATION2 0x22
+#define SMB_COM_QUERY_INFORMATION2 0x23
+#define SMB_COM_LOCKING_ANDX 0x24
+#define SMB_COM_TRANSACTION 0x25
+#define SMB_COM_TRANSACTION_SECONDARY 0x26
+#define SMB_COM_IOCTL 0x27
+#define SMB_COM_IOCTL_SECONDARY 0x28
+#define SMB_COM_COPY 0x29
+#define SMB_COM_MOVE 0x2A
+#define SMB_COM_ECHO 0x2B
+#define SMB_COM_WRITE_AND_CLOSE 0x2C
+#define SMB_COM_OPEN_ANDX 0x2D
+#define SMB_COM_READ_ANDX 0x2E
+#define SMB_COM_WRITE_ANDX 0x2F
+#define SMB_COM_CLOSE_AND_TREE_DISC 0x31
+#define SMB_COM_TRANSACTION2 0x32
+#define SMB_COM_TRANSACTION2_SECONDARY 0x33
+#define SMB_COM_FIND_CLOSE2 0x34
+#define SMB_COM_FIND_NOTIFY_CLOSE 0x35
+#define SMB_COM_TREE_CONNECT 0x70
+#define SMB_COM_TREE_DISCONNECT 0x71
+#define SMB_COM_NEGOTIATE 0x72
+#define SMB_COM_SESSION_SETUP_ANDX 0x73
+#define SMB_COM_LOGOFF_ANDX 0x74
+#define SMB_COM_TREE_CONNECT_ANDX 0x75
+#define SMB_COM_QUERY_INFORMATION_DISK 0x80
+#define SMB_COM_SEARCH 0x81
+#define SMB_COM_FIND 0x82
+#define SMB_COM_FIND_UNIQUE 0x83
+#define SMB_COM_NT_TRANSACT 0xA0
+#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1
+#define SMB_COM_NT_CREATE_ANDX 0xA2
+#define SMB_COM_NT_CANCEL 0xA4
+#define SMB_COM_OPEN_PRINT_FILE 0xC0
+#define SMB_COM_WRITE_PRINT_FILE 0xC1
+#define SMB_COM_CLOSE_PRINT_FILE 0xC2
+#define SMB_COM_GET_PRINT_QUEUE 0xC3
+#define SMB_COM_READ_BULK 0xD8
+#define SMB_COM_WRITE_BULK 0xD9
+#define SMB_COM_WRITE_BULK_DATA 0xDA
+
+/*
+ * SMB_COM_TRANSACTION2 subcommands
+ */
+#define SMB_TRANS2_OPEN2 0x00
+#define SMB_TRANS2_FIND_FIRST2 0x01
+#define SMB_TRANS2_FIND_NEXT2 0x02
+#define SMB_TRANS2_QUERY_FS_INFORMATION 0x03
+#define SMB_TRANS2_SETFSINFO 0x04
+#define SMB_TRANS2_QUERY_PATH_INFORMATION 0x05
+#define SMB_TRANS2_SET_PATH_INFORMATION 0x06
+#define SMB_TRANS2_QUERY_FILE_INFORMATION 0x07
+#define SMB_TRANS2_SET_FILE_INFORMATION 0x08
+#define SMB_TRANS2_FSCTL 0x09
+#define SMB_TRANS2_IOCTL2 0x0A
+#define SMB_TRANS2_FIND_NOTIFY_FIRST 0x0B
+#define SMB_TRANS2_FIND_NOTIFY_NEXT 0x0C
+#define SMB_TRANS2_CREATE_DIRECTORY 0x0D
+#define SMB_TRANS2_SESSION_SETUP 0x0E
+#define SMB_TRANS2_GET_DFS_REFERRAL 0x10
+#define SMB_TRANS2_REPORT_DFS_INCONSISTENCY 0x11
+
+/*
+ * SMB_COM_NT_TRANSACT subcommands
+ */
+#define NT_TRANSACT_CREATE 0x01
+#define NT_TRANSACT_IOCTL 0x02
+#define NT_TRANSACT_SET_SECURITY_DESC 0x03
+#define NT_TRANSACT_NOTIFY_CHANGE 0x04
+#define NT_TRANSACT_RENAME 0x05
+#define NT_TRANSACT_QUERY_SECURITY_DESC 0x06
+#define NT_TRANSACT_GET_USER_QUOTA 0x07
+#define NT_TRANSACT_SET_USER_QUOTA 0x08
+
+/*
+ * SMB_TRANS2_QUERY_FS_INFORMATION levels
+ */
+#define SMB_QFS_ALLOCATION 1
+#define SMB_QFS_VOLUME 2
+#define SMB_QFS_LABEL_INFO 0x101
+#define SMB_QFS_VOLUME_INFO 0x102
+#define SMB_QFS_SIZE_INFO 0x103
+#define SMB_QFS_DEVICE_INFO 0x104
+#define SMB_QFS_ATTRIBUTE_INFO 0x105
+#define SMB_QFS_UNIX_INFO 0x200
+#define SMB_QFS_MAC_FS_INFO 0x301
+#define SMB_QFS_VOLUME_INFORMATION 1001
+#define SMB_QFS_SIZE_INFORMATION 1003
+#define SMB_QFS_DEVICE_INFORMATION 1004
+#define SMB_QFS_ATTRIBUTE_INFORMATION 1005
+#define SMB_QFS_QUOTA_INFORMATION 1006
+#define SMB_QFS_FULL_SIZE_INFORMATION 1007
+#define SMB_QFS_OBJECTID_INFORMATION 1008
+
+
+/*
+ * SMB_QFS_ATTRIBUTE_INFO bits.
+ * The following info found in msdn
+ * (http://msdn.microsoft.com/library/default.asp?
+ * url=/library/en-us/wmisdk/wmi/win32_cdromdrive.asp)
+ * Naming is mostly as in samba, to help Those Who Google.
+ */
+#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
+#define FILE_CASE_PRESERVED_NAMES 0x00000002
+#define FILE_UNICODE_ON_DISK 0x00000004
+#define FILE_PERSISTENT_ACLS 0x00000008
+#define FILE_FILE_COMPRESSION 0x00000010
+#define FILE_VOLUME_QUOTAS 0x00000020
+#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
+#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080
+#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100
+#define FILE_SUPPORTS_LONG_NAMES 0x00004000
+#define FILE_VOLUME_IS_COMPRESSED 0x00008000
+#define FILE_SUPPORTS_OBJECT_IDS 0x00010000
+#define FILE_SUPPORTS_ENCRYPTION 0x00020000
+#define FILE_NAMED_STREAMS 0x00040000
+
+/*
+ * SMB_TRANS2_QUERY_PATH levels
+ */
+#define SMB_QFILEINFO_STANDARD 1
+#define SMB_QFILEINFO_EA_SIZE 2
+#define SMB_QFILEINFO_EAS_FROM_LIST 3
+#define SMB_QFILEINFO_ALL_EAS 4
+#define SMB_QFILEINFO_IS_NAME_VALID 6 /* QPATHINFO only? */
+#define SMB_QFILEINFO_BASIC_INFO 0x101
+#define SMB_QFILEINFO_STANDARD_INFO 0x102
+#define SMB_QFILEINFO_EA_INFO 0x103
+#define SMB_QFILEINFO_NAME_INFO 0x104
+#define SMB_QFILEINFO_ALLOCATION_INFO 0x105
+#define SMB_QFILEINFO_END_OF_FILE_INFO 0x106
+#define SMB_QFILEINFO_ALL_INFO 0x107
+#define SMB_QFILEINFO_ALT_NAME_INFO 0x108
+#define SMB_QFILEINFO_STREAM_INFO 0x109
+#define SMB_QFILEINFO_COMPRESSION_INFO 0x10b
+#define SMB_QFILEINFO_UNIX_BASIC 0x200
+#define SMB_QFILEINFO_UNIX_LINK 0x201
+#define SMB_QFILEINFO_MAC_DT_GET_APPL 0x306
+#define SMB_QFILEINFO_MAC_DT_GET_ICON 0x307
+#define SMB_QFILEINFO_MAC_DT_GET_ICON_INFO 0x308
+#define SMB_QFILEINFO_BASIC_INFORMATION 1004
+#define SMB_QFILEINFO_STANDARD_INFORMATION 1005
+#define SMB_QFILEINFO_INTERNAL_INFORMATION 1006
+#define SMB_QFILEINFO_EA_INFORMATION 1007
+#define SMB_QFILEINFO_ACCESS_INFORMATION 1008
+#define SMB_QFILEINFO_NAME_INFORMATION 1009
+#define SMB_QFILEINFO_POSITION_INFORMATION 1014
+#define SMB_QFILEINFO_MODE_INFORMATION 1016
+#define SMB_QFILEINFO_ALIGNMENT_INFORMATION 1017
+#define SMB_QFILEINFO_ALL_INFORMATION 1018
+#define SMB_QFILEINFO_ALT_NAME_INFORMATION 1021
+#define SMB_QFILEINFO_STREAM_INFORMATION 1022
+#define SMB_QFILEINFO_COMPRESSION_INFORMATION 1028
+#define SMB_QFILEINFO_NETWORK_OPEN_INFORMATION 1034
+#define SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION 1035
+
+/*
+ * SMB_TRANS2_FIND_FIRST2 information levels
+ */
+#define SMB_FIND_STANDARD 1
+#define SMB_FIND_EA_SIZE 2
+#define SMB_FIND_EAS_FROM_LIST 3
+#define SMB_FIND_DIRECTORY_INFO 0x101
+#define SMB_FIND_FULL_DIRECTORY_INFO 0x102
+#define SMB_FIND_NAME_INFO 0x103
+#define SMB_FIND_BOTH_DIRECTORY_INFO 0x104
+#define SMB_FIND_UNIX_INFO 0x200
+
+/*
+ * Selectors for NT_TRANSACT_QUERY_SECURITY_DESC and
+ * NT_TRANSACT_SET_SECURITY_DESC. Details found in the MSDN
+ * library by searching on security_information.
+ * Note the protected/unprotected bits did not exist in NT.
+ */
+
+#define OWNER_SECURITY_INFORMATION 0x00000001
+#define GROUP_SECURITY_INFORMATION 0x00000002
+#define DACL_SECURITY_INFORMATION 0x00000004
+#define SACL_SECURITY_INFORMATION 0x00000008
+#define UNPROTECTED_SACL_SECURITY_INFORMATION 0x10000000
+#define UNPROTECTED_DACL_SECURITY_INFORMATION 0x20000000
+#define PROTECTED_SACL_SECURITY_INFORMATION 0x40000000
+#define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000
+
+/*
+ * security descriptor header
+ * it is followed by the optional SIDs and ACLs
+ * note this is "raw", ie little-endian
+ */
+struct ntsecdesc {
+ uint8_t sd_revision; /* 0x01 observed between W2K */
+ uint8_t sd_pad1;
+ uint16_t sd_flags;
+ uint32_t sd_owneroff; /* offset to owner SID */
+ uint32_t sd_groupoff; /* offset to group SID */
+ uint32_t sd_sacloff; /* offset to system/audit ACL */
+ uint32_t sd_dacloff; /* offset to discretionary ACL */
+}; /* XXX: __attribute__((__packed__)); */
+typedef struct ntsecdesc ntsecdesc_t;
+
+#define wset_sdrevision(s) ((s)->sd_revision = 0x01)
+#define sdflags(s) (letohs((s)->sd_flags))
+#define wset_sdflags(s, f) ((s)->sd_flags = letohs(f))
+#define sdowner(s) \
+ ((struct ntsid *)((s)->sd_owneroff ? \
+ (char *)(s) + letohl((s)->sd_owneroff) : \
+ NULL))
+#define wset_sdowneroff(s, o) ((s)->sd_owneroff = htolel(o))
+#define sdgroup(s) \
+ ((struct ntsid *)((s)->sd_groupoff ? \
+ (char *)(s) + letohl((s)->sd_groupoff) : \
+ NULL))
+#define wset_sdgroupoff(s, o) ((s)->sd_groupoff = htolel(o))
+#define sdsacl(s) \
+ ((struct ntacl *)((s)->sd_sacloff ? \
+ (char *)(s) + letohl((s)->sd_sacloff) : \
+ NULL))
+#define wset_sdsacloff(s, o) ((s)->sd_sacloff = htolel(o))
+#define sddacl(s) \
+ ((struct ntacl *)((s)->sd_dacloff ? \
+ (char *)(s) + letohl((s)->sd_dacloff) : \
+ NULL))
+#define wset_sddacloff(s, o) ((s)->sd_dacloff = htolel(o))
+
+/*
+ * sd_flags bits
+ */
+#define SD_OWNER_DEFAULTED 0x0001
+#define SD_GROUP_DEFAULTED 0x0002
+#define SD_DACL_PRESENT 0x0004
+#define SD_DACL_DEFAULTED 0x0008
+#define SD_SACL_PRESENT 0x0010
+#define SD_SACL_DEFAULTED 0x0020
+#define SD_DACL_TRUSTED 0x0040
+#define SD_SERVER_SECURITY 0x0080
+#define SD_DACL_AUTO_INHERIT_REQ 0x0100
+#define SD_SACL_AUTO_INHERIT_REQ 0x0200
+#define SD_DACL_AUTO_INHERITED 0x0400
+#define SD_SACL_AUTO_INHERITED 0x0800
+#define SD_DACL_PROTECTED 0x1000
+#define SD_SACL_PROTECTED 0x2000
+#define SD_RM_CONTROL_VALID 0x4000
+#define SD_SELF_RELATIVE 0x8000
+
+/*
+ * access control list header
+ * it is followed by the ACEs
+ * note this is "raw", ie little-endian
+ */
+struct ntacl {
+ uint8_t acl_revision; /* 0x02 observed with W2K */
+ uint8_t acl_pad1;
+ uint16_t acl_len; /* bytes; includes this header */
+ uint16_t acl_acecount;
+ uint16_t acl_pad2;
+}; /* XXX: __attribute__((__packed__)); */
+typedef struct ntacl ntacl_t;
+
+#define wset_aclrevision(a) ((a)->acl_revision = 0x02)
+#define acllen(a) (letohs((a)->acl_len))
+#define wset_acllen(a, l) ((a)->acl_len = htoles(l))
+#define aclacecount(a) (letohs((a)->acl_acecount))
+#define wset_aclacecount(a, c) ((a)->acl_acecount = htoles(c))
+#define aclace(a) ((struct ntace *)((char *)(a) + sizeof (struct ntacl)))
+
+/*
+ * access control entry header
+ * it is followed by type-specific ace data,
+ * which for the simple types is just a SID
+ * note this is "raw", ie little-endian
+ */
+struct ntace {
+ uint8_t ace_type;
+ uint8_t ace_flags;
+ uint16_t ace_len; /* bytes; includes this header */
+ uint32_t ace_rights; /* generic, standard, specific, etc */
+}; /* XXX: __attribute__((__packed__)); */
+
+#define acetype(a) ((a)->ace_type)
+#define wset_acetype(a, t) ((a)->ace_type = (t))
+#define aceflags(a) ((a)->ace_flags)
+#define wset_aceflags(a, f) ((a)->ace_flags = (f))
+#define acelen(a) (letohs((a)->ace_len))
+#define wset_acelen(a, l) ((a)->ace_len = htoles(l))
+#define acerights(a) (letohl((a)->ace_rights))
+#define wset_acerights(a, r) ((a)->ace_rights = htolel(r))
+#define aceace(a) ((struct ntace *)((char *)(a) + acelen(a)))
+#define acesid(a) ((struct ntsid *)((char *)(a) + sizeof (struct ntace)))
+
+/*
+ * ace_rights
+ * (Samba bit names are used here, with permission, as the shorter Windows
+ * names are more likely to cause namespace collisions)
+ */
+#define SA_RIGHT_FILE_READ_DATA 0x00000001
+#define SA_RIGHT_FILE_WRITE_DATA 0x00000002
+#define SA_RIGHT_FILE_APPEND_DATA 0x00000004
+#define SA_RIGHT_FILE_READ_EA 0x00000008
+#define SA_RIGHT_FILE_WRITE_EA 0x00000010
+#define SA_RIGHT_FILE_EXECUTE 0x00000020
+#define SA_RIGHT_FILE_DELETE_CHILD 0x00000040
+#define SA_RIGHT_FILE_READ_ATTRIBUTES 0x00000080
+#define SA_RIGHT_FILE_WRITE_ATTRIBUTES 0x00000100
+#define SA_RIGHT_FILE_ALL_ACCESS 0x000001FF
+
+#define STD_RIGHT_DELETE_ACCESS 0x00010000
+#define STD_RIGHT_READ_CONTROL_ACCESS 0x00020000
+#define STD_RIGHT_WRITE_DAC_ACCESS 0x00040000
+#define STD_RIGHT_WRITE_OWNER_ACCESS 0x00080000
+#define STD_RIGHT_SYNCHRONIZE_ACCESS 0x00100000
+#define STD_RIGHT_ALL_ACCESS 0x001F0000
+
+#define SEC_RIGHT_SYSTEM_SECURITY 0x01000000
+/*
+ * Don't use MAXIMUM_ALLOWED as Samba (2.2.3 at least) will
+ * return NT_STATUS_INVALID_LOCK_SEQUENCE
+ */
+#define SEC_RIGHT_MAXIMUM_ALLOWED 0x02000000
+
+#define GENERIC_RIGHT_ALL_ACCESS 0x10000000
+#define GENERIC_RIGHT_EXECUTE_ACCESS 0x20000000
+#define GENERIC_RIGHT_WRITE_ACCESS 0x40000000
+#define GENERIC_RIGHT_READ_ACCESS 0x80000000
+
+/*
+ * these mappings are from Windows sample code but are likely incomplete
+ *
+ * GENERIC_RIGHT_READ_ACCESS :
+ * STD_RIGHT_SYNCHRONIZE_ACCESS |
+ * STD_RIGHT_READ_CONTROL_ACCESS |
+ * SA_RIGHT_FILE_READ_ATTRIBUTES |
+ * SA_RIGHT_FILE_READ_EA |
+ * SA_RIGHT_FILE_READ_DATA
+ * GENERIC_RIGHT_WRITE_ACCESS :
+ * STD_RIGHT_SYNCHRONIZE_ACCESS |
+ * STD_RIGHT_READ_CONTROL_ACCESS |
+ * SA_RIGHT_FILE_WRITE_ATTRIBUTES |
+ * SA_RIGHT_FILE_WRITE_EA |
+ * SA_RIGHT_FILE_APPEND_DATA |
+ * SA_RIGHT_FILE_WRITE_DATA
+ * GENERIC_RIGHT_EXECUTE_ACCESS :
+ * STD_RIGHT_SYNCHRONIZE_ACCESS |
+ * STD_RIGHT_READ_CONTROL_ACCESS |
+ * SA_RIGHT_FILE_READ_ATTRIBUTES |
+ * SA_RIGHT_FILE_EXECUTE
+ * GENERIC_RIGHT_ALL_ACCESS :
+ * STD_RIGHT_SYNCHRONIZE_ACCESS |
+ * STD_RIGHT_WRITE_OWNER_ACCESS |
+ * STD_RIGHT_WRITE_DAC_ACCESS |
+ * STD_RIGHT_READ_CONTROL_ACCESS |
+ * STD_RIGHT_DELETE_ACCESS |
+ * SA_RIGHT_FILE_ALL_ACCESS
+ */
+
+/*
+ * security identifier header
+ * it is followed by sid_numauth sub-authorities,
+ * which are 32 bits each.
+ * note the subauths are little-endian on the wire, but
+ * need to be big-endian for memberd/DS
+ */
+#define SIDAUTHSIZE 6
+struct ntsid {
+ uint8_t sid_revision;
+ uint8_t sid_subauthcount;
+ uint8_t sid_authority[SIDAUTHSIZE]; /* ie not little endian */
+}; /* XXX: __attribute__((__packed__)); */
+typedef struct ntsid ntsid_t;
+
+#define sidsubauthcount(s) (s->sid_subauthcount)
+#define sidlen(s) (sizeof (struct ntsid) + 4 * (s)->sid_subauthcount)
+#define MAXSIDLEN (sizeof (struct ntsid) + 4 * KAUTH_NTSID_MAX_AUTHORITIES)
+#define sidsub(s) ((uint32_t *)((char *)(s) + sizeof (struct ntsid)))
+
+/*
+ * MS' defined values for ace_type
+ */
+#define ACCESS_ALLOWED_ACE_TYPE 0x0
+#define ACCESS_DENIED_ACE_TYPE 0x1
+#define SYSTEM_AUDIT_ACE_TYPE 0x2
+#define SYSTEM_ALARM_ACE_TYPE 0x3
+#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x4
+#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x5
+#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x6
+#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x7
+#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x8
+#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x9
+#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0xA
+#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0xB
+#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0xC
+#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0xD
+#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0xE
+#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0xF
+#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10
+
+/*
+ * MS' defined values for ace_flags
+ */
+#define OBJECT_INHERIT_ACE_FLAG 0x01
+#define CONTAINER_INHERIT_ACE_FLAG 0x02
+#define NO_PROPAGATE_INHERIT_ACE_FLAG 0x04
+#define INHERIT_ONLY_ACE_FLAG 0x08
+#define INHERITED_ACE_FLAG 0x10
+#define UNDEF_ACE_FLAG 0x20 /* MS doesn't define it */
+#define VALID_INHERIT_ACE_FLAGS 0x1F
+#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40
+#define FAILED_ACCESS_ACE_FLAG 0x80
+
+/*
+ * Set PATH/FILE information levels
+ */
+#define SMB_SFILEINFO_STANDARD 1
+#define SMB_SFILEINFO_EA_SET 2
+#define SMB_SFILEINFO_BASIC_INFO 0x101
+#define SMB_SFILEINFO_DISPOSITION_INFO 0x102
+#define SMB_SFILEINFO_ALLOCATION_INFO 0x103
+#define SMB_SFILEINFO_END_OF_FILE_INFO 0x104
+#define SMB_SFILEINFO_UNIX_BASIC 0x200
+#define SMB_SFILEINFO_UNIX_LINK 0x201
+#define SMB_SFILEINFO_UNIX_HLINK 0x203
+#define SMB_SFILEINFO_DIRECTORY_INFORMATION 1001
+#define SMB_SFILEINFO_FULL_DIRECTORY_INFORMATION 1002
+#define SMB_SFILEINFO_BOTH_DIRECTORY_INFORMATION 1003
+#define SMB_SFILEINFO_BASIC_INFORMATION 1004
+#define SMB_SFILEINFO_STANDARD_INFORMATION 1005
+#define SMB_SFILEINFO_INTERNAL_INFORMATION 1006
+#define SMB_SFILEINFO_EA_INFORMATION 1007
+#define SMB_SFILEINFO_ACCESS_INFORMATION 1008
+#define SMB_SFILEINFO_NAME_INFORMATION 1009
+#define SMB_SFILEINFO_RENAME_INFORMATION 1010
+#define SMB_SFILEINFO_LINK_INFORMATION 1011
+#define SMB_SFILEINFO_NAMES_INFORMATION 1012
+#define SMB_SFILEINFO_DISPOSITION_INFORMATION 1013
+#define SMB_SFILEINFO_POSITION_INFORMATION 1014
+#define SMB_SFILEINFO_1015 1015 /* ? */
+#define SMB_SFILEINFO_MODE_INFORMATION 1016
+#define SMB_SFILEINFO_ALIGNMENT_INFORMATION 1017
+#define SMB_SFILEINFO_ALL_INFORMATION 1018
+#define SMB_SFILEINFO_ALLOCATION_INFORMATION 1019
+#define SMB_SFILEINFO_END_OF_FILE_INFORMATION 1020
+#define SMB_SFILEINFO_ALT_NAME_INFORMATION 1021
+#define SMB_SFILEINFO_STREAM_INFORMATION 1022
+#define SMB_SFILEINFO_PIPE_INFORMATION 1023
+#define SMB_SFILEINFO_PIPE_LOCAL_INFORMATION 1024
+#define SMB_SFILEINFO_PIPE_REMOTE_INFORMATION 1025
+#define SMB_SFILEINFO_MAILSLOT_QUERY_INFORMATION 1026
+#define SMB_SFILEINFO_MAILSLOT_SET_INFORMATION 1027
+#define SMB_SFILEINFO_COMPRESSION_INFORMATION 1028
+#define SMB_SFILEINFO_OBJECT_ID_INFORMATION 1029
+#define SMB_SFILEINFO_COMPLETION_INFORMATION 1030
+#define SMB_SFILEINFO_MOVE_CLUSTER_INFORMATION 1031
+#define SMB_SFILEINFO_QUOTA_INFORMATION 1032
+#define SMB_SFILEINFO_REPARSE_POINT_INFORMATION 1033
+#define SMB_SFILEINFO_NETWORK_OPEN_INFORMATION 1034
+#define SMB_SFILEINFO_ATTRIBUTE_TAG_INFORMATION 1035
+#define SMB_SFILEINFO_TRACKING_INFORMATION 1036
+#define SMB_SFILEINFO_MAXIMUM_INFORMATION 1037
+
+/*
+ * LOCKING_ANDX LockType flags
+ */
+#define SMB_LOCKING_ANDX_SHARED_LOCK 0x01
+#define SMB_LOCKING_ANDX_OPLOCK_RELEASE 0x02
+#define SMB_LOCKING_ANDX_CHANGE_LOCKTYPE 0x04
+#define SMB_LOCKING_ANDX_CANCEL_LOCK 0x08
+#define SMB_LOCKING_ANDX_LARGE_FILES 0x10
+
+/*
+ * Some names length limitations. Some of them aren't declared by specs,
+ * but we need reasonable limits.
+ */
+#define SMB_MAXSRVNAMELEN 15 /* NetBIOS limit */
+#define SMB_MAXUSERNAMELEN 128
+#define SMB_MAXPASSWORDLEN 128
+#define SMB_MAXSHARENAMELEN 128
+#define SMB_MAXPKTLEN 0x1FFFF
+#define SMB_MAXCHALLENGELEN 8
+#define SMB_MAXFNAMELEN 255 /* Keep in sync with MAXNAMLEN */
+
+#define SMB_RCNDELAY 2 /* seconds between reconnect attempts */
+/*
+ * leave this zero - we can't ssecond guess server side effects of
+ * duplicate ops, this isn't nfs!
+ */
+#define SMBMAXRESTARTS 0
+#define SMB_MAXSETUPWORDS 3 /* max # of setup words in trans/t2 */
+
+/*
+ * Error classes
+ */
+#define SMBSUCCESS 0x00
+#define ERRDOS 0x01
+#define ERRSRV 0x02
+#define ERRHRD 0x03 /* Error is an hardware error. */
+#define ERRCMD 0xFF /* Command was not in the "SMB" format. */
+
+/*
+ * Error codes for the ERRDOS class
+ */
+#define ERRbadfunc 1 /* Invalid function */
+#define ERRbadfile 2 /* File not found (last component) */
+#define ERRbadpath 3 /* Directory invalid */
+#define ERRnofids 4 /* Too many open files */
+#define ERRnoaccess 5 /* Access denied */
+#define ERRbadfid 6 /* Invalid file handle */
+#define ERRbadmcb 7 /* Memory control blocks destroyed (huh ?) */
+#define ERRnomem 8 /* Insufficient memory */
+#define ERRbadmem 9 /* Invalid memory block address */
+#define ERRbadenv 10 /* Invalid environment */
+#define ERRbadformat 11 /* Invalid format */
+#define ERRbadaccess 12 /* Invalid open mode */
+#define ERRbaddata 13 /* Invalid data */
+#define ERRoutofmem 14 /* out of memory */
+#define ERRbaddrive 15 /* Invalid drive specified */
+#define ERRremcd 16 /* An attempt to delete current directory */
+#define ERRdiffdevice 17 /* cross fs rename/move */
+#define ERRnofiles 18 /* no more files found in file search */
+#define ERRwriteprotect 19
+#define ERRnotready 21
+#define ERRbadcmd 22
+#define ERRcrc 23
+#define ERRbadlength 24
+#define ERRsectornotfound 27
+#define ERRbadshare 32 /* Share mode can't be granted */
+#define ERRlock 33 /* Lock conflicts with existing lock */
+#define ERRwrongdisk 34
+#define ERRhandleeof 38
+#define ERRunsup 50 /* unsupported - Win 95 */
+#define ERRnetnamedel 64
+#define ERRnoipc 66 /* ipc unsupported */
+#define ERRnosuchshare 67 /* invalid share name */
+#define ERRtoomanynames 68
+#define ERRfilexists 80 /* requested file name already exists */
+#define ERRinvalidparam 87
+#define ERRcannotopen 110 /* cannot open the file */
+#define ERRinsufficientbuffer 122
+#define ERRinvalidname 123
+#define ERRunknownlevel 124
+#define ERRdirnotempty 145
+#define ERRnotlocked 158 /* region was not locked by this context */
+#define ERRrename 183
+#define ERRbadpipe 230 /* named pipe invalid */
+#define ERRpipebusy 231 /* all pipe instances are busy */
+#define ERRpipeclosing 232 /* close in progress */
+#define ERRnotconnected 233 /* nobody on other end of pipe */
+#define ERRmoredata 234 /* more data to be returned */
+#define ERRnomoreitems 259
+#define ERRbaddirectory 267 /* invalid directory name */
+#define ERReasunsupported 282 /* extended attributes not supported */
+#define ERRlogonfailure 1326
+#define ERRbuftoosmall 2123
+#define ERRunknownipc 2142
+#define ERRnosuchprintjob 2151
+#define ERRinvgroup 2455
+
+/*
+ * Error codes for the ERRSRV class
+ */
+#define ERRerror 1 /* Non-specific error code */
+#define ERRbadpw 2 /* Bad password */
+#define ERRbadtype 3 /* reserved */
+#define ERRaccess 4 /* client doesn't have enough access rights */
+#define ERRinvnid 5 /* The Tid specified in a command is invalid */
+#define ERRinvnetname 6 /* Invalid server name in the tree connect */
+#define ERRinvdevice 7 /* Printer and not printer devices are mixed */
+#define ERRqfull 49 /* Print queue full */
+#define ERRqtoobig 50 /* Print queue full - no space */
+#define ERRinvpfid 52 /* Invalid print file FID */
+#define ERRsmbcmd 64 /* The server did not recognise the command */
+#define ERRsrverror 65 /* The server encountered and internal error */
+#define ERRfilespecs 67 /* The Fid and path name contains an */
+ /* invalid combination */
+#define ERRbadpermits 69 /* Access mode invalid */
+#define ERRsetattrmode 71 /* Attribute mode invalid */
+#define ERRpaused 81 /* Server is paused */
+#define ERRmsgoff 82 /* Not receiving messages */
+#define ERRnoroom 83 /* No room to buffer message */
+#define ERRrmuns 87 /* Too many remote user names */
+#define ERRtimeout 88 /* Operation timed out */
+#define ERRnoresource 89 /* No resources currently available for req */
+#define ERRtoomanyuids 90 /* Too many UIDs active on this session */
+#define ERRbaduid 91 /* The UID is not known in this session */
+#define ERRusempx 250 /* Temporarily unable to support Raw, */
+ /* use MPX mode */
+#define ERRusestd 251 /* Temporarily unable to support Raw, */
+ /* use stdandard r/w */
+#define ERRcontmpx 252 /* Continue in MPX mode */
+#define ERRacctexpired 2239
+#define ERRnosupport 65535 /* Invalid function */
+
+/*
+ * Error codes for the ERRHRD class
+ */
+#define ERRnowrite 19 /* write protected media */
+#define ERRbadunit 20 /* Unknown unit */
+#define ERRnotready 21 /* Drive not ready */
+#define ERRbadcmd 22 /* Unknown command */
+#define ERRdata 23 /* Data error (CRC) */
+#define ERRbadreq 24 /* Bad request structure length */
+#define ERRseek 25 /* Seek error */
+#define ERRbadmedia 26 /* Unknown media type */
+#define ERRbadsector 27 /* Sector not found */
+#define ERRnopaper 28 /* Printer out of paper */
+#define ERRwrite 29 /* Write fault */
+#define ERRread 30 /* Read fault */
+#define ERRgeneral 31 /* General failure */
+#define ERRbadshare 32 /* A open conflicts with an existing open */
+#define ERRlock 33 /* lock/unlock conflict */
+#define ERRwrongdisk 34 /* The wrong disk was found in a drive */
+#define ERRFCBunavail 35 /* No FCBs available */
+#define ERRsharebufexc 36 /* A sharing buffer has been exceeded */
+#define ERRdiskfull 39
+
+/*
+ * RAP error codes (it seems that they returned not only by RAP)
+ */
+#define SMB_ERROR_ACCESS_DENIED 5
+#define SMB_ERROR_NETWORK_ACCESS_DENIED 65
+#define SMB_ERROR_MORE_DATA ERRmoredata
+
+/*
+ * An INCOMPLETE list of 32 bit error codes
+ * For more detail see MSDN and ntstatus.h in the MS DDK
+ *
+ * XXX - these should have the severity and "customer defined" fields
+ * added back in, and smb_maperr32() shouldn't mask those fields out;
+ * 0x80000005 is STATUS_BUFFER_OVERFLOW, with 0xC0000000 is
+ * STATUS_ACCESS_VIOLATION, and we need to distinguish between them.
+ * We use STATUS_BUFFER_OVERFLOW, and need to know its exact value,
+ * so we #define it correctly here; don't strip off the leading
+ * 0x80000000 from it!
+ */
+#define NT_STATUS_BUFFER_OVERFLOW 0x80000005
+#define NT_STATUS_UNSUCCESSFUL 0x0001
+#define NT_STATUS_NOT_IMPLEMENTED 0x0002
+#define NT_STATUS_INVALID_INFO_CLASS 0x0003
+#define NT_STATUS_INFO_LENGTH_MISMATCH 0x0004
+#define NT_STATUS_ACCESS_VIOLATION 0x0005
+#define NT_STATUS_IN_PAGE_ERROR 0x0006
+#define NT_STATUS_PAGEFILE_QUOTA 0x0007
+#define NT_STATUS_INVALID_HANDLE 0x0008
+#define NT_STATUS_BAD_INITIAL_STACK 0x0009
+#define NT_STATUS_BAD_INITIAL_PC 0x000a
+#define NT_STATUS_INVALID_CID 0x000b
+#define NT_STATUS_TIMER_NOT_CANCELED 0x000c
+#define NT_STATUS_INVALID_PARAMETER 0x000d
+#define NT_STATUS_NO_SUCH_DEVICE 0x000e
+#define NT_STATUS_NO_SUCH_FILE 0x000f
+#define NT_STATUS_INVALID_DEVICE_REQUEST 0x0010
+#define NT_STATUS_END_OF_FILE 0x0011
+#define NT_STATUS_WRONG_VOLUME 0x0012
+#define NT_STATUS_NO_MEDIA_IN_DEVICE 0x0013
+#define NT_STATUS_UNRECOGNIZED_MEDIA 0x0014
+#define NT_STATUS_NONEXISTENT_SECTOR 0x0015
+#define NT_STATUS_MORE_PROCESSING_REQUIRED 0x0016
+#define NT_STATUS_NO_MEMORY 0x0017
+#define NT_STATUS_CONFLICTING_ADDRESSES 0x0018
+#define NT_STATUS_NOT_MAPPED_VIEW 0x0019
+#define NT_STATUS_UNABLE_TO_FREE_VM 0x001a
+#define NT_STATUS_UNABLE_TO_DELETE_SECTION 0x001b
+#define NT_STATUS_INVALID_SYSTEM_SERVICE 0x001c
+#define NT_STATUS_ILLEGAL_INSTRUCTION 0x001d
+#define NT_STATUS_INVALID_LOCK_SEQUENCE 0x001e
+#define NT_STATUS_INVALID_VIEW_SIZE 0x001f
+#define NT_STATUS_INVALID_FILE_FOR_SECTION 0x0020
+#define NT_STATUS_ALREADY_COMMITTED 0x0021
+#define NT_STATUS_ACCESS_DENIED 0x0022
+#define NT_STATUS_BUFFER_TOO_SMALL 0x0023
+#define NT_STATUS_OBJECT_TYPE_MISMATCH 0x0024
+#define NT_STATUS_NONCONTINUABLE_EXCEPTION 0x0025
+#define NT_STATUS_INVALID_DISPOSITION 0x0026
+#define NT_STATUS_UNWIND 0x0027
+#define NT_STATUS_BAD_STACK 0x0028
+#define NT_STATUS_INVALID_UNWIND_TARGET 0x0029
+#define NT_STATUS_NOT_LOCKED 0x002a
+#define NT_STATUS_PARITY_ERROR 0x002b
+#define NT_STATUS_UNABLE_TO_DECOMMIT_VM 0x002c
+#define NT_STATUS_NOT_COMMITTED 0x002d
+#define NT_STATUS_INVALID_PORT_ATTRIBUTES 0x002e
+#define NT_STATUS_PORT_MESSAGE_TOO_LONG 0x002f
+#define NT_STATUS_INVALID_PARAMETER_MIX 0x0030
+#define NT_STATUS_INVALID_QUOTA_LOWER 0x0031
+#define NT_STATUS_DISK_CORRUPT_ERROR 0x0032
+#define NT_STATUS_OBJECT_NAME_INVALID 0x0033
+#define NT_STATUS_OBJECT_NAME_NOT_FOUND 0x0034
+#define NT_STATUS_OBJECT_NAME_COLLISION 0x0035
+#define NT_STATUS_HANDLE_NOT_WAITABLE 0x0036
+#define NT_STATUS_PORT_DISCONNECTED 0x0037
+#define NT_STATUS_DEVICE_ALREADY_ATTACHED 0x0038
+#define NT_STATUS_OBJECT_PATH_INVALID 0x0039
+#define NT_STATUS_OBJECT_PATH_NOT_FOUND 0x003a
+#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD 0x003b
+#define NT_STATUS_DATA_OVERRUN 0x003c
+#define NT_STATUS_DATA_LATE_ERROR 0x003d
+#define NT_STATUS_DATA_ERROR 0x003e
+#define NT_STATUS_CRC_ERROR 0x003f
+#define NT_STATUS_SECTION_TOO_BIG 0x0040
+#define NT_STATUS_PORT_CONNECTION_REFUSED 0x0041
+#define NT_STATUS_INVALID_PORT_HANDLE 0x0042
+#define NT_STATUS_SHARING_VIOLATION 0x0043
+#define NT_STATUS_QUOTA_EXCEEDED 0x0044
+#define NT_STATUS_INVALID_PAGE_PROTECTION 0x0045
+#define NT_STATUS_MUTANT_NOT_OWNED 0x0046
+#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED 0x0047
+#define NT_STATUS_PORT_ALREADY_SET 0x0048
+#define NT_STATUS_SECTION_NOT_IMAGE 0x0049
+#define NT_STATUS_SUSPEND_COUNT_EXCEEDED 0x004a
+#define NT_STATUS_THREAD_IS_TERMINATING 0x004b
+#define NT_STATUS_BAD_WORKING_SET_LIMIT 0x004c
+#define NT_STATUS_INCOMPATIBLE_FILE_MAP 0x004d
+#define NT_STATUS_SECTION_PROTECTION 0x004e
+#define NT_STATUS_EAS_NOT_SUPPORTED 0x004f
+#define NT_STATUS_EA_TOO_LARGE 0x0050
+#define NT_STATUS_NONEXISTENT_EA_ENTRY 0x0051
+#define NT_STATUS_NO_EAS_ON_FILE 0x0052
+#define NT_STATUS_EA_CORRUPT_ERROR 0x0053
+#define NT_STATUS_FILE_LOCK_CONFLICT 0x0054
+#define NT_STATUS_LOCK_NOT_GRANTED 0x0055
+#define NT_STATUS_DELETE_PENDING 0x0056
+#define NT_STATUS_CTL_FILE_NOT_SUPPORTED 0x0057
+#define NT_STATUS_UNKNOWN_REVISION 0x0058
+#define NT_STATUS_REVISION_MISMATCH 0x0059
+#define NT_STATUS_INVALID_OWNER 0x005a
+#define NT_STATUS_INVALID_PRIMARY_GROUP 0x005b
+#define NT_STATUS_NO_IMPERSONATION_TOKEN 0x005c
+#define NT_STATUS_CANT_DISABLE_MANDATORY 0x005d
+#define NT_STATUS_NO_LOGON_SERVERS 0x005e
+#define NT_STATUS_NO_SUCH_LOGON_SESSION 0x005f
+#define NT_STATUS_NO_SUCH_PRIVILEGE 0x0060
+#define NT_STATUS_PRIVILEGE_NOT_HELD 0x0061
+#define NT_STATUS_INVALID_ACCOUNT_NAME 0x0062
+#define NT_STATUS_USER_EXISTS 0x0063
+#define NT_STATUS_NO_SUCH_USER 0x0064
+#define NT_STATUS_GROUP_EXISTS 0x0065
+#define NT_STATUS_NO_SUCH_GROUP 0x0066
+#define NT_STATUS_MEMBER_IN_GROUP 0x0067
+#define NT_STATUS_MEMBER_NOT_IN_GROUP 0x0068
+#define NT_STATUS_LAST_ADMIN 0x0069
+#define NT_STATUS_WRONG_PASSWORD 0x006a
+#define NT_STATUS_ILL_FORMED_PASSWORD 0x006b
+#define NT_STATUS_PASSWORD_RESTRICTION 0x006c
+#define NT_STATUS_LOGON_FAILURE 0x006d
+#define NT_STATUS_ACCOUNT_RESTRICTION 0x006e
+#define NT_STATUS_INVALID_LOGON_HOURS 0x006f
+#define NT_STATUS_INVALID_WORKSTATION 0x0070
+#define NT_STATUS_PASSWORD_EXPIRED 0x0071
+#define NT_STATUS_ACCOUNT_DISABLED 0x0072
+#define NT_STATUS_NONE_MAPPED 0x0073
+#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED 0x0074
+#define NT_STATUS_LUIDS_EXHAUSTED 0x0075
+#define NT_STATUS_INVALID_SUB_AUTHORITY 0x0076
+#define NT_STATUS_INVALID_ACL 0x0077
+#define NT_STATUS_INVALID_SID 0x0078
+#define NT_STATUS_INVALID_SECURITY_DESCR 0x0079
+#define NT_STATUS_PROCEDURE_NOT_FOUND 0x007a
+#define NT_STATUS_INVALID_IMAGE_FORMAT 0x007b
+#define NT_STATUS_NO_TOKEN 0x007c
+#define NT_STATUS_BAD_INHERITANCE_ACL 0x007d
+#define NT_STATUS_RANGE_NOT_LOCKED 0x007e
+#define NT_STATUS_DISK_FULL 0x007f
+#define NT_STATUS_SERVER_DISABLED 0x0080
+#define NT_STATUS_SERVER_NOT_DISABLED 0x0081
+#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED 0x0082
+#define NT_STATUS_GUIDS_EXHAUSTED 0x0083
+#define NT_STATUS_INVALID_ID_AUTHORITY 0x0084
+#define NT_STATUS_AGENTS_EXHAUSTED 0x0085
+#define NT_STATUS_INVALID_VOLUME_LABEL 0x0086
+#define NT_STATUS_SECTION_NOT_EXTENDED 0x0087
+#define NT_STATUS_NOT_MAPPED_DATA 0x0088
+#define NT_STATUS_RESOURCE_DATA_NOT_FOUND 0x0089
+#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND 0x008a
+#define NT_STATUS_RESOURCE_NAME_NOT_FOUND 0x008b
+#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED 0x008c
+#define NT_STATUS_FLOAT_DENORMAL_OPERAND 0x008d
+#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO 0x008e
+#define NT_STATUS_FLOAT_INEXACT_RESULT 0x008f
+#define NT_STATUS_FLOAT_INVALID_OPERATION 0x0090
+#define NT_STATUS_FLOAT_OVERFLOW 0x0091
+#define NT_STATUS_FLOAT_STACK_CHECK 0x0092
+#define NT_STATUS_FLOAT_UNDERFLOW 0x0093
+#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO 0x0094
+#define NT_STATUS_INTEGER_OVERFLOW 0x0095
+#define NT_STATUS_PRIVILEGED_INSTRUCTION 0x0096
+#define NT_STATUS_TOO_MANY_PAGING_FILES 0x0097
+#define NT_STATUS_FILE_INVALID 0x0098
+#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED 0x0099
+#define NT_STATUS_INSUFFICIENT_RESOURCES 0x009a
+#define NT_STATUS_DFS_EXIT_PATH_FOUND 0x009b
+#define NT_STATUS_DEVICE_DATA_ERROR 0x009c
+#define NT_STATUS_DEVICE_NOT_CONNECTED 0x009d
+#define NT_STATUS_DEVICE_POWER_FAILURE 0x009e
+#define NT_STATUS_FREE_VM_NOT_AT_BASE 0x009f
+#define NT_STATUS_MEMORY_NOT_ALLOCATED 0x00a0
+#define NT_STATUS_WORKING_SET_QUOTA 0x00a1
+#define NT_STATUS_MEDIA_WRITE_PROTECTED 0x00a2
+#define NT_STATUS_DEVICE_NOT_READY 0x00a3
+#define NT_STATUS_INVALID_GROUP_ATTRIBUTES 0x00a4
+#define NT_STATUS_BAD_IMPERSONATION_LEVEL 0x00a5
+#define NT_STATUS_CANT_OPEN_ANONYMOUS 0x00a6
+#define NT_STATUS_BAD_VALIDATION_CLASS 0x00a7
+#define NT_STATUS_BAD_TOKEN_TYPE 0x00a8
+#define NT_STATUS_BAD_MASTER_BOOT_RECORD 0x00a9
+#define NT_STATUS_INSTRUCTION_MISALIGNMENT 0x00aa
+#define NT_STATUS_INSTANCE_NOT_AVAILABLE 0x00ab
+#define NT_STATUS_PIPE_NOT_AVAILABLE 0x00ac
+#define NT_STATUS_INVALID_PIPE_STATE 0x00ad
+#define NT_STATUS_PIPE_BUSY 0x00ae
+#define NT_STATUS_ILLEGAL_FUNCTION 0x00af
+#define NT_STATUS_PIPE_DISCONNECTED 0x00b0
+#define NT_STATUS_PIPE_CLOSING 0x00b1
+#define NT_STATUS_PIPE_CONNECTED 0x00b2
+#define NT_STATUS_PIPE_LISTENING 0x00b3
+#define NT_STATUS_INVALID_READ_MODE 0x00b4
+#define NT_STATUS_IO_TIMEOUT 0x00b5
+#define NT_STATUS_FILE_FORCED_CLOSED 0x00b6
+#define NT_STATUS_PROFILING_NOT_STARTED 0x00b7
+#define NT_STATUS_PROFILING_NOT_STOPPED 0x00b8
+#define NT_STATUS_COULD_NOT_INTERPRET 0x00b9
+#define NT_STATUS_FILE_IS_A_DIRECTORY 0x00ba
+#define NT_STATUS_NOT_SUPPORTED 0x00bb
+#define NT_STATUS_REMOTE_NOT_LISTENING 0x00bc
+#define NT_STATUS_DUPLICATE_NAME 0x00bd
+#define NT_STATUS_BAD_NETWORK_PATH 0x00be
+#define NT_STATUS_NETWORK_BUSY 0x00bf
+#define NT_STATUS_DEVICE_DOES_NOT_EXIST 0x00c0
+#define NT_STATUS_TOO_MANY_COMMANDS 0x00c1
+#define NT_STATUS_ADAPTER_HARDWARE_ERROR 0x00c2
+#define NT_STATUS_INVALID_NETWORK_RESPONSE 0x00c3
+#define NT_STATUS_UNEXPECTED_NETWORK_ERROR 0x00c4
+#define NT_STATUS_BAD_REMOTE_ADAPTER 0x00c5
+#define NT_STATUS_PRINT_QUEUE_FULL 0x00c6
+#define NT_STATUS_NO_SPOOL_SPACE 0x00c7
+#define NT_STATUS_PRINT_CANCELLED 0x00c8
+#define NT_STATUS_NETWORK_NAME_DELETED 0x00c9
+#define NT_STATUS_NETWORK_ACCESS_DENIED 0x00ca
+#define NT_STATUS_BAD_DEVICE_TYPE 0x00cb
+#define NT_STATUS_BAD_NETWORK_NAME 0x00cc
+#define NT_STATUS_TOO_MANY_NAMES 0x00cd
+#define NT_STATUS_TOO_MANY_SESSIONS 0x00ce
+#define NT_STATUS_SHARING_PAUSED 0x00cf
+#define NT_STATUS_REQUEST_NOT_ACCEPTED 0x00d0
+#define NT_STATUS_REDIRECTOR_PAUSED 0x00d1
+#define NT_STATUS_NET_WRITE_FAULT 0x00d2
+#define NT_STATUS_PROFILING_AT_LIMIT 0x00d3
+#define NT_STATUS_NOT_SAME_DEVICE 0x00d4
+#define NT_STATUS_FILE_RENAMED 0x00d5
+#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED 0x00d6
+#define NT_STATUS_NO_SECURITY_ON_OBJECT 0x00d7
+#define NT_STATUS_CANT_WAIT 0x00d8
+#define NT_STATUS_PIPE_EMPTY 0x00d9
+#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO 0x00da
+#define NT_STATUS_CANT_TERMINATE_SELF 0x00db
+#define NT_STATUS_INVALID_SERVER_STATE 0x00dc
+#define NT_STATUS_INVALID_DOMAIN_STATE 0x00dd
+#define NT_STATUS_INVALID_DOMAIN_ROLE 0x00de
+#define NT_STATUS_NO_SUCH_DOMAIN 0x00df
+#define NT_STATUS_DOMAIN_EXISTS 0x00e0
+#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED 0x00e1
+#define NT_STATUS_OPLOCK_NOT_GRANTED 0x00e2
+#define NT_STATUS_INVALID_OPLOCK_PROTOCOL 0x00e3
+#define NT_STATUS_INTERNAL_DB_CORRUPTION 0x00e4
+#define NT_STATUS_INTERNAL_ERROR 0x00e5
+#define NT_STATUS_GENERIC_NOT_MAPPED 0x00e6
+#define NT_STATUS_BAD_DESCRIPTOR_FORMAT 0x00e7
+#define NT_STATUS_INVALID_USER_BUFFER 0x00e8
+#define NT_STATUS_UNEXPECTED_IO_ERROR 0x00e9
+#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR 0x00ea
+#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR 0x00eb
+#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR 0x00ec
+#define NT_STATUS_NOT_LOGON_PROCESS 0x00ed
+#define NT_STATUS_LOGON_SESSION_EXISTS 0x00ee
+#define NT_STATUS_INVALID_PARAMETER_1 0x00ef
+#define NT_STATUS_INVALID_PARAMETER_2 0x00f0
+#define NT_STATUS_INVALID_PARAMETER_3 0x00f1
+#define NT_STATUS_INVALID_PARAMETER_4 0x00f2
+#define NT_STATUS_INVALID_PARAMETER_5 0x00f3
+#define NT_STATUS_INVALID_PARAMETER_6 0x00f4
+#define NT_STATUS_INVALID_PARAMETER_7 0x00f5
+#define NT_STATUS_INVALID_PARAMETER_8 0x00f6
+#define NT_STATUS_INVALID_PARAMETER_9 0x00f7
+#define NT_STATUS_INVALID_PARAMETER_10 0x00f8
+#define NT_STATUS_INVALID_PARAMETER_11 0x00f9
+#define NT_STATUS_INVALID_PARAMETER_12 0x00fa
+#define NT_STATUS_REDIRECTOR_NOT_STARTED 0x00fb
+#define NT_STATUS_REDIRECTOR_STARTED 0x00fc
+#define NT_STATUS_STACK_OVERFLOW 0x00fd
+#define NT_STATUS_NO_SUCH_PACKAGE 0x00fe
+#define NT_STATUS_BAD_FUNCTION_TABLE 0x00ff
+#define NT_STATUS_VARIABLE_NOT_FOUND 0x0100
+#define NT_STATUS_DIRECTORY_NOT_EMPTY 0x0101
+#define NT_STATUS_FILE_CORRUPT_ERROR 0x0102
+#define NT_STATUS_NOT_A_DIRECTORY 0x0103
+#define NT_STATUS_BAD_LOGON_SESSION_STATE 0x0104
+#define NT_STATUS_LOGON_SESSION_COLLISION 0x0105
+#define NT_STATUS_NAME_TOO_LONG 0x0106
+#define NT_STATUS_FILES_OPEN 0x0107
+#define NT_STATUS_CONNECTION_IN_USE 0x0108
+#define NT_STATUS_MESSAGE_NOT_FOUND 0x0109
+#define NT_STATUS_PROCESS_IS_TERMINATING 0x010a
+#define NT_STATUS_INVALID_LOGON_TYPE 0x010b
+#define NT_STATUS_NO_GUID_TRANSLATION 0x010c
+#define NT_STATUS_CANNOT_IMPERSONATE 0x010d
+#define NT_STATUS_IMAGE_ALREADY_LOADED 0x010e
+#define NT_STATUS_ABIOS_NOT_PRESENT 0x010f
+#define NT_STATUS_ABIOS_LID_NOT_EXIST 0x0110
+#define NT_STATUS_ABIOS_LID_ALREADY_OWNED 0x0111
+#define NT_STATUS_ABIOS_NOT_LID_OWNER 0x0112
+#define NT_STATUS_ABIOS_INVALID_COMMAND 0x0113
+#define NT_STATUS_ABIOS_INVALID_LID 0x0114
+#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE 0x0115
+#define NT_STATUS_ABIOS_INVALID_SELECTOR 0x0116
+#define NT_STATUS_NO_LDT 0x0117
+#define NT_STATUS_INVALID_LDT_SIZE 0x0118
+#define NT_STATUS_INVALID_LDT_OFFSET 0x0119
+#define NT_STATUS_INVALID_LDT_DESCRIPTOR 0x011a
+#define NT_STATUS_INVALID_IMAGE_NE_FORMAT 0x011b
+#define NT_STATUS_RXACT_INVALID_STATE 0x011c
+#define NT_STATUS_RXACT_COMMIT_FAILURE 0x011d
+#define NT_STATUS_MAPPED_FILE_SIZE_ZERO 0x011e
+#define NT_STATUS_TOO_MANY_OPENED_FILES 0x011f
+#define NT_STATUS_CANCELLED 0x0120
+#define NT_STATUS_CANNOT_DELETE 0x0121
+#define NT_STATUS_INVALID_COMPUTER_NAME 0x0122
+#define NT_STATUS_FILE_DELETED 0x0123
+#define NT_STATUS_SPECIAL_ACCOUNT 0x0124
+#define NT_STATUS_SPECIAL_GROUP 0x0125
+#define NT_STATUS_SPECIAL_USER 0x0126
+#define NT_STATUS_MEMBERS_PRIMARY_GROUP 0x0127
+#define NT_STATUS_FILE_CLOSED 0x0128
+#define NT_STATUS_TOO_MANY_THREADS 0x0129
+#define NT_STATUS_THREAD_NOT_IN_PROCESS 0x012a
+#define NT_STATUS_TOKEN_ALREADY_IN_USE 0x012b
+#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED 0x012c
+#define NT_STATUS_COMMITMENT_LIMIT 0x012d
+#define NT_STATUS_INVALID_IMAGE_LE_FORMAT 0x012e
+#define NT_STATUS_INVALID_IMAGE_NOT_MZ 0x012f
+#define NT_STATUS_INVALID_IMAGE_PROTECT 0x0130
+#define NT_STATUS_INVALID_IMAGE_WIN_16 0x0131
+#define NT_STATUS_LOGON_SERVER_CONFLICT 0x0132
+#define NT_STATUS_TIME_DIFFERENCE_AT_DC 0x0133
+#define NT_STATUS_SYNCHRONIZATION_REQUIRED 0x0134
+#define NT_STATUS_DLL_NOT_FOUND 0x0135
+#define NT_STATUS_OPEN_FAILED 0x0136
+#define NT_STATUS_IO_PRIVILEGE_FAILED 0x0137
+#define NT_STATUS_ORDINAL_NOT_FOUND 0x0138
+#define NT_STATUS_ENTRYPOINT_NOT_FOUND 0x0139
+#define NT_STATUS_CONTROL_C_EXIT 0x013a
+#define NT_STATUS_LOCAL_DISCONNECT 0x013b
+#define NT_STATUS_REMOTE_DISCONNECT 0x013c
+#define NT_STATUS_REMOTE_RESOURCES 0x013d
+#define NT_STATUS_LINK_FAILED 0x013e
+#define NT_STATUS_LINK_TIMEOUT 0x013f
+#define NT_STATUS_INVALID_CONNECTION 0x0140
+#define NT_STATUS_INVALID_ADDRESS 0x0141
+#define NT_STATUS_DLL_INIT_FAILED 0x0142
+#define NT_STATUS_MISSING_SYSTEMFILE 0x0143
+#define NT_STATUS_UNHANDLED_EXCEPTION 0x0144
+#define NT_STATUS_APP_INIT_FAILURE 0x0145
+#define NT_STATUS_PAGEFILE_CREATE_FAILED 0x0146
+#define NT_STATUS_NO_PAGEFILE 0x0147
+#define NT_STATUS_INVALID_LEVEL 0x0148
+#define NT_STATUS_WRONG_PASSWORD_CORE 0x0149
+#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT 0x014a
+#define NT_STATUS_PIPE_BROKEN 0x014b
+#define NT_STATUS_REGISTRY_CORRUPT 0x014c
+#define NT_STATUS_REGISTRY_IO_FAILED 0x014d
+#define NT_STATUS_NO_EVENT_PAIR 0x014e
+#define NT_STATUS_UNRECOGNIZED_VOLUME 0x014f
+#define NT_STATUS_SERIAL_NO_DEVICE_INITED 0x0150
+#define NT_STATUS_NO_SUCH_ALIAS 0x0151
+#define NT_STATUS_MEMBER_NOT_IN_ALIAS 0x0152
+#define NT_STATUS_MEMBER_IN_ALIAS 0x0153
+#define NT_STATUS_ALIAS_EXISTS 0x0154
+#define NT_STATUS_LOGON_NOT_GRANTED 0x0155
+#define NT_STATUS_TOO_MANY_SECRETS 0x0156
+#define NT_STATUS_SECRET_TOO_LONG 0x0157
+#define NT_STATUS_INTERNAL_DB_ERROR 0x0158
+#define NT_STATUS_FULLSCREEN_MODE 0x0159
+#define NT_STATUS_TOO_MANY_CONTEXT_IDS 0x015a
+#define NT_STATUS_LOGON_TYPE_NOT_GRANTED 0x015b
+#define NT_STATUS_NOT_REGISTRY_FILE 0x015c
+#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED 0x015d
+#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR 0x015e
+#define NT_STATUS_FT_MISSING_MEMBER 0x015f
+#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY 0x0160
+#define NT_STATUS_ILLEGAL_CHARACTER 0x0161
+#define NT_STATUS_UNMAPPABLE_CHARACTER 0x0162
+#define NT_STATUS_UNDEFINED_CHARACTER 0x0163
+#define NT_STATUS_FLOPPY_VOLUME 0x0164
+#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND 0x0165
+#define NT_STATUS_FLOPPY_WRONG_CYLINDER 0x0166
+#define NT_STATUS_FLOPPY_UNKNOWN_ERROR 0x0167
+#define NT_STATUS_FLOPPY_BAD_REGISTERS 0x0168
+#define NT_STATUS_DISK_RECALIBRATE_FAILED 0x0169
+#define NT_STATUS_DISK_OPERATION_FAILED 0x016a
+#define NT_STATUS_DISK_RESET_FAILED 0x016b
+#define NT_STATUS_SHARED_IRQ_BUSY 0x016c
+#define NT_STATUS_FT_ORPHANING 0x016d
+#define NT_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT 0x016e
+#define NT_STATUS_16F 0x016f
+#define NT_STATUS_170 0x0170
+#define NT_STATUS_171 0x0171
+#define NT_STATUS_PARTITION_FAILURE 0x0172
+#define NT_STATUS_INVALID_BLOCK_LENGTH 0x0173
+#define NT_STATUS_DEVICE_NOT_PARTITIONED 0x0174
+#define NT_STATUS_UNABLE_TO_LOCK_MEDIA 0x0175
+#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA 0x0176
+#define NT_STATUS_EOM_OVERFLOW 0x0177
+#define NT_STATUS_NO_MEDIA 0x0178
+#define NT_STATUS_179 0x0179
+#define NT_STATUS_NO_SUCH_MEMBER 0x017a
+#define NT_STATUS_INVALID_MEMBER 0x017b
+#define NT_STATUS_KEY_DELETED 0x017c
+#define NT_STATUS_NO_LOG_SPACE 0x017d
+#define NT_STATUS_TOO_MANY_SIDS 0x017e
+#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED 0x017f
+#define NT_STATUS_KEY_HAS_CHILDREN 0x0180
+#define NT_STATUS_CHILD_MUST_BE_VOLATILE 0x0181
+#define NT_STATUS_DEVICE_CONFIGURATION_ERROR 0x0182
+#define NT_STATUS_DRIVER_INTERNAL_ERROR 0x0183
+#define NT_STATUS_INVALID_DEVICE_STATE 0x0184
+#define NT_STATUS_IO_DEVICE_ERROR 0x0185
+#define NT_STATUS_DEVICE_PROTOCOL_ERROR 0x0186
+#define NT_STATUS_BACKUP_CONTROLLER 0x0187
+#define NT_STATUS_LOG_FILE_FULL 0x0188
+#define NT_STATUS_TOO_LATE 0x0189
+#define NT_STATUS_NO_TRUST_LSA_SECRET 0x018a
+#define NT_STATUS_NO_TRUST_SAM_ACCOUNT 0x018b
+#define NT_STATUS_TRUSTED_DOMAIN_FAILURE 0x018c
+#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE 0x018d
+#define NT_STATUS_EVENTLOG_FILE_CORRUPT 0x018e
+#define NT_STATUS_EVENTLOG_CANT_START 0x018f
+#define NT_STATUS_TRUST_FAILURE 0x0190
+#define NT_STATUS_MUTANT_LIMIT_EXCEEDED 0x0191
+#define NT_STATUS_NETLOGON_NOT_STARTED 0x0192
+#define NT_STATUS_ACCOUNT_EXPIRED 0x0193
+#define NT_STATUS_POSSIBLE_DEADLOCK 0x0194
+#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT 0x0195
+#define NT_STATUS_REMOTE_SESSION_LIMIT 0x0196
+#define NT_STATUS_EVENTLOG_FILE_CHANGED 0x0197
+#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT 0x0198
+#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT 0x0199
+#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT 0x019a
+#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT 0x019b
+#define NT_STATUS_FS_DRIVER_REQUIRED 0x019c
+#define NT_STATUS_NO_USER_SESSION_KEY 0x0202
+#define NT_STATUS_USER_SESSION_DELETED 0x0203
+#define NT_STATUS_RESOURCE_LANG_NOT_FOUND 0x0204
+#define NT_STATUS_INSUFF_SERVER_RESOURCES 0x0205
+#define NT_STATUS_INVALID_BUFFER_SIZE 0x0206
+#define NT_STATUS_INVALID_ADDRESS_COMPONENT 0x0207
+#define NT_STATUS_INVALID_ADDRESS_WILDCARD 0x0208
+#define NT_STATUS_TOO_MANY_ADDRESSES 0x0209
+#define NT_STATUS_ADDRESS_ALREADY_EXISTS 0x020a
+#define NT_STATUS_ADDRESS_CLOSED 0x020b
+#define NT_STATUS_CONNECTION_DISCONNECTED 0x020c
+#define NT_STATUS_CONNECTION_RESET 0x020d
+#define NT_STATUS_TOO_MANY_NODES 0x020e
+#define NT_STATUS_TRANSACTION_ABORTED 0x020f
+#define NT_STATUS_TRANSACTION_TIMED_OUT 0x0210
+#define NT_STATUS_TRANSACTION_NO_RELEASE 0x0211
+#define NT_STATUS_TRANSACTION_NO_MATCH 0x0212
+#define NT_STATUS_TRANSACTION_RESPONDED 0x0213
+#define NT_STATUS_TRANSACTION_INVALID_ID 0x0214
+#define NT_STATUS_TRANSACTION_INVALID_TYPE 0x0215
+#define NT_STATUS_NOT_SERVER_SESSION 0x0216
+#define NT_STATUS_NOT_CLIENT_SESSION 0x0217
+#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE 0x0218
+#define NT_STATUS_DEBUG_ATTACH_FAILED 0x0219
+#define NT_STATUS_SYSTEM_PROCESS_TERMINATED 0x021a
+#define NT_STATUS_DATA_NOT_ACCEPTED 0x021b
+#define NT_STATUS_NO_BROWSER_SERVERS_FOUND 0x021c
+#define NT_STATUS_VDM_HARD_ERROR 0x021d
+#define NT_STATUS_DRIVER_CANCEL_TIMEOUT 0x021e
+#define NT_STATUS_REPLY_MESSAGE_MISMATCH 0x021f
+#define NT_STATUS_MAPPED_ALIGNMENT 0x0220
+#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH 0x0221
+#define NT_STATUS_LOST_WRITEBEHIND_DATA 0x0222
+#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID 0x0223
+#define NT_STATUS_PASSWORD_MUST_CHANGE 0x0224
+#define NT_STATUS_NOT_FOUND 0x0225
+#define NT_STATUS_NOT_TINY_STREAM 0x0226
+#define NT_STATUS_RECOVERY_FAILURE 0x0227
+#define NT_STATUS_STACK_OVERFLOW_READ 0x0228
+#define NT_STATUS_FAIL_CHECK 0x0229
+#define NT_STATUS_DUPLICATE_OBJECTID 0x022a
+#define NT_STATUS_OBJECTID_EXISTS 0x022b
+#define NT_STATUS_CONVERT_TO_LARGE 0x022c
+#define NT_STATUS_RETRY 0x022d
+#define NT_STATUS_FOUND_OUT_OF_SCOPE 0x022e
+#define NT_STATUS_ALLOCATE_BUCKET 0x022f
+#define NT_STATUS_PROPSET_NOT_FOUND 0x0230
+#define NT_STATUS_MARSHALL_OVERFLOW 0x0231
+#define NT_STATUS_INVALID_VARIANT 0x0232
+#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND 0x0233
+#define NT_STATUS_ACCOUNT_LOCKED_OUT 0x0234
+#define NT_STATUS_HANDLE_NOT_CLOSABLE 0x0235
+#define NT_STATUS_CONNECTION_REFUSED 0x0236
+#define NT_STATUS_GRACEFUL_DISCONNECT 0x0237
+#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED 0x0238
+#define NT_STATUS_ADDRESS_NOT_ASSOCIATED 0x0239
+#define NT_STATUS_CONNECTION_INVALID 0x023a
+#define NT_STATUS_CONNECTION_ACTIVE 0x023b
+#define NT_STATUS_NETWORK_UNREACHABLE 0x023c
+#define NT_STATUS_HOST_UNREACHABLE 0x023d
+#define NT_STATUS_PROTOCOL_UNREACHABLE 0x023e
+#define NT_STATUS_PORT_UNREACHABLE 0x023f
+#define NT_STATUS_REQUEST_ABORTED 0x0240
+#define NT_STATUS_CONNECTION_ABORTED 0x0241
+#define NT_STATUS_BAD_COMPRESSION_BUFFER 0x0242
+#define NT_STATUS_USER_MAPPED_FILE 0x0243
+#define NT_STATUS_AUDIT_FAILED 0x0244
+#define NT_STATUS_TIMER_RESOLUTION_NOT_SET 0x0245
+#define NT_STATUS_CONNECTION_COUNT_LIMIT 0x0246
+#define NT_STATUS_LOGIN_TIME_RESTRICTION 0x0247
+#define NT_STATUS_LOGIN_WKSTA_RESTRICTION 0x0248
+#define NT_STATUS_IMAGE_MP_UP_MISMATCH 0x0249
+#define NT_STATUS_INSUFFICIENT_LOGON_INFO 0x0250
+#define NT_STATUS_BAD_DLL_ENTRYPOINT 0x0251
+#define NT_STATUS_BAD_SERVICE_ENTRYPOINT 0x0252
+#define NT_STATUS_LPC_REPLY_LOST 0x0253
+#define NT_STATUS_IP_ADDRESS_CONFLICT1 0x0254
+#define NT_STATUS_IP_ADDRESS_CONFLICT2 0x0255
+#define NT_STATUS_REGISTRY_QUOTA_LIMIT 0x0256
+#define NT_STATUS_PATH_NOT_COVERED 0x0257
+#define NT_STATUS_NO_CALLBACK_ACTIVE 0x0258
+#define NT_STATUS_LICENSE_QUOTA_EXCEEDED 0x0259
+#define NT_STATUS_PWD_TOO_SHORT 0x025a
+#define NT_STATUS_PWD_TOO_RECENT 0x025b
+#define NT_STATUS_PWD_HISTORY_CONFLICT 0x025c
+#define NT_STATUS_PLUGPLAY_NO_DEVICE 0x025e
+#define NT_STATUS_UNSUPPORTED_COMPRESSION 0x025f
+#define NT_STATUS_INVALID_HW_PROFILE 0x0260
+#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH 0x0261
+#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND 0x0262
+#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND 0x0263
+#define NT_STATUS_RESOURCE_NOT_OWNED 0x0264
+#define NT_STATUS_TOO_MANY_LINKS 0x0265
+#define NT_STATUS_QUOTA_LIST_INCONSISTENT 0x0266
+#define NT_STATUS_FILE_IS_OFFLINE 0x0267
+
+#define NT_STATUS_LICENSE_VIOLATION 0x026a
+
+#define NT_STATUS_DFS_UNAVAILABLE 0x026d
+#define NT_STATUS_VOLUME_DISMOUNTED 0x026e
+
+#define NT_STATUS_NOT_A_REPARSE_POINT 0x0275
+
+#define NT_STATUS_REPARSE_POINT_NOT_RESOLVED 0x0280
+#define NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT 0x0281
+
+#define NT_STATUS_ENCRYPTION_FAILED 0x028a
+#define NT_STATUS_DECRYPTION_FAILED 0x028b
+#define NT_STATUS_RANGE_NOT_FOUND 0x028c
+#define NT_STATUS_NO_RECOVERY_POLICY 0x028d
+#define NT_STATUS_NO_EFS 0x028e
+#define NT_STATUS_WRONG_EFS 0x028f
+#define NT_STATUS_NO_USER_KEYS 0x0290
+#define NT_STATUS_FILE_NOT_ENCRYPTED 0x0291
+
+#define NT_STATUS_FILE_ENCRYPTED 0x0293
+
+#define NT_STATUS_VOLUME_NOT_UPGRADED 0x029c
+
+#define NT_STATUS_KDC_CERT_EXPIRED 0x040e
+/*
+ * 0x00010000-0x0001ffff are "DBG" errors
+ * 0x00020000-0x0003ffff are "RPC" errors
+ * 0x00040000-0x0004ffff are "PNP" errors
+ * 0x000A0000-0x000Affff are "CTX" errors
+ * 0x00130000-0x0013ffff are "CLUSTER" errors
+ * 0x00140000-0x0014ffff are "ACPI" errors
+ * 0x00150000-0x0015ffff are "SXS" errors
+ */
+
+/*
+ * size of the GUID returned in an extended security negotiate response
+ */
+#define SMB_GUIDLEN 16
+
+typedef uint16_t smbfh;
+
+/*
+ * NTLMv2 blob header structure.
+ */
+struct ntlmv2_blobhdr {
+ uint32_t header;
+ uint32_t reserved;
+ uint64_t timestamp;
+ uint64_t client_nonce;
+ uint32_t unknown1;
+};
+typedef struct ntlmv2_blobhdr ntlmv2_blobhdr_t;
+
+/*
+ * NTLMv2 name header structure, for names in a blob.
+ */
+struct ntlmv2_namehdr {
+ uint16_t type;
+ uint16_t len;
+};
+typedef struct ntlmv2_namehdr ntlmv2_namehdr_t;
+
+#define NAMETYPE_EOL 0x0000 /* end of list of names */
+#define NAMETYPE_MACHINE_NB 0x0001 /* NetBIOS machine name */
+#define NAMETYPE_DOMAIN_NB 0x0002 /* NetBIOS domain name */
+#define NAMETYPE_MACHINE_DNS 0x0003 /* DNS machine name */
+#define NAMETYPE_DOMAIN_DNS 0x0004 /* DNS Active Directory domain name */
+
+/*
+ * Named pipe commands.
+ */
+#define TRANS_CALL_NAMED_PIPE 0x54 /* open/write/read/close pipe */
+#define TRANS_WAIT_NAMED_PIPE 0x53 /* wait for pipe to be !busy */
+#define TRANS_PEEK_NAMED_PIPE 0x23 /* read but don't remove data */
+#define TRANS_Q_NAMED_PIPE_HAND_STATE 0x21 /* query pipe handle modes */
+#define TRANS_SET_NAMED_PIPE_HAND_STATE 0x01 /* set pipe handle modes */
+#define TRANS_Q_NAMED_PIPE_INFO 0x22 /* query pipe attributes */
+#define TRANS_TRANSACT_NAMED_PIPE 0x26 /* r/w operation on pipe */
+#define TRANS_READ_NAMED_PIPE 0x11 /* read pipe in "raw" mode */
+ /* (non message mode) */
+#define TRANS_WRITE_NAMED_PIPE 0x31 /* write pipe "raw" mode */
+ /* (non message mode) */
+
+/*
+ * Share types, visible via NetShareEnum
+ */
+#define STYPE_DISKTREE 0x00000000
+#define STYPE_PRINTQ 0x00000001
+#define STYPE_DEVICE 0x00000002
+#define STYPE_IPC 0x00000003
+#define STYPE_UNKNOWN 0x00000004
+#define STYPE_MASK 0x0000000F
+#define STYPE_TEMPORARY 0x40000000
+#define STYPE_HIDDEN 0x80000000
+
+#endif /* _NETSMB_SMB_H_ */
diff --git a/usr/src/uts/common/netsmb/smb_dev.h b/usr/src/uts/common/netsmb/smb_dev.h
new file mode 100644
index 0000000000..b414bcf9b8
--- /dev/null
+++ b/usr/src/uts/common/netsmb/smb_dev.h
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_dev.h,v 1.10.178.1 2005/05/27 02:35:29 lindak Exp $
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NETSMB_DEV_H_
+#define _NETSMB_DEV_H_
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file defines an internal ABI for the "nsmb" driver,
+ * particularly the various data structures passed to ioctl.
+ * In order to avoid some messy 32-bit to 64-bit conversions
+ * in the driver, we take pains to define all data structures
+ * that pass across the user/kernel boundary in a way that
+ * makes them invariant across 32-bit and 64-bit ABIs.
+ * This invariance is checked during the driver build
+ * using a mechanism similar to genassym.h builds.
+ *
+ * If you change any of the ioctl data structures in
+ * this file, YOU MUST ALSO edit this file:
+ * uts/common/fs/smbclnt/netsmb/offsets.in
+ * and then verify the invariance describe above.
+ *
+ * Also, remember to "bump" NSMB_VER below when
+ * any part of this user/kernel I/F changes.
+ */
+
+#ifndef _KERNEL
+#include <sys/types.h>
+#endif
+
+#include <sys/socket_impl.h>
+#include <netsmb/smb.h>
+#include <netsmb/netbios.h>
+
+#define NSMB_NAME "nsmb"
+
+/*
+ * Update NSMB_VER* if any of the ioctl codes and/or
+ * associated structures change in ways that would
+ * make them incompatible with an old driver.
+ */
+#define NSMB_VERMAJ 1
+#define NSMB_VERMIN 3500
+#define NSMB_VERSION (NSMB_VERMAJ * 100000 + NSMB_VERMIN)
+#define NSMB_VER_STR "1.35"
+
+#define NSMBFL_OPEN 0x0001
+#define NSMBFL_NEWVC 0x0002
+
+/*
+ * Hack-ish errno values we need to expose to the library.
+ * EBADRPC is used for message decoding errors.
+ * EAUTH is used for CIFS authentication errors.
+ */
+#ifndef EBADRPC
+#define EBADRPC 113 /* XXX */
+#endif
+#ifndef EAUTH
+#define EAUTH 114 /* XXX */
+#endif
+
+/*
+ * "Level" in the connection object hierarchy
+ */
+#define SMBL_SM 0
+#define SMBL_VC 1
+#define SMBL_SHARE 2
+#define SMBL_NUM 3
+#define SMBL_NONE (-1)
+
+/*
+ * Upper/lower case options
+ */
+#define SMB_CS_NONE 0x0000
+#define SMB_CS_UPPER 0x0001 /* convert passed string to upper case */
+#define SMB_CS_LOWER 0x0002 /* convert passed string to lower case */
+
+/*
+ * access mode stuff (see also smb_lib.h)
+ */
+#define SMBM_ANY_OWNER ((uid_t)-1)
+#define SMBM_ANY_GROUP ((gid_t)-1)
+#define SMBM_MASK 0777
+#define SMBM_EXACT 010000 /* check for specified mode exactly */
+#ifdef _KERNEL
+/* In-kernel, we prefer the vnode.h names. */
+#define SMBM_READ VREAD /* (S_IRUSR) read conn attrs. */
+#define SMBM_WRITE VWRITE /* (S_IWUSR) modify conn attrs */
+#define SMBM_EXEC VEXEC /* (S_IXUSR) can send SMB requests */
+#endif
+
+/*
+ * Option flags in smbioc_ossn.ioc_opt
+ * and vcspec.optflags
+ */
+#define SMBVOPT_CREATE 0x0001 /* create object if necessary */
+#define SMBVOPT_PRIVATE 0x0002 /* connection should be private */
+#define SMBVOPT_SINGLESHARE 0x0004 /* keep only one share at this VC */
+#define SMBVOPT_PERMANENT 0x0010 /* object will keep last reference */
+#define SMBVOPT_EXT_SEC 0x0020 /* extended security negotiation */
+#define SMBVOPT_USE_KEYCHAIN 0x0040 /* get p/w from keychain */
+#define SMBVOPT_KC_DOMAIN 0x0080 /* keychain lookup uses domain */
+/* XXX: How about a separate field for these? */
+#define SMBVOPT_MINAUTH 0x7000 /* min. auth. level (mask) */
+#define SMBVOPT_MINAUTH_NONE 0x0000 /* any authentication OK */
+#define SMBVOPT_MINAUTH_LM 0x1000 /* no plaintext passwords */
+#define SMBVOPT_MINAUTH_NTLM 0x2000 /* don't send LM reply */
+#define SMBVOPT_MINAUTH_NTLMV2 0x3000 /* don't fall back to NTLMv1 */
+#define SMBVOPT_MINAUTH_KERBEROS 0x4000 /* don't do NTLMv1 or v2 */
+
+/*
+ * Option flags in smbioc_oshare.ioc_opt
+ * and sharespec.optflags
+ */
+#define SMBSOPT_CREATE SMBVOPT_CREATE
+#define SMBSOPT_PERMANENT SMBVOPT_PERMANENT
+
+#define MAX_STR_LEN 8 /* Maxilum length of the minor device name */
+
+/*
+ * We're now using structures that are invariant
+ * across 32-bit vs 64-bit compilers for all
+ * member sizes and offsets. Scalar members
+ * simply have to use fixed-size types.
+ * Pointers are a little harder...
+ * We use this union for all pointers that
+ * must pass between user and kernel.
+ */
+typedef union lptr {
+ uint64_t lp_ll;
+#ifdef _LP64
+ void *lp_ptr;
+#endif
+#ifdef _ILP32
+ void *_lp_p2[2];
+#ifdef _LITTLE_ENDIAN
+#define lp_ptr _lp_p2[0]
+#define lp_pad _lp_p2[1]
+#else /* _ENDIAN */
+#define lp_pad _lp_p2[0]
+#define lp_ptr _lp_p2[1]
+#endif /* _ENDIAN */
+#endif /* _ILP32 */
+} lptr_t;
+
+/*
+ * Handy union of sockaddr types we use.
+ * Type discriminator is sa_family
+ */
+union sockaddr_any {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_nb nb;
+};
+
+
+/*
+ * SMBIOC_LOOKUP flags
+ */
+#define SMBLK_CREATE SMBVOPT_CREATE
+
+#define DEF_SEC_TOKEN_LEN 2048
+
+struct smbioc_ossn {
+ union sockaddr_any ioc_server;
+ union sockaddr_any ioc_local;
+ char ioc_localcs[16]; /* local charset */
+ char ioc_servercs[16]; /* server charset */
+ char ioc_srvname[SMB_MAXSRVNAMELEN + 1];
+ char ioc_user[SMB_MAXUSERNAMELEN + 1];
+ char ioc_workgroup[SMB_MAXUSERNAMELEN + 1];
+ char ioc_password[SMB_MAXPASSWORDLEN + 1];
+ int32_t ioc_opt;
+ int32_t ioc_timeout; /* ignored?! XXX */
+ int32_t ioc_retrycount; /* number of retries before giveup */
+ uid_t ioc_owner; /* proposed owner */
+ gid_t ioc_group; /* proposed group */
+ mode_t ioc_mode; /* desired access mode */
+ mode_t ioc_rights; /* SMBM_* */
+ int32_t ioc_intoklen;
+ int32_t ioc_outtoklen;
+ /* copyout ends at this offset */
+ lptr_t _ioc_intok;
+ lptr_t _ioc_outtok;
+};
+typedef struct smbioc_ossn smbioc_ossn_t;
+#define ioc_intok _ioc_intok.lp_ptr
+#define ioc_outtok _ioc_outtok.lp_ptr
+
+
+struct smbioc_oshare {
+ char ioc_share[SMB_MAXSHARENAMELEN + 1];
+ char ioc_password[SMB_MAXPASSWORDLEN + 1];
+ int32_t ioc_opt;
+ int32_t ioc_stype; /* share type */
+ uid_t ioc_owner; /* proposed owner of share */
+ gid_t ioc_group; /* proposed group of share */
+ mode_t ioc_mode; /* desired access mode to share */
+ mode_t ioc_rights; /* SMBM_* */
+ /*
+ * Hack: need the size of this to be 8-byte aligned
+ * so that the ioc_ossn following it in smbioc_lookup
+ * is correctly aligned...
+ */
+ int32_t ioc__pad;
+};
+typedef struct smbioc_oshare smbioc_oshare_t;
+
+typedef struct smbioc_rq {
+ uchar_t ioc_cmd;
+ uchar_t ioc_twc; /* _twords */
+ ushort_t ioc_tbc; /* _tbytes */
+ int32_t ioc_rpbufsz; /* _rpbuf */
+ uchar_t ioc__pad1;
+ uchar_t ioc_rwc;
+ ushort_t ioc_rbc;
+ uchar_t ioc__pad2;
+ uint8_t ioc_errclass;
+ uint16_t ioc_serror;
+ uint32_t ioc_error;
+ uint32_t ioc__pad3;
+ /*
+ * Copyout all but the pointers, which
+ * we may have set to kernel memory.
+ * See ..._COPYOUT_SIZE
+ */
+ lptr_t _ioc_twords;
+ lptr_t _ioc_tbytes;
+ lptr_t _ioc_rpbuf;
+} smbioc_rq_t;
+#define ioc_twords _ioc_twords.lp_ptr
+#define ioc_tbytes _ioc_tbytes.lp_ptr
+#define ioc_rpbuf _ioc_rpbuf.lp_ptr
+#define SMBIOC_RQ_COPYOUT_SIZE \
+ (offsetof(smbioc_rq_t, _ioc_twords))
+
+
+typedef struct smbioc_t2rq {
+ uint16_t ioc_setup[SMB_MAXSETUPWORDS];
+ int32_t ioc_setupcnt;
+ char ioc_name[128];
+ ushort_t ioc_tparamcnt;
+ ushort_t ioc_tdatacnt;
+ ushort_t ioc_rparamcnt;
+ ushort_t ioc_rdatacnt;
+ uint8_t ioc__pad1;
+ uint8_t ioc_errclass;
+ uint16_t ioc_serror;
+ uint32_t ioc_error;
+ uint16_t ioc_rpflags2;
+ uint16_t ioc__pad2;
+ /*
+ * Copyout all but the pointers, which
+ * we may have set to kernel memory.
+ * See ..._COPYOUT_SIZE
+ */
+ lptr_t _ioc_tparam;
+ lptr_t _ioc_tdata;
+ lptr_t _ioc_rparam;
+ lptr_t _ioc_rdata;
+} smbioc_t2rq_t;
+#define ioc_tparam _ioc_tparam.lp_ptr
+#define ioc_tdata _ioc_tdata.lp_ptr
+#define ioc_rparam _ioc_rparam.lp_ptr
+#define ioc_rdata _ioc_rdata.lp_ptr
+#define SMBIOC_T2RQ_COPYOUT_SIZE \
+ (offsetof(smbioc_t2rq_t, _ioc_tparam))
+
+
+typedef struct smbioc_flags {
+ int32_t ioc_level; /* 0 - session, 1 - share */
+ int32_t ioc_mask;
+ int32_t ioc_flags;
+} smbioc_flags_t;
+
+typedef struct smbioc_lookup {
+ int32_t ioc_level;
+ int32_t ioc_flags;
+ struct smbioc_oshare ioc_sh;
+ struct smbioc_ossn ioc_ssn;
+} smbioc_lookup_t;
+#define SMBIOC_LOOK_COPYOUT_SIZE \
+ (offsetof(smbioc_lookup_t, ioc_ssn._ioc_intok))
+
+typedef struct smbioc_rw {
+ uint16_t ioc_fh;
+ uint32_t ioc_cnt;
+ lloff_t _ioc_offset;
+ lptr_t _ioc_base;
+} smbioc_rw_t;
+#define ioc_offset _ioc_offset._f
+#define ioc_base _ioc_base.lp_ptr
+#define SMBIOC_RW_COPYOUT_SIZE \
+ (offsetof(smbioc_rw_t, _ioc_base))
+
+/* Password Keychain (PK) support. */
+#define SMBIOC_PK_MAXLEN 255
+typedef struct smbioc_pk {
+ uid_t pk_uid; /* UID for PAM use */
+ char pk_dom[SMBIOC_PK_MAXLEN+1]; /* CIFS domain name */
+ char pk_usr[SMBIOC_PK_MAXLEN+1]; /* CIFS user name */
+ char pk_pass[SMBIOC_PK_MAXLEN+1]; /* CIFS password */
+} smbioc_pk_t;
+
+
+/*
+ * Device IOCTLs
+ *
+ * Define ioctl codes the way ZFS does.
+ * The "base" value is arbitrary, and can
+ * occupy the high word if we like, because
+ * our driver does its own copyin/copyout.
+ * Keep GETVERS first and use it to verify
+ * driver compatibility with the library.
+ */
+#define SMBIOC_BASE ((('n' << 8) | 's') << 8)
+typedef enum nsmb_ioc {
+ SMBIOC_GETVERS = SMBIOC_BASE,
+ SMBIOC_REQUEST,
+ SMBIOC_T2RQ,
+ SMBIOC_LOOKUP,
+ SMBIOC_READ,
+ SMBIOC_WRITE,
+ SMBIOC_NEGOTIATE,
+ SMBIOC_SSNSETUP,
+ SMBIOC_TCON,
+ SMBIOC_TDIS,
+ SMBIOC_FLAGS2,
+ /* Password Keychain (PK) support. */
+ SMBIOC_PK_ADD, /* Add/Modify a password entry */
+ SMBIOC_PK_CHK, /* Check for a password entry */
+ SMBIOC_PK_DEL, /* Delete specified password entry */
+ SMBIOC_PK_DEL_OWNER, /* all owned by the caller */
+ SMBIOC_PK_DEL_EVERYONE /* all owned by everyone */
+} nsmb_ioc_t;
+
+#ifdef _KERNEL
+#include <sys/dditypes.h> /* for dev_info_t */
+
+#define SMBST_CONNECTED 1
+
+/* Size of storage for p/w hashes. */
+#define SMB_PWH_MAX 24
+
+extern const uint32_t nsmb_version;
+
+struct smb_cred;
+struct smb_share;
+struct smb_vc;
+
+typedef struct smb_dev {
+ int sd_opened; /* Opened or not */
+ int sd_level; /* Future use */
+ struct smb_vc *sd_vc; /* Reference to VC */
+ struct smb_share *sd_share; /* Reference to share if any */
+ int sd_poll; /* Future use */
+ int sd_seq; /* Kind of minor number/instance no */
+ int sd_flags; /* State of connection */
+ zoneid_t zoneid; /* Zone id */
+ dev_info_t *smb_dip; /* ptr to dev_info node */
+ void *sd_devfs; /* Dont know how to use this. but */
+ struct cred *smb_cred; /* per dev credentails. Future use */
+} smb_dev_t;
+
+/*
+ * Compound user interface
+ */
+int smb_usr_negotiate(struct smbioc_lookup *dp, struct smb_cred *scred,
+ struct smb_vc **vcpp);
+int smb_usr_ssnsetup(struct smbioc_lookup *dp, struct smb_cred *scred,
+ struct smb_vc *vcp);
+int smb_usr_tcon(struct smbioc_lookup *dp, struct smb_cred *scred,
+ struct smb_vc *vcp, struct smb_share **sspp);
+int smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *data,
+ struct smb_cred *scred);
+int smb_usr_t2request(struct smb_share *ssp, struct smbioc_t2rq *data,
+ struct smb_cred *scred);
+int smb_usr_rw(struct smb_share *ssp, smbioc_rw_t *dp,
+ int cmd, struct smb_cred *scred);
+int smb_dev2share(int fd, struct smb_share **sspp);
+
+#endif /* _KERNEL */
+#endif /* _NETSMB_DEV_H_ */
diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c
index d5e83e8e6a..b79374602a 100644
--- a/usr/src/uts/common/os/policy.c
+++ b/usr/src/uts/common/os/policy.c
@@ -2096,3 +2096,24 @@ secpolicy_vscan(const cred_t *cr)
return (0);
}
+
+/*
+ * secpolicy_smbfs_login
+ *
+ * Determines if the caller can add and delete the smbfs login
+ * password in the the nsmb kernel module for the CIFS client.
+ *
+ * Returns:
+ * 0 access is allowed.
+ * EPERM access is NOT allowed.
+ */
+int
+secpolicy_smbfs_login(const cred_t *cr, uid_t uid)
+{
+ uid_t cruid = crgetruid(cr);
+
+ if (cruid == uid)
+ return (0);
+ return (PRIV_POLICY(cr, PRIV_PROC_OWNER, B_FALSE,
+ EPERM, NULL));
+}
diff --git a/usr/src/uts/common/os/vfs_conf.c b/usr/src/uts/common/os/vfs_conf.c
index b15c834fbe..6e433284b4 100644
--- a/usr/src/uts/common/os/vfs_conf.c
+++ b/usr/src/uts/common/os/vfs_conf.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -81,6 +81,7 @@ struct vfssw vfssw[] = {
{ "objfs" }, /* OBJFS */
{ "sharefs" }, /* SHAREFS */
{ "dcfs" }, /* DCFS */
+ { "smbfs" }, /* SMBFS */
{ "" }, /* reserved for loadable fs */
{ "" },
{ "" },
@@ -94,7 +95,6 @@ struct vfssw vfssw[] = {
{ "" },
{ "" },
{ "" },
- { "" },
};
const int nfstype = (sizeof (vfssw) / sizeof (vfssw[0]));
diff --git a/usr/src/uts/common/sys/fs/smbfs_mount.h b/usr/src/uts/common/sys/fs/smbfs_mount.h
new file mode 100644
index 0000000000..6eeb8f909f
--- /dev/null
+++ b/usr/src/uts/common/sys/fs/smbfs_mount.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smbfs.h,v 1.30.100.1 2005/05/27 02:35:28 lindak Exp $
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SMBFS_MOUNT_H
+#define _SMBFS_MOUNT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file defines the interface used by mount_smbfs.
+ * Some of this came from the Darwin file:
+ * smb-217.2/kernel/fs/smbfs/smbfs.h
+ */
+
+#define SMBFS_VERMAJ 1
+#define SMBFS_VERMIN 3200
+#define SMBFS_VERSION (SMBFS_VERMAJ*100000 + SMBFS_VERMIN)
+#define SMBFS_VER_STR "1.32"
+
+#define SMBFS_VFSNAME "smbfs"
+
+/* Values for flags */
+#define SMBFS_MOUNT_SOFT 0x0001
+#define SMBFS_MOUNT_INTR 0x0002
+#define SMBFS_MOUNT_STRONG 0x0004
+#define SMBFS_MOUNT_HAVE_NLS 0x0008
+#define SMBFS_MOUNT_NO_LONG 0x0010
+#define SMBFS_MOUNT_HOSTNAME 0x020
+#define SMBFS_MOUNT_SEMISOFT 0x200000 /* read soft, modify hard */
+#define SMBFS_MOUNT_NOPRINT 0x400000 /* don't print messages */
+
+#define MNT_RDONLY 0x0001
+#define MNT_NODEV 0x0002
+#define MNT_NOEXEC 0x0004
+#define MNT_NOSUID 0x0008
+#define MNT_UNION 0x0010
+#define MNT_DONTBROWSE 0x0020
+#define MNT_AUTOMOUNTED 0x0040
+
+/* Layout of the mount control block for an smb file system. */
+struct smbfs_args {
+ int version; /* smbfs mount version */
+ int devfd; /* file descriptor */
+ uint_t flags; /* mount options, eg: soft */
+ mode_t file_mode; /* octal srwx for files */
+ mode_t dir_mode; /* octal srwx for dirs */
+ int caseopt; /* convert upper|lower|none */
+ caddr_t addr; /* file server address */
+ caddr_t hostname; /* server's hostname */
+ caddr_t sharename; /* server's sharename */
+ uid_t uid; /* octal user id */
+ gid_t gid; /* octal group id */
+};
+
+#ifdef _SYSCALL32
+
+/* Layout of the mount control block for an smb file system. */
+struct smbfs_args32 {
+ int32_t version; /* smbfs mount version */
+ int32_t devfd; /* file descriptor */
+ uint_t flags; /* mount options, eg: soft */
+ mode_t file_mode; /* octal srwx for files */
+ mode_t dir_mode; /* octal srwx for dirs */
+ int32_t caseopt; /* convert upper|lower|none */
+ caddr32_t addr; /* file server address */
+ caddr32_t hostname; /* server's hostname */
+ caddr32_t sharename; /* server's sharename */
+ uid32_t uid; /* octal user id */
+ gid32_t gid; /* octal group id */
+};
+
+#endif /* _SYSCALL32 */
+#endif /* _SMBFS_MOUNT_H */
diff --git a/usr/src/uts/common/sys/mntent.h b/usr/src/uts/common/sys/mntent.h
index ee35c0561c..7340a0d4f2 100644
--- a/usr/src/uts/common/sys/mntent.h
+++ b/usr/src/uts/common/sys/mntent.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
@@ -41,6 +41,7 @@ extern "C" {
#define MNTTYPE_ZFS "zfs" /* ZFS file system */
#define MNTTYPE_UFS "ufs" /* Unix file system */
+#define MNTTYPE_SMBFS "smbfs" /* SMBFS file system */
#define MNTTYPE_NFS "nfs" /* NFS file system */
#define MNTTYPE_NFS3 "nfs3" /* NFS Version 3 file system */
#define MNTTYPE_NFS4 "nfs4" /* NFS Version 4 file system */
diff --git a/usr/src/uts/common/sys/policy.h b/usr/src/uts/common/sys/policy.h
index 6e7d1a169e..757ba3abc3 100644
--- a/usr/src/uts/common/sys/policy.h
+++ b/usr/src/uts/common/sys/policy.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -130,6 +130,7 @@ int secpolicy_rsm_access(const cred_t *, uid_t, mode_t);
int secpolicy_setpriority(const cred_t *);
int secpolicy_settime(const cred_t *);
int secpolicy_smb(const cred_t *);
+int secpolicy_smbfs_login(const cred_t *, uid_t);
int secpolicy_spec_open(const cred_t *, struct vnode *, int);
int secpolicy_sti(const cred_t *);
int secpolicy_swapctl(const cred_t *);
diff --git a/usr/src/uts/intel/Makefile b/usr/src/uts/intel/Makefile
index 231223883d..dbebc6fd66 100644
--- a/usr/src/uts/intel/Makefile
+++ b/usr/src/uts/intel/Makefile
@@ -20,7 +20,7 @@
#
# uts/intel/Makefile
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -33,7 +33,9 @@ UTSBASE = ..
include Makefile.intel
-LINT_KMODLIBS = $(LINT_KMODS:e1000g=)
+LINT_KMODS_X1 = $(LINT_KMODS:nsmb=)
+LINT_KMODS_X2 = $(LINT_KMODS_X1:smbfs=)
+LINT_KMODLIBS = $(LINT_KMODS_X2:e1000g=)
LINT_LIBS = $(LINT_LIB) $(GEN_LINT_LIB) \
$(LINT_KMODLIBS:%=$(LINT_LIB_DIR)/llib-l%.ln) \
$(CLOSED_LINT_KMODS:%=$(LINT_LIB_DIR)/llib-l%.ln)
diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared
index 6a3ab2e8b9..151efe1d3a 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -323,6 +323,7 @@ DRV_KMODS += wpi
DRV_KMODS += xge
DRV_KMODS += zcons
DRV_KMODS += chxge
+DRV_KMODS += nsmb
#
# Don't build some of these for OpenSolaris, since they will be
@@ -467,6 +468,7 @@ SCHED_KMODS += IA RT TS RT_DPTBL TS_DPTBL FSS FX FX_DPTBL
FS_KMODS += autofs cachefs ctfs dev devfs fdfs fifofs hsfs lofs
FS_KMODS += lx_afs lx_proc mntfs namefs nfs objfs zfs
FS_KMODS += pcfs procfs sockfs specfs tmpfs udfs ufs sharefs
+FS_KMODS += smbfs
#
# Streams Modules (/kernel/strmod):
diff --git a/usr/src/uts/intel/nsmb/Makefile b/usr/src/uts/intel/nsmb/Makefile
new file mode 100644
index 0000000000..3b8d751ac2
--- /dev/null
+++ b/usr/src/uts/intel/nsmb/Makefile
@@ -0,0 +1,108 @@
+#
+# 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
+#
+#
+# uts/intel/nsmb/Makefile
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# intel architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = nsmb
+OBJECTS = $(NSMB_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(NSMB_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/fs/smbclnt/netsmb
+OFFSETS_SRC = $(CONF_SRCDIR)/offsets.in
+IOC_CHECK_H = $(OBJS_DIR)/ioc_check.h
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Overrides.
+#
+MODSTUBS_DIR = $(OBJS_DIR)
+$(MODSTUBS_O) := AS_CPPFLAGS += -DNSMB_MODULE
+CLEANFILES += $(MODSTUBS_O) $(IOC_CHECK_H)
+C99MODE = $(C99_ENABLE)
+CERRWARN += -erroff=E_STATEMENT_NOT_REACHED
+INC_PATH += -I$(UTSBASE)/common/fs/smbclnt
+LDFLAGS += -dy -Ncrypto/md4 -Ncrypto/md5 -Nmisc/tlimod
+
+LINTTAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2
+LINTTAGS += -erroff=E_FUNC_RET_MAYBE_IGNORED2
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Create ioc_check.h and compare with the saved
+# ioc_check.ref to ensure 32/64-bit invariance.
+#
+$(OBJECTS) : $(IOC_CHECK_H)
+$(IOC_CHECK_H): $(OFFSETS_SRC)
+ $(OFFSETS_CREATE) <$(OFFSETS_SRC) >$@.tmp
+ cmp -s ioc_check.ref $@.tmp && \
+ mv -f $@.tmp $@
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/nsmb/ioc_check.ref b/usr/src/uts/intel/nsmb/ioc_check.ref
new file mode 100644
index 0000000000..ead69ec44f
--- /dev/null
+++ b/usr/src/uts/intel/nsmb/ioc_check.ref
@@ -0,0 +1,92 @@
+#define SIZEOF_SOCKADDR_ANY 0x18
+#define SIZEOF_SOCKADDR_IN 0x10
+#define SIZEOF_SOCKADDR_NB 0x18
+#define SIZEOF_SMBIOC_OSSN 0x218
+#define IOC_SERVER 0x0
+#define IOC_LOCAL 0x18
+#define IOC_LOCALCS 0x30
+#define IOC_LOCALCS_INCR 0x1
+#define IOC_SERVERCS 0x40
+#define IOC_SERVERCS_INCR 0x1
+#define IOC_SRVNAME 0x50
+#define IOC_SRVNAME_INCR 0x1
+#define IOC_USER 0x60
+#define IOC_USER_INCR 0x1
+#define IOC_WORKGROUP 0xe1
+#define IOC_WORKGROUP_INCR 0x1
+#define IOC_SSN_PASSWD 0x162
+#define IOC_SSN_PASSWD_INCR 0x1
+#define IOC_SSN_OPT 0x1e4
+#define IOC_TIMEOUT 0x1e8
+#define IOC_RETRYCOUNT 0x1ec
+#define IOC_SSN_OWNER 0x1f0
+#define IOC_SSN_GROUP 0x1f4
+#define IOC_SSN_MODE 0x1f8
+#define IOC_SSN_RIGHTS 0x1fc
+#define IOC_INTOKLEN 0x200
+#define IOC_OUTTOKLEN 0x204
+#define _IOC_INTOK 0x208
+#define _IOC_OUTTOK 0x210
+#define SIZEOF_SMBIOC_OSHARE 0x120
+#define IOC_SHARE 0x0
+#define IOC_SHARE_INCR 0x1
+#define IOC_SH_PASSWD 0x81
+#define IOC_SH_PASSWD_INCR 0x1
+#define IOC_SH_OPT 0x104
+#define IOC_STYPE 0x108
+#define IOC_SH_OWNER 0x10c
+#define IOC_SH_GROUP 0x110
+#define IOC_SH_MODE 0x114
+#define IOC_SH_RIGHTS 0x118
+#define SIZEOF_SMBIOC_RQ 0x30
+#define IOC_CMD 0x0
+#define IOC_TWC 0x1
+#define IOC_TBC 0x2
+#define IOC_RPBUFSZ 0x4
+#define IOC_RWC 0x9
+#define IOC_RBC 0xa
+#define IOC_RQ_ERRCLASS 0xd
+#define IOC_RQ_SERROR 0xe
+#define IOC_RQ_ERROR 0x10
+#define _IOC_TWORDS 0x18
+#define _IOC_TBYTES 0x20
+#define _IOC_RPBUF 0x28
+#define SIZEOF_SMBIOC_T2RQ 0xc0
+#define IOC_SETUP 0x0
+#define IOC_SETUP_INCR 0x2
+#define IOC_SETUPCNT 0x8
+#define IOC_NAME 0xc
+#define IOC_NAME_INCR 0x1
+#define IOC_TPARAMCNT 0x8c
+#define IOC_TDATACNT 0x8e
+#define IOC_RPARAMCNT 0x90
+#define IOC_RDATACNT 0x92
+#define IOC_T2_ERRCLASS 0x95
+#define IOC_T2_SERROR 0x96
+#define IOC_T2_ERROR 0x98
+#define IOC_RPFLAGS2 0x9c
+#define _IOC_TPARAM 0xa0
+#define _IOC_TDATA 0xa8
+#define _IOC_RPARAM 0xb0
+#define _IOC_RDATA 0xb8
+#define SIZEOF_SMBIOC_FLAGS 0xc
+#define IOC_LEVEL 0x0
+#define IOC_MASK 0x4
+#define IOC_FLAGS 0x8
+#define SIZEOF_SMBIOC_LOOKUP 0x340
+#define IOC_LOOK_LEVEL 0x0
+#define IOC_LOOK_FLAGS 0x4
+#define IOC_SH 0x8
+#define IOC_SSN 0x128
+#define SIZEOF_SMBIOC_RW 0x18
+#define IOC_FH 0x0
+#define IOC_CNT 0x4
+#define _IOC_OFFSET 0x8
+#define _IOC_BASE 0x10
+#define SIZEOF_SMBIOC_PK 0x304
+#define PK_DOM 0x4
+#define PK_DOM_INCR 0x1
+#define PK_USR 0x104
+#define PK_USR_INCR 0x1
+#define PK_PASS 0x204
+#define PK_PASS_INCR 0x1
diff --git a/usr/src/uts/intel/os/minor_perm b/usr/src/uts/intel/os/minor_perm
index 58b741b30f..8261fb776f 100644
--- a/usr/src/uts/intel/os/minor_perm
+++ b/usr/src/uts/intel/os/minor_perm
@@ -166,3 +166,4 @@ dmfe:* 0666 root sys
afe:* 0666 root sys
mxfe:* 0666 root sys
vscan:* 0640 root sys
+nsmb:* 0666 root sys
diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major
index d9fc868ae5..efa9f0428f 100644
--- a/usr/src/uts/intel/os/name_to_major
+++ b/usr/src/uts/intel/os/name_to_major
@@ -148,3 +148,4 @@ vnic 252
pit_beep 253
vscan 254
softmac 255
+nsmb 256
diff --git a/usr/src/uts/intel/smbfs/Makefile b/usr/src/uts/intel/smbfs/Makefile
new file mode 100644
index 0000000000..aff87ca8de
--- /dev/null
+++ b/usr/src/uts/intel/smbfs/Makefile
@@ -0,0 +1,98 @@
+#
+# 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
+#
+#
+# uts/intel/smbfs/Makefile
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the smbfs (Server
+# message block file system) kernel module.
+#
+# intel architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = smbfs
+OBJECTS = $(SMBFS_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(SMBFS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_FS_DIR)/$(MODULE)
+ROOTLINK = $(USR_SYS_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK)
+
+#
+# Overrides.
+#
+MODSTUBS_DIR = $(OBJS_DIR)
+$(MODSTUBS_O) := AS_CPPFLAGS += -DSMBFS_MODULE
+CLEANFILES += $(MODSTUBS_O)
+C99MODE = $(C99_ENABLE)
+INC_PATH += -I$(UTSBASE)/common/fs/smbclnt
+LDFLAGS += -dy -Ndrv/nsmb
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+$(ROOTLINK): $(USR_SYS_DIR) $(ROOTMODULE)
+ -$(RM) $@; ln $(ROOTMODULE) $@
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/sparc/Makefile b/usr/src/uts/sparc/Makefile
index 50ef3ecbbc..10154b6098 100644
--- a/usr/src/uts/sparc/Makefile
+++ b/usr/src/uts/sparc/Makefile
@@ -21,7 +21,7 @@
#
#ident "%Z%%M% %I% %E% SMI"
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# uts/sparc/Makefile
@@ -35,9 +35,11 @@ UTSBASE = ..
include Makefile.sparc
-LINT_KMODLIBS = $(LINT_KMODS:e1000g=)
-LINT_LIBS = $(LINT_LIB) $(GEN_LINT_LIB) \
- $(LINT_KMODLIBS:%=$(LINT_LIB_DIR)/llib-l%.ln)
+LINT_KMODS_X1 = $(LINT_KMODS:nsmb=)
+LINT_KMODS_X2 = $(LINT_KMODS_X1:smbfs=)
+LINT_KMODLIBS = $(LINT_KMODS_X2:e1000g=)
+LINT_LIBS = $(LINT_LIB) $(GEN_LINT_LIB) \
+ $(LINT_KMODLIBS:%=$(LINT_LIB_DIR)/llib-l%.ln)
$(CLOSED_BUILD)LINT_LIBS += $(CLOSED_LINT_KMODS:%=$(LINT_LIB_DIR)/llib-l%.ln)
diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared
index 4b7acadf5c..9ff8cc2020 100644
--- a/usr/src/uts/sparc/Makefile.sparc.shared
+++ b/usr/src/uts/sparc/Makefile.sparc.shared
@@ -236,6 +236,7 @@ DRV_KMODS += rds
DRV_KMODS += chxge
DRV_KMODS += smbsrv
DRV_KMODS += vscan
+DRV_KMODS += nsmb
#
# Don't build some of these for OpenSolaris, since they will be
@@ -337,7 +338,7 @@ SCHED_KMODS += RT TS RT_DPTBL TS_DPTBL IA FSS FX FX_DPTBL
#
FS_KMODS += dev devfs fdfs fifofs hsfs lofs namefs nfs pcfs tmpfs zfs
FS_KMODS += specfs udfs ufs autofs cachefs procfs sockfs mntfs
-FS_KMODS += ctfs objfs sharefs dcfs
+FS_KMODS += ctfs objfs sharefs dcfs smbfs
#
# Streams Modules (/kernel/strmod):
diff --git a/usr/src/uts/sparc/nsmb/Makefile b/usr/src/uts/sparc/nsmb/Makefile
new file mode 100644
index 0000000000..85c05e9fcb
--- /dev/null
+++ b/usr/src/uts/sparc/nsmb/Makefile
@@ -0,0 +1,135 @@
+#
+# 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
+#
+#
+# uts/sparc/nsmb/Makefile
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# sparc architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = nsmb
+OBJECTS = $(NSMB_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(NSMB_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/fs/smbclnt/netsmb
+OFFSETS_SRC = $(CONF_SRCDIR)/offsets.in
+IOC_CHECK_H = $(OBJS_DIR)/ioc_check.h
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+# Define targets
+#
+ALL_TARGET = $(ALL_TARGET_$(OBJS_DIR))
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(INSTALL_TARGET_$(OBJS_DIR))
+
+#
+# Overrides.
+#
+# We need some unusual overrides here so we'll
+# build ioc_check.h for both 32-bit/64-bit,
+# but only build 64-bit binaries.
+#
+
+# Build 32-bit also...
+DEF_BUILDS = $(DEF_BUILDS64) $(DEF_BUILDS32)
+ALL_BUILDS = $(ALL_BUILDS64) $(ALL_BUILDS32)
+# ... but don't build any 32-bit objects
+ALL_TARGET_debug32 = $(IOC_CHECK_H)
+ALL_TARGET_debug64 = $(BINARY) $(SRC_CONFILE)
+ALL_TARGET_obj32 = $(IOC_CHECK_H)
+ALL_TARGET_obj64 = $(BINARY) $(SRC_CONFILE)
+# ... and remove -xcg92 (not supported by cw)
+CFLAGS_32=
+# ... same deal for install targets
+INSTALL_TARGET_debug32 = $(IOC_CHECK_H)
+INSTALL_TARGET_debug64 = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+INSTALL_TARGET_obj32 = $(IOC_CHECK_H)
+INSTALL_TARGET_obj64 = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Now the normal overrides...
+#
+MODSTUBS_DIR = $(OBJS_DIR)
+$(MODSTUBS_O) := AS_CPPFLAGS += -DNSMB_MODULE
+CLEANFILES += $(MODSTUBS_O) $(IOC_CHECK_H)
+C99MODE = $(C99_ENABLE)
+CERRWARN += -erroff=E_STATEMENT_NOT_REACHED
+INC_PATH += -I$(UTSBASE)/common/fs/smbclnt
+LDFLAGS += -dy -Ncrypto/md4 -Ncrypto/md5 -Nmisc/tlimod
+
+LINTTAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2
+LINTTAGS += -erroff=E_FUNC_RET_MAYBE_IGNORED2
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Create ioc_check.h and compare with the saved
+# ioc_check.ref to ensure 32/64-bit invariance.
+# We don't need _ASM_INLINES for this, and it
+# causes #error "port me" in 32-bit builds.
+#
+$(OBJECTS) : $(IOC_CHECK_H)
+$(IOC_CHECK_H): $(OFFSETS_SRC)
+ $(OFFSETS_CREATE) -U_ASM_INLINES \
+ <$(OFFSETS_SRC) >$@.tmp
+ cmp -s ioc_check.ref $@.tmp && \
+ mv -f $@.tmp $@
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
diff --git a/usr/src/uts/sparc/nsmb/inc.flg b/usr/src/uts/sparc/nsmb/inc.flg
new file mode 100644
index 0000000000..91c4d47cff
--- /dev/null
+++ b/usr/src/uts/sparc/nsmb/inc.flg
@@ -0,0 +1,40 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+# Find the CIFS sources.
+
+find_files "s.*.c" usr/src/uts/common/fs/smbclnt/netsmb
+
+# Find header files.
+
+find_files "s.*.h" \
+ usr/src/uts/common/fs \
+ usr/src/uts/common/netinet \
+ usr/src/uts/common/vm \
+ usr/src/uts/sparc/sys \
+ usr/src/uts/sun/sys
+
diff --git a/usr/src/uts/sparc/nsmb/ioc_check.ref b/usr/src/uts/sparc/nsmb/ioc_check.ref
new file mode 100644
index 0000000000..ead69ec44f
--- /dev/null
+++ b/usr/src/uts/sparc/nsmb/ioc_check.ref
@@ -0,0 +1,92 @@
+#define SIZEOF_SOCKADDR_ANY 0x18
+#define SIZEOF_SOCKADDR_IN 0x10
+#define SIZEOF_SOCKADDR_NB 0x18
+#define SIZEOF_SMBIOC_OSSN 0x218
+#define IOC_SERVER 0x0
+#define IOC_LOCAL 0x18
+#define IOC_LOCALCS 0x30
+#define IOC_LOCALCS_INCR 0x1
+#define IOC_SERVERCS 0x40
+#define IOC_SERVERCS_INCR 0x1
+#define IOC_SRVNAME 0x50
+#define IOC_SRVNAME_INCR 0x1
+#define IOC_USER 0x60
+#define IOC_USER_INCR 0x1
+#define IOC_WORKGROUP 0xe1
+#define IOC_WORKGROUP_INCR 0x1
+#define IOC_SSN_PASSWD 0x162
+#define IOC_SSN_PASSWD_INCR 0x1
+#define IOC_SSN_OPT 0x1e4
+#define IOC_TIMEOUT 0x1e8
+#define IOC_RETRYCOUNT 0x1ec
+#define IOC_SSN_OWNER 0x1f0
+#define IOC_SSN_GROUP 0x1f4
+#define IOC_SSN_MODE 0x1f8
+#define IOC_SSN_RIGHTS 0x1fc
+#define IOC_INTOKLEN 0x200
+#define IOC_OUTTOKLEN 0x204
+#define _IOC_INTOK 0x208
+#define _IOC_OUTTOK 0x210
+#define SIZEOF_SMBIOC_OSHARE 0x120
+#define IOC_SHARE 0x0
+#define IOC_SHARE_INCR 0x1
+#define IOC_SH_PASSWD 0x81
+#define IOC_SH_PASSWD_INCR 0x1
+#define IOC_SH_OPT 0x104
+#define IOC_STYPE 0x108
+#define IOC_SH_OWNER 0x10c
+#define IOC_SH_GROUP 0x110
+#define IOC_SH_MODE 0x114
+#define IOC_SH_RIGHTS 0x118
+#define SIZEOF_SMBIOC_RQ 0x30
+#define IOC_CMD 0x0
+#define IOC_TWC 0x1
+#define IOC_TBC 0x2
+#define IOC_RPBUFSZ 0x4
+#define IOC_RWC 0x9
+#define IOC_RBC 0xa
+#define IOC_RQ_ERRCLASS 0xd
+#define IOC_RQ_SERROR 0xe
+#define IOC_RQ_ERROR 0x10
+#define _IOC_TWORDS 0x18
+#define _IOC_TBYTES 0x20
+#define _IOC_RPBUF 0x28
+#define SIZEOF_SMBIOC_T2RQ 0xc0
+#define IOC_SETUP 0x0
+#define IOC_SETUP_INCR 0x2
+#define IOC_SETUPCNT 0x8
+#define IOC_NAME 0xc
+#define IOC_NAME_INCR 0x1
+#define IOC_TPARAMCNT 0x8c
+#define IOC_TDATACNT 0x8e
+#define IOC_RPARAMCNT 0x90
+#define IOC_RDATACNT 0x92
+#define IOC_T2_ERRCLASS 0x95
+#define IOC_T2_SERROR 0x96
+#define IOC_T2_ERROR 0x98
+#define IOC_RPFLAGS2 0x9c
+#define _IOC_TPARAM 0xa0
+#define _IOC_TDATA 0xa8
+#define _IOC_RPARAM 0xb0
+#define _IOC_RDATA 0xb8
+#define SIZEOF_SMBIOC_FLAGS 0xc
+#define IOC_LEVEL 0x0
+#define IOC_MASK 0x4
+#define IOC_FLAGS 0x8
+#define SIZEOF_SMBIOC_LOOKUP 0x340
+#define IOC_LOOK_LEVEL 0x0
+#define IOC_LOOK_FLAGS 0x4
+#define IOC_SH 0x8
+#define IOC_SSN 0x128
+#define SIZEOF_SMBIOC_RW 0x18
+#define IOC_FH 0x0
+#define IOC_CNT 0x4
+#define _IOC_OFFSET 0x8
+#define _IOC_BASE 0x10
+#define SIZEOF_SMBIOC_PK 0x304
+#define PK_DOM 0x4
+#define PK_DOM_INCR 0x1
+#define PK_USR 0x104
+#define PK_USR_INCR 0x1
+#define PK_PASS 0x204
+#define PK_PASS_INCR 0x1
diff --git a/usr/src/uts/sparc/os/minor_perm b/usr/src/uts/sparc/os/minor_perm
index 6751d9069a..d56b37858a 100644
--- a/usr/src/uts/sparc/os/minor_perm
+++ b/usr/src/uts/sparc/os/minor_perm
@@ -180,3 +180,4 @@ rtls:* 0666 root sys
vnic:* 0666 root sys
physmem:* 0600 root sys
sdp:sdp 0666 root sys
+nsmb:* 0666 root sys
diff --git a/usr/src/uts/sparc/os/name_to_major b/usr/src/uts/sparc/os/name_to_major
index fc4efd835f..6e1339bf60 100644
--- a/usr/src/uts/sparc/os/name_to_major
+++ b/usr/src/uts/sparc/os/name_to_major
@@ -221,3 +221,4 @@ ds_pri 272
vnic 273
vscan 274
softmac 275
+nsmb 276
diff --git a/usr/src/uts/sparc/smbfs/Makefile b/usr/src/uts/sparc/smbfs/Makefile
new file mode 100644
index 0000000000..55cea236b9
--- /dev/null
+++ b/usr/src/uts/sparc/smbfs/Makefile
@@ -0,0 +1,93 @@
+#
+# 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
+#
+#
+# uts/sparc/smbfs/Makefile
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the smbfs (Server
+# message block file system) kernel module.
+#
+# sparc architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = smbfs
+OBJECTS = $(SMBFS_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(SMBFS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(USR_FS_DIR)/$(MODULE)
+ROOTLINK = $(USR_SYS_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK)
+
+#
+# Overrides.
+#
+MODSTUBS_DIR = $(OBJS_DIR)
+$(MODSTUBS_O) := AS_CPPFLAGS += -DSMBFS_MODULE
+CLEANFILES += $(MODSTUBS_O)
+C99MODE = $(C99_ENABLE)
+INC_PATH += -I$(UTSBASE)/common/fs/smbclnt
+LDFLAGS += -dy -Ndrv/nsmb
+
+.KEEP_STATE:
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+$(ROOTLINK): $(USR_SYS_DIR) $(ROOTMODULE)
+ -$(RM) $@; ln $(ROOTMODULE) $@
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
diff --git a/usr/src/uts/sparc/smbfs/inc.flg b/usr/src/uts/sparc/smbfs/inc.flg
new file mode 100644
index 0000000000..12a251f511
--- /dev/null
+++ b/usr/src/uts/sparc/smbfs/inc.flg
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+# Find the CIFS sources.
+
+find_files "s.*.c" usr/src/uts/common/fs/smbclnt/smbfs
+
+# Find header files.
+
+find_files "s.*.h" \
+ usr/src/uts/common/fs \
+ usr/src/uts/common/netinet \
+ usr/src/uts/common/rpc \
+ usr/src/uts/common/sys \
+ usr/src/uts/common/vm \
+ usr/src/uts/sparc/sys \
+ usr/src/uts/sun/sys