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