17348SJose.Borrego@Sun.COM /* 27348SJose.Borrego@Sun.COM * CDDL HEADER START 37348SJose.Borrego@Sun.COM * 47348SJose.Borrego@Sun.COM * The contents of this file are subject to the terms of the 57348SJose.Borrego@Sun.COM * Common Development and Distribution License (the "License"). 67348SJose.Borrego@Sun.COM * You may not use this file except in compliance with the License. 77348SJose.Borrego@Sun.COM * 87348SJose.Borrego@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97348SJose.Borrego@Sun.COM * or http://www.opensolaris.org/os/licensing. 107348SJose.Borrego@Sun.COM * See the License for the specific language governing permissions 117348SJose.Borrego@Sun.COM * and limitations under the License. 127348SJose.Borrego@Sun.COM * 137348SJose.Borrego@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 147348SJose.Borrego@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157348SJose.Borrego@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 167348SJose.Borrego@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 177348SJose.Borrego@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 187348SJose.Borrego@Sun.COM * 197348SJose.Borrego@Sun.COM * CDDL HEADER END 207348SJose.Borrego@Sun.COM */ 217348SJose.Borrego@Sun.COM /* 228934SJose.Borrego@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237348SJose.Borrego@Sun.COM * Use is subject to license terms. 247348SJose.Borrego@Sun.COM */ 257348SJose.Borrego@Sun.COM 267348SJose.Borrego@Sun.COM /* 277348SJose.Borrego@Sun.COM * This module provides range lock functionality for CIFS/SMB clients. 287348SJose.Borrego@Sun.COM * Lock range service functions process SMB lock and and unlock 297348SJose.Borrego@Sun.COM * requests for a file by applying lock rules and marks file range 307348SJose.Borrego@Sun.COM * as locked if the lock is successful otherwise return proper 317348SJose.Borrego@Sun.COM * error code. 327348SJose.Borrego@Sun.COM */ 337348SJose.Borrego@Sun.COM 347348SJose.Borrego@Sun.COM #include <smbsrv/smb_incl.h> 357348SJose.Borrego@Sun.COM #include <smbsrv/smb_fsops.h> 367348SJose.Borrego@Sun.COM #include <sys/nbmlock.h> 377348SJose.Borrego@Sun.COM #include <sys/param.h> 387348SJose.Borrego@Sun.COM 397348SJose.Borrego@Sun.COM extern caller_context_t smb_ct; 407348SJose.Borrego@Sun.COM 417348SJose.Borrego@Sun.COM static void smb_lock_posix_unlock(smb_node_t *, smb_lock_t *, cred_t *); 427348SJose.Borrego@Sun.COM static boolean_t smb_is_range_unlocked(uint64_t, uint64_t, uint32_t, 437348SJose.Borrego@Sun.COM smb_llist_t *, uint64_t *); 447348SJose.Borrego@Sun.COM static int smb_lock_range_overlap(smb_lock_t *, uint64_t, uint64_t); 457348SJose.Borrego@Sun.COM static uint32_t smb_lock_range_lckrules(smb_request_t *, smb_ofile_t *, 467348SJose.Borrego@Sun.COM smb_node_t *, smb_lock_t *, smb_lock_t **); 477348SJose.Borrego@Sun.COM static clock_t smb_lock_wait(smb_request_t *, smb_lock_t *, smb_lock_t *); 487348SJose.Borrego@Sun.COM static uint32_t smb_lock_range_ulckrules(smb_request_t *, smb_node_t *, 497348SJose.Borrego@Sun.COM uint64_t, uint64_t, smb_lock_t **nodelock); 507348SJose.Borrego@Sun.COM static smb_lock_t *smb_lock_create(smb_request_t *, uint64_t, uint64_t, 517348SJose.Borrego@Sun.COM uint32_t, uint32_t); 527348SJose.Borrego@Sun.COM static void smb_lock_destroy(smb_lock_t *); 537348SJose.Borrego@Sun.COM static void smb_lock_free(smb_lock_t *); 547348SJose.Borrego@Sun.COM 557348SJose.Borrego@Sun.COM 567348SJose.Borrego@Sun.COM 577348SJose.Borrego@Sun.COM /* 587348SJose.Borrego@Sun.COM * smb_unlock_range 597348SJose.Borrego@Sun.COM * 607348SJose.Borrego@Sun.COM * locates lock range performed for corresponding to unlock request. 617348SJose.Borrego@Sun.COM * 627348SJose.Borrego@Sun.COM * NT_STATUS_SUCCESS - Lock range performed successfully. 637348SJose.Borrego@Sun.COM * !NT_STATUS_SUCCESS - Error in unlock range operation. 647348SJose.Borrego@Sun.COM */ 657348SJose.Borrego@Sun.COM uint32_t 667348SJose.Borrego@Sun.COM smb_unlock_range( 677348SJose.Borrego@Sun.COM smb_request_t *sr, 687348SJose.Borrego@Sun.COM smb_node_t *node, 697348SJose.Borrego@Sun.COM uint64_t start, 707348SJose.Borrego@Sun.COM uint64_t length) 717348SJose.Borrego@Sun.COM { 727348SJose.Borrego@Sun.COM smb_lock_t *lock = NULL; 737348SJose.Borrego@Sun.COM uint32_t status; 747348SJose.Borrego@Sun.COM 757348SJose.Borrego@Sun.COM /* Apply unlocking rules */ 767348SJose.Borrego@Sun.COM smb_llist_enter(&node->n_lock_list, RW_WRITER); 777348SJose.Borrego@Sun.COM status = smb_lock_range_ulckrules(sr, node, start, length, &lock); 787348SJose.Borrego@Sun.COM if (status != NT_STATUS_SUCCESS) { 797348SJose.Borrego@Sun.COM /* 807348SJose.Borrego@Sun.COM * If lock range is not matching in the list 817348SJose.Borrego@Sun.COM * return error. 827348SJose.Borrego@Sun.COM */ 837348SJose.Borrego@Sun.COM ASSERT(lock == NULL); 847348SJose.Borrego@Sun.COM smb_llist_exit(&node->n_lock_list); 857348SJose.Borrego@Sun.COM return (status); 867348SJose.Borrego@Sun.COM } 877348SJose.Borrego@Sun.COM 887348SJose.Borrego@Sun.COM smb_llist_remove(&node->n_lock_list, lock); 897348SJose.Borrego@Sun.COM smb_lock_posix_unlock(node, lock, sr->user_cr); 907348SJose.Borrego@Sun.COM smb_llist_exit(&node->n_lock_list); 917348SJose.Borrego@Sun.COM smb_lock_destroy(lock); 927348SJose.Borrego@Sun.COM 937348SJose.Borrego@Sun.COM return (status); 947348SJose.Borrego@Sun.COM } 957348SJose.Borrego@Sun.COM 967348SJose.Borrego@Sun.COM /* 977348SJose.Borrego@Sun.COM * smb_lock_range 987348SJose.Borrego@Sun.COM * 997348SJose.Borrego@Sun.COM * checks for integrity of file lock operation for the given range of file data. 1007348SJose.Borrego@Sun.COM * This is performed by applying lock rules with all the elements of the node 1017348SJose.Borrego@Sun.COM * lock list. 1027348SJose.Borrego@Sun.COM * 1037348SJose.Borrego@Sun.COM * The function returns with new lock added if lock request is non-conflicting 1047348SJose.Borrego@Sun.COM * with existing range lock for the file. Otherwise smb request is filed 1057348SJose.Borrego@Sun.COM * without returning. 1067348SJose.Borrego@Sun.COM * 1077348SJose.Borrego@Sun.COM * NT_STATUS_SUCCESS - Lock range performed successfully. 1087348SJose.Borrego@Sun.COM * !NT_STATUS_SUCCESS - Error in lock range operation. 1097348SJose.Borrego@Sun.COM */ 1107348SJose.Borrego@Sun.COM uint32_t 1117348SJose.Borrego@Sun.COM smb_lock_range( 1127348SJose.Borrego@Sun.COM smb_request_t *sr, 1137348SJose.Borrego@Sun.COM uint64_t start, 1147348SJose.Borrego@Sun.COM uint64_t length, 1157348SJose.Borrego@Sun.COM uint32_t timeout, 1167348SJose.Borrego@Sun.COM uint32_t locktype) 1177348SJose.Borrego@Sun.COM { 1187348SJose.Borrego@Sun.COM smb_ofile_t *file = sr->fid_ofile; 1197348SJose.Borrego@Sun.COM smb_node_t *node = file->f_node; 1207348SJose.Borrego@Sun.COM smb_lock_t *lock; 1217348SJose.Borrego@Sun.COM smb_lock_t *clock = NULL; 1227348SJose.Borrego@Sun.COM uint32_t result = NT_STATUS_SUCCESS; 1237348SJose.Borrego@Sun.COM boolean_t lock_has_timeout = (timeout != 0); 1247348SJose.Borrego@Sun.COM 1257348SJose.Borrego@Sun.COM lock = smb_lock_create(sr, start, length, locktype, timeout); 1267348SJose.Borrego@Sun.COM 1277348SJose.Borrego@Sun.COM smb_llist_enter(&node->n_lock_list, RW_WRITER); 1287348SJose.Borrego@Sun.COM for (;;) { 1297348SJose.Borrego@Sun.COM clock_t rc; 1307348SJose.Borrego@Sun.COM 1317348SJose.Borrego@Sun.COM /* Apply locking rules */ 1327348SJose.Borrego@Sun.COM result = smb_lock_range_lckrules(sr, file, node, lock, &clock); 1337348SJose.Borrego@Sun.COM 1347348SJose.Borrego@Sun.COM if ((result == NT_STATUS_CANCELLED) || 1357348SJose.Borrego@Sun.COM (result == NT_STATUS_SUCCESS) || 1367348SJose.Borrego@Sun.COM (result == NT_STATUS_RANGE_NOT_LOCKED)) { 1377348SJose.Borrego@Sun.COM ASSERT(clock == NULL); 1387348SJose.Borrego@Sun.COM break; 1397348SJose.Borrego@Sun.COM } else if (timeout == 0) { 1407348SJose.Borrego@Sun.COM break; 1417348SJose.Borrego@Sun.COM } 1427348SJose.Borrego@Sun.COM 1437348SJose.Borrego@Sun.COM ASSERT(result == NT_STATUS_LOCK_NOT_GRANTED); 1447348SJose.Borrego@Sun.COM ASSERT(clock); 1457348SJose.Borrego@Sun.COM /* 1467348SJose.Borrego@Sun.COM * Call smb_lock_wait holding write lock for 1477348SJose.Borrego@Sun.COM * node lock list. smb_lock_wait will release 1487348SJose.Borrego@Sun.COM * this lock if it blocks. 1497348SJose.Borrego@Sun.COM */ 1507348SJose.Borrego@Sun.COM ASSERT(node == clock->l_file->f_node); 1517348SJose.Borrego@Sun.COM 1527348SJose.Borrego@Sun.COM rc = smb_lock_wait(sr, lock, clock); 1537348SJose.Borrego@Sun.COM if (rc == 0) { 1547348SJose.Borrego@Sun.COM result = NT_STATUS_CANCELLED; 1557348SJose.Borrego@Sun.COM break; 1567348SJose.Borrego@Sun.COM } 1577348SJose.Borrego@Sun.COM if (rc == -1) 1587348SJose.Borrego@Sun.COM timeout = 0; 1597348SJose.Borrego@Sun.COM 1607348SJose.Borrego@Sun.COM clock = NULL; 1617348SJose.Borrego@Sun.COM } 1627348SJose.Borrego@Sun.COM 1637348SJose.Borrego@Sun.COM lock->l_blocked_by = NULL; 1647348SJose.Borrego@Sun.COM 1657348SJose.Borrego@Sun.COM if (result != NT_STATUS_SUCCESS) { 1667348SJose.Borrego@Sun.COM /* 1677348SJose.Borrego@Sun.COM * Under certain conditions NT_STATUS_FILE_LOCK_CONFLICT 1687348SJose.Borrego@Sun.COM * should be returned instead of NT_STATUS_LOCK_NOT_GRANTED. 1697348SJose.Borrego@Sun.COM */ 1707348SJose.Borrego@Sun.COM if (result == NT_STATUS_LOCK_NOT_GRANTED) { 1717348SJose.Borrego@Sun.COM /* 1727348SJose.Borrego@Sun.COM * Locks with timeouts always return 1737348SJose.Borrego@Sun.COM * NT_STATUS_FILE_LOCK_CONFLICT 1747348SJose.Borrego@Sun.COM */ 1757348SJose.Borrego@Sun.COM if (lock_has_timeout) 1767348SJose.Borrego@Sun.COM result = NT_STATUS_FILE_LOCK_CONFLICT; 1777348SJose.Borrego@Sun.COM 1787348SJose.Borrego@Sun.COM /* 1797348SJose.Borrego@Sun.COM * Locks starting higher than 0xef000000 that do not 1807348SJose.Borrego@Sun.COM * have the MSB set always return 1817348SJose.Borrego@Sun.COM * NT_STATUS_FILE_LOCK_CONFLICT 1827348SJose.Borrego@Sun.COM */ 1837348SJose.Borrego@Sun.COM if ((lock->l_start >= 0xef000000) && 1847348SJose.Borrego@Sun.COM !(lock->l_start & (1ULL << 63))) { 1857348SJose.Borrego@Sun.COM result = NT_STATUS_FILE_LOCK_CONFLICT; 1867348SJose.Borrego@Sun.COM } 1877348SJose.Borrego@Sun.COM 1887348SJose.Borrego@Sun.COM /* 1897348SJose.Borrego@Sun.COM * If the last lock attempt to fail on this file handle 1907348SJose.Borrego@Sun.COM * started at the same offset as this one then return 1917348SJose.Borrego@Sun.COM * NT_STATUS_FILE_LOCK_CONFLICT 1927348SJose.Borrego@Sun.COM */ 1937348SJose.Borrego@Sun.COM mutex_enter(&file->f_mutex); 1947348SJose.Borrego@Sun.COM if ((file->f_flags & SMB_OFLAGS_LLF_POS_VALID) && 1957348SJose.Borrego@Sun.COM (lock->l_start == file->f_llf_pos)) { 1967348SJose.Borrego@Sun.COM result = NT_STATUS_FILE_LOCK_CONFLICT; 1977348SJose.Borrego@Sun.COM } 1987348SJose.Borrego@Sun.COM mutex_exit(&file->f_mutex); 1997348SJose.Borrego@Sun.COM } 2007348SJose.Borrego@Sun.COM 2017348SJose.Borrego@Sun.COM /* Update last lock failed offset */ 2027348SJose.Borrego@Sun.COM mutex_enter(&file->f_mutex); 2037348SJose.Borrego@Sun.COM file->f_llf_pos = lock->l_start; 2047348SJose.Borrego@Sun.COM file->f_flags |= SMB_OFLAGS_LLF_POS_VALID; 2057348SJose.Borrego@Sun.COM mutex_exit(&file->f_mutex); 2067348SJose.Borrego@Sun.COM 2077348SJose.Borrego@Sun.COM smb_lock_free(lock); 2087348SJose.Borrego@Sun.COM } else { 2097348SJose.Borrego@Sun.COM /* 2107348SJose.Borrego@Sun.COM * don't insert into the CIFS lock list unless the 2117348SJose.Borrego@Sun.COM * posix lock worked 2127348SJose.Borrego@Sun.COM */ 2137348SJose.Borrego@Sun.COM if (smb_fsop_frlock(node, lock, B_FALSE, sr->user_cr)) 2147348SJose.Borrego@Sun.COM result = NT_STATUS_FILE_LOCK_CONFLICT; 2157348SJose.Borrego@Sun.COM else 2167348SJose.Borrego@Sun.COM smb_llist_insert_tail(&node->n_lock_list, lock); 2177348SJose.Borrego@Sun.COM } 2187348SJose.Borrego@Sun.COM smb_llist_exit(&node->n_lock_list); 2197348SJose.Borrego@Sun.COM 2207348SJose.Borrego@Sun.COM return (result); 2217348SJose.Borrego@Sun.COM } 2227348SJose.Borrego@Sun.COM 2237348SJose.Borrego@Sun.COM 2247348SJose.Borrego@Sun.COM /* 2257348SJose.Borrego@Sun.COM * smb_lock_range_access 2267348SJose.Borrego@Sun.COM * 2277348SJose.Borrego@Sun.COM * scans node lock list 2287348SJose.Borrego@Sun.COM * to check if there is any overlapping lock. Overlapping 2297348SJose.Borrego@Sun.COM * lock is allowed only under same session and client pid. 2307348SJose.Borrego@Sun.COM * 2317348SJose.Borrego@Sun.COM * Return values 2327348SJose.Borrego@Sun.COM * NT_STATUS_SUCCESS lock access granted. 2337348SJose.Borrego@Sun.COM * NT_STATUS_FILE_LOCK_CONFLICT access denied due to lock conflict. 2347348SJose.Borrego@Sun.COM */ 2357348SJose.Borrego@Sun.COM int 2367348SJose.Borrego@Sun.COM smb_lock_range_access( 2377348SJose.Borrego@Sun.COM smb_request_t *sr, 2387348SJose.Borrego@Sun.COM smb_node_t *node, 2397348SJose.Borrego@Sun.COM uint64_t start, 2407348SJose.Borrego@Sun.COM uint64_t length, 2417348SJose.Borrego@Sun.COM boolean_t will_write) 2427348SJose.Borrego@Sun.COM { 2437348SJose.Borrego@Sun.COM smb_lock_t *lock; 2447348SJose.Borrego@Sun.COM smb_llist_t *llist; 2457348SJose.Borrego@Sun.COM int status = NT_STATUS_SUCCESS; 2467348SJose.Borrego@Sun.COM 2477348SJose.Borrego@Sun.COM llist = &node->n_lock_list; 2487348SJose.Borrego@Sun.COM smb_llist_enter(llist, RW_READER); 2497348SJose.Borrego@Sun.COM /* Search for any applicable lock */ 2507348SJose.Borrego@Sun.COM for (lock = smb_llist_head(llist); 2517348SJose.Borrego@Sun.COM lock != NULL; 2527348SJose.Borrego@Sun.COM lock = smb_llist_next(llist, lock)) { 2537348SJose.Borrego@Sun.COM 2547348SJose.Borrego@Sun.COM if (!smb_lock_range_overlap(lock, start, length)) 2557348SJose.Borrego@Sun.COM /* Lock does not overlap */ 2567348SJose.Borrego@Sun.COM continue; 2577348SJose.Borrego@Sun.COM 2587348SJose.Borrego@Sun.COM if (lock->l_type == SMB_LOCK_TYPE_READONLY && !will_write) 2597348SJose.Borrego@Sun.COM continue; 2607348SJose.Borrego@Sun.COM 2617348SJose.Borrego@Sun.COM if (lock->l_type == SMB_LOCK_TYPE_READWRITE && 2627348SJose.Borrego@Sun.COM lock->l_session_kid == sr->session->s_kid && 2637348SJose.Borrego@Sun.COM lock->l_pid == sr->smb_pid) 2647348SJose.Borrego@Sun.COM continue; 2657348SJose.Borrego@Sun.COM 2667348SJose.Borrego@Sun.COM status = NT_STATUS_FILE_LOCK_CONFLICT; 2677348SJose.Borrego@Sun.COM break; 2687348SJose.Borrego@Sun.COM } 2697348SJose.Borrego@Sun.COM smb_llist_exit(llist); 2707348SJose.Borrego@Sun.COM return (status); 2717348SJose.Borrego@Sun.COM } 2727348SJose.Borrego@Sun.COM 2737348SJose.Borrego@Sun.COM void 2747348SJose.Borrego@Sun.COM smb_node_destroy_lock_by_ofile(smb_node_t *node, smb_ofile_t *file) 2757348SJose.Borrego@Sun.COM { 2767348SJose.Borrego@Sun.COM smb_lock_t *lock; 2777348SJose.Borrego@Sun.COM smb_lock_t *nxtl; 2787348SJose.Borrego@Sun.COM list_t destroy_list; 2797348SJose.Borrego@Sun.COM 2808934SJose.Borrego@Sun.COM SMB_NODE_VALID(node); 2817348SJose.Borrego@Sun.COM ASSERT(node->n_refcnt); 2827348SJose.Borrego@Sun.COM 2837348SJose.Borrego@Sun.COM /* 2847348SJose.Borrego@Sun.COM * Move locks matching the specified file from the node->n_lock_list 2857348SJose.Borrego@Sun.COM * to a temporary list (holding the lock the entire time) then 2867348SJose.Borrego@Sun.COM * destroy all the matching locks. We can't call smb_lock_destroy 2877348SJose.Borrego@Sun.COM * while we are holding the lock for node->n_lock_list because we will 2887348SJose.Borrego@Sun.COM * deadlock and we can't drop the lock because the list contents might 2897348SJose.Borrego@Sun.COM * change (for example nxtl might get removed on another thread). 2907348SJose.Borrego@Sun.COM */ 2917348SJose.Borrego@Sun.COM list_create(&destroy_list, sizeof (smb_lock_t), 2927348SJose.Borrego@Sun.COM offsetof(smb_lock_t, l_lnd)); 2937348SJose.Borrego@Sun.COM 2947348SJose.Borrego@Sun.COM smb_llist_enter(&node->n_lock_list, RW_WRITER); 2957348SJose.Borrego@Sun.COM lock = smb_llist_head(&node->n_lock_list); 2967348SJose.Borrego@Sun.COM while (lock) { 2977348SJose.Borrego@Sun.COM nxtl = smb_llist_next(&node->n_lock_list, lock); 2987348SJose.Borrego@Sun.COM if (lock->l_file == file) { 2997348SJose.Borrego@Sun.COM smb_llist_remove(&node->n_lock_list, lock); 3007348SJose.Borrego@Sun.COM smb_lock_posix_unlock(node, lock, file->f_user->u_cred); 3017348SJose.Borrego@Sun.COM list_insert_tail(&destroy_list, lock); 3027348SJose.Borrego@Sun.COM } 3037348SJose.Borrego@Sun.COM lock = nxtl; 3047348SJose.Borrego@Sun.COM } 3057348SJose.Borrego@Sun.COM smb_llist_exit(&node->n_lock_list); 3067348SJose.Borrego@Sun.COM 3077348SJose.Borrego@Sun.COM lock = list_head(&destroy_list); 3087348SJose.Borrego@Sun.COM while (lock) { 3097348SJose.Borrego@Sun.COM nxtl = list_next(&destroy_list, lock); 3107348SJose.Borrego@Sun.COM list_remove(&destroy_list, lock); 3117348SJose.Borrego@Sun.COM smb_lock_destroy(lock); 3127348SJose.Borrego@Sun.COM lock = nxtl; 3137348SJose.Borrego@Sun.COM } 3147348SJose.Borrego@Sun.COM 3157348SJose.Borrego@Sun.COM list_destroy(&destroy_list); 3167348SJose.Borrego@Sun.COM } 3177348SJose.Borrego@Sun.COM 3187348SJose.Borrego@Sun.COM void 3197348SJose.Borrego@Sun.COM smb_lock_range_error(smb_request_t *sr, uint32_t status32) 3207348SJose.Borrego@Sun.COM { 3217348SJose.Borrego@Sun.COM uint16_t errcode; 3227348SJose.Borrego@Sun.COM 3237348SJose.Borrego@Sun.COM if (status32 == NT_STATUS_CANCELLED) 3247348SJose.Borrego@Sun.COM errcode = ERROR_OPERATION_ABORTED; 3257348SJose.Borrego@Sun.COM else 3267348SJose.Borrego@Sun.COM errcode = ERRlock; 3277348SJose.Borrego@Sun.COM 3287348SJose.Borrego@Sun.COM smbsr_error(sr, status32, ERRDOS, errcode); 3297348SJose.Borrego@Sun.COM } 3307348SJose.Borrego@Sun.COM 3317348SJose.Borrego@Sun.COM /* 3327348SJose.Borrego@Sun.COM * smb_range_check() 3337348SJose.Borrego@Sun.COM * 3347348SJose.Borrego@Sun.COM * Perform range checking. First check for internal CIFS range conflicts 3357348SJose.Borrego@Sun.COM * and then check for external conflicts, for example, with NFS or local 3367348SJose.Borrego@Sun.COM * access. 3377348SJose.Borrego@Sun.COM * 3387348SJose.Borrego@Sun.COM * If nbmand is enabled, this function must be called from within an nbmand 3397348SJose.Borrego@Sun.COM * critical region 3407348SJose.Borrego@Sun.COM */ 3417348SJose.Borrego@Sun.COM 3427348SJose.Borrego@Sun.COM DWORD 3437348SJose.Borrego@Sun.COM smb_range_check(smb_request_t *sr, smb_node_t *node, uint64_t start, 3447348SJose.Borrego@Sun.COM uint64_t length, boolean_t will_write) 3457348SJose.Borrego@Sun.COM { 3467348SJose.Borrego@Sun.COM smb_error_t smberr; 3477348SJose.Borrego@Sun.COM int svmand; 3487348SJose.Borrego@Sun.COM int nbl_op; 3497348SJose.Borrego@Sun.COM int rc; 3507348SJose.Borrego@Sun.COM 3518934SJose.Borrego@Sun.COM SMB_NODE_VALID(node); 3527348SJose.Borrego@Sun.COM 3537348SJose.Borrego@Sun.COM ASSERT(smb_node_in_crit(node)); 3547348SJose.Borrego@Sun.COM 355*10001SJoyce.McIntosh@Sun.COM if (smb_node_is_dir(node)) 3567348SJose.Borrego@Sun.COM return (NT_STATUS_SUCCESS); 3577348SJose.Borrego@Sun.COM 3587348SJose.Borrego@Sun.COM rc = smb_lock_range_access(sr, node, start, length, will_write); 3597348SJose.Borrego@Sun.COM if (rc) 3607348SJose.Borrego@Sun.COM return (NT_STATUS_FILE_LOCK_CONFLICT); 3617348SJose.Borrego@Sun.COM 3627348SJose.Borrego@Sun.COM if ((rc = nbl_svmand(node->vp, kcred, &svmand)) != 0) { 3637348SJose.Borrego@Sun.COM smbsr_map_errno(rc, &smberr); 3647348SJose.Borrego@Sun.COM return (smberr.status); 3657348SJose.Borrego@Sun.COM } 3667348SJose.Borrego@Sun.COM 3677348SJose.Borrego@Sun.COM nbl_op = (will_write) ? NBL_WRITE : NBL_READ; 3687348SJose.Borrego@Sun.COM 3697348SJose.Borrego@Sun.COM if (nbl_lock_conflict(node->vp, nbl_op, start, length, svmand, &smb_ct)) 3707348SJose.Borrego@Sun.COM return (NT_STATUS_FILE_LOCK_CONFLICT); 3717348SJose.Borrego@Sun.COM 3727348SJose.Borrego@Sun.COM return (NT_STATUS_SUCCESS); 3737348SJose.Borrego@Sun.COM } 3747348SJose.Borrego@Sun.COM 3757348SJose.Borrego@Sun.COM /* 3767348SJose.Borrego@Sun.COM * smb_lock_posix_unlock 3777348SJose.Borrego@Sun.COM * 3787348SJose.Borrego@Sun.COM * checks if the current unlock request is in another lock and repeatedly calls 3797348SJose.Borrego@Sun.COM * smb_is_range_unlocked on a sliding basis to unlock all bits of the lock 3807348SJose.Borrego@Sun.COM * that are not in other locks 3817348SJose.Borrego@Sun.COM * 3827348SJose.Borrego@Sun.COM */ 3837348SJose.Borrego@Sun.COM static void 3847348SJose.Borrego@Sun.COM smb_lock_posix_unlock(smb_node_t *node, smb_lock_t *lock, cred_t *cr) 3857348SJose.Borrego@Sun.COM { 3867348SJose.Borrego@Sun.COM uint64_t new_mark; 3877348SJose.Borrego@Sun.COM uint64_t unlock_start; 3887348SJose.Borrego@Sun.COM uint64_t unlock_end; 3897348SJose.Borrego@Sun.COM smb_lock_t new_unlock; 3907348SJose.Borrego@Sun.COM smb_llist_t *llist; 3917348SJose.Borrego@Sun.COM boolean_t can_unlock; 3927348SJose.Borrego@Sun.COM 3937348SJose.Borrego@Sun.COM new_mark = 0; 3947348SJose.Borrego@Sun.COM unlock_start = lock->l_start; 3957348SJose.Borrego@Sun.COM unlock_end = unlock_start + lock->l_length; 3967348SJose.Borrego@Sun.COM llist = &node->n_lock_list; 3977348SJose.Borrego@Sun.COM 3987348SJose.Borrego@Sun.COM for (;;) { 3997348SJose.Borrego@Sun.COM can_unlock = smb_is_range_unlocked(unlock_start, unlock_end, 4007348SJose.Borrego@Sun.COM lock->l_file->f_uniqid, llist, &new_mark); 4017348SJose.Borrego@Sun.COM if (can_unlock) { 4027348SJose.Borrego@Sun.COM if (new_mark) { 4037348SJose.Borrego@Sun.COM new_unlock = *lock; 4047348SJose.Borrego@Sun.COM new_unlock.l_start = unlock_start; 4057348SJose.Borrego@Sun.COM new_unlock.l_length = new_mark - unlock_start; 4067348SJose.Borrego@Sun.COM (void) smb_fsop_frlock(node, &new_unlock, 4077348SJose.Borrego@Sun.COM B_TRUE, cr); 4087348SJose.Borrego@Sun.COM unlock_start = new_mark; 4097348SJose.Borrego@Sun.COM } else { 4107348SJose.Borrego@Sun.COM new_unlock = *lock; 4117348SJose.Borrego@Sun.COM new_unlock.l_start = unlock_start; 4127348SJose.Borrego@Sun.COM new_unlock.l_length = unlock_end - unlock_start; 4137348SJose.Borrego@Sun.COM (void) smb_fsop_frlock(node, &new_unlock, 4147348SJose.Borrego@Sun.COM B_TRUE, cr); 4157348SJose.Borrego@Sun.COM break; 4167348SJose.Borrego@Sun.COM } 4177348SJose.Borrego@Sun.COM } else if (new_mark) { 4187348SJose.Borrego@Sun.COM unlock_start = new_mark; 4197348SJose.Borrego@Sun.COM } else { 4207348SJose.Borrego@Sun.COM break; 4217348SJose.Borrego@Sun.COM } 4227348SJose.Borrego@Sun.COM } 4237348SJose.Borrego@Sun.COM } 4247348SJose.Borrego@Sun.COM 4257348SJose.Borrego@Sun.COM /* 4267348SJose.Borrego@Sun.COM * smb_lock_range_overlap 4277348SJose.Borrego@Sun.COM * 4287348SJose.Borrego@Sun.COM * Checks if lock range(start, length) overlaps range in lock structure. 4297348SJose.Borrego@Sun.COM * 4307348SJose.Borrego@Sun.COM * Zero-length byte range locks actually affect no single byte of the stream, 4317348SJose.Borrego@Sun.COM * meaning they can still be accessed even with such locks in place. However, 4327348SJose.Borrego@Sun.COM * they do conflict with other ranges in the following manner: 4337348SJose.Borrego@Sun.COM * conflict will only exist if the positive-length range contains the 4347348SJose.Borrego@Sun.COM * zero-length range's offset but doesn't start at it 4357348SJose.Borrego@Sun.COM * 4367348SJose.Borrego@Sun.COM * return values: 4377348SJose.Borrego@Sun.COM * 0 - Lock range doesn't overlap 4387348SJose.Borrego@Sun.COM * 1 - Lock range overlaps. 4397348SJose.Borrego@Sun.COM */ 4407348SJose.Borrego@Sun.COM 4417348SJose.Borrego@Sun.COM #define RANGE_NO_OVERLAP 0 4427348SJose.Borrego@Sun.COM #define RANGE_OVERLAP 1 4437348SJose.Borrego@Sun.COM 4447348SJose.Borrego@Sun.COM static int 4457348SJose.Borrego@Sun.COM smb_lock_range_overlap(struct smb_lock *lock, uint64_t start, uint64_t length) 4467348SJose.Borrego@Sun.COM { 4477348SJose.Borrego@Sun.COM if (length == 0) { 4487348SJose.Borrego@Sun.COM if ((lock->l_start < start) && 4497348SJose.Borrego@Sun.COM ((lock->l_start + lock->l_length) > start)) 4507348SJose.Borrego@Sun.COM return (RANGE_OVERLAP); 4517348SJose.Borrego@Sun.COM 4527348SJose.Borrego@Sun.COM return (RANGE_NO_OVERLAP); 4537348SJose.Borrego@Sun.COM } 4547348SJose.Borrego@Sun.COM 4557348SJose.Borrego@Sun.COM /* The following test is intended to catch roll over locks. */ 4567348SJose.Borrego@Sun.COM if ((start == lock->l_start) && (length == lock->l_length)) 4577348SJose.Borrego@Sun.COM return (RANGE_OVERLAP); 4587348SJose.Borrego@Sun.COM 4597348SJose.Borrego@Sun.COM if (start < lock->l_start) { 4607348SJose.Borrego@Sun.COM if (start + length > lock->l_start) 4617348SJose.Borrego@Sun.COM return (RANGE_OVERLAP); 4627348SJose.Borrego@Sun.COM } else if (start < lock->l_start + lock->l_length) 4637348SJose.Borrego@Sun.COM return (RANGE_OVERLAP); 4647348SJose.Borrego@Sun.COM 4657348SJose.Borrego@Sun.COM return (RANGE_NO_OVERLAP); 4667348SJose.Borrego@Sun.COM } 4677348SJose.Borrego@Sun.COM 4687348SJose.Borrego@Sun.COM /* 4697348SJose.Borrego@Sun.COM * smb_lock_range_lckrules 4707348SJose.Borrego@Sun.COM * 4717348SJose.Borrego@Sun.COM * Lock range rules: 4727348SJose.Borrego@Sun.COM * 1. Overlapping read locks are allowed if the 4737348SJose.Borrego@Sun.COM * current locks in the region are only read locks 4747348SJose.Borrego@Sun.COM * irrespective of pid of smb client issuing lock request. 4757348SJose.Borrego@Sun.COM * 4767348SJose.Borrego@Sun.COM * 2. Read lock in the overlapped region of write lock 4777348SJose.Borrego@Sun.COM * are allowed if the pervious lock is performed by the 4787348SJose.Borrego@Sun.COM * same pid and connection. 4797348SJose.Borrego@Sun.COM * 4807348SJose.Borrego@Sun.COM * return status: 4817348SJose.Borrego@Sun.COM * NT_STATUS_SUCCESS - Input lock range adapts to lock rules. 4827348SJose.Borrego@Sun.COM * NT_STATUS_LOCK_NOT_GRANTED - Input lock conflicts lock rules. 4837348SJose.Borrego@Sun.COM * NT_STATUS_CANCELLED - Error in processing lock rules 4847348SJose.Borrego@Sun.COM */ 4857348SJose.Borrego@Sun.COM static uint32_t 4867348SJose.Borrego@Sun.COM smb_lock_range_lckrules( 4877348SJose.Borrego@Sun.COM smb_request_t *sr, 4887348SJose.Borrego@Sun.COM smb_ofile_t *file, 4897348SJose.Borrego@Sun.COM smb_node_t *node, 4907348SJose.Borrego@Sun.COM smb_lock_t *dlock, 4917348SJose.Borrego@Sun.COM smb_lock_t **clockp) 4927348SJose.Borrego@Sun.COM { 4937348SJose.Borrego@Sun.COM smb_lock_t *lock; 4947348SJose.Borrego@Sun.COM uint32_t status = NT_STATUS_SUCCESS; 4957348SJose.Borrego@Sun.COM 4967348SJose.Borrego@Sun.COM /* Check if file is closed */ 4977348SJose.Borrego@Sun.COM if (!smb_ofile_is_open(file)) { 4987348SJose.Borrego@Sun.COM return (NT_STATUS_RANGE_NOT_LOCKED); 4997348SJose.Borrego@Sun.COM } 5007348SJose.Borrego@Sun.COM 5017348SJose.Borrego@Sun.COM /* Caller must hold lock for node->n_lock_list */ 5027348SJose.Borrego@Sun.COM for (lock = smb_llist_head(&node->n_lock_list); 5037348SJose.Borrego@Sun.COM lock != NULL; 5047348SJose.Borrego@Sun.COM lock = smb_llist_next(&node->n_lock_list, lock)) { 5057348SJose.Borrego@Sun.COM 5067348SJose.Borrego@Sun.COM if (!smb_lock_range_overlap(lock, dlock->l_start, 5077348SJose.Borrego@Sun.COM dlock->l_length)) 5087348SJose.Borrego@Sun.COM continue; 5097348SJose.Borrego@Sun.COM 5107348SJose.Borrego@Sun.COM /* 5117348SJose.Borrego@Sun.COM * Check to see if lock in the overlapping record 5127348SJose.Borrego@Sun.COM * is only read lock. Current finding is read 5137348SJose.Borrego@Sun.COM * locks can overlapped irrespective of pids. 5147348SJose.Borrego@Sun.COM */ 5157348SJose.Borrego@Sun.COM if ((lock->l_type == SMB_LOCK_TYPE_READONLY) && 5167348SJose.Borrego@Sun.COM (dlock->l_type == SMB_LOCK_TYPE_READONLY)) { 5177348SJose.Borrego@Sun.COM continue; 5187348SJose.Borrego@Sun.COM } 5197348SJose.Borrego@Sun.COM 5207348SJose.Borrego@Sun.COM /* 5217348SJose.Borrego@Sun.COM * When the read lock overlaps write lock, check if 5227348SJose.Borrego@Sun.COM * allowed. 5237348SJose.Borrego@Sun.COM */ 5247348SJose.Borrego@Sun.COM if ((dlock->l_type == SMB_LOCK_TYPE_READONLY) && 5257348SJose.Borrego@Sun.COM !(lock->l_type == SMB_LOCK_TYPE_READONLY)) { 5267348SJose.Borrego@Sun.COM if (lock->l_file == sr->fid_ofile && 5277348SJose.Borrego@Sun.COM lock->l_session_kid == sr->session->s_kid && 5287348SJose.Borrego@Sun.COM lock->l_pid == sr->smb_pid && 5297348SJose.Borrego@Sun.COM lock->l_uid == sr->smb_uid) { 5307348SJose.Borrego@Sun.COM continue; 5317348SJose.Borrego@Sun.COM } 5327348SJose.Borrego@Sun.COM } 5337348SJose.Borrego@Sun.COM 5347348SJose.Borrego@Sun.COM /* Conflict in overlapping lock element */ 5357348SJose.Borrego@Sun.COM *clockp = lock; 5367348SJose.Borrego@Sun.COM status = NT_STATUS_LOCK_NOT_GRANTED; 5377348SJose.Borrego@Sun.COM break; 5387348SJose.Borrego@Sun.COM } 5397348SJose.Borrego@Sun.COM 5407348SJose.Borrego@Sun.COM return (status); 5417348SJose.Borrego@Sun.COM } 5427348SJose.Borrego@Sun.COM 5437348SJose.Borrego@Sun.COM /* 5447348SJose.Borrego@Sun.COM * smb_lock_wait 5457348SJose.Borrego@Sun.COM * 5467348SJose.Borrego@Sun.COM * Wait operation for smb overlapping lock to be released. Caller must hold 5477348SJose.Borrego@Sun.COM * write lock for node->n_lock_list so that the set of active locks can't 5487348SJose.Borrego@Sun.COM * change unexpectedly. The lock for node->n_lock_list will be released 5497348SJose.Borrego@Sun.COM * within this function during the sleep after the lock dependency has 5507348SJose.Borrego@Sun.COM * been recorded. 5517348SJose.Borrego@Sun.COM * 5527348SJose.Borrego@Sun.COM * return value 5537348SJose.Borrego@Sun.COM * 5547348SJose.Borrego@Sun.COM * 0 The request was canceled. 5557348SJose.Borrego@Sun.COM * -1 The timeout was reached. 5567348SJose.Borrego@Sun.COM * >0 Condition met. 5577348SJose.Borrego@Sun.COM */ 5587348SJose.Borrego@Sun.COM static clock_t 5597348SJose.Borrego@Sun.COM smb_lock_wait(smb_request_t *sr, smb_lock_t *b_lock, smb_lock_t *c_lock) 5607348SJose.Borrego@Sun.COM { 5617348SJose.Borrego@Sun.COM clock_t rc; 5627348SJose.Borrego@Sun.COM 5637348SJose.Borrego@Sun.COM ASSERT(sr->sr_awaiting == NULL); 5647348SJose.Borrego@Sun.COM 5657348SJose.Borrego@Sun.COM mutex_enter(&sr->sr_mutex); 5667348SJose.Borrego@Sun.COM 5677348SJose.Borrego@Sun.COM switch (sr->sr_state) { 5687348SJose.Borrego@Sun.COM case SMB_REQ_STATE_ACTIVE: 5697348SJose.Borrego@Sun.COM /* 5707348SJose.Borrego@Sun.COM * Wait up till the timeout time keeping track of actual 5717348SJose.Borrego@Sun.COM * time waited for possible retry failure. 5727348SJose.Borrego@Sun.COM */ 5737348SJose.Borrego@Sun.COM sr->sr_state = SMB_REQ_STATE_WAITING_LOCK; 5747348SJose.Borrego@Sun.COM sr->sr_awaiting = c_lock; 5757348SJose.Borrego@Sun.COM mutex_exit(&sr->sr_mutex); 5767348SJose.Borrego@Sun.COM 5777348SJose.Borrego@Sun.COM mutex_enter(&c_lock->l_mutex); 5787348SJose.Borrego@Sun.COM /* 5797348SJose.Borrego@Sun.COM * The conflict list (l_conflict_list) for a lock contains 5807348SJose.Borrego@Sun.COM * all the locks that are blocked by and in conflict with 5817348SJose.Borrego@Sun.COM * that lock. Add the new lock to the conflict list for the 5827348SJose.Borrego@Sun.COM * active lock. 5837348SJose.Borrego@Sun.COM * 5847348SJose.Borrego@Sun.COM * l_conflict_list is currently a fancy way of representing 5857348SJose.Borrego@Sun.COM * the references/dependencies on a lock. It could be 5867348SJose.Borrego@Sun.COM * replaced with a reference count but this approach 5877348SJose.Borrego@Sun.COM * has the advantage that MDB can display the lock 5887348SJose.Borrego@Sun.COM * dependencies at any point in time. In the future 5897348SJose.Borrego@Sun.COM * we should be able to leverage the list to implement 5907348SJose.Borrego@Sun.COM * an asynchronous locking model. 5917348SJose.Borrego@Sun.COM * 5927348SJose.Borrego@Sun.COM * l_blocked_by is the reverse of the conflict list. It 5937348SJose.Borrego@Sun.COM * points to the lock that the new lock conflicts with. 5947348SJose.Borrego@Sun.COM * As currently implemented this value is purely for 5957348SJose.Borrego@Sun.COM * debug purposes -- there are windows of time when 5967348SJose.Borrego@Sun.COM * l_blocked_by may be non-NULL even though there is no 5977348SJose.Borrego@Sun.COM * conflict list 5987348SJose.Borrego@Sun.COM */ 5997348SJose.Borrego@Sun.COM b_lock->l_blocked_by = c_lock; 6007348SJose.Borrego@Sun.COM smb_slist_insert_tail(&c_lock->l_conflict_list, b_lock); 6017348SJose.Borrego@Sun.COM smb_llist_exit(&c_lock->l_file->f_node->n_lock_list); 6027348SJose.Borrego@Sun.COM 6037348SJose.Borrego@Sun.COM if (SMB_LOCK_INDEFINITE_WAIT(b_lock)) { 6047348SJose.Borrego@Sun.COM cv_wait(&c_lock->l_cv, &c_lock->l_mutex); 6057348SJose.Borrego@Sun.COM } else { 6067348SJose.Borrego@Sun.COM rc = cv_timedwait(&c_lock->l_cv, 6077348SJose.Borrego@Sun.COM &c_lock->l_mutex, b_lock->l_end_time); 6087348SJose.Borrego@Sun.COM } 6097348SJose.Borrego@Sun.COM 6107348SJose.Borrego@Sun.COM mutex_exit(&c_lock->l_mutex); 6117348SJose.Borrego@Sun.COM 6127348SJose.Borrego@Sun.COM smb_llist_enter(&c_lock->l_file->f_node->n_lock_list, 6137348SJose.Borrego@Sun.COM RW_WRITER); 6147348SJose.Borrego@Sun.COM smb_slist_remove(&c_lock->l_conflict_list, b_lock); 6157348SJose.Borrego@Sun.COM 6167348SJose.Borrego@Sun.COM mutex_enter(&sr->sr_mutex); 6177348SJose.Borrego@Sun.COM sr->sr_awaiting = NULL; 6187348SJose.Borrego@Sun.COM if (sr->sr_state == SMB_REQ_STATE_CANCELED) { 6197348SJose.Borrego@Sun.COM rc = 0; 6207348SJose.Borrego@Sun.COM } else { 6217348SJose.Borrego@Sun.COM sr->sr_state = SMB_REQ_STATE_ACTIVE; 6227348SJose.Borrego@Sun.COM } 6237348SJose.Borrego@Sun.COM break; 6247348SJose.Borrego@Sun.COM 6257348SJose.Borrego@Sun.COM default: 6267348SJose.Borrego@Sun.COM ASSERT(sr->sr_state == SMB_REQ_STATE_CANCELED); 6277348SJose.Borrego@Sun.COM rc = 0; 6287348SJose.Borrego@Sun.COM break; 6297348SJose.Borrego@Sun.COM } 6307348SJose.Borrego@Sun.COM mutex_exit(&sr->sr_mutex); 6317348SJose.Borrego@Sun.COM 6327348SJose.Borrego@Sun.COM return (rc); 6337348SJose.Borrego@Sun.COM } 6347348SJose.Borrego@Sun.COM 6357348SJose.Borrego@Sun.COM /* 6367348SJose.Borrego@Sun.COM * smb_lock_range_ulckrules 6377348SJose.Borrego@Sun.COM * 6387348SJose.Borrego@Sun.COM * 1. Unlock should be performed at exactly matching ends. 6397348SJose.Borrego@Sun.COM * This has been changed because overlapping ends is 6407348SJose.Borrego@Sun.COM * allowed and there is no other precise way of locating 6417348SJose.Borrego@Sun.COM * lock entity in node lock list. 6427348SJose.Borrego@Sun.COM * 6437348SJose.Borrego@Sun.COM * 2. Unlock is failed if there is no corresponding lock exists. 6447348SJose.Borrego@Sun.COM * 6457348SJose.Borrego@Sun.COM * Return values 6467348SJose.Borrego@Sun.COM * 6477348SJose.Borrego@Sun.COM * NT_STATUS_SUCCESS Unlock request matches lock record 6487348SJose.Borrego@Sun.COM * pointed by 'nodelock' lock structure. 6497348SJose.Borrego@Sun.COM * 6507348SJose.Borrego@Sun.COM * NT_STATUS_RANGE_NOT_LOCKED Unlock request doen't match any 6517348SJose.Borrego@Sun.COM * of lock record in node lock request or 6527348SJose.Borrego@Sun.COM * error in unlock range processing. 6537348SJose.Borrego@Sun.COM */ 6547348SJose.Borrego@Sun.COM static uint32_t 6557348SJose.Borrego@Sun.COM smb_lock_range_ulckrules( 6567348SJose.Borrego@Sun.COM smb_request_t *sr, 6577348SJose.Borrego@Sun.COM smb_node_t *node, 6587348SJose.Borrego@Sun.COM uint64_t start, 6597348SJose.Borrego@Sun.COM uint64_t length, 6607348SJose.Borrego@Sun.COM smb_lock_t **nodelock) 6617348SJose.Borrego@Sun.COM { 6627348SJose.Borrego@Sun.COM smb_lock_t *lock; 6637348SJose.Borrego@Sun.COM uint32_t status = NT_STATUS_RANGE_NOT_LOCKED; 6647348SJose.Borrego@Sun.COM 6657348SJose.Borrego@Sun.COM /* Caller must hold lock for node->n_lock_list */ 6667348SJose.Borrego@Sun.COM for (lock = smb_llist_head(&node->n_lock_list); 6677348SJose.Borrego@Sun.COM lock != NULL; 6687348SJose.Borrego@Sun.COM lock = smb_llist_next(&node->n_lock_list, lock)) { 6697348SJose.Borrego@Sun.COM 6707348SJose.Borrego@Sun.COM if ((start == lock->l_start) && 6717348SJose.Borrego@Sun.COM (length == lock->l_length) && 6727348SJose.Borrego@Sun.COM lock->l_file == sr->fid_ofile && 6737348SJose.Borrego@Sun.COM lock->l_session_kid == sr->session->s_kid && 6747348SJose.Borrego@Sun.COM lock->l_pid == sr->smb_pid && 6757348SJose.Borrego@Sun.COM lock->l_uid == sr->smb_uid) { 6767348SJose.Borrego@Sun.COM *nodelock = lock; 6777348SJose.Borrego@Sun.COM status = NT_STATUS_SUCCESS; 6787348SJose.Borrego@Sun.COM break; 6797348SJose.Borrego@Sun.COM } 6807348SJose.Borrego@Sun.COM } 6817348SJose.Borrego@Sun.COM 6827348SJose.Borrego@Sun.COM return (status); 6837348SJose.Borrego@Sun.COM } 6847348SJose.Borrego@Sun.COM 6857348SJose.Borrego@Sun.COM static smb_lock_t * 6867348SJose.Borrego@Sun.COM smb_lock_create( 6877348SJose.Borrego@Sun.COM smb_request_t *sr, 6887348SJose.Borrego@Sun.COM uint64_t start, 6897348SJose.Borrego@Sun.COM uint64_t length, 6907348SJose.Borrego@Sun.COM uint32_t locktype, 6917348SJose.Borrego@Sun.COM uint32_t timeout) 6927348SJose.Borrego@Sun.COM { 6937348SJose.Borrego@Sun.COM smb_lock_t *lock; 6947348SJose.Borrego@Sun.COM 6957348SJose.Borrego@Sun.COM ASSERT(locktype == SMB_LOCK_TYPE_READWRITE || 6967348SJose.Borrego@Sun.COM locktype == SMB_LOCK_TYPE_READONLY); 6977348SJose.Borrego@Sun.COM 6987348SJose.Borrego@Sun.COM lock = kmem_zalloc(sizeof (smb_lock_t), KM_SLEEP); 6997348SJose.Borrego@Sun.COM lock->l_magic = SMB_LOCK_MAGIC; 7007348SJose.Borrego@Sun.COM lock->l_sr = sr; /* Invalid after lock is active */ 7017348SJose.Borrego@Sun.COM lock->l_session_kid = sr->session->s_kid; 7027348SJose.Borrego@Sun.COM lock->l_session = sr->session; 7037348SJose.Borrego@Sun.COM lock->l_file = sr->fid_ofile; 7047348SJose.Borrego@Sun.COM lock->l_uid = sr->smb_uid; 7057348SJose.Borrego@Sun.COM lock->l_pid = sr->smb_pid; 7067348SJose.Borrego@Sun.COM lock->l_type = locktype; 7077348SJose.Borrego@Sun.COM lock->l_start = start; 7087348SJose.Borrego@Sun.COM lock->l_length = length; 7097348SJose.Borrego@Sun.COM /* 7107348SJose.Borrego@Sun.COM * Calculate the absolute end time so that we can use it 7117348SJose.Borrego@Sun.COM * in cv_timedwait. 7127348SJose.Borrego@Sun.COM */ 7137348SJose.Borrego@Sun.COM lock->l_end_time = lbolt + MSEC_TO_TICK(timeout); 7147348SJose.Borrego@Sun.COM if (timeout == UINT_MAX) 7157348SJose.Borrego@Sun.COM lock->l_flags |= SMB_LOCK_FLAG_INDEFINITE; 7167348SJose.Borrego@Sun.COM 7177348SJose.Borrego@Sun.COM mutex_init(&lock->l_mutex, NULL, MUTEX_DEFAULT, NULL); 7187348SJose.Borrego@Sun.COM cv_init(&lock->l_cv, NULL, CV_DEFAULT, NULL); 7197348SJose.Borrego@Sun.COM smb_slist_constructor(&lock->l_conflict_list, sizeof (smb_lock_t), 7207348SJose.Borrego@Sun.COM offsetof(smb_lock_t, l_conflict_lnd)); 7217348SJose.Borrego@Sun.COM 7227348SJose.Borrego@Sun.COM return (lock); 7237348SJose.Borrego@Sun.COM } 7247348SJose.Borrego@Sun.COM 7257348SJose.Borrego@Sun.COM static void 7267348SJose.Borrego@Sun.COM smb_lock_free(smb_lock_t *lock) 7277348SJose.Borrego@Sun.COM { 7287348SJose.Borrego@Sun.COM smb_slist_destructor(&lock->l_conflict_list); 7297348SJose.Borrego@Sun.COM cv_destroy(&lock->l_cv); 7307348SJose.Borrego@Sun.COM mutex_destroy(&lock->l_mutex); 7317348SJose.Borrego@Sun.COM 7327348SJose.Borrego@Sun.COM kmem_free(lock, sizeof (smb_lock_t)); 7337348SJose.Borrego@Sun.COM } 7347348SJose.Borrego@Sun.COM 7357348SJose.Borrego@Sun.COM /* 7367348SJose.Borrego@Sun.COM * smb_lock_destroy 7377348SJose.Borrego@Sun.COM * 7387348SJose.Borrego@Sun.COM * Caller must hold node->n_lock_list 7397348SJose.Borrego@Sun.COM */ 7407348SJose.Borrego@Sun.COM static void 7417348SJose.Borrego@Sun.COM smb_lock_destroy(smb_lock_t *lock) 7427348SJose.Borrego@Sun.COM { 7437348SJose.Borrego@Sun.COM /* 7447348SJose.Borrego@Sun.COM * Caller must hold node->n_lock_list lock. 7457348SJose.Borrego@Sun.COM */ 7467348SJose.Borrego@Sun.COM mutex_enter(&lock->l_mutex); 7477348SJose.Borrego@Sun.COM cv_broadcast(&lock->l_cv); 7487348SJose.Borrego@Sun.COM mutex_exit(&lock->l_mutex); 7497348SJose.Borrego@Sun.COM 7507348SJose.Borrego@Sun.COM /* 7517348SJose.Borrego@Sun.COM * The cv_broadcast above should wake up any locks that previous 7527348SJose.Borrego@Sun.COM * had conflicts with this lock. Wait for the locking threads 7537348SJose.Borrego@Sun.COM * to remove their references to this lock. 7547348SJose.Borrego@Sun.COM */ 7557348SJose.Borrego@Sun.COM smb_slist_wait_for_empty(&lock->l_conflict_list); 7567348SJose.Borrego@Sun.COM 7577348SJose.Borrego@Sun.COM smb_lock_free(lock); 7587348SJose.Borrego@Sun.COM } 7597348SJose.Borrego@Sun.COM 7607348SJose.Borrego@Sun.COM /* 7617348SJose.Borrego@Sun.COM * smb_is_range_unlocked 7627348SJose.Borrego@Sun.COM * 7637348SJose.Borrego@Sun.COM * Checks if the current unlock byte range request overlaps another lock 7647348SJose.Borrego@Sun.COM * This function is used to determine where POSIX unlocks should be 7657348SJose.Borrego@Sun.COM * applied. 7667348SJose.Borrego@Sun.COM * 7677348SJose.Borrego@Sun.COM * The return code and the value of new_mark must be interpreted as 7687348SJose.Borrego@Sun.COM * follows: 7697348SJose.Borrego@Sun.COM * 7707348SJose.Borrego@Sun.COM * B_TRUE and (new_mark == 0): 7717348SJose.Borrego@Sun.COM * This is the last or only lock left to be unlocked 7727348SJose.Borrego@Sun.COM * 7737348SJose.Borrego@Sun.COM * B_TRUE and (new_mark > 0): 7747348SJose.Borrego@Sun.COM * The range from start to new_mark can be unlocked 7757348SJose.Borrego@Sun.COM * 7767348SJose.Borrego@Sun.COM * B_FALSE and (new_mark == 0): 7777348SJose.Borrego@Sun.COM * The unlock can't be performed and we are done 7787348SJose.Borrego@Sun.COM * 7797348SJose.Borrego@Sun.COM * B_FALSE and (new_mark > 0), 7807348SJose.Borrego@Sun.COM * The range from start to new_mark can't be unlocked 7817348SJose.Borrego@Sun.COM * Start should be reset to new_mark for the next pass 7827348SJose.Borrego@Sun.COM */ 7837348SJose.Borrego@Sun.COM 7847348SJose.Borrego@Sun.COM static boolean_t 7857348SJose.Borrego@Sun.COM smb_is_range_unlocked(uint64_t start, uint64_t end, uint32_t uniqid, 7867348SJose.Borrego@Sun.COM smb_llist_t *llist_head, uint64_t *new_mark) 7877348SJose.Borrego@Sun.COM { 7887348SJose.Borrego@Sun.COM struct smb_lock *lk = NULL; 7897348SJose.Borrego@Sun.COM uint64_t low_water_mark = MAXOFFSET_T; 7907348SJose.Borrego@Sun.COM uint64_t lk_start; 7917348SJose.Borrego@Sun.COM uint64_t lk_end; 7927348SJose.Borrego@Sun.COM 7937348SJose.Borrego@Sun.COM *new_mark = 0; 7947348SJose.Borrego@Sun.COM lk = smb_llist_head(llist_head); 7957348SJose.Borrego@Sun.COM while (lk) { 7967348SJose.Borrego@Sun.COM if (lk->l_length == 0) { 7977348SJose.Borrego@Sun.COM lk = smb_llist_next(llist_head, lk); 7987348SJose.Borrego@Sun.COM continue; 7997348SJose.Borrego@Sun.COM } 8007348SJose.Borrego@Sun.COM 8017348SJose.Borrego@Sun.COM if (lk->l_file->f_uniqid != uniqid) { 8027348SJose.Borrego@Sun.COM lk = smb_llist_next(llist_head, lk); 8037348SJose.Borrego@Sun.COM continue; 8047348SJose.Borrego@Sun.COM } 8057348SJose.Borrego@Sun.COM 8067348SJose.Borrego@Sun.COM lk_end = lk->l_start + lk->l_length - 1; 8077348SJose.Borrego@Sun.COM lk_start = lk->l_start; 8087348SJose.Borrego@Sun.COM 8097348SJose.Borrego@Sun.COM /* 8107348SJose.Borrego@Sun.COM * there is no overlap for the first 2 cases 8117348SJose.Borrego@Sun.COM * check next node 8127348SJose.Borrego@Sun.COM */ 8137348SJose.Borrego@Sun.COM if (lk_end < start) { 8147348SJose.Borrego@Sun.COM lk = smb_llist_next(llist_head, lk); 8157348SJose.Borrego@Sun.COM continue; 8167348SJose.Borrego@Sun.COM } 8177348SJose.Borrego@Sun.COM if (lk_start > end) { 8187348SJose.Borrego@Sun.COM lk = smb_llist_next(llist_head, lk); 8197348SJose.Borrego@Sun.COM continue; 8207348SJose.Borrego@Sun.COM } 8217348SJose.Borrego@Sun.COM 8227348SJose.Borrego@Sun.COM /* this range is completely locked */ 8237348SJose.Borrego@Sun.COM if ((lk_start <= start) && (lk_end >= end)) { 8247348SJose.Borrego@Sun.COM return (B_FALSE); 8257348SJose.Borrego@Sun.COM } 8267348SJose.Borrego@Sun.COM 8277348SJose.Borrego@Sun.COM /* the first part of this range is locked */ 8287348SJose.Borrego@Sun.COM if ((start >= lk_start) && (start <= lk_end)) { 8297348SJose.Borrego@Sun.COM if (end > lk_end) 8307348SJose.Borrego@Sun.COM *new_mark = lk_end + 1; 8317348SJose.Borrego@Sun.COM return (B_FALSE); 8327348SJose.Borrego@Sun.COM } 8337348SJose.Borrego@Sun.COM 8347348SJose.Borrego@Sun.COM /* this piece is unlocked */ 8357348SJose.Borrego@Sun.COM if ((lk_start >= start) && (lk_start <= end)) { 8367348SJose.Borrego@Sun.COM if (low_water_mark > lk_start) 8377348SJose.Borrego@Sun.COM low_water_mark = lk_start; 8387348SJose.Borrego@Sun.COM } 8397348SJose.Borrego@Sun.COM 8407348SJose.Borrego@Sun.COM lk = smb_llist_next(llist_head, lk); 8417348SJose.Borrego@Sun.COM } 8427348SJose.Borrego@Sun.COM 8437348SJose.Borrego@Sun.COM if (low_water_mark != MAXOFFSET_T) { 8447348SJose.Borrego@Sun.COM *new_mark = low_water_mark; 8457348SJose.Borrego@Sun.COM return (B_TRUE); 8467348SJose.Borrego@Sun.COM } 8477348SJose.Borrego@Sun.COM /* the range is completely unlocked */ 8487348SJose.Borrego@Sun.COM return (B_TRUE); 8497348SJose.Borrego@Sun.COM } 850