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