Print this page
NEX-18761 panic in smb_ofile_free with vdbench
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-5273 SMB 3 Encryption
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-6858 Non-admin users unable to mount CIFS shares
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-6276 SMB sparse file support
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5737 mutex_enter: bad mutex in smb2 causes panic in mutex_vector_enter
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-3553 SMB2/3 durable handles
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-5537 Want reference counts for users, trees...
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-4313 want iops, bandwidth, and latency kstats for smb
Portions contributed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-3863 Would like an SMB share property to enable/disable quotas
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-3441 CLONE - PORT NEX-3398 Restore SMB map/unmap upcall feature
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)
SMB-74 Process oplock breaks as session requests (part two)
SMB-50 User-mode SMB server
 Includes work by these authors:
 Thomas Keiser <thomas.keiser@nexenta.com>
 Albert Lee <trisk@nexenta.com>
SMB-65 SMB server in non-global zones (use zone_kcred())
SUP-694 panic on bad mutex in smb_event_wait()
SMB-65 SMB server in non-global zones (data structure changes)
Many things move to the smb_server_t object, and
many functions gain an sv arg (which server).
SMB-65 SMB server in non-global zones (kmem_caches)
common kmem_cache instances across zones
separate GZ-only init from NGZ init
SMB-63 taskq_create_proc ... TQ_DYNAMIC puts tasks in p0
re #11974 CIFS Share - Tree connect fails from Windows 7 Clients
re #13470 rb4432 Sync some SMB differences from illumos
re #6813 rb1757 port 2976 Child folder visibility through shares
re #6812 rb1753 backport illumos 1604 smbd print_enable doesn't really work

@@ -19,11 +19,11 @@
  * CDDL HEADER END
  */
 
 /*
  * 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.
  */
 
 /*
  * General Structures Layout

@@ -120,14 +120,18 @@
  *
  *    This transition occurs in smb_tree_disconnect().
  *
  * 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
  * --------
  *
  *    The state machine of the tree structures is controlled by 3 elements:

@@ -142,11 +146,15 @@
  *    the tree from it, the lock must be entered in RW_WRITER mode.
  *
  *    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.
  *
  *    3) There are 2 ways of getting a reference count: when a tree is
  *       connected and when a tree is looked up.

@@ -174,22 +182,18 @@
 
 uint32_t        smb_tree_connect_core(smb_request_t *);
 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 *);
 static void smb_tree_netinfo_init(smb_tree_t *tree, smb_netconnectinfo_t *);
 static void smb_tree_netinfo_fini(smb_netconnectinfo_t *);

@@ -250,10 +254,29 @@
 
         /* NB: name points into tcon->path - don't free it. */
         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);
                 break;
         case STYPE_IPC:

@@ -265,57 +288,65 @@
         default:
                 status = NT_STATUS_BAD_DEVICE_TYPE;
                 break;
         }
 
+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);
 }
 
 /*
  * 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);
 
         mutex_enter(&tree->t_mutex);
         ASSERT(tree->t_refcnt);
 
-        if (smb_tree_is_connected_locked(tree)) {
+        if (!smb_tree_is_connected_locked(tree)) {
+                mutex_exit(&tree->t_mutex);
+                return;
+        }
+
                 /*
                  * Indicate that the disconnect process has started.
                  */
                 tree->t_state = SMB_TREE_STATE_DISCONNECTING;
                 mutex_exit(&tree->t_mutex);
 
-                if (do_exec) {
                         /*
                          * The files opened under this tree are closed.
                          */
-                        smb_ofile_close_all(tree);
+        smb_ofile_close_all(tree, 0);
                         /*
                          * 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);
-        }
-
-        mutex_exit(&tree->t_mutex);
-
-        if (do_exec && (tree->t_state == SMB_TREE_STATE_DISCONNECTED) &&
-            (tree->t_execflags & SMB_EXEC_UNMAP)) {
-
+        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);
         }
 }
 

@@ -369,48 +400,37 @@
 smb_tree_release(
     smb_tree_t          *tree)
 {
         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.
  */
 void
 smb_tree_close_pid(

@@ -418,11 +438,11 @@
     uint32_t            pid)
 {
         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);
 }
 
 /*
  * Check whether or not a tree supports the features identified by flags.

@@ -443,34 +463,32 @@
  * This function should be called with a hold on the tree.
  */
 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);
 
