xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_session.c (revision 2c2961f8403049d948b9f3e6c35d6488b6b7e1aa)
1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21da6c28aaSamw /*
227f667e74Sjose borrego  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23da6c28aaSamw  * Use is subject to license terms.
24da6c28aaSamw  */
25da6c28aaSamw 
26da6c28aaSamw #include <sys/atomic.h>
27da6c28aaSamw #include <sys/strsubr.h>
28da6c28aaSamw #include <sys/synch.h>
29da6c28aaSamw #include <sys/types.h>
30da6c28aaSamw #include <sys/socketvar.h>
31da6c28aaSamw #include <sys/sdt.h>
32da6c28aaSamw #include <smbsrv/netbios.h>
33da6c28aaSamw #include <smbsrv/smb_incl.h>
34da6c28aaSamw #include <smbsrv/smb_i18n.h>
357f667e74Sjose borrego #include <inet/tcp.h>
36da6c28aaSamw 
37faa1795aSjb static volatile uint64_t smb_kids;
38da6c28aaSamw 
39faa1795aSjb uint32_t smb_keep_alive = SSN_KEEP_ALIVE_TIMEOUT;
40da6c28aaSamw 
41*2c2961f8Sjose borrego static void smb_session_cancel(smb_session_t *);
42da6c28aaSamw static int smb_session_message(smb_session_t *);
43da6c28aaSamw static int smb_session_xprt_puthdr(smb_session_t *, smb_xprt_t *,
44da6c28aaSamw     uint8_t *, size_t);
45b89a8333Snatalie li - Sun Microsystems - Irvine United States static smb_user_t *smb_session_lookup_user(smb_session_t *, char *, char *);
46*2c2961f8Sjose borrego static void smb_session_oplock_broken(smb_session_t *);
47faa1795aSjb static void smb_request_init_command_mbuf(smb_request_t *sr);
487f667e74Sjose borrego void dump_smb_inaddr(smb_inaddr_t *ipaddr);
49da6c28aaSamw 
50da6c28aaSamw void
51faa1795aSjb smb_session_timers(smb_session_list_t *se)
52da6c28aaSamw {
53faa1795aSjb 	smb_session_t	*session;
54da6c28aaSamw 
55faa1795aSjb 	rw_enter(&se->se_lock, RW_READER);
56faa1795aSjb 	session = list_head(&se->se_act.lst);
57faa1795aSjb 	while (session) {
58da6c28aaSamw 		/*
59da6c28aaSamw 		 * Walk through the table and decrement each keep_alive
60da6c28aaSamw 		 * timer that has not timed out yet. (keepalive > 0)
61da6c28aaSamw 		 */
62faa1795aSjb 		ASSERT(session->s_magic == SMB_SESSION_MAGIC);
63faa1795aSjb 		if (session->keep_alive &&
64faa1795aSjb 		    (session->keep_alive != (uint32_t)-1))
65faa1795aSjb 			session->keep_alive--;
66faa1795aSjb 		session = list_next(&se->se_act.lst, session);
67faa1795aSjb 	}
68faa1795aSjb 	rw_exit(&se->se_lock);
69faa1795aSjb }
70da6c28aaSamw 
71faa1795aSjb void
72faa1795aSjb smb_session_correct_keep_alive_values(
73faa1795aSjb     smb_session_list_t	*se,
74faa1795aSjb     uint32_t		new_keep_alive)
75faa1795aSjb {
76faa1795aSjb 	smb_session_t		*sn;
77faa1795aSjb 
78faa1795aSjb 	if (new_keep_alive == smb_keep_alive)
79faa1795aSjb 		return;
80faa1795aSjb 	/*
81faa1795aSjb 	 * keep alive == 0 means do not drop connection if it's idle
82faa1795aSjb 	 */
83faa1795aSjb 	smb_keep_alive = (new_keep_alive) ? new_keep_alive : -1;
84faa1795aSjb 
85faa1795aSjb 	/*
86faa1795aSjb 	 * Walk through the table and set each session to the new keep_alive
87faa1795aSjb 	 * value if they have not already timed out.  Block clock interrupts.
88faa1795aSjb 	 */
89faa1795aSjb 	rw_enter(&se->se_lock, RW_READER);
90faa1795aSjb 	sn = list_head(&se->se_rdy.lst);
91faa1795aSjb 	while (sn) {
92faa1795aSjb 		ASSERT(sn->s_magic == SMB_SESSION_MAGIC);
93faa1795aSjb 		sn->keep_alive = new_keep_alive;
94faa1795aSjb 		sn = list_next(&se->se_rdy.lst, sn);
95da6c28aaSamw 	}
96faa1795aSjb 	sn = list_head(&se->se_act.lst);
97faa1795aSjb 	while (sn) {
98faa1795aSjb 		ASSERT(sn->s_magic == SMB_SESSION_MAGIC);
99faa1795aSjb 		if (sn->keep_alive)
100faa1795aSjb 			sn->keep_alive = new_keep_alive;
101faa1795aSjb 		sn = list_next(&se->se_act.lst, sn);
102faa1795aSjb 	}
103faa1795aSjb 	rw_exit(&se->se_lock);
104da6c28aaSamw }
105da6c28aaSamw 
106da6c28aaSamw /*
107da6c28aaSamw  * smb_reconnection_check
108da6c28aaSamw  *
109da6c28aaSamw  * This function is called when a client indicates its current connection
110da6c28aaSamw  * should be the only one it has with the server, as indicated by VC=0 in
111da6c28aaSamw  * a SessionSetupX request. We go through the session list and destroy any
112da6c28aaSamw  * stale connections for that client.
113da6c28aaSamw  *
114da6c28aaSamw  * Clients don't associate IP addresses and servers. So a client may make
115da6c28aaSamw  * independent connections (i.e. with VC=0) to a server with multiple
116da6c28aaSamw  * IP addresses. So, when checking for a reconnection, we need to include
117da6c28aaSamw  * the local IP address, to which the client is connecting, when checking
118da6c28aaSamw  * for stale sessions.
119da6c28aaSamw  *
120da6c28aaSamw  * Also check the server's NetBIOS name to support simultaneous access by
121da6c28aaSamw  * multiple clients behind a NAT server.  This will only work for SMB over
122da6c28aaSamw  * NetBIOS on TCP port 139, it will not work SMB over TCP port 445 because
123da6c28aaSamw  * there is no NetBIOS name.  See also Knowledge Base article Q301673.
124da6c28aaSamw  */
125da6c28aaSamw void
1267f667e74Sjose borrego smb_session_reconnection_check(smb_session_list_t *se, smb_session_t *sess)
127da6c28aaSamw {
128faa1795aSjb 	smb_session_t	*sn;
129da6c28aaSamw 
130faa1795aSjb 	rw_enter(&se->se_lock, RW_READER);
131faa1795aSjb 	sn = list_head(&se->se_act.lst);
132faa1795aSjb 	while (sn) {
133da6c28aaSamw 		ASSERT(sn->s_magic == SMB_SESSION_MAGIC);
1347f667e74Sjose borrego 		if ((sn != sess) &&
1357f667e74Sjose borrego 		    smb_inet_equal(&sn->ipaddr, &sess->ipaddr,
1367f667e74Sjose borrego 		    SMB_INET_NOMASK) &&
1377f667e74Sjose borrego 		    smb_inet_equal(&sn->local_ipaddr, &sess->local_ipaddr,
1387f667e74Sjose borrego 		    SMB_INET_NOMASK) &&
1397f667e74Sjose borrego 		    (strcasecmp(sn->workstation, sess->workstation) == 0) &&
1407f667e74Sjose borrego 		    (sn->opentime <= sess->opentime) &&
1417f667e74Sjose borrego 		    (sn->s_kid < sess->s_kid)) {
142faa1795aSjb 			tsignal(sn->s_thread, SIGINT);
143da6c28aaSamw 		}
144faa1795aSjb 		sn = list_next(&se->se_act.lst, sn);
145da6c28aaSamw 	}
146faa1795aSjb 	rw_exit(&se->se_lock);
147da6c28aaSamw }
148da6c28aaSamw 
149da6c28aaSamw /*
150da6c28aaSamw  * Send a session message - supports SMB-over-NBT and SMB-over-TCP.
151da6c28aaSamw  *
152da6c28aaSamw  * The mbuf chain is copied into a contiguous buffer so that the whole
153da6c28aaSamw  * message is submitted to smb_sosend as a single request.  This should
154da6c28aaSamw  * help Ethereal/Wireshark delineate the packets correctly even though
155da6c28aaSamw  * TCP_NODELAY has been set on the socket.
156da6c28aaSamw  *
157da6c28aaSamw  * If an mbuf chain is provided, it will be freed and set to NULL here.
158da6c28aaSamw  */
159da6c28aaSamw int
16021b7895dSjb smb_session_send(smb_session_t *session, uint8_t type, mbuf_chain_t *mbc)
161da6c28aaSamw {
16221b7895dSjb 	smb_txreq_t	*txr;
1635cdbe942Sjb 	smb_xprt_t	hdr;
1645cdbe942Sjb 	int		rc;
165da6c28aaSamw 
166da6c28aaSamw 	switch (session->s_state) {
167da6c28aaSamw 	case SMB_SESSION_STATE_DISCONNECTED:
168da6c28aaSamw 	case SMB_SESSION_STATE_TERMINATED:
169da6c28aaSamw 		if ((mbc != NULL) && (mbc->chain != NULL)) {
170da6c28aaSamw 			m_freem(mbc->chain);
171da6c28aaSamw 			mbc->chain = NULL;
172da6c28aaSamw 			mbc->flags = 0;
173da6c28aaSamw 		}
174da6c28aaSamw 		return (ENOTCONN);
175da6c28aaSamw 	default:
176da6c28aaSamw 		break;
177da6c28aaSamw 	}
178da6c28aaSamw 
17921b7895dSjb 	txr = smb_net_txr_alloc();
180da6c28aaSamw 
181da6c28aaSamw 	if ((mbc != NULL) && (mbc->chain != NULL)) {
18221b7895dSjb 		rc = mbc_moveout(mbc, (caddr_t)&txr->tr_buf[NETBIOS_HDR_SZ],
18321b7895dSjb 		    sizeof (txr->tr_buf) - NETBIOS_HDR_SZ, &txr->tr_len);
18421b7895dSjb 		if (rc != 0) {
18521b7895dSjb 			smb_net_txr_free(txr);
18621b7895dSjb 			return (rc);
187da6c28aaSamw 		}
188da6c28aaSamw 	}
189da6c28aaSamw 
190da6c28aaSamw 	hdr.xh_type = type;
19121b7895dSjb 	hdr.xh_length = (uint32_t)txr->tr_len;
192da6c28aaSamw 
19321b7895dSjb 	rc = smb_session_xprt_puthdr(session, &hdr, txr->tr_buf,
1945cdbe942Sjb 	    NETBIOS_HDR_SZ);
19521b7895dSjb 
19621b7895dSjb 	if (rc != 0) {
19721b7895dSjb 		smb_net_txr_free(txr);
19821b7895dSjb 		return (rc);
199da6c28aaSamw 	}
20021b7895dSjb 	txr->tr_len += NETBIOS_HDR_SZ;
20121b7895dSjb 	return (smb_net_txr_send(session->sock, &session->s_txlst, txr));
202da6c28aaSamw }
203da6c28aaSamw 
204da6c28aaSamw /*
205da6c28aaSamw  * Read, process and respond to a NetBIOS session request.
206da6c28aaSamw  *
207da6c28aaSamw  * A NetBIOS session must be established for SMB-over-NetBIOS.  Validate
208da6c28aaSamw  * the calling and called name format and save the client NetBIOS name,
209da6c28aaSamw  * which is used when a NetBIOS session is established to check for and
210da6c28aaSamw  * cleanup leftover state from a previous session.
211da6c28aaSamw  *
212da6c28aaSamw  * Session requests are not valid for SMB-over-TCP, which is unfortunate
213da6c28aaSamw  * because without the client name leftover state cannot be cleaned up
214da6c28aaSamw  * if the client is behind a NAT server.
215da6c28aaSamw  */
216da6c28aaSamw static int
217da6c28aaSamw smb_session_request(struct smb_session *session)
218da6c28aaSamw {
219da6c28aaSamw 	int			rc;
220da6c28aaSamw 	char			*calling_name;
221da6c28aaSamw 	char			*called_name;
222da6c28aaSamw 	char 			client_name[NETBIOS_NAME_SZ];
223da6c28aaSamw 	struct mbuf_chain 	mbc;
224da6c28aaSamw 	char 			*names = NULL;
225da6c28aaSamw 	mts_wchar_t		*wbuf = NULL;
226da6c28aaSamw 	smb_xprt_t		hdr;
227da6c28aaSamw 	char *p;
228da6c28aaSamw 	unsigned int cpid = oem_get_smb_cpid();
229da6c28aaSamw 	int rc1, rc2;
230da6c28aaSamw 
231da6c28aaSamw 	session->keep_alive = smb_keep_alive;
232da6c28aaSamw 
233faa1795aSjb 	if ((rc = smb_session_xprt_gethdr(session, &hdr)) != 0)
234faa1795aSjb 		return (rc);
235da6c28aaSamw 
236da6c28aaSamw 	DTRACE_PROBE2(receive__session__req__xprthdr, struct session *, session,
237da6c28aaSamw 	    smb_xprt_t *, &hdr);
238da6c28aaSamw 
239da6c28aaSamw 	if ((hdr.xh_type != SESSION_REQUEST) ||
240da6c28aaSamw 	    (hdr.xh_length != NETBIOS_SESSION_REQUEST_DATA_LENGTH)) {
241da6c28aaSamw 		DTRACE_PROBE1(receive__session__req__failed,
242da6c28aaSamw 		    struct session *, session);
243da6c28aaSamw 		return (EINVAL);
244da6c28aaSamw 	}
245da6c28aaSamw 
246da6c28aaSamw 	names = kmem_alloc(hdr.xh_length, KM_SLEEP);
247da6c28aaSamw 
248da6c28aaSamw 	if ((rc = smb_sorecv(session->sock, names, hdr.xh_length)) != 0) {
249da6c28aaSamw 		kmem_free(names, hdr.xh_length);
250da6c28aaSamw 		DTRACE_PROBE1(receive__session__req__failed,
251da6c28aaSamw 		    struct session *, session);
252da6c28aaSamw 		return (rc);
253da6c28aaSamw 	}
254da6c28aaSamw 
255da6c28aaSamw 	DTRACE_PROBE3(receive__session__req__data, struct session *, session,
256da6c28aaSamw 	    char *, names, uint32_t, hdr.xh_length);
257da6c28aaSamw 
258da6c28aaSamw 	called_name = &names[0];
259da6c28aaSamw 	calling_name = &names[NETBIOS_ENCODED_NAME_SZ + 2];
260da6c28aaSamw 
261da6c28aaSamw 	rc1 = netbios_name_isvalid(called_name, 0);
262da6c28aaSamw 	rc2 = netbios_name_isvalid(calling_name, client_name);
263da6c28aaSamw 
264da6c28aaSamw 	if (rc1 == 0 || rc2 == 0) {
265da6c28aaSamw 
266da6c28aaSamw 		DTRACE_PROBE3(receive__invalid__session__req,
267da6c28aaSamw 		    struct session *, session, char *, names,
268da6c28aaSamw 		    uint32_t, hdr.xh_length);
269da6c28aaSamw 
270da6c28aaSamw 		kmem_free(names, hdr.xh_length);
271da6c28aaSamw 		MBC_INIT(&mbc, MAX_DATAGRAM_LENGTH);
2723db3f65cSamw 		(void) smb_mbc_encodef(&mbc, "b",
273da6c28aaSamw 		    DATAGRAM_INVALID_SOURCE_NAME_FORMAT);
274da6c28aaSamw 		(void) smb_session_send(session, NEGATIVE_SESSION_RESPONSE,
275da6c28aaSamw 		    &mbc);
276da6c28aaSamw 		return (EINVAL);
277da6c28aaSamw 	}
278da6c28aaSamw 
279da6c28aaSamw 	DTRACE_PROBE3(receive__session__req__calling__decoded,
280da6c28aaSamw 	    struct session *, session,
281da6c28aaSamw 	    char *, calling_name, char *, client_name);
282da6c28aaSamw 
283da6c28aaSamw 	/*
284da6c28aaSamw 	 * The client NetBIOS name is in oem codepage format.
285da6c28aaSamw 	 * We need to convert it to unicode and store it in
286da6c28aaSamw 	 * multi-byte format.  We also need to strip off any
287da6c28aaSamw 	 * spaces added as part of the NetBIOS name encoding.
288da6c28aaSamw 	 */
289da6c28aaSamw 	wbuf = kmem_alloc((SMB_PI_MAX_HOST * sizeof (mts_wchar_t)), KM_SLEEP);
290da6c28aaSamw 	(void) oemstounicodes(wbuf, client_name, SMB_PI_MAX_HOST, cpid);
291da6c28aaSamw 	(void) mts_wcstombs(session->workstation, wbuf, SMB_PI_MAX_HOST);
292da6c28aaSamw 	kmem_free(wbuf, (SMB_PI_MAX_HOST * sizeof (mts_wchar_t)));
293da6c28aaSamw 
294da6c28aaSamw 	if ((p = strchr(session->workstation, ' ')) != 0)
295da6c28aaSamw 		*p = '\0';
296da6c28aaSamw 
297da6c28aaSamw 	kmem_free(names, hdr.xh_length);
298da6c28aaSamw 	return (smb_session_send(session, POSITIVE_SESSION_RESPONSE, NULL));
299da6c28aaSamw }
300da6c28aaSamw 
301da6c28aaSamw /*
302da6c28aaSamw  * Read 4-byte header from the session socket and build an in-memory
303da6c28aaSamw  * session transport header.  See smb_xprt_t definition for header
304da6c28aaSamw  * format information.
305da6c28aaSamw  *
306da6c28aaSamw  * Direct hosted NetBIOS-less SMB (SMB-over-TCP) uses port 445.  The
307da6c28aaSamw  * first byte of the four-byte header must be 0 and the next three
308da6c28aaSamw  * bytes contain the length of the remaining data.
309da6c28aaSamw  */
310da6c28aaSamw int
311da6c28aaSamw smb_session_xprt_gethdr(smb_session_t *session, smb_xprt_t *ret_hdr)
312da6c28aaSamw {
313faa1795aSjb 	int		rc;
314faa1795aSjb 	unsigned char	buf[NETBIOS_HDR_SZ];
315da6c28aaSamw 
316faa1795aSjb 	if ((rc = smb_sorecv(session->sock, buf, NETBIOS_HDR_SZ)) != 0)
317faa1795aSjb 		return (rc);
318da6c28aaSamw 
319da6c28aaSamw 	switch (session->s_local_port) {
320da6c28aaSamw 	case SSN_SRVC_TCP_PORT:
321da6c28aaSamw 		ret_hdr->xh_type = buf[0];
322da6c28aaSamw 		ret_hdr->xh_length = (((uint32_t)buf[1] & 1) << 16) |
323da6c28aaSamw 		    ((uint32_t)buf[2] << 8) |
324da6c28aaSamw 		    ((uint32_t)buf[3]);
325da6c28aaSamw 		break;
326da6c28aaSamw 
327da6c28aaSamw 	case SMB_SRVC_TCP_PORT:
328da6c28aaSamw 		ret_hdr->xh_type = buf[0];
329da6c28aaSamw 
330da6c28aaSamw 		if (ret_hdr->xh_type != 0) {
3317f667e74Sjose borrego 			cmn_err(CE_WARN, "invalid type (%u)", ret_hdr->xh_type);
3327f667e74Sjose borrego 			dump_smb_inaddr(&session->ipaddr);
333faa1795aSjb 			return (EPROTO);
334da6c28aaSamw 		}
335da6c28aaSamw 
336da6c28aaSamw 		ret_hdr->xh_length = ((uint32_t)buf[1] << 16) |
337da6c28aaSamw 		    ((uint32_t)buf[2] << 8) |
338da6c28aaSamw 		    ((uint32_t)buf[3]);
339da6c28aaSamw 		break;
340da6c28aaSamw 
341da6c28aaSamw 	default:
3427f667e74Sjose borrego 		cmn_err(CE_WARN, "invalid port %u", session->s_local_port);
3437f667e74Sjose borrego 		dump_smb_inaddr(&session->ipaddr);
344faa1795aSjb 		return (EPROTO);
345da6c28aaSamw 	}
346da6c28aaSamw 
347da6c28aaSamw 	return (0);
348da6c28aaSamw }
349da6c28aaSamw 
350da6c28aaSamw /*
351da6c28aaSamw  * Encode a transport session packet header into a 4-byte buffer.
352da6c28aaSamw  * See smb_xprt_t definition for header format information.
353da6c28aaSamw  */
354da6c28aaSamw static int
355da6c28aaSamw smb_session_xprt_puthdr(smb_session_t *session, smb_xprt_t *hdr,
356da6c28aaSamw     uint8_t *buf, size_t buflen)
357da6c28aaSamw {
358da6c28aaSamw 	if (session == NULL || hdr == NULL ||
359da6c28aaSamw 	    buf == NULL || buflen < NETBIOS_HDR_SZ) {
360da6c28aaSamw 		return (-1);
361da6c28aaSamw 	}
362da6c28aaSamw 
363da6c28aaSamw 	switch (session->s_local_port) {
364da6c28aaSamw 	case SSN_SRVC_TCP_PORT:
365da6c28aaSamw 		buf[0] = hdr->xh_type;
366da6c28aaSamw 		buf[1] = ((hdr->xh_length >> 16) & 1);
367da6c28aaSamw 		buf[2] = (hdr->xh_length >> 8) & 0xff;
368da6c28aaSamw 		buf[3] = hdr->xh_length & 0xff;
369da6c28aaSamw 		break;
370da6c28aaSamw 
371da6c28aaSamw 	case SMB_SRVC_TCP_PORT:
372da6c28aaSamw 		buf[0] = hdr->xh_type;
373da6c28aaSamw 		buf[1] = (hdr->xh_length >> 16) & 0xff;
374da6c28aaSamw 		buf[2] = (hdr->xh_length >> 8) & 0xff;
375da6c28aaSamw 		buf[3] = hdr->xh_length & 0xff;
376da6c28aaSamw 		break;
377da6c28aaSamw 
378da6c28aaSamw 	default:
3797f667e74Sjose borrego 		cmn_err(CE_WARN, "invalid port %u", session->s_local_port);
3807f667e74Sjose borrego 		dump_smb_inaddr(&session->ipaddr);
381da6c28aaSamw 		return (-1);
382da6c28aaSamw 	}
383da6c28aaSamw 
384da6c28aaSamw 	return (0);
385da6c28aaSamw }
386da6c28aaSamw 
387faa1795aSjb static void
388da6c28aaSamw smb_request_init_command_mbuf(smb_request_t *sr)
389da6c28aaSamw {
390da6c28aaSamw 	MGET(sr->command.chain, 0, MT_DATA);
391da6c28aaSamw 
392da6c28aaSamw 	/*
393da6c28aaSamw 	 * Setup mbuf, mimic MCLGET but use the complete packet buffer.
394da6c28aaSamw 	 */
395da6c28aaSamw 	sr->command.chain->m_ext.ext_buf = sr->sr_request_buf;
396da6c28aaSamw 	sr->command.chain->m_data = sr->command.chain->m_ext.ext_buf;
397da6c28aaSamw 	sr->command.chain->m_len = sr->sr_req_length;
398da6c28aaSamw 	sr->command.chain->m_flags |= M_EXT;
399da6c28aaSamw 	sr->command.chain->m_ext.ext_size = sr->sr_req_length;
400da6c28aaSamw 	sr->command.chain->m_ext.ext_ref = &mclrefnoop;
401da6c28aaSamw 
402da6c28aaSamw 	/*
403da6c28aaSamw 	 * Initialize the rest of the mbuf_chain fields
404da6c28aaSamw 	 */
405da6c28aaSamw 	sr->command.flags = 0;
406da6c28aaSamw 	sr->command.shadow_of = 0;
407da6c28aaSamw 	sr->command.max_bytes = sr->sr_req_length;
408da6c28aaSamw 	sr->command.chain_offset = 0;
409da6c28aaSamw }
410da6c28aaSamw 
411da6c28aaSamw /*
412da6c28aaSamw  * smb_request_cancel
413da6c28aaSamw  *
414da6c28aaSamw  * Handle a cancel for a request properly depending on the current request
415da6c28aaSamw  * state.
416da6c28aaSamw  */
417da6c28aaSamw void
418da6c28aaSamw smb_request_cancel(smb_request_t *sr)
419da6c28aaSamw {
420da6c28aaSamw 	mutex_enter(&sr->sr_mutex);
421da6c28aaSamw 	switch (sr->sr_state) {
422da6c28aaSamw 
423da6c28aaSamw 	case SMB_REQ_STATE_SUBMITTED:
424da6c28aaSamw 	case SMB_REQ_STATE_ACTIVE:
425da6c28aaSamw 	case SMB_REQ_STATE_CLEANED_UP:
426da6c28aaSamw 		sr->sr_state = SMB_REQ_STATE_CANCELED;
427da6c28aaSamw 		break;
428da6c28aaSamw 
429da6c28aaSamw 	case SMB_REQ_STATE_WAITING_LOCK:
430da6c28aaSamw 		/*
431da6c28aaSamw 		 * This request is waiting on a lock.  Wakeup everything
432da6c28aaSamw 		 * waiting on the lock so that the relevant thread regains
433da6c28aaSamw 		 * control and notices that is has been canceled.  The
434da6c28aaSamw 		 * other lock request threads waiting on this lock will go
435da6c28aaSamw 		 * back to sleep when they discover they are still blocked.
436da6c28aaSamw 		 */
437da6c28aaSamw 		sr->sr_state = SMB_REQ_STATE_CANCELED;
438da6c28aaSamw 
439da6c28aaSamw 		ASSERT(sr->sr_awaiting != NULL);
440da6c28aaSamw 		mutex_enter(&sr->sr_awaiting->l_mutex);
441da6c28aaSamw 		cv_broadcast(&sr->sr_awaiting->l_cv);
442da6c28aaSamw 		mutex_exit(&sr->sr_awaiting->l_mutex);
443da6c28aaSamw 		break;
444da6c28aaSamw 
445da6c28aaSamw 	case SMB_REQ_STATE_WAITING_EVENT:
446da6c28aaSamw 	case SMB_REQ_STATE_EVENT_OCCURRED:
447da6c28aaSamw 		/*
448da6c28aaSamw 		 * Cancellations for these states are handled by the
449da6c28aaSamw 		 * notify-change code
450da6c28aaSamw 		 */
451da6c28aaSamw 		break;
452da6c28aaSamw 
453da6c28aaSamw 	case SMB_REQ_STATE_COMPLETED:
454da6c28aaSamw 	case SMB_REQ_STATE_CANCELED:
455da6c28aaSamw 		/*
456da6c28aaSamw 		 * No action required for these states since the request
457da6c28aaSamw 		 * is completing.
458da6c28aaSamw 		 */
459da6c28aaSamw 		break;
460da6c28aaSamw 	/*
461da6c28aaSamw 	 * Cases included:
462da6c28aaSamw 	 *	SMB_REQ_STATE_FREE:
463da6c28aaSamw 	 *	SMB_REQ_STATE_INITIALIZING:
464da6c28aaSamw 	 */
465da6c28aaSamw 	default:
466*2c2961f8Sjose borrego 		SMB_PANIC();
467da6c28aaSamw 	}
468da6c28aaSamw 	mutex_exit(&sr->sr_mutex);
469da6c28aaSamw }
470da6c28aaSamw 
471da6c28aaSamw /*
472da6c28aaSamw  * This is the entry point for processing SMB messages over NetBIOS or
473da6c28aaSamw  * SMB-over-TCP.
474da6c28aaSamw  *
475da6c28aaSamw  * NetBIOS connections require a session request to establish a session
476da6c28aaSamw  * on which to send session messages.
477da6c28aaSamw  *
478da6c28aaSamw  * Session requests are not valid on SMB-over-TCP.  We don't need to do
479da6c28aaSamw  * anything here as session requests will be treated as an error when
480da6c28aaSamw  * handling session messages.
481da6c28aaSamw  */
482faa1795aSjb int
483faa1795aSjb smb_session_daemon(smb_session_list_t *se)
484da6c28aaSamw {
485faa1795aSjb 	int		rc = 0;
486faa1795aSjb 	smb_session_t	*session;
487da6c28aaSamw 
488faa1795aSjb 	session = smb_session_list_activate_head(se);
489faa1795aSjb 	if (session == NULL)
490faa1795aSjb 		return (EINVAL);
491da6c28aaSamw 
492faa1795aSjb 	if (session->s_local_port == SSN_SRVC_TCP_PORT) {
493da6c28aaSamw 		rc = smb_session_request(session);
494faa1795aSjb 		if (rc) {
495faa1795aSjb 			smb_rwx_rwenter(&session->s_lock, RW_WRITER);
496faa1795aSjb 			session->s_state = SMB_SESSION_STATE_DISCONNECTED;
497faa1795aSjb 			smb_rwx_rwexit(&session->s_lock);
498faa1795aSjb 			smb_session_list_terminate(se, session);
499faa1795aSjb 			return (rc);
500faa1795aSjb 		}
501faa1795aSjb 	}
502da6c28aaSamw 
503da6c28aaSamw 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
504faa1795aSjb 	session->s_state = SMB_SESSION_STATE_ESTABLISHED;
505faa1795aSjb 	smb_rwx_rwexit(&session->s_lock);
506da6c28aaSamw 
507faa1795aSjb 	rc = smb_session_message(session);
508da6c28aaSamw 
509faa1795aSjb 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
510da6c28aaSamw 	session->s_state = SMB_SESSION_STATE_DISCONNECTED;
511da6c28aaSamw 	smb_rwx_rwexit(&session->s_lock);
512da6c28aaSamw 
513faa1795aSjb 	smb_soshutdown(session->sock);
514faa1795aSjb 
515da6c28aaSamw 	DTRACE_PROBE2(session__drop, struct session *, session, int, rc);
516da6c28aaSamw 
517da6c28aaSamw 	smb_session_cancel(session);
518da6c28aaSamw 
519da6c28aaSamw 	/*
520da6c28aaSamw 	 * At this point everything related to the session should have been
521da6c28aaSamw 	 * cleaned up and we expect that nothing will attempt to use the
522da6c28aaSamw 	 * socket.
523da6c28aaSamw 	 */
524faa1795aSjb 	smb_session_list_terminate(se, session);
525da6c28aaSamw 
526faa1795aSjb 	return (rc);
527da6c28aaSamw }
528da6c28aaSamw 
529da6c28aaSamw /*
530da6c28aaSamw  * Read and process SMB requests.
531da6c28aaSamw  *
532da6c28aaSamw  * Returns:
533da6c28aaSamw  *	0	Success
534da6c28aaSamw  *	1	Unable to read transport header
535da6c28aaSamw  *	2	Invalid transport header type
536da6c28aaSamw  *	3	Invalid SMB length (too small)
537da6c28aaSamw  *	4	Unable to read SMB header
538da6c28aaSamw  *	5	Invalid SMB header (bad magic number)
539da6c28aaSamw  *	6	Unable to read SMB data
540da6c28aaSamw  *	2x	Write raw failed
541da6c28aaSamw  */
542da6c28aaSamw static int
543da6c28aaSamw smb_session_message(smb_session_t *session)
544da6c28aaSamw {
545faa1795aSjb 	smb_request_t	*sr = NULL;
546faa1795aSjb 	smb_xprt_t	hdr;
547faa1795aSjb 	uint8_t		*req_buf;
548faa1795aSjb 	uint32_t	resid;
549faa1795aSjb 	int		rc;
550da6c28aaSamw 
551faa1795aSjb 	for (;;) {
552faa1795aSjb 
553faa1795aSjb 		rc = smb_session_xprt_gethdr(session, &hdr);
554faa1795aSjb 		if (rc)
555faa1795aSjb 			return (rc);
556faa1795aSjb 
557faa1795aSjb 		DTRACE_PROBE2(session__receive__xprthdr, session_t *, session,
558faa1795aSjb 		    smb_xprt_t *, &hdr);
559faa1795aSjb 
560faa1795aSjb 		if (hdr.xh_type != SESSION_MESSAGE) {
561faa1795aSjb 			/*
562faa1795aSjb 			 * Anything other than SESSION_MESSAGE or
563faa1795aSjb 			 * SESSION_KEEP_ALIVE is an error.  A SESSION_REQUEST
564faa1795aSjb 			 * may indicate a new session request but we need to
565faa1795aSjb 			 * close this session and we can treat it as an error
566faa1795aSjb 			 * here.
567faa1795aSjb 			 */
568faa1795aSjb 			if (hdr.xh_type == SESSION_KEEP_ALIVE) {
569faa1795aSjb 				session->keep_alive = smb_keep_alive;
570da6c28aaSamw 				continue;
571da6c28aaSamw 			}
572faa1795aSjb 			return (EPROTO);
573da6c28aaSamw 		}
574da6c28aaSamw 
575faa1795aSjb 		if (hdr.xh_length < SMB_HEADER_LEN)
576faa1795aSjb 			return (EPROTO);
577da6c28aaSamw 
578faa1795aSjb 		session->keep_alive = smb_keep_alive;
579da6c28aaSamw 
580da6c28aaSamw 		/*
581faa1795aSjb 		 * Allocate a request context, read the SMB header and validate
582faa1795aSjb 		 * it. The sr includes a buffer large enough to hold the SMB
583faa1795aSjb 		 * request payload.  If the header looks valid, read any
584faa1795aSjb 		 * remaining data.
585da6c28aaSamw 		 */
586faa1795aSjb 		sr = smb_request_alloc(session, hdr.xh_length);
587da6c28aaSamw 
588faa1795aSjb 		req_buf = (uint8_t *)sr->sr_request_buf;
589faa1795aSjb 		resid = hdr.xh_length;
590da6c28aaSamw 
591faa1795aSjb 		rc = smb_sorecv(session->sock, req_buf, SMB_HEADER_LEN);
592faa1795aSjb 		if (rc) {
593faa1795aSjb 			smb_request_free(sr);
594faa1795aSjb 			return (rc);
595faa1795aSjb 		}
596da6c28aaSamw 
597faa1795aSjb 		if (SMB_PROTOCOL_MAGIC_INVALID(sr)) {
598faa1795aSjb 			smb_request_free(sr);
599faa1795aSjb 			return (EPROTO);
600faa1795aSjb 		}
601da6c28aaSamw 
602faa1795aSjb 		if (resid > SMB_HEADER_LEN) {
603faa1795aSjb 			req_buf += SMB_HEADER_LEN;
604faa1795aSjb 			resid -= SMB_HEADER_LEN;
605da6c28aaSamw 
606faa1795aSjb 			rc = smb_sorecv(session->sock, req_buf, resid);
607faa1795aSjb 			if (rc) {
608faa1795aSjb 				smb_request_free(sr);
609faa1795aSjb 				return (rc);
610da6c28aaSamw 			}
611da6c28aaSamw 		}
612da6c28aaSamw 
613faa1795aSjb 		/*
614faa1795aSjb 		 * Initialize command MBC to represent the received data.
615faa1795aSjb 		 */
616faa1795aSjb 		smb_request_init_command_mbuf(sr);
617da6c28aaSamw 
618faa1795aSjb 		DTRACE_PROBE1(session__receive__smb, smb_request_t *, sr);
619da6c28aaSamw 
620da6c28aaSamw 		/*
621faa1795aSjb 		 * If this is a raw write, hand off the request.  The handler
622faa1795aSjb 		 * will retrieve the remaining raw data and process the request.
623da6c28aaSamw 		 */
624faa1795aSjb 		if (SMB_IS_WRITERAW(sr)) {
625faa1795aSjb 			rc = smb_handle_write_raw(session, sr);
626faa1795aSjb 			/* XXX smb_request_free(sr); ??? */
627faa1795aSjb 			return (rc);
628faa1795aSjb 		}
629da6c28aaSamw 
630faa1795aSjb 		sr->sr_state = SMB_REQ_STATE_SUBMITTED;
631faa1795aSjb 		(void) taskq_dispatch(session->s_server->sv_thread_pool,
632faa1795aSjb 		    smb_session_worker, sr, TQ_SLEEP);
633da6c28aaSamw 	}
634da6c28aaSamw }
635da6c28aaSamw 
636da6c28aaSamw /*
637da6c28aaSamw  * Port will be SSN_SRVC_TCP_PORT or SMB_SRVC_TCP_PORT.
638da6c28aaSamw  */
639da6c28aaSamw smb_session_t *
6407f667e74Sjose borrego smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
6417f667e74Sjose borrego     int family)
642da6c28aaSamw {
643da6c28aaSamw 	struct sockaddr_in	sin;
6440f1702c5SYu Xiangning 	socklen_t		slen;
6457f667e74Sjose borrego 	struct sockaddr_in6	sin6;
646da6c28aaSamw 	smb_session_t		*session;
647da6c28aaSamw 
648faa1795aSjb 	session = kmem_cache_alloc(sv->si_cache_session, KM_SLEEP);
649da6c28aaSamw 	bzero(session, sizeof (smb_session_t));
650da6c28aaSamw 
651da6c28aaSamw 	if (smb_idpool_constructor(&session->s_uid_pool)) {
652faa1795aSjb 		kmem_cache_free(sv->si_cache_session, session);
653da6c28aaSamw 		return (NULL);
654da6c28aaSamw 	}
655da6c28aaSamw 
656da6c28aaSamw 	session->s_kid = SMB_NEW_KID();
657faa1795aSjb 	session->s_state = SMB_SESSION_STATE_INITIALIZED;
658da6c28aaSamw 	session->native_os = NATIVE_OS_UNKNOWN;
659da6c28aaSamw 	session->opentime = lbolt64;
660da6c28aaSamw 	session->keep_alive = smb_keep_alive;
661da6c28aaSamw 	session->activity_timestamp = lbolt64;
662da6c28aaSamw 
663da6c28aaSamw 	smb_slist_constructor(&session->s_req_list, sizeof (smb_request_t),
664da6c28aaSamw 	    offsetof(smb_request_t, sr_session_lnd));
665da6c28aaSamw 
666da6c28aaSamw 	smb_llist_constructor(&session->s_user_list, sizeof (smb_user_t),
667da6c28aaSamw 	    offsetof(smb_user_t, u_lnd));
668da6c28aaSamw 
669da6c28aaSamw 	smb_llist_constructor(&session->s_xa_list, sizeof (smb_xa_t),
670da6c28aaSamw 	    offsetof(smb_xa_t, xa_lnd));
671da6c28aaSamw 
672*2c2961f8Sjose borrego 	list_create(&session->s_oplock_brkreqs, sizeof (mbuf_chain_t),
673*2c2961f8Sjose borrego 	    offsetof(mbuf_chain_t, mbc_lnd));
674*2c2961f8Sjose borrego 
6755cdbe942Sjb 	smb_net_txl_constructor(&session->s_txlst);
6765cdbe942Sjb 
677da6c28aaSamw 	smb_rwx_init(&session->s_lock);
678da6c28aaSamw 
679faa1795aSjb 	if (new_so) {
6807f667e74Sjose borrego 		if (family == AF_INET) {
6817f667e74Sjose borrego 			slen = sizeof (sin);
6827f667e74Sjose borrego 			(void) ksocket_getsockname(new_so,
6837f667e74Sjose borrego 			    (struct sockaddr *)&sin, &slen, CRED());
6847f667e74Sjose borrego 			bcopy(&sin, &session->local_ipaddr.a_ip, slen);
6857f667e74Sjose borrego 			(void) ksocket_getpeername(new_so,
6867f667e74Sjose borrego 			    (struct sockaddr *)&sin, &slen, CRED());
687743a77edSAlan Wright 			bcopy(&sin, &session->ipaddr.a_ip, slen);
6887f667e74Sjose borrego 		} else {
6897f667e74Sjose borrego 			slen = sizeof (sin6);
6907f667e74Sjose borrego 			(void) ksocket_getsockname(new_so,
6917f667e74Sjose borrego 			    (struct sockaddr *)&sin6, &slen, CRED());
692743a77edSAlan Wright 			bcopy(&sin6, &session->local_ipaddr.a_ip, slen);
6937f667e74Sjose borrego 			(void) ksocket_getpeername(new_so,
6947f667e74Sjose borrego 			    (struct sockaddr *)&sin6, &slen, CRED());
6957f667e74Sjose borrego 			bcopy(&sin6, &session->ipaddr.a_ip, slen);
6967f667e74Sjose borrego 		}
6977f667e74Sjose borrego 		session->ipaddr.a_family = family;
6987f667e74Sjose borrego 		session->local_ipaddr.a_family = family;
699faa1795aSjb 		session->s_local_port = port;
700faa1795aSjb 		session->sock = new_so;
701faa1795aSjb 	}
702da6c28aaSamw 
703faa1795aSjb 	session->s_server = sv;
704faa1795aSjb 	smb_server_get_cfg(sv, &session->s_cfg);
705faa1795aSjb 	session->s_cache_request = sv->si_cache_request;
706faa1795aSjb 	session->s_cache = sv->si_cache_session;
707da6c28aaSamw 	session->s_magic = SMB_SESSION_MAGIC;
708da6c28aaSamw 	return (session);
709da6c28aaSamw }
710da6c28aaSamw 
711da6c28aaSamw void
712da6c28aaSamw smb_session_delete(smb_session_t *session)
713da6c28aaSamw {
714*2c2961f8Sjose borrego 	mbuf_chain_t	*mbc;
715*2c2961f8Sjose borrego 
716da6c28aaSamw 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
717da6c28aaSamw 
718*2c2961f8Sjose borrego 	session->s_magic = 0;
719da6c28aaSamw 
720da6c28aaSamw 	smb_rwx_destroy(&session->s_lock);
7215cdbe942Sjb 	smb_net_txl_destructor(&session->s_txlst);
722*2c2961f8Sjose borrego 
723*2c2961f8Sjose borrego 	while ((mbc = list_head(&session->s_oplock_brkreqs)) != NULL) {
724*2c2961f8Sjose borrego 		SMB_MBC_VALID(mbc);
725*2c2961f8Sjose borrego 		list_remove(&session->s_oplock_brkreqs, mbc);
726*2c2961f8Sjose borrego 		smb_mbc_free(mbc);
727*2c2961f8Sjose borrego 	}
728*2c2961f8Sjose borrego 	list_destroy(&session->s_oplock_brkreqs);
729*2c2961f8Sjose borrego 
730da6c28aaSamw 	smb_slist_destructor(&session->s_req_list);
731da6c28aaSamw 	smb_llist_destructor(&session->s_user_list);
732da6c28aaSamw 	smb_llist_destructor(&session->s_xa_list);
733da6c28aaSamw 
734da6c28aaSamw 	ASSERT(session->s_tree_cnt == 0);
735da6c28aaSamw 	ASSERT(session->s_file_cnt == 0);
736da6c28aaSamw 	ASSERT(session->s_dir_cnt == 0);
737da6c28aaSamw 
738da6c28aaSamw 	smb_idpool_destructor(&session->s_uid_pool);
739faa1795aSjb 	kmem_cache_free(session->s_cache, session);
740da6c28aaSamw }
741da6c28aaSamw 
742*2c2961f8Sjose borrego static void
743da6c28aaSamw smb_session_cancel(smb_session_t *session)
744da6c28aaSamw {
745da6c28aaSamw 	smb_xa_t	*xa, *nextxa;
746da6c28aaSamw 
747da6c28aaSamw 	/* All the request currently being treated must be canceled. */
748c8ec8eeaSjose borrego 	smb_session_cancel_requests(session, NULL, NULL);
749da6c28aaSamw 
750da6c28aaSamw 	/*
751da6c28aaSamw 	 * We wait for the completion of all the requests associated with
752da6c28aaSamw 	 * this session.
753da6c28aaSamw 	 */
754da6c28aaSamw 	smb_slist_wait_for_empty(&session->s_req_list);
755da6c28aaSamw 
756da6c28aaSamw 	/*
757da6c28aaSamw 	 * At this point the reference count of the users, trees, files,
758da6c28aaSamw 	 * directories should be zero. It should be possible to destroy them
759da6c28aaSamw 	 * without any problem.
760da6c28aaSamw 	 */
761da6c28aaSamw 	xa = smb_llist_head(&session->s_xa_list);
762da6c28aaSamw 	while (xa) {
763da6c28aaSamw 		nextxa = smb_llist_next(&session->s_xa_list, xa);
764da6c28aaSamw 		smb_xa_close(xa);
765da6c28aaSamw 		xa = nextxa;
766da6c28aaSamw 	}
767*2c2961f8Sjose borrego 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
768da6c28aaSamw 	smb_user_logoff_all(session);
769*2c2961f8Sjose borrego 	smb_rwx_rwexit(&session->s_lock);
770da6c28aaSamw }
771da6c28aaSamw 
772c8ec8eeaSjose borrego /*
773c8ec8eeaSjose borrego  * Cancel requests.  If a non-null tree is specified, only requests specific
774c8ec8eeaSjose borrego  * to that tree will be cancelled.  If a non-null sr is specified, that sr
775c8ec8eeaSjose borrego  * will be not be cancelled - this would typically be the caller's sr.
776c8ec8eeaSjose borrego  */
777da6c28aaSamw void
778da6c28aaSamw smb_session_cancel_requests(
779c8ec8eeaSjose borrego     smb_session_t	*session,
780c8ec8eeaSjose borrego     smb_tree_t		*tree,
781c8ec8eeaSjose borrego     smb_request_t	*exclude_sr)
782da6c28aaSamw {
783da6c28aaSamw 	smb_request_t	*sr;
784da6c28aaSamw 
785c8ec8eeaSjose borrego 	smb_process_session_notify_change_queue(session, tree);
786da6c28aaSamw 
787da6c28aaSamw 	smb_slist_enter(&session->s_req_list);
788da6c28aaSamw 	sr = smb_slist_head(&session->s_req_list);
789c8ec8eeaSjose borrego 
790da6c28aaSamw 	while (sr) {
791da6c28aaSamw 		ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
792c8ec8eeaSjose borrego 		if ((sr != exclude_sr) &&
793c8ec8eeaSjose borrego 		    (tree == NULL || sr->tid_tree == tree))
794c8ec8eeaSjose borrego 			smb_request_cancel(sr);
795da6c28aaSamw 
796c8ec8eeaSjose borrego 		sr = smb_slist_next(&session->s_req_list, sr);
797da6c28aaSamw 	}
798c8ec8eeaSjose borrego 
799da6c28aaSamw 	smb_slist_exit(&session->s_req_list);
800da6c28aaSamw }
801da6c28aaSamw 
802da6c28aaSamw void
803*2c2961f8Sjose borrego smb_session_worker(void	*arg)
804da6c28aaSamw {
805da6c28aaSamw 	smb_request_t	*sr;
806da6c28aaSamw 
807da6c28aaSamw 	sr = (smb_request_t *)arg;
808da6c28aaSamw 
809da6c28aaSamw 	ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
810da6c28aaSamw 
811*2c2961f8Sjose borrego 	sr->sr_worker = curthread;
812da6c28aaSamw 	mutex_enter(&sr->sr_mutex);
813da6c28aaSamw 	switch (sr->sr_state) {
814da6c28aaSamw 	case SMB_REQ_STATE_SUBMITTED:
815da6c28aaSamw 		mutex_exit(&sr->sr_mutex);
81659229f98Sjose borrego 		if (smb_dispatch_request(sr)) {
81759229f98Sjose borrego 			mutex_enter(&sr->sr_mutex);
818da6c28aaSamw 			sr->sr_state = SMB_REQ_STATE_COMPLETED;
819da6c28aaSamw 			mutex_exit(&sr->sr_mutex);
820da6c28aaSamw 			smb_request_free(sr);
821da6c28aaSamw 		}
822da6c28aaSamw 		break;
823da6c28aaSamw 
824da6c28aaSamw 	default:
825da6c28aaSamw 		ASSERT(sr->sr_state == SMB_REQ_STATE_CANCELED);
826da6c28aaSamw 		sr->sr_state = SMB_REQ_STATE_COMPLETED;
827da6c28aaSamw 		mutex_exit(&sr->sr_mutex);
828da6c28aaSamw 		smb_request_free(sr);
829da6c28aaSamw 		break;
830da6c28aaSamw 	}
831da6c28aaSamw }
832da6c28aaSamw 
833da6c28aaSamw /*
834da6c28aaSamw  * smb_session_disconnect_share
835da6c28aaSamw  *
836da6c28aaSamw  * Disconnects the specified share. This function should be called after the
837da6c28aaSamw  * share passed in has been made unavailable by the "share manager".
838da6c28aaSamw  */
839da6c28aaSamw void
840faa1795aSjb smb_session_disconnect_share(smb_session_list_t *se, char *sharename)
841da6c28aaSamw {
842da6c28aaSamw 	smb_session_t	*session;
843da6c28aaSamw 
844faa1795aSjb 	rw_enter(&se->se_lock, RW_READER);
845faa1795aSjb 	session = list_head(&se->se_act.lst);
846faa1795aSjb 	while (session) {
847da6c28aaSamw 		ASSERT(session->s_magic == SMB_SESSION_MAGIC);
848da6c28aaSamw 		smb_rwx_rwenter(&session->s_lock, RW_READER);
849da6c28aaSamw 		switch (session->s_state) {
850da6c28aaSamw 		case SMB_SESSION_STATE_NEGOTIATED:
851da6c28aaSamw 		case SMB_SESSION_STATE_OPLOCK_BREAKING:
852da6c28aaSamw 		case SMB_SESSION_STATE_WRITE_RAW_ACTIVE: {
853da6c28aaSamw 			smb_user_t	*user;
854da6c28aaSamw 			smb_user_t	*next;
855da6c28aaSamw 
856da6c28aaSamw 			user = smb_user_lookup_by_state(session, NULL);
857da6c28aaSamw 			while (user) {
858da6c28aaSamw 				smb_user_disconnect_share(user, sharename);
859da6c28aaSamw 				next = smb_user_lookup_by_state(session, user);
860da6c28aaSamw 				smb_user_release(user);
861da6c28aaSamw 				user = next;
862da6c28aaSamw 			}
863da6c28aaSamw 			break;
864da6c28aaSamw 
865da6c28aaSamw 		}
866da6c28aaSamw 		default:
867da6c28aaSamw 			break;
868da6c28aaSamw 		}
869da6c28aaSamw 		smb_rwx_rwexit(&session->s_lock);
870faa1795aSjb 		session = list_next(&se->se_act.lst, session);
871faa1795aSjb 	}
872faa1795aSjb 	rw_exit(&se->se_lock);
873faa1795aSjb }
874faa1795aSjb 
875faa1795aSjb void
876faa1795aSjb smb_session_list_constructor(smb_session_list_t *se)
877faa1795aSjb {
878faa1795aSjb 	bzero(se, sizeof (*se));
879faa1795aSjb 	rw_init(&se->se_lock, NULL, RW_DEFAULT, NULL);
880faa1795aSjb 	list_create(&se->se_rdy.lst, sizeof (smb_session_t),
881faa1795aSjb 	    offsetof(smb_session_t, s_lnd));
882faa1795aSjb 	list_create(&se->se_act.lst, sizeof (smb_session_t),
883faa1795aSjb 	    offsetof(smb_session_t, s_lnd));
884faa1795aSjb }
885faa1795aSjb 
886faa1795aSjb void
887faa1795aSjb smb_session_list_destructor(smb_session_list_t *se)
888faa1795aSjb {
889faa1795aSjb 	list_destroy(&se->se_rdy.lst);
890faa1795aSjb 	list_destroy(&se->se_act.lst);
891faa1795aSjb 	rw_destroy(&se->se_lock);
892faa1795aSjb }
893faa1795aSjb 
894faa1795aSjb void
895faa1795aSjb smb_session_list_append(smb_session_list_t *se, smb_session_t *session)
896faa1795aSjb {
897faa1795aSjb 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
898faa1795aSjb 	ASSERT(session->s_state == SMB_SESSION_STATE_INITIALIZED);
899faa1795aSjb 
900faa1795aSjb 	rw_enter(&se->se_lock, RW_WRITER);
901faa1795aSjb 	list_insert_tail(&se->se_rdy.lst, session);
902faa1795aSjb 	se->se_rdy.count++;
903faa1795aSjb 	se->se_wrop++;
904faa1795aSjb 	rw_exit(&se->se_lock);
905faa1795aSjb }
906faa1795aSjb 
907faa1795aSjb void
908faa1795aSjb smb_session_list_delete_tail(smb_session_list_t *se)
909faa1795aSjb {
910faa1795aSjb 	smb_session_t	*session;
911faa1795aSjb 
912faa1795aSjb 	rw_enter(&se->se_lock, RW_WRITER);
913faa1795aSjb 	session = list_tail(&se->se_rdy.lst);
914faa1795aSjb 	if (session) {
915faa1795aSjb 		ASSERT(session->s_magic == SMB_SESSION_MAGIC);
916faa1795aSjb 		ASSERT(session->s_state == SMB_SESSION_STATE_INITIALIZED);
917faa1795aSjb 		list_remove(&se->se_rdy.lst, session);
918faa1795aSjb 		ASSERT(se->se_rdy.count);
919faa1795aSjb 		se->se_rdy.count--;
920faa1795aSjb 		rw_exit(&se->se_lock);
921faa1795aSjb 		smb_session_delete(session);
922faa1795aSjb 		return;
923faa1795aSjb 	}
924faa1795aSjb 	rw_exit(&se->se_lock);
925faa1795aSjb }
926faa1795aSjb 
927faa1795aSjb smb_session_t *
928faa1795aSjb smb_session_list_activate_head(smb_session_list_t *se)
929faa1795aSjb {
930faa1795aSjb 	smb_session_t	*session;
931faa1795aSjb 
932faa1795aSjb 	rw_enter(&se->se_lock, RW_WRITER);
933faa1795aSjb 	session = list_head(&se->se_rdy.lst);
934faa1795aSjb 	if (session) {
935faa1795aSjb 		ASSERT(session->s_magic == SMB_SESSION_MAGIC);
936faa1795aSjb 		smb_rwx_rwenter(&session->s_lock, RW_WRITER);
937faa1795aSjb 		ASSERT(session->s_state == SMB_SESSION_STATE_INITIALIZED);
938faa1795aSjb 		session->s_thread = curthread;
939faa1795aSjb 		session->s_ktdid = session->s_thread->t_did;
940faa1795aSjb 		smb_rwx_rwexit(&session->s_lock);
941faa1795aSjb 		list_remove(&se->se_rdy.lst, session);
942faa1795aSjb 		se->se_rdy.count--;
943faa1795aSjb 		list_insert_tail(&se->se_act.lst, session);
944faa1795aSjb 		se->se_act.count++;
945faa1795aSjb 		se->se_wrop++;
946faa1795aSjb 	}
947faa1795aSjb 	rw_exit(&se->se_lock);
948faa1795aSjb 	return (session);
949faa1795aSjb }
950faa1795aSjb 
951faa1795aSjb void
952faa1795aSjb smb_session_list_terminate(smb_session_list_t *se, smb_session_t *session)
953faa1795aSjb {
954faa1795aSjb 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
955faa1795aSjb 
956faa1795aSjb 	rw_enter(&se->se_lock, RW_WRITER);
957faa1795aSjb 
958faa1795aSjb 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
959faa1795aSjb 	ASSERT(session->s_state == SMB_SESSION_STATE_DISCONNECTED);
960faa1795aSjb 	session->s_state = SMB_SESSION_STATE_TERMINATED;
961faa1795aSjb 	smb_sodestroy(session->sock);
962faa1795aSjb 	session->sock = NULL;
963faa1795aSjb 	smb_rwx_rwexit(&session->s_lock);
964faa1795aSjb 
965faa1795aSjb 	list_remove(&se->se_act.lst, session);
966faa1795aSjb 	se->se_act.count--;
967faa1795aSjb 	se->se_wrop++;
968faa1795aSjb 
969faa1795aSjb 	ASSERT(session->s_thread == curthread);
970faa1795aSjb 
971faa1795aSjb 	rw_exit(&se->se_lock);
972faa1795aSjb 
973faa1795aSjb 	smb_session_delete(session);
974faa1795aSjb }
975faa1795aSjb 
976faa1795aSjb /*
977faa1795aSjb  * smb_session_list_signal
978faa1795aSjb  *
979faa1795aSjb  * This function signals all the session threads. The intent is to terminate
980faa1795aSjb  * them. The sessions still in the SMB_SESSION_STATE_INITIALIZED are delete
981faa1795aSjb  * immediately.
982faa1795aSjb  *
983faa1795aSjb  * This function must only be called by the threads listening and accepting
984faa1795aSjb  * connections. They must pass in their respective session list.
985faa1795aSjb  */
986faa1795aSjb void
987faa1795aSjb smb_session_list_signal(smb_session_list_t *se)
988faa1795aSjb {
989faa1795aSjb 	smb_session_t	*session;
990faa1795aSjb 
991faa1795aSjb 	rw_enter(&se->se_lock, RW_WRITER);
992faa1795aSjb 	while (session = list_head(&se->se_rdy.lst)) {
993faa1795aSjb 
994faa1795aSjb 		ASSERT(session->s_magic == SMB_SESSION_MAGIC);
995faa1795aSjb 
996faa1795aSjb 		smb_rwx_rwenter(&session->s_lock, RW_WRITER);
997faa1795aSjb 		ASSERT(session->s_state == SMB_SESSION_STATE_INITIALIZED);
998faa1795aSjb 		session->s_state = SMB_SESSION_STATE_TERMINATED;
999faa1795aSjb 		smb_sodestroy(session->sock);
1000faa1795aSjb 		session->sock = NULL;
1001faa1795aSjb 		smb_rwx_rwexit(&session->s_lock);
1002faa1795aSjb 
1003faa1795aSjb 		list_remove(&se->se_rdy.lst, session);
1004faa1795aSjb 		se->se_rdy.count--;
1005faa1795aSjb 		se->se_wrop++;
1006faa1795aSjb 
1007faa1795aSjb 		rw_exit(&se->se_lock);
1008faa1795aSjb 		smb_session_delete(session);
1009faa1795aSjb 		rw_enter(&se->se_lock, RW_WRITER);
1010da6c28aaSamw 	}
1011faa1795aSjb 	rw_downgrade(&se->se_lock);
1012faa1795aSjb 
1013faa1795aSjb 	session = list_head(&se->se_act.lst);
1014faa1795aSjb 	while (session) {
1015faa1795aSjb 
1016faa1795aSjb 		ASSERT(session->s_magic == SMB_SESSION_MAGIC);
1017faa1795aSjb 		tsignal(session->s_thread, SIGINT);
1018faa1795aSjb 		session = list_next(&se->se_act.lst, session);
1019faa1795aSjb 	}
1020faa1795aSjb 	rw_exit(&se->se_lock);
1021faa1795aSjb }
1022faa1795aSjb 
1023b89a8333Snatalie li - Sun Microsystems - Irvine United States /*
1024b89a8333Snatalie li - Sun Microsystems - Irvine United States  * smb_session_lookup_user
1025b89a8333Snatalie li - Sun Microsystems - Irvine United States  */
1026b89a8333Snatalie li - Sun Microsystems - Irvine United States static smb_user_t *
1027b89a8333Snatalie li - Sun Microsystems - Irvine United States smb_session_lookup_user(smb_session_t *session, char *domain, char *name)
1028b89a8333Snatalie li - Sun Microsystems - Irvine United States {
1029b89a8333Snatalie li - Sun Microsystems - Irvine United States 	smb_user_t	*user;
1030b89a8333Snatalie li - Sun Microsystems - Irvine United States 	smb_llist_t	*ulist;
1031b89a8333Snatalie li - Sun Microsystems - Irvine United States 
1032b89a8333Snatalie li - Sun Microsystems - Irvine United States 	ulist = &session->s_user_list;
1033b89a8333Snatalie li - Sun Microsystems - Irvine United States 	smb_llist_enter(ulist, RW_READER);
1034b89a8333Snatalie li - Sun Microsystems - Irvine United States 	user = smb_llist_head(ulist);
1035b89a8333Snatalie li - Sun Microsystems - Irvine United States 	while (user) {
1036b89a8333Snatalie li - Sun Microsystems - Irvine United States 		ASSERT(user->u_magic == SMB_USER_MAGIC);
1037b89a8333Snatalie li - Sun Microsystems - Irvine United States 		if (!utf8_strcasecmp(user->u_name, name) &&
1038b89a8333Snatalie li - Sun Microsystems - Irvine United States 		    !utf8_strcasecmp(user->u_domain, domain)) {
1039b89a8333Snatalie li - Sun Microsystems - Irvine United States 			mutex_enter(&user->u_mutex);
1040b89a8333Snatalie li - Sun Microsystems - Irvine United States 			if (user->u_state == SMB_USER_STATE_LOGGED_IN) {
1041b89a8333Snatalie li - Sun Microsystems - Irvine United States 				user->u_refcnt++;
1042b89a8333Snatalie li - Sun Microsystems - Irvine United States 				mutex_exit(&user->u_mutex);
1043b89a8333Snatalie li - Sun Microsystems - Irvine United States 				break;
1044b89a8333Snatalie li - Sun Microsystems - Irvine United States 			}
1045b89a8333Snatalie li - Sun Microsystems - Irvine United States 			mutex_exit(&user->u_mutex);
1046b89a8333Snatalie li - Sun Microsystems - Irvine United States 		}
1047b89a8333Snatalie li - Sun Microsystems - Irvine United States 		user = smb_llist_next(ulist, user);
1048b89a8333Snatalie li - Sun Microsystems - Irvine United States 	}
1049b89a8333Snatalie li - Sun Microsystems - Irvine United States 	smb_llist_exit(ulist);
1050b89a8333Snatalie li - Sun Microsystems - Irvine United States 
1051b89a8333Snatalie li - Sun Microsystems - Irvine United States 	return (user);
1052b89a8333Snatalie li - Sun Microsystems - Irvine United States }
1053b89a8333Snatalie li - Sun Microsystems - Irvine United States 
1054b89a8333Snatalie li - Sun Microsystems - Irvine United States /*
1055b89a8333Snatalie li - Sun Microsystems - Irvine United States  * If a user attempts to log in subsequently from the specified session,
1056b89a8333Snatalie li - Sun Microsystems - Irvine United States  * duplicates the existing SMB user instance such that all SMB user
1057b89a8333Snatalie li - Sun Microsystems - Irvine United States  * instances that corresponds to the same user on the given session
1058b89a8333Snatalie li - Sun Microsystems - Irvine United States  * reference the same user's cred.
1059b89a8333Snatalie li - Sun Microsystems - Irvine United States  *
1060b89a8333Snatalie li - Sun Microsystems - Irvine United States  * Returns NULL if the given user hasn't yet logged in from this
1061b89a8333Snatalie li - Sun Microsystems - Irvine United States  * specified session.  Otherwise, returns a user instance that corresponds
1062b89a8333Snatalie li - Sun Microsystems - Irvine United States  * to this subsequent login.
1063b89a8333Snatalie li - Sun Microsystems - Irvine United States  */
1064b89a8333Snatalie li - Sun Microsystems - Irvine United States smb_user_t *
1065b89a8333Snatalie li - Sun Microsystems - Irvine United States smb_session_dup_user(smb_session_t *session, char *domain, char *account_name)
1066b89a8333Snatalie li - Sun Microsystems - Irvine United States {
1067b89a8333Snatalie li - Sun Microsystems - Irvine United States 	smb_user_t *orig_user = NULL;
1068b89a8333Snatalie li - Sun Microsystems - Irvine United States 	smb_user_t *user = NULL;
1069b89a8333Snatalie li - Sun Microsystems - Irvine United States 
1070b89a8333Snatalie li - Sun Microsystems - Irvine United States 	orig_user = smb_session_lookup_user(session, domain,
1071b89a8333Snatalie li - Sun Microsystems - Irvine United States 	    account_name);
1072b89a8333Snatalie li - Sun Microsystems - Irvine United States 
1073b89a8333Snatalie li - Sun Microsystems - Irvine United States 	if (orig_user) {
1074b89a8333Snatalie li - Sun Microsystems - Irvine United States 		user = smb_user_dup(orig_user);
1075b89a8333Snatalie li - Sun Microsystems - Irvine United States 		smb_user_release(orig_user);
1076b89a8333Snatalie li - Sun Microsystems - Irvine United States 	}
1077b89a8333Snatalie li - Sun Microsystems - Irvine United States 
1078b89a8333Snatalie li - Sun Microsystems - Irvine United States 	return (user);
1079b89a8333Snatalie li - Sun Microsystems - Irvine United States }
1080b89a8333Snatalie li - Sun Microsystems - Irvine United States 
1081faa1795aSjb /*
1082faa1795aSjb  * smb_request_alloc
1083faa1795aSjb  *
1084faa1795aSjb  * Allocate an smb_request_t structure from the kmem_cache.  Partially
1085faa1795aSjb  * initialize the found/new request.
1086faa1795aSjb  *
1087faa1795aSjb  * Returns pointer to a request
1088faa1795aSjb  */
1089faa1795aSjb smb_request_t *
1090faa1795aSjb smb_request_alloc(smb_session_t *session, int req_length)
1091faa1795aSjb {
1092faa1795aSjb 	smb_request_t	*sr;
1093faa1795aSjb 
1094faa1795aSjb 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
1095faa1795aSjb 
1096faa1795aSjb 	sr = kmem_cache_alloc(session->s_cache_request, KM_SLEEP);
1097faa1795aSjb 
1098faa1795aSjb 	/*
1099faa1795aSjb 	 * Future:  Use constructor to pre-initialize some fields.  For now
1100faa1795aSjb 	 * there are so many fields that it is easiest just to zero the
1101faa1795aSjb 	 * whole thing and start over.
1102faa1795aSjb 	 */
1103faa1795aSjb 	bzero(sr, sizeof (smb_request_t));
1104faa1795aSjb 
1105faa1795aSjb 	mutex_init(&sr->sr_mutex, NULL, MUTEX_DEFAULT, NULL);
1106faa1795aSjb 	sr->session = session;
1107faa1795aSjb 	sr->sr_server = session->s_server;
1108faa1795aSjb 	sr->sr_gmtoff = session->s_server->si_gmtoff;
1109faa1795aSjb 	sr->sr_cache = session->s_server->si_cache_request;
1110faa1795aSjb 	sr->sr_cfg = &session->s_cfg;
1111faa1795aSjb 	sr->request_storage.forw = &sr->request_storage;
1112faa1795aSjb 	sr->request_storage.back = &sr->request_storage;
1113faa1795aSjb 	sr->command.max_bytes = req_length;
1114faa1795aSjb 	sr->reply.max_bytes = smb_maxbufsize;
1115faa1795aSjb 	sr->sr_req_length = req_length;
1116faa1795aSjb 	if (req_length)
1117faa1795aSjb 		sr->sr_request_buf = kmem_alloc(req_length, KM_SLEEP);
1118faa1795aSjb 	sr->sr_magic = SMB_REQ_MAGIC;
1119faa1795aSjb 	sr->sr_state = SMB_REQ_STATE_INITIALIZING;
1120faa1795aSjb 	smb_slist_insert_tail(&session->s_req_list, sr);
1121faa1795aSjb 	return (sr);
1122faa1795aSjb }
1123faa1795aSjb 
1124faa1795aSjb /*
1125faa1795aSjb  * smb_request_free
1126faa1795aSjb  *
1127faa1795aSjb  * release the memories which have been allocated for a smb request.
1128faa1795aSjb  */
1129faa1795aSjb void
1130faa1795aSjb smb_request_free(smb_request_t *sr)
1131faa1795aSjb {
1132faa1795aSjb 	ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
1133faa1795aSjb 	ASSERT(sr->session);
1134faa1795aSjb 	ASSERT(sr->r_xa == NULL);
1135faa1795aSjb 
1136*2c2961f8Sjose borrego 	if (sr->fid_ofile != NULL)
1137*2c2961f8Sjose borrego 		smb_ofile_release(sr->fid_ofile);
1138*2c2961f8Sjose borrego 
1139*2c2961f8Sjose borrego 	if (sr->tid_tree != NULL)
1140faa1795aSjb 		smb_tree_release(sr->tid_tree);
1141faa1795aSjb 
1142*2c2961f8Sjose borrego 	if (sr->uid_user != NULL)
1143faa1795aSjb 		smb_user_release(sr->uid_user);
1144faa1795aSjb 
1145faa1795aSjb 	smb_slist_remove(&sr->session->s_req_list, sr);
1146faa1795aSjb 
1147faa1795aSjb 	sr->session = NULL;
1148faa1795aSjb 
1149faa1795aSjb 	/* Release any temp storage */
1150faa1795aSjb 	smbsr_free_malloc_list(&sr->request_storage);
1151faa1795aSjb 
1152faa1795aSjb 	if (sr->sr_request_buf)
1153faa1795aSjb 		kmem_free(sr->sr_request_buf, sr->sr_req_length);
1154faa1795aSjb 	if (sr->command.chain)
1155faa1795aSjb 		m_freem(sr->command.chain);
1156faa1795aSjb 	if (sr->reply.chain)
1157faa1795aSjb 		m_freem(sr->reply.chain);
1158faa1795aSjb 	if (sr->raw_data.chain)
1159faa1795aSjb 		m_freem(sr->raw_data.chain);
1160faa1795aSjb 
1161faa1795aSjb 	sr->sr_magic = 0;
1162faa1795aSjb 	mutex_destroy(&sr->sr_mutex);
1163faa1795aSjb 	kmem_cache_free(sr->sr_cache, sr);
1164da6c28aaSamw }
11657f667e74Sjose borrego 
11667f667e74Sjose borrego void
11677f667e74Sjose borrego dump_smb_inaddr(smb_inaddr_t *ipaddr)
11687f667e74Sjose borrego {
11697f667e74Sjose borrego char ipstr[INET6_ADDRSTRLEN];
11707f667e74Sjose borrego 
11717f667e74Sjose borrego 	if (smb_inet_ntop(ipaddr, ipstr, SMB_IPSTRLEN(ipaddr->a_family)))
11727f667e74Sjose borrego 		cmn_err(CE_WARN, "error ipstr=%s", ipstr);
11737f667e74Sjose borrego 	else
11747f667e74Sjose borrego 		cmn_err(CE_WARN, "error converting ip address");
11757f667e74Sjose borrego }
1176*2c2961f8Sjose borrego 
1177*2c2961f8Sjose borrego boolean_t
1178*2c2961f8Sjose borrego smb_session_oplocks_enable(smb_session_t *session)
1179*2c2961f8Sjose borrego {
1180*2c2961f8Sjose borrego 	SMB_SESSION_VALID(session);
1181*2c2961f8Sjose borrego 	if (session->s_cfg.skc_oplock_enable == 0)
1182*2c2961f8Sjose borrego 		return (B_FALSE);
1183*2c2961f8Sjose borrego 	else
1184*2c2961f8Sjose borrego 		return (B_TRUE);
1185*2c2961f8Sjose borrego }
1186*2c2961f8Sjose borrego 
1187*2c2961f8Sjose borrego /*
1188*2c2961f8Sjose borrego  * smb_session_breaking_oplock
1189*2c2961f8Sjose borrego  *
1190*2c2961f8Sjose borrego  * This MUST be a cross-session call, i.e. the caller must be in a different
1191*2c2961f8Sjose borrego  * context than the one passed.
1192*2c2961f8Sjose borrego  */
1193*2c2961f8Sjose borrego void
1194*2c2961f8Sjose borrego smb_session_oplock_break(smb_session_t *session, smb_ofile_t *of)
1195*2c2961f8Sjose borrego {
1196*2c2961f8Sjose borrego 	mbuf_chain_t	*mbc;
1197*2c2961f8Sjose borrego 
1198*2c2961f8Sjose borrego 	SMB_SESSION_VALID(session);
1199*2c2961f8Sjose borrego 
1200*2c2961f8Sjose borrego 	mbc = smb_mbc_alloc(MLEN);
1201*2c2961f8Sjose borrego 
1202*2c2961f8Sjose borrego 	(void) smb_mbc_encodef(mbc, "Mb19.wwwwbb3.ww10.",
1203*2c2961f8Sjose borrego 	    SMB_COM_LOCKING_ANDX,
1204*2c2961f8Sjose borrego 	    SMB_TREE_GET_TID(SMB_OFILE_GET_TREE(of)),
1205*2c2961f8Sjose borrego 	    0xFFFF, 0, 0xFFFF, 8, 0xFF,
1206*2c2961f8Sjose borrego 	    SMB_OFILE_GET_FID(of),
1207*2c2961f8Sjose borrego 	    LOCKING_ANDX_OPLOCK_RELEASE);
1208*2c2961f8Sjose borrego 
1209*2c2961f8Sjose borrego 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
1210*2c2961f8Sjose borrego 	switch (session->s_state) {
1211*2c2961f8Sjose borrego 	case SMB_SESSION_STATE_NEGOTIATED:
1212*2c2961f8Sjose borrego 	case SMB_SESSION_STATE_OPLOCK_BREAKING:
1213*2c2961f8Sjose borrego 		session->s_state = SMB_SESSION_STATE_OPLOCK_BREAKING;
1214*2c2961f8Sjose borrego 		session->s_oplock_brkcntr++;
1215*2c2961f8Sjose borrego 		(void) smb_session_send(session, 0, mbc);
1216*2c2961f8Sjose borrego 		smb_mbc_free(mbc);
1217*2c2961f8Sjose borrego 		break;
1218*2c2961f8Sjose borrego 
1219*2c2961f8Sjose borrego 	case SMB_SESSION_STATE_READ_RAW_ACTIVE:
1220*2c2961f8Sjose borrego 		list_insert_tail(&session->s_oplock_brkreqs, mbc);
1221*2c2961f8Sjose borrego 		break;
1222*2c2961f8Sjose borrego 
1223*2c2961f8Sjose borrego 	case SMB_SESSION_STATE_DISCONNECTED:
1224*2c2961f8Sjose borrego 	case SMB_SESSION_STATE_TERMINATED:
1225*2c2961f8Sjose borrego 		smb_mbc_free(mbc);
1226*2c2961f8Sjose borrego 		break;
1227*2c2961f8Sjose borrego 
1228*2c2961f8Sjose borrego 	default:
1229*2c2961f8Sjose borrego 		SMB_PANIC();
1230*2c2961f8Sjose borrego 	}
1231*2c2961f8Sjose borrego 	smb_rwx_rwexit(&session->s_lock);
1232*2c2961f8Sjose borrego }
1233*2c2961f8Sjose borrego 
1234*2c2961f8Sjose borrego /*
1235*2c2961f8Sjose borrego  * smb_session_oplock_released
1236*2c2961f8Sjose borrego  *
1237*2c2961f8Sjose borrego  * This function MUST be called in the context of the session of the client
1238*2c2961f8Sjose borrego  * holding the oplock. The lock of the session must have been entered in
1239*2c2961f8Sjose borrego  * RW_READER or RW_WRITER mode.
1240*2c2961f8Sjose borrego  */
1241*2c2961f8Sjose borrego void
1242*2c2961f8Sjose borrego smb_session_oplock_released(smb_session_t *session)
1243*2c2961f8Sjose borrego {
1244*2c2961f8Sjose borrego 	krw_t	mode;
1245*2c2961f8Sjose borrego 
1246*2c2961f8Sjose borrego 	SMB_SESSION_VALID(session);
1247*2c2961f8Sjose borrego 
1248*2c2961f8Sjose borrego 	mode = smb_rwx_rwupgrade(&session->s_lock);
1249*2c2961f8Sjose borrego 	smb_session_oplock_broken(session);
1250*2c2961f8Sjose borrego 	smb_rwx_rwdowngrade(&session->s_lock, mode);
1251*2c2961f8Sjose borrego }
1252*2c2961f8Sjose borrego 
1253*2c2961f8Sjose borrego /*
1254*2c2961f8Sjose borrego  * smb_session_oplock_break_timedout
1255*2c2961f8Sjose borrego  *
1256*2c2961f8Sjose borrego  * This function MUST be called when the client holding the oplock to file
1257*2c2961f8Sjose borrego  * failed to release it in the time alloted. It is a cross-session call (The
1258*2c2961f8Sjose borrego  * caller must be calling in the context of another session).
1259*2c2961f8Sjose borrego  */
1260*2c2961f8Sjose borrego void
1261*2c2961f8Sjose borrego smb_session_oplock_break_timedout(smb_session_t *session)
1262*2c2961f8Sjose borrego {
1263*2c2961f8Sjose borrego 	SMB_SESSION_VALID(session);
1264*2c2961f8Sjose borrego 
1265*2c2961f8Sjose borrego 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
1266*2c2961f8Sjose borrego 	smb_session_oplock_broken(session);
1267*2c2961f8Sjose borrego 	smb_rwx_rwexit(&session->s_lock);
1268*2c2961f8Sjose borrego }
1269*2c2961f8Sjose borrego 
1270*2c2961f8Sjose borrego /*
1271*2c2961f8Sjose borrego  * smb_session_oplock_broken
1272*2c2961f8Sjose borrego  *
1273*2c2961f8Sjose borrego  * Does the actual work.
1274*2c2961f8Sjose borrego  */
1275*2c2961f8Sjose borrego static void
1276*2c2961f8Sjose borrego smb_session_oplock_broken(smb_session_t *session)
1277*2c2961f8Sjose borrego {
1278*2c2961f8Sjose borrego 	switch (session->s_state) {
1279*2c2961f8Sjose borrego 	case SMB_SESSION_STATE_OPLOCK_BREAKING:
1280*2c2961f8Sjose borrego 		if (--session->s_oplock_brkcntr == 0)
1281*2c2961f8Sjose borrego 			session->s_state = SMB_SESSION_STATE_NEGOTIATED;
1282*2c2961f8Sjose borrego 		break;
1283*2c2961f8Sjose borrego 
1284*2c2961f8Sjose borrego 	case SMB_SESSION_STATE_NEGOTIATED:
1285*2c2961f8Sjose borrego 	case SMB_SESSION_STATE_WRITE_RAW_ACTIVE:
1286*2c2961f8Sjose borrego 	case SMB_SESSION_STATE_DISCONNECTED:
1287*2c2961f8Sjose borrego 	case SMB_SESSION_STATE_TERMINATED:
1288*2c2961f8Sjose borrego 		break;
1289*2c2961f8Sjose borrego 
1290*2c2961f8Sjose borrego 	default:
1291*2c2961f8Sjose borrego 		SMB_PANIC();
1292*2c2961f8Sjose borrego 	}
1293*2c2961f8Sjose borrego }
1294