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