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