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 2021 Tintri by DDN, Inc. All rights reserved. 14 * Copyright 2022 RackTop Systems, Inc. 15 */ 16 17 /* 18 * Dispatch function for SMB2_OPLOCK_BREAK 19 */ 20 21 #include <smbsrv/smb2_kproto.h> 22 #include <smbsrv/smb_oplock.h> 23 24 /* StructSize for the two "break" message formats. */ 25 #define SSZ_OPLOCK 24 26 #define SSZ_LEASE_ACK 36 27 #define SSZ_LEASE_BRK 44 28 29 #define NODE_FLAGS_DELETING (NODE_FLAGS_DELETE_ON_CLOSE |\ 30 NODE_FLAGS_DELETE_COMMITTED) 31 32 static const char lease_zero[UUID_LEN] = { 0 }; 33 34 static kmem_cache_t *smb_lease_cache = NULL; 35 36 void 37 smb2_lease_init() 38 { 39 if (smb_lease_cache != NULL) 40 return; 41 42 smb_lease_cache = kmem_cache_create("smb_lease_cache", 43 sizeof (smb_lease_t), 8, NULL, NULL, NULL, NULL, NULL, 0); 44 } 45 46 void 47 smb2_lease_fini() 48 { 49 if (smb_lease_cache != NULL) { 50 kmem_cache_destroy(smb_lease_cache); 51 smb_lease_cache = NULL; 52 } 53 } 54 55 static void 56 smb2_lease_hold(smb_lease_t *ls) 57 { 58 mutex_enter(&ls->ls_mutex); 59 ls->ls_refcnt++; 60 mutex_exit(&ls->ls_mutex); 61 } 62 63 static void 64 lease_destroy(smb_lease_t *ls) 65 { 66 smb_node_release(ls->ls_node); 67 mutex_destroy(&ls->ls_mutex); 68 kmem_cache_free(smb_lease_cache, ls); 69 } 70 71 void 72 smb2_lease_rele(smb_lease_t *ls) 73 { 74 smb_llist_t *bucket; 75 boolean_t destroy = B_FALSE; 76 77 mutex_enter(&ls->ls_mutex); 78 ls->ls_refcnt--; 79 if (ls->ls_refcnt != 0) { 80 mutex_exit(&ls->ls_mutex); 81 return; 82 } 83 mutex_exit(&ls->ls_mutex); 84 85 /* 86 * Get the list lock, then re-check the refcnt 87 * and if it's still zero, unlink & destroy. 88 */ 89 bucket = ls->ls_bucket; 90 smb_llist_enter(bucket, RW_WRITER); 91 92 mutex_enter(&ls->ls_mutex); 93 if (ls->ls_refcnt == 0) { 94 smb_llist_remove(bucket, ls); 95 destroy = B_TRUE; 96 } 97 mutex_exit(&ls->ls_mutex); 98 99 smb_llist_exit(bucket); 100 101 if (destroy) { 102 lease_destroy(ls); 103 } 104 } 105 106 /* 107 * Compute a hash from a uuid 108 * Based on mod_hash_bystr() 109 */ 110 static uint_t 111 smb_hash_uuid(const uint8_t *uuid) 112 { 113 char *k = (char *)uuid; 114 uint_t hash = 0; 115 uint_t g; 116 int i; 117 118 ASSERT(k); 119 for (i = 0; i < UUID_LEN; i++) { 120 hash = (hash << 4) + k[i]; 121 if ((g = (hash & 0xf0000000)) != 0) { 122 hash ^= (g >> 24); 123 hash ^= g; 124 } 125 } 126 return (hash); 127 } 128 129 /* 130 * Add or update a lease table entry for a new ofile. 131 * (in the per-session lease table) 132 * See [MS-SMB2] 3.3.5.9.8 133 * Handling the SMB2_CREATE_REQUEST_LEASE Create Context 134 */ 135 uint32_t 136 smb2_lease_create(smb_request_t *sr, uint8_t *clnt) 137 { 138 smb_arg_open_t *op = &sr->arg.open; 139 uint8_t *key = op->lease_key; 140 smb_ofile_t *of = sr->fid_ofile; 141 smb_hash_t *ht = sr->sr_server->sv_lease_ht; 142 smb_llist_t *bucket; 143 smb_lease_t *lease; 144 smb_lease_t *newlease; 145 size_t hashkey; 146 uint32_t status = NT_STATUS_INVALID_PARAMETER; 147 148 if (bcmp(key, lease_zero, UUID_LEN) == 0) 149 return (status); 150 151 /* 152 * Find or create, and add a ref for the new ofile. 153 */ 154 hashkey = smb_hash_uuid(key); 155 hashkey &= (ht->num_buckets - 1); 156 bucket = &ht->buckets[hashkey].b_list; 157 158 newlease = kmem_cache_alloc(smb_lease_cache, KM_SLEEP); 159 bzero(newlease, sizeof (smb_lease_t)); 160 mutex_init(&newlease->ls_mutex, NULL, MUTEX_DEFAULT, NULL); 161 newlease->ls_bucket = bucket; 162 newlease->ls_node = of->f_node; 163 smb_node_ref(newlease->ls_node); 164 newlease->ls_refcnt = 1; 165 newlease->ls_epoch = op->lease_epoch; 166 newlease->ls_version = op->lease_version; 167 bcopy(key, newlease->ls_key, UUID_LEN); 168 bcopy(clnt, newlease->ls_clnt, UUID_LEN); 169 170 smb_llist_enter(bucket, RW_WRITER); 171 for (lease = smb_llist_head(bucket); lease != NULL; 172 lease = smb_llist_next(bucket, lease)) { 173 /* 174 * Looking for this lease ID, on a node 175 * that's not being deleted. 176 */ 177 if (bcmp(lease->ls_key, key, UUID_LEN) == 0 && 178 bcmp(lease->ls_clnt, clnt, UUID_LEN) == 0 && 179 (lease->ls_node->flags & NODE_FLAGS_DELETING) == 0) 180 break; 181 } 182 if (lease != NULL) { 183 /* 184 * Found existing lease. Make sure it refers to 185 * the same node... 186 */ 187 if (lease->ls_node == of->f_node) { 188 smb2_lease_hold(lease); 189 } else { 190 /* Same lease ID, different node! */ 191 #ifdef DEBUG 192 cmn_err(CE_NOTE, "new lease on node %p (%s) " 193 "conflicts with existing node %p (%s)", 194 (void *) of->f_node, 195 of->f_node->od_name, 196 (void *) lease->ls_node, 197 lease->ls_node->od_name); 198 #endif 199 DTRACE_PROBE2(dup_lease, smb_request_t *, sr, 200 smb_lease_t *, lease); 201 lease = NULL; /* error */ 202 } 203 } else { 204 lease = newlease; 205 smb_llist_insert_head(bucket, lease); 206 newlease = NULL; /* don't free */ 207 } 208 smb_llist_exit(bucket); 209 210 if (newlease != NULL) { 211 lease_destroy(newlease); 212 } 213 214 if (lease != NULL) { 215 of->f_lease = lease; 216 status = NT_STATUS_SUCCESS; 217 } 218 219 return (status); 220 } 221 222 /* 223 * Find the lease for a given: client_uuid, lease_key 224 * Returns the lease with a new ref. 225 */ 226 static smb_lease_t * 227 lease_lookup(smb_request_t *sr, uint8_t *lease_key) 228 { 229 smb_server_t *sv = sr->sr_server; 230 uint8_t *clnt_uuid = sr->session->clnt_uuid; 231 smb_hash_t *ht = sv->sv_lease_ht; 232 smb_llist_t *bucket; 233 smb_lease_t *lease; 234 size_t hashkey; 235 236 hashkey = smb_hash_uuid(lease_key); 237 hashkey &= (ht->num_buckets - 1); 238 bucket = &ht->buckets[hashkey].b_list; 239 240 smb_llist_enter(bucket, RW_READER); 241 lease = smb_llist_head(bucket); 242 while (lease != NULL) { 243 if (bcmp(lease->ls_key, lease_key, UUID_LEN) == 0 && 244 bcmp(lease->ls_clnt, clnt_uuid, UUID_LEN) == 0) { 245 smb2_lease_hold(lease); 246 break; 247 } 248 lease = smb_llist_next(bucket, lease); 249 } 250 smb_llist_exit(bucket); 251 252 return (lease); 253 } 254 255 /* 256 * Find the oplock smb_ofile_t for the specified lease. 257 * If no such ofile, NT_STATUS_UNSUCCESSFUL. 258 * On success, ofile (held) in sr->fid_ofile. 259 */ 260 static uint32_t 261 lease_find_oplock(smb_request_t *sr, smb_lease_t *lease) 262 { 263 smb_node_t *node = lease->ls_node; 264 smb_ofile_t *o; 265 uint32_t status = NT_STATUS_UNSUCCESSFUL; 266 267 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock)); 268 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex)); 269 ASSERT(sr->fid_ofile == NULL); 270 271 FOREACH_NODE_OFILE(node, o) { 272 if (o->f_lease != lease) 273 continue; 274 if (o != lease->ls_oplock_ofile) 275 continue; 276 /* 277 * Found the ofile holding the oplock 278 * This hold released in smb_request_free 279 */ 280 if (smb_ofile_hold_olbrk(o)) { 281 sr->fid_ofile = o; 282 status = NT_STATUS_SUCCESS; 283 break; 284 } 285 } 286 287 return (status); 288 } 289 290 /* 291 * This is called by smb2_oplock_break_ack when the struct size 292 * indicates this is a lease break (SZ_LEASE). See: 293 * [MS-SMB2] 3.3.5.22.2 Processing a Lease Acknowledgment 294 * This is an "Ack" from the client. 295 */ 296 smb_sdrc_t 297 smb2_lease_break_ack(smb_request_t *sr) 298 { 299 smb_arg_olbrk_t *olbrk = &sr->arg.olbrk; 300 smb_lease_t *lease; 301 smb_node_t *node; 302 smb_ofile_t *ofile; 303 uint32_t LeaseState; 304 uint32_t status; 305 int rc = 0; 306 307 if (sr->session->dialect < SMB_VERS_2_1) 308 return (SDRC_ERROR); 309 310 /* 311 * Decode an SMB2 Lease Acknowldgement 312 * [MS-SMB2] 2.2.24.2 313 * Note: Struct size decoded by caller. 314 */ 315 rc = smb_mbc_decodef( 316 &sr->smb_data, "6.#cl8.", 317 /* reserved 6. */ 318 UUID_LEN, /* # */ 319 olbrk->LeaseKey, /* c */ 320 &olbrk->NewLevel); /* l */ 321 /* duration 8. */ 322 if (rc != 0) 323 return (SDRC_ERROR); 324 LeaseState = olbrk->NewLevel; 325 326 /* 327 * Find the lease via the given key. 328 */ 329 lease = lease_lookup(sr, olbrk->LeaseKey); 330 if (lease == NULL) { 331 /* 332 * It's unusual to skip the dtrace start/done 333 * probes like this, but trying to run them 334 * with no lease->node would be complex and 335 * would not show anything particularly useful. 336 * Do the start probe after we find the ofile. 337 */ 338 status = NT_STATUS_OBJECT_NAME_NOT_FOUND; 339 smb2sr_put_error(sr, status); 340 return (SDRC_SUCCESS); 341 } 342 // Note: lease ref; smb_lease_rele() below. 343 node = lease->ls_node; 344 345 /* 346 * Find the leased oplock. Hold locks so it can't move 347 * until we're done with ACK-break processing. 348 */ 349 smb_llist_enter(&node->n_ofile_list, RW_READER); 350 mutex_enter(&node->n_oplock.ol_mutex); 351 352 status = lease_find_oplock(sr, lease); 353 /* Normally have sr->fid_ofile now. */ 354 355 DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr); 356 357 if (status != 0) { 358 /* Leased oplock not found. Must have closed. */ 359 goto errout; 360 } 361 362 /* Success, so have sr->fid_ofile */ 363 ofile = sr->fid_ofile; 364 365 if (lease->ls_breaking == B_FALSE) { 366 /* 367 * This ACK is either unsolicited or too late, 368 * eg. we timed out the ACK and did it locally. 369 */ 370 status = NT_STATUS_UNSUCCESSFUL; 371 goto errout; 372 } 373 374 /* 375 * If the new LeaseState has any bits in excess of 376 * the lease state we sent in the break, error... 377 */ 378 if ((LeaseState & ~(lease->ls_breakto)) != 0) { 379 status = NT_STATUS_REQUEST_NOT_ACCEPTED; 380 goto errout; 381 } 382 383 /* 384 * Process the lease break ack. 385 * 386 * Clear breaking flags before we ack, 387 * because ack might set those. 388 * Signal both CVs, out of paranoia. 389 */ 390 ofile->f_oplock.og_breaking = B_FALSE; 391 cv_broadcast(&ofile->f_oplock.og_ack_cv); 392 lease->ls_breaking = B_FALSE; 393 cv_broadcast(&lease->ls_ack_cv); 394 395 LeaseState |= OPLOCK_LEVEL_GRANULAR; 396 status = smb_oplock_ack_break(sr, ofile, &LeaseState); 397 398 ofile->f_oplock.og_state = LeaseState; 399 lease->ls_state = LeaseState; 400 /* ls_epoch does not change here */ 401 402 if (ofile->dh_persist) 403 smb2_dh_update_oplock(sr, ofile); 404 405 errout: 406 sr->smb2_status = status; 407 DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr); 408 409 mutex_exit(&node->n_oplock.ol_mutex); 410 smb_llist_exit(&node->n_ofile_list); 411 412 smb2_lease_rele(lease); 413 414 if (status) { 415 smb2sr_put_error(sr, status); 416 return (SDRC_SUCCESS); 417 } 418 419 /* 420 * Encode an SMB2 Lease Ack. response 421 * [MS-SMB2] 2.2.25.2 422 */ 423 LeaseState &= OPLOCK_LEVEL_CACHE_MASK; 424 (void) smb_mbc_encodef( 425 &sr->reply, "w6.#cl8.", 426 SSZ_LEASE_ACK, /* w */ 427 /* reserved 6. */ 428 UUID_LEN, /* # */ 429 olbrk->LeaseKey, /* c */ 430 LeaseState); /* l */ 431 /* duration 8. */ 432 433 return (SDRC_SUCCESS); 434 435 } 436 437 /* 438 * Compose an SMB2 Lease Break Notification packet, including 439 * the SMB2 header and everything, in sr->reply. 440 * The caller will send it and free the request. 441 * 442 * [MS-SMB2] 2.2.23.2 Lease Break Notification 443 */ 444 static void 445 smb2_lease_break_notification(smb_request_t *sr, 446 uint32_t OldLevel, uint32_t NewLevel, 447 uint16_t Epoch, boolean_t AckReq) 448 { 449 smb_lease_t *ls = sr->fid_ofile->f_lease; 450 uint16_t Flags = 0; 451 452 /* 453 * Convert internal lease info to SMB2 454 */ 455 if (AckReq) 456 Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED; 457 if (ls->ls_version < 2) 458 Epoch = 0; 459 OldLevel &= OPLOCK_LEVEL_CACHE_MASK; 460 NewLevel &= OPLOCK_LEVEL_CACHE_MASK; 461 462 /* 463 * SMB2 Header 464 */ 465 sr->smb2_cmd_code = SMB2_OPLOCK_BREAK; 466 sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR; 467 sr->smb_tid = 0; 468 sr->smb_pid = 0; 469 sr->smb2_ssnid = 0; 470 sr->smb2_messageid = UINT64_MAX; 471 (void) smb2_encode_header(sr, B_FALSE); 472 473 /* 474 * SMB2 Oplock Break, variable part 475 * 476 * [MS-SMB2] says the current lease state preceeds the 477 * new lease state, but that looks like an error... 478 */ 479 (void) smb_mbc_encodef( 480 &sr->reply, "wwl#cll4.4.4.", 481 SSZ_LEASE_BRK, /* w */ 482 Epoch, /* w */ 483 Flags, /* l */ 484 SMB_LEASE_KEY_SZ, /* # */ 485 ls->ls_key, /* c */ 486 OldLevel, /* cur.st l */ 487 NewLevel); /* new.st l */ 488 /* reserved (4.4.4.) */ 489 } 490 491 /* 492 * Do our best to send a lease break message to the client. 493 * When we get to multi-channel, this is supposed to try 494 * every channel before giving up. For now, try every 495 * connected session with an ofile sharing this lease. 496 * 497 * If this ofile has a valid session, try that first. 498 * Otherwise look on the node list for other ofiles with 499 * the same lease and a connected session. 500 */ 501 static int 502 lease_send_any_cn(smb_request_t *sr) 503 { 504 smb_ofile_t *o; 505 smb_ofile_t *ofile = sr->fid_ofile; 506 smb_lease_t *lease = ofile->f_lease; 507 smb_node_t *node = ofile->f_node; 508 int rc = ENOTCONN; 509 510 /* 511 * If the passed oplock ofile has a session, 512 * this IF expression will be true. 513 */ 514 if (sr->session == ofile->f_session) { 515 rc = smb_session_send(sr->session, 0, &sr->reply); 516 if (rc == 0) 517 return (rc); 518 } 519 520 smb_llist_enter(&node->n_ofile_list, RW_READER); 521 FOREACH_NODE_OFILE(node, o) { 522 if (o->f_lease != lease) 523 continue; 524 if (smb_ofile_hold(o)) { 525 /* Has a session. */ 526 rc = smb_session_send(o->f_session, 0, &sr->reply); 527 smb_llist_post(&node->n_ofile_list, o, 528 smb_ofile_release_LL); 529 } 530 if (rc == 0) 531 break; 532 } 533 smb_llist_exit(&node->n_ofile_list); 534 535 return (rc); 536 } 537 538 /* 539 * See smb_llist_post on node->n_ofile_list below. 540 * Can't call smb_ofile_close with that list entered. 541 */ 542 static void 543 lease_ofile_close_rele(void *arg) 544 { 545 smb_ofile_t *of = (smb_ofile_t *)arg; 546 547 smb_ofile_close(of, 0); 548 smb_ofile_release(of); 549 } 550 551 /* 552 * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break 553 * If no connection, for each Open in Lease.LeaseOpens, 554 * the server MUST close the Open as specified in sec... 555 * for the following cases: 556 * - Open.IsDurable, Open.IsResilient, and 557 * Open.IsPersistent are all FALSE. 558 * - Open.IsDurable is TRUE and Lease.BreakToLeaseState 559 * does not contain SMB2_LEASE_HANDLE_CACHING and 560 */ 561 static void 562 lease_close_notconn(smb_request_t *sr, uint32_t NewLevel) 563 { 564 smb_ofile_t *o; 565 smb_ofile_t *ofile = sr->fid_ofile; 566 smb_lease_t *lease = ofile->f_lease; 567 smb_node_t *node = ofile->f_node; 568 569 smb_llist_enter(&node->n_ofile_list, RW_READER); 570 FOREACH_NODE_OFILE(node, o) { 571 if (o->f_lease != lease) 572 continue; 573 if (o->f_oplock_closing) 574 continue; 575 if (o->dh_persist) 576 continue; 577 if (o->dh_vers == SMB2_RESILIENT) 578 continue; 579 if (o->dh_vers == SMB2_NOT_DURABLE || 580 (NewLevel & OPLOCK_LEVEL_CACHE_HANDLE) == 0) { 581 if (smb_ofile_hold_olbrk(o)) { 582 smb_llist_post(&node->n_ofile_list, o, 583 lease_ofile_close_rele); 584 } 585 } 586 } 587 smb_llist_exit(&node->n_ofile_list); 588 } 589 590 /* 591 * Send a lease break over the wire, or if we can't, 592 * then process the lease break locally. 593 * 594 * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break 595 * 596 * This is mostly similar to smb2_oplock_send_break() 597 * See top comment there about the design. 598 * 599 * Differences beween a lease break and oplock break: 600 * 601 * Leases are an SMB-level mechanism whereby multiple open 602 * SMB file handles can share an oplock. All SMB handles 603 * on the lease enjoy the same caching rights. Down at the 604 * file-system level, just one oplock holds the cache rights 605 * for a lease, but (this is the tricky part) that oplock can 606 * MOVE among the SMB file handles sharing the lease. Such 607 * oplock moves can happen when a handle is closed (if that 608 * handle is the one with the oplock) or when a new open on 609 * the lease causes an upgrade of the caching rights. 610 * 611 * We have to deal here with lease movement because this call 612 * happens asynchronously after the smb_oplock_ind_break call, 613 * meaning that the oplock for the lease may have moved by the 614 * time this runs. In addition, the ofile holding the oplock 615 * might not be the best one to use to send a lease break. 616 * If the oplock is held by a handle that's "orphaned" and 617 * there are other handles on the lease with active sessions, 618 * we want to send the lease break on an active session. 619 * 620 * Also note: NewLevel (as provided by smb_oplock_ind_break etc.) 621 * does NOT include the GRANULAR flag. This level is expected to 622 * keep track of how each oplock was acquired (by lease or not) 623 * and put the GRANULAR flag back in when appropriate. 624 */ 625 void 626 smb2_lease_send_break(smb_request_t *sr) 627 { 628 smb_ofile_t *old_ofile; 629 smb_ofile_t *ofile = sr->fid_ofile; 630 smb_node_t *node = ofile->f_node; 631 smb_lease_t *lease = ofile->f_lease; 632 smb_arg_olbrk_t *olbrk = &sr->arg.olbrk; 633 boolean_t AckReq = olbrk->AckRequired; 634 uint32_t OldLevel = olbrk->OldLevel; 635 uint32_t NewLevel = olbrk->NewLevel; 636 uint32_t status; 637 int rc; 638 639 NewLevel |= OPLOCK_LEVEL_GRANULAR; 640 641 /* 642 * Build the break message in sr->reply. 643 * It's free'd in smb_request_free(). 644 * Always an SMB2 lease here. 645 */ 646 sr->reply.max_bytes = MLEN; 647 smb2_lease_break_notification(sr, 648 OldLevel, NewLevel, lease->ls_epoch, AckReq); 649 650 /* 651 * Try to send the break message to the client, 652 * on any connection with this lease. 653 */ 654 rc = lease_send_any_cn(sr); 655 if (rc != 0) { 656 /* 657 * We were unable to send the oplock break request, 658 * presumably because the connection is gone. 659 * Close uninteresting handles. 660 */ 661 lease_close_notconn(sr, NewLevel); 662 /* Note: some handles may remain on the lease. */ 663 if (!AckReq) 664 return; 665 /* Do local Ack below. */ 666 } else { 667 /* 668 * OK, we were able to send the break message. 669 * If no ack. required, we're done. 670 */ 671 if (!AckReq) 672 return; 673 674 /* 675 * We're expecting an ACK. Wait in this thread 676 * so we can log clients that don't respond. 677 * Note: this can also fail for other reasons 678 * such as client disconnect or server shutdown. 679 */ 680 status = smb_oplock_wait_ack(sr, NewLevel); 681 if (status == 0) 682 return; 683 684 DTRACE_PROBE2(wait__ack__failed, smb_request_t *, sr, 685 uint32_t, status); 686 687 /* 688 * Will do local ack below. Note, after timeout, 689 * do a break to none or "no caching" regardless 690 * of what the passed in cache level was. 691 * That means: clear all except GRANULAR. 692 */ 693 NewLevel = OPLOCK_LEVEL_GRANULAR; 694 } 695 696 /* 697 * Do the ack locally. 698 * 699 * Find the ofile with the leased oplock 700 * (may have moved before we took locks) 701 */ 702 smb_llist_enter(&node->n_ofile_list, RW_READER); 703 mutex_enter(&node->n_oplock.ol_mutex); 704 705 old_ofile = ofile; 706 sr->fid_ofile = NULL; 707 status = lease_find_oplock(sr, lease); 708 if (status != 0) { 709 /* put back old_ofile */ 710 sr->fid_ofile = old_ofile; 711 goto unlock_out; 712 } 713 smb_llist_post(&node->n_ofile_list, old_ofile, 714 smb_ofile_release_LL); 715 716 ofile = sr->fid_ofile; 717 718 /* 719 * Now continue like the non-lease code 720 */ 721 ofile->f_oplock.og_breaking = B_FALSE; 722 lease->ls_breaking = B_FALSE; 723 cv_broadcast(&lease->ls_ack_cv); 724 725 status = smb_oplock_ack_break(sr, ofile, &NewLevel); 726 727 ofile->f_oplock.og_state = NewLevel; 728 lease->ls_state = NewLevel; 729 /* ls_epoch does not change here */ 730 731 if (ofile->dh_persist) 732 smb2_dh_update_oplock(sr, ofile); 733 734 unlock_out: 735 mutex_exit(&node->n_oplock.ol_mutex); 736 smb_llist_exit(&node->n_ofile_list); 737 738 #ifdef DEBUG 739 if (status != 0) { 740 cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x", 741 sr->session->ip_addr_str, status); 742 } 743 #endif 744 } 745 746 /* 747 * Client has an open handle and requests a lease. 748 * Convert SMB2 lease request info in to internal form, 749 * call common oplock code, convert result to SMB2. 750 * 751 * If necessary, "go async" here (at the end). 752 */ 753 void 754 smb2_lease_acquire(smb_request_t *sr) 755 { 756 smb_arg_open_t *op = &sr->arg.open; 757 smb_ofile_t *ofile = sr->fid_ofile; 758 smb_lease_t *lease = ofile->f_lease; 759 smb_node_t *node = ofile->f_node; 760 uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED; 761 uint32_t have, want; /* lease flags */ 762 boolean_t NewGrant = B_FALSE; 763 764 /* Only disk trees get oplocks. */ 765 ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE); 766 767 /* 768 * Only plain files (for now). 769 * Later, test SMB2_CAP_DIRECTORY_LEASING 770 */ 771 if (!smb_node_is_file(ofile->f_node)) { 772 op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 773 return; 774 } 775 776 if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) { 777 op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 778 return; 779 } 780 781 /* 782 * SMB2: Convert to internal form. 783 * Caller should have setup the lease. 784 */ 785 ASSERT(op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE); 786 ASSERT(lease != NULL); 787 if (lease == NULL) { 788 op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 789 return; 790 } 791 op->op_oplock_state = OPLOCK_LEVEL_GRANULAR | 792 (op->lease_state & CACHE_RWH); 793 794 /* 795 * Tree options may force shared oplocks, 796 * in which case we reduce the request. 797 */ 798 if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) { 799 op->op_oplock_state &= ~WRITE_CACHING; 800 } 801 802 /* 803 * Using the "Locks Held" (LH) variant of smb_oplock_request 804 * below so things won't change underfoot. 805 */ 806 smb_llist_enter(&node->n_ofile_list, RW_READER); 807 mutex_enter(&node->n_oplock.ol_mutex); 808 809 /* 810 * MS-SMB2 3.3.5.9.8 and 3.3.5.9.11 Lease (V2) create contexts 811 * 812 * If the caching state requested in LeaseState of the (create ctx) 813 * is not a superset of Lease.LeaseState or if Lease.Breaking is TRUE, 814 * the server MUST NOT promote Lease.LeaseState. If the lease state 815 * requested is a superset of Lease.LeaseState and Lease.Breaking is 816 * FALSE, the server MUST request promotion of the lease state from 817 * the underlying object store to the new caching state. 818 */ 819 have = lease->ls_state & CACHE_RWH; 820 want = op->op_oplock_state & CACHE_RWH; 821 if ((have & ~want) != 0 || lease->ls_breaking) { 822 op->op_oplock_state = have | 823 OPLOCK_LEVEL_GRANULAR; 824 goto done; 825 } 826 827 /* 828 * Handle oplock requests in three parts: 829 * a: Requests with WRITE_CACHING 830 * b: Requests with HANDLE_CACHING 831 * c: Requests with READ_CACHING 832 * reducing the request before b and c. 833 * 834 * In each: first check if the lease grants the 835 * (possibly reduced) request, in which case we 836 * leave the lease unchanged and return what's 837 * granted by the lease. Otherwise, try to get 838 * the oplock, and if the succeeds, wait for any 839 * breaks, update the lease, and return. 840 */ 841 842 /* 843 * Try exclusive (request is RW or RWH) 844 */ 845 if ((op->op_oplock_state & WRITE_CACHING) != 0) { 846 /* Alread checked (want & ~have) */ 847 848 status = smb_oplock_request_LH(sr, ofile, 849 &op->op_oplock_state); 850 if (status == NT_STATUS_SUCCESS || 851 status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 852 NewGrant = B_TRUE; 853 goto done; 854 } 855 856 /* 857 * We did not get the exclusive oplock. 858 * 859 * There are odd rules about lease upgrade. 860 * If the existing lease grants R and the 861 * client fails to upgrade it to "RWH" 862 * (presumably due to handle conflicts) 863 * then just return the existing lease, 864 * even though upgrade to RH would work. 865 */ 866 if (have != 0) { 867 op->op_oplock_state = have | 868 OPLOCK_LEVEL_GRANULAR; 869 goto done; 870 } 871 872 /* 873 * Keep trying without write. 874 * Need to re-init op_oplock_state 875 */ 876 op->op_oplock_state = OPLOCK_LEVEL_GRANULAR | 877 (op->lease_state & CACHE_RH); 878 } 879 880 /* 881 * Try shared ("RH") 882 */ 883 if ((op->op_oplock_state & HANDLE_CACHING) != 0) { 884 want = op->op_oplock_state & CACHE_RWH; 885 if ((want & ~have) == 0) 886 goto done; 887 888 status = smb_oplock_request_LH(sr, ofile, 889 &op->op_oplock_state); 890 if (status == NT_STATUS_SUCCESS || 891 status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 892 NewGrant = B_TRUE; 893 goto done; 894 } 895 896 /* 897 * We did not get "RH", probably because 898 * ther were (old style) Level II oplocks. 899 * Continue, try for just read. 900 * Again, re-init op_oplock_state 901 */ 902 op->op_oplock_state = OPLOCK_LEVEL_GRANULAR | 903 (op->lease_state & CACHE_R); 904 } 905 906 /* 907 * Try shared ("R") 908 */ 909 if ((op->op_oplock_state & READ_CACHING) != 0) { 910 want = op->op_oplock_state & CACHE_RWH; 911 if ((want & ~have) == 0) 912 goto done; 913 914 status = smb_oplock_request_LH(sr, ofile, 915 &op->op_oplock_state); 916 if (status == NT_STATUS_SUCCESS || 917 status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 918 NewGrant = B_TRUE; 919 goto done; 920 } 921 922 /* 923 * We did not get "R". 924 * Fall into "none". 925 */ 926 } 927 928 /* 929 * None of the above were able to get an oplock. 930 * The lease has no caching rights, and we didn't 931 * add any in this request. Return it as-is. 932 */ 933 op->op_oplock_state = OPLOCK_LEVEL_GRANULAR; 934 935 done: 936 /* 937 * Only success cases get here 938 */ 939 940 /* 941 * Keep track of what we got (ofile->f_oplock.og_state etc) 942 * so we'll know what we had when sending a break later. 943 * Also keep a copy of some things in the lease. 944 * 945 * Not using og_dialect here, as ofile->f_lease tells us 946 * this has to be using granular oplocks. 947 */ 948 if (NewGrant) { 949 ofile->f_oplock.og_state = op->op_oplock_state; 950 ofile->f_oplock.og_breakto = op->op_oplock_state; 951 ofile->f_oplock.og_breaking = B_FALSE; 952 953 lease->ls_oplock_ofile = ofile; 954 lease->ls_state = ofile->f_oplock.og_state; 955 lease->ls_breakto = ofile->f_oplock.og_breakto; 956 lease->ls_breaking = B_FALSE; 957 lease->ls_epoch++; 958 959 if (ofile->dh_persist) { 960 smb2_dh_update_oplock(sr, ofile); 961 } 962 } 963 964 /* 965 * Convert internal oplock state to SMB2 966 */ 967 op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE; 968 op->lease_state = lease->ls_state & CACHE_RWH; 969 op->lease_flags = (lease->ls_breaking != 0) ? 970 SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0; 971 op->lease_epoch = lease->ls_epoch; 972 op->lease_version = lease->ls_version; 973 974 /* 975 * End of lock-held region 976 */ 977 mutex_exit(&node->n_oplock.ol_mutex); 978 smb_llist_exit(&node->n_ofile_list); 979 980 /* 981 * After a new oplock grant, the status return 982 * may indicate we need to wait for breaks. 983 */ 984 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 985 (void) smb2sr_go_async(sr); 986 (void) smb_oplock_wait_break(sr, ofile->f_node, 0); 987 } 988 } 989 990 /* 991 * This ofile has a lease and is about to close. 992 * Called by smb_ofile_close when there's a lease. 993 * 994 * Note that a client may close an ofile in response to an 995 * oplock break or lease break intead of doing an Ack break, 996 * so this must wake anything that might be waiting on an ack 997 * when the last close of a lease happens. 998 * 999 * With leases, just one ofile on a lease owns the oplock. 1000 * If an ofile with a lease is closed and it's the one that 1001 * owns the oplock, try to move the oplock to another ofile 1002 * on the same lease. 1003 * 1004 * Would prefer that we could just use smb_ofile_hold_olbrk 1005 * to select a suitable destination for the move, but this 1006 * is called while holding the owning tree ofile list etc 1007 * which can cause deadlock as described in illumos 13850 1008 * when smb_ofile_hold_olbrk has to wait. XXX todo 1009 */ 1010 void 1011 smb2_lease_ofile_close(smb_ofile_t *ofile) 1012 { 1013 smb_node_t *node = ofile->f_node; 1014 smb_lease_t *lease = ofile->f_lease; 1015 smb_ofile_t *o; 1016 1017 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock)); 1018 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex)); 1019 1020 #ifdef DEBUG 1021 FOREACH_NODE_OFILE(node, o) { 1022 DTRACE_PROBE1(each_ofile, smb_ofile_t *, o); 1023 } 1024 #endif 1025 1026 /* 1027 * If this ofile was not the oplock owner for this lease, 1028 * we can leave things as they are. 1029 */ 1030 if (lease->ls_oplock_ofile != ofile) 1031 return; 1032 1033 /* 1034 * Find another ofile to which we can move the oplock. 1035 * First try for one that's open. Usually find one. 1036 */ 1037 FOREACH_NODE_OFILE(node, o) { 1038 if (o == ofile) 1039 continue; 1040 if (o->f_lease != lease) 1041 continue; 1042 if (o->f_oplock_closing) 1043 continue; 1044 1045 mutex_enter(&o->f_mutex); 1046 if (o->f_state == SMB_OFILE_STATE_OPEN) { 1047 smb_oplock_move(node, ofile, o); 1048 lease->ls_oplock_ofile = o; 1049 mutex_exit(&o->f_mutex); 1050 return; 1051 } 1052 mutex_exit(&o->f_mutex); 1053 } 1054 1055 /* 1056 * Now try for one that's orphaned etc. 1057 */ 1058 FOREACH_NODE_OFILE(node, o) { 1059 if (o == ofile) 1060 continue; 1061 if (o->f_lease != lease) 1062 continue; 1063 if (o->f_oplock_closing) 1064 continue; 1065 1066 /* 1067 * Allow most states as seen in smb_ofile_hold_olbrk 1068 * without waiting for "_reconnect" or "_saving". 1069 * Skip "_expired" because that's about to close. 1070 * This is OK because just swapping the oplock state 1071 * between two ofiles does not interfere with the 1072 * dh_save or reconnect code paths. 1073 */ 1074 mutex_enter(&o->f_mutex); 1075 switch (o->f_state) { 1076 case SMB_OFILE_STATE_OPEN: 1077 case SMB_OFILE_STATE_SAVE_DH: 1078 case SMB_OFILE_STATE_SAVING: 1079 case SMB_OFILE_STATE_ORPHANED: 1080 case SMB_OFILE_STATE_RECONNECT: 1081 smb_oplock_move(node, ofile, o); 1082 lease->ls_oplock_ofile = o; 1083 mutex_exit(&o->f_mutex); 1084 return; 1085 } 1086 mutex_exit(&o->f_mutex); 1087 } 1088 1089 /* 1090 * Normal for last close on a lease. 1091 * Wakeup ACK waiters too. 1092 */ 1093 lease->ls_state = 0; 1094 lease->ls_breakto = 0; 1095 lease->ls_breaking = B_FALSE; 1096 cv_broadcast(&lease->ls_ack_cv); 1097 1098 lease->ls_oplock_ofile = NULL; 1099 } 1100