1*94047d49SGordon Ross /* 2*94047d49SGordon Ross * This file and its contents are supplied under the terms of the 3*94047d49SGordon Ross * Common Development and Distribution License ("CDDL"), version 1.0. 4*94047d49SGordon Ross * You may only use this file in accordance with the terms of version 5*94047d49SGordon Ross * 1.0 of the CDDL. 6*94047d49SGordon Ross * 7*94047d49SGordon Ross * A full copy of the text of the CDDL should have accompanied this 8*94047d49SGordon Ross * source. A copy of the CDDL is also available via the Internet at 9*94047d49SGordon Ross * http://www.illumos.org/license/CDDL. 10*94047d49SGordon Ross */ 11*94047d49SGordon Ross 12*94047d49SGordon Ross /* 13*94047d49SGordon Ross * Copyright 2017 Nexenta Systems, Inc. All rights reserved. 14*94047d49SGordon Ross */ 15*94047d49SGordon Ross 16*94047d49SGordon Ross /* 17*94047d49SGordon Ross * Dispatch function for SMB2_OPLOCK_BREAK 18*94047d49SGordon Ross */ 19*94047d49SGordon Ross 20*94047d49SGordon Ross #include <smbsrv/smb2_kproto.h> 21*94047d49SGordon Ross #include <smbsrv/smb_oplock.h> 22*94047d49SGordon Ross 23*94047d49SGordon Ross /* StructSize for the two "break" message formats. */ 24*94047d49SGordon Ross #define SSZ_OPLOCK 24 25*94047d49SGordon Ross #define SSZ_LEASE_ACK 36 26*94047d49SGordon Ross #define SSZ_LEASE_BRK 44 27*94047d49SGordon Ross 28*94047d49SGordon Ross #define NODE_FLAGS_DELETING (NODE_FLAGS_DELETE_ON_CLOSE |\ 29*94047d49SGordon Ross NODE_FLAGS_DELETE_COMMITTED) 30*94047d49SGordon Ross 31*94047d49SGordon Ross static const char lease_zero[UUID_LEN] = { 0 }; 32*94047d49SGordon Ross 33*94047d49SGordon Ross static kmem_cache_t *smb_lease_cache = NULL; 34*94047d49SGordon Ross 35*94047d49SGordon Ross void 36*94047d49SGordon Ross smb2_lease_init() 37*94047d49SGordon Ross { 38*94047d49SGordon Ross if (smb_lease_cache != NULL) 39*94047d49SGordon Ross return; 40*94047d49SGordon Ross 41*94047d49SGordon Ross smb_lease_cache = kmem_cache_create("smb_lease_cache", 42*94047d49SGordon Ross sizeof (smb_lease_t), 8, NULL, NULL, NULL, NULL, NULL, 0); 43*94047d49SGordon Ross } 44*94047d49SGordon Ross 45*94047d49SGordon Ross void 46*94047d49SGordon Ross smb2_lease_fini() 47*94047d49SGordon Ross { 48*94047d49SGordon Ross if (smb_lease_cache != NULL) { 49*94047d49SGordon Ross kmem_cache_destroy(smb_lease_cache); 50*94047d49SGordon Ross smb_lease_cache = NULL; 51*94047d49SGordon Ross } 52*94047d49SGordon Ross } 53*94047d49SGordon Ross 54*94047d49SGordon Ross static void 55*94047d49SGordon Ross smb2_lease_hold(smb_lease_t *ls) 56*94047d49SGordon Ross { 57*94047d49SGordon Ross mutex_enter(&ls->ls_mutex); 58*94047d49SGordon Ross ls->ls_refcnt++; 59*94047d49SGordon Ross mutex_exit(&ls->ls_mutex); 60*94047d49SGordon Ross } 61*94047d49SGordon Ross 62*94047d49SGordon Ross void 63*94047d49SGordon Ross smb2_lease_rele(smb_lease_t *ls) 64*94047d49SGordon Ross { 65*94047d49SGordon Ross smb_llist_t *bucket; 66*94047d49SGordon Ross 67*94047d49SGordon Ross mutex_enter(&ls->ls_mutex); 68*94047d49SGordon Ross ls->ls_refcnt--; 69*94047d49SGordon Ross if (ls->ls_refcnt != 0) { 70*94047d49SGordon Ross mutex_exit(&ls->ls_mutex); 71*94047d49SGordon Ross return; 72*94047d49SGordon Ross } 73*94047d49SGordon Ross mutex_exit(&ls->ls_mutex); 74*94047d49SGordon Ross 75*94047d49SGordon Ross /* 76*94047d49SGordon Ross * Get the list lock, then re-check the refcnt 77*94047d49SGordon Ross * and if it's still zero, unlink & destroy. 78*94047d49SGordon Ross */ 79*94047d49SGordon Ross bucket = ls->ls_bucket; 80*94047d49SGordon Ross smb_llist_enter(bucket, RW_WRITER); 81*94047d49SGordon Ross 82*94047d49SGordon Ross mutex_enter(&ls->ls_mutex); 83*94047d49SGordon Ross if (ls->ls_refcnt == 0) 84*94047d49SGordon Ross smb_llist_remove(bucket, ls); 85*94047d49SGordon Ross mutex_exit(&ls->ls_mutex); 86*94047d49SGordon Ross 87*94047d49SGordon Ross if (ls->ls_refcnt == 0) { 88*94047d49SGordon Ross mutex_destroy(&ls->ls_mutex); 89*94047d49SGordon Ross kmem_cache_free(smb_lease_cache, ls); 90*94047d49SGordon Ross } 91*94047d49SGordon Ross 92*94047d49SGordon Ross smb_llist_exit(bucket); 93*94047d49SGordon Ross } 94*94047d49SGordon Ross 95*94047d49SGordon Ross /* 96*94047d49SGordon Ross * Compute a hash from a uuid 97*94047d49SGordon Ross * Based on mod_hash_bystr() 98*94047d49SGordon Ross */ 99*94047d49SGordon Ross static uint_t 100*94047d49SGordon Ross smb_hash_uuid(const uint8_t *uuid) 101*94047d49SGordon Ross { 102*94047d49SGordon Ross char *k = (char *)uuid; 103*94047d49SGordon Ross uint_t hash = 0; 104*94047d49SGordon Ross uint_t g; 105*94047d49SGordon Ross int i; 106*94047d49SGordon Ross 107*94047d49SGordon Ross ASSERT(k); 108*94047d49SGordon Ross for (i = 0; i < UUID_LEN; i++) { 109*94047d49SGordon Ross hash = (hash << 4) + k[i]; 110*94047d49SGordon Ross if ((g = (hash & 0xf0000000)) != 0) { 111*94047d49SGordon Ross hash ^= (g >> 24); 112*94047d49SGordon Ross hash ^= g; 113*94047d49SGordon Ross } 114*94047d49SGordon Ross } 115*94047d49SGordon Ross return (hash); 116*94047d49SGordon Ross } 117*94047d49SGordon Ross 118*94047d49SGordon Ross /* 119*94047d49SGordon Ross * Add or update a lease table entry for a new ofile. 120*94047d49SGordon Ross * (in the per-session lease table) 121*94047d49SGordon Ross * See [MS-SMB2] 3.3.5.9.8 122*94047d49SGordon Ross * Handling the SMB2_CREATE_REQUEST_LEASE Create Context 123*94047d49SGordon Ross */ 124*94047d49SGordon Ross uint32_t 125*94047d49SGordon Ross smb2_lease_create(smb_request_t *sr) 126*94047d49SGordon Ross { 127*94047d49SGordon Ross smb_arg_open_t *op = &sr->arg.open; 128*94047d49SGordon Ross uint8_t *key = op->lease_key; 129*94047d49SGordon Ross uint8_t *clnt = sr->session->clnt_uuid; 130*94047d49SGordon Ross smb_ofile_t *of = sr->fid_ofile; 131*94047d49SGordon Ross smb_hash_t *ht = sr->sr_server->sv_lease_ht; 132*94047d49SGordon Ross smb_llist_t *bucket; 133*94047d49SGordon Ross smb_lease_t *lease; 134*94047d49SGordon Ross smb_lease_t *newlease; 135*94047d49SGordon Ross size_t hashkey; 136*94047d49SGordon Ross uint32_t status = NT_STATUS_INVALID_PARAMETER; 137*94047d49SGordon Ross 138*94047d49SGordon Ross if (bcmp(key, lease_zero, UUID_LEN) == 0) 139*94047d49SGordon Ross return (status); 140*94047d49SGordon Ross 141*94047d49SGordon Ross /* 142*94047d49SGordon Ross * Find or create, and add a ref for the new ofile. 143*94047d49SGordon Ross */ 144*94047d49SGordon Ross hashkey = smb_hash_uuid(key); 145*94047d49SGordon Ross hashkey &= (ht->num_buckets - 1); 146*94047d49SGordon Ross bucket = &ht->buckets[hashkey].b_list; 147*94047d49SGordon Ross 148*94047d49SGordon Ross newlease = kmem_cache_alloc(smb_lease_cache, KM_SLEEP); 149*94047d49SGordon Ross bzero(newlease, sizeof (smb_lease_t)); 150*94047d49SGordon Ross mutex_init(&newlease->ls_mutex, NULL, MUTEX_DEFAULT, NULL); 151*94047d49SGordon Ross newlease->ls_bucket = bucket; 152*94047d49SGordon Ross newlease->ls_node = of->f_node; 153*94047d49SGordon Ross newlease->ls_refcnt = 1; 154*94047d49SGordon Ross newlease->ls_epoch = op->lease_epoch; 155*94047d49SGordon Ross newlease->ls_version = op->lease_version; 156*94047d49SGordon Ross bcopy(key, newlease->ls_key, UUID_LEN); 157*94047d49SGordon Ross bcopy(clnt, newlease->ls_clnt, UUID_LEN); 158*94047d49SGordon Ross 159*94047d49SGordon Ross smb_llist_enter(bucket, RW_WRITER); 160*94047d49SGordon Ross for (lease = smb_llist_head(bucket); lease != NULL; 161*94047d49SGordon Ross lease = smb_llist_next(bucket, lease)) { 162*94047d49SGordon Ross /* 163*94047d49SGordon Ross * Looking for this lease ID, on a node 164*94047d49SGordon Ross * that's not being deleted. 165*94047d49SGordon Ross */ 166*94047d49SGordon Ross if (bcmp(lease->ls_key, key, UUID_LEN) == 0 && 167*94047d49SGordon Ross bcmp(lease->ls_clnt, clnt, UUID_LEN) == 0 && 168*94047d49SGordon Ross (lease->ls_node->flags & NODE_FLAGS_DELETING) == 0) 169*94047d49SGordon Ross break; 170*94047d49SGordon Ross } 171*94047d49SGordon Ross if (lease != NULL) { 172*94047d49SGordon Ross /* 173*94047d49SGordon Ross * Found existing lease. Make sure it refers to 174*94047d49SGordon Ross * the same node... 175*94047d49SGordon Ross */ 176*94047d49SGordon Ross if (lease->ls_node == of->f_node) { 177*94047d49SGordon Ross smb2_lease_hold(lease); 178*94047d49SGordon Ross } else { 179*94047d49SGordon Ross /* Same lease ID, different node! */ 180*94047d49SGordon Ross #ifdef DEBUG 181*94047d49SGordon Ross cmn_err(CE_NOTE, "new lease on node %p (%s) " 182*94047d49SGordon Ross "conflicts with existing node %p (%s)", 183*94047d49SGordon Ross (void *) of->f_node, 184*94047d49SGordon Ross of->f_node->od_name, 185*94047d49SGordon Ross (void *) lease->ls_node, 186*94047d49SGordon Ross lease->ls_node->od_name); 187*94047d49SGordon Ross #endif 188*94047d49SGordon Ross DTRACE_PROBE2(dup_lease, smb_request_t, sr, 189*94047d49SGordon Ross smb_lease_t, lease); 190*94047d49SGordon Ross lease = NULL; /* error */ 191*94047d49SGordon Ross } 192*94047d49SGordon Ross } else { 193*94047d49SGordon Ross lease = newlease; 194*94047d49SGordon Ross smb_llist_insert_head(bucket, lease); 195*94047d49SGordon Ross newlease = NULL; /* don't free */ 196*94047d49SGordon Ross } 197*94047d49SGordon Ross smb_llist_exit(bucket); 198*94047d49SGordon Ross 199*94047d49SGordon Ross if (newlease != NULL) { 200*94047d49SGordon Ross mutex_destroy(&newlease->ls_mutex); 201*94047d49SGordon Ross kmem_cache_free(smb_lease_cache, newlease); 202*94047d49SGordon Ross } 203*94047d49SGordon Ross 204*94047d49SGordon Ross if (lease != NULL) { 205*94047d49SGordon Ross of->f_lease = lease; 206*94047d49SGordon Ross status = NT_STATUS_SUCCESS; 207*94047d49SGordon Ross } 208*94047d49SGordon Ross 209*94047d49SGordon Ross return (status); 210*94047d49SGordon Ross } 211*94047d49SGordon Ross 212*94047d49SGordon Ross /* 213*94047d49SGordon Ross * Find the lease for a given: client_uuid, lease_key 214*94047d49SGordon Ross * Returns the lease with a new ref. 215*94047d49SGordon Ross */ 216*94047d49SGordon Ross smb_lease_t * 217*94047d49SGordon Ross smb2_lease_lookup(smb_server_t *sv, uint8_t *clnt_uuid, uint8_t *lease_key) 218*94047d49SGordon Ross { 219*94047d49SGordon Ross smb_hash_t *ht = sv->sv_lease_ht; 220*94047d49SGordon Ross smb_llist_t *bucket; 221*94047d49SGordon Ross smb_lease_t *lease; 222*94047d49SGordon Ross size_t hashkey; 223*94047d49SGordon Ross 224*94047d49SGordon Ross hashkey = smb_hash_uuid(lease_key); 225*94047d49SGordon Ross hashkey &= (ht->num_buckets - 1); 226*94047d49SGordon Ross bucket = &ht->buckets[hashkey].b_list; 227*94047d49SGordon Ross 228*94047d49SGordon Ross smb_llist_enter(bucket, RW_READER); 229*94047d49SGordon Ross lease = smb_llist_head(bucket); 230*94047d49SGordon Ross while (lease != NULL) { 231*94047d49SGordon Ross if (bcmp(lease->ls_key, lease_key, UUID_LEN) == 0 && 232*94047d49SGordon Ross bcmp(lease->ls_clnt, clnt_uuid, UUID_LEN) == 0) { 233*94047d49SGordon Ross smb2_lease_hold(lease); 234*94047d49SGordon Ross break; 235*94047d49SGordon Ross } 236*94047d49SGordon Ross lease = smb_llist_next(bucket, lease); 237*94047d49SGordon Ross } 238*94047d49SGordon Ross smb_llist_exit(bucket); 239*94047d49SGordon Ross 240*94047d49SGordon Ross return (lease); 241*94047d49SGordon Ross } 242*94047d49SGordon Ross 243*94047d49SGordon Ross /* 244*94047d49SGordon Ross * Find an smb_ofile_t in the current tree that shares the 245*94047d49SGordon Ross * specified lease and has some oplock breaking flags set. 246*94047d49SGordon Ross * If lease not found, NT_STATUS_OBJECT_NAME_NOT_FOUND. 247*94047d49SGordon Ross * If ofile not breaking NT_STATUS_UNSUCCESSFUL. 248*94047d49SGordon Ross * On success, ofile (held) in sr->fid_ofile. 249*94047d49SGordon Ross */ 250*94047d49SGordon Ross static uint32_t 251*94047d49SGordon Ross find_breaking_ofile(smb_request_t *sr, uint8_t *lease_key) 252*94047d49SGordon Ross { 253*94047d49SGordon Ross smb_tree_t *tree = sr->tid_tree; 254*94047d49SGordon Ross smb_lease_t *lease; 255*94047d49SGordon Ross smb_llist_t *of_list; 256*94047d49SGordon Ross smb_ofile_t *o; 257*94047d49SGordon Ross uint32_t status = NT_STATUS_OBJECT_NAME_NOT_FOUND; 258*94047d49SGordon Ross 259*94047d49SGordon Ross SMB_TREE_VALID(tree); 260*94047d49SGordon Ross of_list = &tree->t_ofile_list; 261*94047d49SGordon Ross 262*94047d49SGordon Ross smb_llist_enter(of_list, RW_READER); 263*94047d49SGordon Ross for (o = smb_llist_head(of_list); o != NULL; 264*94047d49SGordon Ross o = smb_llist_next(of_list, o)) { 265*94047d49SGordon Ross 266*94047d49SGordon Ross ASSERT(o->f_magic == SMB_OFILE_MAGIC); 267*94047d49SGordon Ross ASSERT(o->f_tree == tree); 268*94047d49SGordon Ross 269*94047d49SGordon Ross if ((lease = o->f_lease) == NULL) 270*94047d49SGordon Ross continue; // no lease 271*94047d49SGordon Ross 272*94047d49SGordon Ross if (bcmp(lease->ls_key, lease_key, UUID_LEN) != 0) 273*94047d49SGordon Ross continue; // wrong lease 274*94047d49SGordon Ross 275*94047d49SGordon Ross /* 276*94047d49SGordon Ross * Now we know the lease exists, so if we don't 277*94047d49SGordon Ross * find an ofile with breaking flags, return: 278*94047d49SGordon Ross */ 279*94047d49SGordon Ross status = NT_STATUS_UNSUCCESSFUL; 280*94047d49SGordon Ross 281*94047d49SGordon Ross if (o->f_oplock.og_breaking == 0) 282*94047d49SGordon Ross continue; // not breaking 283*94047d49SGordon Ross 284*94047d49SGordon Ross /* Found breaking ofile. */ 285*94047d49SGordon Ross if (smb_ofile_hold(o)) { 286*94047d49SGordon Ross sr->fid_ofile = o; 287*94047d49SGordon Ross status = NT_STATUS_SUCCESS; 288*94047d49SGordon Ross break; 289*94047d49SGordon Ross } 290*94047d49SGordon Ross } 291*94047d49SGordon Ross smb_llist_exit(of_list); 292*94047d49SGordon Ross 293*94047d49SGordon Ross return (status); 294*94047d49SGordon Ross } 295*94047d49SGordon Ross 296*94047d49SGordon Ross /* 297*94047d49SGordon Ross * This is called by smb2_oplock_break_ack when the struct size 298*94047d49SGordon Ross * indicates this is a lease break (SZ_LEASE). See: 299*94047d49SGordon Ross * [MS-SMB2] 3.3.5.22.2 Processing a Lease Acknowledgment 300*94047d49SGordon Ross */ 301*94047d49SGordon Ross smb_sdrc_t 302*94047d49SGordon Ross smb2_lease_break_ack(smb_request_t *sr) 303*94047d49SGordon Ross { 304*94047d49SGordon Ross smb_lease_t *lease; 305*94047d49SGordon Ross smb_ofile_t *ofile; 306*94047d49SGordon Ross uint8_t LeaseKey[UUID_LEN]; 307*94047d49SGordon Ross uint32_t LeaseState; 308*94047d49SGordon Ross uint32_t LeaseBreakTo; 309*94047d49SGordon Ross uint32_t status; 310*94047d49SGordon Ross int rc = 0; 311*94047d49SGordon Ross 312*94047d49SGordon Ross if (sr->session->dialect < SMB_VERS_2_1) 313*94047d49SGordon Ross return (SDRC_ERROR); 314*94047d49SGordon Ross 315*94047d49SGordon Ross /* 316*94047d49SGordon Ross * Decode an SMB2 Lease Acknowldgement 317*94047d49SGordon Ross * [MS-SMB2] 2.2.24.2 318*94047d49SGordon Ross * Note: Struct size decoded by caller. 319*94047d49SGordon Ross */ 320*94047d49SGordon Ross rc = smb_mbc_decodef( 321*94047d49SGordon Ross &sr->smb_data, "6.#cl8.", 322*94047d49SGordon Ross /* reserved 6. */ 323*94047d49SGordon Ross UUID_LEN, /* # */ 324*94047d49SGordon Ross LeaseKey, /* c */ 325*94047d49SGordon Ross &LeaseState); /* l */ 326*94047d49SGordon Ross /* duration 8. */ 327*94047d49SGordon Ross if (rc != 0) 328*94047d49SGordon Ross return (SDRC_ERROR); 329*94047d49SGordon Ross 330*94047d49SGordon Ross status = find_breaking_ofile(sr, LeaseKey); 331*94047d49SGordon Ross 332*94047d49SGordon Ross DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr); 333*94047d49SGordon Ross if (status != 0) 334*94047d49SGordon Ross goto errout; 335*94047d49SGordon Ross 336*94047d49SGordon Ross /* Success, so have sr->fid_ofile and lease */ 337*94047d49SGordon Ross ofile = sr->fid_ofile; 338*94047d49SGordon Ross lease = ofile->f_lease; 339*94047d49SGordon Ross 340*94047d49SGordon Ross /* 341*94047d49SGordon Ross * Process the lease break ack. 342*94047d49SGordon Ross * 343*94047d49SGordon Ross * If the new LeaseState has any bits in excess of 344*94047d49SGordon Ross * the lease state we sent in the break, error... 345*94047d49SGordon Ross */ 346*94047d49SGordon Ross LeaseBreakTo = (lease->ls_breaking >> BREAK_SHIFT) & 347*94047d49SGordon Ross OPLOCK_LEVEL_CACHE_MASK; 348*94047d49SGordon Ross if ((LeaseState & ~LeaseBreakTo) != 0) { 349*94047d49SGordon Ross status = NT_STATUS_REQUEST_NOT_ACCEPTED; 350*94047d49SGordon Ross goto errout; 351*94047d49SGordon Ross } 352*94047d49SGordon Ross 353*94047d49SGordon Ross ofile->f_oplock.og_breaking = 0; 354*94047d49SGordon Ross lease->ls_breaking = 0; 355*94047d49SGordon Ross 356*94047d49SGordon Ross LeaseState |= OPLOCK_LEVEL_GRANULAR; 357*94047d49SGordon Ross status = smb_oplock_ack_break(sr, ofile, &LeaseState); 358*94047d49SGordon Ross if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 359*94047d49SGordon Ross (void) smb2sr_go_async(sr); 360*94047d49SGordon Ross (void) smb_oplock_wait_break(ofile->f_node, 0); 361*94047d49SGordon Ross status = NT_STATUS_SUCCESS; 362*94047d49SGordon Ross } 363*94047d49SGordon Ross 364*94047d49SGordon Ross ofile->f_oplock.og_state = LeaseState; 365*94047d49SGordon Ross lease->ls_state = LeaseState & 366*94047d49SGordon Ross OPLOCK_LEVEL_CACHE_MASK; 367*94047d49SGordon Ross 368*94047d49SGordon Ross errout: 369*94047d49SGordon Ross sr->smb2_status = status; 370*94047d49SGordon Ross DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr); 371*94047d49SGordon Ross if (status) { 372*94047d49SGordon Ross smb2sr_put_error(sr, status); 373*94047d49SGordon Ross return (SDRC_SUCCESS); 374*94047d49SGordon Ross } 375*94047d49SGordon Ross 376*94047d49SGordon Ross /* 377*94047d49SGordon Ross * Encode an SMB2 Lease Ack. response 378*94047d49SGordon Ross * [MS-SMB2] 2.2.25.2 379*94047d49SGordon Ross */ 380*94047d49SGordon Ross LeaseState &= OPLOCK_LEVEL_CACHE_MASK; 381*94047d49SGordon Ross (void) smb_mbc_encodef( 382*94047d49SGordon Ross &sr->reply, "w6.#cl8.", 383*94047d49SGordon Ross SSZ_LEASE_ACK, /* w */ 384*94047d49SGordon Ross /* reserved 6. */ 385*94047d49SGordon Ross UUID_LEN, /* # */ 386*94047d49SGordon Ross LeaseKey, /* c */ 387*94047d49SGordon Ross LeaseState); /* l */ 388*94047d49SGordon Ross /* duration 8. */ 389*94047d49SGordon Ross 390*94047d49SGordon Ross return (SDRC_SUCCESS); 391*94047d49SGordon Ross 392*94047d49SGordon Ross } 393*94047d49SGordon Ross 394*94047d49SGordon Ross /* 395*94047d49SGordon Ross * Compose an SMB2 Lease Break Notification packet, including 396*94047d49SGordon Ross * the SMB2 header and everything, in sr->reply. 397*94047d49SGordon Ross * The caller will send it and free the request. 398*94047d49SGordon Ross * 399*94047d49SGordon Ross * [MS-SMB2] 2.2.23.2 Lease Break Notification 400*94047d49SGordon Ross */ 401*94047d49SGordon Ross void 402*94047d49SGordon Ross smb2_lease_break_notification(smb_request_t *sr, uint32_t NewLevel, 403*94047d49SGordon Ross boolean_t AckReq) 404*94047d49SGordon Ross { 405*94047d49SGordon Ross smb_ofile_t *ofile = sr->fid_ofile; 406*94047d49SGordon Ross smb_oplock_grant_t *og = &ofile->f_oplock; 407*94047d49SGordon Ross smb_lease_t *ls = ofile->f_lease; 408*94047d49SGordon Ross uint32_t oldcache; 409*94047d49SGordon Ross uint32_t newcache; 410*94047d49SGordon Ross uint16_t Epoch; 411*94047d49SGordon Ross uint16_t Flags; 412*94047d49SGordon Ross 413*94047d49SGordon Ross /* 414*94047d49SGordon Ross * Convert internal level to SMB2 415*94047d49SGordon Ross */ 416*94047d49SGordon Ross oldcache = og->og_state & OPLOCK_LEVEL_CACHE_MASK; 417*94047d49SGordon Ross newcache = NewLevel & OPLOCK_LEVEL_CACHE_MASK; 418*94047d49SGordon Ross if (ls->ls_version < 2) 419*94047d49SGordon Ross Epoch = 0; 420*94047d49SGordon Ross else 421*94047d49SGordon Ross Epoch = ls->ls_epoch; 422*94047d49SGordon Ross 423*94047d49SGordon Ross /* 424*94047d49SGordon Ross * SMB2 Header 425*94047d49SGordon Ross */ 426*94047d49SGordon Ross sr->smb2_cmd_code = SMB2_OPLOCK_BREAK; 427*94047d49SGordon Ross sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR; 428*94047d49SGordon Ross sr->smb_tid = 0; 429*94047d49SGordon Ross sr->smb_pid = 0; 430*94047d49SGordon Ross sr->smb2_ssnid = 0; 431*94047d49SGordon Ross sr->smb2_messageid = UINT64_MAX; 432*94047d49SGordon Ross (void) smb2_encode_header(sr, B_FALSE); 433*94047d49SGordon Ross 434*94047d49SGordon Ross /* 435*94047d49SGordon Ross * SMB2 Oplock Break, variable part 436*94047d49SGordon Ross * 437*94047d49SGordon Ross * [MS-SMB2] says the current lease state preceeds the 438*94047d49SGordon Ross * new lease state, but that looks like an error... 439*94047d49SGordon Ross */ 440*94047d49SGordon Ross Flags = AckReq ? SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED : 0; 441*94047d49SGordon Ross (void) smb_mbc_encodef( 442*94047d49SGordon Ross &sr->reply, "wwl#cll4.4.4.", 443*94047d49SGordon Ross SSZ_LEASE_BRK, /* w */ 444*94047d49SGordon Ross Epoch, /* w */ 445*94047d49SGordon Ross Flags, /* l */ 446*94047d49SGordon Ross SMB_LEASE_KEY_SZ, /* # */ 447*94047d49SGordon Ross ls->ls_key, /* c */ 448*94047d49SGordon Ross oldcache, /* cur.st l */ 449*94047d49SGordon Ross newcache); /* new.st l */ 450*94047d49SGordon Ross /* reserved (4.4.4.) */ 451*94047d49SGordon Ross } 452*94047d49SGordon Ross 453*94047d49SGordon Ross /* 454*94047d49SGordon Ross * Client has an open handle and requests a lease. 455*94047d49SGordon Ross * Convert SMB2 lease request info in to internal form, 456*94047d49SGordon Ross * call common oplock code, convert result to SMB2. 457*94047d49SGordon Ross * 458*94047d49SGordon Ross * If necessary, "go async" here. 459*94047d49SGordon Ross */ 460*94047d49SGordon Ross void 461*94047d49SGordon Ross smb2_lease_acquire(smb_request_t *sr) 462*94047d49SGordon Ross { 463*94047d49SGordon Ross smb_arg_open_t *op = &sr->arg.open; 464*94047d49SGordon Ross smb_ofile_t *ofile = sr->fid_ofile; 465*94047d49SGordon Ross smb_lease_t *lease = ofile->f_lease; 466*94047d49SGordon Ross uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED; 467*94047d49SGordon Ross uint32_t have, want; /* lease flags */ 468*94047d49SGordon Ross boolean_t NewGrant = B_FALSE; 469*94047d49SGordon Ross 470*94047d49SGordon Ross /* Only disk trees get oplocks. */ 471*94047d49SGordon Ross ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE); 472*94047d49SGordon Ross 473*94047d49SGordon Ross /* 474*94047d49SGordon Ross * Only plain files (for now). 475*94047d49SGordon Ross * Later, test SMB2_CAP_DIRECTORY_LEASING 476*94047d49SGordon Ross */ 477*94047d49SGordon Ross if (!smb_node_is_file(ofile->f_node)) { 478*94047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 479*94047d49SGordon Ross return; 480*94047d49SGordon Ross } 481*94047d49SGordon Ross 482*94047d49SGordon Ross if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) { 483*94047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 484*94047d49SGordon Ross return; 485*94047d49SGordon Ross } 486*94047d49SGordon Ross 487*94047d49SGordon Ross /* 488*94047d49SGordon Ross * SMB2: Convert to internal form. 489*94047d49SGordon Ross * Caller should have setup the lease. 490*94047d49SGordon Ross */ 491*94047d49SGordon Ross ASSERT(op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE); 492*94047d49SGordon Ross ASSERT(lease != NULL); 493*94047d49SGordon Ross if (lease == NULL) { 494*94047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 495*94047d49SGordon Ross return; 496*94047d49SGordon Ross } 497*94047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_GRANULAR | 498*94047d49SGordon Ross (op->lease_state & CACHE_RWH); 499*94047d49SGordon Ross 500*94047d49SGordon Ross /* 501*94047d49SGordon Ross * Tree options may force shared oplocks, 502*94047d49SGordon Ross * in which case we reduce the request. 503*94047d49SGordon Ross */ 504*94047d49SGordon Ross if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) { 505*94047d49SGordon Ross op->op_oplock_state &= ~WRITE_CACHING; 506*94047d49SGordon Ross } 507*94047d49SGordon Ross 508*94047d49SGordon Ross /* 509*94047d49SGordon Ross * Disallow downgrade 510*94047d49SGordon Ross * 511*94047d49SGordon Ross * Note that open with a lease is not allowed to turn off 512*94047d49SGordon Ross * any cache rights. If the client tries to "downgrade", 513*94047d49SGordon Ross * any bits, just return the existing lease cache bits. 514*94047d49SGordon Ross */ 515*94047d49SGordon Ross have = lease->ls_state & CACHE_RWH; 516*94047d49SGordon Ross want = op->op_oplock_state & CACHE_RWH; 517*94047d49SGordon Ross if ((have & ~want) != 0) { 518*94047d49SGordon Ross op->op_oplock_state = have | 519*94047d49SGordon Ross OPLOCK_LEVEL_GRANULAR; 520*94047d49SGordon Ross goto done; 521*94047d49SGordon Ross } 522*94047d49SGordon Ross 523*94047d49SGordon Ross /* 524*94047d49SGordon Ross * Handle oplock requests in three parts: 525*94047d49SGordon Ross * a: Requests with WRITE_CACHING 526*94047d49SGordon Ross * b: Requests with HANDLE_CACHING 527*94047d49SGordon Ross * c: Requests with READ_CACHING 528*94047d49SGordon Ross * reducing the request before b and c. 529*94047d49SGordon Ross * 530*94047d49SGordon Ross * In each: first check if the lease grants the 531*94047d49SGordon Ross * (possibly reduced) request, in which case we 532*94047d49SGordon Ross * leave the lease unchanged and return what's 533*94047d49SGordon Ross * granted by the lease. Otherwise, try to get 534*94047d49SGordon Ross * the oplock, and if the succeeds, wait for any 535*94047d49SGordon Ross * breaks, update the lease, and return. 536*94047d49SGordon Ross */ 537*94047d49SGordon Ross 538*94047d49SGordon Ross /* 539*94047d49SGordon Ross * Try exclusive (request is RW or RWH) 540*94047d49SGordon Ross */ 541*94047d49SGordon Ross if ((op->op_oplock_state & WRITE_CACHING) != 0) { 542*94047d49SGordon Ross want = op->op_oplock_state & CACHE_RWH; 543*94047d49SGordon Ross if (have == want) 544*94047d49SGordon Ross goto done; 545*94047d49SGordon Ross 546*94047d49SGordon Ross status = smb_oplock_request(sr, ofile, 547*94047d49SGordon Ross &op->op_oplock_state); 548*94047d49SGordon Ross if (status == NT_STATUS_SUCCESS || 549*94047d49SGordon Ross status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 550*94047d49SGordon Ross NewGrant = B_TRUE; 551*94047d49SGordon Ross goto done; 552*94047d49SGordon Ross } 553*94047d49SGordon Ross 554*94047d49SGordon Ross /* 555*94047d49SGordon Ross * We did not get the exclusive oplock. 556*94047d49SGordon Ross * 557*94047d49SGordon Ross * There are odd rules about lease upgrade. 558*94047d49SGordon Ross * If the existing lease grants R and the 559*94047d49SGordon Ross * client fails to upgrade it to "RWH" 560*94047d49SGordon Ross * (presumably due to handle conflicts) 561*94047d49SGordon Ross * then just return the existing lease, 562*94047d49SGordon Ross * even though upgrade to RH would work. 563*94047d49SGordon Ross */ 564*94047d49SGordon Ross if (have != 0) { 565*94047d49SGordon Ross op->op_oplock_state = have | 566*94047d49SGordon Ross OPLOCK_LEVEL_GRANULAR; 567*94047d49SGordon Ross goto done; 568*94047d49SGordon Ross } 569*94047d49SGordon Ross 570*94047d49SGordon Ross /* 571*94047d49SGordon Ross * Keep trying without write. 572*94047d49SGordon Ross * Need to re-init op_oplock_state 573*94047d49SGordon Ross */ 574*94047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_GRANULAR | 575*94047d49SGordon Ross (op->lease_state & CACHE_RH); 576*94047d49SGordon Ross } 577*94047d49SGordon Ross 578*94047d49SGordon Ross /* 579*94047d49SGordon Ross * Try shared ("RH") 580*94047d49SGordon Ross */ 581*94047d49SGordon Ross if ((op->op_oplock_state & HANDLE_CACHING) != 0) { 582*94047d49SGordon Ross want = op->op_oplock_state & CACHE_RWH; 583*94047d49SGordon Ross if (have == want) 584*94047d49SGordon Ross goto done; 585*94047d49SGordon Ross 586*94047d49SGordon Ross status = smb_oplock_request(sr, ofile, 587*94047d49SGordon Ross &op->op_oplock_state); 588*94047d49SGordon Ross if (status == NT_STATUS_SUCCESS || 589*94047d49SGordon Ross status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 590*94047d49SGordon Ross NewGrant = B_TRUE; 591*94047d49SGordon Ross goto done; 592*94047d49SGordon Ross } 593*94047d49SGordon Ross 594*94047d49SGordon Ross /* 595*94047d49SGordon Ross * We did not get "RH", probably because 596*94047d49SGordon Ross * ther were (old style) Level II oplocks. 597*94047d49SGordon Ross * Continue, try for just read. 598*94047d49SGordon Ross */ 599*94047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_GRANULAR | 600*94047d49SGordon Ross (op->lease_state & CACHE_R); 601*94047d49SGordon Ross } 602*94047d49SGordon Ross 603*94047d49SGordon Ross /* 604*94047d49SGordon Ross * Try shared ("R") 605*94047d49SGordon Ross */ 606*94047d49SGordon Ross if ((op->op_oplock_state & READ_CACHING) != 0) { 607*94047d49SGordon Ross want = op->op_oplock_state & CACHE_RWH; 608*94047d49SGordon Ross if (have == want) 609*94047d49SGordon Ross goto done; 610*94047d49SGordon Ross 611*94047d49SGordon Ross status = smb_oplock_request(sr, ofile, 612*94047d49SGordon Ross &op->op_oplock_state); 613*94047d49SGordon Ross if (status == NT_STATUS_SUCCESS || 614*94047d49SGordon Ross status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 615*94047d49SGordon Ross NewGrant = B_TRUE; 616*94047d49SGordon Ross goto done; 617*94047d49SGordon Ross } 618*94047d49SGordon Ross 619*94047d49SGordon Ross /* 620*94047d49SGordon Ross * We did not get "R". 621*94047d49SGordon Ross * Fall into "none". 622*94047d49SGordon Ross */ 623*94047d49SGordon Ross } 624*94047d49SGordon Ross 625*94047d49SGordon Ross /* 626*94047d49SGordon Ross * None of the above were able to get an oplock. 627*94047d49SGordon Ross * The lease has no caching rights, and we didn't 628*94047d49SGordon Ross * add any in this request. Return it as-is. 629*94047d49SGordon Ross */ 630*94047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_GRANULAR; 631*94047d49SGordon Ross 632*94047d49SGordon Ross done: 633*94047d49SGordon Ross if (NewGrant) { 634*94047d49SGordon Ross /* 635*94047d49SGordon Ross * After a new oplock grant, the status return 636*94047d49SGordon Ross * may indicate we need to wait for breaks. 637*94047d49SGordon Ross */ 638*94047d49SGordon Ross if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 639*94047d49SGordon Ross (void) smb2sr_go_async(sr); 640*94047d49SGordon Ross (void) smb_oplock_wait_break(ofile->f_node, 0); 641*94047d49SGordon Ross status = NT_STATUS_SUCCESS; 642*94047d49SGordon Ross } 643*94047d49SGordon Ross ASSERT(status == NT_STATUS_SUCCESS); 644*94047d49SGordon Ross 645*94047d49SGordon Ross /* 646*94047d49SGordon Ross * Keep track of what we got (in ofile->f_oplock.og_state) 647*94047d49SGordon Ross * so we'll know what we had when sending a break later. 648*94047d49SGordon Ross * Also update the lease with the new oplock state. 649*94047d49SGordon Ross * Also track which ofile on the lease owns the oplock. 650*94047d49SGordon Ross * The og_dialect here is the oplock dialect, not the 651*94047d49SGordon Ross * SMB dialect. Leasing, so SMB 2.1 (or later). 652*94047d49SGordon Ross */ 653*94047d49SGordon Ross ofile->f_oplock.og_dialect = SMB_VERS_2_1; 654*94047d49SGordon Ross ofile->f_oplock.og_state = op->op_oplock_state; 655*94047d49SGordon Ross mutex_enter(&lease->ls_mutex); 656*94047d49SGordon Ross lease->ls_state = op->op_oplock_state & CACHE_RWH; 657*94047d49SGordon Ross lease->ls_oplock_ofile = ofile; 658*94047d49SGordon Ross lease->ls_epoch++; 659*94047d49SGordon Ross mutex_exit(&lease->ls_mutex); 660*94047d49SGordon Ross } 661*94047d49SGordon Ross 662*94047d49SGordon Ross /* 663*94047d49SGordon Ross * Convert internal oplock state to SMB2 664*94047d49SGordon Ross */ 665*94047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE; 666*94047d49SGordon Ross op->lease_state = lease->ls_state & CACHE_RWH; 667*94047d49SGordon Ross op->lease_flags = (lease->ls_breaking != 0) ? 668*94047d49SGordon Ross SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0; 669*94047d49SGordon Ross op->lease_epoch = lease->ls_epoch; 670*94047d49SGordon Ross op->lease_version = lease->ls_version; 671*94047d49SGordon Ross } 672*94047d49SGordon Ross 673*94047d49SGordon Ross /* 674*94047d49SGordon Ross * This ofile has a lease and is about to close. 675*94047d49SGordon Ross * Called by smb_ofile_close when there's a lease. 676*94047d49SGordon Ross * 677*94047d49SGordon Ross * With leases, just one ofile on a lease owns the oplock. 678*94047d49SGordon Ross * If an ofile with a lease is closed and it's the one that 679*94047d49SGordon Ross * owns the oplock, try to move the oplock to another ofile 680*94047d49SGordon Ross * on the same lease. 681*94047d49SGordon Ross */ 682*94047d49SGordon Ross void 683*94047d49SGordon Ross smb2_lease_ofile_close(smb_ofile_t *ofile) 684*94047d49SGordon Ross { 685*94047d49SGordon Ross smb_node_t *node = ofile->f_node; 686*94047d49SGordon Ross smb_lease_t *lease = ofile->f_lease; 687*94047d49SGordon Ross smb_ofile_t *o; 688*94047d49SGordon Ross 689*94047d49SGordon Ross /* 690*94047d49SGordon Ross * If this ofile was not the oplock owner for this lease, 691*94047d49SGordon Ross * we can leave things as they are. 692*94047d49SGordon Ross */ 693*94047d49SGordon Ross if (lease->ls_oplock_ofile != ofile) 694*94047d49SGordon Ross return; 695*94047d49SGordon Ross 696*94047d49SGordon Ross /* 697*94047d49SGordon Ross * Find another ofile to which we can move the oplock. 698*94047d49SGordon Ross * The ofile must be open and allow a new ref. 699*94047d49SGordon Ross */ 700*94047d49SGordon Ross smb_llist_enter(&node->n_ofile_list, RW_READER); 701*94047d49SGordon Ross FOREACH_NODE_OFILE(node, o) { 702*94047d49SGordon Ross if (o == ofile) 703*94047d49SGordon Ross continue; 704*94047d49SGordon Ross if (o->f_lease != lease) 705*94047d49SGordon Ross continue; 706*94047d49SGordon Ross /* If we can get a hold, use this ofile. */ 707*94047d49SGordon Ross if (smb_ofile_hold(o)) 708*94047d49SGordon Ross break; 709*94047d49SGordon Ross } 710*94047d49SGordon Ross if (o == NULL) { 711*94047d49SGordon Ross /* Normal for last close on a lease. */ 712*94047d49SGordon Ross smb_llist_exit(&node->n_ofile_list); 713*94047d49SGordon Ross return; 714*94047d49SGordon Ross } 715*94047d49SGordon Ross smb_oplock_move(node, ofile, o); 716*94047d49SGordon Ross lease->ls_oplock_ofile = o; 717*94047d49SGordon Ross 718*94047d49SGordon Ross smb_llist_exit(&node->n_ofile_list); 719*94047d49SGordon Ross smb_ofile_release(o); 720*94047d49SGordon Ross } 721