1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21da6c28aaSamw /*
22148c5f43SAlan Wright  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23525641e8SGordon Ross  * Copyright 2020 Tintri by DDN, Inc.  All rights reserved.
2472b35b05SGordon Ross  * Copyright 2022 RackTop Systems, Inc.
25da6c28aaSamw  */
26cb174861Sjoyce mcintosh 
27da6c28aaSamw /*
2894047d49SGordon Ross  * smb1 oplock support
29da6c28aaSamw  */
30da6c28aaSamw 
31bbf6f00cSJordan Brown #include <smbsrv/smb_kproto.h>
32da6c28aaSamw 
3394047d49SGordon Ross #define	BATCH_OR_EXCL	(OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE)
34a90cf9f2SGordon Ross 
3572b35b05SGordon Ross /*
3672b35b05SGordon Ross  * This is called by the SMB1 "Locking_andX" handler,
3772b35b05SGordon Ross  * for SMB1 oplock break acknowledgement.
3872b35b05SGordon Ross  * This is an "Ack" from the client.
3972b35b05SGordon Ross  */
4072b35b05SGordon Ross void
smb1_oplock_ack_break(smb_request_t * sr,uchar_t oplock_level)4172b35b05SGordon Ross smb1_oplock_ack_break(smb_request_t *sr, uchar_t oplock_level)
4272b35b05SGordon Ross {
4372b35b05SGordon Ross 	smb_ofile_t	*ofile;
4472b35b05SGordon Ross 	smb_node_t	*node;
4572b35b05SGordon Ross 	uint32_t	NewLevel;
4672b35b05SGordon Ross 
4772b35b05SGordon Ross 	ofile = sr->fid_ofile;
4872b35b05SGordon Ross 	node = ofile->f_node;
4972b35b05SGordon Ross 
5072b35b05SGordon Ross 	if (oplock_level == 0)
5172b35b05SGordon Ross 		NewLevel = OPLOCK_LEVEL_NONE;
5272b35b05SGordon Ross 	else
5372b35b05SGordon Ross 		NewLevel = OPLOCK_LEVEL_TWO;
5472b35b05SGordon Ross 
5572b35b05SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
5672b35b05SGordon Ross 	mutex_enter(&node->n_oplock.ol_mutex);
5772b35b05SGordon Ross 
587f6a299eSGordon Ross 	ofile->f_oplock.og_breaking = B_FALSE;
5972b35b05SGordon Ross 	cv_broadcast(&ofile->f_oplock.og_ack_cv);
6072b35b05SGordon Ross 
6172b35b05SGordon Ross 	(void) smb_oplock_ack_break(sr, ofile, &NewLevel);
6272b35b05SGordon Ross 
6372b35b05SGordon Ross 	ofile->f_oplock.og_state = NewLevel;
6472b35b05SGordon Ross 
6572b35b05SGordon Ross 	mutex_exit(&node->n_oplock.ol_mutex);
6672b35b05SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
6772b35b05SGordon Ross }
6872b35b05SGordon Ross 
6972b35b05SGordon Ross /*
7072b35b05SGordon Ross  * Compose an SMB1 Oplock Break Notification packet, including
7172b35b05SGordon Ross  * the SMB1 header and everything, in sr->reply.
7272b35b05SGordon Ross  * The caller will send it and free the request.
7372b35b05SGordon Ross  */
7472b35b05SGordon Ross static void
smb1_oplock_break_notification(smb_request_t * sr,uint32_t NewLevel)7572b35b05SGordon Ross smb1_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel)
7672b35b05SGordon Ross {
7772b35b05SGordon Ross 	smb_ofile_t *ofile = sr->fid_ofile;
7872b35b05SGordon Ross 	uint16_t fid;
7972b35b05SGordon Ross 	uint8_t lock_type;
8072b35b05SGordon Ross 	uint8_t oplock_level;
8172b35b05SGordon Ross 
8272b35b05SGordon Ross 	/*
8372b35b05SGordon Ross 	 * Convert internal level to SMB1
8472b35b05SGordon Ross 	 */
8572b35b05SGordon Ross 	switch (NewLevel) {
8672b35b05SGordon Ross 	default:
8772b35b05SGordon Ross 		ASSERT(0);
8872b35b05SGordon Ross 		/* FALLTHROUGH */
8972b35b05SGordon Ross 	case OPLOCK_LEVEL_NONE:
9072b35b05SGordon Ross 		oplock_level = 0;
9172b35b05SGordon Ross 		break;
9272b35b05SGordon Ross 
9372b35b05SGordon Ross 	case OPLOCK_LEVEL_TWO:
9472b35b05SGordon Ross 		oplock_level = 1;
9572b35b05SGordon Ross 		break;
9672b35b05SGordon Ross 	}
9772b35b05SGordon Ross 
9872b35b05SGordon Ross 	sr->smb_com = SMB_COM_LOCKING_ANDX;
9972b35b05SGordon Ross 	sr->smb_tid = ofile->f_tree->t_tid;
10072b35b05SGordon Ross 	sr->smb_pid = 0xFFFF;
10172b35b05SGordon Ross 	sr->smb_uid = 0;
10272b35b05SGordon Ross 	sr->smb_mid = 0xFFFF;
10372b35b05SGordon Ross 	fid = ofile->f_fid;
10472b35b05SGordon Ross 	lock_type = LOCKING_ANDX_OPLOCK_RELEASE;
10572b35b05SGordon Ross 
10672b35b05SGordon Ross 	(void) smb_mbc_encodef(
10772b35b05SGordon Ross 	    &sr->reply, "Mb19.wwwwbb3.wbb10.",
10872b35b05SGordon Ross 	    /*  "\xffSMB"		   M */
10972b35b05SGordon Ross 	    sr->smb_com,		/* b */
11072b35b05SGordon Ross 	    /* status, flags, signature	 19. */
11172b35b05SGordon Ross 	    sr->smb_tid,		/* w */
11272b35b05SGordon Ross 	    sr->smb_pid,		/* w */
11372b35b05SGordon Ross 	    sr->smb_uid,		/* w */
11472b35b05SGordon Ross 	    sr->smb_mid,		/* w */
11572b35b05SGordon Ross 	    8,		/* word count	   b */
11672b35b05SGordon Ross 	    0xFF,	/* AndX cmd	   b */
11772b35b05SGordon Ross 	    /*  AndX reserved, offset	  3. */
11872b35b05SGordon Ross 	    fid,
11972b35b05SGordon Ross 	    lock_type,
12072b35b05SGordon Ross 	    oplock_level);
12172b35b05SGordon Ross }
12272b35b05SGordon Ross 
12372b35b05SGordon Ross /*
12472b35b05SGordon Ross  * Send an oplock break over the wire, or if we can't,
12572b35b05SGordon Ross  * then process the oplock break locally.
12672b35b05SGordon Ross  *
12772b35b05SGordon Ross  * [MS-CIFS] 3.3.4.2 Object Store Indicates an OpLock Break
12872b35b05SGordon Ross  *
12972b35b05SGordon Ross  * This is mostly similar to smb2_oplock_send_break()
13072b35b05SGordon Ross  * See top comment there about the design.
13172b35b05SGordon Ross  * Called from smb_oplock_async_break.
13272b35b05SGordon Ross  *
13372b35b05SGordon Ross  * This handles only SMB1, which has no durable handles,
13472b35b05SGordon Ross  * and never has GRANULAR oplocks.
13572b35b05SGordon Ross  */
13672b35b05SGordon Ross void
smb1_oplock_send_break(smb_request_t * sr)13772b35b05SGordon Ross smb1_oplock_send_break(smb_request_t *sr)
13872b35b05SGordon Ross {
13972b35b05SGordon Ross 	smb_ofile_t	*ofile = sr->fid_ofile;
14072b35b05SGordon Ross 	smb_node_t	*node = ofile->f_node;
14172b35b05SGordon Ross 	uint32_t	NewLevel = sr->arg.olbrk.NewLevel;
14272b35b05SGordon Ross 	boolean_t	AckReq = sr->arg.olbrk.AckRequired;
14372b35b05SGordon Ross 	uint32_t	status;
14472b35b05SGordon Ross 	int		rc;
14572b35b05SGordon Ross 
14672b35b05SGordon Ross 	/*
14772b35b05SGordon Ross 	 * SMB1 clients should only get Level II oplocks if they
14872b35b05SGordon Ross 	 * set the capability indicating they know about them.
14972b35b05SGordon Ross 	 */
15072b35b05SGordon Ross 	if (NewLevel == OPLOCK_LEVEL_TWO &&
15172b35b05SGordon Ross 	    ofile->f_oplock.og_dialect < NT_LM_0_12)
15272b35b05SGordon Ross 		NewLevel = OPLOCK_LEVEL_NONE;
15372b35b05SGordon Ross 
15472b35b05SGordon Ross 	/*
15572b35b05SGordon Ross 	 * Build the break message in sr->reply.
15672b35b05SGordon Ross 	 * It's free'd in smb_request_free().
15772b35b05SGordon Ross 	 * Always SMB1 here.
15872b35b05SGordon Ross 	 */
15972b35b05SGordon Ross 	sr->reply.max_bytes = MLEN;
16072b35b05SGordon Ross 	smb1_oplock_break_notification(sr, NewLevel);
16172b35b05SGordon Ross 
16272b35b05SGordon Ross 	/*
16372b35b05SGordon Ross 	 * Try to send the break message to the client.
16472b35b05SGordon Ross 	 * If connected, this IF body will be true.
16572b35b05SGordon Ross 	 */
16672b35b05SGordon Ross 	if (sr->session == ofile->f_session)
16772b35b05SGordon Ross 		rc = smb_session_send(sr->session, 0, &sr->reply);
16872b35b05SGordon Ross 	else
16972b35b05SGordon Ross 		rc = ENOTCONN;
17072b35b05SGordon Ross 
17172b35b05SGordon Ross 	if (rc != 0) {
17272b35b05SGordon Ross 		/*
17372b35b05SGordon Ross 		 * We were unable to send the oplock break request,
17472b35b05SGordon Ross 		 * presumably because the connection is gone.
17572b35b05SGordon Ross 		 * Just close the handle.
17672b35b05SGordon Ross 		 */
17772b35b05SGordon Ross 		smb_ofile_close(ofile, 0);
17872b35b05SGordon Ross 		return;
17972b35b05SGordon Ross 	}
18072b35b05SGordon Ross 
18172b35b05SGordon Ross 	/*
18272b35b05SGordon Ross 	 * OK, we were able to send the break message.
18372b35b05SGordon Ross 	 * If no ack. required, we're done.
18472b35b05SGordon Ross 	 */
18572b35b05SGordon Ross 	if (!AckReq)
18672b35b05SGordon Ross 		return;
18772b35b05SGordon Ross 
18872b35b05SGordon Ross 	/*
18972b35b05SGordon Ross 	 * We're expecting an ACK.  Wait in this thread
19072b35b05SGordon Ross 	 * so we can log clients that don't respond.
191*3b24312dSGordon Ross 	 * Note: this can also fail for other reasons
192*3b24312dSGordon Ross 	 * such as client disconnect or server shutdown.
19372b35b05SGordon Ross 	 */
1947f6a299eSGordon Ross 	status = smb_oplock_wait_ack(sr, NewLevel);
19572b35b05SGordon Ross 	if (status == 0)
19672b35b05SGordon Ross 		return;
19772b35b05SGordon Ross 
198*3b24312dSGordon Ross 	DTRACE_PROBE2(wait__ack__failed, smb_request_t *, sr,
199*3b24312dSGordon Ross 	    uint32_t, status);
20072b35b05SGordon Ross 
20172b35b05SGordon Ross 	/*
20272b35b05SGordon Ross 	 * Did not get an ACK, so do the ACK locally.
20372b35b05SGordon Ross 	 * Note: always break to none here, regardless
20472b35b05SGordon Ross 	 * of what the passed in cache level was.
20572b35b05SGordon Ross 	 */
20672b35b05SGordon Ross 	NewLevel = OPLOCK_LEVEL_NONE;
20772b35b05SGordon Ross 
20872b35b05SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
20972b35b05SGordon Ross 	mutex_enter(&node->n_oplock.ol_mutex);
21072b35b05SGordon Ross 
2117f6a299eSGordon Ross 	ofile->f_oplock.og_breaking = B_FALSE;
21272b35b05SGordon Ross 	cv_broadcast(&ofile->f_oplock.og_ack_cv);
21372b35b05SGordon Ross 
21472b35b05SGordon Ross 	status = smb_oplock_ack_break(sr, ofile, &NewLevel);
21572b35b05SGordon Ross 
21672b35b05SGordon Ross 	ofile->f_oplock.og_state = NewLevel;
21772b35b05SGordon Ross 
21872b35b05SGordon Ross 	mutex_exit(&node->n_oplock.ol_mutex);
21972b35b05SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
22072b35b05SGordon Ross 
22172b35b05SGordon Ross #ifdef	DEBUG
22272b35b05SGordon Ross 	if (status != 0) {
22372b35b05SGordon Ross 		cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x",
22472b35b05SGordon Ross 		    sr->session->ip_addr_str, status);
22572b35b05SGordon Ross 	}
22672b35b05SGordon Ross #endif
22772b35b05SGordon Ross }
22872b35b05SGordon Ross 
229da6c28aaSamw /*
23094047d49SGordon Ross  * Client has an open handle and requests an oplock.
23194047d49SGordon Ross  * Convert SMB1 oplock request info in to internal form,
23294047d49SGordon Ross  * call common oplock code, convert result to SMB1.
233da6c28aaSamw  */
2342c2961f8Sjose borrego void
smb1_oplock_acquire(smb_request_t * sr,boolean_t level2ok)23594047d49SGordon Ross smb1_oplock_acquire(smb_request_t *sr, boolean_t level2ok)
236da6c28aaSamw {
23794047d49SGordon Ross 	smb_arg_open_t *op = &sr->arg.open;
23894047d49SGordon Ross 	smb_ofile_t *ofile = sr->fid_ofile;
23994047d49SGordon Ross 	uint32_t status;
240da6c28aaSamw 
24194047d49SGordon Ross 	/* Only disk trees get oplocks. */
24294047d49SGordon Ross 	if ((sr->tid_tree->t_res_type & STYPE_MASK) != STYPE_DISKTREE) {
2432c2961f8Sjose borrego 		op->op_oplock_level = SMB_OPLOCK_NONE;
2442c2961f8Sjose borrego 		return;
2452c2961f8Sjose borrego 	}
246da6c28aaSamw 
24794047d49SGordon Ross 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
248cb174861Sjoyce mcintosh 		op->op_oplock_level = SMB_OPLOCK_NONE;
249cb174861Sjoyce mcintosh 		return;
250da6c28aaSamw 	}
251cb174861Sjoyce mcintosh 
25294047d49SGordon Ross 	if (!smb_session_levelII_oplocks(sr->session))
25394047d49SGordon Ross 		level2ok = B_FALSE;
254cb174861Sjoyce mcintosh 
25594047d49SGordon Ross 	/* Common code checks file type. */
2568c10a865Sas 
25794047d49SGordon Ross 	/*
25894047d49SGordon Ross 	 * SMB1: Convert to internal form.
25994047d49SGordon Ross 	 */
26094047d49SGordon Ross 	switch (op->op_oplock_level) {
26194047d49SGordon Ross 	case SMB_OPLOCK_BATCH:
26294047d49SGordon Ross 		op->op_oplock_state = OPLOCK_LEVEL_BATCH;
263cb174861Sjoyce mcintosh 		break;
26494047d49SGordon Ross 	case SMB_OPLOCK_EXCLUSIVE:
26594047d49SGordon Ross 		op->op_oplock_state = OPLOCK_LEVEL_ONE;
266cb174861Sjoyce mcintosh 		break;
26794047d49SGordon Ross 	case SMB_OPLOCK_LEVEL_II:
26894047d49SGordon Ross 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
269cb174861Sjoyce mcintosh 		break;
27094047d49SGordon Ross 	case SMB_OPLOCK_NONE:
27194047d49SGordon Ross 	default:
27294047d49SGordon Ross 		op->op_oplock_level = SMB_OPLOCK_NONE;
2739c856e86SMatt Barden 		return;
2749c856e86SMatt Barden 	}
2759c856e86SMatt Barden 
276811599a4SMatt Barden 	/*
27794047d49SGordon Ross 	 * Tree options may force shared oplocks
278811599a4SMatt Barden 	 */
27994047d49SGordon Ross 	if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
28094047d49SGordon Ross 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
28194047d49SGordon Ross 	}
282cb174861Sjoyce mcintosh 
283811599a4SMatt Barden 	/*
28494047d49SGordon Ross 	 * Try exclusive first, if requested
285811599a4SMatt Barden 	 */
28694047d49SGordon Ross 	if ((op->op_oplock_state & BATCH_OR_EXCL) != 0) {
28794047d49SGordon Ross 		status = smb_oplock_request(sr, ofile,
28894047d49SGordon Ross 		    &op->op_oplock_state);
289811599a4SMatt Barden 	} else {
29094047d49SGordon Ross 		status = NT_STATUS_OPLOCK_NOT_GRANTED;
291811599a4SMatt Barden 	}
292811599a4SMatt Barden 
293811599a4SMatt Barden 	/*
29472b35b05SGordon Ross 	 * If exclusive failed (or the tree forced shared oplocks)
29594047d49SGordon Ross 	 * and if the caller supports Level II, try shared.
296811599a4SMatt Barden 	 */
29794047d49SGordon Ross 	if (status == NT_STATUS_OPLOCK_NOT_GRANTED && level2ok) {
29894047d49SGordon Ross 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
29994047d49SGordon Ross 		status = smb_oplock_request(sr, ofile,
30094047d49SGordon Ross 		    &op->op_oplock_state);
301da6c28aaSamw 	}
302cb174861Sjoyce mcintosh 
30394047d49SGordon Ross 	/*
30472b35b05SGordon Ross 	 * Keep track of what we got (ofile->f_oplock.og_state etc)
30594047d49SGordon Ross 	 * so we'll know what we had when sending a break later.
30694047d49SGordon Ross 	 * The og_dialect here is the oplock dialect, which may be
30794047d49SGordon Ross 	 * different than SMB dialect.  Pre-NT clients did not
30894047d49SGordon Ross 	 * support "Level II" oplocks.  If we're talking to a
30994047d49SGordon Ross 	 * client that didn't set the CAP_LEVEL_II_OPLOCKS in
31094047d49SGordon Ross 	 * its capabilities, let og_dialect = LANMAN2_1.
31194047d49SGordon Ross 	 */
31294047d49SGordon Ross 	switch (status) {
31394047d49SGordon Ross 	case NT_STATUS_SUCCESS:
31472b35b05SGordon Ross 	case NT_STATUS_OPLOCK_BREAK_IN_PROGRESS:
31572b35b05SGordon Ross 		ofile->f_oplock.og_dialect = (level2ok) ?
31672b35b05SGordon Ross 		    NT_LM_0_12 : LANMAN2_1;
3177f6a299eSGordon Ross 		ofile->f_oplock.og_state   = op->op_oplock_state;
3187f6a299eSGordon Ross 		ofile->f_oplock.og_breakto = op->op_oplock_state;
3197f6a299eSGordon Ross 		ofile->f_oplock.og_breaking = B_FALSE;
3202c2961f8Sjose borrego 		break;
32194047d49SGordon Ross 	case NT_STATUS_OPLOCK_NOT_GRANTED:
32294047d49SGordon Ross 		op->op_oplock_level = SMB_OPLOCK_NONE;
32394047d49SGordon Ross 		return;
3242c2961f8Sjose borrego 	default:
32594047d49SGordon Ross 		/* Caller did not check args sufficiently? */
32694047d49SGordon Ross 		cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x",
32794047d49SGordon Ross 		    sr->session->ip_addr_str, status);
32894047d49SGordon Ross 		op->op_oplock_level = SMB_OPLOCK_NONE;
32994047d49SGordon Ross 		return;
330cb174861Sjoyce mcintosh 	}
331cb174861Sjoyce mcintosh 
33294047d49SGordon Ross 	/*
33372b35b05SGordon Ross 	 * Only succes cases get here.
33494047d49SGordon Ross 	 * Convert internal oplock state to SMB1
33594047d49SGordon Ross 	 */
33694047d49SGordon Ross 	if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
33794047d49SGordon Ross 		op->op_oplock_level = SMB_OPLOCK_BATCH;
33894047d49SGordon Ross 	} else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
33994047d49SGordon Ross 		op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE;
34094047d49SGordon Ross 	} else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
34194047d49SGordon Ross 		op->op_oplock_level = SMB_OPLOCK_LEVEL_II;
34294047d49SGordon Ross 	} else {
34394047d49SGordon Ross 		op->op_oplock_level = SMB_OPLOCK_NONE;
3442c2961f8Sjose borrego 	}
34572b35b05SGordon Ross 
34672b35b05SGordon Ross 	/*
34772b35b05SGordon Ross 	 * An smb_oplock_reqest call may have returned the
34872b35b05SGordon Ross 	 * status code that says we should wait.
34972b35b05SGordon Ross 	 */
35072b35b05SGordon Ross 	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
35172b35b05SGordon Ross 		(void) smb_oplock_wait_break(sr, ofile->f_node, 0);
35272b35b05SGordon Ross 	}
353cb174861Sjoyce mcintosh }
354