Print this page
NEX-20459 smb/server service restart fails, stuck in offlining state
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-4856 SMB2 kstats don't correctly count compound requests
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-18748 (Hyper-V 2016) VM goes to poweroff state when smbd is restarted
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
NEX-17589 Get "too high" smbd error when copy big file to cifs share (redo)
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@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-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-1643 dtrace provider for smbsrv
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-9864 Some SMB cancel races remain after NEX-5845
Revert (part of) "NEX-5845 rework SMB immediate cancel"
reverts (part of) commit 7a5da69f6d42b17ebcc95ca3d02925d07a01343e.
Reviewed by: Matt Barden <matt.barden@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-8120 smbstat -C requires IPv4 addresses to be prefixed with ::ffff: if ipv6_enable is true
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Jean McCormack <jean.mccormack@nexenta.com>
NEX-6308 namespace collision for per-share kstats when changing sharesmb property
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@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-5845 rework SMB immediate cancel
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-3553 SMB2/3 durable handles
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-5560 smb2 should use 64-bit server-global uids
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-5314 SMB server may wait for oplock break ack on disconnected session
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-3906 Prefer that SMB change notify not tie up a worker thread
NEX-5278 SMB notify should buffer per file handle
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-5175 want SMB statistics separately for reads, writes, other
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-4313 want iops, bandwidth, and latency kstats for smb
Portions contributed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-4714 smbstat -t tbytes/s always zero
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-2522 svcadm disable network/smb/server may hang
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-4391 improve smb cancel support
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-4147 Cancelled SMB2 requests sometimes have no response (missed things)
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>
SUP-672 Zero-padded IP address strings returned by SMB server...
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@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-3610 CLONE NEX-3591 SMB3 signing
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
NEX-3004 Keep track of remote TCP port
Reviewed by: Dan Fields <Dan.Fields@nexenta.com>
Reviewed by: Josef Sipek <Josef.Sipek@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
Reviewed by: Kevin Crowe <Kevin.Crowe@nexenta.com>
Reviewed by: Alek Pinchuk <Alek.Pinchuk@nexenta.com>
Reviewed by: Rick McNeal <Rick.McNeal@nexenta.com>
NEX-2798 SMB 1 disconnects after large write attempt
NEX-2781 SMB2 credit handling needs work
NEX-2353 Codenomicon: SMB2 TC # 448950 - PANIC in SMB2.Compounded-commands...
NEX-1991 SMB2 never grants level II oplocks
SMB-137 assertion failed: (sr->reply.chain_offset & 7) == 0, file: ../../common/fs/smbsrv/smb2_dispatch.c, line: 727
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-50 User-mode SMB server (fix elfchk noise)
SMB-56 extended security NTLMSSP, inbound (fix a leak)
SMB-39 Use AF_UNIX pipes for RPC (fix a leak)
SMB-75 smb_session_timers way too frequent
SMB-69 read-raw, write-raw are dead code
SMB-56 extended security NTLMSSP, inbound
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 (kmem_caches)
common kmem_cache instances across zones
separate GZ-only init from NGZ init
SMB-63 taskq_create_proc ... TQ_DYNAMIC puts tasks in p0
re #11974 CIFS Share - Tree connect fails from Windows 7 Clients
Fix up some merges where we wanted the upstream version.
SMB-48 Panic with smbtorture raw.scan-eamax
re #7126 rb4153 smbd panic with missing negotiate challenge
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 #6813 rb1757 port 2976 Child folder visibility through shares
*** 18,28 ****
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
! * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/atomic.h>
#include <sys/synch.h>
#include <sys/types.h>
--- 18,28 ----
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
! * Copyright 2019 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/atomic.h>
#include <sys/synch.h>
#include <sys/types.h>
*** 44,70 ****
* We track the keepalive in minutes, but this constant
* specifies it in seconds, so convert to minutes.
*/
uint32_t smb_keep_alive = SMB_PI_KEEP_ALIVE_MIN / 60;
static int smbsr_newrq_initial(smb_request_t *);
static void smb_session_cancel(smb_session_t *);
static int smb_session_reader(smb_session_t *);
static int smb_session_xprt_puthdr(smb_session_t *,
uint8_t msg_type, uint32_t msg_len,
uint8_t *dst, size_t dstlen);
! static smb_tree_t *smb_session_get_tree(smb_session_t *, smb_tree_t *);
! static void smb_session_logoff(smb_session_t *);
static void smb_request_init_command_mbuf(smb_request_t *sr);
static void smb_session_genkey(smb_session_t *);
void
! smb_session_timers(smb_llist_t *ll)
{
smb_session_t *session;
smb_llist_enter(ll, RW_READER);
session = smb_llist_head(ll);
while (session != NULL) {
/*
* Walk through the table and decrement each keep_alive
--- 44,105 ----
* We track the keepalive in minutes, but this constant
* specifies it in seconds, so convert to minutes.
*/
uint32_t smb_keep_alive = SMB_PI_KEEP_ALIVE_MIN / 60;
+ /*
+ * There are many smbtorture test cases that send
+ * racing requests, and where the tests fail if we
+ * don't execute them in exactly the order sent.
+ * These are test bugs. The protocol makes no
+ * guarantees about execution order of requests
+ * that are concurrently active.
+ *
+ * Nonetheless, smbtorture has many useful tests,
+ * so we have this work-around we can enable to
+ * basically force sequential execution. When
+ * enabled, insert a delay after each request is
+ * issued a taskq job. Enable this with mdb by
+ * setting smb_reader_delay to 10. Don't make it
+ * more than 500 or so or the server will appear
+ * to be so slow that tests may time out.
+ */
+ int smb_reader_delay = 0; /* mSec. */
+
static int smbsr_newrq_initial(smb_request_t *);
static void smb_session_cancel(smb_session_t *);
static int smb_session_reader(smb_session_t *);
static int smb_session_xprt_puthdr(smb_session_t *,
uint8_t msg_type, uint32_t msg_len,
uint8_t *dst, size_t dstlen);
! static void smb_session_disconnect_trees(smb_session_t *);
static void smb_request_init_command_mbuf(smb_request_t *sr);
static void smb_session_genkey(smb_session_t *);
+ static int smb_session_kstat_update(kstat_t *, int);
+ void session_stats_init(smb_server_t *, smb_session_t *);
+ void session_stats_fini(smb_session_t *);
+ /*
+ * This (legacy) code is in support of an "idle timeout" feature,
+ * which is apparently incomplete. To complete it, we should:
+ * when the keep_alive timer expires, check whether the client
+ * has any open files, and if not then kill their session.
+ * Right now the timers are there, but nothing happens when
+ * a timer expires.
+ *
+ * Todo: complete logic to kill idle sessions.
+ *
+ * Only called when sv_cfg.skc_keepalive != 0
+ */
void
! smb_session_timers(smb_server_t *sv)
{
smb_session_t *session;
+ smb_llist_t *ll;
+ ll = &sv->sv_session_list;
smb_llist_enter(ll, RW_READER);
session = smb_llist_head(ll);
while (session != NULL) {
/*
* Walk through the table and decrement each keep_alive
*** 72,119 ****
*/
SMB_SESSION_VALID(session);
if (session->keep_alive &&
(session->keep_alive != (uint32_t)-1))
session->keep_alive--;
session = smb_llist_next(ll, session);
}
smb_llist_exit(ll);
}
- void
- smb_session_correct_keep_alive_values(smb_llist_t *ll, uint32_t new_keep_alive)
- {
- smb_session_t *sn;
-
- /*
- * Caller specifies seconds, but we track in minutes, so
- * convert to minutes (rounded up).
- */
- new_keep_alive = (new_keep_alive + 59) / 60;
-
- if (new_keep_alive == smb_keep_alive)
- return;
- /*
- * keep alive == 0 means do not drop connection if it's idle
- */
- smb_keep_alive = (new_keep_alive) ? new_keep_alive : -1;
-
- /*
- * Walk through the table and set each session to the new keep_alive
- * value if they have not already timed out. Block clock interrupts.
- */
- smb_llist_enter(ll, RW_READER);
- sn = smb_llist_head(ll);
- while (sn != NULL) {
- SMB_SESSION_VALID(sn);
- if (sn->keep_alive != 0)
- sn->keep_alive = new_keep_alive;
- sn = smb_llist_next(ll, sn);
- }
- smb_llist_exit(ll);
- }
-
/*
* Send a session message - supports SMB-over-NBT and SMB-over-TCP.
* If an mbuf chain is provided (optional), it will be freed and
* set to NULL -- unconditionally! (error or not)
*
--- 107,122 ----
*/
SMB_SESSION_VALID(session);
if (session->keep_alive &&
(session->keep_alive != (uint32_t)-1))
session->keep_alive--;
+
session = smb_llist_next(ll, session);
}
smb_llist_exit(ll);
}
/*
* Send a session message - supports SMB-over-NBT and SMB-over-TCP.
* If an mbuf chain is provided (optional), it will be freed and
* set to NULL -- unconditionally! (error or not)
*
*** 415,461 ****
* state.
*/
void
smb_request_cancel(smb_request_t *sr)
{
mutex_enter(&sr->sr_mutex);
switch (sr->sr_state) {
case SMB_REQ_STATE_INITIALIZING:
case SMB_REQ_STATE_SUBMITTED:
case SMB_REQ_STATE_ACTIVE:
case SMB_REQ_STATE_CLEANED_UP:
! sr->sr_state = SMB_REQ_STATE_CANCELED;
break;
case SMB_REQ_STATE_WAITING_LOCK:
/*
! * This request is waiting on a lock. Wakeup everything
! * waiting on the lock so that the relevant thread regains
! * control and notices that is has been canceled. The
! * other lock request threads waiting on this lock will go
! * back to sleep when they discover they are still blocked.
*/
! sr->sr_state = SMB_REQ_STATE_CANCELED;
!
! ASSERT(sr->sr_awaiting != NULL);
! mutex_enter(&sr->sr_awaiting->l_mutex);
! cv_broadcast(&sr->sr_awaiting->l_cv);
! mutex_exit(&sr->sr_awaiting->l_mutex);
break;
! case SMB_REQ_STATE_WAITING_EVENT:
! /*
! * This request is waiting in change notify.
! */
! sr->sr_state = SMB_REQ_STATE_CANCELED;
! cv_signal(&sr->sr_ncr.nc_cv);
! break;
!
! case SMB_REQ_STATE_EVENT_OCCURRED:
case SMB_REQ_STATE_COMPLETED:
! case SMB_REQ_STATE_CANCELED:
/*
* No action required for these states since the request
* is completing.
*/
break;
--- 418,461 ----
* state.
*/
void
smb_request_cancel(smb_request_t *sr)
{
+ void (*cancel_method)(smb_request_t *) = NULL;
+
mutex_enter(&sr->sr_mutex);
switch (sr->sr_state) {
case SMB_REQ_STATE_INITIALIZING:
case SMB_REQ_STATE_SUBMITTED:
case SMB_REQ_STATE_ACTIVE:
case SMB_REQ_STATE_CLEANED_UP:
! sr->sr_state = SMB_REQ_STATE_CANCELLED;
break;
+ case SMB_REQ_STATE_WAITING_AUTH:
+ case SMB_REQ_STATE_WAITING_FCN1:
case SMB_REQ_STATE_WAITING_LOCK:
+ case SMB_REQ_STATE_WAITING_PIPE:
/*
! * These are states that have a cancel_method.
! * Make the state change now, to ensure that
! * we call cancel_method exactly once. Do the
! * method call below, after we drop sr_mutex.
! * When the cancelled request thread resumes,
! * it should re-take sr_mutex and set sr_state
! * to CANCELLED, then return STATUS_CANCELLED.
*/
! sr->sr_state = SMB_REQ_STATE_CANCEL_PENDING;
! cancel_method = sr->cancel_method;
! VERIFY(cancel_method != NULL);
break;
! case SMB_REQ_STATE_WAITING_FCN2:
case SMB_REQ_STATE_COMPLETED:
! case SMB_REQ_STATE_CANCEL_PENDING:
! case SMB_REQ_STATE_CANCELLED:
/*
* No action required for these states since the request
* is completing.
*/
break;
*** 463,478 ****
--- 463,487 ----
case SMB_REQ_STATE_FREE:
default:
SMB_PANIC();
}
mutex_exit(&sr->sr_mutex);
+
+ if (cancel_method != NULL) {
+ cancel_method(sr);
+ }
}
/*
* smb_session_receiver
*
* Receives request from the network and dispatches them to a worker.
+ *
+ * When we receive a disconnect here, it _could_ be due to the server
+ * having initiated disconnect, in which case the session state will be
+ * SMB_SESSION_STATE_TERMINATED and we want to keep that state so later
+ * tear-down logic will know which side initiated.
*/
void
smb_session_receiver(smb_session_t *session)
{
int rc = 0;
*** 483,493 ****
if (session->s_local_port == IPPORT_NETBIOS_SSN) {
rc = smb_netbios_session_request(session);
if (rc != 0) {
smb_rwx_rwenter(&session->s_lock, RW_WRITER);
! session->s_state = SMB_SESSION_STATE_DISCONNECTED;
smb_rwx_rwexit(&session->s_lock);
return;
}
}
--- 492,504 ----
if (session->s_local_port == IPPORT_NETBIOS_SSN) {
rc = smb_netbios_session_request(session);
if (rc != 0) {
smb_rwx_rwenter(&session->s_lock, RW_WRITER);
! if (session->s_state != SMB_SESSION_STATE_TERMINATED)
! session->s_state =
! SMB_SESSION_STATE_DISCONNECTED;
smb_rwx_rwexit(&session->s_lock);
return;
}
}
*** 496,505 ****
--- 507,517 ----
smb_rwx_rwexit(&session->s_lock);
(void) smb_session_reader(session);
smb_rwx_rwenter(&session->s_lock, RW_WRITER);
+ if (session->s_state != SMB_SESSION_STATE_TERMINATED)
session->s_state = SMB_SESSION_STATE_DISCONNECTED;
smb_rwx_rwexit(&session->s_lock);
smb_soshutdown(session->sock);
*** 514,524 ****
}
/*
* smb_session_disconnect
*
! * Disconnects the session passed in.
*/
void
smb_session_disconnect(smb_session_t *session)
{
SMB_SESSION_VALID(session);
--- 526,536 ----
}
/*
* smb_session_disconnect
*
! * Server-initiated disconnect (i.e. server shutdown)
*/
void
smb_session_disconnect(smb_session_t *session)
{
SMB_SESSION_VALID(session);
*** 528,539 ****
case SMB_SESSION_STATE_INITIALIZED:
case SMB_SESSION_STATE_CONNECTED:
case SMB_SESSION_STATE_ESTABLISHED:
case SMB_SESSION_STATE_NEGOTIATED:
smb_soshutdown(session->sock);
! session->s_state = SMB_SESSION_STATE_DISCONNECTED;
! _NOTE(FALLTHRU)
case SMB_SESSION_STATE_DISCONNECTED:
case SMB_SESSION_STATE_TERMINATED:
break;
}
smb_rwx_rwexit(&session->s_lock);
--- 540,551 ----
case SMB_SESSION_STATE_INITIALIZED:
case SMB_SESSION_STATE_CONNECTED:
case SMB_SESSION_STATE_ESTABLISHED:
case SMB_SESSION_STATE_NEGOTIATED:
smb_soshutdown(session->sock);
! session->s_state = SMB_SESSION_STATE_TERMINATED;
! break;
case SMB_SESSION_STATE_DISCONNECTED:
case SMB_SESSION_STATE_TERMINATED:
break;
}
smb_rwx_rwexit(&session->s_lock);
*** 600,611 ****
session->keep_alive = smb_keep_alive;
/*
* Allocate a request context, read the whole message.
*/
! sr = smb_request_alloc(session, hdr.xh_length);
req_buf = (uint8_t *)sr->sr_request_buf;
resid = hdr.xh_length;
rc = smb_sorecv(session->sock, req_buf, resid);
--- 612,626 ----
session->keep_alive = smb_keep_alive;
/*
* Allocate a request context, read the whole message.
+ * If the request alloc fails, we've disconnected
+ * and won't be able to send the reply anyway, so bail now.
*/
! if ((sr = smb_request_alloc(session, hdr.xh_length)) == NULL)
! break;
req_buf = (uint8_t *)sr->sr_request_buf;
resid = hdr.xh_length;
rc = smb_sorecv(session->sock, req_buf, resid);
*** 612,623 ****
if (rc) {
smb_request_free(sr);
break;
}
! /* accounting: requests, received bytes */
! smb_server_inc_req(sv);
smb_server_add_rxb(sv,
(int64_t)(hdr.xh_length + NETBIOS_HDR_SZ));
/*
* Initialize command MBC to represent the received data.
--- 627,637 ----
if (rc) {
smb_request_free(sr);
break;
}
! /* accounting: received bytes */
smb_server_add_rxb(sv,
(int64_t)(hdr.xh_length + NETBIOS_HDR_SZ));
/*
* Initialize command MBC to represent the received data.
*** 628,638 ****
--- 642,657 ----
rc = session->newrq_func(sr);
sr = NULL; /* enqueued or freed */
if (rc != 0)
break;
+
+ /* See notes where this is defined (above). */
+ if (smb_reader_delay) {
+ delay(MSEC_TO_TICK(smb_reader_delay));
}
+ }
return (rc);
}
/*
* This is the initial handler for new smb requests, called from
*** 647,656 ****
--- 666,678 ----
*
* This and other "post a request" handlers must either enqueue
* the new request for the session taskq, or smb_request_free it
* (in case we've decided to drop this connection). In this
* (special) new request handler, we always free the request.
+ *
+ * Return value is 0 for success, and anything else will
+ * terminate the reader thread (drop the connection).
*/
static int
smbsr_newrq_initial(smb_request_t *sr)
{
uint32_t magic;
*** 701,717 ****
return (NULL);
}
now = ddi_get_lbolt64();
session->s_kid = SMB_NEW_KID();
session->s_state = SMB_SESSION_STATE_INITIALIZED;
session->native_os = NATIVE_OS_UNKNOWN;
session->opentime = now;
session->keep_alive = smb_keep_alive;
session->activity_timestamp = now;
-
smb_session_genkey(session);
mutex_init(&session->s_credits_mutex, NULL, MUTEX_DEFAULT, NULL);
smb_slist_constructor(&session->s_req_list, sizeof (smb_request_t),
--- 723,739 ----
return (NULL);
}
now = ddi_get_lbolt64();
+ session->s_server = sv;
session->s_kid = SMB_NEW_KID();
session->s_state = SMB_SESSION_STATE_INITIALIZED;
session->native_os = NATIVE_OS_UNKNOWN;
session->opentime = now;
session->keep_alive = smb_keep_alive;
session->activity_timestamp = now;
smb_session_genkey(session);
mutex_init(&session->s_credits_mutex, NULL, MUTEX_DEFAULT, NULL);
smb_slist_constructor(&session->s_req_list, sizeof (smb_request_t),
*** 728,738 ****
smb_net_txl_constructor(&session->s_txlst);
smb_rwx_init(&session->s_lock);
! if (new_so != NULL) {
if (family == AF_INET) {
slen = sizeof (sin);
(void) ksocket_getsockname(new_so,
(struct sockaddr *)&sin, &slen, CRED());
bcopy(&sin.sin_addr,
--- 750,775 ----
smb_net_txl_constructor(&session->s_txlst);
smb_rwx_init(&session->s_lock);
! session->s_srqueue = &sv->sv_srqueue;
! smb_server_get_cfg(sv, &session->s_cfg);
!
! if (new_so == NULL) {
! /*
! * This call is creating the special "server" session,
! * used for kshare export, oplock breaks, CA import.
! * CA import creates temporary trees on this session
! * and those should never get map/unmap up-calls, so
! * force the map/unmap flags zero on this session.
! * Set a "modern" dialect for CA import too, so
! * pathname parse doesn't do OS/2 stuff, etc.
! */
! session->s_cfg.skc_execflags = 0;
! session->dialect = session->s_cfg.skc_max_protocol;
! } else {
if (family == AF_INET) {
slen = sizeof (sin);
(void) ksocket_getsockname(new_so,
(struct sockaddr *)&sin, &slen, CRED());
bcopy(&sin.sin_addr,
*** 770,782 ****
if (port == IPPORT_NETBIOS_SSN)
smb_server_inc_nbt_sess(sv);
else
smb_server_inc_tcp_sess(sv);
}
- session->s_server = sv;
- smb_server_get_cfg(sv, &session->s_cfg);
- session->s_srqueue = &sv->sv_srqueue;
/*
* The initial new request handler is special,
* and only accepts negotiation requests.
*/
--- 807,816 ----
*** 784,803 ****
--- 818,950 ----
/* These may increase in SMB2 negotiate. */
session->cmd_max_bytes = SMB_REQ_MAX_SIZE;
session->reply_max_bytes = SMB_REQ_MAX_SIZE;
+ session_stats_init(sv, session);
+
session->s_magic = SMB_SESSION_MAGIC;
+
return (session);
}
void
+ session_stats_init(smb_server_t *sv, smb_session_t *ss)
+ {
+ static const char *kr_names[] = SMBSRV_CLSH__NAMES;
+ char ks_name[KSTAT_STRLEN];
+ char *ipaddr_str = ss->ip_addr_str;
+ smbsrv_clsh_kstats_t *ksr;
+ int idx;
+
+ /* Don't include the special internal session (sv->sv_session). */
+ if (ss->sock == NULL)
+ return;
+
+ /*
+ * If ipv6_enable is set to true, IPv4 addresses will be prefixed
+ * with ::ffff: (IPv4-mapped IPv6 address), strip it.
+ */
+ if (strncasecmp(ipaddr_str, "::ffff:", 7) == 0)
+ ipaddr_str += 7;
+
+ /*
+ * Create raw kstats for sessions with a name composed as:
+ * cl/$IPADDR and instance ss->s_kid
+ * These will look like: smbsrv:0:cl/10.10.0.5
+ */
+ (void) snprintf(ks_name, sizeof (ks_name), "cl/%s", ipaddr_str);
+
+ ss->s_ksp = kstat_create_zone(SMBSRV_KSTAT_MODULE, ss->s_kid,
+ ks_name, SMBSRV_KSTAT_CLASS, KSTAT_TYPE_RAW,
+ sizeof (smbsrv_clsh_kstats_t), 0, sv->sv_zid);
+
+ if (ss->s_ksp == NULL)
+ return;
+
+ ss->s_ksp->ks_update = smb_session_kstat_update;
+ ss->s_ksp->ks_private = ss;
+
+ /*
+ * In-line equivalent of smb_dispatch_stats_init
+ */
+ ksr = (smbsrv_clsh_kstats_t *)ss->s_ksp->ks_data;
+ for (idx = 0; idx < SMBSRV_CLSH__NREQ; idx++) {
+ smb_latency_init(&ss->s_stats[idx].sdt_lat);
+ (void) strlcpy(ksr->ks_clsh[idx].kr_name, kr_names[idx],
+ KSTAT_STRLEN);
+ }
+
+ kstat_install(ss->s_ksp);
+ }
+
+ void
+ session_stats_fini(smb_session_t *ss)
+ {
+ int idx;
+
+ for (idx = 0; idx < SMBSRV_CLSH__NREQ; idx++)
+ smb_latency_destroy(&ss->s_stats[idx].sdt_lat);
+ }
+
+ /*
+ * Update the kstat data from our private stats.
+ */
+ static int
+ smb_session_kstat_update(kstat_t *ksp, int rw)
+ {
+ smb_session_t *session;
+ smb_disp_stats_t *sds;
+ smbsrv_clsh_kstats_t *clsh;
+ smb_kstat_req_t *ksr;
+ int i;
+
+ if (rw == KSTAT_WRITE)
+ return (EACCES);
+
+ session = ksp->ks_private;
+ SMB_SESSION_VALID(session);
+ sds = session->s_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);
+ }
+
+ void
smb_session_delete(smb_session_t *session)
{
ASSERT(session->s_magic == SMB_SESSION_MAGIC);
+ if (session->s_ksp != NULL) {
+ kstat_delete(session->s_ksp);
+ session->s_ksp = NULL;
+ session_stats_fini(session);
+ }
+
+ if (session->enc_mech != NULL)
+ smb3_encrypt_fini(session);
+
if (session->sign_fini != NULL)
session->sign_fini(session);
if (session->signing.mackey != NULL) {
kmem_free(session->signing.mackey,
*** 845,865 ****
* this session.
*/
smb_slist_wait_for_empty(&session->s_req_list);
/*
! * At this point the reference count of the users, trees, files,
! * directories should be zero. It should be possible to destroy them
! * without any problem.
*/
xa = smb_llist_head(&session->s_xa_list);
while (xa) {
nextxa = smb_llist_next(&session->s_xa_list, xa);
smb_xa_close(xa);
xa = nextxa;
}
smb_session_logoff(session);
}
/*
* Cancel requests. If a non-null tree is specified, only requests specific
--- 992,1015 ----
* this session.
*/
smb_slist_wait_for_empty(&session->s_req_list);
/*
! * Cleanup transact state objects
*/
xa = smb_llist_head(&session->s_xa_list);
while (xa) {
nextxa = smb_llist_next(&session->s_xa_list, xa);
smb_xa_close(xa);
xa = nextxa;
}
+ /*
+ * At this point the reference count of the files and directories
+ * should be zero. It should be possible to destroy them without
+ * any problem, which should trigger the destruction of other objects.
+ */
smb_session_logoff(session);
}
/*
* Cancel requests. If a non-null tree is specified, only requests specific
*** 893,954 ****
* Find a user on the specified session by SMB UID.
*/
smb_user_t *
smb_session_lookup_uid(smb_session_t *session, uint16_t uid)
{
! return (smb_session_lookup_uid_st(session, uid,
SMB_USER_STATE_LOGGED_ON));
}
smb_user_t *
! smb_session_lookup_uid_st(smb_session_t *session, uint16_t uid,
! smb_user_state_t st)
{
smb_user_t *user;
smb_llist_t *user_list;
SMB_SESSION_VALID(session);
user_list = &session->s_user_list;
smb_llist_enter(user_list, RW_READER);
! user = smb_llist_head(user_list);
! while (user) {
SMB_USER_VALID(user);
ASSERT(user->u_session == session);
! if (user->u_uid == uid && user->u_state == st) {
! smb_user_hold_internal(user);
break;
}
!
! user = smb_llist_next(user_list, user);
}
smb_llist_exit(user_list);
return (user);
}
- void
- smb_session_post_user(smb_session_t *session, smb_user_t *user)
- {
- SMB_USER_VALID(user);
- ASSERT(user->u_refcnt == 0);
- ASSERT(user->u_state == SMB_USER_STATE_LOGGED_OFF);
- ASSERT(user->u_session == session);
-
- smb_llist_post(&session->s_user_list, user, smb_user_delete);
- }
-
/*
* Find a tree by tree-id.
*/
smb_tree_t *
smb_session_lookup_tree(
smb_session_t *session,
uint16_t tid)
-
{
smb_tree_t *tree;
SMB_SESSION_VALID(session);
--- 1043,1109 ----
* Find a user on the specified session by SMB UID.
*/
smb_user_t *
smb_session_lookup_uid(smb_session_t *session, uint16_t uid)
{
! return (smb_session_lookup_uid_st(session, 0, uid,
SMB_USER_STATE_LOGGED_ON));
}
+ /*
+ * Find a user on the specified session by SMB2 SSNID.
+ */
smb_user_t *
! smb_session_lookup_ssnid(smb_session_t *session, uint64_t ssnid)
{
+ return (smb_session_lookup_uid_st(session, ssnid, 0,
+ SMB_USER_STATE_LOGGED_ON));
+ }
+
+ smb_user_t *
+ smb_session_lookup_uid_st(smb_session_t *session, uint64_t ssnid,
+ uint16_t uid, smb_user_state_t st)
+ {
smb_user_t *user;
smb_llist_t *user_list;
SMB_SESSION_VALID(session);
user_list = &session->s_user_list;
smb_llist_enter(user_list, RW_READER);
! for (user = smb_llist_head(user_list);
! user != NULL;
! user = smb_llist_next(user_list, user)) {
!
SMB_USER_VALID(user);
ASSERT(user->u_session == session);
! if (user->u_ssnid != ssnid && user->u_uid != uid)
! continue;
!
! mutex_enter(&user->u_mutex);
! if (user->u_state == st) {
! // smb_user_hold_internal(user);
! user->u_refcnt++;
! mutex_exit(&user->u_mutex);
break;
}
! mutex_exit(&user->u_mutex);
}
smb_llist_exit(user_list);
return (user);
}
/*
* Find a tree by tree-id.
*/
smb_tree_t *
smb_session_lookup_tree(
smb_session_t *session,
uint16_t tid)
{
smb_tree_t *tree;
SMB_SESSION_VALID(session);
*** 975,1095 ****
smb_llist_exit(&session->s_tree_list);
return (NULL);
}
/*
- * Find the first connected tree that matches the specified sharename.
- * If the specified tree is NULL the search starts from the beginning of
- * the user's tree list. If a tree is provided the search starts just
- * after that tree.
- */
- smb_tree_t *
- smb_session_lookup_share(
- smb_session_t *session,
- const char *sharename,
- smb_tree_t *tree)
- {
- SMB_SESSION_VALID(session);
- ASSERT(sharename);
-
- smb_llist_enter(&session->s_tree_list, RW_READER);
-
- if (tree) {
- ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
- ASSERT(tree->t_session == session);
- tree = smb_llist_next(&session->s_tree_list, tree);
- } else {
- tree = smb_llist_head(&session->s_tree_list);
- }
-
- while (tree) {
- ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
- ASSERT(tree->t_session == session);
- if (smb_strcasecmp(tree->t_sharename, sharename, 0) == 0) {
- if (smb_tree_hold(tree)) {
- smb_llist_exit(&session->s_tree_list);
- return (tree);
- }
- }
- tree = smb_llist_next(&session->s_tree_list, tree);
- }
-
- smb_llist_exit(&session->s_tree_list);
- return (NULL);
- }
-
- /*
- * Find the first connected tree that matches the specified volume name.
- * If the specified tree is NULL the search starts from the beginning of
- * the user's tree list. If a tree is provided the search starts just
- * after that tree.
- */
- smb_tree_t *
- smb_session_lookup_volume(
- smb_session_t *session,
- const char *name,
- smb_tree_t *tree)
- {
- SMB_SESSION_VALID(session);
- ASSERT(name);
-
- smb_llist_enter(&session->s_tree_list, RW_READER);
-
- if (tree) {
- ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
- ASSERT(tree->t_session == session);
- tree = smb_llist_next(&session->s_tree_list, tree);
- } else {
- tree = smb_llist_head(&session->s_tree_list);
- }
-
- while (tree) {
- ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
- ASSERT(tree->t_session == session);
-
- if (smb_strcasecmp(tree->t_volume, name, 0) == 0) {
- if (smb_tree_hold(tree)) {
- smb_llist_exit(&session->s_tree_list);
- return (tree);
- }
- }
-
- tree = smb_llist_next(&session->s_tree_list, tree);
- }
-
- smb_llist_exit(&session->s_tree_list);
- return (NULL);
- }
-
- /*
* Disconnect all trees that match the specified client process-id.
*/
void
smb_session_close_pid(
smb_session_t *session,
uint32_t pid)
{
smb_tree_t *tree;
! SMB_SESSION_VALID(session);
! tree = smb_session_get_tree(session, NULL);
while (tree) {
! smb_tree_t *next;
! ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
! ASSERT(tree->t_session == session);
smb_tree_close_pid(tree, pid);
- next = smb_session_get_tree(session, tree);
smb_tree_release(tree);
- tree = next;
}
}
static void
! smb_session_tree_dtor(void *t)
{
! smb_tree_t *tree = (smb_tree_t *)t;
smb_tree_disconnect(tree, B_TRUE);
/* release the ref acquired during the traversal loop */
smb_tree_release(tree);
}
--- 1130,1168 ----
smb_llist_exit(&session->s_tree_list);
return (NULL);
}
/*
* Disconnect all trees that match the specified client process-id.
+ * Used by the SMB1 "process exit" request.
*/
void
smb_session_close_pid(
smb_session_t *session,
uint32_t pid)
{
+ smb_llist_t *tree_list = &session->s_tree_list;
smb_tree_t *tree;
! smb_llist_enter(tree_list, RW_READER);
! tree = smb_llist_head(tree_list);
while (tree) {
! if (smb_tree_hold(tree)) {
smb_tree_close_pid(tree, pid);
smb_tree_release(tree);
}
+ tree = smb_llist_next(tree_list, tree);
+ }
+
+ smb_llist_exit(tree_list);
}
static void
! smb_session_tree_dtor(void *arg)
{
! smb_tree_t *tree = arg;
smb_tree_disconnect(tree, B_TRUE);
/* release the ref acquired during the traversal loop */
smb_tree_release(tree);
}
*** 1116,1127 ****
if ((tree->t_owner == owner) &&
smb_tree_hold(tree)) {
/*
* smb_tree_hold() succeeded, hence we are in state
* SMB_TREE_STATE_CONNECTED; schedule this tree
! * for asynchronous disconnect, which will fire
! * after we drop the llist traversal lock.
*/
smb_llist_post(tree_list, tree, smb_session_tree_dtor);
}
tree = smb_llist_next(tree_list, tree);
}
--- 1189,1201 ----
if ((tree->t_owner == owner) &&
smb_tree_hold(tree)) {
/*
* smb_tree_hold() succeeded, hence we are in state
* SMB_TREE_STATE_CONNECTED; schedule this tree
! * for disconnect after smb_llist_exit because
! * the "unmap exec" up-call can block, and we'd
! * rather not block with the tree list locked.
*/
smb_llist_post(tree_list, tree, smb_session_tree_dtor);
}
tree = smb_llist_next(tree_list, tree);
}
*** 1131,1275 ****
}
/*
* Disconnect all trees that this user has connected.
*/
! void
smb_session_disconnect_trees(
smb_session_t *session)
{
smb_tree_t *tree;
! SMB_SESSION_VALID(session);
! tree = smb_session_get_tree(session, NULL);
while (tree) {
! ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
! ASSERT(tree->t_session == session);
! smb_tree_disconnect(tree, B_TRUE);
! smb_tree_release(tree);
! tree = smb_session_get_tree(session, NULL);
}
}
/*
! * Disconnect all trees that match the specified share name.
*/
! void
! smb_session_disconnect_share(
! smb_session_t *session,
! const char *sharename)
{
! smb_tree_t *tree;
! smb_tree_t *next;
! SMB_SESSION_VALID(session);
- tree = smb_session_lookup_share(session, sharename, NULL);
- while (tree) {
- ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
- ASSERT(tree->t_session == session);
- smb_session_cancel_requests(session, tree, NULL);
smb_tree_disconnect(tree, B_TRUE);
! next = smb_session_lookup_share(session, sharename, tree);
smb_tree_release(tree);
- tree = next;
- }
}
- void
- smb_session_post_tree(smb_session_t *session, smb_tree_t *tree)
- {
- SMB_SESSION_VALID(session);
- SMB_TREE_VALID(tree);
- ASSERT0(tree->t_refcnt);
- ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
- ASSERT(tree->t_session == session);
-
- smb_llist_post(&session->s_tree_list, tree, smb_tree_dealloc);
- }
-
/*
! * Get the next connected tree in the list. A reference is taken on
! * the tree, which can be released later with smb_tree_release().
! *
! * If the specified tree is NULL the search starts from the beginning of
! * the tree list. If a tree is provided the search starts just after
! * that tree.
! *
! * Returns NULL if there are no connected trees in the list.
*/
! static smb_tree_t *
! smb_session_get_tree(
smb_session_t *session,
! smb_tree_t *tree)
{
! smb_llist_t *tree_list;
SMB_SESSION_VALID(session);
- tree_list = &session->s_tree_list;
! smb_llist_enter(tree_list, RW_READER);
! if (tree) {
! ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
! tree = smb_llist_next(tree_list, tree);
! } else {
! tree = smb_llist_head(tree_list);
! }
! while (tree) {
! if (smb_tree_hold(tree))
! break;
! tree = smb_llist_next(tree_list, tree);
}
! smb_llist_exit(tree_list);
! return (tree);
}
/*
* Logoff all users associated with the specified session.
*/
! static void
smb_session_logoff(smb_session_t *session)
{
smb_user_t *user;
SMB_SESSION_VALID(session);
! smb_session_disconnect_trees(session);
! smb_llist_enter(&session->s_user_list, RW_READER);
!
! user = smb_llist_head(&session->s_user_list);
while (user) {
SMB_USER_VALID(user);
ASSERT(user->u_session == session);
switch (user->u_state) {
case SMB_USER_STATE_LOGGING_ON:
case SMB_USER_STATE_LOGGED_ON:
! smb_user_hold_internal(user);
smb_user_logoff(user);
smb_user_release(user);
break;
case SMB_USER_STATE_LOGGED_OFF:
case SMB_USER_STATE_LOGGING_OFF:
break;
default:
ASSERT(0);
break;
}
! user = smb_llist_next(&session->s_user_list, user);
}
! smb_llist_exit(&session->s_user_list);
}
/*
* Copy the session workstation/client name to buf. If the workstation
* is an empty string (which it will be on TCP connections), use the
--- 1205,1393 ----
}
/*
* Disconnect all trees that this user has connected.
*/
! static void
smb_session_disconnect_trees(
smb_session_t *session)
{
+ smb_llist_t *tree_list = &session->s_tree_list;
smb_tree_t *tree;
! smb_llist_enter(tree_list, RW_READER);
! tree = smb_llist_head(tree_list);
while (tree) {
! if (smb_tree_hold(tree)) {
! smb_llist_post(tree_list, tree,
! smb_session_tree_dtor);
}
+ tree = smb_llist_next(tree_list, tree);
+ }
+
+ /* drop the lock and flush the dtor queue */
+ smb_llist_exit(tree_list);
}
/*
! * Variant of smb_session_tree_dtor that also
! * cancels requests using this tree.
*/
! static void
! smb_session_tree_kill(void *arg)
{
! smb_tree_t *tree = arg;
! SMB_TREE_VALID(tree);
smb_tree_disconnect(tree, B_TRUE);
! smb_session_cancel_requests(tree->t_session, tree, NULL);
!
! /* release the ref acquired during the traversal loop */
smb_tree_release(tree);
}
/*
! * Disconnect all trees that match the specified share name,
! * and kill requests using those trees.
*/
! void
! smb_session_disconnect_share(
smb_session_t *session,
! const char *sharename)
{
! smb_llist_t *ll;
! smb_tree_t *tree;
SMB_SESSION_VALID(session);
! ll = &session->s_tree_list;
! smb_llist_enter(ll, RW_READER);
! for (tree = smb_llist_head(ll);
! tree != NULL;
! tree = smb_llist_next(ll, tree)) {
! SMB_TREE_VALID(tree);
! ASSERT(tree->t_session == session);
! if (smb_strcasecmp(tree->t_sharename, sharename, 0) != 0)
! continue;
!
! if (smb_tree_hold(tree)) {
! smb_llist_post(ll, tree,
! smb_session_tree_kill);
}
+ }
! smb_llist_exit(ll);
}
/*
* Logoff all users associated with the specified session.
+ *
+ * This is called for both server-initiated disconnect
+ * (SMB_SESSION_STATE_TERMINATED) and client-initiated
+ * disconnect (SMB_SESSION_STATE_DISCONNECTED).
+ * If client-initiated, save durable handles.
*/
! void
smb_session_logoff(smb_session_t *session)
{
+ smb_llist_t *ulist;
smb_user_t *user;
SMB_SESSION_VALID(session);
! top:
! ulist = &session->s_user_list;
! smb_llist_enter(ulist, RW_READER);
! user = smb_llist_head(ulist);
while (user) {
SMB_USER_VALID(user);
ASSERT(user->u_session == session);
+ mutex_enter(&user->u_mutex);
switch (user->u_state) {
case SMB_USER_STATE_LOGGING_ON:
case SMB_USER_STATE_LOGGED_ON:
! // smb_user_hold_internal(user);
! user->u_refcnt++;
! mutex_exit(&user->u_mutex);
smb_user_logoff(user);
smb_user_release(user);
break;
case SMB_USER_STATE_LOGGED_OFF:
case SMB_USER_STATE_LOGGING_OFF:
+ mutex_exit(&user->u_mutex);
break;
default:
+ mutex_exit(&user->u_mutex);
ASSERT(0);
break;
}
! user = smb_llist_next(ulist, user);
}
! /* Needed below (Was the list empty?) */
! user = smb_llist_head(ulist);
!
! smb_llist_exit(ulist);
!
! /*
! * It's possible for user objects to remain due to references
! * obtained via smb_server_lookup_ssnid(), when an SMB2
! * session setup is destroying a previous session.
! *
! * Wait for user objects to clear out (last refs. go away,
! * then smb_user_delete takes them out of the list). When
! * the last user object is removed, the session state is
! * set to SHUTDOWN and s_lock is signaled.
! *
! * Not all places that call smb_user_release necessarily
! * flush the delete queue, so after we wait for the list
! * to empty out, go back to the top and recheck the list
! * delete queue to make sure smb_user_delete happens.
! */
! if (user == NULL) {
! /* User list is empty. */
! smb_rwx_rwenter(&session->s_lock, RW_WRITER);
! session->s_state = SMB_SESSION_STATE_SHUTDOWN;
! smb_rwx_rwexit(&session->s_lock);
! } else {
! smb_rwx_rwenter(&session->s_lock, RW_READER);
! if (session->s_state != SMB_SESSION_STATE_SHUTDOWN) {
! (void) smb_rwx_cvwait(&session->s_lock,
! MSEC_TO_TICK(200));
! smb_rwx_rwexit(&session->s_lock);
! goto top;
! }
! smb_rwx_rwexit(&session->s_lock);
! }
! ASSERT(session->s_state == SMB_SESSION_STATE_SHUTDOWN);
!
! /*
! * User list should be empty now.
! */
! #ifdef DEBUG
! if (ulist->ll_count != 0) {
! cmn_err(CE_WARN, "user list not empty?");
! debug_enter("s_user_list");
! }
! #endif
!
! /*
! * User logoff happens first so we'll set preserve_opens
! * for client-initiated disconnect. When that's done
! * there should be no trees left, but check anyway.
! */
! smb_session_disconnect_trees(session);
}
/*
* Copy the session workstation/client name to buf. If the workstation
* is an empty string (which it will be on TCP connections), use the
*** 1318,1328 ****
* smb_request_alloc
*
* Allocate an smb_request_t structure from the kmem_cache. Partially
* initialize the found/new request.
*
! * Returns pointer to a request
*/
smb_request_t *
smb_request_alloc(smb_session_t *session, int req_length)
{
smb_request_t *sr;
--- 1436,1447 ----
* smb_request_alloc
*
* Allocate an smb_request_t structure from the kmem_cache. Partially
* initialize the found/new request.
*
! * Returns pointer to a request, or NULL if the session state is
! * one in which new requests are no longer allowed.
*/
smb_request_t *
smb_request_alloc(smb_session_t *session, int req_length)
{
smb_request_t *sr;
*** 1338,1348 ****
* whole thing and start over.
*/
bzero(sr, sizeof (smb_request_t));
mutex_init(&sr->sr_mutex, NULL, MUTEX_DEFAULT, NULL);
- cv_init(&sr->sr_ncr.nc_cv, NULL, CV_DEFAULT, NULL);
smb_srm_init(sr);
sr->session = session;
sr->sr_server = session->s_server;
sr->sr_gmtoff = session->s_server->si_gmtoff;
sr->sr_cfg = &session->s_cfg;
--- 1457,1466 ----
*** 1351,1361 ****
--- 1469,1509 ----
sr->sr_req_length = req_length;
if (req_length)
sr->sr_request_buf = kmem_alloc(req_length, KM_SLEEP);
sr->sr_magic = SMB_REQ_MAGIC;
sr->sr_state = SMB_REQ_STATE_INITIALIZING;
+
+ /*
+ * Only allow new SMB requests in some states.
+ */
+ smb_rwx_rwenter(&session->s_lock, RW_WRITER);
+ switch (session->s_state) {
+ case SMB_SESSION_STATE_CONNECTED:
+ case SMB_SESSION_STATE_INITIALIZED:
+ case SMB_SESSION_STATE_ESTABLISHED:
+ case SMB_SESSION_STATE_NEGOTIATED:
smb_slist_insert_tail(&session->s_req_list, sr);
+ break;
+
+ default:
+ ASSERT(0);
+ /* FALLTHROUGH */
+ case SMB_SESSION_STATE_DISCONNECTED:
+ case SMB_SESSION_STATE_SHUTDOWN:
+ case SMB_SESSION_STATE_TERMINATED:
+ /* Disallow new requests in these states. */
+ if (sr->sr_request_buf)
+ kmem_free(sr->sr_request_buf, sr->sr_req_length);
+ sr->session = NULL;
+ sr->sr_magic = 0;
+ mutex_destroy(&sr->sr_mutex);
+ kmem_cache_free(smb_cache_request, sr);
+ sr = NULL;
+ break;
+ }
+ smb_rwx_rwexit(&session->s_lock);
+
return (sr);
}
/*
* smb_request_free
*** 1366,1388 ****
smb_request_free(smb_request_t *sr)
{
ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
ASSERT(sr->session);
ASSERT(sr->r_xa == NULL);
- ASSERT(sr->sr_ncr.nc_fname == NULL);
if (sr->fid_ofile != NULL) {
- smb_ofile_request_complete(sr->fid_ofile);
smb_ofile_release(sr->fid_ofile);
}
if (sr->tid_tree != NULL)
smb_tree_release(sr->tid_tree);
if (sr->uid_user != NULL)
smb_user_release(sr->uid_user);
smb_slist_remove(&sr->session->s_req_list, sr);
sr->session = NULL;
smb_srm_fini(sr);
--- 1514,1543 ----
smb_request_free(smb_request_t *sr)
{
ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
ASSERT(sr->session);
ASSERT(sr->r_xa == NULL);
if (sr->fid_ofile != NULL) {
smb_ofile_release(sr->fid_ofile);
}
if (sr->tid_tree != NULL)
smb_tree_release(sr->tid_tree);
if (sr->uid_user != NULL)
smb_user_release(sr->uid_user);
+ if (sr->tform_ssn != NULL)
+ smb_user_release(sr->tform_ssn);
+
+ /*
+ * The above may have left work on the delete queues
+ */
+ smb_llist_flush(&sr->session->s_tree_list);
+ smb_llist_flush(&sr->session->s_user_list);
+
smb_slist_remove(&sr->session->s_req_list, sr);
sr->session = NULL;
smb_srm_fini(sr);
*** 1395,1405 ****
m_freem(sr->reply.chain);
if (sr->raw_data.chain)
m_freem(sr->raw_data.chain);
sr->sr_magic = 0;
- cv_destroy(&sr->sr_ncr.nc_cv);
mutex_destroy(&sr->sr_mutex);
kmem_cache_free(smb_cache_request, sr);
}
boolean_t
--- 1550,1559 ----
*** 1415,1463 ****
boolean_t
smb_session_levelII_oplocks(smb_session_t *session)
{
SMB_SESSION_VALID(session);
- /* Clients using SMB2 and later always know about oplocks. */
- if (session->dialect > NT_LM_0_12)
- return (B_TRUE);
-
/* Older clients only do Level II oplocks if negotiated. */
if ((session->capabilities & CAP_LEVEL_II_OPLOCKS) != 0)
return (B_TRUE);
return (B_FALSE);
}
- /*
- * smb_session_oplock_break
- *
- * Send an oplock break request to the client,
- * recalling some cache delegation.
- */
- void
- smb_session_oplock_break(smb_request_t *sr, uint8_t brk)
- {
- smb_session_t *session = sr->session;
- mbuf_chain_t *mbc = &sr->reply;
-
- SMB_SESSION_VALID(session);
-
- /*
- * Build the break message in sr->reply and then send it.
- * The mbc is free'd later, in smb_request_free().
- */
- mbc->max_bytes = MLEN;
- if (session->dialect <= NT_LM_0_12) {
- smb1_oplock_break_notification(sr, brk);
- } else {
- smb2_oplock_break_notification(sr, brk);
- }
-
- (void) smb_session_send(session, 0, mbc);
- }
-
static void
smb_session_genkey(smb_session_t *session)
{
uint8_t tmp_key[SMB_CHALLENGE_SZ];
--- 1569,1585 ----