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