smb_lock.c revision 0897f7fbb62326e60e858c62a1654b2ca3e2667e
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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
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
39extern caller_context_t smb_ct;
40
41#ifdef	DEBUG
42int smb_lock_debug = 0;
43static void smb_lock_dump1(smb_lock_t *);
44static void smb_lock_dumplist(smb_llist_t *);
45static void smb_lock_dumpnode(smb_node_t *);
46#endif
47
48static void smb_lock_posix_unlock(smb_node_t *, smb_lock_t *, cred_t *);
49static boolean_t smb_is_range_unlocked(uint64_t, uint64_t, uint32_t,
50    smb_llist_t *, uint64_t *);
51static int smb_lock_range_overlap(smb_lock_t *, uint64_t, uint64_t);
52static uint32_t smb_lock_range_lckrules(smb_ofile_t *, smb_lock_t *,
53    smb_lock_t **);
54static uint32_t smb_lock_wait(smb_request_t *, smb_lock_t *, smb_lock_t *);
55static uint32_t smb_lock_range_ulckrules(smb_ofile_t *,
56    uint64_t, uint64_t, uint32_t, smb_lock_t **);
57static smb_lock_t *smb_lock_create(smb_request_t *, uint64_t, uint64_t,
58    uint32_t, uint32_t, uint32_t);
59static void smb_lock_destroy(smb_lock_t *);
60static void smb_lock_free(smb_lock_t *);
61
62/*
63 * Return the number of range locks on the specified ofile.
64 */
65uint32_t
66smb_lock_get_lock_count(smb_node_t *node, smb_ofile_t *of)
67{
68	smb_lock_t 	*lock;
69	smb_llist_t	*llist;
70	uint32_t	count = 0;
71
72	SMB_NODE_VALID(node);
73	SMB_OFILE_VALID(of);
74
75	llist = &node->n_lock_list;
76
77	smb_llist_enter(llist, RW_READER);
78	for (lock = smb_llist_head(llist);
79	    lock != NULL;
80	    lock = smb_llist_next(llist, lock)) {
81		if (lock->l_file == of)
82			++count;
83	}
84	smb_llist_exit(llist);
85
86	return (count);
87}
88
89/*
90 * smb_unlock_range
91 *
92 * locates lock range performed for corresponding to unlock request.
93 *
94 * NT_STATUS_SUCCESS - Lock range performed successfully.
95 * !NT_STATUS_SUCCESS - Error in unlock range operation.
96 */
97uint32_t
98smb_unlock_range(
99    smb_request_t	*sr,
100    uint64_t		start,
101    uint64_t		length,
102    uint32_t		pid)
103{
104	smb_ofile_t	*file = sr->fid_ofile;
105	smb_node_t	*node = file->f_node;
106	smb_lock_t	*lock = NULL;
107	uint32_t	status;
108
109	if (length > 1 &&
110	    (start + length) < start)
111		return (NT_STATUS_INVALID_LOCK_RANGE);
112
113#ifdef	DEBUG
114	if (smb_lock_debug) {
115		cmn_err(CE_CONT, "smb_unlock_range "
116		    "off=0x%llx, len=0x%llx, f=%p, pid=%d\n",
117		    (long long)start, (long long)length,
118		    (void *)sr->fid_ofile, pid);
119	}
120#endif
121
122	/* Apply unlocking rules */
123	smb_llist_enter(&node->n_lock_list, RW_WRITER);
124	status = smb_lock_range_ulckrules(file, start, length, pid, &lock);
125	if (status != NT_STATUS_SUCCESS) {
126		/*
127		 * If lock range is not matching in the list
128		 * return error.
129		 */
130		ASSERT(lock == NULL);
131	}
132	if (lock != NULL) {
133		smb_llist_remove(&node->n_lock_list, lock);
134		smb_lock_posix_unlock(node, lock, sr->user_cr);
135	}
136
137#ifdef	DEBUG
138	if (smb_lock_debug && lock == NULL) {
139		cmn_err(CE_CONT, "unlock failed, 0x%x\n", status);
140		smb_lock_dumpnode(node);
141	}
142#endif
143
144	smb_llist_exit(&node->n_lock_list);
145
146	if (lock != NULL)
147		smb_lock_destroy(lock);
148
149	return (status);
150}
151
152/*
153 * smb_lock_range
154 *
155 * Checks for integrity of file lock operation for the given range of file data.
156 * This is performed by applying lock rules with all the elements of the node
157 * lock list.
158 *
159 * Break shared (levelII) oplocks. If there is an exclusive oplock, it is
160 * owned by this ofile and therefore should not be broken.
161 *
162 * The function returns with new lock added if lock request is non-conflicting
163 * with existing range lock for the file. Otherwise smb request is filed
164 * without returning.
165 *
166 * NT_STATUS_SUCCESS - Lock range performed successfully.
167 * !NT_STATUS_SUCCESS - Error in lock range operation.
168 */
169uint32_t
170smb_lock_range(
171    smb_request_t	*sr,
172    uint64_t		start,
173    uint64_t		length,
174    uint32_t		pid,
175    uint32_t		locktype,
176    uint32_t		timeout)
177{
178	smb_ofile_t	*file = sr->fid_ofile;
179	smb_node_t	*node = file->f_node;
180	smb_lock_t	*lock;
181	smb_lock_t	*conflict = NULL;
182	uint32_t	result;
183	int		rc;
184	boolean_t	lock_has_timeout =
185	    (timeout != 0 && timeout != UINT_MAX);
186
187	if (length > 1 &&
188	    (start + length) < start)
189		return (NT_STATUS_INVALID_LOCK_RANGE);
190
191#ifdef	DEBUG
192	if (smb_lock_debug) {
193		cmn_err(CE_CONT, "smb_lock_range "
194		    "off=0x%llx, len=0x%llx, "
195		    "f=%p, pid=%d, typ=%d, tmo=%d\n",
196		    (long long)start, (long long)length,
197		    (void *)sr->fid_ofile, pid, locktype, timeout);
198	}
199#endif
200
201	lock = smb_lock_create(sr, start, length, pid, locktype, timeout);
202
203	smb_llist_enter(&node->n_lock_list, RW_WRITER);
204	for (;;) {
205
206		/* Apply locking rules */
207		result = smb_lock_range_lckrules(file, lock, &conflict);
208		switch (result) {
209		case NT_STATUS_LOCK_NOT_GRANTED: /* conflict! */
210			/* may need to wait */
211			break;
212		case NT_STATUS_SUCCESS:
213		case NT_STATUS_FILE_CLOSED:
214			goto break_loop;
215		default:
216			cmn_err(CE_CONT, "smb_lock_range1, status 0x%x\n",
217			    result);
218			goto break_loop;
219		}
220		if (timeout == 0)
221			goto break_loop;
222
223		/*
224		 * Call smb_lock_wait holding write lock for
225		 * node lock list.  smb_lock_wait will release
226		 * the node list lock if it blocks, so after
227		 * the call, (*conflict) may no longer exist.
228		 */
229		result = smb_lock_wait(sr, lock, conflict);
230		conflict = NULL;
231		switch (result) {
232		case NT_STATUS_SUCCESS:
233			/* conflict gone, try again */
234			break;
235		case NT_STATUS_TIMEOUT:
236			/* try just once more */
237			timeout = 0;
238			break;
239		case NT_STATUS_CANCELLED:
240		case NT_STATUS_FILE_CLOSED:
241			goto break_loop;
242		default:
243			cmn_err(CE_CONT, "smb_lock_range2, status 0x%x\n",
244			    result);
245			goto break_loop;
246		}
247	}
248
249break_loop:
250	lock->l_blocked_by = NULL;
251
252	if (result != NT_STATUS_SUCCESS) {
253		if (result == NT_STATUS_FILE_CLOSED)
254			result = NT_STATUS_RANGE_NOT_LOCKED;
255
256		/*
257		 * Under certain conditions NT_STATUS_FILE_LOCK_CONFLICT
258		 * should be returned instead of NT_STATUS_LOCK_NOT_GRANTED.
259		 * All of this appears to be specific to SMB1
260		 */
261		if (sr->session->dialect <= NT_LM_0_12 &&
262		    result == NT_STATUS_LOCK_NOT_GRANTED) {
263			/*
264			 * Locks with timeouts always return
265			 * NT_STATUS_FILE_LOCK_CONFLICT
266			 */
267			if (lock_has_timeout)
268				result = NT_STATUS_FILE_LOCK_CONFLICT;
269
270			/*
271			 * Locks starting higher than 0xef000000 that do not
272			 * have the MSB set always return
273			 * NT_STATUS_FILE_LOCK_CONFLICT
274			 */
275			if ((lock->l_start >= 0xef000000) &&
276			    !(lock->l_start & (1ULL << 63))) {
277				result = NT_STATUS_FILE_LOCK_CONFLICT;
278			}
279
280			/*
281			 * If the last lock attempt to fail on this file handle
282			 * started at the same offset as this one then return
283			 * NT_STATUS_FILE_LOCK_CONFLICT
284			 */
285			mutex_enter(&file->f_mutex);
286			if ((file->f_flags & SMB_OFLAGS_LLF_POS_VALID) &&
287			    (lock->l_start == file->f_llf_pos)) {
288				result = NT_STATUS_FILE_LOCK_CONFLICT;
289			}
290			mutex_exit(&file->f_mutex);
291		}
292
293		/* Update last lock failed offset */
294		mutex_enter(&file->f_mutex);
295		file->f_llf_pos = lock->l_start;
296		file->f_flags |= SMB_OFLAGS_LLF_POS_VALID;
297		mutex_exit(&file->f_mutex);
298
299		smb_lock_free(lock);
300	} else {
301		/*
302		 * don't insert into the CIFS lock list unless the
303		 * posix lock worked
304		 */
305		rc = smb_fsop_frlock(node, lock, B_FALSE, sr->user_cr);
306		if (rc != 0) {
307#ifdef	DEBUG
308			if (smb_lock_debug)
309				cmn_err(CE_CONT, "fop_frlock, err=%d\n", rc);
310#endif
311			result = NT_STATUS_FILE_LOCK_CONFLICT;
312		} else {
313			/*
314			 * We want unlock to find exclusive locks before
315			 * shared locks, so insert those at the head.
316			 */
317			if (lock->l_type == SMB_LOCK_TYPE_READWRITE)
318				smb_llist_insert_head(&node->n_lock_list, lock);
319			else
320				smb_llist_insert_tail(&node->n_lock_list, lock);
321		}
322	}
323
324#ifdef	DEBUG
325	if (smb_lock_debug && result != 0) {
326		cmn_err(CE_CONT, "lock failed, 0x%x\n", result);
327		smb_lock_dumpnode(node);
328	}
329#endif
330
331	smb_llist_exit(&node->n_lock_list);
332
333	if (result == NT_STATUS_SUCCESS)
334		smb_oplock_break_levelII(node);
335
336	return (result);
337}
338
339/*
340 * smb_lock_range_access
341 *
342 * scans node lock list
343 * to check if there is any overlapping lock. Overlapping
344 * lock is allowed only under same session and client pid.
345 *
346 * Return values
347 *	NT_STATUS_SUCCESS		lock access granted.
348 *	NT_STATUS_FILE_LOCK_CONFLICT 	access denied due to lock conflict.
349 */
350int
351smb_lock_range_access(
352    smb_request_t	*sr,
353    smb_node_t		*node,
354    uint64_t		start,
355    uint64_t		length,
356    boolean_t		will_write)
357{
358	smb_lock_t	*lock;
359	smb_llist_t	*llist;
360	uint32_t	lk_pid = 0;
361	int		status = NT_STATUS_SUCCESS;
362
363	if (length == 0)
364		return (status);
365
366	/*
367	 * What PID to use for lock conflict checks?
368	 * SMB2 locking ignores PIDs (have lk_pid=0)
369	 * SMB1 uses low 16 bits of sr->smb_pid
370	 */
371	if (sr->session->dialect < SMB_VERS_2_BASE)
372		lk_pid = sr->smb_pid & 0xFFFF;
373
374	llist = &node->n_lock_list;
375	smb_llist_enter(llist, RW_READER);
376	/* Search for any applicable lock */
377	for (lock = smb_llist_head(llist);
378	    lock != NULL;
379	    lock = smb_llist_next(llist, lock)) {
380
381		if (!smb_lock_range_overlap(lock, start, length))
382			/* Lock does not overlap */
383			continue;
384
385		if (lock->l_type == SMB_LOCK_TYPE_READONLY && !will_write)
386			continue;
387
388		if (lock->l_type == SMB_LOCK_TYPE_READWRITE &&
389		    lock->l_file == sr->fid_ofile &&
390		    lock->l_pid == lk_pid)
391			continue;
392
393#ifdef	DEBUG
394		if (smb_lock_debug) {
395			cmn_err(CE_CONT, "smb_lock_range_access conflict: "
396			    "off=0x%llx, len=0x%llx, "
397			    "f=%p, pid=%d, typ=%d\n",
398			    (long long)lock->l_start,
399			    (long long)lock->l_length,
400			    (void *)lock->l_file,
401			    lock->l_pid, lock->l_type);
402		}
403#endif
404		status = NT_STATUS_FILE_LOCK_CONFLICT;
405		break;
406	}
407	smb_llist_exit(llist);
408	return (status);
409}
410
411/*
412 * The ofile is being closed.  Wake any waiting locks and
413 * clear any granted locks.
414 */
415void
416smb_node_destroy_lock_by_ofile(smb_node_t *node, smb_ofile_t *file)
417{
418	smb_lock_t	*lock;
419	smb_lock_t	*nxtl;
420	list_t		destroy_list;
421
422	SMB_NODE_VALID(node);
423	ASSERT(node->n_refcnt);
424
425	/*
426	 * Cancel any waiting locks for this ofile
427	 */
428	smb_llist_enter(&node->n_wlock_list, RW_READER);
429	for (lock = smb_llist_head(&node->n_wlock_list);
430	    lock != NULL;
431	    lock = smb_llist_next(&node->n_wlock_list, lock)) {
432
433		if (lock->l_file == file) {
434			mutex_enter(&lock->l_mutex);
435			lock->l_blocked_by = NULL;
436			lock->l_flags |= SMB_LOCK_FLAG_CLOSED;
437			cv_broadcast(&lock->l_cv);
438			mutex_exit(&lock->l_mutex);
439		}
440	}
441	smb_llist_exit(&node->n_wlock_list);
442
443	/*
444	 * Move locks matching the specified file from the node->n_lock_list
445	 * to a temporary list (holding the lock the entire time) then
446	 * destroy all the matching locks.  We can't call smb_lock_destroy
447	 * while we are holding the lock for node->n_lock_list because we will
448	 * deadlock and we can't drop the lock because the list contents might
449	 * change (for example nxtl might get removed on another thread).
450	 */
451	list_create(&destroy_list, sizeof (smb_lock_t),
452	    offsetof(smb_lock_t, l_lnd));
453
454	smb_llist_enter(&node->n_lock_list, RW_WRITER);
455	lock = smb_llist_head(&node->n_lock_list);
456	while (lock) {
457		nxtl = smb_llist_next(&node->n_lock_list, lock);
458		if (lock->l_file == file) {
459			smb_llist_remove(&node->n_lock_list, lock);
460			smb_lock_posix_unlock(node, lock, file->f_user->u_cred);
461			list_insert_tail(&destroy_list, lock);
462		}
463		lock = nxtl;
464	}
465	smb_llist_exit(&node->n_lock_list);
466
467	lock = list_head(&destroy_list);
468	while (lock) {
469		nxtl = list_next(&destroy_list, lock);
470		list_remove(&destroy_list, lock);
471		smb_lock_destroy(lock);
472		lock = nxtl;
473	}
474
475	list_destroy(&destroy_list);
476}
477
478/*
479 * Cause a waiting lock to stop waiting and return an error.
480 * returns same status codes as unlock:
481 * NT_STATUS_SUCCESS, NT_STATUS_RANGE_NOT_LOCKED
482 */
483uint32_t
484smb_lock_range_cancel(smb_request_t *sr,
485    uint64_t start, uint64_t length, uint32_t pid)
486{
487	smb_node_t *node;
488	smb_lock_t *lock;
489	uint32_t status = NT_STATUS_RANGE_NOT_LOCKED;
490	int cnt = 0;
491
492	node = sr->fid_ofile->f_node;
493
494	smb_llist_enter(&node->n_wlock_list, RW_READER);
495
496#ifdef	DEBUG
497	if (smb_lock_debug) {
498		cmn_err(CE_CONT, "smb_lock_range_cancel:\n"
499		    "\tstart=0x%llx, len=0x%llx, of=%p, pid=%d\n",
500		    (long long)start, (long long)length,
501		    (void *)sr->fid_ofile, pid);
502	}
503#endif
504
505	for (lock = smb_llist_head(&node->n_wlock_list);
506	    lock != NULL;
507	    lock = smb_llist_next(&node->n_wlock_list, lock)) {
508
509		if ((start == lock->l_start) &&
510		    (length == lock->l_length) &&
511		    lock->l_file == sr->fid_ofile &&
512		    lock->l_pid == pid) {
513
514			mutex_enter(&lock->l_mutex);
515			lock->l_blocked_by = NULL;
516			lock->l_flags |= SMB_LOCK_FLAG_CANCELLED;
517			cv_broadcast(&lock->l_cv);
518			mutex_exit(&lock->l_mutex);
519			status = NT_STATUS_SUCCESS;
520			cnt++;
521		}
522	}
523
524#ifdef	DEBUG
525	if (smb_lock_debug && cnt != 1) {
526		cmn_err(CE_CONT, "cancel found %d\n", cnt);
527		smb_lock_dumpnode(node);
528	}
529#endif
530
531	smb_llist_exit(&node->n_wlock_list);
532
533	return (status);
534}
535
536void
537smb_lock_range_error(smb_request_t *sr, uint32_t status32)
538{
539	uint16_t errcode;
540
541	if (status32 == NT_STATUS_CANCELLED) {
542		status32 = NT_STATUS_FILE_LOCK_CONFLICT;
543		errcode = ERROR_LOCK_VIOLATION;
544	} else {
545		errcode = ERRlock;
546	}
547
548	smbsr_error(sr, status32, ERRDOS, errcode);
549}
550
551/*
552 * An SMB variant of nbl_conflict().
553 *
554 * SMB prevents remove or rename when conflicting locks exist
555 * (unlike NFS, which is why we can't just use nbl_conflict).
556 *
557 * Returns:
558 *	NT_STATUS_SHARING_VIOLATION - nbl_share_conflict
559 *	NT_STATUS_FILE_LOCK_CONFLICT - nbl_lock_conflict
560 *	NT_STATUS_SUCCESS - operation can proceed
561 *
562 * NB: This function used to also check the list of ofiles,
563 * via: smb_lock_range_access() but we _can't_ do that here
564 * due to lock order constraints between node->n_lock_list
565 * and node->vp->vnbllock (taken via nvl_start_crit).
566 * They must be taken in that order, and in here, we
567 * already hold vp->vnbllock.
568 */
569DWORD
570smb_nbl_conflict(smb_node_t *node, uint64_t off, uint64_t len, nbl_op_t op)
571{
572	int svmand;
573
574	SMB_NODE_VALID(node);
575	ASSERT(smb_node_in_crit(node));
576	ASSERT(op == NBL_READ || op == NBL_WRITE || op == NBL_READWRITE ||
577	    op == NBL_REMOVE || op == NBL_RENAME);
578
579	if (smb_node_is_dir(node))
580		return (NT_STATUS_SUCCESS);
581
582	if (nbl_share_conflict(node->vp, op, &smb_ct))
583		return (NT_STATUS_SHARING_VIOLATION);
584
585	/*
586	 * When checking for lock conflicts, rename and remove
587	 * are not allowed, so treat those as read/write.
588	 */
589	if (op == NBL_RENAME || op == NBL_REMOVE)
590		op = NBL_READWRITE;
591
592	if (nbl_svmand(node->vp, zone_kcred(), &svmand))
593		svmand = 1;
594
595	if (nbl_lock_conflict(node->vp, op, off, len, svmand, &smb_ct))
596		return (NT_STATUS_FILE_LOCK_CONFLICT);
597
598	return (NT_STATUS_SUCCESS);
599}
600
601/*
602 * smb_lock_posix_unlock
603 *
604 * checks if the current unlock request is in another lock and repeatedly calls
605 * smb_is_range_unlocked on a sliding basis to unlock all bits of the lock
606 * that are not in other locks
607 *
608 */
609static void
610smb_lock_posix_unlock(smb_node_t *node, smb_lock_t *lock, cred_t *cr)
611{
612	uint64_t	new_mark;
613	uint64_t	unlock_start;
614	uint64_t	unlock_end;
615	smb_lock_t	new_unlock;
616	smb_llist_t	*llist;
617	boolean_t	can_unlock;
618
619	new_mark = 0;
620	unlock_start = lock->l_start;
621	unlock_end = unlock_start + lock->l_length;
622	llist = &node->n_lock_list;
623
624	for (;;) {
625		can_unlock = smb_is_range_unlocked(unlock_start, unlock_end,
626		    lock->l_file->f_uniqid, llist, &new_mark);
627		if (can_unlock) {
628			if (new_mark) {
629				new_unlock = *lock;
630				new_unlock.l_start = unlock_start;
631				new_unlock.l_length = new_mark - unlock_start;
632				(void) smb_fsop_frlock(node, &new_unlock,
633				    B_TRUE, cr);
634				unlock_start = new_mark;
635			} else {
636				new_unlock = *lock;
637				new_unlock.l_start = unlock_start;
638				new_unlock.l_length = unlock_end - unlock_start;
639				(void) smb_fsop_frlock(node, &new_unlock,
640				    B_TRUE, cr);
641				break;
642			}
643		} else if (new_mark) {
644			unlock_start = new_mark;
645		} else {
646			break;
647		}
648	}
649}
650
651/*
652 * smb_lock_range_overlap
653 *
654 * Checks if lock range(start, length) overlaps range in lock structure.
655 *
656 * Zero-length byte range locks actually affect no single byte of the stream,
657 * meaning they can still be accessed even with such locks in place. However,
658 * they do conflict with other ranges in the following manner:
659 *  conflict will only exist if the positive-length range contains the
660 *  zero-length range's offset but doesn't start at it
661 *
662 * return values:
663 *	0 - Lock range doesn't overlap
664 *	1 - Lock range overlaps.
665 */
666
667#define	RANGE_NO_OVERLAP	0
668#define	RANGE_OVERLAP		1
669
670static int
671smb_lock_range_overlap(struct smb_lock *lock, uint64_t start, uint64_t length)
672{
673	if (length == 0) {
674		if ((lock->l_start < start) &&
675		    ((lock->l_start + lock->l_length) > start))
676			return (RANGE_OVERLAP);
677
678		return (RANGE_NO_OVERLAP);
679	}
680
681	/* The following test is intended to catch roll over locks. */
682	if ((start == lock->l_start) && (length == lock->l_length))
683		return (RANGE_OVERLAP);
684
685	if (start < lock->l_start) {
686		if (start + length > lock->l_start)
687			return (RANGE_OVERLAP);
688	} else if (start < lock->l_start + lock->l_length)
689		return (RANGE_OVERLAP);
690
691	return (RANGE_NO_OVERLAP);
692}
693
694/*
695 * smb_lock_range_lckrules
696 *
697 * Lock range rules:
698 *	1. Overlapping read locks are allowed if the
699 *	   current locks in the region are only read locks
700 *	   irrespective of pid of smb client issuing lock request.
701 *
702 *	2. Read lock in the overlapped region of write lock
703 *	   are allowed if the previous lock is performed by the
704 *	   same pid and connection.
705 *
706 * return status:
707 *	NT_STATUS_SUCCESS - Input lock range conforms to lock rules.
708 *	NT_STATUS_LOCK_NOT_GRANTED - Input lock conflicts lock rules.
709 *	NT_STATUS_FILE_CLOSED
710 */
711static uint32_t
712smb_lock_range_lckrules(
713    smb_ofile_t		*file,
714    smb_lock_t		*dlock,		/* desired lock */
715    smb_lock_t		**conflictp)
716{
717	smb_node_t	*node = file->f_node;
718	smb_lock_t	*lock;
719	uint32_t	status = NT_STATUS_SUCCESS;
720
721	/* Check if file is closed */
722	if (!smb_ofile_is_open(file)) {
723		return (NT_STATUS_FILE_CLOSED);
724	}
725
726	/* Caller must hold lock for node->n_lock_list */
727	for (lock = smb_llist_head(&node->n_lock_list);
728	    lock != NULL;
729	    lock = smb_llist_next(&node->n_lock_list, lock)) {
730
731		if (!smb_lock_range_overlap(lock, dlock->l_start,
732		    dlock->l_length))
733			continue;
734
735		/*
736		 * Check to see if lock in the overlapping record
737		 * is only read lock. Current finding is read
738		 * locks can overlapped irrespective of pids.
739		 */
740		if ((lock->l_type == SMB_LOCK_TYPE_READONLY) &&
741		    (dlock->l_type == SMB_LOCK_TYPE_READONLY)) {
742			continue;
743		}
744
745		/*
746		 * When the read lock overlaps write lock, check if
747		 * allowed.
748		 */
749		if ((dlock->l_type == SMB_LOCK_TYPE_READONLY) &&
750		    !(lock->l_type == SMB_LOCK_TYPE_READONLY)) {
751			if (lock->l_file == dlock->l_file &&
752			    lock->l_pid == dlock->l_pid) {
753				continue;
754			}
755		}
756
757		/* Conflict in overlapping lock element */
758		*conflictp = lock;
759		status = NT_STATUS_LOCK_NOT_GRANTED;
760		break;
761	}
762
763	return (status);
764}
765
766/*
767 * Cancel method for smb_lock_wait()
768 *
769 * This request is waiting on a lock.  Wakeup everything
770 * waiting on the lock so that the relevant thread regains
771 * control and notices that is has been cancelled.  The
772 * other lock request threads waiting on this lock will go
773 * back to sleep when they discover they are still blocked.
774 */
775static void
776smb_lock_cancel_sr(smb_request_t *sr)
777{
778	smb_lock_t *lock = sr->cancel_arg2;
779
780	ASSERT(lock->l_magic == SMB_LOCK_MAGIC);
781	mutex_enter(&lock->l_mutex);
782	lock->l_blocked_by = NULL;
783	lock->l_flags |= SMB_LOCK_FLAG_CANCELLED;
784	cv_broadcast(&lock->l_cv);
785	mutex_exit(&lock->l_mutex);
786}
787
788/*
789 * smb_lock_wait
790 *
791 * Wait operation for smb overlapping lock to be released.  Caller must hold
792 * write lock for node->n_lock_list so that the set of active locks can't
793 * change unexpectedly.  The lock for node->n_lock_list  will be released
794 * within this function during the sleep after the lock dependency has
795 * been recorded.
796 *
797 * Returns NT_STATUS_SUCCESS when the lock can be granted,
798 * otherwise NT_STATUS_CANCELLED, etc.
799 */
800static uint32_t
801smb_lock_wait(smb_request_t *sr, smb_lock_t *lock, smb_lock_t *conflict)
802{
803	smb_node_t	*node;
804	clock_t		rc;
805	uint32_t	status = NT_STATUS_SUCCESS;
806
807	node = lock->l_file->f_node;
808	ASSERT(node == conflict->l_file->f_node);
809
810	/*
811	 * Let the blocked lock (lock) l_blocked_by point to the
812	 * conflicting lock (conflict), and increment a count of
813	 * conflicts with the latter.  When the conflicting lock
814	 * is destroyed, we'll search the list of waiting locks
815	 * (on the node) and wake any with l_blocked_by ==
816	 * the formerly conflicting lock.
817	 */
818	mutex_enter(&lock->l_mutex);
819	lock->l_blocked_by = conflict;
820	mutex_exit(&lock->l_mutex);
821
822	mutex_enter(&conflict->l_mutex);
823	conflict->l_conflicts++;
824	mutex_exit(&conflict->l_mutex);
825
826	/*
827	 * Put the blocked lock on the waiting list.
828	 */
829	smb_llist_enter(&node->n_wlock_list, RW_WRITER);
830	smb_llist_insert_tail(&node->n_wlock_list, lock);
831	smb_llist_exit(&node->n_wlock_list);
832
833#ifdef	DEBUG
834	if (smb_lock_debug) {
835		cmn_err(CE_CONT, "smb_lock_wait: lock=%p conflict=%p\n",
836		    (void *)lock, (void *)conflict);
837		smb_lock_dumpnode(node);
838	}
839#endif
840
841	/*
842	 * We come in with n_lock_list already held, and keep
843	 * that hold until we're done with conflict (are now).
844	 * Drop that now, and retake later.  Note that the lock
845	 * (*conflict) may go away once we exit this list.
846	 */
847	smb_llist_exit(&node->n_lock_list);
848	conflict = NULL;
849
850	/*
851	 * Before we actually start waiting, setup the hooks
852	 * smb_request_cancel uses to unblock this wait.
853	 */
854	mutex_enter(&sr->sr_mutex);
855	if (sr->sr_state == SMB_REQ_STATE_ACTIVE) {
856		sr->sr_state = SMB_REQ_STATE_WAITING_LOCK;
857		sr->cancel_method = smb_lock_cancel_sr;
858		sr->cancel_arg2 = lock;
859	} else {
860		status = NT_STATUS_CANCELLED;
861	}
862	mutex_exit(&sr->sr_mutex);
863
864	/*
865	 * Now we're ready to actually wait for the conflicting
866	 * lock to be removed, or for the wait to be ended by
867	 * an external cancel, or a timeout.
868	 */
869	mutex_enter(&lock->l_mutex);
870	while (status == NT_STATUS_SUCCESS &&
871	    lock->l_blocked_by != NULL) {
872		if (lock->l_flags & SMB_LOCK_FLAG_INDEFINITE) {
873			cv_wait(&lock->l_cv, &lock->l_mutex);
874		} else {
875			rc = cv_timedwait(&lock->l_cv,
876			    &lock->l_mutex, lock->l_end_time);
877			if (rc < 0)
878				status = NT_STATUS_TIMEOUT;
879		}
880	}
881	if (status == NT_STATUS_SUCCESS) {
882		if (lock->l_flags & SMB_LOCK_FLAG_CANCELLED)
883			status = NT_STATUS_CANCELLED;
884		if (lock->l_flags & SMB_LOCK_FLAG_CLOSED)
885			status = NT_STATUS_FILE_CLOSED;
886	}
887	mutex_exit(&lock->l_mutex);
888
889	/*
890	 * Done waiting.  Cleanup cancel hooks and
891	 * finish SR state transitions.
892	 */
893	mutex_enter(&sr->sr_mutex);
894	sr->cancel_method = NULL;
895	sr->cancel_arg2 = NULL;
896
897	switch (sr->sr_state) {
898	case SMB_REQ_STATE_WAITING_LOCK:
899		/* Normal wakeup.  Keep status from above. */
900		sr->sr_state = SMB_REQ_STATE_ACTIVE;
901		break;
902
903	case SMB_REQ_STATE_CANCEL_PENDING:
904		/* Cancelled via smb_lock_cancel_sr */
905		sr->sr_state = SMB_REQ_STATE_CANCELLED;
906		/* FALLTHROUGH */
907	case SMB_REQ_STATE_CANCELLED:
908		if (status == NT_STATUS_SUCCESS)
909			status = NT_STATUS_CANCELLED;
910		break;
911
912	default:
913		break;
914	}
915	mutex_exit(&sr->sr_mutex);
916
917	/* Return to the caller with n_lock_list held. */
918	smb_llist_enter(&node->n_lock_list, RW_WRITER);
919
920	smb_llist_enter(&node->n_wlock_list, RW_WRITER);
921	smb_llist_remove(&node->n_wlock_list, lock);
922	smb_llist_exit(&node->n_wlock_list);
923
924	return (status);
925}
926
927/*
928 * smb_lock_range_ulckrules
929 *
930 *	1. Unlock should be performed at exactly matching ends.
931 *	   This has been changed because overlapping ends is
932 *	   allowed and there is no other precise way of locating
933 *	   lock entity in node lock list.
934 *
935 *	2. Unlock is failed if there is no corresponding lock exists.
936 *
937 * Return values
938 *
939 *	NT_STATUS_SUCCESS		Unlock request matches lock record
940 *					pointed by 'foundlock' lock structure.
941 *
942 *	NT_STATUS_RANGE_NOT_LOCKED	Unlock request doen't match any
943 *					of lock record in node lock request or
944 *					error in unlock range processing.
945 */
946static uint32_t
947smb_lock_range_ulckrules(
948    smb_ofile_t		*file,
949    uint64_t		start,
950    uint64_t		length,
951    uint32_t		pid,
952    smb_lock_t		**foundlock)
953{
954	smb_node_t	*node = file->f_node;
955	smb_lock_t	*lock;
956	uint32_t	status = NT_STATUS_RANGE_NOT_LOCKED;
957
958	/* Caller must hold lock for node->n_lock_list */
959	for (lock = smb_llist_head(&node->n_lock_list);
960	    lock != NULL;
961	    lock = smb_llist_next(&node->n_lock_list, lock)) {
962
963		if ((start == lock->l_start) &&
964		    (length == lock->l_length) &&
965		    lock->l_file == file &&
966		    lock->l_pid == pid) {
967			*foundlock = lock;
968			status = NT_STATUS_SUCCESS;
969			break;
970		}
971	}
972
973	return (status);
974}
975
976static smb_lock_t *
977smb_lock_create(
978    smb_request_t *sr,
979    uint64_t start,
980    uint64_t length,
981    uint32_t pid,
982    uint32_t locktype,
983    uint32_t timeout)
984{
985	smb_lock_t *lock;
986
987	ASSERT(locktype == SMB_LOCK_TYPE_READWRITE ||
988	    locktype == SMB_LOCK_TYPE_READONLY);
989
990	lock = kmem_cache_alloc(smb_cache_lock, KM_SLEEP);
991	bzero(lock, sizeof (*lock));
992	lock->l_magic = SMB_LOCK_MAGIC;
993	lock->l_file = sr->fid_ofile;
994	/* l_file == fid_ofile implies same connection (see ofile lookup) */
995	lock->l_pid = pid;
996	lock->l_type = locktype;
997	lock->l_start = start;
998	lock->l_length = length;
999	/*
1000	 * Calculate the absolute end time so that we can use it
1001	 * in cv_timedwait.
1002	 */
1003	lock->l_end_time = ddi_get_lbolt() + MSEC_TO_TICK(timeout);
1004	if (timeout == UINT_MAX)
1005		lock->l_flags |= SMB_LOCK_FLAG_INDEFINITE;
1006
1007	mutex_init(&lock->l_mutex, NULL, MUTEX_DEFAULT, NULL);
1008	cv_init(&lock->l_cv, NULL, CV_DEFAULT, NULL);
1009
1010	return (lock);
1011}
1012
1013static void
1014smb_lock_free(smb_lock_t *lock)
1015{
1016
1017	lock->l_magic = 0;
1018	cv_destroy(&lock->l_cv);
1019	mutex_destroy(&lock->l_mutex);
1020
1021	kmem_cache_free(smb_cache_lock, lock);
1022}
1023
1024/*
1025 * smb_lock_destroy
1026 *
1027 * Caller must hold node->n_lock_list
1028 */
1029static void
1030smb_lock_destroy(smb_lock_t *lock)
1031{
1032	smb_lock_t *tl;
1033	smb_node_t *node;
1034	uint32_t ccnt;
1035
1036	/*
1037	 * Wake any waiting locks that were blocked by this.
1038	 * We want them to wake and continue in FIFO order,
1039	 * so enter/exit the llist every time...
1040	 */
1041	mutex_enter(&lock->l_mutex);
1042	ccnt = lock->l_conflicts;
1043	lock->l_conflicts = 0;
1044	mutex_exit(&lock->l_mutex);
1045
1046	node = lock->l_file->f_node;
1047	while (ccnt) {
1048
1049		smb_llist_enter(&node->n_wlock_list, RW_READER);
1050
1051		for (tl = smb_llist_head(&node->n_wlock_list);
1052		    tl != NULL;
1053		    tl = smb_llist_next(&node->n_wlock_list, tl)) {
1054			mutex_enter(&tl->l_mutex);
1055			if (tl->l_blocked_by == lock) {
1056				tl->l_blocked_by = NULL;
1057				cv_broadcast(&tl->l_cv);
1058				mutex_exit(&tl->l_mutex);
1059				goto woke_one;
1060			}
1061			mutex_exit(&tl->l_mutex);
1062		}
1063		/* No more in the list blocked by this lock. */
1064		ccnt = 0;
1065	woke_one:
1066		smb_llist_exit(&node->n_wlock_list);
1067		if (ccnt) {
1068			/*
1069			 * Let the thread we woke have a chance to run
1070			 * before we wake competitors for their lock.
1071			 */
1072			delay(MSEC_TO_TICK(1));
1073		}
1074	}
1075
1076	smb_lock_free(lock);
1077}
1078
1079/*
1080 * smb_is_range_unlocked
1081 *
1082 * Checks if the current unlock byte range request overlaps another lock
1083 * This function is used to determine where POSIX unlocks should be
1084 * applied.
1085 *
1086 * The return code and the value of new_mark must be interpreted as
1087 * follows:
1088 *
1089 * B_TRUE and (new_mark == 0):
1090 *   This is the last or only lock left to be unlocked
1091 *
1092 * B_TRUE and (new_mark > 0):
1093 *   The range from start to new_mark can be unlocked
1094 *
1095 * B_FALSE and (new_mark == 0):
1096 *   The unlock can't be performed and we are done
1097 *
1098 * B_FALSE and (new_mark > 0),
1099 *   The range from start to new_mark can't be unlocked
1100 *   Start should be reset to new_mark for the next pass
1101 */
1102
1103static boolean_t
1104smb_is_range_unlocked(uint64_t start, uint64_t end, uint32_t uniqid,
1105    smb_llist_t *llist_head, uint64_t *new_mark)
1106{
1107	struct smb_lock *lk = NULL;
1108	uint64_t low_water_mark = MAXOFFSET_T;
1109	uint64_t lk_start;
1110	uint64_t lk_end;
1111
1112	*new_mark = 0;
1113	lk = smb_llist_head(llist_head);
1114	while (lk) {
1115		if (lk->l_length == 0) {
1116			lk = smb_llist_next(llist_head, lk);
1117			continue;
1118		}
1119
1120		if (lk->l_file->f_uniqid != uniqid) {
1121			lk = smb_llist_next(llist_head, lk);
1122			continue;
1123		}
1124
1125		lk_end = lk->l_start + lk->l_length - 1;
1126		lk_start = lk->l_start;
1127
1128		/*
1129		 * there is no overlap for the first 2 cases
1130		 * check next node
1131		 */
1132		if (lk_end < start) {
1133			lk = smb_llist_next(llist_head, lk);
1134			continue;
1135		}
1136		if (lk_start > end) {
1137			lk = smb_llist_next(llist_head, lk);
1138			continue;
1139		}
1140
1141		/* this range is completely locked */
1142		if ((lk_start <= start) && (lk_end >= end)) {
1143			return (B_FALSE);
1144		}
1145
1146		/* the first part of this range is locked */
1147		if ((start >= lk_start) && (start <= lk_end)) {
1148			if (end > lk_end)
1149				*new_mark = lk_end + 1;
1150			return (B_FALSE);
1151		}
1152
1153		/* this piece is unlocked */
1154		if ((lk_start >= start) && (lk_start <= end)) {
1155			if (low_water_mark > lk_start)
1156				low_water_mark  = lk_start;
1157		}
1158
1159		lk = smb_llist_next(llist_head, lk);
1160	}
1161
1162	if (low_water_mark != MAXOFFSET_T) {
1163		*new_mark = low_water_mark;
1164		return (B_TRUE);
1165	}
1166	/* the range is completely unlocked */
1167	return (B_TRUE);
1168}
1169
1170#ifdef	DEBUG
1171static void
1172smb_lock_dump1(smb_lock_t *lock)
1173{
1174	cmn_err(CE_CONT, "\t0x%p: 0x%llx, 0x%llx, %p, %d\n",
1175	    (void *)lock,
1176	    (long long)lock->l_start,
1177	    (long long)lock->l_length,
1178	    (void *)lock->l_file,
1179	    lock->l_pid);
1180
1181}
1182
1183static void
1184smb_lock_dumplist(smb_llist_t *llist)
1185{
1186	smb_lock_t *lock;
1187
1188	for (lock = smb_llist_head(llist);
1189	    lock != NULL;
1190	    lock = smb_llist_next(llist, lock)) {
1191		smb_lock_dump1(lock);
1192	}
1193}
1194
1195static void
1196smb_lock_dumpnode(smb_node_t *node)
1197{
1198	cmn_err(CE_CONT, "Granted Locks on %p (%d)\n",
1199	    (void *)node, node->n_lock_list.ll_count);
1200	smb_lock_dumplist(&node->n_lock_list);
1201
1202	cmn_err(CE_CONT, "Waiting Locks on %p (%d)\n",
1203	    (void *)node, node->n_wlock_list.ll_count);
1204	smb_lock_dumplist(&node->n_wlock_list);
1205}
1206
1207#endif
1208