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