1*7348SJose.Borrego@Sun.COM /* 2*7348SJose.Borrego@Sun.COM * CDDL HEADER START 3*7348SJose.Borrego@Sun.COM * 4*7348SJose.Borrego@Sun.COM * The contents of this file are subject to the terms of the 5*7348SJose.Borrego@Sun.COM * Common Development and Distribution License (the "License"). 6*7348SJose.Borrego@Sun.COM * You may not use this file except in compliance with the License. 7*7348SJose.Borrego@Sun.COM * 8*7348SJose.Borrego@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*7348SJose.Borrego@Sun.COM * or http://www.opensolaris.org/os/licensing. 10*7348SJose.Borrego@Sun.COM * See the License for the specific language governing permissions 11*7348SJose.Borrego@Sun.COM * and limitations under the License. 12*7348SJose.Borrego@Sun.COM * 13*7348SJose.Borrego@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 14*7348SJose.Borrego@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*7348SJose.Borrego@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 16*7348SJose.Borrego@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 17*7348SJose.Borrego@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 18*7348SJose.Borrego@Sun.COM * 19*7348SJose.Borrego@Sun.COM * CDDL HEADER END 20*7348SJose.Borrego@Sun.COM */ 21*7348SJose.Borrego@Sun.COM /* 22*7348SJose.Borrego@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23*7348SJose.Borrego@Sun.COM * Use is subject to license terms. 24*7348SJose.Borrego@Sun.COM */ 25*7348SJose.Borrego@Sun.COM 26*7348SJose.Borrego@Sun.COM #pragma ident "@(#)smb_lock.c 1.10 08/08/04 SMI" 27*7348SJose.Borrego@Sun.COM 28*7348SJose.Borrego@Sun.COM /* 29*7348SJose.Borrego@Sun.COM * This module provides range lock functionality for CIFS/SMB clients. 30*7348SJose.Borrego@Sun.COM * Lock range service functions process SMB lock and and unlock 31*7348SJose.Borrego@Sun.COM * requests for a file by applying lock rules and marks file range 32*7348SJose.Borrego@Sun.COM * as locked if the lock is successful otherwise return proper 33*7348SJose.Borrego@Sun.COM * error code. 34*7348SJose.Borrego@Sun.COM */ 35*7348SJose.Borrego@Sun.COM 36*7348SJose.Borrego@Sun.COM #include <smbsrv/smb_incl.h> 37*7348SJose.Borrego@Sun.COM #include <smbsrv/smb_fsops.h> 38*7348SJose.Borrego@Sun.COM #include <sys/nbmlock.h> 39*7348SJose.Borrego@Sun.COM #include <sys/param.h> 40*7348SJose.Borrego@Sun.COM 41*7348SJose.Borrego@Sun.COM extern caller_context_t smb_ct; 42*7348SJose.Borrego@Sun.COM 43*7348SJose.Borrego@Sun.COM static void smb_lock_posix_unlock(smb_node_t *, smb_lock_t *, cred_t *); 44*7348SJose.Borrego@Sun.COM static boolean_t smb_is_range_unlocked(uint64_t, uint64_t, uint32_t, 45*7348SJose.Borrego@Sun.COM smb_llist_t *, uint64_t *); 46*7348SJose.Borrego@Sun.COM static int smb_lock_range_overlap(smb_lock_t *, uint64_t, uint64_t); 47*7348SJose.Borrego@Sun.COM static uint32_t smb_lock_range_lckrules(smb_request_t *, smb_ofile_t *, 48*7348SJose.Borrego@Sun.COM smb_node_t *, smb_lock_t *, smb_lock_t **); 49*7348SJose.Borrego@Sun.COM static clock_t smb_lock_wait(smb_request_t *, smb_lock_t *, smb_lock_t *); 50*7348SJose.Borrego@Sun.COM static uint32_t smb_lock_range_ulckrules(smb_request_t *, smb_node_t *, 51*7348SJose.Borrego@Sun.COM uint64_t, uint64_t, smb_lock_t **nodelock); 52*7348SJose.Borrego@Sun.COM static smb_lock_t *smb_lock_create(smb_request_t *, uint64_t, uint64_t, 53*7348SJose.Borrego@Sun.COM uint32_t, uint32_t); 54*7348SJose.Borrego@Sun.COM static void smb_lock_destroy(smb_lock_t *); 55*7348SJose.Borrego@Sun.COM static void smb_lock_free(smb_lock_t *); 56*7348SJose.Borrego@Sun.COM 57*7348SJose.Borrego@Sun.COM 58*7348SJose.Borrego@Sun.COM 59*7348SJose.Borrego@Sun.COM /* 60*7348SJose.Borrego@Sun.COM * smb_unlock_range 61*7348SJose.Borrego@Sun.COM * 62*7348SJose.Borrego@Sun.COM * locates lock range performed for corresponding to unlock request. 63*7348SJose.Borrego@Sun.COM * 64*7348SJose.Borrego@Sun.COM * NT_STATUS_SUCCESS - Lock range performed successfully. 65*7348SJose.Borrego@Sun.COM * !NT_STATUS_SUCCESS - Error in unlock range operation. 66*7348SJose.Borrego@Sun.COM */ 67*7348SJose.Borrego@Sun.COM uint32_t 68*7348SJose.Borrego@Sun.COM smb_unlock_range( 69*7348SJose.Borrego@Sun.COM smb_request_t *sr, 70*7348SJose.Borrego@Sun.COM smb_node_t *node, 71*7348SJose.Borrego@Sun.COM uint64_t start, 72*7348SJose.Borrego@Sun.COM uint64_t length) 73*7348SJose.Borrego@Sun.COM { 74*7348SJose.Borrego@Sun.COM smb_lock_t *lock = NULL; 75*7348SJose.Borrego@Sun.COM uint32_t status; 76*7348SJose.Borrego@Sun.COM 77*7348SJose.Borrego@Sun.COM /* Apply unlocking rules */ 78*7348SJose.Borrego@Sun.COM smb_llist_enter(&node->n_lock_list, RW_WRITER); 79*7348SJose.Borrego@Sun.COM status = smb_lock_range_ulckrules(sr, node, start, length, &lock); 80*7348SJose.Borrego@Sun.COM if (status != NT_STATUS_SUCCESS) { 81*7348SJose.Borrego@Sun.COM /* 82*7348SJose.Borrego@Sun.COM * If lock range is not matching in the list 83*7348SJose.Borrego@Sun.COM * return error. 84*7348SJose.Borrego@Sun.COM */ 85*7348SJose.Borrego@Sun.COM ASSERT(lock == NULL); 86*7348SJose.Borrego@Sun.COM smb_llist_exit(&node->n_lock_list); 87*7348SJose.Borrego@Sun.COM return (status); 88*7348SJose.Borrego@Sun.COM } 89*7348SJose.Borrego@Sun.COM 90*7348SJose.Borrego@Sun.COM smb_llist_remove(&node->n_lock_list, lock); 91*7348SJose.Borrego@Sun.COM smb_lock_posix_unlock(node, lock, sr->user_cr); 92*7348SJose.Borrego@Sun.COM smb_llist_exit(&node->n_lock_list); 93*7348SJose.Borrego@Sun.COM smb_lock_destroy(lock); 94*7348SJose.Borrego@Sun.COM 95*7348SJose.Borrego@Sun.COM return (status); 96*7348SJose.Borrego@Sun.COM } 97*7348SJose.Borrego@Sun.COM 98*7348SJose.Borrego@Sun.COM /* 99*7348SJose.Borrego@Sun.COM * smb_lock_range 100*7348SJose.Borrego@Sun.COM * 101*7348SJose.Borrego@Sun.COM * checks for integrity of file lock operation for the given range of file data. 102*7348SJose.Borrego@Sun.COM * This is performed by applying lock rules with all the elements of the node 103*7348SJose.Borrego@Sun.COM * lock list. 104*7348SJose.Borrego@Sun.COM * 105*7348SJose.Borrego@Sun.COM * The function returns with new lock added if lock request is non-conflicting 106*7348SJose.Borrego@Sun.COM * with existing range lock for the file. Otherwise smb request is filed 107*7348SJose.Borrego@Sun.COM * without returning. 108*7348SJose.Borrego@Sun.COM * 109*7348SJose.Borrego@Sun.COM * NT_STATUS_SUCCESS - Lock range performed successfully. 110*7348SJose.Borrego@Sun.COM * !NT_STATUS_SUCCESS - Error in lock range operation. 111*7348SJose.Borrego@Sun.COM */ 112*7348SJose.Borrego@Sun.COM uint32_t 113*7348SJose.Borrego@Sun.COM smb_lock_range( 114*7348SJose.Borrego@Sun.COM smb_request_t *sr, 115*7348SJose.Borrego@Sun.COM uint64_t start, 116*7348SJose.Borrego@Sun.COM uint64_t length, 117*7348SJose.Borrego@Sun.COM uint32_t timeout, 118*7348SJose.Borrego@Sun.COM uint32_t locktype) 119*7348SJose.Borrego@Sun.COM { 120*7348SJose.Borrego@Sun.COM smb_ofile_t *file = sr->fid_ofile; 121*7348SJose.Borrego@Sun.COM smb_node_t *node = file->f_node; 122*7348SJose.Borrego@Sun.COM smb_lock_t *lock; 123*7348SJose.Borrego@Sun.COM smb_lock_t *clock = NULL; 124*7348SJose.Borrego@Sun.COM uint32_t result = NT_STATUS_SUCCESS; 125*7348SJose.Borrego@Sun.COM boolean_t lock_has_timeout = (timeout != 0); 126*7348SJose.Borrego@Sun.COM 127*7348SJose.Borrego@Sun.COM lock = smb_lock_create(sr, start, length, locktype, timeout); 128*7348SJose.Borrego@Sun.COM 129*7348SJose.Borrego@Sun.COM smb_llist_enter(&node->n_lock_list, RW_WRITER); 130*7348SJose.Borrego@Sun.COM for (;;) { 131*7348SJose.Borrego@Sun.COM clock_t rc; 132*7348SJose.Borrego@Sun.COM 133*7348SJose.Borrego@Sun.COM /* Apply locking rules */ 134*7348SJose.Borrego@Sun.COM result = smb_lock_range_lckrules(sr, file, node, lock, &clock); 135*7348SJose.Borrego@Sun.COM 136*7348SJose.Borrego@Sun.COM if ((result == NT_STATUS_CANCELLED) || 137*7348SJose.Borrego@Sun.COM (result == NT_STATUS_SUCCESS) || 138*7348SJose.Borrego@Sun.COM (result == NT_STATUS_RANGE_NOT_LOCKED)) { 139*7348SJose.Borrego@Sun.COM ASSERT(clock == NULL); 140*7348SJose.Borrego@Sun.COM break; 141*7348SJose.Borrego@Sun.COM } else if (timeout == 0) { 142*7348SJose.Borrego@Sun.COM break; 143*7348SJose.Borrego@Sun.COM } 144*7348SJose.Borrego@Sun.COM 145*7348SJose.Borrego@Sun.COM ASSERT(result == NT_STATUS_LOCK_NOT_GRANTED); 146*7348SJose.Borrego@Sun.COM ASSERT(clock); 147*7348SJose.Borrego@Sun.COM /* 148*7348SJose.Borrego@Sun.COM * Call smb_lock_wait holding write lock for 149*7348SJose.Borrego@Sun.COM * node lock list. smb_lock_wait will release 150*7348SJose.Borrego@Sun.COM * this lock if it blocks. 151*7348SJose.Borrego@Sun.COM */ 152*7348SJose.Borrego@Sun.COM ASSERT(node == clock->l_file->f_node); 153*7348SJose.Borrego@Sun.COM 154*7348SJose.Borrego@Sun.COM rc = smb_lock_wait(sr, lock, clock); 155*7348SJose.Borrego@Sun.COM if (rc == 0) { 156*7348SJose.Borrego@Sun.COM result = NT_STATUS_CANCELLED; 157*7348SJose.Borrego@Sun.COM break; 158*7348SJose.Borrego@Sun.COM } 159*7348SJose.Borrego@Sun.COM if (rc == -1) 160*7348SJose.Borrego@Sun.COM timeout = 0; 161*7348SJose.Borrego@Sun.COM 162*7348SJose.Borrego@Sun.COM clock = NULL; 163*7348SJose.Borrego@Sun.COM } 164*7348SJose.Borrego@Sun.COM 165*7348SJose.Borrego@Sun.COM lock->l_blocked_by = NULL; 166*7348SJose.Borrego@Sun.COM 167*7348SJose.Borrego@Sun.COM if (result != NT_STATUS_SUCCESS) { 168*7348SJose.Borrego@Sun.COM /* 169*7348SJose.Borrego@Sun.COM * Under certain conditions NT_STATUS_FILE_LOCK_CONFLICT 170*7348SJose.Borrego@Sun.COM * should be returned instead of NT_STATUS_LOCK_NOT_GRANTED. 171*7348SJose.Borrego@Sun.COM */ 172*7348SJose.Borrego@Sun.COM if (result == NT_STATUS_LOCK_NOT_GRANTED) { 173*7348SJose.Borrego@Sun.COM /* 174*7348SJose.Borrego@Sun.COM * Locks with timeouts always return 175*7348SJose.Borrego@Sun.COM * NT_STATUS_FILE_LOCK_CONFLICT 176*7348SJose.Borrego@Sun.COM */ 177*7348SJose.Borrego@Sun.COM if (lock_has_timeout) 178*7348SJose.Borrego@Sun.COM result = NT_STATUS_FILE_LOCK_CONFLICT; 179*7348SJose.Borrego@Sun.COM 180*7348SJose.Borrego@Sun.COM /* 181*7348SJose.Borrego@Sun.COM * Locks starting higher than 0xef000000 that do not 182*7348SJose.Borrego@Sun.COM * have the MSB set always return 183*7348SJose.Borrego@Sun.COM * NT_STATUS_FILE_LOCK_CONFLICT 184*7348SJose.Borrego@Sun.COM */ 185*7348SJose.Borrego@Sun.COM if ((lock->l_start >= 0xef000000) && 186*7348SJose.Borrego@Sun.COM !(lock->l_start & (1ULL << 63))) { 187*7348SJose.Borrego@Sun.COM result = NT_STATUS_FILE_LOCK_CONFLICT; 188*7348SJose.Borrego@Sun.COM } 189*7348SJose.Borrego@Sun.COM 190*7348SJose.Borrego@Sun.COM /* 191*7348SJose.Borrego@Sun.COM * If the last lock attempt to fail on this file handle 192*7348SJose.Borrego@Sun.COM * started at the same offset as this one then return 193*7348SJose.Borrego@Sun.COM * NT_STATUS_FILE_LOCK_CONFLICT 194*7348SJose.Borrego@Sun.COM */ 195*7348SJose.Borrego@Sun.COM mutex_enter(&file->f_mutex); 196*7348SJose.Borrego@Sun.COM if ((file->f_flags & SMB_OFLAGS_LLF_POS_VALID) && 197*7348SJose.Borrego@Sun.COM (lock->l_start == file->f_llf_pos)) { 198*7348SJose.Borrego@Sun.COM result = NT_STATUS_FILE_LOCK_CONFLICT; 199*7348SJose.Borrego@Sun.COM } 200*7348SJose.Borrego@Sun.COM mutex_exit(&file->f_mutex); 201*7348SJose.Borrego@Sun.COM } 202*7348SJose.Borrego@Sun.COM 203*7348SJose.Borrego@Sun.COM /* Update last lock failed offset */ 204*7348SJose.Borrego@Sun.COM mutex_enter(&file->f_mutex); 205*7348SJose.Borrego@Sun.COM file->f_llf_pos = lock->l_start; 206*7348SJose.Borrego@Sun.COM file->f_flags |= SMB_OFLAGS_LLF_POS_VALID; 207*7348SJose.Borrego@Sun.COM mutex_exit(&file->f_mutex); 208*7348SJose.Borrego@Sun.COM 209*7348SJose.Borrego@Sun.COM smb_lock_free(lock); 210*7348SJose.Borrego@Sun.COM } else { 211*7348SJose.Borrego@Sun.COM /* 212*7348SJose.Borrego@Sun.COM * don't insert into the CIFS lock list unless the 213*7348SJose.Borrego@Sun.COM * posix lock worked 214*7348SJose.Borrego@Sun.COM */ 215*7348SJose.Borrego@Sun.COM if (smb_fsop_frlock(node, lock, B_FALSE, sr->user_cr)) 216*7348SJose.Borrego@Sun.COM result = NT_STATUS_FILE_LOCK_CONFLICT; 217*7348SJose.Borrego@Sun.COM else 218*7348SJose.Borrego@Sun.COM smb_llist_insert_tail(&node->n_lock_list, lock); 219*7348SJose.Borrego@Sun.COM } 220*7348SJose.Borrego@Sun.COM smb_llist_exit(&node->n_lock_list); 221*7348SJose.Borrego@Sun.COM 222*7348SJose.Borrego@Sun.COM return (result); 223*7348SJose.Borrego@Sun.COM } 224*7348SJose.Borrego@Sun.COM 225*7348SJose.Borrego@Sun.COM 226*7348SJose.Borrego@Sun.COM /* 227*7348SJose.Borrego@Sun.COM * smb_lock_range_access 228*7348SJose.Borrego@Sun.COM * 229*7348SJose.Borrego@Sun.COM * scans node lock list 230*7348SJose.Borrego@Sun.COM * to check if there is any overlapping lock. Overlapping 231*7348SJose.Borrego@Sun.COM * lock is allowed only under same session and client pid. 232*7348SJose.Borrego@Sun.COM * 233*7348SJose.Borrego@Sun.COM * Return values 234*7348SJose.Borrego@Sun.COM * NT_STATUS_SUCCESS lock access granted. 235*7348SJose.Borrego@Sun.COM * NT_STATUS_FILE_LOCK_CONFLICT access denied due to lock conflict. 236*7348SJose.Borrego@Sun.COM */ 237*7348SJose.Borrego@Sun.COM int 238*7348SJose.Borrego@Sun.COM smb_lock_range_access( 239*7348SJose.Borrego@Sun.COM smb_request_t *sr, 240*7348SJose.Borrego@Sun.COM smb_node_t *node, 241*7348SJose.Borrego@Sun.COM uint64_t start, 242*7348SJose.Borrego@Sun.COM uint64_t length, 243*7348SJose.Borrego@Sun.COM boolean_t will_write) 244*7348SJose.Borrego@Sun.COM { 245*7348SJose.Borrego@Sun.COM smb_lock_t *lock; 246*7348SJose.Borrego@Sun.COM smb_llist_t *llist; 247*7348SJose.Borrego@Sun.COM int status = NT_STATUS_SUCCESS; 248*7348SJose.Borrego@Sun.COM 249*7348SJose.Borrego@Sun.COM llist = &node->n_lock_list; 250*7348SJose.Borrego@Sun.COM smb_llist_enter(llist, RW_READER); 251*7348SJose.Borrego@Sun.COM /* Search for any applicable lock */ 252*7348SJose.Borrego@Sun.COM for (lock = smb_llist_head(llist); 253*7348SJose.Borrego@Sun.COM lock != NULL; 254*7348SJose.Borrego@Sun.COM lock = smb_llist_next(llist, lock)) { 255*7348SJose.Borrego@Sun.COM 256*7348SJose.Borrego@Sun.COM if (!smb_lock_range_overlap(lock, start, length)) 257*7348SJose.Borrego@Sun.COM /* Lock does not overlap */ 258*7348SJose.Borrego@Sun.COM continue; 259*7348SJose.Borrego@Sun.COM 260*7348SJose.Borrego@Sun.COM if (lock->l_type == SMB_LOCK_TYPE_READONLY && !will_write) 261*7348SJose.Borrego@Sun.COM continue; 262*7348SJose.Borrego@Sun.COM 263*7348SJose.Borrego@Sun.COM if (lock->l_type == SMB_LOCK_TYPE_READWRITE && 264*7348SJose.Borrego@Sun.COM lock->l_session_kid == sr->session->s_kid && 265*7348SJose.Borrego@Sun.COM lock->l_pid == sr->smb_pid) 266*7348SJose.Borrego@Sun.COM continue; 267*7348SJose.Borrego@Sun.COM 268*7348SJose.Borrego@Sun.COM status = NT_STATUS_FILE_LOCK_CONFLICT; 269*7348SJose.Borrego@Sun.COM break; 270*7348SJose.Borrego@Sun.COM } 271*7348SJose.Borrego@Sun.COM smb_llist_exit(llist); 272*7348SJose.Borrego@Sun.COM return (status); 273*7348SJose.Borrego@Sun.COM } 274*7348SJose.Borrego@Sun.COM 275*7348SJose.Borrego@Sun.COM void 276*7348SJose.Borrego@Sun.COM smb_node_destroy_lock_by_ofile(smb_node_t *node, smb_ofile_t *file) 277*7348SJose.Borrego@Sun.COM { 278*7348SJose.Borrego@Sun.COM smb_lock_t *lock; 279*7348SJose.Borrego@Sun.COM smb_lock_t *nxtl; 280*7348SJose.Borrego@Sun.COM list_t destroy_list; 281*7348SJose.Borrego@Sun.COM 282*7348SJose.Borrego@Sun.COM ASSERT(node); 283*7348SJose.Borrego@Sun.COM ASSERT(node->n_magic == SMB_NODE_MAGIC); 284*7348SJose.Borrego@Sun.COM ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 285*7348SJose.Borrego@Sun.COM ASSERT(node->n_refcnt); 286*7348SJose.Borrego@Sun.COM 287*7348SJose.Borrego@Sun.COM /* 288*7348SJose.Borrego@Sun.COM * Move locks matching the specified file from the node->n_lock_list 289*7348SJose.Borrego@Sun.COM * to a temporary list (holding the lock the entire time) then 290*7348SJose.Borrego@Sun.COM * destroy all the matching locks. We can't call smb_lock_destroy 291*7348SJose.Borrego@Sun.COM * while we are holding the lock for node->n_lock_list because we will 292*7348SJose.Borrego@Sun.COM * deadlock and we can't drop the lock because the list contents might 293*7348SJose.Borrego@Sun.COM * change (for example nxtl might get removed on another thread). 294*7348SJose.Borrego@Sun.COM */ 295*7348SJose.Borrego@Sun.COM list_create(&destroy_list, sizeof (smb_lock_t), 296*7348SJose.Borrego@Sun.COM offsetof(smb_lock_t, l_lnd)); 297*7348SJose.Borrego@Sun.COM 298*7348SJose.Borrego@Sun.COM smb_llist_enter(&node->n_lock_list, RW_WRITER); 299*7348SJose.Borrego@Sun.COM lock = smb_llist_head(&node->n_lock_list); 300*7348SJose.Borrego@Sun.COM while (lock) { 301*7348SJose.Borrego@Sun.COM nxtl = smb_llist_next(&node->n_lock_list, lock); 302*7348SJose.Borrego@Sun.COM if (lock->l_file == file) { 303*7348SJose.Borrego@Sun.COM smb_llist_remove(&node->n_lock_list, lock); 304*7348SJose.Borrego@Sun.COM smb_lock_posix_unlock(node, lock, file->f_user->u_cred); 305*7348SJose.Borrego@Sun.COM list_insert_tail(&destroy_list, lock); 306*7348SJose.Borrego@Sun.COM } 307*7348SJose.Borrego@Sun.COM lock = nxtl; 308*7348SJose.Borrego@Sun.COM } 309*7348SJose.Borrego@Sun.COM smb_llist_exit(&node->n_lock_list); 310*7348SJose.Borrego@Sun.COM 311*7348SJose.Borrego@Sun.COM lock = list_head(&destroy_list); 312*7348SJose.Borrego@Sun.COM while (lock) { 313*7348SJose.Borrego@Sun.COM nxtl = list_next(&destroy_list, lock); 314*7348SJose.Borrego@Sun.COM list_remove(&destroy_list, lock); 315*7348SJose.Borrego@Sun.COM smb_lock_destroy(lock); 316*7348SJose.Borrego@Sun.COM lock = nxtl; 317*7348SJose.Borrego@Sun.COM } 318*7348SJose.Borrego@Sun.COM 319*7348SJose.Borrego@Sun.COM list_destroy(&destroy_list); 320*7348SJose.Borrego@Sun.COM } 321*7348SJose.Borrego@Sun.COM 322*7348SJose.Borrego@Sun.COM void 323*7348SJose.Borrego@Sun.COM smb_lock_range_error(smb_request_t *sr, uint32_t status32) 324*7348SJose.Borrego@Sun.COM { 325*7348SJose.Borrego@Sun.COM uint16_t errcode; 326*7348SJose.Borrego@Sun.COM 327*7348SJose.Borrego@Sun.COM if (status32 == NT_STATUS_CANCELLED) 328*7348SJose.Borrego@Sun.COM errcode = ERROR_OPERATION_ABORTED; 329*7348SJose.Borrego@Sun.COM else 330*7348SJose.Borrego@Sun.COM errcode = ERRlock; 331*7348SJose.Borrego@Sun.COM 332*7348SJose.Borrego@Sun.COM smbsr_error(sr, status32, ERRDOS, errcode); 333*7348SJose.Borrego@Sun.COM } 334*7348SJose.Borrego@Sun.COM 335*7348SJose.Borrego@Sun.COM /* 336*7348SJose.Borrego@Sun.COM * smb_range_check() 337*7348SJose.Borrego@Sun.COM * 338*7348SJose.Borrego@Sun.COM * Perform range checking. First check for internal CIFS range conflicts 339*7348SJose.Borrego@Sun.COM * and then check for external conflicts, for example, with NFS or local 340*7348SJose.Borrego@Sun.COM * access. 341*7348SJose.Borrego@Sun.COM * 342*7348SJose.Borrego@Sun.COM * If nbmand is enabled, this function must be called from within an nbmand 343*7348SJose.Borrego@Sun.COM * critical region 344*7348SJose.Borrego@Sun.COM */ 345*7348SJose.Borrego@Sun.COM 346*7348SJose.Borrego@Sun.COM DWORD 347*7348SJose.Borrego@Sun.COM smb_range_check(smb_request_t *sr, smb_node_t *node, uint64_t start, 348*7348SJose.Borrego@Sun.COM uint64_t length, boolean_t will_write) 349*7348SJose.Borrego@Sun.COM { 350*7348SJose.Borrego@Sun.COM smb_error_t smberr; 351*7348SJose.Borrego@Sun.COM int svmand; 352*7348SJose.Borrego@Sun.COM int nbl_op; 353*7348SJose.Borrego@Sun.COM int rc; 354*7348SJose.Borrego@Sun.COM 355*7348SJose.Borrego@Sun.COM ASSERT(node); 356*7348SJose.Borrego@Sun.COM ASSERT(node->n_magic == SMB_NODE_MAGIC); 357*7348SJose.Borrego@Sun.COM ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); 358*7348SJose.Borrego@Sun.COM 359*7348SJose.Borrego@Sun.COM ASSERT(smb_node_in_crit(node)); 360*7348SJose.Borrego@Sun.COM 361*7348SJose.Borrego@Sun.COM if (node->attr.sa_vattr.va_type == VDIR) 362*7348SJose.Borrego@Sun.COM return (NT_STATUS_SUCCESS); 363*7348SJose.Borrego@Sun.COM 364*7348SJose.Borrego@Sun.COM rc = smb_lock_range_access(sr, node, start, length, will_write); 365*7348SJose.Borrego@Sun.COM if (rc) 366*7348SJose.Borrego@Sun.COM return (NT_STATUS_FILE_LOCK_CONFLICT); 367*7348SJose.Borrego@Sun.COM 368*7348SJose.Borrego@Sun.COM if ((rc = nbl_svmand(node->vp, kcred, &svmand)) != 0) { 369*7348SJose.Borrego@Sun.COM smbsr_map_errno(rc, &smberr); 370*7348SJose.Borrego@Sun.COM return (smberr.status); 371*7348SJose.Borrego@Sun.COM } 372*7348SJose.Borrego@Sun.COM 373*7348SJose.Borrego@Sun.COM nbl_op = (will_write) ? NBL_WRITE : NBL_READ; 374*7348SJose.Borrego@Sun.COM 375*7348SJose.Borrego@Sun.COM if (nbl_lock_conflict(node->vp, nbl_op, start, length, svmand, &smb_ct)) 376*7348SJose.Borrego@Sun.COM return (NT_STATUS_FILE_LOCK_CONFLICT); 377*7348SJose.Borrego@Sun.COM 378*7348SJose.Borrego@Sun.COM return (NT_STATUS_SUCCESS); 379*7348SJose.Borrego@Sun.COM } 380*7348SJose.Borrego@Sun.COM 381*7348SJose.Borrego@Sun.COM /* 382*7348SJose.Borrego@Sun.COM * smb_lock_posix_unlock 383*7348SJose.Borrego@Sun.COM * 384*7348SJose.Borrego@Sun.COM * checks if the current unlock request is in another lock and repeatedly calls 385*7348SJose.Borrego@Sun.COM * smb_is_range_unlocked on a sliding basis to unlock all bits of the lock 386*7348SJose.Borrego@Sun.COM * that are not in other locks 387*7348SJose.Borrego@Sun.COM * 388*7348SJose.Borrego@Sun.COM */ 389*7348SJose.Borrego@Sun.COM static void 390*7348SJose.Borrego@Sun.COM smb_lock_posix_unlock(smb_node_t *node, smb_lock_t *lock, cred_t *cr) 391*7348SJose.Borrego@Sun.COM { 392*7348SJose.Borrego@Sun.COM uint64_t new_mark; 393*7348SJose.Borrego@Sun.COM uint64_t unlock_start; 394*7348SJose.Borrego@Sun.COM uint64_t unlock_end; 395*7348SJose.Borrego@Sun.COM smb_lock_t new_unlock; 396*7348SJose.Borrego@Sun.COM smb_llist_t *llist; 397*7348SJose.Borrego@Sun.COM boolean_t can_unlock; 398*7348SJose.Borrego@Sun.COM 399*7348SJose.Borrego@Sun.COM new_mark = 0; 400*7348SJose.Borrego@Sun.COM unlock_start = lock->l_start; 401*7348SJose.Borrego@Sun.COM unlock_end = unlock_start + lock->l_length; 402*7348SJose.Borrego@Sun.COM llist = &node->n_lock_list; 403*7348SJose.Borrego@Sun.COM 404*7348SJose.Borrego@Sun.COM for (;;) { 405*7348SJose.Borrego@Sun.COM can_unlock = smb_is_range_unlocked(unlock_start, unlock_end, 406*7348SJose.Borrego@Sun.COM lock->l_file->f_uniqid, llist, &new_mark); 407*7348SJose.Borrego@Sun.COM if (can_unlock) { 408*7348SJose.Borrego@Sun.COM if (new_mark) { 409*7348SJose.Borrego@Sun.COM new_unlock = *lock; 410*7348SJose.Borrego@Sun.COM new_unlock.l_start = unlock_start; 411*7348SJose.Borrego@Sun.COM new_unlock.l_length = new_mark - unlock_start; 412*7348SJose.Borrego@Sun.COM (void) smb_fsop_frlock(node, &new_unlock, 413*7348SJose.Borrego@Sun.COM B_TRUE, cr); 414*7348SJose.Borrego@Sun.COM unlock_start = new_mark; 415*7348SJose.Borrego@Sun.COM } else { 416*7348SJose.Borrego@Sun.COM new_unlock = *lock; 417*7348SJose.Borrego@Sun.COM new_unlock.l_start = unlock_start; 418*7348SJose.Borrego@Sun.COM new_unlock.l_length = unlock_end - unlock_start; 419*7348SJose.Borrego@Sun.COM (void) smb_fsop_frlock(node, &new_unlock, 420*7348SJose.Borrego@Sun.COM B_TRUE, cr); 421*7348SJose.Borrego@Sun.COM break; 422*7348SJose.Borrego@Sun.COM } 423*7348SJose.Borrego@Sun.COM } else if (new_mark) { 424*7348SJose.Borrego@Sun.COM unlock_start = new_mark; 425*7348SJose.Borrego@Sun.COM } else { 426*7348SJose.Borrego@Sun.COM break; 427*7348SJose.Borrego@Sun.COM } 428*7348SJose.Borrego@Sun.COM } 429*7348SJose.Borrego@Sun.COM } 430*7348SJose.Borrego@Sun.COM 431*7348SJose.Borrego@Sun.COM /* 432*7348SJose.Borrego@Sun.COM * smb_lock_range_overlap 433*7348SJose.Borrego@Sun.COM * 434*7348SJose.Borrego@Sun.COM * Checks if lock range(start, length) overlaps range in lock structure. 435*7348SJose.Borrego@Sun.COM * 436*7348SJose.Borrego@Sun.COM * Zero-length byte range locks actually affect no single byte of the stream, 437*7348SJose.Borrego@Sun.COM * meaning they can still be accessed even with such locks in place. However, 438*7348SJose.Borrego@Sun.COM * they do conflict with other ranges in the following manner: 439*7348SJose.Borrego@Sun.COM * conflict will only exist if the positive-length range contains the 440*7348SJose.Borrego@Sun.COM * zero-length range's offset but doesn't start at it 441*7348SJose.Borrego@Sun.COM * 442*7348SJose.Borrego@Sun.COM * return values: 443*7348SJose.Borrego@Sun.COM * 0 - Lock range doesn't overlap 444*7348SJose.Borrego@Sun.COM * 1 - Lock range overlaps. 445*7348SJose.Borrego@Sun.COM */ 446*7348SJose.Borrego@Sun.COM 447*7348SJose.Borrego@Sun.COM #define RANGE_NO_OVERLAP 0 448*7348SJose.Borrego@Sun.COM #define RANGE_OVERLAP 1 449*7348SJose.Borrego@Sun.COM 450*7348SJose.Borrego@Sun.COM static int 451*7348SJose.Borrego@Sun.COM smb_lock_range_overlap(struct smb_lock *lock, uint64_t start, uint64_t length) 452*7348SJose.Borrego@Sun.COM { 453*7348SJose.Borrego@Sun.COM if (length == 0) { 454*7348SJose.Borrego@Sun.COM if ((lock->l_start < start) && 455*7348SJose.Borrego@Sun.COM ((lock->l_start + lock->l_length) > start)) 456*7348SJose.Borrego@Sun.COM return (RANGE_OVERLAP); 457*7348SJose.Borrego@Sun.COM 458*7348SJose.Borrego@Sun.COM return (RANGE_NO_OVERLAP); 459*7348SJose.Borrego@Sun.COM } 460*7348SJose.Borrego@Sun.COM 461*7348SJose.Borrego@Sun.COM /* The following test is intended to catch roll over locks. */ 462*7348SJose.Borrego@Sun.COM if ((start == lock->l_start) && (length == lock->l_length)) 463*7348SJose.Borrego@Sun.COM return (RANGE_OVERLAP); 464*7348SJose.Borrego@Sun.COM 465*7348SJose.Borrego@Sun.COM if (start < lock->l_start) { 466*7348SJose.Borrego@Sun.COM if (start + length > lock->l_start) 467*7348SJose.Borrego@Sun.COM return (RANGE_OVERLAP); 468*7348SJose.Borrego@Sun.COM } else if (start < lock->l_start + lock->l_length) 469*7348SJose.Borrego@Sun.COM return (RANGE_OVERLAP); 470*7348SJose.Borrego@Sun.COM 471*7348SJose.Borrego@Sun.COM return (RANGE_NO_OVERLAP); 472*7348SJose.Borrego@Sun.COM } 473*7348SJose.Borrego@Sun.COM 474*7348SJose.Borrego@Sun.COM /* 475*7348SJose.Borrego@Sun.COM * smb_lock_range_lckrules 476*7348SJose.Borrego@Sun.COM * 477*7348SJose.Borrego@Sun.COM * Lock range rules: 478*7348SJose.Borrego@Sun.COM * 1. Overlapping read locks are allowed if the 479*7348SJose.Borrego@Sun.COM * current locks in the region are only read locks 480*7348SJose.Borrego@Sun.COM * irrespective of pid of smb client issuing lock request. 481*7348SJose.Borrego@Sun.COM * 482*7348SJose.Borrego@Sun.COM * 2. Read lock in the overlapped region of write lock 483*7348SJose.Borrego@Sun.COM * are allowed if the pervious lock is performed by the 484*7348SJose.Borrego@Sun.COM * same pid and connection. 485*7348SJose.Borrego@Sun.COM * 486*7348SJose.Borrego@Sun.COM * return status: 487*7348SJose.Borrego@Sun.COM * NT_STATUS_SUCCESS - Input lock range adapts to lock rules. 488*7348SJose.Borrego@Sun.COM * NT_STATUS_LOCK_NOT_GRANTED - Input lock conflicts lock rules. 489*7348SJose.Borrego@Sun.COM * NT_STATUS_CANCELLED - Error in processing lock rules 490*7348SJose.Borrego@Sun.COM */ 491*7348SJose.Borrego@Sun.COM static uint32_t 492*7348SJose.Borrego@Sun.COM smb_lock_range_lckrules( 493*7348SJose.Borrego@Sun.COM smb_request_t *sr, 494*7348SJose.Borrego@Sun.COM smb_ofile_t *file, 495*7348SJose.Borrego@Sun.COM smb_node_t *node, 496*7348SJose.Borrego@Sun.COM smb_lock_t *dlock, 497*7348SJose.Borrego@Sun.COM smb_lock_t **clockp) 498*7348SJose.Borrego@Sun.COM { 499*7348SJose.Borrego@Sun.COM smb_lock_t *lock; 500*7348SJose.Borrego@Sun.COM uint32_t status = NT_STATUS_SUCCESS; 501*7348SJose.Borrego@Sun.COM 502*7348SJose.Borrego@Sun.COM /* Check if file is closed */ 503*7348SJose.Borrego@Sun.COM if (!smb_ofile_is_open(file)) { 504*7348SJose.Borrego@Sun.COM return (NT_STATUS_RANGE_NOT_LOCKED); 505*7348SJose.Borrego@Sun.COM } 506*7348SJose.Borrego@Sun.COM 507*7348SJose.Borrego@Sun.COM /* Caller must hold lock for node->n_lock_list */ 508*7348SJose.Borrego@Sun.COM for (lock = smb_llist_head(&node->n_lock_list); 509*7348SJose.Borrego@Sun.COM lock != NULL; 510*7348SJose.Borrego@Sun.COM lock = smb_llist_next(&node->n_lock_list, lock)) { 511*7348SJose.Borrego@Sun.COM 512*7348SJose.Borrego@Sun.COM if (!smb_lock_range_overlap(lock, dlock->l_start, 513*7348SJose.Borrego@Sun.COM dlock->l_length)) 514*7348SJose.Borrego@Sun.COM continue; 515*7348SJose.Borrego@Sun.COM 516*7348SJose.Borrego@Sun.COM /* 517*7348SJose.Borrego@Sun.COM * Check to see if lock in the overlapping record 518*7348SJose.Borrego@Sun.COM * is only read lock. Current finding is read 519*7348SJose.Borrego@Sun.COM * locks can overlapped irrespective of pids. 520*7348SJose.Borrego@Sun.COM */ 521*7348SJose.Borrego@Sun.COM if ((lock->l_type == SMB_LOCK_TYPE_READONLY) && 522*7348SJose.Borrego@Sun.COM (dlock->l_type == SMB_LOCK_TYPE_READONLY)) { 523*7348SJose.Borrego@Sun.COM continue; 524*7348SJose.Borrego@Sun.COM } 525*7348SJose.Borrego@Sun.COM 526*7348SJose.Borrego@Sun.COM /* 527*7348SJose.Borrego@Sun.COM * When the read lock overlaps write lock, check if 528*7348SJose.Borrego@Sun.COM * allowed. 529*7348SJose.Borrego@Sun.COM */ 530*7348SJose.Borrego@Sun.COM if ((dlock->l_type == SMB_LOCK_TYPE_READONLY) && 531*7348SJose.Borrego@Sun.COM !(lock->l_type == SMB_LOCK_TYPE_READONLY)) { 532*7348SJose.Borrego@Sun.COM if (lock->l_file == sr->fid_ofile && 533*7348SJose.Borrego@Sun.COM lock->l_session_kid == sr->session->s_kid && 534*7348SJose.Borrego@Sun.COM lock->l_pid == sr->smb_pid && 535*7348SJose.Borrego@Sun.COM lock->l_uid == sr->smb_uid) { 536*7348SJose.Borrego@Sun.COM continue; 537*7348SJose.Borrego@Sun.COM } 538*7348SJose.Borrego@Sun.COM } 539*7348SJose.Borrego@Sun.COM 540*7348SJose.Borrego@Sun.COM /* Conflict in overlapping lock element */ 541*7348SJose.Borrego@Sun.COM *clockp = lock; 542*7348SJose.Borrego@Sun.COM status = NT_STATUS_LOCK_NOT_GRANTED; 543*7348SJose.Borrego@Sun.COM break; 544*7348SJose.Borrego@Sun.COM } 545*7348SJose.Borrego@Sun.COM 546*7348SJose.Borrego@Sun.COM return (status); 547*7348SJose.Borrego@Sun.COM } 548*7348SJose.Borrego@Sun.COM 549*7348SJose.Borrego@Sun.COM /* 550*7348SJose.Borrego@Sun.COM * smb_lock_wait 551*7348SJose.Borrego@Sun.COM * 552*7348SJose.Borrego@Sun.COM * Wait operation for smb overlapping lock to be released. Caller must hold 553*7348SJose.Borrego@Sun.COM * write lock for node->n_lock_list so that the set of active locks can't 554*7348SJose.Borrego@Sun.COM * change unexpectedly. The lock for node->n_lock_list will be released 555*7348SJose.Borrego@Sun.COM * within this function during the sleep after the lock dependency has 556*7348SJose.Borrego@Sun.COM * been recorded. 557*7348SJose.Borrego@Sun.COM * 558*7348SJose.Borrego@Sun.COM * return value 559*7348SJose.Borrego@Sun.COM * 560*7348SJose.Borrego@Sun.COM * 0 The request was canceled. 561*7348SJose.Borrego@Sun.COM * -1 The timeout was reached. 562*7348SJose.Borrego@Sun.COM * >0 Condition met. 563*7348SJose.Borrego@Sun.COM */ 564*7348SJose.Borrego@Sun.COM static clock_t 565*7348SJose.Borrego@Sun.COM smb_lock_wait(smb_request_t *sr, smb_lock_t *b_lock, smb_lock_t *c_lock) 566*7348SJose.Borrego@Sun.COM { 567*7348SJose.Borrego@Sun.COM clock_t rc; 568*7348SJose.Borrego@Sun.COM 569*7348SJose.Borrego@Sun.COM ASSERT(sr->sr_awaiting == NULL); 570*7348SJose.Borrego@Sun.COM 571*7348SJose.Borrego@Sun.COM mutex_enter(&sr->sr_mutex); 572*7348SJose.Borrego@Sun.COM 573*7348SJose.Borrego@Sun.COM switch (sr->sr_state) { 574*7348SJose.Borrego@Sun.COM case SMB_REQ_STATE_ACTIVE: 575*7348SJose.Borrego@Sun.COM /* 576*7348SJose.Borrego@Sun.COM * Wait up till the timeout time keeping track of actual 577*7348SJose.Borrego@Sun.COM * time waited for possible retry failure. 578*7348SJose.Borrego@Sun.COM */ 579*7348SJose.Borrego@Sun.COM sr->sr_state = SMB_REQ_STATE_WAITING_LOCK; 580*7348SJose.Borrego@Sun.COM sr->sr_awaiting = c_lock; 581*7348SJose.Borrego@Sun.COM mutex_exit(&sr->sr_mutex); 582*7348SJose.Borrego@Sun.COM 583*7348SJose.Borrego@Sun.COM mutex_enter(&c_lock->l_mutex); 584*7348SJose.Borrego@Sun.COM /* 585*7348SJose.Borrego@Sun.COM * The conflict list (l_conflict_list) for a lock contains 586*7348SJose.Borrego@Sun.COM * all the locks that are blocked by and in conflict with 587*7348SJose.Borrego@Sun.COM * that lock. Add the new lock to the conflict list for the 588*7348SJose.Borrego@Sun.COM * active lock. 589*7348SJose.Borrego@Sun.COM * 590*7348SJose.Borrego@Sun.COM * l_conflict_list is currently a fancy way of representing 591*7348SJose.Borrego@Sun.COM * the references/dependencies on a lock. It could be 592*7348SJose.Borrego@Sun.COM * replaced with a reference count but this approach 593*7348SJose.Borrego@Sun.COM * has the advantage that MDB can display the lock 594*7348SJose.Borrego@Sun.COM * dependencies at any point in time. In the future 595*7348SJose.Borrego@Sun.COM * we should be able to leverage the list to implement 596*7348SJose.Borrego@Sun.COM * an asynchronous locking model. 597*7348SJose.Borrego@Sun.COM * 598*7348SJose.Borrego@Sun.COM * l_blocked_by is the reverse of the conflict list. It 599*7348SJose.Borrego@Sun.COM * points to the lock that the new lock conflicts with. 600*7348SJose.Borrego@Sun.COM * As currently implemented this value is purely for 601*7348SJose.Borrego@Sun.COM * debug purposes -- there are windows of time when 602*7348SJose.Borrego@Sun.COM * l_blocked_by may be non-NULL even though there is no 603*7348SJose.Borrego@Sun.COM * conflict list 604*7348SJose.Borrego@Sun.COM */ 605*7348SJose.Borrego@Sun.COM b_lock->l_blocked_by = c_lock; 606*7348SJose.Borrego@Sun.COM smb_slist_insert_tail(&c_lock->l_conflict_list, b_lock); 607*7348SJose.Borrego@Sun.COM smb_llist_exit(&c_lock->l_file->f_node->n_lock_list); 608*7348SJose.Borrego@Sun.COM 609*7348SJose.Borrego@Sun.COM /* 610*7348SJose.Borrego@Sun.COM * XXX Hack.. drop s_lock to avoid blocking subsequent SMBs 611*7348SJose.Borrego@Sun.COM * that might affect the state of this lock (i.e. 612*7348SJose.Borrego@Sun.COM * smb_com_close). We shouldn't sleep while holding 613*7348SJose.Borrego@Sun.COM * locks anyway. 614*7348SJose.Borrego@Sun.COM */ 615*7348SJose.Borrego@Sun.COM smb_rwx_rwexit(&sr->session->s_lock); 616*7348SJose.Borrego@Sun.COM 617*7348SJose.Borrego@Sun.COM if (SMB_LOCK_INDEFINITE_WAIT(b_lock)) { 618*7348SJose.Borrego@Sun.COM cv_wait(&c_lock->l_cv, &c_lock->l_mutex); 619*7348SJose.Borrego@Sun.COM } else { 620*7348SJose.Borrego@Sun.COM rc = cv_timedwait(&c_lock->l_cv, 621*7348SJose.Borrego@Sun.COM &c_lock->l_mutex, b_lock->l_end_time); 622*7348SJose.Borrego@Sun.COM } 623*7348SJose.Borrego@Sun.COM 624*7348SJose.Borrego@Sun.COM /* 625*7348SJose.Borrego@Sun.COM * XXX Hack continued from above... re-acquire s_lock 626*7348SJose.Borrego@Sun.COM * OK to hardcode RW_READER since this is just a hack and 627*7348SJose.Borrego@Sun.COM * we really should yank it out and do something else. 628*7348SJose.Borrego@Sun.COM */ 629*7348SJose.Borrego@Sun.COM smb_rwx_rwenter(&sr->session->s_lock, RW_READER); 630*7348SJose.Borrego@Sun.COM 631*7348SJose.Borrego@Sun.COM mutex_exit(&c_lock->l_mutex); 632*7348SJose.Borrego@Sun.COM 633*7348SJose.Borrego@Sun.COM smb_llist_enter(&c_lock->l_file->f_node->n_lock_list, 634*7348SJose.Borrego@Sun.COM RW_WRITER); 635*7348SJose.Borrego@Sun.COM smb_slist_remove(&c_lock->l_conflict_list, b_lock); 636*7348SJose.Borrego@Sun.COM 637*7348SJose.Borrego@Sun.COM mutex_enter(&sr->sr_mutex); 638*7348SJose.Borrego@Sun.COM sr->sr_awaiting = NULL; 639*7348SJose.Borrego@Sun.COM if (sr->sr_state == SMB_REQ_STATE_CANCELED) { 640*7348SJose.Borrego@Sun.COM rc = 0; 641*7348SJose.Borrego@Sun.COM } else { 642*7348SJose.Borrego@Sun.COM sr->sr_state = SMB_REQ_STATE_ACTIVE; 643*7348SJose.Borrego@Sun.COM } 644*7348SJose.Borrego@Sun.COM break; 645*7348SJose.Borrego@Sun.COM 646*7348SJose.Borrego@Sun.COM default: 647*7348SJose.Borrego@Sun.COM ASSERT(sr->sr_state == SMB_REQ_STATE_CANCELED); 648*7348SJose.Borrego@Sun.COM rc = 0; 649*7348SJose.Borrego@Sun.COM break; 650*7348SJose.Borrego@Sun.COM } 651*7348SJose.Borrego@Sun.COM mutex_exit(&sr->sr_mutex); 652*7348SJose.Borrego@Sun.COM 653*7348SJose.Borrego@Sun.COM return (rc); 654*7348SJose.Borrego@Sun.COM } 655*7348SJose.Borrego@Sun.COM 656*7348SJose.Borrego@Sun.COM /* 657*7348SJose.Borrego@Sun.COM * smb_lock_range_ulckrules 658*7348SJose.Borrego@Sun.COM * 659*7348SJose.Borrego@Sun.COM * 1. Unlock should be performed at exactly matching ends. 660*7348SJose.Borrego@Sun.COM * This has been changed because overlapping ends is 661*7348SJose.Borrego@Sun.COM * allowed and there is no other precise way of locating 662*7348SJose.Borrego@Sun.COM * lock entity in node lock list. 663*7348SJose.Borrego@Sun.COM * 664*7348SJose.Borrego@Sun.COM * 2. Unlock is failed if there is no corresponding lock exists. 665*7348SJose.Borrego@Sun.COM * 666*7348SJose.Borrego@Sun.COM * Return values 667*7348SJose.Borrego@Sun.COM * 668*7348SJose.Borrego@Sun.COM * NT_STATUS_SUCCESS Unlock request matches lock record 669*7348SJose.Borrego@Sun.COM * pointed by 'nodelock' lock structure. 670*7348SJose.Borrego@Sun.COM * 671*7348SJose.Borrego@Sun.COM * NT_STATUS_RANGE_NOT_LOCKED Unlock request doen't match any 672*7348SJose.Borrego@Sun.COM * of lock record in node lock request or 673*7348SJose.Borrego@Sun.COM * error in unlock range processing. 674*7348SJose.Borrego@Sun.COM */ 675*7348SJose.Borrego@Sun.COM static uint32_t 676*7348SJose.Borrego@Sun.COM smb_lock_range_ulckrules( 677*7348SJose.Borrego@Sun.COM smb_request_t *sr, 678*7348SJose.Borrego@Sun.COM smb_node_t *node, 679*7348SJose.Borrego@Sun.COM uint64_t start, 680*7348SJose.Borrego@Sun.COM uint64_t length, 681*7348SJose.Borrego@Sun.COM smb_lock_t **nodelock) 682*7348SJose.Borrego@Sun.COM { 683*7348SJose.Borrego@Sun.COM smb_lock_t *lock; 684*7348SJose.Borrego@Sun.COM uint32_t status = NT_STATUS_RANGE_NOT_LOCKED; 685*7348SJose.Borrego@Sun.COM 686*7348SJose.Borrego@Sun.COM /* Caller must hold lock for node->n_lock_list */ 687*7348SJose.Borrego@Sun.COM for (lock = smb_llist_head(&node->n_lock_list); 688*7348SJose.Borrego@Sun.COM lock != NULL; 689*7348SJose.Borrego@Sun.COM lock = smb_llist_next(&node->n_lock_list, lock)) { 690*7348SJose.Borrego@Sun.COM 691*7348SJose.Borrego@Sun.COM if ((start == lock->l_start) && 692*7348SJose.Borrego@Sun.COM (length == lock->l_length) && 693*7348SJose.Borrego@Sun.COM lock->l_file == sr->fid_ofile && 694*7348SJose.Borrego@Sun.COM lock->l_session_kid == sr->session->s_kid && 695*7348SJose.Borrego@Sun.COM lock->l_pid == sr->smb_pid && 696*7348SJose.Borrego@Sun.COM lock->l_uid == sr->smb_uid) { 697*7348SJose.Borrego@Sun.COM *nodelock = lock; 698*7348SJose.Borrego@Sun.COM status = NT_STATUS_SUCCESS; 699*7348SJose.Borrego@Sun.COM break; 700*7348SJose.Borrego@Sun.COM } 701*7348SJose.Borrego@Sun.COM } 702*7348SJose.Borrego@Sun.COM 703*7348SJose.Borrego@Sun.COM return (status); 704*7348SJose.Borrego@Sun.COM } 705*7348SJose.Borrego@Sun.COM 706*7348SJose.Borrego@Sun.COM static smb_lock_t * 707*7348SJose.Borrego@Sun.COM smb_lock_create( 708*7348SJose.Borrego@Sun.COM smb_request_t *sr, 709*7348SJose.Borrego@Sun.COM uint64_t start, 710*7348SJose.Borrego@Sun.COM uint64_t length, 711*7348SJose.Borrego@Sun.COM uint32_t locktype, 712*7348SJose.Borrego@Sun.COM uint32_t timeout) 713*7348SJose.Borrego@Sun.COM { 714*7348SJose.Borrego@Sun.COM smb_lock_t *lock; 715*7348SJose.Borrego@Sun.COM 716*7348SJose.Borrego@Sun.COM ASSERT(locktype == SMB_LOCK_TYPE_READWRITE || 717*7348SJose.Borrego@Sun.COM locktype == SMB_LOCK_TYPE_READONLY); 718*7348SJose.Borrego@Sun.COM 719*7348SJose.Borrego@Sun.COM lock = kmem_zalloc(sizeof (smb_lock_t), KM_SLEEP); 720*7348SJose.Borrego@Sun.COM lock->l_magic = SMB_LOCK_MAGIC; 721*7348SJose.Borrego@Sun.COM lock->l_sr = sr; /* Invalid after lock is active */ 722*7348SJose.Borrego@Sun.COM lock->l_session_kid = sr->session->s_kid; 723*7348SJose.Borrego@Sun.COM lock->l_session = sr->session; 724*7348SJose.Borrego@Sun.COM lock->l_file = sr->fid_ofile; 725*7348SJose.Borrego@Sun.COM lock->l_uid = sr->smb_uid; 726*7348SJose.Borrego@Sun.COM lock->l_pid = sr->smb_pid; 727*7348SJose.Borrego@Sun.COM lock->l_type = locktype; 728*7348SJose.Borrego@Sun.COM lock->l_start = start; 729*7348SJose.Borrego@Sun.COM lock->l_length = length; 730*7348SJose.Borrego@Sun.COM /* 731*7348SJose.Borrego@Sun.COM * Calculate the absolute end time so that we can use it 732*7348SJose.Borrego@Sun.COM * in cv_timedwait. 733*7348SJose.Borrego@Sun.COM */ 734*7348SJose.Borrego@Sun.COM lock->l_end_time = lbolt + MSEC_TO_TICK(timeout); 735*7348SJose.Borrego@Sun.COM if (timeout == UINT_MAX) 736*7348SJose.Borrego@Sun.COM lock->l_flags |= SMB_LOCK_FLAG_INDEFINITE; 737*7348SJose.Borrego@Sun.COM 738*7348SJose.Borrego@Sun.COM mutex_init(&lock->l_mutex, NULL, MUTEX_DEFAULT, NULL); 739*7348SJose.Borrego@Sun.COM cv_init(&lock->l_cv, NULL, CV_DEFAULT, NULL); 740*7348SJose.Borrego@Sun.COM smb_slist_constructor(&lock->l_conflict_list, sizeof (smb_lock_t), 741*7348SJose.Borrego@Sun.COM offsetof(smb_lock_t, l_conflict_lnd)); 742*7348SJose.Borrego@Sun.COM 743*7348SJose.Borrego@Sun.COM return (lock); 744*7348SJose.Borrego@Sun.COM } 745*7348SJose.Borrego@Sun.COM 746*7348SJose.Borrego@Sun.COM static void 747*7348SJose.Borrego@Sun.COM smb_lock_free(smb_lock_t *lock) 748*7348SJose.Borrego@Sun.COM { 749*7348SJose.Borrego@Sun.COM smb_slist_destructor(&lock->l_conflict_list); 750*7348SJose.Borrego@Sun.COM cv_destroy(&lock->l_cv); 751*7348SJose.Borrego@Sun.COM mutex_destroy(&lock->l_mutex); 752*7348SJose.Borrego@Sun.COM 753*7348SJose.Borrego@Sun.COM kmem_free(lock, sizeof (smb_lock_t)); 754*7348SJose.Borrego@Sun.COM } 755*7348SJose.Borrego@Sun.COM 756*7348SJose.Borrego@Sun.COM /* 757*7348SJose.Borrego@Sun.COM * smb_lock_destroy 758*7348SJose.Borrego@Sun.COM * 759*7348SJose.Borrego@Sun.COM * Caller must hold node->n_lock_list 760*7348SJose.Borrego@Sun.COM */ 761*7348SJose.Borrego@Sun.COM static void 762*7348SJose.Borrego@Sun.COM smb_lock_destroy(smb_lock_t *lock) 763*7348SJose.Borrego@Sun.COM { 764*7348SJose.Borrego@Sun.COM /* 765*7348SJose.Borrego@Sun.COM * Caller must hold node->n_lock_list lock. 766*7348SJose.Borrego@Sun.COM */ 767*7348SJose.Borrego@Sun.COM mutex_enter(&lock->l_mutex); 768*7348SJose.Borrego@Sun.COM cv_broadcast(&lock->l_cv); 769*7348SJose.Borrego@Sun.COM mutex_exit(&lock->l_mutex); 770*7348SJose.Borrego@Sun.COM 771*7348SJose.Borrego@Sun.COM /* 772*7348SJose.Borrego@Sun.COM * The cv_broadcast above should wake up any locks that previous 773*7348SJose.Borrego@Sun.COM * had conflicts with this lock. Wait for the locking threads 774*7348SJose.Borrego@Sun.COM * to remove their references to this lock. 775*7348SJose.Borrego@Sun.COM */ 776*7348SJose.Borrego@Sun.COM smb_slist_wait_for_empty(&lock->l_conflict_list); 777*7348SJose.Borrego@Sun.COM 778*7348SJose.Borrego@Sun.COM smb_lock_free(lock); 779*7348SJose.Borrego@Sun.COM } 780*7348SJose.Borrego@Sun.COM 781*7348SJose.Borrego@Sun.COM /* 782*7348SJose.Borrego@Sun.COM * smb_is_range_unlocked 783*7348SJose.Borrego@Sun.COM * 784*7348SJose.Borrego@Sun.COM * Checks if the current unlock byte range request overlaps another lock 785*7348SJose.Borrego@Sun.COM * This function is used to determine where POSIX unlocks should be 786*7348SJose.Borrego@Sun.COM * applied. 787*7348SJose.Borrego@Sun.COM * 788*7348SJose.Borrego@Sun.COM * The return code and the value of new_mark must be interpreted as 789*7348SJose.Borrego@Sun.COM * follows: 790*7348SJose.Borrego@Sun.COM * 791*7348SJose.Borrego@Sun.COM * B_TRUE and (new_mark == 0): 792*7348SJose.Borrego@Sun.COM * This is the last or only lock left to be unlocked 793*7348SJose.Borrego@Sun.COM * 794*7348SJose.Borrego@Sun.COM * B_TRUE and (new_mark > 0): 795*7348SJose.Borrego@Sun.COM * The range from start to new_mark can be unlocked 796*7348SJose.Borrego@Sun.COM * 797*7348SJose.Borrego@Sun.COM * B_FALSE and (new_mark == 0): 798*7348SJose.Borrego@Sun.COM * The unlock can't be performed and we are done 799*7348SJose.Borrego@Sun.COM * 800*7348SJose.Borrego@Sun.COM * B_FALSE and (new_mark > 0), 801*7348SJose.Borrego@Sun.COM * The range from start to new_mark can't be unlocked 802*7348SJose.Borrego@Sun.COM * Start should be reset to new_mark for the next pass 803*7348SJose.Borrego@Sun.COM */ 804*7348SJose.Borrego@Sun.COM 805*7348SJose.Borrego@Sun.COM static boolean_t 806*7348SJose.Borrego@Sun.COM smb_is_range_unlocked(uint64_t start, uint64_t end, uint32_t uniqid, 807*7348SJose.Borrego@Sun.COM smb_llist_t *llist_head, uint64_t *new_mark) 808*7348SJose.Borrego@Sun.COM { 809*7348SJose.Borrego@Sun.COM struct smb_lock *lk = NULL; 810*7348SJose.Borrego@Sun.COM uint64_t low_water_mark = MAXOFFSET_T; 811*7348SJose.Borrego@Sun.COM uint64_t lk_start; 812*7348SJose.Borrego@Sun.COM uint64_t lk_end; 813*7348SJose.Borrego@Sun.COM 814*7348SJose.Borrego@Sun.COM *new_mark = 0; 815*7348SJose.Borrego@Sun.COM lk = smb_llist_head(llist_head); 816*7348SJose.Borrego@Sun.COM while (lk) { 817*7348SJose.Borrego@Sun.COM if (lk->l_length == 0) { 818*7348SJose.Borrego@Sun.COM lk = smb_llist_next(llist_head, lk); 819*7348SJose.Borrego@Sun.COM continue; 820*7348SJose.Borrego@Sun.COM } 821*7348SJose.Borrego@Sun.COM 822*7348SJose.Borrego@Sun.COM if (lk->l_file->f_uniqid != uniqid) { 823*7348SJose.Borrego@Sun.COM lk = smb_llist_next(llist_head, lk); 824*7348SJose.Borrego@Sun.COM continue; 825*7348SJose.Borrego@Sun.COM } 826*7348SJose.Borrego@Sun.COM 827*7348SJose.Borrego@Sun.COM lk_end = lk->l_start + lk->l_length - 1; 828*7348SJose.Borrego@Sun.COM lk_start = lk->l_start; 829*7348SJose.Borrego@Sun.COM 830*7348SJose.Borrego@Sun.COM /* 831*7348SJose.Borrego@Sun.COM * there is no overlap for the first 2 cases 832*7348SJose.Borrego@Sun.COM * check next node 833*7348SJose.Borrego@Sun.COM */ 834*7348SJose.Borrego@Sun.COM if (lk_end < start) { 835*7348SJose.Borrego@Sun.COM lk = smb_llist_next(llist_head, lk); 836*7348SJose.Borrego@Sun.COM continue; 837*7348SJose.Borrego@Sun.COM } 838*7348SJose.Borrego@Sun.COM if (lk_start > end) { 839*7348SJose.Borrego@Sun.COM lk = smb_llist_next(llist_head, lk); 840*7348SJose.Borrego@Sun.COM continue; 841*7348SJose.Borrego@Sun.COM } 842*7348SJose.Borrego@Sun.COM 843*7348SJose.Borrego@Sun.COM /* this range is completely locked */ 844*7348SJose.Borrego@Sun.COM if ((lk_start <= start) && (lk_end >= end)) { 845*7348SJose.Borrego@Sun.COM return (B_FALSE); 846*7348SJose.Borrego@Sun.COM } 847*7348SJose.Borrego@Sun.COM 848*7348SJose.Borrego@Sun.COM /* the first part of this range is locked */ 849*7348SJose.Borrego@Sun.COM if ((start >= lk_start) && (start <= lk_end)) { 850*7348SJose.Borrego@Sun.COM if (end > lk_end) 851*7348SJose.Borrego@Sun.COM *new_mark = lk_end + 1; 852*7348SJose.Borrego@Sun.COM return (B_FALSE); 853*7348SJose.Borrego@Sun.COM } 854*7348SJose.Borrego@Sun.COM 855*7348SJose.Borrego@Sun.COM /* this piece is unlocked */ 856*7348SJose.Borrego@Sun.COM if ((lk_start >= start) && (lk_start <= end)) { 857*7348SJose.Borrego@Sun.COM if (low_water_mark > lk_start) 858*7348SJose.Borrego@Sun.COM low_water_mark = lk_start; 859*7348SJose.Borrego@Sun.COM } 860*7348SJose.Borrego@Sun.COM 861*7348SJose.Borrego@Sun.COM lk = smb_llist_next(llist_head, lk); 862*7348SJose.Borrego@Sun.COM } 863*7348SJose.Borrego@Sun.COM 864*7348SJose.Borrego@Sun.COM if (low_water_mark != MAXOFFSET_T) { 865*7348SJose.Borrego@Sun.COM *new_mark = low_water_mark; 866*7348SJose.Borrego@Sun.COM return (B_TRUE); 867*7348SJose.Borrego@Sun.COM } 868*7348SJose.Borrego@Sun.COM /* the range is completely unlocked */ 869*7348SJose.Borrego@Sun.COM return (B_TRUE); 870*7348SJose.Borrego@Sun.COM } 871