1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 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);
  72             lock != NULL;
  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) &&
 215                             !(lock->l_start & (1ULL << 63))) {
 216                                 result = NT_STATUS_FILE_LOCK_CONFLICT;
 217                         }
 218 
 219                         /*
 220                          * If the last lock attempt to fail on this file handle
 221                          * started at the same offset as this one then return
 222                          * NT_STATUS_FILE_LOCK_CONFLICT
 223                          */
 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
 381  * already hold vp->vnbllock.
 382  */
 383 DWORD
 384 smb_nbl_conflict(smb_node_t *node, uint64_t off, uint64_t len, nbl_op_t op)
 385 {
 386         int svmand;
 387 
 388         SMB_NODE_VALID(node);
 389         ASSERT(smb_node_in_crit(node));
 390         ASSERT(op == NBL_READ || op == NBL_WRITE || op == NBL_READWRITE ||
 391             op == NBL_REMOVE || op == NBL_RENAME);
 392 
 393         if (smb_node_is_dir(node))
 394                 return (NT_STATUS_SUCCESS);
 395 
 396         if (nbl_share_conflict(node->vp, op, &smb_ct))
 397                 return (NT_STATUS_SHARING_VIOLATION);
 398 
 399         /*
 400          * When checking for lock conflicts, rename and remove
 401          * are not allowed, so treat those as read/write.
 402          */
 403         if (op == NBL_RENAME || op == NBL_REMOVE)
 404                 op = NBL_READWRITE;
 405 
 406         if (nbl_svmand(node->vp, zone_kcred(), &svmand))
 407                 svmand = 1;
 408 
 409         if (nbl_lock_conflict(node->vp, op, off, len, svmand, &smb_ct))
 410                 return (NT_STATUS_FILE_LOCK_CONFLICT);
 411 
 412         return (NT_STATUS_SUCCESS);
 413 }
 414 
 415 /*
 416  * smb_lock_posix_unlock
 417  *
 418  * checks if the current unlock request is in another lock and repeatedly calls
 419  * smb_is_range_unlocked on a sliding basis to unlock all bits of the lock
 420  * that are not in other locks
 421  *
 422  */
 423 static void
 424 smb_lock_posix_unlock(smb_node_t *node, smb_lock_t *lock, cred_t *cr)
 425 {
 426         uint64_t        new_mark;
 427         uint64_t        unlock_start;
 428         uint64_t        unlock_end;
 429         smb_lock_t      new_unlock;
 430         smb_llist_t     *llist;
 431         boolean_t       can_unlock;
 432 
 433         new_mark = 0;
 434         unlock_start = lock->l_start;
 435         unlock_end = unlock_start + lock->l_length;
 436         llist = &node->n_lock_list;
 437 
 438         for (;;) {
 439                 can_unlock = smb_is_range_unlocked(unlock_start, unlock_end,
 440                     lock->l_file->f_uniqid, llist, &new_mark);
 441                 if (can_unlock) {
 442                         if (new_mark) {
 443                                 new_unlock = *lock;
 444                                 new_unlock.l_start = unlock_start;
 445                                 new_unlock.l_length = new_mark - unlock_start;
 446                                 (void) smb_fsop_frlock(node, &new_unlock,
 447                                     B_TRUE, cr);
 448                                 unlock_start = new_mark;
 449                         } else {
 450                                 new_unlock = *lock;
 451                                 new_unlock.l_start = unlock_start;
 452                                 new_unlock.l_length = unlock_end - unlock_start;
 453                                 (void) smb_fsop_frlock(node, &new_unlock,
 454                                     B_TRUE, cr);
 455                                 break;
 456                         }
 457                 } else if (new_mark) {
 458                         unlock_start = new_mark;
 459                 } else {
 460                         break;
 461                 }
 462         }
 463 }
 464 
 465 /*
 466  * smb_lock_range_overlap
 467  *
 468  * Checks if lock range(start, length) overlaps range in lock structure.
 469  *
 470  * Zero-length byte range locks actually affect no single byte of the stream,
 471  * meaning they can still be accessed even with such locks in place. However,
 472  * they do conflict with other ranges in the following manner:
 473  *  conflict will only exist if the positive-length range contains the
 474  *  zero-length range's offset but doesn't start at it
 475  *
 476  * return values:
 477  *      0 - Lock range doesn't overlap
 478  *      1 - Lock range overlaps.
 479  */
 480 
 481 #define RANGE_NO_OVERLAP        0
 482 #define RANGE_OVERLAP           1
 483 
 484 static int
 485 smb_lock_range_overlap(struct smb_lock *lock, uint64_t start, uint64_t length)
 486 {
 487         if (length == 0) {
 488                 if ((lock->l_start < start) &&
 489                     ((lock->l_start + lock->l_length) > start))
 490                         return (RANGE_OVERLAP);
 491 
 492                 return (RANGE_NO_OVERLAP);
 493         }
 494 
 495         /* The following test is intended to catch roll over locks. */
 496         if ((start == lock->l_start) && (length == lock->l_length))
 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  *
 816  * B_FALSE and (new_mark == 0):
 817  *   The unlock can't be performed and we are done
 818  *
 819  * B_FALSE and (new_mark > 0),
 820  *   The range from start to new_mark can't be unlocked
 821  *   Start should be reset to new_mark for the next pass
 822  */
 823 
 824 static boolean_t
 825 smb_is_range_unlocked(uint64_t start, uint64_t end, uint32_t uniqid,
 826     smb_llist_t *llist_head, uint64_t *new_mark)
 827 {
 828         struct smb_lock *lk = NULL;
 829         uint64_t low_water_mark = MAXOFFSET_T;
 830         uint64_t lk_start;
 831         uint64_t lk_end;
 832 
 833         *new_mark = 0;
 834         lk = smb_llist_head(llist_head);
 835         while (lk) {
 836                 if (lk->l_length == 0) {
 837                         lk = smb_llist_next(llist_head, lk);
 838                         continue;
 839                 }
 840 
 841                 if (lk->l_file->f_uniqid != uniqid) {
 842                         lk = smb_llist_next(llist_head, lk);
 843                         continue;
 844                 }
 845 
 846                 lk_end = lk->l_start + lk->l_length - 1;
 847                 lk_start = lk->l_start;
 848 
 849                 /*
 850                  * there is no overlap for the first 2 cases
 851                  * check next node
 852                  */
 853                 if (lk_end < start) {
 854                         lk = smb_llist_next(llist_head, lk);
 855                         continue;
 856                 }
 857                 if (lk_start > end) {
 858                         lk = smb_llist_next(llist_head, lk);
 859                         continue;
 860                 }
 861 
 862                 /* this range is completely locked */
 863                 if ((lk_start <= start) && (lk_end >= end)) {
 864                         return (B_FALSE);
 865                 }
 866 
 867                 /* the first part of this range is locked */
 868                 if ((start >= lk_start) && (start <= lk_end)) {
 869                         if (end > lk_end)
 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 }