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 ----