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.
14a5a9a6bbSGordon Ross  * Copyright 2022 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
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
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 
5594047d49SGordon Ross static void
5694047d49SGordon Ross smb2_lease_hold(smb_lease_t *ls)
5794047d49SGordon Ross {
5894047d49SGordon Ross 	mutex_enter(&ls->ls_mutex);
5994047d49SGordon Ross 	ls->ls_refcnt++;
6094047d49SGordon Ross 	mutex_exit(&ls->ls_mutex);
6194047d49SGordon Ross }
6294047d49SGordon Ross 
63*72b35b05SGordon Ross static void
64*72b35b05SGordon Ross lease_destroy(smb_lease_t *ls)
65*72b35b05SGordon Ross {
66*72b35b05SGordon Ross 	smb_node_release(ls->ls_node);
67*72b35b05SGordon Ross 	mutex_destroy(&ls->ls_mutex);
68*72b35b05SGordon Ross 	kmem_cache_free(smb_lease_cache, ls);
69*72b35b05SGordon Ross }
70*72b35b05SGordon Ross 
7194047d49SGordon Ross void
7294047d49SGordon Ross smb2_lease_rele(smb_lease_t *ls)
7394047d49SGordon Ross {
7494047d49SGordon Ross 	smb_llist_t *bucket;
75*72b35b05SGordon Ross 	boolean_t destroy = B_FALSE;
7694047d49SGordon Ross 
7794047d49SGordon Ross 	mutex_enter(&ls->ls_mutex);
7894047d49SGordon Ross 	ls->ls_refcnt--;
7994047d49SGordon Ross 	if (ls->ls_refcnt != 0) {
8094047d49SGordon Ross 		mutex_exit(&ls->ls_mutex);
8194047d49SGordon Ross 		return;
8294047d49SGordon Ross 	}
8394047d49SGordon Ross 	mutex_exit(&ls->ls_mutex);
8494047d49SGordon Ross 
8594047d49SGordon Ross 	/*
8694047d49SGordon Ross 	 * Get the list lock, then re-check the refcnt
8794047d49SGordon Ross 	 * and if it's still zero, unlink & destroy.
8894047d49SGordon Ross 	 */
8994047d49SGordon Ross 	bucket = ls->ls_bucket;
9094047d49SGordon Ross 	smb_llist_enter(bucket, RW_WRITER);
9194047d49SGordon Ross 
9294047d49SGordon Ross 	mutex_enter(&ls->ls_mutex);
9394047d49SGordon Ross 	if (ls->ls_refcnt == 0) {
94*72b35b05SGordon Ross 		smb_llist_remove(bucket, ls);
95*72b35b05SGordon Ross 		destroy = B_TRUE;
9694047d49SGordon Ross 	}
97*72b35b05SGordon Ross 	mutex_exit(&ls->ls_mutex);
9894047d49SGordon Ross 
9994047d49SGordon Ross 	smb_llist_exit(bucket);
100*72b35b05SGordon Ross 
101*72b35b05SGordon Ross 	if (destroy) {
102*72b35b05SGordon Ross 		lease_destroy(ls);
103*72b35b05SGordon Ross 	}
10494047d49SGordon Ross }
10594047d49SGordon Ross 
10694047d49SGordon Ross /*
10794047d49SGordon Ross  * Compute a hash from a uuid
10894047d49SGordon Ross  * Based on mod_hash_bystr()
10994047d49SGordon Ross  */
11094047d49SGordon Ross static uint_t
11194047d49SGordon Ross smb_hash_uuid(const uint8_t *uuid)
11294047d49SGordon Ross {
11394047d49SGordon Ross 	char *k = (char *)uuid;
11494047d49SGordon Ross 	uint_t hash = 0;
11594047d49SGordon Ross 	uint_t g;
11694047d49SGordon Ross 	int i;
11794047d49SGordon Ross 
11894047d49SGordon Ross 	ASSERT(k);
11994047d49SGordon Ross 	for (i = 0; i < UUID_LEN; i++) {
12094047d49SGordon Ross 		hash = (hash << 4) + k[i];
12194047d49SGordon Ross 		if ((g = (hash & 0xf0000000)) != 0) {
12294047d49SGordon Ross 			hash ^= (g >> 24);
12394047d49SGordon Ross 			hash ^= g;
12494047d49SGordon Ross 		}
12594047d49SGordon Ross 	}
12694047d49SGordon Ross 	return (hash);
12794047d49SGordon Ross }
12894047d49SGordon Ross 
12994047d49SGordon Ross /*
13094047d49SGordon Ross  * Add or update a lease table entry for a new ofile.
13194047d49SGordon Ross  * (in the per-session lease table)
13294047d49SGordon Ross  * See [MS-SMB2] 3.3.5.9.8
13394047d49SGordon Ross  * Handling the SMB2_CREATE_REQUEST_LEASE Create Context
13494047d49SGordon Ross  */
13594047d49SGordon Ross uint32_t
1368d94f651SGordon Ross smb2_lease_create(smb_request_t *sr, uint8_t *clnt)
13794047d49SGordon Ross {
13894047d49SGordon Ross 	smb_arg_open_t *op = &sr->arg.open;
13994047d49SGordon Ross 	uint8_t *key = op->lease_key;
14094047d49SGordon Ross 	smb_ofile_t *of = sr->fid_ofile;
14194047d49SGordon Ross 	smb_hash_t *ht = sr->sr_server->sv_lease_ht;
14294047d49SGordon Ross 	smb_llist_t *bucket;
14394047d49SGordon Ross 	smb_lease_t *lease;
14494047d49SGordon Ross 	smb_lease_t *newlease;
14594047d49SGordon Ross 	size_t hashkey;
14694047d49SGordon Ross 	uint32_t status = NT_STATUS_INVALID_PARAMETER;
14794047d49SGordon Ross 
14894047d49SGordon Ross 	if (bcmp(key, lease_zero, UUID_LEN) == 0)
14994047d49SGordon Ross 		return (status);
15094047d49SGordon Ross 
15194047d49SGordon Ross 	/*
15294047d49SGordon Ross 	 * Find or create, and add a ref for the new ofile.
15394047d49SGordon Ross 	 */
15494047d49SGordon Ross 	hashkey = smb_hash_uuid(key);
15594047d49SGordon Ross 	hashkey &= (ht->num_buckets - 1);
15694047d49SGordon Ross 	bucket = &ht->buckets[hashkey].b_list;
15794047d49SGordon Ross 
15894047d49SGordon Ross 	newlease = kmem_cache_alloc(smb_lease_cache, KM_SLEEP);
15994047d49SGordon Ross 	bzero(newlease, sizeof (smb_lease_t));
16094047d49SGordon Ross 	mutex_init(&newlease->ls_mutex, NULL, MUTEX_DEFAULT, NULL);
16194047d49SGordon Ross 	newlease->ls_bucket = bucket;
16294047d49SGordon Ross 	newlease->ls_node = of->f_node;
163*72b35b05SGordon Ross 	smb_node_ref(newlease->ls_node);
16494047d49SGordon Ross 	newlease->ls_refcnt = 1;
16594047d49SGordon Ross 	newlease->ls_epoch = op->lease_epoch;
16694047d49SGordon Ross 	newlease->ls_version = op->lease_version;
16794047d49SGordon Ross 	bcopy(key, newlease->ls_key, UUID_LEN);
16894047d49SGordon Ross 	bcopy(clnt, newlease->ls_clnt, UUID_LEN);
16994047d49SGordon Ross 
17094047d49SGordon Ross 	smb_llist_enter(bucket, RW_WRITER);
17194047d49SGordon Ross 	for (lease = smb_llist_head(bucket); lease != NULL;
17294047d49SGordon Ross 	    lease = smb_llist_next(bucket, lease)) {
17394047d49SGordon Ross 		/*
17494047d49SGordon Ross 		 * Looking for this lease ID, on a node
17594047d49SGordon Ross 		 * that's not being deleted.
17694047d49SGordon Ross 		 */
17794047d49SGordon Ross 		if (bcmp(lease->ls_key, key, UUID_LEN) == 0 &&
17894047d49SGordon Ross 		    bcmp(lease->ls_clnt, clnt, UUID_LEN) == 0 &&
17994047d49SGordon Ross 		    (lease->ls_node->flags & NODE_FLAGS_DELETING) == 0)
18094047d49SGordon Ross 			break;
18194047d49SGordon Ross 	}
18294047d49SGordon Ross 	if (lease != NULL) {
18394047d49SGordon Ross 		/*
18494047d49SGordon Ross 		 * Found existing lease.  Make sure it refers to
18594047d49SGordon Ross 		 * the same node...
18694047d49SGordon Ross 		 */
18794047d49SGordon Ross 		if (lease->ls_node == of->f_node) {
18894047d49SGordon Ross 			smb2_lease_hold(lease);
18994047d49SGordon Ross 		} else {
19094047d49SGordon Ross 			/* Same lease ID, different node! */
19194047d49SGordon Ross #ifdef DEBUG
19294047d49SGordon Ross 			cmn_err(CE_NOTE, "new lease on node %p (%s) "
19394047d49SGordon Ross 			    "conflicts with existing node %p (%s)",
19494047d49SGordon Ross 			    (void *) of->f_node,
19594047d49SGordon Ross 			    of->f_node->od_name,
19694047d49SGordon Ross 			    (void *) lease->ls_node,
19794047d49SGordon Ross 			    lease->ls_node->od_name);
19894047d49SGordon Ross #endif
199a5a9a6bbSGordon Ross 			DTRACE_PROBE2(dup_lease, smb_request_t *, sr,
200a5a9a6bbSGordon Ross 			    smb_lease_t *, lease);
20194047d49SGordon Ross 			lease = NULL; /* error */
20294047d49SGordon Ross 		}
20394047d49SGordon Ross 	} else {
20494047d49SGordon Ross 		lease = newlease;
20594047d49SGordon Ross 		smb_llist_insert_head(bucket, lease);
20694047d49SGordon Ross 		newlease = NULL; /* don't free */
20794047d49SGordon Ross 	}
20894047d49SGordon Ross 	smb_llist_exit(bucket);
20994047d49SGordon Ross 
21094047d49SGordon Ross 	if (newlease != NULL) {
211*72b35b05SGordon Ross 		lease_destroy(newlease);
21294047d49SGordon Ross 	}
21394047d49SGordon Ross 
21494047d49SGordon Ross 	if (lease != NULL) {
21594047d49SGordon Ross 		of->f_lease = lease;
21694047d49SGordon Ross 		status = NT_STATUS_SUCCESS;
21794047d49SGordon Ross 	}
21894047d49SGordon Ross 
21994047d49SGordon Ross 	return (status);
22094047d49SGordon Ross }
22194047d49SGordon Ross 
22294047d49SGordon Ross /*
22394047d49SGordon Ross  * Find the lease for a given: client_uuid, lease_key
22494047d49SGordon Ross  * Returns the lease with a new ref.
22594047d49SGordon Ross  */
226*72b35b05SGordon Ross static smb_lease_t *
227*72b35b05SGordon Ross lease_lookup(smb_request_t *sr, uint8_t *lease_key)
22894047d49SGordon Ross {
229*72b35b05SGordon Ross 	smb_server_t *sv = sr->sr_server;
230*72b35b05SGordon Ross 	uint8_t *clnt_uuid = sr->session->clnt_uuid;
23194047d49SGordon Ross 	smb_hash_t *ht = sv->sv_lease_ht;
23294047d49SGordon Ross 	smb_llist_t *bucket;
23394047d49SGordon Ross 	smb_lease_t *lease;
23494047d49SGordon Ross 	size_t hashkey;
23594047d49SGordon Ross 
23694047d49SGordon Ross 	hashkey = smb_hash_uuid(lease_key);
23794047d49SGordon Ross 	hashkey &= (ht->num_buckets - 1);
23894047d49SGordon Ross 	bucket = &ht->buckets[hashkey].b_list;
23994047d49SGordon Ross 
24094047d49SGordon Ross 	smb_llist_enter(bucket, RW_READER);
24194047d49SGordon Ross 	lease = smb_llist_head(bucket);
24294047d49SGordon Ross 	while (lease != NULL) {
24394047d49SGordon Ross 		if (bcmp(lease->ls_key, lease_key, UUID_LEN) == 0 &&
24494047d49SGordon Ross 		    bcmp(lease->ls_clnt, clnt_uuid, UUID_LEN) == 0) {
24594047d49SGordon Ross 			smb2_lease_hold(lease);
24694047d49SGordon Ross 			break;
24794047d49SGordon Ross 		}
24894047d49SGordon Ross 		lease = smb_llist_next(bucket, lease);
24994047d49SGordon Ross 	}
25094047d49SGordon Ross 	smb_llist_exit(bucket);
25194047d49SGordon Ross 
25294047d49SGordon Ross 	return (lease);
25394047d49SGordon Ross }
25494047d49SGordon Ross 
25594047d49SGordon Ross /*
256*72b35b05SGordon Ross  * Find the oplock smb_ofile_t for the specified lease.
257*72b35b05SGordon Ross  * If no such ofile, NT_STATUS_UNSUCCESSFUL.
25894047d49SGordon Ross  * On success, ofile (held) in sr->fid_ofile.
25994047d49SGordon Ross  */
26094047d49SGordon Ross static uint32_t
261*72b35b05SGordon Ross lease_find_oplock(smb_request_t *sr, smb_lease_t *lease)
26294047d49SGordon Ross {
263*72b35b05SGordon Ross 	smb_node_t	*node = lease->ls_node;
26494047d49SGordon Ross 	smb_ofile_t	*o;
265*72b35b05SGordon Ross 	uint32_t	status = NT_STATUS_UNSUCCESSFUL;
26694047d49SGordon Ross 
267*72b35b05SGordon Ross 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
268*72b35b05SGordon Ross 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
269*72b35b05SGordon Ross 	ASSERT(sr->fid_ofile == NULL);
27094047d49SGordon Ross 
271*72b35b05SGordon Ross 	FOREACH_NODE_OFILE(node, o) {
272*72b35b05SGordon Ross 		if (o->f_lease != lease)
273*72b35b05SGordon Ross 			continue;
274*72b35b05SGordon Ross 		if (o != lease->ls_oplock_ofile)
275*72b35b05SGordon Ross 			continue;
27694047d49SGordon Ross 		/*
277*72b35b05SGordon Ross 		 * Found the ofile holding the oplock
278*72b35b05SGordon Ross 		 * This hold released in smb_request_free
27994047d49SGordon Ross 		 */
280*72b35b05SGordon Ross 		if (smb_ofile_hold_olbrk(o)) {
28194047d49SGordon Ross 			sr->fid_ofile = o;
28294047d49SGordon Ross 			status = NT_STATUS_SUCCESS;
28394047d49SGordon Ross 			break;
28494047d49SGordon Ross 		}
28594047d49SGordon Ross 	}
28694047d49SGordon Ross 
28794047d49SGordon Ross 	return (status);
28894047d49SGordon Ross }
28994047d49SGordon Ross 
29094047d49SGordon Ross /*
29194047d49SGordon Ross  * This is called by smb2_oplock_break_ack when the struct size
29294047d49SGordon Ross  * indicates this is a lease break (SZ_LEASE).  See:
29394047d49SGordon Ross  * [MS-SMB2] 3.3.5.22.2 Processing a Lease Acknowledgment
294*72b35b05SGordon Ross  * This is an "Ack" from the client.
29594047d49SGordon Ross  */
29694047d49SGordon Ross smb_sdrc_t
29794047d49SGordon Ross smb2_lease_break_ack(smb_request_t *sr)
29894047d49SGordon Ross {
299*72b35b05SGordon Ross 	smb_arg_olbrk_t	*olbrk = &sr->arg.olbrk;
30094047d49SGordon Ross 	smb_lease_t *lease;
301*72b35b05SGordon Ross 	smb_node_t  *node;
30294047d49SGordon Ross 	smb_ofile_t *ofile;
30394047d49SGordon Ross 	uint32_t LeaseState;
304*72b35b05SGordon Ross 	uint32_t BreakTo;
30594047d49SGordon Ross 	uint32_t status;
30694047d49SGordon Ross 	int rc = 0;
30794047d49SGordon Ross 
30894047d49SGordon Ross 	if (sr->session->dialect < SMB_VERS_2_1)
30994047d49SGordon Ross 		return (SDRC_ERROR);
31094047d49SGordon Ross 
31194047d49SGordon Ross 	/*
31294047d49SGordon Ross 	 * Decode an SMB2 Lease Acknowldgement
31394047d49SGordon Ross 	 * [MS-SMB2] 2.2.24.2
31494047d49SGordon Ross 	 * Note: Struct size decoded by caller.
31594047d49SGordon Ross 	 */
31694047d49SGordon Ross 	rc = smb_mbc_decodef(
31794047d49SGordon Ross 	    &sr->smb_data, "6.#cl8.",
31894047d49SGordon Ross 	    /* reserved		  6. */
31994047d49SGordon Ross 	    UUID_LEN,		/* # */
320*72b35b05SGordon Ross 	    olbrk->LeaseKey,	/* c */
321*72b35b05SGordon Ross 	    &olbrk->NewLevel);	/* l */
32294047d49SGordon Ross 	    /* duration		  8. */
32394047d49SGordon Ross 	if (rc != 0)
32494047d49SGordon Ross 		return (SDRC_ERROR);
325*72b35b05SGordon Ross 	LeaseState = olbrk->NewLevel;
32694047d49SGordon Ross 
327*72b35b05SGordon Ross 	/*
328*72b35b05SGordon Ross 	 * Find the lease via the given key.
329*72b35b05SGordon Ross 	 */
330*72b35b05SGordon Ross 	lease = lease_lookup(sr, olbrk->LeaseKey);
331*72b35b05SGordon Ross 	if (lease == NULL) {
332*72b35b05SGordon Ross 		/*
333*72b35b05SGordon Ross 		 * It's unusual to skip the dtrace start/done
334*72b35b05SGordon Ross 		 * probes like this, but trying to run them
335*72b35b05SGordon Ross 		 * with no lease->node would be complex and
336*72b35b05SGordon Ross 		 * would not show anything particularly useful.
337*72b35b05SGordon Ross 		 * Do the start probe after we find the ofile.
338*72b35b05SGordon Ross 		 */
339*72b35b05SGordon Ross 		status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
340*72b35b05SGordon Ross 		smb2sr_put_error(sr, status);
341*72b35b05SGordon Ross 		return (SDRC_SUCCESS);
342*72b35b05SGordon Ross 	}
343*72b35b05SGordon Ross 	// Note: lease ref; smb_lease_rele() below.
344*72b35b05SGordon Ross 	node = lease->ls_node;
345*72b35b05SGordon Ross 
346*72b35b05SGordon Ross 	/*
347*72b35b05SGordon Ross 	 * Find the leased oplock.  Hold locks so it can't move
348*72b35b05SGordon Ross 	 * until we're done with ACK-break processing.
349*72b35b05SGordon Ross 	 */
350*72b35b05SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
351*72b35b05SGordon Ross 	mutex_enter(&node->n_oplock.ol_mutex);
352*72b35b05SGordon Ross 
353*72b35b05SGordon Ross 	status = lease_find_oplock(sr, lease);
354*72b35b05SGordon Ross 	/* Normally have sr->fid_ofile now. */
35594047d49SGordon Ross 
35694047d49SGordon Ross 	DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr);
357*72b35b05SGordon Ross 
358*72b35b05SGordon Ross 	if (status != 0) {
359*72b35b05SGordon Ross 		/* Leased oplock not found.  Must have closed. */
36094047d49SGordon Ross 		goto errout;
361*72b35b05SGordon Ross 	}
36294047d49SGordon Ross 
363*72b35b05SGordon Ross 	/* Success, so have sr->fid_ofile */
36494047d49SGordon Ross 	ofile = sr->fid_ofile;
36594047d49SGordon Ross 
3666f8336c5SGordon Ross 	if (lease->ls_breaking == 0) {
367*72b35b05SGordon Ross 		/*
368*72b35b05SGordon Ross 		 * This ACK is either unsolicited or too late,
369*72b35b05SGordon Ross 		 * eg. we timed out the ACK and did it locally.
370*72b35b05SGordon Ross 		 */
3716f8336c5SGordon Ross 		status = NT_STATUS_UNSUCCESSFUL;
3726f8336c5SGordon Ross 		goto errout;
3736f8336c5SGordon Ross 	}
3746f8336c5SGordon Ross 
37594047d49SGordon Ross 	/*
37694047d49SGordon Ross 	 * If the new LeaseState has any bits in excess of
37794047d49SGordon Ross 	 * the lease state we sent in the break, error...
37894047d49SGordon Ross 	 */
379*72b35b05SGordon Ross 	BreakTo = (lease->ls_breaking >> BREAK_SHIFT) &
38094047d49SGordon Ross 	    OPLOCK_LEVEL_CACHE_MASK;
381*72b35b05SGordon Ross 	if ((LeaseState & ~BreakTo) != 0) {
38294047d49SGordon Ross 		status = NT_STATUS_REQUEST_NOT_ACCEPTED;
38394047d49SGordon Ross 		goto errout;
38494047d49SGordon Ross 	}
38594047d49SGordon Ross 
386*72b35b05SGordon Ross 	/*
387*72b35b05SGordon Ross 	 * Process the lease break ack.
388*72b35b05SGordon Ross 	 *
389*72b35b05SGordon Ross 	 * Clear breaking flags before we ack,
390*72b35b05SGordon Ross 	 * because ack might set those.
391*72b35b05SGordon Ross 	 */
392*72b35b05SGordon Ross 	ofile->f_oplock.og_breaking = 0;
39394047d49SGordon Ross 	lease->ls_breaking = 0;
394*72b35b05SGordon Ross 	cv_broadcast(&lease->ls_ack_cv);
39594047d49SGordon Ross 
39694047d49SGordon Ross 	LeaseState |= OPLOCK_LEVEL_GRANULAR;
39794047d49SGordon Ross 	status = smb_oplock_ack_break(sr, ofile, &LeaseState);
39894047d49SGordon Ross 
399*72b35b05SGordon Ross 	ofile->f_oplock.og_state = LeaseState;
400*72b35b05SGordon Ross 	lease->ls_state = LeaseState & CACHE_RWH;
401*72b35b05SGordon Ross 	/* ls_epoch does not change here */
402*72b35b05SGordon Ross 
403*72b35b05SGordon Ross 	if (ofile->dh_persist)
404*72b35b05SGordon Ross 		smb2_dh_update_oplock(sr, ofile);
40594047d49SGordon Ross 
40694047d49SGordon Ross errout:
40794047d49SGordon Ross 	sr->smb2_status = status;
40894047d49SGordon Ross 	DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr);
409*72b35b05SGordon Ross 
410*72b35b05SGordon Ross 	mutex_exit(&node->n_oplock.ol_mutex);
411*72b35b05SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
412*72b35b05SGordon Ross 
413*72b35b05SGordon Ross 	smb2_lease_rele(lease);
414*72b35b05SGordon Ross 
41594047d49SGordon Ross 	if (status) {
41694047d49SGordon Ross 		smb2sr_put_error(sr, status);
41794047d49SGordon Ross 		return (SDRC_SUCCESS);
41894047d49SGordon Ross 	}
41994047d49SGordon Ross 
42094047d49SGordon Ross 	/*
42194047d49SGordon Ross 	 * Encode an SMB2 Lease Ack. response
42294047d49SGordon Ross 	 * [MS-SMB2] 2.2.25.2
42394047d49SGordon Ross 	 */
42494047d49SGordon Ross 	LeaseState &= OPLOCK_LEVEL_CACHE_MASK;
42594047d49SGordon Ross 	(void) smb_mbc_encodef(
42694047d49SGordon Ross 	    &sr->reply, "w6.#cl8.",
42794047d49SGordon Ross 	    SSZ_LEASE_ACK,	/* w */
42894047d49SGordon Ross 	    /* reserved		  6. */
42994047d49SGordon Ross 	    UUID_LEN,		/* # */
430*72b35b05SGordon Ross 	    olbrk->LeaseKey,	/* c */
43194047d49SGordon Ross 	    LeaseState);	/* l */
43294047d49SGordon Ross 	    /* duration		  8. */
43394047d49SGordon Ross 
43494047d49SGordon Ross 	return (SDRC_SUCCESS);
43594047d49SGordon Ross 
43694047d49SGordon Ross }
43794047d49SGordon Ross 
43894047d49SGordon Ross /*
43994047d49SGordon Ross  * Compose an SMB2 Lease Break Notification packet, including
44094047d49SGordon Ross  * the SMB2 header and everything, in sr->reply.
44194047d49SGordon Ross  * The caller will send it and free the request.
44294047d49SGordon Ross  *
44394047d49SGordon Ross  * [MS-SMB2] 2.2.23.2 Lease Break Notification
44494047d49SGordon Ross  */
445*72b35b05SGordon Ross static void
446*72b35b05SGordon Ross smb2_lease_break_notification(smb_request_t *sr,
447*72b35b05SGordon Ross     uint32_t OldLevel, uint32_t NewLevel,
448*72b35b05SGordon Ross     uint16_t Epoch, boolean_t AckReq)
44994047d49SGordon Ross {
4506f8336c5SGordon Ross 	smb_lease_t *ls = sr->fid_ofile->f_lease;
451*72b35b05SGordon Ross 	uint16_t Flags = 0;
45294047d49SGordon Ross 
45394047d49SGordon Ross 	/*
454*72b35b05SGordon Ross 	 * Convert internal lease info to SMB2
45594047d49SGordon Ross 	 */
456*72b35b05SGordon Ross 	if (AckReq)
457*72b35b05SGordon Ross 		Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
45894047d49SGordon Ross 	if (ls->ls_version < 2)
45994047d49SGordon Ross 		Epoch = 0;
460*72b35b05SGordon Ross 	OldLevel &= OPLOCK_LEVEL_CACHE_MASK;
461*72b35b05SGordon Ross 	NewLevel &= OPLOCK_LEVEL_CACHE_MASK;
46294047d49SGordon Ross 
46394047d49SGordon Ross 	/*
46494047d49SGordon Ross 	 * SMB2 Header
46594047d49SGordon Ross 	 */
46694047d49SGordon Ross 	sr->smb2_cmd_code = SMB2_OPLOCK_BREAK;
46794047d49SGordon Ross 	sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR;
46894047d49SGordon Ross 	sr->smb_tid = 0;
46994047d49SGordon Ross 	sr->smb_pid = 0;
47094047d49SGordon Ross 	sr->smb2_ssnid = 0;
47194047d49SGordon Ross 	sr->smb2_messageid = UINT64_MAX;
47294047d49SGordon Ross 	(void) smb2_encode_header(sr, B_FALSE);
47394047d49SGordon Ross 
47494047d49SGordon Ross 	/*
47594047d49SGordon Ross 	 * SMB2 Oplock Break, variable part
47694047d49SGordon Ross 	 *
47794047d49SGordon Ross 	 * [MS-SMB2] says the current lease state preceeds the
47894047d49SGordon Ross 	 * new lease state, but that looks like an error...
47994047d49SGordon Ross 	 */
48094047d49SGordon Ross 	(void) smb_mbc_encodef(
48194047d49SGordon Ross 	    &sr->reply, "wwl#cll4.4.4.",
48294047d49SGordon Ross 	    SSZ_LEASE_BRK,		/* w */
48394047d49SGordon Ross 	    Epoch,			/* w */
48494047d49SGordon Ross 	    Flags,			/* l */
48594047d49SGordon Ross 	    SMB_LEASE_KEY_SZ,		/* # */
48694047d49SGordon Ross 	    ls->ls_key,			/* c */
487*72b35b05SGordon Ross 	    OldLevel,		/* cur.st  l */
488*72b35b05SGordon Ross 	    NewLevel);		/* new.st  l */
48994047d49SGordon Ross 	    /* reserved (4.4.4.) */
49094047d49SGordon Ross }
49194047d49SGordon Ross 
492*72b35b05SGordon Ross /*
493*72b35b05SGordon Ross  * Do our best to send a lease break message to the client.
494*72b35b05SGordon Ross  * When we get to multi-channel, this is supposed to try
495*72b35b05SGordon Ross  * every channel before giving up.  For now, try every
496*72b35b05SGordon Ross  * connected session with an ofile sharing this lease.
497*72b35b05SGordon Ross  *
498*72b35b05SGordon Ross  * If this ofile has a valid session, try that first.
499*72b35b05SGordon Ross  * Otherwise look on the node list for other ofiles with
500*72b35b05SGordon Ross  * the same lease and a connected session.
501*72b35b05SGordon Ross  */
502*72b35b05SGordon Ross static int
503*72b35b05SGordon Ross lease_send_any_cn(smb_request_t *sr)
504*72b35b05SGordon Ross {
505*72b35b05SGordon Ross 	smb_ofile_t	*o;
506*72b35b05SGordon Ross 	smb_ofile_t	*ofile = sr->fid_ofile;
507*72b35b05SGordon Ross 	smb_lease_t	*lease = ofile->f_lease;
508*72b35b05SGordon Ross 	smb_node_t	*node = ofile->f_node;
509*72b35b05SGordon Ross 	int		rc = ENOTCONN;
510*72b35b05SGordon Ross 
511*72b35b05SGordon Ross 	/*
512*72b35b05SGordon Ross 	 * If the passed oplock ofile has a session,
513*72b35b05SGordon Ross 	 * this IF expression will be true.
514*72b35b05SGordon Ross 	 */
515*72b35b05SGordon Ross 	if (sr->session == ofile->f_session) {
516*72b35b05SGordon Ross 		rc = smb_session_send(sr->session, 0, &sr->reply);
517*72b35b05SGordon Ross 		if (rc == 0)
518*72b35b05SGordon Ross 			return (rc);
519*72b35b05SGordon Ross 	}
520*72b35b05SGordon Ross 
521*72b35b05SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
522*72b35b05SGordon Ross 	FOREACH_NODE_OFILE(node, o) {
523*72b35b05SGordon Ross 		if (o->f_lease != lease)
524*72b35b05SGordon Ross 			continue;
525*72b35b05SGordon Ross 		if (smb_ofile_hold(o)) {
526*72b35b05SGordon Ross 			/* Has a session. */
527*72b35b05SGordon Ross 			rc = smb_session_send(o->f_session, 0, &sr->reply);
528*72b35b05SGordon Ross 			smb_llist_post(&node->n_ofile_list, o,
529*72b35b05SGordon Ross 			    smb_ofile_release_LL);
530*72b35b05SGordon Ross 		}
531*72b35b05SGordon Ross 		if (rc == 0)
532*72b35b05SGordon Ross 			break;
533*72b35b05SGordon Ross 	}
534*72b35b05SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
535*72b35b05SGordon Ross 
536*72b35b05SGordon Ross 	return (rc);
537*72b35b05SGordon Ross }
538*72b35b05SGordon Ross 
539*72b35b05SGordon Ross /*
540*72b35b05SGordon Ross  * See smb_llist_post on node->n_ofile_list below.
541*72b35b05SGordon Ross  * Can't call smb_ofile_close with that list entered.
542*72b35b05SGordon Ross  */
543*72b35b05SGordon Ross static void
544*72b35b05SGordon Ross lease_ofile_close_rele(void *arg)
545*72b35b05SGordon Ross {
546*72b35b05SGordon Ross 	smb_ofile_t *of = (smb_ofile_t *)arg;
547*72b35b05SGordon Ross 
548*72b35b05SGordon Ross 	smb_ofile_close(of, 0);
549*72b35b05SGordon Ross 	smb_ofile_release(of);
550*72b35b05SGordon Ross }
551*72b35b05SGordon Ross 
552*72b35b05SGordon Ross /*
553*72b35b05SGordon Ross  * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break
554*72b35b05SGordon Ross  * If no connection, for each Open in Lease.LeaseOpens,
555*72b35b05SGordon Ross  * the server MUST close the Open as specified in sec...
556*72b35b05SGordon Ross  * for the following cases:
557*72b35b05SGordon Ross  * - Open.IsDurable, Open.IsResilient, and
558*72b35b05SGordon Ross  *   Open.IsPersistent are all FALSE.
559*72b35b05SGordon Ross  * - Open.IsDurable is TRUE and Lease.BreakToLeaseState
560*72b35b05SGordon Ross  *   does not contain SMB2_LEASE_HANDLE_CACHING and
561*72b35b05SGordon Ross  */
562*72b35b05SGordon Ross static void
563*72b35b05SGordon Ross lease_close_notconn(smb_request_t *sr, uint32_t NewLevel)
564*72b35b05SGordon Ross {
565*72b35b05SGordon Ross 	smb_ofile_t	*o;
566*72b35b05SGordon Ross 	smb_ofile_t	*ofile = sr->fid_ofile;
567*72b35b05SGordon Ross 	smb_lease_t	*lease = ofile->f_lease;
568*72b35b05SGordon Ross 	smb_node_t	*node = ofile->f_node;
569*72b35b05SGordon Ross 
570*72b35b05SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
571*72b35b05SGordon Ross 	FOREACH_NODE_OFILE(node, o) {
572*72b35b05SGordon Ross 		if (o->f_lease != lease)
573*72b35b05SGordon Ross 			continue;
574*72b35b05SGordon Ross 		if (o->f_oplock_closing)
575*72b35b05SGordon Ross 			continue;
576*72b35b05SGordon Ross 		if (o->dh_persist)
577*72b35b05SGordon Ross 			continue;
578*72b35b05SGordon Ross 		if (o->dh_vers == SMB2_RESILIENT)
579*72b35b05SGordon Ross 			continue;
580*72b35b05SGordon Ross 		if (o->dh_vers == SMB2_NOT_DURABLE ||
581*72b35b05SGordon Ross 		    (NewLevel & OPLOCK_LEVEL_CACHE_HANDLE) == 0) {
582*72b35b05SGordon Ross 			if (smb_ofile_hold_olbrk(o)) {
583*72b35b05SGordon Ross 				smb_llist_post(&node->n_ofile_list, o,
584*72b35b05SGordon Ross 				    lease_ofile_close_rele);
585*72b35b05SGordon Ross 			}
586*72b35b05SGordon Ross 		}
587*72b35b05SGordon Ross 	}
588*72b35b05SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
589*72b35b05SGordon Ross }
590*72b35b05SGordon Ross 
591*72b35b05SGordon Ross /*
592*72b35b05SGordon Ross  * Send a lease break over the wire, or if we can't,
593*72b35b05SGordon Ross  * then process the lease break locally.
594*72b35b05SGordon Ross  *
595*72b35b05SGordon Ross  * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break
596*72b35b05SGordon Ross  *
597*72b35b05SGordon Ross  * This is mostly similar to smb2_oplock_send_break()
598*72b35b05SGordon Ross  * See top comment there about the design.
599*72b35b05SGordon Ross  *
600*72b35b05SGordon Ross  * Differences beween a lease break and oplock break:
601*72b35b05SGordon Ross  *
602*72b35b05SGordon Ross  * Leases are an SMB-level mechanism whereby multiple open
603*72b35b05SGordon Ross  * SMB file handles can share an oplock.  All SMB handles
604*72b35b05SGordon Ross  * on the lease enjoy the same caching rights.  Down at the
605*72b35b05SGordon Ross  * file-system level, just one oplock holds the cache rights
606*72b35b05SGordon Ross  * for a lease, but (this is the tricky part) that oplock can
607*72b35b05SGordon Ross  * MOVE among the SMB file handles sharing the lease. Such
608*72b35b05SGordon Ross  * oplock moves can happen when a handle is closed (if that
609*72b35b05SGordon Ross  * handle is the one with the oplock) or when a new open on
610*72b35b05SGordon Ross  * the lease causes an upgrade of the caching rights.
611*72b35b05SGordon Ross  *
612*72b35b05SGordon Ross  * We have to deal here with lease movement because this call
613*72b35b05SGordon Ross  * happens asynchronously after the smb_oplock_ind_break call,
614*72b35b05SGordon Ross  * meaning that the oplock for the lease may have moved by the
615*72b35b05SGordon Ross  * time this runs.  In addition, the ofile holding the oplock
616*72b35b05SGordon Ross  * might not be the best one to use to send a lease break.
617*72b35b05SGordon Ross  * If the oplock is held by a handle that's "orphaned" and
618*72b35b05SGordon Ross  * there are other handles on the lease with active sessions,
619*72b35b05SGordon Ross  * we want to send the lease break on an active session.
620*72b35b05SGordon Ross  *
621*72b35b05SGordon Ross  * Also note: NewLevel (as provided by smb_oplock_ind_break etc.)
622*72b35b05SGordon Ross  * does NOT include the GRANULAR flag.  This level is expected to
623*72b35b05SGordon Ross  * keep track of how each oplock was acquired (by lease or not)
624*72b35b05SGordon Ross  * and put the GRANULAR flag back in when appropriate.
625*72b35b05SGordon Ross  */
626*72b35b05SGordon Ross void
627*72b35b05SGordon Ross smb2_lease_send_break(smb_request_t *sr)
628*72b35b05SGordon Ross {
629*72b35b05SGordon Ross 	smb_ofile_t	*old_ofile;
630*72b35b05SGordon Ross 	smb_ofile_t	*ofile = sr->fid_ofile;
631*72b35b05SGordon Ross 	smb_node_t	*node = ofile->f_node;
632*72b35b05SGordon Ross 	smb_lease_t	*lease = ofile->f_lease;
633*72b35b05SGordon Ross 	smb_arg_olbrk_t	*olbrk = &sr->arg.olbrk;
634*72b35b05SGordon Ross 	boolean_t	AckReq = olbrk->AckRequired;
635*72b35b05SGordon Ross 	uint32_t	OldLevel = olbrk->OldLevel;
636*72b35b05SGordon Ross 	uint32_t	NewLevel = olbrk->NewLevel;
637*72b35b05SGordon Ross 	uint32_t	status;
638*72b35b05SGordon Ross 	int		rc;
639*72b35b05SGordon Ross 
640*72b35b05SGordon Ross 	NewLevel |= OPLOCK_LEVEL_GRANULAR;
641*72b35b05SGordon Ross 
642*72b35b05SGordon Ross 	/*
643*72b35b05SGordon Ross 	 * Build the break message in sr->reply.
644*72b35b05SGordon Ross 	 * It's free'd in smb_request_free().
645*72b35b05SGordon Ross 	 * Always an SMB2 lease here.
646*72b35b05SGordon Ross 	 */
647*72b35b05SGordon Ross 	sr->reply.max_bytes = MLEN;
648*72b35b05SGordon Ross 	smb2_lease_break_notification(sr,
649*72b35b05SGordon Ross 	    OldLevel, NewLevel, lease->ls_epoch, AckReq);
650*72b35b05SGordon Ross 
651*72b35b05SGordon Ross 	/*
652*72b35b05SGordon Ross 	 * Try to send the break message to the client,
653*72b35b05SGordon Ross 	 * on any connection with this lease.
654*72b35b05SGordon Ross 	 */
655*72b35b05SGordon Ross 	rc = lease_send_any_cn(sr);
656*72b35b05SGordon Ross 	if (rc != 0) {
657*72b35b05SGordon Ross 		/*
658*72b35b05SGordon Ross 		 * We were unable to send the oplock break request,
659*72b35b05SGordon Ross 		 * presumably because the connection is gone.
660*72b35b05SGordon Ross 		 * Close uninteresting handles.
661*72b35b05SGordon Ross 		 */
662*72b35b05SGordon Ross 		lease_close_notconn(sr, NewLevel);
663*72b35b05SGordon Ross 		/* Note: some handles may remain on the lease. */
664*72b35b05SGordon Ross 		if (!AckReq)
665*72b35b05SGordon Ross 			return;
666*72b35b05SGordon Ross 		/* Do local Ack below. */
667*72b35b05SGordon Ross 	} else {
668*72b35b05SGordon Ross 		/*
669*72b35b05SGordon Ross 		 * OK, we were able to send the break message.
670*72b35b05SGordon Ross 		 * If no ack. required, we're done.
671*72b35b05SGordon Ross 		 */
672*72b35b05SGordon Ross 		if (!AckReq)
673*72b35b05SGordon Ross 			return;
674*72b35b05SGordon Ross 
675*72b35b05SGordon Ross 		/*
676*72b35b05SGordon Ross 		 * We're expecting an ACK.  Wait in this thread
677*72b35b05SGordon Ross 		 * so we can log clients that don't respond.
678*72b35b05SGordon Ross 		 */
679*72b35b05SGordon Ross 		status = smb_oplock_wait_ack(sr);
680*72b35b05SGordon Ross 		if (status == 0)
681*72b35b05SGordon Ross 			return;
682*72b35b05SGordon Ross 
683*72b35b05SGordon Ross 		cmn_err(CE_NOTE, "clnt %s oplock break timeout",
684*72b35b05SGordon Ross 		    sr->session->ip_addr_str);
685*72b35b05SGordon Ross 		DTRACE_PROBE1(ack_timeout, smb_request_t *, sr);
686*72b35b05SGordon Ross 
687*72b35b05SGordon Ross 		/*
688*72b35b05SGordon Ross 		 * Will do local ack below.  Note, after timeout,
689*72b35b05SGordon Ross 		 * do a break to none or "no caching" regardless
690*72b35b05SGordon Ross 		 * of what the passed in cache level was.
691*72b35b05SGordon Ross 		 * That means: clear all except GRANULAR.
692*72b35b05SGordon Ross 		 */
693*72b35b05SGordon Ross 		NewLevel = OPLOCK_LEVEL_GRANULAR;
694*72b35b05SGordon Ross 	}
695*72b35b05SGordon Ross 
696*72b35b05SGordon Ross 	/*
697*72b35b05SGordon Ross 	 * Do the ack locally.
698*72b35b05SGordon Ross 	 *
699*72b35b05SGordon Ross 	 * Find the ofile with the leased oplock
700*72b35b05SGordon Ross 	 * (may have moved before we took locks)
701*72b35b05SGordon Ross 	 */
702*72b35b05SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
703*72b35b05SGordon Ross 	mutex_enter(&node->n_oplock.ol_mutex);
704*72b35b05SGordon Ross 
705*72b35b05SGordon Ross 	old_ofile = ofile;
706*72b35b05SGordon Ross 	sr->fid_ofile = NULL;
707*72b35b05SGordon Ross 	status = lease_find_oplock(sr, lease);
708*72b35b05SGordon Ross 	if (status != 0) {
709*72b35b05SGordon Ross 		/* put back old_ofile */
710*72b35b05SGordon Ross 		sr->fid_ofile = old_ofile;
711*72b35b05SGordon Ross 		goto unlock_out;
712*72b35b05SGordon Ross 	}
713*72b35b05SGordon Ross 	smb_llist_post(&node->n_ofile_list, old_ofile,
714*72b35b05SGordon Ross 	    smb_ofile_release_LL);
715*72b35b05SGordon Ross 
716*72b35b05SGordon Ross 	ofile = sr->fid_ofile;
717*72b35b05SGordon Ross 
718*72b35b05SGordon Ross 	/*
719*72b35b05SGordon Ross 	 * Now continue like the non-lease code
720*72b35b05SGordon Ross 	 */
721*72b35b05SGordon Ross 	ofile->f_oplock.og_breaking = 0;
722*72b35b05SGordon Ross 	lease->ls_breaking = 0;
723*72b35b05SGordon Ross 	cv_broadcast(&lease->ls_ack_cv);
724*72b35b05SGordon Ross 
725*72b35b05SGordon Ross 	status = smb_oplock_ack_break(sr, ofile, &NewLevel);
726*72b35b05SGordon Ross 
727*72b35b05SGordon Ross 	ofile->f_oplock.og_state = NewLevel;
728*72b35b05SGordon Ross 	lease->ls_state = NewLevel & CACHE_RWH;
729*72b35b05SGordon Ross 	/* ls_epoch does not change here */
730*72b35b05SGordon Ross 
731*72b35b05SGordon Ross 	if (ofile->dh_persist)
732*72b35b05SGordon Ross 		smb2_dh_update_oplock(sr, ofile);
733*72b35b05SGordon Ross 
734*72b35b05SGordon Ross unlock_out:
735*72b35b05SGordon Ross 	mutex_exit(&node->n_oplock.ol_mutex);
736*72b35b05SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
737*72b35b05SGordon Ross 
738*72b35b05SGordon Ross #ifdef	DEBUG
739*72b35b05SGordon Ross 	if (status != 0) {
740*72b35b05SGordon Ross 		cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x",
741*72b35b05SGordon Ross 		    sr->session->ip_addr_str, status);
742*72b35b05SGordon Ross 	}
743*72b35b05SGordon Ross #endif
744*72b35b05SGordon Ross }
745*72b35b05SGordon Ross 
74694047d49SGordon Ross /*
74794047d49SGordon Ross  * Client has an open handle and requests a lease.
74894047d49SGordon Ross  * Convert SMB2 lease request info in to internal form,
74994047d49SGordon Ross  * call common oplock code, convert result to SMB2.
75094047d49SGordon Ross  *
75194047d49SGordon Ross  * If necessary, "go async" here.
75294047d49SGordon Ross  */
75394047d49SGordon Ross void
75494047d49SGordon Ross smb2_lease_acquire(smb_request_t *sr)
75594047d49SGordon Ross {
75694047d49SGordon Ross 	smb_arg_open_t *op = &sr->arg.open;
75794047d49SGordon Ross 	smb_ofile_t *ofile = sr->fid_ofile;
75894047d49SGordon Ross 	smb_lease_t *lease = ofile->f_lease;
759*72b35b05SGordon Ross 	smb_node_t *node = ofile->f_node;
76094047d49SGordon Ross 	uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED;
76194047d49SGordon Ross 	uint32_t have, want; /* lease flags */
76294047d49SGordon Ross 	boolean_t NewGrant = B_FALSE;
76394047d49SGordon Ross 
76494047d49SGordon Ross 	/* Only disk trees get oplocks. */
76594047d49SGordon Ross 	ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE);
76694047d49SGordon Ross 
76794047d49SGordon Ross 	/*
76894047d49SGordon Ross 	 * Only plain files (for now).
76994047d49SGordon Ross 	 * Later, test SMB2_CAP_DIRECTORY_LEASING
77094047d49SGordon Ross 	 */
77194047d49SGordon Ross 	if (!smb_node_is_file(ofile->f_node)) {
77294047d49SGordon Ross 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
77394047d49SGordon Ross 		return;
77494047d49SGordon Ross 	}
77594047d49SGordon Ross 
77694047d49SGordon Ross 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
77794047d49SGordon Ross 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
77894047d49SGordon Ross 		return;
77994047d49SGordon Ross 	}
78094047d49SGordon Ross 
78194047d49SGordon Ross 	/*
78294047d49SGordon Ross 	 * SMB2: Convert to internal form.
78394047d49SGordon Ross 	 * Caller should have setup the lease.
78494047d49SGordon Ross 	 */
78594047d49SGordon Ross 	ASSERT(op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE);
78694047d49SGordon Ross 	ASSERT(lease != NULL);
78794047d49SGordon Ross 	if (lease == NULL) {
78894047d49SGordon Ross 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
78994047d49SGordon Ross 		return;
79094047d49SGordon Ross 	}
79194047d49SGordon Ross 	op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
79294047d49SGordon Ross 	    (op->lease_state & CACHE_RWH);
79394047d49SGordon Ross 
79494047d49SGordon Ross 	/*
79594047d49SGordon Ross 	 * Tree options may force shared oplocks,
79694047d49SGordon Ross 	 * in which case we reduce the request.
79794047d49SGordon Ross 	 */
79894047d49SGordon Ross 	if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
79994047d49SGordon Ross 		op->op_oplock_state &= ~WRITE_CACHING;
80094047d49SGordon Ross 	}
80194047d49SGordon Ross 
802*72b35b05SGordon Ross 	/*
803*72b35b05SGordon Ross 	 * Using the "Locks Held" (LH) variant of smb_oplock_request
804*72b35b05SGordon Ross 	 * below so things won't change underfoot.
805*72b35b05SGordon Ross 	 */
806*72b35b05SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
807*72b35b05SGordon Ross 	mutex_enter(&node->n_oplock.ol_mutex);
808*72b35b05SGordon Ross 
80994047d49SGordon Ross 	/*
81094047d49SGordon Ross 	 * Disallow downgrade
81194047d49SGordon Ross 	 *
81294047d49SGordon Ross 	 * Note that open with a lease is not allowed to turn off
81394047d49SGordon Ross 	 * any cache rights.  If the client tries to "downgrade",
81494047d49SGordon Ross 	 * any bits, just return the existing lease cache bits.
81594047d49SGordon Ross 	 */
81694047d49SGordon Ross 	have = lease->ls_state & CACHE_RWH;
81794047d49SGordon Ross 	want = op->op_oplock_state & CACHE_RWH;
81894047d49SGordon Ross 	if ((have & ~want) != 0) {
81994047d49SGordon Ross 		op->op_oplock_state = have |
82094047d49SGordon Ross 		    OPLOCK_LEVEL_GRANULAR;
82194047d49SGordon Ross 		goto done;
82294047d49SGordon Ross 	}
82394047d49SGordon Ross 
82494047d49SGordon Ross 	/*
82594047d49SGordon Ross 	 * Handle oplock requests in three parts:
82694047d49SGordon Ross 	 *	a: Requests with WRITE_CACHING
82794047d49SGordon Ross 	 *	b: Requests with HANDLE_CACHING
82894047d49SGordon Ross 	 *	c: Requests with READ_CACHING
82994047d49SGordon Ross 	 * reducing the request before b and c.
83094047d49SGordon Ross 	 *
83194047d49SGordon Ross 	 * In each: first check if the lease grants the
83294047d49SGordon Ross 	 * (possibly reduced) request, in which case we
83394047d49SGordon Ross 	 * leave the lease unchanged and return what's
83494047d49SGordon Ross 	 * granted by the lease.  Otherwise, try to get
83594047d49SGordon Ross 	 * the oplock, and if the succeeds, wait for any
83694047d49SGordon Ross 	 * breaks, update the lease, and return.
83794047d49SGordon Ross 	 */
83894047d49SGordon Ross 
83994047d49SGordon Ross 	/*
84094047d49SGordon Ross 	 * Try exclusive (request is RW or RWH)
84194047d49SGordon Ross 	 */
84294047d49SGordon Ross 	if ((op->op_oplock_state & WRITE_CACHING) != 0) {
84394047d49SGordon Ross 		want = op->op_oplock_state & CACHE_RWH;
84494047d49SGordon Ross 		if (have == want)
84594047d49SGordon Ross 			goto done;
84694047d49SGordon Ross 
847*72b35b05SGordon Ross 		status = smb_oplock_request_LH(sr, ofile,
84894047d49SGordon Ross 		    &op->op_oplock_state);
84994047d49SGordon Ross 		if (status == NT_STATUS_SUCCESS ||
85094047d49SGordon Ross 		    status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
85194047d49SGordon Ross 			NewGrant = B_TRUE;
85294047d49SGordon Ross 			goto done;
85394047d49SGordon Ross 		}
85494047d49SGordon Ross 
85594047d49SGordon Ross 		/*
85694047d49SGordon Ross 		 * We did not get the exclusive oplock.
85794047d49SGordon Ross 		 *
85894047d49SGordon Ross 		 * There are odd rules about lease upgrade.
85994047d49SGordon Ross 		 * If the existing lease grants R and the
86094047d49SGordon Ross 		 * client fails to upgrade it to "RWH"
86194047d49SGordon Ross 		 * (presumably due to handle conflicts)
86294047d49SGordon Ross 		 * then just return the existing lease,
86394047d49SGordon Ross 		 * even though upgrade to RH would work.
86494047d49SGordon Ross 		 */
86594047d49SGordon Ross 		if (have != 0) {
86694047d49SGordon Ross 			op->op_oplock_state = have |
86794047d49SGordon Ross 			    OPLOCK_LEVEL_GRANULAR;
86894047d49SGordon Ross 			goto done;
86994047d49SGordon Ross 		}
87094047d49SGordon Ross 
87194047d49SGordon Ross 		/*
87294047d49SGordon Ross 		 * Keep trying without write.
87394047d49SGordon Ross 		 * Need to re-init op_oplock_state
87494047d49SGordon Ross 		 */
87594047d49SGordon Ross 		op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
87694047d49SGordon Ross 		    (op->lease_state & CACHE_RH);
87794047d49SGordon Ross 	}
87894047d49SGordon Ross 
87994047d49SGordon Ross 	/*
88094047d49SGordon Ross 	 * Try shared ("RH")
88194047d49SGordon Ross 	 */
88294047d49SGordon Ross 	if ((op->op_oplock_state & HANDLE_CACHING) != 0) {
88394047d49SGordon Ross 		want = op->op_oplock_state & CACHE_RWH;
88494047d49SGordon Ross 		if (have == want)
88594047d49SGordon Ross 			goto done;
88694047d49SGordon Ross 
887*72b35b05SGordon Ross 		status = smb_oplock_request_LH(sr, ofile,
88894047d49SGordon Ross 		    &op->op_oplock_state);
88994047d49SGordon Ross 		if (status == NT_STATUS_SUCCESS ||
89094047d49SGordon Ross 		    status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
89194047d49SGordon Ross 			NewGrant = B_TRUE;
89294047d49SGordon Ross 			goto done;
89394047d49SGordon Ross 		}
89494047d49SGordon Ross 
89594047d49SGordon Ross 		/*
89694047d49SGordon Ross 		 * We did not get "RH", probably because
89794047d49SGordon Ross 		 * ther were (old style) Level II oplocks.
89894047d49SGordon Ross 		 * Continue, try for just read.
899*72b35b05SGordon Ross 		 * Again, re-init op_oplock_state
90094047d49SGordon Ross 		 */
90194047d49SGordon Ross 		op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
90294047d49SGordon Ross 		    (op->lease_state & CACHE_R);
90394047d49SGordon Ross 	}
90494047d49SGordon Ross 
90594047d49SGordon Ross 	/*
90694047d49SGordon Ross 	 * Try shared ("R")
90794047d49SGordon Ross 	 */
90894047d49SGordon Ross 	if ((op->op_oplock_state & READ_CACHING) != 0) {
90994047d49SGordon Ross 		want = op->op_oplock_state & CACHE_RWH;
91094047d49SGordon Ross 		if (have == want)
91194047d49SGordon Ross 			goto done;
91294047d49SGordon Ross 
913*72b35b05SGordon Ross 		status = smb_oplock_request_LH(sr, ofile,
91494047d49SGordon Ross 		    &op->op_oplock_state);
91594047d49SGordon Ross 		if (status == NT_STATUS_SUCCESS ||
91694047d49SGordon Ross 		    status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
91794047d49SGordon Ross 			NewGrant = B_TRUE;
91894047d49SGordon Ross 			goto done;
91994047d49SGordon Ross 		}
92094047d49SGordon Ross 
92194047d49SGordon Ross 		/*
92294047d49SGordon Ross 		 * We did not get "R".
92394047d49SGordon Ross 		 * Fall into "none".
92494047d49SGordon Ross 		 */
92594047d49SGordon Ross 	}
92694047d49SGordon Ross 
92794047d49SGordon Ross 	/*
92894047d49SGordon Ross 	 * None of the above were able to get an oplock.
92994047d49SGordon Ross 	 * The lease has no caching rights, and we didn't
93094047d49SGordon Ross 	 * add any in this request.  Return it as-is.
93194047d49SGordon Ross 	 */
93294047d49SGordon Ross 	op->op_oplock_state = OPLOCK_LEVEL_GRANULAR;
93394047d49SGordon Ross 
93494047d49SGordon Ross done:
935*72b35b05SGordon Ross 	/*
936*72b35b05SGordon Ross 	 * Only success cases get here
937*72b35b05SGordon Ross 	 */
938*72b35b05SGordon Ross 
939*72b35b05SGordon Ross 	/*
940*72b35b05SGordon Ross 	 * Keep track of what we got (ofile->f_oplock.og_state etc)
941*72b35b05SGordon Ross 	 * so we'll know what we had when sending a break later.
942*72b35b05SGordon Ross 	 * Also keep a copy of some things in the lease.
943*72b35b05SGordon Ross 	 *
944*72b35b05SGordon Ross 	 * Not using og_dialect here, as ofile->f_lease tells us
945*72b35b05SGordon Ross 	 * this has to be using granular oplocks.
946*72b35b05SGordon Ross 	 */
94794047d49SGordon Ross 	if (NewGrant) {
948*72b35b05SGordon Ross 		ofile->f_oplock.og_state = op->op_oplock_state;
949*72b35b05SGordon Ross 		ofile->f_oplock.og_breaking = 0;
95094047d49SGordon Ross 
951*72b35b05SGordon Ross 		lease->ls_oplock_ofile = ofile;
952*72b35b05SGordon Ross 		lease->ls_state = op->op_oplock_state;
953*72b35b05SGordon Ross 		lease->ls_breaking = B_FALSE;
95494047d49SGordon Ross 		lease->ls_epoch++;
955*72b35b05SGordon Ross 
956*72b35b05SGordon Ross 		if (ofile->dh_persist) {
957*72b35b05SGordon Ross 			smb2_dh_update_oplock(sr, ofile);
958*72b35b05SGordon Ross 		}
95994047d49SGordon Ross 	}
96094047d49SGordon Ross 
96194047d49SGordon Ross 	/*
96294047d49SGordon Ross 	 * Convert internal oplock state to SMB2
96394047d49SGordon Ross 	 */
96494047d49SGordon Ross 	op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
96594047d49SGordon Ross 	op->lease_state = lease->ls_state & CACHE_RWH;
96694047d49SGordon Ross 	op->lease_flags = (lease->ls_breaking != 0) ?
96794047d49SGordon Ross 	    SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0;
96894047d49SGordon Ross 	op->lease_epoch = lease->ls_epoch;
96994047d49SGordon Ross 	op->lease_version = lease->ls_version;
970*72b35b05SGordon Ross 
971*72b35b05SGordon Ross 	/*
972*72b35b05SGordon Ross 	 * End of lock-held region
973*72b35b05SGordon Ross 	 */
974*72b35b05SGordon Ross 	mutex_exit(&node->n_oplock.ol_mutex);
975*72b35b05SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
976*72b35b05SGordon Ross 
977*72b35b05SGordon Ross 	/*
978*72b35b05SGordon Ross 	 * After a new oplock grant, the status return
979*72b35b05SGordon Ross 	 * may indicate we need to wait for breaks.
980*72b35b05SGordon Ross 	 */
981*72b35b05SGordon Ross 	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
982*72b35b05SGordon Ross 		(void) smb2sr_go_async(sr);
983*72b35b05SGordon Ross 		(void) smb_oplock_wait_break(sr, ofile->f_node, 0);
984*72b35b05SGordon Ross 	}
98594047d49SGordon Ross }
98694047d49SGordon Ross 
98794047d49SGordon Ross /*
98894047d49SGordon Ross  * This ofile has a lease and is about to close.
98994047d49SGordon Ross  * Called by smb_ofile_close when there's a lease.
99094047d49SGordon Ross  *
99194047d49SGordon Ross  * With leases, just one ofile on a lease owns the oplock.
99294047d49SGordon Ross  * If an ofile with a lease is closed and it's the one that
99394047d49SGordon Ross  * owns the oplock, try to move the oplock to another ofile
99494047d49SGordon Ross  * on the same lease.
995*72b35b05SGordon Ross  *
996*72b35b05SGordon Ross  * Would prefer that we could just use smb_ofile_hold_olbrk
997*72b35b05SGordon Ross  * to select a suitable destination for the move, but this
998*72b35b05SGordon Ross  * is called while holding the owning tree ofile list etc
999*72b35b05SGordon Ross  * which can cause deadlock as described in illumos 13850
1000*72b35b05SGordon Ross  * when smb_ofile_hold_olbrk has to wait.  XXX todo
100194047d49SGordon Ross  */
100294047d49SGordon Ross void
100394047d49SGordon Ross smb2_lease_ofile_close(smb_ofile_t *ofile)
100494047d49SGordon Ross {
100594047d49SGordon Ross 	smb_node_t *node = ofile->f_node;
100694047d49SGordon Ross 	smb_lease_t *lease = ofile->f_lease;
100794047d49SGordon Ross 	smb_ofile_t *o;
100894047d49SGordon Ross 
10091f0845f1SGordon Ross 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
10101f0845f1SGordon Ross 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
10111f0845f1SGordon Ross 
101294047d49SGordon Ross 	/*
101394047d49SGordon Ross 	 * If this ofile was not the oplock owner for this lease,
101494047d49SGordon Ross 	 * we can leave things as they are.
101594047d49SGordon Ross 	 */
101694047d49SGordon Ross 	if (lease->ls_oplock_ofile != ofile)
101794047d49SGordon Ross 		return;
101894047d49SGordon Ross 
101994047d49SGordon Ross 	/*
102094047d49SGordon Ross 	 * Find another ofile to which we can move the oplock.
1021*72b35b05SGordon Ross 	 * First try for one that's open.  Usually find one.
102294047d49SGordon Ross 	 */
102394047d49SGordon Ross 	FOREACH_NODE_OFILE(node, o) {
102494047d49SGordon Ross 		if (o == ofile)
102594047d49SGordon Ross 			continue;
102694047d49SGordon Ross 		if (o->f_lease != lease)
102794047d49SGordon Ross 			continue;
10286f8336c5SGordon Ross 		if (o->f_oplock_closing)
10291f0845f1SGordon Ross 			continue;
1030*72b35b05SGordon Ross 
1031*72b35b05SGordon Ross 		mutex_enter(&o->f_mutex);
1032*72b35b05SGordon Ross 		if (o->f_state == SMB_OFILE_STATE_OPEN) {
1033*72b35b05SGordon Ross 			smb_oplock_move(node, ofile, o);
1034*72b35b05SGordon Ross 			lease->ls_oplock_ofile = o;
1035*72b35b05SGordon Ross 			mutex_exit(&o->f_mutex);
1036*72b35b05SGordon Ross 			return;
1037*72b35b05SGordon Ross 		}
1038*72b35b05SGordon Ross 		mutex_exit(&o->f_mutex);
103994047d49SGordon Ross 	}
1040*72b35b05SGordon Ross 
1041*72b35b05SGordon Ross 	/*
1042*72b35b05SGordon Ross 	 * Now try for one that's orphaned etc.
1043*72b35b05SGordon Ross 	 */
1044*72b35b05SGordon Ross 	FOREACH_NODE_OFILE(node, o) {
1045*72b35b05SGordon Ross 		if (o == ofile)
1046*72b35b05SGordon Ross 			continue;
1047*72b35b05SGordon Ross 		if (o->f_lease != lease)
1048*72b35b05SGordon Ross 			continue;
1049*72b35b05SGordon Ross 		if (o->f_oplock_closing)
1050*72b35b05SGordon Ross 			continue;
1051*72b35b05SGordon Ross 
1052*72b35b05SGordon Ross 		/*
1053*72b35b05SGordon Ross 		 * Allow most states as seen in smb_ofile_hold_olbrk
1054*72b35b05SGordon Ross 		 * without waiting for "_reconnect" or "_saving".
1055*72b35b05SGordon Ross 		 * Skip "_expired" because that's about to close.
1056*72b35b05SGordon Ross 		 * This is OK because just swapping the oplock state
1057*72b35b05SGordon Ross 		 * between two ofiles does not interfere with the
1058*72b35b05SGordon Ross 		 * dh_save or reconnect code paths.
1059*72b35b05SGordon Ross 		 */
1060*72b35b05SGordon Ross 		mutex_enter(&o->f_mutex);
1061*72b35b05SGordon Ross 		switch (o->f_state) {
1062*72b35b05SGordon Ross 		case SMB_OFILE_STATE_OPEN:
1063*72b35b05SGordon Ross 		case SMB_OFILE_STATE_SAVE_DH:
1064*72b35b05SGordon Ross 		case SMB_OFILE_STATE_SAVING:
1065*72b35b05SGordon Ross 		case SMB_OFILE_STATE_ORPHANED:
1066*72b35b05SGordon Ross 		case SMB_OFILE_STATE_RECONNECT:
1067*72b35b05SGordon Ross 			smb_oplock_move(node, ofile, o);
1068*72b35b05SGordon Ross 			lease->ls_oplock_ofile = o;
1069*72b35b05SGordon Ross 			mutex_exit(&o->f_mutex);
1070*72b35b05SGordon Ross 			return;
1071*72b35b05SGordon Ross 		}
1072*72b35b05SGordon Ross 		mutex_exit(&o->f_mutex);
107394047d49SGordon Ross 	}
107494047d49SGordon Ross 
1075*72b35b05SGordon Ross 	/*
1076*72b35b05SGordon Ross 	 * Normal for last close on a lease.
1077*72b35b05SGordon Ross 	 * Wakeup ACK waiters too.
1078*72b35b05SGordon Ross 	 */
1079*72b35b05SGordon Ross 	lease->ls_state = 0;
1080*72b35b05SGordon Ross 	lease->ls_breaking = 0;
1081*72b35b05SGordon Ross 	cv_broadcast(&lease->ls_ack_cv);
1082*72b35b05SGordon Ross 
1083*72b35b05SGordon Ross 	lease->ls_oplock_ofile = NULL;
108494047d49SGordon Ross }
1085