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