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