194047d49SGordon Ross /*
294047d49SGordon Ross  * This file and its contents are supplied under the terms of the
394047d49SGordon Ross  * Common Development and Distribution License ("CDDL"), version 1.0.
494047d49SGordon Ross  * You may only use this file in accordance with the terms of version
594047d49SGordon Ross  * 1.0 of the CDDL.
694047d49SGordon Ross  *
794047d49SGordon Ross  * A full copy of the text of the CDDL should have accompanied this
894047d49SGordon Ross  * source.  A copy of the CDDL is also available via the Internet at
994047d49SGordon Ross  * http://www.illumos.org/license/CDDL.
1094047d49SGordon Ross  */
1194047d49SGordon Ross 
1294047d49SGordon Ross /*
131f0845f1SGordon Ross  * Copyright 2020 Nexenta by DDN, Inc.  All rights reserved.
14a5a9a6bbSGordon Ross  * Copyright 2022 RackTop Systems, Inc.
1594047d49SGordon Ross  */
1694047d49SGordon Ross 
1794047d49SGordon Ross /*
1894047d49SGordon Ross  * (SMB1/SMB2) common (FS-level) Oplock support.
1994047d49SGordon Ross  *
2094047d49SGordon Ross  * This is the file-system (FS) level oplock code.  This level
2194047d49SGordon Ross  * knows about the rules by which various kinds of oplocks may
2294047d49SGordon Ross  * coexist and how they interact.  Note that this code should
2394047d49SGordon Ross  * have NO knowledge of specific SMB protocol details.  Those
2494047d49SGordon Ross  * details are handled in smb_srv_oplock.c and related.
2594047d49SGordon Ross  *
2694047d49SGordon Ross  * This file is intentionally written to very closely follow the
2794047d49SGordon Ross  * [MS-FSA] specification sections about oplocks.  Almost every
2894047d49SGordon Ross  * section of code is preceeded by a block of text from that
2994047d49SGordon Ross  * specification describing the logic.  Where the implementation
3094047d49SGordon Ross  * differs from what the spec. describes, there are notes like:
3194047d49SGordon Ross  * Implementation specific: ...
3294047d49SGordon Ross  */
3394047d49SGordon Ross 
3494047d49SGordon Ross #include <smbsrv/smb_kproto.h>
3594047d49SGordon Ross #include <smbsrv/smb_oplock.h>
3694047d49SGordon Ross 
3794047d49SGordon Ross /*
3894047d49SGordon Ross  * Several short-hand defines and enums used in this file.
3994047d49SGordon Ross  */
4094047d49SGordon Ross 
4194047d49SGordon Ross #define	NODE_FLAGS_DELETING	(NODE_FLAGS_DELETE_ON_CLOSE |\
4294047d49SGordon Ross 				NODE_FLAGS_DELETE_COMMITTED)
4394047d49SGordon Ross 
4494047d49SGordon Ross static uint32_t
4594047d49SGordon Ross smb_oplock_req_excl(
4694047d49SGordon Ross     smb_ofile_t *ofile,		/* in: the "Open" */
4794047d49SGordon Ross     uint32_t *rop);		/* in: "RequestedOplock", out:NewOplockLevel */
4894047d49SGordon Ross 
4994047d49SGordon Ross static uint32_t
5094047d49SGordon Ross smb_oplock_req_shared(
5194047d49SGordon Ross     smb_ofile_t *ofile,		/* the "Open" */
5294047d49SGordon Ross     uint32_t *rop,		/* in: "RequestedOplock", out:NewOplockLevel */
5394047d49SGordon Ross     boolean_t GrantingInAck);
5494047d49SGordon Ross 
5594047d49SGordon Ross static uint32_t smb_oplock_break_cmn(smb_node_t *node,
5694047d49SGordon Ross     smb_ofile_t *ofile, uint32_t BreakCacheLevel);
5794047d49SGordon Ross 
5894047d49SGordon Ross 
5994047d49SGordon Ross /*
6094047d49SGordon Ross  * [MS-FSA] 2.1.4.12.2 Algorithm to Compare Oplock Keys
6194047d49SGordon Ross  *
6294047d49SGordon Ross  * The inputs for this algorithm are:
6394047d49SGordon Ross  *
6494047d49SGordon Ross  *	OperationOpen: The Open used in the request that can
6594047d49SGordon Ross  *	  cause an oplock to break.
6694047d49SGordon Ross  *	OplockOpen: The Open originally used to request the oplock,
6794047d49SGordon Ross  *	  as specified in section 2.1.5.17.
6894047d49SGordon Ross  *	Flags: If unspecified it is considered to contain 0.
6994047d49SGordon Ross  *	  Valid nonzero values are:
7094047d49SGordon Ross  *		PARENT_OBJECT
7194047d49SGordon Ross  *
7294047d49SGordon Ross  * This algorithm returns TRUE if the appropriate oplock key field of
7394047d49SGordon Ross  * OperationOpen equals OplockOpen.TargetOplockKey, and FALSE otherwise.
7494047d49SGordon Ross  *
7594047d49SGordon Ross  * Note: Unlike many comparison functions, ARG ORDER MATTERS.
7694047d49SGordon Ross  */
7794047d49SGordon Ross 
7894047d49SGordon Ross static boolean_t
CompareOplockKeys(smb_ofile_t * OperOpen,smb_ofile_t * OplockOpen,int flags)7994047d49SGordon Ross CompareOplockKeys(smb_ofile_t *OperOpen, smb_ofile_t *OplockOpen, int flags)
8094047d49SGordon Ross {
8194047d49SGordon Ross 	static const uint8_t key0[SMB_LEASE_KEY_SZ] = { 0 };
8294047d49SGordon Ross 
8394047d49SGordon Ross 	/*
8494047d49SGordon Ross 	 * When we're called via FEM, (smb_oplock_break_...)
8594047d49SGordon Ross 	 * the OperOpen arg is NULL because I/O outside of SMB
8694047d49SGordon Ross 	 * doesn't have an "ofile".  That's "not a match".
8794047d49SGordon Ross 	 */
8894047d49SGordon Ross 	if (OperOpen == NULL)
8994047d49SGordon Ross 		return (B_FALSE);
9094047d49SGordon Ross 	ASSERT(OplockOpen != NULL);
9194047d49SGordon Ross 
9294047d49SGordon Ross 	/*
9394047d49SGordon Ross 	 * If OperationOpen equals OplockOpen:
9494047d49SGordon Ross 	 * Return TRUE.
9594047d49SGordon Ross 	 */
9694047d49SGordon Ross 	if (OperOpen == OplockOpen)
9794047d49SGordon Ross 		return (B_TRUE);
9894047d49SGordon Ross 
9994047d49SGordon Ross 	/*
10094047d49SGordon Ross 	 * If both OperationOpen.TargetOplockKey and
10194047d49SGordon Ross 	 * OperationOpen.ParentOplockKey are empty
10294047d49SGordon Ross 	 * or both OplockOpen.TargetOplockKey and
10394047d49SGordon Ross 	 * OplockOpen.ParentOplockKey are empty:
10494047d49SGordon Ross 	 * Return FALSE.
10594047d49SGordon Ross 	 */
10694047d49SGordon Ross 	if (bcmp(OperOpen->TargetOplockKey, key0, sizeof (key0)) == 0 &&
10794047d49SGordon Ross 	    bcmp(OperOpen->ParentOplockKey, key0, sizeof (key0)) == 0)
10894047d49SGordon Ross 		return (B_FALSE);
10994047d49SGordon Ross 	if (bcmp(OplockOpen->TargetOplockKey, key0, sizeof (key0)) == 0 &&
11094047d49SGordon Ross 	    bcmp(OplockOpen->ParentOplockKey, key0, sizeof (key0)) == 0)
11194047d49SGordon Ross 		return (B_FALSE);
11294047d49SGordon Ross 
11394047d49SGordon Ross 	/*
11494047d49SGordon Ross 	 * If OplockOpen.TargetOplockKey is empty or...
11594047d49SGordon Ross 	 */
11694047d49SGordon Ross 	if (bcmp(OplockOpen->TargetOplockKey, key0, sizeof (key0)) == 0)
11794047d49SGordon Ross 		return (B_FALSE);
11894047d49SGordon Ross 
11994047d49SGordon Ross 	/*
12094047d49SGordon Ross 	 * If Flags contains PARENT_OBJECT:
12194047d49SGordon Ross 	 */
12294047d49SGordon Ross 	if ((flags & PARENT_OBJECT) != 0) {
12394047d49SGordon Ross 		/*
12494047d49SGordon Ross 		 * If OperationOpen.ParentOplockKey is empty:
12594047d49SGordon Ross 		 * Return FALSE.
12694047d49SGordon Ross 		 */
12794047d49SGordon Ross 		if (bcmp(OperOpen->ParentOplockKey, key0, sizeof (key0)) == 0)
12894047d49SGordon Ross 			return (B_FALSE);
12994047d49SGordon Ross 
13094047d49SGordon Ross 		/*
13194047d49SGordon Ross 		 * If OperationOpen.ParentOplockKey equals
13294047d49SGordon Ross 		 * OplockOpen.TargetOplockKey:
13394047d49SGordon Ross 		 * return TRUE, else FALSE
13494047d49SGordon Ross 		 */
13594047d49SGordon Ross 		if (bcmp(OperOpen->ParentOplockKey,
13694047d49SGordon Ross 		    OplockOpen->TargetOplockKey,
13794047d49SGordon Ross 		    SMB_LEASE_KEY_SZ) == 0) {
13894047d49SGordon Ross 			return (B_TRUE);
13994047d49SGordon Ross 		}
14094047d49SGordon Ross 	} else {
14194047d49SGordon Ross 		/*
14294047d49SGordon Ross 		 * ... from above:
14394047d49SGordon Ross 		 * (Flags does not contain PARENT_OBJECT and
14494047d49SGordon Ross 		 * OperationOpen.TargetOplockKey is empty):
14594047d49SGordon Ross 		 * Return FALSE.
14694047d49SGordon Ross 		 */
14794047d49SGordon Ross 		if (bcmp(OperOpen->TargetOplockKey, key0, sizeof (key0)) == 0)
14894047d49SGordon Ross 			return (B_FALSE);
14994047d49SGordon Ross 
15094047d49SGordon Ross 		/*
15194047d49SGordon Ross 		 * If OperationOpen.TargetOplockKey equals
15294047d49SGordon Ross 		 * OplockOpen.TargetOplockKey:
15394047d49SGordon Ross 		 *  Return TRUE, else FALSE
15494047d49SGordon Ross 		 */
15594047d49SGordon Ross 		if (bcmp(OperOpen->TargetOplockKey,
15694047d49SGordon Ross 		    OplockOpen->TargetOplockKey,
15794047d49SGordon Ross 		    SMB_LEASE_KEY_SZ) == 0) {
15894047d49SGordon Ross 			return (B_TRUE);
15994047d49SGordon Ross 		}
16094047d49SGordon Ross 	}
16194047d49SGordon Ross 
16294047d49SGordon Ross 	return (B_FALSE);
16394047d49SGordon Ross }
16494047d49SGordon Ross 
16594047d49SGordon Ross /*
16694047d49SGordon Ross  * 2.1.4.13 Algorithm to Recompute the State of a Shared Oplock
16794047d49SGordon Ross  *
16894047d49SGordon Ross  * The inputs for this algorithm are:
16994047d49SGordon Ross  *	ThisOplock: The Oplock on whose state is being recomputed.
17094047d49SGordon Ross  */
17194047d49SGordon Ross static void
RecomputeOplockState(smb_node_t * node)17294047d49SGordon Ross RecomputeOplockState(smb_node_t *node)
17394047d49SGordon Ross {
17494047d49SGordon Ross 	smb_oplock_t *ol = &node->n_oplock;
17594047d49SGordon Ross 
17694047d49SGordon Ross 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
17794047d49SGordon Ross 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
17894047d49SGordon Ross 
17994047d49SGordon Ross 	/*
18094047d49SGordon Ross 	 * If ThisOplock.IIOplocks, ThisOplock.ROplocks, ThisOplock.RHOplocks,
18194047d49SGordon Ross 	 * and ThisOplock.RHBreakQueue are all empty:
18294047d49SGordon Ross 	 *	Set ThisOplock.State to NO_OPLOCK.
18394047d49SGordon Ross 	 */
18494047d49SGordon Ross 	if (ol->cnt_II == 0 && ol->cnt_R == 0 &&
18594047d49SGordon Ross 	    ol->cnt_RH == 0 && ol->cnt_RHBQ == 0) {
18694047d49SGordon Ross 		ol->ol_state = NO_OPLOCK;
18794047d49SGordon Ross 		return;
18894047d49SGordon Ross 	}
18994047d49SGordon Ross 
19094047d49SGordon Ross 	/*
19194047d49SGordon Ross 	 * Else If ThisOplock.ROplocks is not empty and either
19294047d49SGordon Ross 	 *    ThisOplock.RHOplocks or ThisOplock.RHBreakQueue are not empty:
19394047d49SGordon Ross 	 *	Set ThisOplock.State to
19494047d49SGordon Ross 	 *	  (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH).
19594047d49SGordon Ross 	 */
19694047d49SGordon Ross 	else if (ol->cnt_R != 0 && (ol->cnt_RH != 0 || ol->cnt_RHBQ != 0)) {
19794047d49SGordon Ross 		ol->ol_state = (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH);
19894047d49SGordon Ross 	}
19994047d49SGordon Ross 
20094047d49SGordon Ross 	/*
20194047d49SGordon Ross 	 * Else If ThisOplock.ROplocks is empty and
20294047d49SGordon Ross 	 * ThisOplock.RHOplocks is not empty:
20394047d49SGordon Ross 	 *	Set ThisOplock.State to (READ_CACHING|HANDLE_CACHING).
20494047d49SGordon Ross 	 */
20594047d49SGordon Ross 	else if (ol->cnt_R == 0 && ol->cnt_RH != 0) {
20694047d49SGordon Ross 		ol->ol_state = (READ_CACHING|HANDLE_CACHING);
20794047d49SGordon Ross 	}
20894047d49SGordon Ross 
20994047d49SGordon Ross 	/*
21094047d49SGordon Ross 	 * Else If ThisOplock.ROplocks is not empty and
21194047d49SGordon Ross 	 * ThisOplock.IIOplocks is not empty:
21294047d49SGordon Ross 	 *	Set ThisOplock.State to (READ_CACHING|LEVEL_TWO_OPLOCK).
21394047d49SGordon Ross 	 */
21494047d49SGordon Ross 	else if (ol->cnt_R != 0 && ol->cnt_II != 0) {
21594047d49SGordon Ross 		ol->ol_state = (READ_CACHING|LEVEL_TWO_OPLOCK);
21694047d49SGordon Ross 	}
21794047d49SGordon Ross 
21894047d49SGordon Ross 	/*
21994047d49SGordon Ross 	 * Else If ThisOplock.ROplocks is not empty and
22094047d49SGordon Ross 	 * ThisOplock.IIOplocks is empty:
22194047d49SGordon Ross 	 *	Set ThisOplock.State to READ_CACHING.
22294047d49SGordon Ross 	 */
22394047d49SGordon Ross 	else if (ol->cnt_R != 0 && ol->cnt_II == 0) {
22494047d49SGordon Ross 		ol->ol_state = READ_CACHING;
22594047d49SGordon Ross 	}
22694047d49SGordon Ross 
22794047d49SGordon Ross 	/*
22894047d49SGordon Ross 	 * Else If ThisOplock.ROplocks is empty and
22994047d49SGordon Ross 	 * ThisOplock.IIOplocks is not empty:
23094047d49SGordon Ross 	 *	Set ThisOplock.State to LEVEL_TWO_OPLOCK.
23194047d49SGordon Ross 	 */
23294047d49SGordon Ross 	else if (ol->cnt_R == 0 && ol->cnt_II != 0) {
23394047d49SGordon Ross 		ol->ol_state = LEVEL_TWO_OPLOCK;
23494047d49SGordon Ross 	}
23594047d49SGordon Ross 
23694047d49SGordon Ross 	else {
23794047d49SGordon Ross 		smb_ofile_t *o;
23894047d49SGordon Ross 		int cntBrkToRead;
23994047d49SGordon Ross 
24094047d49SGordon Ross 		/*
24194047d49SGordon Ross 		 * ThisOplock.RHBreakQueue MUST be non-empty by this point.
24294047d49SGordon Ross 		 */
24394047d49SGordon Ross 		ASSERT(ol->cnt_RHBQ != 0);
24494047d49SGordon Ross 
24594047d49SGordon Ross 		/*
24694047d49SGordon Ross 		 * How many on RHBQ have BreakingToRead set?
24794047d49SGordon Ross 		 */
24894047d49SGordon Ross 		cntBrkToRead = 0;
24994047d49SGordon Ross 		FOREACH_NODE_OFILE(node, o) {
25094047d49SGordon Ross 			if (o->f_oplock.onlist_RHBQ == 0)
25194047d49SGordon Ross 				continue;
25294047d49SGordon Ross 			if (o->f_oplock.BreakingToRead)
25394047d49SGordon Ross 				cntBrkToRead++;
25494047d49SGordon Ross 		}
25594047d49SGordon Ross 
25694047d49SGordon Ross 		/*
25794047d49SGordon Ross 		 * If RHOpContext.BreakingToRead is TRUE for
25894047d49SGordon Ross 		 *  every RHOpContext on ThisOplock.RHBreakQueue:
25994047d49SGordon Ross 		 */
26094047d49SGordon Ross 		if (cntBrkToRead == ol->cnt_RHBQ) {
26194047d49SGordon Ross 			/*
26294047d49SGordon Ross 			 * Set ThisOplock.State to
26394047d49SGordon Ross 			 * (READ_CACHING|HANDLE_CACHING|BREAK_TO_READ_CACHING).
26494047d49SGordon Ross 			 */
26594047d49SGordon Ross 			ol->ol_state = (READ_CACHING|HANDLE_CACHING|
26694047d49SGordon Ross 			    BREAK_TO_READ_CACHING);
26794047d49SGordon Ross 		}
26894047d49SGordon Ross 
26994047d49SGordon Ross 		/*
27094047d49SGordon Ross 		 * Else If RHOpContext.BreakingToRead is FALSE for
27194047d49SGordon Ross 		 *  every RHOpContext on ThisOplock.RHBreakQueue:
27294047d49SGordon Ross 		 */
27394047d49SGordon Ross 		else if (cntBrkToRead == 0) {
27494047d49SGordon Ross 			/*
27594047d49SGordon Ross 			 * Set ThisOplock.State to
27694047d49SGordon Ross 			 *  (READ_CACHING|HANDLE_CACHING|BREAK_TO_NO_CACHING).
27794047d49SGordon Ross 			 */
27894047d49SGordon Ross 			ol->ol_state = (READ_CACHING|HANDLE_CACHING|
27994047d49SGordon Ross 			    BREAK_TO_NO_CACHING);
28094047d49SGordon Ross 		} else {
28194047d49SGordon Ross 			/*
28294047d49SGordon Ross 			 * Set ThisOplock.State to
28394047d49SGordon Ross 			 *  (READ_CACHING|HANDLE_CACHING).
28494047d49SGordon Ross 			 */
28594047d49SGordon Ross 			ol->ol_state = (READ_CACHING|HANDLE_CACHING);
28694047d49SGordon Ross 		}
28794047d49SGordon Ross 	}
28894047d49SGordon Ross }
28994047d49SGordon Ross 
29094047d49SGordon Ross /*
29194047d49SGordon Ross  * [MS-FSA] 2.1.5.17 Server Requests an Oplock
29294047d49SGordon Ross  *
29394047d49SGordon Ross  * The server (caller) provides:
29494047d49SGordon Ross  *	Open - The Open on which the oplock is being requested. (ofile)
29594047d49SGordon Ross  *	Type - The type of oplock being requested. Valid values are as follows:
29694047d49SGordon Ross  *		LEVEL_TWO (Corresponds to SMB2_OPLOCK_LEVEL_II)
29794047d49SGordon Ross  *		LEVEL_ONE (Corresponds to SMB2_OPLOCK_LEVEL_EXCLUSIVE)
29894047d49SGordon Ross  *		LEVEL_BATCH (Corresponds to SMB2_OPLOCK_LEVEL_BATCH)
29994047d49SGordon Ross  *		LEVEL_GRANULAR (Corresponds to SMB2_OPLOCK_LEVEL_LEASE)
30094047d49SGordon Ross  *	RequestedOplockLevel - A combination of zero or more of the
30194047d49SGordon Ross  *	  following flags (ignored if Type != LEVEL_GRANULAR)
30294047d49SGordon Ross  *		READ_CACHING
30394047d49SGordon Ross  *		HANDLE_CACHING
30494047d49SGordon Ross  *		WRITE_CACHING
30594047d49SGordon Ross  *
30694047d49SGordon Ross  *	(Type + RequestedOplockLevel come in *statep)
30794047d49SGordon Ross  *
30894047d49SGordon Ross  * Returns:
30994047d49SGordon Ross  *	*statep = NewOplockLevel (possibly less than requested)
31094047d49SGordon Ross  *		  containing: LEVEL_NONE, LEVEL_TWO + cache_flags
31194047d49SGordon Ross  *	NTSTATUS
31294047d49SGordon Ross  */
31394047d49SGordon Ross 
31494047d49SGordon Ross uint32_t
smb_oplock_request(smb_request_t * sr,smb_ofile_t * ofile,uint32_t * statep)31594047d49SGordon Ross smb_oplock_request(smb_request_t *sr, smb_ofile_t *ofile, uint32_t *statep)
31672b35b05SGordon Ross {
31772b35b05SGordon Ross 	smb_node_t *node = ofile->f_node;
31872b35b05SGordon Ross 	uint32_t status;
31972b35b05SGordon Ross 
32072b35b05SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
32172b35b05SGordon Ross 	mutex_enter(&node->n_oplock.ol_mutex);
32272b35b05SGordon Ross 
32372b35b05SGordon Ross 	status = smb_oplock_request_LH(sr, ofile, statep);
32472b35b05SGordon Ross 
32572b35b05SGordon Ross 	mutex_exit(&node->n_oplock.ol_mutex);
32672b35b05SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
32772b35b05SGordon Ross 
32872b35b05SGordon Ross 	return (status);
32972b35b05SGordon Ross }
33072b35b05SGordon Ross 
33172b35b05SGordon Ross uint32_t
smb_oplock_request_LH(smb_request_t * sr,smb_ofile_t * ofile,uint32_t * statep)33272b35b05SGordon Ross smb_oplock_request_LH(smb_request_t *sr, smb_ofile_t *ofile, uint32_t *statep)
33394047d49SGordon Ross {
33494047d49SGordon Ross 	smb_node_t *node = ofile->f_node;
33594047d49SGordon Ross 	uint32_t type = *statep & OPLOCK_LEVEL_TYPE_MASK;
33694047d49SGordon Ross 	uint32_t level = *statep & OPLOCK_LEVEL_CACHE_MASK;
33794047d49SGordon Ross 	uint32_t status;
33894047d49SGordon Ross 
33972b35b05SGordon Ross 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
34072b35b05SGordon Ross 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
34172b35b05SGordon Ross 
34294047d49SGordon Ross 	*statep = LEVEL_NONE;
34394047d49SGordon Ross 
34494047d49SGordon Ross 	/*
34594047d49SGordon Ross 	 * If Open.Stream.StreamType is DirectoryStream:
34694047d49SGordon Ross 	 *	The operation MUST be failed with STATUS_INVALID_PARAMETER
34794047d49SGordon Ross 	 *	under either of the following conditions:
34894047d49SGordon Ross 	 *	* Type is not LEVEL_GRANULAR.
34994047d49SGordon Ross 	 *	* Type is LEVEL_GRANULAR but RequestedOplockLevel is
35094047d49SGordon Ross 	 *	  neither READ_CACHING nor (READ_CACHING|HANDLE_CACHING).
35194047d49SGordon Ross 	 */
35294047d49SGordon Ross 	if (!smb_node_is_file(node)) {
35394047d49SGordon Ross 		/* ofile is a directory. */
35494047d49SGordon Ross 		if (type != LEVEL_GRANULAR)
35594047d49SGordon Ross 			return (NT_STATUS_INVALID_PARAMETER);
35694047d49SGordon Ross 		if (level != READ_CACHING &&
35794047d49SGordon Ross 		    level != (READ_CACHING|HANDLE_CACHING))
35894047d49SGordon Ross 			return (NT_STATUS_INVALID_PARAMETER);
35994047d49SGordon Ross 		/*
36094047d49SGordon Ross 		 * We're not supporting directory leases yet.
36194047d49SGordon Ross 		 * Todo.
36294047d49SGordon Ross 		 */
36394047d49SGordon Ross 		return (NT_STATUS_OPLOCK_NOT_GRANTED);
36494047d49SGordon Ross 	}
36594047d49SGordon Ross 
36694047d49SGordon Ross 	/*
36794047d49SGordon Ross 	 * If Type is LEVEL_ONE or LEVEL_BATCH:
36894047d49SGordon Ross 	 * The operation MUST be failed with STATUS_OPLOCK_NOT_GRANTED
36994047d49SGordon Ross 	 * under either of the following conditions:
37094047d49SGordon Ross 	 *	Open.File.OpenList contains more than one Open
37194047d49SGordon Ross 	 *	  whose Stream is the same as Open.Stream.
37294047d49SGordon Ross 	 *	Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or
37394047d49SGordon Ross 	 *	  FILE_SYNCHRONOUS_IO_NONALERT.
37494047d49SGordon Ross 	 * Request an exclusive oplock according to the algorithm in
37594047d49SGordon Ross 	 * section 2.1.5.17.1, setting the algorithm's params as follows:
37694047d49SGordon Ross 	 *	Pass in the current Open.
37794047d49SGordon Ross 	 *	RequestedOplock = Type.
37894047d49SGordon Ross 	 * The operation MUST at this point return any status code
37994047d49SGordon Ross 	 * returned by the exclusive oplock request algorithm.
38094047d49SGordon Ross 	 */
38194047d49SGordon Ross 	if (type == LEVEL_ONE || type == LEVEL_BATCH) {
38294047d49SGordon Ross 		if (node->n_open_count > 1) {
38394047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
38494047d49SGordon Ross 			goto out;
38594047d49SGordon Ross 		}
38694047d49SGordon Ross 		/* XXX: Should be a flag on the ofile. */
38794047d49SGordon Ross 		if (node->flags & NODE_FLAGS_WRITE_THROUGH) {
38894047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
38994047d49SGordon Ross 			goto out;
39094047d49SGordon Ross 		}
39194047d49SGordon Ross 		*statep = type;
39294047d49SGordon Ross 		status = smb_oplock_req_excl(ofile, statep);
39394047d49SGordon Ross 		goto out;
39494047d49SGordon Ross 	}
39594047d49SGordon Ross 
39694047d49SGordon Ross 	/*
39794047d49SGordon Ross 	 * Else If Type is LEVEL_TWO:
39894047d49SGordon Ross 	 * The operation MUST be failed with STATUS_OPLOCK_NOT_GRANTED under
39994047d49SGordon Ross 	 *  either of the following conditions:
40094047d49SGordon Ross 	 *	Open.Stream.ByteRangeLockList is not empty.
40194047d49SGordon Ross 	 *	Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or
40294047d49SGordon Ross 	 *	  FILE_SYNCHRONOUS_IO_NONALERT.
40394047d49SGordon Ross 	 * Request a shared oplock according to the algorithm in
40494047d49SGordon Ross 	 * section 2.1.5.17.2, setting the algorithm's parameters as follows:
40594047d49SGordon Ross 	 *	Pass in the current Open.
40694047d49SGordon Ross 	 *	RequestedOplock = Type.
40794047d49SGordon Ross 	 *	GrantingInAck = FALSE.
40894047d49SGordon Ross 	 * The operation MUST at this point return any status code
40994047d49SGordon Ross 	 * returned by the shared oplock request algorithm.
41094047d49SGordon Ross 	 */
41194047d49SGordon Ross 	if (type == LEVEL_TWO) {
41294047d49SGordon Ross 		if (smb_lock_range_access(sr, node, 0, ~0, B_FALSE) != 0) {
41394047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
41494047d49SGordon Ross 			goto out;
41594047d49SGordon Ross 		}
41694047d49SGordon Ross 		/* XXX: Should be a flag on the ofile. */
41794047d49SGordon Ross 		if (node->flags & NODE_FLAGS_WRITE_THROUGH) {
41894047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
41994047d49SGordon Ross 			goto out;
42094047d49SGordon Ross 		}
42194047d49SGordon Ross 		*statep = type;
42294047d49SGordon Ross 		status = smb_oplock_req_shared(ofile, statep, B_FALSE);
42394047d49SGordon Ross 		goto out;
42494047d49SGordon Ross 	}
42594047d49SGordon Ross 
42694047d49SGordon Ross 	/*
42794047d49SGordon Ross 	 * Else If Type is LEVEL_GRANULAR:
42894047d49SGordon Ross 	 *   Sub-cases on RequestedOplockLevel (our "level")
42994047d49SGordon Ross 	 *
43094047d49SGordon Ross 	 * This is the last Type, so error on !granular and then
43194047d49SGordon Ross 	 * deal with the cache levels using one less indent.
43294047d49SGordon Ross 	 */
43394047d49SGordon Ross 	if (type != LEVEL_GRANULAR) {
43494047d49SGordon Ross 		status = NT_STATUS_INVALID_PARAMETER;
43594047d49SGordon Ross 		goto out;
43694047d49SGordon Ross 	}
43794047d49SGordon Ross 
43894047d49SGordon Ross 	switch (level) {
43994047d49SGordon Ross 
44094047d49SGordon Ross 	/*
44194047d49SGordon Ross 	 * If RequestedOplockLevel is READ_CACHING or
44294047d49SGordon Ross 	 *   (READ_CACHING|HANDLE_CACHING):
44394047d49SGordon Ross 	 *	The operation MUST be failed with STATUS_OPLOCK_NOT_GRANTED
44494047d49SGordon Ross 	 *	under either of the following conditions:
44594047d49SGordon Ross 	 *		Open.Stream.ByteRangeLockList is not empty.
44694047d49SGordon Ross 	 *		Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or
44794047d49SGordon Ross 	 *		  FILE_SYNCHRONOUS_IO_NONALERT.
44894047d49SGordon Ross 	 *	Request a shared oplock according to the algorithm in
44994047d49SGordon Ross 	 *	section 2.1.5.17.2, setting the parameters as follows:
45094047d49SGordon Ross 	 *		Pass in the current Open.
45194047d49SGordon Ross 	 *		RequestedOplock = RequestedOplockLevel.
45294047d49SGordon Ross 	 *		GrantingInAck = FALSE.
45394047d49SGordon Ross 	 *
45494047d49SGordon Ross 	 *	The operation MUST at this point return any status code
45594047d49SGordon Ross 	 *	  returned by the shared oplock request algorithm.
45694047d49SGordon Ross 	 */
45794047d49SGordon Ross 	case READ_CACHING:
45894047d49SGordon Ross 	case (READ_CACHING|HANDLE_CACHING):
45994047d49SGordon Ross 		if (smb_lock_range_access(sr, node, 0, ~0, B_FALSE) != 0) {
46094047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
46194047d49SGordon Ross 			goto out;
46294047d49SGordon Ross 		}
46394047d49SGordon Ross 		/* XXX: Should be a flag on the ofile. */
46494047d49SGordon Ross 		if (node->flags & NODE_FLAGS_WRITE_THROUGH) {
46594047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
46694047d49SGordon Ross 			goto out;
46794047d49SGordon Ross 		}
46894047d49SGordon Ross 		*statep = level;
46994047d49SGordon Ross 		status = smb_oplock_req_shared(ofile, statep, B_FALSE);
47094047d49SGordon Ross 		break;
47194047d49SGordon Ross 
47294047d49SGordon Ross 	/*
47394047d49SGordon Ross 	 * Else If RequestedOplockLevel is
47494047d49SGordon Ross 	 * (READ_CACHING|WRITE_CACHING) or
47594047d49SGordon Ross 	 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING):
47694047d49SGordon Ross 	 * If Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or
47794047d49SGordon Ross 	 * FILE_SYNCHRONOUS_IO_NONALERT, the operation MUST be failed
47894047d49SGordon Ross 	 * with STATUS_OPLOCK_NOT_GRANTED.
47994047d49SGordon Ross 	 * Request an exclusive oplock according to the algorithm in
48094047d49SGordon Ross 	 * section 2.1.5.17.1, setting the parameters as follows:
48194047d49SGordon Ross 	 *	Pass in the current Open.
48294047d49SGordon Ross 	 *	RequestedOplock = RequestedOplockLevel.
48394047d49SGordon Ross 	 * The operation MUST at this point return any status code
48494047d49SGordon Ross 	 * returned by the exclusive oplock request algorithm.
48594047d49SGordon Ross 	 */
48694047d49SGordon Ross 	case (READ_CACHING | WRITE_CACHING):
48794047d49SGordon Ross 	case (READ_CACHING | WRITE_CACHING | HANDLE_CACHING):
48894047d49SGordon Ross 		/* XXX: Should be a flag on the ofile. */
48994047d49SGordon Ross 		if (node->flags & NODE_FLAGS_WRITE_THROUGH) {
49094047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
49194047d49SGordon Ross 			goto out;
49294047d49SGordon Ross 		}
49394047d49SGordon Ross 		*statep = level;
49494047d49SGordon Ross 		status = smb_oplock_req_excl(ofile, statep);
49594047d49SGordon Ross 		break;
49694047d49SGordon Ross 
49794047d49SGordon Ross 	/*
49894047d49SGordon Ross 	 * Else if RequestedOplockLevel is 0 (that is, no flags):
49994047d49SGordon Ross 	 * The operation MUST return STATUS_SUCCESS at this point.
50094047d49SGordon Ross 	 */
50194047d49SGordon Ross 	case 0:
50294047d49SGordon Ross 		*statep = 0;
50394047d49SGordon Ross 		status = NT_STATUS_SUCCESS;
50494047d49SGordon Ross 		break;
50594047d49SGordon Ross 
50694047d49SGordon Ross 	/*
50794047d49SGordon Ross 	 * Else
50894047d49SGordon Ross 	 *  The operation MUST be failed with STATUS_INVALID_PARAMETER.
50994047d49SGordon Ross 	 */
51094047d49SGordon Ross 	default:
51194047d49SGordon Ross 		status = NT_STATUS_INVALID_PARAMETER;
51294047d49SGordon Ross 		break;
51394047d49SGordon Ross 	}
51494047d49SGordon Ross 
51572b35b05SGordon Ross 	/*
51672b35b05SGordon Ross 	 * Give caller back the "Granular" bit, eg. when
51772b35b05SGordon Ross 	 * NT_STATUS_SUCCESS or NT_STATUS_OPLOCK_BREAK_IN_PROGRESS
51872b35b05SGordon Ross 	 */
51972b35b05SGordon Ross 	if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_SUCCESS) {
52094047d49SGordon Ross 		*statep |= LEVEL_GRANULAR;
5211f0845f1SGordon Ross 	}
5221f0845f1SGordon Ross 
52394047d49SGordon Ross out:
52494047d49SGordon Ross 	return (status);
52594047d49SGordon Ross }
52694047d49SGordon Ross 
52794047d49SGordon Ross /*
52894047d49SGordon Ross  * 2.1.5.17.1 Algorithm to Request an Exclusive Oplock
52994047d49SGordon Ross  *
53094047d49SGordon Ross  * The inputs for requesting an exclusive oplock are:
53194047d49SGordon Ross  *	Open: The Open on which the oplock is being requested.
53294047d49SGordon Ross  *	RequestedOplock: The oplock type being requested. One of:
53394047d49SGordon Ross  *	  LEVEL_ONE, LEVEL_BATCH, CACHE_RW, CACHE_RWH
53494047d49SGordon Ross  *
53594047d49SGordon Ross  * On completion, the object store MUST return:
53694047d49SGordon Ross  *	Status: An NTSTATUS code that specifies the result.
53794047d49SGordon Ross  *	NewOplockLevel: The type of oplock that the requested oplock has been
53894047d49SGordon Ross  *	  broken (reduced) to.  If a failure status is returned in Status,
53994047d49SGordon Ross  *	  the value of this field is undefined.  Valid values are as follows:
54094047d49SGordon Ross  *		LEVEL_NONE (that is, no oplock)
54194047d49SGordon Ross  *		LEVEL_TWO
54294047d49SGordon Ross  *		A combination of one or more of the following flags:
54394047d49SGordon Ross  *			READ_CACHING
54494047d49SGordon Ross  *			HANDLE_CACHING
54594047d49SGordon Ross  *			WRITE_CACHING
54694047d49SGordon Ross  *	AcknowledgeRequired: A Boolean value: TRUE if the server MUST
54794047d49SGordon Ross  *	acknowledge the oplock break; FALSE if not, as specified in
54894047d49SGordon Ross  *	section 2.1.5.18. If a failure status is returned in Status,
54994047d49SGordon Ross  *	the value of this field is undefined.
55094047d49SGordon Ross  *
55194047d49SGordon Ross  * Note: Stores NewOplockLevel in *rop
55294047d49SGordon Ross  */
55394047d49SGordon Ross static uint32_t
smb_oplock_req_excl(smb_ofile_t * ofile,uint32_t * rop)55494047d49SGordon Ross smb_oplock_req_excl(
55594047d49SGordon Ross     smb_ofile_t *ofile,		/* in: the "Open" */
55694047d49SGordon Ross     uint32_t *rop)		/* in: "RequestedOplock", out:NewOplockLevel */
55794047d49SGordon Ross {
55894047d49SGordon Ross 	smb_node_t *node = ofile->f_node;
55994047d49SGordon Ross 	smb_ofile_t *o;
56094047d49SGordon Ross 	boolean_t GrantExcl = B_FALSE;
56194047d49SGordon Ross 	uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED;
56294047d49SGordon Ross 
56394047d49SGordon Ross 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
56494047d49SGordon Ross 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
56594047d49SGordon Ross 
566*7f6a299eSGordon Ross #ifdef	DEBUG
567*7f6a299eSGordon Ross 	FOREACH_NODE_OFILE(node, o) {
568*7f6a299eSGordon Ross 		DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
569*7f6a299eSGordon Ross 	}
570*7f6a299eSGordon Ross #endif
571*7f6a299eSGordon Ross 
5721f0845f1SGordon Ross 	/*
5731f0845f1SGordon Ross 	 * Don't allow grants on closing ofiles.
5741f0845f1SGordon Ross 	 */
5756f8336c5SGordon Ross 	if (ofile->f_oplock_closing)
5761f0845f1SGordon Ross 		return (status);
5771f0845f1SGordon Ross 
57894047d49SGordon Ross 	/*
57994047d49SGordon Ross 	 * If Open.Stream.Oplock is empty:
58094047d49SGordon Ross 	 *   Build a new Oplock object with fields initialized as follows:
58194047d49SGordon Ross 	 *	Oplock.State set to NO_OPLOCK.
58294047d49SGordon Ross 	 *	All other fields set to 0/empty.
58394047d49SGordon Ross 	 *   Store the new Oplock object in Open.Stream.Oplock.
58494047d49SGordon Ross 	 * EndIf
58594047d49SGordon Ross 	 *
58694047d49SGordon Ross 	 * Implementation specific:
58794047d49SGordon Ross 	 * Open.Stream.Oplock maps to: node->n_oplock
58894047d49SGordon Ross 	 */
58994047d49SGordon Ross 	if (node->n_oplock.ol_state == 0) {
59094047d49SGordon Ross 		node->n_oplock.ol_state = NO_OPLOCK;
59194047d49SGordon Ross 	}
59294047d49SGordon Ross 
59394047d49SGordon Ross 	/*
59494047d49SGordon Ross 	 * If Open.Stream.Oplock.State contains
59594047d49SGordon Ross 	 * LEVEL_TWO_OPLOCK or NO_OPLOCK: ...
59694047d49SGordon Ross 	 *
59794047d49SGordon Ross 	 * Per ms, this is the "If" matching the unbalalanced
59894047d49SGordon Ross 	 * "Else If" below (for which we requested clarification).
59994047d49SGordon Ross 	 */
60094047d49SGordon Ross 	if ((node->n_oplock.ol_state & (LEVEL_TWO | NO_OPLOCK)) != 0) {
60194047d49SGordon Ross 
60294047d49SGordon Ross 		/*
60394047d49SGordon Ross 		 * If Open.Stream.Oplock.State contains LEVEL_TWO_OPLOCK and
60494047d49SGordon Ross 		 * RequestedOplock contains one or more of READ_CACHING,
60594047d49SGordon Ross 		 * HANDLE_CACHING, or WRITE_CACHING, the operation MUST be
60694047d49SGordon Ross 		 * failed with Status set to STATUS_OPLOCK_NOT_GRANTED.
60794047d49SGordon Ross 		 */
60894047d49SGordon Ross 		if ((node->n_oplock.ol_state & LEVEL_TWO) != 0 &&
60994047d49SGordon Ross 		    (*rop & CACHE_RWH) != 0) {
61094047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
61194047d49SGordon Ross 			goto out;
61294047d49SGordon Ross 		}
61394047d49SGordon Ross 
61494047d49SGordon Ross 		/*
61594047d49SGordon Ross 		 * [ from dochelp@ms ]
61694047d49SGordon Ross 		 *
61794047d49SGordon Ross 		 * By this point if there is a level II oplock present,
61894047d49SGordon Ross 		 * the caller can only be requesting an old-style oplock
61994047d49SGordon Ross 		 * because we rejected enhanced oplock requests above.
62094047d49SGordon Ross 		 * If the caller is requesting an old-style oplock our
62194047d49SGordon Ross 		 * caller already verfied that there is only one handle
62294047d49SGordon Ross 		 * open to this stream, and we've already verified that
62394047d49SGordon Ross 		 * this request is for a legacy oplock, meaning that there
62494047d49SGordon Ross 		 * can be at most one level II oplock (and no R oplocks),
62594047d49SGordon Ross 		 * and the level II oplock belongs to this handle.  Clear
62694047d49SGordon Ross 		 * the level II oplock and grant the exclusive oplock.
62794047d49SGordon Ross 		 */
62894047d49SGordon Ross 
62994047d49SGordon Ross 		/*
63094047d49SGordon Ross 		 * If Open.Stream.Oplock.State is equal to LEVEL_TWO_OPLOCK:
63194047d49SGordon Ross 		 * Remove the first Open ThisOpen from
63294047d49SGordon Ross 		 *  Open.Stream.Oplock.IIOplocks (there is supposed to be
63394047d49SGordon Ross 		 * exactly one present), and notify the server of an
63494047d49SGordon Ross 		 * oplock break according to the algorithm in section
63594047d49SGordon Ross 		 *  2.1.5.17.3, setting the algorithm's parameters as follows:
63694047d49SGordon Ross 		 *	BreakingOplockOpen = ThisOpen.
63794047d49SGordon Ross 		 *	NewOplockLevel = LEVEL_NONE.
63894047d49SGordon Ross 		 *	AcknowledgeRequired = FALSE.
63994047d49SGordon Ross 		 *	OplockCompletionStatus = STATUS_SUCCESS.
64094047d49SGordon Ross 		 * (The operation does not end at this point; this call
64194047d49SGordon Ross 		 *  to 2.1.5.17.3 completes some earlier call to 2.1.5.17.2.)
64294047d49SGordon Ross 		 *
64394047d49SGordon Ross 		 * Implementation specific:
64494047d49SGordon Ross 		 *
64594047d49SGordon Ross 		 * As explained above, the passed in ofile should be the
64694047d49SGordon Ross 		 * only open file on this node.  Out of caution, we'll
64794047d49SGordon Ross 		 * walk the ofile list as usual here, making sure there
64894047d49SGordon Ross 		 * are no LevelII oplocks remaining, as those may not
64994047d49SGordon Ross 		 * coexist with the exclusive oplock were're creating
65094047d49SGordon Ross 		 * in this call.  Also, if the passed in ofile has a
65194047d49SGordon Ross 		 * LevelII oplock, don't do an "ind break" up call on
65294047d49SGordon Ross 		 * this ofile, as that would just cause an immediate
65394047d49SGordon Ross 		 * "break to none" of the oplock we'll grant here.
65494047d49SGordon Ross 		 * If there were other ofiles with LevelII oplocks,
65594047d49SGordon Ross 		 * it would be appropriate to "ind break" those.
65694047d49SGordon Ross 		 */
65794047d49SGordon Ross 		if ((node->n_oplock.ol_state & LEVEL_TWO) != 0) {
65894047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
65994047d49SGordon Ross 				if (o->f_oplock.onlist_II == 0)
66094047d49SGordon Ross 					continue;
66194047d49SGordon Ross 				o->f_oplock.onlist_II = B_FALSE;
66294047d49SGordon Ross 				node->n_oplock.cnt_II--;
66394047d49SGordon Ross 				ASSERT(node->n_oplock.cnt_II >= 0);
66494047d49SGordon Ross 				if (o == ofile)
66594047d49SGordon Ross 					continue;
666a5a9a6bbSGordon Ross 				DTRACE_PROBE1(unexpected, smb_ofile_t *, o);
66794047d49SGordon Ross 				smb_oplock_ind_break(o,
66894047d49SGordon Ross 				    LEVEL_NONE, B_FALSE,
66994047d49SGordon Ross 				    NT_STATUS_SUCCESS);
67094047d49SGordon Ross 			}
67194047d49SGordon Ross 		}
67294047d49SGordon Ross 
67394047d49SGordon Ross 		/*
67494047d49SGordon Ross 		 * Note the spec. had an extra "EndIf" here.
67594047d49SGordon Ross 		 * Confirmed by dochelp@ms
67694047d49SGordon Ross 		 */
67794047d49SGordon Ross 
67894047d49SGordon Ross 		/*
67994047d49SGordon Ross 		 * If Open.File.OpenList contains more than one Open whose
68094047d49SGordon Ross 		 * Stream is the same as Open.Stream, and NO_OPLOCK is present
68194047d49SGordon Ross 		 * in Open.Stream.Oplock.State, the operation MUST be failed
68294047d49SGordon Ross 		 * with Status set to STATUS_OPLOCK_NOT_GRANTED.
68394047d49SGordon Ross 		 *
68494047d49SGordon Ross 		 * Implementation specific:
68594047d49SGordon Ross 		 * Allow other opens if they have the same lease ours,
68694047d49SGordon Ross 		 * so we can upgrade RH to RWH (for example). Therefore
68794047d49SGordon Ross 		 * only count opens with a different TargetOplockKey.
68894047d49SGordon Ross 		 * Also ignore "attribute-only" opens.
68994047d49SGordon Ross 		 */
69094047d49SGordon Ross 		if ((node->n_oplock.ol_state & NO_OPLOCK) != 0) {
69194047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
69294047d49SGordon Ross 				if (!smb_ofile_is_open(o))
69394047d49SGordon Ross 					continue;
69494047d49SGordon Ross 				if ((o->f_granted_access & FILE_DATA_ALL) == 0)
69594047d49SGordon Ross 					continue;
69694047d49SGordon Ross 				if (!CompareOplockKeys(ofile, o, 0)) {
69794047d49SGordon Ross 					status = NT_STATUS_OPLOCK_NOT_GRANTED;
69894047d49SGordon Ross 					goto out;
69994047d49SGordon Ross 				}
70094047d49SGordon Ross 			}
70194047d49SGordon Ross 		}
70294047d49SGordon Ross 
70394047d49SGordon Ross 		/*
70494047d49SGordon Ross 		 * If Open.Stream.IsDeleted is TRUE and RequestedOplock
70594047d49SGordon Ross 		 * contains HANDLE_CACHING, the operation MUST be failed
70694047d49SGordon Ross 		 * with Status set to STATUS_OPLOCK_NOT_GRANTED.
70794047d49SGordon Ross 		 */
70894047d49SGordon Ross 		if (((node->flags & NODE_FLAGS_DELETING) != 0) &&
70994047d49SGordon Ross 		    (*rop & HANDLE_CACHING) != 0) {
71094047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
71194047d49SGordon Ross 			goto out;
71294047d49SGordon Ross 		}
71394047d49SGordon Ross 
71494047d49SGordon Ross 		/* Set GrantExclusiveOplock to TRUE. */
71594047d49SGordon Ross 		GrantExcl = B_TRUE;
71694047d49SGordon Ross 	}
71794047d49SGordon Ross 
71894047d49SGordon Ross 	/*
71994047d49SGordon Ross 	 * "Else" If (Open.Stream.Oplock.State contains one or more of
72094047d49SGordon Ross 	 * READ_CACHING, WRITE_CACHING, or HANDLE_CACHING) and
72194047d49SGordon Ross 	 * (Open.Stream.Oplock.State contains none of (BREAK_ANY)) and
72294047d49SGordon Ross 	 * (Open.Stream.Oplock.RHBreakQueue is empty):
72394047d49SGordon Ross 	 */
72494047d49SGordon Ross 	else if ((node->n_oplock.ol_state & CACHE_RWH) != 0 &&
72594047d49SGordon Ross 	    (node->n_oplock.ol_state & BREAK_ANY) == 0 &&
72694047d49SGordon Ross 	    node->n_oplock.cnt_RHBQ == 0) {
72794047d49SGordon Ross 
72894047d49SGordon Ross 		/*
72994047d49SGordon Ross 		 * This is a granular oplock and it is not breaking.
73094047d49SGordon Ross 		 */
73194047d49SGordon Ross 
73294047d49SGordon Ross 		/*
73394047d49SGordon Ross 		 * If RequestedOplock contains none of READ_CACHING,
73494047d49SGordon Ross 		 * WRITE_CACHING, or HANDLE_CACHING, the operation
73594047d49SGordon Ross 		 * MUST be failed with Status set to
73694047d49SGordon Ross 		 * STATUS_OPLOCK_NOT_GRANTED.
73794047d49SGordon Ross 		 */
73894047d49SGordon Ross 		if ((*rop & CACHE_RWH) == 0) {
73994047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
74094047d49SGordon Ross 			goto out;
74194047d49SGordon Ross 		}
74294047d49SGordon Ross 
74394047d49SGordon Ross 		/*
74494047d49SGordon Ross 		 * If Open.Stream.IsDeleted (already checked above)
74594047d49SGordon Ross 		 */
74694047d49SGordon Ross 
74794047d49SGordon Ross 		/*
74894047d49SGordon Ross 		 * Switch (Open.Stream.Oplock.State):
74994047d49SGordon Ross 		 */
75094047d49SGordon Ross 		switch (node->n_oplock.ol_state) {
75194047d49SGordon Ross 
75294047d49SGordon Ross 		case CACHE_R:
75394047d49SGordon Ross 			/*
75494047d49SGordon Ross 			 * If RequestedOplock is neither
75594047d49SGordon Ross 			 * (READ_CACHING|WRITE_CACHING) nor
75694047d49SGordon Ross 			 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING),
75794047d49SGordon Ross 			 * the operation MUST be failed with Status set
75894047d49SGordon Ross 			 * to STATUS_OPLOCK_NOT_GRANTED.
75994047d49SGordon Ross 			 */
76094047d49SGordon Ross 			if (*rop != CACHE_RW && *rop != CACHE_RWH) {
76194047d49SGordon Ross 				status = NT_STATUS_OPLOCK_NOT_GRANTED;
76294047d49SGordon Ross 				goto out;
76394047d49SGordon Ross 			}
76494047d49SGordon Ross 
76594047d49SGordon Ross 			/*
76694047d49SGordon Ross 			 * For each Open ThisOpen in
76794047d49SGordon Ross 			 *  Open.Stream.Oplock.ROplocks:
76894047d49SGordon Ross 			 *	If ThisOpen.TargetOplockKey !=
76994047d49SGordon Ross 			 *	Open.TargetOplockKey, the operation
77094047d49SGordon Ross 			 *	MUST be failed with Status set to
77194047d49SGordon Ross 			 *	STATUS_OPLOCK_NOT_GRANTED.
77294047d49SGordon Ross 			 * EndFor
77394047d49SGordon Ross 			 */
77494047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
77594047d49SGordon Ross 				if (o->f_oplock.onlist_R == 0)
77694047d49SGordon Ross 					continue;
77794047d49SGordon Ross 				if (!CompareOplockKeys(ofile, o, 0)) {
77894047d49SGordon Ross 					status = NT_STATUS_OPLOCK_NOT_GRANTED;
77994047d49SGordon Ross 					goto out;
78094047d49SGordon Ross 				}
78194047d49SGordon Ross 			}
78294047d49SGordon Ross 
78394047d49SGordon Ross 			/*
78494047d49SGordon Ross 			 * For each Open o in Open.Stream.Oplock.ROplocks:
78594047d49SGordon Ross 			 *	Remove o from Open.Stream.Oplock.ROplocks.
78694047d49SGordon Ross 			 *	Notify the server of an oplock break
78794047d49SGordon Ross 			 *	according to the algorithm in section
78894047d49SGordon Ross 			 *	2.1.5.17.3, setting the algorithm's
78994047d49SGordon Ross 			 *	parameters as follows:
79094047d49SGordon Ross 			 *		BreakingOplockOpen = o.
79194047d49SGordon Ross 			 *		NewOplockLevel = RequestedOplock.
79294047d49SGordon Ross 			 *		AcknowledgeRequired = FALSE.
79394047d49SGordon Ross 			 *		OplockCompletionStatus =
79494047d49SGordon Ross 			 *		  STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
79594047d49SGordon Ross 			 *	(The operation does not end at this point;
79694047d49SGordon Ross 			 *	 this call to 2.1.5.17.3 completes some
79794047d49SGordon Ross 			 *	 earlier call to 2.1.5.17.2.)
79894047d49SGordon Ross 			 * EndFor
79994047d49SGordon Ross 			 *
80094047d49SGordon Ross 			 * Note: Upgrade to excl. on same lease.
80194047d49SGordon Ross 			 * Won't send a break for this.
80294047d49SGordon Ross 			 */
80394047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
80494047d49SGordon Ross 				if (o->f_oplock.onlist_R == 0)
80594047d49SGordon Ross 					continue;
80694047d49SGordon Ross 				o->f_oplock.onlist_R = B_FALSE;
80794047d49SGordon Ross 				node->n_oplock.cnt_R--;
80894047d49SGordon Ross 				ASSERT(node->n_oplock.cnt_R >= 0);
80994047d49SGordon Ross 
81094047d49SGordon Ross 				smb_oplock_ind_break(o, *rop,
81194047d49SGordon Ross 				    B_FALSE, STATUS_NEW_HANDLE);
81294047d49SGordon Ross 			}
81394047d49SGordon Ross 			/*
81494047d49SGordon Ross 			 * Set GrantExclusiveOplock to TRUE.
81594047d49SGordon Ross 			 * EndCase // _R
81694047d49SGordon Ross 			 */
81794047d49SGordon Ross 			GrantExcl = B_TRUE;
81894047d49SGordon Ross 			break;
81994047d49SGordon Ross 
82094047d49SGordon Ross 		case CACHE_RH:
82194047d49SGordon Ross 			/*
82294047d49SGordon Ross 			 * If RequestedOplock is not
82394047d49SGordon Ross 			 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING)
82494047d49SGordon Ross 			 * or Open.Stream.Oplock.RHBreakQueue is not empty,
82594047d49SGordon Ross 			 * the operation MUST be failed with Status set to
82694047d49SGordon Ross 			 * STATUS_OPLOCK_NOT_GRANTED.
82794047d49SGordon Ross 			 * Note: Have RHBreakQueue==0 from above.
82894047d49SGordon Ross 			 */
82994047d49SGordon Ross 			if (*rop != CACHE_RWH) {
83094047d49SGordon Ross 				status = NT_STATUS_OPLOCK_NOT_GRANTED;
83194047d49SGordon Ross 				goto out;
83294047d49SGordon Ross 			}
83394047d49SGordon Ross 
83494047d49SGordon Ross 			/*
83594047d49SGordon Ross 			 * For each Open ThisOpen in
83694047d49SGordon Ross 			 *  Open.Stream.Oplock.RHOplocks:
83794047d49SGordon Ross 			 *	If ThisOpen.TargetOplockKey !=
83894047d49SGordon Ross 			 *	Open.TargetOplockKey, the operation
83994047d49SGordon Ross 			 *	MUST be failed with Status set to
84094047d49SGordon Ross 			 *	STATUS_OPLOCK_NOT_GRANTED.
84194047d49SGordon Ross 			 * EndFor
84294047d49SGordon Ross 			 */
84394047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
84494047d49SGordon Ross 				if (o->f_oplock.onlist_RH == 0)
84594047d49SGordon Ross 					continue;
84694047d49SGordon Ross 				if (!CompareOplockKeys(ofile, o, 0)) {
84794047d49SGordon Ross 					status = NT_STATUS_OPLOCK_NOT_GRANTED;
84894047d49SGordon Ross 					goto out;
84994047d49SGordon Ross 				}
85094047d49SGordon Ross 			}
85194047d49SGordon Ross 
85294047d49SGordon Ross 			/*
85394047d49SGordon Ross 			 * For each Open o in Open.Stream.Oplock.RHOplocks:
85494047d49SGordon Ross 			 *	Remove o from Open.Stream.Oplock.RHOplocks.
85594047d49SGordon Ross 			 *	Notify the server of an oplock break
85694047d49SGordon Ross 			 *	according to the algorithm in section
85794047d49SGordon Ross 			 *	2.1.5.17.3, setting the algorithm's
85894047d49SGordon Ross 			 *	parameters as follows:
85994047d49SGordon Ross 			 *		BreakingOplockOpen = o.
86094047d49SGordon Ross 			 *		NewOplockLevel = RequestedOplock.
86194047d49SGordon Ross 			 *		AcknowledgeRequired = FALSE.
86294047d49SGordon Ross 			 *		OplockCompletionStatus =
86394047d49SGordon Ross 			 *		  STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
86494047d49SGordon Ross 			 *	(The operation does not end at this point;
86594047d49SGordon Ross 			 *	 this call to 2.1.5.17.3 completes some
86694047d49SGordon Ross 			 *	 earlier call to 2.1.5.17.2.)
86794047d49SGordon Ross 			 * EndFor
86894047d49SGordon Ross 			 *
86994047d49SGordon Ross 			 * Note: Upgrade to excl. on same lease.
87094047d49SGordon Ross 			 * Won't send a break for this.
87194047d49SGordon Ross 			 */
87294047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
87394047d49SGordon Ross 				if (o->f_oplock.onlist_RH == 0)
87494047d49SGordon Ross 					continue;
87594047d49SGordon Ross 				o->f_oplock.onlist_RH = B_FALSE;
87694047d49SGordon Ross 				node->n_oplock.cnt_RH--;
87794047d49SGordon Ross 				ASSERT(node->n_oplock.cnt_RH >= 0);
87894047d49SGordon Ross 
87994047d49SGordon Ross 				smb_oplock_ind_break(o, *rop,
88094047d49SGordon Ross 				    B_FALSE, STATUS_NEW_HANDLE);
88194047d49SGordon Ross 			}
88294047d49SGordon Ross 			/*
88394047d49SGordon Ross 			 * Set GrantExclusiveOplock to TRUE.
88494047d49SGordon Ross 			 * EndCase // _RH
88594047d49SGordon Ross 			 */
88694047d49SGordon Ross 			GrantExcl = B_TRUE;
88794047d49SGordon Ross 			break;
88894047d49SGordon Ross 
88994047d49SGordon Ross 		case (CACHE_RWH | EXCLUSIVE):
89094047d49SGordon Ross 			/*
89194047d49SGordon Ross 			 * If RequestedOplock is not
89294047d49SGordon Ross 			 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING),
89394047d49SGordon Ross 			 * the operation MUST be failed with Status set to
89494047d49SGordon Ross 			 * STATUS_OPLOCK_NOT_GRANTED.
89594047d49SGordon Ross 			 */
89694047d49SGordon Ross 			if (*rop != CACHE_RWH) {
89794047d49SGordon Ross 				status = NT_STATUS_OPLOCK_NOT_GRANTED;
89894047d49SGordon Ross 				goto out;
89994047d49SGordon Ross 			}
90094047d49SGordon Ross 			/* Deliberate FALL-THROUGH to next Case statement. */
90194047d49SGordon Ross 			/* FALLTHROUGH */
90294047d49SGordon Ross 
90394047d49SGordon Ross 		case (CACHE_RW | EXCLUSIVE):
90494047d49SGordon Ross 			/*
90594047d49SGordon Ross 			 * If RequestedOplock is neither
90694047d49SGordon Ross 			 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING) nor
90794047d49SGordon Ross 			 * (READ_CACHING|WRITE_CACHING), the operation MUST be
90894047d49SGordon Ross 			 * failed with Status set to STATUS_OPLOCK_NOT_GRANTED.
90994047d49SGordon Ross 			 */
91094047d49SGordon Ross 			if (*rop != CACHE_RWH && *rop != CACHE_RW) {
91194047d49SGordon Ross 				status = NT_STATUS_OPLOCK_NOT_GRANTED;
91294047d49SGordon Ross 				goto out;
91394047d49SGordon Ross 			}
91494047d49SGordon Ross 
91594047d49SGordon Ross 			o = node->n_oplock.excl_open;
91694047d49SGordon Ross 			if (o == NULL) {
91794047d49SGordon Ross 				ASSERT(0);
91894047d49SGordon Ross 				GrantExcl = B_TRUE;
91994047d49SGordon Ross 				break;
92094047d49SGordon Ross 			}
92194047d49SGordon Ross 
92294047d49SGordon Ross 			/*
92394047d49SGordon Ross 			 * If Open.TargetOplockKey !=
92494047d49SGordon Ross 			 * Open.Stream.Oplock.ExclusiveOpen.TargetOplockKey,
92594047d49SGordon Ross 			 * the operation MUST be failed with Status set to
92694047d49SGordon Ross 			 * STATUS_OPLOCK_NOT_GRANTED.
92794047d49SGordon Ross 			 */
92894047d49SGordon Ross 			if (!CompareOplockKeys(ofile, o, 0)) {
92994047d49SGordon Ross 				status = NT_STATUS_OPLOCK_NOT_GRANTED;
93094047d49SGordon Ross 				goto out;
93194047d49SGordon Ross 			}
93294047d49SGordon Ross 
93394047d49SGordon Ross 			/*
93494047d49SGordon Ross 			 * Notify the server of an oplock break according to
93594047d49SGordon Ross 			 * the algorithm in section 2.1.5.17.3, setting the
93694047d49SGordon Ross 			 * algorithm's parameters as follows:
93794047d49SGordon Ross 			 *	BreakingOplockOpen =
93894047d49SGordon Ross 			 *	  Open.Stream.Oplock.ExclusiveOpen.
93994047d49SGordon Ross 			 *	NewOplockLevel = RequestedOplock.
94094047d49SGordon Ross 			 *	AcknowledgeRequired = FALSE.
94194047d49SGordon Ross 			 *	OplockCompletionStatus =
94294047d49SGordon Ross 			 *	  STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
94394047d49SGordon Ross 			 * (The operation does not end at this point;
94494047d49SGordon Ross 			 *  this call to 2.1.5.17.3 completes some
94594047d49SGordon Ross 			 *  earlier call to 2.1.5.17.1.)
94694047d49SGordon Ross 			 *
94794047d49SGordon Ross 			 * Set Open.Stream.Oplock.ExclusiveOpen to NULL.
94894047d49SGordon Ross 			 * Set GrantExclusiveOplock to TRUE.
94994047d49SGordon Ross 			 *
95094047d49SGordon Ross 			 * Note: We will keep this exclusive oplock,
95194047d49SGordon Ross 			 * but move it to a new handle on this lease.
95294047d49SGordon Ross 			 * Won't send a break for this.
95394047d49SGordon Ross 			 */
95494047d49SGordon Ross 			smb_oplock_ind_break(o, *rop,
95594047d49SGordon Ross 			    B_FALSE, STATUS_NEW_HANDLE);
95694047d49SGordon Ross 			node->n_oplock.excl_open = o = NULL;
95794047d49SGordon Ross 			GrantExcl = B_TRUE;
95894047d49SGordon Ross 			break;
95994047d49SGordon Ross 
96094047d49SGordon Ross 		default:
96194047d49SGordon Ross 			/*
96294047d49SGordon Ross 			 * The operation MUST be failed with Status set to
96394047d49SGordon Ross 			 * STATUS_OPLOCK_NOT_GRANTED.
96494047d49SGordon Ross 			 */
96594047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
96694047d49SGordon Ross 			goto out;
96794047d49SGordon Ross 
96894047d49SGordon Ross 		} /* switch n_oplock.ol_state */
96994047d49SGordon Ross 	} /* EndIf CACHE_RWH & !BREAK_ANY... */
97094047d49SGordon Ross 	else {
97194047d49SGordon Ross 		/*
97294047d49SGordon Ross 		 * The operation MUST be failed with...
97394047d49SGordon Ross 		 */
97494047d49SGordon Ross 		status = NT_STATUS_OPLOCK_NOT_GRANTED;
97594047d49SGordon Ross 		goto out;
97694047d49SGordon Ross 	}
97794047d49SGordon Ross 
97894047d49SGordon Ross 	/*
97994047d49SGordon Ross 	 * If GrantExclusiveOplock is TRUE:
98094047d49SGordon Ross 	 *
98194047d49SGordon Ross 	 * Set Open.Stream.Oplock.ExclusiveOpen = Open.
98294047d49SGordon Ross 	 * Set Open.Stream.Oplock.State =
98394047d49SGordon Ross 	 *   (RequestedOplock|EXCLUSIVE).
98494047d49SGordon Ross 	 */
98594047d49SGordon Ross 	if (GrantExcl) {
98694047d49SGordon Ross 		node->n_oplock.excl_open = ofile;
98794047d49SGordon Ross 		node->n_oplock.ol_state = *rop | EXCLUSIVE;
98894047d49SGordon Ross 
98994047d49SGordon Ross 		/*
99094047d49SGordon Ross 		 * This operation MUST be made cancelable...
99194047d49SGordon Ross 		 * This operation waits until the oplock is
99294047d49SGordon Ross 		 * broken or canceled, as specified in
993e8754e84SGordon Ross 		 * section 2.1.5.17.3. Note: This function
994e8754e84SGordon Ross 		 * does not cause breaks that require a wait,
995e8754e84SGordon Ross 		 * so never returns ..._BREAK_IN_PROGRESS.
99694047d49SGordon Ross 		 *
99794047d49SGordon Ross 		 * When the operation specified in section
99894047d49SGordon Ross 		 * 2.1.5.17.3 is called, its following input
99994047d49SGordon Ross 		 * parameters are transferred to this routine
100094047d49SGordon Ross 		 * and then returned by it:
100194047d49SGordon Ross 		 *
100294047d49SGordon Ross 		 * Status is set to OplockCompletionStatus
100394047d49SGordon Ross 		 * NewOplockLevel, AcknowledgeRequired...
100494047d49SGordon Ross 		 * from the operation specified in
100594047d49SGordon Ross 		 * section 2.1.5.17.3.
100694047d49SGordon Ross 		 */
100794047d49SGordon Ross 		/* Keep *rop = ... from caller. */
1008e8754e84SGordon Ross 		status = NT_STATUS_SUCCESS;
1009e8754e84SGordon Ross 
1010e8754e84SGordon Ross 		/*
1011e8754e84SGordon Ross 		 * First oplock grant installs FEM hooks.
1012e8754e84SGordon Ross 		 */
1013e8754e84SGordon Ross 		if (node->n_oplock.ol_fem == B_FALSE) {
1014e8754e84SGordon Ross 			if (smb_fem_oplock_install(node) != 0) {
1015e8754e84SGordon Ross 				cmn_err(CE_NOTE,
1016e8754e84SGordon Ross 				    "smb_fem_oplock_install failed");
1017e8754e84SGordon Ross 			} else {
1018e8754e84SGordon Ross 				node->n_oplock.ol_fem =	B_TRUE;
1019e8754e84SGordon Ross 			}
102094047d49SGordon Ross 		}
102194047d49SGordon Ross 	}
102294047d49SGordon Ross 
102394047d49SGordon Ross out:
102494047d49SGordon Ross 	if (status == NT_STATUS_OPLOCK_NOT_GRANTED)
102594047d49SGordon Ross 		*rop = LEVEL_NONE;
102694047d49SGordon Ross 
102794047d49SGordon Ross 	return (status);
102894047d49SGordon Ross }
102994047d49SGordon Ross 
103094047d49SGordon Ross /*
103194047d49SGordon Ross  * 2.1.5.17.2 Algorithm to Request a Shared Oplock
103294047d49SGordon Ross  *
103394047d49SGordon Ross  * The inputs for requesting a shared oplock are:
103494047d49SGordon Ross  *	Open: The Open on which the oplock is being requested.
103594047d49SGordon Ross  *	RequestedOplock: The oplock type being requested.
103694047d49SGordon Ross  *	GrantingInAck: A Boolean value, TRUE if this oplock is being
103794047d49SGordon Ross  *	  requested as part of an oplock break acknowledgement,
103894047d49SGordon Ross  *	  FALSE if not.
103994047d49SGordon Ross  *
104094047d49SGordon Ross  * On completion, the object store MUST return:
104194047d49SGordon Ross  *	Status: An NTSTATUS code that specifies the result.
104294047d49SGordon Ross  *	NewOplockLevel: The type of oplock that the requested oplock has been
104394047d49SGordon Ross  *	  broken (reduced) to.  If a failure status is returned in Status,
104494047d49SGordon Ross  *	  the value of this field is undefined.  Valid values are as follows:
104594047d49SGordon Ross  *		LEVEL_NONE (that is, no oplock)
104694047d49SGordon Ross  *		LEVEL_TWO
104794047d49SGordon Ross  *		A combination of one or more of the following flags:
104894047d49SGordon Ross  *			READ_CACHING
104994047d49SGordon Ross  *			HANDLE_CACHING
105094047d49SGordon Ross  *			WRITE_CACHING
105194047d49SGordon Ross  *	AcknowledgeRequired: A Boolean value: TRUE if the server MUST
105294047d49SGordon Ross  *	acknowledge the oplock break; FALSE if not, as specified in
105394047d49SGordon Ross  *	section 2.1.5.18. If a failure status is returned in Status,
105494047d49SGordon Ross  *	the value of this field is undefined.
105594047d49SGordon Ross  *
105694047d49SGordon Ross  * Note: Stores NewOplockLevel in *rop
105794047d49SGordon Ross  */
105894047d49SGordon Ross static uint32_t
smb_oplock_req_shared(smb_ofile_t * ofile,uint32_t * rop,boolean_t GrantingInAck)105994047d49SGordon Ross smb_oplock_req_shared(
106094047d49SGordon Ross     smb_ofile_t *ofile,		/* in: the "Open" */
106194047d49SGordon Ross     uint32_t *rop,		/* in: "RequestedOplock", out:NewOplockLevel */
106294047d49SGordon Ross     boolean_t GrantingInAck)
106394047d49SGordon Ross {
106494047d49SGordon Ross 	smb_node_t *node = ofile->f_node;
106594047d49SGordon Ross 	smb_ofile_t *o;
106694047d49SGordon Ross 	boolean_t OplockGranted = B_FALSE;
106794047d49SGordon Ross 	uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED;
106894047d49SGordon Ross 
106994047d49SGordon Ross 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
107094047d49SGordon Ross 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
107194047d49SGordon Ross 
1072*7f6a299eSGordon Ross #ifdef	DEBUG
1073*7f6a299eSGordon Ross 	FOREACH_NODE_OFILE(node, o) {
1074*7f6a299eSGordon Ross 		DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
1075*7f6a299eSGordon Ross 	}
1076*7f6a299eSGordon Ross #endif
1077*7f6a299eSGordon Ross 
10781f0845f1SGordon Ross 	/*
10791f0845f1SGordon Ross 	 * Don't allow grants on closing ofiles.
10801f0845f1SGordon Ross 	 */
10816f8336c5SGordon Ross 	if (ofile->f_oplock_closing)
10821f0845f1SGordon Ross 		return (status);
10831f0845f1SGordon Ross 
108494047d49SGordon Ross 	/*
108594047d49SGordon Ross 	 * If Open.Stream.Oplock is empty:
108694047d49SGordon Ross 	 *   Build a new Oplock object with fields initialized as follows:
108794047d49SGordon Ross 	 *	Oplock.State set to NO_OPLOCK.
108894047d49SGordon Ross 	 *	All other fields set to 0/empty.
108994047d49SGordon Ross 	 *   Store the new Oplock object in Open.Stream.Oplock.
109094047d49SGordon Ross 	 * EndIf
109194047d49SGordon Ross 	 *
109294047d49SGordon Ross 	 * Implementation specific:
109394047d49SGordon Ross 	 * Open.Stream.Oplock maps to: node->n_oplock
109494047d49SGordon Ross 	 */
109594047d49SGordon Ross 	if (node->n_oplock.ol_state == 0) {
109694047d49SGordon Ross 		node->n_oplock.ol_state = NO_OPLOCK;
109794047d49SGordon Ross 	}
109894047d49SGordon Ross 
109994047d49SGordon Ross 	/*
110094047d49SGordon Ross 	 * If (GrantingInAck is FALSE) and (Open.Stream.Oplock.State
110194047d49SGordon Ross 	 * contains one or more of BREAK_TO_TWO, BREAK_TO_NONE,
110294047d49SGordon Ross 	 * BREAK_TO_TWO_TO_NONE, BREAK_TO_READ_CACHING,
110394047d49SGordon Ross 	 * BREAK_TO_WRITE_CACHING, BREAK_TO_HANDLE_CACHING,
110494047d49SGordon Ross 	 * BREAK_TO_NO_CACHING, or EXCLUSIVE), then:
110594047d49SGordon Ross 	 *	The operation MUST be failed with Status set to
110694047d49SGordon Ross 	 *	STATUS_OPLOCK_NOT_GRANTED.
110794047d49SGordon Ross 	 * EndIf
110894047d49SGordon Ross 	 */
110994047d49SGordon Ross 	if (GrantingInAck == B_FALSE &&
111094047d49SGordon Ross 	    (node->n_oplock.ol_state & (BREAK_ANY | EXCLUSIVE)) != 0) {
111194047d49SGordon Ross 		status = NT_STATUS_OPLOCK_NOT_GRANTED;
111294047d49SGordon Ross 		goto out;
111394047d49SGordon Ross 	}
111494047d49SGordon Ross 
111594047d49SGordon Ross 	/* Switch (RequestedOplock): */
111694047d49SGordon Ross 	switch (*rop) {
111794047d49SGordon Ross 
111894047d49SGordon Ross 	case LEVEL_TWO:
111994047d49SGordon Ross 		/*
112094047d49SGordon Ross 		 * The operation MUST be failed with Status set to
112194047d49SGordon Ross 		 * STATUS_OPLOCK_NOT_GRANTED if Open.Stream.Oplock.State
112294047d49SGordon Ross 		 * is anything other than the following:
112394047d49SGordon Ross 		 *	NO_OPLOCK
112494047d49SGordon Ross 		 *	LEVEL_TWO_OPLOCK
112594047d49SGordon Ross 		 *	READ_CACHING
112694047d49SGordon Ross 		 *	(LEVEL_TWO_OPLOCK|READ_CACHING)
112794047d49SGordon Ross 		 */
112894047d49SGordon Ross 		switch (node->n_oplock.ol_state) {
112994047d49SGordon Ross 		default:
113094047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
113194047d49SGordon Ross 			goto out;
113294047d49SGordon Ross 		case NO_OPLOCK:
113394047d49SGordon Ross 		case LEVEL_TWO:
113494047d49SGordon Ross 		case READ_CACHING:
113594047d49SGordon Ross 		case (LEVEL_TWO | READ_CACHING):
113694047d49SGordon Ross 			break;
113794047d49SGordon Ross 		}
113894047d49SGordon Ross 		/* Deliberate FALL-THROUGH to next Case statement. */
113994047d49SGordon Ross 		/* FALLTHROUGH */
114094047d49SGordon Ross 
114194047d49SGordon Ross 	case READ_CACHING:
114294047d49SGordon Ross 		/*
114394047d49SGordon Ross 		 * The operation MUST be failed with Status set to
114494047d49SGordon Ross 		 * STATUS_OPLOCK_NOT_GRANTED if GrantingInAck is FALSE
114594047d49SGordon Ross 		 * and Open.Stream.Oplock.State is anything other than...
114694047d49SGordon Ross 		 */
114794047d49SGordon Ross 		switch (node->n_oplock.ol_state) {
114894047d49SGordon Ross 		default:
114994047d49SGordon Ross 			if (GrantingInAck == B_FALSE) {
115094047d49SGordon Ross 				status = NT_STATUS_OPLOCK_NOT_GRANTED;
115194047d49SGordon Ross 				goto out;
115294047d49SGordon Ross 			}
115394047d49SGordon Ross 			break;
115494047d49SGordon Ross 		case NO_OPLOCK:
115594047d49SGordon Ross 		case LEVEL_TWO:
115694047d49SGordon Ross 		case READ_CACHING:
115794047d49SGordon Ross 		case (LEVEL_TWO | READ_CACHING):
115894047d49SGordon Ross 		case (READ_CACHING | HANDLE_CACHING):
115994047d49SGordon Ross 		case (READ_CACHING | HANDLE_CACHING | MIXED_R_AND_RH):
116094047d49SGordon Ross 		case (READ_CACHING | HANDLE_CACHING | BREAK_TO_READ_CACHING):
116194047d49SGordon Ross 		case (READ_CACHING | HANDLE_CACHING | BREAK_TO_NO_CACHING):
116294047d49SGordon Ross 			break;
116394047d49SGordon Ross 		}
116494047d49SGordon Ross 
116594047d49SGordon Ross 		if (GrantingInAck == B_FALSE) {
116694047d49SGordon Ross 			/*
116794047d49SGordon Ross 			 * If there is an Open on
116894047d49SGordon Ross 			 * Open.Stream.Oplock.RHOplocks
116994047d49SGordon Ross 			 * whose TargetOplockKey is equal to
117094047d49SGordon Ross 			 * Open.TargetOplockKey, the operation
117194047d49SGordon Ross 			 * MUST be failed with Status set to
117294047d49SGordon Ross 			 * STATUS_OPLOCK_NOT_GRANTED.
117394047d49SGordon Ross 			 *
117494047d49SGordon Ross 			 * If there is an Open on
117594047d49SGordon Ross 			 * Open.Stream.Oplock.RHBreakQueue
117694047d49SGordon Ross 			 * whose TargetOplockKey is equal to
117794047d49SGordon Ross 			 * Open.TargetOplockKey, the operation
117894047d49SGordon Ross 			 * MUST be failed with Status set to
117994047d49SGordon Ross 			 * STATUS_OPLOCK_NOT_GRANTED.
118094047d49SGordon Ross 			 *
118194047d49SGordon Ross 			 * Implement both in one list walk.
118294047d49SGordon Ross 			 */
118394047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
118494047d49SGordon Ross 				if ((o->f_oplock.onlist_RH ||
118594047d49SGordon Ross 				    o->f_oplock.onlist_RHBQ) &&
118694047d49SGordon Ross 				    CompareOplockKeys(ofile, o, 0)) {
118794047d49SGordon Ross 					status = NT_STATUS_OPLOCK_NOT_GRANTED;
118894047d49SGordon Ross 					goto out;
118994047d49SGordon Ross 				}
119094047d49SGordon Ross 			}
119194047d49SGordon Ross 
119294047d49SGordon Ross 			/*
119394047d49SGordon Ross 			 * If there is an Open ThisOpen on
119494047d49SGordon Ross 			 * Open.Stream.Oplock.ROplocks whose
119594047d49SGordon Ross 			 * TargetOplockKey is equal to Open.TargetOplockKey
119694047d49SGordon Ross 			 * (there is supposed to be at most one present):
119794047d49SGordon Ross 			 *	* Remove ThisOpen from Open...ROplocks.
119894047d49SGordon Ross 			 *	* Notify the server of an oplock break
119994047d49SGordon Ross 			 *	  according to the algorithm in section
120094047d49SGordon Ross 			 *	  2.1.5.17.3, setting the algorithm's
120194047d49SGordon Ross 			 *	  parameters as follows:
120294047d49SGordon Ross 			 *		* BreakingOplockOpen = ThisOpen
120394047d49SGordon Ross 			 *		* NewOplockLevel = READ_CACHING
120494047d49SGordon Ross 			 *		* AcknowledgeRequired = FALSE
120594047d49SGordon Ross 			 *		* OplockCompletionStatus =
120694047d49SGordon Ross 			 *		  STATUS_..._NEW_HANDLE
120794047d49SGordon Ross 			 * (The operation does not end at this point;
120894047d49SGordon Ross 			 *  this call to 2.1.5.17.3 completes some
120994047d49SGordon Ross 			 *  earlier call to 2.1.5.17.2.)
121094047d49SGordon Ross 			 * EndIf
121194047d49SGordon Ross 			 *
121294047d49SGordon Ross 			 * If this SMB2 lease already has an "R" handle,
121394047d49SGordon Ross 			 * we'll update that lease locally to point to
121494047d49SGordon Ross 			 * this new handle.
121594047d49SGordon Ross 			 */
121694047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
121794047d49SGordon Ross 				if (o->f_oplock.onlist_R == 0)
121894047d49SGordon Ross 					continue;
121994047d49SGordon Ross 				if (CompareOplockKeys(ofile, o, 0)) {
122094047d49SGordon Ross 					o->f_oplock.onlist_R = B_FALSE;
122194047d49SGordon Ross 					node->n_oplock.cnt_R--;
122294047d49SGordon Ross 					ASSERT(node->n_oplock.cnt_R >= 0);
122394047d49SGordon Ross 					smb_oplock_ind_break(o,
122494047d49SGordon Ross 					    CACHE_R, B_FALSE,
122594047d49SGordon Ross 					    STATUS_NEW_HANDLE);
122694047d49SGordon Ross 				}
122794047d49SGordon Ross 			}
122894047d49SGordon Ross 		} /* EndIf !GrantingInAck */
122994047d49SGordon Ross 
123094047d49SGordon Ross 		/*
123194047d49SGordon Ross 		 * If RequestedOplock equals LEVEL_TWO:
123294047d49SGordon Ross 		 *	Add Open to Open.Stream.Oplock.IIOplocks.
123394047d49SGordon Ross 		 * Else // RequestedOplock equals READ_CACHING:
123494047d49SGordon Ross 		 *	Add Open to Open.Stream.Oplock.ROplocks.
123594047d49SGordon Ross 		 * EndIf
123694047d49SGordon Ross 		 */
123794047d49SGordon Ross 		if (*rop == LEVEL_TWO) {
123894047d49SGordon Ross 			ofile->f_oplock.onlist_II = B_TRUE;
123994047d49SGordon Ross 			node->n_oplock.cnt_II++;
124094047d49SGordon Ross 		} else {
124194047d49SGordon Ross 			/* (*rop == READ_CACHING) */
124294047d49SGordon Ross 			if (ofile->f_oplock.onlist_R == B_FALSE) {
124394047d49SGordon Ross 				ofile->f_oplock.onlist_R = B_TRUE;
124494047d49SGordon Ross 				node->n_oplock.cnt_R++;
124594047d49SGordon Ross 			}
124694047d49SGordon Ross 		}
124794047d49SGordon Ross 
124894047d49SGordon Ross 		/*
124994047d49SGordon Ross 		 * Recompute Open.Stream.Oplock.State according to the
125094047d49SGordon Ross 		 * algorithm in section 2.1.4.13, passing Open.Stream.Oplock
125194047d49SGordon Ross 		 * as the ThisOplock parameter.
125294047d49SGordon Ross 		 * Set OplockGranted to TRUE.
125394047d49SGordon Ross 		 */
125494047d49SGordon Ross 		RecomputeOplockState(node);
125594047d49SGordon Ross 		OplockGranted = B_TRUE;
125694047d49SGordon Ross 		break;
125794047d49SGordon Ross 
125894047d49SGordon Ross 	case (READ_CACHING|HANDLE_CACHING):
125994047d49SGordon Ross 		/*
126094047d49SGordon Ross 		 * The operation MUST be failed with Status set to
126194047d49SGordon Ross 		 * STATUS_OPLOCK_NOT_GRANTED if GrantingInAck is FALSE
126294047d49SGordon Ross 		 * and Open.Stream.Oplock.State is anything other than...
126394047d49SGordon Ross 		 */
126494047d49SGordon Ross 		switch (node->n_oplock.ol_state) {
126594047d49SGordon Ross 		default:
126694047d49SGordon Ross 			if (GrantingInAck == B_FALSE) {
126794047d49SGordon Ross 				status = NT_STATUS_OPLOCK_NOT_GRANTED;
126894047d49SGordon Ross 				goto out;
126994047d49SGordon Ross 			}
127094047d49SGordon Ross 			break;
127194047d49SGordon Ross 		case NO_OPLOCK:
127294047d49SGordon Ross 		case READ_CACHING:
127394047d49SGordon Ross 		case (READ_CACHING | HANDLE_CACHING):
127494047d49SGordon Ross 		case (READ_CACHING | HANDLE_CACHING | MIXED_R_AND_RH):
127594047d49SGordon Ross 		case (READ_CACHING | HANDLE_CACHING | BREAK_TO_READ_CACHING):
127694047d49SGordon Ross 		case (READ_CACHING | HANDLE_CACHING | BREAK_TO_NO_CACHING):
127794047d49SGordon Ross 			break;
127894047d49SGordon Ross 		}
127994047d49SGordon Ross 
128094047d49SGordon Ross 		/*
128194047d49SGordon Ross 		 * If Open.Stream.IsDeleted is TRUE, the operation MUST be
128294047d49SGordon Ross 		 *  failed with Status set to STATUS_OPLOCK_NOT_GRANTED.
128394047d49SGordon Ross 		 */
128494047d49SGordon Ross 		if ((node->flags & NODE_FLAGS_DELETING) != 0) {
128594047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
128694047d49SGordon Ross 			goto out;
128794047d49SGordon Ross 		}
128894047d49SGordon Ross 
128994047d49SGordon Ross 		if (GrantingInAck == B_FALSE) {
129094047d49SGordon Ross 			/*
129194047d49SGordon Ross 			 * If there is an Open ThisOpen on
129294047d49SGordon Ross 			 * Open.Stream.Oplock.ROplocks whose
129394047d49SGordon Ross 			 * TargetOplockKey is equal to Open.TargetOplockKey
129494047d49SGordon Ross 			 * (there is supposed to be at most one present):
129594047d49SGordon Ross 			 *	* Remove ThisOpen from Open...ROplocks.
129694047d49SGordon Ross 			 *	* Notify the server of an oplock break
129794047d49SGordon Ross 			 *	  according to the algorithm in section
129894047d49SGordon Ross 			 *	  2.1.5.17.3, setting the algorithm's
129994047d49SGordon Ross 			 *	  parameters as follows:
130094047d49SGordon Ross 			 *		* BreakingOplockOpen = ThisOpen
130194047d49SGordon Ross 			 *		* NewOplockLevel = CACHE_RH
130294047d49SGordon Ross 			 *		* AcknowledgeRequired = FALSE
130394047d49SGordon Ross 			 *		* OplockCompletionStatus =
130494047d49SGordon Ross 			 *		  STATUS_..._NEW_HANDLE
130594047d49SGordon Ross 			 * (The operation does not end at this point;
130694047d49SGordon Ross 			 *  this call to 2.1.5.17.3 completes some
130794047d49SGordon Ross 			 *  earlier call to 2.1.5.17.2.)
130894047d49SGordon Ross 			 * EndIf
130994047d49SGordon Ross 			 *
131094047d49SGordon Ross 			 * If this SMB2 lease already has an "R" handle,
131194047d49SGordon Ross 			 * we'll update that lease locally to point to
131294047d49SGordon Ross 			 * this new handle (upgrade to "RH").
131394047d49SGordon Ross 			 */
131494047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
131594047d49SGordon Ross 				if (o->f_oplock.onlist_R == 0)
131694047d49SGordon Ross 					continue;
131794047d49SGordon Ross 				if (CompareOplockKeys(ofile, o, 0)) {
131894047d49SGordon Ross 					o->f_oplock.onlist_R = B_FALSE;
131994047d49SGordon Ross 					node->n_oplock.cnt_R--;
132094047d49SGordon Ross 					ASSERT(node->n_oplock.cnt_R >= 0);
132194047d49SGordon Ross 					smb_oplock_ind_break(o,
132294047d49SGordon Ross 					    CACHE_RH, B_FALSE,
132394047d49SGordon Ross 					    STATUS_NEW_HANDLE);
132494047d49SGordon Ross 				}
132594047d49SGordon Ross 			}
132694047d49SGordon Ross 
132794047d49SGordon Ross 			/*
132894047d49SGordon Ross 			 * If there is an Open ThisOpen on
132994047d49SGordon Ross 			 * Open.Stream.Oplock.RHOplocks whose
133094047d49SGordon Ross 			 * TargetOplockKey is equal to Open.TargetOplockKey
133194047d49SGordon Ross 			 * (there is supposed to be at most one present):
133294047d49SGordon Ross 			 *	XXX: Note, the spec. was missing a step:
133394047d49SGordon Ross 			 *	XXX: Remove the open from RHOplocks
133494047d49SGordon Ross 			 *	XXX: Confirm with MS dochelp
133594047d49SGordon Ross 			 *	* Notify the server of an oplock break
133694047d49SGordon Ross 			 *	  according to the algorithm in section
133794047d49SGordon Ross 			 *	  2.1.5.17.3, setting the algorithm's
133894047d49SGordon Ross 			 *	  parameters as follows:
133994047d49SGordon Ross 			 *		* BreakingOplockOpen = ThisOpen
134094047d49SGordon Ross 			 *		* NewOplockLevel =
134194047d49SGordon Ross 			 *		  (READ_CACHING|HANDLE_CACHING)
134294047d49SGordon Ross 			 *		* AcknowledgeRequired = FALSE
134394047d49SGordon Ross 			 *		* OplockCompletionStatus =
134494047d49SGordon Ross 			 *		  STATUS_..._NEW_HANDLE
134594047d49SGordon Ross 			 * (The operation does not end at this point;
134694047d49SGordon Ross 			 *  this call to 2.1.5.17.3 completes some
134794047d49SGordon Ross 			 *  earlier call to 2.1.5.17.2.)
134894047d49SGordon Ross 			 * EndIf
134994047d49SGordon Ross 			 *
135094047d49SGordon Ross 			 * If this SMB2 lease already has an "RH" handle,
135194047d49SGordon Ross 			 * we'll update that lease locally to point to
135294047d49SGordon Ross 			 * this new handle.
135394047d49SGordon Ross 			 */
135494047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
135594047d49SGordon Ross 				if (o->f_oplock.onlist_RH == 0)
135694047d49SGordon Ross 					continue;
135794047d49SGordon Ross 				if (CompareOplockKeys(ofile, o, 0)) {
135894047d49SGordon Ross 					o->f_oplock.onlist_RH = B_FALSE;
135994047d49SGordon Ross 					node->n_oplock.cnt_RH--;
136094047d49SGordon Ross 					ASSERT(node->n_oplock.cnt_RH >= 0);
136194047d49SGordon Ross 					smb_oplock_ind_break(o,
136294047d49SGordon Ross 					    CACHE_RH, B_FALSE,
136394047d49SGordon Ross 					    STATUS_NEW_HANDLE);
136494047d49SGordon Ross 				}
136594047d49SGordon Ross 			}
136694047d49SGordon Ross 		} /* EndIf !GrantingInAck */
136794047d49SGordon Ross 
136894047d49SGordon Ross 		/*
136994047d49SGordon Ross 		 * Add Open to Open.Stream.Oplock.RHOplocks.
137094047d49SGordon Ross 		 */
137194047d49SGordon Ross 		if (ofile->f_oplock.onlist_RH == B_FALSE) {
137294047d49SGordon Ross 			ofile->f_oplock.onlist_RH = B_TRUE;
137394047d49SGordon Ross 			node->n_oplock.cnt_RH++;
137494047d49SGordon Ross 		}
137594047d49SGordon Ross 
137694047d49SGordon Ross 		/*
137794047d49SGordon Ross 		 * Recompute Open.Stream.Oplock.State according to the
137894047d49SGordon Ross 		 * algorithm in section 2.1.4.13, passing Open.Stream.Oplock
137994047d49SGordon Ross 		 * as the ThisOplock parameter.
138094047d49SGordon Ross 		 * Set OplockGranted to TRUE.
138194047d49SGordon Ross 		 */
138294047d49SGordon Ross 		RecomputeOplockState(node);
138394047d49SGordon Ross 		OplockGranted = B_TRUE;
138494047d49SGordon Ross 		break;
138594047d49SGordon Ross 
138694047d49SGordon Ross 	default:
138794047d49SGordon Ross 		/* No other value of RequestedOplock is possible. */
138894047d49SGordon Ross 		ASSERT(0);
138994047d49SGordon Ross 		status = NT_STATUS_OPLOCK_NOT_GRANTED;
139094047d49SGordon Ross 		goto out;
139194047d49SGordon Ross 	}  /* EndSwitch (RequestedOplock) */
139294047d49SGordon Ross 
139394047d49SGordon Ross 	/*
139494047d49SGordon Ross 	 * If OplockGranted is TRUE:
139594047d49SGordon Ross 	 * This operation MUST be made cancelable by inserting it into
139694047d49SGordon Ross 	 *   CancelableOperations.CancelableOperationList.
139794047d49SGordon Ross 	 * The operation waits until the oplock is broken or canceled,
139894047d49SGordon Ross 	 * as specified in section 2.1.5.17.3.
139994047d49SGordon Ross 	 * When the operation specified in section 2.1.5.17.3 is called,
140094047d49SGordon Ross 	 * its following input parameters are transferred to this routine
140194047d49SGordon Ross 	 * and returned by it:
140294047d49SGordon Ross 	 *	Status is set to OplockCompletionStatus from the
140394047d49SGordon Ross 	 *	  operation specified in section 2.1.5.17.3.
140494047d49SGordon Ross 	 *	NewOplockLevel is set to NewOplockLevel from the
140594047d49SGordon Ross 	 *	  operation specified in section 2.1.5.17.3.
140694047d49SGordon Ross 	 *	AcknowledgeRequired is set to AcknowledgeRequired from
140794047d49SGordon Ross 	 *	  the operation specified in section 2.1.5.17.3.
140894047d49SGordon Ross 	 * EndIf
140994047d49SGordon Ross 	 */
141094047d49SGordon Ross 	if (OplockGranted) {
141194047d49SGordon Ross 		/* Note: *rop already set. */
141294047d49SGordon Ross 		if ((node->n_oplock.ol_state & BREAK_ANY) != 0) {
141394047d49SGordon Ross 			status = NT_STATUS_OPLOCK_BREAK_IN_PROGRESS;
141494047d49SGordon Ross 			/* Caller does smb_oplock_wait_break() */
141594047d49SGordon Ross 		} else {
141694047d49SGordon Ross 			status = NT_STATUS_SUCCESS;
141794047d49SGordon Ross 		}
1418e8754e84SGordon Ross 
1419e8754e84SGordon Ross 		/*
1420e8754e84SGordon Ross 		 * First oplock grant installs FEM hooks.
1421e8754e84SGordon Ross 		 */
1422e8754e84SGordon Ross 		if (node->n_oplock.ol_fem == B_FALSE) {
1423e8754e84SGordon Ross 			if (smb_fem_oplock_install(node) != 0) {
1424e8754e84SGordon Ross 				cmn_err(CE_NOTE,
1425e8754e84SGordon Ross 				    "smb_fem_oplock_install failed");
1426e8754e84SGordon Ross 			} else {
1427e8754e84SGordon Ross 				node->n_oplock.ol_fem =	B_TRUE;
1428e8754e84SGordon Ross 			}
1429e8754e84SGordon Ross 		}
143094047d49SGordon Ross 	}
143194047d49SGordon Ross 
143294047d49SGordon Ross out:
143394047d49SGordon Ross 	if (status == NT_STATUS_OPLOCK_NOT_GRANTED)
143494047d49SGordon Ross 		*rop = LEVEL_NONE;
143594047d49SGordon Ross 
143694047d49SGordon Ross 	return (status);
143794047d49SGordon Ross }
143894047d49SGordon Ross 
143994047d49SGordon Ross /*
144094047d49SGordon Ross  * 2.1.5.17.3 Indicating an Oplock Break to the Server
144194047d49SGordon Ross  * See smb_srv_oplock.c
144294047d49SGordon Ross  */
144394047d49SGordon Ross 
144494047d49SGordon Ross /*
144594047d49SGordon Ross  * 2.1.5.18 Server Acknowledges an Oplock Break
144694047d49SGordon Ross  *
144794047d49SGordon Ross  * The server provides:
144894047d49SGordon Ross  *	Open - The Open associated with the oplock that has broken.
144994047d49SGordon Ross  *	Type - As part of the acknowledgement, the server indicates a
145094047d49SGordon Ross  *	  new oplock it would like in place of the one that has broken.
145194047d49SGordon Ross  *	  Valid values are as follows:
145294047d49SGordon Ross  *		LEVEL_NONE
145394047d49SGordon Ross  *		LEVEL_TWO
145494047d49SGordon Ross  *		LEVEL_GRANULAR - If this oplock type is specified,
145594047d49SGordon Ross  *		  the server additionally provides:
145694047d49SGordon Ross  *	RequestedOplockLevel - A combination of zero or more of
145794047d49SGordon Ross  *	  the following flags:
145894047d49SGordon Ross  *		READ_CACHING
145994047d49SGordon Ross  *		HANDLE_CACHING
146094047d49SGordon Ross  *		WRITE_CACHING
146194047d49SGordon Ross  *
146294047d49SGordon Ross  * If the server requests a new oplock and it is granted, the request
146394047d49SGordon Ross  * does not complete until the oplock is broken; the operation waits for
146494047d49SGordon Ross  * this to happen. Processing of an oplock break is described in
146594047d49SGordon Ross  * section 2.1.5.17.3.  Whether the new oplock is granted or not, the
146694047d49SGordon Ross  * object store MUST return:
146794047d49SGordon Ross  *
146894047d49SGordon Ross  *	Status - An NTSTATUS code indicating the result of the operation.
146994047d49SGordon Ross  *
147094047d49SGordon Ross  * If the server requests a new oplock and it is granted, then when the
147194047d49SGordon Ross  * oplock breaks and the request finally completes, the object store MUST
147294047d49SGordon Ross  * additionally return:
147394047d49SGordon Ross  *	NewOplockLevel: The type of oplock the requested oplock has
147494047d49SGordon Ross  *	  been broken to. Valid values are as follows:
147594047d49SGordon Ross  *		LEVEL_NONE (that is, no oplock)
147694047d49SGordon Ross  *		LEVEL_TWO
147794047d49SGordon Ross  *		A combination of one or more of the following flags:
147894047d49SGordon Ross  *			READ_CACHING
147994047d49SGordon Ross  *			HANDLE_CACHING
148094047d49SGordon Ross  *			WRITE_CACHING
148194047d49SGordon Ross  *	AcknowledgeRequired: A Boolean value; TRUE if the server MUST
148294047d49SGordon Ross  *	  acknowledge the oplock break, FALSE if not, as specified in
148394047d49SGordon Ross  *	  section 2.1.5.17.2.
148494047d49SGordon Ross  *
148594047d49SGordon Ross  * Note: Stores NewOplockLevel in *rop
148694047d49SGordon Ross  */
148794047d49SGordon Ross uint32_t
smb_oplock_ack_break(smb_request_t * sr,smb_ofile_t * ofile,uint32_t * rop)148894047d49SGordon Ross smb_oplock_ack_break(
148994047d49SGordon Ross     smb_request_t *sr,
149094047d49SGordon Ross     smb_ofile_t *ofile,
149194047d49SGordon Ross     uint32_t *rop)
149294047d49SGordon Ross {
149394047d49SGordon Ross 	smb_node_t *node = ofile->f_node;
149494047d49SGordon Ross 	uint32_t type = *rop & OPLOCK_LEVEL_TYPE_MASK;
149594047d49SGordon Ross 	uint32_t level = *rop & OPLOCK_LEVEL_CACHE_MASK;
149694047d49SGordon Ross 	uint32_t status = NT_STATUS_SUCCESS;
149794047d49SGordon Ross 	uint32_t BreakToLevel;
149894047d49SGordon Ross 	boolean_t NewOplockGranted = B_FALSE;
149994047d49SGordon Ross 	boolean_t ReturnBreakToNone = B_FALSE;
150094047d49SGordon Ross 	boolean_t FoundMatchingRHOplock = B_FALSE;
150194047d49SGordon Ross 	int other_keys;
150294047d49SGordon Ross 
150372b35b05SGordon Ross 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
150472b35b05SGordon Ross 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
150594047d49SGordon Ross 
150694047d49SGordon Ross 	/*
150794047d49SGordon Ross 	 * If Open.Stream.Oplock is empty, the operation MUST be
150894047d49SGordon Ross 	 * failed with Status set to STATUS_INVALID_OPLOCK_PROTOCOL.
150994047d49SGordon Ross 	 */
151094047d49SGordon Ross 	if (node->n_oplock.ol_state == 0) {
151194047d49SGordon Ross 		status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
151294047d49SGordon Ross 		goto out;
151394047d49SGordon Ross 	}
151494047d49SGordon Ross 
151594047d49SGordon Ross 	if (type == LEVEL_NONE || type == LEVEL_TWO) {
151694047d49SGordon Ross 		/*
151794047d49SGordon Ross 		 * If Open.Stream.Oplock.ExclusiveOpen is not equal to Open,
151894047d49SGordon Ross 		 * the operation MUST be failed with Status set to
151994047d49SGordon Ross 		 * STATUS_INVALID_OPLOCK_PROTOCOL.
152094047d49SGordon Ross 		 */
152194047d49SGordon Ross 		if (node->n_oplock.excl_open != ofile) {
152294047d49SGordon Ross 			status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
152394047d49SGordon Ross 			goto out;
152494047d49SGordon Ross 		}
152594047d49SGordon Ross 
152694047d49SGordon Ross 		/*
152794047d49SGordon Ross 		 * If Type is LEVEL_TWO and Open.Stream.Oplock.State
152894047d49SGordon Ross 		 * contains BREAK_TO_TWO:
152994047d49SGordon Ross 		 *	Set Open.Stream.Oplock.State to LEVEL_TWO_OPLOCK.
153094047d49SGordon Ross 		 *	Set NewOplockGranted to TRUE.
153194047d49SGordon Ross 		 */
153294047d49SGordon Ross 		if (type == LEVEL_TWO &&
153394047d49SGordon Ross 		    (node->n_oplock.ol_state & BREAK_TO_TWO) != 0) {
153494047d49SGordon Ross 			node->n_oplock.ol_state = LEVEL_TWO;
153594047d49SGordon Ross 			NewOplockGranted = B_TRUE;
153694047d49SGordon Ross 		}
153794047d49SGordon Ross 
153894047d49SGordon Ross 		/*
153994047d49SGordon Ross 		 * Else If Open.Stream.Oplock.State contains
154094047d49SGordon Ross 		 * BREAK_TO_TWO or BREAK_TO_NONE:
154194047d49SGordon Ross 		 *	Set Open.Stream.Oplock.State to NO_OPLOCK.
154294047d49SGordon Ross 		 */
154394047d49SGordon Ross 		else if ((node->n_oplock.ol_state &
154494047d49SGordon Ross 		    (BREAK_TO_TWO | BREAK_TO_NONE)) != 0) {
154594047d49SGordon Ross 			node->n_oplock.ol_state = NO_OPLOCK;
154694047d49SGordon Ross 		}
154794047d49SGordon Ross 
154894047d49SGordon Ross 		/*
154994047d49SGordon Ross 		 * Else If Open.Stream.Oplock.State contains
155094047d49SGordon Ross 		 * BREAK_TO_TWO_TO_NONE:
155194047d49SGordon Ross 		 *	Set Open.Stream.Oplock.State to NO_OPLOCK.
155294047d49SGordon Ross 		 *	Set ReturnBreakToNone to TRUE.
155394047d49SGordon Ross 		 */
155494047d49SGordon Ross 		else if ((node->n_oplock.ol_state &
155594047d49SGordon Ross 		    BREAK_TO_TWO_TO_NONE) != 0) {
155694047d49SGordon Ross 			node->n_oplock.ol_state = NO_OPLOCK;
155794047d49SGordon Ross 			ReturnBreakToNone = B_TRUE;
155894047d49SGordon Ross 		}
155994047d49SGordon Ross 
156094047d49SGordon Ross 		/*
156194047d49SGordon Ross 		 * Else
156294047d49SGordon Ross 		 *	The operation MUST be failed with Status set to
156394047d49SGordon Ross 		 *	STATUS_INVALID_OPLOCK_PROTOCOL.
156494047d49SGordon Ross 		 */
156594047d49SGordon Ross 		else {
156694047d49SGordon Ross 			status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
156794047d49SGordon Ross 			goto out;
156894047d49SGordon Ross 		}
156994047d49SGordon Ross 
157094047d49SGordon Ross 		/*
157194047d49SGordon Ross 		 * For each Open WaitingOpen on Open.Stream.Oplock.WaitList:
157294047d49SGordon Ross 		 *	Indicate that the operation associated with
157394047d49SGordon Ross 		 *	  WaitingOpen can continue according to the
157494047d49SGordon Ross 		 *	  algorithm in section 2.1.4.12.1, setting
157594047d49SGordon Ross 		 *	  OpenToRelease = WaitingOpen.
157694047d49SGordon Ross 		 *	Remove WaitingOpen from Open.Stream.Oplock.WaitList.
157794047d49SGordon Ross 		 * EndFor
157894047d49SGordon Ross 		 */
157994047d49SGordon Ross 		if (node->n_oplock.waiters)
158094047d49SGordon Ross 			cv_broadcast(&node->n_oplock.WaitingOpenCV);
158194047d49SGordon Ross 
158294047d49SGordon Ross 		/*
158394047d49SGordon Ross 		 * Set Open.Stream.Oplock.ExclusiveOpen to NULL.
158494047d49SGordon Ross 		 */
158594047d49SGordon Ross 		node->n_oplock.excl_open = NULL;
158694047d49SGordon Ross 
158794047d49SGordon Ross 		if (NewOplockGranted) {
158894047d49SGordon Ross 			/*
158994047d49SGordon Ross 			 * The operation waits until the newly-granted
159094047d49SGordon Ross 			 * Level 2 oplock is broken, as specified in
159194047d49SGordon Ross 			 * section 2.1.5.17.3.
159294047d49SGordon Ross 			 *
159394047d49SGordon Ross 			 * Here we have just Ack'ed a break-to-II
159494047d49SGordon Ross 			 * so now get the level II oplock.  We also
159594047d49SGordon Ross 			 * checked for break-to-none above, so this
159694047d49SGordon Ross 			 * will not need to wait for oplock breaks.
159794047d49SGordon Ross 			 */
159894047d49SGordon Ross 			status = smb_oplock_req_shared(ofile, rop, B_TRUE);
159994047d49SGordon Ross 		}
160094047d49SGordon Ross 
160194047d49SGordon Ross 		else if (ReturnBreakToNone) {
160294047d49SGordon Ross 			/*
160394047d49SGordon Ross 			 * In this case the server was expecting the oplock
160494047d49SGordon Ross 			 * to break to Level 2, but because the oplock is
160594047d49SGordon Ross 			 * actually breaking to None (that is, no oplock),
160694047d49SGordon Ross 			 * the object store MUST indicate an oplock break
160794047d49SGordon Ross 			 * to the server according to the algorithm in
160894047d49SGordon Ross 			 * section 2.1.5.17.3, setting the algorithm's
160994047d49SGordon Ross 			 * parameters as follows:
161094047d49SGordon Ross 			 *	BreakingOplockOpen = Open.
161194047d49SGordon Ross 			 *	NewOplockLevel = LEVEL_NONE.
161294047d49SGordon Ross 			 *	AcknowledgeRequired = FALSE.
161394047d49SGordon Ross 			 *	OplockCompletionStatus = STATUS_SUCCESS.
161494047d49SGordon Ross 			 * (Because BreakingOplockOpen is equal to the
161594047d49SGordon Ross 			 * passed-in Open, the operation ends at this point.)
161694047d49SGordon Ross 			 */
1617*7f6a299eSGordon Ross 			smb_oplock_ind_break_in_ack(
1618*7f6a299eSGordon Ross 			    sr, ofile,
1619*7f6a299eSGordon Ross 			    LEVEL_NONE, B_FALSE);
162094047d49SGordon Ross 		}
162194047d49SGordon Ross 		status = NT_STATUS_SUCCESS;
162294047d49SGordon Ross 		goto out;
162394047d49SGordon Ross 	} /* LEVEL_NONE or LEVEL_TWO */
162494047d49SGordon Ross 
162594047d49SGordon Ross 	if (type != LEVEL_GRANULAR) {
162694047d49SGordon Ross 		status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
162794047d49SGordon Ross 		goto out;
162894047d49SGordon Ross 	}
162994047d49SGordon Ross 
163094047d49SGordon Ross 	/* LEVEL_GRANULAR */
163194047d49SGordon Ross 
163294047d49SGordon Ross 	/*
163394047d49SGordon Ross 	 * Let BREAK_LEVEL_MASK = (BREAK_TO_READ_CACHING |
163494047d49SGordon Ross 	 *   BREAK_TO_WRITE_CACHING | BREAK_TO_HANDLE_CACHING |
163594047d49SGordon Ross 	 *   BREAK_TO_NO_CACHING),
163694047d49SGordon Ross 	 * R_AND_RH_GRANTED = (READ_CACHING | HANDLE_CACHING |
163794047d49SGordon Ross 	 *   MIXED_R_AND_RH),
163894047d49SGordon Ross 	 * RH_GRANTED = (READ_CACHING | HANDLE_CACHING)
163994047d49SGordon Ross 	 *
164094047d49SGordon Ross 	 * (See BREAK_LEVEL_MASK in smb_oplock.h)
164194047d49SGordon Ross 	 */
164294047d49SGordon Ross #define	RH_GRANTED		(READ_CACHING|HANDLE_CACHING)
164394047d49SGordon Ross #define	R_AND_RH_GRANTED	(RH_GRANTED|MIXED_R_AND_RH)
164494047d49SGordon Ross 
164594047d49SGordon Ross 	/*
164694047d49SGordon Ross 	 * If there are no BREAK_LEVEL_MASK flags set, this is invalid,
164794047d49SGordon Ross 	 * unless the state is R_AND_RH_GRANTED or RH_GRANTED, in which
164894047d49SGordon Ross 	 * case we'll need to see if the RHBreakQueue is empty.
164994047d49SGordon Ross 	 */
165094047d49SGordon Ross 
165194047d49SGordon Ross 	/*
165294047d49SGordon Ross 	 * If (Open.Stream.Oplock.State does not contain any flag in
165394047d49SGordon Ross 	 * BREAK_LEVEL_MASK and
165494047d49SGordon Ross 	 *  (Open.Stream.Oplock.State != R_AND_RH_GRANTED) and
165594047d49SGordon Ross 	 *   (Open.Stream.Oplock.State != RH_GRANTED)) or
165694047d49SGordon Ross 	 *   (((Open.Stream.Oplock.State == R_AND_RH_GRANTED) or
165794047d49SGordon Ross 	 *  (Open.Stream.Oplock.State == RH_GRANTED)) and
165894047d49SGordon Ross 	 *   Open.Stream.Oplock.RHBreakQueue is empty):
165994047d49SGordon Ross 	 *	The request MUST be failed with Status set to
166094047d49SGordon Ross 	 *	  STATUS_INVALID_OPLOCK_PROTOCOL.
166194047d49SGordon Ross 	 * EndIf
166294047d49SGordon Ross 	 */
166394047d49SGordon Ross 	if ((node->n_oplock.ol_state & BREAK_LEVEL_MASK) == 0) {
166494047d49SGordon Ross 		if ((node->n_oplock.ol_state != R_AND_RH_GRANTED) &&
166594047d49SGordon Ross 		    (node->n_oplock.ol_state != RH_GRANTED)) {
166694047d49SGordon Ross 			status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
166794047d49SGordon Ross 			goto out;
166894047d49SGordon Ross 		}
166994047d49SGordon Ross 		/* State is R_AND_RH_GRANTED or RH_GRANTED */
167094047d49SGordon Ross 		if (node->n_oplock.cnt_RHBQ == 0) {
167194047d49SGordon Ross 			status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
167294047d49SGordon Ross 			goto out;
167394047d49SGordon Ross 		}
167494047d49SGordon Ross 	}
167594047d49SGordon Ross 
167694047d49SGordon Ross 	/*
167794047d49SGordon Ross 	 * Compute the "Break To" cache level from the
167894047d49SGordon Ross 	 * BREAK_TO_... flags
167994047d49SGordon Ross 	 */
168094047d49SGordon Ross 	switch (node->n_oplock.ol_state & BREAK_LEVEL_MASK) {
168194047d49SGordon Ross 	case (BREAK_TO_READ_CACHING | BREAK_TO_WRITE_CACHING |
168294047d49SGordon Ross 	    BREAK_TO_HANDLE_CACHING):
168394047d49SGordon Ross 		BreakToLevel = CACHE_RWH;
168494047d49SGordon Ross 		break;
168594047d49SGordon Ross 	case (BREAK_TO_READ_CACHING | BREAK_TO_WRITE_CACHING):
168694047d49SGordon Ross 		BreakToLevel = CACHE_RW;
168794047d49SGordon Ross 		break;
168894047d49SGordon Ross 	case (BREAK_TO_READ_CACHING | BREAK_TO_HANDLE_CACHING):
168994047d49SGordon Ross 		BreakToLevel = CACHE_RH;
169094047d49SGordon Ross 		break;
169194047d49SGordon Ross 	case BREAK_TO_READ_CACHING:
169294047d49SGordon Ross 		BreakToLevel = READ_CACHING;
169394047d49SGordon Ross 		break;
169494047d49SGordon Ross 	case BREAK_TO_NO_CACHING:
169594047d49SGordon Ross 		BreakToLevel = LEVEL_NONE;
169694047d49SGordon Ross 		break;
1697*7f6a299eSGordon Ross 	default:
1698*7f6a299eSGordon Ross 		ASSERT(0);
1699*7f6a299eSGordon Ross 		/* FALLTHROUGH */
1700*7f6a299eSGordon Ross 	case 0:
1701*7f6a299eSGordon Ross 		/*
1702*7f6a299eSGordon Ross 		 * This can happen when we have multiple RH opens,
1703*7f6a299eSGordon Ross 		 * and one of them breaks (RH to R).  Happens in
1704*7f6a299eSGordon Ross 		 * the smbtorture smb2.lease.v2rename test.
1705*7f6a299eSGordon Ross 		 */
1706*7f6a299eSGordon Ross 		BreakToLevel = CACHE_R;
1707*7f6a299eSGordon Ross 		break;
170894047d49SGordon Ross 	}
170994047d49SGordon Ross 
171094047d49SGordon Ross 	/* Switch Open.Stream.Oplock.State */
171194047d49SGordon Ross 	switch (node->n_oplock.ol_state) {
171294047d49SGordon Ross 
171394047d49SGordon Ross 	case (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH):
171494047d49SGordon Ross 	case (READ_CACHING|HANDLE_CACHING):
171594047d49SGordon Ross 	case (READ_CACHING|HANDLE_CACHING|BREAK_TO_READ_CACHING):
171694047d49SGordon Ross 	case (READ_CACHING|HANDLE_CACHING|BREAK_TO_NO_CACHING):
1717*7f6a299eSGordon Ross 		/*
1718*7f6a299eSGordon Ross 		 * XXX: Missing from [MS-FSA]
1719*7f6a299eSGordon Ross 		 *
1720*7f6a299eSGordon Ross 		 * If we previously sent a break to none and the
1721*7f6a299eSGordon Ross 		 * client Ack level is R instead of none, we
1722*7f6a299eSGordon Ross 		 * need to send another break. We can then
1723*7f6a299eSGordon Ross 		 * proceed as if we got level = none.
1724*7f6a299eSGordon Ross 		 */
1725*7f6a299eSGordon Ross 		if (level == CACHE_R && BreakToLevel == LEVEL_NONE) {
1726*7f6a299eSGordon Ross 			smb_oplock_ind_break_in_ack(
1727*7f6a299eSGordon Ross 			    sr, ofile,
1728*7f6a299eSGordon Ross 			    LEVEL_NONE, B_FALSE);
1729*7f6a299eSGordon Ross 			level = LEVEL_NONE;
1730*7f6a299eSGordon Ross 		}
1731*7f6a299eSGordon Ross 
173294047d49SGordon Ross 		/*
173394047d49SGordon Ross 		 * For each RHOpContext ThisContext in
173494047d49SGordon Ross 		 * Open.Stream.Oplock.RHBreakQueue:
173594047d49SGordon Ross 		 *	If ThisContext.Open equals Open:
173694047d49SGordon Ross 		 *		(see below)
173794047d49SGordon Ross 		 *
173894047d49SGordon Ross 		 * Implementation skips the list walk, because
173994047d49SGordon Ross 		 * we can get the ofile directly.
174094047d49SGordon Ross 		 */
174194047d49SGordon Ross 		if (ofile->f_oplock.onlist_RHBQ) {
174294047d49SGordon Ross 			smb_ofile_t *o;
174394047d49SGordon Ross 
174494047d49SGordon Ross 			/*
174594047d49SGordon Ross 			 * Set FoundMatchingRHOplock to TRUE.
174694047d49SGordon Ross 			 * If ThisContext.BreakingToRead is FALSE:
174794047d49SGordon Ross 			 *	If RequestedOplockLevel is not 0 and
174894047d49SGordon Ross 			 *	Open.Stream.Oplock.WaitList is not empty:
174994047d49SGordon Ross 			 *	    The object store MUST indicate an
175094047d49SGordon Ross 			 *	    oplock break to the server according to
175194047d49SGordon Ross 			 *	    the algorithm in section 2.1.5.17.3,
175294047d49SGordon Ross 			 *	    setting the algorithm's params as follows:
175394047d49SGordon Ross 			 *		BreakingOplockOpen = Open.
175494047d49SGordon Ross 			 *		NewOplockLevel = LEVEL_NONE.
175594047d49SGordon Ross 			 *		AcknowledgeRequired = TRUE.
175694047d49SGordon Ross 			 *		OplockCompletionStatus =
175794047d49SGordon Ross 			 *		  STATUS_CANNOT_GRANT_...
175894047d49SGordon Ross 			 *  (Because BreakingOplockOpen is equal to the
175994047d49SGordon Ross 			 *   passed Open, the operation ends at this point.)
176094047d49SGordon Ross 			 * EndIf
176194047d49SGordon Ross 			 */
176294047d49SGordon Ross 			FoundMatchingRHOplock = B_TRUE;
176394047d49SGordon Ross 			if (ofile->f_oplock.BreakingToRead == B_FALSE) {
176494047d49SGordon Ross 				if (level != 0 && node->n_oplock.waiters) {
176594047d49SGordon Ross 					/* The ofile stays on RHBQ */
176694047d49SGordon Ross 					smb_oplock_ind_break_in_ack(
176794047d49SGordon Ross 					    sr, ofile,
176894047d49SGordon Ross 					    LEVEL_NONE, B_TRUE);
176994047d49SGordon Ross 					status = NT_STATUS_SUCCESS;
177094047d49SGordon Ross 					goto out;
177194047d49SGordon Ross 				}
177294047d49SGordon Ross 			}
177394047d49SGordon Ross 
177494047d49SGordon Ross 			/*
177594047d49SGordon Ross 			 * Else // ThisContext.BreakingToRead is TRUE.
177694047d49SGordon Ross 			 *    If Open.Stream.Oplock.WaitList is not empty and
177794047d49SGordon Ross 			 *    (RequestedOplockLevel is CACHE_RW or CACHE_RWH:
177894047d49SGordon Ross 			 *	The object store MUST indicate an oplock
177994047d49SGordon Ross 			 *	break to the server according to the
178094047d49SGordon Ross 			 *	algorithm in section 2.1.5.17.3, setting
178194047d49SGordon Ross 			 *	the algorithm's parameters as follows:
178294047d49SGordon Ross 			 *		* BreakingOplockOpen = Open
178394047d49SGordon Ross 			 *		* NewOplockLevel = READ_CACHING
178494047d49SGordon Ross 			 *		* AcknowledgeRequired = TRUE
178594047d49SGordon Ross 			 *		* OplockCompletionStatus =
178694047d49SGordon Ross 			 *		  STATUS_CANNOT_GRANT...
178794047d49SGordon Ross 			 *	(Because BreakingOplockOpen is equal to the
178894047d49SGordon Ross 			 *	 passed-in Open, the operation ends at this
178994047d49SGordon Ross 			 *	 point.)
179094047d49SGordon Ross 			 *    EndIf
179194047d49SGordon Ross 			 * EndIf
179294047d49SGordon Ross 			 */
179394047d49SGordon Ross 			else { /* BreakingToRead is TRUE */
179494047d49SGordon Ross 				if (node->n_oplock.waiters &&
179594047d49SGordon Ross 				    (level == CACHE_RW ||
179694047d49SGordon Ross 				    level == CACHE_RWH)) {
179794047d49SGordon Ross 					/* The ofile stays on RHBQ */
179894047d49SGordon Ross 					smb_oplock_ind_break_in_ack(
179994047d49SGordon Ross 					    sr, ofile,
180094047d49SGordon Ross 					    CACHE_R, B_TRUE);
180194047d49SGordon Ross 					status = NT_STATUS_SUCCESS;
180294047d49SGordon Ross 					goto out;
180394047d49SGordon Ross 				}
180494047d49SGordon Ross 			}
180594047d49SGordon Ross 
180694047d49SGordon Ross 			/*
180794047d49SGordon Ross 			 * Remove ThisContext from Open...RHBreakQueue.
180894047d49SGordon Ross 			 */
180994047d49SGordon Ross 			ofile->f_oplock.onlist_RHBQ = B_FALSE;
181094047d49SGordon Ross 			node->n_oplock.cnt_RHBQ--;
181194047d49SGordon Ross 			ASSERT(node->n_oplock.cnt_RHBQ >= 0);
181294047d49SGordon Ross 
181394047d49SGordon Ross 			/*
181494047d49SGordon Ross 			 * The operation waiting for the Read-Handle
181594047d49SGordon Ross 			 * oplock to break can continue if there are
181694047d49SGordon Ross 			 * no more Read-Handle oplocks outstanding, or
181794047d49SGordon Ross 			 * if all the remaining Read-Handle oplocks
181894047d49SGordon Ross 			 * have the same oplock key as the waiting
181994047d49SGordon Ross 			 * operation.
182094047d49SGordon Ross 			 *
182194047d49SGordon Ross 			 * For each Open WaitingOpen on Open...WaitList:
182294047d49SGordon Ross 			 *
182394047d49SGordon Ross 			 *	* If (Open...RHBreakQueue is empty) or
182494047d49SGordon Ross 			 *	  (all RHOpContext.Open.TargetOplockKey values
182594047d49SGordon Ross 			 *	  on Open.Stream.Oplock.RHBreakQueue are
182694047d49SGordon Ross 			 *	  equal to WaitingOpen.TargetOplockKey):
182794047d49SGordon Ross 			 *		* Indicate that the operation assoc.
182894047d49SGordon Ross 			 *		  with WaitingOpen can continue
182994047d49SGordon Ross 			 *		  according to the algorithm in
183094047d49SGordon Ross 			 *		  section 2.1.4.12.1, setting
183194047d49SGordon Ross 			 *		  OpenToRelease = WaitingOpen.
183294047d49SGordon Ross 			 *		* Remove WaitingOpen from
183394047d49SGordon Ross 			 *		  Open.Stream.Oplock.WaitList.
183494047d49SGordon Ross 			 *	* EndIf
183594047d49SGordon Ross 			 * EndFor
183694047d49SGordon Ross 			 */
183794047d49SGordon Ross 			other_keys = 0;
183894047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
183994047d49SGordon Ross 				if (o->f_oplock.onlist_RHBQ == 0)
184094047d49SGordon Ross 					continue;
184194047d49SGordon Ross 				if (!CompareOplockKeys(ofile, o, 0))
184294047d49SGordon Ross 					other_keys++;
184394047d49SGordon Ross 			}
184494047d49SGordon Ross 			if (other_keys == 0)
184594047d49SGordon Ross 				cv_broadcast(&node->n_oplock.WaitingOpenCV);
184694047d49SGordon Ross 
184794047d49SGordon Ross 			/*
184894047d49SGordon Ross 			 * If RequestedOplockLevel is 0 (that is, no flags):
184994047d49SGordon Ross 			 *	* Recompute Open.Stream.Oplock.State
185094047d49SGordon Ross 			 *	  according to the algorithm in section
185194047d49SGordon Ross 			 *	  2.1.4.13, passing Open.Stream.Oplock as
185294047d49SGordon Ross 			 *	  the ThisOplock parameter.
185394047d49SGordon Ross 			 *	* The algorithm MUST return Status set to
185494047d49SGordon Ross 			 *	  STATUS_SUCCESS at this point.
185594047d49SGordon Ross 			 */
185694047d49SGordon Ross 			if (level == 0) {
185794047d49SGordon Ross 				RecomputeOplockState(node);
185894047d49SGordon Ross 				status = NT_STATUS_SUCCESS;
185994047d49SGordon Ross 				goto out;
186094047d49SGordon Ross 			}
186194047d49SGordon Ross 
186294047d49SGordon Ross 			/*
186394047d49SGordon Ross 			 * Else If RequestedOplockLevel does not contain
186494047d49SGordon Ross 			 * WRITE_CACHING:
186594047d49SGordon Ross 			 *	* The object store MUST request a shared oplock
186694047d49SGordon Ross 			 *	  according to the algorithm in section
186794047d49SGordon Ross 			 *	  2.1.5.17.2, setting the algorithm's
186894047d49SGordon Ross 			 *	  parameters as follows:
186994047d49SGordon Ross 			 *		* Open = current Open.
187094047d49SGordon Ross 			 *		* RequestedOplock =
187194047d49SGordon Ross 			 *		  RequestedOplockLevel.
187294047d49SGordon Ross 			 *		* GrantingInAck = TRUE.
187394047d49SGordon Ross 			 *	* The operation MUST at this point return any
187494047d49SGordon Ross 			 *	  status code returned by the shared oplock
187594047d49SGordon Ross 			 *	  request algorithm.
187694047d49SGordon Ross 			 */
187794047d49SGordon Ross 			else if ((level & WRITE_CACHING) == 0) {
187894047d49SGordon Ross 				*rop = level;
187994047d49SGordon Ross 				status = smb_oplock_req_shared(
188094047d49SGordon Ross 				    ofile, rop, B_TRUE);
188194047d49SGordon Ross 				goto out;
188294047d49SGordon Ross 			}
188394047d49SGordon Ross 
188494047d49SGordon Ross 			/*
188594047d49SGordon Ross 			 * Set Open.Stream.Oplock.ExclusiveOpen to
188694047d49SGordon Ross 			 *   ThisContext.Open.
188794047d49SGordon Ross 			 * Set Open.Stream.Oplock.State to
188894047d49SGordon Ross 			 *   (RequestedOplockLevel|EXCLUSIVE).
188994047d49SGordon Ross 			 * This operation MUST be made cancelable by
189094047d49SGordon Ross 			 *   inserting it into CancelableOperations...
189194047d49SGordon Ross 			 * This operation waits until the oplock is
189294047d49SGordon Ross 			 * broken or canceled, as specified in
189394047d49SGordon Ross 			 * section 2.1.5.17.3.
189494047d49SGordon Ross 			 *
189594047d49SGordon Ross 			 * Implementation note:
189694047d49SGordon Ross 			 *
189794047d49SGordon Ross 			 * Once we assing ol_state below, there
189894047d49SGordon Ross 			 * will be no BREAK_TO_... flags set,
189994047d49SGordon Ross 			 * so no need to wait for oplock breaks.
190094047d49SGordon Ross 			 */
190194047d49SGordon Ross 			node->n_oplock.excl_open = ofile;
190294047d49SGordon Ross 			node->n_oplock.ol_state = level | EXCLUSIVE;
190394047d49SGordon Ross 			status = NT_STATUS_SUCCESS;
190494047d49SGordon Ross 		} /* onlist_RHBQ */
190594047d49SGordon Ross 		if (FoundMatchingRHOplock == B_FALSE) {
190694047d49SGordon Ross 			/* The operation MUST be failed with Status... */
190794047d49SGordon Ross 			status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
190894047d49SGordon Ross 			goto out;
190994047d49SGordon Ross 		}
191094047d49SGordon Ross 		break;	/* case (READ_CACHING|HANDLE_CACHING...) */
191194047d49SGordon Ross 
191294047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|EXCLUSIVE|BREAK_TO_READ_CACHING):
191394047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|EXCLUSIVE|BREAK_TO_NO_CACHING):
191494047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
191594047d49SGordon Ross 	    BREAK_TO_READ_CACHING|BREAK_TO_WRITE_CACHING):
191694047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
191794047d49SGordon Ross 	    BREAK_TO_READ_CACHING|BREAK_TO_HANDLE_CACHING):
191894047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
191994047d49SGordon Ross 	    BREAK_TO_READ_CACHING):
192094047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
192194047d49SGordon Ross 	    BREAK_TO_NO_CACHING):
192294047d49SGordon Ross 		/*
192394047d49SGordon Ross 		 * If Open.Stream.Oplock.ExclusiveOpen != Open:
192494047d49SGordon Ross 		 *	* The operation MUST be failed with Status set to
192594047d49SGordon Ross 		 *	  STATUS_INVALID_OPLOCK_PROTOCOL.
192694047d49SGordon Ross 		 * EndIf
192794047d49SGordon Ross 		 */
192894047d49SGordon Ross 		if (node->n_oplock.excl_open != ofile) {
192994047d49SGordon Ross 			status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
193094047d49SGordon Ross 			goto out;
193194047d49SGordon Ross 		}
193294047d49SGordon Ross 
193394047d49SGordon Ross 		/*
193494047d49SGordon Ross 		 * If Open.Stream.Oplock.WaitList is not empty and
193594047d49SGordon Ross 		 * Open.Stream.Oplock.State does not contain HANDLE_CACHING
193694047d49SGordon Ross 		 * and RequestedOplockLevel is CACHE_RWH:
193794047d49SGordon Ross 		 *	The object store MUST indicate an oplock break to
193894047d49SGordon Ross 		 *	the server according to the algorithm in section
193994047d49SGordon Ross 		 *	2.1.5.17.3, setting the algorithm's params as follows:
194094047d49SGordon Ross 		 *	* BreakingOplockOpen = Open.
194194047d49SGordon Ross 		 *	* NewOplockLevel = BreakToLevel (see above)
194294047d49SGordon Ross 		 *	* AcknowledgeRequired = TRUE.
194394047d49SGordon Ross 		 *	* OplockCompletionStatus =
194494047d49SGordon Ross 		 *	  STATUS_CANNOT_GRANT_REQUESTED_OPLOCK.
194594047d49SGordon Ross 		 *   (Because BreakingOplockOpen is equal to the passed-in
194694047d49SGordon Ross 		 *    Open, the operation ends at this point.)
194794047d49SGordon Ross 		 */
194894047d49SGordon Ross 		if (node->n_oplock.waiters &&
194994047d49SGordon Ross 		    (node->n_oplock.ol_state & HANDLE_CACHING) == 0 &&
195094047d49SGordon Ross 		    level == CACHE_RWH) {
195194047d49SGordon Ross 			smb_oplock_ind_break_in_ack(
195294047d49SGordon Ross 			    sr, ofile,
195394047d49SGordon Ross 			    BreakToLevel, B_TRUE);
195494047d49SGordon Ross 			status = NT_STATUS_SUCCESS;
195594047d49SGordon Ross 			goto out;
195694047d49SGordon Ross 		}
195794047d49SGordon Ross 
195894047d49SGordon Ross 		/*
195994047d49SGordon Ross 		 * Else If Open.Stream.IsDeleted is TRUE and
196094047d49SGordon Ross 		 * RequestedOplockLevel contains HANDLE_CACHING:
196194047d49SGordon Ross 		 */
196294047d49SGordon Ross 		else if (((node->flags & NODE_FLAGS_DELETING) != 0) &&
196394047d49SGordon Ross 		    (level & HANDLE_CACHING) != 0) {
196494047d49SGordon Ross 
196594047d49SGordon Ross 			/*
196694047d49SGordon Ross 			 * The object store MUST indicate an oplock break to
196794047d49SGordon Ross 			 * the server according to the algorithm in section
196894047d49SGordon Ross 			 * 2.1.5.17.3, setting the algorithm's params as
196994047d49SGordon Ross 			 * follows:
197094047d49SGordon Ross 			 *	* BreakingOplockOpen = Open.
197194047d49SGordon Ross 			 *	* NewOplockLevel = RequestedOplockLevel
197294047d49SGordon Ross 			 *	  without HANDLE_CACHING (for example if
197394047d49SGordon Ross 			 *	  RequestedOplockLevel is
197494047d49SGordon Ross 			 *	  (READ_CACHING|HANDLE_CACHING), then
197594047d49SGordon Ross 			 *	   NewOplockLevel would be just READ_CACHING).
197694047d49SGordon Ross 			 *	* AcknowledgeRequired = TRUE.
197794047d49SGordon Ross 			 *	* OplockCompletionStatus =
197894047d49SGordon Ross 			 *	  STATUS_CANNOT_GRANT_REQUESTED_OPLOCK.
197994047d49SGordon Ross 			 * (Because BreakingOplockOpen is equal to the
198094047d49SGordon Ross 			 *  passed-in Open, the operation ends at this point.)
198194047d49SGordon Ross 			 */
198294047d49SGordon Ross 			level &= ~HANDLE_CACHING;
198394047d49SGordon Ross 			smb_oplock_ind_break_in_ack(
198494047d49SGordon Ross 			    sr, ofile,
198594047d49SGordon Ross 			    level, B_TRUE);
198694047d49SGordon Ross 			status = NT_STATUS_SUCCESS;
198794047d49SGordon Ross 			goto out;
198894047d49SGordon Ross 		}
198994047d49SGordon Ross 
199094047d49SGordon Ross 		/*
199194047d49SGordon Ross 		 * For each Open WaitingOpen on Open.Stream.Oplock.WaitList:
199294047d49SGordon Ross 		 *	* Indicate that the operation associated with
199394047d49SGordon Ross 		 *	  WaitingOpen can continue according to the algorithm
199494047d49SGordon Ross 		 *	  in section 2.1.4.12.1, setting OpenToRelease
199594047d49SGordon Ross 		 *	  = WaitingOpen.
199694047d49SGordon Ross 		 *	* Remove WaitingOpen from Open.Stream.Oplock.WaitList.
199794047d49SGordon Ross 		 * EndFor
199894047d49SGordon Ross 		 */
199994047d49SGordon Ross 		cv_broadcast(&node->n_oplock.WaitingOpenCV);
200094047d49SGordon Ross 
200194047d49SGordon Ross 		/*
200294047d49SGordon Ross 		 * If RequestedOplockLevel does not contain WRITE_CACHING:
200394047d49SGordon Ross 		 *	* Set Open.Stream.Oplock.ExclusiveOpen to NULL.
200494047d49SGordon Ross 		 * EndIf
200594047d49SGordon Ross 		 */
200694047d49SGordon Ross 		if ((level & WRITE_CACHING) == 0) {
200794047d49SGordon Ross 			node->n_oplock.excl_open = NULL;
200894047d49SGordon Ross 		}
200994047d49SGordon Ross 
201094047d49SGordon Ross 		/*
201194047d49SGordon Ross 		 * If RequestedOplockLevel is 0 (that is, no flags):
201294047d49SGordon Ross 		 *	* Set Open.Stream.Oplock.State to NO_OPLOCK.
201394047d49SGordon Ross 		 *	* The operation returns Status set to STATUS_SUCCESS
201494047d49SGordon Ross 		 *	  at this point.
201594047d49SGordon Ross 		 */
201694047d49SGordon Ross 		if (level == 0) {
201794047d49SGordon Ross 			node->n_oplock.ol_state = NO_OPLOCK;
201894047d49SGordon Ross 			status = NT_STATUS_SUCCESS;
201994047d49SGordon Ross 			goto out;
202094047d49SGordon Ross 		}
202194047d49SGordon Ross 
202294047d49SGordon Ross 		/*
202394047d49SGordon Ross 		 * Deal with possibly still pending breaks.
202494047d49SGordon Ross 		 * Two cases: R to none, RH to R or none.
202594047d49SGordon Ross 		 *
202694047d49SGordon Ross 		 * XXX: These two missing from [MS-FSA]
202794047d49SGordon Ross 		 */
202894047d49SGordon Ross 
202994047d49SGordon Ross 		/*
2030*7f6a299eSGordon Ross 		 * Breaking R to none.
2031*7f6a299eSGordon Ross 		 *
2032*7f6a299eSGordon Ross 		 * We sent break exclusive (RWH or RW) to none and
2033*7f6a299eSGordon Ross 		 * the client Ack reduces to R instead of to none.
2034*7f6a299eSGordon Ross 		 * Need to send another break. This is like:
203594047d49SGordon Ross 		 * "If BreakCacheLevel contains READ_CACHING..."
203694047d49SGordon Ross 		 * from smb_oplock_break_cmn.
203794047d49SGordon Ross 		 */
203894047d49SGordon Ross 		if (level == CACHE_R && BreakToLevel == LEVEL_NONE) {
203994047d49SGordon Ross 			smb_oplock_ind_break_in_ack(
204094047d49SGordon Ross 			    sr, ofile,
204194047d49SGordon Ross 			    LEVEL_NONE, B_FALSE);
204294047d49SGordon Ross 			node->n_oplock.ol_state = NO_OPLOCK;
204394047d49SGordon Ross 			status = NT_STATUS_SUCCESS;
204494047d49SGordon Ross 			goto out;
204594047d49SGordon Ross 		}
204694047d49SGordon Ross 
204794047d49SGordon Ross 		/*
2048*7f6a299eSGordon Ross 		 * Breaking RH to R or RH to none.
2049*7f6a299eSGordon Ross 		 *
2050*7f6a299eSGordon Ross 		 * We sent break from (RWH or RW) to (R or none),
2051*7f6a299eSGordon Ross 		 * and the client Ack reduces to RH instead of none.
2052*7f6a299eSGordon Ross 		 * Need to send another break. This is like:
205394047d49SGordon Ross 		 * "If BreakCacheLevel equals HANDLE_CACHING..."
205494047d49SGordon Ross 		 * from smb_oplock_break_cmn.
2055*7f6a299eSGordon Ross 		 *
2056*7f6a299eSGordon Ross 		 * Note: Windows always does break to CACHE_R here,
2057*7f6a299eSGordon Ross 		 * letting another Ack and ind_break round trip
2058*7f6a299eSGordon Ross 		 * take us the rest of the way from R to none.
205994047d49SGordon Ross 		 */
206094047d49SGordon Ross 		if (level == CACHE_RH &&
206194047d49SGordon Ross 		    (BreakToLevel == CACHE_R ||
206294047d49SGordon Ross 		    BreakToLevel == LEVEL_NONE)) {
206394047d49SGordon Ross 			smb_oplock_ind_break_in_ack(
206494047d49SGordon Ross 			    sr, ofile,
2065*7f6a299eSGordon Ross 			    CACHE_R, B_TRUE);
206694047d49SGordon Ross 
206794047d49SGordon Ross 			ofile->f_oplock.BreakingToRead =
206894047d49SGordon Ross 			    (BreakToLevel & READ_CACHING) ? 1: 0;
206994047d49SGordon Ross 
207094047d49SGordon Ross 			ASSERT(!(ofile->f_oplock.onlist_RHBQ));
207194047d49SGordon Ross 			ofile->f_oplock.onlist_RHBQ = B_TRUE;
207294047d49SGordon Ross 			node->n_oplock.cnt_RHBQ++;
207394047d49SGordon Ross 
207494047d49SGordon Ross 			RecomputeOplockState(node);
207594047d49SGordon Ross 			status = NT_STATUS_SUCCESS;
207694047d49SGordon Ross 			goto out;
207794047d49SGordon Ross 		}
207894047d49SGordon Ross 
207994047d49SGordon Ross 		/*
208094047d49SGordon Ross 		 * Else If RequestedOplockLevel does not contain WRITE_CACHING:
208194047d49SGordon Ross 		 *	* The object store MUST request a shared oplock
208294047d49SGordon Ross 		 *	  according to the algorithm in section 2.1.5.17.2,
208394047d49SGordon Ross 		 *	  setting the algorithm's parameters as follows:
208494047d49SGordon Ross 		 *		* Pass in the current Open.
208594047d49SGordon Ross 		 *		* RequestedOplock = RequestedOplockLevel.
208694047d49SGordon Ross 		 *		* GrantingInAck = TRUE.
208794047d49SGordon Ross 		 *	* The operation MUST at this point return any status
208894047d49SGordon Ross 		 *	  returned by the shared oplock request algorithm.
208994047d49SGordon Ross 		 */
209094047d49SGordon Ross 		if ((level & WRITE_CACHING) == 0) {
209194047d49SGordon Ross 			*rop = level;
209294047d49SGordon Ross 			status = smb_oplock_req_shared(ofile, rop, B_TRUE);
209394047d49SGordon Ross 			goto out;
209494047d49SGordon Ross 		}
209594047d49SGordon Ross 
209694047d49SGordon Ross 		/*
209794047d49SGordon Ross 		 * Note that because this oplock is being set up as part of
209894047d49SGordon Ross 		 * an acknowledgement of an exclusive oplock break,
209994047d49SGordon Ross 		 * Open.Stream.Oplock.ExclusiveOpen was set
210094047d49SGordon Ross 		 * at the time of the original oplock request;
210194047d49SGordon Ross 		 * it contains Open.
210294047d49SGordon Ross 		 *	* Set Open.Stream.Oplock.State to
210394047d49SGordon Ross 		 *	  (RequestedOplockLevel|EXCLUSIVE).
210494047d49SGordon Ross 		 *	* This operation MUST be made cancelable...
210594047d49SGordon Ross 		 *	* This operation waits until the oplock is broken or
210694047d49SGordon Ross 		 *	  canceled, as specified in section 2.1.5.17.3.
210794047d49SGordon Ross 		 *
210894047d49SGordon Ross 		 * Implementation notes:
210994047d49SGordon Ross 		 *
211094047d49SGordon Ross 		 * This can only be a break from RWH to RW.
211194047d49SGordon Ross 		 * The assignment of ol_state below means there will be
211294047d49SGordon Ross 		 * no BREAK_TO_... bits set, and therefore no need for
211394047d49SGordon Ross 		 * "waits until the oplock is broken" as described in
211494047d49SGordon Ross 		 * the spec for this bit of code.  Therefore, this will
211594047d49SGordon Ross 		 * return SUCCESS instead of OPLOCK_BREAK_IN_PROGRESS.
211694047d49SGordon Ross 		 */
211772b35b05SGordon Ross 		ASSERT(node->n_oplock.excl_open == ofile);
211894047d49SGordon Ross 		node->n_oplock.ol_state = level | EXCLUSIVE;
211994047d49SGordon Ross 		status = NT_STATUS_SUCCESS;
212094047d49SGordon Ross 		break;	/* case (READ_CACHING|WRITE_CACHING|...) */
212194047d49SGordon Ross 
212294047d49SGordon Ross 	default:
212394047d49SGordon Ross 		/* The operation MUST be failed with Status */
212494047d49SGordon Ross 		status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
212594047d49SGordon Ross 		break;
212694047d49SGordon Ross 
212794047d49SGordon Ross 	} /* Switch (oplock.state) */
212894047d49SGordon Ross 
212994047d49SGordon Ross out:
21301f0845f1SGordon Ross 	if (status == NT_STATUS_INVALID_OPLOCK_PROTOCOL)
21311f0845f1SGordon Ross 		*rop = LEVEL_NONE;
21321f0845f1SGordon Ross 
21331f0845f1SGordon Ross 	if (status == NT_STATUS_SUCCESS &&
21341f0845f1SGordon Ross 	    type == LEVEL_GRANULAR &&
21351f0845f1SGordon Ross 	    *rop != LEVEL_NONE) {
21361f0845f1SGordon Ross 		*rop |= LEVEL_GRANULAR;
21371f0845f1SGordon Ross 	}
21381f0845f1SGordon Ross 
2139e8754e84SGordon Ross 	/*
2140e8754e84SGordon Ross 	 * If this node no longer has any oplock grants, let's
2141e8754e84SGordon Ross 	 * go ahead and remove the FEM hooks now. We could leave
2142e8754e84SGordon Ross 	 * that until close, but this lets access outside of SMB
2143e8754e84SGordon Ross 	 * be free of FEM oplock work after a "break to none".
2144e8754e84SGordon Ross 	 */
2145e8754e84SGordon Ross 	if (node->n_oplock.ol_state == NO_OPLOCK &&
2146e8754e84SGordon Ross 	    node->n_oplock.ol_fem == B_TRUE) {
2147e8754e84SGordon Ross 		smb_fem_oplock_uninstall(node);
2148e8754e84SGordon Ross 		node->n_oplock.ol_fem = B_FALSE;
2149e8754e84SGordon Ross 	}
2150e8754e84SGordon Ross 
215194047d49SGordon Ross 	/*
215294047d49SGordon Ross 	 * The spec. describes waiting for a break here,
215394047d49SGordon Ross 	 * but we let the caller do that (when needed) if
215494047d49SGordon Ross 	 * status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS
215572b35b05SGordon Ross 	 *
215672b35b05SGordon Ross 	 * After some research, smb_oplock_ack_break()
215772b35b05SGordon Ross 	 * never returns that status.  Paranoid check.
215894047d49SGordon Ross 	 */
215972b35b05SGordon Ross 	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
216072b35b05SGordon Ross 		ASSERT(!"Unexpected OPLOCK_BREAK_IN_PROGRESS");
216172b35b05SGordon Ross 		status = NT_STATUS_SUCCESS;
216272b35b05SGordon Ross 	}
216394047d49SGordon Ross 
216494047d49SGordon Ross 	return (status);
216594047d49SGordon Ross }
216694047d49SGordon Ross 
216794047d49SGordon Ross /*
216894047d49SGordon Ross  * 2.1.4.12 Algorithm to Check for an Oplock Break
216994047d49SGordon Ross  *
217094047d49SGordon Ross  * The inputs for this algorithm are:
217194047d49SGordon Ross  *
217294047d49SGordon Ross  * Open: The Open being used in the request calling this algorithm.
217394047d49SGordon Ross  *
217494047d49SGordon Ross  * Oplock: The Oplock being checked.
217594047d49SGordon Ross  *
217694047d49SGordon Ross  * Operation: A code describing the operation being processed.
217794047d49SGordon Ross  *
217894047d49SGordon Ross  * OpParams: Parameters associated with the Operation code that are
217994047d49SGordon Ross  *   passed in from the calling request. For example, if Operation is
218094047d49SGordon Ross  *   OPEN, as specified in section 2.1.5.1, then OpParams will have the
218194047d49SGordon Ross  *   members DesiredAccess and CreateDisposition. Each of these is a
218294047d49SGordon Ross  *   parameter to the open request as specified in section 2.1.5.1.
218394047d49SGordon Ross  *   This parameter could be empty, depending on the Operation code.
218494047d49SGordon Ross  *
218594047d49SGordon Ross  * Flags: An optional parameter. If unspecified it is considered to
218694047d49SGordon Ross  *   contain 0. Valid nonzero values are:
218794047d49SGordon Ross  *	PARENT_OBJECT
218894047d49SGordon Ross  *
218994047d49SGordon Ross  * The algorithm uses the following local variables:
219094047d49SGordon Ross  *
219194047d49SGordon Ross  * Boolean values (initialized to FALSE):
219294047d49SGordon Ross  *   BreakToTwo, BreakToNone, NeedToWait
219394047d49SGordon Ross  *
219494047d49SGordon Ross  * BreakCacheLevel – MAY contain 0 or a combination of one or more of
219594047d49SGordon Ross  *   READ_CACHING, WRITE_CACHING, or HANDLE_CACHING, as specified in
219694047d49SGordon Ross  *   section 2.1.1.10. Initialized to 0.
219794047d49SGordon Ross  *   Note that there are only four legal nonzero combinations of flags
219894047d49SGordon Ross  *   for BreakCacheLevel:
219994047d49SGordon Ross  *	(READ_CACHING|WRITE_CACHING|HANDLE_CACHING)
220094047d49SGordon Ross  *	(READ_CACHING|WRITE_CACHING)
220194047d49SGordon Ross  *	WRITE_CACHING
220294047d49SGordon Ross  *	HANDLE_CACHING
220394047d49SGordon Ross  *
220494047d49SGordon Ross  * Algorithm: (all)
220594047d49SGordon Ross  * If Oplock is not empty and Oplock.State is not NO_OPLOCK:
220694047d49SGordon Ross  *	If Flags contains PARENT_OBJECT:
220794047d49SGordon Ross  *		If Operation is OPEN, CLOSE, FLUSH_DATA,
220894047d49SGordon Ross  *		  FS_CONTROL(set_encryption) or
220994047d49SGordon Ross  *		  SET_INFORMATION(Basic, Allocation, EoF,
221094047d49SGordon Ross  *		  Rename, Link, Shortname, VDL):
221194047d49SGordon Ross  *			Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING).
221294047d49SGordon Ross  *		EndIf
221394047d49SGordon Ross  *	Else // Normal operation (not PARENT_OBJECT)
221494047d49SGordon Ross  *		Switch (Operation):
221594047d49SGordon Ross  *		Case OPEN, CLOSE, ...
221694047d49SGordon Ross  *		EndSwitch
221794047d49SGordon Ross  *	EndIf // not parent
221894047d49SGordon Ross  *	// Common section for all above
221994047d49SGordon Ross  *	If BreakToTwo is TRUE:
222094047d49SGordon Ross  *		...
222194047d49SGordon Ross  *	Else If BreakToNone
222294047d49SGordon Ross  *		...
222394047d49SGordon Ross  *	EndIf
222494047d49SGordon Ross  *	...
222594047d49SGordon Ross  * EndIf
222694047d49SGordon Ross  *
222794047d49SGordon Ross  * This implementation uses separate functions for each of:
222894047d49SGordon Ross  *	if (flags & PARENT)... else
222994047d49SGordon Ross  *	switch (Operation)...
223094047d49SGordon Ross  */
223194047d49SGordon Ross 
223294047d49SGordon Ross 
223394047d49SGordon Ross /*
223494047d49SGordon Ross  * If Flags contains PARENT_OBJECT:
223594047d49SGordon Ross  * ...
223694047d49SGordon Ross  * Note that this function is unusual in that the node arg is
223794047d49SGordon Ross  * the PARENT directory node, and ofile is NOT on the ofile list
223894047d49SGordon Ross  * of that directory but one of the nodes under it.
223994047d49SGordon Ross  *
224094047d49SGordon Ross  * Note that until we implement directory leases, this is a no-op.
224194047d49SGordon Ross  */
224294047d49SGordon Ross uint32_t
smb_oplock_break_PARENT(smb_node_t * node,smb_ofile_t * ofile)224394047d49SGordon Ross smb_oplock_break_PARENT(smb_node_t *node, smb_ofile_t *ofile)
224494047d49SGordon Ross {
224594047d49SGordon Ross 	uint32_t BreakCacheLevel;
224694047d49SGordon Ross 
224794047d49SGordon Ross 	/*
224894047d49SGordon Ross 	 * If Operation is OPEN, CLOSE, FLUSH_DATA,
224994047d49SGordon Ross 	 *  FS_CONTROL(set_encryption) or
225094047d49SGordon Ross 	 * SET_INFORMATION(Basic, Allocation, EoF,
225194047d49SGordon Ross 	 * Rename, Link, Shortname, VDL):
225294047d49SGordon Ross 	 *	 Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING).
225394047d49SGordon Ross 	 * EndIf
225494047d49SGordon Ross 	 */
225594047d49SGordon Ross 	BreakCacheLevel = PARENT_OBJECT |
225694047d49SGordon Ross 	    (READ_CACHING|WRITE_CACHING);
225794047d49SGordon Ross 
225894047d49SGordon Ross 	return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
225994047d49SGordon Ross }
226094047d49SGordon Ross 
226194047d49SGordon Ross /*
226294047d49SGordon Ross  * Helper for the cases where section 2.1.5.1 says:
226394047d49SGordon Ross  *
226494047d49SGordon Ross  * If Open.Stream.Oplock is not empty and Open.Stream.Oplock.State
226594047d49SGordon Ross  * contains BATCH_OPLOCK, the object store MUST check for an oplock
226694047d49SGordon Ross  * break according to the algorithm in section 2.1.4.12,
226794047d49SGordon Ross  * with input values as follows:
226894047d49SGordon Ross  *	Open equal to this operation's Open
226994047d49SGordon Ross  *	Oplock equal to Open.Stream.Oplock
227094047d49SGordon Ross  *	Operation equal to "OPEN"
227194047d49SGordon Ross  *	OpParams containing two members:
227294047d49SGordon Ross  *      (DesiredAccess, CreateDisposition)
227394047d49SGordon Ross  *
227494047d49SGordon Ross  * So basically, just call smb_oplock_break_OPEN(), but
227594047d49SGordon Ross  * only if there's a batch oplock.
227694047d49SGordon Ross  */
227794047d49SGordon Ross uint32_t
smb_oplock_break_BATCH(smb_node_t * node,smb_ofile_t * ofile,uint32_t DesiredAccess,uint32_t CreateDisposition)227894047d49SGordon Ross smb_oplock_break_BATCH(smb_node_t *node, smb_ofile_t *ofile,
227994047d49SGordon Ross     uint32_t DesiredAccess, uint32_t CreateDisposition)
228094047d49SGordon Ross {
228194047d49SGordon Ross 	if ((node->n_oplock.ol_state & BATCH_OPLOCK) == 0)
228294047d49SGordon Ross 		return (0);
228394047d49SGordon Ross 
228494047d49SGordon Ross 	return (smb_oplock_break_OPEN(node, ofile,
228594047d49SGordon Ross 	    DesiredAccess, CreateDisposition));
228694047d49SGordon Ross }
228794047d49SGordon Ross 
228894047d49SGordon Ross /*
228994047d49SGordon Ross  * Case OPEN, as specified in section 2.1.5.1:
229094047d49SGordon Ross  *
229194047d49SGordon Ross  * Note: smb_ofile_open constructs a partially complete smb_ofile_t
229294047d49SGordon Ross  * for this call, which can be considerd a "proposed open".  This
229394047d49SGordon Ross  * open may or may not turn into a usable open depending on what
229494047d49SGordon Ross  * happens in the remainder of the ofile_open code path.
229594047d49SGordon Ross  */
229694047d49SGordon Ross uint32_t
smb_oplock_break_OPEN(smb_node_t * node,smb_ofile_t * ofile,uint32_t DesiredAccess,uint32_t CreateDisposition)229794047d49SGordon Ross smb_oplock_break_OPEN(smb_node_t *node, smb_ofile_t *ofile,
229894047d49SGordon Ross     uint32_t DesiredAccess, uint32_t CreateDisposition)
229994047d49SGordon Ross {
230094047d49SGordon Ross 	uint32_t BreakCacheLevel = 0;
230194047d49SGordon Ross 	/* BreakToTwo, BreakToNone, NeedToWait */
230294047d49SGordon Ross 
230394047d49SGordon Ross 	/*
230494047d49SGordon Ross 	 * If OpParams.DesiredAccess contains no flags other than
230594047d49SGordon Ross 	 * FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES, or SYNCHRONIZE,
230694047d49SGordon Ross 	 *   the algorithm returns at this point.
230794047d49SGordon Ross 	 * EndIf
230894047d49SGordon Ross 	 */
230994047d49SGordon Ross 	if ((DesiredAccess & ~(FILE_READ_ATTRIBUTES |
231094047d49SGordon Ross 	    FILE_WRITE_ATTRIBUTES | SYNCHRONIZE | READ_CONTROL)) == 0)
231194047d49SGordon Ross 		return (0);
231294047d49SGordon Ross 
231394047d49SGordon Ross 	/*
231494047d49SGordon Ross 	 * If OpParams.CreateDisposition is FILE_SUPERSEDE,
231594047d49SGordon Ross 	 * FILE_OVERWRITE, or FILE_OVERWRITE_IF:
231694047d49SGordon Ross 	 *	Set BreakToNone to TRUE, set BreakCacheLevel to
231794047d49SGordon Ross 	 *	   (READ_CACHING|WRITE_CACHING).
231894047d49SGordon Ross 	 * Else
231994047d49SGordon Ross 	 *	Set BreakToTwo to TRUE,
232094047d49SGordon Ross 	 *	set BreakCacheLevel to WRITE_CACHING.
232194047d49SGordon Ross 	 * EndIf
232294047d49SGordon Ross 	 */
232394047d49SGordon Ross 	if (CreateDisposition == FILE_SUPERSEDE ||
232494047d49SGordon Ross 	    CreateDisposition == FILE_OVERWRITE ||
232594047d49SGordon Ross 	    CreateDisposition == FILE_OVERWRITE_IF) {
232694047d49SGordon Ross 		BreakCacheLevel = BREAK_TO_NONE |
232794047d49SGordon Ross 		    (READ_CACHING|WRITE_CACHING);
232894047d49SGordon Ross 	} else {
232994047d49SGordon Ross 		/*
233094047d49SGordon Ross 		 * CreateDispositons: OPEN, OPEN_IF
233194047d49SGordon Ross 		 */
233294047d49SGordon Ross 		BreakCacheLevel = BREAK_TO_TWO |
233394047d49SGordon Ross 		    WRITE_CACHING;
233494047d49SGordon Ross 	}
233594047d49SGordon Ross 
233694047d49SGordon Ross 	return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
233794047d49SGordon Ross }
233894047d49SGordon Ross 
233994047d49SGordon Ross /*
234094047d49SGordon Ross  * Case OPEN_BREAK_H, as specified in section 2.1.5.1:
234194047d49SGordon Ross  *	Set BreakCacheLevel to HANDLE_CACHING.
234294047d49SGordon Ross  * EndCase
234394047d49SGordon Ross  */
234494047d49SGordon Ross uint32_t
smb_oplock_break_HANDLE(smb_node_t * node,smb_ofile_t * ofile)234594047d49SGordon Ross smb_oplock_break_HANDLE(smb_node_t *node, smb_ofile_t *ofile)
234694047d49SGordon Ross {
234794047d49SGordon Ross 	uint32_t BreakCacheLevel = HANDLE_CACHING;
234894047d49SGordon Ross 
234994047d49SGordon Ross 	return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
235094047d49SGordon Ross }
235194047d49SGordon Ross 
235294047d49SGordon Ross /*
235394047d49SGordon Ross  * Case CLOSE, as specified in section 2.1.5.4:
235494047d49SGordon Ross  *
235594047d49SGordon Ross  * The MS-FSA spec. describes sending oplock break indications
235694047d49SGordon Ross  * (smb_oplock_ind_break ... NT_STATUS_OPLOCK_HANDLE_CLOSED)
235794047d49SGordon Ross  * for several cases where the ofile we're closing has some
235894047d49SGordon Ross  * oplock grants.  We modify these slightly and use them to
235994047d49SGordon Ross  * clear out the SMB-level oplock state.  We could probably
236094047d49SGordon Ross  * just skip most of these, as the caller knows this handle is
236194047d49SGordon Ross  * closing and could just discard the SMB-level oplock state.
236294047d49SGordon Ross  * For now, keeping this close to what the spec says.
236394047d49SGordon Ross  */
236494047d49SGordon Ross void
smb_oplock_break_CLOSE(smb_node_t * node,smb_ofile_t * ofile)236594047d49SGordon Ross smb_oplock_break_CLOSE(smb_node_t *node, smb_ofile_t *ofile)
236694047d49SGordon Ross {
236794047d49SGordon Ross 	smb_ofile_t *o;
236894047d49SGordon Ross 
23691f0845f1SGordon Ross 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
23701f0845f1SGordon Ross 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
237194047d49SGordon Ross 
2372*7f6a299eSGordon Ross #ifdef	DEBUG
2373*7f6a299eSGordon Ross 	FOREACH_NODE_OFILE(node, o) {
2374*7f6a299eSGordon Ross 		DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
2375*7f6a299eSGordon Ross 	}
2376*7f6a299eSGordon Ross #endif
2377*7f6a299eSGordon Ross 
237894047d49SGordon Ross 	/*
237994047d49SGordon Ross 	 * If Oplock.IIOplocks is not empty:
238094047d49SGordon Ross 	 *   For each Open ThisOpen in Oplock.IIOplocks:
238194047d49SGordon Ross 	 *	If ThisOpen == Open:
238294047d49SGordon Ross 	 *		Remove ThisOpen from Oplock.IIOplocks.
238394047d49SGordon Ross 	 *		Notify the server of an oplock break according to
238494047d49SGordon Ross 	 *		  the algorithm in section 2.1.5.17.3, setting the
238594047d49SGordon Ross 	 *		  algorithm's parameters as follows:
238694047d49SGordon Ross 	 *			BreakingOplockOpen = ThisOpen.
238794047d49SGordon Ross 	 *			NewOplockLevel = LEVEL_NONE.
238894047d49SGordon Ross 	 *			AcknowledgeRequired = FALSE.
238994047d49SGordon Ross 	 *			OplockCompletionStatus = STATUS_SUCCESS.
239094047d49SGordon Ross 	 *		(The operation does not end at this point;
239194047d49SGordon Ross 	 *		 this call to 2.1.5.17.3 completes some
239294047d49SGordon Ross 	 *		 earlier call to 2.1.5.17.2.)
239394047d49SGordon Ross 	 *	EndIf
239494047d49SGordon Ross 	 *   EndFor
239594047d49SGordon Ross 	 *   Recompute Oplock.State according to the algorithm in
239694047d49SGordon Ross 	 *     section 2.1.4.13, passing Oplock as the ThisOplock parameter.
239794047d49SGordon Ross 	 * EndIf
239894047d49SGordon Ross 	 */
239994047d49SGordon Ross 	if (node->n_oplock.cnt_II > 0) {
240094047d49SGordon Ross 		o = ofile; /* No need for list walk */
240194047d49SGordon Ross 		if (o->f_oplock.onlist_II) {
240294047d49SGordon Ross 			o->f_oplock.onlist_II = B_FALSE;
240394047d49SGordon Ross 			node->n_oplock.cnt_II--;
240494047d49SGordon Ross 			ASSERT(node->n_oplock.cnt_II >= 0);
240594047d49SGordon Ross 			/*
240694047d49SGordon Ross 			 * The spec. says to do:
240794047d49SGordon Ross 			 * smb_oplock_ind_break(o,
240894047d49SGordon Ross 			 *    LEVEL_NONE, B_FALSE,
240994047d49SGordon Ross 			 *    NT_STATUS_SUCCESS);
241094047d49SGordon Ross 			 *
241194047d49SGordon Ross 			 * We'll use STATUS_OPLOCK_HANDLE_CLOSED
241294047d49SGordon Ross 			 * like all the other ind_break calls in
241394047d49SGordon Ross 			 * this function, so the SMB-level will
241494047d49SGordon Ross 			 * just clear out its oplock state.
241594047d49SGordon Ross 			 */
241694047d49SGordon Ross 			smb_oplock_ind_break(o,
241794047d49SGordon Ross 			    LEVEL_NONE, B_FALSE,
241894047d49SGordon Ross 			    NT_STATUS_OPLOCK_HANDLE_CLOSED);
241994047d49SGordon Ross 		}
242094047d49SGordon Ross 		RecomputeOplockState(node);
242194047d49SGordon Ross 	}
242294047d49SGordon Ross 
242394047d49SGordon Ross 	/*
242494047d49SGordon Ross 	 * If Oplock.ROplocks is not empty:
242594047d49SGordon Ross 	 *   For each Open ThisOpen in Oplock.ROplocks:
242694047d49SGordon Ross 	 *	If ThisOpen == Open:
242794047d49SGordon Ross 	 *		Remove ThisOpen from Oplock.ROplocks.
242894047d49SGordon Ross 	 *		Notify the server of an oplock break according to
242994047d49SGordon Ross 	 *		  the algorithm in section 2.1.5.17.3, setting the
243094047d49SGordon Ross 	 *		  algorithm's parameters as follows:
243194047d49SGordon Ross 	 *			BreakingOplockOpen = ThisOpen.
243294047d49SGordon Ross 	 *			NewOplockLevel = LEVEL_NONE.
243394047d49SGordon Ross 	 *			AcknowledgeRequired = FALSE.
243494047d49SGordon Ross 	 *			OplockCompletionStatus =
243594047d49SGordon Ross 	 *			  STATUS_OPLOCK_HANDLE_CLOSED.
243694047d49SGordon Ross 	 *		(The operation does not end at this point;
243794047d49SGordon Ross 	 *		 this call to 2.1.5.17.3 completes some
243894047d49SGordon Ross 	 *		 earlier call to 2.1.5.17.2.)
243994047d49SGordon Ross 	 *	EndIf
244094047d49SGordon Ross 	 *   EndFor
244194047d49SGordon Ross 	 *   Recompute Oplock.State according to the algorithm in
244294047d49SGordon Ross 	 *     section 2.1.4.13, passing Oplock as the ThisOplock parameter.
244394047d49SGordon Ross 	 * EndIf
244494047d49SGordon Ross 	 */
244594047d49SGordon Ross 	if (node->n_oplock.cnt_R > 0) {
244694047d49SGordon Ross 		o = ofile; /* No need for list walk */
244794047d49SGordon Ross 		if (o->f_oplock.onlist_R) {
244894047d49SGordon Ross 			o->f_oplock.onlist_R = B_FALSE;
244994047d49SGordon Ross 			node->n_oplock.cnt_R--;
245094047d49SGordon Ross 			ASSERT(node->n_oplock.cnt_R >= 0);
245194047d49SGordon Ross 
245294047d49SGordon Ross 			smb_oplock_ind_break(o,
245394047d49SGordon Ross 			    LEVEL_NONE, B_FALSE,
245494047d49SGordon Ross 			    NT_STATUS_OPLOCK_HANDLE_CLOSED);
245594047d49SGordon Ross 		}
245694047d49SGordon Ross 		RecomputeOplockState(node);
245794047d49SGordon Ross 	}
245894047d49SGordon Ross 
245994047d49SGordon Ross 	/*
246094047d49SGordon Ross 	 * If Oplock.RHOplocks is not empty:
246194047d49SGordon Ross 	 *   For each Open ThisOpen in Oplock.RHOplocks:
246294047d49SGordon Ross 	 *	If ThisOpen == Open:
246394047d49SGordon Ross 	 *		Remove ThisOpen from Oplock.RHOplocks.
246494047d49SGordon Ross 	 *		Notify the server of an oplock break according to
246594047d49SGordon Ross 	 *		  the algorithm in section 2.1.5.17.3, setting the
246694047d49SGordon Ross 	 *		  algorithm's parameters as follows:
246794047d49SGordon Ross 	 *			BreakingOplockOpen = ThisOpen.
246894047d49SGordon Ross 	 *			NewOplockLevel = LEVEL_NONE.
246994047d49SGordon Ross 	 *			AcknowledgeRequired = FALSE.
247094047d49SGordon Ross 	 *			OplockCompletionStatus =
247194047d49SGordon Ross 	 *			   STATUS_OPLOCK_HANDLE_CLOSED.
247294047d49SGordon Ross 	 *		(The operation does not end at this point;
247394047d49SGordon Ross 	 *		 this call to 2.1.5.17.3 completes some
247494047d49SGordon Ross 	 *		 earlier call to 2.1.5.17.2.)
247594047d49SGordon Ross 	 *	EndIf
247694047d49SGordon Ross 	 *   EndFor
247794047d49SGordon Ross 	 *   Recompute Oplock.State according to the algorithm in
247894047d49SGordon Ross 	 *     section 2.1.4.13, passing Oplock as the ThisOplock parameter.
247994047d49SGordon Ross 	 * EndIf
248094047d49SGordon Ross 	 */
248194047d49SGordon Ross 	if (node->n_oplock.cnt_RH > 0) {
248294047d49SGordon Ross 		o = ofile; /* No need for list walk */
248394047d49SGordon Ross 		if (o->f_oplock.onlist_RH) {
248494047d49SGordon Ross 			o->f_oplock.onlist_RH = B_FALSE;
248594047d49SGordon Ross 			node->n_oplock.cnt_RH--;
248694047d49SGordon Ross 			ASSERT(node->n_oplock.cnt_RH >= 0);
248794047d49SGordon Ross 
248894047d49SGordon Ross 			smb_oplock_ind_break(o,
248994047d49SGordon Ross 			    LEVEL_NONE, B_FALSE,
249094047d49SGordon Ross 			    NT_STATUS_OPLOCK_HANDLE_CLOSED);
249194047d49SGordon Ross 		}
249294047d49SGordon Ross 		RecomputeOplockState(node);
249394047d49SGordon Ross 	}
249494047d49SGordon Ross 
249594047d49SGordon Ross 	/*
249694047d49SGordon Ross 	 * If Oplock.RHBreakQueue is not empty:
249794047d49SGordon Ross 	 *	For each RHOpContext ThisContext in Oplock.RHBreakQueue:
249894047d49SGordon Ross 	 *		If ThisContext.Open == Open:
249994047d49SGordon Ross 	 *			Remove ThisContext from Oplock.RHBreakQueue.
250094047d49SGordon Ross 	 *		EndIf
250194047d49SGordon Ross 	 *	EndFor
250294047d49SGordon Ross 	 *	Recompute Oplock.State according to the algorithm in
250394047d49SGordon Ross 	 *	  section 2.1.4.13, passing Oplock as the ThisOplock parameter.
250494047d49SGordon Ross 	 *	For each Open WaitingOpen on Oplock.WaitList:
250594047d49SGordon Ross 	 *		If Oplock.RHBreakQueue is empty:
250694047d49SGordon Ross 	 *		(or) If the value of every
250794047d49SGordon Ross 	 *		RHOpContext.Open.TargetOplockKey
250894047d49SGordon Ross 	 *		on Oplock.RHBreakQueue is equal to
250994047d49SGordon Ross 	 *		WaitingOpen .TargetOplockKey:
251094047d49SGordon Ross 	 *			Indicate that the op. assoc. with
251194047d49SGordon Ross 	 *			WaitingOpen can continue according to
251294047d49SGordon Ross 	 *			the algorithm in section 2.1.4.12.1,
251394047d49SGordon Ross 	 *			setting OpenToRelease = WaitingOpen.
251494047d49SGordon Ross 	 *			Remove WaitingOpen from Oplock.WaitList.
251594047d49SGordon Ross 	 *		EndIf
251694047d49SGordon Ross 	 *	EndFor
251794047d49SGordon Ross 	 * EndIf
251894047d49SGordon Ross 	 */
251994047d49SGordon Ross 	if (node->n_oplock.cnt_RHBQ > 0) {
252094047d49SGordon Ross 		o = ofile; /* No need for list walk */
252194047d49SGordon Ross 		if (o->f_oplock.onlist_RHBQ) {
252294047d49SGordon Ross 			o->f_oplock.onlist_RHBQ = B_FALSE;
252394047d49SGordon Ross 			node->n_oplock.cnt_RHBQ--;
252494047d49SGordon Ross 			ASSERT(node->n_oplock.cnt_RHBQ >= 0);
252594047d49SGordon Ross 		}
252694047d49SGordon Ross 		RecomputeOplockState(node);
252794047d49SGordon Ross 		/*
252894047d49SGordon Ross 		 * We don't keep a WaitingOpen list, so just
252994047d49SGordon Ross 		 * wake them all and let them look at the
253094047d49SGordon Ross 		 * updated Oplock.RHBreakQueue
253194047d49SGordon Ross 		 */
253294047d49SGordon Ross 		cv_broadcast(&node->n_oplock.WaitingOpenCV);
253394047d49SGordon Ross 	}
253494047d49SGordon Ross 
253594047d49SGordon Ross 	/*
253694047d49SGordon Ross 	 * If Open equals Open.Oplock.ExclusiveOpen
253794047d49SGordon Ross 	 *	If Oplock.State contains none of (BREAK_ANY):
253894047d49SGordon Ross 	 *		Notify the server of an oplock break according to
253994047d49SGordon Ross 	 *		  the algorithm in section 2.1.5.17.3, setting the
254094047d49SGordon Ross 	 *		  algorithm's parameters as follows:
254194047d49SGordon Ross 	 *			BreakingOplockOpen = Oplock.ExclusiveOpen.
254294047d49SGordon Ross 	 *			NewOplockLevel = LEVEL_NONE.
254394047d49SGordon Ross 	 *			AcknowledgeRequired = FALSE.
254494047d49SGordon Ross 	 *			OplockCompletionStatus equal to:
254594047d49SGordon Ross 	 *				STATUS_OPLOCK_HANDLE_CLOSED if
254694047d49SGordon Ross 	 *				  Oplock.State contains any of
254794047d49SGordon Ross 	 *				  READ_CACHING, WRITE_CACHING, or
254894047d49SGordon Ross 	 *				  HANDLE_CACHING.
254994047d49SGordon Ross 	 *				STATUS_SUCCESS otherwise.
255094047d49SGordon Ross 	 *		(The operation does not end at this point;
255194047d49SGordon Ross 	 *		 this call to 2.1.5.17.3 completes some
255294047d49SGordon Ross 	 *		 earlier call to 2.1.5.17.1.)
255394047d49SGordon Ross 	 *	EndIf
255494047d49SGordon Ross 	 *	Set Oplock.ExclusiveOpen to NULL.
255594047d49SGordon Ross 	 *	Set Oplock.State to NO_OPLOCK.
255694047d49SGordon Ross 	 *	For each Open WaitingOpen on Oplock.WaitList:
255794047d49SGordon Ross 	 *		Indicate that the operation associated with WaitingOpen
255894047d49SGordon Ross 	 *		  can continue according to the algorithm in section
255994047d49SGordon Ross 	 *		  2.1.4.12.1, setting OpenToRelease = WaitingOpen.
256094047d49SGordon Ross 	 *		Remove WaitingOpen from Oplock.WaitList.
256194047d49SGordon Ross 	 *	EndFor
256294047d49SGordon Ross 	 * EndIf
256394047d49SGordon Ross 	 *
256494047d49SGordon Ross 	 * Modify this slightly from what the spec. says and only
256594047d49SGordon Ross 	 * up-call the break with status STATUS_OPLOCK_HANDLE_CLOSED.
256694047d49SGordon Ross 	 * The STATUS_SUCCESS case would do nothing at the SMB level,
256794047d49SGordon Ross 	 * so we'll just skip that part.
256894047d49SGordon Ross 	 */
256994047d49SGordon Ross 	if (ofile == node->n_oplock.excl_open) {
257094047d49SGordon Ross 		uint32_t level = node->n_oplock.ol_state & CACHE_RWH;
257194047d49SGordon Ross 		if (level != 0 &&
257294047d49SGordon Ross 		    (node->n_oplock.ol_state & BREAK_ANY) == 0) {
257394047d49SGordon Ross 			smb_oplock_ind_break(ofile,
257494047d49SGordon Ross 			    LEVEL_NONE, B_FALSE,
257594047d49SGordon Ross 			    NT_STATUS_OPLOCK_HANDLE_CLOSED);
257694047d49SGordon Ross 		}
257794047d49SGordon Ross 		node->n_oplock.excl_open = NULL;
257894047d49SGordon Ross 		node->n_oplock.ol_state = NO_OPLOCK;
257994047d49SGordon Ross 		cv_broadcast(&node->n_oplock.WaitingOpenCV);
258094047d49SGordon Ross 	}
258194047d49SGordon Ross 
258294047d49SGordon Ross 	/*
258394047d49SGordon Ross 	 * The CLOSE sub-case of 2.1.5.4 (separate function here)
258494047d49SGordon Ross 	 * happens to always leave BreakCacheLevel=0 (see 2.1.5.4)
258594047d49SGordon Ross 	 * so there's never a need to call smb_oplock_break_cmn()
258694047d49SGordon Ross 	 * in this function.  If that changed and we were to have
258794047d49SGordon Ross 	 * BreakCacheLevel != 0 here, then we'd need to call:
258894047d49SGordon Ross 	 * smb_oplock_break_cmn(node, ofile, BreakCacheLevel);
258994047d49SGordon Ross 	 */
259094047d49SGordon Ross 
259194047d49SGordon Ross 	if ((node->n_oplock.ol_state & BREAK_ANY) == 0)
259294047d49SGordon Ross 		cv_broadcast(&node->n_oplock.WaitingOpenCV);
259394047d49SGordon Ross 
2594e8754e84SGordon Ross 	/*
2595e8754e84SGordon Ross 	 * If no longer any oplock, remove FEM hooks.
2596e8754e84SGordon Ross 	 */
2597e8754e84SGordon Ross 	if (node->n_oplock.ol_state == NO_OPLOCK &&
2598e8754e84SGordon Ross 	    node->n_oplock.ol_fem == B_TRUE) {
2599e8754e84SGordon Ross 		smb_fem_oplock_uninstall(node);
2600e8754e84SGordon Ross 		node->n_oplock.ol_fem = B_FALSE;
2601e8754e84SGordon Ross 	}
260294047d49SGordon Ross }
260394047d49SGordon Ross 
260494047d49SGordon Ross /*
260594047d49SGordon Ross  * Case READ, as specified in section 2.1.5.2:
260694047d49SGordon Ross  *	Set BreakToTwo to TRUE
260794047d49SGordon Ross  *	Set BreakCacheLevel to WRITE_CACHING.
260894047d49SGordon Ross  * EndCase
260994047d49SGordon Ross  */
261094047d49SGordon Ross uint32_t
smb_oplock_break_READ(smb_node_t * node,smb_ofile_t * ofile)261194047d49SGordon Ross smb_oplock_break_READ(smb_node_t *node, smb_ofile_t *ofile)
261294047d49SGordon Ross {
261394047d49SGordon Ross 	uint32_t BreakCacheLevel = BREAK_TO_TWO | WRITE_CACHING;
261494047d49SGordon Ross 
261594047d49SGordon Ross 	return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
261694047d49SGordon Ross }
261794047d49SGordon Ross 
261894047d49SGordon Ross /*
261994047d49SGordon Ross  * Case FLUSH_DATA, as specified in section 2.1.5.6:
262094047d49SGordon Ross  *	Set BreakToTwo to TRUE
262194047d49SGordon Ross  *	Set BreakCacheLevel to WRITE_CACHING.
262294047d49SGordon Ross  * EndCase
262394047d49SGordon Ross  * Callers just use smb_oplock_break_READ() -- same thing.
262494047d49SGordon Ross  */
262594047d49SGordon Ross 
262694047d49SGordon Ross /*
262794047d49SGordon Ross  * Case LOCK_CONTROL, as specified in section 2.1.5.7:
262894047d49SGordon Ross  * Note: Spec does fall-through to WRITE here.
262994047d49SGordon Ross  *
263094047d49SGordon Ross  * Case WRITE, as specified in section 2.1.5.3:
263194047d49SGordon Ross  *	Set BreakToNone to TRUE
263294047d49SGordon Ross  *	Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING).
263394047d49SGordon Ross  * EndCase
263494047d49SGordon Ross  */
263594047d49SGordon Ross uint32_t
smb_oplock_break_WRITE(smb_node_t * node,smb_ofile_t * ofile)263694047d49SGordon Ross smb_oplock_break_WRITE(smb_node_t *node, smb_ofile_t *ofile)
263794047d49SGordon Ross {
263894047d49SGordon Ross 	uint32_t BreakCacheLevel = BREAK_TO_NONE |
263994047d49SGordon Ross 	    (READ_CACHING|WRITE_CACHING);
264094047d49SGordon Ross 
264194047d49SGordon Ross 	return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
264294047d49SGordon Ross }
264394047d49SGordon Ross 
264494047d49SGordon Ross /*
264594047d49SGordon Ross  * Case SET_INFORMATION, as specified in section 2.1.5.14:
264694047d49SGordon Ross  * Switch (OpParams.FileInformationClass):
264794047d49SGordon Ross  *	Case FileEndOfFileInformation:
264894047d49SGordon Ross  *	Case FileAllocationInformation:
264994047d49SGordon Ross  *		Set BreakToNone to TRUE
265094047d49SGordon Ross  *		Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING).
265194047d49SGordon Ross  *	EndCase
265294047d49SGordon Ross  *	Case FileRenameInformation:
265394047d49SGordon Ross  *	Case FileLinkInformation:
265494047d49SGordon Ross  *	Case FileShortNameInformation:
265594047d49SGordon Ross  *		Set BreakCacheLevel to HANDLE_CACHING.
265694047d49SGordon Ross  *		If Oplock.State contains BATCH_OPLOCK,
265794047d49SGordon Ross  *		  set BreakToNone to TRUE.
265894047d49SGordon Ross  *	EndCase
265994047d49SGordon Ross  *	Case FileDispositionInformation:
266094047d49SGordon Ross  *		If OpParams.DeleteFile is TRUE,
266194047d49SGordon Ross  *		Set BreakCacheLevel to HANDLE_CACHING.
266294047d49SGordon Ross  *	EndCase
266394047d49SGordon Ross  * EndSwitch
266494047d49SGordon Ross  */
266594047d49SGordon Ross uint32_t
smb_oplock_break_SETINFO(smb_node_t * node,smb_ofile_t * ofile,uint32_t InfoClass)266694047d49SGordon Ross smb_oplock_break_SETINFO(smb_node_t *node, smb_ofile_t *ofile,
266794047d49SGordon Ross     uint32_t InfoClass)
266894047d49SGordon Ross {
266994047d49SGordon Ross 	uint32_t BreakCacheLevel = 0;
267094047d49SGordon Ross 
267194047d49SGordon Ross 	switch (InfoClass) {
267294047d49SGordon Ross 	case FileEndOfFileInformation:
267394047d49SGordon Ross 	case FileAllocationInformation:
267494047d49SGordon Ross 		BreakCacheLevel = BREAK_TO_NONE |
267594047d49SGordon Ross 		    (READ_CACHING|WRITE_CACHING);
267694047d49SGordon Ross 		break;
267794047d49SGordon Ross 
267894047d49SGordon Ross 	case FileRenameInformation:
267994047d49SGordon Ross 	case FileLinkInformation:
268094047d49SGordon Ross 	case FileShortNameInformation:
268194047d49SGordon Ross 		BreakCacheLevel = HANDLE_CACHING;
268294047d49SGordon Ross 		if (node->n_oplock.ol_state & BATCH_OPLOCK) {
268394047d49SGordon Ross 			BreakCacheLevel |= BREAK_TO_NONE;
268494047d49SGordon Ross 		}
268594047d49SGordon Ross 		break;
268694047d49SGordon Ross 	case FileDispositionInformation:
268794047d49SGordon Ross 		/* Only called if (OpParams.DeleteFile is TRUE) */
268894047d49SGordon Ross 		BreakCacheLevel = HANDLE_CACHING;
268994047d49SGordon Ross 		break;
269094047d49SGordon Ross 
269194047d49SGordon Ross 	}
269294047d49SGordon Ross 
269394047d49SGordon Ross 	return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
269494047d49SGordon Ross }
269594047d49SGordon Ross 
269694047d49SGordon Ross /*
269794047d49SGordon Ross  * This one is not from the spec.  It appears that Windows will
269894047d49SGordon Ross  * open a handle for an SMB1 delete call (at least internally).
269994047d49SGordon Ross  * We don't open a handle for delete, but do want to break as if
270094047d49SGordon Ross  * we had done, so this breaks like a combination of:
270194047d49SGordon Ross  *	break_BATCH(... DELETE, FILE_OPEN_IF)
270294047d49SGordon Ross  *	break_HANDLE(...)
270394047d49SGordon Ross  */
270494047d49SGordon Ross uint32_t
smb_oplock_break_DELETE(smb_node_t * node,smb_ofile_t * ofile)270594047d49SGordon Ross smb_oplock_break_DELETE(smb_node_t *node, smb_ofile_t *ofile)
270694047d49SGordon Ross {
270794047d49SGordon Ross 	uint32_t BreakCacheLevel = HANDLE_CACHING;
270894047d49SGordon Ross 
270994047d49SGordon Ross 	if ((node->n_oplock.ol_state & BATCH_OPLOCK) != 0)
271094047d49SGordon Ross 		BreakCacheLevel |= BREAK_TO_TWO;
271194047d49SGordon Ross 
271294047d49SGordon Ross 	return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
271394047d49SGordon Ross }
271494047d49SGordon Ross 
271594047d49SGordon Ross /*
271694047d49SGordon Ross  * Case FS_CONTROL, as specified in section 2.1.5.9:
271794047d49SGordon Ross  *	If OpParams.ControlCode is FSCTL_SET_ZERO_DATA:
271894047d49SGordon Ross  *		Set BreakToNone to TRUE.
271994047d49SGordon Ross  *		Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING).
272094047d49SGordon Ross  *	EndIf
272194047d49SGordon Ross  * EndCase
272294047d49SGordon Ross  * Callers just use smb_oplock_break_WRITE() -- same thing.
272394047d49SGordon Ross  */
272494047d49SGordon Ross 
272594047d49SGordon Ross /*
272694047d49SGordon Ross  * Common section for all cases above
272794047d49SGordon Ross  * Note: When called via FEM: ofile == NULL
272894047d49SGordon Ross  */
272994047d49SGordon Ross static uint32_t
smb_oplock_break_cmn(smb_node_t * node,smb_ofile_t * ofile,uint32_t BreakCacheLevel)273094047d49SGordon Ross smb_oplock_break_cmn(smb_node_t *node,
273194047d49SGordon Ross     smb_ofile_t *ofile, uint32_t BreakCacheLevel)
273294047d49SGordon Ross {
273394047d49SGordon Ross 	smb_oplock_t *nol = &node->n_oplock;
273494047d49SGordon Ross 	uint32_t CmpFlags, status;
273594047d49SGordon Ross 	boolean_t BreakToTwo, BreakToNone, NeedToWait;
273694047d49SGordon Ross 	smb_ofile_t *o = NULL;
273794047d49SGordon Ross 
273894047d49SGordon Ross 	CmpFlags = (BreakCacheLevel & PARENT_OBJECT);
273994047d49SGordon Ross 	BreakToTwo = (BreakCacheLevel & BREAK_TO_TWO) != 0;
274094047d49SGordon Ross 	BreakToNone = (BreakCacheLevel & BREAK_TO_NONE) != 0;
274194047d49SGordon Ross 	BreakCacheLevel &= (READ_CACHING | WRITE_CACHING | HANDLE_CACHING);
274294047d49SGordon Ross 	NeedToWait = B_FALSE;
274394047d49SGordon Ross 	status = NT_STATUS_SUCCESS;
274494047d49SGordon Ross 
274594047d49SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
274694047d49SGordon Ross 	mutex_enter(&node->n_oplock.ol_mutex);
274794047d49SGordon Ross 
2748*7f6a299eSGordon Ross #ifdef	DEBUG
2749*7f6a299eSGordon Ross 	FOREACH_NODE_OFILE(node, o) {
2750*7f6a299eSGordon Ross 		DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
2751*7f6a299eSGordon Ross 	}
2752*7f6a299eSGordon Ross #endif
2753*7f6a299eSGordon Ross 
275494047d49SGordon Ross 	if (node->n_oplock.ol_state == 0 ||
275594047d49SGordon Ross 	    node->n_oplock.ol_state == NO_OPLOCK)
275694047d49SGordon Ross 		goto out;
275794047d49SGordon Ross 
275894047d49SGordon Ross 	if (BreakToTwo) {
275994047d49SGordon Ross 		/*
276094047d49SGordon Ross 		 * If (Oplock.State != LEVEL_TWO_OPLOCK) and
276194047d49SGordon Ross 		 *    ((Oplock.ExclusiveOpen is empty) or
276294047d49SGordon Ross 		 *     (Oplock.ExclusiveOpen.TargetOplockKey !=
276394047d49SGordon Ross 		 *      Open.TargetOplockKey)):
276494047d49SGordon Ross 		 */
276594047d49SGordon Ross 		if ((nol->ol_state != LEVEL_TWO_OPLOCK) &&
276694047d49SGordon Ross 		    (((o = nol->excl_open) == NULL) ||
276794047d49SGordon Ross 		    !CompareOplockKeys(ofile, o, CmpFlags))) {
276894047d49SGordon Ross 
276994047d49SGordon Ross 			/*
277094047d49SGordon Ross 			 * If (Oplock.State contains EXCLUSIVE) and
277194047d49SGordon Ross 			 *  (Oplock.State contains none of READ_CACHING,
277294047d49SGordon Ross 			 *   WRITE_CACHING, or HANDLE_CACHING):
277394047d49SGordon Ross 			 */
277494047d49SGordon Ross 			if ((nol->ol_state & EXCLUSIVE) != 0 &&
277594047d49SGordon Ross 			    (nol->ol_state & CACHE_RWH) == 0) {
277694047d49SGordon Ross 				/*
277794047d49SGordon Ross 				 * If Oplock.State contains none of:
277894047d49SGordon Ross 				 *	BREAK_TO_NONE,
277994047d49SGordon Ross 				 *	BREAK_TO_TWO,
278094047d49SGordon Ross 				 *	BREAK_TO_TWO_TO_NONE,
278194047d49SGordon Ross 				 *	BREAK_TO_READ_CACHING,
278294047d49SGordon Ross 				 *	BREAK_TO_WRITE_CACHING,
278394047d49SGordon Ross 				 *	BREAK_TO_HANDLE_CACHING,
278494047d49SGordon Ross 				 *	BREAK_TO_NO_CACHING:
278594047d49SGordon Ross 				 */
278694047d49SGordon Ross 				if ((nol->ol_state & BREAK_ANY) == 0) {
278794047d49SGordon Ross 
278894047d49SGordon Ross 					/*
278994047d49SGordon Ross 					 * Oplock.State MUST contain either
279094047d49SGordon Ross 					 * LEVEL_ONE_OPLOCK or BATCH_OPLOCK.
279194047d49SGordon Ross 					 * Set BREAK_TO_TWO in Oplock.State.
279294047d49SGordon Ross 					 */
279394047d49SGordon Ross 					ASSERT((nol->ol_state &
279494047d49SGordon Ross 					    (LEVEL_ONE | LEVEL_BATCH)) != 0);
279594047d49SGordon Ross 					nol->ol_state |= BREAK_TO_TWO;
279694047d49SGordon Ross 
279794047d49SGordon Ross 					/*
279894047d49SGordon Ross 					 * Notify the server of an oplock break
279994047d49SGordon Ross 					 * according to the algorithm in section
280094047d49SGordon Ross 					 * 2.1.5.17.3, setting the algorithm's
280194047d49SGordon Ross 					 * parameters as follows:
280294047d49SGordon Ross 					 *	BreakingOplockOpen =
280394047d49SGordon Ross 					 *	  Oplock.ExclusiveOpen.
280494047d49SGordon Ross 					 *	NewOplockLevel = LEVEL_TWO.
280594047d49SGordon Ross 					 *	AcknowledgeRequired = TRUE.
280694047d49SGordon Ross 					 *	Compl_Status = STATUS_SUCCESS.
280794047d49SGordon Ross 					 * (The operation does not end at this
280894047d49SGordon Ross 					 * point; this call to 2.1.5.17.3
280994047d49SGordon Ross 					 * completes some earlier call to
281094047d49SGordon Ross 					 * 2.1.5.17.1.)
281194047d49SGordon Ross 					 */
281272b35b05SGordon Ross 					o = nol->excl_open;
281372b35b05SGordon Ross 					ASSERT(o != NULL);
281494047d49SGordon Ross 					smb_oplock_ind_break(o,
281594047d49SGordon Ross 					    LEVEL_TWO, B_TRUE,
281694047d49SGordon Ross 					    NT_STATUS_SUCCESS);
281794047d49SGordon Ross 				}
281894047d49SGordon Ross 
281994047d49SGordon Ross 				/*
282094047d49SGordon Ross 				 * The operation that called this algorithm
282194047d49SGordon Ross 				 *  MUST be made cancelable by ...
282294047d49SGordon Ross 				 * The operation that called this algorithm
282394047d49SGordon Ross 				 *  waits until the oplock break is
282494047d49SGordon Ross 				 *  acknowledged, as specified in section
282594047d49SGordon Ross 				 *  2.1.5.18, or the operation is canceled.
282694047d49SGordon Ross 				 */
282794047d49SGordon Ross 				status = NT_STATUS_OPLOCK_BREAK_IN_PROGRESS;
282894047d49SGordon Ross 				/* Caller does smb_oplock_wait_break() */
282994047d49SGordon Ross 			}
283094047d49SGordon Ross 		}
283194047d49SGordon Ross 	} else if (BreakToNone) {
283294047d49SGordon Ross 		/*
283394047d49SGordon Ross 		 * If (Oplock.State == LEVEL_TWO_OPLOCK) or
283494047d49SGordon Ross 		 *  (Oplock.ExclusiveOpen is empty) or
283594047d49SGordon Ross 		 *  (Oplock.ExclusiveOpen.TargetOplockKey !=
283694047d49SGordon Ross 		 *   Open.TargetOplockKey):
283794047d49SGordon Ross 		 */
283894047d49SGordon Ross 		if (nol->ol_state == LEVEL_TWO_OPLOCK ||
283994047d49SGordon Ross 		    (((o = nol->excl_open) == NULL) ||
284094047d49SGordon Ross 		    !CompareOplockKeys(ofile, o, CmpFlags))) {
284194047d49SGordon Ross 
284294047d49SGordon Ross 			/*
284394047d49SGordon Ross 			 * If (Oplock.State != NO_OPLOCK) and
284494047d49SGordon Ross 			 * (Oplock.State contains neither
284594047d49SGordon Ross 			 *  WRITE_CACHING nor HANDLE_CACHING):
284694047d49SGordon Ross 			 */
284794047d49SGordon Ross 			if (nol->ol_state != NO_OPLOCK &&
284894047d49SGordon Ross 			    (nol->ol_state &
284994047d49SGordon Ross 			    (WRITE_CACHING | HANDLE_CACHING)) == 0) {
285094047d49SGordon Ross 
285194047d49SGordon Ross 				/*
285294047d49SGordon Ross 				 * If Oplock.State contains none of:
285394047d49SGordon Ross 				 *	LEVEL_TWO_OPLOCK,
285494047d49SGordon Ross 				 *	BREAK_TO_NONE,
285594047d49SGordon Ross 				 *	BREAK_TO_TWO,
285694047d49SGordon Ross 				 *	BREAK_TO_TWO_TO_NONE,
285794047d49SGordon Ross 				 *	BREAK_TO_READ_CACHING,
285894047d49SGordon Ross 				 *	BREAK_TO_WRITE_CACHING,
285994047d49SGordon Ross 				 *	BREAK_TO_HANDLE_CACHING, or
286094047d49SGordon Ross 				 *	BREAK_TO_NO_CACHING:
286194047d49SGordon Ross 				 */
286294047d49SGordon Ross 				if ((nol->ol_state &
286394047d49SGordon Ross 				    (LEVEL_TWO_OPLOCK | BREAK_ANY)) == 0) {
286494047d49SGordon Ross 
286594047d49SGordon Ross 					/*
286694047d49SGordon Ross 					 * There could be a READ_CACHING-only
286794047d49SGordon Ross 					 * oplock here. Those are broken later.
286894047d49SGordon Ross 					 *
286994047d49SGordon Ross 					 * If Oplock.State contains READ_CACHING
287094047d49SGordon Ross 					 *  go to the LeaveBreakToNone label.
287194047d49SGordon Ross 					 * Set BREAK_TO_NONE in Oplock.State.
287294047d49SGordon Ross 					 */
287394047d49SGordon Ross 					if ((nol->ol_state & READ_CACHING) != 0)
287494047d49SGordon Ross 						goto LeaveBreakToNone;
287594047d49SGordon Ross 					nol->ol_state |= BREAK_TO_NONE;
287694047d49SGordon Ross 
287794047d49SGordon Ross 					/*
287894047d49SGordon Ross 					 * Notify the server of an oplock break
287994047d49SGordon Ross 					 * according to the algorithm in section
288094047d49SGordon Ross 					 * 2.1.5.17.3, setting the algorithm's
288194047d49SGordon Ross 					 * parameters as follows:
288294047d49SGordon Ross 					 *	BreakingOplockOpen =
288394047d49SGordon Ross 					 *	  Oplock.ExclusiveOpen.
288494047d49SGordon Ross 					 *	NewOplockLevel = LEVEL_NONE.
288594047d49SGordon Ross 					 *	AcknowledgeRequired = TRUE.
288694047d49SGordon Ross 					 *	Commpl_Status = STATUS_SUCCESS.
288794047d49SGordon Ross 					 * (The operation does not end at this
288894047d49SGordon Ross 					 * point; this call to 2.1.5.17.3
288994047d49SGordon Ross 					 * completes some earlier call to
289094047d49SGordon Ross 					 * 2.1.5.17.1.)
289194047d49SGordon Ross 					 */
289272b35b05SGordon Ross 					o = nol->excl_open;
289372b35b05SGordon Ross 					ASSERT(o != NULL);
289494047d49SGordon Ross 					smb_oplock_ind_break(o,
289594047d49SGordon Ross 					    LEVEL_NONE, B_TRUE,
289694047d49SGordon Ross 					    NT_STATUS_SUCCESS);
289794047d49SGordon Ross 				}
289894047d49SGordon Ross 
289994047d49SGordon Ross 				/*
290094047d49SGordon Ross 				 * Else If Oplock.State equals LEVEL_TWO_OPLOCK
290194047d49SGordon Ross 				 *  or (LEVEL_TWO_OPLOCK|READ_CACHING):
290294047d49SGordon Ross 				 */
290394047d49SGordon Ross 				else if (nol->ol_state == LEVEL_TWO ||
290494047d49SGordon Ross 				    nol->ol_state == (LEVEL_TWO|READ_CACHING)) {
290594047d49SGordon Ross 
290694047d49SGordon Ross 					/*
290794047d49SGordon Ross 					 * For each Open O in Oplock.IIOplocks:
290894047d49SGordon Ross 					 *   Remove O from Oplock.IIOplocks.
290994047d49SGordon Ross 					 *   Notify the server of an oplock
291094047d49SGordon Ross 					 *    break according to the algorithm
291194047d49SGordon Ross 					 *    in section 2.1.5.17.3, setting the
291294047d49SGordon Ross 					 *    algorithm's parameters as follows:
291394047d49SGordon Ross 					 *	BreakingOplockOpen = ThisOpen.
291494047d49SGordon Ross 					 *	NewOplockLevel = LEVEL_NONE.
291594047d49SGordon Ross 					 *	AcknowledgeRequired = FALSE.
291694047d49SGordon Ross 					 *	Compl_Status = STATUS_SUCCESS.
291794047d49SGordon Ross 					 *    (The operation does not end at
291894047d49SGordon Ross 					 *    this point; this call to
291994047d49SGordon Ross 					 *    2.1.5.17.3 completes some
292094047d49SGordon Ross 					 *    earlier call to 2.1.5.17.2.)
292194047d49SGordon Ross 					 * EndFor
292294047d49SGordon Ross 					 */
292394047d49SGordon Ross 					FOREACH_NODE_OFILE(node, o) {
292494047d49SGordon Ross 						if (o->f_oplock.onlist_II == 0)
292594047d49SGordon Ross 							continue;
292694047d49SGordon Ross 						o->f_oplock.onlist_II = B_FALSE;
292794047d49SGordon Ross 						nol->cnt_II--;
292894047d49SGordon Ross 						ASSERT(nol->cnt_II >= 0);
292994047d49SGordon Ross 
293094047d49SGordon Ross 						smb_oplock_ind_break(o,
293194047d49SGordon Ross 						    LEVEL_NONE, B_FALSE,
293294047d49SGordon Ross 						    NT_STATUS_SUCCESS);
293394047d49SGordon Ross 					}
293494047d49SGordon Ross 					/*
293594047d49SGordon Ross 					 * If Oplock.State equals
293694047d49SGordon Ross 					 *  (LEVEL_TWO_OPLOCK|READ_CACHING):
293794047d49SGordon Ross 					 *	Set Oplock.State = READ_CACHING.
293894047d49SGordon Ross 					 * Else
293994047d49SGordon Ross 					 *	Set Oplock.State = NO_OPLOCK.
294094047d49SGordon Ross 					 * EndIf
294194047d49SGordon Ross 					 * Go to the LeaveBreakToNone label.
294294047d49SGordon Ross 					 */
294394047d49SGordon Ross 					if (nol->ol_state ==
294494047d49SGordon Ross 					    (LEVEL_TWO_OPLOCK | READ_CACHING)) {
294594047d49SGordon Ross 						nol->ol_state = READ_CACHING;
294694047d49SGordon Ross 					} else {
294794047d49SGordon Ross 						nol->ol_state = NO_OPLOCK;
294894047d49SGordon Ross 					}
294994047d49SGordon Ross 					goto LeaveBreakToNone;
295094047d49SGordon Ross 				}
295194047d49SGordon Ross 
295294047d49SGordon Ross 				/*
295394047d49SGordon Ross 				 * Else If Oplock.State contains BREAK_TO_TWO:
295494047d49SGordon Ross 				 *	Clear BREAK_TO_TWO from Oplock.State.
295594047d49SGordon Ross 				 *	Set BREAK_TO_TWO_TO_NONE in Oplock.State
295694047d49SGordon Ross 				 * EndIf
295794047d49SGordon Ross 				 */
295894047d49SGordon Ross 				else if (nol->ol_state & BREAK_TO_TWO) {
295994047d49SGordon Ross 					nol->ol_state &= ~BREAK_TO_TWO;
296094047d49SGordon Ross 					nol->ol_state |= BREAK_TO_TWO_TO_NONE;
296194047d49SGordon Ross 				}
296294047d49SGordon Ross 
296394047d49SGordon Ross 				/*
296494047d49SGordon Ross 				 * If Oplock.ExclusiveOpen is not empty,
296594047d49SGordon Ross 				 *  and Oplock.Excl_Open.TargetOplockKey
296694047d49SGordon Ross 				 *  equals Open.TargetOplockKey,
296794047d49SGordon Ross 				 *	 go to the LeaveBreakToNone label.
296894047d49SGordon Ross 				 */
296972b35b05SGordon Ross 				if ((o = nol->excl_open) != NULL &&
297094047d49SGordon Ross 				    CompareOplockKeys(ofile, o, CmpFlags))
297194047d49SGordon Ross 					goto LeaveBreakToNone;
297294047d49SGordon Ross 
297394047d49SGordon Ross 				/*
297494047d49SGordon Ross 				 * The operation that called this algorithm
297594047d49SGordon Ross 				 *  MUST be made cancelable by ...
297694047d49SGordon Ross 				 * The operation that called this algorithm
297794047d49SGordon Ross 				 * waits until the opl. break is acknowledged,
297894047d49SGordon Ross 				 * as specified in section 2.1.5.18, or the
297994047d49SGordon Ross 				 * operation is canceled.
298094047d49SGordon Ross 				 */
298194047d49SGordon Ross 				status = NT_STATUS_OPLOCK_BREAK_IN_PROGRESS;
298294047d49SGordon Ross 				/* Caller does smb_oplock_wait_break() */
298394047d49SGordon Ross 			}
298494047d49SGordon Ross 		}
298594047d49SGordon Ross 	}
298694047d49SGordon Ross 
298794047d49SGordon Ross LeaveBreakToNone:
298894047d49SGordon Ross 
298994047d49SGordon Ross 	/*
299094047d49SGordon Ross 	 * if (BreakCacheLevel != 0) and		(pp 37)
299194047d49SGordon Ross 	 * If Oplock.State contains any flags that are in BreakCacheLevel:
299294047d49SGordon Ross 	 * (Body of that "If" was here to just above the out label.)
299394047d49SGordon Ross 	 */
299494047d49SGordon Ross 	if ((nol->ol_state & BreakCacheLevel) == 0)
299594047d49SGordon Ross 		goto out;
299694047d49SGordon Ross 
299794047d49SGordon Ross 	/*
299894047d49SGordon Ross 	 * If Oplock.ExclusiveOpen is not empty, call the
299994047d49SGordon Ross 	 * algorithm in section 2.1.4.12.2, passing
300094047d49SGordon Ross 	 *	Open as the OperationOpen parameter,
300194047d49SGordon Ross 	 *	Oplock.ExclusiveOpen as the OplockOpen parameter,
300294047d49SGordon Ross 	 *	and Flags as the Flagsparameter.
300394047d49SGordon Ross 	 * If the algorithm returns TRUE:
300494047d49SGordon Ross 	 *	The algorithm returns at this point.
300594047d49SGordon Ross 	 */
300694047d49SGordon Ross 	if ((o = nol->excl_open) != NULL &&
300794047d49SGordon Ross 	    CompareOplockKeys(ofile, o, CmpFlags) == B_TRUE) {
300894047d49SGordon Ross 		status = NT_STATUS_SUCCESS;
300994047d49SGordon Ross 		goto out;
301094047d49SGordon Ross 	}
301194047d49SGordon Ross 
301294047d49SGordon Ross 	/*
301394047d49SGordon Ross 	 * Switch (Oplock.State):
301494047d49SGordon Ross 	 */
301594047d49SGordon Ross 	switch (nol->ol_state) {
301694047d49SGordon Ross 
301794047d49SGordon Ross 	case (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH):
301894047d49SGordon Ross 	case READ_CACHING:
301994047d49SGordon Ross 	case (LEVEL_TWO_OPLOCK|READ_CACHING):
302094047d49SGordon Ross 		/*
302194047d49SGordon Ross 		 * If BreakCacheLevel contains READ_CACHING:
302294047d49SGordon Ross 		 */
302394047d49SGordon Ross 		if ((BreakCacheLevel & READ_CACHING) != 0) {
302494047d49SGordon Ross 			/*
302594047d49SGordon Ross 			 * For each Open ThisOpen in Oplock.ROplocks:
302694047d49SGordon Ross 			 *   Call the algorithm in section 2.1.4.12.2, pass:
302794047d49SGordon Ross 			 *	Open as the OperationOpen parameter,
302894047d49SGordon Ross 			 *	ThisOpen as the OplockOpen parameter,
302994047d49SGordon Ross 			 *	and Flags as the Flagsparameter.
303094047d49SGordon Ross 			 *   If the algorithm returns FALSE:
303194047d49SGordon Ross 			 *	Remove ThisOpen from Oplock.ROplocks.
303294047d49SGordon Ross 			 *	Notify the server of an oplock break
303394047d49SGordon Ross 			 *	  according to the algorithm in
303494047d49SGordon Ross 			 *	  section 2.1.5.17.3, setting the
303594047d49SGordon Ross 			 *	  algorithm's parameters as follows:
303694047d49SGordon Ross 			 *		BreakingOplockOpen = ThisOpen.
303794047d49SGordon Ross 			 *		NewOplockLevel = LEVEL_NONE.
303894047d49SGordon Ross 			 *		AcknowledgeRequired = FALSE.
303994047d49SGordon Ross 			 *		Compl_Status = STATUS_SUCCESS.
304094047d49SGordon Ross 			 *	(The operation does not end at this point;
304194047d49SGordon Ross 			 *	 this call to 2.1.5.17.3 completes some
304294047d49SGordon Ross 			 *	 earlier call to 2.1.5.17.2.)
304394047d49SGordon Ross 			 *	EndIf
304494047d49SGordon Ross 			 * EndFor
304594047d49SGordon Ross 			 */
304694047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
304794047d49SGordon Ross 				if (o->f_oplock.onlist_R == 0)
304894047d49SGordon Ross 					continue;
304994047d49SGordon Ross 				if (!CompareOplockKeys(ofile, o, CmpFlags)) {
305094047d49SGordon Ross 					o->f_oplock.onlist_R = B_FALSE;
305194047d49SGordon Ross 					nol->cnt_R--;
305294047d49SGordon Ross 					ASSERT(nol->cnt_R >= 0);
305394047d49SGordon Ross 
305494047d49SGordon Ross 					smb_oplock_ind_break(o,
305594047d49SGordon Ross 					    LEVEL_NONE, B_FALSE,
305694047d49SGordon Ross 					    NT_STATUS_SUCCESS);
305794047d49SGordon Ross 				}
305894047d49SGordon Ross 			}
305994047d49SGordon Ross 		}
306094047d49SGordon Ross 		/*
306194047d49SGordon Ross 		 * If Oplock.State equals
306294047d49SGordon Ross 		 *  (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH):
306394047d49SGordon Ross 		 *	// Do nothing; FALL THROUGH to next Case statement.
306494047d49SGordon Ross 		 * Else
306594047d49SGordon Ross 		 *	Recompute Oplock.State according to the
306694047d49SGordon Ross 		 *	algorithm in section 2.1.4.13, passing
306794047d49SGordon Ross 		 *	Oplock as the ThisOplock parameter.
306894047d49SGordon Ross 		 * EndIf
306994047d49SGordon Ross 		 */
307094047d49SGordon Ross 		if (nol->ol_state ==
307194047d49SGordon Ross 		    (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH))
307294047d49SGordon Ross 			goto case_cache_rh;
307394047d49SGordon Ross 
307494047d49SGordon Ross 		RecomputeOplockState(node);
307594047d49SGordon Ross 		break;
307694047d49SGordon Ross 	/* EndCase	XXX Note: spec. swapped this with prev. Endif. */
307794047d49SGordon Ross 
307894047d49SGordon Ross 	case_cache_rh:
307994047d49SGordon Ross 	case (READ_CACHING|HANDLE_CACHING):
308094047d49SGordon Ross 
308194047d49SGordon Ross 		/*
308294047d49SGordon Ross 		 * If BreakCacheLevel equals HANDLE_CACHING:
308394047d49SGordon Ross 		 */
308494047d49SGordon Ross 		if (BreakCacheLevel == HANDLE_CACHING) {
308594047d49SGordon Ross 
308694047d49SGordon Ross 			/*
308794047d49SGordon Ross 			 * For each Open ThisOpen in Oplock.RHOplocks:
308894047d49SGordon Ross 			 *	If ThisOpen.OplockKey != Open.OplockKey:
308994047d49SGordon Ross 			 */
309094047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
309194047d49SGordon Ross 				if (o->f_oplock.onlist_RH == 0)
309294047d49SGordon Ross 					continue;
309394047d49SGordon Ross 				if (!CompareOplockKeys(ofile, o, CmpFlags)) {
309494047d49SGordon Ross 
309594047d49SGordon Ross 					/*
309694047d49SGordon Ross 					 * Remove ThisOpen from
309794047d49SGordon Ross 					 *  Oplock.RHOplocks.
309894047d49SGordon Ross 					 */
309994047d49SGordon Ross 					o->f_oplock.onlist_RH = B_FALSE;
310094047d49SGordon Ross 					nol->cnt_RH--;
310194047d49SGordon Ross 					ASSERT(nol->cnt_RH >= 0);
310294047d49SGordon Ross 
310394047d49SGordon Ross 					/*
310494047d49SGordon Ross 					 * Notify the server of an oplock break
310594047d49SGordon Ross 					 *   according to the algorithm in
310694047d49SGordon Ross 					 *   section 2.1.5.17.3, setting the
310794047d49SGordon Ross 					 *   algorithm's parameters as follows:
310894047d49SGordon Ross 					 *	BreakingOplockOpen = ThisOpen.
310994047d49SGordon Ross 					 *	NewOplockLevel = READ_CACHING.
311094047d49SGordon Ross 					 *	AcknowledgeRequired = TRUE.
311194047d49SGordon Ross 					 *	Compl_Status = STATUS_SUCCESS.
311294047d49SGordon Ross 					 * (The operation does not end at this
311394047d49SGordon Ross 					 *  point; this call to 2.1.5.17.3
311494047d49SGordon Ross 					 *  completes some earlier call to
311594047d49SGordon Ross 					 *  2.1.5.17.2.)
311694047d49SGordon Ross 					 */
311794047d49SGordon Ross 					smb_oplock_ind_break(o,
311894047d49SGordon Ross 					    READ_CACHING, B_TRUE,
311994047d49SGordon Ross 					    NT_STATUS_SUCCESS);
312094047d49SGordon Ross 
312194047d49SGordon Ross 					/*
312294047d49SGordon Ross 					 * Initialize a new RHOpContext object,
312394047d49SGordon Ross 					 *   setting its fields as follows:
312494047d49SGordon Ross 					 *	RHOpCtx.Open = ThisOpen.
312594047d49SGordon Ross 					 *	RHOpCtx.BreakingToRead = TRUE.
312694047d49SGordon Ross 					 * Add the new RHOpContext object to
312794047d49SGordon Ross 					 *    Oplock.RHBreakQueue.
312894047d49SGordon Ross 					 * Set NeedToWait to TRUE.
312994047d49SGordon Ross 					 */
313094047d49SGordon Ross 					o->f_oplock.BreakingToRead = B_TRUE;
313194047d49SGordon Ross 					ASSERT(!(o->f_oplock.onlist_RHBQ));
313294047d49SGordon Ross 					o->f_oplock.onlist_RHBQ = B_TRUE;
313394047d49SGordon Ross 					nol->cnt_RHBQ++;
313494047d49SGordon Ross 
313594047d49SGordon Ross 					NeedToWait = B_TRUE;
313694047d49SGordon Ross 				}
313794047d49SGordon Ross 			}
313894047d49SGordon Ross 		}
313994047d49SGordon Ross 
314094047d49SGordon Ross 		/*
314194047d49SGordon Ross 		 * Else If BreakCacheLevel contains both
314294047d49SGordon Ross 		 *   READ_CACHING and WRITE_CACHING:
314394047d49SGordon Ross 		 */
314494047d49SGordon Ross 		else if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) ==
314594047d49SGordon Ross 		    (READ_CACHING | WRITE_CACHING)) {
314694047d49SGordon Ross 
314794047d49SGordon Ross 			/*
314894047d49SGordon Ross 			 * For each RHOpContext ThisContext in
314994047d49SGordon Ross 			 * Oplock.RHBreakQueue:
315094047d49SGordon Ross 			 *	Call the algorithm in section 2.1.4.12.2,
315194047d49SGordon Ross 			 *	  passing Open as the OperationOpen parameter,
315294047d49SGordon Ross 			 *	  ThisContext.Open as the OplockOpen parameter,
315394047d49SGordon Ross 			 *	  and Flags as the Flags parameter.
315494047d49SGordon Ross 			 *	If the algorithm returns FALSE:
315594047d49SGordon Ross 			 *		Set ThisContext.BreakingToRead to FALSE.
315694047d49SGordon Ross 			 *		If BreakCacheLevel & HANDLE_CACHING:
315794047d49SGordon Ross 			 *			Set NeedToWait to TRUE.
315894047d49SGordon Ross 			 *		EndIf
315994047d49SGordon Ross 			 *	EndIf
316094047d49SGordon Ross 			 * EndFor
316194047d49SGordon Ross 			 */
316294047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
316394047d49SGordon Ross 				if (o->f_oplock.onlist_RHBQ == 0)
316494047d49SGordon Ross 					continue;
316594047d49SGordon Ross 				if (!CompareOplockKeys(ofile, o, CmpFlags)) {
316694047d49SGordon Ross 					o->f_oplock.BreakingToRead = B_FALSE;
316794047d49SGordon Ross 					if (BreakCacheLevel & HANDLE_CACHING)
316894047d49SGordon Ross 						NeedToWait = B_TRUE;
316994047d49SGordon Ross 				}
317094047d49SGordon Ross 			}
317194047d49SGordon Ross 
317294047d49SGordon Ross 			/*
317394047d49SGordon Ross 			 * For each Open ThisOpen in Oplock.RHOplocks:
317494047d49SGordon Ross 			 *	Call the algorithm in section 2.1.4.12.2,
317594047d49SGordon Ross 			 *	  passing Open as the OperationOpen parameter,
317694047d49SGordon Ross 			 *	  ThisOpen as the OplockOpen parameter, and
317794047d49SGordon Ross 			 *	  Flags as the Flagsparameter.
317894047d49SGordon Ross 			 *	If the algorithm  returns FALSE:
317994047d49SGordon Ross 			 *		Remove ThisOpen from Oplock.RHOplocks.
318094047d49SGordon Ross 			 *		Notify the server of an oplock break
318194047d49SGordon Ross 			 *		  according to the algorithm in
318294047d49SGordon Ross 			 *		  section 2.1.5.17.3, setting the
318394047d49SGordon Ross 			 *		  algorithm's parameters as follows:
318494047d49SGordon Ross 			 *			BreakingOplockOpen = ThisOpen.
318594047d49SGordon Ross 			 *			NewOplockLevel = LEVEL_NONE.
318694047d49SGordon Ross 			 *			AcknowledgeRequired = TRUE.
318794047d49SGordon Ross 			 *			Compl_Status = STATUS_SUCCESS.
318894047d49SGordon Ross 			 *		(The operation does not end at this
318994047d49SGordon Ross 			 *		 point; this call to 2.1.5.17.3
319094047d49SGordon Ross 			 *		 completes some earlier call to
319194047d49SGordon Ross 			 *		 2.1.5.17.2.)
319294047d49SGordon Ross 			 *		Initialize a new RHOpContext object,
319394047d49SGordon Ross 			 *		  setting its fields as follows:
319494047d49SGordon Ross 			 *			RHOpCtx.Open = ThisOpen.
319594047d49SGordon Ross 			 *			RHOpCtx.BreakingToRead = FALSE
319694047d49SGordon Ross 			 *		Add the new RHOpContext object to
319794047d49SGordon Ross 			 *		  Oplock.RHBreakQueue.
319894047d49SGordon Ross 			 *		If BreakCacheLevel contains
319994047d49SGordon Ross 			 *		  HANDLE_CACHING:
320094047d49SGordon Ross 			 *			Set NeedToWait to TRUE.
320194047d49SGordon Ross 			 *		EndIf
320294047d49SGordon Ross 			 *	EndIf
320394047d49SGordon Ross 			 * EndFor
320494047d49SGordon Ross 			 */
320594047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
320694047d49SGordon Ross 				if (o->f_oplock.onlist_RH == 0)
320794047d49SGordon Ross 					continue;
320894047d49SGordon Ross 				if (!CompareOplockKeys(ofile, o, CmpFlags)) {
320994047d49SGordon Ross 					o->f_oplock.onlist_RH = B_FALSE;
321094047d49SGordon Ross 					nol->cnt_RH--;
321194047d49SGordon Ross 					ASSERT(nol->cnt_RH >= 0);
321294047d49SGordon Ross 
321394047d49SGordon Ross 					smb_oplock_ind_break(o,
321494047d49SGordon Ross 					    LEVEL_NONE, B_TRUE,
321594047d49SGordon Ross 					    NT_STATUS_SUCCESS);
321694047d49SGordon Ross 
321794047d49SGordon Ross 					o->f_oplock.BreakingToRead = B_FALSE;
321894047d49SGordon Ross 					ASSERT(!(o->f_oplock.onlist_RHBQ));
321994047d49SGordon Ross 					o->f_oplock.onlist_RHBQ = B_TRUE;
322094047d49SGordon Ross 					nol->cnt_RHBQ++;
322194047d49SGordon Ross 
322294047d49SGordon Ross 					if (BreakCacheLevel & HANDLE_CACHING)
322394047d49SGordon Ross 						NeedToWait = B_TRUE;
322494047d49SGordon Ross 				}
322594047d49SGordon Ross 			}
322694047d49SGordon Ross 		}
322794047d49SGordon Ross 
322894047d49SGordon Ross // If the oplock is explicitly losing HANDLE_CACHING, RHBreakQueue is
322994047d49SGordon Ross // not empty, and the algorithm has not yet decided to wait, this operation
323094047d49SGordon Ross // might have to wait if there is an oplock on RHBreakQueue with a
323194047d49SGordon Ross // non-matching key. This is done because even if this operation didn't
323294047d49SGordon Ross // cause a break of a currently-granted Read-Handle caching oplock, it
323394047d49SGordon Ross // might have done so had a currently-breaking oplock still been granted.
323494047d49SGordon Ross 
323594047d49SGordon Ross 		/*
323694047d49SGordon Ross 		 * If (NeedToWait is FALSE) and
323794047d49SGordon Ross 		 *   (Oplock.RHBreakQueue is empty) and   (XXX: Not empty)
323894047d49SGordon Ross 		 *   (BreakCacheLevel contains HANDLE_CACHING):
323994047d49SGordon Ross 		 *	For each RHOpContext ThisContex in Oplock.RHBreakQueue:
324094047d49SGordon Ross 		 *		If ThisContext.Open.OplockKey != Open.OplockKey:
324194047d49SGordon Ross 		 *			Set NeedToWait to TRUE.
324294047d49SGordon Ross 		 *			Break out of the For loop.
324394047d49SGordon Ross 		 *		EndIf
324494047d49SGordon Ross 		 *	EndFor
324594047d49SGordon Ross 		 * EndIf
324694047d49SGordon Ross 		 * Recompute Oplock.State according to the algorithm in
324794047d49SGordon Ross 		 *   section 2.1.4.13, passing Oplock as ThisOplock.
324894047d49SGordon Ross 		 */
324994047d49SGordon Ross 		if (NeedToWait == B_FALSE &&
325094047d49SGordon Ross 		    (BreakCacheLevel & HANDLE_CACHING) != 0) {
325194047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
325294047d49SGordon Ross 				if (o->f_oplock.onlist_RHBQ == 0)
325394047d49SGordon Ross 					continue;
325494047d49SGordon Ross 				if (!CompareOplockKeys(ofile, o, CmpFlags)) {
325594047d49SGordon Ross 					NeedToWait = B_TRUE;
325694047d49SGordon Ross 					break;
325794047d49SGordon Ross 				}
325894047d49SGordon Ross 			}
325994047d49SGordon Ross 		}
326094047d49SGordon Ross 		RecomputeOplockState(node);
326194047d49SGordon Ross 		break;
326294047d49SGordon Ross 
326394047d49SGordon Ross 	case (READ_CACHING|HANDLE_CACHING|BREAK_TO_READ_CACHING):
326494047d49SGordon Ross 		/*
326594047d49SGordon Ross 		 * If BreakCacheLevel contains READ_CACHING:
326694047d49SGordon Ross 		 */
326794047d49SGordon Ross 		if ((BreakCacheLevel & READ_CACHING) != 0) {
326894047d49SGordon Ross 			/*
326994047d49SGordon Ross 			 * For each RHOpContext ThisContext in
327094047d49SGordon Ross 			 *  Oplock.RHBreakQueue:
327194047d49SGordon Ross 			 *	Call the algorithm in section 2.1.4.12.2,
327294047d49SGordon Ross 			 *	  passing Open = OperationOpen parameter,
327394047d49SGordon Ross 			 *	  ThisContext.Open = OplockOpen parameter,
327494047d49SGordon Ross 			 *	  and Flags as the Flags parameter.
327594047d49SGordon Ross 			 *	If the algorithm returns FALSE:
327694047d49SGordon Ross 			 *		Set ThisCtx.BreakingToRead = FALSE.
327794047d49SGordon Ross 			 *	EndIf
327894047d49SGordon Ross 			 *	Recompute Oplock.State according to the
327994047d49SGordon Ross 			 *	  algorithm in section 2.1.4.13, passing
328094047d49SGordon Ross 			 *	  Oplock as the ThisOplock parameter.
328194047d49SGordon Ross 			 * EndFor
328294047d49SGordon Ross 			 */
328394047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
328494047d49SGordon Ross 				if (o->f_oplock.onlist_RHBQ == 0)
328594047d49SGordon Ross 					continue;
328694047d49SGordon Ross 				if (!CompareOplockKeys(ofile, o, CmpFlags)) {
328794047d49SGordon Ross 					o->f_oplock.BreakingToRead = B_FALSE;
328894047d49SGordon Ross 				}
328994047d49SGordon Ross 			}
329094047d49SGordon Ross 			RecomputeOplockState(node);
329194047d49SGordon Ross 		}
329294047d49SGordon Ross 		/* FALLTHROUGH */
329394047d49SGordon Ross 
329494047d49SGordon Ross 	case (READ_CACHING|HANDLE_CACHING|BREAK_TO_NO_CACHING):
329594047d49SGordon Ross 		/*
329694047d49SGordon Ross 		 * If BreakCacheLevel contains HANDLE_CACHING:
329794047d49SGordon Ross 		 *	For each RHOpContext ThisContext in Oplock.RHBreakQueue:
329894047d49SGordon Ross 		 *		If ThisContext.Open.OplockKey != Open.OplockKey:
329994047d49SGordon Ross 		 *			Set NeedToWait to TRUE.
330094047d49SGordon Ross 		 *			Break out of the For loop.
330194047d49SGordon Ross 		 *		EndIf
330294047d49SGordon Ross 		 *	EndFor
330394047d49SGordon Ross 		 * EndIf
330494047d49SGordon Ross 		 */
330594047d49SGordon Ross 		if ((BreakCacheLevel & HANDLE_CACHING) != 0) {
330694047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
330794047d49SGordon Ross 				if (o->f_oplock.onlist_RHBQ == 0)
330894047d49SGordon Ross 					continue;
330994047d49SGordon Ross 				if (!CompareOplockKeys(ofile, o, CmpFlags)) {
331094047d49SGordon Ross 					NeedToWait = B_TRUE;
331194047d49SGordon Ross 					break;
331294047d49SGordon Ross 				}
331394047d49SGordon Ross 			}
331494047d49SGordon Ross 		}
331594047d49SGordon Ross 		break;
331694047d49SGordon Ross 
331794047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|EXCLUSIVE):
331894047d49SGordon Ross 		/*
331994047d49SGordon Ross 		 * If BreakCacheLevel contains both
332094047d49SGordon Ross 		 *  READ_CACHING and WRITE_CACHING:
332194047d49SGordon Ross 		 *	Notify the server of an oplock break according to
332294047d49SGordon Ross 		 *	  the algorithm in section 2.1.5.17.3, setting the
332394047d49SGordon Ross 		 *	  algorithm's parameters as follows:
332494047d49SGordon Ross 		 *		BreakingOplockOpen = Oplock.ExclusiveOpen.
332594047d49SGordon Ross 		 *		NewOplockLevel = LEVEL_NONE.
332694047d49SGordon Ross 		 *		AcknowledgeRequired = TRUE.
332794047d49SGordon Ross 		 *		OplockCompletionStatus = STATUS_SUCCESS.
332894047d49SGordon Ross 		 *	(The operation does not end at this point;
332994047d49SGordon Ross 		 *	 this call to 2.1.5.17.3 completes some
333094047d49SGordon Ross 		 *	 earlier call to 2.1.5.17.1.)
333194047d49SGordon Ross 		 *	Set Oplock.State to (READ_CACHING|WRITE_CACHING| \
333294047d49SGordon Ross 		 *			EXCLUSIVE|BREAK_TO_NO_CACHING).
333394047d49SGordon Ross 		 *	Set NeedToWait to TRUE.
333494047d49SGordon Ross 		 */
333594047d49SGordon Ross 		if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) ==
333694047d49SGordon Ross 		    (READ_CACHING | WRITE_CACHING)) {
333794047d49SGordon Ross 			o = nol->excl_open;
333894047d49SGordon Ross 			ASSERT(o != NULL);
333994047d49SGordon Ross 			smb_oplock_ind_break(o,
334094047d49SGordon Ross 			    LEVEL_NONE, B_TRUE,
334194047d49SGordon Ross 			    NT_STATUS_SUCCESS);
334294047d49SGordon Ross 
334394047d49SGordon Ross 			nol->ol_state =
334494047d49SGordon Ross 			    (READ_CACHING|WRITE_CACHING|
334594047d49SGordon Ross 			    EXCLUSIVE|BREAK_TO_NO_CACHING);
334694047d49SGordon Ross 			NeedToWait = B_TRUE;
334794047d49SGordon Ross 		}
334894047d49SGordon Ross 
334994047d49SGordon Ross 		/*
335094047d49SGordon Ross 		 * Else If BreakCacheLevel contains WRITE_CACHING:
335194047d49SGordon Ross 		 *	Notify the server of an oplock break according to
335294047d49SGordon Ross 		 *	  the algorithm in section 2.1.5.17.3, setting the
335394047d49SGordon Ross 		 *	  algorithm's parameters as follows:
335494047d49SGordon Ross 		 *		BreakingOplockOpen = Oplock.ExclusiveOpen.
335594047d49SGordon Ross 		 *		NewOplockLevel = READ_CACHING.
335694047d49SGordon Ross 		 *		AcknowledgeRequired = TRUE.
335794047d49SGordon Ross 		 *		OplockCompletionStatus = STATUS_SUCCESS.
335894047d49SGordon Ross 		 *	(The operation does not end at this point;
335994047d49SGordon Ross 		 *	 this call to 2.1.5.17.3 completes some
336094047d49SGordon Ross 		 *	 earlier call to 2.1.5.17.1.)
336194047d49SGordon Ross 		 *	Set Oplock.State to (READ_CACHING|WRITE_CACHING|
336294047d49SGordon Ross 		 *			 EXCLUSIVE|BREAK_TO_READ_CACHING).
336394047d49SGordon Ross 		 *	Set NeedToWait to TRUE.
336494047d49SGordon Ross 		 * EndIf
336594047d49SGordon Ross 		 */
336694047d49SGordon Ross 		else if ((BreakCacheLevel & WRITE_CACHING) != 0) {
336794047d49SGordon Ross 			o = nol->excl_open;
336894047d49SGordon Ross 			ASSERT(o != NULL);
336994047d49SGordon Ross 			smb_oplock_ind_break(o,
337094047d49SGordon Ross 			    READ_CACHING, B_TRUE,
337194047d49SGordon Ross 			    NT_STATUS_SUCCESS);
337294047d49SGordon Ross 
337394047d49SGordon Ross 			nol->ol_state =
337494047d49SGordon Ross 			    (READ_CACHING|WRITE_CACHING|
337594047d49SGordon Ross 			    EXCLUSIVE|BREAK_TO_READ_CACHING);
337694047d49SGordon Ross 			NeedToWait = B_TRUE;
337794047d49SGordon Ross 		}
337894047d49SGordon Ross 		break;
337994047d49SGordon Ross 
338094047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE):
338194047d49SGordon Ross 		/*
338294047d49SGordon Ross 		 * If BreakCacheLevel equals WRITE_CACHING:
338394047d49SGordon Ross 		 *	Notify the server of an oplock break according to
338494047d49SGordon Ross 		 *	  the algorithm in section 2.1.5.17.3, setting the
338594047d49SGordon Ross 		 *	  algorithm's parameters as follows:
338694047d49SGordon Ross 		 *		BreakingOplockOpen = Oplock.ExclusiveOpen.
338794047d49SGordon Ross 		 *		NewOplockLevel = (READ_CACHING|HANDLE_CACHING).
338894047d49SGordon Ross 		 *		AcknowledgeRequired = TRUE.
338994047d49SGordon Ross 		 *		OplockCompletionStatus = STATUS_SUCCESS.
339094047d49SGordon Ross 		 *	(The operation does not end at this point;
339194047d49SGordon Ross 		 *	 this call to 2.1.5.17.3 completes some
339294047d49SGordon Ross 		 *	 earlier call to 2.1.5.17.1.)
339394047d49SGordon Ross 		 *	Set Oplock.State to (READ_CACHING|WRITE_CACHING|
339494047d49SGordon Ross 		 *			HANDLE_CACHING|EXCLUSIVE|
339594047d49SGordon Ross 		 *			BREAK_TO_READ_CACHING|
339694047d49SGordon Ross 		 *			BREAK_TO_HANDLE_CACHING).
339794047d49SGordon Ross 		 *	Set NeedToWait to TRUE.
339894047d49SGordon Ross 		 */
339994047d49SGordon Ross 		if (BreakCacheLevel == WRITE_CACHING) {
340094047d49SGordon Ross 			o = nol->excl_open;
340194047d49SGordon Ross 			ASSERT(o != NULL);
340294047d49SGordon Ross 			smb_oplock_ind_break(o,
340394047d49SGordon Ross 			    CACHE_RH, B_TRUE,
340494047d49SGordon Ross 			    NT_STATUS_SUCCESS);
340594047d49SGordon Ross 
340694047d49SGordon Ross 			nol->ol_state =
340794047d49SGordon Ross 			    (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|
340894047d49SGordon Ross 			    EXCLUSIVE|BREAK_TO_READ_CACHING|
340994047d49SGordon Ross 			    BREAK_TO_HANDLE_CACHING);
341094047d49SGordon Ross 			NeedToWait = B_TRUE;
341194047d49SGordon Ross 		}
341294047d49SGordon Ross 
341394047d49SGordon Ross 		/*
341494047d49SGordon Ross 		 * Else If BreakCacheLevel equals HANDLE_CACHING:
341594047d49SGordon Ross 		 *	Notify the server of an oplock break according to
341694047d49SGordon Ross 		 *	  the algorithm in section 2.1.5.17.3, setting the
341794047d49SGordon Ross 		 *	  algorithm's parameters as follows:
341894047d49SGordon Ross 		 *		BreakingOplockOpen = Oplock.ExclusiveOpen.
341994047d49SGordon Ross 		 *		NewOplockLevel = (READ_CACHING|WRITE_CACHING).
342094047d49SGordon Ross 		 *		AcknowledgeRequired = TRUE.
342194047d49SGordon Ross 		 *		OplockCompletionStatus = STATUS_SUCCESS.
342294047d49SGordon Ross 		 *	(The operation does not end at this point;
342394047d49SGordon Ross 		 *	 this call to 2.1.5.17.3 completes some
342494047d49SGordon Ross 		 *	 earlier call to 2.1.5.17.1.)
342594047d49SGordon Ross 		 *	Set Oplock.State to (READ_CACHING|WRITE_CACHING|
342694047d49SGordon Ross 		 *			HANDLE_CACHING|EXCLUSIVE|
342794047d49SGordon Ross 		 *			BREAK_TO_READ_CACHING|
342894047d49SGordon Ross 		 *			BREAK_TO_WRITE_CACHING).
342994047d49SGordon Ross 		 *	Set NeedToWait to TRUE.
343094047d49SGordon Ross 		 */
343194047d49SGordon Ross 		else if (BreakCacheLevel == HANDLE_CACHING) {
343294047d49SGordon Ross 			o = nol->excl_open;
343394047d49SGordon Ross 			ASSERT(o != NULL);
343494047d49SGordon Ross 			smb_oplock_ind_break(o,
343594047d49SGordon Ross 			    CACHE_RW, B_TRUE,
343694047d49SGordon Ross 			    NT_STATUS_SUCCESS);
343794047d49SGordon Ross 
343894047d49SGordon Ross 			nol->ol_state =
343994047d49SGordon Ross 			    (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|
344094047d49SGordon Ross 			    EXCLUSIVE|BREAK_TO_READ_CACHING|
344194047d49SGordon Ross 			    BREAK_TO_WRITE_CACHING);
344294047d49SGordon Ross 			NeedToWait = B_TRUE;
344394047d49SGordon Ross 		}
344494047d49SGordon Ross 
344594047d49SGordon Ross 		/*
344694047d49SGordon Ross 		 * Else If BreakCacheLevel contains both
344794047d49SGordon Ross 		 *  READ_CACHING and WRITE_CACHING:
344894047d49SGordon Ross 		 *	Notify the server of an oplock break according to
344994047d49SGordon Ross 		 *	  the algorithm in section 2.1.5.17.3, setting the
345094047d49SGordon Ross 		 *	  algorithm's parameters as follows:
345194047d49SGordon Ross 		 *		BreakingOplockOpen = Oplock.ExclusiveOpen.
345294047d49SGordon Ross 		 *		NewOplockLevel = LEVEL_NONE.
345394047d49SGordon Ross 		 *		AcknowledgeRequired = TRUE.
345494047d49SGordon Ross 		 *		OplockCompletionStatus = STATUS_SUCCESS.
345594047d49SGordon Ross 		 *	(The operation does not end at this point;
345694047d49SGordon Ross 		 *	 this call to 2.1.5.17.3 completes some
345794047d49SGordon Ross 		 *	 earlier call to 2.1.5.17.1.)
345894047d49SGordon Ross 		 *	Set Oplock.State to (READ_CACHING|WRITE_CACHING|
345994047d49SGordon Ross 		 *			HANDLE_CACHING|EXCLUSIVE|
346094047d49SGordon Ross 		 *			BREAK_TO_NO_CACHING).
346194047d49SGordon Ross 		 *	Set NeedToWait to TRUE.
346294047d49SGordon Ross 		 * EndIf
346394047d49SGordon Ross 		 */
346494047d49SGordon Ross 		else if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) ==
346594047d49SGordon Ross 		    (READ_CACHING | WRITE_CACHING)) {
346694047d49SGordon Ross 			o = nol->excl_open;
346794047d49SGordon Ross 			ASSERT(o != NULL);
346894047d49SGordon Ross 			smb_oplock_ind_break(o,
346994047d49SGordon Ross 			    LEVEL_NONE, B_TRUE,
347094047d49SGordon Ross 			    NT_STATUS_SUCCESS);
347194047d49SGordon Ross 
347294047d49SGordon Ross 			nol->ol_state =
347394047d49SGordon Ross 			    (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|
347494047d49SGordon Ross 			    EXCLUSIVE|BREAK_TO_NO_CACHING);
347594047d49SGordon Ross 			NeedToWait = B_TRUE;
347694047d49SGordon Ross 		}
347794047d49SGordon Ross 		break;
347894047d49SGordon Ross 
347994047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|EXCLUSIVE|BREAK_TO_READ_CACHING):
348094047d49SGordon Ross 		/*
348194047d49SGordon Ross 		 * If BreakCacheLevel contains READ_CACHING:
348294047d49SGordon Ross 		 *	Set Oplock.State to (READ_CACHING|WRITE_CACHING|
348394047d49SGordon Ross 		 *			EXCLUSIVE|BREAK_TO_NO_CACHING).
348494047d49SGordon Ross 		 * EndIf
348594047d49SGordon Ross 		 * If BreakCacheLevel contains either
348694047d49SGordon Ross 		 *  READ_CACHING or WRITE_CACHING:
348794047d49SGordon Ross 		 *	Set NeedToWait to TRUE.
348894047d49SGordon Ross 		 * EndIf
348994047d49SGordon Ross 		 */
349094047d49SGordon Ross 		if ((BreakCacheLevel & READ_CACHING) != 0) {
349194047d49SGordon Ross 			nol->ol_state =
349294047d49SGordon Ross 			    (READ_CACHING|WRITE_CACHING|
349394047d49SGordon Ross 			    EXCLUSIVE|BREAK_TO_NO_CACHING);
349494047d49SGordon Ross 		}
349594047d49SGordon Ross 		if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) != 0) {
349694047d49SGordon Ross 			NeedToWait = B_TRUE;
349794047d49SGordon Ross 		}
349894047d49SGordon Ross 		break;
349994047d49SGordon Ross 
350094047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|EXCLUSIVE|BREAK_TO_NO_CACHING):
350194047d49SGordon Ross 		/*
350294047d49SGordon Ross 		 * If BreakCacheLevel contains either
350394047d49SGordon Ross 		 *  READ_CACHING or WRITE_CACHING:
350494047d49SGordon Ross 		 *	Set NeedToWait to TRUE.
350594047d49SGordon Ross 		 * EndIf
350694047d49SGordon Ross 		 */
350794047d49SGordon Ross 		if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) != 0) {
350894047d49SGordon Ross 			NeedToWait = B_TRUE;
350994047d49SGordon Ross 		}
351094047d49SGordon Ross 		break;
351194047d49SGordon Ross 
351294047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
351394047d49SGordon Ross 	    BREAK_TO_READ_CACHING|BREAK_TO_WRITE_CACHING):
351494047d49SGordon Ross 		/*
351594047d49SGordon Ross 		 * If BreakCacheLevel == WRITE_CACHING:
351694047d49SGordon Ross 		 *	Set Oplock.State to (READ_CACHING|WRITE_CACHING|
351794047d49SGordon Ross 		 *	    HANDLE_CACHING|EXCLUSIVE|BREAK_TO_READ_CACHING).
351894047d49SGordon Ross 		 * Else If BreakCacheLevel contains both
351994047d49SGordon Ross 		 *  READ_CACHING and WRITE_CACHING:
352094047d49SGordon Ross 		 *	Set Oplock.State to (READ_CACHING|WRITE_CACHING|
352194047d49SGordon Ross 		 *	    HANDLE_CACHING|EXCLUSIVE|BREAK_TO_NO_CACHING).
352294047d49SGordon Ross 		 * EndIf
352394047d49SGordon Ross 		 * Set NeedToWait to TRUE.
352494047d49SGordon Ross 		 */
352594047d49SGordon Ross 		if (BreakCacheLevel == WRITE_CACHING) {
352694047d49SGordon Ross 			nol->ol_state = (READ_CACHING|WRITE_CACHING|
352794047d49SGordon Ross 			    HANDLE_CACHING|EXCLUSIVE|BREAK_TO_READ_CACHING);
352894047d49SGordon Ross 		}
352994047d49SGordon Ross 		else if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) ==
353094047d49SGordon Ross 		    (READ_CACHING | WRITE_CACHING)) {
353194047d49SGordon Ross 			nol->ol_state = (READ_CACHING|WRITE_CACHING|
353294047d49SGordon Ross 			    HANDLE_CACHING|EXCLUSIVE|BREAK_TO_NO_CACHING);
353394047d49SGordon Ross 		}
353494047d49SGordon Ross 		NeedToWait = B_TRUE;
353594047d49SGordon Ross 		break;
353694047d49SGordon Ross 
353794047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
353894047d49SGordon Ross 	    BREAK_TO_READ_CACHING|BREAK_TO_HANDLE_CACHING):
353994047d49SGordon Ross 		/*
354094047d49SGordon Ross 		 * If BreakCacheLevel == HANDLE_CACHING:
354194047d49SGordon Ross 		 *	Set Oplock.State to (READ_CACHING|WRITE_CACHING|
354294047d49SGordon Ross 		 *			HANDLE_CACHING|EXCLUSIVE|
354394047d49SGordon Ross 		 *			BREAK_TO_READ_CACHING).
354494047d49SGordon Ross 		 * Else If BreakCacheLevel contains READ_CACHING:
354594047d49SGordon Ross 		 *	Set Oplock.State to (READ_CACHING|WRITE_CACHING|
354694047d49SGordon Ross 		 *			HANDLE_CACHING|EXCLUSIVE|
354794047d49SGordon Ross 		 *			BREAK_TO_NO_CACHING).
354894047d49SGordon Ross 		 * EndIf
354994047d49SGordon Ross 		 * Set NeedToWait to TRUE.
355094047d49SGordon Ross 		 */
355194047d49SGordon Ross 		if (BreakCacheLevel == HANDLE_CACHING) {
355294047d49SGordon Ross 			nol->ol_state =
355394047d49SGordon Ross 			    (READ_CACHING|WRITE_CACHING|
355494047d49SGordon Ross 			    HANDLE_CACHING|EXCLUSIVE|
355594047d49SGordon Ross 			    BREAK_TO_READ_CACHING);
355694047d49SGordon Ross 		}
355794047d49SGordon Ross 		else if ((BreakCacheLevel & READ_CACHING) != 0) {
355894047d49SGordon Ross 			nol->ol_state =
355994047d49SGordon Ross 			    (READ_CACHING|WRITE_CACHING|
356094047d49SGordon Ross 			    HANDLE_CACHING|EXCLUSIVE|
356194047d49SGordon Ross 			    BREAK_TO_NO_CACHING);
356294047d49SGordon Ross 		}
356394047d49SGordon Ross 		NeedToWait = B_TRUE;
356494047d49SGordon Ross 		break;
356594047d49SGordon Ross 
356694047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
356794047d49SGordon Ross 	    BREAK_TO_READ_CACHING):
356894047d49SGordon Ross 		/*
356994047d49SGordon Ross 		 * If BreakCacheLevel contains READ_CACHING,
357094047d49SGordon Ross 		 *	Set Oplock.State to (READ_CACHING|WRITE_CACHING|
357194047d49SGordon Ross 		 *			HANDLE_CACHING|EXCLUSIVE|
357294047d49SGordon Ross 		 *			BREAK_TO_NO_CACHING).
357394047d49SGordon Ross 		 * EndIf
357494047d49SGordon Ross 		 * Set NeedToWait to TRUE.
357594047d49SGordon Ross 		 */
357694047d49SGordon Ross 		if ((BreakCacheLevel & READ_CACHING) != 0) {
357794047d49SGordon Ross 			nol->ol_state =
357894047d49SGordon Ross 			    (READ_CACHING|WRITE_CACHING|
357994047d49SGordon Ross 			    HANDLE_CACHING|EXCLUSIVE|
358094047d49SGordon Ross 			    BREAK_TO_NO_CACHING);
358194047d49SGordon Ross 		}
358294047d49SGordon Ross 		NeedToWait = B_TRUE;
358394047d49SGordon Ross 		break;
358494047d49SGordon Ross 
358594047d49SGordon Ross 	case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
358694047d49SGordon Ross 	    BREAK_TO_NO_CACHING):
358794047d49SGordon Ross 		NeedToWait = B_TRUE;
358894047d49SGordon Ross 		break;
358994047d49SGordon Ross 
359094047d49SGordon Ross 	} /* Switch */
359194047d49SGordon Ross 
359294047d49SGordon Ross 	if (NeedToWait) {
359394047d49SGordon Ross 		/*
359494047d49SGordon Ross 		 * The operation that called this algorithm MUST be
359594047d49SGordon Ross 		 *   made cancelable by inserting it into
359694047d49SGordon Ross 		 *   CancelableOperations.CancelableOperationList.
359794047d49SGordon Ross 		 * The operation that called this algorithm waits until
359894047d49SGordon Ross 		 *   the oplock break is acknowledged, as specified in
359994047d49SGordon Ross 		 *   section 2.1.5.18, or the operation is canceled.
360094047d49SGordon Ross 		 */
360194047d49SGordon Ross 		status = NT_STATUS_OPLOCK_BREAK_IN_PROGRESS;
360294047d49SGordon Ross 		/* Caller does smb_oplock_wait_break() */
360394047d49SGordon Ross 	}
360494047d49SGordon Ross 
360594047d49SGordon Ross out:
360694047d49SGordon Ross 	mutex_exit(&node->n_oplock.ol_mutex);
360794047d49SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
360894047d49SGordon Ross 
360994047d49SGordon Ross 	return (status);
361094047d49SGordon Ross }
361194047d49SGordon Ross 
361294047d49SGordon Ross /*
361394047d49SGordon Ross  * smb_oplock_move()
361494047d49SGordon Ross  *
361594047d49SGordon Ross  * Helper function for smb2_lease_ofile_close, where we're closing the
361694047d49SGordon Ross  * ofile that has the oplock for a given lease, and need to move that
361794047d49SGordon Ross  * oplock to another handle with the same lease.
361894047d49SGordon Ross  *
361994047d49SGordon Ross  * This is not described in [MS-FSA], so presumably Windows does this
362094047d49SGordon Ross  * by keeping oplock objects separate from the open files (no action
362194047d49SGordon Ross  * needed in the FSA layer).  We keep the oplock state as part of the
362294047d49SGordon Ross  * ofile, so we need to relocate the oplock state in this case.
362394047d49SGordon Ross  *
362494047d49SGordon Ross  * Note that in here, we're moving state for both the FSA level and
362594047d49SGordon Ross  * the SMB level (which is unusual) but this is the easiest way to
362694047d49SGordon Ross  * make sure we move the state without any other effects.
362794047d49SGordon Ross  */
362894047d49SGordon Ross void
smb_oplock_move(smb_node_t * node,smb_ofile_t * fr_ofile,smb_ofile_t * to_ofile)362994047d49SGordon Ross smb_oplock_move(smb_node_t *node,
363094047d49SGordon Ross     smb_ofile_t *fr_ofile, smb_ofile_t *to_ofile)
363194047d49SGordon Ross {
363294047d49SGordon Ross 	/*
363394047d49SGordon Ross 	 * These are the two common states for an ofile with
363494047d49SGordon Ross 	 * a lease that's not the one holding the oplock.
363594047d49SGordon Ross 	 * Log if it's not either of these.
363694047d49SGordon Ross 	 */
363794047d49SGordon Ross 	static const smb_oplock_grant_t og0 = { 0 };
363894047d49SGordon Ross 	static const smb_oplock_grant_t og8 = {
363994047d49SGordon Ross 	    .og_state = OPLOCK_LEVEL_GRANULAR, 0 };
364094047d49SGordon Ross 	smb_oplock_grant_t og_tmp;
364194047d49SGordon Ross 
364294047d49SGordon Ross 	ASSERT(fr_ofile->f_node == node);
364394047d49SGordon Ross 	ASSERT(to_ofile->f_node == node);
36441f0845f1SGordon Ross 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
364594047d49SGordon Ross 
364694047d49SGordon Ross 	/*
364794047d49SGordon Ross 	 * The ofile to which we're moving the oplock
364894047d49SGordon Ross 	 * should NOT have any oplock state.  However,
364994047d49SGordon Ross 	 * as long as we just swap state between the
365094047d49SGordon Ross 	 * two oplocks, we won't invalidate any of
365194047d49SGordon Ross 	 * the node's "onlist" counts etc.
365294047d49SGordon Ross 	 */
365394047d49SGordon Ross 	if (bcmp(&to_ofile->f_oplock, &og0, sizeof (og0)) != 0 &&
365494047d49SGordon Ross 	    bcmp(&to_ofile->f_oplock, &og8, sizeof (og8)) != 0) {
365594047d49SGordon Ross #ifdef	DEBUG
365694047d49SGordon Ross 		cmn_err(CE_NOTE, "smb_oplock_move: not empty?");
365794047d49SGordon Ross #endif
365894047d49SGordon Ross 		DTRACE_PROBE2(dst__not__empty,
3659a5a9a6bbSGordon Ross 		    smb_node_t *, node, smb_ofile_t *, to_ofile);
366094047d49SGordon Ross 	}
366194047d49SGordon Ross 
366294047d49SGordon Ross 	og_tmp = to_ofile->f_oplock;
366394047d49SGordon Ross 	to_ofile->f_oplock = fr_ofile->f_oplock;
366494047d49SGordon Ross 	fr_ofile->f_oplock = og_tmp;
366594047d49SGordon Ross 
366694047d49SGordon Ross 	if (node->n_oplock.excl_open == fr_ofile)
366794047d49SGordon Ross 		node->n_oplock.excl_open = to_ofile;
366894047d49SGordon Ross 
366994047d49SGordon Ross }
3670