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)) &&
 106             !bcmp(OperOpen->ParentOplockKey, key0, sizeof (key0)))
 107                 return (B_FALSE);
 108         if (!bcmp(OplockOpen->TargetOplockKey, key0, sizeof (key0)) &&
 109             !bcmp(OplockOpen->ParentOplockKey, key0, sizeof (key0)))
 110                 return (B_FALSE);
 111 
 112         /*
 113          * If OplockOpen.TargetOplockKey is empty or...
 114          */
 115         if (!bcmp(OplockOpen->TargetOplockKey, key0, sizeof (key0)))
 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)))
 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)) {
 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)))
 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)) {
 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 }