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 2020 Nexenta by DDN, Inc. All rights reserved. 14 * Copyright 2021 RackTop Systems, Inc. 15 */ 16 17 /* 18 * (SMB1/SMB2) common (FS-level) Oplock support. 19 * 20 * This is the file-system (FS) level oplock code. This level 21 * knows about the rules by which various kinds of oplocks may 22 * coexist and how they interact. Note that this code should 23 * have NO knowledge of specific SMB protocol details. Those 24 * details are handled in smb_srv_oplock.c and related. 25 * 26 * This file is intentionally written to very closely follow the 27 * [MS-FSA] specification sections about oplocks. Almost every 28 * section of code is preceeded by a block of text from that 29 * specification describing the logic. Where the implementation 30 * differs from what the spec. describes, there are notes like: 31 * Implementation specific: ... 32 */ 33 34 #include <smbsrv/smb_kproto.h> 35 #include <smbsrv/smb_oplock.h> 36 37 /* 38 * Several short-hand defines and enums used in this file. 39 */ 40 41 #define NODE_FLAGS_DELETING (NODE_FLAGS_DELETE_ON_CLOSE |\ 42 NODE_FLAGS_DELETE_COMMITTED) 43 44 static uint32_t 45 smb_oplock_req_excl( 46 smb_ofile_t *ofile, /* in: the "Open" */ 47 uint32_t *rop); /* in: "RequestedOplock", out:NewOplockLevel */ 48 49 static uint32_t 50 smb_oplock_req_shared( 51 smb_ofile_t *ofile, /* the "Open" */ 52 uint32_t *rop, /* in: "RequestedOplock", out:NewOplockLevel */ 53 boolean_t GrantingInAck); 54 55 static uint32_t smb_oplock_break_cmn(smb_node_t *node, 56 smb_ofile_t *ofile, uint32_t BreakCacheLevel); 57 58 59 /* 60 * [MS-FSA] 2.1.4.12.2 Algorithm to Compare Oplock Keys 61 * 62 * The inputs for this algorithm are: 63 * 64 * OperationOpen: The Open used in the request that can 65 * cause an oplock to break. 66 * OplockOpen: The Open originally used to request the oplock, 67 * as specified in section 2.1.5.17. 68 * Flags: If unspecified it is considered to contain 0. 69 * Valid nonzero values are: 70 * PARENT_OBJECT 71 * 72 * This algorithm returns TRUE if the appropriate oplock key field of 73 * OperationOpen equals OplockOpen.TargetOplockKey, and FALSE otherwise. 74 * 75 * Note: Unlike many comparison functions, ARG ORDER MATTERS. 76 */ 77 78 static boolean_t 79 CompareOplockKeys(smb_ofile_t *OperOpen, smb_ofile_t *OplockOpen, int flags) 80 { 81 static const uint8_t key0[SMB_LEASE_KEY_SZ] = { 0 }; 82 83 /* 84 * When we're called via FEM, (smb_oplock_break_...) 85 * the OperOpen arg is NULL because I/O outside of SMB 86 * doesn't have an "ofile". That's "not a match". 87 */ 88 if (OperOpen == NULL) 89 return (B_FALSE); 90 ASSERT(OplockOpen != NULL); 91 92 /* 93 * If OperationOpen equals OplockOpen: 94 * Return TRUE. 95 */ 96 if (OperOpen == OplockOpen) 97 return (B_TRUE); 98 99 /* 100 * If both OperationOpen.TargetOplockKey and 101 * OperationOpen.ParentOplockKey are empty 102 * or both OplockOpen.TargetOplockKey and 103 * OplockOpen.ParentOplockKey are empty: 104 * Return FALSE. 105 */ 106 if (bcmp(OperOpen->TargetOplockKey, key0, sizeof (key0)) == 0 && 107 bcmp(OperOpen->ParentOplockKey, key0, sizeof (key0)) == 0) 108 return (B_FALSE); 109 if (bcmp(OplockOpen->TargetOplockKey, key0, sizeof (key0)) == 0 && 110 bcmp(OplockOpen->ParentOplockKey, key0, sizeof (key0)) == 0) 111 return (B_FALSE); 112 113 /* 114 * If OplockOpen.TargetOplockKey is empty or... 115 */ 116 if (bcmp(OplockOpen->TargetOplockKey, key0, sizeof (key0)) == 0) 117 return (B_FALSE); 118 119 /* 120 * If Flags contains PARENT_OBJECT: 121 */ 122 if ((flags & PARENT_OBJECT) != 0) { 123 /* 124 * If OperationOpen.ParentOplockKey is empty: 125 * Return FALSE. 126 */ 127 if (bcmp(OperOpen->ParentOplockKey, key0, sizeof (key0)) == 0) 128 return (B_FALSE); 129 130 /* 131 * If OperationOpen.ParentOplockKey equals 132 * OplockOpen.TargetOplockKey: 133 * return TRUE, else FALSE 134 */ 135 if (bcmp(OperOpen->ParentOplockKey, 136 OplockOpen->TargetOplockKey, 137 SMB_LEASE_KEY_SZ) == 0) { 138 return (B_TRUE); 139 } 140 } else { 141 /* 142 * ... from above: 143 * (Flags does not contain PARENT_OBJECT and 144 * OperationOpen.TargetOplockKey is empty): 145 * Return FALSE. 146 */ 147 if (bcmp(OperOpen->TargetOplockKey, key0, sizeof (key0)) == 0) 148 return (B_FALSE); 149 150 /* 151 * If OperationOpen.TargetOplockKey equals 152 * OplockOpen.TargetOplockKey: 153 * Return TRUE, else FALSE 154 */ 155 if (bcmp(OperOpen->TargetOplockKey, 156 OplockOpen->TargetOplockKey, 157 SMB_LEASE_KEY_SZ) == 0) { 158 return (B_TRUE); 159 } 160 } 161 162 return (B_FALSE); 163 } 164 165 /* 166 * 2.1.4.13 Algorithm to Recompute the State of a Shared Oplock 167 * 168 * The inputs for this algorithm are: 169 * ThisOplock: The Oplock on whose state is being recomputed. 170 */ 171 static void 172 RecomputeOplockState(smb_node_t *node) 173 { 174 smb_oplock_t *ol = &node->n_oplock; 175 176 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock)); 177 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex)); 178 179 /* 180 * If ThisOplock.IIOplocks, ThisOplock.ROplocks, ThisOplock.RHOplocks, 181 * and ThisOplock.RHBreakQueue are all empty: 182 * Set ThisOplock.State to NO_OPLOCK. 183 */ 184 if (ol->cnt_II == 0 && ol->cnt_R == 0 && 185 ol->cnt_RH == 0 && ol->cnt_RHBQ == 0) { 186 ol->ol_state = NO_OPLOCK; 187 return; 188 } 189 190 /* 191 * Else If ThisOplock.ROplocks is not empty and either 192 * ThisOplock.RHOplocks or ThisOplock.RHBreakQueue are not empty: 193 * Set ThisOplock.State to 194 * (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH). 195 */ 196 else if (ol->cnt_R != 0 && (ol->cnt_RH != 0 || ol->cnt_RHBQ != 0)) { 197 ol->ol_state = (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH); 198 } 199 200 /* 201 * Else If ThisOplock.ROplocks is empty and 202 * ThisOplock.RHOplocks is not empty: 203 * Set ThisOplock.State to (READ_CACHING|HANDLE_CACHING). 204 */ 205 else if (ol->cnt_R == 0 && ol->cnt_RH != 0) { 206 ol->ol_state = (READ_CACHING|HANDLE_CACHING); 207 } 208 209 /* 210 * Else If ThisOplock.ROplocks is not empty and 211 * ThisOplock.IIOplocks is not empty: 212 * Set ThisOplock.State to (READ_CACHING|LEVEL_TWO_OPLOCK). 213 */ 214 else if (ol->cnt_R != 0 && ol->cnt_II != 0) { 215 ol->ol_state = (READ_CACHING|LEVEL_TWO_OPLOCK); 216 } 217 218 /* 219 * Else If ThisOplock.ROplocks is not empty and 220 * ThisOplock.IIOplocks is empty: 221 * Set ThisOplock.State to READ_CACHING. 222 */ 223 else if (ol->cnt_R != 0 && ol->cnt_II == 0) { 224 ol->ol_state = READ_CACHING; 225 } 226 227 /* 228 * Else If ThisOplock.ROplocks is empty and 229 * ThisOplock.IIOplocks is not empty: 230 * Set ThisOplock.State to LEVEL_TWO_OPLOCK. 231 */ 232 else if (ol->cnt_R == 0 && ol->cnt_II != 0) { 233 ol->ol_state = LEVEL_TWO_OPLOCK; 234 } 235 236 else { 237 smb_ofile_t *o; 238 int cntBrkToRead; 239 240 /* 241 * ThisOplock.RHBreakQueue MUST be non-empty by this point. 242 */ 243 ASSERT(ol->cnt_RHBQ != 0); 244 245 /* 246 * How many on RHBQ have BreakingToRead set? 247 */ 248 cntBrkToRead = 0; 249 FOREACH_NODE_OFILE(node, o) { 250 if (o->f_oplock.onlist_RHBQ == 0) 251 continue; 252 if (o->f_oplock.BreakingToRead) 253 cntBrkToRead++; 254 } 255 256 /* 257 * If RHOpContext.BreakingToRead is TRUE for 258 * every RHOpContext on ThisOplock.RHBreakQueue: 259 */ 260 if (cntBrkToRead == ol->cnt_RHBQ) { 261 /* 262 * Set ThisOplock.State to 263 * (READ_CACHING|HANDLE_CACHING|BREAK_TO_READ_CACHING). 264 */ 265 ol->ol_state = (READ_CACHING|HANDLE_CACHING| 266 BREAK_TO_READ_CACHING); 267 } 268 269 /* 270 * Else If RHOpContext.BreakingToRead is FALSE for 271 * every RHOpContext on ThisOplock.RHBreakQueue: 272 */ 273 else if (cntBrkToRead == 0) { 274 /* 275 * Set ThisOplock.State to 276 * (READ_CACHING|HANDLE_CACHING|BREAK_TO_NO_CACHING). 277 */ 278 ol->ol_state = (READ_CACHING|HANDLE_CACHING| 279 BREAK_TO_NO_CACHING); 280 } else { 281 /* 282 * Set ThisOplock.State to 283 * (READ_CACHING|HANDLE_CACHING). 284 */ 285 ol->ol_state = (READ_CACHING|HANDLE_CACHING); 286 } 287 } 288 } 289 290 /* 291 * [MS-FSA] 2.1.5.17 Server Requests an Oplock 292 * 293 * The server (caller) provides: 294 * Open - The Open on which the oplock is being requested. (ofile) 295 * Type - The type of oplock being requested. Valid values are as follows: 296 * LEVEL_TWO (Corresponds to SMB2_OPLOCK_LEVEL_II) 297 * LEVEL_ONE (Corresponds to SMB2_OPLOCK_LEVEL_EXCLUSIVE) 298 * LEVEL_BATCH (Corresponds to SMB2_OPLOCK_LEVEL_BATCH) 299 * LEVEL_GRANULAR (Corresponds to SMB2_OPLOCK_LEVEL_LEASE) 300 * RequestedOplockLevel - A combination of zero or more of the 301 * following flags (ignored if Type != LEVEL_GRANULAR) 302 * READ_CACHING 303 * HANDLE_CACHING 304 * WRITE_CACHING 305 * 306 * (Type + RequestedOplockLevel come in *statep) 307 * 308 * Returns: 309 * *statep = NewOplockLevel (possibly less than requested) 310 * containing: LEVEL_NONE, LEVEL_TWO + cache_flags 311 * NTSTATUS 312 */ 313 314 uint32_t 315 smb_oplock_request(smb_request_t *sr, smb_ofile_t *ofile, uint32_t *statep) 316 { 317 smb_node_t *node = ofile->f_node; 318 uint32_t type = *statep & OPLOCK_LEVEL_TYPE_MASK; 319 uint32_t level = *statep & OPLOCK_LEVEL_CACHE_MASK; 320 uint32_t status; 321 322 *statep = LEVEL_NONE; 323 324 /* 325 * If Open.Stream.StreamType is DirectoryStream: 326 * The operation MUST be failed with STATUS_INVALID_PARAMETER 327 * under either of the following conditions: 328 * * Type is not LEVEL_GRANULAR. 329 * * Type is LEVEL_GRANULAR but RequestedOplockLevel is 330 * neither READ_CACHING nor (READ_CACHING|HANDLE_CACHING). 331 */ 332 if (!smb_node_is_file(node)) { 333 /* ofile is a directory. */ 334 if (type != LEVEL_GRANULAR) 335 return (NT_STATUS_INVALID_PARAMETER); 336 if (level != READ_CACHING && 337 level != (READ_CACHING|HANDLE_CACHING)) 338 return (NT_STATUS_INVALID_PARAMETER); 339 /* 340 * We're not supporting directory leases yet. 341 * Todo. 342 */ 343 return (NT_STATUS_OPLOCK_NOT_GRANTED); 344 } 345 346 smb_llist_enter(&node->n_ofile_list, RW_READER); 347 mutex_enter(&node->n_oplock.ol_mutex); 348 349 /* 350 * If Type is LEVEL_ONE or LEVEL_BATCH: 351 * The operation MUST be failed with STATUS_OPLOCK_NOT_GRANTED 352 * under either of the following conditions: 353 * Open.File.OpenList contains more than one Open 354 * whose Stream is the same as Open.Stream. 355 * Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or 356 * FILE_SYNCHRONOUS_IO_NONALERT. 357 * Request an exclusive oplock according to the algorithm in 358 * section 2.1.5.17.1, setting the algorithm's params as follows: 359 * Pass in the current Open. 360 * RequestedOplock = Type. 361 * The operation MUST at this point return any status code 362 * returned by the exclusive oplock request algorithm. 363 */ 364 if (type == LEVEL_ONE || type == LEVEL_BATCH) { 365 if (node->n_open_count > 1) { 366 status = NT_STATUS_OPLOCK_NOT_GRANTED; 367 goto out; 368 } 369 /* XXX: Should be a flag on the ofile. */ 370 if (node->flags & NODE_FLAGS_WRITE_THROUGH) { 371 status = NT_STATUS_OPLOCK_NOT_GRANTED; 372 goto out; 373 } 374 *statep = type; 375 status = smb_oplock_req_excl(ofile, statep); 376 goto out; 377 } 378 379 /* 380 * Else If Type is LEVEL_TWO: 381 * The operation MUST be failed with STATUS_OPLOCK_NOT_GRANTED under 382 * either of the following conditions: 383 * Open.Stream.ByteRangeLockList is not empty. 384 * Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or 385 * FILE_SYNCHRONOUS_IO_NONALERT. 386 * Request a shared oplock according to the algorithm in 387 * section 2.1.5.17.2, setting the algorithm's parameters as follows: 388 * Pass in the current Open. 389 * RequestedOplock = Type. 390 * GrantingInAck = FALSE. 391 * The operation MUST at this point return any status code 392 * returned by the shared oplock request algorithm. 393 */ 394 if (type == LEVEL_TWO) { 395 if (smb_lock_range_access(sr, node, 0, ~0, B_FALSE) != 0) { 396 status = NT_STATUS_OPLOCK_NOT_GRANTED; 397 goto out; 398 } 399 /* XXX: Should be a flag on the ofile. */ 400 if (node->flags & NODE_FLAGS_WRITE_THROUGH) { 401 status = NT_STATUS_OPLOCK_NOT_GRANTED; 402 goto out; 403 } 404 *statep = type; 405 status = smb_oplock_req_shared(ofile, statep, B_FALSE); 406 goto out; 407 } 408 409 /* 410 * Else If Type is LEVEL_GRANULAR: 411 * Sub-cases on RequestedOplockLevel (our "level") 412 * 413 * This is the last Type, so error on !granular and then 414 * deal with the cache levels using one less indent. 415 */ 416 if (type != LEVEL_GRANULAR) { 417 status = NT_STATUS_INVALID_PARAMETER; 418 goto out; 419 } 420 421 switch (level) { 422 423 /* 424 * If RequestedOplockLevel is READ_CACHING or 425 * (READ_CACHING|HANDLE_CACHING): 426 * The operation MUST be failed with STATUS_OPLOCK_NOT_GRANTED 427 * under either of the following conditions: 428 * Open.Stream.ByteRangeLockList is not empty. 429 * Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or 430 * FILE_SYNCHRONOUS_IO_NONALERT. 431 * Request a shared oplock according to the algorithm in 432 * section 2.1.5.17.2, setting the parameters as follows: 433 * Pass in the current Open. 434 * RequestedOplock = RequestedOplockLevel. 435 * GrantingInAck = FALSE. 436 * 437 * The operation MUST at this point return any status code 438 * returned by the shared oplock request algorithm. 439 */ 440 case READ_CACHING: 441 case (READ_CACHING|HANDLE_CACHING): 442 if (smb_lock_range_access(sr, node, 0, ~0, B_FALSE) != 0) { 443 status = NT_STATUS_OPLOCK_NOT_GRANTED; 444 goto out; 445 } 446 /* XXX: Should be a flag on the ofile. */ 447 if (node->flags & NODE_FLAGS_WRITE_THROUGH) { 448 status = NT_STATUS_OPLOCK_NOT_GRANTED; 449 goto out; 450 } 451 *statep = level; 452 status = smb_oplock_req_shared(ofile, statep, B_FALSE); 453 break; 454 455 /* 456 * Else If RequestedOplockLevel is 457 * (READ_CACHING|WRITE_CACHING) or 458 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING): 459 * If Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or 460 * FILE_SYNCHRONOUS_IO_NONALERT, the operation MUST be failed 461 * with STATUS_OPLOCK_NOT_GRANTED. 462 * Request an exclusive oplock according to the algorithm in 463 * section 2.1.5.17.1, setting the parameters as follows: 464 * Pass in the current Open. 465 * RequestedOplock = RequestedOplockLevel. 466 * The operation MUST at this point return any status code 467 * returned by the exclusive oplock request algorithm. 468 */ 469 case (READ_CACHING | WRITE_CACHING): 470 case (READ_CACHING | WRITE_CACHING | HANDLE_CACHING): 471 /* XXX: Should be a flag on the ofile. */ 472 if (node->flags & NODE_FLAGS_WRITE_THROUGH) { 473 status = NT_STATUS_OPLOCK_NOT_GRANTED; 474 goto out; 475 } 476 *statep = level; 477 status = smb_oplock_req_excl(ofile, statep); 478 break; 479 480 /* 481 * Else if RequestedOplockLevel is 0 (that is, no flags): 482 * The operation MUST return STATUS_SUCCESS at this point. 483 */ 484 case 0: 485 *statep = 0; 486 status = NT_STATUS_SUCCESS; 487 break; 488 489 /* 490 * Else 491 * The operation MUST be failed with STATUS_INVALID_PARAMETER. 492 */ 493 default: 494 status = NT_STATUS_INVALID_PARAMETER; 495 break; 496 } 497 498 /* Give caller back the "Granular" bit. */ 499 if (status == NT_STATUS_SUCCESS) { 500 *statep |= LEVEL_GRANULAR; 501 502 /* 503 * The oplock lease may have moved to this ofile. Update. 504 * Minor violation of layering here (leases vs oplocks) 505 * but we want this update coverd by the oplock mutex. 506 */ 507 #ifndef TESTJIG 508 if (ofile->f_lease != NULL) 509 ofile->f_lease->ls_oplock_ofile = ofile; 510 #endif 511 } 512 513 out: 514 mutex_exit(&node->n_oplock.ol_mutex); 515 smb_llist_exit(&node->n_ofile_list); 516 517 return (status); 518 } 519 520 /* 521 * 2.1.5.17.1 Algorithm to Request an Exclusive Oplock 522 * 523 * The inputs for requesting an exclusive oplock are: 524 * Open: The Open on which the oplock is being requested. 525 * RequestedOplock: The oplock type being requested. One of: 526 * LEVEL_ONE, LEVEL_BATCH, CACHE_RW, CACHE_RWH 527 * 528 * On completion, the object store MUST return: 529 * Status: An NTSTATUS code that specifies the result. 530 * NewOplockLevel: The type of oplock that the requested oplock has been 531 * broken (reduced) to. If a failure status is returned in Status, 532 * the value of this field is undefined. Valid values are as follows: 533 * LEVEL_NONE (that is, no oplock) 534 * LEVEL_TWO 535 * A combination of one or more of the following flags: 536 * READ_CACHING 537 * HANDLE_CACHING 538 * WRITE_CACHING 539 * AcknowledgeRequired: A Boolean value: TRUE if the server MUST 540 * acknowledge the oplock break; FALSE if not, as specified in 541 * section 2.1.5.18. If a failure status is returned in Status, 542 * the value of this field is undefined. 543 * 544 * Note: Stores NewOplockLevel in *rop 545 */ 546 static uint32_t 547 smb_oplock_req_excl( 548 smb_ofile_t *ofile, /* in: the "Open" */ 549 uint32_t *rop) /* in: "RequestedOplock", out:NewOplockLevel */ 550 { 551 smb_node_t *node = ofile->f_node; 552 smb_ofile_t *o; 553 boolean_t GrantExcl = B_FALSE; 554 uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED; 555 556 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock)); 557 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex)); 558 559 /* 560 * Don't allow grants on closing ofiles. 561 */ 562 if (ofile->f_oplock_closing) 563 return (status); 564 565 /* 566 * If Open.Stream.Oplock is empty: 567 * Build a new Oplock object with fields initialized as follows: 568 * Oplock.State set to NO_OPLOCK. 569 * All other fields set to 0/empty. 570 * Store the new Oplock object in Open.Stream.Oplock. 571 * EndIf 572 * 573 * Implementation specific: 574 * Open.Stream.Oplock maps to: node->n_oplock 575 */ 576 if (node->n_oplock.ol_state == 0) { 577 node->n_oplock.ol_state = NO_OPLOCK; 578 } 579 580 /* 581 * If Open.Stream.Oplock.State contains 582 * LEVEL_TWO_OPLOCK or NO_OPLOCK: ... 583 * 584 * Per ms, this is the "If" matching the unbalalanced 585 * "Else If" below (for which we requested clarification). 586 */ 587 if ((node->n_oplock.ol_state & (LEVEL_TWO | NO_OPLOCK)) != 0) { 588 589 /* 590 * If Open.Stream.Oplock.State contains LEVEL_TWO_OPLOCK and 591 * RequestedOplock contains one or more of READ_CACHING, 592 * HANDLE_CACHING, or WRITE_CACHING, the operation MUST be 593 * failed with Status set to STATUS_OPLOCK_NOT_GRANTED. 594 */ 595 if ((node->n_oplock.ol_state & LEVEL_TWO) != 0 && 596 (*rop & CACHE_RWH) != 0) { 597 status = NT_STATUS_OPLOCK_NOT_GRANTED; 598 goto out; 599 } 600 601 /* 602 * [ from dochelp@ms ] 603 * 604 * By this point if there is a level II oplock present, 605 * the caller can only be requesting an old-style oplock 606 * because we rejected enhanced oplock requests above. 607 * If the caller is requesting an old-style oplock our 608 * caller already verfied that there is only one handle 609 * open to this stream, and we've already verified that 610 * this request is for a legacy oplock, meaning that there 611 * can be at most one level II oplock (and no R oplocks), 612 * and the level II oplock belongs to this handle. Clear 613 * the level II oplock and grant the exclusive oplock. 614 */ 615 616 /* 617 * If Open.Stream.Oplock.State is equal to LEVEL_TWO_OPLOCK: 618 * Remove the first Open ThisOpen from 619 * Open.Stream.Oplock.IIOplocks (there is supposed to be 620 * exactly one present), and notify the server of an 621 * oplock break according to the algorithm in section 622 * 2.1.5.17.3, setting the algorithm's parameters as follows: 623 * BreakingOplockOpen = ThisOpen. 624 * NewOplockLevel = LEVEL_NONE. 625 * AcknowledgeRequired = FALSE. 626 * OplockCompletionStatus = STATUS_SUCCESS. 627 * (The operation does not end at this point; this call 628 * to 2.1.5.17.3 completes some earlier call to 2.1.5.17.2.) 629 * 630 * Implementation specific: 631 * 632 * As explained above, the passed in ofile should be the 633 * only open file on this node. Out of caution, we'll 634 * walk the ofile list as usual here, making sure there 635 * are no LevelII oplocks remaining, as those may not 636 * coexist with the exclusive oplock were're creating 637 * in this call. Also, if the passed in ofile has a 638 * LevelII oplock, don't do an "ind break" up call on 639 * this ofile, as that would just cause an immediate 640 * "break to none" of the oplock we'll grant here. 641 * If there were other ofiles with LevelII oplocks, 642 * it would be appropriate to "ind break" those. 643 */ 644 if ((node->n_oplock.ol_state & LEVEL_TWO) != 0) { 645 FOREACH_NODE_OFILE(node, o) { 646 if (o->f_oplock.onlist_II == 0) 647 continue; 648 o->f_oplock.onlist_II = B_FALSE; 649 node->n_oplock.cnt_II--; 650 ASSERT(node->n_oplock.cnt_II >= 0); 651 if (o == ofile) 652 continue; 653 DTRACE_PROBE1(unexpected, smb_ofile_t, o); 654 smb_oplock_ind_break(o, 655 LEVEL_NONE, B_FALSE, 656 NT_STATUS_SUCCESS); 657 } 658 } 659 660 /* 661 * Note the spec. had an extra "EndIf" here. 662 * Confirmed by dochelp@ms 663 */ 664 665 /* 666 * If Open.File.OpenList contains more than one Open whose 667 * Stream is the same as Open.Stream, and NO_OPLOCK is present 668 * in Open.Stream.Oplock.State, the operation MUST be failed 669 * with Status set to STATUS_OPLOCK_NOT_GRANTED. 670 * 671 * Implementation specific: 672 * Allow other opens if they have the same lease ours, 673 * so we can upgrade RH to RWH (for example). Therefore 674 * only count opens with a different TargetOplockKey. 675 * Also ignore "attribute-only" opens. 676 */ 677 if ((node->n_oplock.ol_state & NO_OPLOCK) != 0) { 678 FOREACH_NODE_OFILE(node, o) { 679 if (!smb_ofile_is_open(o)) 680 continue; 681 if ((o->f_granted_access & FILE_DATA_ALL) == 0) 682 continue; 683 if (!CompareOplockKeys(ofile, o, 0)) { 684 status = NT_STATUS_OPLOCK_NOT_GRANTED; 685 goto out; 686 } 687 } 688 } 689 690 /* 691 * If Open.Stream.IsDeleted is TRUE and RequestedOplock 692 * contains HANDLE_CACHING, the operation MUST be failed 693 * with Status set to STATUS_OPLOCK_NOT_GRANTED. 694 */ 695 if (((node->flags & NODE_FLAGS_DELETING) != 0) && 696 (*rop & HANDLE_CACHING) != 0) { 697 status = NT_STATUS_OPLOCK_NOT_GRANTED; 698 goto out; 699 } 700 701 /* Set GrantExclusiveOplock to TRUE. */ 702 GrantExcl = B_TRUE; 703 } 704 705 /* 706 * "Else" If (Open.Stream.Oplock.State contains one or more of 707 * READ_CACHING, WRITE_CACHING, or HANDLE_CACHING) and 708 * (Open.Stream.Oplock.State contains none of (BREAK_ANY)) and 709 * (Open.Stream.Oplock.RHBreakQueue is empty): 710 */ 711 else if ((node->n_oplock.ol_state & CACHE_RWH) != 0 && 712 (node->n_oplock.ol_state & BREAK_ANY) == 0 && 713 node->n_oplock.cnt_RHBQ == 0) { 714 715 /* 716 * This is a granular oplock and it is not breaking. 717 */ 718 719 /* 720 * If RequestedOplock contains none of READ_CACHING, 721 * WRITE_CACHING, or HANDLE_CACHING, the operation 722 * MUST be failed with Status set to 723 * STATUS_OPLOCK_NOT_GRANTED. 724 */ 725 if ((*rop & CACHE_RWH) == 0) { 726 status = NT_STATUS_OPLOCK_NOT_GRANTED; 727 goto out; 728 } 729 730 /* 731 * If Open.Stream.IsDeleted (already checked above) 732 */ 733 734 /* 735 * Switch (Open.Stream.Oplock.State): 736 */ 737 switch (node->n_oplock.ol_state) { 738 739 case CACHE_R: 740 /* 741 * If RequestedOplock is neither 742 * (READ_CACHING|WRITE_CACHING) nor 743 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING), 744 * the operation MUST be failed with Status set 745 * to STATUS_OPLOCK_NOT_GRANTED. 746 */ 747 if (*rop != CACHE_RW && *rop != CACHE_RWH) { 748 status = NT_STATUS_OPLOCK_NOT_GRANTED; 749 goto out; 750 } 751 752 /* 753 * For each Open ThisOpen in 754 * Open.Stream.Oplock.ROplocks: 755 * If ThisOpen.TargetOplockKey != 756 * Open.TargetOplockKey, the operation 757 * MUST be failed with Status set to 758 * STATUS_OPLOCK_NOT_GRANTED. 759 * EndFor 760 */ 761 FOREACH_NODE_OFILE(node, o) { 762 if (o->f_oplock.onlist_R == 0) 763 continue; 764 if (!CompareOplockKeys(ofile, o, 0)) { 765 status = NT_STATUS_OPLOCK_NOT_GRANTED; 766 goto out; 767 } 768 } 769 770 /* 771 * For each Open o in Open.Stream.Oplock.ROplocks: 772 * Remove o from Open.Stream.Oplock.ROplocks. 773 * Notify the server of an oplock break 774 * according to the algorithm in section 775 * 2.1.5.17.3, setting the algorithm's 776 * parameters as follows: 777 * BreakingOplockOpen = o. 778 * NewOplockLevel = RequestedOplock. 779 * AcknowledgeRequired = FALSE. 780 * OplockCompletionStatus = 781 * STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE. 782 * (The operation does not end at this point; 783 * this call to 2.1.5.17.3 completes some 784 * earlier call to 2.1.5.17.2.) 785 * EndFor 786 * 787 * Note: Upgrade to excl. on same lease. 788 * Won't send a break for this. 789 */ 790 FOREACH_NODE_OFILE(node, o) { 791 if (o->f_oplock.onlist_R == 0) 792 continue; 793 o->f_oplock.onlist_R = B_FALSE; 794 node->n_oplock.cnt_R--; 795 ASSERT(node->n_oplock.cnt_R >= 0); 796 797 smb_oplock_ind_break(o, *rop, 798 B_FALSE, STATUS_NEW_HANDLE); 799 } 800 /* 801 * Set GrantExclusiveOplock to TRUE. 802 * EndCase // _R 803 */ 804 GrantExcl = B_TRUE; 805 break; 806 807 case CACHE_RH: 808 /* 809 * If RequestedOplock is not 810 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING) 811 * or Open.Stream.Oplock.RHBreakQueue is not empty, 812 * the operation MUST be failed with Status set to 813 * STATUS_OPLOCK_NOT_GRANTED. 814 * Note: Have RHBreakQueue==0 from above. 815 */ 816 if (*rop != CACHE_RWH) { 817 status = NT_STATUS_OPLOCK_NOT_GRANTED; 818 goto out; 819 } 820 821 /* 822 * For each Open ThisOpen in 823 * Open.Stream.Oplock.RHOplocks: 824 * If ThisOpen.TargetOplockKey != 825 * Open.TargetOplockKey, the operation 826 * MUST be failed with Status set to 827 * STATUS_OPLOCK_NOT_GRANTED. 828 * EndFor 829 */ 830 FOREACH_NODE_OFILE(node, o) { 831 if (o->f_oplock.onlist_RH == 0) 832 continue; 833 if (!CompareOplockKeys(ofile, o, 0)) { 834 status = NT_STATUS_OPLOCK_NOT_GRANTED; 835 goto out; 836 } 837 } 838 839 /* 840 * For each Open o in Open.Stream.Oplock.RHOplocks: 841 * Remove o from Open.Stream.Oplock.RHOplocks. 842 * Notify the server of an oplock break 843 * according to the algorithm in section 844 * 2.1.5.17.3, setting the algorithm's 845 * parameters as follows: 846 * BreakingOplockOpen = o. 847 * NewOplockLevel = RequestedOplock. 848 * AcknowledgeRequired = FALSE. 849 * OplockCompletionStatus = 850 * STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE. 851 * (The operation does not end at this point; 852 * this call to 2.1.5.17.3 completes some 853 * earlier call to 2.1.5.17.2.) 854 * EndFor 855 * 856 * Note: Upgrade to excl. on same lease. 857 * Won't send a break for this. 858 */ 859 FOREACH_NODE_OFILE(node, o) { 860 if (o->f_oplock.onlist_RH == 0) 861 continue; 862 o->f_oplock.onlist_RH = B_FALSE; 863 node->n_oplock.cnt_RH--; 864 ASSERT(node->n_oplock.cnt_RH >= 0); 865 866 smb_oplock_ind_break(o, *rop, 867 B_FALSE, STATUS_NEW_HANDLE); 868 } 869 /* 870 * Set GrantExclusiveOplock to TRUE. 871 * EndCase // _RH 872 */ 873 GrantExcl = B_TRUE; 874 break; 875 876 case (CACHE_RWH | EXCLUSIVE): 877 /* 878 * If RequestedOplock is not 879 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING), 880 * the operation MUST be failed with Status set to 881 * STATUS_OPLOCK_NOT_GRANTED. 882 */ 883 if (*rop != CACHE_RWH) { 884 status = NT_STATUS_OPLOCK_NOT_GRANTED; 885 goto out; 886 } 887 /* Deliberate FALL-THROUGH to next Case statement. */ 888 /* FALLTHROUGH */ 889 890 case (CACHE_RW | EXCLUSIVE): 891 /* 892 * If RequestedOplock is neither 893 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING) nor 894 * (READ_CACHING|WRITE_CACHING), the operation MUST be 895 * failed with Status set to STATUS_OPLOCK_NOT_GRANTED. 896 */ 897 if (*rop != CACHE_RWH && *rop != CACHE_RW) { 898 status = NT_STATUS_OPLOCK_NOT_GRANTED; 899 goto out; 900 } 901 902 o = node->n_oplock.excl_open; 903 if (o == NULL) { 904 ASSERT(0); 905 GrantExcl = B_TRUE; 906 break; 907 } 908 909 /* 910 * If Open.TargetOplockKey != 911 * Open.Stream.Oplock.ExclusiveOpen.TargetOplockKey, 912 * the operation MUST be failed with Status set to 913 * STATUS_OPLOCK_NOT_GRANTED. 914 */ 915 if (!CompareOplockKeys(ofile, o, 0)) { 916 status = NT_STATUS_OPLOCK_NOT_GRANTED; 917 goto out; 918 } 919 920 /* 921 * Notify the server of an oplock break according to 922 * the algorithm in section 2.1.5.17.3, setting the 923 * algorithm's parameters as follows: 924 * BreakingOplockOpen = 925 * Open.Stream.Oplock.ExclusiveOpen. 926 * NewOplockLevel = RequestedOplock. 927 * AcknowledgeRequired = FALSE. 928 * OplockCompletionStatus = 929 * STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE. 930 * (The operation does not end at this point; 931 * this call to 2.1.5.17.3 completes some 932 * earlier call to 2.1.5.17.1.) 933 * 934 * Set Open.Stream.Oplock.ExclusiveOpen to NULL. 935 * Set GrantExclusiveOplock to TRUE. 936 * 937 * Note: We will keep this exclusive oplock, 938 * but move it to a new handle on this lease. 939 * Won't send a break for this. 940 */ 941 smb_oplock_ind_break(o, *rop, 942 B_FALSE, STATUS_NEW_HANDLE); 943 node->n_oplock.excl_open = o = NULL; 944 GrantExcl = B_TRUE; 945 break; 946 947 default: 948 /* 949 * The operation MUST be failed with Status set to 950 * STATUS_OPLOCK_NOT_GRANTED. 951 */ 952 status = NT_STATUS_OPLOCK_NOT_GRANTED; 953 goto out; 954 955 } /* switch n_oplock.ol_state */ 956 } /* EndIf CACHE_RWH & !BREAK_ANY... */ 957 else { 958 /* 959 * The operation MUST be failed with... 960 */ 961 status = NT_STATUS_OPLOCK_NOT_GRANTED; 962 goto out; 963 } 964 965 /* 966 * If GrantExclusiveOplock is TRUE: 967 * 968 * Set Open.Stream.Oplock.ExclusiveOpen = Open. 969 * Set Open.Stream.Oplock.State = 970 * (RequestedOplock|EXCLUSIVE). 971 */ 972 if (GrantExcl) { 973 node->n_oplock.excl_open = ofile; 974 node->n_oplock.ol_state = *rop | EXCLUSIVE; 975 976 /* 977 * This operation MUST be made cancelable... 978 * This operation waits until the oplock is 979 * broken or canceled, as specified in 980 * section 2.1.5.17.3. Note: This function 981 * does not cause breaks that require a wait, 982 * so never returns ..._BREAK_IN_PROGRESS. 983 * 984 * When the operation specified in section 985 * 2.1.5.17.3 is called, its following input 986 * parameters are transferred to this routine 987 * and then returned by it: 988 * 989 * Status is set to OplockCompletionStatus 990 * NewOplockLevel, AcknowledgeRequired... 991 * from the operation specified in 992 * section 2.1.5.17.3. 993 */ 994 /* Keep *rop = ... from caller. */ 995 status = NT_STATUS_SUCCESS; 996 997 /* 998 * First oplock grant installs FEM hooks. 999 */ 1000 if (node->n_oplock.ol_fem == B_FALSE) { 1001 if (smb_fem_oplock_install(node) != 0) { 1002 cmn_err(CE_NOTE, 1003 "smb_fem_oplock_install failed"); 1004 } else { 1005 node->n_oplock.ol_fem = B_TRUE; 1006 } 1007 } 1008 } 1009 1010 out: 1011 if (status == NT_STATUS_OPLOCK_NOT_GRANTED) 1012 *rop = LEVEL_NONE; 1013 1014 return (status); 1015 } 1016 1017 /* 1018 * 2.1.5.17.2 Algorithm to Request a Shared Oplock 1019 * 1020 * The inputs for requesting a shared oplock are: 1021 * Open: The Open on which the oplock is being requested. 1022 * RequestedOplock: The oplock type being requested. 1023 * GrantingInAck: A Boolean value, TRUE if this oplock is being 1024 * requested as part of an oplock break acknowledgement, 1025 * FALSE if not. 1026 * 1027 * On completion, the object store MUST return: 1028 * Status: An NTSTATUS code that specifies the result. 1029 * NewOplockLevel: The type of oplock that the requested oplock has been 1030 * broken (reduced) to. If a failure status is returned in Status, 1031 * the value of this field is undefined. Valid values are as follows: 1032 * LEVEL_NONE (that is, no oplock) 1033 * LEVEL_TWO 1034 * A combination of one or more of the following flags: 1035 * READ_CACHING 1036 * HANDLE_CACHING 1037 * WRITE_CACHING 1038 * AcknowledgeRequired: A Boolean value: TRUE if the server MUST 1039 * acknowledge the oplock break; FALSE if not, as specified in 1040 * section 2.1.5.18. If a failure status is returned in Status, 1041 * the value of this field is undefined. 1042 * 1043 * Note: Stores NewOplockLevel in *rop 1044 */ 1045 static uint32_t 1046 smb_oplock_req_shared( 1047 smb_ofile_t *ofile, /* in: the "Open" */ 1048 uint32_t *rop, /* in: "RequestedOplock", out:NewOplockLevel */ 1049 boolean_t GrantingInAck) 1050 { 1051 smb_node_t *node = ofile->f_node; 1052 smb_ofile_t *o; 1053 boolean_t OplockGranted = B_FALSE; 1054 uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED; 1055 1056 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock)); 1057 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex)); 1058 1059 /* 1060 * Don't allow grants on closing ofiles. 1061 */ 1062 if (ofile->f_oplock_closing) 1063 return (status); 1064 1065 /* 1066 * If Open.Stream.Oplock is empty: 1067 * Build a new Oplock object with fields initialized as follows: 1068 * Oplock.State set to NO_OPLOCK. 1069 * All other fields set to 0/empty. 1070 * Store the new Oplock object in Open.Stream.Oplock. 1071 * EndIf 1072 * 1073 * Implementation specific: 1074 * Open.Stream.Oplock maps to: node->n_oplock 1075 */ 1076 if (node->n_oplock.ol_state == 0) { 1077 node->n_oplock.ol_state = NO_OPLOCK; 1078 } 1079 1080 /* 1081 * If (GrantingInAck is FALSE) and (Open.Stream.Oplock.State 1082 * contains one or more of BREAK_TO_TWO, BREAK_TO_NONE, 1083 * BREAK_TO_TWO_TO_NONE, BREAK_TO_READ_CACHING, 1084 * BREAK_TO_WRITE_CACHING, BREAK_TO_HANDLE_CACHING, 1085 * BREAK_TO_NO_CACHING, or EXCLUSIVE), then: 1086 * The operation MUST be failed with Status set to 1087 * STATUS_OPLOCK_NOT_GRANTED. 1088 * EndIf 1089 */ 1090 if (GrantingInAck == B_FALSE && 1091 (node->n_oplock.ol_state & (BREAK_ANY | EXCLUSIVE)) != 0) { 1092 status = NT_STATUS_OPLOCK_NOT_GRANTED; 1093 goto out; 1094 } 1095 1096 /* Switch (RequestedOplock): */ 1097 switch (*rop) { 1098 1099 case LEVEL_TWO: 1100 /* 1101 * The operation MUST be failed with Status set to 1102 * STATUS_OPLOCK_NOT_GRANTED if Open.Stream.Oplock.State 1103 * is anything other than the following: 1104 * NO_OPLOCK 1105 * LEVEL_TWO_OPLOCK 1106 * READ_CACHING 1107 * (LEVEL_TWO_OPLOCK|READ_CACHING) 1108 */ 1109 switch (node->n_oplock.ol_state) { 1110 default: 1111 status = NT_STATUS_OPLOCK_NOT_GRANTED; 1112 goto out; 1113 case NO_OPLOCK: 1114 case LEVEL_TWO: 1115 case READ_CACHING: 1116 case (LEVEL_TWO | READ_CACHING): 1117 break; 1118 } 1119 /* Deliberate FALL-THROUGH to next Case statement. */ 1120 /* FALLTHROUGH */ 1121 1122 case READ_CACHING: 1123 /* 1124 * The operation MUST be failed with Status set to 1125 * STATUS_OPLOCK_NOT_GRANTED if GrantingInAck is FALSE 1126 * and Open.Stream.Oplock.State is anything other than... 1127 */ 1128 switch (node->n_oplock.ol_state) { 1129 default: 1130 if (GrantingInAck == B_FALSE) { 1131 status = NT_STATUS_OPLOCK_NOT_GRANTED; 1132 goto out; 1133 } 1134 break; 1135 case NO_OPLOCK: 1136 case LEVEL_TWO: 1137 case READ_CACHING: 1138 case (LEVEL_TWO | READ_CACHING): 1139 case (READ_CACHING | HANDLE_CACHING): 1140 case (READ_CACHING | HANDLE_CACHING | MIXED_R_AND_RH): 1141 case (READ_CACHING | HANDLE_CACHING | BREAK_TO_READ_CACHING): 1142 case (READ_CACHING | HANDLE_CACHING | BREAK_TO_NO_CACHING): 1143 break; 1144 } 1145 1146 if (GrantingInAck == B_FALSE) { 1147 /* 1148 * If there is an Open on 1149 * Open.Stream.Oplock.RHOplocks 1150 * whose TargetOplockKey is equal to 1151 * Open.TargetOplockKey, the operation 1152 * MUST be failed with Status set to 1153 * STATUS_OPLOCK_NOT_GRANTED. 1154 * 1155 * If there is an Open on 1156 * Open.Stream.Oplock.RHBreakQueue 1157 * whose TargetOplockKey is equal to 1158 * Open.TargetOplockKey, the operation 1159 * MUST be failed with Status set to 1160 * STATUS_OPLOCK_NOT_GRANTED. 1161 * 1162 * Implement both in one list walk. 1163 */ 1164 FOREACH_NODE_OFILE(node, o) { 1165 if ((o->f_oplock.onlist_RH || 1166 o->f_oplock.onlist_RHBQ) && 1167 CompareOplockKeys(ofile, o, 0)) { 1168 status = NT_STATUS_OPLOCK_NOT_GRANTED; 1169 goto out; 1170 } 1171 } 1172 1173 /* 1174 * If there is an Open ThisOpen on 1175 * Open.Stream.Oplock.ROplocks whose 1176 * TargetOplockKey is equal to Open.TargetOplockKey 1177 * (there is supposed to be at most one present): 1178 * * Remove ThisOpen from Open...ROplocks. 1179 * * Notify the server of an oplock break 1180 * according to the algorithm in section 1181 * 2.1.5.17.3, setting the algorithm's 1182 * parameters as follows: 1183 * * BreakingOplockOpen = ThisOpen 1184 * * NewOplockLevel = READ_CACHING 1185 * * AcknowledgeRequired = FALSE 1186 * * OplockCompletionStatus = 1187 * STATUS_..._NEW_HANDLE 1188 * (The operation does not end at this point; 1189 * this call to 2.1.5.17.3 completes some 1190 * earlier call to 2.1.5.17.2.) 1191 * EndIf 1192 * 1193 * If this SMB2 lease already has an "R" handle, 1194 * we'll update that lease locally to point to 1195 * this new handle. 1196 */ 1197 FOREACH_NODE_OFILE(node, o) { 1198 if (o->f_oplock.onlist_R == 0) 1199 continue; 1200 if (CompareOplockKeys(ofile, o, 0)) { 1201 o->f_oplock.onlist_R = B_FALSE; 1202 node->n_oplock.cnt_R--; 1203 ASSERT(node->n_oplock.cnt_R >= 0); 1204 smb_oplock_ind_break(o, 1205 CACHE_R, B_FALSE, 1206 STATUS_NEW_HANDLE); 1207 } 1208 } 1209 } /* EndIf !GrantingInAck */ 1210 1211 /* 1212 * If RequestedOplock equals LEVEL_TWO: 1213 * Add Open to Open.Stream.Oplock.IIOplocks. 1214 * Else // RequestedOplock equals READ_CACHING: 1215 * Add Open to Open.Stream.Oplock.ROplocks. 1216 * EndIf 1217 */ 1218 if (*rop == LEVEL_TWO) { 1219 ofile->f_oplock.onlist_II = B_TRUE; 1220 node->n_oplock.cnt_II++; 1221 } else { 1222 /* (*rop == READ_CACHING) */ 1223 if (ofile->f_oplock.onlist_R == B_FALSE) { 1224 ofile->f_oplock.onlist_R = B_TRUE; 1225 node->n_oplock.cnt_R++; 1226 } 1227 } 1228 1229 /* 1230 * Recompute Open.Stream.Oplock.State according to the 1231 * algorithm in section 2.1.4.13, passing Open.Stream.Oplock 1232 * as the ThisOplock parameter. 1233 * Set OplockGranted to TRUE. 1234 */ 1235 RecomputeOplockState(node); 1236 OplockGranted = B_TRUE; 1237 break; 1238 1239 case (READ_CACHING|HANDLE_CACHING): 1240 /* 1241 * The operation MUST be failed with Status set to 1242 * STATUS_OPLOCK_NOT_GRANTED if GrantingInAck is FALSE 1243 * and Open.Stream.Oplock.State is anything other than... 1244 */ 1245 switch (node->n_oplock.ol_state) { 1246 default: 1247 if (GrantingInAck == B_FALSE) { 1248 status = NT_STATUS_OPLOCK_NOT_GRANTED; 1249 goto out; 1250 } 1251 break; 1252 case NO_OPLOCK: 1253 case READ_CACHING: 1254 case (READ_CACHING | HANDLE_CACHING): 1255 case (READ_CACHING | HANDLE_CACHING | MIXED_R_AND_RH): 1256 case (READ_CACHING | HANDLE_CACHING | BREAK_TO_READ_CACHING): 1257 case (READ_CACHING | HANDLE_CACHING | BREAK_TO_NO_CACHING): 1258 break; 1259 } 1260 1261 /* 1262 * If Open.Stream.IsDeleted is TRUE, the operation MUST be 1263 * failed with Status set to STATUS_OPLOCK_NOT_GRANTED. 1264 */ 1265 if ((node->flags & NODE_FLAGS_DELETING) != 0) { 1266 status = NT_STATUS_OPLOCK_NOT_GRANTED; 1267 goto out; 1268 } 1269 1270 if (GrantingInAck == B_FALSE) { 1271 /* 1272 * If there is an Open ThisOpen on 1273 * Open.Stream.Oplock.ROplocks whose 1274 * TargetOplockKey is equal to Open.TargetOplockKey 1275 * (there is supposed to be at most one present): 1276 * * Remove ThisOpen from Open...ROplocks. 1277 * * Notify the server of an oplock break 1278 * according to the algorithm in section 1279 * 2.1.5.17.3, setting the algorithm's 1280 * parameters as follows: 1281 * * BreakingOplockOpen = ThisOpen 1282 * * NewOplockLevel = CACHE_RH 1283 * * AcknowledgeRequired = FALSE 1284 * * OplockCompletionStatus = 1285 * STATUS_..._NEW_HANDLE 1286 * (The operation does not end at this point; 1287 * this call to 2.1.5.17.3 completes some 1288 * earlier call to 2.1.5.17.2.) 1289 * EndIf 1290 * 1291 * If this SMB2 lease already has an "R" handle, 1292 * we'll update that lease locally to point to 1293 * this new handle (upgrade to "RH"). 1294 */ 1295 FOREACH_NODE_OFILE(node, o) { 1296 if (o->f_oplock.onlist_R == 0) 1297 continue; 1298 if (CompareOplockKeys(ofile, o, 0)) { 1299 o->f_oplock.onlist_R = B_FALSE; 1300 node->n_oplock.cnt_R--; 1301 ASSERT(node->n_oplock.cnt_R >= 0); 1302 smb_oplock_ind_break(o, 1303 CACHE_RH, B_FALSE, 1304 STATUS_NEW_HANDLE); 1305 } 1306 } 1307 1308 /* 1309 * If there is an Open ThisOpen on 1310 * Open.Stream.Oplock.RHOplocks whose 1311 * TargetOplockKey is equal to Open.TargetOplockKey 1312 * (there is supposed to be at most one present): 1313 * XXX: Note, the spec. was missing a step: 1314 * XXX: Remove the open from RHOplocks 1315 * XXX: Confirm with MS dochelp 1316 * * Notify the server of an oplock break 1317 * according to the algorithm in section 1318 * 2.1.5.17.3, setting the algorithm's 1319 * parameters as follows: 1320 * * BreakingOplockOpen = ThisOpen 1321 * * NewOplockLevel = 1322 * (READ_CACHING|HANDLE_CACHING) 1323 * * AcknowledgeRequired = FALSE 1324 * * OplockCompletionStatus = 1325 * STATUS_..._NEW_HANDLE 1326 * (The operation does not end at this point; 1327 * this call to 2.1.5.17.3 completes some 1328 * earlier call to 2.1.5.17.2.) 1329 * EndIf 1330 * 1331 * If this SMB2 lease already has an "RH" handle, 1332 * we'll update that lease locally to point to 1333 * this new handle. 1334 */ 1335 FOREACH_NODE_OFILE(node, o) { 1336 if (o->f_oplock.onlist_RH == 0) 1337 continue; 1338 if (CompareOplockKeys(ofile, o, 0)) { 1339 o->f_oplock.onlist_RH = B_FALSE; 1340 node->n_oplock.cnt_RH--; 1341 ASSERT(node->n_oplock.cnt_RH >= 0); 1342 smb_oplock_ind_break(o, 1343 CACHE_RH, B_FALSE, 1344 STATUS_NEW_HANDLE); 1345 } 1346 } 1347 } /* EndIf !GrantingInAck */ 1348 1349 /* 1350 * Add Open to Open.Stream.Oplock.RHOplocks. 1351 */ 1352 if (ofile->f_oplock.onlist_RH == B_FALSE) { 1353 ofile->f_oplock.onlist_RH = B_TRUE; 1354 node->n_oplock.cnt_RH++; 1355 } 1356 1357 /* 1358 * Recompute Open.Stream.Oplock.State according to the 1359 * algorithm in section 2.1.4.13, passing Open.Stream.Oplock 1360 * as the ThisOplock parameter. 1361 * Set OplockGranted to TRUE. 1362 */ 1363 RecomputeOplockState(node); 1364 OplockGranted = B_TRUE; 1365 break; 1366 1367 default: 1368 /* No other value of RequestedOplock is possible. */ 1369 ASSERT(0); 1370 status = NT_STATUS_OPLOCK_NOT_GRANTED; 1371 goto out; 1372 } /* EndSwitch (RequestedOplock) */ 1373 1374 /* 1375 * If OplockGranted is TRUE: 1376 * This operation MUST be made cancelable by inserting it into 1377 * CancelableOperations.CancelableOperationList. 1378 * The operation waits until the oplock is broken or canceled, 1379 * as specified in section 2.1.5.17.3. 1380 * When the operation specified in section 2.1.5.17.3 is called, 1381 * its following input parameters are transferred to this routine 1382 * and returned by it: 1383 * Status is set to OplockCompletionStatus from the 1384 * operation specified in section 2.1.5.17.3. 1385 * NewOplockLevel is set to NewOplockLevel from the 1386 * operation specified in section 2.1.5.17.3. 1387 * AcknowledgeRequired is set to AcknowledgeRequired from 1388 * the operation specified in section 2.1.5.17.3. 1389 * EndIf 1390 */ 1391 if (OplockGranted) { 1392 /* Note: *rop already set. */ 1393 if ((node->n_oplock.ol_state & BREAK_ANY) != 0) { 1394 status = NT_STATUS_OPLOCK_BREAK_IN_PROGRESS; 1395 /* Caller does smb_oplock_wait_break() */ 1396 } else { 1397 status = NT_STATUS_SUCCESS; 1398 } 1399 1400 /* 1401 * First oplock grant installs FEM hooks. 1402 */ 1403 if (node->n_oplock.ol_fem == B_FALSE) { 1404 if (smb_fem_oplock_install(node) != 0) { 1405 cmn_err(CE_NOTE, 1406 "smb_fem_oplock_install failed"); 1407 } else { 1408 node->n_oplock.ol_fem = B_TRUE; 1409 } 1410 } 1411 } 1412 1413 out: 1414 if (status == NT_STATUS_OPLOCK_NOT_GRANTED) 1415 *rop = LEVEL_NONE; 1416 1417 return (status); 1418 } 1419 1420 /* 1421 * 2.1.5.17.3 Indicating an Oplock Break to the Server 1422 * See smb_srv_oplock.c 1423 */ 1424 1425 /* 1426 * 2.1.5.18 Server Acknowledges an Oplock Break 1427 * 1428 * The server provides: 1429 * Open - The Open associated with the oplock that has broken. 1430 * Type - As part of the acknowledgement, the server indicates a 1431 * new oplock it would like in place of the one that has broken. 1432 * Valid values are as follows: 1433 * LEVEL_NONE 1434 * LEVEL_TWO 1435 * LEVEL_GRANULAR - If this oplock type is specified, 1436 * the server additionally provides: 1437 * RequestedOplockLevel - A combination of zero or more of 1438 * the following flags: 1439 * READ_CACHING 1440 * HANDLE_CACHING 1441 * WRITE_CACHING 1442 * 1443 * If the server requests a new oplock and it is granted, the request 1444 * does not complete until the oplock is broken; the operation waits for 1445 * this to happen. Processing of an oplock break is described in 1446 * section 2.1.5.17.3. Whether the new oplock is granted or not, the 1447 * object store MUST return: 1448 * 1449 * Status - An NTSTATUS code indicating the result of the operation. 1450 * 1451 * If the server requests a new oplock and it is granted, then when the 1452 * oplock breaks and the request finally completes, the object store MUST 1453 * additionally return: 1454 * NewOplockLevel: The type of oplock the requested oplock has 1455 * been broken to. Valid values are as follows: 1456 * LEVEL_NONE (that is, no oplock) 1457 * LEVEL_TWO 1458 * A combination of one or more of the following flags: 1459 * READ_CACHING 1460 * HANDLE_CACHING 1461 * WRITE_CACHING 1462 * AcknowledgeRequired: A Boolean value; TRUE if the server MUST 1463 * acknowledge the oplock break, FALSE if not, as specified in 1464 * section 2.1.5.17.2. 1465 * 1466 * Note: Stores NewOplockLevel in *rop 1467 */ 1468 uint32_t 1469 smb_oplock_ack_break( 1470 smb_request_t *sr, 1471 smb_ofile_t *ofile, 1472 uint32_t *rop) 1473 { 1474 smb_node_t *node = ofile->f_node; 1475 uint32_t type = *rop & OPLOCK_LEVEL_TYPE_MASK; 1476 uint32_t level = *rop & OPLOCK_LEVEL_CACHE_MASK; 1477 uint32_t status = NT_STATUS_SUCCESS; 1478 uint32_t BreakToLevel; 1479 boolean_t NewOplockGranted = B_FALSE; 1480 boolean_t ReturnBreakToNone = B_FALSE; 1481 boolean_t FoundMatchingRHOplock = B_FALSE; 1482 int other_keys; 1483 1484 smb_llist_enter(&node->n_ofile_list, RW_READER); 1485 mutex_enter(&node->n_oplock.ol_mutex); 1486 1487 /* 1488 * If Open.Stream.Oplock is empty, the operation MUST be 1489 * failed with Status set to STATUS_INVALID_OPLOCK_PROTOCOL. 1490 */ 1491 if (node->n_oplock.ol_state == 0) { 1492 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL; 1493 goto out; 1494 } 1495 1496 if (type == LEVEL_NONE || type == LEVEL_TWO) { 1497 /* 1498 * If Open.Stream.Oplock.ExclusiveOpen is not equal to Open, 1499 * the operation MUST be failed with Status set to 1500 * STATUS_INVALID_OPLOCK_PROTOCOL. 1501 */ 1502 if (node->n_oplock.excl_open != ofile) { 1503 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL; 1504 goto out; 1505 } 1506 1507 /* 1508 * If Type is LEVEL_TWO and Open.Stream.Oplock.State 1509 * contains BREAK_TO_TWO: 1510 * Set Open.Stream.Oplock.State to LEVEL_TWO_OPLOCK. 1511 * Set NewOplockGranted to TRUE. 1512 */ 1513 if (type == LEVEL_TWO && 1514 (node->n_oplock.ol_state & BREAK_TO_TWO) != 0) { 1515 node->n_oplock.ol_state = LEVEL_TWO; 1516 NewOplockGranted = B_TRUE; 1517 } 1518 1519 /* 1520 * Else If Open.Stream.Oplock.State contains 1521 * BREAK_TO_TWO or BREAK_TO_NONE: 1522 * Set Open.Stream.Oplock.State to NO_OPLOCK. 1523 */ 1524 else if ((node->n_oplock.ol_state & 1525 (BREAK_TO_TWO | BREAK_TO_NONE)) != 0) { 1526 node->n_oplock.ol_state = NO_OPLOCK; 1527 } 1528 1529 /* 1530 * Else If Open.Stream.Oplock.State contains 1531 * BREAK_TO_TWO_TO_NONE: 1532 * Set Open.Stream.Oplock.State to NO_OPLOCK. 1533 * Set ReturnBreakToNone to TRUE. 1534 */ 1535 else if ((node->n_oplock.ol_state & 1536 BREAK_TO_TWO_TO_NONE) != 0) { 1537 node->n_oplock.ol_state = NO_OPLOCK; 1538 ReturnBreakToNone = B_TRUE; 1539 } 1540 1541 /* 1542 * Else 1543 * The operation MUST be failed with Status set to 1544 * STATUS_INVALID_OPLOCK_PROTOCOL. 1545 */ 1546 else { 1547 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL; 1548 goto out; 1549 } 1550 1551 /* 1552 * For each Open WaitingOpen on Open.Stream.Oplock.WaitList: 1553 * Indicate that the operation associated with 1554 * WaitingOpen can continue according to the 1555 * algorithm in section 2.1.4.12.1, setting 1556 * OpenToRelease = WaitingOpen. 1557 * Remove WaitingOpen from Open.Stream.Oplock.WaitList. 1558 * EndFor 1559 */ 1560 if (node->n_oplock.waiters) 1561 cv_broadcast(&node->n_oplock.WaitingOpenCV); 1562 1563 /* 1564 * Set Open.Stream.Oplock.ExclusiveOpen to NULL. 1565 */ 1566 node->n_oplock.excl_open = NULL; 1567 1568 if (NewOplockGranted) { 1569 /* 1570 * The operation waits until the newly-granted 1571 * Level 2 oplock is broken, as specified in 1572 * section 2.1.5.17.3. 1573 * 1574 * Here we have just Ack'ed a break-to-II 1575 * so now get the level II oplock. We also 1576 * checked for break-to-none above, so this 1577 * will not need to wait for oplock breaks. 1578 */ 1579 status = smb_oplock_req_shared(ofile, rop, B_TRUE); 1580 } 1581 1582 else if (ReturnBreakToNone) { 1583 /* 1584 * In this case the server was expecting the oplock 1585 * to break to Level 2, but because the oplock is 1586 * actually breaking to None (that is, no oplock), 1587 * the object store MUST indicate an oplock break 1588 * to the server according to the algorithm in 1589 * section 2.1.5.17.3, setting the algorithm's 1590 * parameters as follows: 1591 * BreakingOplockOpen = Open. 1592 * NewOplockLevel = LEVEL_NONE. 1593 * AcknowledgeRequired = FALSE. 1594 * OplockCompletionStatus = STATUS_SUCCESS. 1595 * (Because BreakingOplockOpen is equal to the 1596 * passed-in Open, the operation ends at this point.) 1597 * 1598 * It should be OK to return the reduced oplock 1599 * (*rop = LEVEL_NONE) here and avoid the need 1600 * to send another oplock break. This is safe 1601 * because we already have an Ack of the break 1602 * to Level_II, and the additional break to none 1603 * would use AckRequired = FALSE. 1604 * 1605 * If we followed the spec here, we'd have: 1606 * smb_oplock_ind_break(ofile, 1607 * LEVEL_NONE, B_FALSE, 1608 * NT_STATUS_SUCCESS); 1609 * (Or smb_oplock_ind_break_in_ack...) 1610 */ 1611 *rop = LEVEL_NONE; /* Reduced from L2 */ 1612 } 1613 status = NT_STATUS_SUCCESS; 1614 goto out; 1615 } /* LEVEL_NONE or LEVEL_TWO */ 1616 1617 if (type != LEVEL_GRANULAR) { 1618 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL; 1619 goto out; 1620 } 1621 1622 /* LEVEL_GRANULAR */ 1623 1624 /* 1625 * Let BREAK_LEVEL_MASK = (BREAK_TO_READ_CACHING | 1626 * BREAK_TO_WRITE_CACHING | BREAK_TO_HANDLE_CACHING | 1627 * BREAK_TO_NO_CACHING), 1628 * R_AND_RH_GRANTED = (READ_CACHING | HANDLE_CACHING | 1629 * MIXED_R_AND_RH), 1630 * RH_GRANTED = (READ_CACHING | HANDLE_CACHING) 1631 * 1632 * (See BREAK_LEVEL_MASK in smb_oplock.h) 1633 */ 1634 #define RH_GRANTED (READ_CACHING|HANDLE_CACHING) 1635 #define R_AND_RH_GRANTED (RH_GRANTED|MIXED_R_AND_RH) 1636 1637 /* 1638 * If there are no BREAK_LEVEL_MASK flags set, this is invalid, 1639 * unless the state is R_AND_RH_GRANTED or RH_GRANTED, in which 1640 * case we'll need to see if the RHBreakQueue is empty. 1641 */ 1642 1643 /* 1644 * If (Open.Stream.Oplock.State does not contain any flag in 1645 * BREAK_LEVEL_MASK and 1646 * (Open.Stream.Oplock.State != R_AND_RH_GRANTED) and 1647 * (Open.Stream.Oplock.State != RH_GRANTED)) or 1648 * (((Open.Stream.Oplock.State == R_AND_RH_GRANTED) or 1649 * (Open.Stream.Oplock.State == RH_GRANTED)) and 1650 * Open.Stream.Oplock.RHBreakQueue is empty): 1651 * The request MUST be failed with Status set to 1652 * STATUS_INVALID_OPLOCK_PROTOCOL. 1653 * EndIf 1654 */ 1655 if ((node->n_oplock.ol_state & BREAK_LEVEL_MASK) == 0) { 1656 if ((node->n_oplock.ol_state != R_AND_RH_GRANTED) && 1657 (node->n_oplock.ol_state != RH_GRANTED)) { 1658 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL; 1659 goto out; 1660 } 1661 /* State is R_AND_RH_GRANTED or RH_GRANTED */ 1662 if (node->n_oplock.cnt_RHBQ == 0) { 1663 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL; 1664 goto out; 1665 } 1666 } 1667 1668 /* 1669 * Compute the "Break To" cache level from the 1670 * BREAK_TO_... flags 1671 */ 1672 switch (node->n_oplock.ol_state & BREAK_LEVEL_MASK) { 1673 case (BREAK_TO_READ_CACHING | BREAK_TO_WRITE_CACHING | 1674 BREAK_TO_HANDLE_CACHING): 1675 BreakToLevel = CACHE_RWH; 1676 break; 1677 case (BREAK_TO_READ_CACHING | BREAK_TO_WRITE_CACHING): 1678 BreakToLevel = CACHE_RW; 1679 break; 1680 case (BREAK_TO_READ_CACHING | BREAK_TO_HANDLE_CACHING): 1681 BreakToLevel = CACHE_RH; 1682 break; 1683 case BREAK_TO_READ_CACHING: 1684 BreakToLevel = READ_CACHING; 1685 break; 1686 case BREAK_TO_NO_CACHING: 1687 default: 1688 BreakToLevel = LEVEL_NONE; 1689 break; 1690 } 1691 1692 /* Switch Open.Stream.Oplock.State */ 1693 switch (node->n_oplock.ol_state) { 1694 1695 case (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH): 1696 case (READ_CACHING|HANDLE_CACHING): 1697 case (READ_CACHING|HANDLE_CACHING|BREAK_TO_READ_CACHING): 1698 case (READ_CACHING|HANDLE_CACHING|BREAK_TO_NO_CACHING): 1699 /* 1700 * For each RHOpContext ThisContext in 1701 * Open.Stream.Oplock.RHBreakQueue: 1702 * If ThisContext.Open equals Open: 1703 * (see below) 1704 * 1705 * Implementation skips the list walk, because 1706 * we can get the ofile directly. 1707 */ 1708 if (ofile->f_oplock.onlist_RHBQ) { 1709 smb_ofile_t *o; 1710 1711 /* 1712 * Set FoundMatchingRHOplock to TRUE. 1713 * If ThisContext.BreakingToRead is FALSE: 1714 * If RequestedOplockLevel is not 0 and 1715 * Open.Stream.Oplock.WaitList is not empty: 1716 * The object store MUST indicate an 1717 * oplock break to the server according to 1718 * the algorithm in section 2.1.5.17.3, 1719 * setting the algorithm's params as follows: 1720 * BreakingOplockOpen = Open. 1721 * NewOplockLevel = LEVEL_NONE. 1722 * AcknowledgeRequired = TRUE. 1723 * OplockCompletionStatus = 1724 * STATUS_CANNOT_GRANT_... 1725 * (Because BreakingOplockOpen is equal to the 1726 * passed Open, the operation ends at this point.) 1727 * EndIf 1728 */ 1729 FoundMatchingRHOplock = B_TRUE; 1730 if (ofile->f_oplock.BreakingToRead == B_FALSE) { 1731 if (level != 0 && node->n_oplock.waiters) { 1732 /* The ofile stays on RHBQ */ 1733 smb_oplock_ind_break_in_ack( 1734 sr, ofile, 1735 LEVEL_NONE, B_TRUE); 1736 status = NT_STATUS_SUCCESS; 1737 goto out; 1738 } 1739 } 1740 1741 /* 1742 * Else // ThisContext.BreakingToRead is TRUE. 1743 * If Open.Stream.Oplock.WaitList is not empty and 1744 * (RequestedOplockLevel is CACHE_RW or CACHE_RWH: 1745 * The object store MUST indicate an oplock 1746 * break to the server according to the 1747 * algorithm in section 2.1.5.17.3, setting 1748 * the algorithm's parameters as follows: 1749 * * BreakingOplockOpen = Open 1750 * * NewOplockLevel = READ_CACHING 1751 * * AcknowledgeRequired = TRUE 1752 * * OplockCompletionStatus = 1753 * STATUS_CANNOT_GRANT... 1754 * (Because BreakingOplockOpen is equal to the 1755 * passed-in Open, the operation ends at this 1756 * point.) 1757 * EndIf 1758 * EndIf 1759 */ 1760 else { /* BreakingToRead is TRUE */ 1761 if (node->n_oplock.waiters && 1762 (level == CACHE_RW || 1763 level == CACHE_RWH)) { 1764 /* The ofile stays on RHBQ */ 1765 smb_oplock_ind_break_in_ack( 1766 sr, ofile, 1767 CACHE_R, B_TRUE); 1768 status = NT_STATUS_SUCCESS; 1769 goto out; 1770 } 1771 } 1772 1773 /* 1774 * Remove ThisContext from Open...RHBreakQueue. 1775 */ 1776 ofile->f_oplock.onlist_RHBQ = B_FALSE; 1777 node->n_oplock.cnt_RHBQ--; 1778 ASSERT(node->n_oplock.cnt_RHBQ >= 0); 1779 1780 /* 1781 * The operation waiting for the Read-Handle 1782 * oplock to break can continue if there are 1783 * no more Read-Handle oplocks outstanding, or 1784 * if all the remaining Read-Handle oplocks 1785 * have the same oplock key as the waiting 1786 * operation. 1787 * 1788 * For each Open WaitingOpen on Open...WaitList: 1789 * 1790 * * If (Open...RHBreakQueue is empty) or 1791 * (all RHOpContext.Open.TargetOplockKey values 1792 * on Open.Stream.Oplock.RHBreakQueue are 1793 * equal to WaitingOpen.TargetOplockKey): 1794 * * Indicate that the operation assoc. 1795 * with WaitingOpen can continue 1796 * according to the algorithm in 1797 * section 2.1.4.12.1, setting 1798 * OpenToRelease = WaitingOpen. 1799 * * Remove WaitingOpen from 1800 * Open.Stream.Oplock.WaitList. 1801 * * EndIf 1802 * EndFor 1803 */ 1804 other_keys = 0; 1805 FOREACH_NODE_OFILE(node, o) { 1806 if (o->f_oplock.onlist_RHBQ == 0) 1807 continue; 1808 if (!CompareOplockKeys(ofile, o, 0)) 1809 other_keys++; 1810 } 1811 if (other_keys == 0) 1812 cv_broadcast(&node->n_oplock.WaitingOpenCV); 1813 1814 /* 1815 * If RequestedOplockLevel is 0 (that is, no flags): 1816 * * Recompute Open.Stream.Oplock.State 1817 * according to the algorithm in section 1818 * 2.1.4.13, passing Open.Stream.Oplock as 1819 * the ThisOplock parameter. 1820 * * The algorithm MUST return Status set to 1821 * STATUS_SUCCESS at this point. 1822 */ 1823 if (level == 0) { 1824 RecomputeOplockState(node); 1825 status = NT_STATUS_SUCCESS; 1826 goto out; 1827 } 1828 1829 /* 1830 * Else If RequestedOplockLevel does not contain 1831 * WRITE_CACHING: 1832 * * The object store MUST request a shared oplock 1833 * according to the algorithm in section 1834 * 2.1.5.17.2, setting the algorithm's 1835 * parameters as follows: 1836 * * Open = current Open. 1837 * * RequestedOplock = 1838 * RequestedOplockLevel. 1839 * * GrantingInAck = TRUE. 1840 * * The operation MUST at this point return any 1841 * status code returned by the shared oplock 1842 * request algorithm. 1843 */ 1844 else if ((level & WRITE_CACHING) == 0) { 1845 *rop = level; 1846 status = smb_oplock_req_shared( 1847 ofile, rop, B_TRUE); 1848 goto out; 1849 } 1850 1851 /* 1852 * Set Open.Stream.Oplock.ExclusiveOpen to 1853 * ThisContext.Open. 1854 * Set Open.Stream.Oplock.State to 1855 * (RequestedOplockLevel|EXCLUSIVE). 1856 * This operation MUST be made cancelable by 1857 * inserting it into CancelableOperations... 1858 * This operation waits until the oplock is 1859 * broken or canceled, as specified in 1860 * section 2.1.5.17.3. 1861 * 1862 * Implementation note: 1863 * 1864 * Once we assing ol_state below, there 1865 * will be no BREAK_TO_... flags set, 1866 * so no need to wait for oplock breaks. 1867 */ 1868 node->n_oplock.excl_open = ofile; 1869 node->n_oplock.ol_state = level | EXCLUSIVE; 1870 status = NT_STATUS_SUCCESS; 1871 } /* onlist_RHBQ */ 1872 if (FoundMatchingRHOplock == B_FALSE) { 1873 /* The operation MUST be failed with Status... */ 1874 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL; 1875 goto out; 1876 } 1877 break; /* case (READ_CACHING|HANDLE_CACHING...) */ 1878 1879 case (READ_CACHING|WRITE_CACHING|EXCLUSIVE|BREAK_TO_READ_CACHING): 1880 case (READ_CACHING|WRITE_CACHING|EXCLUSIVE|BREAK_TO_NO_CACHING): 1881 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE| 1882 BREAK_TO_READ_CACHING|BREAK_TO_WRITE_CACHING): 1883 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE| 1884 BREAK_TO_READ_CACHING|BREAK_TO_HANDLE_CACHING): 1885 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE| 1886 BREAK_TO_READ_CACHING): 1887 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE| 1888 BREAK_TO_NO_CACHING): 1889 /* 1890 * If Open.Stream.Oplock.ExclusiveOpen != Open: 1891 * * The operation MUST be failed with Status set to 1892 * STATUS_INVALID_OPLOCK_PROTOCOL. 1893 * EndIf 1894 */ 1895 if (node->n_oplock.excl_open != ofile) { 1896 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL; 1897 goto out; 1898 } 1899 1900 /* 1901 * If Open.Stream.Oplock.WaitList is not empty and 1902 * Open.Stream.Oplock.State does not contain HANDLE_CACHING 1903 * and RequestedOplockLevel is CACHE_RWH: 1904 * The object store MUST indicate an oplock break to 1905 * the server according to the algorithm in section 1906 * 2.1.5.17.3, setting the algorithm's params as follows: 1907 * * BreakingOplockOpen = Open. 1908 * * NewOplockLevel = BreakToLevel (see above) 1909 * * AcknowledgeRequired = TRUE. 1910 * * OplockCompletionStatus = 1911 * STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. 1912 * (Because BreakingOplockOpen is equal to the passed-in 1913 * Open, the operation ends at this point.) 1914 */ 1915 if (node->n_oplock.waiters && 1916 (node->n_oplock.ol_state & HANDLE_CACHING) == 0 && 1917 level == CACHE_RWH) { 1918 smb_oplock_ind_break_in_ack( 1919 sr, ofile, 1920 BreakToLevel, B_TRUE); 1921 status = NT_STATUS_SUCCESS; 1922 goto out; 1923 } 1924 1925 /* 1926 * Else If Open.Stream.IsDeleted is TRUE and 1927 * RequestedOplockLevel contains HANDLE_CACHING: 1928 */ 1929 else if (((node->flags & NODE_FLAGS_DELETING) != 0) && 1930 (level & HANDLE_CACHING) != 0) { 1931 1932 /* 1933 * The object store MUST indicate an oplock break to 1934 * the server according to the algorithm in section 1935 * 2.1.5.17.3, setting the algorithm's params as 1936 * follows: 1937 * * BreakingOplockOpen = Open. 1938 * * NewOplockLevel = RequestedOplockLevel 1939 * without HANDLE_CACHING (for example if 1940 * RequestedOplockLevel is 1941 * (READ_CACHING|HANDLE_CACHING), then 1942 * NewOplockLevel would be just READ_CACHING). 1943 * * AcknowledgeRequired = TRUE. 1944 * * OplockCompletionStatus = 1945 * STATUS_CANNOT_GRANT_REQUESTED_OPLOCK. 1946 * (Because BreakingOplockOpen is equal to the 1947 * passed-in Open, the operation ends at this point.) 1948 */ 1949 level &= ~HANDLE_CACHING; 1950 smb_oplock_ind_break_in_ack( 1951 sr, ofile, 1952 level, B_TRUE); 1953 status = NT_STATUS_SUCCESS; 1954 goto out; 1955 } 1956 1957 /* 1958 * For each Open WaitingOpen on Open.Stream.Oplock.WaitList: 1959 * * Indicate that the operation associated with 1960 * WaitingOpen can continue according to the algorithm 1961 * in section 2.1.4.12.1, setting OpenToRelease 1962 * = WaitingOpen. 1963 * * Remove WaitingOpen from Open.Stream.Oplock.WaitList. 1964 * EndFor 1965 */ 1966 cv_broadcast(&node->n_oplock.WaitingOpenCV); 1967 1968 /* 1969 * If RequestedOplockLevel does not contain WRITE_CACHING: 1970 * * Set Open.Stream.Oplock.ExclusiveOpen to NULL. 1971 * EndIf 1972 */ 1973 if ((level & WRITE_CACHING) == 0) { 1974 node->n_oplock.excl_open = NULL; 1975 } 1976 1977 /* 1978 * If RequestedOplockLevel is 0 (that is, no flags): 1979 * * Set Open.Stream.Oplock.State to NO_OPLOCK. 1980 * * The operation returns Status set to STATUS_SUCCESS 1981 * at this point. 1982 */ 1983 if (level == 0) { 1984 node->n_oplock.ol_state = NO_OPLOCK; 1985 status = NT_STATUS_SUCCESS; 1986 goto out; 1987 } 1988 1989 /* 1990 * Deal with possibly still pending breaks. 1991 * Two cases: R to none, RH to R or none. 1992 * 1993 * XXX: These two missing from [MS-FSA] 1994 */ 1995 1996 /* 1997 * Breaking R to none? This is like: 1998 * "If BreakCacheLevel contains READ_CACHING..." 1999 * from smb_oplock_break_cmn. 2000 */ 2001 if (level == CACHE_R && BreakToLevel == LEVEL_NONE) { 2002 smb_oplock_ind_break_in_ack( 2003 sr, ofile, 2004 LEVEL_NONE, B_FALSE); 2005 node->n_oplock.ol_state = NO_OPLOCK; 2006 status = NT_STATUS_SUCCESS; 2007 goto out; 2008 } 2009 2010 /* 2011 * Breaking RH to R or RH to none? This is like: 2012 * "If BreakCacheLevel equals HANDLE_CACHING..." 2013 * from smb_oplock_break_cmn. 2014 */ 2015 if (level == CACHE_RH && 2016 (BreakToLevel == CACHE_R || 2017 BreakToLevel == LEVEL_NONE)) { 2018 smb_oplock_ind_break_in_ack( 2019 sr, ofile, 2020 BreakToLevel, B_TRUE); 2021 2022 ofile->f_oplock.BreakingToRead = 2023 (BreakToLevel & READ_CACHING) ? 1: 0; 2024 2025 ASSERT(!(ofile->f_oplock.onlist_RHBQ)); 2026 ofile->f_oplock.onlist_RHBQ = B_TRUE; 2027 node->n_oplock.cnt_RHBQ++; 2028 2029 RecomputeOplockState(node); 2030 status = NT_STATUS_SUCCESS; 2031 goto out; 2032 } 2033 2034 /* 2035 * Else If RequestedOplockLevel does not contain WRITE_CACHING: 2036 * * The object store MUST request a shared oplock 2037 * according to the algorithm in section 2.1.5.17.2, 2038 * setting the algorithm's parameters as follows: 2039 * * Pass in the current Open. 2040 * * RequestedOplock = RequestedOplockLevel. 2041 * * GrantingInAck = TRUE. 2042 * * The operation MUST at this point return any status 2043 * returned by the shared oplock request algorithm. 2044 */ 2045 if ((level & WRITE_CACHING) == 0) { 2046 *rop = level; 2047 status = smb_oplock_req_shared(ofile, rop, B_TRUE); 2048 goto out; 2049 } 2050 2051 /* 2052 * Note that because this oplock is being set up as part of 2053 * an acknowledgement of an exclusive oplock break, 2054 * Open.Stream.Oplock.ExclusiveOpen was set 2055 * at the time of the original oplock request; 2056 * it contains Open. 2057 * * Set Open.Stream.Oplock.State to 2058 * (RequestedOplockLevel|EXCLUSIVE). 2059 * * This operation MUST be made cancelable... 2060 * * This operation waits until the oplock is broken or 2061 * canceled, as specified in section 2.1.5.17.3. 2062 * 2063 * Implementation notes: 2064 * 2065 * This can only be a break from RWH to RW. 2066 * The assignment of ol_state below means there will be 2067 * no BREAK_TO_... bits set, and therefore no need for 2068 * "waits until the oplock is broken" as described in 2069 * the spec for this bit of code. Therefore, this will 2070 * return SUCCESS instead of OPLOCK_BREAK_IN_PROGRESS. 2071 */ 2072 node->n_oplock.ol_state = level | EXCLUSIVE; 2073 status = NT_STATUS_SUCCESS; 2074 break; /* case (READ_CACHING|WRITE_CACHING|...) */ 2075 2076 default: 2077 /* The operation MUST be failed with Status */ 2078 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL; 2079 break; 2080 2081 } /* Switch (oplock.state) */ 2082 2083 out: 2084 if (status == NT_STATUS_INVALID_OPLOCK_PROTOCOL) 2085 *rop = LEVEL_NONE; 2086 2087 if (status == NT_STATUS_SUCCESS && 2088 type == LEVEL_GRANULAR && 2089 *rop != LEVEL_NONE) { 2090 *rop |= LEVEL_GRANULAR; 2091 /* As above, leased oplock may have moved. */ 2092 #ifndef TESTJIG 2093 if (ofile->f_lease != NULL) 2094 ofile->f_lease->ls_oplock_ofile = ofile; 2095 #endif 2096 } 2097 2098 /* 2099 * If this node no longer has any oplock grants, let's 2100 * go ahead and remove the FEM hooks now. We could leave 2101 * that until close, but this lets access outside of SMB 2102 * be free of FEM oplock work after a "break to none". 2103 */ 2104 if (node->n_oplock.ol_state == NO_OPLOCK && 2105 node->n_oplock.ol_fem == B_TRUE) { 2106 smb_fem_oplock_uninstall(node); 2107 node->n_oplock.ol_fem = B_FALSE; 2108 } 2109 2110 /* 2111 * The spec. describes waiting for a break here, 2112 * but we let the caller do that (when needed) if 2113 * status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS 2114 */ 2115 mutex_exit(&node->n_oplock.ol_mutex); 2116 smb_llist_exit(&node->n_ofile_list); 2117 2118 return (status); 2119 } 2120 2121 /* 2122 * 2.1.4.12 Algorithm to Check for an Oplock Break 2123 * 2124 * The inputs for this algorithm are: 2125 * 2126 * Open: The Open being used in the request calling this algorithm. 2127 * 2128 * Oplock: The Oplock being checked. 2129 * 2130 * Operation: A code describing the operation being processed. 2131 * 2132 * OpParams: Parameters associated with the Operation code that are 2133 * passed in from the calling request. For example, if Operation is 2134 * OPEN, as specified in section 2.1.5.1, then OpParams will have the 2135 * members DesiredAccess and CreateDisposition. Each of these is a 2136 * parameter to the open request as specified in section 2.1.5.1. 2137 * This parameter could be empty, depending on the Operation code. 2138 * 2139 * Flags: An optional parameter. If unspecified it is considered to 2140 * contain 0. Valid nonzero values are: 2141 * PARENT_OBJECT 2142 * 2143 * The algorithm uses the following local variables: 2144 * 2145 * Boolean values (initialized to FALSE): 2146 * BreakToTwo, BreakToNone, NeedToWait 2147 * 2148 * BreakCacheLevel – MAY contain 0 or a combination of one or more of 2149 * READ_CACHING, WRITE_CACHING, or HANDLE_CACHING, as specified in 2150 * section 2.1.1.10. Initialized to 0. 2151 * Note that there are only four legal nonzero combinations of flags 2152 * for BreakCacheLevel: 2153 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING) 2154 * (READ_CACHING|WRITE_CACHING) 2155 * WRITE_CACHING 2156 * HANDLE_CACHING 2157 * 2158 * Algorithm: (all) 2159 * If Oplock is not empty and Oplock.State is not NO_OPLOCK: 2160 * If Flags contains PARENT_OBJECT: 2161 * If Operation is OPEN, CLOSE, FLUSH_DATA, 2162 * FS_CONTROL(set_encryption) or 2163 * SET_INFORMATION(Basic, Allocation, EoF, 2164 * Rename, Link, Shortname, VDL): 2165 * Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING). 2166 * EndIf 2167 * Else // Normal operation (not PARENT_OBJECT) 2168 * Switch (Operation): 2169 * Case OPEN, CLOSE, ... 2170 * EndSwitch 2171 * EndIf // not parent 2172 * // Common section for all above 2173 * If BreakToTwo is TRUE: 2174 * ... 2175 * Else If BreakToNone 2176 * ... 2177 * EndIf 2178 * ... 2179 * EndIf 2180 * 2181 * This implementation uses separate functions for each of: 2182 * if (flags & PARENT)... else 2183 * switch (Operation)... 2184 */ 2185 2186 2187 /* 2188 * If Flags contains PARENT_OBJECT: 2189 * ... 2190 * Note that this function is unusual in that the node arg is 2191 * the PARENT directory node, and ofile is NOT on the ofile list 2192 * of that directory but one of the nodes under it. 2193 * 2194 * Note that until we implement directory leases, this is a no-op. 2195 */ 2196 uint32_t 2197 smb_oplock_break_PARENT(smb_node_t *node, smb_ofile_t *ofile) 2198 { 2199 uint32_t BreakCacheLevel; 2200 2201 /* 2202 * If Operation is OPEN, CLOSE, FLUSH_DATA, 2203 * FS_CONTROL(set_encryption) or 2204 * SET_INFORMATION(Basic, Allocation, EoF, 2205 * Rename, Link, Shortname, VDL): 2206 * Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING). 2207 * EndIf 2208 */ 2209 BreakCacheLevel = PARENT_OBJECT | 2210 (READ_CACHING|WRITE_CACHING); 2211 2212 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel)); 2213 } 2214 2215 /* 2216 * Helper for the cases where section 2.1.5.1 says: 2217 * 2218 * If Open.Stream.Oplock is not empty and Open.Stream.Oplock.State 2219 * contains BATCH_OPLOCK, the object store MUST check for an oplock 2220 * break according to the algorithm in section 2.1.4.12, 2221 * with input values as follows: 2222 * Open equal to this operation's Open 2223 * Oplock equal to Open.Stream.Oplock 2224 * Operation equal to "OPEN" 2225 * OpParams containing two members: 2226 * (DesiredAccess, CreateDisposition) 2227 * 2228 * So basically, just call smb_oplock_break_OPEN(), but 2229 * only if there's a batch oplock. 2230 */ 2231 uint32_t 2232 smb_oplock_break_BATCH(smb_node_t *node, smb_ofile_t *ofile, 2233 uint32_t DesiredAccess, uint32_t CreateDisposition) 2234 { 2235 if ((node->n_oplock.ol_state & BATCH_OPLOCK) == 0) 2236 return (0); 2237 2238 return (smb_oplock_break_OPEN(node, ofile, 2239 DesiredAccess, CreateDisposition)); 2240 } 2241 2242 /* 2243 * Case OPEN, as specified in section 2.1.5.1: 2244 * 2245 * Note: smb_ofile_open constructs a partially complete smb_ofile_t 2246 * for this call, which can be considerd a "proposed open". This 2247 * open may or may not turn into a usable open depending on what 2248 * happens in the remainder of the ofile_open code path. 2249 */ 2250 uint32_t 2251 smb_oplock_break_OPEN(smb_node_t *node, smb_ofile_t *ofile, 2252 uint32_t DesiredAccess, uint32_t CreateDisposition) 2253 { 2254 uint32_t BreakCacheLevel = 0; 2255 /* BreakToTwo, BreakToNone, NeedToWait */ 2256 2257 /* 2258 * If OpParams.DesiredAccess contains no flags other than 2259 * FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES, or SYNCHRONIZE, 2260 * the algorithm returns at this point. 2261 * EndIf 2262 */ 2263 if ((DesiredAccess & ~(FILE_READ_ATTRIBUTES | 2264 FILE_WRITE_ATTRIBUTES | SYNCHRONIZE | READ_CONTROL)) == 0) 2265 return (0); 2266 2267 /* 2268 * If OpParams.CreateDisposition is FILE_SUPERSEDE, 2269 * FILE_OVERWRITE, or FILE_OVERWRITE_IF: 2270 * Set BreakToNone to TRUE, set BreakCacheLevel to 2271 * (READ_CACHING|WRITE_CACHING). 2272 * Else 2273 * Set BreakToTwo to TRUE, 2274 * set BreakCacheLevel to WRITE_CACHING. 2275 * EndIf 2276 */ 2277 if (CreateDisposition == FILE_SUPERSEDE || 2278 CreateDisposition == FILE_OVERWRITE || 2279 CreateDisposition == FILE_OVERWRITE_IF) { 2280 BreakCacheLevel = BREAK_TO_NONE | 2281 (READ_CACHING|WRITE_CACHING); 2282 } else { 2283 /* 2284 * CreateDispositons: OPEN, OPEN_IF 2285 */ 2286 BreakCacheLevel = BREAK_TO_TWO | 2287 WRITE_CACHING; 2288 } 2289 2290 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel)); 2291 } 2292 2293 /* 2294 * Case OPEN_BREAK_H, as specified in section 2.1.5.1: 2295 * Set BreakCacheLevel to HANDLE_CACHING. 2296 * EndCase 2297 */ 2298 uint32_t 2299 smb_oplock_break_HANDLE(smb_node_t *node, smb_ofile_t *ofile) 2300 { 2301 uint32_t BreakCacheLevel = HANDLE_CACHING; 2302 2303 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel)); 2304 } 2305 2306 /* 2307 * Case CLOSE, as specified in section 2.1.5.4: 2308 * 2309 * The MS-FSA spec. describes sending oplock break indications 2310 * (smb_oplock_ind_break ... NT_STATUS_OPLOCK_HANDLE_CLOSED) 2311 * for several cases where the ofile we're closing has some 2312 * oplock grants. We modify these slightly and use them to 2313 * clear out the SMB-level oplock state. We could probably 2314 * just skip most of these, as the caller knows this handle is 2315 * closing and could just discard the SMB-level oplock state. 2316 * For now, keeping this close to what the spec says. 2317 */ 2318 void 2319 smb_oplock_break_CLOSE(smb_node_t *node, smb_ofile_t *ofile) 2320 { 2321 smb_ofile_t *o; 2322 2323 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock)); 2324 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex)); 2325 2326 /* 2327 * If Oplock.IIOplocks is not empty: 2328 * For each Open ThisOpen in Oplock.IIOplocks: 2329 * If ThisOpen == Open: 2330 * Remove ThisOpen from Oplock.IIOplocks. 2331 * Notify the server of an oplock break according to 2332 * the algorithm in section 2.1.5.17.3, setting the 2333 * algorithm's parameters as follows: 2334 * BreakingOplockOpen = ThisOpen. 2335 * NewOplockLevel = LEVEL_NONE. 2336 * AcknowledgeRequired = FALSE. 2337 * OplockCompletionStatus = STATUS_SUCCESS. 2338 * (The operation does not end at this point; 2339 * this call to 2.1.5.17.3 completes some 2340 * earlier call to 2.1.5.17.2.) 2341 * EndIf 2342 * EndFor 2343 * Recompute Oplock.State according to the algorithm in 2344 * section 2.1.4.13, passing Oplock as the ThisOplock parameter. 2345 * EndIf 2346 */ 2347 if (node->n_oplock.cnt_II > 0) { 2348 o = ofile; /* No need for list walk */ 2349 if (o->f_oplock.onlist_II) { 2350 o->f_oplock.onlist_II = B_FALSE; 2351 node->n_oplock.cnt_II--; 2352 ASSERT(node->n_oplock.cnt_II >= 0); 2353 /* 2354 * The spec. says to do: 2355 * smb_oplock_ind_break(o, 2356 * LEVEL_NONE, B_FALSE, 2357 * NT_STATUS_SUCCESS); 2358 * 2359 * We'll use STATUS_OPLOCK_HANDLE_CLOSED 2360 * like all the other ind_break calls in 2361 * this function, so the SMB-level will 2362 * just clear out its oplock state. 2363 */ 2364 smb_oplock_ind_break(o, 2365 LEVEL_NONE, B_FALSE, 2366 NT_STATUS_OPLOCK_HANDLE_CLOSED); 2367 } 2368 RecomputeOplockState(node); 2369 } 2370 2371 /* 2372 * If Oplock.ROplocks is not empty: 2373 * For each Open ThisOpen in Oplock.ROplocks: 2374 * If ThisOpen == Open: 2375 * Remove ThisOpen from Oplock.ROplocks. 2376 * Notify the server of an oplock break according to 2377 * the algorithm in section 2.1.5.17.3, setting the 2378 * algorithm's parameters as follows: 2379 * BreakingOplockOpen = ThisOpen. 2380 * NewOplockLevel = LEVEL_NONE. 2381 * AcknowledgeRequired = FALSE. 2382 * OplockCompletionStatus = 2383 * STATUS_OPLOCK_HANDLE_CLOSED. 2384 * (The operation does not end at this point; 2385 * this call to 2.1.5.17.3 completes some 2386 * earlier call to 2.1.5.17.2.) 2387 * EndIf 2388 * EndFor 2389 * Recompute Oplock.State according to the algorithm in 2390 * section 2.1.4.13, passing Oplock as the ThisOplock parameter. 2391 * EndIf 2392 */ 2393 if (node->n_oplock.cnt_R > 0) { 2394 o = ofile; /* No need for list walk */ 2395 if (o->f_oplock.onlist_R) { 2396 o->f_oplock.onlist_R = B_FALSE; 2397 node->n_oplock.cnt_R--; 2398 ASSERT(node->n_oplock.cnt_R >= 0); 2399 2400 smb_oplock_ind_break(o, 2401 LEVEL_NONE, B_FALSE, 2402 NT_STATUS_OPLOCK_HANDLE_CLOSED); 2403 } 2404 RecomputeOplockState(node); 2405 } 2406 2407 /* 2408 * If Oplock.RHOplocks is not empty: 2409 * For each Open ThisOpen in Oplock.RHOplocks: 2410 * If ThisOpen == Open: 2411 * Remove ThisOpen from Oplock.RHOplocks. 2412 * Notify the server of an oplock break according to 2413 * the algorithm in section 2.1.5.17.3, setting the 2414 * algorithm's parameters as follows: 2415 * BreakingOplockOpen = ThisOpen. 2416 * NewOplockLevel = LEVEL_NONE. 2417 * AcknowledgeRequired = FALSE. 2418 * OplockCompletionStatus = 2419 * STATUS_OPLOCK_HANDLE_CLOSED. 2420 * (The operation does not end at this point; 2421 * this call to 2.1.5.17.3 completes some 2422 * earlier call to 2.1.5.17.2.) 2423 * EndIf 2424 * EndFor 2425 * Recompute Oplock.State according to the algorithm in 2426 * section 2.1.4.13, passing Oplock as the ThisOplock parameter. 2427 * EndIf 2428 */ 2429 if (node->n_oplock.cnt_RH > 0) { 2430 o = ofile; /* No need for list walk */ 2431 if (o->f_oplock.onlist_RH) { 2432 o->f_oplock.onlist_RH = B_FALSE; 2433 node->n_oplock.cnt_RH--; 2434 ASSERT(node->n_oplock.cnt_RH >= 0); 2435 2436 smb_oplock_ind_break(o, 2437 LEVEL_NONE, B_FALSE, 2438 NT_STATUS_OPLOCK_HANDLE_CLOSED); 2439 } 2440 RecomputeOplockState(node); 2441 } 2442 2443 /* 2444 * If Oplock.RHBreakQueue is not empty: 2445 * For each RHOpContext ThisContext in Oplock.RHBreakQueue: 2446 * If ThisContext.Open == Open: 2447 * Remove ThisContext from Oplock.RHBreakQueue. 2448 * EndIf 2449 * EndFor 2450 * Recompute Oplock.State according to the algorithm in 2451 * section 2.1.4.13, passing Oplock as the ThisOplock parameter. 2452 * For each Open WaitingOpen on Oplock.WaitList: 2453 * If Oplock.RHBreakQueue is empty: 2454 * (or) If the value of every 2455 * RHOpContext.Open.TargetOplockKey 2456 * on Oplock.RHBreakQueue is equal to 2457 * WaitingOpen .TargetOplockKey: 2458 * Indicate that the op. assoc. with 2459 * WaitingOpen can continue according to 2460 * the algorithm in section 2.1.4.12.1, 2461 * setting OpenToRelease = WaitingOpen. 2462 * Remove WaitingOpen from Oplock.WaitList. 2463 * EndIf 2464 * EndFor 2465 * EndIf 2466 */ 2467 if (node->n_oplock.cnt_RHBQ > 0) { 2468 o = ofile; /* No need for list walk */ 2469 if (o->f_oplock.onlist_RHBQ) { 2470 o->f_oplock.onlist_RHBQ = B_FALSE; 2471 node->n_oplock.cnt_RHBQ--; 2472 ASSERT(node->n_oplock.cnt_RHBQ >= 0); 2473 } 2474 RecomputeOplockState(node); 2475 /* 2476 * We don't keep a WaitingOpen list, so just 2477 * wake them all and let them look at the 2478 * updated Oplock.RHBreakQueue 2479 */ 2480 cv_broadcast(&node->n_oplock.WaitingOpenCV); 2481 } 2482 2483 /* 2484 * If Open equals Open.Oplock.ExclusiveOpen 2485 * If Oplock.State contains none of (BREAK_ANY): 2486 * Notify the server of an oplock break according to 2487 * the algorithm in section 2.1.5.17.3, setting the 2488 * algorithm's parameters as follows: 2489 * BreakingOplockOpen = Oplock.ExclusiveOpen. 2490 * NewOplockLevel = LEVEL_NONE. 2491 * AcknowledgeRequired = FALSE. 2492 * OplockCompletionStatus equal to: 2493 * STATUS_OPLOCK_HANDLE_CLOSED if 2494 * Oplock.State contains any of 2495 * READ_CACHING, WRITE_CACHING, or 2496 * HANDLE_CACHING. 2497 * STATUS_SUCCESS otherwise. 2498 * (The operation does not end at this point; 2499 * this call to 2.1.5.17.3 completes some 2500 * earlier call to 2.1.5.17.1.) 2501 * EndIf 2502 * Set Oplock.ExclusiveOpen to NULL. 2503 * Set Oplock.State to NO_OPLOCK. 2504 * For each Open WaitingOpen on Oplock.WaitList: 2505 * Indicate that the operation associated with WaitingOpen 2506 * can continue according to the algorithm in section 2507 * 2.1.4.12.1, setting OpenToRelease = WaitingOpen. 2508 * Remove WaitingOpen from Oplock.WaitList. 2509 * EndFor 2510 * EndIf 2511 * 2512 * Modify this slightly from what the spec. says and only 2513 * up-call the break with status STATUS_OPLOCK_HANDLE_CLOSED. 2514 * The STATUS_SUCCESS case would do nothing at the SMB level, 2515 * so we'll just skip that part. 2516 */ 2517 if (ofile == node->n_oplock.excl_open) { 2518 uint32_t level = node->n_oplock.ol_state & CACHE_RWH; 2519 if (level != 0 && 2520 (node->n_oplock.ol_state & BREAK_ANY) == 0) { 2521 smb_oplock_ind_break(ofile, 2522 LEVEL_NONE, B_FALSE, 2523 NT_STATUS_OPLOCK_HANDLE_CLOSED); 2524 } 2525 node->n_oplock.excl_open = NULL; 2526 node->n_oplock.ol_state = NO_OPLOCK; 2527 cv_broadcast(&node->n_oplock.WaitingOpenCV); 2528 } 2529 2530 /* 2531 * The CLOSE sub-case of 2.1.5.4 (separate function here) 2532 * happens to always leave BreakCacheLevel=0 (see 2.1.5.4) 2533 * so there's never a need to call smb_oplock_break_cmn() 2534 * in this function. If that changed and we were to have 2535 * BreakCacheLevel != 0 here, then we'd need to call: 2536 * smb_oplock_break_cmn(node, ofile, BreakCacheLevel); 2537 */ 2538 2539 if ((node->n_oplock.ol_state & BREAK_ANY) == 0) 2540 cv_broadcast(&node->n_oplock.WaitingOpenCV); 2541 2542 /* 2543 * If no longer any oplock, remove FEM hooks. 2544 */ 2545 if (node->n_oplock.ol_state == NO_OPLOCK && 2546 node->n_oplock.ol_fem == B_TRUE) { 2547 smb_fem_oplock_uninstall(node); 2548 node->n_oplock.ol_fem = B_FALSE; 2549 } 2550 } 2551 2552 /* 2553 * Case READ, as specified in section 2.1.5.2: 2554 * Set BreakToTwo to TRUE 2555 * Set BreakCacheLevel to WRITE_CACHING. 2556 * EndCase 2557 */ 2558 uint32_t 2559 smb_oplock_break_READ(smb_node_t *node, smb_ofile_t *ofile) 2560 { 2561 uint32_t BreakCacheLevel = BREAK_TO_TWO | WRITE_CACHING; 2562 2563 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel)); 2564 } 2565 2566 /* 2567 * Case FLUSH_DATA, as specified in section 2.1.5.6: 2568 * Set BreakToTwo to TRUE 2569 * Set BreakCacheLevel to WRITE_CACHING. 2570 * EndCase 2571 * Callers just use smb_oplock_break_READ() -- same thing. 2572 */ 2573 2574 /* 2575 * Case LOCK_CONTROL, as specified in section 2.1.5.7: 2576 * Note: Spec does fall-through to WRITE here. 2577 * 2578 * Case WRITE, as specified in section 2.1.5.3: 2579 * Set BreakToNone to TRUE 2580 * Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING). 2581 * EndCase 2582 */ 2583 uint32_t 2584 smb_oplock_break_WRITE(smb_node_t *node, smb_ofile_t *ofile) 2585 { 2586 uint32_t BreakCacheLevel = BREAK_TO_NONE | 2587 (READ_CACHING|WRITE_CACHING); 2588 2589 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel)); 2590 } 2591 2592 /* 2593 * Case SET_INFORMATION, as specified in section 2.1.5.14: 2594 * Switch (OpParams.FileInformationClass): 2595 * Case FileEndOfFileInformation: 2596 * Case FileAllocationInformation: 2597 * Set BreakToNone to TRUE 2598 * Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING). 2599 * EndCase 2600 * Case FileRenameInformation: 2601 * Case FileLinkInformation: 2602 * Case FileShortNameInformation: 2603 * Set BreakCacheLevel to HANDLE_CACHING. 2604 * If Oplock.State contains BATCH_OPLOCK, 2605 * set BreakToNone to TRUE. 2606 * EndCase 2607 * Case FileDispositionInformation: 2608 * If OpParams.DeleteFile is TRUE, 2609 * Set BreakCacheLevel to HANDLE_CACHING. 2610 * EndCase 2611 * EndSwitch 2612 */ 2613 uint32_t 2614 smb_oplock_break_SETINFO(smb_node_t *node, smb_ofile_t *ofile, 2615 uint32_t InfoClass) 2616 { 2617 uint32_t BreakCacheLevel = 0; 2618 2619 switch (InfoClass) { 2620 case FileEndOfFileInformation: 2621 case FileAllocationInformation: 2622 BreakCacheLevel = BREAK_TO_NONE | 2623 (READ_CACHING|WRITE_CACHING); 2624 break; 2625 2626 case FileRenameInformation: 2627 case FileLinkInformation: 2628 case FileShortNameInformation: 2629 BreakCacheLevel = HANDLE_CACHING; 2630 if (node->n_oplock.ol_state & BATCH_OPLOCK) { 2631 BreakCacheLevel |= BREAK_TO_NONE; 2632 } 2633 break; 2634 case FileDispositionInformation: 2635 /* Only called if (OpParams.DeleteFile is TRUE) */ 2636 BreakCacheLevel = HANDLE_CACHING; 2637 break; 2638 2639 } 2640 2641 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel)); 2642 } 2643 2644 /* 2645 * This one is not from the spec. It appears that Windows will 2646 * open a handle for an SMB1 delete call (at least internally). 2647 * We don't open a handle for delete, but do want to break as if 2648 * we had done, so this breaks like a combination of: 2649 * break_BATCH(... DELETE, FILE_OPEN_IF) 2650 * break_HANDLE(...) 2651 */ 2652 uint32_t 2653 smb_oplock_break_DELETE(smb_node_t *node, smb_ofile_t *ofile) 2654 { 2655 uint32_t BreakCacheLevel = HANDLE_CACHING; 2656 2657 if ((node->n_oplock.ol_state & BATCH_OPLOCK) != 0) 2658 BreakCacheLevel |= BREAK_TO_TWO; 2659 2660 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel)); 2661 } 2662 2663 /* 2664 * Case FS_CONTROL, as specified in section 2.1.5.9: 2665 * If OpParams.ControlCode is FSCTL_SET_ZERO_DATA: 2666 * Set BreakToNone to TRUE. 2667 * Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING). 2668 * EndIf 2669 * EndCase 2670 * Callers just use smb_oplock_break_WRITE() -- same thing. 2671 */ 2672 2673 /* 2674 * Common section for all cases above 2675 * Note: When called via FEM: ofile == NULL 2676 */ 2677 static uint32_t 2678 smb_oplock_break_cmn(smb_node_t *node, 2679 smb_ofile_t *ofile, uint32_t BreakCacheLevel) 2680 { 2681 smb_oplock_t *nol = &node->n_oplock; 2682 uint32_t CmpFlags, status; 2683 boolean_t BreakToTwo, BreakToNone, NeedToWait; 2684 smb_ofile_t *o = NULL; 2685 2686 CmpFlags = (BreakCacheLevel & PARENT_OBJECT); 2687 BreakToTwo = (BreakCacheLevel & BREAK_TO_TWO) != 0; 2688 BreakToNone = (BreakCacheLevel & BREAK_TO_NONE) != 0; 2689 BreakCacheLevel &= (READ_CACHING | WRITE_CACHING | HANDLE_CACHING); 2690 NeedToWait = B_FALSE; 2691 status = NT_STATUS_SUCCESS; 2692 2693 smb_llist_enter(&node->n_ofile_list, RW_READER); 2694 mutex_enter(&node->n_oplock.ol_mutex); 2695 2696 if (node->n_oplock.ol_state == 0 || 2697 node->n_oplock.ol_state == NO_OPLOCK) 2698 goto out; 2699 2700 if (BreakToTwo) { 2701 /* 2702 * If (Oplock.State != LEVEL_TWO_OPLOCK) and 2703 * ((Oplock.ExclusiveOpen is empty) or 2704 * (Oplock.ExclusiveOpen.TargetOplockKey != 2705 * Open.TargetOplockKey)): 2706 */ 2707 if ((nol->ol_state != LEVEL_TWO_OPLOCK) && 2708 (((o = nol->excl_open) == NULL) || 2709 !CompareOplockKeys(ofile, o, CmpFlags))) { 2710 2711 /* 2712 * If (Oplock.State contains EXCLUSIVE) and 2713 * (Oplock.State contains none of READ_CACHING, 2714 * WRITE_CACHING, or HANDLE_CACHING): 2715 */ 2716 if ((nol->ol_state & EXCLUSIVE) != 0 && 2717 (nol->ol_state & CACHE_RWH) == 0) { 2718 /* 2719 * If Oplock.State contains none of: 2720 * BREAK_TO_NONE, 2721 * BREAK_TO_TWO, 2722 * BREAK_TO_TWO_TO_NONE, 2723 * BREAK_TO_READ_CACHING, 2724 * BREAK_TO_WRITE_CACHING, 2725 * BREAK_TO_HANDLE_CACHING, 2726 * BREAK_TO_NO_CACHING: 2727 */ 2728 if ((nol->ol_state & BREAK_ANY) == 0) { 2729 2730 /* 2731 * Oplock.State MUST contain either 2732 * LEVEL_ONE_OPLOCK or BATCH_OPLOCK. 2733 * Set BREAK_TO_TWO in Oplock.State. 2734 */ 2735 ASSERT((nol->ol_state & 2736 (LEVEL_ONE | LEVEL_BATCH)) != 0); 2737 nol->ol_state |= BREAK_TO_TWO; 2738 2739 /* 2740 * Notify the server of an oplock break 2741 * according to the algorithm in section 2742 * 2.1.5.17.3, setting the algorithm's 2743 * parameters as follows: 2744 * BreakingOplockOpen = 2745 * Oplock.ExclusiveOpen. 2746 * NewOplockLevel = LEVEL_TWO. 2747 * AcknowledgeRequired = TRUE. 2748 * Compl_Status = STATUS_SUCCESS. 2749 * (The operation does not end at this 2750 * point; this call to 2.1.5.17.3 2751 * completes some earlier call to 2752 * 2.1.5.17.1.) 2753 */ 2754 smb_oplock_ind_break(o, 2755 LEVEL_TWO, B_TRUE, 2756 NT_STATUS_SUCCESS); 2757 } 2758 2759 /* 2760 * The operation that called this algorithm 2761 * MUST be made cancelable by ... 2762 * The operation that called this algorithm 2763 * waits until the oplock break is 2764 * acknowledged, as specified in section 2765 * 2.1.5.18, or the operation is canceled. 2766 */ 2767 status = NT_STATUS_OPLOCK_BREAK_IN_PROGRESS; 2768 /* Caller does smb_oplock_wait_break() */ 2769 } 2770 } 2771 } else if (BreakToNone) { 2772 /* 2773 * If (Oplock.State == LEVEL_TWO_OPLOCK) or 2774 * (Oplock.ExclusiveOpen is empty) or 2775 * (Oplock.ExclusiveOpen.TargetOplockKey != 2776 * Open.TargetOplockKey): 2777 */ 2778 if (nol->ol_state == LEVEL_TWO_OPLOCK || 2779 (((o = nol->excl_open) == NULL) || 2780 !CompareOplockKeys(ofile, o, CmpFlags))) { 2781 2782 /* 2783 * If (Oplock.State != NO_OPLOCK) and 2784 * (Oplock.State contains neither 2785 * WRITE_CACHING nor HANDLE_CACHING): 2786 */ 2787 if (nol->ol_state != NO_OPLOCK && 2788 (nol->ol_state & 2789 (WRITE_CACHING | HANDLE_CACHING)) == 0) { 2790 2791 /* 2792 * If Oplock.State contains none of: 2793 * LEVEL_TWO_OPLOCK, 2794 * BREAK_TO_NONE, 2795 * BREAK_TO_TWO, 2796 * BREAK_TO_TWO_TO_NONE, 2797 * BREAK_TO_READ_CACHING, 2798 * BREAK_TO_WRITE_CACHING, 2799 * BREAK_TO_HANDLE_CACHING, or 2800 * BREAK_TO_NO_CACHING: 2801 */ 2802 if ((nol->ol_state & 2803 (LEVEL_TWO_OPLOCK | BREAK_ANY)) == 0) { 2804 2805 /* 2806 * There could be a READ_CACHING-only 2807 * oplock here. Those are broken later. 2808 * 2809 * If Oplock.State contains READ_CACHING 2810 * go to the LeaveBreakToNone label. 2811 * Set BREAK_TO_NONE in Oplock.State. 2812 */ 2813 if ((nol->ol_state & READ_CACHING) != 0) 2814 goto LeaveBreakToNone; 2815 nol->ol_state |= BREAK_TO_NONE; 2816 2817 /* 2818 * Notify the server of an oplock break 2819 * according to the algorithm in section 2820 * 2.1.5.17.3, setting the algorithm's 2821 * parameters as follows: 2822 * BreakingOplockOpen = 2823 * Oplock.ExclusiveOpen. 2824 * NewOplockLevel = LEVEL_NONE. 2825 * AcknowledgeRequired = TRUE. 2826 * Commpl_Status = STATUS_SUCCESS. 2827 * (The operation does not end at this 2828 * point; this call to 2.1.5.17.3 2829 * completes some earlier call to 2830 * 2.1.5.17.1.) 2831 */ 2832 smb_oplock_ind_break(o, 2833 LEVEL_NONE, B_TRUE, 2834 NT_STATUS_SUCCESS); 2835 } 2836 2837 /* 2838 * Else If Oplock.State equals LEVEL_TWO_OPLOCK 2839 * or (LEVEL_TWO_OPLOCK|READ_CACHING): 2840 */ 2841 else if (nol->ol_state == LEVEL_TWO || 2842 nol->ol_state == (LEVEL_TWO|READ_CACHING)) { 2843 2844 /* 2845 * For each Open O in Oplock.IIOplocks: 2846 * Remove O from Oplock.IIOplocks. 2847 * Notify the server of an oplock 2848 * break according to the algorithm 2849 * in section 2.1.5.17.3, setting the 2850 * algorithm's parameters as follows: 2851 * BreakingOplockOpen = ThisOpen. 2852 * NewOplockLevel = LEVEL_NONE. 2853 * AcknowledgeRequired = FALSE. 2854 * Compl_Status = STATUS_SUCCESS. 2855 * (The operation does not end at 2856 * this point; this call to 2857 * 2.1.5.17.3 completes some 2858 * earlier call to 2.1.5.17.2.) 2859 * EndFor 2860 */ 2861 FOREACH_NODE_OFILE(node, o) { 2862 if (o->f_oplock.onlist_II == 0) 2863 continue; 2864 o->f_oplock.onlist_II = B_FALSE; 2865 nol->cnt_II--; 2866 ASSERT(nol->cnt_II >= 0); 2867 2868 smb_oplock_ind_break(o, 2869 LEVEL_NONE, B_FALSE, 2870 NT_STATUS_SUCCESS); 2871 } 2872 /* 2873 * If Oplock.State equals 2874 * (LEVEL_TWO_OPLOCK|READ_CACHING): 2875 * Set Oplock.State = READ_CACHING. 2876 * Else 2877 * Set Oplock.State = NO_OPLOCK. 2878 * EndIf 2879 * Go to the LeaveBreakToNone label. 2880 */ 2881 if (nol->ol_state == 2882 (LEVEL_TWO_OPLOCK | READ_CACHING)) { 2883 nol->ol_state = READ_CACHING; 2884 } else { 2885 nol->ol_state = NO_OPLOCK; 2886 } 2887 goto LeaveBreakToNone; 2888 } 2889 2890 /* 2891 * Else If Oplock.State contains BREAK_TO_TWO: 2892 * Clear BREAK_TO_TWO from Oplock.State. 2893 * Set BREAK_TO_TWO_TO_NONE in Oplock.State 2894 * EndIf 2895 */ 2896 else if (nol->ol_state & BREAK_TO_TWO) { 2897 nol->ol_state &= ~BREAK_TO_TWO; 2898 nol->ol_state |= BREAK_TO_TWO_TO_NONE; 2899 } 2900 2901 /* 2902 * If Oplock.ExclusiveOpen is not empty, 2903 * and Oplock.Excl_Open.TargetOplockKey 2904 * equals Open.TargetOplockKey, 2905 * go to the LeaveBreakToNone label. 2906 */ 2907 if (o != NULL && 2908 CompareOplockKeys(ofile, o, CmpFlags)) 2909 goto LeaveBreakToNone; 2910 2911 /* 2912 * The operation that called this algorithm 2913 * MUST be made cancelable by ... 2914 * The operation that called this algorithm 2915 * waits until the opl. break is acknowledged, 2916 * as specified in section 2.1.5.18, or the 2917 * operation is canceled. 2918 */ 2919 status = NT_STATUS_OPLOCK_BREAK_IN_PROGRESS; 2920 /* Caller does smb_oplock_wait_break() */ 2921 } 2922 } 2923 } 2924 2925 LeaveBreakToNone: 2926 2927 /* 2928 * if (BreakCacheLevel != 0) and (pp 37) 2929 * If Oplock.State contains any flags that are in BreakCacheLevel: 2930 * (Body of that "If" was here to just above the out label.) 2931 */ 2932 if ((nol->ol_state & BreakCacheLevel) == 0) 2933 goto out; 2934 2935 /* 2936 * If Oplock.ExclusiveOpen is not empty, call the 2937 * algorithm in section 2.1.4.12.2, passing 2938 * Open as the OperationOpen parameter, 2939 * Oplock.ExclusiveOpen as the OplockOpen parameter, 2940 * and Flags as the Flagsparameter. 2941 * If the algorithm returns TRUE: 2942 * The algorithm returns at this point. 2943 */ 2944 if ((o = nol->excl_open) != NULL && 2945 CompareOplockKeys(ofile, o, CmpFlags) == B_TRUE) { 2946 status = NT_STATUS_SUCCESS; 2947 goto out; 2948 } 2949 2950 /* 2951 * Switch (Oplock.State): 2952 */ 2953 switch (nol->ol_state) { 2954 2955 case (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH): 2956 case READ_CACHING: 2957 case (LEVEL_TWO_OPLOCK|READ_CACHING): 2958 /* 2959 * If BreakCacheLevel contains READ_CACHING: 2960 */ 2961 if ((BreakCacheLevel & READ_CACHING) != 0) { 2962 /* 2963 * For each Open ThisOpen in Oplock.ROplocks: 2964 * Call the algorithm in section 2.1.4.12.2, pass: 2965 * Open as the OperationOpen parameter, 2966 * ThisOpen as the OplockOpen parameter, 2967 * and Flags as the Flagsparameter. 2968 * If the algorithm returns FALSE: 2969 * Remove ThisOpen from Oplock.ROplocks. 2970 * Notify the server of an oplock break 2971 * according to the algorithm in 2972 * section 2.1.5.17.3, setting the 2973 * algorithm's parameters as follows: 2974 * BreakingOplockOpen = ThisOpen. 2975 * NewOplockLevel = LEVEL_NONE. 2976 * AcknowledgeRequired = FALSE. 2977 * Compl_Status = STATUS_SUCCESS. 2978 * (The operation does not end at this point; 2979 * this call to 2.1.5.17.3 completes some 2980 * earlier call to 2.1.5.17.2.) 2981 * EndIf 2982 * EndFor 2983 */ 2984 FOREACH_NODE_OFILE(node, o) { 2985 if (o->f_oplock.onlist_R == 0) 2986 continue; 2987 if (!CompareOplockKeys(ofile, o, CmpFlags)) { 2988 o->f_oplock.onlist_R = B_FALSE; 2989 nol->cnt_R--; 2990 ASSERT(nol->cnt_R >= 0); 2991 2992 smb_oplock_ind_break(o, 2993 LEVEL_NONE, B_FALSE, 2994 NT_STATUS_SUCCESS); 2995 } 2996 } 2997 } 2998 /* 2999 * If Oplock.State equals 3000 * (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH): 3001 * // Do nothing; FALL THROUGH to next Case statement. 3002 * Else 3003 * Recompute Oplock.State according to the 3004 * algorithm in section 2.1.4.13, passing 3005 * Oplock as the ThisOplock parameter. 3006 * EndIf 3007 */ 3008 if (nol->ol_state == 3009 (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH)) 3010 goto case_cache_rh; 3011 3012 RecomputeOplockState(node); 3013 break; 3014 /* EndCase XXX Note: spec. swapped this with prev. Endif. */ 3015 3016 case_cache_rh: 3017 case (READ_CACHING|HANDLE_CACHING): 3018 3019 /* 3020 * If BreakCacheLevel equals HANDLE_CACHING: 3021 */ 3022 if (BreakCacheLevel == HANDLE_CACHING) { 3023 3024 /* 3025 * For each Open ThisOpen in Oplock.RHOplocks: 3026 * If ThisOpen.OplockKey != Open.OplockKey: 3027 */ 3028 FOREACH_NODE_OFILE(node, o) { 3029 if (o->f_oplock.onlist_RH == 0) 3030 continue; 3031 if (!CompareOplockKeys(ofile, o, CmpFlags)) { 3032 3033 /* 3034 * Remove ThisOpen from 3035 * Oplock.RHOplocks. 3036 */ 3037 o->f_oplock.onlist_RH = B_FALSE; 3038 nol->cnt_RH--; 3039 ASSERT(nol->cnt_RH >= 0); 3040 3041 /* 3042 * Notify the server of an oplock break 3043 * according to the algorithm in 3044 * section 2.1.5.17.3, setting the 3045 * algorithm's parameters as follows: 3046 * BreakingOplockOpen = ThisOpen. 3047 * NewOplockLevel = READ_CACHING. 3048 * AcknowledgeRequired = TRUE. 3049 * Compl_Status = STATUS_SUCCESS. 3050 * (The operation does not end at this 3051 * point; this call to 2.1.5.17.3 3052 * completes some earlier call to 3053 * 2.1.5.17.2.) 3054 */ 3055 smb_oplock_ind_break(o, 3056 READ_CACHING, B_TRUE, 3057 NT_STATUS_SUCCESS); 3058 3059 /* 3060 * Initialize a new RHOpContext object, 3061 * setting its fields as follows: 3062 * RHOpCtx.Open = ThisOpen. 3063 * RHOpCtx.BreakingToRead = TRUE. 3064 * Add the new RHOpContext object to 3065 * Oplock.RHBreakQueue. 3066 * Set NeedToWait to TRUE. 3067 */ 3068 o->f_oplock.BreakingToRead = B_TRUE; 3069 ASSERT(!(o->f_oplock.onlist_RHBQ)); 3070 o->f_oplock.onlist_RHBQ = B_TRUE; 3071 nol->cnt_RHBQ++; 3072 3073 NeedToWait = B_TRUE; 3074 } 3075 } 3076 } 3077 3078 /* 3079 * Else If BreakCacheLevel contains both 3080 * READ_CACHING and WRITE_CACHING: 3081 */ 3082 else if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) == 3083 (READ_CACHING | WRITE_CACHING)) { 3084 3085 /* 3086 * For each RHOpContext ThisContext in 3087 * Oplock.RHBreakQueue: 3088 * Call the algorithm in section 2.1.4.12.2, 3089 * passing Open as the OperationOpen parameter, 3090 * ThisContext.Open as the OplockOpen parameter, 3091 * and Flags as the Flags parameter. 3092 * If the algorithm returns FALSE: 3093 * Set ThisContext.BreakingToRead to FALSE. 3094 * If BreakCacheLevel & HANDLE_CACHING: 3095 * Set NeedToWait to TRUE. 3096 * EndIf 3097 * EndIf 3098 * EndFor 3099 */ 3100 FOREACH_NODE_OFILE(node, o) { 3101 if (o->f_oplock.onlist_RHBQ == 0) 3102 continue; 3103 if (!CompareOplockKeys(ofile, o, CmpFlags)) { 3104 o->f_oplock.BreakingToRead = B_FALSE; 3105 if (BreakCacheLevel & HANDLE_CACHING) 3106 NeedToWait = B_TRUE; 3107 } 3108 } 3109 3110 /* 3111 * For each Open ThisOpen in Oplock.RHOplocks: 3112 * Call the algorithm in section 2.1.4.12.2, 3113 * passing Open as the OperationOpen parameter, 3114 * ThisOpen as the OplockOpen parameter, and 3115 * Flags as the Flagsparameter. 3116 * If the algorithm returns FALSE: 3117 * Remove ThisOpen from Oplock.RHOplocks. 3118 * Notify the server of an oplock break 3119 * according to the algorithm in 3120 * section 2.1.5.17.3, setting the 3121 * algorithm's parameters as follows: 3122 * BreakingOplockOpen = ThisOpen. 3123 * NewOplockLevel = LEVEL_NONE. 3124 * AcknowledgeRequired = TRUE. 3125 * Compl_Status = STATUS_SUCCESS. 3126 * (The operation does not end at this 3127 * point; this call to 2.1.5.17.3 3128 * completes some earlier call to 3129 * 2.1.5.17.2.) 3130 * Initialize a new RHOpContext object, 3131 * setting its fields as follows: 3132 * RHOpCtx.Open = ThisOpen. 3133 * RHOpCtx.BreakingToRead = FALSE 3134 * Add the new RHOpContext object to 3135 * Oplock.RHBreakQueue. 3136 * If BreakCacheLevel contains 3137 * HANDLE_CACHING: 3138 * Set NeedToWait to TRUE. 3139 * EndIf 3140 * EndIf 3141 * EndFor 3142 */ 3143 FOREACH_NODE_OFILE(node, o) { 3144 if (o->f_oplock.onlist_RH == 0) 3145 continue; 3146 if (!CompareOplockKeys(ofile, o, CmpFlags)) { 3147 o->f_oplock.onlist_RH = B_FALSE; 3148 nol->cnt_RH--; 3149 ASSERT(nol->cnt_RH >= 0); 3150 3151 smb_oplock_ind_break(o, 3152 LEVEL_NONE, B_TRUE, 3153 NT_STATUS_SUCCESS); 3154 3155 o->f_oplock.BreakingToRead = B_FALSE; 3156 ASSERT(!(o->f_oplock.onlist_RHBQ)); 3157 o->f_oplock.onlist_RHBQ = B_TRUE; 3158 nol->cnt_RHBQ++; 3159 3160 if (BreakCacheLevel & HANDLE_CACHING) 3161 NeedToWait = B_TRUE; 3162 } 3163 } 3164 } 3165 3166 // If the oplock is explicitly losing HANDLE_CACHING, RHBreakQueue is 3167 // not empty, and the algorithm has not yet decided to wait, this operation 3168 // might have to wait if there is an oplock on RHBreakQueue with a 3169 // non-matching key. This is done because even if this operation didn't 3170 // cause a break of a currently-granted Read-Handle caching oplock, it 3171 // might have done so had a currently-breaking oplock still been granted. 3172 3173 /* 3174 * If (NeedToWait is FALSE) and 3175 * (Oplock.RHBreakQueue is empty) and (XXX: Not empty) 3176 * (BreakCacheLevel contains HANDLE_CACHING): 3177 * For each RHOpContext ThisContex in Oplock.RHBreakQueue: 3178 * If ThisContext.Open.OplockKey != Open.OplockKey: 3179 * Set NeedToWait to TRUE. 3180 * Break out of the For loop. 3181 * EndIf 3182 * EndFor 3183 * EndIf 3184 * Recompute Oplock.State according to the algorithm in 3185 * section 2.1.4.13, passing Oplock as ThisOplock. 3186 */ 3187 if (NeedToWait == B_FALSE && 3188 (BreakCacheLevel & HANDLE_CACHING) != 0) { 3189 FOREACH_NODE_OFILE(node, o) { 3190 if (o->f_oplock.onlist_RHBQ == 0) 3191 continue; 3192 if (!CompareOplockKeys(ofile, o, CmpFlags)) { 3193 NeedToWait = B_TRUE; 3194 break; 3195 } 3196 } 3197 } 3198 RecomputeOplockState(node); 3199 break; 3200 3201 case (READ_CACHING|HANDLE_CACHING|BREAK_TO_READ_CACHING): 3202 /* 3203 * If BreakCacheLevel contains READ_CACHING: 3204 */ 3205 if ((BreakCacheLevel & READ_CACHING) != 0) { 3206 /* 3207 * For each RHOpContext ThisContext in 3208 * Oplock.RHBreakQueue: 3209 * Call the algorithm in section 2.1.4.12.2, 3210 * passing Open = OperationOpen parameter, 3211 * ThisContext.Open = OplockOpen parameter, 3212 * and Flags as the Flags parameter. 3213 * If the algorithm returns FALSE: 3214 * Set ThisCtx.BreakingToRead = FALSE. 3215 * EndIf 3216 * Recompute Oplock.State according to the 3217 * algorithm in section 2.1.4.13, passing 3218 * Oplock as the ThisOplock parameter. 3219 * EndFor 3220 */ 3221 FOREACH_NODE_OFILE(node, o) { 3222 if (o->f_oplock.onlist_RHBQ == 0) 3223 continue; 3224 if (!CompareOplockKeys(ofile, o, CmpFlags)) { 3225 o->f_oplock.BreakingToRead = B_FALSE; 3226 } 3227 } 3228 RecomputeOplockState(node); 3229 } 3230 /* FALLTHROUGH */ 3231 3232 case (READ_CACHING|HANDLE_CACHING|BREAK_TO_NO_CACHING): 3233 /* 3234 * If BreakCacheLevel contains HANDLE_CACHING: 3235 * For each RHOpContext ThisContext in Oplock.RHBreakQueue: 3236 * If ThisContext.Open.OplockKey != Open.OplockKey: 3237 * Set NeedToWait to TRUE. 3238 * Break out of the For loop. 3239 * EndIf 3240 * EndFor 3241 * EndIf 3242 */ 3243 if ((BreakCacheLevel & HANDLE_CACHING) != 0) { 3244 FOREACH_NODE_OFILE(node, o) { 3245 if (o->f_oplock.onlist_RHBQ == 0) 3246 continue; 3247 if (!CompareOplockKeys(ofile, o, CmpFlags)) { 3248 NeedToWait = B_TRUE; 3249 break; 3250 } 3251 } 3252 } 3253 break; 3254 3255 case (READ_CACHING|WRITE_CACHING|EXCLUSIVE): 3256 /* 3257 * If BreakCacheLevel contains both 3258 * READ_CACHING and WRITE_CACHING: 3259 * Notify the server of an oplock break according to 3260 * the algorithm in section 2.1.5.17.3, setting the 3261 * algorithm's parameters as follows: 3262 * BreakingOplockOpen = Oplock.ExclusiveOpen. 3263 * NewOplockLevel = LEVEL_NONE. 3264 * AcknowledgeRequired = TRUE. 3265 * OplockCompletionStatus = STATUS_SUCCESS. 3266 * (The operation does not end at this point; 3267 * this call to 2.1.5.17.3 completes some 3268 * earlier call to 2.1.5.17.1.) 3269 * Set Oplock.State to (READ_CACHING|WRITE_CACHING| \ 3270 * EXCLUSIVE|BREAK_TO_NO_CACHING). 3271 * Set NeedToWait to TRUE. 3272 */ 3273 if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) == 3274 (READ_CACHING | WRITE_CACHING)) { 3275 o = nol->excl_open; 3276 ASSERT(o != NULL); 3277 smb_oplock_ind_break(o, 3278 LEVEL_NONE, B_TRUE, 3279 NT_STATUS_SUCCESS); 3280 3281 nol->ol_state = 3282 (READ_CACHING|WRITE_CACHING| 3283 EXCLUSIVE|BREAK_TO_NO_CACHING); 3284 NeedToWait = B_TRUE; 3285 } 3286 3287 /* 3288 * Else If BreakCacheLevel contains WRITE_CACHING: 3289 * Notify the server of an oplock break according to 3290 * the algorithm in section 2.1.5.17.3, setting the 3291 * algorithm's parameters as follows: 3292 * BreakingOplockOpen = Oplock.ExclusiveOpen. 3293 * NewOplockLevel = READ_CACHING. 3294 * AcknowledgeRequired = TRUE. 3295 * OplockCompletionStatus = STATUS_SUCCESS. 3296 * (The operation does not end at this point; 3297 * this call to 2.1.5.17.3 completes some 3298 * earlier call to 2.1.5.17.1.) 3299 * Set Oplock.State to (READ_CACHING|WRITE_CACHING| 3300 * EXCLUSIVE|BREAK_TO_READ_CACHING). 3301 * Set NeedToWait to TRUE. 3302 * EndIf 3303 */ 3304 else if ((BreakCacheLevel & WRITE_CACHING) != 0) { 3305 o = nol->excl_open; 3306 ASSERT(o != NULL); 3307 smb_oplock_ind_break(o, 3308 READ_CACHING, B_TRUE, 3309 NT_STATUS_SUCCESS); 3310 3311 nol->ol_state = 3312 (READ_CACHING|WRITE_CACHING| 3313 EXCLUSIVE|BREAK_TO_READ_CACHING); 3314 NeedToWait = B_TRUE; 3315 } 3316 break; 3317 3318 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE): 3319 /* 3320 * If BreakCacheLevel equals WRITE_CACHING: 3321 * Notify the server of an oplock break according to 3322 * the algorithm in section 2.1.5.17.3, setting the 3323 * algorithm's parameters as follows: 3324 * BreakingOplockOpen = Oplock.ExclusiveOpen. 3325 * NewOplockLevel = (READ_CACHING|HANDLE_CACHING). 3326 * AcknowledgeRequired = TRUE. 3327 * OplockCompletionStatus = STATUS_SUCCESS. 3328 * (The operation does not end at this point; 3329 * this call to 2.1.5.17.3 completes some 3330 * earlier call to 2.1.5.17.1.) 3331 * Set Oplock.State to (READ_CACHING|WRITE_CACHING| 3332 * HANDLE_CACHING|EXCLUSIVE| 3333 * BREAK_TO_READ_CACHING| 3334 * BREAK_TO_HANDLE_CACHING). 3335 * Set NeedToWait to TRUE. 3336 */ 3337 if (BreakCacheLevel == WRITE_CACHING) { 3338 o = nol->excl_open; 3339 ASSERT(o != NULL); 3340 smb_oplock_ind_break(o, 3341 CACHE_RH, B_TRUE, 3342 NT_STATUS_SUCCESS); 3343 3344 nol->ol_state = 3345 (READ_CACHING|WRITE_CACHING|HANDLE_CACHING| 3346 EXCLUSIVE|BREAK_TO_READ_CACHING| 3347 BREAK_TO_HANDLE_CACHING); 3348 NeedToWait = B_TRUE; 3349 } 3350 3351 /* 3352 * Else If BreakCacheLevel equals HANDLE_CACHING: 3353 * Notify the server of an oplock break according to 3354 * the algorithm in section 2.1.5.17.3, setting the 3355 * algorithm's parameters as follows: 3356 * BreakingOplockOpen = Oplock.ExclusiveOpen. 3357 * NewOplockLevel = (READ_CACHING|WRITE_CACHING). 3358 * AcknowledgeRequired = TRUE. 3359 * OplockCompletionStatus = STATUS_SUCCESS. 3360 * (The operation does not end at this point; 3361 * this call to 2.1.5.17.3 completes some 3362 * earlier call to 2.1.5.17.1.) 3363 * Set Oplock.State to (READ_CACHING|WRITE_CACHING| 3364 * HANDLE_CACHING|EXCLUSIVE| 3365 * BREAK_TO_READ_CACHING| 3366 * BREAK_TO_WRITE_CACHING). 3367 * Set NeedToWait to TRUE. 3368 */ 3369 else if (BreakCacheLevel == HANDLE_CACHING) { 3370 o = nol->excl_open; 3371 ASSERT(o != NULL); 3372 smb_oplock_ind_break(o, 3373 CACHE_RW, B_TRUE, 3374 NT_STATUS_SUCCESS); 3375 3376 nol->ol_state = 3377 (READ_CACHING|WRITE_CACHING|HANDLE_CACHING| 3378 EXCLUSIVE|BREAK_TO_READ_CACHING| 3379 BREAK_TO_WRITE_CACHING); 3380 NeedToWait = B_TRUE; 3381 } 3382 3383 /* 3384 * Else If BreakCacheLevel contains both 3385 * READ_CACHING and WRITE_CACHING: 3386 * Notify the server of an oplock break according to 3387 * the algorithm in section 2.1.5.17.3, setting the 3388 * algorithm's parameters as follows: 3389 * BreakingOplockOpen = Oplock.ExclusiveOpen. 3390 * NewOplockLevel = LEVEL_NONE. 3391 * AcknowledgeRequired = TRUE. 3392 * OplockCompletionStatus = STATUS_SUCCESS. 3393 * (The operation does not end at this point; 3394 * this call to 2.1.5.17.3 completes some 3395 * earlier call to 2.1.5.17.1.) 3396 * Set Oplock.State to (READ_CACHING|WRITE_CACHING| 3397 * HANDLE_CACHING|EXCLUSIVE| 3398 * BREAK_TO_NO_CACHING). 3399 * Set NeedToWait to TRUE. 3400 * EndIf 3401 */ 3402 else if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) == 3403 (READ_CACHING | WRITE_CACHING)) { 3404 o = nol->excl_open; 3405 ASSERT(o != NULL); 3406 smb_oplock_ind_break(o, 3407 LEVEL_NONE, B_TRUE, 3408 NT_STATUS_SUCCESS); 3409 3410 nol->ol_state = 3411 (READ_CACHING|WRITE_CACHING|HANDLE_CACHING| 3412 EXCLUSIVE|BREAK_TO_NO_CACHING); 3413 NeedToWait = B_TRUE; 3414 } 3415 break; 3416 3417 case (READ_CACHING|WRITE_CACHING|EXCLUSIVE|BREAK_TO_READ_CACHING): 3418 /* 3419 * If BreakCacheLevel contains READ_CACHING: 3420 * Set Oplock.State to (READ_CACHING|WRITE_CACHING| 3421 * EXCLUSIVE|BREAK_TO_NO_CACHING). 3422 * EndIf 3423 * If BreakCacheLevel contains either 3424 * READ_CACHING or WRITE_CACHING: 3425 * Set NeedToWait to TRUE. 3426 * EndIf 3427 */ 3428 if ((BreakCacheLevel & READ_CACHING) != 0) { 3429 nol->ol_state = 3430 (READ_CACHING|WRITE_CACHING| 3431 EXCLUSIVE|BREAK_TO_NO_CACHING); 3432 } 3433 if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) != 0) { 3434 NeedToWait = B_TRUE; 3435 } 3436 break; 3437 3438 case (READ_CACHING|WRITE_CACHING|EXCLUSIVE|BREAK_TO_NO_CACHING): 3439 /* 3440 * If BreakCacheLevel contains either 3441 * READ_CACHING or WRITE_CACHING: 3442 * Set NeedToWait to TRUE. 3443 * EndIf 3444 */ 3445 if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) != 0) { 3446 NeedToWait = B_TRUE; 3447 } 3448 break; 3449 3450 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE| 3451 BREAK_TO_READ_CACHING|BREAK_TO_WRITE_CACHING): 3452 /* 3453 * If BreakCacheLevel == WRITE_CACHING: 3454 * Set Oplock.State to (READ_CACHING|WRITE_CACHING| 3455 * HANDLE_CACHING|EXCLUSIVE|BREAK_TO_READ_CACHING). 3456 * Else If BreakCacheLevel contains both 3457 * READ_CACHING and WRITE_CACHING: 3458 * Set Oplock.State to (READ_CACHING|WRITE_CACHING| 3459 * HANDLE_CACHING|EXCLUSIVE|BREAK_TO_NO_CACHING). 3460 * EndIf 3461 * Set NeedToWait to TRUE. 3462 */ 3463 if (BreakCacheLevel == WRITE_CACHING) { 3464 nol->ol_state = (READ_CACHING|WRITE_CACHING| 3465 HANDLE_CACHING|EXCLUSIVE|BREAK_TO_READ_CACHING); 3466 } 3467 else if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) == 3468 (READ_CACHING | WRITE_CACHING)) { 3469 nol->ol_state = (READ_CACHING|WRITE_CACHING| 3470 HANDLE_CACHING|EXCLUSIVE|BREAK_TO_NO_CACHING); 3471 } 3472 NeedToWait = B_TRUE; 3473 break; 3474 3475 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE| 3476 BREAK_TO_READ_CACHING|BREAK_TO_HANDLE_CACHING): 3477 /* 3478 * If BreakCacheLevel == HANDLE_CACHING: 3479 * Set Oplock.State to (READ_CACHING|WRITE_CACHING| 3480 * HANDLE_CACHING|EXCLUSIVE| 3481 * BREAK_TO_READ_CACHING). 3482 * Else If BreakCacheLevel contains READ_CACHING: 3483 * Set Oplock.State to (READ_CACHING|WRITE_CACHING| 3484 * HANDLE_CACHING|EXCLUSIVE| 3485 * BREAK_TO_NO_CACHING). 3486 * EndIf 3487 * Set NeedToWait to TRUE. 3488 */ 3489 if (BreakCacheLevel == HANDLE_CACHING) { 3490 nol->ol_state = 3491 (READ_CACHING|WRITE_CACHING| 3492 HANDLE_CACHING|EXCLUSIVE| 3493 BREAK_TO_READ_CACHING); 3494 } 3495 else if ((BreakCacheLevel & READ_CACHING) != 0) { 3496 nol->ol_state = 3497 (READ_CACHING|WRITE_CACHING| 3498 HANDLE_CACHING|EXCLUSIVE| 3499 BREAK_TO_NO_CACHING); 3500 } 3501 NeedToWait = B_TRUE; 3502 break; 3503 3504 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE| 3505 BREAK_TO_READ_CACHING): 3506 /* 3507 * If BreakCacheLevel contains READ_CACHING, 3508 * Set Oplock.State to (READ_CACHING|WRITE_CACHING| 3509 * HANDLE_CACHING|EXCLUSIVE| 3510 * BREAK_TO_NO_CACHING). 3511 * EndIf 3512 * Set NeedToWait to TRUE. 3513 */ 3514 if ((BreakCacheLevel & READ_CACHING) != 0) { 3515 nol->ol_state = 3516 (READ_CACHING|WRITE_CACHING| 3517 HANDLE_CACHING|EXCLUSIVE| 3518 BREAK_TO_NO_CACHING); 3519 } 3520 NeedToWait = B_TRUE; 3521 break; 3522 3523 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE| 3524 BREAK_TO_NO_CACHING): 3525 NeedToWait = B_TRUE; 3526 break; 3527 3528 } /* Switch */ 3529 3530 if (NeedToWait) { 3531 /* 3532 * The operation that called this algorithm MUST be 3533 * made cancelable by inserting it into 3534 * CancelableOperations.CancelableOperationList. 3535 * The operation that called this algorithm waits until 3536 * the oplock break is acknowledged, as specified in 3537 * section 2.1.5.18, or the operation is canceled. 3538 */ 3539 status = NT_STATUS_OPLOCK_BREAK_IN_PROGRESS; 3540 /* Caller does smb_oplock_wait_break() */ 3541 } 3542 3543 out: 3544 mutex_exit(&node->n_oplock.ol_mutex); 3545 smb_llist_exit(&node->n_ofile_list); 3546 3547 return (status); 3548 } 3549 3550 /* 3551 * smb_oplock_move() 3552 * 3553 * Helper function for smb2_lease_ofile_close, where we're closing the 3554 * ofile that has the oplock for a given lease, and need to move that 3555 * oplock to another handle with the same lease. 3556 * 3557 * This is not described in [MS-FSA], so presumably Windows does this 3558 * by keeping oplock objects separate from the open files (no action 3559 * needed in the FSA layer). We keep the oplock state as part of the 3560 * ofile, so we need to relocate the oplock state in this case. 3561 * 3562 * Note that in here, we're moving state for both the FSA level and 3563 * the SMB level (which is unusual) but this is the easiest way to 3564 * make sure we move the state without any other effects. 3565 */ 3566 void 3567 smb_oplock_move(smb_node_t *node, 3568 smb_ofile_t *fr_ofile, smb_ofile_t *to_ofile) 3569 { 3570 /* 3571 * These are the two common states for an ofile with 3572 * a lease that's not the one holding the oplock. 3573 * Log if it's not either of these. 3574 */ 3575 static const smb_oplock_grant_t og0 = { 0 }; 3576 static const smb_oplock_grant_t og8 = { 3577 .og_state = OPLOCK_LEVEL_GRANULAR, 0 }; 3578 smb_oplock_grant_t og_tmp; 3579 3580 ASSERT(fr_ofile->f_node == node); 3581 ASSERT(to_ofile->f_node == node); 3582 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex)); 3583 3584 /* 3585 * The ofile to which we're moving the oplock 3586 * should NOT have any oplock state. However, 3587 * as long as we just swap state between the 3588 * two oplocks, we won't invalidate any of 3589 * the node's "onlist" counts etc. 3590 */ 3591 if (bcmp(&to_ofile->f_oplock, &og0, sizeof (og0)) != 0 && 3592 bcmp(&to_ofile->f_oplock, &og8, sizeof (og8)) != 0) { 3593 #ifdef DEBUG 3594 cmn_err(CE_NOTE, "smb_oplock_move: not empty?"); 3595 #endif 3596 DTRACE_PROBE2(dst__not__empty, 3597 smb_node_t, node, smb_ofile_t, to_ofile); 3598 } 3599 3600 og_tmp = to_ofile->f_oplock; 3601 to_ofile->f_oplock = fr_ofile->f_oplock; 3602 fr_ofile->f_oplock = og_tmp; 3603 3604 if (node->n_oplock.excl_open == fr_ofile) 3605 node->n_oplock.excl_open = to_ofile; 3606 3607 } 3608