--- old/usr/src/uts/common/fs/smbsrv/smb_tree.c	Mon Aug  5 17:26:41 2019
+++ new/usr/src/uts/common/fs/smbsrv/smb_tree.c	Mon Aug  5 17:26:41 2019
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2016 by Delphix. All rights reserved.
  */
 
@@ -122,10 +122,14 @@
  *
  * Transition T2
  *
+ *    This transition occurs in smb_tree_disconnect()
+ *
+ * Transition T3
+ *
  *    This transition occurs in smb_tree_release(). The resources associated
  *    with the tree are freed as well as the tree structure. For the transition
- *    to occur, the tree must be in the SMB_TREE_STATE_DISCONNECTED state and
- *    the reference count be zero.
+ *    to occur, the tree must be in the SMB_TREE_STATE_DISCONNECTED and the
+ *    reference count must be zero.
  *
  * Comments
  * --------
@@ -144,7 +148,11 @@
  *    Rules of access to a tree structure:
  *
  *    1) In order to avoid deadlocks, when both (mutex and lock of the user
- *       list) have to be entered, the lock must be entered first.
+ *       list) have to be entered, the lock must be entered first. Additionally,
+ *       when both the (mutex and lock of the ofile list) have to be entered,
+ *       the mutex must be entered first. However, the ofile list lock must NOT
+ *       be dropped while the mutex is held in such a way that the ofile deleteq
+ *       is flushed.
  *
  *    2) All actions applied to a tree require a reference count.
  *
@@ -176,18 +184,14 @@
 uint32_t	smb_tree_connect_disk(smb_request_t *, smb_arg_tcon_t *);
 uint32_t	smb_tree_connect_printq(smb_request_t *, smb_arg_tcon_t *);
 uint32_t	smb_tree_connect_ipc(smb_request_t *, smb_arg_tcon_t *);
-static smb_tree_t *smb_tree_alloc(smb_request_t *, const smb_kshare_t *,
-    smb_node_t *, uint32_t, uint32_t);
+static void smb_tree_dealloc(void *);
 static boolean_t smb_tree_is_connected_locked(smb_tree_t *);
-static boolean_t smb_tree_is_disconnected(smb_tree_t *);
 static char *smb_tree_get_sharename(char *);
 static int smb_tree_getattr(const smb_kshare_t *, smb_node_t *, smb_tree_t *);
 static void smb_tree_get_volname(vfs_t *, smb_tree_t *);
 static void smb_tree_get_flags(const smb_kshare_t *, vfs_t *, smb_tree_t *);
 static void smb_tree_log(smb_request_t *, const char *, const char *, ...);
-static void smb_tree_close_odirs(smb_tree_t *, uint16_t);
-static smb_ofile_t *smb_tree_get_ofile(smb_tree_t *, smb_ofile_t *);
-static smb_odir_t *smb_tree_get_odir(smb_tree_t *, smb_odir_t *);
+static void smb_tree_close_odirs(smb_tree_t *, uint32_t);
 static void smb_tree_set_execinfo(smb_tree_t *, smb_shr_execinfo_t *, int);
 static int smb_tree_enum_private(smb_tree_t *, smb_svcenum_t *);
 static int smb_tree_netinfo_encode(smb_tree_t *, uint8_t *, size_t, uint32_t *);
@@ -252,6 +256,25 @@
 	tcon->name = name;
 	sr->sr_tcon.si = si;
 
+	/*
+	 * [MS-SMB2] 3.3.5.7 Receiving an SMB2 TREE_CONNECT Request
+	 *
+	 * If we support 3.x, RejectUnencryptedAccess is TRUE,
+	 * if Tcon.EncryptData is TRUE or global EncryptData is TRUE,
+	 * and the connection doesn't support encryption,
+	 * return ACCESS_DENIED.
+	 *
+	 * If RejectUnencryptedAccess is TRUE, we force max_protocol
+	 * to at least 3.0. Additionally, if the tree requires encryption,
+	 * we don't care what we support, we still enforce encryption.
+	 */
+	if ((sr->sr_server->sv_cfg.skc_encrypt == SMB_CONFIG_REQUIRED ||
+	    si->shr_encrypt == SMB_CONFIG_REQUIRED) &&
+	    (sr->session->srv_cap & SMB2_CAP_ENCRYPTION) == 0) {
+		status = NT_STATUS_ACCESS_DENIED;
+		goto out;
+	}
+
 	switch (si->shr_type & STYPE_MASK) {
 	case STYPE_DISKTREE:
 		status = smb_tree_connect_disk(sr, &sr->sr_tcon);
@@ -267,7 +290,19 @@
 		break;
 	}
 
