Print this page
NEX-17669 SMB shares missing after CA share import
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
NEX-16519 Panic while running IOmeter to a pool through an SMB share
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-17095 illumos 8935 SMB ioctl fixes incomplete
8935 SMB ioctl fixes incomplete
Reviewed by: Alex Wilson <alex.wilson@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Rui Loura <rui.loura@joyent.com>
Reviewed by: Garrett D'Amore <garrett@damore.org>
Reviewed by: Dominik Hassler <hasslerd@gmx.li>
Approved by: Garrett D'Amore <garrett@damore.org>
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-15581 SMB keep-alive feature is just noise
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-15581 SMB keep-alive feature is just noise
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-10019 SMB server min_protocol setting
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@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-6096 Enable compile warnings re. parentheses in smbsrv
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Jean McCormack <jean.mccormack@nexenta.com>
NEX-6041 Should pass the smbtorture lock tests
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-4541 SMB server listener stops after a SYN-ACK flood (fksmbd noise)
NEX-4541 SMB server listener stops after a SYN-ACK flood (lint fix)
NEX-5983 remove post-merge cruft in usr/src/uts/intel/io/vmxnet3s
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-4541 SMB server listener stops after a SYN-ACK flood
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-3553 SMB2/3 durable handles
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-3776 SMB should handle PreviousSessionID
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-5537 Want reference counts for users, trees...
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-5330 SMB server should combine TCP+NBT session lists
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-4811 SMB needs to export a header for kstats
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Jeffry Molanus <jeffry.molanus@nexenta.com>
NEX-2522 svcadm disable network/smb/server may hang
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-4083 Upstream changes from illumos 5917 and 5995
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-3738 Should support SMB2_CAP_LARGE_MTU
Reviewed by: Alek Pinchuk <alek@nexenta.com>
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-3620 need upstream cleanups for smbsrv
Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com>
NEX-3611 CLONE NEX-3550 Replace smb2_enable with max_protocol
Reviewed by: Yuri Pankov <Yuri.Pankov@nexenta.com>
NEX-2485 SMB authentication flood handled poorly
SUP-866 smbd lwps stuck in libsocket recv() for no apparent reason
NEX-1050 enable_smb2 should be smb2_enable
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-39 Use AF_UNIX pipes for RPC (fix a leak)
SMB-75 smb_session_timers way too frequent
SMB-74 Process oplock breaks as session requests
SMB-69 read-raw, write-raw are dead code
SMB-50 User-mode SMB server (oops)
SMB-56 extended security NTLMSSP, inbound
SMB-39 Use AF_UNIX pipes for RPC
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-64 smbsrv workers run at excessively high priority
SMB-63 taskq_create_proc ... TQ_DYNAMIC puts tasks in p0
re #11974 CIFS Share - Tree connect fails from Windows 7 Clients
re #11215 rb3676 sesctl to SGI JBOD hangs in biowait() with a command stuck in mptsas driver
re #10734 NT Trans. Notify returning too quickly
re #9812 rb3153 System panic'd with assertion failed: sl->sl_count == 0 in smb_slist_destructor after hostname change, smbd restart and attempts to rejoin active directory domain
re #6813 rb1757 port 2976 Child folder visibility through shares
re #6812 rb1753 backport illumos 1604 smbd print_enable doesn't really work
re #6811 rb1752 backport illumos 1603 smbsrv raw mode is ill-advised

@@ -18,12 +18,12 @@
  *
  * CDDL HEADER END
  */
 /*
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2017 by Delphix. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
  * General Structures Layout
  * -------------------------

@@ -217,17 +217,10 @@
 #include <smbsrv/smb_fsops.h>
 #include <smbsrv/smb_share.h>
 #include <smbsrv/smb_door.h>
 #include <smbsrv/smb_kstat.h>
 
-extern void smb_reply_notify_change_request(smb_request_t *);
-
-typedef struct {
-        smb_listener_daemon_t   *ra_listener;
-        smb_session_t           *ra_session;
-} smb_receiver_arg_t;
-
 static void smb_server_kstat_init(smb_server_t *);
 static void smb_server_kstat_fini(smb_server_t *);
 static void smb_server_timers(smb_thread_t *, void *);
 static void smb_server_store_cfg(smb_server_t *, smb_ioc_cfg_t *);
 static void smb_server_shutdown(smb_server_t *);

@@ -234,16 +227,16 @@
 static int smb_server_fsop_start(smb_server_t *);
 static void smb_server_fsop_stop(smb_server_t *);
 static void smb_event_cancel(smb_server_t *, uint32_t);
 static uint32_t smb_event_alloc_txid(void);
 
-static void smb_server_disconnect_share(smb_llist_t *, const char *);
-static void smb_server_enum_users(smb_llist_t *, smb_svcenum_t *);
-static void smb_server_enum_trees(smb_llist_t *, smb_svcenum_t *);
-static int smb_server_session_disconnect(smb_llist_t *, const char *,
+static void smb_server_disconnect_share(smb_server_t *, const char *);
+static void smb_server_enum_users(smb_server_t *, smb_svcenum_t *);
+static void smb_server_enum_trees(smb_server_t *, smb_svcenum_t *);
+static int smb_server_session_disconnect(smb_server_t *, const char *,
     const char *);
-static int smb_server_fclose(smb_llist_t *, uint32_t);
+static int smb_server_fclose(smb_server_t *, uint32_t);
 static int smb_server_kstat_update(kstat_t *, int);
 static int smb_server_legacy_kstat_update(kstat_t *, int);
 static void smb_server_listener_init(smb_server_t *, smb_listener_daemon_t *,
     char *, in_port_t, int);
 static void smb_server_listener_destroy(smb_listener_daemon_t *);

@@ -250,16 +243,29 @@
 static int smb_server_listener_start(smb_listener_daemon_t *);
 static void smb_server_listener_stop(smb_listener_daemon_t *);
 static void smb_server_listener(smb_thread_t *, void *);
 static void smb_server_receiver(void *);
 static void smb_server_create_session(smb_listener_daemon_t *, ksocket_t);
-static void smb_server_destroy_session(smb_listener_daemon_t *,
-    smb_session_t *);
+static void smb_server_destroy_session(smb_session_t *);
 static uint16_t smb_spool_get_fid(smb_server_t *);
 static boolean_t smb_spool_lookup_doc_byfid(smb_server_t *, uint16_t,
     smb_kspooldoc_t *);
 
+/*
+ * How many "buckets" should our hash tables use?  On a "real" server,
+ * make them much larger than the number of CPUs we're likely to have.
+ * On "fksmbd" make it smaller so dtrace logs are shorter.
+ * These must be powers of two.
+ */
+#ifdef  _KERNEL
+#define DEFAULT_HASH_NBUCKETS   256     /* real server */
+#else
+#define DEFAULT_HASH_NBUCKETS   16      /* for "fksmbd" */
+#endif
+uint32_t SMB_OFILE_HASH_NBUCKETS = DEFAULT_HASH_NBUCKETS;
+uint32_t SMB_LEASE_HASH_NBUCKETS = DEFAULT_HASH_NBUCKETS;
+
 int smb_event_debug = 0;
 
 static smb_llist_t      smb_servers;
 
 kmem_cache_t            *smb_cache_request;

