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