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