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);
+ }
}