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