+        of = smb_llist_head(of_list);
+        while (of) {
+                if (smb_ofile_hold(of)) {
                 rc = smb_ofile_enum(of, svcenum);
-                if (rc != 0) {
                         smb_ofile_release(of);
+                }
+                if (rc != 0)
                         break;
+                of = smb_llist_next(of_list, of);
                 }
 
-                next = smb_tree_get_ofile(tree, of);
-                smb_ofile_release(of);
-                of = next;
-        }
+        smb_llist_exit(of_list);
 
         return (rc);
 }
 
 /*

@@ -482,10 +500,15 @@
         smb_ofile_t     *of;
 
         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);
 
         if (smb_ofile_disallow_fclose(of)) {
                 smb_ofile_release(of);

@@ -637,28 +660,30 @@
         }
 
         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.
  */
 uint32_t
 smb_tree_connect_disk(smb_request_t *sr, smb_arg_tcon_t *tcon)
 {
         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;
         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);
 
         if (service != NULL &&

@@ -669,34 +694,37 @@
         }
 
         /*
          * 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;
 
         switch (si->shr_flags & SMB_SHRF_CSC_MASK) {

@@ -729,22 +757,30 @@
         if (!smb_shortnames)
                 sr->arg.tcon.optional_support |= SMB_UNIQUE_FILE_NAME;
 
         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);
 
         if (tree->t_execflags & SMB_EXEC_MAP) {
                 smb_tree_set_execinfo(tree, &execinfo, SMB_EXEC_MAP);
 
                 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);
                 }
         }
 

@@ -872,11 +908,11 @@
 }
 
 /*
  * 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)
 {
         smb_session_t   *session = sr->session;
         smb_tree_t      *tree;

@@ -916,11 +952,11 @@
                 kmem_cache_free(smb_cache_tree, tree);
                 return (NULL);
         }
 
         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));
 
         (void) strlcpy(tree->t_sharename, si->shr_name,

@@ -962,32 +998,46 @@
  * empty.
  *
  * 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;
         smb_tree_t      *tree = (smb_tree_t *)arg;
 
         SMB_TREE_VALID(tree);
         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);
         smb_idpool_free(&session->s_tid_pool, tree->t_tid);
         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);
 
         mutex_destroy(&tree->t_mutex);
         smb_llist_destructor(&tree->t_ofile_list);

@@ -1013,11 +1063,11 @@
                 return (B_TRUE);
 
         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);
 
         default:
                 ASSERT(0);

@@ -1024,31 +1074,10 @@
                 return (B_FALSE);
         }
 }
 
 /*
- * 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
  * (\\server\share) or simply the share name.  We validate the UNC
  * format but we don't look at the server name.

@@ -1084,19 +1113,33 @@
  */
 static int
 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);
 
         if (getvfs(&vfsp->vfs_fsid) != vfsp)
                 return (ESTALE);
 
         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);
 }
 
 /*

@@ -1145,10 +1188,14 @@
                 size_t          mt_namelen;
                 uint32_t        mt_flags;
         } 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 },
                 { "tmpfs",  5,  SMB_TREE_NO_EXPORT }
         };

@@ -1166,10 +1213,16 @@
                 flags |= SMB_TREE_CATIA;
 
         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;
         }
 

@@ -1199,10 +1252,17 @@
                 mtype = &smb_mtype[i];
                 if (strncasecmp(name, mtype->mt_name, mtype->mt_namelen) == 0)
                         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);
 
         if (vfs_has_feature(vfsp, VFSFT_XVATTR))
                 flags |= SMB_TREE_XVATTR;

@@ -1318,113 +1378,45 @@
         mutex_exit(&tree->t_mutex);
         return (rb);
 }
 
 /*
- * Get the next open ofile in the list.  A reference is taken on
- * the ofile, which can be released later with smb_ofile_release().
+ * smb_tree_close_odirs
  *
- * 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.
+ * Close all open odirs in the tree's list which were opened by
+ * the process identified by pid.
+ * If pid is zero, close all open odirs in the tree's list.
  */
-static smb_ofile_t *
-smb_tree_get_ofile(smb_tree_t *tree, smb_ofile_t *of)
+static void
+smb_tree_close_odirs(smb_tree_t *tree, uint32_t pid)
 {
-        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;
+        smb_odir_t      *od;
 
         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);
-        }
+        for (od = smb_llist_head(od_list);
+            od != NULL;
+            od = smb_llist_next(od_list, od)) {
 
-        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
- * the process identified by pid.
- * 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_odir_t *od, *next_od;
-
-        ASSERT(tree);
-        ASSERT(tree->t_magic == SMB_TREE_MAGIC);
-
-        od = smb_tree_get_odir(tree, NULL);
-        while (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))
+                if (pid != 0 && od->d_opened_by_pid != pid)
+                        continue;
+
+                if (smb_odir_hold(od)) {
                         smb_odir_close(od);
                 smb_odir_release(od);
-
-                od = next_od;
         }
+        }
+
+        smb_llist_exit(od_list);
 }
 
 static void
 smb_tree_set_execinfo(smb_tree_t *tree, smb_shr_execinfo_t *exec,
     int exec_type)