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