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