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