--- old/usr/src/uts/common/fs/smbsrv/smb_kshare.c	Mon Aug  5 17:26:32 2019
+++ new/usr/src/uts/common/fs/smbsrv/smb_kshare.c	Mon Aug  5 17:26:32 2019
@@ -21,13 +21,14 @@
 
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  * Copyright 2017 Joyent, Inc.
  */
 
 #include <smbsrv/smb_door.h>
-#include <smbsrv/smb_kproto.h>
 #include <smbsrv/smb_ktypes.h>
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_kstat.h>
 
 typedef struct smb_unshare {
 	list_node_t	us_lnd;
@@ -36,7 +37,6 @@
 
 static kmem_cache_t	*smb_kshare_cache_share;
 static kmem_cache_t	*smb_kshare_cache_unexport;
-kmem_cache_t	*smb_kshare_cache_vfs;
 
 static int smb_kshare_cmp(const void *, const void *);
 static void smb_kshare_hold(const void *);
@@ -52,6 +52,10 @@
 static int smb_kshare_unexport(smb_server_t *, const char *);
 static int smb_kshare_export_trans(smb_server_t *, char *, char *, char *);
 static void smb_kshare_csc_flags(smb_kshare_t *, const char *);
+static int smb_named_kstat_update(kstat_t *ks, int rw);
+static int smb_kshare_kstat_update(kstat_t *, int);
+void kshare_stats_init(smb_server_t *, smb_kshare_t *);
+void kshare_stats_fini(smb_kshare_t *);
 
 static boolean_t smb_export_isready(smb_server_t *);
 
@@ -294,7 +298,6 @@
 	mutex_exit(&sv->sv_export.e_mutex);
 
 	smb_avl_destroy(&sv->sv_export.e_share_avl);
-	smb_vfs_rele_all(&sv->sv_export);
 }
 
 void
