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