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 /*
13a9931e68SGordon Ross  * Copyright 2021 Tintri by DDN, Inc.  All rights reserved.
14*4cd29e08SMatt Barden  * Copyright 2021-2023 RackTop Systems, Inc.
1594047d49SGordon Ross  */
1694047d49SGordon Ross 
1794047d49SGordon Ross /*
1894047d49SGordon Ross  * Dispatch function for SMB2_OPLOCK_BREAK
1994047d49SGordon Ross  */
2094047d49SGordon Ross 
2194047d49SGordon Ross #include <smbsrv/smb2_kproto.h>
2294047d49SGordon Ross #include <smbsrv/smb_oplock.h>
2394047d49SGordon Ross 
2494047d49SGordon Ross /* StructSize for the two "break" message formats. */
2594047d49SGordon Ross #define	SSZ_OPLOCK	24
2694047d49SGordon Ross #define	SSZ_LEASE_ACK	36
2794047d49SGordon Ross #define	SSZ_LEASE_BRK	44
2894047d49SGordon Ross 
2994047d49SGordon Ross #define	NODE_FLAGS_DELETING	(NODE_FLAGS_DELETE_ON_CLOSE |\
3094047d49SGordon Ross 				NODE_FLAGS_DELETE_COMMITTED)
3194047d49SGordon Ross 
3294047d49SGordon Ross static const char lease_zero[UUID_LEN] = { 0 };
3394047d49SGordon Ross 
3494047d49SGordon Ross static kmem_cache_t	*smb_lease_cache = NULL;
3594047d49SGordon Ross 
3694047d49SGordon Ross void
smb2_lease_init()3794047d49SGordon Ross smb2_lease_init()
3894047d49SGordon Ross {
3994047d49SGordon Ross 	if (smb_lease_cache != NULL)
4094047d49SGordon Ross 		return;
4194047d49SGordon Ross 
4294047d49SGordon Ross 	smb_lease_cache = kmem_cache_create("smb_lease_cache",
4394047d49SGordon Ross 	    sizeof (smb_lease_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
4494047d49SGordon Ross }
4594047d49SGordon Ross 
4694047d49SGordon Ross void
smb2_lease_fini()4794047d49SGordon Ross smb2_lease_fini()
4894047d49SGordon Ross {
4994047d49SGordon Ross 	if (smb_lease_cache != NULL) {
5094047d49SGordon Ross 		kmem_cache_destroy(smb_lease_cache);
5194047d49SGordon Ross 		smb_lease_cache = NULL;
5294047d49SGordon Ross 	}
5394047d49SGordon Ross }
5494047d49SGordon Ross 
55*4cd29e08SMatt Barden /*
56*4cd29e08SMatt Barden  * Take a ref on the lease if it's not being destroyed.
57*4cd29e08SMatt Barden  * Returns false when the lease is being destroyed.
58*4cd29e08SMatt Barden  * Otherwise, returns true.
59*4cd29e08SMatt Barden  */
60*4cd29e08SMatt Barden static boolean_t
smb2_lease_hold(smb_lease_t * ls)6194047d49SGordon Ross smb2_lease_hold(smb_lease_t *ls)
6294047d49SGordon Ross {
63*4cd29e08SMatt Barden 	boolean_t ret;
64*4cd29e08SMatt Barden 
6594047d49SGordon Ross 	mutex_enter(&ls->ls_mutex);
66*4cd29e08SMatt Barden 	ret = !ls->ls_destroying;
67*4cd29e08SMatt Barden 	if (ret)
68*4cd29e08SMatt Barden 		ls->ls_refcnt++;
6994047d49SGordon Ross 	mutex_exit(&ls->ls_mutex);
70*4cd29e08SMatt Barden 
71*4cd29e08SMatt Barden 	return (ret);
7294047d49SGordon Ross }
7394047d49SGordon Ross 
7472b35b05SGordon Ross static void
lease_destroy(smb_lease_t * ls)7572b35b05SGordon Ross lease_destroy(smb_lease_t *ls)
7672b35b05SGordon Ross {
7772b35b05SGordon Ross 	smb_node_release(ls->ls_node);
7872b35b05SGordon Ross 	mutex_destroy(&ls->ls_mutex);
7972b35b05SGordon Ross 	kmem_cache_free(smb_lease_cache, ls);
8072b35b05SGordon Ross }
8172b35b05SGordon Ross 
8294047d49SGordon Ross void
smb2_lease_rele(smb_lease_t * ls)8394047d49SGordon Ross smb2_lease_rele(smb_lease_t *ls)
8494047d49SGordon Ross {
8594047d49SGordon Ross 	smb_llist_t *bucket;
8672b35b05SGordon Ross 	boolean_t destroy = B_FALSE;
8794047d49SGordon Ross 
8894047d49SGordon Ross 	mutex_enter(&ls->ls_mutex);
8994047d49SGordon Ross 	ls->ls_refcnt--;
90*4cd29e08SMatt Barden 	if (ls->ls_refcnt != 0 || ls->ls_destroying) {
9194047d49SGordon Ross 		mutex_exit(&ls->ls_mutex);
9294047d49SGordon Ross 		return;
9394047d49SGordon Ross 	}
94*4cd29e08SMatt Barden 	ls->ls_destroying = B_TRUE;
9594047d49SGordon Ross 	mutex_exit(&ls->ls_mutex);
9694047d49SGordon Ross 
9794047d49SGordon Ross 	/*
9894047d49SGordon Ross 	 * Get the list lock, then re-check the refcnt
9994047d49SGordon Ross 	 * and if it's still zero, unlink & destroy.
10094047d49SGordon Ross 	 */
10194047d49SGordon Ross 	bucket = ls->ls_bucket;
10294047d49SGordon Ross 	smb_llist_enter(bucket, RW_WRITER);
10394047d49SGordon Ross 
10494047d49SGordon Ross 	mutex_enter(&ls->ls_mutex);
10594047d49SGordon Ross 	if (ls->ls_refcnt == 0) {
10672b35b05SGordon Ross 		smb_llist_remove(bucket, ls);
10772b35b05SGordon Ross 		destroy = B_TRUE;
108*4cd29e08SMatt Barden 	} else {
109*4cd29e08SMatt Barden 		ls->ls_destroying = B_FALSE;
11094047d49SGordon Ross 	}
11172b35b05SGordon Ross 	mutex_exit(&ls->ls_mutex);
11294047d49SGordon Ross 
11394047d49SGordon Ross 	smb_llist_exit(bucket);
11472b35b05SGordon Ross 
11572b35b05SGordon Ross 	if (destroy) {
11672b35b05SGordon Ross 		lease_destroy(ls);
11772b35b05SGordon Ross 	}
11894047d49SGordon Ross }
11994047d49SGordon Ross 
12094047d49SGordon Ross /*
12194047d49SGordon Ross  * Compute a hash from a uuid
12294047d49SGordon Ross  * Based on mod_hash_bystr()
12394047d49SGordon Ross  */
12494047d49SGordon Ross static uint_t
smb_hash_uuid(const uint8_t * uuid)12594047d49SGordon Ross smb_hash_uuid(const uint8_t *uuid)
12694047d49SGordon Ross {
12794047d49SGordon Ross 	char *k = (char *)uuid;
12894047d49SGordon Ross 	uint_t hash = 0;
12994047d49SGordon Ross 	uint_t g;
13094047d49SGordon Ross 	int i;
13194047d49SGordon Ross 
13294047d49SGordon Ross 	ASSERT(k);
13394047d49SGordon Ross 	for (i = 0; i < UUID_LEN; i++) {
13494047d49SGordon Ross 		hash = (hash << 4) + k[i];
13594047d49SGordon Ross 		if ((g = (hash & 0xf0000000)) != 0) {
13694047d49SGordon Ross 			hash ^= (g >> 24);
13794047d49SGordon Ross 			hash ^= g;
13894047d49SGordon Ross 		}
13994047d49SGordon Ross 	}
14094047d49SGordon Ross 	return (hash);
14194047d49SGordon Ross }
14294047d49SGordon Ross 
14394047d49SGordon Ross /*
14494047d49SGordon Ross  * Add or update a lease table entry for a new ofile.
14594047d49SGordon Ross  * (in the per-session lease table)
14694047d49SGordon Ross  * See [MS-SMB2] 3.3.5.9.8
14794047d49SGordon Ross  * Handling the SMB2_CREATE_REQUEST_LEASE Create Context
14894047d49SGordon Ross  */
14994047d49SGordon Ross uint32_t
smb2_lease_create(smb_request_t * sr,uint8_t * clnt)1508d94f651SGordon Ross smb2_lease_create(smb_request_t *sr, uint8_t *clnt)
15194047d49SGordon Ross {
15294047d49SGordon Ross 	smb_arg_open_t *op = &sr->arg.open;
15394047d49SGordon Ross 	uint8_t *key = op->lease_key;
15494047d49SGordon Ross 	smb_ofile_t *of = sr->fid_ofile;
15594047d49SGordon Ross 	smb_hash_t *ht = sr->sr_server->sv_lease_ht;
15694047d49SGordon Ross 	smb_llist_t *bucket;
15794047d49SGordon Ross 	smb_lease_t *lease;
15894047d49SGordon Ross 	smb_lease_t *newlease;
15994047d49SGordon Ross 	size_t hashkey;
16094047d49SGordon Ross 	uint32_t status = NT_STATUS_INVALID_PARAMETER;
16194047d49SGordon Ross 
16294047d49SGordon Ross 	if (bcmp(key, lease_zero, UUID_LEN) == 0)
16394047d49SGordon Ross 		return (status);
16494047d49SGordon Ross 
16594047d49SGordon Ross 	/*
16694047d49SGordon Ross 	 * Find or create, and add a ref for the new ofile.
16794047d49SGordon Ross 	 */
16894047d49SGordon Ross 	hashkey = smb_hash_uuid(key);
16994047d49SGordon Ross 	hashkey &= (ht->num_buckets - 1);
17094047d49SGordon Ross 	bucket = &ht->buckets[hashkey].b_list;
17194047d49SGordon Ross 
17294047d49SGordon Ross 	newlease = kmem_cache_alloc(smb_lease_cache, KM_SLEEP);
17394047d49SGordon Ross 	bzero(newlease, sizeof (smb_lease_t));
17494047d49SGordon Ross 	mutex_init(&newlease->ls_mutex, NULL, MUTEX_DEFAULT, NULL);
17594047d49SGordon Ross 	newlease->ls_bucket = bucket;
17694047d49SGordon Ross 	newlease->ls_node = of->f_node;
17772b35b05SGordon Ross 	smb_node_ref(newlease->ls_node);
17894047d49SGordon Ross 	newlease->ls_refcnt = 1;
17994047d49SGordon Ross 	newlease->ls_epoch = op->lease_epoch;
18094047d49SGordon Ross 	newlease->ls_version = op->lease_version;
18194047d49SGordon Ross 	bcopy(key, newlease->ls_key, UUID_LEN);
18294047d49SGordon Ross 	bcopy(clnt, newlease->ls_clnt, UUID_LEN);
18394047d49SGordon Ross 
18494047d49SGordon Ross 	smb_llist_enter(bucket, RW_WRITER);
18594047d49SGordon Ross 	for (lease = smb_llist_head(bucket); lease != NULL;
18694047d49SGordon Ross 	    lease = smb_llist_next(bucket, lease)) {
18794047d49SGordon Ross 		/*
18894047d49SGordon Ross 		 * Looking for this lease ID, on a node
18994047d49SGordon Ross 		 * that's not being deleted.
19094047d49SGordon Ross 		 */
19194047d49SGordon Ross 		if (bcmp(lease->ls_key, key, UUID_LEN) == 0 &&
19294047d49SGordon Ross 		    bcmp(lease->ls_clnt, clnt, UUID_LEN) == 0 &&
193*4cd29e08SMatt Barden 		    (lease->ls_node->flags & NODE_FLAGS_DELETING) == 0 &&
194*4cd29e08SMatt Barden 		    smb2_lease_hold(lease))
19594047d49SGordon Ross 			break;
19694047d49SGordon Ross 	}
197*4cd29e08SMatt Barden 
198*4cd29e08SMatt Barden 	if (lease == NULL) {
19994047d49SGordon Ross 		lease = newlease;
20094047d49SGordon Ross 		smb_llist_insert_head(bucket, lease);
20194047d49SGordon Ross 		newlease = NULL; /* don't free */
20294047d49SGordon Ross 	}
20394047d49SGordon Ross 	smb_llist_exit(bucket);
20494047d49SGordon Ross 
205*4cd29e08SMatt Barden 	/*
206*4cd29e08SMatt Barden 	 * If we found an existing lease, make sure it refers to the same node.
207*4cd29e08SMatt Barden 	 */
208*4cd29e08SMatt Barden 	if (lease->ls_node != of->f_node) {
209*4cd29e08SMatt Barden 		/* Same lease ID, different node! */
210*4cd29e08SMatt Barden #ifdef DEBUG
211*4cd29e08SMatt Barden 		cmn_err(CE_NOTE, "new lease on node %p (%s) "
212*4cd29e08SMatt Barden 		    "conflicts with existing node %p (%s)",
213*4cd29e08SMatt Barden 		    (void *) of->f_node,
214*4cd29e08SMatt Barden 		    of->f_node->od_name,
215*4cd29e08SMatt Barden 		    (void *) lease->ls_node,
216*4cd29e08SMatt Barden 		    lease->ls_node->od_name);
217*4cd29e08SMatt Barden #endif
218*4cd29e08SMatt Barden 		DTRACE_PROBE2(dup_lease, smb_request_t *, sr,
219*4cd29e08SMatt Barden 		    smb_lease_t *, lease);
220*4cd29e08SMatt Barden 		smb2_lease_rele(lease);
221*4cd29e08SMatt Barden 		lease = NULL; /* error */
222*4cd29e08SMatt Barden 	}
223*4cd29e08SMatt Barden 
22494047d49SGordon Ross 	if (newlease != NULL) {
22572b35b05SGordon Ross 		lease_destroy(newlease);
22694047d49SGordon Ross 	}
22794047d49SGordon Ross 
22894047d49SGordon Ross 	if (lease != NULL) {
22994047d49SGordon Ross 		of->f_lease = lease;
23094047d49SGordon Ross 		status = NT_STATUS_SUCCESS;
23194047d49SGordon Ross 	}
23294047d49SGordon Ross 
23394047d49SGordon Ross 	return (status);
23494047d49SGordon Ross }
23594047d49SGordon Ross 
23694047d49SGordon Ross /*
23794047d49SGordon Ross  * Find the lease for a given: client_uuid, lease_key
23894047d49SGordon Ross  * Returns the lease with a new ref.
23994047d49SGordon Ross  */
24072b35b05SGordon Ross static smb_lease_t *
lease_lookup(smb_request_t * sr,uint8_t * lease_key)24172b35b05SGordon Ross lease_lookup(smb_request_t *sr, uint8_t *lease_key)
24294047d49SGordon Ross {
24372b35b05SGordon Ross 	smb_server_t *sv = sr->sr_server;
24472b35b05SGordon Ross 	uint8_t *clnt_uuid = sr->session->clnt_uuid;
24594047d49SGordon Ross 	smb_hash_t *ht = sv->sv_lease_ht;
24694047d49SGordon Ross 	smb_llist_t *bucket;
24794047d49SGordon Ross 	smb_lease_t *lease;
24894047d49SGordon Ross 	size_t hashkey;
24994047d49SGordon Ross 
25094047d49SGordon Ross 	hashkey = smb_hash_uuid(lease_key);
25194047d49SGordon Ross 	hashkey &= (ht->num_buckets - 1);
25294047d49SGordon Ross 	bucket = &ht->buckets[hashkey].b_list;
25394047d49SGordon Ross 
25494047d49SGordon Ross 	smb_llist_enter(bucket, RW_READER);
25594047d49SGordon Ross 	lease = smb_llist_head(bucket);
25694047d49SGordon Ross 	while (lease != NULL) {
25794047d49SGordon Ross 		if (bcmp(lease->ls_key, lease_key, UUID_LEN) == 0 &&
258*4cd29e08SMatt Barden 		    bcmp(lease->ls_clnt, clnt_uuid, UUID_LEN) == 0 &&
259*4cd29e08SMatt Barden 		    smb2_lease_hold(lease))
26094047d49SGordon Ross 			break;
26194047d49SGordon Ross 		lease = smb_llist_next(bucket, lease);
26294047d49SGordon Ross 	}
26394047d49SGordon Ross 	smb_llist_exit(bucket);
26494047d49SGordon Ross 
26594047d49SGordon Ross 	return (lease);
26694047d49SGordon Ross }
26794047d49SGordon Ross 
26894047d49SGordon Ross /*
26972b35b05SGordon Ross  * Find the oplock smb_ofile_t for the specified lease.
27072b35b05SGordon Ross  * If no such ofile, NT_STATUS_UNSUCCESSFUL.
27194047d49SGordon Ross  * On success, ofile (held) in sr->fid_ofile.
27294047d49SGordon Ross  */
27394047d49SGordon Ross static uint32_t
lease_find_oplock(smb_request_t * sr,smb_lease_t * lease)27472b35b05SGordon Ross lease_find_oplock(smb_request_t *sr, smb_lease_t *lease)
27594047d49SGordon Ross {
27672b35b05SGordon Ross 	smb_node_t	*node = lease->ls_node;
27794047d49SGordon Ross 	smb_ofile_t	*o;
27872b35b05SGordon Ross 	uint32_t	status = NT_STATUS_UNSUCCESSFUL;
27994047d49SGordon Ross 
28072b35b05SGordon Ross 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
28172b35b05SGordon Ross 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
28272b35b05SGordon Ross 	ASSERT(sr->fid_ofile == NULL);
28394047d49SGordon Ross 
28472b35b05SGordon Ross 	FOREACH_NODE_OFILE(node, o) {
28572b35b05SGordon Ross 		if (o->f_lease != lease)
28672b35b05SGordon Ross 			continue;
28772b35b05SGordon Ross 		if (o != lease->ls_oplock_ofile)
28872b35b05SGordon Ross 			continue;
28994047d49SGordon Ross 		/*
29072b35b05SGordon Ross 		 * Found the ofile holding the oplock
29172b35b05SGordon Ross 		 * This hold released in smb_request_free
29294047d49SGordon Ross 		 */
29372b35b05SGordon Ross 		if (smb_ofile_hold_olbrk(o)) {
29494047d49SGordon Ross 			sr->fid_ofile = o;
29594047d49SGordon Ross 			status = NT_STATUS_SUCCESS;
29694047d49SGordon Ross 			break;
29794047d49SGordon Ross 		}
29894047d49SGordon Ross 	}
29994047d49SGordon Ross 
30094047d49SGordon Ross 	return (status);
30194047d49SGordon Ross }
30294047d49SGordon Ross 
30394047d49SGordon Ross /*
30494047d49SGordon Ross  * This is called by smb2_oplock_break_ack when the struct size
30594047d49SGordon Ross  * indicates this is a lease break (SZ_LEASE).  See:
30694047d49SGordon Ross  * [MS-SMB2] 3.3.5.22.2 Processing a Lease Acknowledgment
30772b35b05SGordon Ross  * This is an "Ack" from the client.
30894047d49SGordon Ross  */
30994047d49SGordon Ross smb_sdrc_t
smb2_lease_break_ack(smb_request_t * sr)31094047d49SGordon Ross smb2_lease_break_ack(smb_request_t *sr)
31194047d49SGordon Ross {
31272b35b05SGordon Ross 	smb_arg_olbrk_t	*olbrk = &sr->arg.olbrk;
31394047d49SGordon Ross 	smb_lease_t *lease;
31472b35b05SGordon Ross 	smb_node_t  *node;
31594047d49SGordon Ross 	smb_ofile_t *ofile;
31694047d49SGordon Ross 	uint32_t LeaseState;
31794047d49SGordon Ross 	uint32_t status;
31894047d49SGordon Ross 	int rc = 0;
31994047d49SGordon Ross 
32094047d49SGordon Ross 	if (sr->session->dialect < SMB_VERS_2_1)
32194047d49SGordon Ross 		return (SDRC_ERROR);
32294047d49SGordon Ross 
32394047d49SGordon Ross 	/*
32494047d49SGordon Ross 	 * Decode an SMB2 Lease Acknowldgement
32594047d49SGordon Ross 	 * [MS-SMB2] 2.2.24.2
32694047d49SGordon Ross 	 * Note: Struct size decoded by caller.
32794047d49SGordon Ross 	 */
32894047d49SGordon Ross 	rc = smb_mbc_decodef(
32994047d49SGordon Ross 	    &sr->smb_data, "6.#cl8.",
33094047d49SGordon Ross 	    /* reserved		  6. */
33194047d49SGordon Ross 	    UUID_LEN,		/* # */
33272b35b05SGordon Ross 	    olbrk->LeaseKey,	/* c */
33372b35b05SGordon Ross 	    &olbrk->NewLevel);	/* l */
33494047d49SGordon Ross 	    /* duration		  8. */
33594047d49SGordon Ross 	if (rc != 0)
33694047d49SGordon Ross 		return (SDRC_ERROR);
33772b35b05SGordon Ross 	LeaseState = olbrk->NewLevel;
33894047d49SGordon Ross 
33972b35b05SGordon Ross 	/*
34072b35b05SGordon Ross 	 * Find the lease via the given key.
34172b35b05SGordon Ross 	 */
34272b35b05SGordon Ross 	lease = lease_lookup(sr, olbrk->LeaseKey);
34372b35b05SGordon Ross 	if (lease == NULL) {
34472b35b05SGordon Ross 		/*
34572b35b05SGordon Ross 		 * It's unusual to skip the dtrace start/done
34672b35b05SGordon Ross 		 * probes like this, but trying to run them
34772b35b05SGordon Ross 		 * with no lease->node would be complex and
34872b35b05SGordon Ross 		 * would not show anything particularly useful.
34972b35b05SGordon Ross 		 * Do the start probe after we find the ofile.
35072b35b05SGordon Ross 		 */
35172b35b05SGordon Ross 		status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
35272b35b05SGordon Ross 		smb2sr_put_error(sr, status);
35372b35b05SGordon Ross 		return (SDRC_SUCCESS);
35472b35b05SGordon Ross 	}
35572b35b05SGordon Ross 	// Note: lease ref; smb_lease_rele() below.
35672b35b05SGordon Ross 	node = lease->ls_node;
35772b35b05SGordon Ross 
35872b35b05SGordon Ross 	/*
35972b35b05SGordon Ross 	 * Find the leased oplock.  Hold locks so it can't move
36072b35b05SGordon Ross 	 * until we're done with ACK-break processing.
36172b35b05SGordon Ross 	 */
36272b35b05SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
36372b35b05SGordon Ross 	mutex_enter(&node->n_oplock.ol_mutex);
36472b35b05SGordon Ross 
36572b35b05SGordon Ross 	status = lease_find_oplock(sr, lease);
36672b35b05SGordon Ross 	/* Normally have sr->fid_ofile now. */
36794047d49SGordon Ross 
36894047d49SGordon Ross 	DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr);
36972b35b05SGordon Ross 
37072b35b05SGordon Ross 	if (status != 0) {
37172b35b05SGordon Ross 		/* Leased oplock not found.  Must have closed. */
37294047d49SGordon Ross 		goto errout;
37372b35b05SGordon Ross 	}
37494047d49SGordon Ross 
37572b35b05SGordon Ross 	/* Success, so have sr->fid_ofile */
37694047d49SGordon Ross 	ofile = sr->fid_ofile;
37794047d49SGordon Ross 
3787f6a299eSGordon Ross 	if (lease->ls_breaking == B_FALSE) {
37972b35b05SGordon Ross 		/*
38072b35b05SGordon Ross 		 * This ACK is either unsolicited or too late,
38172b35b05SGordon Ross 		 * eg. we timed out the ACK and did it locally.
38272b35b05SGordon Ross 		 */
3836f8336c5SGordon Ross 		status = NT_STATUS_UNSUCCESSFUL;
3846f8336c5SGordon Ross 		goto errout;
3856f8336c5SGordon Ross 	}
3866f8336c5SGordon Ross 
38794047d49SGordon Ross 	/*
38894047d49SGordon Ross 	 * If the new LeaseState has any bits in excess of
38994047d49SGordon Ross 	 * the lease state we sent in the break, error...
39094047d49SGordon Ross 	 */
3917f6a299eSGordon Ross 	if ((LeaseState & ~(lease->ls_breakto)) != 0) {
39294047d49SGordon Ross 		status = NT_STATUS_REQUEST_NOT_ACCEPTED;
39394047d49SGordon Ross 		goto errout;
39494047d49SGordon Ross 	}
39594047d49SGordon Ross 
39672b35b05SGordon Ross 	/*
39772b35b05SGordon Ross 	 * Process the lease break ack.
39872b35b05SGordon Ross 	 *
39972b35b05SGordon Ross 	 * Clear breaking flags before we ack,
40072b35b05SGordon Ross 	 * because ack might set those.
4017f6a299eSGordon Ross 	 * Signal both CVs, out of paranoia.
40272b35b05SGordon Ross 	 */
4037f6a299eSGordon Ross 	ofile->f_oplock.og_breaking = B_FALSE;
4047f6a299eSGordon Ross 	cv_broadcast(&ofile->f_oplock.og_ack_cv);
4057f6a299eSGordon Ross 	lease->ls_breaking = B_FALSE;
40672b35b05SGordon Ross 	cv_broadcast(&lease->ls_ack_cv);
40794047d49SGordon Ross 
40894047d49SGordon Ross 	LeaseState |= OPLOCK_LEVEL_GRANULAR;
40994047d49SGordon Ross 	status = smb_oplock_ack_break(sr, ofile, &LeaseState);
41094047d49SGordon Ross 
41172b35b05SGordon Ross 	ofile->f_oplock.og_state = LeaseState;
4127f6a299eSGordon Ross 	lease->ls_state = LeaseState;
41372b35b05SGordon Ross 	/* ls_epoch does not change here */
41472b35b05SGordon Ross 
41572b35b05SGordon Ross 	if (ofile->dh_persist)
41672b35b05SGordon Ross 		smb2_dh_update_oplock(sr, ofile);
41794047d49SGordon Ross 
41894047d49SGordon Ross errout:
41994047d49SGordon Ross 	sr->smb2_status = status;
42094047d49SGordon Ross 	DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr);
42172b35b05SGordon Ross 
42272b35b05SGordon Ross 	mutex_exit(&node->n_oplock.ol_mutex);
42372b35b05SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
42472b35b05SGordon Ross 
42572b35b05SGordon Ross 	smb2_lease_rele(lease);
42672b35b05SGordon Ross 
42794047d49SGordon Ross 	if (status) {
42894047d49SGordon Ross 		smb2sr_put_error(sr, status);
42994047d49SGordon Ross 		return (SDRC_SUCCESS);
43094047d49SGordon Ross 	}
43194047d49SGordon Ross 
43294047d49SGordon Ross 	/*
43394047d49SGordon Ross 	 * Encode an SMB2 Lease Ack. response
43494047d49SGordon Ross 	 * [MS-SMB2] 2.2.25.2
43594047d49SGordon Ross 	 */
43694047d49SGordon Ross 	LeaseState &= OPLOCK_LEVEL_CACHE_MASK;
43794047d49SGordon Ross 	(void) smb_mbc_encodef(
43894047d49SGordon Ross 	    &sr->reply, "w6.#cl8.",
43994047d49SGordon Ross 	    SSZ_LEASE_ACK,	/* w */
44094047d49SGordon Ross 	    /* reserved		  6. */
44194047d49SGordon Ross 	    UUID_LEN,		/* # */
44272b35b05SGordon Ross 	    olbrk->LeaseKey,	/* c */
44394047d49SGordon Ross 	    LeaseState);	/* l */
44494047d49SGordon Ross 	    /* duration		  8. */
44594047d49SGordon Ross 
44694047d49SGordon Ross 	return (SDRC_SUCCESS);
44794047d49SGordon Ross 
44894047d49SGordon Ross }
44994047d49SGordon Ross 
45094047d49SGordon Ross /*
45194047d49SGordon Ross  * Compose an SMB2 Lease Break Notification packet, including
45294047d49SGordon Ross  * the SMB2 header and everything, in sr->reply.
45394047d49SGordon Ross  * The caller will send it and free the request.
45494047d49SGordon Ross  *
45594047d49SGordon Ross  * [MS-SMB2] 2.2.23.2 Lease Break Notification
45694047d49SGordon Ross  */
45772b35b05SGordon Ross static void
smb2_lease_break_notification(smb_request_t * sr,uint32_t OldLevel,uint32_t NewLevel,uint16_t Epoch,boolean_t AckReq)45872b35b05SGordon Ross smb2_lease_break_notification(smb_request_t *sr,
45972b35b05SGordon Ross     uint32_t OldLevel, uint32_t NewLevel,
46072b35b05SGordon Ross     uint16_t Epoch, boolean_t AckReq)
46194047d49SGordon Ross {
4626f8336c5SGordon Ross 	smb_lease_t *ls = sr->fid_ofile->f_lease;
46372b35b05SGordon Ross 	uint16_t Flags = 0;
46494047d49SGordon Ross 
46594047d49SGordon Ross 	/*
46672b35b05SGordon Ross 	 * Convert internal lease info to SMB2
46794047d49SGordon Ross 	 */
46872b35b05SGordon Ross 	if (AckReq)
46972b35b05SGordon Ross 		Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
47094047d49SGordon Ross 	if (ls->ls_version < 2)
47194047d49SGordon Ross 		Epoch = 0;
47272b35b05SGordon Ross 	OldLevel &= OPLOCK_LEVEL_CACHE_MASK;
47372b35b05SGordon Ross 	NewLevel &= OPLOCK_LEVEL_CACHE_MASK;
47494047d49SGordon Ross 
47594047d49SGordon Ross 	/*
47694047d49SGordon Ross 	 * SMB2 Header
47794047d49SGordon Ross 	 */
47894047d49SGordon Ross 	sr->smb2_cmd_code = SMB2_OPLOCK_BREAK;
47994047d49SGordon Ross 	sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR;
48094047d49SGordon Ross 	sr->smb_tid = 0;
48194047d49SGordon Ross 	sr->smb_pid = 0;
48294047d49SGordon Ross 	sr->smb2_ssnid = 0;
48394047d49SGordon Ross 	sr->smb2_messageid = UINT64_MAX;
48494047d49SGordon Ross 	(void) smb2_encode_header(sr, B_FALSE);
48594047d49SGordon Ross 
48694047d49SGordon Ross 	/*
48794047d49SGordon Ross 	 * SMB2 Oplock Break, variable part
48894047d49SGordon Ross 	 *
48994047d49SGordon Ross 	 * [MS-SMB2] says the current lease state preceeds the
49094047d49SGordon Ross 	 * new lease state, but that looks like an error...
49194047d49SGordon Ross 	 */
49294047d49SGordon Ross 	(void) smb_mbc_encodef(
49394047d49SGordon Ross 	    &sr->reply, "wwl#cll4.4.4.",
49494047d49SGordon Ross 	    SSZ_LEASE_BRK,		/* w */
49594047d49SGordon Ross 	    Epoch,			/* w */
49694047d49SGordon Ross 	    Flags,			/* l */
49794047d49SGordon Ross 	    SMB_LEASE_KEY_SZ,		/* # */
49894047d49SGordon Ross 	    ls->ls_key,			/* c */
49972b35b05SGordon Ross 	    OldLevel,		/* cur.st  l */
50072b35b05SGordon Ross 	    NewLevel);		/* new.st  l */
50194047d49SGordon Ross 	    /* reserved (4.4.4.) */
50294047d49SGordon Ross }
50394047d49SGordon Ross 
50472b35b05SGordon Ross /*
50572b35b05SGordon Ross  * Do our best to send a lease break message to the client.
50672b35b05SGordon Ross  * When we get to multi-channel, this is supposed to try
50772b35b05SGordon Ross  * every channel before giving up.  For now, try every
50872b35b05SGordon Ross  * connected session with an ofile sharing this lease.
50972b35b05SGordon Ross  *
51072b35b05SGordon Ross  * If this ofile has a valid session, try that first.
51172b35b05SGordon Ross  * Otherwise look on the node list for other ofiles with
51272b35b05SGordon Ross  * the same lease and a connected session.
51372b35b05SGordon Ross  */
51472b35b05SGordon Ross static int
lease_send_any_cn(smb_request_t * sr)51572b35b05SGordon Ross lease_send_any_cn(smb_request_t *sr)
51672b35b05SGordon Ross {
51772b35b05SGordon Ross 	smb_ofile_t	*o;
51872b35b05SGordon Ross 	smb_ofile_t	*ofile = sr->fid_ofile;
51972b35b05SGordon Ross 	smb_lease_t	*lease = ofile->f_lease;
52072b35b05SGordon Ross 	smb_node_t	*node = ofile->f_node;
52172b35b05SGordon Ross 	int		rc = ENOTCONN;
52272b35b05SGordon Ross 
52372b35b05SGordon Ross 	/*
52472b35b05SGordon Ross 	 * If the passed oplock ofile has a session,
52572b35b05SGordon Ross 	 * this IF expression will be true.
52672b35b05SGordon Ross 	 */
52772b35b05SGordon Ross 	if (sr->session == ofile->f_session) {
52872b35b05SGordon Ross 		rc = smb_session_send(sr->session, 0, &sr->reply);
52972b35b05SGordon Ross 		if (rc == 0)
53072b35b05SGordon Ross 			return (rc);
53172b35b05SGordon Ross 	}
53272b35b05SGordon Ross 
53372b35b05SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
53472b35b05SGordon Ross 	FOREACH_NODE_OFILE(node, o) {
53572b35b05SGordon Ross 		if (o->f_lease != lease)
53672b35b05SGordon Ross 			continue;
53772b35b05SGordon Ross 		if (smb_ofile_hold(o)) {
53872b35b05SGordon Ross 			/* Has a session. */
53972b35b05SGordon Ross 			rc = smb_session_send(o->f_session, 0, &sr->reply);
54072b35b05SGordon Ross 			smb_llist_post(&node->n_ofile_list, o,
54172b35b05SGordon Ross 			    smb_ofile_release_LL);
54272b35b05SGordon Ross 		}
54372b35b05SGordon Ross 		if (rc == 0)
54472b35b05SGordon Ross 			break;
54572b35b05SGordon Ross 	}
54672b35b05SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
54772b35b05SGordon Ross 
54872b35b05SGordon Ross 	return (rc);
54972b35b05SGordon Ross }
55072b35b05SGordon Ross 
55172b35b05SGordon Ross /*
55272b35b05SGordon Ross  * See smb_llist_post on node->n_ofile_list below.
55372b35b05SGordon Ross  * Can't call smb_ofile_close with that list entered.
55472b35b05SGordon Ross  */
55572b35b05SGordon Ross static void
lease_ofile_close_rele(void * arg)55672b35b05SGordon Ross lease_ofile_close_rele(void *arg)
55772b35b05SGordon Ross {
55872b35b05SGordon Ross 	smb_ofile_t *of = (smb_ofile_t *)arg;
55972b35b05SGordon Ross 
56072b35b05SGordon Ross 	smb_ofile_close(of, 0);
56172b35b05SGordon Ross 	smb_ofile_release(of);
56272b35b05SGordon Ross }
56372b35b05SGordon Ross 
56472b35b05SGordon Ross /*
56572b35b05SGordon Ross  * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break
56672b35b05SGordon Ross  * If no connection, for each Open in Lease.LeaseOpens,
56772b35b05SGordon Ross  * the server MUST close the Open as specified in sec...
56872b35b05SGordon Ross  * for the following cases:
56972b35b05SGordon Ross  * - Open.IsDurable, Open.IsResilient, and
57072b35b05SGordon Ross  *   Open.IsPersistent are all FALSE.
57172b35b05SGordon Ross  * - Open.IsDurable is TRUE and Lease.BreakToLeaseState
57272b35b05SGordon Ross  *   does not contain SMB2_LEASE_HANDLE_CACHING and
57372b35b05SGordon Ross  */
57472b35b05SGordon Ross static void
lease_close_notconn(smb_request_t * sr,uint32_t NewLevel)57572b35b05SGordon Ross lease_close_notconn(smb_request_t *sr, uint32_t NewLevel)
57672b35b05SGordon Ross {
57772b35b05SGordon Ross 	smb_ofile_t	*o;
57872b35b05SGordon Ross 	smb_ofile_t	*ofile = sr->fid_ofile;
57972b35b05SGordon Ross 	smb_lease_t	*lease = ofile->f_lease;
58072b35b05SGordon Ross 	smb_node_t	*node = ofile->f_node;
58172b35b05SGordon Ross 
58272b35b05SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
58372b35b05SGordon Ross 	FOREACH_NODE_OFILE(node, o) {
58472b35b05SGordon Ross 		if (o->f_lease != lease)
58572b35b05SGordon Ross 			continue;
58672b35b05SGordon Ross 		if (o->f_oplock_closing)
58772b35b05SGordon Ross 			continue;
58872b35b05SGordon Ross 		if (o->dh_persist)
58972b35b05SGordon Ross 			continue;
59072b35b05SGordon Ross 		if (o->dh_vers == SMB2_RESILIENT)
59172b35b05SGordon Ross 			continue;
59272b35b05SGordon Ross 		if (o->dh_vers == SMB2_NOT_DURABLE ||
59372b35b05SGordon Ross 		    (NewLevel & OPLOCK_LEVEL_CACHE_HANDLE) == 0) {
59472b35b05SGordon Ross 			if (smb_ofile_hold_olbrk(o)) {
59572b35b05SGordon Ross 				smb_llist_post(&node->n_ofile_list, o,
59672b35b05SGordon Ross 				    lease_ofile_close_rele);
59772b35b05SGordon Ross 			}
59872b35b05SGordon Ross 		}
59972b35b05SGordon Ross 	}
60072b35b05SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
60172b35b05SGordon Ross }
60272b35b05SGordon Ross 
60372b35b05SGordon Ross /*
60472b35b05SGordon Ross  * Send a lease break over the wire, or if we can't,
60572b35b05SGordon Ross  * then process the lease break locally.
60672b35b05SGordon Ross  *
60772b35b05SGordon Ross  * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break
60872b35b05SGordon Ross  *
60972b35b05SGordon Ross  * This is mostly similar to smb2_oplock_send_break()
61072b35b05SGordon Ross  * See top comment there about the design.
61172b35b05SGordon Ross  *
61272b35b05SGordon Ross  * Differences beween a lease break and oplock break:
61372b35b05SGordon Ross  *
61472b35b05SGordon Ross  * Leases are an SMB-level mechanism whereby multiple open
61572b35b05SGordon Ross  * SMB file handles can share an oplock.  All SMB handles
61672b35b05SGordon Ross  * on the lease enjoy the same caching rights.  Down at the
61772b35b05SGordon Ross  * file-system level, just one oplock holds the cache rights
61872b35b05SGordon Ross  * for a lease, but (this is the tricky part) that oplock can
61972b35b05SGordon Ross  * MOVE among the SMB file handles sharing the lease. Such
62072b35b05SGordon Ross  * oplock moves can happen when a handle is closed (if that
62172b35b05SGordon Ross  * handle is the one with the oplock) or when a new open on
62272b35b05SGordon Ross  * the lease causes an upgrade of the caching rights.
62372b35b05SGordon Ross  *
62472b35b05SGordon Ross  * We have to deal here with lease movement because this call
62572b35b05SGordon Ross  * happens asynchronously after the smb_oplock_ind_break call,
62672b35b05SGordon Ross  * meaning that the oplock for the lease may have moved by the
62772b35b05SGordon Ross  * time this runs.  In addition, the ofile holding the oplock
62872b35b05SGordon Ross  * might not be the best one to use to send a lease break.
62972b35b05SGordon Ross  * If the oplock is held by a handle that's "orphaned" and
63072b35b05SGordon Ross  * there are other handles on the lease with active sessions,
63172b35b05SGordon Ross  * we want to send the lease break on an active session.
63272b35b05SGordon Ross  *
63372b35b05SGordon Ross  * Also note: NewLevel (as provided by smb_oplock_ind_break etc.)
63472b35b05SGordon Ross  * does NOT include the GRANULAR flag.  This level is expected to
63572b35b05SGordon Ross  * keep track of how each oplock was acquired (by lease or not)
63672b35b05SGordon Ross  * and put the GRANULAR flag back in when appropriate.
63772b35b05SGordon Ross  */
63872b35b05SGordon Ross void
smb2_lease_send_break(smb_request_t * sr)63972b35b05SGordon Ross smb2_lease_send_break(smb_request_t *sr)
64072b35b05SGordon Ross {
64172b35b05SGordon Ross 	smb_ofile_t	*old_ofile;
64272b35b05SGordon Ross 	smb_ofile_t	*ofile = sr->fid_ofile;
64372b35b05SGordon Ross 	smb_node_t	*node = ofile->f_node;
64472b35b05SGordon Ross 	smb_lease_t	*lease = ofile->f_lease;
64572b35b05SGordon Ross 	smb_arg_olbrk_t	*olbrk = &sr->arg.olbrk;
64672b35b05SGordon Ross 	boolean_t	AckReq = olbrk->AckRequired;
64772b35b05SGordon Ross 	uint32_t	OldLevel = olbrk->OldLevel;
64872b35b05SGordon Ross 	uint32_t	NewLevel = olbrk->NewLevel;
64972b35b05SGordon Ross 	uint32_t	status;
65072b35b05SGordon Ross 	int		rc;
65172b35b05SGordon Ross 
65272b35b05SGordon Ross 	NewLevel |= OPLOCK_LEVEL_GRANULAR;
65372b35b05SGordon Ross 
65472b35b05SGordon Ross 	/*
65572b35b05SGordon Ross 	 * Build the break message in sr->reply.
65672b35b05SGordon Ross 	 * It's free'd in smb_request_free().
65772b35b05SGordon Ross 	 * Always an SMB2 lease here.
65872b35b05SGordon Ross 	 */
65972b35b05SGordon Ross 	sr->reply.max_bytes = MLEN;
66072b35b05SGordon Ross 	smb2_lease_break_notification(sr,
66172b35b05SGordon Ross 	    OldLevel, NewLevel, lease->ls_epoch, AckReq);
66272b35b05SGordon Ross 
66372b35b05SGordon Ross 	/*
66472b35b05SGordon Ross 	 * Try to send the break message to the client,
66572b35b05SGordon Ross 	 * on any connection with this lease.
66672b35b05SGordon Ross 	 */
66772b35b05SGordon Ross 	rc = lease_send_any_cn(sr);
66872b35b05SGordon Ross 	if (rc != 0) {
66972b35b05SGordon Ross 		/*
67072b35b05SGordon Ross 		 * We were unable to send the oplock break request,
67172b35b05SGordon Ross 		 * presumably because the connection is gone.
67272b35b05SGordon Ross 		 * Close uninteresting handles.
67372b35b05SGordon Ross 		 */
67472b35b05SGordon Ross 		lease_close_notconn(sr, NewLevel);
67572b35b05SGordon Ross 		/* Note: some handles may remain on the lease. */
67672b35b05SGordon Ross 		if (!AckReq)
67772b35b05SGordon Ross 			return;
67872b35b05SGordon Ross 		/* Do local Ack below. */
67972b35b05SGordon Ross 	} else {
68072b35b05SGordon Ross 		/*
68172b35b05SGordon Ross 		 * OK, we were able to send the break message.
68272b35b05SGordon Ross 		 * If no ack. required, we're done.
68372b35b05SGordon Ross 		 */
68472b35b05SGordon Ross 		if (!AckReq)
68572b35b05SGordon Ross 			return;
68672b35b05SGordon Ross 
68772b35b05SGordon Ross 		/*
68872b35b05SGordon Ross 		 * We're expecting an ACK.  Wait in this thread
68972b35b05SGordon Ross 		 * so we can log clients that don't respond.
6903b24312dSGordon Ross 		 * Note: this can also fail for other reasons
6913b24312dSGordon Ross 		 * such as client disconnect or server shutdown.
69272b35b05SGordon Ross 		 */
6937f6a299eSGordon Ross 		status = smb_oplock_wait_ack(sr, NewLevel);
69472b35b05SGordon Ross 		if (status == 0)
69572b35b05SGordon Ross 			return;
69672b35b05SGordon Ross 
6973b24312dSGordon Ross 		DTRACE_PROBE2(wait__ack__failed, smb_request_t *, sr,
6983b24312dSGordon Ross 		    uint32_t, status);
69972b35b05SGordon Ross 
70072b35b05SGordon Ross 		/*
70172b35b05SGordon Ross 		 * Will do local ack below.  Note, after timeout,
70272b35b05SGordon Ross 		 * do a break to none or "no caching" regardless
70372b35b05SGordon Ross 		 * of what the passed in cache level was.
70472b35b05SGordon Ross 		 * That means: clear all except GRANULAR.
70572b35b05SGordon Ross 		 */
70672b35b05SGordon Ross 		NewLevel = OPLOCK_LEVEL_GRANULAR;
70772b35b05SGordon Ross 	}
70872b35b05SGordon Ross 
70972b35b05SGordon Ross 	/*
71072b35b05SGordon Ross 	 * Do the ack locally.
71172b35b05SGordon Ross 	 *
71272b35b05SGordon Ross 	 * Find the ofile with the leased oplock
71372b35b05SGordon Ross 	 * (may have moved before we took locks)
71472b35b05SGordon Ross 	 */
71572b35b05SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
71672b35b05SGordon Ross 	mutex_enter(&node->n_oplock.ol_mutex);
71772b35b05SGordon Ross 
71872b35b05SGordon Ross 	old_ofile = ofile;
71972b35b05SGordon Ross 	sr->fid_ofile = NULL;
72072b35b05SGordon Ross 	status = lease_find_oplock(sr, lease);
72172b35b05SGordon Ross 	if (status != 0) {
72272b35b05SGordon Ross 		/* put back old_ofile */
72372b35b05SGordon Ross 		sr->fid_ofile = old_ofile;
72472b35b05SGordon Ross 		goto unlock_out;
72572b35b05SGordon Ross 	}
72672b35b05SGordon Ross 	smb_llist_post(&node->n_ofile_list, old_ofile,
72772b35b05SGordon Ross 	    smb_ofile_release_LL);
72872b35b05SGordon Ross 
72972b35b05SGordon Ross 	ofile = sr->fid_ofile;
73072b35b05SGordon Ross 
73172b35b05SGordon Ross 	/*
73272b35b05SGordon Ross 	 * Now continue like the non-lease code
73372b35b05SGordon Ross 	 */
7347f6a299eSGordon Ross 	ofile->f_oplock.og_breaking = B_FALSE;
7357f6a299eSGordon Ross 	lease->ls_breaking = B_FALSE;
73672b35b05SGordon Ross 	cv_broadcast(&lease->ls_ack_cv);
73772b35b05SGordon Ross 
73872b35b05SGordon Ross 	status = smb_oplock_ack_break(sr, ofile, &NewLevel);
73972b35b05SGordon Ross 
74072b35b05SGordon Ross 	ofile->f_oplock.og_state = NewLevel;
7417f6a299eSGordon Ross 	lease->ls_state = NewLevel;
74272b35b05SGordon Ross 	/* ls_epoch does not change here */
74372b35b05SGordon Ross 
74472b35b05SGordon Ross 	if (ofile->dh_persist)
74572b35b05SGordon Ross 		smb2_dh_update_oplock(sr, ofile);
74672b35b05SGordon Ross 
74772b35b05SGordon Ross unlock_out:
74872b35b05SGordon Ross 	mutex_exit(&node->n_oplock.ol_mutex);
74972b35b05SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
75072b35b05SGordon Ross 
75172b35b05SGordon Ross #ifdef	DEBUG
75272b35b05SGordon Ross 	if (status != 0) {
75372b35b05SGordon Ross 		cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x",
75472b35b05SGordon Ross 		    sr->session->ip_addr_str, status);
75572b35b05SGordon Ross 	}
75672b35b05SGordon Ross #endif
75772b35b05SGordon Ross }
75872b35b05SGordon Ross 
75994047d49SGordon Ross /*
76094047d49SGordon Ross  * Client has an open handle and requests a lease.
76194047d49SGordon Ross  * Convert SMB2 lease request info in to internal form,
76294047d49SGordon Ross  * call common oplock code, convert result to SMB2.
76394047d49SGordon Ross  *
7647f6a299eSGordon Ross  * If necessary, "go async" here (at the end).
76594047d49SGordon Ross  */
76694047d49SGordon Ross void
smb2_lease_acquire(smb_request_t * sr)76794047d49SGordon Ross smb2_lease_acquire(smb_request_t *sr)
76894047d49SGordon Ross {
76994047d49SGordon Ross 	smb_arg_open_t *op = &sr->arg.open;
77094047d49SGordon Ross 	smb_ofile_t *ofile = sr->fid_ofile;
77194047d49SGordon Ross 	smb_lease_t *lease = ofile->f_lease;
77272b35b05SGordon Ross 	smb_node_t *node = ofile->f_node;
77394047d49SGordon Ross 	uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED;
77494047d49SGordon Ross 	uint32_t have, want; /* lease flags */
77594047d49SGordon Ross 	boolean_t NewGrant = B_FALSE;
77694047d49SGordon Ross 
77794047d49SGordon Ross 	/* Only disk trees get oplocks. */
77894047d49SGordon Ross 	ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE);
77994047d49SGordon Ross 
78094047d49SGordon Ross 	/*
78194047d49SGordon Ross 	 * Only plain files (for now).
78294047d49SGordon Ross 	 * Later, test SMB2_CAP_DIRECTORY_LEASING
78394047d49SGordon Ross 	 */
78494047d49SGordon Ross 	if (!smb_node_is_file(ofile->f_node)) {
78594047d49SGordon Ross 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
78694047d49SGordon Ross 		return;
78794047d49SGordon Ross 	}
78894047d49SGordon Ross 
78994047d49SGordon Ross 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
79094047d49SGordon Ross 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
79194047d49SGordon Ross 		return;
79294047d49SGordon Ross 	}
79394047d49SGordon Ross 
79494047d49SGordon Ross 	/*
79594047d49SGordon Ross 	 * SMB2: Convert to internal form.
79694047d49SGordon Ross 	 * Caller should have setup the lease.
79794047d49SGordon Ross 	 */
79894047d49SGordon Ross 	ASSERT(op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE);
79994047d49SGordon Ross 	ASSERT(lease != NULL);
80094047d49SGordon Ross 	if (lease == NULL) {
80194047d49SGordon Ross 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
80294047d49SGordon Ross 		return;
80394047d49SGordon Ross 	}
80494047d49SGordon Ross 	op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
80594047d49SGordon Ross 	    (op->lease_state & CACHE_RWH);
80694047d49SGordon Ross 
80794047d49SGordon Ross 	/*
80894047d49SGordon Ross 	 * Tree options may force shared oplocks,
80994047d49SGordon Ross 	 * in which case we reduce the request.
81094047d49SGordon Ross 	 */
81194047d49SGordon Ross 	if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
81294047d49SGordon Ross 		op->op_oplock_state &= ~WRITE_CACHING;
81394047d49SGordon Ross 	}
81494047d49SGordon Ross 
81572b35b05SGordon Ross 	/*
81672b35b05SGordon Ross 	 * Using the "Locks Held" (LH) variant of smb_oplock_request
81772b35b05SGordon Ross 	 * below so things won't change underfoot.
81872b35b05SGordon Ross 	 */
81972b35b05SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
82072b35b05SGordon Ross 	mutex_enter(&node->n_oplock.ol_mutex);
82172b35b05SGordon Ross 
82294047d49SGordon Ross 	/*
8237f6a299eSGordon Ross 	 * MS-SMB2 3.3.5.9.8 and 3.3.5.9.11 Lease (V2) create contexts
82494047d49SGordon Ross 	 *
8257f6a299eSGordon Ross 	 * If the caching state requested in LeaseState of the (create ctx)
8267f6a299eSGordon Ross 	 * is not a superset of Lease.LeaseState or if Lease.Breaking is TRUE,
8277f6a299eSGordon Ross 	 * the server MUST NOT promote Lease.LeaseState. If the lease state
8287f6a299eSGordon Ross 	 * requested is a superset of Lease.LeaseState and Lease.Breaking is
8297f6a299eSGordon Ross 	 * FALSE, the server MUST request promotion of the lease state from
8307f6a299eSGordon Ross 	 * the underlying object store to the new caching state.
83194047d49SGordon Ross 	 */
83294047d49SGordon Ross 	have = lease->ls_state & CACHE_RWH;
83394047d49SGordon Ross 	want = op->op_oplock_state & CACHE_RWH;
8347f6a299eSGordon Ross 	if ((have & ~want) != 0 || lease->ls_breaking) {
83594047d49SGordon Ross 		op->op_oplock_state = have |
83694047d49SGordon Ross 		    OPLOCK_LEVEL_GRANULAR;
83794047d49SGordon Ross 		goto done;
83894047d49SGordon Ross 	}
83994047d49SGordon Ross 
84094047d49SGordon Ross 	/*
84194047d49SGordon Ross 	 * Handle oplock requests in three parts:
84294047d49SGordon Ross 	 *	a: Requests with WRITE_CACHING
84394047d49SGordon Ross 	 *	b: Requests with HANDLE_CACHING
84494047d49SGordon Ross 	 *	c: Requests with READ_CACHING
84594047d49SGordon Ross 	 * reducing the request before b and c.
84694047d49SGordon Ross 	 *
84794047d49SGordon Ross 	 * In each: first check if the lease grants the
84894047d49SGordon Ross 	 * (possibly reduced) request, in which case we
84994047d49SGordon Ross 	 * leave the lease unchanged and return what's
85094047d49SGordon Ross 	 * granted by the lease.  Otherwise, try to get
85194047d49SGordon Ross 	 * the oplock, and if the succeeds, wait for any
85294047d49SGordon Ross 	 * breaks, update the lease, and return.
85394047d49SGordon Ross 	 */
85494047d49SGordon Ross 
85594047d49SGordon Ross 	/*
85694047d49SGordon Ross 	 * Try exclusive (request is RW or RWH)
85794047d49SGordon Ross 	 */
85894047d49SGordon Ross 	if ((op->op_oplock_state & WRITE_CACHING) != 0) {
8597f6a299eSGordon Ross 		/* Alread checked (want & ~have) */
86094047d49SGordon Ross 
86172b35b05SGordon Ross 		status = smb_oplock_request_LH(sr, ofile,
86294047d49SGordon Ross 		    &op->op_oplock_state);
86394047d49SGordon Ross 		if (status == NT_STATUS_SUCCESS ||
86494047d49SGordon Ross 		    status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
86594047d49SGordon Ross 			NewGrant = B_TRUE;
86694047d49SGordon Ross 			goto done;
86794047d49SGordon Ross 		}
86894047d49SGordon Ross 
86994047d49SGordon Ross 		/*
87094047d49SGordon Ross 		 * We did not get the exclusive oplock.
87194047d49SGordon Ross 		 *
87294047d49SGordon Ross 		 * There are odd rules about lease upgrade.
87394047d49SGordon Ross 		 * If the existing lease grants R and the
87494047d49SGordon Ross 		 * client fails to upgrade it to "RWH"
87594047d49SGordon Ross 		 * (presumably due to handle conflicts)
87694047d49SGordon Ross 		 * then just return the existing lease,
87794047d49SGordon Ross 		 * even though upgrade to RH would work.
87894047d49SGordon Ross 		 */
87994047d49SGordon Ross 		if (have != 0) {
88094047d49SGordon Ross 			op->op_oplock_state = have |
88194047d49SGordon Ross 			    OPLOCK_LEVEL_GRANULAR;
88294047d49SGordon Ross 			goto done;
88394047d49SGordon Ross 		}
88494047d49SGordon Ross 
88594047d49SGordon Ross 		/*
88694047d49SGordon Ross 		 * Keep trying without write.
88794047d49SGordon Ross 		 * Need to re-init op_oplock_state
88894047d49SGordon Ross 		 */
88994047d49SGordon Ross 		op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
89094047d49SGordon Ross 		    (op->lease_state & CACHE_RH);
89194047d49SGordon Ross 	}
89294047d49SGordon Ross 
89394047d49SGordon Ross 	/*
89494047d49SGordon Ross 	 * Try shared ("RH")
89594047d49SGordon Ross 	 */
89694047d49SGordon Ross 	if ((op->op_oplock_state & HANDLE_CACHING) != 0) {
89794047d49SGordon Ross 		want = op->op_oplock_state & CACHE_RWH;
8987f6a299eSGordon Ross 		if ((want & ~have) == 0)
89994047d49SGordon Ross 			goto done;
90094047d49SGordon Ross 
90172b35b05SGordon Ross 		status = smb_oplock_request_LH(sr, ofile,
90294047d49SGordon Ross 		    &op->op_oplock_state);
90394047d49SGordon Ross 		if (status == NT_STATUS_SUCCESS ||
90494047d49SGordon Ross 		    status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
90594047d49SGordon Ross 			NewGrant = B_TRUE;
90694047d49SGordon Ross 			goto done;
90794047d49SGordon Ross 		}
90894047d49SGordon Ross 
90994047d49SGordon Ross 		/*
91094047d49SGordon Ross 		 * We did not get "RH", probably because
91194047d49SGordon Ross 		 * ther were (old style) Level II oplocks.
91294047d49SGordon Ross 		 * Continue, try for just read.
91372b35b05SGordon Ross 		 * Again, re-init op_oplock_state
91494047d49SGordon Ross 		 */
91594047d49SGordon Ross 		op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
91694047d49SGordon Ross 		    (op->lease_state & CACHE_R);
91794047d49SGordon Ross 	}
91894047d49SGordon Ross 
91994047d49SGordon Ross 	/*
92094047d49SGordon Ross 	 * Try shared ("R")
92194047d49SGordon Ross 	 */
92294047d49SGordon Ross 	if ((op->op_oplock_state & READ_CACHING) != 0) {
92394047d49SGordon Ross 		want = op->op_oplock_state & CACHE_RWH;
9247f6a299eSGordon Ross 		if ((want & ~have) == 0)
92594047d49SGordon Ross 			goto done;
92694047d49SGordon Ross 
92772b35b05SGordon Ross 		status = smb_oplock_request_LH(sr, ofile,
92894047d49SGordon Ross 		    &op->op_oplock_state);
92994047d49SGordon Ross 		if (status == NT_STATUS_SUCCESS ||
93094047d49SGordon Ross 		    status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
93194047d49SGordon Ross 			NewGrant = B_TRUE;
93294047d49SGordon Ross 			goto done;
93394047d49SGordon Ross 		}
93494047d49SGordon Ross 
93594047d49SGordon Ross 		/*
93694047d49SGordon Ross 		 * We did not get "R".
93794047d49SGordon Ross 		 * Fall into "none".
93894047d49SGordon Ross 		 */
93994047d49SGordon Ross 	}
94094047d49SGordon Ross 
94194047d49SGordon Ross 	/*
94294047d49SGordon Ross 	 * None of the above were able to get an oplock.
94394047d49SGordon Ross 	 * The lease has no caching rights, and we didn't
94494047d49SGordon Ross 	 * add any in this request.  Return it as-is.
94594047d49SGordon Ross 	 */
94694047d49SGordon Ross 	op->op_oplock_state = OPLOCK_LEVEL_GRANULAR;
94794047d49SGordon Ross 
94894047d49SGordon Ross done:
94972b35b05SGordon Ross 	/*
95072b35b05SGordon Ross 	 * Only success cases get here
95172b35b05SGordon Ross 	 */
95272b35b05SGordon Ross 
95372b35b05SGordon Ross 	/*
95472b35b05SGordon Ross 	 * Keep track of what we got (ofile->f_oplock.og_state etc)
95572b35b05SGordon Ross 	 * so we'll know what we had when sending a break later.
95672b35b05SGordon Ross 	 * Also keep a copy of some things in the lease.
95772b35b05SGordon Ross 	 *
95872b35b05SGordon Ross 	 * Not using og_dialect here, as ofile->f_lease tells us
95972b35b05SGordon Ross 	 * this has to be using granular oplocks.
96072b35b05SGordon Ross 	 */
96194047d49SGordon Ross 	if (NewGrant) {
9627f6a299eSGordon Ross 		ofile->f_oplock.og_state   = op->op_oplock_state;
9637f6a299eSGordon Ross 		ofile->f_oplock.og_breakto = op->op_oplock_state;
9647f6a299eSGordon Ross 		ofile->f_oplock.og_breaking = B_FALSE;
96594047d49SGordon Ross 
96672b35b05SGordon Ross 		lease->ls_oplock_ofile = ofile;
9677f6a299eSGordon Ross 		lease->ls_state   = ofile->f_oplock.og_state;
9687f6a299eSGordon Ross 		lease->ls_breakto = ofile->f_oplock.og_breakto;
96972b35b05SGordon Ross 		lease->ls_breaking = B_FALSE;
97094047d49SGordon Ross 		lease->ls_epoch++;
97172b35b05SGordon Ross 
97272b35b05SGordon Ross 		if (ofile->dh_persist) {
97372b35b05SGordon Ross 			smb2_dh_update_oplock(sr, ofile);
97472b35b05SGordon Ross 		}
97594047d49SGordon Ross 	}
97694047d49SGordon Ross 
97794047d49SGordon Ross 	/*
97894047d49SGordon Ross 	 * Convert internal oplock state to SMB2
97994047d49SGordon Ross 	 */
98094047d49SGordon Ross 	op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
98194047d49SGordon Ross 	op->lease_state = lease->ls_state & CACHE_RWH;
98294047d49SGordon Ross 	op->lease_flags = (lease->ls_breaking != 0) ?
98394047d49SGordon Ross 	    SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0;
98494047d49SGordon Ross 	op->lease_epoch = lease->ls_epoch;
98594047d49SGordon Ross 	op->lease_version = lease->ls_version;
98672b35b05SGordon Ross 
98772b35b05SGordon Ross 	/*
98872b35b05SGordon Ross 	 * End of lock-held region
98972b35b05SGordon Ross 	 */
99072b35b05SGordon Ross 	mutex_exit(&node->n_oplock.ol_mutex);
99172b35b05SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
99272b35b05SGordon Ross 
99372b35b05SGordon Ross 	/*
99472b35b05SGordon Ross 	 * After a new oplock grant, the status return
99572b35b05SGordon Ross 	 * may indicate we need to wait for breaks.
99672b35b05SGordon Ross 	 */
99772b35b05SGordon Ross 	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
99872b35b05SGordon Ross 		(void) smb2sr_go_async(sr);
99972b35b05SGordon Ross 		(void) smb_oplock_wait_break(sr, ofile->f_node, 0);
100072b35b05SGordon Ross 	}
100194047d49SGordon Ross }
100294047d49SGordon Ross 
100394047d49SGordon Ross /*
100494047d49SGordon Ross  * This ofile has a lease and is about to close.
100594047d49SGordon Ross  * Called by smb_ofile_close when there's a lease.
100694047d49SGordon Ross  *
10077f6a299eSGordon Ross  * Note that a client may close an ofile in response to an
10087f6a299eSGordon Ross  * oplock break or lease break intead of doing an Ack break,
10097f6a299eSGordon Ross  * so this must wake anything that might be waiting on an ack
10107f6a299eSGordon Ross  * when the last close of a lease happens.
10117f6a299eSGordon Ross  *
101294047d49SGordon Ross  * With leases, just one ofile on a lease owns the oplock.
101394047d49SGordon Ross  * If an ofile with a lease is closed and it's the one that
101494047d49SGordon Ross  * owns the oplock, try to move the oplock to another ofile
101594047d49SGordon Ross  * on the same lease.
101672b35b05SGordon Ross  *
101772b35b05SGordon Ross  * Would prefer that we could just use smb_ofile_hold_olbrk
101872b35b05SGordon Ross  * to select a suitable destination for the move, but this
101972b35b05SGordon Ross  * is called while holding the owning tree ofile list etc
102072b35b05SGordon Ross  * which can cause deadlock as described in illumos 13850
102172b35b05SGordon Ross  * when smb_ofile_hold_olbrk has to wait.  XXX todo
102294047d49SGordon Ross  */
102394047d49SGordon Ross void
smb2_lease_ofile_close(smb_ofile_t * ofile)102494047d49SGordon Ross smb2_lease_ofile_close(smb_ofile_t *ofile)
102594047d49SGordon Ross {
102694047d49SGordon Ross 	smb_node_t *node = ofile->f_node;
102794047d49SGordon Ross 	smb_lease_t *lease = ofile->f_lease;
102894047d49SGordon Ross 	smb_ofile_t *o;
102994047d49SGordon Ross 
10301f0845f1SGordon Ross 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
10311f0845f1SGordon Ross 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
10321f0845f1SGordon Ross 
10337f6a299eSGordon Ross #ifdef	DEBUG
10347f6a299eSGordon Ross 	FOREACH_NODE_OFILE(node, o) {
10357f6a299eSGordon Ross 		DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
10367f6a299eSGordon Ross 	}
10377f6a299eSGordon Ross #endif
10387f6a299eSGordon Ross 
103994047d49SGordon Ross 	/*
104094047d49SGordon Ross 	 * If this ofile was not the oplock owner for this lease,
104194047d49SGordon Ross 	 * we can leave things as they are.
104294047d49SGordon Ross 	 */
104394047d49SGordon Ross 	if (lease->ls_oplock_ofile != ofile)
104494047d49SGordon Ross 		return;
104594047d49SGordon Ross 
104694047d49SGordon Ross 	/*
104794047d49SGordon Ross 	 * Find another ofile to which we can move the oplock.
104872b35b05SGordon Ross 	 * First try for one that's open.  Usually find one.
104994047d49SGordon Ross 	 */
105094047d49SGordon Ross 	FOREACH_NODE_OFILE(node, o) {
105194047d49SGordon Ross 		if (o == ofile)
105294047d49SGordon Ross 			continue;
105394047d49SGordon Ross 		if (o->f_lease != lease)
105494047d49SGordon Ross 			continue;
10556f8336c5SGordon Ross 		if (o->f_oplock_closing)
10561f0845f1SGordon Ross 			continue;
105772b35b05SGordon Ross 
105872b35b05SGordon Ross 		mutex_enter(&o->f_mutex);
105972b35b05SGordon Ross 		if (o->f_state == SMB_OFILE_STATE_OPEN) {
106072b35b05SGordon Ross 			smb_oplock_move(node, ofile, o);
106172b35b05SGordon Ross 			lease->ls_oplock_ofile = o;
106272b35b05SGordon Ross 			mutex_exit(&o->f_mutex);
106372b35b05SGordon Ross 			return;
106472b35b05SGordon Ross 		}
106572b35b05SGordon Ross 		mutex_exit(&o->f_mutex);
106694047d49SGordon Ross 	}
106772b35b05SGordon Ross 
106872b35b05SGordon Ross 	/*
106972b35b05SGordon Ross 	 * Now try for one that's orphaned etc.
107072b35b05SGordon Ross 	 */
107172b35b05SGordon Ross 	FOREACH_NODE_OFILE(node, o) {
107272b35b05SGordon Ross 		if (o == ofile)
107372b35b05SGordon Ross 			continue;
107472b35b05SGordon Ross 		if (o->f_lease != lease)
107572b35b05SGordon Ross 			continue;
107672b35b05SGordon Ross 		if (o->f_oplock_closing)
107772b35b05SGordon Ross 			continue;
107872b35b05SGordon Ross 
107972b35b05SGordon Ross 		/*
108072b35b05SGordon Ross 		 * Allow most states as seen in smb_ofile_hold_olbrk
108172b35b05SGordon Ross 		 * without waiting for "_reconnect" or "_saving".
108272b35b05SGordon Ross 		 * Skip "_expired" because that's about to close.
108372b35b05SGordon Ross 		 * This is OK because just swapping the oplock state
108472b35b05SGordon Ross 		 * between two ofiles does not interfere with the
108572b35b05SGordon Ross 		 * dh_save or reconnect code paths.
108672b35b05SGordon Ross 		 */
108772b35b05SGordon Ross 		mutex_enter(&o->f_mutex);
108872b35b05SGordon Ross 		switch (o->f_state) {
108972b35b05SGordon Ross 		case SMB_OFILE_STATE_OPEN:
109072b35b05SGordon Ross 		case SMB_OFILE_STATE_SAVE_DH:
109172b35b05SGordon Ross 		case SMB_OFILE_STATE_SAVING:
109272b35b05SGordon Ross 		case SMB_OFILE_STATE_ORPHANED:
109372b35b05SGordon Ross 		case SMB_OFILE_STATE_RECONNECT:
109472b35b05SGordon Ross 			smb_oplock_move(node, ofile, o);
109572b35b05SGordon Ross 			lease->ls_oplock_ofile = o;
109672b35b05SGordon Ross 			mutex_exit(&o->f_mutex);
109772b35b05SGordon Ross 			return;
109872b35b05SGordon Ross 		}
109972b35b05SGordon Ross 		mutex_exit(&o->f_mutex);
110094047d49SGordon Ross 	}
110194047d49SGordon Ross 
110272b35b05SGordon Ross 	/*
110372b35b05SGordon Ross 	 * Normal for last close on a lease.
110472b35b05SGordon Ross 	 * Wakeup ACK waiters too.
110572b35b05SGordon Ross 	 */
110672b35b05SGordon Ross 	lease->ls_state = 0;
11077f6a299eSGordon Ross 	lease->ls_breakto = 0;
11087f6a299eSGordon Ross 	lease->ls_breaking = B_FALSE;
110972b35b05SGordon Ross 	cv_broadcast(&lease->ls_ack_cv);
111072b35b05SGordon Ross 
111172b35b05SGordon Ross 	lease->ls_oplock_ofile = NULL;
111294047d49SGordon Ross }
1113