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