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 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);
  79             lock != NULL;
  80             lock = smb_llist_next(llist, lock)) {
  81                 if (lock->l_file == of)
  82                         ++count;
  83         }
  84         smb_llist_exit(llist);
  85 
  86         return (count);
  87 }
  88 
  89 /*
  90  * smb_unlock_range
  91  *
  92  * locates lock range performed for corresponding to unlock request.
  93  *
  94  * NT_STATUS_SUCCESS - Lock range performed successfully.
  95  * !NT_STATUS_SUCCESS - Error in unlock range operation.
  96  */
  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) &&
 276                             !(lock->l_start & (1ULL << 63))) {
 277                                 result = NT_STATUS_FILE_LOCK_CONFLICT;
 278                         }
 279 
 280                         /*
 281                          * If the last lock attempt to fail on this file handle
 282                          * started at the same offset as this one then return
 283                          * NT_STATUS_FILE_LOCK_CONFLICT
 284                          */
 285                         mutex_enter(&file->f_mutex);
 286                         if ((file->f_flags & SMB_OFLAGS_LLF_POS_VALID) &&
 287                             (lock->l_start == file->f_llf_pos)) {
 288                                 result = NT_STATUS_FILE_LOCK_CONFLICT;
 289                         }
 290                         mutex_exit(&file->f_mutex);
 291                 }
 292 
 293                 /* Update last lock failed offset */
 294                 mutex_enter(&file->f_mutex);
 295                 file->f_llf_pos = lock->l_start;
 296                 file->f_flags |= SMB_OFLAGS_LLF_POS_VALID;
 297                 mutex_exit(&file->f_mutex);
 298 
 299                 smb_lock_free(lock);
 300         } else {
 301                 /*
 302                  * don't insert into the CIFS lock list unless the
 303                  * posix lock worked
 304                  */
 305                 rc = smb_fsop_frlock(node, lock, B_FALSE, sr->user_cr);
 306                 if (rc != 0) {
 307 #ifdef  DEBUG
 308                         if (smb_lock_debug)
 309                                 cmn_err(CE_CONT, "fop_frlock, err=%d\n", rc);
 310 #endif
 311                         result = NT_STATUS_FILE_LOCK_CONFLICT;
 312                 } else {
 313                         /*
 314                          * We want unlock to find exclusive locks before
 315                          * shared locks, so insert those at the head.
 316                          */
 317                         if (lock->l_type == SMB_LOCK_TYPE_READWRITE)
 318                                 smb_llist_insert_head(&node->n_lock_list, lock);
 319                         else
 320                                 smb_llist_insert_tail(&node->n_lock_list, lock);
 321                 }
 322         }
 323 
 324 #ifdef  DEBUG
 325         if (smb_lock_debug && result != 0) {
 326                 cmn_err(CE_CONT, "lock failed, 0x%x\n", result);
 327                 smb_lock_dumpnode(node);
 328         }
 329 #endif
 330 
 331         smb_llist_exit(&node->n_lock_list);
 332 
 333         if (result == NT_STATUS_SUCCESS) {
 334                 /* 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
 570  * already hold vp->vnbllock.
 571  */
 572 DWORD
 573 smb_nbl_conflict(smb_node_t *node, uint64_t off, uint64_t len, nbl_op_t op)
 574 {
 575         int svmand;
 576 
 577         SMB_NODE_VALID(node);
 578         ASSERT(smb_node_in_crit(node));
 579         ASSERT(op == NBL_READ || op == NBL_WRITE || op == NBL_READWRITE ||
 580             op == NBL_REMOVE || op == NBL_RENAME);
 581 
 582         if (smb_node_is_dir(node))
 583                 return (NT_STATUS_SUCCESS);
 584 
 585         if (nbl_share_conflict(node->vp, op, &smb_ct))
 586                 return (NT_STATUS_SHARING_VIOLATION);
 587 
 588         /*
 589          * When checking for lock conflicts, rename and remove
 590          * are not allowed, so treat those as read/write.
 591          */
 592         if (op == NBL_RENAME || op == NBL_REMOVE)
 593                 op = NBL_READWRITE;
 594 
 595         if (nbl_svmand(node->vp, zone_kcred(), &svmand))
 596                 svmand = 1;
 597 
 598         if (nbl_lock_conflict(node->vp, op, off, len, svmand, &smb_ct))
 599                 return (NT_STATUS_FILE_LOCK_CONFLICT);
 600 
 601         return (NT_STATUS_SUCCESS);
 602 }
 603 
 604 /*
 605  * smb_lock_posix_unlock
 606  *
 607  * checks if the current unlock request is in another lock and repeatedly calls
 608  * smb_is_range_unlocked on a sliding basis to unlock all bits of the lock
 609  * that are not in other locks
 610  *
 611  */
 612 static void
 613 smb_lock_posix_unlock(smb_node_t *node, smb_lock_t *lock, cred_t *cr)
 614 {
 615         uint64_t        new_mark;
 616         uint64_t        unlock_start;
 617         uint64_t        unlock_end;
 618         smb_lock_t      new_unlock;
 619         smb_llist_t     *llist;
 620         boolean_t       can_unlock;
 621 
 622         new_mark = 0;
 623         unlock_start = lock->l_start;
 624         unlock_end = unlock_start + lock->l_length;
 625         llist = &node->n_lock_list;
 626 
 627         for (;;) {
 628                 can_unlock = smb_is_range_unlocked(unlock_start, unlock_end,
 629                     lock->l_file->f_uniqid, llist, &new_mark);
 630                 if (can_unlock) {
 631                         if (new_mark) {
 632                                 new_unlock = *lock;
 633                                 new_unlock.l_start = unlock_start;
 634                                 new_unlock.l_length = new_mark - unlock_start;
 635                                 (void) smb_fsop_frlock(node, &new_unlock,
 636                                     B_TRUE, cr);
 637                                 unlock_start = new_mark;
 638                         } else {
 639                                 new_unlock = *lock;
 640                                 new_unlock.l_start = unlock_start;
 641                                 new_unlock.l_length = unlock_end - unlock_start;
 642                                 (void) smb_fsop_frlock(node, &new_unlock,
 643                                     B_TRUE, cr);
 644                                 break;
 645                         }
 646                 } else if (new_mark) {
 647                         unlock_start = new_mark;
 648                 } else {
 649                         break;
 650                 }
 651         }
 652 }
 653 
 654 /*
 655  * smb_lock_range_overlap
 656  *
 657  * Checks if lock range(start, length) overlaps range in lock structure.
 658  *
 659  * Zero-length byte range locks actually affect no single byte of the stream,
 660  * meaning they can still be accessed even with such locks in place. However,
 661  * they do conflict with other ranges in the following manner:
 662  *  conflict will only exist if the positive-length range contains the
 663  *  zero-length range's offset but doesn't start at it
 664  *
 665  * return values:
 666  *      0 - Lock range doesn't overlap
 667  *      1 - Lock range overlaps.
 668  */
 669 
 670 #define RANGE_NO_OVERLAP        0
 671 #define RANGE_OVERLAP           1
 672 
 673 static int
 674 smb_lock_range_overlap(struct smb_lock *lock, uint64_t start, uint64_t length)
 675 {
 676         if (length == 0) {
 677                 if ((lock->l_start < start) &&
 678                     ((lock->l_start + lock->l_length) > start))
 679                         return (RANGE_OVERLAP);
 680 
 681                 return (RANGE_NO_OVERLAP);
 682         }
 683 
 684         /* The following test is intended to catch roll over locks. */
 685         if ((start == lock->l_start) && (length == lock->l_length))
 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  *
1098  * B_FALSE and (new_mark == 0):
1099  *   The unlock can't be performed and we are done
1100  *
1101  * B_FALSE and (new_mark > 0),
1102  *   The range from start to new_mark can't be unlocked
1103  *   Start should be reset to new_mark for the next pass
1104  */
1105 
1106 static boolean_t
1107 smb_is_range_unlocked(uint64_t start, uint64_t end, uint32_t uniqid,
1108     smb_llist_t *llist_head, uint64_t *new_mark)
1109 {
1110         struct smb_lock *lk = NULL;
1111         uint64_t low_water_mark = MAXOFFSET_T;
1112         uint64_t lk_start;
1113         uint64_t lk_end;
1114 
1115         *new_mark = 0;
1116         lk = smb_llist_head(llist_head);
1117         while (lk) {
1118                 if (lk->l_length == 0) {
1119                         lk = smb_llist_next(llist_head, lk);
1120                         continue;
1121                 }
1122 
1123                 if (lk->l_file->f_uniqid != uniqid) {
1124                         lk = smb_llist_next(llist_head, lk);
1125                         continue;
1126                 }
1127 
1128                 lk_end = lk->l_start + lk->l_length - 1;
1129                 lk_start = lk->l_start;
1130 
1131                 /*
1132                  * there is no overlap for the first 2 cases
1133                  * check next node
1134                  */
1135                 if (lk_end < start) {
1136                         lk = smb_llist_next(llist_head, lk);
1137                         continue;
1138                 }
1139                 if (lk_start > end) {
1140                         lk = smb_llist_next(llist_head, lk);
1141                         continue;
1142                 }
1143 
1144                 /* this range is completely locked */
1145                 if ((lk_start <= start) && (lk_end >= end)) {
1146                         return (B_FALSE);
1147                 }
1148 
1149                 /* the first part of this range is locked */
1150                 if ((start >= lk_start) && (start <= lk_end)) {
1151                         if (end > lk_end)
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