@@ -268,10 +274,11 @@
 kmem_cache_t            *smb_cache_tree;
 kmem_cache_t            *smb_cache_ofile;
 kmem_cache_t            *smb_cache_odir;
 kmem_cache_t            *smb_cache_opipe;
 kmem_cache_t            *smb_cache_event;
+kmem_cache_t            *smb_cache_lock;
 
 /*
  * *****************************************************************************
  * **************** Functions called from the device interface *****************
  * *****************************************************************************

@@ -306,10 +313,11 @@
 
         smb_kshare_g_init();
         smb_codepage_init();
         smb_mbc_init();         /* smb_mbc_cache */
         smb_node_init();        /* smb_node_cache, lists */
+        smb2_lease_init();
 
         smb_cache_request = kmem_cache_create("smb_request_cache",
             sizeof (smb_request_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
         smb_cache_session = kmem_cache_create("smb_session_cache",
             sizeof (smb_session_t), 8, NULL, NULL, NULL, NULL, NULL, 0);

@@ -323,10 +331,12 @@
             sizeof (smb_odir_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
         smb_cache_opipe = kmem_cache_create("smb_opipe_cache",
             sizeof (smb_opipe_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
         smb_cache_event = kmem_cache_create("smb_event_cache",
             sizeof (smb_event_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
+        smb_cache_lock = kmem_cache_create("smb_lock_cache",
+            sizeof (smb_lock_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
 
         smb_llist_init();
         smb_llist_constructor(&smb_servers, sizeof (smb_server_t),
             offsetof(smb_server_t, sv_lnd));
 

@@ -358,11 +368,13 @@
         kmem_cache_destroy(smb_cache_tree);
         kmem_cache_destroy(smb_cache_ofile);
         kmem_cache_destroy(smb_cache_odir);
         kmem_cache_destroy(smb_cache_opipe);
         kmem_cache_destroy(smb_cache_event);
+        kmem_cache_destroy(smb_cache_lock);
 
+        smb2_lease_fini();
         smb_node_fini();
         smb_mbc_fini();
         smb_codepage_fini();
         smb_kshare_g_fini();
 

@@ -406,10 +418,19 @@
 
         mutex_init(&sv->sv_mutex, NULL, MUTEX_DEFAULT, NULL);
         cv_init(&sv->sv_cv, NULL, CV_DEFAULT, NULL);
         cv_init(&sv->sp_info.sp_cv, NULL, CV_DEFAULT, NULL);
 
+        sv->sv_persistid_ht = smb_hash_create(sizeof (smb_ofile_t),
+            offsetof(smb_ofile_t, f_dh_lnd), SMB_OFILE_HASH_NBUCKETS);
+
+        sv->sv_lease_ht = smb_hash_create(sizeof (smb_lease_t),
+            offsetof(smb_lease_t, ls_lnd), SMB_LEASE_HASH_NBUCKETS);
+
+        smb_llist_constructor(&sv->sv_session_list, sizeof (smb_session_t),
+            offsetof(smb_session_t, s_lnd));
+
         smb_llist_constructor(&sv->sv_event_list, sizeof (smb_event_t),
             offsetof(smb_event_t, se_lnd));
 
         smb_llist_constructor(&sv->sp_info.sp_list, sizeof (smb_kspooldoc_t),
             offsetof(smb_kspooldoc_t, sd_lnd));

@@ -450,19 +471,13 @@
  *
  * This function will delete the server passed in. It will make sure that all
  * activity associated that server has ceased before destroying it.
  */
 int
-smb_server_delete(void)
+smb_server_delete(smb_server_t  *sv)
 {
-        smb_server_t    *sv;
-        int             rc;
 
-        rc = smb_server_lookup(&sv);
-        if (rc != 0)
-                return (rc);
-
         mutex_enter(&sv->sv_mutex);
         switch (sv->sv_state) {
         case SMB_SERVER_STATE_RUNNING:
                 sv->sv_state = SMB_SERVER_STATE_STOPPING;
                 mutex_exit(&sv->sv_mutex);

@@ -506,10 +521,11 @@
         rw_destroy(&sv->sv_cfg_lock);
         smb_server_kstat_fini(sv);
         smb_kshare_fini(sv);
         smb_kdoor_fini(sv);
         smb_llist_destructor(&sv->sv_event_list);
+        smb_llist_destructor(&sv->sv_session_list);
 
         kmem_free(sv->sv_disp_stats1,
             SMB_COM_NUM * sizeof (smb_disp_stats_t));
 
         kmem_free(sv->sv_disp_stats2,

@@ -517,10 +533,12 @@
 
         smb_srqueue_destroy(&sv->sv_srqueue);
         smb_thread_destroy(&sv->si_thread_timers);
 
         mutex_destroy(&sv->sv_mutex);
+        smb_hash_destroy(sv->sv_lease_ht);
+        smb_hash_destroy(sv->sv_persistid_ht);
         cv_destroy(&sv->sv_cv);
         sv->sv_magic = 0;
         kmem_free(sv, sizeof (smb_server_t));
 
         return (0);

@@ -582,10 +600,11 @@
 smb_server_start(smb_ioc_start_t *ioc)
 {
         int             rc = 0;
         int             family;
         smb_server_t    *sv;
+        cred_t          *ucr;
 
         rc = smb_server_lookup(&sv);
         if (rc)
                 return (rc);
 

@@ -594,10 +613,35 @@
         case SMB_SERVER_STATE_CONFIGURED:
 
                 if ((rc = smb_server_fsop_start(sv)) != 0)
                         break;
 
+                /*
+                 * Note: smb_kshare_start needs sv_session.
+                 */
+                sv->sv_session = smb_session_create(NULL, 0, sv, 0);
+                if (sv->sv_session == NULL) {
+                        rc = ENOMEM;
+                        break;
+                }
+
+                /*
+                 * Create a logon on the server session,
+                 * used when importing CA shares.
+                 */
+                sv->sv_rootuser = smb_user_new(sv->sv_session);
+                ucr = smb_kcred_create();
+                rc = smb_user_logon(sv->sv_rootuser, ucr, "", "root",
+                    SMB_USER_FLAG_ADMIN, 0, 0);
+                crfree(ucr);
+                ucr = NULL;
+                if (rc != 0) {
+                        cmn_err(CE_NOTE, "smb_server_start: "
+                            "failed to create root user");
+                        break;
+                }
+
                 if ((rc = smb_kshare_start(sv)) != 0)
                         break;
 
                 /*
                  * NB: the proc passed here has to be a "system" one.

@@ -611,32 +655,31 @@
                 sv->sv_receiver_pool = taskq_create_proc("smb_receivers",
                     sv->sv_cfg.skc_maxconnections, smbsrv_receive_pri,
                     sv->sv_cfg.skc_maxconnections, INT_MAX,
                     curzone->zone_zsched, TASKQ_DYNAMIC);
 
-                sv->sv_session = smb_session_create(NULL, 0, sv, 0);
-
-                if (sv->sv_worker_pool == NULL || sv->sv_session == NULL) {
+                if (sv->sv_worker_pool == NULL ||
+                    sv->sv_receiver_pool == NULL) {
                         rc = ENOMEM;
                         break;
                 }
 
 #ifdef  _KERNEL
                 ASSERT(sv->sv_lmshrd == NULL);
                 sv->sv_lmshrd = smb_kshare_door_init(ioc->lmshrd);
                 if (sv->sv_lmshrd == NULL)
                         break;
-                if (rc = smb_kdoor_open(sv, ioc->udoor)) {
+                if ((rc = smb_kdoor_open(sv, ioc->udoor)) != 0) {
                         cmn_err(CE_WARN, "Cannot open smbd door");
                         break;
                 }
 #else   /* _KERNEL */
                 /* Fake kernel does not use the kshare_door */
                 fksmb_kdoor_open(sv, ioc->udoor_func);
 #endif  /* _KERNEL */
 
-                if (rc = smb_thread_start(&sv->si_thread_timers))
+                if ((rc = smb_thread_start(&sv->si_thread_timers)) != 0)
                         break;
 
                 family = AF_INET;
                 smb_server_listener_init(sv, &sv->sv_nbt_daemon,
                     "smb_nbt_listener", IPPORT_NETBIOS_SSN, family);

@@ -862,21 +905,15 @@
         svcenum->se_bused = 0;
         svcenum->se_nitems = 0;
 
         switch (svcenum->se_type) {
         case SMB_SVCENUM_TYPE_USER:
-                smb_server_enum_users(&sv->sv_nbt_daemon.ld_session_list,
-                    svcenum);
-                smb_server_enum_users(&sv->sv_tcp_daemon.ld_session_list,
-                    svcenum);
+                smb_server_enum_users(sv, svcenum);
                 break;
         case SMB_SVCENUM_TYPE_TREE:
         case SMB_SVCENUM_TYPE_FILE:
-                smb_server_enum_trees(&sv->sv_nbt_daemon.ld_session_list,
-                    svcenum);
-                smb_server_enum_trees(&sv->sv_tcp_daemon.ld_session_list,
-                    svcenum);
+                smb_server_enum_trees(sv, svcenum);
                 break;
         default:
                 rc = EINVAL;
         }
 

@@ -888,28 +925,22 @@
  * Look for sessions to disconnect by client and user name.
  */
 int
 smb_server_session_close(smb_ioc_session_t *ioc)
 {
-        smb_llist_t     *ll;
         smb_server_t    *sv;
-        int             nbt_cnt;
-        int             tcp_cnt;
+        int             cnt;
         int             rc;
 
         if ((rc = smb_server_lookup(&sv)) != 0)
                 return (rc);
 
-        ll = &sv->sv_nbt_daemon.ld_session_list;
-        nbt_cnt = smb_server_session_disconnect(ll, ioc->client, ioc->username);
+        cnt = smb_server_session_disconnect(sv, ioc->client, ioc->username);
 
-        ll = &sv->sv_tcp_daemon.ld_session_list;
-        tcp_cnt = smb_server_session_disconnect(ll, ioc->client, ioc->username);
-
         smb_server_release(sv);
 
-        if ((nbt_cnt == 0) && (tcp_cnt == 0))
+        if (cnt == 0)
                 return (ENOENT);
         return (0);
 }
 
 /*

@@ -917,25 +948,18 @@
  */
 int
 smb_server_file_close(smb_ioc_fileid_t *ioc)
 {
         uint32_t        uniqid = ioc->uniqid;
-        smb_llist_t     *ll;
         smb_server_t    *sv;
         int             rc;
 
         if ((rc = smb_server_lookup(&sv)) != 0)
                 return (rc);
 
-        ll = &sv->sv_nbt_daemon.ld_session_list;
-        rc = smb_server_fclose(ll, uniqid);
+        rc = smb_server_fclose(sv, uniqid);
 
-        if (rc == ENOENT) {
-                ll = &sv->sv_tcp_daemon.ld_session_list;
-                rc = smb_server_fclose(ll, uniqid);
-        }
-
         smb_server_release(sv);
         return (rc);
 }
 
 /*

@@ -945,28 +969,26 @@
 uint32_t
 smb_server_get_session_count(smb_server_t *sv)
 {
         uint32_t        counter = 0;
 
-        counter = smb_llist_get_count(&sv->sv_nbt_daemon.ld_session_list);
-        counter += smb_llist_get_count(&sv->sv_tcp_daemon.ld_session_list);
+        counter = smb_llist_get_count(&sv->sv_session_list);
 
         return (counter);
 }
 
 /*
- * Gets the vnode of the specified share path.
- *
- * A hold on the returned vnode pointer is taken so the caller
- * must call VN_RELE.
+ * Gets the smb_node of the specified share path.
+ * Node is returned held (caller must rele.)
  */
 int
-smb_server_sharevp(smb_server_t *sv, const char *shr_path, vnode_t **vp)
+smb_server_share_lookup(smb_server_t *sv, const char *shr_path,
+    smb_node_t **nodepp)
 {
         smb_request_t   *sr;
         smb_node_t      *fnode = NULL;
-        smb_node_t      *dnode;
+        smb_node_t      *dnode = NULL;
         char            last_comp[MAXNAMELEN];
         int             rc = 0;
 
         ASSERT(shr_path);
 

@@ -979,11 +1001,11 @@
                 return (ENOTACTIVE);
         }
         mutex_exit(&sv->sv_mutex);
 
         if ((sr = smb_request_alloc(sv->sv_session, 0)) == NULL) {
-                return (ENOMEM);
+                return (ENOTCONN);
         }
         sr->user_cr = zone_kcred();
 
         rc = smb_pathname_reduce(sr, sr->user_cr, shr_path,
             NULL, NULL, &dnode, last_comp);

@@ -999,15 +1021,12 @@
         if (rc != 0)
                 return (rc);
 
         ASSERT(fnode->vp && fnode->vp->v_vfsp);
 
-        VN_HOLD(fnode->vp);
-        *vp = fnode->vp;
+        *nodepp = fnode;
 
-        smb_node_release(fnode);
-
         return (0);
 }
 
 #ifdef  _KERNEL
 /*

@@ -1044,11 +1063,10 @@
 
 int
 smb_server_unshare(const char *sharename)
 {
         smb_server_t    *sv;
-        smb_llist_t     *ll;
         int             rc;
 
         if ((rc = smb_server_lookup(&sv)))
                 return (rc);
 

@@ -1062,43 +1080,42 @@
                 smb_server_release(sv);
                 return (ENOTACTIVE);
         }
         mutex_exit(&sv->sv_mutex);
 
-        ll = &sv->sv_nbt_daemon.ld_session_list;
-        smb_server_disconnect_share(ll, sharename);
+        smb_server_disconnect_share(sv, sharename);
 
-        ll = &sv->sv_tcp_daemon.ld_session_list;
-        smb_server_disconnect_share(ll, sharename);
-
         smb_server_release(sv);
         return (0);
 }
 
 /*
  * Disconnect the specified share.
  * Typically called when a share has been removed.
  */
 static void
-smb_server_disconnect_share(smb_llist_t *ll, const char *sharename)
+smb_server_disconnect_share(smb_server_t *sv, const char *sharename)
 {
+        smb_llist_t     *ll;
         smb_session_t   *session;
 
+        ll = &sv->sv_session_list;
         smb_llist_enter(ll, RW_READER);
 
         session = smb_llist_head(ll);
         while (session) {
                 SMB_SESSION_VALID(session);
                 smb_rwx_rwenter(&session->s_lock, RW_READER);
                 switch (session->s_state) {
                 case SMB_SESSION_STATE_NEGOTIATED:
+                        smb_rwx_rwexit(&session->s_lock);
                         smb_session_disconnect_share(session, sharename);
                         break;
                 default:
+                        smb_rwx_rwexit(&session->s_lock);
                         break;
                 }
-                smb_rwx_rwexit(&session->s_lock);
                 session = smb_llist_next(ll, session);
         }
 
         smb_llist_exit(ll);
 }

@@ -1239,16 +1256,17 @@
         smb_server_t    *sv = (smb_server_t *)arg;
 
         ASSERT(sv != NULL);
 
         /*
-         * This just kills old inactive sessions.  No urgency.
-         * The session code expects one call per minute.
+         * This kills old inactive sessions and expired durable
+         * handles. The session code expects one call per minute.
          */
         while (smb_thread_continue_timedwait(thread, 60 /* Seconds */)) {
-                smb_session_timers(&sv->sv_nbt_daemon.ld_session_list);
-                smb_session_timers(&sv->sv_tcp_daemon.ld_session_list);
+                if (sv->sv_cfg.skc_keepalive != 0)
+                        smb_session_timers(sv);
+                smb2_durable_timers(sv);
         }
 }
 
 /*
  * smb_server_kstat_init

@@ -1321,10 +1339,16 @@
                 smb2_dispatch_stats_fini(sv);
         }
 }
 
 /*
+ * Verify the defines in smb_kstat.h used by ks_reqs1 ks_reqs2
+ */
+CTASSERT(SMBSRV_KS_NREQS1 == SMB_COM_NUM);
+CTASSERT(SMBSRV_KS_NREQS2 == SMB2__NCMDS);
+
+/*
  * smb_server_kstat_update
  */
 static int
 smb_server_kstat_update(kstat_t *ksp, int rw)
 {

@@ -1405,10 +1429,14 @@
  * smb_server_shutdown
  */
 static void
 smb_server_shutdown(smb_server_t *sv)
 {
+        smb_llist_t *sl = &sv->sv_session_list;
+        smb_session_t *session;
+        clock_t time;
+
         SMB_SERVER_VALID(sv);
 
         /*
          * Stop the listeners first, so we don't get any more
          * new work while we're trying to shut down.

@@ -1415,10 +1443,19 @@
          */
         smb_server_listener_stop(&sv->sv_nbt_daemon);
         smb_server_listener_stop(&sv->sv_tcp_daemon);
         smb_thread_stop(&sv->si_thread_timers);
 
+        /* Disconnect all of the sessions */
+        smb_llist_enter(sl, RW_READER);
+        session = smb_llist_head(sl);
+        while (session != NULL) {
+                smb_session_disconnect(session);
+                session = smb_llist_next(sl, session);
+        }
+        smb_llist_exit(sl);
+
         /*
          * Wake up any threads we might have blocked.
          * Must precede kdoor_close etc. because those will
          * wait for such threads to get out.
          */

@@ -1425,27 +1462,72 @@
         smb_event_cancel(sv, 0);
         smb_threshold_wake_all(&sv->sv_ssetup_ct);
         smb_threshold_wake_all(&sv->sv_tcon_ct);
         smb_threshold_wake_all(&sv->sv_opipe_ct);
 
+        /*
+         * Wait for the session list to empty.
+         * (cv_signal in smb_server_destroy_session)
+         *
+         * This should not take long, but if there are any leaked
+         * references to ofiles, trees, or users, there could be a
+         * session hanging around.  If that happens, the ll_count
+         * never gets to zero and we'll never get the sv_signal.
+         * Defend against that problem using timed wait, then
+         * complain if we find sessions left over and continue
+         * with shutdown in spite of any leaked sessions.
+         * That's better than a server that won't reboot.
+         */
+        time = SEC_TO_TICK(10) + ddi_get_lbolt();
+        mutex_enter(&sv->sv_mutex);
+        while (sv->sv_session_list.ll_count != 0) {
+                if (cv_timedwait(&sv->sv_cv, &sv->sv_mutex, time) < 0)
+                        break;
+        }
+        mutex_exit(&sv->sv_mutex);
+#ifdef  DEBUG
+        if (sv->sv_session_list.ll_count != 0) {
+                cmn_err(CE_NOTE, "shutdown leaked sessions");
+                debug_enter("shutdown leaked sessions");
+        }
+#endif
+
+        /*
+         * Clean out any durable handles.  After this we should
+         * have no ofiles remaining (and no more oplock breaks).
+         */
+        smb2_dh_shutdown(sv);
+
         smb_kdoor_close(sv);
 #ifdef  _KERNEL
         smb_kshare_door_fini(sv->sv_lmshrd);
 #endif  /* _KERNEL */
         sv->sv_lmshrd = NULL;
 
         smb_export_stop(sv);
+        smb_kshare_stop(sv);
 
-        if (sv->sv_session != NULL) {
                 /*
-                 * smb_kshare_export may have a request on here.
-                 * Normal sessions do this in smb_session_cancel()
-                 * but this is a "fake" session used only for the
-                 * requests used by the kshare thread(s).
+         * Both kshare and the oplock break sub-systems may have
+         * taskq jobs on the spcial "server" session, until we've
+         * closed all ofiles and stopped the kshare exporter.
+         * Now it's safe to destroy the server session, but first
+         * wait for any requests on it to finish.  Note that for
+         * normal sessions, this happens in smb_session_cancel,
+         * but that's not called for the server session.
                  */
+        if (sv->sv_rootuser != NULL) {
+                smb_user_logoff(sv->sv_rootuser);
+                smb_user_release(sv->sv_rootuser);
+                sv->sv_rootuser = NULL;
+        }
+        if (sv->sv_session != NULL) {
                 smb_slist_wait_for_empty(&sv->sv_session->s_req_list);
 
+                /* Just in case import left users and trees */
+                smb_session_logoff(sv->sv_session);
+
                 smb_session_delete(sv->sv_session);
                 sv->sv_session = NULL;
         }
 
         if (sv->sv_receiver_pool != NULL) {

@@ -1456,11 +1538,10 @@
         if (sv->sv_worker_pool != NULL) {
                 taskq_destroy(sv->sv_worker_pool);
                 sv->sv_worker_pool = NULL;
         }
 
-        smb_kshare_stop(sv);
         smb_server_fsop_stop(sv);
 }
 
 /*
  * smb_server_listener_init

@@ -1492,12 +1573,10 @@
                 ld->ld_sin6.sin6_port = htons(port);
                 (void) memset(&ld->ld_sin6.sin6_addr.s6_addr, 0,
                     sizeof (ld->ld_sin6.sin6_addr.s6_addr));
         }
 
-        smb_llist_constructor(&ld->ld_session_list, sizeof (smb_session_t),
-            offsetof(smb_session_t, s_lnd));
         smb_thread_init(&ld->ld_thread, name, smb_server_listener, ld,
             smbsrv_listen_pri);
         ld->ld_magic = SMB_LISTENER_MAGIC;
 }
 

@@ -1517,11 +1596,10 @@
                 return;
 
         SMB_LISTENER_VALID(ld);
         ASSERT(ld->ld_so == NULL);
         smb_thread_destroy(&ld->ld_thread);
-        smb_llist_destructor(&ld->ld_session_list);
         ld->ld_magic = 0;
 }
 
 /*
  * smb_server_listener_start

@@ -1615,11 +1693,10 @@
 static void
 smb_server_listener(smb_thread_t *thread, void *arg)
 {
         _NOTE(ARGUNUSED(thread))
         smb_listener_daemon_t   *ld;
-        smb_session_t           *session;
         ksocket_t               s_so;
         int                     on;
         int                     txbuf_size;
 
         ld = (smb_listener_daemon_t *)arg;

@@ -1626,12 +1703,28 @@
 
         SMB_LISTENER_VALID(ld);
 
         DTRACE_PROBE1(so__wait__accept, struct sonode *, ld->ld_so);
 
-        while (ksocket_accept(ld->ld_so, NULL, NULL, &s_so, CRED())
-            == 0) {
+        for (;;) {
+                int ret = ksocket_accept(ld->ld_so, NULL, NULL, &s_so, CRED());
+
+                switch (ret) {
+                case 0:
+                        break;
+                case ECONNABORTED:
+                        continue;
+                case EINTR:
+                case EBADF:     /* libfakekernel */
+                        goto out;
+                default:
+                        cmn_err(CE_WARN,
+                            "smb_server_listener: ksocket_accept(%d)",
+                            ret);
+                        goto out;
+                }
+
                 DTRACE_PROBE1(so__accept, struct sonode *, s_so);
 
                 on = 1;
                 (void) ksocket_setsockopt(s_so, IPPROTO_TCP, TCP_NODELAY,
                     &on, sizeof (on), CRED());

@@ -1647,37 +1740,32 @@
                 /*
                  * Create a session for this connection.
                  */
                 smb_server_create_session(ld, s_so);
         }
-        /* Disconnect all the sessions this listener created. */
-        smb_llist_enter(&ld->ld_session_list, RW_READER);
-        session = smb_llist_head(&ld->ld_session_list);
-        while (session != NULL) {
-                smb_session_disconnect(session);
-                session = smb_llist_next(&ld->ld_session_list, session);
-        }
-        smb_llist_exit(&ld->ld_session_list);
+out:
         ksocket_rele(ld->ld_so);
 }
 
 /*
  * smb_server_receiver
  *
  * Entry point of the receiver threads.
+ * Also does cleanup when socket disconnected.
  */
 static void
 smb_server_receiver(void *arg)
 {
-        smb_listener_daemon_t   *ld;
         smb_session_t           *session;
 
-        ld = ((smb_receiver_arg_t *)arg)->ra_listener;
-        session = ((smb_receiver_arg_t *)arg)->ra_session;
-        smb_mem_free(arg);
+        session = (smb_session_t *)arg;
+
+        /* We stay in here until socket disconnect. */
         smb_session_receiver(session);
-        smb_server_destroy_session(ld, session);
+
+        ASSERT(session->s_state == SMB_SESSION_STATE_SHUTDOWN);
+        smb_server_destroy_session(session);
 }
 
 /*
  * smb_server_lookup
  *

@@ -1736,12 +1824,13 @@
 
 /*
  * Enumerate the users associated with a session list.
  */
 static void
-smb_server_enum_users(smb_llist_t *ll, smb_svcenum_t *svcenum)
+smb_server_enum_users(smb_server_t *sv, smb_svcenum_t *svcenum)
 {
+        smb_llist_t     *ll = &sv->sv_session_list;
         smb_session_t   *sn;
         smb_llist_t     *ulist;
         smb_user_t      *user;
         int             rc = 0;
 

@@ -1778,12 +1867,13 @@
 
 /*
  * Enumerate the trees/files associated with a session list.
  */
 static void
-smb_server_enum_trees(smb_llist_t *ll, smb_svcenum_t *svcenum)
+smb_server_enum_trees(smb_server_t *sv, smb_svcenum_t *svcenum)
 {
+        smb_llist_t     *ll = &sv->sv_session_list;
         smb_session_t   *sn;
         smb_llist_t     *tlist;
         smb_tree_t      *tree;
         int             rc = 0;
 

@@ -1821,58 +1911,48 @@
 /*
  * Disconnect sessions associated with the specified client and username.
  * Empty strings are treated as wildcards.
  */
 static int
-smb_server_session_disconnect(smb_llist_t *ll,
+smb_server_session_disconnect(smb_server_t *sv,
     const char *client, const char *name)
 {
+        smb_llist_t     *ll = &sv->sv_session_list;
         smb_session_t   *sn;
         smb_llist_t     *ulist;
         smb_user_t      *user;
-        boolean_t       match;
         int             count = 0;
 
         smb_llist_enter(ll, RW_READER);
-        sn = smb_llist_head(ll);
 
-        while (sn != NULL) {
+        for (sn = smb_llist_head(ll);
+            sn != NULL;
+            sn = smb_llist_next(ll, sn)) {
                 SMB_SESSION_VALID(sn);
 
-                if ((*client != '\0') && (!smb_session_isclient(sn, client))) {
-                        sn = smb_llist_next(ll, sn);
+                if (*client != '\0' && !smb_session_isclient(sn, client))
                         continue;
-                }
 
                 ulist = &sn->s_user_list;
                 smb_llist_enter(ulist, RW_READER);
-                user = smb_llist_head(ulist);
 
-                while (user != NULL) {
-                        if (smb_user_hold(user)) {
-                                match = (*name == '\0');
-                                if (!match)
-                                        match = smb_user_namecmp(user, name);
+                for (user = smb_llist_head(ulist);
+                    user != NULL;
+                    user = smb_llist_next(ulist, user)) {
+                        SMB_USER_VALID(user);
 
-                                if (match) {
-                                        smb_llist_exit(ulist);
-                                        smb_user_logoff(user);
-                                        ++count;
-                                        smb_user_release(user);
-                                        smb_llist_enter(ulist, RW_READER);
-                                        user = smb_llist_head(ulist);
+                        if (*name != '\0' && !smb_user_namecmp(user, name))
                                         continue;
-                                }
 
+                        if (smb_user_hold(user)) {
+                                smb_user_logoff(user);
                                 smb_user_release(user);
+                                count++;
                         }
-
-                        user = smb_llist_next(ulist, user);
                 }
 
                 smb_llist_exit(ulist);
-                sn = smb_llist_next(ll, sn);
         }
 
         smb_llist_exit(ll);
         return (count);
 }

@@ -1879,17 +1959,19 @@
 
 /*
  * Close a file by its unique id.
  */
 static int
-smb_server_fclose(smb_llist_t *ll, uint32_t uniqid)
+smb_server_fclose(smb_server_t *sv, uint32_t uniqid)
 {
+        smb_llist_t     *ll;
         smb_session_t   *sn;
         smb_llist_t     *tlist;
         smb_tree_t      *tree;
         int             rc = ENOENT;
 
+        ll = &sv->sv_session_list;
         smb_llist_enter(ll, RW_READER);
         sn = smb_llist_head(ll);
 
         while ((sn != NULL) && (rc == ENOENT)) {
                 SMB_SESSION_VALID(sn);

@@ -1912,21 +1994,81 @@
 
         smb_llist_exit(ll);
         return (rc);
 }
 
+/*
+ * This is used by SMB2 session setup to find a previous session,
+ * so it can force a logoff that we haven't noticed yet.
+ * This is not called frequently, so we just walk the list of
+ * connections searching for the user.
+ */
+smb_user_t *
+smb_server_lookup_ssnid(smb_server_t *sv, uint64_t ssnid)
+{
+        smb_llist_t *sl;
+        smb_session_t *sess;
+        smb_user_t *user = NULL;
+
+        sl = &sv->sv_session_list;
+        smb_llist_enter(sl, RW_READER);
+
+        for (sess = smb_llist_head(sl);
+            sess != NULL;
+            sess = smb_llist_next(sl, sess)) {
+
+                SMB_SESSION_VALID(sess);
+
+                if (sess->dialect < SMB_VERS_2_BASE)
+                        continue;
+
+                /*
+                 * Only look in sessions that are still active.
+                 * Avoid doing an smb_rwx_rwenter sess->s_lock
+                 * on every session here, but re-check below
+                 * with s_lock held.
+                 */
+                if (sess->s_state != SMB_SESSION_STATE_NEGOTIATED)
+                        continue;
+
+                user = smb_session_lookup_ssnid(sess, ssnid);
+                if (user != NULL) {
+                        break;
+                }
+        }
+
+        smb_llist_exit(sl);
+
+        /* The sess check is warning avoidance. */
+        if (user != NULL && sess != NULL) {
+                /*
+                 * Re-check the state with s_lock held.
+                 */
+                smb_rwx_rwenter(&sess->s_lock, RW_READER);
+                if (sess->s_state != SMB_SESSION_STATE_NEGOTIATED) {
+                        smb_user_release(user);
+                        user = NULL;
+                }
+                smb_rwx_rwexit(&sess->s_lock);
+        }
+
+        return (user);
+}
+
 /* See also: libsmb smb_kmod_setcfg */
 static void
 smb_server_store_cfg(smb_server_t *sv, smb_ioc_cfg_t *ioc)
 {
         if (ioc->maxconnections == 0)
                 ioc->maxconnections = 0xFFFFFFFF;
 
-        smb_session_correct_keep_alive_values(
-            &sv->sv_nbt_daemon.ld_session_list, ioc->keepalive);
-        smb_session_correct_keep_alive_values(
-            &sv->sv_tcp_daemon.ld_session_list, ioc->keepalive);
+        if (ioc->encrypt == SMB_CONFIG_REQUIRED &&
+            ioc->max_protocol < SMB_VERS_3_0) {
+                cmn_err(CE_WARN, "Server set to require encryption; "
+                    "forcing max_protocol to 3.0");
+                ioc->max_protocol = SMB_VERS_3_0;
+        }
 
         sv->sv_cfg.skc_maxworkers = ioc->maxworkers;
         sv->sv_cfg.skc_maxconnections = ioc->maxconnections;
         sv->sv_cfg.skc_keepalive = ioc->keepalive;
         sv->sv_cfg.skc_restrict_anon = ioc->restrict_anon;

@@ -1938,10 +2080,12 @@
         sv->sv_cfg.skc_netbios_enable = ioc->netbios_enable;
         sv->sv_cfg.skc_ipv6_enable = ioc->ipv6_enable;
         sv->sv_cfg.skc_print_enable = ioc->print_enable;
         sv->sv_cfg.skc_traverse_mounts = ioc->traverse_mounts;
         sv->sv_cfg.skc_max_protocol = ioc->max_protocol;
+        sv->sv_cfg.skc_min_protocol = ioc->min_protocol;
+        sv->sv_cfg.skc_encrypt = ioc->encrypt;
         sv->sv_cfg.skc_execflags = ioc->exec_flags;
         sv->sv_cfg.skc_negtok_len = ioc->negtok_len;
         sv->sv_cfg.skc_version = ioc->version;
         sv->sv_cfg.skc_initial_credits = ioc->initial_credits;
         sv->sv_cfg.skc_maximum_credits = ioc->maximum_credits;

@@ -2321,52 +2465,79 @@
  */
 static void
 smb_server_create_session(smb_listener_daemon_t *ld, ksocket_t s_so)
 {
         smb_session_t           *session;
-        smb_receiver_arg_t      *rarg;
         taskqid_t               tqid;
+        smb_llist_t             *sl;
+        smb_server_t            *sv = ld->ld_sv;
 
-        session = smb_session_create(s_so, ld->ld_port, ld->ld_sv,
+        session = smb_session_create(s_so, ld->ld_port, sv,
             ld->ld_family);
 
         if (session == NULL) {
                 smb_soshutdown(s_so);
                 smb_sodestroy(s_so);
                 cmn_err(CE_WARN, "SMB Session: alloc failed");
                 return;
         }
 
-        smb_llist_enter(&ld->ld_session_list, RW_WRITER);
-        smb_llist_insert_tail(&ld->ld_session_list, session);
-        smb_llist_exit(&ld->ld_session_list);
+        sl = &sv->sv_session_list;
+        smb_llist_enter(sl, RW_WRITER);
+        smb_llist_insert_tail(sl, session);
+        smb_llist_exit(sl);
 
-        rarg = (smb_receiver_arg_t *)smb_mem_alloc(
-            sizeof (smb_receiver_arg_t));
-        rarg->ra_listener = ld;
-        rarg->ra_session = session;
-
         /*
          * These taskq entries must run independently of one another,
          * so TQ_NOQUEUE.  TQ_SLEEP (==0) just for clarity.
          */
-        tqid = taskq_dispatch(ld->ld_sv->sv_receiver_pool,
-            smb_server_receiver, rarg, TQ_NOQUEUE | TQ_SLEEP);
+        tqid = taskq_dispatch(sv->sv_receiver_pool,
+            smb_server_receiver, session, TQ_NOQUEUE | TQ_SLEEP);
         if (tqid == 0) {
-                smb_mem_free(rarg);
                 smb_session_disconnect(session);
-                smb_server_destroy_session(ld, session);
+                smb_server_destroy_session(session);
                 cmn_err(CE_WARN, "SMB Session: taskq_dispatch failed");
                 return;
         }
         /* handy for debugging */
         session->s_receiver_tqid = tqid;
 }
 
 static void
-smb_server_destroy_session(smb_listener_daemon_t *ld, smb_session_t *session)
+smb_server_destroy_session(smb_session_t *session)
 {
-        smb_llist_enter(&ld->ld_session_list, RW_WRITER);
-        smb_llist_remove(&ld->ld_session_list, session);
-        smb_llist_exit(&ld->ld_session_list);
+        smb_server_t *sv;
+        smb_llist_t *ll;
+        uint32_t count;
+
+        ASSERT(session->s_server != NULL);
+        sv = session->s_server;
+        ll = &sv->sv_session_list;
+
+        smb_llist_flush(&session->s_tree_list);
+        smb_llist_flush(&session->s_user_list);
+
+        /*
+         * The user and tree lists should be empty now.
+         */
+#ifdef DEBUG
+        if (session->s_user_list.ll_count != 0) {
+                cmn_err(CE_WARN, "user list not empty?");
+                debug_enter("s_user_list");
+        }
+        if (session->s_tree_list.ll_count != 0) {
+                cmn_err(CE_WARN, "tree list not empty?");
+                debug_enter("s_tree_list");
+        }
+#endif
+
+        smb_llist_enter(ll, RW_WRITER);
+        smb_llist_remove(ll, session);
+        count = ll->ll_count;
+        smb_llist_exit(ll);
+
         smb_session_delete(session);
+        if (count == 0) {
+                /* See smb_server_shutdown */
+                cv_signal(&sv->sv_cv);
+        }
 }