1a90cf9f2SGordon Ross /*
2a90cf9f2SGordon Ross * This file and its contents are supplied under the terms of the
3a90cf9f2SGordon Ross * Common Development and Distribution License ("CDDL"), version 1.0.
4a90cf9f2SGordon Ross * You may only use this file in accordance with the terms of version
5a90cf9f2SGordon Ross * 1.0 of the CDDL.
6a90cf9f2SGordon Ross *
7a90cf9f2SGordon Ross * A full copy of the text of the CDDL should have accompanied this
8a90cf9f2SGordon Ross * source. A copy of the CDDL is also available via the Internet at
9a90cf9f2SGordon Ross * http://www.illumos.org/license/CDDL.
10a90cf9f2SGordon Ross */
11a90cf9f2SGordon Ross
12a90cf9f2SGordon Ross /*
13525641e8SGordon Ross * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
1472b35b05SGordon Ross * Copyright 2022 RackTop Systems, Inc.
15a90cf9f2SGordon Ross */
16a90cf9f2SGordon Ross
17a90cf9f2SGordon Ross /*
18a90cf9f2SGordon Ross * Dispatch function for SMB2_OPLOCK_BREAK
19a90cf9f2SGordon Ross */
20a90cf9f2SGordon Ross
21a90cf9f2SGordon Ross #include <smbsrv/smb2_kproto.h>
2272b35b05SGordon Ross #include <smbsrv/smb_oplock.h>
23a90cf9f2SGordon Ross
2494047d49SGordon Ross #define BATCH_OR_EXCL (OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE)
2594047d49SGordon Ross
2694047d49SGordon Ross /* StructSize for the two "break" message formats. */
2794047d49SGordon Ross #define SSZ_OPLOCK 24
2894047d49SGordon Ross #define SSZ_LEASE 36
2994047d49SGordon Ross
30a90cf9f2SGordon Ross /*
31a90cf9f2SGordon Ross * SMB2 Oplock Break Acknowledgement
3294047d49SGordon Ross * [MS-SMB2] 3.3.5.22.1 Processing an Oplock Acknowledgment
3394047d49SGordon Ross * Called via smb2_disp_table[]
3472b35b05SGordon Ross * This is an "Ack" from the client.
35a90cf9f2SGordon Ross */
36a90cf9f2SGordon Ross smb_sdrc_t
smb2_oplock_break_ack(smb_request_t * sr)37a90cf9f2SGordon Ross smb2_oplock_break_ack(smb_request_t *sr)
38a90cf9f2SGordon Ross {
3972b35b05SGordon Ross smb_arg_olbrk_t *olbrk = &sr->arg.olbrk;
4072b35b05SGordon Ross smb_node_t *node;
4194047d49SGordon Ross smb_ofile_t *ofile;
4272b35b05SGordon Ross smb_oplock_grant_t *og;
43a90cf9f2SGordon Ross smb2fid_t smb2fid;
44a90cf9f2SGordon Ross uint32_t status;
4594047d49SGordon Ross uint32_t NewLevel;
4694047d49SGordon Ross uint8_t smbOplockLevel;
47a90cf9f2SGordon Ross int rc = 0;
4894047d49SGordon Ross uint16_t StructSize;
4994047d49SGordon Ross
5094047d49SGordon Ross /*
5194047d49SGordon Ross * Decode the SMB2 Oplock Break Ack (24 bytes) or
5294047d49SGordon Ross * Lease Break Ack (36 bytes), starting with just
5394047d49SGordon Ross * the StructSize, which tells us what this is.
5494047d49SGordon Ross */
5594047d49SGordon Ross rc = smb_mbc_decodef(&sr->smb_data, "w", &StructSize);
5694047d49SGordon Ross if (rc != 0)
5794047d49SGordon Ross return (SDRC_ERROR);
5894047d49SGordon Ross
5994047d49SGordon Ross if (StructSize == SSZ_LEASE) {
6094047d49SGordon Ross /* See smb2_lease.c */
6194047d49SGordon Ross return (smb2_lease_break_ack(sr));
6294047d49SGordon Ross }
6394047d49SGordon Ross if (StructSize != SSZ_OPLOCK)
6494047d49SGordon Ross return (SDRC_ERROR);
65a90cf9f2SGordon Ross
66a90cf9f2SGordon Ross /*
6794047d49SGordon Ross * Decode an SMB2 Oplock Break Ack.
6894047d49SGordon Ross * [MS-SMB2] 2.2.24.1
6994047d49SGordon Ross * Note: Struct size decoded above.
70a90cf9f2SGordon Ross */
71a90cf9f2SGordon Ross rc = smb_mbc_decodef(
7294047d49SGordon Ross &sr->smb_data, "b5.qq",
7394047d49SGordon Ross &smbOplockLevel, /* b */
74a90cf9f2SGordon Ross /* reserved 5. */
75a90cf9f2SGordon Ross &smb2fid.persistent, /* q */
76a90cf9f2SGordon Ross &smb2fid.temporal); /* q */
7794047d49SGordon Ross if (rc != 0)
78a90cf9f2SGordon Ross return (SDRC_ERROR);
79a90cf9f2SGordon Ross
80a90cf9f2SGordon Ross /*
8172b35b05SGordon Ross * Convert SMB oplock level to internal form.
82a90cf9f2SGordon Ross */
8394047d49SGordon Ross switch (smbOplockLevel) {
84a90cf9f2SGordon Ross case SMB2_OPLOCK_LEVEL_NONE: /* 0x00 */
8594047d49SGordon Ross NewLevel = OPLOCK_LEVEL_NONE;
86a90cf9f2SGordon Ross break;
87a90cf9f2SGordon Ross case SMB2_OPLOCK_LEVEL_II: /* 0x01 */
8894047d49SGordon Ross NewLevel = OPLOCK_LEVEL_TWO;
89a90cf9f2SGordon Ross break;
90a90cf9f2SGordon Ross case SMB2_OPLOCK_LEVEL_EXCLUSIVE: /* 0x08 */
9194047d49SGordon Ross NewLevel = OPLOCK_LEVEL_ONE;
9294047d49SGordon Ross break;
93a90cf9f2SGordon Ross case SMB2_OPLOCK_LEVEL_BATCH: /* 0x09 */
9494047d49SGordon Ross NewLevel = OPLOCK_LEVEL_BATCH;
9594047d49SGordon Ross break;
9672b35b05SGordon Ross
9772b35b05SGordon Ross /* Note: _LEVEL_LEASE is not valid here. */
98a90cf9f2SGordon Ross case SMB2_OPLOCK_LEVEL_LEASE: /* 0xFF */
99dde7ba52SGordon Ross default:
10072b35b05SGordon Ross /*
10172b35b05SGordon Ross * Impossible NewLevel here, will cause
10272b35b05SGordon Ross * NT_STATUS_INVALID_PARAMETER below.
10372b35b05SGordon Ross */
10472b35b05SGordon Ross NewLevel = OPLOCK_LEVEL_GRANULAR;
10572b35b05SGordon Ross break;
10672b35b05SGordon Ross }
10772b35b05SGordon Ross
10872b35b05SGordon Ross /* for dtrace */
10972b35b05SGordon Ross olbrk->NewLevel = NewLevel;
11072b35b05SGordon Ross
11172b35b05SGordon Ross /* Find the ofile */
11272b35b05SGordon Ross status = smb2sr_lookup_fid(sr, &smb2fid);
11372b35b05SGordon Ross /* Success or NT_STATUS_FILE_CLOSED */
11472b35b05SGordon Ross
11572b35b05SGordon Ross DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr);
11672b35b05SGordon Ross
11772b35b05SGordon Ross if (status != 0) {
11872b35b05SGordon Ross /* lookup fid failed */
11972b35b05SGordon Ross goto errout;
12072b35b05SGordon Ross }
12172b35b05SGordon Ross
12272b35b05SGordon Ross if (NewLevel == OPLOCK_LEVEL_GRANULAR) {
12372b35b05SGordon Ross /* Switch above got invalid smbOplockLevel */
124dde7ba52SGordon Ross status = NT_STATUS_INVALID_PARAMETER;
125dde7ba52SGordon Ross goto errout;
12694047d49SGordon Ross }
12794047d49SGordon Ross
12872b35b05SGordon Ross /* Success, so have sr->fid_ofile */
12994047d49SGordon Ross ofile = sr->fid_ofile;
13072b35b05SGordon Ross og = &ofile->f_oplock;
13172b35b05SGordon Ross node = ofile->f_node;
13272b35b05SGordon Ross
13372b35b05SGordon Ross smb_llist_enter(&node->n_ofile_list, RW_READER);
13472b35b05SGordon Ross mutex_enter(&node->n_oplock.ol_mutex);
13572b35b05SGordon Ross
1367f6a299eSGordon Ross if (og->og_breaking == B_FALSE) {
137dde7ba52SGordon Ross /*
138dde7ba52SGordon Ross * This is an unsolicited Ack. (There is no
139dde7ba52SGordon Ross * outstanding oplock break in progress now.)
140dde7ba52SGordon Ross * There are WPTS tests that care which error
141dde7ba52SGordon Ross * is returned. See [MS-SMB2] 3.3.5.22.1
142dde7ba52SGordon Ross */
14372b35b05SGordon Ross if (NewLevel >= (og->og_state & OPLOCK_LEVEL_TYPE_MASK)) {
144dde7ba52SGordon Ross status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
14572b35b05SGordon Ross goto unlock_out;
146dde7ba52SGordon Ross }
147dde7ba52SGordon Ross status = NT_STATUS_INVALID_DEVICE_STATE;
14872b35b05SGordon Ross goto unlock_out;
149dde7ba52SGordon Ross }
15072b35b05SGordon Ross
15172b35b05SGordon Ross /*
15272b35b05SGordon Ross * Process the oplock break ack.
15372b35b05SGordon Ross *
15472b35b05SGordon Ross * Clear breaking flags before we ack,
15572b35b05SGordon Ross * because ack might set those.
15672b35b05SGordon Ross */
1577f6a299eSGordon Ross ofile->f_oplock.og_breaking = B_FALSE;
15872b35b05SGordon Ross cv_broadcast(&ofile->f_oplock.og_ack_cv);
159dde7ba52SGordon Ross
16094047d49SGordon Ross status = smb_oplock_ack_break(sr, ofile, &NewLevel);
161a90cf9f2SGordon Ross
16294047d49SGordon Ross ofile->f_oplock.og_state = NewLevel;
16372b35b05SGordon Ross if (ofile->dh_persist)
16472b35b05SGordon Ross smb2_dh_update_oplock(sr, ofile);
16572b35b05SGordon Ross
16672b35b05SGordon Ross unlock_out:
16772b35b05SGordon Ross mutex_exit(&node->n_oplock.ol_mutex);
16872b35b05SGordon Ross smb_llist_exit(&node->n_ofile_list);
16972b35b05SGordon Ross
17072b35b05SGordon Ross errout:
17172b35b05SGordon Ross sr->smb2_status = status;
17272b35b05SGordon Ross DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr);
17372b35b05SGordon Ross if (status) {
17472b35b05SGordon Ross smb2sr_put_error(sr, status);
17572b35b05SGordon Ross return (SDRC_SUCCESS);
17672b35b05SGordon Ross }
17772b35b05SGordon Ross
17872b35b05SGordon Ross /*
17972b35b05SGordon Ross * Convert internal oplock state back to SMB form.
18072b35b05SGordon Ross */
18194047d49SGordon Ross switch (NewLevel & OPLOCK_LEVEL_TYPE_MASK) {
18294047d49SGordon Ross case OPLOCK_LEVEL_NONE:
18394047d49SGordon Ross smbOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
18494047d49SGordon Ross break;
18594047d49SGordon Ross case OPLOCK_LEVEL_TWO:
18694047d49SGordon Ross smbOplockLevel = SMB2_OPLOCK_LEVEL_II;
18794047d49SGordon Ross break;
18894047d49SGordon Ross case OPLOCK_LEVEL_ONE:
18994047d49SGordon Ross smbOplockLevel = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
19094047d49SGordon Ross break;
19194047d49SGordon Ross case OPLOCK_LEVEL_BATCH:
19294047d49SGordon Ross smbOplockLevel = SMB2_OPLOCK_LEVEL_BATCH;
19394047d49SGordon Ross break;
19494047d49SGordon Ross case OPLOCK_LEVEL_GRANULAR:
19594047d49SGordon Ross default:
19694047d49SGordon Ross smbOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
19794047d49SGordon Ross break;
19894047d49SGordon Ross }
199a90cf9f2SGordon Ross
200a90cf9f2SGordon Ross /*
20194047d49SGordon Ross * Encode an SMB2 Oplock Break Ack response
20294047d49SGordon Ross * [MS-SMB2] 2.2.25.1
203a90cf9f2SGordon Ross */
204a90cf9f2SGordon Ross (void) smb_mbc_encodef(
205a90cf9f2SGordon Ross &sr->reply, "wb5.qq",
20694047d49SGordon Ross SSZ_OPLOCK, /* w */
20794047d49SGordon Ross smbOplockLevel, /* b */
208a90cf9f2SGordon Ross /* reserved 5. */
209a90cf9f2SGordon Ross smb2fid.persistent, /* q */
210a90cf9f2SGordon Ross smb2fid.temporal); /* q */
21194047d49SGordon Ross
212a90cf9f2SGordon Ross return (SDRC_SUCCESS);
213a90cf9f2SGordon Ross }
214a90cf9f2SGordon Ross
215a90cf9f2SGordon Ross /*
216a90cf9f2SGordon Ross * Compose an SMB2 Oplock Break Notification packet, including
217a90cf9f2SGordon Ross * the SMB2 header and everything, in sr->reply.
218a90cf9f2SGordon Ross * The caller will send it and free the request.
219a90cf9f2SGordon Ross */
22072b35b05SGordon Ross static void
smb2_oplock_break_notification(smb_request_t * sr,uint32_t NewLevel)22194047d49SGordon Ross smb2_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel)
222a90cf9f2SGordon Ross {
223a90cf9f2SGordon Ross smb_ofile_t *ofile = sr->fid_ofile;
224a90cf9f2SGordon Ross smb2fid_t smb2fid;
225a90cf9f2SGordon Ross uint16_t StructSize;
226a90cf9f2SGordon Ross uint8_t OplockLevel;
227a90cf9f2SGordon Ross
22894047d49SGordon Ross /*
22994047d49SGordon Ross * Convert internal level to SMB2
23094047d49SGordon Ross */
23194047d49SGordon Ross switch (NewLevel) {
232a90cf9f2SGordon Ross default:
233a90cf9f2SGordon Ross ASSERT(0);
234a90cf9f2SGordon Ross /* FALLTHROUGH */
23594047d49SGordon Ross case OPLOCK_LEVEL_NONE:
236a90cf9f2SGordon Ross OplockLevel = SMB2_OPLOCK_LEVEL_NONE;
237a90cf9f2SGordon Ross break;
23894047d49SGordon Ross case OPLOCK_LEVEL_TWO:
239a90cf9f2SGordon Ross OplockLevel = SMB2_OPLOCK_LEVEL_II;
240a90cf9f2SGordon Ross break;
241a90cf9f2SGordon Ross }
242a90cf9f2SGordon Ross
243a90cf9f2SGordon Ross /*
244a90cf9f2SGordon Ross * SMB2 Header
245a90cf9f2SGordon Ross */
246a90cf9f2SGordon Ross sr->smb2_cmd_code = SMB2_OPLOCK_BREAK;
247a90cf9f2SGordon Ross sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR;
248811599a4SMatt Barden sr->smb_tid = 0;
249a90cf9f2SGordon Ross sr->smb_pid = 0;
250811599a4SMatt Barden sr->smb2_ssnid = 0;
251a90cf9f2SGordon Ross sr->smb2_messageid = UINT64_MAX;
252a90cf9f2SGordon Ross (void) smb2_encode_header(sr, B_FALSE);
253a90cf9f2SGordon Ross
254a90cf9f2SGordon Ross /*
255a90cf9f2SGordon Ross * SMB2 Oplock Break, variable part
256a90cf9f2SGordon Ross */
257a90cf9f2SGordon Ross StructSize = 24;
258811599a4SMatt Barden smb2fid.persistent = ofile->f_persistid;
259a90cf9f2SGordon Ross smb2fid.temporal = ofile->f_fid;
260a90cf9f2SGordon Ross (void) smb_mbc_encodef(
261a90cf9f2SGordon Ross &sr->reply, "wb5.qq",
262a90cf9f2SGordon Ross StructSize, /* w */
263a90cf9f2SGordon Ross OplockLevel, /* b */
264a90cf9f2SGordon Ross /* reserved 5. */
265a90cf9f2SGordon Ross smb2fid.persistent, /* q */
266a90cf9f2SGordon Ross smb2fid.temporal); /* q */
267a90cf9f2SGordon Ross }
26894047d49SGordon Ross
26972b35b05SGordon Ross /*
27072b35b05SGordon Ross * Send an oplock break over the wire, or if we can't,
27172b35b05SGordon Ross * then process the oplock break locally.
27272b35b05SGordon Ross *
27372b35b05SGordon Ross * [MS-SMB2] 3.3.4.6 Object Store Indicates an Oplock Break
27472b35b05SGordon Ross *
27572b35b05SGordon Ross * Note: When "AckRequired" is set, and we're for any reason
27672b35b05SGordon Ross * unable to communicate with the client so that they do an
27772b35b05SGordon Ross * "oplock break ACK", then we absolutely MUST do a local ACK
27872b35b05SGordon Ross * for this break indication (or close the ofile).
27972b35b05SGordon Ross *
28072b35b05SGordon Ross * The file-system level oplock code (smb_cmn_oplock.c)
28172b35b05SGordon Ross * requires these ACK calls to clear "breaking" flags.
28272b35b05SGordon Ross *
28372b35b05SGordon Ross * This is called either from smb_oplock_async_break via a
28472b35b05SGordon Ross * taskq job scheduled in smb_oplock_ind_break, or from the
28572b35b05SGordon Ross * smb2sr_append_postwork() mechanism when we're doing a
28672b35b05SGordon Ross * "break in ack", via smb_oplock_ind_break_in_ack.
28772b35b05SGordon Ross *
28872b35b05SGordon Ross * This runs much like other smb_request_t handlers, in the
28972b35b05SGordon Ross * context of a worker task that calls with no locks held.
29072b35b05SGordon Ross *
29172b35b05SGordon Ross * Note that we have sr->fid_ofile here but all the other
29272b35b05SGordon Ross * normal sr members may be NULL: uid_user, tid_tree.
29372b35b05SGordon Ross * Also sr->session may or may not be the same session as
29472b35b05SGordon Ross * the ofile came from (ofile->f_session) depending on
29572b35b05SGordon Ross * whether this is a "live" open or an orphaned DH,
29672b35b05SGordon Ross * where ofile->f_session will be NULL.
29772b35b05SGordon Ross */
29872b35b05SGordon Ross void
smb2_oplock_send_break(smb_request_t * sr)29972b35b05SGordon Ross smb2_oplock_send_break(smb_request_t *sr)
30072b35b05SGordon Ross {
30172b35b05SGordon Ross smb_ofile_t *ofile = sr->fid_ofile;
30272b35b05SGordon Ross smb_node_t *node = ofile->f_node;
30372b35b05SGordon Ross uint32_t NewLevel = sr->arg.olbrk.NewLevel;
30472b35b05SGordon Ross boolean_t AckReq = sr->arg.olbrk.AckRequired;
30572b35b05SGordon Ross uint32_t status;
30672b35b05SGordon Ross int rc;
30772b35b05SGordon Ross
30872b35b05SGordon Ross /*
30972b35b05SGordon Ross * Build the break message in sr->reply.
31072b35b05SGordon Ross * It's free'd in smb_request_free().
31172b35b05SGordon Ross * Always SMB2 oplock here (no lease)
31272b35b05SGordon Ross */
31372b35b05SGordon Ross sr->reply.max_bytes = MLEN;
31472b35b05SGordon Ross smb2_oplock_break_notification(sr, NewLevel);
31572b35b05SGordon Ross
31672b35b05SGordon Ross /*
31772b35b05SGordon Ross * Try to send the break message to the client.
31872b35b05SGordon Ross * If connected, this IF body will be true.
31972b35b05SGordon Ross */
32072b35b05SGordon Ross if (sr->session == ofile->f_session)
32172b35b05SGordon Ross rc = smb_session_send(sr->session, 0, &sr->reply);
32272b35b05SGordon Ross else
32372b35b05SGordon Ross rc = ENOTCONN;
32472b35b05SGordon Ross
32572b35b05SGordon Ross if (rc != 0) {
32672b35b05SGordon Ross /*
32772b35b05SGordon Ross * We were unable to send the oplock break request,
32872b35b05SGordon Ross * presumably because the connection is gone.
32972b35b05SGordon Ross *
33072b35b05SGordon Ross * [MS-SMB2] 3.3.4.6 Object Store Indicates an Oplock Break
33172b35b05SGordon Ross * If no connection is available, Open.IsResilient is FALSE,
33272b35b05SGordon Ross * Open.IsDurable is FALSE, and Open.IsPersistent is FALSE,
33372b35b05SGordon Ross * the server SHOULD close the Open as specified in...
33472b35b05SGordon Ross */
33572b35b05SGordon Ross if (ofile->dh_persist == B_FALSE &&
33672b35b05SGordon Ross ofile->dh_vers != SMB2_RESILIENT &&
33772b35b05SGordon Ross (ofile->dh_vers == SMB2_NOT_DURABLE ||
33872b35b05SGordon Ross (NewLevel & OPLOCK_LEVEL_BATCH) == 0)) {
33972b35b05SGordon Ross smb_ofile_close(ofile, 0);
34072b35b05SGordon Ross return;
34172b35b05SGordon Ross }
34272b35b05SGordon Ross /* Keep this (durable) open. */
34372b35b05SGordon Ross if (!AckReq)
34472b35b05SGordon Ross return;
34572b35b05SGordon Ross /* Do local Ack below. */
34672b35b05SGordon Ross } else {
34772b35b05SGordon Ross /*
34872b35b05SGordon Ross * OK, we were able to send the break message.
34972b35b05SGordon Ross * If no ack. required, we're done.
35072b35b05SGordon Ross */
35172b35b05SGordon Ross if (!AckReq)
35272b35b05SGordon Ross return;
35372b35b05SGordon Ross
35472b35b05SGordon Ross /*
35572b35b05SGordon Ross * We're expecting an ACK. Wait in this thread
35672b35b05SGordon Ross * so we can log clients that don't respond.
357*3b24312dSGordon Ross * Note: this can also fail for other reasons
358*3b24312dSGordon Ross * such as client disconnect or server shutdown.
35972b35b05SGordon Ross */
3607f6a299eSGordon Ross status = smb_oplock_wait_ack(sr, NewLevel);
36172b35b05SGordon Ross if (status == 0)
36272b35b05SGordon Ross return;
36372b35b05SGordon Ross
364*3b24312dSGordon Ross DTRACE_PROBE2(wait__ack__failed, smb_request_t *, sr,
365*3b24312dSGordon Ross uint32_t, status);
36672b35b05SGordon Ross
36772b35b05SGordon Ross /*
36872b35b05SGordon Ross * Will do local ack below. Note, after timeout,
36972b35b05SGordon Ross * do a break to none or "no caching" regardless
37072b35b05SGordon Ross * of what the passed in cache level was.
37172b35b05SGordon Ross */
37272b35b05SGordon Ross NewLevel = OPLOCK_LEVEL_NONE;
37372b35b05SGordon Ross }
37472b35b05SGordon Ross
37572b35b05SGordon Ross /*
37672b35b05SGordon Ross * Do the ack locally.
37772b35b05SGordon Ross */
37872b35b05SGordon Ross smb_llist_enter(&node->n_ofile_list, RW_READER);
37972b35b05SGordon Ross mutex_enter(&node->n_oplock.ol_mutex);
38072b35b05SGordon Ross
3817f6a299eSGordon Ross ofile->f_oplock.og_breaking = B_FALSE;
38272b35b05SGordon Ross cv_broadcast(&ofile->f_oplock.og_ack_cv);
38372b35b05SGordon Ross
38472b35b05SGordon Ross status = smb_oplock_ack_break(sr, ofile, &NewLevel);
38572b35b05SGordon Ross
38672b35b05SGordon Ross ofile->f_oplock.og_state = NewLevel;
38772b35b05SGordon Ross if (ofile->dh_persist)
38872b35b05SGordon Ross smb2_dh_update_oplock(sr, ofile);
38972b35b05SGordon Ross
39072b35b05SGordon Ross mutex_exit(&node->n_oplock.ol_mutex);
39172b35b05SGordon Ross smb_llist_exit(&node->n_ofile_list);
39272b35b05SGordon Ross
39372b35b05SGordon Ross #ifdef DEBUG
39472b35b05SGordon Ross if (status != 0) {
39572b35b05SGordon Ross cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x",
39672b35b05SGordon Ross sr->session->ip_addr_str, status);
39772b35b05SGordon Ross }
39872b35b05SGordon Ross #endif
39972b35b05SGordon Ross }
40072b35b05SGordon Ross
40194047d49SGordon Ross /*
40294047d49SGordon Ross * Client has an open handle and requests an oplock.
40394047d49SGordon Ross * Convert SMB2 oplock request info in to internal form,
40494047d49SGordon Ross * call common oplock code, convert result to SMB2.
40594047d49SGordon Ross *
40694047d49SGordon Ross * If necessary, "go async" here.
40794047d49SGordon Ross */
40894047d49SGordon Ross void
smb2_oplock_acquire(smb_request_t * sr)40994047d49SGordon Ross smb2_oplock_acquire(smb_request_t *sr)
41094047d49SGordon Ross {
41194047d49SGordon Ross smb_arg_open_t *op = &sr->arg.open;
41294047d49SGordon Ross smb_ofile_t *ofile = sr->fid_ofile;
41394047d49SGordon Ross uint32_t status;
41494047d49SGordon Ross
41594047d49SGordon Ross /* Only disk trees get oplocks. */
41694047d49SGordon Ross ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE);
41794047d49SGordon Ross
41894047d49SGordon Ross /* Only plain files... */
41994047d49SGordon Ross if (!smb_node_is_file(ofile->f_node)) {
42094047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
42194047d49SGordon Ross return;
42294047d49SGordon Ross }
42394047d49SGordon Ross
42494047d49SGordon Ross if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
42594047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
42694047d49SGordon Ross return;
42794047d49SGordon Ross }
42894047d49SGordon Ross
42994047d49SGordon Ross /*
43094047d49SGordon Ross * SMB2: Convert to internal form.
43194047d49SGordon Ross */
43294047d49SGordon Ross switch (op->op_oplock_level) {
43394047d49SGordon Ross case SMB2_OPLOCK_LEVEL_BATCH:
43494047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_BATCH;
43594047d49SGordon Ross break;
43694047d49SGordon Ross case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
43794047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_ONE;
43894047d49SGordon Ross break;
43994047d49SGordon Ross case SMB2_OPLOCK_LEVEL_II:
44094047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_TWO;
44194047d49SGordon Ross break;
44294047d49SGordon Ross case SMB2_OPLOCK_LEVEL_LEASE:
44394047d49SGordon Ross ASSERT(0); /* Handled elsewhere */
44494047d49SGordon Ross /* FALLTHROUGH */
44594047d49SGordon Ross case SMB2_OPLOCK_LEVEL_NONE:
44694047d49SGordon Ross default:
44794047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
44894047d49SGordon Ross return;
44994047d49SGordon Ross }
45094047d49SGordon Ross
45194047d49SGordon Ross /*
45294047d49SGordon Ross * Tree options may force shared oplocks,
45394047d49SGordon Ross * in which case we reduce the request.
45472b35b05SGordon Ross * Can't get here with LEVEL_NONE, so
45572b35b05SGordon Ross * this can only decrease the level.
45694047d49SGordon Ross */
45794047d49SGordon Ross if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
45894047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_TWO;
45994047d49SGordon Ross }
46094047d49SGordon Ross
46194047d49SGordon Ross /*
46294047d49SGordon Ross * Try exclusive first, if requested
46394047d49SGordon Ross */
46494047d49SGordon Ross if ((op->op_oplock_state & BATCH_OR_EXCL) != 0) {
46594047d49SGordon Ross status = smb_oplock_request(sr, ofile,
46694047d49SGordon Ross &op->op_oplock_state);
46794047d49SGordon Ross } else {
46894047d49SGordon Ross status = NT_STATUS_OPLOCK_NOT_GRANTED;
46994047d49SGordon Ross }
47094047d49SGordon Ross
47194047d49SGordon Ross /*
47294047d49SGordon Ross * If exclusive failed (or the tree forced shared oplocks)
47394047d49SGordon Ross * try for a shared oplock (Level II)
47494047d49SGordon Ross */
47594047d49SGordon Ross if (status == NT_STATUS_OPLOCK_NOT_GRANTED) {
47694047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_TWO;
47794047d49SGordon Ross status = smb_oplock_request(sr, ofile,
47894047d49SGordon Ross &op->op_oplock_state);
47994047d49SGordon Ross }
48094047d49SGordon Ross
48194047d49SGordon Ross /*
48272b35b05SGordon Ross * Keep track of what we got (ofile->f_oplock.og_state etc)
48394047d49SGordon Ross * so we'll know what we had when sending a break later.
48494047d49SGordon Ross * The og_dialect here is the oplock dialect, not the
48594047d49SGordon Ross * SMB dialect. No lease here, so SMB 2.0.
48694047d49SGordon Ross */
48794047d49SGordon Ross switch (status) {
48894047d49SGordon Ross case NT_STATUS_SUCCESS:
48972b35b05SGordon Ross case NT_STATUS_OPLOCK_BREAK_IN_PROGRESS:
49072b35b05SGordon Ross ofile->f_oplock.og_dialect = SMB_VERS_2_002;
4917f6a299eSGordon Ross ofile->f_oplock.og_state = op->op_oplock_state;
4927f6a299eSGordon Ross ofile->f_oplock.og_breakto = op->op_oplock_state;
4937f6a299eSGordon Ross ofile->f_oplock.og_breaking = B_FALSE;
49472b35b05SGordon Ross if (ofile->dh_persist) {
49572b35b05SGordon Ross smb2_dh_update_oplock(sr, ofile);
49672b35b05SGordon Ross }
49794047d49SGordon Ross break;
49872b35b05SGordon Ross
49994047d49SGordon Ross case NT_STATUS_OPLOCK_NOT_GRANTED:
50094047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
50194047d49SGordon Ross return;
50272b35b05SGordon Ross
50394047d49SGordon Ross default:
50494047d49SGordon Ross /* Caller did not check args sufficiently? */
50594047d49SGordon Ross cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x",
50694047d49SGordon Ross sr->session->ip_addr_str, status);
50772b35b05SGordon Ross DTRACE_PROBE2(other__error, smb_request_t *, sr,
50872b35b05SGordon Ross uint32_t, status);
50994047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
51094047d49SGordon Ross return;
51194047d49SGordon Ross }
51294047d49SGordon Ross
51394047d49SGordon Ross /*
51472b35b05SGordon Ross * Only success cases get here
51594047d49SGordon Ross * Convert internal oplock state to SMB2
51694047d49SGordon Ross */
51794047d49SGordon Ross if (op->op_oplock_state & OPLOCK_LEVEL_GRANULAR) {
51894047d49SGordon Ross ASSERT(0);
51994047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
52094047d49SGordon Ross } else if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
52194047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
52294047d49SGordon Ross } else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
52394047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
52494047d49SGordon Ross } else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
52594047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
52694047d49SGordon Ross } else {
52794047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
52894047d49SGordon Ross }
52972b35b05SGordon Ross
53072b35b05SGordon Ross /*
53172b35b05SGordon Ross * An smb_oplock_reqest call may have returned the
53272b35b05SGordon Ross * status code that says we should wait.
53372b35b05SGordon Ross */
53472b35b05SGordon Ross if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
53572b35b05SGordon Ross (void) smb2sr_go_async(sr);
53672b35b05SGordon Ross (void) smb_oplock_wait_break(sr, ofile->f_node, 0);
53772b35b05SGordon Ross }
53894047d49SGordon Ross }
53994047d49SGordon Ross
54094047d49SGordon Ross /*
54194047d49SGordon Ross * smb2_oplock_reconnect() Helper for smb2_dh_reconnect
54294047d49SGordon Ross * Get oplock state into op->op_oplock_level etc.
54394047d49SGordon Ross *
54494047d49SGordon Ross * Similar to the end of smb2_lease_acquire (for leases) or
54594047d49SGordon Ross * the end of smb2_oplock_acquire (for old-style oplocks).
54694047d49SGordon Ross */
54794047d49SGordon Ross void
smb2_oplock_reconnect(smb_request_t * sr)54894047d49SGordon Ross smb2_oplock_reconnect(smb_request_t *sr)
54994047d49SGordon Ross {
55094047d49SGordon Ross smb_arg_open_t *op = &sr->arg.open;
55194047d49SGordon Ross smb_ofile_t *ofile = sr->fid_ofile;
55294047d49SGordon Ross
55394047d49SGordon Ross op->op_oplock_state = ofile->f_oplock.og_state;
55494047d49SGordon Ross if (ofile->f_lease != NULL) {
55594047d49SGordon Ross smb_lease_t *ls = ofile->f_lease;
55694047d49SGordon Ross
55794047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
55894047d49SGordon Ross op->lease_state = ls->ls_state &
55994047d49SGordon Ross OPLOCK_LEVEL_CACHE_MASK;
56094047d49SGordon Ross op->lease_flags = (ls->ls_breaking != 0) ?
56194047d49SGordon Ross SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0;
56294047d49SGordon Ross op->lease_epoch = ls->ls_epoch;
56394047d49SGordon Ross op->lease_version = ls->ls_version;
56494047d49SGordon Ross } else {
56594047d49SGordon Ross switch (op->op_oplock_state & OPLOCK_LEVEL_TYPE_MASK) {
56694047d49SGordon Ross default:
56794047d49SGordon Ross case OPLOCK_LEVEL_NONE:
56894047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
56994047d49SGordon Ross break;
57094047d49SGordon Ross case OPLOCK_LEVEL_TWO:
57194047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
57294047d49SGordon Ross break;
57394047d49SGordon Ross case OPLOCK_LEVEL_ONE:
57494047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
57594047d49SGordon Ross break;
57694047d49SGordon Ross case OPLOCK_LEVEL_BATCH:
57794047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
57894047d49SGordon Ross break;
57994047d49SGordon Ross }
58094047d49SGordon Ross }
58194047d49SGordon Ross }
582