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