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*6f8336c5SGordon Ross * Copyright 2021 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 6394047d49SGordon Ross void 6494047d49SGordon Ross smb2_lease_rele(smb_lease_t *ls) 6594047d49SGordon Ross { 6694047d49SGordon Ross smb_llist_t *bucket; 6794047d49SGordon Ross 6894047d49SGordon Ross mutex_enter(&ls->ls_mutex); 6994047d49SGordon Ross ls->ls_refcnt--; 7094047d49SGordon Ross if (ls->ls_refcnt != 0) { 7194047d49SGordon Ross mutex_exit(&ls->ls_mutex); 7294047d49SGordon Ross return; 7394047d49SGordon Ross } 7494047d49SGordon Ross mutex_exit(&ls->ls_mutex); 7594047d49SGordon Ross 7694047d49SGordon Ross /* 7794047d49SGordon Ross * Get the list lock, then re-check the refcnt 7894047d49SGordon Ross * and if it's still zero, unlink & destroy. 7994047d49SGordon Ross */ 8094047d49SGordon Ross bucket = ls->ls_bucket; 8194047d49SGordon Ross smb_llist_enter(bucket, RW_WRITER); 8294047d49SGordon Ross 8394047d49SGordon Ross mutex_enter(&ls->ls_mutex); 8494047d49SGordon Ross if (ls->ls_refcnt == 0) 8594047d49SGordon Ross smb_llist_remove(bucket, ls); 8694047d49SGordon Ross mutex_exit(&ls->ls_mutex); 8794047d49SGordon Ross 8894047d49SGordon Ross if (ls->ls_refcnt == 0) { 8994047d49SGordon Ross mutex_destroy(&ls->ls_mutex); 9094047d49SGordon Ross kmem_cache_free(smb_lease_cache, ls); 9194047d49SGordon Ross } 9294047d49SGordon Ross 9394047d49SGordon Ross smb_llist_exit(bucket); 9494047d49SGordon Ross } 9594047d49SGordon Ross 9694047d49SGordon Ross /* 9794047d49SGordon Ross * Compute a hash from a uuid 9894047d49SGordon Ross * Based on mod_hash_bystr() 9994047d49SGordon Ross */ 10094047d49SGordon Ross static uint_t 10194047d49SGordon Ross smb_hash_uuid(const uint8_t *uuid) 10294047d49SGordon Ross { 10394047d49SGordon Ross char *k = (char *)uuid; 10494047d49SGordon Ross uint_t hash = 0; 10594047d49SGordon Ross uint_t g; 10694047d49SGordon Ross int i; 10794047d49SGordon Ross 10894047d49SGordon Ross ASSERT(k); 10994047d49SGordon Ross for (i = 0; i < UUID_LEN; i++) { 11094047d49SGordon Ross hash = (hash << 4) + k[i]; 11194047d49SGordon Ross if ((g = (hash & 0xf0000000)) != 0) { 11294047d49SGordon Ross hash ^= (g >> 24); 11394047d49SGordon Ross hash ^= g; 11494047d49SGordon Ross } 11594047d49SGordon Ross } 11694047d49SGordon Ross return (hash); 11794047d49SGordon Ross } 11894047d49SGordon Ross 11994047d49SGordon Ross /* 12094047d49SGordon Ross * Add or update a lease table entry for a new ofile. 12194047d49SGordon Ross * (in the per-session lease table) 12294047d49SGordon Ross * See [MS-SMB2] 3.3.5.9.8 12394047d49SGordon Ross * Handling the SMB2_CREATE_REQUEST_LEASE Create Context 12494047d49SGordon Ross */ 12594047d49SGordon Ross uint32_t 1268d94f651SGordon Ross smb2_lease_create(smb_request_t *sr, uint8_t *clnt) 12794047d49SGordon Ross { 12894047d49SGordon Ross smb_arg_open_t *op = &sr->arg.open; 12994047d49SGordon Ross uint8_t *key = op->lease_key; 13094047d49SGordon Ross smb_ofile_t *of = sr->fid_ofile; 13194047d49SGordon Ross smb_hash_t *ht = sr->sr_server->sv_lease_ht; 13294047d49SGordon Ross smb_llist_t *bucket; 13394047d49SGordon Ross smb_lease_t *lease; 13494047d49SGordon Ross smb_lease_t *newlease; 13594047d49SGordon Ross size_t hashkey; 13694047d49SGordon Ross uint32_t status = NT_STATUS_INVALID_PARAMETER; 13794047d49SGordon Ross 13894047d49SGordon Ross if (bcmp(key, lease_zero, UUID_LEN) == 0) 13994047d49SGordon Ross return (status); 14094047d49SGordon Ross 14194047d49SGordon Ross /* 14294047d49SGordon Ross * Find or create, and add a ref for the new ofile. 14394047d49SGordon Ross */ 14494047d49SGordon Ross hashkey = smb_hash_uuid(key); 14594047d49SGordon Ross hashkey &= (ht->num_buckets - 1); 14694047d49SGordon Ross bucket = &ht->buckets[hashkey].b_list; 14794047d49SGordon Ross 14894047d49SGordon Ross newlease = kmem_cache_alloc(smb_lease_cache, KM_SLEEP); 14994047d49SGordon Ross bzero(newlease, sizeof (smb_lease_t)); 15094047d49SGordon Ross mutex_init(&newlease->ls_mutex, NULL, MUTEX_DEFAULT, NULL); 15194047d49SGordon Ross newlease->ls_bucket = bucket; 15294047d49SGordon Ross newlease->ls_node = of->f_node; 15394047d49SGordon Ross newlease->ls_refcnt = 1; 15494047d49SGordon Ross newlease->ls_epoch = op->lease_epoch; 15594047d49SGordon Ross newlease->ls_version = op->lease_version; 15694047d49SGordon Ross bcopy(key, newlease->ls_key, UUID_LEN); 15794047d49SGordon Ross bcopy(clnt, newlease->ls_clnt, UUID_LEN); 15894047d49SGordon Ross 15994047d49SGordon Ross smb_llist_enter(bucket, RW_WRITER); 16094047d49SGordon Ross for (lease = smb_llist_head(bucket); lease != NULL; 16194047d49SGordon Ross lease = smb_llist_next(bucket, lease)) { 16294047d49SGordon Ross /* 16394047d49SGordon Ross * Looking for this lease ID, on a node 16494047d49SGordon Ross * that's not being deleted. 16594047d49SGordon Ross */ 16694047d49SGordon Ross if (bcmp(lease->ls_key, key, UUID_LEN) == 0 && 16794047d49SGordon Ross bcmp(lease->ls_clnt, clnt, UUID_LEN) == 0 && 16894047d49SGordon Ross (lease->ls_node->flags & NODE_FLAGS_DELETING) == 0) 16994047d49SGordon Ross break; 17094047d49SGordon Ross } 17194047d49SGordon Ross if (lease != NULL) { 17294047d49SGordon Ross /* 17394047d49SGordon Ross * Found existing lease. Make sure it refers to 17494047d49SGordon Ross * the same node... 17594047d49SGordon Ross */ 17694047d49SGordon Ross if (lease->ls_node == of->f_node) { 17794047d49SGordon Ross smb2_lease_hold(lease); 17894047d49SGordon Ross } else { 17994047d49SGordon Ross /* Same lease ID, different node! */ 18094047d49SGordon Ross #ifdef DEBUG 18194047d49SGordon Ross cmn_err(CE_NOTE, "new lease on node %p (%s) " 18294047d49SGordon Ross "conflicts with existing node %p (%s)", 18394047d49SGordon Ross (void *) of->f_node, 18494047d49SGordon Ross of->f_node->od_name, 18594047d49SGordon Ross (void *) lease->ls_node, 18694047d49SGordon Ross lease->ls_node->od_name); 18794047d49SGordon Ross #endif 18894047d49SGordon Ross DTRACE_PROBE2(dup_lease, smb_request_t, sr, 18994047d49SGordon Ross smb_lease_t, lease); 19094047d49SGordon Ross lease = NULL; /* error */ 19194047d49SGordon Ross } 19294047d49SGordon Ross } else { 19394047d49SGordon Ross lease = newlease; 19494047d49SGordon Ross smb_llist_insert_head(bucket, lease); 19594047d49SGordon Ross newlease = NULL; /* don't free */ 19694047d49SGordon Ross } 19794047d49SGordon Ross smb_llist_exit(bucket); 19894047d49SGordon Ross 19994047d49SGordon Ross if (newlease != NULL) { 20094047d49SGordon Ross mutex_destroy(&newlease->ls_mutex); 20194047d49SGordon Ross kmem_cache_free(smb_lease_cache, newlease); 20294047d49SGordon Ross } 20394047d49SGordon Ross 20494047d49SGordon Ross if (lease != NULL) { 20594047d49SGordon Ross of->f_lease = lease; 20694047d49SGordon Ross status = NT_STATUS_SUCCESS; 20794047d49SGordon Ross } 20894047d49SGordon Ross 20994047d49SGordon Ross return (status); 21094047d49SGordon Ross } 21194047d49SGordon Ross 21294047d49SGordon Ross /* 21394047d49SGordon Ross * Find the lease for a given: client_uuid, lease_key 21494047d49SGordon Ross * Returns the lease with a new ref. 21594047d49SGordon Ross */ 21694047d49SGordon Ross smb_lease_t * 21794047d49SGordon Ross smb2_lease_lookup(smb_server_t *sv, uint8_t *clnt_uuid, uint8_t *lease_key) 21894047d49SGordon Ross { 21994047d49SGordon Ross smb_hash_t *ht = sv->sv_lease_ht; 22094047d49SGordon Ross smb_llist_t *bucket; 22194047d49SGordon Ross smb_lease_t *lease; 22294047d49SGordon Ross size_t hashkey; 22394047d49SGordon Ross 22494047d49SGordon Ross hashkey = smb_hash_uuid(lease_key); 22594047d49SGordon Ross hashkey &= (ht->num_buckets - 1); 22694047d49SGordon Ross bucket = &ht->buckets[hashkey].b_list; 22794047d49SGordon Ross 22894047d49SGordon Ross smb_llist_enter(bucket, RW_READER); 22994047d49SGordon Ross lease = smb_llist_head(bucket); 23094047d49SGordon Ross while (lease != NULL) { 23194047d49SGordon Ross if (bcmp(lease->ls_key, lease_key, UUID_LEN) == 0 && 23294047d49SGordon Ross bcmp(lease->ls_clnt, clnt_uuid, UUID_LEN) == 0) { 23394047d49SGordon Ross smb2_lease_hold(lease); 23494047d49SGordon Ross break; 23594047d49SGordon Ross } 23694047d49SGordon Ross lease = smb_llist_next(bucket, lease); 23794047d49SGordon Ross } 23894047d49SGordon Ross smb_llist_exit(bucket); 23994047d49SGordon Ross 24094047d49SGordon Ross return (lease); 24194047d49SGordon Ross } 24294047d49SGordon Ross 24394047d49SGordon Ross /* 24494047d49SGordon Ross * Find an smb_ofile_t in the current tree that shares the 245*6f8336c5SGordon Ross * specified lease and holds the oplock for the lease. 24694047d49SGordon Ross * If lease not found, NT_STATUS_OBJECT_NAME_NOT_FOUND. 247*6f8336c5SGordon Ross * If no ofile (on the lease) holds the oplock, NT_STATUS_UNSUCCESSFUL. 24894047d49SGordon Ross * On success, ofile (held) in sr->fid_ofile. 24994047d49SGordon Ross */ 25094047d49SGordon Ross static uint32_t 251*6f8336c5SGordon Ross find_oplock_ofile(smb_request_t *sr, uint8_t *lease_key) 25294047d49SGordon Ross { 25394047d49SGordon Ross smb_tree_t *tree = sr->tid_tree; 25494047d49SGordon Ross smb_lease_t *lease; 25594047d49SGordon Ross smb_llist_t *of_list; 25694047d49SGordon Ross smb_ofile_t *o; 25794047d49SGordon Ross uint32_t status = NT_STATUS_OBJECT_NAME_NOT_FOUND; 25894047d49SGordon Ross 25994047d49SGordon Ross SMB_TREE_VALID(tree); 26094047d49SGordon Ross of_list = &tree->t_ofile_list; 26194047d49SGordon Ross 26294047d49SGordon Ross smb_llist_enter(of_list, RW_READER); 26394047d49SGordon Ross for (o = smb_llist_head(of_list); o != NULL; 26494047d49SGordon Ross o = smb_llist_next(of_list, o)) { 26594047d49SGordon Ross 26694047d49SGordon Ross ASSERT(o->f_magic == SMB_OFILE_MAGIC); 26794047d49SGordon Ross ASSERT(o->f_tree == tree); 26894047d49SGordon Ross 269*6f8336c5SGordon Ross DTRACE_PROBE1(every_ofile, smb_ofile_t *, o); 27094047d49SGordon Ross if ((lease = o->f_lease) == NULL) 27194047d49SGordon Ross continue; // no lease 27294047d49SGordon Ross 27394047d49SGordon Ross if (bcmp(lease->ls_key, lease_key, UUID_LEN) != 0) 27494047d49SGordon Ross continue; // wrong lease 27594047d49SGordon Ross 27694047d49SGordon Ross /* 27794047d49SGordon Ross * Now we know the lease exists, so if we don't 278*6f8336c5SGordon Ross * find the ofile that has the oplock, return: 27994047d49SGordon Ross */ 28094047d49SGordon Ross status = NT_STATUS_UNSUCCESSFUL; 28194047d49SGordon Ross 282*6f8336c5SGordon Ross DTRACE_PROBE2(lease_ofile, smb_lease_t *, lease, 283*6f8336c5SGordon Ross smb_ofile_t *, o); 284*6f8336c5SGordon Ross if (lease->ls_oplock_ofile != o) 285*6f8336c5SGordon Ross continue; // not oplock holder 28694047d49SGordon Ross 287*6f8336c5SGordon Ross /* Found the ofile holding the oplock */ 28894047d49SGordon Ross if (smb_ofile_hold(o)) { 28994047d49SGordon Ross sr->fid_ofile = o; 29094047d49SGordon Ross status = NT_STATUS_SUCCESS; 29194047d49SGordon Ross break; 29294047d49SGordon Ross } 29394047d49SGordon Ross } 29494047d49SGordon Ross smb_llist_exit(of_list); 29594047d49SGordon Ross 29694047d49SGordon Ross return (status); 29794047d49SGordon Ross } 29894047d49SGordon Ross 29994047d49SGordon Ross /* 30094047d49SGordon Ross * This is called by smb2_oplock_break_ack when the struct size 30194047d49SGordon Ross * indicates this is a lease break (SZ_LEASE). See: 30294047d49SGordon Ross * [MS-SMB2] 3.3.5.22.2 Processing a Lease Acknowledgment 30394047d49SGordon Ross */ 30494047d49SGordon Ross smb_sdrc_t 30594047d49SGordon Ross smb2_lease_break_ack(smb_request_t *sr) 30694047d49SGordon Ross { 30794047d49SGordon Ross smb_lease_t *lease; 30894047d49SGordon Ross smb_ofile_t *ofile; 30994047d49SGordon Ross uint8_t LeaseKey[UUID_LEN]; 31094047d49SGordon Ross uint32_t LeaseState; 31194047d49SGordon Ross uint32_t LeaseBreakTo; 31294047d49SGordon Ross uint32_t status; 31394047d49SGordon Ross int rc = 0; 31494047d49SGordon Ross 31594047d49SGordon Ross if (sr->session->dialect < SMB_VERS_2_1) 31694047d49SGordon Ross return (SDRC_ERROR); 31794047d49SGordon Ross 31894047d49SGordon Ross /* 31994047d49SGordon Ross * Decode an SMB2 Lease Acknowldgement 32094047d49SGordon Ross * [MS-SMB2] 2.2.24.2 32194047d49SGordon Ross * Note: Struct size decoded by caller. 32294047d49SGordon Ross */ 32394047d49SGordon Ross rc = smb_mbc_decodef( 32494047d49SGordon Ross &sr->smb_data, "6.#cl8.", 32594047d49SGordon Ross /* reserved 6. */ 32694047d49SGordon Ross UUID_LEN, /* # */ 32794047d49SGordon Ross LeaseKey, /* c */ 32894047d49SGordon Ross &LeaseState); /* l */ 32994047d49SGordon Ross /* duration 8. */ 33094047d49SGordon Ross if (rc != 0) 33194047d49SGordon Ross return (SDRC_ERROR); 33294047d49SGordon Ross 333*6f8336c5SGordon Ross status = find_oplock_ofile(sr, LeaseKey); 33494047d49SGordon Ross 33594047d49SGordon Ross DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr); 33694047d49SGordon Ross if (status != 0) 33794047d49SGordon Ross goto errout; 33894047d49SGordon Ross 33994047d49SGordon Ross /* Success, so have sr->fid_ofile and lease */ 34094047d49SGordon Ross ofile = sr->fid_ofile; 34194047d49SGordon Ross lease = ofile->f_lease; 34294047d49SGordon Ross 343*6f8336c5SGordon Ross /* Either this ACK is unsolicited, or we timed out waiting. */ 344*6f8336c5SGordon Ross if (lease->ls_breaking == 0) { 345*6f8336c5SGordon Ross status = NT_STATUS_UNSUCCESSFUL; 346*6f8336c5SGordon Ross goto errout; 347*6f8336c5SGordon Ross } 348*6f8336c5SGordon Ross 34994047d49SGordon Ross /* 35094047d49SGordon Ross * Process the lease break ack. 35194047d49SGordon Ross * 35294047d49SGordon Ross * If the new LeaseState has any bits in excess of 35394047d49SGordon Ross * the lease state we sent in the break, error... 35494047d49SGordon Ross */ 35594047d49SGordon Ross LeaseBreakTo = (lease->ls_breaking >> BREAK_SHIFT) & 35694047d49SGordon Ross OPLOCK_LEVEL_CACHE_MASK; 35794047d49SGordon Ross if ((LeaseState & ~LeaseBreakTo) != 0) { 35894047d49SGordon Ross status = NT_STATUS_REQUEST_NOT_ACCEPTED; 35994047d49SGordon Ross goto errout; 36094047d49SGordon Ross } 36194047d49SGordon Ross 36294047d49SGordon Ross lease->ls_breaking = 0; 36394047d49SGordon Ross 36494047d49SGordon Ross LeaseState |= OPLOCK_LEVEL_GRANULAR; 36594047d49SGordon Ross status = smb_oplock_ack_break(sr, ofile, &LeaseState); 36694047d49SGordon Ross if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 36794047d49SGordon Ross (void) smb2sr_go_async(sr); 36894047d49SGordon Ross (void) smb_oplock_wait_break(ofile->f_node, 0); 36994047d49SGordon Ross status = NT_STATUS_SUCCESS; 37094047d49SGordon Ross } 37194047d49SGordon Ross 372*6f8336c5SGordon Ross lease->ls_state = LeaseState & OPLOCK_LEVEL_CACHE_MASK; 37394047d49SGordon Ross 37494047d49SGordon Ross errout: 37594047d49SGordon Ross sr->smb2_status = status; 37694047d49SGordon Ross DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr); 37794047d49SGordon Ross if (status) { 37894047d49SGordon Ross smb2sr_put_error(sr, status); 37994047d49SGordon Ross return (SDRC_SUCCESS); 38094047d49SGordon Ross } 38194047d49SGordon Ross 38294047d49SGordon Ross /* 38394047d49SGordon Ross * Encode an SMB2 Lease Ack. response 38494047d49SGordon Ross * [MS-SMB2] 2.2.25.2 38594047d49SGordon Ross */ 38694047d49SGordon Ross LeaseState &= OPLOCK_LEVEL_CACHE_MASK; 38794047d49SGordon Ross (void) smb_mbc_encodef( 38894047d49SGordon Ross &sr->reply, "w6.#cl8.", 38994047d49SGordon Ross SSZ_LEASE_ACK, /* w */ 39094047d49SGordon Ross /* reserved 6. */ 39194047d49SGordon Ross UUID_LEN, /* # */ 39294047d49SGordon Ross LeaseKey, /* c */ 39394047d49SGordon Ross LeaseState); /* l */ 39494047d49SGordon Ross /* duration 8. */ 39594047d49SGordon Ross 39694047d49SGordon Ross return (SDRC_SUCCESS); 39794047d49SGordon Ross 39894047d49SGordon Ross } 39994047d49SGordon Ross 40094047d49SGordon Ross /* 40194047d49SGordon Ross * Compose an SMB2 Lease Break Notification packet, including 40294047d49SGordon Ross * the SMB2 header and everything, in sr->reply. 40394047d49SGordon Ross * The caller will send it and free the request. 40494047d49SGordon Ross * 40594047d49SGordon Ross * [MS-SMB2] 2.2.23.2 Lease Break Notification 40694047d49SGordon Ross */ 40794047d49SGordon Ross void 40894047d49SGordon Ross smb2_lease_break_notification(smb_request_t *sr, uint32_t NewLevel, 40994047d49SGordon Ross boolean_t AckReq) 41094047d49SGordon Ross { 411*6f8336c5SGordon Ross smb_lease_t *ls = sr->fid_ofile->f_lease; 41294047d49SGordon Ross uint32_t oldcache; 41394047d49SGordon Ross uint32_t newcache; 41494047d49SGordon Ross uint16_t Epoch; 41594047d49SGordon Ross uint16_t Flags; 41694047d49SGordon Ross 41794047d49SGordon Ross /* 41894047d49SGordon Ross * Convert internal level to SMB2 41994047d49SGordon Ross */ 420*6f8336c5SGordon Ross oldcache = ls->ls_state & OPLOCK_LEVEL_CACHE_MASK; 42194047d49SGordon Ross newcache = NewLevel & OPLOCK_LEVEL_CACHE_MASK; 42294047d49SGordon Ross if (ls->ls_version < 2) 42394047d49SGordon Ross Epoch = 0; 42494047d49SGordon Ross else 42594047d49SGordon Ross Epoch = ls->ls_epoch; 42694047d49SGordon Ross 42794047d49SGordon Ross /* 42894047d49SGordon Ross * SMB2 Header 42994047d49SGordon Ross */ 43094047d49SGordon Ross sr->smb2_cmd_code = SMB2_OPLOCK_BREAK; 43194047d49SGordon Ross sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR; 43294047d49SGordon Ross sr->smb_tid = 0; 43394047d49SGordon Ross sr->smb_pid = 0; 43494047d49SGordon Ross sr->smb2_ssnid = 0; 43594047d49SGordon Ross sr->smb2_messageid = UINT64_MAX; 43694047d49SGordon Ross (void) smb2_encode_header(sr, B_FALSE); 43794047d49SGordon Ross 43894047d49SGordon Ross /* 43994047d49SGordon Ross * SMB2 Oplock Break, variable part 44094047d49SGordon Ross * 44194047d49SGordon Ross * [MS-SMB2] says the current lease state preceeds the 44294047d49SGordon Ross * new lease state, but that looks like an error... 44394047d49SGordon Ross */ 44494047d49SGordon Ross Flags = AckReq ? SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED : 0; 44594047d49SGordon Ross (void) smb_mbc_encodef( 44694047d49SGordon Ross &sr->reply, "wwl#cll4.4.4.", 44794047d49SGordon Ross SSZ_LEASE_BRK, /* w */ 44894047d49SGordon Ross Epoch, /* w */ 44994047d49SGordon Ross Flags, /* l */ 45094047d49SGordon Ross SMB_LEASE_KEY_SZ, /* # */ 45194047d49SGordon Ross ls->ls_key, /* c */ 45294047d49SGordon Ross oldcache, /* cur.st l */ 45394047d49SGordon Ross newcache); /* new.st l */ 45494047d49SGordon Ross /* reserved (4.4.4.) */ 45594047d49SGordon Ross } 45694047d49SGordon Ross 45794047d49SGordon Ross /* 45894047d49SGordon Ross * Client has an open handle and requests a lease. 45994047d49SGordon Ross * Convert SMB2 lease request info in to internal form, 46094047d49SGordon Ross * call common oplock code, convert result to SMB2. 46194047d49SGordon Ross * 46294047d49SGordon Ross * If necessary, "go async" here. 46394047d49SGordon Ross */ 46494047d49SGordon Ross void 46594047d49SGordon Ross smb2_lease_acquire(smb_request_t *sr) 46694047d49SGordon Ross { 46794047d49SGordon Ross smb_arg_open_t *op = &sr->arg.open; 46894047d49SGordon Ross smb_ofile_t *ofile = sr->fid_ofile; 46994047d49SGordon Ross smb_lease_t *lease = ofile->f_lease; 47094047d49SGordon Ross uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED; 47194047d49SGordon Ross uint32_t have, want; /* lease flags */ 47294047d49SGordon Ross boolean_t NewGrant = B_FALSE; 47394047d49SGordon Ross 47494047d49SGordon Ross /* Only disk trees get oplocks. */ 47594047d49SGordon Ross ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE); 47694047d49SGordon Ross 47794047d49SGordon Ross /* 47894047d49SGordon Ross * Only plain files (for now). 47994047d49SGordon Ross * Later, test SMB2_CAP_DIRECTORY_LEASING 48094047d49SGordon Ross */ 48194047d49SGordon Ross if (!smb_node_is_file(ofile->f_node)) { 48294047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 48394047d49SGordon Ross return; 48494047d49SGordon Ross } 48594047d49SGordon Ross 48694047d49SGordon Ross if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) { 48794047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 48894047d49SGordon Ross return; 48994047d49SGordon Ross } 49094047d49SGordon Ross 49194047d49SGordon Ross /* 49294047d49SGordon Ross * SMB2: Convert to internal form. 49394047d49SGordon Ross * Caller should have setup the lease. 49494047d49SGordon Ross */ 49594047d49SGordon Ross ASSERT(op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE); 49694047d49SGordon Ross ASSERT(lease != NULL); 49794047d49SGordon Ross if (lease == NULL) { 49894047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 49994047d49SGordon Ross return; 50094047d49SGordon Ross } 50194047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_GRANULAR | 50294047d49SGordon Ross (op->lease_state & CACHE_RWH); 50394047d49SGordon Ross 50494047d49SGordon Ross /* 50594047d49SGordon Ross * Tree options may force shared oplocks, 50694047d49SGordon Ross * in which case we reduce the request. 50794047d49SGordon Ross */ 50894047d49SGordon Ross if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) { 50994047d49SGordon Ross op->op_oplock_state &= ~WRITE_CACHING; 51094047d49SGordon Ross } 51194047d49SGordon Ross 51294047d49SGordon Ross /* 51394047d49SGordon Ross * Disallow downgrade 51494047d49SGordon Ross * 51594047d49SGordon Ross * Note that open with a lease is not allowed to turn off 51694047d49SGordon Ross * any cache rights. If the client tries to "downgrade", 51794047d49SGordon Ross * any bits, just return the existing lease cache bits. 51894047d49SGordon Ross */ 51994047d49SGordon Ross have = lease->ls_state & CACHE_RWH; 52094047d49SGordon Ross want = op->op_oplock_state & CACHE_RWH; 52194047d49SGordon Ross if ((have & ~want) != 0) { 52294047d49SGordon Ross op->op_oplock_state = have | 52394047d49SGordon Ross OPLOCK_LEVEL_GRANULAR; 52494047d49SGordon Ross goto done; 52594047d49SGordon Ross } 52694047d49SGordon Ross 52794047d49SGordon Ross /* 52894047d49SGordon Ross * Handle oplock requests in three parts: 52994047d49SGordon Ross * a: Requests with WRITE_CACHING 53094047d49SGordon Ross * b: Requests with HANDLE_CACHING 53194047d49SGordon Ross * c: Requests with READ_CACHING 53294047d49SGordon Ross * reducing the request before b and c. 53394047d49SGordon Ross * 53494047d49SGordon Ross * In each: first check if the lease grants the 53594047d49SGordon Ross * (possibly reduced) request, in which case we 53694047d49SGordon Ross * leave the lease unchanged and return what's 53794047d49SGordon Ross * granted by the lease. Otherwise, try to get 53894047d49SGordon Ross * the oplock, and if the succeeds, wait for any 53994047d49SGordon Ross * breaks, update the lease, and return. 54094047d49SGordon Ross */ 54194047d49SGordon Ross 54294047d49SGordon Ross /* 54394047d49SGordon Ross * Try exclusive (request is RW or RWH) 54494047d49SGordon Ross */ 54594047d49SGordon Ross if ((op->op_oplock_state & WRITE_CACHING) != 0) { 54694047d49SGordon Ross want = op->op_oplock_state & CACHE_RWH; 54794047d49SGordon Ross if (have == want) 54894047d49SGordon Ross goto done; 54994047d49SGordon Ross 55094047d49SGordon Ross status = smb_oplock_request(sr, ofile, 55194047d49SGordon Ross &op->op_oplock_state); 55294047d49SGordon Ross if (status == NT_STATUS_SUCCESS || 55394047d49SGordon Ross status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 55494047d49SGordon Ross NewGrant = B_TRUE; 55594047d49SGordon Ross goto done; 55694047d49SGordon Ross } 55794047d49SGordon Ross 55894047d49SGordon Ross /* 55994047d49SGordon Ross * We did not get the exclusive oplock. 56094047d49SGordon Ross * 56194047d49SGordon Ross * There are odd rules about lease upgrade. 56294047d49SGordon Ross * If the existing lease grants R and the 56394047d49SGordon Ross * client fails to upgrade it to "RWH" 56494047d49SGordon Ross * (presumably due to handle conflicts) 56594047d49SGordon Ross * then just return the existing lease, 56694047d49SGordon Ross * even though upgrade to RH would work. 56794047d49SGordon Ross */ 56894047d49SGordon Ross if (have != 0) { 56994047d49SGordon Ross op->op_oplock_state = have | 57094047d49SGordon Ross OPLOCK_LEVEL_GRANULAR; 57194047d49SGordon Ross goto done; 57294047d49SGordon Ross } 57394047d49SGordon Ross 57494047d49SGordon Ross /* 57594047d49SGordon Ross * Keep trying without write. 57694047d49SGordon Ross * Need to re-init op_oplock_state 57794047d49SGordon Ross */ 57894047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_GRANULAR | 57994047d49SGordon Ross (op->lease_state & CACHE_RH); 58094047d49SGordon Ross } 58194047d49SGordon Ross 58294047d49SGordon Ross /* 58394047d49SGordon Ross * Try shared ("RH") 58494047d49SGordon Ross */ 58594047d49SGordon Ross if ((op->op_oplock_state & HANDLE_CACHING) != 0) { 58694047d49SGordon Ross want = op->op_oplock_state & CACHE_RWH; 58794047d49SGordon Ross if (have == want) 58894047d49SGordon Ross goto done; 58994047d49SGordon Ross 59094047d49SGordon Ross status = smb_oplock_request(sr, ofile, 59194047d49SGordon Ross &op->op_oplock_state); 59294047d49SGordon Ross if (status == NT_STATUS_SUCCESS || 59394047d49SGordon Ross status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 59494047d49SGordon Ross NewGrant = B_TRUE; 59594047d49SGordon Ross goto done; 59694047d49SGordon Ross } 59794047d49SGordon Ross 59894047d49SGordon Ross /* 59994047d49SGordon Ross * We did not get "RH", probably because 60094047d49SGordon Ross * ther were (old style) Level II oplocks. 60194047d49SGordon Ross * Continue, try for just read. 60294047d49SGordon Ross */ 60394047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_GRANULAR | 60494047d49SGordon Ross (op->lease_state & CACHE_R); 60594047d49SGordon Ross } 60694047d49SGordon Ross 60794047d49SGordon Ross /* 60894047d49SGordon Ross * Try shared ("R") 60994047d49SGordon Ross */ 61094047d49SGordon Ross if ((op->op_oplock_state & READ_CACHING) != 0) { 61194047d49SGordon Ross want = op->op_oplock_state & CACHE_RWH; 61294047d49SGordon Ross if (have == want) 61394047d49SGordon Ross goto done; 61494047d49SGordon Ross 61594047d49SGordon Ross status = smb_oplock_request(sr, ofile, 61694047d49SGordon Ross &op->op_oplock_state); 61794047d49SGordon Ross if (status == NT_STATUS_SUCCESS || 61894047d49SGordon Ross status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 61994047d49SGordon Ross NewGrant = B_TRUE; 62094047d49SGordon Ross goto done; 62194047d49SGordon Ross } 62294047d49SGordon Ross 62394047d49SGordon Ross /* 62494047d49SGordon Ross * We did not get "R". 62594047d49SGordon Ross * Fall into "none". 62694047d49SGordon Ross */ 62794047d49SGordon Ross } 62894047d49SGordon Ross 62994047d49SGordon Ross /* 63094047d49SGordon Ross * None of the above were able to get an oplock. 63194047d49SGordon Ross * The lease has no caching rights, and we didn't 63294047d49SGordon Ross * add any in this request. Return it as-is. 63394047d49SGordon Ross */ 63494047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_GRANULAR; 63594047d49SGordon Ross 63694047d49SGordon Ross done: 63794047d49SGordon Ross if (NewGrant) { 63894047d49SGordon Ross /* 63994047d49SGordon Ross * After a new oplock grant, the status return 64094047d49SGordon Ross * may indicate we need to wait for breaks. 64194047d49SGordon Ross */ 64294047d49SGordon Ross if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 64394047d49SGordon Ross (void) smb2sr_go_async(sr); 64494047d49SGordon Ross (void) smb_oplock_wait_break(ofile->f_node, 0); 64594047d49SGordon Ross status = NT_STATUS_SUCCESS; 64694047d49SGordon Ross } 64794047d49SGordon Ross ASSERT(status == NT_STATUS_SUCCESS); 64894047d49SGordon Ross 64994047d49SGordon Ross /* 650*6f8336c5SGordon Ross * Keep track of what we got (in lease->ls_state) 651*6f8336c5SGordon Ross * so we'll know what we last told the client. 65294047d49SGordon Ross */ 65394047d49SGordon Ross mutex_enter(&lease->ls_mutex); 65494047d49SGordon Ross lease->ls_state = op->op_oplock_state & CACHE_RWH; 65594047d49SGordon Ross lease->ls_epoch++; 65694047d49SGordon Ross mutex_exit(&lease->ls_mutex); 65794047d49SGordon Ross } 65894047d49SGordon Ross 65994047d49SGordon Ross /* 66094047d49SGordon Ross * Convert internal oplock state to SMB2 66194047d49SGordon Ross */ 66294047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE; 66394047d49SGordon Ross op->lease_state = lease->ls_state & CACHE_RWH; 66494047d49SGordon Ross op->lease_flags = (lease->ls_breaking != 0) ? 66594047d49SGordon Ross SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0; 66694047d49SGordon Ross op->lease_epoch = lease->ls_epoch; 66794047d49SGordon Ross op->lease_version = lease->ls_version; 66894047d49SGordon Ross } 66994047d49SGordon Ross 67094047d49SGordon Ross /* 67194047d49SGordon Ross * This ofile has a lease and is about to close. 67294047d49SGordon Ross * Called by smb_ofile_close when there's a lease. 67394047d49SGordon Ross * 67494047d49SGordon Ross * With leases, just one ofile on a lease owns the oplock. 67594047d49SGordon Ross * If an ofile with a lease is closed and it's the one that 67694047d49SGordon Ross * owns the oplock, try to move the oplock to another ofile 67794047d49SGordon Ross * on the same lease. 67894047d49SGordon Ross */ 67994047d49SGordon Ross void 68094047d49SGordon Ross smb2_lease_ofile_close(smb_ofile_t *ofile) 68194047d49SGordon Ross { 68294047d49SGordon Ross smb_node_t *node = ofile->f_node; 68394047d49SGordon Ross smb_lease_t *lease = ofile->f_lease; 68494047d49SGordon Ross smb_ofile_t *o; 68594047d49SGordon Ross 6861f0845f1SGordon Ross ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock)); 6871f0845f1SGordon Ross ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex)); 6881f0845f1SGordon Ross 68994047d49SGordon Ross /* 69094047d49SGordon Ross * If this ofile was not the oplock owner for this lease, 69194047d49SGordon Ross * we can leave things as they are. 69294047d49SGordon Ross */ 69394047d49SGordon Ross if (lease->ls_oplock_ofile != ofile) 69494047d49SGordon Ross return; 69594047d49SGordon Ross 69694047d49SGordon Ross /* 69794047d49SGordon Ross * Find another ofile to which we can move the oplock. 69894047d49SGordon Ross * The ofile must be open and allow a new ref. 69994047d49SGordon Ross */ 70094047d49SGordon Ross FOREACH_NODE_OFILE(node, o) { 70194047d49SGordon Ross if (o == ofile) 70294047d49SGordon Ross continue; 70394047d49SGordon Ross if (o->f_lease != lease) 70494047d49SGordon Ross continue; 705*6f8336c5SGordon Ross if (o->f_oplock_closing) 7061f0845f1SGordon Ross continue; 70794047d49SGordon Ross /* If we can get a hold, use this ofile. */ 708a9931e68SGordon Ross if (smb_ofile_hold(o)) 70994047d49SGordon Ross break; 71094047d49SGordon Ross } 71194047d49SGordon Ross if (o == NULL) { 71294047d49SGordon Ross /* Normal for last close on a lease. */ 713*6f8336c5SGordon Ross lease->ls_oplock_ofile = NULL; 71494047d49SGordon Ross return; 71594047d49SGordon Ross } 71694047d49SGordon Ross smb_oplock_move(node, ofile, o); 717*6f8336c5SGordon Ross lease->ls_oplock_ofile = o; 71894047d49SGordon Ross 71994047d49SGordon Ross smb_ofile_release(o); 72094047d49SGordon Ross } 721