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