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 /*
13*1f0845f1SGordon Ross  * Copyright 2020 Nexenta by DDN, Inc.  All rights reserved.
1494047d49SGordon Ross  */
1594047d49SGordon Ross 
1694047d49SGordon Ross /*
1794047d49SGordon Ross  * (SMB1/SMB2) common (FS-level) Oplock support.
1894047d49SGordon Ross  *
1994047d49SGordon Ross  * This is the file-system (FS) level oplock code.  This level
2094047d49SGordon Ross  * knows about the rules by which various kinds of oplocks may
2194047d49SGordon Ross  * coexist and how they interact.  Note that this code should
2294047d49SGordon Ross  * have NO knowledge of specific SMB protocol details.  Those
2394047d49SGordon Ross  * details are handled in smb_srv_oplock.c and related.
2494047d49SGordon Ross  *
2594047d49SGordon Ross  * This file is intentionally written to very closely follow the
2694047d49SGordon Ross  * [MS-FSA] specification sections about oplocks.  Almost every
2794047d49SGordon Ross  * section of code is preceeded by a block of text from that
2894047d49SGordon Ross  * specification describing the logic.  Where the implementation
2994047d49SGordon Ross  * differs from what the spec. describes, there are notes like:
3094047d49SGordon Ross  * Implementation specific: ...
3194047d49SGordon Ross  */
3294047d49SGordon Ross 
3394047d49SGordon Ross #include <smbsrv/smb_kproto.h>
3494047d49SGordon Ross #include <smbsrv/smb_oplock.h>
3594047d49SGordon Ross 
3694047d49SGordon Ross /*
3794047d49SGordon Ross  * Several short-hand defines and enums used in this file.
3894047d49SGordon Ross  */
3994047d49SGordon Ross 
4194047d49SGordon Ross 				NODE_FLAGS_DELETE_COMMITTED)
4294047d49SGordon Ross 
4394047d49SGordon Ross static uint32_t
4494047d49SGordon Ross smb_oplock_req_excl(
4594047d49SGordon Ross     smb_ofile_t *ofile,		/* in: the "Open" */
4694047d49SGordon Ross     uint32_t *rop);		/* in: "RequestedOplock", out:NewOplockLevel */
4794047d49SGordon Ross 
4894047d49SGordon Ross static uint32_t
4994047d49SGordon Ross smb_oplock_req_shared(
5094047d49SGordon Ross     smb_ofile_t *ofile,		/* the "Open" */
5194047d49SGordon Ross     uint32_t *rop,		/* in: "RequestedOplock", out:NewOplockLevel */
5294047d49SGordon Ross     boolean_t GrantingInAck);
5394047d49SGordon Ross 
5494047d49SGordon Ross static uint32_t smb_oplock_break_cmn(smb_node_t *node,
5594047d49SGordon Ross     smb_ofile_t *ofile, uint32_t BreakCacheLevel);
5694047d49SGordon Ross 
5794047d49SGordon Ross 
5894047d49SGordon Ross /*
5994047d49SGordon Ross  * [MS-FSA] Algorithm to Compare Oplock Keys
6094047d49SGordon Ross  *
6194047d49SGordon Ross  * The inputs for this algorithm are:
6294047d49SGordon Ross  *
6394047d49SGordon Ross  *	OperationOpen: The Open used in the request that can
6494047d49SGordon Ross  *	  cause an oplock to break.
6594047d49SGordon Ross  *	OplockOpen: The Open originally used to request the oplock,
6694047d49SGordon Ross  *	  as specified in section
6794047d49SGordon Ross  *	Flags: If unspecified it is considered to contain 0.
6894047d49SGordon Ross  *	  Valid nonzero values are:
6994047d49SGordon Ross  *		PARENT_OBJECT
7094047d49SGordon Ross  *
7194047d49SGordon Ross  * This algorithm returns TRUE if the appropriate oplock key field of
7294047d49SGordon Ross  * OperationOpen equals OplockOpen.TargetOplockKey, and FALSE otherwise.
7394047d49SGordon Ross  *
7494047d49SGordon Ross  * Note: Unlike many comparison functions, ARG ORDER MATTERS.
7594047d49SGordon Ross  */
7694047d49SGordon Ross 
7794047d49SGordon Ross static boolean_t
CompareOplockKeys(smb_ofile_t * OperOpen,smb_ofile_t * OplockOpen,int flags)7894047d49SGordon Ross CompareOplockKeys(smb_ofile_t *OperOpen, smb_ofile_t *OplockOpen, int flags)
7994047d49SGordon Ross {
8094047d49SGordon Ross 	static const uint8_t key0[SMB_LEASE_KEY_SZ] = { 0 };
8194047d49SGordon Ross 
8294047d49SGordon Ross 	/*
8394047d49SGordon Ross 	 * When we're called via FEM, (smb_oplock_break_...)
8494047d49SGordon Ross 	 * the OperOpen arg is NULL because I/O outside of SMB
8594047d49SGordon Ross 	 * doesn't have an "ofile".  That's "not a match".
8694047d49SGordon Ross 	 */
8794047d49SGordon Ross 	if (OperOpen == NULL)
8894047d49SGordon Ross 		return (B_FALSE);
8994047d49SGordon Ross 	ASSERT(OplockOpen != NULL);
9094047d49SGordon Ross 
9194047d49SGordon Ross 	/*
9294047d49SGordon Ross 	 * If OperationOpen equals OplockOpen:
9394047d49SGordon Ross 	 * Return TRUE.
9494047d49SGordon Ross 	 */
9594047d49SGordon Ross 	if (OperOpen == OplockOpen)
9694047d49SGordon Ross 		return (B_TRUE);
9794047d49SGordon Ross 
9894047d49SGordon Ross 	/*
9994047d49SGordon Ross 	 * If both OperationOpen.TargetOplockKey and
10094047d49SGordon Ross 	 * OperationOpen.ParentOplockKey are empty
10194047d49SGordon Ross 	 * or both OplockOpen.TargetOplockKey and
10294047d49SGordon Ross 	 * OplockOpen.ParentOplockKey are empty:
10394047d49SGordon Ross 	 * Return FALSE.
10494047d49SGordon Ross 	 */
10594047d49SGordon Ross 	if (bcmp(OperOpen->TargetOplockKey, key0, sizeof (key0)) == 0 &&
10694047d49SGordon Ross 	    bcmp(OperOpen->ParentOplockKey, key0, sizeof (key0)) == 0)
10794047d49SGordon Ross 		return (B_FALSE);
10894047d49SGordon Ross 	if (bcmp(OplockOpen->TargetOplockKey, key0, sizeof (key0)) == 0 &&
10994047d49SGordon Ross 	    bcmp(OplockOpen->ParentOplockKey, key0, sizeof (key0)) == 0)
11094047d49SGordon Ross 		return (B_FALSE);
11194047d49SGordon Ross 
11294047d49SGordon Ross 	/*
11394047d49SGordon Ross 	 * If OplockOpen.TargetOplockKey is empty or...
11494047d49SGordon Ross 	 */
11594047d49SGordon Ross 	if (bcmp(OplockOpen->TargetOplockKey, key0, sizeof (key0)) == 0)
11694047d49SGordon Ross 		return (B_FALSE);
11794047d49SGordon Ross 
11894047d49SGordon Ross 	/*
11994047d49SGordon Ross 	 * If Flags contains PARENT_OBJECT:
12094047d49SGordon Ross 	 */
12194047d49SGordon Ross 	if ((flags & PARENT_OBJECT) != 0) {
12294047d49SGordon Ross 		/*
12394047d49SGordon Ross 		 * If OperationOpen.ParentOplockKey is empty:
12494047d49SGordon Ross 		 * Return FALSE.
12594047d49SGordon Ross 		 */
12694047d49SGordon Ross 		if (bcmp(OperOpen->ParentOplockKey, key0, sizeof (key0)) == 0)
12794047d49SGordon Ross 			return (B_FALSE);
12894047d49SGordon Ross 
12994047d49SGordon Ross 		/*
13094047d49SGordon Ross 		 * If OperationOpen.ParentOplockKey equals
13194047d49SGordon Ross 		 * OplockOpen.TargetOplockKey:
13294047d49SGordon Ross 		 * return TRUE, else FALSE
13394047d49SGordon Ross 		 */
13494047d49SGordon Ross 		if (bcmp(OperOpen->ParentOplockKey,
13594047d49SGordon Ross 		    OplockOpen->TargetOplockKey,
13694047d49SGordon Ross 		    SMB_LEASE_KEY_SZ) == 0) {
13794047d49SGordon Ross 			return (B_TRUE);
13894047d49SGordon Ross 		}
13994047d49SGordon Ross 	} else {
14094047d49SGordon Ross 		/*
14194047d49SGordon Ross 		 * ... from above:
14294047d49SGordon Ross 		 * (Flags does not contain PARENT_OBJECT and
14394047d49SGordon Ross 		 * OperationOpen.TargetOplockKey is empty):
14494047d49SGordon Ross 		 * Return FALSE.
14594047d49SGordon Ross 		 */
14694047d49SGordon Ross 		if (bcmp(OperOpen->TargetOplockKey, key0, sizeof (key0)) == 0)
14794047d49SGordon Ross 			return (B_FALSE);
14894047d49SGordon Ross 
14994047d49SGordon Ross 		/*
15094047d49SGordon Ross 		 * If OperationOpen.TargetOplockKey equals
15194047d49SGordon Ross 		 * OplockOpen.TargetOplockKey:
15294047d49SGordon Ross 		 *  Return TRUE, else FALSE
15394047d49SGordon Ross 		 */
15494047d49SGordon Ross 		if (bcmp(OperOpen->TargetOplockKey,
15594047d49SGordon Ross 		    OplockOpen->TargetOplockKey,
15694047d49SGordon Ross 		    SMB_LEASE_KEY_SZ) == 0) {
15794047d49SGordon Ross 			return (B_TRUE);
15894047d49SGordon Ross 		}
15994047d49SGordon Ross 	}
16094047d49SGordon Ross 
16194047d49SGordon Ross 	return (B_FALSE);
16294047d49SGordon Ross }
16394047d49SGordon Ross 
16494047d49SGordon Ross /*
16594047d49SGordon Ross  * Algorithm to Recompute the State of a Shared Oplock
16694047d49SGordon Ross  *
16794047d49SGordon Ross  * The inputs for this algorithm are:
16894047d49SGordon Ross  *	ThisOplock: The Oplock on whose state is being recomputed.
16994047d49SGordon Ross  */
17094047d49SGordon Ross static void
RecomputeOplockState(smb_node_t * node)17194047d49SGordon Ross RecomputeOplockState(smb_node_t *node)
17294047d49SGordon Ross {
17394047d49SGordon Ross 	smb_oplock_t *ol = &node->n_oplock;
17494047d49SGordon Ross 
17594047d49SGordon Ross 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
17694047d49SGordon Ross 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
17794047d49SGordon Ross 
17894047d49SGordon Ross 	/*
17994047d49SGordon Ross 	 * If ThisOplock.IIOplocks, ThisOplock.ROplocks, ThisOplock.RHOplocks,
18094047d49SGordon Ross 	 * and ThisOplock.RHBreakQueue are all empty:
18194047d49SGordon Ross 	 *	Set ThisOplock.State to NO_OPLOCK.
18294047d49SGordon Ross 	 */
18394047d49SGordon Ross 	if (ol->cnt_II == 0 && ol->cnt_R == 0 &&
18494047d49SGordon Ross 	    ol->cnt_RH == 0 && ol->cnt_RHBQ == 0) {
18594047d49SGordon Ross 		ol->ol_state = NO_OPLOCK;
18694047d49SGordon Ross 		return;
18794047d49SGordon Ross 	}
18894047d49SGordon Ross 
18994047d49SGordon Ross 	/*
19094047d49SGordon Ross 	 * Else If ThisOplock.ROplocks is not empty and either
19194047d49SGordon Ross 	 *    ThisOplock.RHOplocks or ThisOplock.RHBreakQueue are not empty:
19294047d49SGordon Ross 	 *	Set ThisOplock.State to
19494047d49SGordon Ross 	 */
19594047d49SGordon Ross 	else if (ol->cnt_R != 0 && (ol->cnt_RH != 0 || ol->cnt_RHBQ != 0)) {
19694047d49SGordon Ross 		ol->ol_state = (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH);
19794047d49SGordon Ross 	}
19894047d49SGordon Ross 
19994047d49SGordon Ross 	/*
20094047d49SGordon Ross 	 * Else If ThisOplock.ROplocks is empty and
20194047d49SGordon Ross 	 * ThisOplock.RHOplocks is not empty:
20294047d49SGordon Ross 	 *	Set ThisOplock.State to (READ_CACHING|HANDLE_CACHING).
20394047d49SGordon Ross 	 */
20494047d49SGordon Ross 	else if (ol->cnt_R == 0 && ol->cnt_RH != 0) {
20594047d49SGordon Ross 		ol->ol_state = (READ_CACHING|HANDLE_CACHING);
20694047d49SGordon Ross 	}
20794047d49SGordon Ross 
20894047d49SGordon Ross 	/*
20994047d49SGordon Ross 	 * Else If ThisOplock.ROplocks is not empty and
21094047d49SGordon Ross 	 * ThisOplock.IIOplocks is not empty:
21194047d49SGordon Ross 	 *	Set ThisOplock.State to (READ_CACHING|LEVEL_TWO_OPLOCK).
21294047d49SGordon Ross 	 */
21394047d49SGordon Ross 	else if (ol->cnt_R != 0 && ol->cnt_II != 0) {
21494047d49SGordon Ross 		ol->ol_state = (READ_CACHING|LEVEL_TWO_OPLOCK);
21594047d49SGordon Ross 	}
21694047d49SGordon Ross 
21794047d49SGordon Ross 	/*
21894047d49SGordon Ross 	 * Else If ThisOplock.ROplocks is not empty and
21994047d49SGordon Ross 	 * ThisOplock.IIOplocks is empty:
22094047d49SGordon Ross 	 *	Set ThisOplock.State to READ_CACHING.
22194047d49SGordon Ross 	 */
22294047d49SGordon Ross 	else if (ol->cnt_R != 0 && ol->cnt_II == 0) {
22394047d49SGordon Ross 		ol->ol_state = READ_CACHING;
22494047d49SGordon Ross 	}
22594047d49SGordon Ross 
22694047d49SGordon Ross 	/*
22794047d49SGordon Ross 	 * Else If ThisOplock.ROplocks is empty and
22894047d49SGordon Ross 	 * ThisOplock.IIOplocks is not empty:
22994047d49SGordon Ross 	 *	Set ThisOplock.State to LEVEL_TWO_OPLOCK.
23094047d49SGordon Ross 	 */
23194047d49SGordon Ross 	else if (ol->cnt_R == 0 && ol->cnt_II != 0) {
23294047d49SGordon Ross 		ol->ol_state = LEVEL_TWO_OPLOCK;
23394047d49SGordon Ross 	}
23494047d49SGordon Ross 
23594047d49SGordon Ross 	else {
23694047d49SGordon Ross 		smb_ofile_t *o;
23794047d49SGordon Ross 		int cntBrkToRead;
23894047d49SGordon Ross 
23994047d49SGordon Ross 		/*
24094047d49SGordon Ross 		 * ThisOplock.RHBreakQueue MUST be non-empty by this point.
24194047d49SGordon Ross 		 */
24294047d49SGordon Ross 		ASSERT(ol->cnt_RHBQ != 0);
24394047d49SGordon Ross 
24494047d49SGordon Ross 		/*
24594047d49SGordon Ross 		 * How many on RHBQ have BreakingToRead set?
24694047d49SGordon Ross 		 */
24794047d49SGordon Ross 		cntBrkToRead = 0;
24894047d49SGordon Ross 		FOREACH_NODE_OFILE(node, o) {
24994047d49SGordon Ross 			if (o->f_oplock.onlist_RHBQ == 0)
25094047d49SGordon Ross 				continue;
25194047d49SGordon Ross 			if (o->f_oplock.BreakingToRead)
25294047d49SGordon Ross 				cntBrkToRead++;
25394047d49SGordon Ross 		}
25494047d49SGordon Ross 
25594047d49SGordon Ross 		/*
25694047d49SGordon Ross 		 * If RHOpContext.BreakingToRead is TRUE for
25794047d49SGordon Ross 		 *  every RHOpContext on ThisOplock.RHBreakQueue:
25894047d49SGordon Ross 		 */
25994047d49SGordon Ross 		if (cntBrkToRead == ol->cnt_RHBQ) {
26094047d49SGordon Ross 			/*
26194047d49SGordon Ross 			 * Set ThisOplock.State to
26394047d49SGordon Ross 			 */
26494047d49SGordon Ross 			ol->ol_state = (READ_CACHING|HANDLE_CACHING|
26594047d49SGordon Ross 			    BREAK_TO_READ_CACHING);
26694047d49SGordon Ross 		}
26794047d49SGordon Ross 
26894047d49SGordon Ross 		/*
26994047d49SGordon Ross 		 * Else If RHOpContext.BreakingToRead is FALSE for
27094047d49SGordon Ross 		 *  every RHOpContext on ThisOplock.RHBreakQueue:
27194047d49SGordon Ross 		 */
27294047d49SGordon Ross 		else if (cntBrkToRead == 0) {
27394047d49SGordon Ross 			/*
27494047d49SGordon Ross 			 * Set ThisOplock.State to
27694047d49SGordon Ross 			 */
27794047d49SGordon Ross 			ol->ol_state = (READ_CACHING|HANDLE_CACHING|
27894047d49SGordon Ross 			    BREAK_TO_NO_CACHING);
27994047d49SGordon Ross 		} else {
28094047d49SGordon Ross 			/*
28194047d49SGordon Ross 			 * Set ThisOplock.State to
28294047d49SGordon Ross 			 *  (READ_CACHING|HANDLE_CACHING).
28394047d49SGordon Ross 			 */
28494047d49SGordon Ross 			ol->ol_state = (READ_CACHING|HANDLE_CACHING);
28594047d49SGordon Ross 		}
28694047d49SGordon Ross 	}
28794047d49SGordon Ross }
28894047d49SGordon Ross 
28994047d49SGordon Ross /*
29094047d49SGordon Ross  * [MS-FSA] Server Requests an Oplock
29194047d49SGordon Ross  *
29294047d49SGordon Ross  * The server (caller) provides:
29394047d49SGordon Ross  *	Open - The Open on which the oplock is being requested. (ofile)
29494047d49SGordon Ross  *	Type - The type of oplock being requested. Valid values are as follows:
29594047d49SGordon Ross  *		LEVEL_TWO (Corresponds to SMB2_OPLOCK_LEVEL_II)
29694047d49SGordon Ross  *		LEVEL_ONE (Corresponds to SMB2_OPLOCK_LEVEL_EXCLUSIVE)
29794047d49SGordon Ross  *		LEVEL_BATCH (Corresponds to SMB2_OPLOCK_LEVEL_BATCH)
29894047d49SGordon Ross  *		LEVEL_GRANULAR (Corresponds to SMB2_OPLOCK_LEVEL_LEASE)
29994047d49SGordon Ross  *	RequestedOplockLevel - A combination of zero or more of the
30094047d49SGordon Ross  *	  following flags (ignored if Type != LEVEL_GRANULAR)
30194047d49SGordon Ross  *		READ_CACHING
30294047d49SGordon Ross  *		HANDLE_CACHING
30394047d49SGordon Ross  *		WRITE_CACHING
30494047d49SGordon Ross  *
30594047d49SGordon Ross  *	(Type + RequestedOplockLevel come in *statep)
30694047d49SGordon Ross  *
30794047d49SGordon Ross  * Returns:
30894047d49SGordon Ross  *	*statep = NewOplockLevel (possibly less than requested)
30994047d49SGordon Ross  *		  containing: LEVEL_NONE, LEVEL_TWO + cache_flags
31094047d49SGordon Ross  *	NTSTATUS
31194047d49SGordon Ross  */
31294047d49SGordon Ross 
31394047d49SGordon Ross uint32_t
smb_oplock_request(smb_request_t * sr,smb_ofile_t * ofile,uint32_t * statep)31494047d49SGordon Ross smb_oplock_request(smb_request_t *sr, smb_ofile_t *ofile, uint32_t *statep)
31594047d49SGordon Ross {
31694047d49SGordon Ross 	smb_node_t *node = ofile->f_node;
31794047d49SGordon Ross 	uint32_t type = *statep & OPLOCK_LEVEL_TYPE_MASK;
31894047d49SGordon Ross 	uint32_t level = *statep & OPLOCK_LEVEL_CACHE_MASK;
31994047d49SGordon Ross 	uint32_t status;
32094047d49SGordon Ross 
32194047d49SGordon Ross 	*statep = LEVEL_NONE;
32294047d49SGordon Ross 
32394047d49SGordon Ross 	/*
32494047d49SGordon Ross 	 * If Open.Stream.StreamType is DirectoryStream:
32594047d49SGordon Ross 	 *	The operation MUST be failed with STATUS_INVALID_PARAMETER
32694047d49SGordon Ross 	 *	under either of the following conditions:
32794047d49SGordon Ross 	 *	* Type is not LEVEL_GRANULAR.
32894047d49SGordon Ross 	 *	* Type is LEVEL_GRANULAR but RequestedOplockLevel is
32994047d49SGordon Ross 	 *	  neither READ_CACHING nor (READ_CACHING|HANDLE_CACHING).
33094047d49SGordon Ross 	 */
33194047d49SGordon Ross 	if (!smb_node_is_file(node)) {
33294047d49SGordon Ross 		/* ofile is a directory. */
33394047d49SGordon Ross 		if (type != LEVEL_GRANULAR)
33494047d49SGordon Ross 			return (NT_STATUS_INVALID_PARAMETER);
33594047d49SGordon Ross 		if (level != READ_CACHING &&
33694047d49SGordon Ross 		    level != (READ_CACHING|HANDLE_CACHING))
33794047d49SGordon Ross 			return (NT_STATUS_INVALID_PARAMETER);
33894047d49SGordon Ross 		/*
33994047d49SGordon Ross 		 * We're not supporting directory leases yet.
34094047d49SGordon Ross 		 * Todo.
34194047d49SGordon Ross 		 */
34294047d49SGordon Ross 		return (NT_STATUS_OPLOCK_NOT_GRANTED);
34394047d49SGordon Ross 	}
34494047d49SGordon Ross 
34594047d49SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
34694047d49SGordon Ross 	mutex_enter(&node->n_oplock.ol_mutex);
34794047d49SGordon Ross 
34894047d49SGordon Ross 	/*
34994047d49SGordon Ross 	 * If Type is LEVEL_ONE or LEVEL_BATCH:
35094047d49SGordon Ross 	 * The operation MUST be failed with STATUS_OPLOCK_NOT_GRANTED
35194047d49SGordon Ross 	 * under either of the following conditions:
35294047d49SGordon Ross 	 *	Open.File.OpenList contains more than one Open
35394047d49SGordon Ross 	 *	  whose Stream is the same as Open.Stream.
35494047d49SGordon Ross 	 *	Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or
35594047d49SGordon Ross 	 *	  FILE_SYNCHRONOUS_IO_NONALERT.
35694047d49SGordon Ross 	 * Request an exclusive oplock according to the algorithm in
35794047d49SGordon Ross 	 * section, setting the algorithm's params as follows:
35894047d49SGordon Ross 	 *	Pass in the current Open.
35994047d49SGordon Ross 	 *	RequestedOplock = Type.
36094047d49SGordon Ross 	 * The operation MUST at this point return any status code
36194047d49SGordon Ross 	 * returned by the exclusive oplock request algorithm.
36294047d49SGordon Ross 	 */
36394047d49SGordon Ross 	if (type == LEVEL_ONE || type == LEVEL_BATCH) {
36494047d49SGordon Ross 		if (node->n_open_count > 1) {
36594047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
36694047d49SGordon Ross 			goto out;
36794047d49SGordon Ross 		}
36894047d49SGordon Ross 		/* XXX: Should be a flag on the ofile. */
36994047d49SGordon Ross 		if (node->flags & NODE_FLAGS_WRITE_THROUGH) {
37094047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
37194047d49SGordon Ross 			goto out;
37294047d49SGordon Ross 		}
37394047d49SGordon Ross 		*statep = type;
37494047d49SGordon Ross 		status = smb_oplock_req_excl(ofile, statep);
37594047d49SGordon Ross 		goto out;
37694047d49SGordon Ross 	}
37794047d49SGordon Ross 
37894047d49SGordon Ross 	/*
37994047d49SGordon Ross 	 * Else If Type is LEVEL_TWO:
38094047d49SGordon Ross 	 * The operation MUST be failed with STATUS_OPLOCK_NOT_GRANTED under
38194047d49SGordon Ross 	 *  either of the following conditions:
38294047d49SGordon Ross 	 *	Open.Stream.ByteRangeLockList is not empty.
38394047d49SGordon Ross 	 *	Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or
38494047d49SGordon Ross 	 *	  FILE_SYNCHRONOUS_IO_NONALERT.
38594047d49SGordon Ross 	 * Request a shared oplock according to the algorithm in
38694047d49SGordon Ross 	 * section, setting the algorithm's parameters as follows:
38794047d49SGordon Ross 	 *	Pass in the current Open.
38894047d49SGordon Ross 	 *	RequestedOplock = Type.
38994047d49SGordon Ross 	 *	GrantingInAck = FALSE.
39094047d49SGordon Ross 	 * The operation MUST at this point return any status code
39194047d49SGordon Ross 	 * returned by the shared oplock request algorithm.
39294047d49SGordon Ross 	 */
39394047d49SGordon Ross 	if (type == LEVEL_TWO) {
39494047d49SGordon Ross 		if (smb_lock_range_access(sr, node, 0, ~0, B_FALSE) != 0) {
39594047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
39694047d49SGordon Ross 			goto out;
39794047d49SGordon Ross 		}
39894047d49SGordon Ross 		/* XXX: Should be a flag on the ofile. */
39994047d49SGordon Ross 		if (node->flags & NODE_FLAGS_WRITE_THROUGH) {
40094047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
40194047d49SGordon Ross 			goto out;
40294047d49SGordon Ross 		}
40394047d49SGordon Ross 		*statep = type;
40494047d49SGordon Ross 		status = smb_oplock_req_shared(ofile, statep, B_FALSE);
40594047d49SGordon Ross 		goto out;
40694047d49SGordon Ross 	}
40794047d49SGordon Ross 
40894047d49SGordon Ross 	/*
40994047d49SGordon Ross 	 * Else If Type is LEVEL_GRANULAR:
41094047d49SGordon Ross 	 *   Sub-cases on RequestedOplockLevel (our "level")
41194047d49SGordon Ross 	 *
41294047d49SGordon Ross 	 * This is the last Type, so error on !granular and then
41394047d49SGordon Ross 	 * deal with the cache levels using one less indent.
41494047d49SGordon Ross 	 */
41594047d49SGordon Ross 	if (type != LEVEL_GRANULAR) {
41694047d49SGordon Ross 		status = NT_STATUS_INVALID_PARAMETER;
41794047d49SGordon Ross 		goto out;
41894047d49SGordon Ross 	}
41994047d49SGordon Ross 
42094047d49SGordon Ross 	switch (level) {
42194047d49SGordon Ross 
42294047d49SGordon Ross 	/*
42394047d49SGordon Ross 	 * If RequestedOplockLevel is READ_CACHING or
42494047d49SGordon Ross 	 *   (READ_CACHING|HANDLE_CACHING):
42594047d49SGordon Ross 	 *	The operation MUST be failed with STATUS_OPLOCK_NOT_GRANTED
42694047d49SGordon Ross 	 *	under either of the following conditions:
42794047d49SGordon Ross 	 *		Open.Stream.ByteRangeLockList is not empty.
42894047d49SGordon Ross 	 *		Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or
42994047d49SGordon Ross 	 *		  FILE_SYNCHRONOUS_IO_NONALERT.
43094047d49SGordon Ross 	 *	Request a shared oplock according to the algorithm in
43194047d49SGordon Ross 	 *	section, setting the parameters as follows:
43294047d49SGordon Ross 	 *		Pass in the current Open.
43394047d49SGordon Ross 	 *		RequestedOplock = RequestedOplockLevel.
43494047d49SGordon Ross 	 *		GrantingInAck = FALSE.
43594047d49SGordon Ross 	 *
43694047d49SGordon Ross 	 *	The operation MUST at this point return any status code
43794047d49SGordon Ross 	 *	  returned by the shared oplock request algorithm.
43894047d49SGordon Ross 	 */
43994047d49SGordon Ross 	case READ_CACHING:
44094047d49SGordon Ross 	case (READ_CACHING|HANDLE_CACHING):
44194047d49SGordon Ross 		if (smb_lock_range_access(sr, node, 0, ~0, B_FALSE) != 0) {
44294047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
44394047d49SGordon Ross 			goto out;
44494047d49SGordon Ross 		}
44594047d49SGordon Ross 		/* XXX: Should be a flag on the ofile. */
44694047d49SGordon Ross 		if (node->flags & NODE_FLAGS_WRITE_THROUGH) {
44794047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
44894047d49SGordon Ross 			goto out;
44994047d49SGordon Ross 		}
45094047d49SGordon Ross 		*statep = level;
45194047d49SGordon Ross 		status = smb_oplock_req_shared(ofile, statep, B_FALSE);
45294047d49SGordon Ross 		break;
45394047d49SGordon Ross 
45494047d49SGordon Ross 	/*
45594047d49SGordon Ross 	 * Else If RequestedOplockLevel is
45694047d49SGordon Ross 	 * (READ_CACHING|WRITE_CACHING) or
45894047d49SGordon Ross 	 * If Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or
45994047d49SGordon Ross 	 * FILE_SYNCHRONOUS_IO_NONALERT, the operation MUST be failed
46094047d49SGordon Ross 	 * with STATUS_OPLOCK_NOT_GRANTED.
46194047d49SGordon Ross 	 * Request an exclusive oplock according to the algorithm in
46294047d49SGordon Ross 	 * section, setting the parameters as follows:
46394047d49SGordon Ross 	 *	Pass in the current Open.
46494047d49SGordon Ross 	 *	RequestedOplock = RequestedOplockLevel.
46594047d49SGordon Ross 	 * The operation MUST at this point return any status code
46694047d49SGordon Ross 	 * returned by the exclusive oplock request algorithm.
46794047d49SGordon Ross 	 */
46894047d49SGordon Ross 	case (READ_CACHING | WRITE_CACHING):
46994047d49SGordon Ross 	case (READ_CACHING | WRITE_CACHING | HANDLE_CACHING):
47094047d49SGordon Ross 		/* XXX: Should be a flag on the ofile. */
47194047d49SGordon Ross 		if (node->flags & NODE_FLAGS_WRITE_THROUGH) {
47294047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
47394047d49SGordon Ross 			goto out;
47494047d49SGordon Ross 		}
47594047d49SGordon Ross 		*statep = level;
47694047d49SGordon Ross 		status = smb_oplock_req_excl(ofile, statep);
47794047d49SGordon Ross 		break;
47894047d49SGordon Ross 
47994047d49SGordon Ross 	/*
48094047d49SGordon Ross 	 * Else if RequestedOplockLevel is 0 (that is, no flags):
48194047d49SGordon Ross 	 * The operation MUST return STATUS_SUCCESS at this point.
48294047d49SGordon Ross 	 */
48394047d49SGordon Ross 	case 0:
48494047d49SGordon Ross 		*statep = 0;
48594047d49SGordon Ross 		status = NT_STATUS_SUCCESS;
48694047d49SGordon Ross 		break;
48794047d49SGordon Ross 
48894047d49SGordon Ross 	/*
48994047d49SGordon Ross 	 * Else
49094047d49SGordon Ross 	 *  The operation MUST be failed with STATUS_INVALID_PARAMETER.
49194047d49SGordon Ross 	 */
49294047d49SGordon Ross 	default:
49394047d49SGordon Ross 		status = NT_STATUS_INVALID_PARAMETER;
49494047d49SGordon Ross 		break;
49594047d49SGordon Ross 	}
49694047d49SGordon Ross 
49794047d49SGordon Ross 	/* Give caller back the "Granular" bit. */
498*1f0845f1SGordon Ross 	if (status == NT_STATUS_SUCCESS) {
49994047d49SGordon Ross 		*statep |= LEVEL_GRANULAR;
50094047d49SGordon Ross 
501*1f0845f1SGordon Ross 		/*
502*1f0845f1SGordon Ross 		 * The oplock lease may have moved to this ofile. Update.
503*1f0845f1SGordon Ross 		 * Minor violation of layering here (leases vs oplocks)
504*1f0845f1SGordon Ross 		 * but we want this update coverd by the oplock mutex.
505*1f0845f1SGordon Ross 		 */
506*1f0845f1SGordon Ross #ifndef	TESTJIG
507*1f0845f1SGordon Ross 		if (ofile->f_lease != NULL)
508*1f0845f1SGordon Ross 			ofile->f_lease->ls_oplock_ofile = ofile;
509*1f0845f1SGordon Ross #endif
510*1f0845f1SGordon Ross 	}
511*1f0845f1SGordon Ross 
51294047d49SGordon Ross out:
51394047d49SGordon Ross 	mutex_exit(&node->n_oplock.ol_mutex);
51494047d49SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
51594047d49SGordon Ross 
51694047d49SGordon Ross 	return (status);
51794047d49SGordon Ross }
51894047d49SGordon Ross 
51994047d49SGordon Ross /*
52094047d49SGordon Ross  * Algorithm to Request an Exclusive Oplock
52194047d49SGordon Ross  *
52294047d49SGordon Ross  * The inputs for requesting an exclusive oplock are:
52394047d49SGordon Ross  *	Open: The Open on which the oplock is being requested.
52494047d49SGordon Ross  *	RequestedOplock: The oplock type being requested. One of:
52594047d49SGordon Ross  *	  LEVEL_ONE, LEVEL_BATCH, CACHE_RW, CACHE_RWH
52694047d49SGordon Ross  *
52794047d49SGordon Ross  * On completion, the object store MUST return:
52894047d49SGordon Ross  *	Status: An NTSTATUS code that specifies the result.
52994047d49SGordon Ross  *	NewOplockLevel: The type of oplock that the requested oplock has been
53094047d49SGordon Ross  *	  broken (reduced) to.  If a failure status is returned in Status,
53194047d49SGordon Ross  *	  the value of this field is undefined.  Valid values are as follows:
53294047d49SGordon Ross  *		LEVEL_NONE (that is, no oplock)
53394047d49SGordon Ross  *		LEVEL_TWO
53494047d49SGordon Ross  *		A combination of one or more of the following flags:
53594047d49SGordon Ross  *			READ_CACHING
53694047d49SGordon Ross  *			HANDLE_CACHING
53794047d49SGordon Ross  *			WRITE_CACHING
53894047d49SGordon Ross  *	AcknowledgeRequired: A Boolean value: TRUE if the server MUST
53994047d49SGordon Ross  *	acknowledge the oplock break; FALSE if not, as specified in
54094047d49SGordon Ross  *	section If a failure status is returned in Status,
54194047d49SGordon Ross  *	the value of this field is undefined.
54294047d49SGordon Ross  *
54394047d49SGordon Ross  * Note: Stores NewOplockLevel in *rop
54494047d49SGordon Ross  */
54594047d49SGordon Ross static uint32_t
smb_oplock_req_excl(smb_ofile_t * ofile,uint32_t * rop)54694047d49SGordon Ross smb_oplock_req_excl(
54794047d49SGordon Ross     smb_ofile_t *ofile,		/* in: the "Open" */
54894047d49SGordon Ross     uint32_t *rop)		/* in: "RequestedOplock", out:NewOplockLevel */
54994047d49SGordon Ross {
55094047d49SGordon Ross 	smb_node_t *node = ofile->f_node;
55194047d49SGordon Ross 	smb_ofile_t *o;
55294047d49SGordon Ross 	boolean_t GrantExcl = B_FALSE;
55394047d49SGordon Ross 	uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED;
55494047d49SGordon Ross 
55594047d49SGordon Ross 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
55694047d49SGordon Ross 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
55794047d49SGordon Ross 
558*1f0845f1SGordon Ross 	/*
559*1f0845f1SGordon Ross 	 * Don't allow grants on closing ofiles.
560*1f0845f1SGordon Ross 	 */
561*1f0845f1SGordon Ross 	if (ofile->f_oplock.og_closing)
562*1f0845f1SGordon Ross 		return (status);
563*1f0845f1SGordon Ross 
56494047d49SGordon Ross 	/*
56594047d49SGordon Ross 	 * If Open.Stream.Oplock is empty:
56694047d49SGordon Ross 	 *   Build a new Oplock object with fields initialized as follows:
56794047d49SGordon Ross 	 *	Oplock.State set to NO_OPLOCK.
56894047d49SGordon Ross 	 *	All other fields set to 0/empty.
56994047d49SGordon Ross 	 *   Store the new Oplock object in Open.Stream.Oplock.
57094047d49SGordon Ross 	 * EndIf
57194047d49SGordon Ross 	 *
57294047d49SGordon Ross 	 * Implementation specific:
57394047d49SGordon Ross 	 * Open.Stream.Oplock maps to: node->n_oplock
57494047d49SGordon Ross 	 */
57594047d49SGordon Ross 	if (node->n_oplock.ol_state == 0) {
57694047d49SGordon Ross 		node->n_oplock.ol_state = NO_OPLOCK;
57794047d49SGordon Ross 	}
57894047d49SGordon Ross 
57994047d49SGordon Ross 	/*
58094047d49SGordon Ross 	 * If Open.Stream.Oplock.State contains
58194047d49SGordon Ross 	 * LEVEL_TWO_OPLOCK or NO_OPLOCK: ...
58294047d49SGordon Ross 	 *
58394047d49SGordon Ross 	 * Per ms, this is the "If" matching the unbalalanced
58494047d49SGordon Ross 	 * "Else If" below (for which we requested clarification).
58594047d49SGordon Ross 	 */
58694047d49SGordon Ross 	if ((node->n_oplock.ol_state & (LEVEL_TWO | NO_OPLOCK)) != 0) {
58794047d49SGordon Ross 
58894047d49SGordon Ross 		/*
58994047d49SGordon Ross 		 * If Open.Stream.Oplock.State contains LEVEL_TWO_OPLOCK and
59094047d49SGordon Ross 		 * RequestedOplock contains one or more of READ_CACHING,
59194047d49SGordon Ross 		 * HANDLE_CACHING, or WRITE_CACHING, the operation MUST be
59294047d49SGordon Ross 		 * failed with Status set to STATUS_OPLOCK_NOT_GRANTED.
59394047d49SGordon Ross 		 */
59494047d49SGordon Ross 		if ((node->n_oplock.ol_state & LEVEL_TWO) != 0 &&
59594047d49SGordon Ross 		    (*rop & CACHE_RWH) != 0) {
59694047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
59794047d49SGordon Ross 			goto out;
59894047d49SGordon Ross 		}
59994047d49SGordon Ross 
60094047d49SGordon Ross 		/*
60194047d49SGordon Ross 		 * [ from dochelp@ms ]
60294047d49SGordon Ross 		 *
60394047d49SGordon Ross 		 * By this point if there is a level II oplock present,
60494047d49SGordon Ross 		 * the caller can only be requesting an old-style oplock
60594047d49SGordon Ross 		 * because we rejected enhanced oplock requests above.
60694047d49SGordon Ross 		 * If the caller is requesting an old-style oplock our
60794047d49SGordon Ross 		 * caller already verfied that there is only one handle
60894047d49SGordon Ross 		 * open to this stream, and we've already verified that
60994047d49SGordon Ross 		 * this request is for a legacy oplock, meaning that there
61094047d49SGordon Ross 		 * can be at most one level II oplock (and no R oplocks),
61194047d49SGordon Ross 		 * and the level II oplock belongs to this handle.  Clear
61294047d49SGordon Ross 		 * the level II oplock and grant the exclusive oplock.
61394047d49SGordon Ross 		 */
61494047d49SGordon Ross 
61594047d49SGordon Ross 		/*
61694047d49SGordon Ross 		 * If Open.Stream.Oplock.State is equal to LEVEL_TWO_OPLOCK:
61794047d49SGordon Ross 		 * Remove the first Open ThisOpen from
61894047d49SGordon Ross 		 *  Open.Stream.Oplock.IIOplocks (there is supposed to be
61994047d49SGordon Ross 		 * exactly one present), and notify the server of an
62094047d49SGordon Ross 		 * oplock break according to the algorithm in section
62194047d49SGordon Ross 		 *, setting the algorithm's parameters as follows:
62294047d49SGordon Ross 		 *	BreakingOplockOpen = ThisOpen.
62394047d49SGordon Ross 		 *	NewOplockLevel = LEVEL_NONE.
62494047d49SGordon Ross 		 *	AcknowledgeRequired = FALSE.
62594047d49SGordon Ross 		 *	OplockCompletionStatus = STATUS_SUCCESS.
62694047d49SGordon Ross 		 * (The operation does not end at this point; this call
62794047d49SGordon Ross 		 *  to completes some earlier call to
62894047d49SGordon Ross 		 *
62994047d49SGordon Ross 		 * Implementation specific:
63094047d49SGordon Ross 		 *
63194047d49SGordon Ross 		 * As explained above, the passed in ofile should be the
63294047d49SGordon Ross 		 * only open file on this node.  Out of caution, we'll
63394047d49SGordon Ross 		 * walk the ofile list as usual here, making sure there
63494047d49SGordon Ross 		 * are no LevelII oplocks remaining, as those may not
63594047d49SGordon Ross 		 * coexist with the exclusive oplock were're creating
63694047d49SGordon Ross 		 * in this call.  Also, if the passed in ofile has a
63794047d49SGordon Ross 		 * LevelII oplock, don't do an "ind break" up call on
63894047d49SGordon Ross 		 * this ofile, as that would just cause an immediate
63994047d49SGordon Ross 		 * "break to none" of the oplock we'll grant here.
64094047d49SGordon Ross 		 * If there were other ofiles with LevelII oplocks,
64194047d49SGordon Ross 		 * it would be appropriate to "ind break" those.
64294047d49SGordon Ross 		 */
64394047d49SGordon Ross 		if ((node->n_oplock.ol_state & LEVEL_TWO) != 0) {
64494047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
64594047d49SGordon Ross 				if (o->f_oplock.onlist_II == 0)
64694047d49SGordon Ross 					continue;
64794047d49SGordon Ross 				o->f_oplock.onlist_II = B_FALSE;
64894047d49SGordon Ross 				node->n_oplock.cnt_II--;
64994047d49SGordon Ross 				ASSERT(node->n_oplock.cnt_II >= 0);
65094047d49SGordon Ross 				if (o == ofile)
65194047d49SGordon Ross 					continue;
65294047d49SGordon Ross 				DTRACE_PROBE1(unexpected, smb_ofile_t, o);
65394047d49SGordon Ross 				smb_oplock_ind_break(o,
65494047d49SGordon Ross 				    LEVEL_NONE, B_FALSE,
65594047d49SGordon Ross 				    NT_STATUS_SUCCESS);
65694047d49SGordon Ross 			}
65794047d49SGordon Ross 		}
65894047d49SGordon Ross 
65994047d49SGordon Ross 		/*
66094047d49SGordon Ross 		 * Note the spec. had an extra "EndIf" here.
66194047d49SGordon Ross 		 * Confirmed by dochelp@ms
66294047d49SGordon Ross 		 */
66394047d49SGordon Ross 
66494047d49SGordon Ross 		/*
66594047d49SGordon Ross 		 * If Open.File.OpenList contains more than one Open whose
66694047d49SGordon Ross 		 * Stream is the same as Open.Stream, and NO_OPLOCK is present
66794047d49SGordon Ross 		 * in Open.Stream.Oplock.State, the operation MUST be failed
66894047d49SGordon Ross 		 * with Status set to STATUS_OPLOCK_NOT_GRANTED.
66994047d49SGordon Ross 		 *
67094047d49SGordon Ross 		 * Implementation specific:
67194047d49SGordon Ross 		 * Allow other opens if they have the same lease ours,
67294047d49SGordon Ross 		 * so we can upgrade RH to RWH (for example). Therefore
67394047d49SGordon Ross 		 * only count opens with a different TargetOplockKey.
67494047d49SGordon Ross 		 * Also ignore "attribute-only" opens.
67594047d49SGordon Ross 		 */
67694047d49SGordon Ross 		if ((node->n_oplock.ol_state & NO_OPLOCK) != 0) {
67794047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
67894047d49SGordon Ross 				if (!smb_ofile_is_open(o))
67994047d49SGordon Ross 					continue;
68094047d49SGordon Ross 				if ((o->f_granted_access & FILE_DATA_ALL) == 0)
68194047d49SGordon Ross 					continue;
68294047d49SGordon Ross 				if (!CompareOplockKeys(ofile, o, 0)) {
68394047d49SGordon Ross 					status = NT_STATUS_OPLOCK_NOT_GRANTED;
68494047d49SGordon Ross 					goto out;
68594047d49SGordon Ross 				}
68694047d49SGordon Ross 			}
68794047d49SGordon Ross 		}
68894047d49SGordon Ross 
68994047d49SGordon Ross 		/*
69094047d49SGordon Ross 		 * If Open.Stream.IsDeleted is TRUE and RequestedOplock
69194047d49SGordon Ross 		 * contains HANDLE_CACHING, the operation MUST be failed
69294047d49SGordon Ross 		 * with Status set to STATUS_OPLOCK_NOT_GRANTED.
69394047d49SGordon Ross 		 */
69494047d49SGordon Ross 		if (((node->flags & NODE_FLAGS_DELETING) != 0) &&
69594047d49SGordon Ross 		    (*rop & HANDLE_CACHING) != 0) {
69694047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
69794047d49SGordon Ross 			goto out;
69894047d49SGordon Ross 		}
69994047d49SGordon Ross 
70094047d49SGordon Ross 		/* Set GrantExclusiveOplock to TRUE. */
70194047d49SGordon Ross 		GrantExcl = B_TRUE;
70294047d49SGordon Ross 	}
70394047d49SGordon Ross 
70494047d49SGordon Ross 	/*
70594047d49SGordon Ross 	 * "Else" If (Open.Stream.Oplock.State contains one or more of
70694047d49SGordon Ross 	 * READ_CACHING, WRITE_CACHING, or HANDLE_CACHING) and
70794047d49SGordon Ross 	 * (Open.Stream.Oplock.State contains none of (BREAK_ANY)) and
70894047d49SGordon Ross 	 * (Open.Stream.Oplock.RHBreakQueue is empty):
70994047d49SGordon Ross 	 */
71094047d49SGordon Ross 	else if ((node->n_oplock.ol_state & CACHE_RWH) != 0 &&
71194047d49SGordon Ross 	    (node->n_oplock.ol_state & BREAK_ANY) == 0 &&
71294047d49SGordon Ross 	    node->n_oplock.cnt_RHBQ == 0) {
71394047d49SGordon Ross 
71494047d49SGordon Ross 		/*
71594047d49SGordon Ross 		 * This is a granular oplock and it is not breaking.
71694047d49SGordon Ross 		 */
71794047d49SGordon Ross 
71894047d49SGordon Ross 		/*
71994047d49SGordon Ross 		 * If RequestedOplock contains none of READ_CACHING,
72094047d49SGordon Ross 		 * WRITE_CACHING, or HANDLE_CACHING, the operation
72194047d49SGordon Ross 		 * MUST be failed with Status set to
72294047d49SGordon Ross 		 * STATUS_OPLOCK_NOT_GRANTED.
72394047d49SGordon Ross 		 */
72494047d49SGordon Ross 		if ((*rop & CACHE_RWH) == 0) {
72594047d49SGordon Ross 			status = NT_STATUS_OPLOCK_NOT_GRANTED;
72694047d49SGordon Ross 			goto out;
72794047d49SGordon Ross 		}
72894047d49SGordon Ross 
72994047d49SGordon Ross 		/*
73094047d49SGordon Ross 		 * If Open.Stream.IsDeleted (already checked above)
73194047d49SGordon Ross 		 */
73294047d49SGordon Ross 
73394047d49SGordon Ross 		/*
73494047d49SGordon Ross 		 * Switch (Open.Stream.Oplock.State):
73594047d49SGordon Ross 		 */
73694047d49SGordon Ross 		switch (node->n_oplock.ol_state) {
73794047d49SGordon Ross 
73894047d49SGordon Ross 		case CACHE_R:
73994047d49SGordon Ross 			/*
74094047d49SGordon Ross 			 * If RequestedOplock is neither
74194047d49SGordon Ross 			 * (READ_CACHING|WRITE_CACHING) nor
74394047d49SGordon Ross 			 * the operation MUST be failed with Status set
74494047d49SGordon Ross 			 * to STATUS_OPLOCK_NOT_GRANTED.
74594047d49SGordon Ross 			 */
74694047d49SGordon Ross 			if (*rop != CACHE_RW && *rop != CACHE_RWH) {
74794047d49SGordon Ross 				status = NT_STATUS_OPLOCK_NOT_GRANTED;
74894047d49SGordon Ross 				goto out;
74994047d49SGordon Ross 			}
75094047d49SGordon Ross 
75194047d49SGordon Ross 			/*
75294047d49SGordon Ross 			 * For each Open ThisOpen in
75394047d49SGordon Ross 			 *  Open.Stream.Oplock.ROplocks:
75494047d49SGordon Ross 			 *	If ThisOpen.TargetOplockKey !=
75594047d49SGordon Ross 			 *	Open.TargetOplockKey, the operation
75694047d49SGordon Ross 			 *	MUST be failed with Status set to
75794047d49SGordon Ross 			 *	STATUS_OPLOCK_NOT_GRANTED.
75894047d49SGordon Ross 			 * EndFor
75994047d49SGordon Ross 			 */
76094047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
76194047d49SGordon Ross 				if (o->f_oplock.onlist_R == 0)
76294047d49SGordon Ross 					continue;
76394047d49SGordon Ross 				if (!CompareOplockKeys(ofile, o, 0)) {
76494047d49SGordon Ross 					status = NT_STATUS_OPLOCK_NOT_GRANTED;
76594047d49SGordon Ross 					goto out;
76694047d49SGordon Ross 				}
76794047d49SGordon Ross 			}
76894047d49SGordon Ross 
76994047d49SGordon Ross 			/*
77094047d49SGordon Ross 			 * For each Open o in Open.Stream.Oplock.ROplocks:
77194047d49SGordon Ross 			 *	Remove o from Open.Stream.Oplock.ROplocks.
77294047d49SGordon Ross 			 *	Notify the server of an oplock break
77394047d49SGordon Ross 			 *	according to the algorithm in section
77494047d49SGordon Ross 			 *, setting the algorithm's
77594047d49SGordon Ross 			 *	parameters as follows:
77694047d49SGordon Ross 			 *		BreakingOplockOpen = o.
77794047d49SGordon Ross 			 *		NewOplockLevel = RequestedOplock.
77894047d49SGordon Ross 			 *		AcknowledgeRequired = FALSE.
77994047d49SGordon Ross 			 *		OplockCompletionStatus =
78094047d49SGordon Ross 			 *		  STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
78194047d49SGordon Ross 			 *	(The operation does not end at this point;
78294047d49SGordon Ross 			 *	 this call to completes some
78394047d49SGordon Ross 			 *	 earlier call to
78494047d49SGordon Ross 			 * EndFor
78594047d49SGordon Ross 			 *
78694047d49SGordon Ross 			 * Note: Upgrade to excl. on same lease.
78794047d49SGordon Ross 			 * Won't send a break for this.
78894047d49SGordon Ross 			 */
78994047d49SGordon Ross 			FOREACH_NODE_OFILE(node, o) {
79094047d49SGordon Ross 				if (o->f_oplock.onlist_R == 0)
79194047d49SGordon Ross 					continue;
79294047d49SGordon Ross 				o->f_oplock.onlist_R = B_FALSE;
79394047d49SGordon Ross 				node->n_oplock.cnt_R--;
79494047d49SGordon Ross 				ASSERT(node->n_oplock.cnt_R >= 0);
79594047d49SGordon Ross 
79694047d49SGordon Ross 				smb_oplock_ind_break(o, *rop,
79794047d49SGordon Ross 				    B_FALSE, STATUS_NEW_HANDLE);
79894047d49SGordon Ross 			}
79994047d49SGordon Ross 			/*
80094047d49SGordon Ross 			 * Set GrantExclusiveOplock to TRUE.
80194047d49SGordon Ross 			 * EndCase // _R
80294047d49SGordon Ross 			 */
80394047d49SGordon Ross 			GrantExcl = B_TRUE;
80494047d49SGordon Ross 			break;
80594047d49SGordon Ross 
80694047d49SGordon Ross 		case CACHE_RH:
80794047d49SGordon Ross 			/*
80894047d49SGordon Ross 			 * If RequestedOplock is not