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