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