xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c (revision 93bc28dbaee6387120d48b12b3dc1ba5f7418e6e)
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, or arrange
312  * for it to be completed and free'd later (if SDRC_SR_KEPT).
313  */
314 void
315 smb2sr_work(struct smb_request *sr)
316 {
317 	const smb_disp_entry_t	*sdd;
318 	smb_disp_stats_t	*sds;
319 	smb_session_t		*session;
320 	uint32_t		msg_len;
321 	uint16_t		cmd_idx;
322 	int			rc = 0;
323 	boolean_t		disconnect = B_FALSE;
324 	boolean_t		related;
325 
326 	session = sr->session;
327 
328 	ASSERT(sr->tid_tree == 0);
329 	ASSERT(sr->uid_user == 0);
330 	ASSERT(sr->fid_ofile == 0);
331 	sr->smb_fid = (uint16_t)-1;
332 	sr->smb2_status = 0;
333 
334 	/* temporary until we identify a user */
335 	sr->user_cr = zone_kcred();
336 
337 cmd_start:
338 	/*
339 	 * Note that we don't check sr_state here and abort the
340 	 * compound if cancelled (etc.) because some SMB2 command
341 	 * handlers need to do work even when cancelled.
342 	 *
343 	 * We treat some status codes as if "sticky", meaning
344 	 * once they're set after some command handler returns,
345 	 * all remaining commands get this status without even
346 	 * calling the command-specific handler.
347 	 */
348 	if (sr->smb2_status != NT_STATUS_CANCELLED &&
349 	    sr->smb2_status != NT_STATUS_INSUFFICIENT_RESOURCES)
350 		sr->smb2_status = 0;
351 
352 	/*
353 	 * Decode the request header
354 	 *
355 	 * Most problems with decoding will result in the error
356 	 * STATUS_INVALID_PARAMETER.  If the decoding problem
357 	 * prevents continuing, we'll close the connection.
358 	 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
359 	 */
360 	sr->smb2_cmd_hdr = sr->command.chain_offset;
361 	if ((rc = smb2_decode_header(sr)) != 0) {
362 		cmn_err(CE_WARN, "clnt %s bad SMB2 header",
363 		    session->ip_addr_str);
364 		disconnect = B_TRUE;
365 		goto cleanup;
366 	}
367 
368 	/*
369 	 * The SMB2_FLAGS_SERVER_TO_REDIR should only appear
370 	 * in messages from the server back to the client.
371 	 */
372 	if ((sr->smb2_hdr_flags & SMB2_FLAGS_SERVER_TO_REDIR) != 0) {
373 		cmn_err(CE_WARN, "clnt %s bad SMB2 flags",
374 		    session->ip_addr_str);
375 		disconnect = B_TRUE;
376 		goto cleanup;
377 	}
378 	related = (sr->smb2_hdr_flags & SMB2_FLAGS_RELATED_OPERATIONS);
379 
380 	/*
381 	 * In case we bail out with an error before we get to the
382 	 * section that computes the credit grant, initialize the
383 	 * response header fields so that credits won't change.
384 	 * Note: SMB 2.02 clients may send credit charge zero.
385 	 */
386 	if (sr->smb2_credit_charge == 0)
387 		sr->smb2_credit_charge = 1;
388 	sr->smb2_credit_response = sr->smb2_credit_charge;
389 
390 	/*
391 	 * Reserve space for the reply header, and save the offset.
392 	 * The reply header will be overwritten later.  If we have
393 	 * already exhausted the output space, then this client is
394 	 * trying something funny.  Log it and kill 'em.
395 	 */
396 	sr->smb2_reply_hdr = sr->reply.chain_offset;
397 	if ((rc = smb2_encode_header(sr, B_FALSE)) != 0) {
398 		cmn_err(CE_WARN, "clnt %s excessive reply",
399 		    session->ip_addr_str);
400 		disconnect = B_TRUE;
401 		goto cleanup;
402 	}
403 
404 	/*
405 	 * Figure out the length of data following the SMB2 header.
406 	 * It ends at either the next SMB2 header if there is one
407 	 * (smb2_next_command != 0) or at the end of the message.
408 	 */
409 	if (sr->smb2_next_command != 0) {
410 		/* [MS-SMB2] says this is 8-byte aligned */
411 		msg_len = sr->smb2_next_command;
412 		if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) ||
413 		    ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) {
414 			cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd",
415 			    session->ip_addr_str);
416 			disconnect = B_TRUE;
417 			goto cleanup;
418 		}
419 	} else {
420 		msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr;
421 	}
422 
423 	/*
424 	 * Setup a shadow chain for this SMB2 command, starting
425 	 * with the header and ending at either the next command
426 	 * or the end of the message.  The signing check below
427 	 * needs the entire SMB2 command.  After that's done, we
428 	 * advance chain_offset to the end of the header where
429 	 * the command specific handlers continue decoding.
430 	 */
431 	(void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
432 	    sr->smb2_cmd_hdr, msg_len);
433 
434 	/*
435 	 * We will consume the data for this request from smb_data.
436 	 * That effectively consumes msg_len bytes from sr->command
437 	 * but doesn't update its chain_offset, so we need to update
438 	 * that here to make later received bytes accounting work.
439 	 */
440 	sr->command.chain_offset = sr->smb2_cmd_hdr + msg_len;
441 	ASSERT(sr->command.chain_offset <= sr->command.max_bytes);
442 
443 	/*
444 	 * Validate the commmand code, get dispatch table entries.
445 	 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted...
446 	 *
447 	 * The last slot in the dispatch table is used to handle
448 	 * invalid commands.  Same for statistics.
449 	 */
450 	if (sr->smb2_cmd_code < SMB2_INVALID_CMD)
451 		cmd_idx = sr->smb2_cmd_code;
452 	else
453 		cmd_idx = SMB2_INVALID_CMD;
454 	sdd = &smb2_disp_table[cmd_idx];
455 	sds = &session->s_server->sv_disp_stats2[cmd_idx];
456 
457 	/*
458 	 * If this command is NOT "related" to the previous,
459 	 * clear out the UID, TID, FID state that might be
460 	 * left over from the previous command.
461 	 *
462 	 * If the command IS related, any new IDs are ignored,
463 	 * and we simply continue with the previous user, tree,
464 	 * and open file.
465 	 */
466 	if (!related) {
467 		/*
468 		 * Drop user, tree, file; carefully ordered to
469 		 * avoid dangling references: file, tree, user
470 		 */
471 		if (sr->fid_ofile != NULL) {
472 			smb_ofile_request_complete(sr->fid_ofile);
473 			smb_ofile_release(sr->fid_ofile);
474 			sr->fid_ofile = NULL;
475 		}
476 		if (sr->tid_tree != NULL) {
477 			smb_tree_release(sr->tid_tree);
478 			sr->tid_tree = NULL;
479 		}
480 		if (sr->uid_user != NULL) {
481 			smb_user_release(sr->uid_user);
482 			sr->uid_user = NULL;
483 			sr->user_cr = zone_kcred();
484 		}
485 	}
486 
487 	/*
488 	 * Make sure we have a user and tree as needed
489 	 * according to the flags for the this command.
490 	 * Note that we may have inherited these.
491 	 */
492 	if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0) {
493 		/*
494 		 * This command requires a user session.
495 		 */
496 		if (related) {
497 			/*
498 			 * Previous command should have given us a user.
499 			 * [MS-SMB2] 3.3.5.2 Handling Related Requests
500 			 */
501 			if (sr->uid_user == NULL) {
502 				smb2sr_put_error(sr,
503 				    NT_STATUS_INVALID_PARAMETER);
504 				goto cmd_done;
505 			}
506 			sr->smb_uid = sr->uid_user->u_uid;
507 		} else {
508 			/*
509 			 * Lookup the UID
510 			 * [MS-SMB2] 3.3.5.2 Verifying the Session
511 			 */
512 			ASSERT(sr->uid_user == NULL);
513 			sr->uid_user = smb_session_lookup_uid(session,
514 			    sr->smb_uid);
515 			if (sr->uid_user == NULL) {
516 				smb2sr_put_error(sr,
517 				    NT_STATUS_USER_SESSION_DELETED);
518 				goto cmd_done;
519 			}
520 			sr->user_cr = smb_user_getcred(sr->uid_user);
521 		}
522 		ASSERT(sr->uid_user != NULL);
523 	}
524 
525 	if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0) {
526 		/*
527 		 * This command requires a tree connection.
528 		 */
529 		if (related) {
530 			/*
531 			 * Previous command should have given us a tree.
532 			 * [MS-SMB2] 3.3.5.2 Handling Related Requests
533 			 */
534 			if (sr->tid_tree == NULL) {
535 				smb2sr_put_error(sr,
536 				    NT_STATUS_INVALID_PARAMETER);
537 				goto cmd_done;
538 			}
539 			sr->smb_tid = sr->tid_tree->t_tid;
540 		} else {
541 			/*
542 			 * Lookup the TID
543 			 * [MS-SMB2] 3.3.5.2 Verifying the Tree Connect
544 			 */
545 			ASSERT(sr->tid_tree == NULL);
546 			sr->tid_tree = smb_session_lookup_tree(session,
547 			    sr->smb_tid);
548 			if (sr->tid_tree == NULL) {
549 				smb2sr_put_error(sr,
550 				    NT_STATUS_NETWORK_NAME_DELETED);
551 				goto cmd_done;
552 			}
553 		}
554 		ASSERT(sr->tid_tree != NULL);
555 	}
556 
557 	/*
558 	 * SMB2 signature verification, two parts:
559 	 * (a) Require SMB2_FLAGS_SIGNED (for most request types)
560 	 * (b) If SMB2_FLAGS_SIGNED is set, check the signature.
561 	 * [MS-SMB2] 3.3.5.2.4 Verifying the Signature
562 	 */
563 
564 	/*
565 	 * No user session means no signature check.  That's OK,
566 	 * i.e. for commands marked SDDF_SUPPRESS_UID above.
567 	 * Note, this also means we won't sign the reply.
568 	 */
569 	if (sr->uid_user == NULL)
570 		sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED;
571 
572 	/*
573 	 * The SDDF_SUPPRESS_UID dispatch is set for requests that
574 	 * don't need a UID (user).  These also don't require a
575 	 * signature check here.
576 	 */
577 	if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 &&
578 	    sr->uid_user != NULL &&
579 	    (sr->uid_user->u_sign_flags & SMB_SIGNING_CHECK) != 0) {
580 		/*
581 		 * This request type should be signed, and
582 		 * we're configured to require signatures.
583 		 */
584 		if ((sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) == 0) {
585 			smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED);
586 			goto cmd_done;
587 		}
588 		rc = smb2_sign_check_request(sr);
589 		if (rc != 0) {
590 			DTRACE_PROBE1(smb2__sign__check, smb_request_t, sr);
591 			smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED);
592 			goto cmd_done;
593 		}
594 	}
595 
596 	/*
597 	 * Now that the signing check is done with smb_data,
598 	 * advance past the SMB2 header we decoded earlier.
599 	 * This leaves sr->smb_data correctly positioned
600 	 * for command-specific decoding in the dispatch
601 	 * function called next.
602 	 */
603 	sr->smb_data.chain_offset = sr->smb2_cmd_hdr + SMB2_HDR_SIZE;
604 
605 	/*
606 	 * SMB2 credits determine how many simultaneous commands the
607 	 * client may issue, and bounds the range of message IDs those
608 	 * commands may use.  With multi-credit support, commands may
609 	 * use ranges of message IDs, where the credits used by each
610 	 * command are proportional to their data transfer size.
611 	 *
612 	 * Every command may request an increase or decrease of
613 	 * the currently granted credits, based on the difference
614 	 * between the credit request and the credit charge.
615 	 * [MS-SMB2] 3.3.1.2 Algorithm for the Granting of Credits
616 	 *
617 	 * Most commands have credit_request=1, credit_charge=1,
618 	 * which keeps the credit grant unchanged.
619 	 *
620 	 * All we're really doing here (for now) is reducing the
621 	 * credit_response if the client requests a credit increase
622 	 * that would take their credit over the maximum, and
623 	 * limiting the decrease so they don't run out of credits.
624 	 *
625 	 * Later, this could do something dynamic based on load.
626 	 *
627 	 * One other non-obvious bit about credits: We keep the
628 	 * session s_max_credits low until the 1st authentication,
629 	 * at which point we'll set the normal maximum_credits.
630 	 * Some clients ask for more credits with session setup,
631 	 * and we need to handle that requested increase _after_
632 	 * the command-specific handler returns so it won't be
633 	 * restricted to the lower (pre-auth) limit.
634 	 */
635 	sr->smb2_credit_response = sr->smb2_credit_request;
636 	if (sr->smb2_credit_request < sr->smb2_credit_charge) {
637 		uint16_t cur, d;
638 
639 		mutex_enter(&session->s_credits_mutex);
640 		cur = session->s_cur_credits;
641 
642 		/* Handle credit decrease. */
643 		d = sr->smb2_credit_charge - sr->smb2_credit_request;
644 		cur -= d;
645 		if (cur & 0x8000) {
646 			/*
647 			 * underflow (bad credit charge or request)
648 			 * leave credits unchanged (response=charge)
649 			 */
650 			cur = session->s_cur_credits;
651 			sr->smb2_credit_response = sr->smb2_credit_charge;
652 			DTRACE_PROBE1(smb2__credit__neg, smb_request_t, sr);
653 		}
654 
655 		/*
656 		 * The server MUST ensure that the number of credits
657 		 * held by the client is never reduced to zero.
658 		 * [MS-SMB2] 3.3.1.2
659 		 */
660 		if (cur == 0) {
661 			cur = 1;
662 			sr->smb2_credit_response += 1;
663 			DTRACE_PROBE1(smb2__credit__min, smb_request_t, sr);
664 		}
665 
666 		DTRACE_PROBE3(smb2__credit__decrease,
667 		    smb_request_t, sr, int, (int)cur,
668 		    int, (int)session->s_cur_credits);
669 
670 		session->s_cur_credits = cur;
671 		mutex_exit(&session->s_credits_mutex);
672 	}
673 
674 	/*
675 	 * The real work: call the SMB2 command handler
676 	 * (except for "sticky" smb2_status - see above)
677 	 */
678 	sr->sr_time_start = gethrtime();
679 	rc = SDRC_SUCCESS;
680 	if (sr->smb2_status == 0) {
681 		/* NB: not using pre_op */
682 		rc = (*sdd->sdt_function)(sr);
683 		/* NB: not using post_op */
684 	} else {
685 		smb2sr_put_error(sr, sr->smb2_status);
686 	}
687 
688 	MBC_FLUSH(&sr->raw_data);
689 
690 	/*
691 	 * Second half of SMB2 credit handling (increases)
692 	 */
693 	if (sr->smb2_credit_request > sr->smb2_credit_charge) {
694 		uint16_t cur, d;
695 
696 		mutex_enter(&session->s_credits_mutex);
697 		cur = session->s_cur_credits;
698 
699 		/* Handle credit increase. */
700 		d = sr->smb2_credit_request - sr->smb2_credit_charge;
701 		cur += d;
702 
703 		/*
704 		 * If new credits would be above max,
705 		 * reduce the credit grant.
706 		 */
707 		if (cur > session->s_max_credits) {
708 			d = cur - session->s_max_credits;
709 			cur = session->s_max_credits;
710 			sr->smb2_credit_response -= d;
711 			DTRACE_PROBE1(smb2__credit__max, smb_request_t, sr);
712 		}
713 
714 		DTRACE_PROBE3(smb2__credit__increase,
715 		    smb_request_t, sr, int, (int)cur,
716 		    int, (int)session->s_cur_credits);
717 
718 		session->s_cur_credits = cur;
719 		mutex_exit(&session->s_credits_mutex);
720 	}
721 
722 cmd_done:
723 	/*
724 	 * Pad the reply to align(8) if necessary.
725 	 */
726 	if (sr->reply.chain_offset & 7) {
727 		int padsz = 8 - (sr->reply.chain_offset & 7);
728 		(void) smb_mbc_encodef(&sr->reply, "#.", padsz);
729 	}
730 	ASSERT((sr->reply.chain_offset & 7) == 0);
731 
732 	/*
733 	 * Record some statistics: latency, rx bytes, tx bytes.
734 	 */
735 	smb_server_inc_req(sr->sr_server);
736 	smb_latency_add_sample(&sds->sdt_lat,
737 	    gethrtime() - sr->sr_time_start);
738 	atomic_add_64(&sds->sdt_rxb,
739 	    (int64_t)(sr->command.chain_offset - sr->smb2_cmd_hdr));
740 	atomic_add_64(&sds->sdt_txb,
741 	    (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr));
742 
743 	switch (rc) {
744 	case SDRC_SUCCESS:
745 		break;
746 	default:
747 		/*
748 		 * SMB2 does not use the other dispatch return codes.
749 		 * If we see something else, log an event so we'll
750 		 * know something is returning bogus status codes.
751 		 * If you see these in the log, use dtrace to find
752 		 * the code returning something else.
753 		 */
754 #ifdef	DEBUG
755 		cmn_err(CE_NOTE, "handler for %u returned 0x%x",
756 		    sr->smb2_cmd_code, rc);
757 #endif
758 		sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
759 		break;
760 	case SDRC_ERROR:
761 		/*
762 		 * Many command handlers return SDRC_ERROR for any
763 		 * problems decoding the request, and don't bother
764 		 * setting smb2_status.  For those cases, the best
765 		 * status return would be "invalid parameter".
766 		 */
767 		if (sr->smb2_status == 0)
768 			sr->smb2_status = NT_STATUS_INVALID_PARAMETER;
769 		break;
770 	case SDRC_DROP_VC:
771 		disconnect = B_TRUE;
772 		goto cleanup;
773 
774 	case SDRC_NO_REPLY:
775 		/* will free sr */
776 		goto cleanup;
777 	}
778 
779 	/*
780 	 * If there's a next command, figure out where it starts,
781 	 * and fill in the next command offset for the reply.
782 	 * Note: We sanity checked smb2_next_command above
783 	 * (the offset to the next command).  Similarly set
784 	 * smb2_next_reply as the offset to the next reply.
785 	 */
786 	if (sr->smb2_next_command != 0) {
787 		sr->command.chain_offset =
788 		    sr->smb2_cmd_hdr + sr->smb2_next_command;
789 		sr->smb2_next_reply =
790 		    sr->reply.chain_offset - sr->smb2_reply_hdr;
791 	} else {
792 		sr->smb2_next_reply = 0;
793 	}
794 
795 	/*
796 	 * Overwrite the SMB2 header for the response of
797 	 * this command (possibly part of a compound).
798 	 * encode_header adds: SMB2_FLAGS_SERVER_TO_REDIR
799 	 */
800 	(void) smb2_encode_header(sr, B_TRUE);
801 
802 	if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED)
803 		smb2_sign_reply(sr);
804 
805 	if (sr->smb2_next_command != 0)
806 		goto cmd_start;
807 
808 	/*
809 	 * We've done all the commands in this compound.
810 	 * Send it out.
811 	 */
812 	smb2_send_reply(sr);
813 
814 	/*
815 	 * If any of the requests "went async", process those now.
816 	 * The async. function "keeps" this sr, changing its state
817 	 * to completed and calling smb_request_free().
818 	 */
819 	if (sr->sr_async_req != NULL) {
820 		smb2sr_do_async(sr);
821 		return;
822 	}
823 
824 cleanup:
825 	if (disconnect) {
826 		smb_rwx_rwenter(&session->s_lock, RW_WRITER);
827 		switch (session->s_state) {
828 		case SMB_SESSION_STATE_DISCONNECTED:
829 		case SMB_SESSION_STATE_TERMINATED:
830 			break;
831 		default:
832 			smb_soshutdown(session->sock);
833 			session->s_state = SMB_SESSION_STATE_DISCONNECTED;
834 			break;
835 		}
836 		smb_rwx_rwexit(&session->s_lock);
837 	}
838 
839 	mutex_enter(&sr->sr_mutex);
840 	sr->sr_state = SMB_REQ_STATE_COMPLETED;
841 	mutex_exit(&sr->sr_mutex);
842 
843 	smb_request_free(sr);
844 }
845 
846 /*
847  * Dispatch an async request using saved information.
848  * See smb2sr_save_async and [MS-SMB2] 3.3.4.2
849  *
850  * This is sort of a "lite" version of smb2sr_work.  Initialize the
851  * command and reply areas as they were when the command-speicific
852  * handler started (in case it needs to decode anything again).
853  * Call the async function, which builds the command-specific part
854  * of the response.  Finally, send the response and free the sr.
855  */
856 void
857 smb2sr_do_async(smb_request_t *sr)
858 {
859 	const smb_disp_entry_t	*sdd;
860 	smb2_async_req_t	*ar;
861 	smb_sdrc_t		(*ar_func)(smb_request_t *);
862 	int sdrc;
863 
864 	/*
865 	 * Restore what smb2_decode_header found.
866 	 * (In lieu of decoding it again.)
867 	 */
868 	ar = sr->sr_async_req;
869 	sr->smb2_cmd_hdr   = ar->ar_cmd_hdr;
870 	sr->smb2_cmd_code  = ar->ar_cmd_code;
871 	sr->smb2_hdr_flags = ar->ar_hdr_flags;
872 	sr->smb2_async_id  = (uintptr_t)ar;
873 	sr->smb2_messageid = ar->ar_messageid;
874 	sr->smb_pid = ar->ar_pid;
875 	sr->smb_tid = ar->ar_tid;
876 	sr->smb_uid = ar->ar_uid;
877 	sr->smb2_status = 0;
878 
879 	/*
880 	 * Async requests don't grant credits, because any credits
881 	 * should have gone out with the interim reply.
882 	 * An async reply goes alone (no next reply).
883 	 */
884 	sr->smb2_credit_response = 0;
885 	sr->smb2_next_reply = 0;
886 
887 	/*
888 	 * Setup input mbuf_chain
889 	 */
890 	ASSERT(ar->ar_cmd_len >= SMB2_HDR_SIZE);
891 	(void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
892 	    sr->smb2_cmd_hdr + SMB2_HDR_SIZE,
893 	    ar->ar_cmd_len - SMB2_HDR_SIZE);
894 
895 	/*
896 	 * Done with sr_async_req
897 	 */
898 	ar_func = ar->ar_func;
899 	kmem_free(ar, sizeof (*ar));
900 	sr->sr_async_req = ar = NULL;
901 
902 	/*
903 	 * Setup output mbuf_chain
904 	 */
905 	MBC_FLUSH(&sr->reply);
906 	sr->smb2_reply_hdr = sr->reply.chain_offset;
907 	(void) smb2_encode_header(sr, B_FALSE);
908 
909 	VERIFY3U(sr->smb2_cmd_code, <, SMB2_INVALID_CMD);
910 	sdd = &smb2_disp_table[sr->smb2_cmd_code];
911 
912 	/*
913 	 * Keep the UID, TID, ofile we have.
914 	 */
915 	if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 &&
916 	    sr->uid_user == NULL) {
917 		smb2sr_put_error(sr, NT_STATUS_USER_SESSION_DELETED);
918 		goto cmd_done;
919 	}
920 	if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0 &&
921 	    sr->tid_tree == NULL) {
922 		smb2sr_put_error(sr, NT_STATUS_NETWORK_NAME_DELETED);
923 		goto cmd_done;
924 	}
925 
926 	/*
927 	 * Signature already verified
928 	 * Credits handled...
929 	 *
930 	 * Just call the async handler function.
931 	 */
932 	sdrc = ar_func(sr);
933 	switch (sdrc) {
934 	case SDRC_SUCCESS:
935 		break;
936 	case SDRC_ERROR:
937 		if (sr->smb2_status == 0)
938 			sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
939 		break;
940 	case SDRC_SR_KEPT:
941 		/* This SR will be completed later. */
942 		return;
943 	}
944 
945 cmd_done:
946 	smb2sr_finish_async(sr);
947 }
948 
949 void
950 smb2sr_finish_async(smb_request_t *sr)
951 {
952 	smb_disp_stats_t	*sds;
953 
954 	/*
955 	 * Pad the reply to align(8) if necessary.
956 	 */
957 	if (sr->reply.chain_offset & 7) {
958 		int padsz = 8 - (sr->reply.chain_offset & 7);
959 		(void) smb_mbc_encodef(&sr->reply, "#.", padsz);
960 	}
961 	ASSERT((sr->reply.chain_offset & 7) == 0);
962 
963 	/*
964 	 * Record some statistics: (just tx bytes here)
965 	 */
966 	sds = &sr->session->s_server->sv_disp_stats2[sr->smb2_cmd_code];
967 	atomic_add_64(&sds->sdt_txb, (int64_t)(sr->reply.chain_offset));
968 
969 	/*
970 	 * Put (overwrite) the final SMB2 header.
971 	 * The call adds: SMB2_FLAGS_SERVER_TO_REDIR
972 	 */
973 	(void) smb2_encode_header(sr, B_TRUE);
974 
975 	if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED)
976 		smb2_sign_reply(sr);
977 
978 	smb2_send_reply(sr);
979 
980 	/*
981 	 * Done.  Unlink and free.
982 	 */
983 
984 	mutex_enter(&sr->sr_mutex);
985 	sr->sr_state = SMB_REQ_STATE_COMPLETED;
986 	mutex_exit(&sr->sr_mutex);
987 
988 	smb_request_free(sr);
989 }
990 
991 /*
992  * In preparation for sending an "interim response", save
993  * all the state we'll need to run an async command later,
994  * and assign an "async id" for this (now async) command.
995  * See [MS-SMB2] 3.3.4.2
996  *
997  * If more than one request in a compound request tries to
998  * "go async", we can "say no".  See [MS-SMB2] 3.3.4.2
999  *	If an operation would require asynchronous processing
1000  *	but resources are constrained, the server MAY choose to
1001  *	fail that operation with STATUS_INSUFFICIENT_RESOURCES.
1002  *
1003  * For simplicity, we further restrict the cases where we're
1004  * willing to "go async", and only allow the last command in a
1005  * compound to "go async".  It happens that this is the only
1006  * case where we're actually asked to go async anyway. This
1007  * simplification also means there can be at most one command
1008  * in a compound that "goes async" (the last one).
1009  *
1010  * If we agree to "go async", this should return STATUS_PENDING.
1011  * Otherwise return STATUS_INSUFFICIENT_RESOURCES for this and
1012  * all requests following this request.  (See the comments re.
1013  * "sticky" smb2_status values in smb2sr_work).
1014  *
1015  * Note: the Async ID we assign here is arbitrary, and need only
1016  * be unique among pending async responses on this connection, so
1017  * this just uses an object address as the Async ID.
1018  *
1019  * Also, the assigned worker is the ONLY thread using this
1020  * async request object (sr_async_req) so no locking.
1021  */
1022 uint32_t
1023 smb2sr_go_async(smb_request_t *sr,
1024     smb_sdrc_t (*async_func)(smb_request_t *))
1025 {
1026 	smb2_async_req_t *ar;
1027 
1028 	if (sr->smb2_next_command != 0)
1029 		return (NT_STATUS_INSUFFICIENT_RESOURCES);
1030 
1031 	ASSERT(sr->sr_async_req == NULL);
1032 	ar = kmem_zalloc(sizeof (*ar), KM_SLEEP);
1033 
1034 	/*
1035 	 * Place an interim response in the compound reply.
1036 	 *
1037 	 * Turn on the "async" flag for both the (synchronous)
1038 	 * interim response and the (later) async response,
1039 	 * by storing that in flags before coping into ar.
1040 	 */
1041 	sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND;
1042 	sr->smb2_async_id = (uintptr_t)ar;
1043 
1044 	ar->ar_func = async_func;
1045 	ar->ar_cmd_hdr = sr->smb2_cmd_hdr;
1046 	ar->ar_cmd_len = sr->smb_data.max_bytes - sr->smb2_cmd_hdr;
1047 
1048 	ar->ar_cmd_code = sr->smb2_cmd_code;
1049 	ar->ar_hdr_flags = sr->smb2_hdr_flags;
1050 	ar->ar_messageid = sr->smb2_messageid;
1051 	ar->ar_pid = sr->smb_pid;
1052 	ar->ar_tid = sr->smb_tid;
1053 	ar->ar_uid = sr->smb_uid;
1054 
1055 	sr->sr_async_req = ar;
1056 
1057 	/* Interim responses are NOT signed. */
1058 	sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED;
1059 
1060 	return (NT_STATUS_PENDING);
1061 }
1062 
1063 int
1064 smb2_decode_header(smb_request_t *sr)
1065 {
1066 	uint64_t ssnid;
1067 	uint32_t pid, tid;
1068 	uint16_t hdr_len;
1069 	int rc;
1070 
1071 	rc = smb_mbc_decodef(
1072 	    &sr->command, "Nwww..wwllqllq16c",
1073 	    &hdr_len,			/* w */
1074 	    &sr->smb2_credit_charge,	/* w */
1075 	    &sr->smb2_chan_seq,		/* w */
1076 	    /* reserved			  .. */
1077 	    &sr->smb2_cmd_code,		/* w */
1078 	    &sr->smb2_credit_request,	/* w */
1079 	    &sr->smb2_hdr_flags,	/* l */
1080 	    &sr->smb2_next_command,	/* l */
1081 	    &sr->smb2_messageid,	/* q */
1082 	    &pid,			/* l */
1083 	    &tid,			/* l */
1084 	    &ssnid,			/* q */
1085 	    sr->smb2_sig);		/* 16c */
1086 	if (rc)
1087 		return (rc);
1088 
1089 	if (hdr_len != SMB2_HDR_SIZE)
1090 		return (-1);
1091 
1092 	sr->smb_uid = (uint16_t)ssnid;	/* XXX wide UIDs */
1093 
1094 	if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
1095 		sr->smb2_async_id = pid |
1096 		    ((uint64_t)tid) << 32;
1097 	} else {
1098 		sr->smb_pid = pid;
1099 		sr->smb_tid = (uint16_t)tid; /* XXX wide TIDs */
1100 	}
1101 
1102 	return (rc);
1103 }
1104 
1105 int
1106 smb2_encode_header(smb_request_t *sr, boolean_t overwrite)
1107 {
1108 	uint64_t ssnid = sr->smb_uid;
1109 	uint64_t pid_tid_aid; /* pid+tid, or async id */
1110 	uint32_t reply_hdr_flags;
1111 	int rc;
1112 
1113 	if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
1114 		pid_tid_aid = sr->smb2_async_id;
1115 	} else {
1116 		pid_tid_aid = sr->smb_pid |
1117 		    ((uint64_t)sr->smb_tid) << 32;
1118 	}
1119 	reply_hdr_flags = sr->smb2_hdr_flags | SMB2_FLAGS_SERVER_TO_REDIR;
1120 
1121 	if (overwrite) {
1122 		rc = smb_mbc_poke(&sr->reply,
1123 		    sr->smb2_reply_hdr,
1124 		    "Nwwlwwllqqq16c",
1125 		    SMB2_HDR_SIZE,		/* w */
1126 		    sr->smb2_credit_charge,	/* w */
1127 		    sr->smb2_status,		/* l */
1128 		    sr->smb2_cmd_code,		/* w */
1129 		    sr->smb2_credit_response,	/* w */
1130 		    reply_hdr_flags,		/* l */
1131 		    sr->smb2_next_reply,	/* l */
1132 		    sr->smb2_messageid,		/* q */
1133 		    pid_tid_aid,		/* q */
1134 		    ssnid,			/* q */
1135 		    sr->smb2_sig);		/* 16c */
1136 	} else {
1137 		rc = smb_mbc_encodef(&sr->reply,
1138 		    "Nwwlwwllqqq16c",
1139 		    SMB2_HDR_SIZE,		/* w */
1140 		    sr->smb2_credit_charge,	/* w */
1141 		    sr->smb2_status,		/* l */
1142 		    sr->smb2_cmd_code,		/* w */
1143 		    sr->smb2_credit_response,	/* w */
1144 		    reply_hdr_flags,		/* l */
1145 		    sr->smb2_next_reply,	/* l */
1146 		    sr->smb2_messageid,		/* q */
1147 		    pid_tid_aid,		/* q */
1148 		    ssnid,			/* q */
1149 		    sr->smb2_sig);		/* 16c */
1150 	}
1151 
1152 	return (rc);
1153 }
1154 
1155 void
1156 smb2_send_reply(smb_request_t *sr)
1157 {
1158 
1159 	if (smb_session_send(sr->session, 0, &sr->reply) == 0)
1160 		sr->reply.chain = 0;
1161 }
1162 
1163 /*
1164  * This wrapper function exists to help catch calls to smbsr_status()
1165  * (which is SMB1-specific) in common code.  See smbsr_status().
1166  * If the log message below is seen, put a dtrace probe on this
1167  * function with a stack() action to see who is calling the SMB1
1168  * "put error" from common code, and fix it.
1169  */
1170 void
1171 smbsr_status_smb2(smb_request_t *sr, DWORD status)
1172 {
1173 	const char *name;
1174 
1175 	if (sr->smb2_cmd_code < SMB2__NCMDS)
1176 		name = smb2_disp_table[sr->smb2_cmd_code].sdt_name;
1177 	else
1178 		name = "<unknown>";
1179 #ifdef	DEBUG
1180 	cmn_err(CE_NOTE, "smbsr_status called for %s", name);
1181 #endif
1182 
1183 	smb2sr_put_error_data(sr, status, NULL);
1184 }
1185 
1186 void
1187 smb2sr_put_errno(struct smb_request *sr, int errnum)
1188 {
1189 	uint32_t status = smb_errno2status(errnum);
1190 	smb2sr_put_error_data(sr, status, NULL);
1191 }
1192 
1193 void
1194 smb2sr_put_error(smb_request_t *sr, uint32_t status)
1195 {
1196 	smb2sr_put_error_data(sr, status, NULL);
1197 }
1198 
1199 /*
1200  * Build an SMB2 error response.  [MS-SMB2] 2.2.2
1201  */
1202 void
1203 smb2sr_put_error_data(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc)
1204 {
1205 	DWORD len;
1206 
1207 	/*
1208 	 * The common dispatch code writes this when it
1209 	 * updates the SMB2 header before sending.
1210 	 */
1211 	sr->smb2_status = status;
1212 
1213 	/* Rewind to the end of the SMB header. */
1214 	sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE;
1215 
1216 	/*
1217 	 * NB: Must provide at least one byte of error data,
1218 	 * per [MS-SMB2] 2.2.2
1219 	 */
1220 	if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) {
1221 		(void) smb_mbc_encodef(
1222 		    &sr->reply,
1223 		    "wwlC",
1224 		    9,	/* StructSize */	/* w */
1225 		    0,	/* reserved */		/* w */
1226 		    len,			/* l */
1227 		    mbc);			/* C */
1228 	} else {
1229 		(void) smb_mbc_encodef(
1230 		    &sr->reply,
1231 		    "wwl.",
1232 		    9,	/* StructSize */	/* w */
1233 		    0,	/* reserved */		/* w */
1234 		    0);				/* l. */
1235 	}
1236 }
1237 
1238 /*
1239  * smb2sr_lookup_fid
1240  *
1241  * Setup sr->fid_ofile, either inherited from a related command,
1242  * or obtained via FID lookup.  Similar inheritance logic as in
1243  * smb2sr_work.
1244  */
1245 uint32_t
1246 smb2sr_lookup_fid(smb_request_t *sr, smb2fid_t *fid)
1247 {
1248 	boolean_t related = sr->smb2_hdr_flags &
1249 	    SMB2_FLAGS_RELATED_OPERATIONS;
1250 
1251 	if (related) {
1252 		if (sr->fid_ofile == NULL)
1253 			return (NT_STATUS_INVALID_PARAMETER);
1254 		sr->smb_fid = sr->fid_ofile->f_fid;
1255 		return (0);
1256 	}
1257 
1258 	/*
1259 	 * If we could be sure this is called only once per cmd,
1260 	 * we could simply ASSERT(sr->fid_ofile == NULL) here.
1261 	 * However, there are cases where it can be called again
1262 	 * handling the same command, so let's tolerate that.
1263 	 */
1264 	if (sr->fid_ofile == NULL) {
1265 		sr->smb_fid = (uint16_t)fid->temporal;
1266 		sr->fid_ofile = smb_ofile_lookup_by_fid(sr, sr->smb_fid);
1267 	}
1268 	if (sr->fid_ofile == NULL)
1269 		return (NT_STATUS_FILE_CLOSED);
1270 
1271 	return (0);
1272 }
1273 
1274 /*
1275  * smb2_dispatch_stats_init
1276  *
1277  * Initializes dispatch statistics for SMB2.
1278  * See also smb_dispatch_stats_init(), which fills in
1279  * the lower part of the statistics array, from zero
1280  * through SMB_COM_NUM;
1281  */
1282 void
1283 smb2_dispatch_stats_init(smb_server_t *sv)
1284 {
1285 	smb_disp_stats_t *sds = sv->sv_disp_stats2;
1286 	smb_kstat_req_t *ksr;
1287 	int		i;
1288 
1289 	ksr = ((smbsrv_kstats_t *)sv->sv_ksp->ks_data)->ks_reqs2;
1290 
1291 	for (i = 0; i < SMB2__NCMDS; i++, ksr++) {
1292 		smb_latency_init(&sds[i].sdt_lat);
1293 		(void) strlcpy(ksr->kr_name, smb2_disp_table[i].sdt_name,
1294 		    sizeof (ksr->kr_name));
1295 	}
1296 }
1297 
1298 /*
1299  * smb2_dispatch_stats_fini
1300  *
1301  * Frees and destroyes the resources used for statistics.
1302  */
1303 void
1304 smb2_dispatch_stats_fini(smb_server_t *sv)
1305 {
1306 	smb_disp_stats_t *sds = sv->sv_disp_stats2;
1307 	int	i;
1308 
1309 	for (i = 0; i < SMB2__NCMDS; i++)
1310 		smb_latency_destroy(&sds[i].sdt_lat);
1311 }
1312 
1313 void
1314 smb2_dispatch_stats_update(smb_server_t *sv,
1315     smb_kstat_req_t *ksr, int first, int nreq)
1316 {
1317 	smb_disp_stats_t *sds = sv->sv_disp_stats2;
1318 	int	i;
1319 	int	last;
1320 
1321 	last = first + nreq - 1;
1322 
1323 	if ((first < SMB2__NCMDS) && (last < SMB2__NCMDS))  {
1324 		for (i = first; i <= last; i++, ksr++) {
1325 			ksr->kr_rxb = sds[i].sdt_rxb;
1326 			ksr->kr_txb = sds[i].sdt_txb;
1327 			mutex_enter(&sds[i].sdt_lat.ly_mutex);
1328 			ksr->kr_nreq = sds[i].sdt_lat.ly_a_nreq;
1329 			ksr->kr_sum = sds[i].sdt_lat.ly_a_sum;
1330 			ksr->kr_a_mean = sds[i].sdt_lat.ly_a_mean;
1331 			ksr->kr_a_stddev =
1332 			    sds[i].sdt_lat.ly_a_stddev;
1333 			ksr->kr_d_mean = sds[i].sdt_lat.ly_d_mean;
1334 			ksr->kr_d_stddev =
1335 			    sds[i].sdt_lat.ly_d_stddev;
1336 			sds[i].sdt_lat.ly_d_mean = 0;
1337 			sds[i].sdt_lat.ly_d_nreq = 0;
1338 			sds[i].sdt_lat.ly_d_stddev = 0;
1339 			sds[i].sdt_lat.ly_d_sum = 0;
1340 			mutex_exit(&sds[i].sdt_lat.ly_mutex);
1341 		}
1342 	}
1343 }
1344