@@ -305,9 +308,6 @@
 
 	smb_kshare_cache_unexport = kmem_cache_create("smb_unexport_cache",
 	    sizeof (smb_unshare_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
-
-	smb_kshare_cache_vfs = kmem_cache_create("smb_vfs_cache",
-	    sizeof (smb_vfs_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
 }
 
 void
@@ -314,9 +314,6 @@
 smb_kshare_init(smb_server_t *sv)
 {
 
-	smb_llist_constructor(&sv->sv_export.e_vfs_list, sizeof (smb_vfs_t),
-	    offsetof(smb_vfs_t, sv_lnd));
-
 	smb_slist_constructor(&sv->sv_export.e_unexport_list,
 	    sizeof (smb_unshare_t), offsetof(smb_unshare_t, us_lnd));
 }
@@ -348,10 +345,6 @@
 		kmem_cache_free(smb_kshare_cache_unexport, ux);
 	}
 	smb_slist_destructor(&sv->sv_export.e_unexport_list);
-
-	smb_vfs_rele_all(&sv->sv_export);
-
-	smb_llist_destructor(&sv->sv_export.e_vfs_list);
 }
 
 void
@@ -359,7 +352,6 @@
 {
 	kmem_cache_destroy(smb_kshare_cache_unexport);
 	kmem_cache_destroy(smb_kshare_cache_share);
-	kmem_cache_destroy(smb_kshare_cache_vfs);
 }
 
 /*
@@ -683,10 +675,8 @@
 
 /*
  * Add the given share in the specified server.
- * If the share is a disk share, smb_vfs_hold() is
- * invoked to ensure that there is a hold on the
- * corresponding file system before the share is
- * added to shares AVL.
+ * If the share is a disk share, lookup the share path
+ * and hold the smb_node_t for the share root.
  *
  * If the share is an Autohome share and it is
  * already in the AVL only a reference count for
@@ -697,7 +687,7 @@
 {
 	smb_avl_t	*share_avl;
 	smb_kshare_t	*auto_shr;
-	vnode_t		*vp;
+	smb_node_t	*snode = NULL;
 	int		rc = 0;
 
 	share_avl = &sv->sv_export.e_share_avl;
@@ -712,40 +702,231 @@
 	}
 
 	if ((auto_shr = smb_avl_lookup(share_avl, shr)) != NULL) {
-		if ((auto_shr->shr_flags & SMB_SHRF_AUTOHOME) == 0) {
-			smb_avl_release(share_avl, auto_shr);
-			return (EEXIST);
+		rc = EEXIST;
+		if ((auto_shr->shr_flags & SMB_SHRF_AUTOHOME) != 0) {
+			mutex_enter(&auto_shr->shr_mutex);
+			auto_shr->shr_autocnt++;
+			mutex_exit(&auto_shr->shr_mutex);
+			rc = 0;
 		}
-
-		mutex_enter(&auto_shr->shr_mutex);
-		auto_shr->shr_autocnt++;
-		mutex_exit(&auto_shr->shr_mutex);
 		smb_avl_release(share_avl, auto_shr);
-		return (0);
+		return (rc);
 	}
 
-	if ((rc = smb_server_sharevp(sv, shr->shr_path, &vp)) != 0) {
-		cmn_err(CE_WARN, "export[%s(%s)]: failed obtaining vnode (%d)",
+	/*
+	 * Get the root smb_node_t for this share, held.
+	 * This hold is normally released during AVL destroy,
+	 * via the element destructor:  smb_kshare_destroy
+	 */
+	rc = smb_server_share_lookup(sv, shr->shr_path, &snode);
+	if (rc != 0) {
+		cmn_err(CE_WARN, "export[%s(%s)]: lookup failed (%d)",
 		    shr->shr_name, shr->shr_path, rc);
 		return (rc);
 	}
 
-	if ((rc = smb_vfs_hold(&sv->sv_export, vp->v_vfsp)) == 0) {
-		if ((rc = smb_avl_add(share_avl, shr)) != 0) {
-			cmn_err(CE_WARN, "export[%s]: failed caching (%d)",
-			    shr->shr_name, rc);
-			smb_vfs_rele(&sv->sv_export, vp->v_vfsp);
+	shr->shr_root_node = snode;
+	if ((rc = smb_avl_add(share_avl, shr)) != 0) {
+		cmn_err(CE_WARN, "export[%s]: failed caching (%d)",
+		    shr->shr_name, rc);
+		shr->shr_root_node = NULL;
+		smb_node_release(snode);
+		return (rc);
+	}
+
+	kshare_stats_init(sv, shr);
+
+	/*
+	 * For CA shares, find or create the CA handle dir,
+	 * and (if restarted) import persistent handles.
+	 */
+	if ((shr->shr_flags & SMB_SHRF_CA) != 0) {
+		rc = smb2_dh_new_ca_share(sv, shr);
+		if (rc != 0) {
+			/* Just make it a non-CA share. */
+			mutex_enter(&shr->shr_mutex);
+			shr->shr_flags &= ~SMB_SHRF_CA;
+			mutex_exit(&shr->shr_mutex);
+			rc = 0;
 		}
-	} else {
-		cmn_err(CE_WARN, "export[%s(%s)]: failed holding VFS (%d)",
-		    shr->shr_name, shr->shr_path, rc);
 	}
 
-	VN_RELE(vp);
 	return (rc);
 }
 
+
 /*
+ * Following a pattern somewhat similar to smb_server_kstat_init,
+ * but organized a little differently.
+ */
+void
+kshare_stats_init(smb_server_t *sv, smb_kshare_t *ks)
+{
+	static const char	*kr_names[] = SMBSRV_CLSH__NAMES;
+	char			ks_name[KSTAT_STRLEN];
+	smbsrv_clsh_kstats_t	*ksr;
+	int			idx;
+	smb_named_stats_t	*smbnsp;
+	kstat_t			*kstat = NULL;
+	int			namelen;
+
+	/*
+	 * Most share names are short, and we'd like to allow consumers like
+	 * the smbstat command to compose the kstat names for short share names
+	 * directly.  In the (rare) case where we have longer share names, we
+	 * need an indirection scheme to get around the limited (31 chars) size
+	 * of kstat names. So for share names 24 chars or shorter, we compose
+	 * the kstat name directly as "smbsrv:0:sh/sharename" and when the share
+	 * name is longer, compose the kstat name using an arbitrary
+	 * (but unique) identifier like: "smbsrv:0:sh/:ffffff0009560a98".  To
+	 * find the unique identifier given a (long) share name, the consumer
+	 * has to enumerate the (fake) "smbsrvshr" kstat module, looking for the
+	 * kstat with the sharename element matching the one they want.  Both
+	 * direct and indirect names are instantiated under the "smbsrvshr"
+	 * module, so a consumer that wants all the share kstats could always
+	 * lookup the kstat names using the indirection scheme if that's easier.
+	 * (Nothing forces the consumer to directly compose kstat names when the
+	 * share name happens to be short -- they can always enumerate to find
+	 * it.)
+	 *
+	 * SMB share names are not allowed to contain a colon.  The naming
+	 * scheme here uses a colon at the beginning of what would otherwise be
+	 * the share name part of the kstat name to indicate that this name uses
+	 * the "indirection" scheme.  With indirection, the part after the colon
+	 * is an arbitrary (but unique per share) identifier of some other
+	 * kstat, and a consumer needs to fetch that kstat to get the real share
+	 * name.
+	 */
+	namelen = strlen(ks->shr_name);
+	if (namelen <= 24) {
+		(void) snprintf(ks_name, sizeof (ks_name), "sh/%s",
+		    ks->shr_name);
+
+		kstat = kstat_hold_byname("smbsrvshr", 0, ks_name, sv->sv_zid);
+	}
+
+	if (namelen > 24 || kstat != NULL) {
+		(void) snprintf(ks_name, sizeof (ks_name), "sh/:%p",
+		    (void *)ks);
+
+		if (kstat != NULL)
+			kstat_rele(kstat);
+	}
+
+	ks->shr_ksp = kstat_create_zone(SMBSRV_KSTAT_MODULE, 0,
+	    ks_name, SMBSRV_KSTAT_CLASS, KSTAT_TYPE_RAW,
+	    sizeof (smbsrv_clsh_kstats_t), 0, sv->sv_zid);
+
+	if (ks->shr_ksp == NULL)
+		return;
+
+	ks->stats.ksns = kstat_create_zone("smbsrvshr", 0,
+	    ks_name, SMBSRV_KSTAT_CLASS, KSTAT_TYPE_NAMED,
+	    0, KSTAT_FLAG_VIRTUAL, sv->sv_zid);
+
+	if (ks->stats.ksns == NULL) {
+		kstat_delete(ks->shr_ksp);
+		ks->shr_ksp = NULL;
+		return;
+	}
+
+	ks->shr_ksp->ks_update = smb_kshare_kstat_update;
+	ks->shr_ksp->ks_private = ks;
+
+	/*
+	 * In-line equivalent of smb_dispatch_stats_init
+	 */
+	ksr = (smbsrv_clsh_kstats_t *)ks->shr_ksp->ks_data;
+	for (idx = 0; idx < SMBSRV_CLSH__NREQ; idx++) {
+		smb_latency_init(&ks->shr_stats[idx].sdt_lat);
+		(void) strlcpy(ksr->ks_clsh[idx].kr_name, kr_names[idx],
+		    KSTAT_STRLEN);
+	}
+
+	/*
+	 * SMB named kstats setup for > KSTAT_STRLEN name to short name mapping.
+	 */
+	smbnsp = &ks->stats.ks_data;
+	ks->stats.ksns->ks_data = &ks->stats.ks_data;
+	ks->stats.ksns->ks_data_size = sizeof (ks->stats.ks_data);
+	ks->stats.ksns->ks_ndata = 1;
+	kstat_named_init(smbnsp->kn, "share name",
+	    KSTAT_DATA_STRING);
+	ks->stats.ksns->ks_update = smb_named_kstat_update;
+	ks->stats.ksns->ks_private = (void *)ks;
+
+	kstat_install(ks->shr_ksp);
+	kstat_install(ks->stats.ksns);
+}
+
+void
+kshare_stats_fini(smb_kshare_t *ks)
+{
+	int idx;
+
+	for (idx = 0; idx < SMBSRV_CLSH__NREQ; idx++)
+		smb_latency_destroy(&ks->shr_stats[idx].sdt_lat);
+}
+
+static int
+smb_named_kstat_update(kstat_t *ks, int rw)
+{
+	smb_kshare_t		*smbksp = (smb_kshare_t *)ks->ks_private;
+	smb_named_stats_t	*smbnsp = &smbksp->stats.ks_data;
+
+	if (rw == KSTAT_READ) {
+		(void) strlcpy(smbnsp->name, smbksp->shr_name,
+		    sizeof (smbnsp->name));
+
+		kstat_named_setstr(&smbnsp->kn[0],
+		    (const char *)smbksp->shr_name);
+	}
+	return (0);
+}
+
+/*
+ * Update the kstat data from our private stats.
+ */
+static int
+smb_kshare_kstat_update(kstat_t *ksp, int rw)
+{
+	smb_kshare_t *kshare;
+	smb_disp_stats_t *sds;
+	smbsrv_clsh_kstats_t	*clsh;
+	smb_kstat_req_t	*ksr;
+	int i;
+
+	if (rw == KSTAT_WRITE)
+		return (EACCES);
+
+	kshare = ksp->ks_private;
+	ASSERT(kshare->shr_magic == SMB_SHARE_MAGIC);
+	sds = kshare->shr_stats;
+
+	clsh = (smbsrv_clsh_kstats_t *)ksp->ks_data;
+	ksr = clsh->ks_clsh;
+
+	for (i = 0; i < SMBSRV_CLSH__NREQ; i++, ksr++, sds++) {
+		ksr->kr_rxb = sds->sdt_rxb;
+		ksr->kr_txb = sds->sdt_txb;
+		mutex_enter(&sds->sdt_lat.ly_mutex);
+		ksr->kr_nreq = sds->sdt_lat.ly_a_nreq;
+		ksr->kr_sum = sds->sdt_lat.ly_a_sum;
+		ksr->kr_a_mean = sds->sdt_lat.ly_a_mean;
+		ksr->kr_a_stddev = sds->sdt_lat.ly_a_stddev;
+		ksr->kr_d_mean = sds->sdt_lat.ly_d_mean;
+		ksr->kr_d_stddev = sds->sdt_lat.ly_d_stddev;
+		sds->sdt_lat.ly_d_mean = 0;
+		sds->sdt_lat.ly_d_nreq = 0;
+		sds->sdt_lat.ly_d_stddev = 0;
+		sds->sdt_lat.ly_d_sum = 0;
+		mutex_exit(&sds->sdt_lat.ly_mutex);
+	}
+
+	return (0);
+}
+
+/*
  * Removes the share specified by 'shrname' from the AVL
  * tree of the given server if it's there.
  *
@@ -763,8 +944,6 @@
 	smb_avl_t	*share_avl;
 	smb_kshare_t	key;
 	smb_kshare_t	*shr;
-	vnode_t		*vp;
-	int		rc;
 	boolean_t	auto_unexport;
 
 	share_avl = &sv->sv_export.e_share_avl;
@@ -784,19 +963,12 @@
 		}
 	}
 
-	if (STYPE_ISDSK(shr->shr_type)) {
-		if ((rc = smb_server_sharevp(sv, shr->shr_path, &vp)) != 0) {
-			smb_avl_release(share_avl, shr);
-			cmn_err(CE_WARN, "unexport[%s]: failed obtaining vnode"
-			    " (%d)", shrname, rc);
-			return (rc);
-		}
+	smb_avl_remove(share_avl, shr);
 
-		smb_vfs_rele(&sv->sv_export, vp->v_vfsp);
-		VN_RELE(vp);
-	}
+	mutex_enter(&shr->shr_mutex);
+	shr->shr_flags |= SMB_SHRF_REMOVED;
+	mutex_exit(&shr->shr_mutex);
 
-	smb_avl_remove(share_avl, shr);
 	smb_avl_release(share_avl, shr);
 
 	return (0);
@@ -849,7 +1021,7 @@
 	smb_kshare_t tmp;
 	smb_kshare_t *shr;
 	nvlist_t *smb;
-	char *csc_name = NULL;
+	char *csc_name = NULL, *strbuf = NULL;
 	int rc;
 
 	ASSERT(share);
@@ -889,7 +1061,11 @@
 	    SMB_SHRF_GUEST_OK);
 	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_DFSROOT,
 	    SMB_SHRF_DFSROOT);
-	tmp.shr_flags |= smb_kshare_decode_bool(smb, "Autohome",
+	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_QUOTAS,
+	    SMB_SHRF_QUOTAS);
+	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_CA, SMB_SHRF_CA);
+	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_FSO, SMB_SHRF_FSO);
+	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_AUTOHOME,
 	    SMB_SHRF_AUTOHOME);
 
 	if ((tmp.shr_flags & SMB_SHRF_AUTOHOME) == SMB_SHRF_AUTOHOME) {
@@ -902,6 +1078,9 @@
 		}
 	}
 
+	(void) nvlist_lookup_string(smb, SHOPT_ENCRYPT, &strbuf);
+	smb_cfg_set_require(strbuf, &tmp.shr_encrypt);
+
 	(void) nvlist_lookup_string(smb, SHOPT_CSC, &csc_name);
 	smb_kshare_csc_flags(&tmp, csc_name);
 
@@ -927,6 +1106,7 @@
 	shr->shr_oemname = smb_kshare_oemname(shr->shr_name);
 	shr->shr_flags = tmp.shr_flags | smb_kshare_is_admin(shr->shr_name);
 	shr->shr_type = tmp.shr_type | smb_kshare_is_special(shr->shr_name);
+	shr->shr_encrypt = tmp.shr_encrypt;
 
 	shr->shr_uid = tmp.shr_uid;
 	shr->shr_gid = tmp.shr_gid;
@@ -933,7 +1113,6 @@
 
 	if ((shr->shr_flags & SMB_SHRF_AUTOHOME) == SMB_SHRF_AUTOHOME)
 		shr->shr_autocnt = 1;
-
 	return (shr);
 }
 
@@ -1033,6 +1212,21 @@
 	ASSERT(shr);
 	ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
 
+	if (shr->shr_ksp != NULL) {
+		kstat_delete(shr->shr_ksp);
+		shr->shr_ksp = NULL;
+		kshare_stats_fini(shr);
+	}
+
+	if (shr->stats.ksns != NULL) {
+		kstat_delete(shr->stats.ksns);
+	}
+
+	if (shr->shr_ca_dir != NULL)
+		smb_node_release(shr->shr_ca_dir);
+	if (shr->shr_root_node)
+		smb_node_release(shr->shr_root_node);
+
 	smb_mem_free(shr->shr_name);
 	smb_mem_free(shr->shr_path);
 	smb_mem_free(shr->shr_cmnt);
