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
4094047d49SGordon Ross #define NODE_FLAGS_DELETING (NODE_FLAGS_DELETE_ON_CLOSE |\
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] 2.1.4.12.2 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 2.1.5.17.
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 * 2.1.4.13 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
19394047d49SGordon Ross * (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH).
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
26294047d49SGordon Ross * (READ_CACHING|HANDLE_CACHING|BREAK_TO_READ_CACHING).
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
27594047d49SGordon Ross * (READ_CACHING|HANDLE_CACHING|BREAK_TO_NO_CACHING).
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] 2.1.5.17 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 2.1.5.17.1, 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 2.1.5.17.2, 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 2.1.5.17.2, 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
45794047d49SGordon Ross * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING):
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 2.1.5.17.1, 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 * 2.1.5.17.1 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 2.1.5.18. 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 * 2.1.5.17.3, 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 2.1.5.17.3 completes some earlier call to 2.1.5.17.2.)
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
74294047d49SGordon Ross * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING),
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 * 2.1.5.17.3, 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 2.1.5.17.3 completes some
78394047d49SGordon Ross * earlier call to 2.1.5.17.2.)
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
80994047d49SGordon Ross * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING)
810