-	smb_kshare_release(sr->sr_server, si);
+out:
+	/*
+	 * On return from smb_tree_connect_* sr->tid_tree is filled in
+	 * and valid for all share types.  We can't call smb_kshare_release
+	 * until we disconnect the tree or we will invalidate the reference
+	 * we have here.
+	 */
+	if (sr->tid_tree != NULL) {
+		sr->tid_tree->t_kshare = si;
+	} else {
+		smb_kshare_release(sr->sr_server, si);
+	}
+
 	sr->sr_tcon.si = NULL;
 
 	return (status);
@@ -275,10 +310,13 @@
 
 /*
  * Disconnect a tree.
+ *
+ * The "do_exec" arg is obsolete and ignored.
  */
 void
 smb_tree_disconnect(smb_tree_t *tree, boolean_t do_exec)
 {
+	_NOTE(ARGUNUSED(do_exec))
 	smb_shr_execinfo_t execinfo;
 
 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
@@ -286,34 +324,27 @@
 	mutex_enter(&tree->t_mutex);
 	ASSERT(tree->t_refcnt);
 
-	if (smb_tree_is_connected_locked(tree)) {
-		/*
-		 * Indicate that the disconnect process has started.
-		 */
-		tree->t_state = SMB_TREE_STATE_DISCONNECTING;
+	if (!smb_tree_is_connected_locked(tree)) {
 		mutex_exit(&tree->t_mutex);
-
-		if (do_exec) {
-			/*
-			 * The files opened under this tree are closed.
-			 */
-			smb_ofile_close_all(tree);
-			/*
-			 * The directories opened under this tree are closed.
-			 */
-			smb_tree_close_odirs(tree, 0);
-		}
-
-		mutex_enter(&tree->t_mutex);
-		tree->t_state = SMB_TREE_STATE_DISCONNECTED;
-		smb_server_dec_trees(tree->t_server);
+		return;
 	}
 
+	/*
+	 * Indicate that the disconnect process has started.
+	 */
+	tree->t_state = SMB_TREE_STATE_DISCONNECTING;
 	mutex_exit(&tree->t_mutex);
 
-	if (do_exec && (tree->t_state == SMB_TREE_STATE_DISCONNECTED) &&
-	    (tree->t_execflags & SMB_EXEC_UNMAP)) {
+	/*
+	 * The files opened under this tree are closed.
+	 */
+	smb_ofile_close_all(tree, 0);
+	/*
+	 * The directories opened under this tree are closed.
+	 */
+	smb_tree_close_odirs(tree, 0);
 
+	if ((tree->t_execflags & SMB_EXEC_UNMAP) != 0) {
 		smb_tree_set_execinfo(tree, &execinfo, SMB_EXEC_UNMAP);
 		(void) smb_kshare_exec(tree->t_server, &execinfo);
 	}
@@ -371,44 +402,33 @@
 {
 	SMB_TREE_VALID(tree);
 
-	mutex_enter(&tree->t_mutex);
-	ASSERT(tree->t_refcnt);
-	tree->t_refcnt--;
-
 	/* flush the ofile and odir lists' delete queues */
 	smb_llist_flush(&tree->t_ofile_list);
 	smb_llist_flush(&tree->t_odir_list);
 
-	if (smb_tree_is_disconnected(tree) && (tree->t_refcnt == 0))
-		smb_session_post_tree(tree->t_session, tree);
+	mutex_enter(&tree->t_mutex);
+	ASSERT(tree->t_refcnt);
+	tree->t_refcnt--;
 
+	switch (tree->t_state) {
+	case SMB_TREE_STATE_DISCONNECTING:
+		if (tree->t_refcnt == 0) {
+			smb_session_t *ssn = tree->t_session;
+			tree->t_state = SMB_TREE_STATE_DISCONNECTED;
+			smb_llist_post(&ssn->s_tree_list, tree,
+			    smb_tree_dealloc);
+		}
+		break;
+	case SMB_TREE_STATE_CONNECTED:
+		break;
+	default:
+		ASSERT(0);
+		break;
+	}
+
 	mutex_exit(&tree->t_mutex);
 }
 
-void
-smb_tree_post_ofile(smb_tree_t *tree, smb_ofile_t *of)
-{
-	SMB_TREE_VALID(tree);
-	SMB_OFILE_VALID(of);
-	ASSERT(of->f_refcnt == 0);
-	ASSERT(of->f_state == SMB_OFILE_STATE_CLOSED);
-	ASSERT(of->f_tree == tree);
-
-	smb_llist_post(&tree->t_ofile_list, of, smb_ofile_delete);
-}
-
-void
-smb_tree_post_odir(smb_tree_t *tree, smb_odir_t *od)
-{
-	SMB_TREE_VALID(tree);
-	SMB_ODIR_VALID(od);
-	ASSERT(od->d_refcnt == 0);
-	ASSERT(od->d_state == SMB_ODIR_STATE_CLOSED);
-	ASSERT(od->d_tree == tree);
-
-	smb_llist_post(&tree->t_odir_list, od, smb_odir_delete);
-}
-
 /*
  * Close ofiles and odirs that match pid.
  */
@@ -420,7 +440,7 @@
 	ASSERT(tree);
 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
 
-	smb_ofile_close_all_by_pid(tree, pid);
+	smb_ofile_close_all(tree, pid);
 	smb_tree_close_odirs(tree, pid);
 }
 
@@ -445,31 +465,29 @@
 int
 smb_tree_enum(smb_tree_t *tree, smb_svcenum_t *svcenum)
 {
+	smb_llist_t	*of_list;
 	smb_ofile_t	*of;
-	smb_ofile_t	*next;
 	int		rc = 0;
 
-	ASSERT(tree);
-	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
-
 	if (svcenum->se_type == SMB_SVCENUM_TYPE_TREE)
 		return (smb_tree_enum_private(tree, svcenum));
 
-	of = smb_tree_get_ofile(tree, NULL);
-	while (of) {
-		ASSERT(of->f_tree == tree);
+	of_list = &tree->t_ofile_list;
+	smb_llist_enter(of_list, RW_READER);
 
-		rc = smb_ofile_enum(of, svcenum);
-		if (rc != 0) {
+	of = smb_llist_head(of_list);
+	while (of) {
+		if (smb_ofile_hold(of)) {
+			rc = smb_ofile_enum(of, svcenum);
 			smb_ofile_release(of);
-			break;
 		}
-
-		next = smb_tree_get_ofile(tree, of);
-		smb_ofile_release(of);
-		of = next;
+		if (rc != 0)
+			break;
+		of = smb_llist_next(of_list, of);
 	}
 
+	smb_llist_exit(of_list);
+
 	return (rc);
 }
 
@@ -484,6 +502,11 @@
 	ASSERT(tree);
 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
 
+	/*
+	 * Note that ORPHANED ofiles aren't fclosable, as they have
+	 * no session, user, or tree by which they might be found.
+	 * They will eventually expire.
+	 */
 	if ((of = smb_ofile_lookup_by_uniqid(tree, uniqid)) == NULL)
 		return (ENOENT);
 
@@ -639,6 +662,9 @@
 	return (access);
 }
 
+/* How long should tree connect wait for DH import to complete? */
+int smb_tcon_import_wait = 20; /* sec. */
+
 /*
  * Connect a share for use with files and directories.
  */
@@ -648,15 +674,14 @@
 	char			*sharename = tcon->path;
 	const char		*any = "?????";
 	smb_user_t		*user = sr->uid_user;
-	smb_node_t		*dnode = NULL;
 	smb_node_t		*snode = NULL;
-	smb_kshare_t 		*si = tcon->si;
+	smb_kshare_t		*si = tcon->si;
 	char			*service = tcon->service;
-	char			last_component[MAXNAMELEN];
 	smb_tree_t		*tree;
 	int			rc;
 	uint32_t		access;
 	smb_shr_execinfo_t	execinfo;
+	clock_t	time;
 
 	ASSERT(user);
 	ASSERT(user->u_cred);
@@ -671,30 +696,33 @@
 	/*
 	 * Check that the shared directory exists.
 	 */
-	rc = smb_pathname_reduce(sr, user->u_cred, si->shr_path, 0, 0, &dnode,
-	    last_component);
-	if (rc == 0) {
-		rc = smb_fsop_lookup(sr, user->u_cred, SMB_FOLLOW_LINKS,
-		    sr->sr_server->si_root_smb_node, dnode, last_component,
-		    &snode);
-
-		smb_node_release(dnode);
-	}
-
-	if (rc) {
-		if (snode)
-			smb_node_release(snode);
-
+	snode = si->shr_root_node;
+	if (snode == NULL) {
 		smb_tree_log(sr, sharename, "bad path: %s", si->shr_path);
 		return (NT_STATUS_BAD_NETWORK_NAME);
 	}
 
 	if ((access = smb_tree_chkaccess(sr, si, snode->vp)) == 0) {
-		smb_node_release(snode);
 		return (NT_STATUS_ACCESS_DENIED);
 	}
 
 	/*
+	 * Wait for DH import of persistent handles to finish.
+	 * If we timeout, it's not clear what status to return,
+	 * but as the share is not really availale yet, let's
+	 * return the status for "no such share".
+	 */
+	time = SEC_TO_TICK(smb_tcon_import_wait) + ddi_get_lbolt();
+	mutex_enter(&si->shr_mutex);
+	while (si->shr_import_busy != NULL) {
+		if (cv_timedwait(&si->shr_cv, &si->shr_mutex, time) < 0) {
+			mutex_exit(&si->shr_mutex);
+			return (NT_STATUS_BAD_NETWORK_NAME);
+		}
+	}
+	mutex_exit(&si->shr_mutex);
+
+	/*
 	 * Set up the OptionalSupport for this share.
 	 */
 	tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
@@ -731,8 +759,6 @@
 
 	tree = smb_tree_alloc(sr, si, snode, access, sr->sr_cfg->skc_execflags);
 
-	smb_node_release(snode);
-
 	if (tree == NULL)
 		return (NT_STATUS_INSUFF_SERVER_RESOURCES);
 
@@ -742,7 +768,17 @@
 		rc = smb_kshare_exec(tree->t_server, &execinfo);
 
 		if ((rc != 0) && (tree->t_execflags & SMB_EXEC_TERM)) {
-			smb_tree_disconnect(tree, B_FALSE);
+			/*
+			 * Inline parts of: smb_tree_disconnect()
+			 * Not using smb_tree_disconnect() for cleanup
+			 * here because: we don't want an exec up-call,
+			 * and there can't be any opens as we never
+			 * returned this TID to the client.
+			 */
+			mutex_enter(&tree->t_mutex);
+			tree->t_state = SMB_TREE_STATE_DISCONNECTING;
+			mutex_enter(&tree->t_mutex);
+
 			smb_tree_release(tree);
 			return (NT_STATUS_ACCESS_DENIED);
 		}
@@ -768,7 +804,7 @@
 	smb_user_t		*user = sr->uid_user;
 	smb_node_t		*dnode = NULL;
 	smb_node_t		*snode = NULL;
-	smb_kshare_t 		*si = tcon->si;
+	smb_kshare_t		*si = tcon->si;
 	char			*service = tcon->service;
 	char			last_component[MAXNAMELEN];
 	smb_tree_t		*tree;
@@ -874,7 +910,7 @@
 /*
  * Allocate a tree.
  */
-static smb_tree_t *
+smb_tree_t *
 smb_tree_alloc(smb_request_t *sr, const smb_kshare_t *si,
     smb_node_t *snode, uint32_t access, uint32_t execflags)
 {
@@ -918,7 +954,7 @@
 	}
 
 	smb_llist_constructor(&tree->t_ofile_list, sizeof (smb_ofile_t),
-	    offsetof(smb_ofile_t, f_lnd));
+	    offsetof(smb_ofile_t, f_tree_lnd));
 
 	smb_llist_constructor(&tree->t_odir_list, sizeof (smb_odir_t),
 	    offsetof(smb_odir_t, d_lnd));
@@ -964,7 +1000,7 @@
  * Remove the tree from the user's tree list before freeing resources
  * associated with the tree.
  */
-void
+static void
 smb_tree_dealloc(void *arg)
 {
 	smb_session_t	*session;
@@ -974,6 +1010,8 @@
 	ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
 	ASSERT(tree->t_refcnt == 0);
 
+	smb_server_dec_trees(tree->t_server);
+
 	session = tree->t_session;
 	smb_llist_enter(&session->s_tree_list, RW_WRITER);
 	smb_llist_remove(&session->s_tree_list, tree);
@@ -981,11 +1019,23 @@
 	atomic_dec_32(&session->s_tree_cnt);
 	smb_llist_exit(&session->s_tree_list);
 
+	/*
+	 * This tree is no longer on s_tree_list, however...
+	 *
+	 * This is called via smb_llist_post, which means it may run
+	 * BEFORE smb_tree_release drops t_mutex (if another thread
+	 * flushes the delete queue before we do).  Synchronize.
+	 */
 	mutex_enter(&tree->t_mutex);
 	mutex_exit(&tree->t_mutex);
 
 	tree->t_magic = (uint32_t)~SMB_TREE_MAGIC;
 
+	if (tree->t_kshare != NULL) {
+		smb_kshare_release(tree->t_server, tree->t_kshare);
+		tree->t_kshare = NULL;
+	}
+
 	if (tree->t_snode)
 		smb_node_release(tree->t_snode);
 
@@ -1015,7 +1065,7 @@
 	case SMB_TREE_STATE_DISCONNECTING:
 	case SMB_TREE_STATE_DISCONNECTED:
 		/*
-		 * The tree exists but being diconnected or destroyed.
+		 * The tree exists but is being disconnected or destroyed.
 		 */
 		return (B_FALSE);
 
@@ -1026,27 +1076,6 @@
 }
 
 /*
- * Determine whether or not a tree is disconnected.
- * This function must be called with the tree mutex held.
- */
-static boolean_t
-smb_tree_is_disconnected(smb_tree_t *tree)
-{
-	switch (tree->t_state) {
-	case SMB_TREE_STATE_DISCONNECTED:
-		return (B_TRUE);
-
-	case SMB_TREE_STATE_CONNECTED:
-	case SMB_TREE_STATE_DISCONNECTING:
-		return (B_FALSE);
-
-	default:
-		ASSERT(0);
-		return (B_FALSE);
-	}
-}
-
-/*
  * Return a pointer to the share name within a share resource path.
  *
  * The share path may be a Uniform Naming Convention (UNC) string
@@ -1086,6 +1115,7 @@
 smb_tree_getattr(const smb_kshare_t *si, smb_node_t *node, smb_tree_t *tree)
 {
 	vfs_t *vfsp = SMB_NODE_VFS(node);
+	smb_cfg_val_t srv_encrypt;
 
 	ASSERT(vfsp);
 
@@ -1095,6 +1125,19 @@
 	smb_tree_get_volname(vfsp, tree);
 	smb_tree_get_flags(si, vfsp, tree);
 
+	srv_encrypt = tree->t_session->s_server->sv_cfg.skc_encrypt;
+	if (tree->t_session->dialect >= SMB_VERS_3_0) {
+		if (si->shr_encrypt == SMB_CONFIG_REQUIRED ||
+		    srv_encrypt == SMB_CONFIG_REQUIRED)
+			tree->t_encrypt = SMB_CONFIG_REQUIRED;
+		else if (si->shr_encrypt == SMB_CONFIG_ENABLED ||
+		    srv_encrypt == SMB_CONFIG_ENABLED)
+			tree->t_encrypt = SMB_CONFIG_ENABLED;
+		else
+			tree->t_encrypt = SMB_CONFIG_DISABLED;
+	} else
+		tree->t_encrypt = SMB_CONFIG_DISABLED;
+
 	VFS_RELE(vfsp);
 	return (0);
 }
@@ -1147,6 +1190,10 @@
 	} smb_mtype_t;
 
 	static smb_mtype_t smb_mtype[] = {
+#ifdef	_FAKE_KERNEL
+		/* See libfksmbsrv:fake_vfs.c */
+		{ "fake",    3,	SMB_TREE_SPARSE},
+#endif	/* _FAKE_KERNEL */
 		{ "zfs",    3,	SMB_TREE_QUOTA | SMB_TREE_SPARSE},
 		{ "ufs",    3,	0 },
 		{ "nfs",    3,	SMB_TREE_NFS_MOUNTED },
@@ -1168,6 +1215,12 @@
 	if (si->shr_flags & SMB_SHRF_ABE)
 		flags |= SMB_TREE_ABE;
 
+	if (si->shr_flags & SMB_SHRF_CA)
+		flags |= SMB_TREE_CA;
+
+	if (si->shr_flags & SMB_SHRF_FSO)
+		flags |= SMB_TREE_FORCE_L2_OPLOCK;
+
 	if (ssn->s_cfg.skc_oplock_enable) {
 		/* if 'smb' zfs property: oplocks=enabled */
 		flags |= SMB_TREE_OPLOCKS;
@@ -1201,6 +1254,13 @@
 			flags |= mtype->mt_flags;
 	}
 
+	/*
+	 * SMB_TREE_QUOTA will be on here if the FS is ZFS.  We want to
+	 * turn it OFF when the share property says false.
+	 */
+	if ((si->shr_flags & SMB_SHRF_QUOTAS) == 0)
+		flags &= ~SMB_TREE_QUOTA;
+
 	(void) strlcpy(tree->t_typename, name, SMB_TYPENAMELEN);
 	(void) smb_strupr((char *)tree->t_typename);
 
@@ -1320,83 +1380,6 @@
 }
 
 /*
- * Get the next open ofile in the list.  A reference is taken on
- * the ofile, which can be released later with smb_ofile_release().
- *
- * If the specified ofile is NULL, search from the beginning of the
- * list.  Otherwise, the search starts just after that ofile.
- *
- * Returns NULL if there are no open files in the list.
- */
-static smb_ofile_t *
-smb_tree_get_ofile(smb_tree_t *tree, smb_ofile_t *of)
-{
-	smb_llist_t *ofile_list;
-
-	ASSERT(tree);
-	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
-
-	ofile_list = &tree->t_ofile_list;
-	smb_llist_enter(ofile_list, RW_READER);
-
-	if (of) {
-		ASSERT(of->f_magic == SMB_OFILE_MAGIC);
-		of = smb_llist_next(ofile_list, of);
-	} else {
-		of = smb_llist_head(ofile_list);
-	}
-
-	while (of) {
-		if (smb_ofile_hold(of))
-			break;
-
-		of = smb_llist_next(ofile_list, of);
-	}
-
-	smb_llist_exit(ofile_list);
-	return (of);
-}
-
-/*
- * smb_tree_get_odir
- *
- * Find the next odir in the tree's list of odirs, and obtain a
- * hold on it.
- * If the specified odir is NULL the search starts at the beginning
- * of the tree's odir list, otherwise the search starts after the
- * specified odir.
- */
-static smb_odir_t *
-smb_tree_get_odir(smb_tree_t *tree, smb_odir_t *od)
-{
-	smb_llist_t *od_list;
-
-	ASSERT(tree);
-	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
-
-	od_list = &tree->t_odir_list;
-	smb_llist_enter(od_list, RW_READER);
-
-	if (od) {
-		ASSERT(od->d_magic == SMB_ODIR_MAGIC);
-		od = smb_llist_next(od_list, od);
-	} else {
-		od = smb_llist_head(od_list);
-	}
-
-	while (od) {
-		ASSERT(od->d_magic == SMB_ODIR_MAGIC);
-
-		if (smb_odir_hold(od))
-			break;
-		od = smb_llist_next(od_list, od);
-	}
-
-	smb_llist_exit(od_list);
-	return (od);
-}
-
-/*
  * smb_tree_close_odirs
  *
  * Close all open odirs in the tree's list which were opened by
@@ -1404,25 +1387,34 @@
  * If pid is zero, close all open odirs in the tree's list.
  */
 static void
-smb_tree_close_odirs(smb_tree_t *tree, uint16_t pid)
+smb_tree_close_odirs(smb_tree_t *tree, uint32_t pid)
 {
-	smb_odir_t *od, *next_od;
+	smb_llist_t	*od_list;
+	smb_odir_t	*od;
 
 	ASSERT(tree);
 	ASSERT(tree->t_magic == SMB_TREE_MAGIC);
 
-	od = smb_tree_get_odir(tree, NULL);
-	while (od) {
+	od_list = &tree->t_odir_list;
+	smb_llist_enter(od_list, RW_READER);
+
+	for (od = smb_llist_head(od_list);
+	    od != NULL;
+	    od = smb_llist_next(od_list, od)) {
+
 		ASSERT(od->d_magic == SMB_ODIR_MAGIC);
 		ASSERT(od->d_tree == tree);
 
-		next_od = smb_tree_get_odir(tree, od);
-		if ((pid == 0) || (od->d_opened_by_pid == pid))
-			smb_odir_close(od);
-		smb_odir_release(od);
+		if (pid != 0 && od->d_opened_by_pid != pid)
+			continue;
 
-		od = next_od;
+		if (smb_odir_hold(od)) {
+			smb_odir_close(od);
+			smb_odir_release(od);
+		}
 	}
+
+	smb_llist_exit(od_list);
 }
 
 static void
