xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c (revision 5677e04907859594bfe1a95ba3bfdb1629b89dac)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
14  */
15 
16 
17 #include <smbsrv/smb2_kproto.h>
18 #include <smbsrv/smb_kstat.h>
19 #include <smbsrv/smb2.h>
20 
21 /*
22  * Saved state for a command that "goes async".  When a compound request
23  * contains a command that may block indefinitely, the compound reply is
24  * composed with an "interim response" for that command, and information
25  * needed to actually dispatch that command is saved on a list of "async"
26  * commands for this compound request.  After the compound reply is sent,
27  * the list of async commands is processed, and those may block as long
28  * as they need to without affecting the initial compound request.
29  *
30  * Now interestingly, this "async" mechanism is not used with the full
31  * range of asynchrony that one might imagine.  The design of async
32  * request processing can be drastically simplified if we can assume
33  * that there's no need to run more than one async command at a time.
34  * With that simplifying assumption, we can continue using the current
35  * "one worker thread per request message" model, which has very simple
36  * locking rules etc.  The same worker thread that handles the initial
37  * compound request can handle the list of async requests.
38  *
39  * As it turns out, SMB2 clients do not try to use more than one "async"
40  * command in a compound.  If they were to do so, the [MS-SMB2] spec.
41  * allows us to decline additional async requests with an error.
42  *
43  * smb_async_req_t is the struct used to save an "async" request on
44  * the list of requests that had an interim reply in the initial
45  * compound reply.  This includes everything needed to restart
46  * processing at the async command.
47  */
48 
49 typedef struct smb2_async_req {
50 
51 	smb_sdrc_t		(*ar_func)(smb_request_t *);
52 
53 	int ar_cmd_hdr;		/* smb2_cmd_hdr offset */
54 	int ar_cmd_len;		/* length from hdr */
55 
56 	/*
57 	 * SMB2 header fields.
58 	 */
59 	uint16_t		ar_cmd_code;
60 	uint16_t		ar_uid;
61 	uint16_t		ar_tid;
62 	uint32_t		ar_pid;
63 	uint32_t		ar_hdr_flags;
64 	uint64_t		ar_messageid;
65 } smb2_async_req_t;
66 
67 void smb2sr_do_async(smb_request_t *);
68 smb_sdrc_t smb2_invalid_cmd(smb_request_t *);
69 static void smb2_tq_work(void *);
70 
71 static const smb_disp_entry_t
72 smb2_disp_table[SMB2__NCMDS] = {
73 
74 	/* text-name, pre, func, post, cmd-code, dialect, flags */
75 
76 	{  "smb2_negotiate", NULL,
77 	    smb2_negotiate, NULL, 0, 0,
78 	    SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
79 
80 	{  "smb2_session_setup", NULL,
81 	    smb2_session_setup, NULL, 0, 0,
82 	    SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
83 
84 	{  "smb2_logoff", NULL,
85 	    smb2_logoff, NULL, 0, 0,
86 	    SDDF_SUPPRESS_TID },
87 
88 	{  "smb2_tree_connect", NULL,
89 	    smb2_tree_connect, NULL, 0, 0,
90 	    SDDF_SUPPRESS_TID },
91 
92 	{  "smb2_tree_disconn", NULL,
93 	    smb2_tree_disconn, NULL, 0, 0 },
94 
95 	{  "smb2_create", NULL,
96 	    smb2_create, NULL, 0, 0 },
97 
98 	{  "smb2_close", NULL,
99 	    smb2_close, NULL, 0, 0 },
100 
101 	{  "smb2_flush", NULL,
102 	    smb2_flush, NULL, 0, 0 },
103 
104 	{  "smb2_read", NULL,
105 	    smb2_read, NULL, 0, 0 },
106 
107 	{  "smb2_write", NULL,
108 	    smb2_write, NULL, 0, 0 },
109 
110 	{  "smb2_lock", NULL,
111 	    smb2_lock, NULL, 0, 0 },
112 
113 	{  "smb2_ioctl", NULL,
114 	    smb2_ioctl, NULL, 0, 0 },
115 
116 	{  "smb2_cancel", NULL,
117 	    smb2_cancel, NULL, 0, 0,
118 	    SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID },
119 
120 	{  "smb2_echo", NULL,
121 	    smb2_echo, NULL, 0, 0,
122 	    SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID },
123 
124 	{  "smb2_query_dir", NULL,
125 	    smb2_query_dir, NULL, 0, 0 },
126 
127 	{  "smb2_change_notify", NULL,
128 	    smb2_change_notify, NULL, 0, 0 },
129 
130 	{  "smb2_query_info", NULL,
131 	    smb2_query_info, NULL, 0, 0 },
132 
133 	{  "smb2_set_info", NULL,
134 	    smb2_set_info, NULL, 0, 0 },
135 
136 	{  "smb2_oplock_break_ack", NULL,
137 	    smb2_oplock_break_ack, NULL, 0, 0 },
138 
139 	{  "smb2_invalid_cmd", NULL,
140 	    smb2_invalid_cmd, NULL, 0, 0,
141 	    SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID },
142 };
143 
144 smb_sdrc_t
145 smb2_invalid_cmd(smb_request_t *sr)
146 {
147 #ifdef	DEBUG
148 	cmn_err(CE_NOTE, "clnt %s bad SMB2 cmd code",
149 	    sr->session->ip_addr_str);
150 #endif
151 	sr->smb2_status = NT_STATUS_INVALID_PARAMETER;
152 	return (SDRC_DROP_VC);
153 }
154 
155 /*
156  * This is the SMB2 handler for new smb requests, called from
157  * smb_session_reader after SMB negotiate is done.  For most SMB2
158  * requests, we just enqueue them for the smb_session_worker to
159  * execute via the task queue, so they can block for resources
160  * without stopping the reader thread.  A few protocol messages
161  * are special cases and are handled directly here in the reader
162  * thread so they don't wait for taskq scheduling.
163  *
164  * This function must either enqueue the new request for
165  * execution via the task queue, or execute it directly
166  * and then free it.  If this returns non-zero, the caller
167  * will drop the session.
168  */
169 int
170 smb2sr_newrq(smb_request_t *sr)
171 {
172 	struct mbuf_chain *mbc = &sr->command;
173 	uint32_t magic;
174 	int rc, skip;
175 
176 	if (smb_mbc_peek(mbc, 0, "l", &magic) != 0)
177 		goto drop;
178 
179 	if (magic != SMB2_PROTOCOL_MAGIC)
180 		goto drop;
181 
182 	/*
183 	 * Walk the SMB2 commands in this compound message and
184 	 * keep track of the range of message IDs it uses.
185 	 */
186 	for (;;) {
187 		if (smb2_decode_header(sr) != 0)
188 			goto drop;
189 
190 		/*
191 		 * Cancel requests are special:  They refer to
192 		 * an earlier message ID (or an async. ID),
193 		 * never a new ID, and are never compounded.
194 		 * This is intentionally not "goto drop"
195 		 * because rc may be zero (success).
196 		 */
197 		if (sr->smb2_cmd_code == SMB2_CANCEL) {
198 			rc = smb2_newrq_cancel(sr);
199 			smb_request_free(sr);
200 			return (rc);
201 		}
202 
203 		/*
204 		 * Keep track of the total credits in this compound
205 		 * and the first (real) message ID (not: 0, -1)
206 		 * While we're looking, verify that all (real) IDs
207 		 * are (first <= ID < (first + msg_credits))
208 		 */
209 		if (sr->smb2_credit_charge == 0)
210 			sr->smb2_credit_charge = 1;
211 		sr->smb2_total_credits += sr->smb2_credit_charge;
212 
213 		if (sr->smb2_messageid != 0 &&
214 		    sr->smb2_messageid != UINT64_MAX) {
215 
216 			if (sr->smb2_first_msgid == 0)
217 				sr->smb2_first_msgid = sr->smb2_messageid;
218 
219 			if (sr->smb2_messageid < sr->smb2_first_msgid ||
220 			    sr->smb2_messageid >= (sr->smb2_first_msgid +
221 			    sr->smb2_total_credits)) {
222 				long long id = (long long) sr->smb2_messageid;
223 				cmn_err(CE_WARN, "clnt %s msg ID 0x%llx "
224 				    "out of sequence in compound",
225 				    sr->session->ip_addr_str, id);
226 			}
227 		}
228 
229 		/* Normal loop exit on next == zero */
230 		if (sr->smb2_next_command == 0)
231 			break;
232 
233 		/* Abundance of caution... */
234 		if (sr->smb2_next_command < SMB2_HDR_SIZE)
235 			goto drop;
236 
237 		/* Advance to the next header. */
238 		skip = sr->smb2_next_command - SMB2_HDR_SIZE;
239 		if (MBC_ROOM_FOR(mbc, skip) == 0)
240 			goto drop;
241 		mbc->chain_offset += skip;
242 	}
243 	/* Rewind back to the top. */
244 	mbc->chain_offset = 0;
245 
246 	/*
247 	 * Submit the request to the task queue, which calls
248 	 * smb2_tq_work when the workload permits.
249 	 */
250 	sr->sr_time_submitted = gethrtime();
251 	sr->sr_state = SMB_REQ_STATE_SUBMITTED;
252 	smb_srqueue_waitq_enter(sr->session->s_srqueue);
253 	(void) taskq_dispatch(sr->sr_server->sv_worker_pool,
254 	    smb2_tq_work, sr, TQ_SLEEP);
255 	return (0);
256 
257 drop:
258 	smb_request_free(sr);
259 	return (-1);
260 }
261 
262 static void
263 smb2_tq_work(void *arg)
264 {
265 	smb_request_t	*sr;
266 	smb_srqueue_t	*srq;
267 
268 	sr = (smb_request_t *)arg;
269 	SMB_REQ_VALID(sr);
270 
271 	srq = sr->session->s_srqueue;
272 	smb_srqueue_waitq_to_runq(srq);
273 	sr->sr_worker = curthread;
274 	sr->sr_time_active = gethrtime();
275 
276 	/*
277 	 * Always dispatch to the work function, because cancelled
278 	 * requests need an error reply (NT_STATUS_CANCELLED).
279 	 */
280 	mutex_enter(&sr->sr_mutex);
281 	if (sr->sr_state == SMB_REQ_STATE_SUBMITTED)
282 		sr->sr_state = SMB_REQ_STATE_ACTIVE;
283 	mutex_exit(&sr->sr_mutex);
284 
285 	smb2sr_work(sr);
286 
287 	smb_srqueue_runq_exit(srq);
288 }
289 
290 /*
291  * smb2sr_work
292  *
293  * This function processes each SMB command in the current request
294  * (which may be a compound request) building a reply containing
295  * SMB reply messages, one-to-one with the SMB commands.  Some SMB
296  * commands (change notify, blocking locks) may require both an
297  * "interim response" and a later "async response" at completion.
298  * In such cases, we'll encode the interim response in the reply
299  * compound we're building, and put the (now async) command on a
300  * list of commands that need further processing.  After we've
301  * finished processing the commands in this compound and building
302  * the compound reply, we'll send the compound reply, and finally
303  * process the list of async commands.
304  *
305  * As we work our way through the compound request and reply,
306  * we need to keep track of the bounds of the current request
307  * and reply.  For the request, this uses an MBC_SHADOW_CHAIN
308  * that begins at smb2_cmd_hdr.  The reply is appended to the
309  * sr->reply chain starting at smb2_reply_hdr.
310  *
311  * This function must always free the smb request.
312  */
313 void
314 smb2sr_work(struct smb_request *sr)
315 {
316 	const smb_disp_entry_t	*sdd;
317 	smb_disp_stats_t	*sds;
318 	smb_session_t		*session;
319 	uint32_t		msg_len;
320 	uint16_t		cmd_idx;
321 	int			rc = 0;
322 	boolean_t		disconnect = B_FALSE;
323 	boolean_t		related;
324 
325 	session = sr->session;
326 
327 	ASSERT(sr->tid_tree == 0);
328 	ASSERT(sr->uid_user == 0);
329 	ASSERT(sr->fid_ofile == 0);
330 	sr->smb_fid = (uint16_t)-1;
331 	sr->smb2_status = 0;
332 
333 	/* temporary until we identify a user */
334 	sr->user_cr = zone_kcred();
335 
336 cmd_start:
337 	/*
338 	 * Note that we don't check sr_state here and abort the
339 	 * compound if cancelled (etc.) because some SMB2 command
340 	 * handlers need to do work even when cancelled.
341 	 *
342 	 * We treat some status codes as if "sticky", meaning
343 	 * once they're set after some command handler returns,
344 	 * all remaining commands get this status without even
345 	 * calling the command-specific handler.
346 	 */
347 	if (sr->smb2_status != NT_STATUS_CANCELLED &&
348 	    sr->smb2_status != NT_STATUS_INSUFFICIENT_RESOURCES)
349 		sr->smb2_status = 0;
350 
351 	/*
352 	 * Decode the request header
353 	 *
354 	 * Most problems with decoding will result in the error
355 	 * STATUS_INVALID_PARAMETER.  If the decoding problem
356 	 * prevents continuing, we'll close the connection.
357 	 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
358 	 */
359 	sr->smb2_cmd_hdr = sr->command.chain_offset;
360 	if ((rc = smb2_decode_header(sr)) != 0) {
361 		cmn_err(CE_WARN, "clnt %s bad SMB2 header",
362 		    session->ip_addr_str);
363 		disconnect = B_TRUE;
364 		goto cleanup;
365 	}
366 
367 	/*
368 	 * The SMB2_FLAGS_SERVER_TO_REDIR should only appear
369 	 * in messages from the server back to the client.
370 	 */
371 	if ((sr->smb2_hdr_flags & SMB2_FLAGS_SERVER_TO_REDIR) != 0) {
372 		cmn_err(CE_WARN, "clnt %s bad SMB2 flags",
373 		    session->ip_addr_str);
374 		disconnect = B_TRUE;
375 		goto cleanup;
376 	}
377 	related = (sr->smb2_hdr_flags & SMB2_FLAGS_RELATED_OPERATIONS);
378 
379 	/*
380 	 * In case we bail out with an error before we get to the
381 	 * section that computes the credit grant, initialize the
382 	 * response header fields so that credits won't change.
383 	 * Note: SMB 2.02 clients may send credit charge zero.
384 	 */
385 	if (sr->smb2_credit_charge == 0)
386 		sr->smb2_credit_charge = 1;
387 	sr->smb2_credit_response = sr->smb2_credit_charge;
388 
389 	/*
390 	 * Reserve space for the reply header, and save the offset.
391 	 * The reply header will be overwritten later.  If we have
392 	 * already exhausted the output space, then this client is
393 	 * trying something funny.  Log it and kill 'em.
394 	 */
395 	sr->smb2_reply_hdr = sr->reply.chain_offset;
396 	if ((rc = smb2_encode_header(sr, B_FALSE)) != 0) {
397 		cmn_err(CE_WARN, "clnt %s excessive reply",
398 		    session->ip_addr_str);
399 		disconnect = B_TRUE;
400 		goto cleanup;
401 	}
402 
403 	/*
404 	 * Figure out the length of data following the SMB2 header.
405 	 * It ends at either the next SMB2 header if there is one
406 	 * (smb2_next_command != 0) or at the end of the message.
407 	 */
408 	if (sr->smb2_next_command != 0) {
409 		/* [MS-SMB2] says this is 8-byte aligned */
410 		msg_len = sr->smb2_next_command;
411 		if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) ||
412 		    ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) {
413 			cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd",
414 			    session->ip_addr_str);
415 			disconnect = B_TRUE;
416 			goto cleanup;
417 		}
418 	} else {
419 		msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr;
420 	}
421 
422 	/*
423 	 * Setup a shadow chain for this SMB2 command, starting
424 	 * with the header and ending at either the next command
425 	 * or the end of the message.  The signing check below
426 	 * needs the entire SMB2 command.  After that's done, we
427 	 * advance chain_offset to the end of the header where
428 	 * the command specific handlers continue decoding.
429 	 */
430 	(void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
431 	    sr->smb2_cmd_hdr, msg_len);
432 
433 	/*
434 	 * We will consume the data for this request from smb_data.
435 	 * That effectively consumes msg_len bytes from sr->command
436 	 * but doesn't update its chain_offset, so we need to update
437 	 * that here to make later received bytes accounting work.
438 	 */
439 	sr->command.chain_offset = sr->smb2_cmd_hdr + msg_len;
440 	ASSERT(sr->command.chain_offset <= sr->command.max_bytes);
441 
442 	/*
443 	 * Validate the commmand code, get dispatch table entries.
444 	 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
445 	 *
446 	 * The last slot in the dispatch table is used to handle
447 	 * invalid commands.  Same for statistics.
448 	 */
449 	if (sr->smb2_cmd_code < SMB2_INVALID_CMD)
450 		cmd_idx = sr->smb2_cmd_code;
451 	else
452 		cmd_idx = SMB2_INVALID_CMD;
453 	sdd = &smb2_disp_table[cmd_idx];
454 	sds = &session->s_server->sv_disp_stats2[cmd_idx];
455 
456 	/*
457 	 * If this command is NOT "related" to the previous,
458 	 * clear out the UID, TID, FID state that might be
459 	 * left over from the previous command.
460 	 *
461 	 * If the command IS related, any new IDs are ignored,
462 	 * and we simply continue with the previous user, tree,
463 	 * and open file.
464 	 */
465 	if (!related) {
466 		/*
467 		 * Drop user, tree, file; carefully ordered to
468 		 * avoid dangling references: file, tree, user
469 		 */
470 		if (sr->fid_ofile != NULL) {
471 			smb_ofile_request_complete(sr->fid_ofile);
472 			smb_ofile_release(sr->fid_ofile);
473 			sr->fid_ofile = NULL;
474 		}
475 		if (sr->tid_tree != NULL) {
476 			smb_tree_release(sr->tid_tree);
477 			sr->tid_tree = NULL;
478 		}
479 		if (sr->uid_user != NULL) {
480 			smb_user_release(sr->uid_user);
481 			sr->uid_user = NULL;
482 			sr->user_cr = zone_kcred();
483 		}
484 	}
485 
486 	/*
487 	 * Make sure we have a user and tree as needed
488 	 * according to the flags for the this command.
489 	 * Note that we may have inherited these.
490 	 */
491 	if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0) {
492 		/*
493 		 * This command requires a user session.
494 		 */
495 		if (related) {
496 			/*
497 			 * Previous command should have given us a user.
498 			 * [MS-SMB2] 3.3.5.2 Handling Related Requests
499 			 */
500 			if (sr->uid_user == NULL) {
501 				smb2sr_put_error(sr,
502 				    NT_STATUS_INVALID_PARAMETER);
503 				goto cmd_done;
504 			}
505 			sr->smb_uid = sr->uid_user->u_uid;
506 		} else {
507 			/*
508 			 * Lookup the UID
509 			 * [MS-SMB2] 3.3.5.2 Verifying the Session
510 			 */
511 			ASSERT(sr->uid_user == NULL);
512 			sr->uid_user = smb_session_lookup_uid(session,
513 			    sr->smb_uid);
514 			if (sr->uid_user == NULL) {
515 				smb2sr_put_error(sr,
516 				    NT_STATUS_USER_SESSION_DELETED);
517 				goto cmd_done;
518 			}
519 			sr->user_cr = smb_user_getcred(sr->uid_user);
520 		}
521 		ASSERT(sr->uid_user != NULL);
522 	}
523 
524 	if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0) {
525 		/*
526 		 * This command requires a tree connection.
527 		 */
528 		if (related) {
529 			/*
530 			 * Previous command should have given us a tree.
531 			 * [MS-SMB2] 3.3.5.2 Handling Related Requests
532 			 */
533 			if (sr->tid_tree == NULL) {
534 				smb2sr_put_error(sr,
535 				    NT_STATUS_INVALID_PARAMETER);
536 				goto cmd_done;
537 			}
538 			sr->smb_tid = sr->tid_tree->t_tid;
539 		} else {
540 			/*
541 			 * Lookup the TID
542 			 * [MS-SMB2] 3.3.5.2 Verifying the Tree Connect
543 			 */
544 			ASSERT(sr->tid_tree == NULL);
545 			sr->tid_tree = smb_session_lookup_tree(session,
546 			    sr->smb_tid);
547 			if (sr->tid_tree == NULL) {
548 				smb2sr_put_error(sr,
549 				    NT_STATUS_NETWORK_NAME_DELETED);
550 				goto cmd_done;
551 			}
552 		}
553 		ASSERT(sr->tid_tree != NULL);
554 	}
555 
556 	/*
557 	 * SMB2 signature verification, two parts:
558 	 * (a) Require SMB2_FLAGS_SIGNED (for most request types)
559 	 * (b) If SMB2_FLAGS_SIGNED is set, check the signature.
560 	 * [MS-SMB2] 3.3.5.2.4 Verifying the Signature
561 	 */
562 
563 	/*
564 	 * No user session means no signature check.  That's OK,
565 	 * i.e. for commands marked SDDF_SUPPRESS_UID above.
566 	 * Note, this also means we won't sign the reply.
567 	 */
568 	if (sr->uid_user == NULL)
569 		sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED;
570 
571 	/*
572 	 * The SDDF_SUPPRESS_UID dispatch is set for requests that
573 	 * don't need a UID (user).  These also don't require a
574 	 * signature check here.
575 	 */
576 	if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 &&
577 	    sr->uid_user != NULL &&
578 	    (sr->uid_user->u_sign_flags & SMB_SIGNING_CHECK) != 0) {
579 		/*
580 		 * This request type should be signed, and
581 		 * we're configured to require signatures.
582 		 */
583 		if ((sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) == 0) {
584 			smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED);
585 			goto cmd_done;
586 		}
587 		rc = smb2_sign_check_request(sr);
588 		if (rc != 0) {
589 			DTRACE_PROBE1(smb2__sign__check, smb_request_t, sr);
590 			smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED);
591 			goto cmd_done;
592 		}
593 	}
594 
595 	/*
596 	 * Now that the signing check is done with smb_data,
597 	 * advance past the SMB2 header we decoded earlier.
598 	 * This leaves sr->smb_data correctly positioned
599 	 * for command-specific decoding in the dispatch
600 	 * function called next.
601 	 */
602 	sr->smb_data.chain_offset = sr->smb2_cmd_hdr + SMB2_HDR_SIZE;
603 
604 	/*
605 	 * SMB2 credits determine how many simultaneous commands the
606 	 * client may issue, and bounds the range of message IDs those
607 	 * commands may use.  With multi-credit support, commands may
608 	 * use ranges of message IDs, where the credits used by each
609 	 * command are proportional to their data transfer size.
610 	 *
611 	 * Every command may request an increase or decrease of
612 	 * the currently granted credits, based on the difference
613 	 * between the credit request and the credit charge.
614 	 * [MS-SMB2] 3.3.1.2 Algorithm for the Granting of Credits
615 	 *
616 	 * Most commands have credit_request=1, credit_charge=1,
617 	 * which keeps the credit grant unchanged.
618 	 *
619 	 * All we're really doing here (for now) is reducing the
620 	 * credit_response if the client requests a credit increase
621 	 * that would take their credit over the maximum, and
622 	 * limiting the decrease so they don't run out of credits.
623 	 *
624 	 * Later, this could do something dynamic based on load.
625 	 *
626 	 * One other non-obvious bit about credits: We keep the
627 	 * session s_max_credits low until the 1st authentication,
628 	 * at which point we'll set the normal maximum_credits.
629 	 * Some clients ask for more credits with session setup,
630 	 * and we need to handle that requested increase _after_
631 	 * the command-specific handler returns so it won't be
632 	 * restricted to the lower (pre-auth) limit.
633 	 */
634 	sr->smb2_credit_response = sr->smb2_credit_request;
635 	if (sr->smb2_credit_request < sr->smb2_credit_charge) {
636 		uint16_t cur, d;
637 
638 		mutex_enter(&session->s_credits_mutex);
639 		cur = session->s_cur_credits;
640 
641 		/* Handle credit decrease. */
642 		d = sr->smb2_credit_charge - sr->smb2_credit_request;
643 		cur -= d;
644 		if (cur & 0x8000) {
645 			/*
646 			 * underflow (bad credit charge or request)
647 			 * leave credits unchanged (response=charge)
648 			 */
649 			cur = session->s_cur_credits;
650 			sr->smb2_credit_response = sr->smb2_credit_charge;
651 			DTRACE_PROBE1(smb2__credit__neg, smb_request_t, sr);
652 		}
653 
654 		/*
655 		 * The server MUST ensure that the number of credits
656 		 * held by the client is never reduced to zero.
657 		 * [MS-SMB2] 3.3.1.2
658 		 */
659 		if (cur == 0) {
660 			cur = 1;
661 			sr->smb2_credit_response += 1;
662 			DTRACE_PROBE1(smb2__credit__min, smb_request_t, sr);
663 		}
664 
665 		DTRACE_PROBE3(smb2__credit__decrease,
666 		    smb_request_t, sr, int, (int)cur,
667 		    int, (int)session->s_cur_credits);
668 
669 		session->s_cur_credits = cur;
670 		mutex_exit(&session->s_credits_mutex);
671 	}
672 
673 	/*
674 	 * The real work: call the SMB2 command handler
675 	 * (except for "sticky" smb2_status - see above)
676 	 */
677 	sr->sr_time_start = gethrtime();
678 	rc = SDRC_SUCCESS;
679 	if (sr->smb2_status == 0) {
680 		/* NB: not using pre_op */
681 		rc = (*sdd->sdt_function)(sr);
682 		/* NB: not using post_op */
683 	} else {
684 		smb2sr_put_error(sr, sr->smb2_status);
685 	}
686 
687 	MBC_FLUSH(&sr->raw_data);
688 
689 	/*
690 	 * Second half of SMB2 credit handling (increases)
691 	 */
692 	if (sr->smb2_credit_request > sr->smb2_credit_charge) {
693 		uint16_t cur, d;
694 
695 		mutex_enter(&session->s_credits_mutex);
696 		cur = session->s_cur_credits;
697 
698 		/* Handle credit increase. */
699 		d = sr->smb2_credit_request - sr->smb2_credit_charge;
700 		cur += d;
701 
702 		/*
703 		 * If new credits would be above max,
704 		 * reduce the credit grant.
705 		 */
706 		if (cur > session->s_max_credits) {
707 			d = cur - session->s_max_credits;
708 			cur = session->s_max_credits;
709 			sr->smb2_credit_response -= d;
710 			DTRACE_PROBE1(smb2__credit__max, smb_request_t, sr);
711 		}
712 
713 		DTRACE_PROBE3(smb2__credit__increase,
714 		    smb_request_t, sr, int, (int)cur,
715 		    int, (int)session->s_cur_credits);
716 
717 		session->s_cur_credits = cur;
718 		mutex_exit(&session->s_credits_mutex);
719 	}
720 
721 cmd_done:
722 	/*
723 	 * Pad the reply to align(8) if necessary.
724 	 */
725 	if (sr->reply.chain_offset & 7) {
726 		int padsz = 8 - (sr->reply.chain_offset & 7);
727 		(void) smb_mbc_encodef(&sr->reply, "#.", padsz);
728 	}
729 	ASSERT((sr->reply.chain_offset & 7) == 0);
730 
731 	/*
732 	 * Record some statistics: latency, rx bytes, tx bytes.
733 	 */
734 	smb_server_inc_req(sr->sr_server);
735 	smb_latency_add_sample(&sds->sdt_lat,
736 	    gethrtime() - sr->sr_time_start);
737 	atomic_add_64(&sds->sdt_rxb,
738 	    (int64_t)(sr->command.chain_offset - sr->smb2_cmd_hdr));
739 	atomic_add_64(&sds->sdt_txb,
740 	    (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr));
741 
742 	switch (rc) {
743 	case SDRC_SUCCESS:
744 		break;
745 	default:
746 		/*
747 		 * SMB2 does not use the other dispatch return codes.
748 		 * If we see something else, log an event so we'll
749 		 * know something is returning bogus status codes.
750 		 * If you see these in the log, use dtrace to find
751 		 * the code returning something else.
752 		 */
753 #ifdef	DEBUG
754 		cmn_err(CE_NOTE, "handler for %u returned 0x%x",
755 		    sr->smb2_cmd_code, rc);
756 #endif
757 		/* FALLTHROUGH */
758 	case SDRC_ERROR:
759 		if (sr->smb2_status == 0)
760 			sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
761 		break;
762 	case SDRC_DROP_VC:
763 		disconnect = B_TRUE;
764 		goto cleanup;
765 
766 	case SDRC_NO_REPLY:
767 		/* will free sr */
768 		goto cleanup;
769 	}
770 
771 	/*
772 	 * If there's a next command, figure out where it starts,
773 	 * and fill in the next command offset for the reply.
774 	 * Note: We sanity checked smb2_next_command above
775 	 * (the offset to the next command).  Similarly set
776 	 * smb2_next_reply as the offset to the next reply.
777 	 */
778 	if (sr->smb2_next_command != 0) {
779 		sr->command.chain_offset =
780 		    sr->smb2_cmd_hdr + sr->smb2_next_command;
781 		sr->smb2_next_reply =
782 		    sr->reply.chain_offset - sr->smb2_reply_hdr;
783 	} else {
784 		sr->smb2_next_reply = 0;
785 	}
786 
787 	/*
788 	 * Overwrite the SMB2 header for the response of
789 	 * this command (possibly part of a compound).
790 	 * encode_header adds: SMB2_FLAGS_SERVER_TO_REDIR
791 	 */
792 	(void) smb2_encode_header(sr, B_TRUE);
793 
794 	if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED)
795 		smb2_sign_reply(sr);
796 
797 	if (sr->smb2_next_command != 0)
798 		goto cmd_start;
799 
800 	/*
801 	 * We've done all the commands in this compound.
802 	 * Send it out.
803 	 */
804 	smb2_send_reply(sr);
805 
806 	/*
807 	 * If any of the requests "went async", process those now.
808 	 * The async. function "keeps" this sr, changing its state
809 	 * to completed and calling smb_request_free().
810 	 */
811 	if (sr->sr_async_req != NULL) {
812 		smb2sr_do_async(sr);
813 		return;
814 	}
815 
816 cleanup:
817 	if (disconnect) {
818 		smb_rwx_rwenter(&session->s_lock, RW_WRITER);
819 		switch (session->s_state) {
820 		case SMB_SESSION_STATE_DISCONNECTED:
821 		case SMB_SESSION_STATE_TERMINATED:
822 			break;
823 		default:
824 			smb_soshutdown(session->sock);
825 			session->s_state = SMB_SESSION_STATE_DISCONNECTED;
826 			break;
827 		}
828 		smb_rwx_rwexit(&session->s_lock);
829 	}
830 
831 	mutex_enter(&sr->sr_mutex);
832 	sr->sr_state = SMB_REQ_STATE_COMPLETED;
833 	mutex_exit(&sr->sr_mutex);
834 
835 	smb_request_free(sr);
836 }
837 
838 /*
839  * Dispatch an async request using saved information.
840  * See smb2sr_save_async and [MS-SMB2] 3.3.4.2
841  *
842  * This is sort of a "lite" version of smb2sr_work.  Initialize the
843  * command and reply areas as they were when the command-speicific
844  * handler started (in case it needs to decode anything again).
845  * Call the async function, which builds the command-specific part
846  * of the response.  Finally, send the response and free the sr.
847  */
848 void
849 smb2sr_do_async(smb_request_t *sr)
850 {
851 	const smb_disp_entry_t	*sdd;
852 	smb_disp_stats_t	*sds;
853 	smb2_async_req_t	*ar;
854 	int rc = 0;
855 
856 	/*
857 	 * Restore what smb2_decode_header found.
858 	 * (In lieu of decoding it again.)
859 	 */
860 	ar = sr->sr_async_req;
861 	sr->smb2_cmd_hdr   = ar->ar_cmd_hdr;
862 	sr->smb2_cmd_code  = ar->ar_cmd_code;
863 	sr->smb2_hdr_flags = ar->ar_hdr_flags;
864 	sr->smb2_async_id  = (uintptr_t)ar;
865 	sr->smb2_messageid = ar->ar_messageid;
866 	sr->smb_pid = ar->ar_pid;
867 	sr->smb_tid = ar->ar_tid;
868 	sr->smb_uid = ar->ar_uid;
869 	sr->smb2_status = 0;
870 
871 	/*
872 	 * Async requests don't grant credits, because any credits
873 	 * should have gone out with the interim reply.
874 	 * An async reply goes alone (no next reply).
875 	 */
876 	sr->smb2_credit_response = 0;
877 	sr->smb2_next_reply = 0;
878 
879 	/*
880 	 * Setup input mbuf_chain
881 	 */
882 	ASSERT(ar->ar_cmd_len >= SMB2_HDR_SIZE);
883 	(void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
884 	    sr->smb2_cmd_hdr + SMB2_HDR_SIZE,
885 	    ar->ar_cmd_len - SMB2_HDR_SIZE);
886 
887 	/*
888 	 * Setup output mbuf_chain
889 	 */
890 	MBC_FLUSH(&sr->reply);
891 	sr->smb2_reply_hdr = sr->reply.chain_offset;
892 	(void) smb2_encode_header(sr, B_FALSE);
893 
894 	VERIFY3U(sr->smb2_cmd_code, <, SMB2_INVALID_CMD);
895 	sdd = &smb2_disp_table[sr->smb2_cmd_code];
896 	sds = sr->session->s_server->sv_disp_stats2;
897 	sds = &sds[sr->smb2_cmd_code];
898 
899 	/*
900 	 * Keep the UID, TID, ofile we have.
901 	 */
902 	if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 &&
903 	    sr->uid_user == NULL) {
904 		smb2sr_put_error(sr, NT_STATUS_USER_SESSION_DELETED);
905 		goto cmd_done;
906 	}
907 	if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0 &&
908 	    sr->tid_tree == NULL) {
909 		smb2sr_put_error(sr, NT_STATUS_NETWORK_NAME_DELETED);
910 		goto cmd_done;
911 	}
912 
913 	/*
914 	 * Signature already verified
915 	 * Credits handled...
916 	 *
917 	 * Just call the async handler function.
918 	 */
919 	rc = ar->ar_func(sr);
920 	if (rc != 0 && sr->smb2_status == 0)
921 		sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
922 
923 cmd_done:
924 	/*
925 	 * Pad the reply to align(8) if necessary.
926 	 */
927 	if (sr->reply.chain_offset & 7) {
928 		int padsz = 8 - (sr->reply.chain_offset & 7);
929 		(void) smb_mbc_encodef(&sr->reply, "#.", padsz);
930 	}
931 	ASSERT((sr->reply.chain_offset & 7) == 0);
932 
933 	/*
934 	 * Record some statistics: (just tx bytes here)
935 	 */
936 	atomic_add_64(&sds->sdt_txb,
937 	    (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr));
938 
939 	/*
940 	 * Overwrite the SMB2 header for the response of
941 	 * this command (possibly part of a compound).
942 	 * The call adds: SMB2_FLAGS_SERVER_TO_REDIR
943 	 */
944 	(void) smb2_encode_header(sr, B_TRUE);
945 
946 	if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED)
947 		smb2_sign_reply(sr);
948 
949 	smb2_send_reply(sr);
950 
951 	/*
952 	 * Done.  Unlink and free.
953 	 */
954 	sr->sr_async_req = NULL;
955 	kmem_free(ar, sizeof (*ar));
956 
957 	mutex_enter(&sr->sr_mutex);
958 	sr->sr_state = SMB_REQ_STATE_COMPLETED;
959 	mutex_exit(&sr->sr_mutex);
960 
961 	smb_request_free(sr);
962 }
963 
964 /*
965  * In preparation for sending an "interim response", save
966  * all the state we'll need to run an async command later,
967  * and assign an "async id" for this (now async) command.
968  * See [MS-SMB2] 3.3.4.2
969  *
970  * If more than one request in a compound request tries to
971  * "go async", we can "say no".  See [MS-SMB2] 3.3.4.2
972  *	If an operation would require asynchronous processing
973  *	but resources are constrained, the server MAY choose to
974  *	fail that operation with STATUS_INSUFFICIENT_RESOURCES.
975  *
976  * For simplicity, we further restrict the cases where we're
977  * willing to "go async", and only allow the last command in a
978  * compound to "go async".  It happens that this is the only
979  * case where we're actually asked to go async anyway. This
980  * simplification also means there can be at most one command
981  * in a compound that "goes async" (the last one).
982  *
983  * If we agree to "go async", this should return STATUS_PENDING.
984  * Otherwise return STATUS_INSUFFICIENT_RESOURCES for this and
985  * all requests following this request.  (See the comments re.
986  * "sticky" smb2_status values in smb2sr_work).
987  *
988  * Note: the Async ID we assign here is arbitrary, and need only
989  * be unique among pending async responses on this connection, so
990  * this just uses an object address as the Async ID.
991  *
992  * Also, the assigned worker is the ONLY thread using this
993  * async request object (sr_async_req) so no locking.
994  */
995 uint32_t
996 smb2sr_go_async(smb_request_t *sr,
997     smb_sdrc_t (*async_func)(smb_request_t *))
998 {
999 	smb2_async_req_t *ar;
1000 
1001 	if (sr->smb2_next_command != 0)
1002 		return (NT_STATUS_INSUFFICIENT_RESOURCES);
1003 
1004 	ASSERT(sr->sr_async_req == NULL);
1005 	ar = kmem_zalloc(sizeof (*ar), KM_SLEEP);
1006 
1007 	/*
1008 	 * Place an interim response in the compound reply.
1009 	 *
1010 	 * Turn on the "async" flag for both the (synchronous)
1011 	 * interim response and the (later) async response,
1012 	 * by storing that in flags before coping into ar.
1013 	 */
1014 	sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND;
1015 	sr->smb2_async_id = (uintptr_t)ar;
1016 
1017 	ar->ar_func = async_func;
1018 	ar->ar_cmd_hdr = sr->smb2_cmd_hdr;
1019 	ar->ar_cmd_len = sr->smb_data.max_bytes - sr->smb2_cmd_hdr;
1020 
1021 	ar->ar_cmd_code = sr->smb2_cmd_code;
1022 	ar->ar_hdr_flags = sr->smb2_hdr_flags;
1023 	ar->ar_messageid = sr->smb2_messageid;
1024 	ar->ar_pid = sr->smb_pid;
1025 	ar->ar_tid = sr->smb_tid;
1026 	ar->ar_uid = sr->smb_uid;
1027 
1028 	sr->sr_async_req = ar;
1029 
1030 	/* Interim responses are NOT signed. */
1031 	sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED;
1032 
1033 	return (NT_STATUS_PENDING);
1034 }
1035 
1036 int
1037 smb2_decode_header(smb_request_t *sr)
1038 {
1039 	uint64_t ssnid;
1040 	uint32_t pid, tid;
1041 	uint16_t hdr_len;
1042 	int rc;
1043 
1044 	rc = smb_mbc_decodef(
1045 	    &sr->command, "Nwww..wwllqllq16c",
1046 	    &hdr_len,			/* w */
1047 	    &sr->smb2_credit_charge,	/* w */
1048 	    &sr->smb2_chan_seq,		/* w */
1049 	    /* reserved			  .. */
1050 	    &sr->smb2_cmd_code,		/* w */
1051 	    &sr->smb2_credit_request,	/* w */
1052 	    &sr->smb2_hdr_flags,	/* l */
1053 	    &sr->smb2_next_command,	/* l */
1054 	    &sr->smb2_messageid,	/* q */
1055 	    &pid,			/* l */
1056 	    &tid,			/* l */
1057 	    &ssnid,			/* q */
1058 	    sr->smb2_sig);		/* 16c */
1059 	if (rc)
1060 		return (rc);
1061 
1062 	if (hdr_len != SMB2_HDR_SIZE)
1063 		return (-1);
1064 
1065 	sr->smb_uid = (uint16_t)ssnid;	/* XXX wide UIDs */
1066 
1067 	if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
1068 		sr->smb2_async_id = pid |
1069 		    ((uint64_t)tid) << 32;
1070 	} else {
1071 		sr->smb_pid = pid;
1072 		sr->smb_tid = (uint16_t)tid; /* XXX wide TIDs */
1073 	}
1074 
1075 	return (rc);
1076 }
1077 
1078 int
1079 smb2_encode_header(smb_request_t *sr, boolean_t overwrite)
1080 {
1081 	uint64_t ssnid = sr->smb_uid;
1082 	uint64_t pid_tid_aid; /* pid+tid, or async id */
1083 	uint32_t reply_hdr_flags;
1084 	int rc;
1085 
1086 	if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
1087 		pid_tid_aid = sr->smb2_async_id;
1088 	} else {
1089 		pid_tid_aid = sr->smb_pid |
1090 		    ((uint64_t)sr->smb_tid) << 32;
1091 	}
1092 	reply_hdr_flags = sr->smb2_hdr_flags | SMB2_FLAGS_SERVER_TO_REDIR;
1093 
1094 	if (overwrite) {
1095 		rc = smb_mbc_poke(&sr->reply,
1096 		    sr->smb2_reply_hdr,
1097 		    "Nwwlwwllqqq16c",
1098 		    SMB2_HDR_SIZE,		/* w */
1099 		    sr->smb2_credit_charge,	/* w */
1100 		    sr->smb2_status,		/* l */
1101 		    sr->smb2_cmd_code,		/* w */
1102 		    sr->smb2_credit_response,	/* w */
1103 		    reply_hdr_flags,		/* l */
1104 		    sr->smb2_next_reply,	/* l */
1105 		    sr->smb2_messageid,		/* q */
1106 		    pid_tid_aid,		/* q */
1107 		    ssnid,			/* q */
1108 		    sr->smb2_sig);		/* 16c */
1109 	} else {
1110 		rc = smb_mbc_encodef(&sr->reply,
1111 		    "Nwwlwwllqqq16c",
1112 		    SMB2_HDR_SIZE,		/* w */
1113 		    sr->smb2_credit_charge,	/* w */
1114 		    sr->smb2_status,		/* l */
1115 		    sr->smb2_cmd_code,		/* w */
1116 		    sr->smb2_credit_response,	/* w */
1117 		    reply_hdr_flags,		/* l */
1118 		    sr->smb2_next_reply,	/* l */
1119 		    sr->smb2_messageid,		/* q */
1120 		    pid_tid_aid,		/* q */
1121 		    ssnid,			/* q */
1122 		    sr->smb2_sig);		/* 16c */
1123 	}
1124 
1125 	return (rc);
1126 }
1127 
1128 void
1129 smb2_send_reply(smb_request_t *sr)
1130 {
1131 
1132 	if (smb_session_send(sr->session, 0, &sr->reply) == 0)
1133 		sr->reply.chain = 0;
1134 }
1135 
1136 /*
1137  * This wrapper function exists to help catch calls to smbsr_status()
1138  * (which is SMB1-specific) in common code.  See smbsr_status().
1139  * If the log message below is seen, put a dtrace probe on this
1140  * function with a stack() action to see who is calling the SMB1
1141  * "put error" from common code, and fix it.
1142  */
1143 void
1144 smbsr_status_smb2(smb_request_t *sr, DWORD status)
1145 {
1146 	const char *name;
1147 
1148 	if (sr->smb2_cmd_code < SMB2__NCMDS)
1149 		name = smb2_disp_table[sr->smb2_cmd_code].sdt_name;
1150 	else
1151 		name = "<unknown>";
1152 #ifdef	DEBUG
1153 	cmn_err(CE_NOTE, "smbsr_status called for %s", name);
1154 #endif
1155 
1156 	smb2sr_put_error_data(sr, status, NULL);
1157 }
1158 
1159 void
1160 smb2sr_put_errno(struct smb_request *sr, int errnum)
1161 {
1162 	uint32_t status = smb_errno2status(errnum);
1163 	smb2sr_put_error_data(sr, status, NULL);
1164 }
1165 
1166 void
1167 smb2sr_put_error(smb_request_t *sr, uint32_t status)
1168 {
1169 	smb2sr_put_error_data(sr, status, NULL);
1170 }
1171 
1172 /*
1173  * Build an SMB2 error response.  [MS-SMB2] 2.2.2
1174  */
1175 void
1176 smb2sr_put_error_data(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc)
1177 {
1178 	DWORD len;
1179 
1180 	/*
1181 	 * The common dispatch code writes this when it
1182 	 * updates the SMB2 header before sending.
1183 	 */
1184 	sr->smb2_status = status;
1185 
1186 	/* Rewind to the end of the SMB header. */
1187 	sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE;
1188 
1189 	/*
1190 	 * NB: Must provide at least one byte of error data,
1191 	 * per [MS-SMB2] 2.2.2
1192 	 */
1193 	if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) {
1194 		(void) smb_mbc_encodef(
1195 		    &sr->reply,
1196 		    "wwlC",
1197 		    9,	/* StructSize */	/* w */
1198 		    0,	/* reserved */		/* w */
1199 		    len,			/* l */
1200 		    mbc);			/* C */
1201 	} else {
1202 		(void) smb_mbc_encodef(
1203 		    &sr->reply,
1204 		    "wwl.",
1205 		    9,	/* StructSize */	/* w */
1206 		    0,	/* reserved */		/* w */
1207 		    0);				/* l. */
1208 	}
1209 }
1210 
1211 /*
1212  * smb2sr_lookup_fid
1213  *
1214  * Setup sr->fid_ofile, either inherited from a related command,
1215  * or obtained via FID lookup.  Similar inheritance logic as in
1216  * smb2sr_work.
1217  */
1218 uint32_t
1219 smb2sr_lookup_fid(smb_request_t *sr, smb2fid_t *fid)
1220 {
1221 	boolean_t related = sr->smb2_hdr_flags &
1222 	    SMB2_FLAGS_RELATED_OPERATIONS;
1223 
1224 	if (related) {
1225 		if (sr->fid_ofile == NULL)
1226 			return (NT_STATUS_INVALID_PARAMETER);
1227 		sr->smb_fid = sr->fid_ofile->f_fid;
1228 		return (0);
1229 	}
1230 
1231 	/*
1232 	 * If we could be sure this is called only once per cmd,
1233 	 * we could simply ASSERT(sr->fid_ofile == NULL) here.
1234 	 * However, there are cases where it can be called again
1235 	 * handling the same command, so let's tolerate that.
1236 	 */
1237 	if (sr->fid_ofile == NULL) {
1238 		sr->smb_fid = (uint16_t)fid->temporal;
1239 		sr->fid_ofile = smb_ofile_lookup_by_fid(sr, sr->smb_fid);
1240 	}
1241 	if (sr->fid_ofile == NULL)
1242 		return (NT_STATUS_FILE_CLOSED);
1243 
1244 	return (0);
1245 }
1246 
1247 /*
1248  * smb2_dispatch_stats_init
1249  *
1250  * Initializes dispatch statistics for SMB2.
1251  * See also smb_dispatch_stats_init(), which fills in
1252  * the lower part of the statistics array, from zero
1253  * through SMB_COM_NUM;
1254  */
1255 void
1256 smb2_dispatch_stats_init(smb_server_t *sv)
1257 {
1258 	smb_disp_stats_t *sds = sv->sv_disp_stats2;
1259 	smb_kstat_req_t *ksr;
1260 	int		i;
1261 
1262 	ksr = ((smbsrv_kstats_t *)sv->sv_ksp->ks_data)->ks_reqs2;
1263 
1264 	for (i = 0; i < SMB2__NCMDS; i++, ksr++) {
1265 		smb_latency_init(&sds[i].sdt_lat);
1266 		(void) strlcpy(ksr->kr_name, smb2_disp_table[i].sdt_name,
1267 		    sizeof (ksr->kr_name));
1268 	}
1269 }
1270 
1271 /*
1272  * smb2_dispatch_stats_fini
1273  *
1274  * Frees and destroyes the resources used for statistics.
1275  */
1276 void
1277 smb2_dispatch_stats_fini(smb_server_t *sv)
1278 {
1279 	smb_disp_stats_t *sds = sv->sv_disp_stats2;
1280 	int	i;
1281 
1282 	for (i = 0; i < SMB2__NCMDS; i++)
1283 		smb_latency_destroy(&sds[i].sdt_lat);
1284 }
1285 
1286 void
1287 smb2_dispatch_stats_update(smb_server_t *sv,
1288     smb_kstat_req_t *ksr, int first, int nreq)
1289 {
1290 	smb_disp_stats_t *sds = sv->sv_disp_stats2;
1291 	int	i;
1292 	int	last;
1293 
1294 	last = first + nreq - 1;
1295 
1296 	if ((first < SMB2__NCMDS) && (last < SMB2__NCMDS))  {
1297 		for (i = first; i <= last; i++, ksr++) {
1298 			ksr->kr_rxb = sds[i].sdt_rxb;
1299 			ksr->kr_txb = sds[i].sdt_txb;
1300 			mutex_enter(&sds[i].sdt_lat.ly_mutex);
1301 			ksr->kr_nreq = sds[i].sdt_lat.ly_a_nreq;
1302 			ksr->kr_sum = sds[i].sdt_lat.ly_a_sum;
1303 			ksr->kr_a_mean = sds[i].sdt_lat.ly_a_mean;
1304 			ksr->kr_a_stddev =
1305 			    sds[i].sdt_lat.ly_a_stddev;
1306 			ksr->kr_d_mean = sds[i].sdt_lat.ly_d_mean;
1307 			ksr->kr_d_stddev =
1308 			    sds[i].sdt_lat.ly_d_stddev;
1309 			sds[i].sdt_lat.ly_d_mean = 0;
1310 			sds[i].sdt_lat.ly_d_nreq = 0;
1311 			sds[i].sdt_lat.ly_d_stddev = 0;
1312 			sds[i].sdt_lat.ly_d_sum = 0;
1313 			mutex_exit(&sds[i].sdt_lat.ly_mutex);
1314 		}
1315 	}
1316 }
1317