1da6c28aaSamw /*
2da6c28aaSamw * CDDL HEADER START
3da6c28aaSamw *
4da6c28aaSamw * The contents of this file are subject to the terms of the
5da6c28aaSamw * Common Development and Distribution License (the "License").
6da6c28aaSamw * You may not use this file except in compliance with the License.
7da6c28aaSamw *
8da6c28aaSamw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw * See the License for the specific language governing permissions
11da6c28aaSamw * and limitations under the License.
12da6c28aaSamw *
13da6c28aaSamw * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw *
19da6c28aaSamw * CDDL HEADER END
20da6c28aaSamw */
21da6c28aaSamw /*
22148c5f43SAlan Wright * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
2394047d49SGordon Ross * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
24da6c28aaSamw */
25da6c28aaSamw
26da6c28aaSamw /*
27da6c28aaSamw * This module provides range lock functionality for CIFS/SMB clients.
28da6c28aaSamw * Lock range service functions process SMB lock and and unlock
29da6c28aaSamw * requests for a file by applying lock rules and marks file range
30da6c28aaSamw * as locked if the lock is successful otherwise return proper
31da6c28aaSamw * error code.
32da6c28aaSamw */
33da6c28aaSamw
34bbf6f00cSJordan Brown #include <smbsrv/smb_kproto.h>
35dc20a302Sas #include <smbsrv/smb_fsops.h>
36dc20a302Sas #include <sys/nbmlock.h>
37dc20a302Sas #include <sys/param.h>
38dc20a302Sas
39faa1795aSjb extern caller_context_t smb_ct;
40faa1795aSjb
410897f7fbSGordon Ross #ifdef DEBUG
420897f7fbSGordon Ross int smb_lock_debug = 0;
430897f7fbSGordon Ross static void smb_lock_dump1(smb_lock_t *);
440897f7fbSGordon Ross static void smb_lock_dumplist(smb_llist_t *);
450897f7fbSGordon Ross static void smb_lock_dumpnode(smb_node_t *);
460897f7fbSGordon Ross #endif
470897f7fbSGordon Ross
486537f381Sas static void smb_lock_posix_unlock(smb_node_t *, smb_lock_t *, cred_t *);
49c8ec8eeaSjose borrego static boolean_t smb_is_range_unlocked(uint64_t, uint64_t, uint32_t,
50c8ec8eeaSjose borrego smb_llist_t *, uint64_t *);
516537f381Sas static int smb_lock_range_overlap(smb_lock_t *, uint64_t, uint64_t);
520897f7fbSGordon Ross static uint32_t smb_lock_range_lckrules(smb_ofile_t *, smb_lock_t *,
530897f7fbSGordon Ross smb_lock_t **);
540897f7fbSGordon Ross static uint32_t smb_lock_wait(smb_request_t *, smb_lock_t *, smb_lock_t *);
550897f7fbSGordon Ross static uint32_t smb_lock_range_ulckrules(smb_ofile_t *,
560897f7fbSGordon Ross uint64_t, uint64_t, uint32_t, smb_lock_t **);
576537f381Sas static smb_lock_t *smb_lock_create(smb_request_t *, uint64_t, uint64_t,
580897f7fbSGordon Ross uint32_t, uint32_t, uint32_t);
596537f381Sas static void smb_lock_destroy(smb_lock_t *);
606537f381Sas static void smb_lock_free(smb_lock_t *);
616537f381Sas
621fcced4cSJordan Brown /*
63148c5f43SAlan Wright * Return the number of range locks on the specified ofile.
641fcced4cSJordan Brown */
651fcced4cSJordan Brown uint32_t
smb_lock_get_lock_count(smb_node_t * node,smb_ofile_t * of)66148c5f43SAlan Wright smb_lock_get_lock_count(smb_node_t *node, smb_ofile_t *of)
671fcced4cSJordan Brown {
6894047d49SGordon Ross smb_lock_t *lock;
69148c5f43SAlan Wright smb_llist_t *llist;
70148c5f43SAlan Wright uint32_t count = 0;
711fcced4cSJordan Brown
721fcced4cSJordan Brown SMB_NODE_VALID(node);
73148c5f43SAlan Wright SMB_OFILE_VALID(of);
741fcced4cSJordan Brown
75148c5f43SAlan Wright llist = &node->n_lock_list;
76148c5f43SAlan Wright
77148c5f43SAlan Wright smb_llist_enter(llist, RW_READER);
78148c5f43SAlan Wright for (lock = smb_llist_head(llist);
79148c5f43SAlan Wright lock != NULL;
80148c5f43SAlan Wright lock = smb_llist_next(llist, lock)) {
81148c5f43SAlan Wright if (lock->l_file == of)
82148c5f43SAlan Wright ++count;
83148c5f43SAlan Wright }
84148c5f43SAlan Wright smb_llist_exit(llist);
856537f381Sas
861fcced4cSJordan Brown return (count);
871fcced4cSJordan Brown }
886537f381Sas
896537f381Sas /*
906537f381Sas * smb_unlock_range
916537f381Sas *
926537f381Sas * locates lock range performed for corresponding to unlock request.
936537f381Sas *
946537f381Sas * NT_STATUS_SUCCESS - Lock range performed successfully.
956537f381Sas * !NT_STATUS_SUCCESS - Error in unlock range operation.
966537f381Sas */
976537f381Sas uint32_t
smb_unlock_range(smb_request_t * sr,uint64_t start,uint64_t length,uint32_t pid)986537f381Sas smb_unlock_range(
996537f381Sas smb_request_t *sr,
1006537f381Sas uint64_t start,
1010897f7fbSGordon Ross uint64_t length,
1020897f7fbSGordon Ross uint32_t pid)
1036537f381Sas {
1040897f7fbSGordon Ross smb_ofile_t *file = sr->fid_ofile;
1050897f7fbSGordon Ross smb_node_t *node = file->f_node;
1066537f381Sas smb_lock_t *lock = NULL;
1076537f381Sas uint32_t status;
1086537f381Sas
1090897f7fbSGordon Ross if (length > 1 &&
1100897f7fbSGordon Ross (start + length) < start)
1110897f7fbSGordon Ross return (NT_STATUS_INVALID_LOCK_RANGE);
1120897f7fbSGordon Ross
1130897f7fbSGordon Ross #ifdef DEBUG
1140897f7fbSGordon Ross if (smb_lock_debug) {
1150897f7fbSGordon Ross cmn_err(CE_CONT, "smb_unlock_range "
1160897f7fbSGordon Ross "off=0x%llx, len=0x%llx, f=%p, pid=%d\n",
1170897f7fbSGordon Ross (long long)start, (long long)length,
1180897f7fbSGordon Ross (void *)sr->fid_ofile, pid);
1190897f7fbSGordon Ross }
1200897f7fbSGordon Ross #endif
1210897f7fbSGordon Ross
1226537f381Sas /* Apply unlocking rules */
1236537f381Sas smb_llist_enter(&node->n_lock_list, RW_WRITER);
1240897f7fbSGordon Ross status = smb_lock_range_ulckrules(file, start, length, pid, &lock);
1256537f381Sas if (status != NT_STATUS_SUCCESS) {
1266537f381Sas /*
1276537f381Sas * If lock range is not matching in the list
1286537f381Sas * return error.
1296537f381Sas */
1306537f381Sas ASSERT(lock == NULL);
1310897f7fbSGordon Ross }
1320897f7fbSGordon Ross if (lock != NULL) {
1330897f7fbSGordon Ross smb_llist_remove(&node->n_lock_list, lock);
1340897f7fbSGordon Ross smb_lock_posix_unlock(node, lock, sr->user_cr);
1356537f381Sas }
1366537f381Sas
1370897f7fbSGordon Ross #ifdef DEBUG
1380897f7fbSGordon Ross if (smb_lock_debug && lock == NULL) {
1390897f7fbSGordon Ross cmn_err(CE_CONT, "unlock failed, 0x%x\n", status);
1400897f7fbSGordon Ross smb_lock_dumpnode(node);
1410897f7fbSGordon Ross }
1420897f7fbSGordon Ross #endif
1430897f7fbSGordon Ross
1446537f381Sas smb_llist_exit(&node->n_lock_list);
1450897f7fbSGordon Ross
1460897f7fbSGordon Ross if (lock != NULL)
1470897f7fbSGordon Ross smb_lock_destroy(lock);
1486537f381Sas
1496537f381Sas return (status);
1506537f381Sas }
1516537f381Sas
1526537f381Sas /*
1536537f381Sas * smb_lock_range
1546537f381Sas *
155cb174861Sjoyce mcintosh * Checks for integrity of file lock operation for the given range of file data.
1566537f381Sas * This is performed by applying lock rules with all the elements of the node
1576537f381Sas * lock list.
1586537f381Sas *
159cb174861Sjoyce mcintosh * Break shared (levelII) oplocks. If there is an exclusive oplock, it is
160cb174861Sjoyce mcintosh * owned by this ofile and therefore should not be broken.
161cb174861Sjoyce mcintosh *
1626537f381Sas * The function returns with new lock added if lock request is non-conflicting
1636537f381Sas * with existing range lock for the file. Otherwise smb request is filed
1646537f381Sas * without returning.
1656537f381Sas *
1666537f381Sas * NT_STATUS_SUCCESS - Lock range performed successfully.
1676537f381Sas * !NT_STATUS_SUCCESS - Error in lock range operation.
1686537f381Sas */
1696537f381Sas uint32_t
smb_lock_range(smb_request_t * sr,uint64_t start,uint64_t length,uint32_t pid,uint32_t locktype,uint32_t timeout)1706537f381Sas smb_lock_range(
1716537f381Sas smb_request_t *sr,
1726537f381Sas uint64_t start,
1736537f381Sas uint64_t length,
1740897f7fbSGordon Ross uint32_t pid,
1750897f7fbSGordon Ross uint32_t locktype,
1760897f7fbSGordon Ross uint32_t timeout)
1776537f381Sas {
1786537f381Sas smb_ofile_t *file = sr->fid_ofile;
1796537f381Sas smb_node_t *node = file->f_node;
1806537f381Sas smb_lock_t *lock;
1810897f7fbSGordon Ross smb_lock_t *conflict = NULL;
1820897f7fbSGordon Ross uint32_t result;
1830897f7fbSGordon Ross int rc;
184a90cf9f2SGordon Ross boolean_t lock_has_timeout =
185a90cf9f2SGordon Ross (timeout != 0 && timeout != UINT_MAX);
1866537f381Sas
1870897f7fbSGordon Ross if (length > 1 &&
1880897f7fbSGordon Ross (start + length) < start)
1890897f7fbSGordon Ross return (NT_STATUS_INVALID_LOCK_RANGE);
1900897f7fbSGordon Ross
1910897f7fbSGordon Ross #ifdef DEBUG
1920897f7fbSGordon Ross if (smb_lock_debug) {
1930897f7fbSGordon Ross cmn_err(CE_CONT, "smb_lock_range "
1940897f7fbSGordon Ross "off=0x%llx, len=0x%llx, "
1950897f7fbSGordon Ross "f=%p, pid=%d, typ=%d, tmo=%d\n",
1960897f7fbSGordon Ross (long long)start, (long long)length,
1970897f7fbSGordon Ross (void *)sr->fid_ofile, pid, locktype, timeout);
1980897f7fbSGordon Ross }
1990897f7fbSGordon Ross #endif
2000897f7fbSGordon Ross
2010897f7fbSGordon Ross lock = smb_lock_create(sr, start, length, pid, locktype, timeout);
2026537f381Sas
2036537f381Sas smb_llist_enter(&node->n_lock_list, RW_WRITER);
2046537f381Sas for (;;) {
2056537f381Sas
2066537f381Sas /* Apply locking rules */
2070897f7fbSGordon Ross result = smb_lock_range_lckrules(file, lock, &conflict);
2080897f7fbSGordon Ross switch (result) {
2090897f7fbSGordon Ross case NT_STATUS_LOCK_NOT_GRANTED: /* conflict! */
2100897f7fbSGordon Ross /* may need to wait */
2116537f381Sas break;
2120897f7fbSGordon Ross case NT_STATUS_SUCCESS:
2130897f7fbSGordon Ross case NT_STATUS_FILE_CLOSED:
2140897f7fbSGordon Ross goto break_loop;
2150897f7fbSGordon Ross default:
2160897f7fbSGordon Ross cmn_err(CE_CONT, "smb_lock_range1, status 0x%x\n",
2170897f7fbSGordon Ross result);
2180897f7fbSGordon Ross goto break_loop;
2196537f381Sas }
2200897f7fbSGordon Ross if (timeout == 0)
2210897f7fbSGordon Ross goto break_loop;
2226537f381Sas
2236537f381Sas /*
2246537f381Sas * Call smb_lock_wait holding write lock for
2256537f381Sas * node lock list. smb_lock_wait will release
2260897f7fbSGordon Ross * the node list lock if it blocks, so after
2270897f7fbSGordon Ross * the call, (*conflict) may no longer exist.
2286537f381Sas */
2290897f7fbSGordon Ross result = smb_lock_wait(sr, lock, conflict);
2300897f7fbSGordon Ross conflict = NULL;
2310897f7fbSGordon Ross switch (result) {
2320897f7fbSGordon Ross case NT_STATUS_SUCCESS:
2330897f7fbSGordon Ross /* conflict gone, try again */
2346537f381Sas break;
2350897f7fbSGordon Ross case NT_STATUS_TIMEOUT:
2360897f7fbSGordon Ross /* try just once more */
2376537f381Sas timeout = 0;
2380897f7fbSGordon Ross break;
2390897f7fbSGordon Ross case NT_STATUS_CANCELLED:
2400897f7fbSGordon Ross case NT_STATUS_FILE_CLOSED:
2410897f7fbSGordon Ross goto break_loop;
2420897f7fbSGordon Ross default:
2430897f7fbSGordon Ross cmn_err(CE_CONT, "smb_lock_range2, status 0x%x\n",
2440897f7fbSGordon Ross result);
2450897f7fbSGordon Ross goto break_loop;
2460897f7fbSGordon Ross }
2476537f381Sas }
2486537f381Sas
2490897f7fbSGordon Ross break_loop:
2506537f381Sas lock->l_blocked_by = NULL;
2516537f381Sas
2526537f381Sas if (result != NT_STATUS_SUCCESS) {
2530897f7fbSGordon Ross if (result == NT_STATUS_FILE_CLOSED)
2540897f7fbSGordon Ross result = NT_STATUS_RANGE_NOT_LOCKED;
2550897f7fbSGordon Ross
2566537f381Sas /*
2576537f381Sas * Under certain conditions NT_STATUS_FILE_LOCK_CONFLICT
2586537f381Sas * should be returned instead of NT_STATUS_LOCK_NOT_GRANTED.
259a90cf9f2SGordon Ross * All of this appears to be specific to SMB1
2606537f381Sas */
261a90cf9f2SGordon Ross if (sr->session->dialect <= NT_LM_0_12 &&
262a90cf9f2SGordon Ross result == NT_STATUS_LOCK_NOT_GRANTED) {
2636537f381Sas /*
2646537f381Sas * Locks with timeouts always return
2656537f381Sas * NT_STATUS_FILE_LOCK_CONFLICT
2666537f381Sas */
267c8ec8eeaSjose borrego if (lock_has_timeout)
2686537f381Sas result = NT_STATUS_FILE_LOCK_CONFLICT;
2696537f381Sas
2706537f381Sas /*
2716537f381Sas * Locks starting higher than 0xef000000 that do not
2726537f381Sas * have the MSB set always return
2736537f381Sas * NT_STATUS_FILE_LOCK_CONFLICT
2746537f381Sas */
2756537f381Sas if ((lock->l_start >= 0xef000000) &&
2766537f381Sas !(lock->l_start & (1ULL << 63))) {
2776537f381Sas result = NT_STATUS_FILE_LOCK_CONFLICT;
2786537f381Sas }
2796537f381Sas
2806537f381Sas /*
2816537f381Sas * If the last lock attempt to fail on this file handle
2826537f381Sas * started at the same offset as this one then return
2836537f381Sas * NT_STATUS_FILE_LOCK_CONFLICT
2846537f381Sas */
2856537f381Sas mutex_enter(&file->f_mutex);
2866537f381Sas if ((file->f_flags & SMB_OFLAGS_LLF_POS_VALID) &&
2876537f381Sas (lock->l_start == file->f_llf_pos)) {
2886537f381Sas result = NT_STATUS_FILE_LOCK_CONFLICT;
2896537f381Sas }
2906537f381Sas mutex_exit(&file->f_mutex);
2916537f381Sas }
2926537f381Sas
2936537f381Sas /* Update last lock failed offset */
2946537f381Sas mutex_enter(&file->f_mutex);
2956537f381Sas file->f_llf_pos = lock->l_start;
2966537f381Sas file->f_flags |= SMB_OFLAGS_LLF_POS_VALID;
2976537f381Sas mutex_exit(&file->f_mutex);
2986537f381Sas
2996537f381Sas smb_lock_free(lock);
3006537f381Sas } else {
3016537f381Sas /*
3026537f381Sas * don't insert into the CIFS lock list unless the
3036537f381Sas * posix lock worked
3046537f381Sas */
3050897f7fbSGordon Ross rc = smb_fsop_frlock(node, lock, B_FALSE, sr->user_cr);
3060897f7fbSGordon Ross if (rc != 0) {
3070897f7fbSGordon Ross #ifdef DEBUG
3080897f7fbSGordon Ross if (smb_lock_debug)
3090897f7fbSGordon Ross cmn_err(CE_CONT, "fop_frlock, err=%d\n", rc);
3100897f7fbSGordon Ross #endif
3116537f381Sas result = NT_STATUS_FILE_LOCK_CONFLICT;
3120897f7fbSGordon Ross } else {
3130897f7fbSGordon Ross /*
3140897f7fbSGordon Ross * We want unlock to find exclusive locks before
3150897f7fbSGordon Ross * shared locks, so insert those at the head.
3160897f7fbSGordon Ross */
3170897f7fbSGordon Ross if (lock->l_type == SMB_LOCK_TYPE_READWRITE)
3180897f7fbSGordon Ross smb_llist_insert_head(&node->n_lock_list, lock);
3190897f7fbSGordon Ross else
3200897f7fbSGordon Ross smb_llist_insert_tail(&node->n_lock_list, lock);
3210897f7fbSGordon Ross }
3220897f7fbSGordon Ross }
3230897f7fbSGordon Ross
3240897f7fbSGordon Ross #ifdef DEBUG
3250897f7fbSGordon Ross if (smb_lock_debug && result != 0) {
3260897f7fbSGordon Ross cmn_err(CE_CONT, "lock failed, 0x%x\n", result);
3270897f7fbSGordon Ross smb_lock_dumpnode(node);
3286537f381Sas }
3290897f7fbSGordon Ross #endif
3300897f7fbSGordon Ross
3316537f381Sas smb_llist_exit(&node->n_lock_list);
3326537f381Sas
33394047d49SGordon Ross if (result == NT_STATUS_SUCCESS) {
33494047d49SGordon Ross /* This revokes read cache delegations. */
33594047d49SGordon Ross (void) smb_oplock_break_WRITE(node, file);
33694047d49SGordon Ross }
337cb174861Sjoyce mcintosh
3386537f381Sas return (result);
3396537f381Sas }
3406537f381Sas
3416537f381Sas /*
3426537f381Sas * smb_lock_range_access
3436537f381Sas *
3446537f381Sas * scans node lock list
3456537f381Sas * to check if there is any overlapping lock. Overlapping
3466537f381Sas * lock is allowed only under same session and client pid.
3476537f381Sas *
3486537f381Sas * Return values
3496537f381Sas * NT_STATUS_SUCCESS lock access granted.
35094047d49SGordon Ross * NT_STATUS_FILE_LOCK_CONFLICT access denied due to lock conflict.
3516537f381Sas */
3526537f381Sas int
smb_lock_range_access(smb_request_t * sr,smb_node_t * node,uint64_t start,uint64_t length,boolean_t will_write)3536537f381Sas smb_lock_range_access(
3546537f381Sas smb_request_t *sr,
3556537f381Sas smb_node_t *node,
3566537f381Sas uint64_t start,
3570897f7fbSGordon Ross uint64_t length,
3586537f381Sas boolean_t will_write)
3596537f381Sas {
3606537f381Sas smb_lock_t *lock;
3616537f381Sas smb_llist_t *llist;
3620897f7fbSGordon Ross uint32_t lk_pid = 0;
3636537f381Sas int status = NT_STATUS_SUCCESS;
3646537f381Sas
3650897f7fbSGordon Ross if (length == 0)
3660897f7fbSGordon Ross return (status);
3670897f7fbSGordon Ross
3680897f7fbSGordon Ross /*
3690897f7fbSGordon Ross * What PID to use for lock conflict checks?
3700897f7fbSGordon Ross * SMB2 locking ignores PIDs (have lk_pid=0)
3710897f7fbSGordon Ross * SMB1 uses low 16 bits of sr->smb_pid
3720897f7fbSGordon Ross */
3730897f7fbSGordon Ross if (sr->session->dialect < SMB_VERS_2_BASE)
3740897f7fbSGordon Ross lk_pid = sr->smb_pid & 0xFFFF;
3750897f7fbSGordon Ross
3766537f381Sas llist = &node->n_lock_list;
3776537f381Sas smb_llist_enter(llist, RW_READER);
3786537f381Sas /* Search for any applicable lock */
3796537f381Sas for (lock = smb_llist_head(llist);
3806537f381Sas lock != NULL;
3816537f381Sas lock = smb_llist_next(llist, lock)) {
3826537f381Sas
3836537f381Sas if (!smb_lock_range_overlap(lock, start, length))
3846537f381Sas /* Lock does not overlap */
3856537f381Sas continue;
3866537f381Sas
3876537f381Sas if (lock->l_type == SMB_LOCK_TYPE_READONLY && !will_write)
3886537f381Sas continue;
3896537f381Sas
3906537f381Sas if (lock->l_type == SMB_LOCK_TYPE_READWRITE &&
3910897f7fbSGordon Ross lock->l_file == sr->fid_ofile &&
3920897f7fbSGordon Ross lock->l_pid == lk_pid)
3936537f381Sas continue;
3946537f381Sas
3950897f7fbSGordon Ross #ifdef DEBUG
3960897f7fbSGordon Ross if (smb_lock_debug) {
3970897f7fbSGordon Ross cmn_err(CE_CONT, "smb_lock_range_access conflict: "
3980897f7fbSGordon Ross "off=0x%llx, len=0x%llx, "
3990897f7fbSGordon Ross "f=%p, pid=%d, typ=%d\n",
4000897f7fbSGordon Ross (long long)lock->l_start,
4010897f7fbSGordon Ross (long long)lock->l_length,
4020897f7fbSGordon Ross (void *)lock->l_file,
4030897f7fbSGordon Ross lock->l_pid, lock->l_type);
4040897f7fbSGordon Ross }
4050897f7fbSGordon Ross #endif
4066537f381Sas status = NT_STATUS_FILE_LOCK_CONFLICT;
4076537f381Sas break;
4086537f381Sas }
4096537f381Sas smb_llist_exit(llist);
4106537f381Sas return (status);
4116537f381Sas }
4126537f381Sas
4130897f7fbSGordon Ross /*
4140897f7fbSGordon Ross * The ofile is being closed. Wake any waiting locks and
4150897f7fbSGordon Ross * clear any granted locks.
4160897f7fbSGordon Ross */
4176537f381Sas void
smb_node_destroy_lock_by_ofile(smb_node_t * node,smb_ofile_t * file)4186537f381Sas smb_node_destroy_lock_by_ofile(smb_node_t *node, smb_ofile_t *file)
4196537f381Sas {
420811599a4SMatt Barden cred_t *kcr = zone_kcred();
4216537f381Sas smb_lock_t *lock;
4226537f381Sas smb_lock_t *nxtl;
4236537f381Sas list_t destroy_list;
4246537f381Sas
4252c2961f8Sjose borrego SMB_NODE_VALID(node);
4266537f381Sas ASSERT(node->n_refcnt);
4276537f381Sas
4280897f7fbSGordon Ross /*
4290897f7fbSGordon Ross * Cancel any waiting locks for this ofile
4300897f7fbSGordon Ross */
4310897f7fbSGordon Ross smb_llist_enter(&node->n_wlock_list, RW_READER);
4320897f7fbSGordon Ross for (lock = smb_llist_head(&node->n_wlock_list);
4330897f7fbSGordon Ross lock != NULL;
4340897f7fbSGordon Ross lock = smb_llist_next(&node->n_wlock_list, lock)) {
4350897f7fbSGordon Ross
4360897f7fbSGordon Ross if (lock->l_file == file) {
4370897f7fbSGordon Ross mutex_enter(&lock->l_mutex);
4380897f7fbSGordon Ross lock->l_blocked_by = NULL;
4390897f7fbSGordon Ross lock->l_flags |= SMB_LOCK_FLAG_CLOSED;
4400897f7fbSGordon Ross cv_broadcast(&lock->l_cv);
4410897f7fbSGordon Ross mutex_exit(&lock->l_mutex);
4420897f7fbSGordon Ross }
4430897f7fbSGordon Ross }
4440897f7fbSGordon Ross smb_llist_exit(&node->n_wlock_list);
4450897f7fbSGordon Ross
4466537f381Sas /*
4476537f381Sas * Move locks matching the specified file from the node->n_lock_list
4486537f381Sas * to a temporary list (holding the lock the entire time) then
4496537f381Sas * destroy all the matching locks. We can't call smb_lock_destroy
4506537f381Sas * while we are holding the lock for node->n_lock_list because we will
4516537f381Sas * deadlock and we can't drop the lock because the list contents might
4526537f381Sas * change (for example nxtl might get removed on another thread).
4536537f381Sas */
4546537f381Sas list_create(&destroy_list, sizeof (smb_lock_t),
4556537f381Sas offsetof(smb_lock_t, l_lnd));
4566537f381Sas
4576537f381Sas smb_llist_enter(&node->n_lock_list, RW_WRITER);
4586537f381Sas lock = smb_llist_head(&node->n_lock_list);
4596537f381Sas while (lock) {
4606537f381Sas nxtl = smb_llist_next(&node->n_lock_list, lock);
4616537f381Sas if (lock->l_file == file) {
4626537f381Sas smb_llist_remove(&node->n_lock_list, lock);
463811599a4SMatt Barden smb_lock_posix_unlock(node, lock, kcr);
4646537f381Sas list_insert_tail(&destroy_list, lock);
4656537f381Sas }
4666537f381Sas lock = nxtl;
4676537f381Sas }
4686537f381Sas smb_llist_exit(&node->n_lock_list);
4696537f381Sas
4706537f381Sas lock = list_head(&destroy_list);
4716537f381Sas while (lock) {
4726537f381Sas nxtl = list_next(&destroy_list, lock);
4736537f381Sas list_remove(&destroy_list, lock);
4746537f381Sas smb_lock_destroy(lock);
4756537f381Sas lock = nxtl;
4766537f381Sas }
4776537f381Sas
4786537f381Sas list_destroy(&destroy_list);
4796537f381Sas }
4806537f381Sas
4810897f7fbSGordon Ross /*
4820897f7fbSGordon Ross * Cause a waiting lock to stop waiting and return an error.
4830897f7fbSGordon Ross * returns same status codes as unlock:
4840897f7fbSGordon Ross * NT_STATUS_SUCCESS, NT_STATUS_RANGE_NOT_LOCKED
4850897f7fbSGordon Ross */
4860897f7fbSGordon Ross uint32_t
smb_lock_range_cancel(smb_request_t * sr,uint64_t start,uint64_t length,uint32_t pid)4870897f7fbSGordon Ross smb_lock_range_cancel(smb_request_t *sr,
4880897f7fbSGordon Ross uint64_t start, uint64_t length, uint32_t pid)
4890897f7fbSGordon Ross {
4900897f7fbSGordon Ross smb_node_t *node;
4910897f7fbSGordon Ross smb_lock_t *lock;
4920897f7fbSGordon Ross uint32_t status = NT_STATUS_RANGE_NOT_LOCKED;
4930897f7fbSGordon Ross int cnt = 0;
4940897f7fbSGordon Ross
4950897f7fbSGordon Ross node = sr->fid_ofile->f_node;
4960897f7fbSGordon Ross
4970897f7fbSGordon Ross smb_llist_enter(&node->n_wlock_list, RW_READER);
4980897f7fbSGordon Ross
4990897f7fbSGordon Ross #ifdef DEBUG
5000897f7fbSGordon Ross if (smb_lock_debug) {
5010897f7fbSGordon Ross cmn_err(CE_CONT, "smb_lock_range_cancel:\n"
5020897f7fbSGordon Ross "\tstart=0x%llx, len=0x%llx, of=%p, pid=%d\n",
5030897f7fbSGordon Ross (long long)start, (long long)length,
5040897f7fbSGordon Ross (void *)sr->fid_ofile, pid);
5050897f7fbSGordon Ross }
5060897f7fbSGordon Ross #endif
5070897f7fbSGordon Ross
5080897f7fbSGordon Ross for (lock = smb_llist_head(&node->n_wlock_list);
5090897f7fbSGordon Ross lock != NULL;
5100897f7fbSGordon Ross lock = smb_llist_next(&node->n_wlock_list, lock)) {
5110897f7fbSGordon Ross
5120897f7fbSGordon Ross if ((start == lock->l_start) &&
5130897f7fbSGordon Ross (length == lock->l_length) &&
5140897f7fbSGordon Ross lock->l_file == sr->fid_ofile &&
5150897f7fbSGordon Ross lock->l_pid == pid) {
5160897f7fbSGordon Ross
5170897f7fbSGordon Ross mutex_enter(&lock->l_mutex);
5180897f7fbSGordon Ross lock->l_blocked_by = NULL;
5190897f7fbSGordon Ross lock->l_flags |= SMB_LOCK_FLAG_CANCELLED;
5200897f7fbSGordon Ross cv_broadcast(&lock->l_cv);
5210897f7fbSGordon Ross mutex_exit(&lock->l_mutex);
5220897f7fbSGordon Ross status = NT_STATUS_SUCCESS;
5230897f7fbSGordon Ross cnt++;
5240897f7fbSGordon Ross }
5250897f7fbSGordon Ross }
5260897f7fbSGordon Ross
5270897f7fbSGordon Ross #ifdef DEBUG
5280897f7fbSGordon Ross if (smb_lock_debug && cnt != 1) {
5290897f7fbSGordon Ross cmn_err(CE_CONT, "cancel found %d\n", cnt);
5300897f7fbSGordon Ross smb_lock_dumpnode(node);
5310897f7fbSGordon Ross }
5320897f7fbSGordon Ross #endif
5330897f7fbSGordon Ross
5340897f7fbSGordon Ross smb_llist_exit(&node->n_wlock_list);
5350897f7fbSGordon Ross
5360897f7fbSGordon Ross return (status);
5370897f7fbSGordon Ross }
5380897f7fbSGordon Ross
5396537f381Sas void
smb_lock_range_error(smb_request_t * sr,uint32_t status32)5406537f381Sas smb_lock_range_error(smb_request_t *sr, uint32_t status32)
5416537f381Sas {
5426537f381Sas uint16_t errcode;
5436537f381Sas
5440897f7fbSGordon Ross if (status32 == NT_STATUS_CANCELLED) {
5450897f7fbSGordon Ross status32 = NT_STATUS_FILE_LOCK_CONFLICT;
5460897f7fbSGordon Ross errcode = ERROR_LOCK_VIOLATION;
5470897f7fbSGordon Ross } else {
5486537f381Sas errcode = ERRlock;
5490897f7fbSGordon Ross }
5506537f381Sas
5516537f381Sas smbsr_error(sr, status32, ERRDOS, errcode);
5526537f381Sas }
5536537f381Sas
5546537f381Sas /*
555bc7c423fSGordon Ross * An SMB variant of nbl_conflict().
556bc7c423fSGordon Ross *
557bc7c423fSGordon Ross * SMB prevents remove or rename when conflicting locks exist
558bc7c423fSGordon Ross * (unlike NFS, which is why we can't just use nbl_conflict).
5596537f381Sas *
560bc7c423fSGordon Ross * Returns:
561bc7c423fSGordon Ross * NT_STATUS_SHARING_VIOLATION - nbl_share_conflict
562bc7c423fSGordon Ross * NT_STATUS_FILE_LOCK_CONFLICT - nbl_lock_conflict
563bc7c423fSGordon Ross * NT_STATUS_SUCCESS - operation can proceed
5646537f381Sas *
565bc7c423fSGordon Ross * NB: This function used to also check the list of ofiles,
566bc7c423fSGordon Ross * via: smb_lock_range_access() but we _can't_ do that here
567bc7c423fSGordon Ross * due to lock order constraints between node->n_lock_list
568bc7c423fSGordon Ross * and node->vp->vnbllock (taken via nvl_start_crit).
569bc7c423fSGordon Ross * They must be taken in that order, and in here, we
570bc7c423fSGordon Ross * already hold vp->vnbllock.
5716537f381Sas */
5726537f381Sas DWORD
smb_nbl_conflict(smb_node_t * node,uint64_t off,uint64_t len,nbl_op_t op)573bc7c423fSGordon Ross smb_nbl_conflict(smb_node_t *node, uint64_t off, uint64_t len, nbl_op_t op)
5746537f381Sas {
5756537f381Sas int svmand;
5766537f381Sas
5772c2961f8Sjose borrego SMB_NODE_VALID(node);
5786537f381Sas ASSERT(smb_node_in_crit(node));
579bc7c423fSGordon Ross ASSERT(op == NBL_READ || op == NBL_WRITE || op == NBL_READWRITE ||
580bc7c423fSGordon Ross op == NBL_REMOVE || op == NBL_RENAME);
5816537f381Sas
582037cac00Sjoyce mcintosh if (smb_node_is_dir(node))
5836537f381Sas return (NT_STATUS_SUCCESS);
5846537f381Sas
585bc7c423fSGordon Ross if (nbl_share_conflict(node->vp, op, &smb_ct))
586bc7c423fSGordon Ross return (NT_STATUS_SHARING_VIOLATION);
587dc20a302Sas
588bc7c423fSGordon Ross /*
589bc7c423fSGordon Ross * When checking for lock conflicts, rename and remove
590bc7c423fSGordon Ross * are not allowed, so treat those as read/write.
591bc7c423fSGordon Ross */
592bc7c423fSGordon Ross if (op == NBL_RENAME || op == NBL_REMOVE)
593bc7c423fSGordon Ross op = NBL_READWRITE;
594da6c28aaSamw
5958622ec45SGordon Ross if (nbl_svmand(node->vp, zone_kcred(), &svmand))
596bc7c423fSGordon Ross svmand = 1;
597da6c28aaSamw
598bc7c423fSGordon Ross if (nbl_lock_conflict(node->vp, op, off, len, svmand, &smb_ct))
599c8ec8eeaSjose borrego return (NT_STATUS_FILE_LOCK_CONFLICT);
600da6c28aaSamw
6016537f381Sas return (NT_STATUS_SUCCESS);
6026537f381Sas }
603da6c28aaSamw
6046537f381Sas /*
6056537f381Sas * smb_lock_posix_unlock
6066537f381Sas *
6076537f381Sas * checks if the current unlock request is in another lock and repeatedly calls
6086537f381Sas * smb_is_range_unlocked on a sliding basis to unlock all bits of the lock
6096537f381Sas * that are not in other locks
6106537f381Sas *
6116537f381Sas */
612c8ec8eeaSjose borrego static void
smb_lock_posix_unlock(smb_node_t * node,smb_lock_t * lock,cred_t * cr)6136537f381Sas smb_lock_posix_unlock(smb_node_t *node, smb_lock_t *lock, cred_t *cr)
6146537f381Sas {
6156537f381Sas uint64_t new_mark;
6166537f381Sas uint64_t unlock_start;
6176537f381Sas uint64_t unlock_end;
6186537f381Sas smb_lock_t new_unlock;
6196537f381Sas smb_llist_t *llist;
620c8ec8eeaSjose borrego boolean_t can_unlock;
6216537f381Sas
6226537f381Sas new_mark = 0;
6236537f381Sas unlock_start = lock->l_start;
6246537f381Sas unlock_end = unlock_start + lock->l_length;
6256537f381Sas llist = &node->n_lock_list;
626da6c28aaSamw
6276537f381Sas for (;;) {
628c8ec8eeaSjose borrego can_unlock = smb_is_range_unlocked(unlock_start, unlock_end,
629c8ec8eeaSjose borrego lock->l_file->f_uniqid, llist, &new_mark);
630c8ec8eeaSjose borrego if (can_unlock) {
6316537f381Sas if (new_mark) {
6326537f381Sas new_unlock = *lock;
6336537f381Sas new_unlock.l_start = unlock_start;
6346537f381Sas new_unlock.l_length = new_mark - unlock_start;
6358c10a865Sas (void) smb_fsop_frlock(node, &new_unlock,
6366537f381Sas B_TRUE, cr);
6376537f381Sas unlock_start = new_mark;
6386537f381Sas } else {
6396537f381Sas new_unlock = *lock;
6406537f381Sas new_unlock.l_start = unlock_start;
6416537f381Sas new_unlock.l_length = unlock_end - unlock_start;
6428c10a865Sas (void) smb_fsop_frlock(node, &new_unlock,
6436537f381Sas B_TRUE, cr);
6446537f381Sas break;
6456537f381Sas }
6466537f381Sas } else if (new_mark) {
6476537f381Sas unlock_start = new_mark;
6486537f381Sas } else {
6496537f381Sas break;
6506537f381Sas }
6516537f381Sas }
6526537f381Sas }
653da6c28aaSamw
654da6c28aaSamw /*
655da6c28aaSamw * smb_lock_range_overlap
656da6c28aaSamw *
657c8ec8eeaSjose borrego * Checks if lock range(start, length) overlaps range in lock structure.
658c8ec8eeaSjose borrego *
659c8ec8eeaSjose borrego * Zero-length byte range locks actually affect no single byte of the stream,
660c8ec8eeaSjose borrego * meaning they can still be accessed even with such locks in place. However,
661c8ec8eeaSjose borrego * they do conflict with other ranges in the following manner:
662c8ec8eeaSjose borrego * conflict will only exist if the positive-length range contains the
663c8ec8eeaSjose borrego * zero-length range's offset but doesn't start at it
664da6c28aaSamw *
665da6c28aaSamw * return values:
666da6c28aaSamw * 0 - Lock range doesn't overlap
667da6c28aaSamw * 1 - Lock range overlaps.
668da6c28aaSamw */
669da6c28aaSamw
670da6c28aaSamw #define RANGE_NO_OVERLAP 0
671da6c28aaSamw #define RANGE_OVERLAP 1
672da6c28aaSamw
673da6c28aaSamw static int
smb_lock_range_overlap(struct smb_lock * lock,uint64_t start,uint64_t length)674da6c28aaSamw smb_lock_range_overlap(struct smb_lock *lock, uint64_t start, uint64_t length)
675da6c28aaSamw {
676c8ec8eeaSjose borrego if (length == 0) {
677c8ec8eeaSjose borrego if ((lock->l_start < start) &&
678c8ec8eeaSjose borrego ((lock->l_start + lock->l_length) > start))
679c8ec8eeaSjose borrego return (RANGE_OVERLAP);
680c8ec8eeaSjose borrego
681da6c28aaSamw return (RANGE_NO_OVERLAP);
682c8ec8eeaSjose borrego }
683da6c28aaSamw
6843ad684d6Sjb /* The following test is intended to catch roll over locks. */
6853ad684d6Sjb if ((start == lock->l_start) && (length == lock->l_length))
6863ad684d6Sjb return (RANGE_OVERLAP);
6873ad684d6Sjb
688da6c28aaSamw if (start < lock->l_start) {
689da6c28aaSamw if (start + length > lock->l_start)
690da6c28aaSamw return (RANGE_OVERLAP);
691da6c28aaSamw } else if (start < lock->l_start + lock->l_length)
692da6c28aaSamw return (RANGE_OVERLAP);
693da6c28aaSamw
694da6c28aaSamw return (RANGE_NO_OVERLAP);
695da6c28aaSamw }
696da6c28aaSamw
697da6c28aaSamw /*
698da6c28aaSamw * smb_lock_range_lckrules
699da6c28aaSamw *
700da6c28aaSamw * Lock range rules:
701da6c28aaSamw * 1. Overlapping read locks are allowed if the
702da6c28aaSamw * current locks in the region are only read locks
703da6c28aaSamw * irrespective of pid of smb client issuing lock request.
704da6c28aaSamw *
705da6c28aaSamw * 2. Read lock in the overlapped region of write lock
7060897f7fbSGordon Ross * are allowed if the previous lock is performed by the
707da6c28aaSamw * same pid and connection.
708da6c28aaSamw *
709da6c28aaSamw * return status:
7100897f7fbSGordon Ross * NT_STATUS_SUCCESS - Input lock range conforms to lock rules.
711da6c28aaSamw * NT_STATUS_LOCK_NOT_GRANTED - Input lock conflicts lock rules.
7120897f7fbSGordon Ross * NT_STATUS_FILE_CLOSED
713da6c28aaSamw */
714da6c28aaSamw static uint32_t
smb_lock_range_lckrules(smb_ofile_t * file,smb_lock_t * dlock,smb_lock_t ** conflictp)715da6c28aaSamw smb_lock_range_lckrules(
716da6c28aaSamw smb_ofile_t *file,
7170897f7fbSGordon Ross smb_lock_t *dlock, /* desired lock */
7180897f7fbSGordon Ross smb_lock_t **conflictp)
719da6c28aaSamw {
7200897f7fbSGordon Ross smb_node_t *node = file->f_node;
721da6c28aaSamw smb_lock_t *lock;
722da6c28aaSamw uint32_t status = NT_STATUS_SUCCESS;
723da6c28aaSamw
724da6c28aaSamw /* Check if file is closed */
725da6c28aaSamw if (!smb_ofile_is_open(file)) {
7260897f7fbSGordon Ross return (NT_STATUS_FILE_CLOSED);
727da6c28aaSamw }
728da6c28aaSamw
729da6c28aaSamw /* Caller must hold lock for node->n_lock_list */
730da6c28aaSamw for (lock = smb_llist_head(&node->n_lock_list);
731da6c28aaSamw lock != NULL;
732da6c28aaSamw lock = smb_llist_next(&node->n_lock_list, lock)) {
733da6c28aaSamw
734da6c28aaSamw if (!smb_lock_range_overlap(lock, dlock->l_start,
735da6c28aaSamw dlock->l_length))
736da6c28aaSamw continue;
737da6c28aaSamw
738da6c28aaSamw /*
739da6c28aaSamw * Check to see if lock in the overlapping record
740da6c28aaSamw * is only read lock. Current finding is read
741da6c28aaSamw * locks can overlapped irrespective of pids.
742da6c28aaSamw */
743da6c28aaSamw if ((lock->l_type == SMB_LOCK_TYPE_READONLY) &&
744da6c28aaSamw (dlock->l_type == SMB_LOCK_TYPE_READONLY)) {
745da6c28aaSamw continue;
746da6c28aaSamw }
747da6c28aaSamw
748da6c28aaSamw /*
749da6c28aaSamw * When the read lock overlaps write lock, check if
750da6c28aaSamw * allowed.
751da6c28aaSamw */
752da6c28aaSamw if ((dlock->l_type == SMB_LOCK_TYPE_READONLY) &&
753da6c28aaSamw !(lock->l_type == SMB_LOCK_TYPE_READONLY)) {
7540897f7fbSGordon Ross if (lock->l_file == dlock->l_file &&
7550897f7fbSGordon Ross lock->l_pid == dlock->l_pid) {
756da6c28aaSamw continue;
757da6c28aaSamw }
758da6c28aaSamw }
759da6c28aaSamw
760da6c28aaSamw /* Conflict in overlapping lock element */
7610897f7fbSGordon Ross *conflictp = lock;
762da6c28aaSamw status = NT_STATUS_LOCK_NOT_GRANTED;
763da6c28aaSamw break;
764da6c28aaSamw }
765da6c28aaSamw
766da6c28aaSamw return (status);
767da6c28aaSamw }
768da6c28aaSamw
7695677e049SGordon Ross /*
7705677e049SGordon Ross * Cancel method for smb_lock_wait()
7715677e049SGordon Ross *
7725677e049SGordon Ross * This request is waiting on a lock. Wakeup everything
7735677e049SGordon Ross * waiting on the lock so that the relevant thread regains
7745677e049SGordon Ross * control and notices that is has been cancelled. The
7755677e049SGordon Ross * other lock request threads waiting on this lock will go
7765677e049SGordon Ross * back to sleep when they discover they are still blocked.
7775677e049SGordon Ross */
7785677e049SGordon Ross static void
smb_lock_cancel_sr(smb_request_t * sr)7790897f7fbSGordon Ross smb_lock_cancel_sr(smb_request_t *sr)
7805677e049SGordon Ross {
7815677e049SGordon Ross smb_lock_t *lock = sr->cancel_arg2;
7825677e049SGordon Ross
7830897f7fbSGordon Ross ASSERT(lock->l_magic == SMB_LOCK_MAGIC);
7840897f7fbSGordon Ross mutex_enter(&lock->l_mutex);
7850897f7fbSGordon Ross lock->l_blocked_by = NULL;
7860897f7fbSGordon Ross lock->l_flags |= SMB_LOCK_FLAG_CANCELLED;
7875677e049SGordon Ross cv_broadcast(&lock->l_cv);
7880897f7fbSGordon Ross mutex_exit(&lock->l_mutex);
7895677e049SGordon Ross }
7905677e049SGordon Ross
791da6c28aaSamw /*
792da6c28aaSamw * smb_lock_wait
793da6c28aaSamw *
794da6c28aaSamw * Wait operation for smb overlapping lock to be released. Caller must hold
795da6c28aaSamw * write lock for node->n_lock_list so that the set of active locks can't
796da6c28aaSamw * change unexpectedly. The lock for node->n_lock_list will be released
797da6c28aaSamw * within this function during the sleep after the lock dependency has
798da6c28aaSamw * been recorded.
799da6c28aaSamw *
8000897f7fbSGordon Ross * Returns NT_STATUS_SUCCESS when the lock can be granted,
8010897f7fbSGordon Ross * otherwise NT_STATUS_CANCELLED, etc.
802da6c28aaSamw */
8030897f7fbSGordon Ross static uint32_t
smb_lock_wait(smb_request_t * sr,smb_lock_t * lock,smb_lock_t * conflict)8040897f7fbSGordon Ross smb_lock_wait(smb_request_t *sr, smb_lock_t *lock, smb_lock_t *conflict)
805da6c28aaSamw {
8060897f7fbSGordon Ross smb_node_t *node;
8070897f7fbSGordon Ross clock_t rc;
8080897f7fbSGordon Ross uint32_t status = NT_STATUS_SUCCESS;
809da6c28aaSamw
8100897f7fbSGordon Ross node = lock->l_file->f_node;
8110897f7fbSGordon Ross ASSERT(node == conflict->l_file->f_node);
812da6c28aaSamw
8135677e049SGordon Ross /*
8140897f7fbSGordon Ross * Let the blocked lock (lock) l_blocked_by point to the
8150897f7fbSGordon Ross * conflicting lock (conflict), and increment a count of
8160897f7fbSGordon Ross * conflicts with the latter. When the conflicting lock
8170897f7fbSGordon Ross * is destroyed, we'll search the list of waiting locks
8180897f7fbSGordon Ross * (on the node) and wake any with l_blocked_by ==
8190897f7fbSGordon Ross * the formerly conflicting lock.
8205677e049SGordon Ross */
8210897f7fbSGordon Ross mutex_enter(&lock->l_mutex);
8220897f7fbSGordon Ross lock->l_blocked_by = conflict;
8230897f7fbSGordon Ross mutex_exit(&lock->l_mutex);
8240897f7fbSGordon Ross
8250897f7fbSGordon Ross mutex_enter(&conflict->l_mutex);
8260897f7fbSGordon Ross conflict->l_conflicts++;
8270897f7fbSGordon Ross mutex_exit(&conflict->l_mutex);
8280897f7fbSGordon Ross
8290897f7fbSGordon Ross /*
8300897f7fbSGordon Ross * Put the blocked lock on the waiting list.
8310897f7fbSGordon Ross */
8320897f7fbSGordon Ross smb_llist_enter(&node->n_wlock_list, RW_WRITER);
8330897f7fbSGordon Ross smb_llist_insert_tail(&node->n_wlock_list, lock);
8340897f7fbSGordon Ross smb_llist_exit(&node->n_wlock_list);
8350897f7fbSGordon Ross
8360897f7fbSGordon Ross #ifdef DEBUG
8370897f7fbSGordon Ross if (smb_lock_debug) {
8380897f7fbSGordon Ross cmn_err(CE_CONT, "smb_lock_wait: lock=%p conflict=%p\n",
8390897f7fbSGordon Ross (void *)lock, (void *)conflict);
8400897f7fbSGordon Ross smb_lock_dumpnode(node);
8410897f7fbSGordon Ross }
8420897f7fbSGordon Ross #endif
843da6c28aaSamw
8445677e049SGordon Ross /*
8450897f7fbSGordon Ross * We come in with n_lock_list already held, and keep
8460897f7fbSGordon Ross * that hold until we're done with conflict (are now).
8470897f7fbSGordon Ross * Drop that now, and retake later. Note that the lock
8480897f7fbSGordon Ross * (*conflict) may go away once we exit this list.
8495677e049SGordon Ross */
8500897f7fbSGordon Ross smb_llist_exit(&node->n_lock_list);
8510897f7fbSGordon Ross conflict = NULL;
852da6c28aaSamw
8530897f7fbSGordon Ross /*
854*66b505f1SGordon Ross * Prepare for cancellable lock wait.
855*66b505f1SGordon Ross *
856*66b505f1SGordon Ross * If cancelled, smb_lock_cancel_sr sets
857*66b505f1SGordon Ross * l_flags |= SMB_LOCK_FLAG_CANCELLED
8580897f7fbSGordon Ross */
8590897f7fbSGordon Ross mutex_enter(&sr->sr_mutex);
8600897f7fbSGordon Ross if (sr->sr_state == SMB_REQ_STATE_ACTIVE) {
8610897f7fbSGordon Ross sr->sr_state = SMB_REQ_STATE_WAITING_LOCK;
8620897f7fbSGordon Ross sr->cancel_method = smb_lock_cancel_sr;
8630897f7fbSGordon Ross sr->cancel_arg2 = lock;
8645677e049SGordon Ross } else {
8650897f7fbSGordon Ross status = NT_STATUS_CANCELLED;
8665677e049SGordon Ross }
8670897f7fbSGordon Ross mutex_exit(&sr->sr_mutex);
8686537f381Sas
8690897f7fbSGordon Ross /*
8700897f7fbSGordon Ross * Now we're ready to actually wait for the conflicting
8710897f7fbSGordon Ross * lock to be removed, or for the wait to be ended by
8720897f7fbSGordon Ross * an external cancel, or a timeout.
8730897f7fbSGordon Ross */
8740897f7fbSGordon Ross mutex_enter(&lock->l_mutex);
8750897f7fbSGordon Ross while (status == NT_STATUS_SUCCESS &&
8760897f7fbSGordon Ross lock->l_blocked_by != NULL) {
8770897f7fbSGordon Ross if (lock->l_flags & SMB_LOCK_FLAG_INDEFINITE) {
8780897f7fbSGordon Ross cv_wait(&lock->l_cv, &lock->l_mutex);
8790897f7fbSGordon Ross } else {
8800897f7fbSGordon Ross rc = cv_timedwait(&lock->l_cv,
8810897f7fbSGordon Ross &lock->l_mutex, lock->l_end_time);
8820897f7fbSGordon Ross if (rc < 0)
8830897f7fbSGordon Ross status = NT_STATUS_TIMEOUT;
8840897f7fbSGordon Ross }
8850897f7fbSGordon Ross }
8860897f7fbSGordon Ross if (status == NT_STATUS_SUCCESS) {
8870897f7fbSGordon Ross if (lock->l_flags & SMB_LOCK_FLAG_CANCELLED)
8880897f7fbSGordon Ross status = NT_STATUS_CANCELLED;
8890897f7fbSGordon Ross if (lock->l_flags & SMB_LOCK_FLAG_CLOSED)
8900897f7fbSGordon Ross status = NT_STATUS_FILE_CLOSED;
8910897f7fbSGordon Ross }
8920897f7fbSGordon Ross mutex_exit(&lock->l_mutex);
8935677e049SGordon Ross
8940897f7fbSGordon Ross /*
895*66b505f1SGordon Ross * Did we get the lock or were we cancelled?
8960897f7fbSGordon Ross */
8975677e049SGordon Ross mutex_enter(&sr->sr_mutex);
898*66b505f1SGordon Ross switch_state:
8995677e049SGordon Ross switch (sr->sr_state) {
9005677e049SGordon Ross case SMB_REQ_STATE_WAITING_LOCK:
9010897f7fbSGordon Ross /* Normal wakeup. Keep status from above. */
9025677e049SGordon Ross sr->sr_state = SMB_REQ_STATE_ACTIVE;
9035677e049SGordon Ross break;
9045677e049SGordon Ross case SMB_REQ_STATE_CANCEL_PENDING:
905*66b505f1SGordon Ross /* cancel_method running. wait. */
906*66b505f1SGordon Ross cv_wait(&sr->sr_st_cv, &sr->sr_mutex);
907*66b505f1SGordon Ross goto switch_state;
9085677e049SGordon Ross case SMB_REQ_STATE_CANCELLED:
909*66b505f1SGordon Ross /* Call should return an error. */
9100897f7fbSGordon Ross if (status == NT_STATUS_SUCCESS)
9110897f7fbSGordon Ross status = NT_STATUS_CANCELLED;
9126537f381Sas break;
9136537f381Sas default:
9146537f381Sas break;
915da6c28aaSamw }
916*66b505f1SGordon Ross sr->cancel_method = NULL;
917*66b505f1SGordon Ross sr->cancel_arg2 = NULL;
9186537f381Sas mutex_exit(&sr->sr_mutex);
919da6c28aaSamw
9200897f7fbSGordon Ross /* Return to the caller with n_lock_list held. */
9210897f7fbSGordon Ross smb_llist_enter(&node->n_lock_list, RW_WRITER);
9220897f7fbSGordon Ross
9230897f7fbSGordon Ross smb_llist_enter(&node->n_wlock_list, RW_WRITER);
9240897f7fbSGordon Ross smb_llist_remove(&node->n_wlock_list, lock);
9250897f7fbSGordon Ross smb_llist_exit(&node->n_wlock_list);
9260897f7fbSGordon Ross
9270897f7fbSGordon Ross return (status);
928da6c28aaSamw }
929da6c28aaSamw
930da6c28aaSamw /*
9316537f381Sas * smb_lock_range_ulckrules
932da6c28aaSamw *
9336537f381Sas * 1. Unlock should be performed at exactly matching ends.
9346537f381Sas * This has been changed because overlapping ends is
9356537f381Sas * allowed and there is no other precise way of locating
9366537f381Sas * lock entity in node lock list.
9376537f381Sas *
9386537f381Sas * 2. Unlock is failed if there is no corresponding lock exists.
939da6c28aaSamw *
940da6c28aaSamw * Return values
9416537f381Sas *
9426537f381Sas * NT_STATUS_SUCCESS Unlock request matches lock record
9430897f7fbSGordon Ross * pointed by 'foundlock' lock structure.
9446537f381Sas *
9456537f381Sas * NT_STATUS_RANGE_NOT_LOCKED Unlock request doen't match any
9466537f381Sas * of lock record in node lock request or
9476537f381Sas * error in unlock range processing.
948da6c28aaSamw */
9496537f381Sas static uint32_t
smb_lock_range_ulckrules(smb_ofile_t * file,uint64_t start,uint64_t length,uint32_t pid,smb_lock_t ** foundlock)9506537f381Sas smb_lock_range_ulckrules(
9510897f7fbSGordon Ross smb_ofile_t *file,
9526537f381Sas uint64_t start,
9536537f381Sas uint64_t length,
9540897f7fbSGordon Ross uint32_t pid,
9550897f7fbSGordon Ross smb_lock_t **foundlock)
956da6c28aaSamw {
9570897f7fbSGordon Ross smb_node_t *node = file->f_node;
958da6c28aaSamw smb_lock_t *lock;
9596537f381Sas uint32_t status = NT_STATUS_RANGE_NOT_LOCKED;
960da6c28aaSamw
9616537f381Sas /* Caller must hold lock for node->n_lock_list */
9626537f381Sas for (lock = smb_llist_head(&node->n_lock_list);
9636537f381Sas lock != NULL;
9646537f381Sas lock = smb_llist_next(&node->n_lock_list, lock)) {
965da6c28aaSamw
9666537f381Sas if ((start == lock->l_start) &&
9676537f381Sas (length == lock->l_length) &&
9680897f7fbSGordon Ross lock->l_file == file &&
9690897f7fbSGordon Ross lock->l_pid == pid) {
9700897f7fbSGordon Ross *foundlock = lock;
9716537f381Sas status = NT_STATUS_SUCCESS;
9726537f381Sas break;
9736537f381Sas }
974da6c28aaSamw }
9756537f381Sas
976da6c28aaSamw return (status);
977da6c28aaSamw }
978da6c28aaSamw
979da6c28aaSamw static smb_lock_t *
smb_lock_create(smb_request_t * sr,uint64_t start,uint64_t length,uint32_t pid,uint32_t locktype,uint32_t timeout)980da6c28aaSamw smb_lock_create(
981da6c28aaSamw smb_request_t *sr,
982da6c28aaSamw uint64_t start,
983da6c28aaSamw uint64_t length,
9840897f7fbSGordon Ross uint32_t pid,
985da6c28aaSamw uint32_t locktype,
986da6c28aaSamw uint32_t timeout)
987da6c28aaSamw {
988da6c28aaSamw smb_lock_t *lock;
989da6c28aaSamw
990da6c28aaSamw ASSERT(locktype == SMB_LOCK_TYPE_READWRITE ||
991da6c28aaSamw locktype == SMB_LOCK_TYPE_READONLY);
992da6c28aaSamw
9930897f7fbSGordon Ross lock = kmem_cache_alloc(smb_cache_lock, KM_SLEEP);
9940897f7fbSGordon Ross bzero(lock, sizeof (*lock));
995da6c28aaSamw lock->l_magic = SMB_LOCK_MAGIC;
996da6c28aaSamw lock->l_file = sr->fid_ofile;
9970897f7fbSGordon Ross /* l_file == fid_ofile implies same connection (see ofile lookup) */
9980897f7fbSGordon Ross lock->l_pid = pid;
999da6c28aaSamw lock->l_type = locktype;
1000da6c28aaSamw lock->l_start = start;
1001da6c28aaSamw lock->l_length = length;
1002da6c28aaSamw /*
1003da6c28aaSamw * Calculate the absolute end time so that we can use it
1004da6c28aaSamw * in cv_timedwait.
1005da6c28aaSamw */
1006d3d50737SRafael Vanoni lock->l_end_time = ddi_get_lbolt() + MSEC_TO_TICK(timeout);
10076537f381Sas if (timeout == UINT_MAX)
1008da6c28aaSamw lock->l_flags |= SMB_LOCK_FLAG_INDEFINITE;
10096537f381Sas
1010da6c28aaSamw mutex_init(&lock->l_mutex, NULL, MUTEX_DEFAULT, NULL);
1011da6c28aaSamw cv_init(&lock->l_cv, NULL, CV_DEFAULT, NULL);
1012da6c28aaSamw
1013da6c28aaSamw return (lock);
1014da6c28aaSamw }
1015da6c28aaSamw
1016da6c28aaSamw static void
smb_lock_free(smb_lock_t * lock)1017da6c28aaSamw smb_lock_free(smb_lock_t *lock)
1018da6c28aaSamw {
10190897f7fbSGordon Ross
10200897f7fbSGordon Ross lock->l_magic = 0;
1021da6c28aaSamw cv_destroy(&lock->l_cv);
1022da6c28aaSamw mutex_destroy(&lock->l_mutex);
1023da6c28aaSamw
10240897f7fbSGordon Ross kmem_cache_free(smb_cache_lock, lock);
1025da6c28aaSamw }
1026da6c28aaSamw
1027da6c28aaSamw /*
1028da6c28aaSamw * smb_lock_destroy
1029da6c28aaSamw *
1030da6c28aaSamw * Caller must hold node->n_lock_list
1031da6c28aaSamw */
1032da6c28aaSamw static void
smb_lock_destroy(smb_lock_t * lock)1033da6c28aaSamw smb_lock_destroy(smb_lock_t *lock)
1034da6c28aaSamw {
10350897f7fbSGordon Ross smb_lock_t *tl;
10360897f7fbSGordon Ross smb_node_t *node;
10370897f7fbSGordon Ross uint32_t ccnt;
10380897f7fbSGordon Ross
1039da6c28aaSamw /*
10400897f7fbSGordon Ross * Wake any waiting locks that were blocked by this.
10410897f7fbSGordon Ross * We want them to wake and continue in FIFO order,
10420897f7fbSGordon Ross * so enter/exit the llist every time...
1043da6c28aaSamw */
1044da6c28aaSamw mutex_enter(&lock->l_mutex);
10450897f7fbSGordon Ross ccnt = lock->l_conflicts;
10460897f7fbSGordon Ross lock->l_conflicts = 0;
1047da6c28aaSamw mutex_exit(&lock->l_mutex);
1048da6c28aaSamw
10490897f7fbSGordon Ross node = lock->l_file->f_node;
10500897f7fbSGordon Ross while (ccnt) {
10510897f7fbSGordon Ross
10520897f7fbSGordon Ross smb_llist_enter(&node->n_wlock_list, RW_READER);
10530897f7fbSGordon Ross
10540897f7fbSGordon Ross for (tl = smb_llist_head(&node->n_wlock_list);
10550897f7fbSGordon Ross tl != NULL;
10560897f7fbSGordon Ross tl = smb_llist_next(&node->n_wlock_list, tl)) {
10570897f7fbSGordon Ross mutex_enter(&tl->l_mutex);
10580897f7fbSGordon Ross if (tl->l_blocked_by == lock) {
10590897f7fbSGordon Ross tl->l_blocked_by = NULL;
10600897f7fbSGordon Ross cv_broadcast(&tl->l_cv);
10610897f7fbSGordon Ross mutex_exit(&tl->l_mutex);
10620897f7fbSGordon Ross goto woke_one;
10630897f7fbSGordon Ross }
10640897f7fbSGordon Ross mutex_exit(&tl->l_mutex);
10650897f7fbSGordon Ross }
10660897f7fbSGordon Ross /* No more in the list blocked by this lock. */
10670897f7fbSGordon Ross ccnt = 0;
10680897f7fbSGordon Ross woke_one:
10690897f7fbSGordon Ross smb_llist_exit(&node->n_wlock_list);
10700897f7fbSGordon Ross if (ccnt) {
10710897f7fbSGordon Ross /*
10720897f7fbSGordon Ross * Let the thread we woke have a chance to run
10730897f7fbSGordon Ross * before we wake competitors for their lock.
10740897f7fbSGordon Ross */
10750897f7fbSGordon Ross delay(MSEC_TO_TICK(1));
10760897f7fbSGordon Ross }
10770897f7fbSGordon Ross }
1078da6c28aaSamw
1079da6c28aaSamw smb_lock_free(lock);
1080da6c28aaSamw }
1081da6c28aaSamw
1082dc20a302Sas /*
1083dc20a302Sas * smb_is_range_unlocked
1084dc20a302Sas *
1085dc20a302Sas * Checks if the current unlock byte range request overlaps another lock
1086c8ec8eeaSjose borrego * This function is used to determine where POSIX unlocks should be
1087c8ec8eeaSjose borrego * applied.
1088c8ec8eeaSjose borrego *
1089dc20a302Sas * The return code and the value of new_mark must be interpreted as
1090dc20a302Sas * follows:
1091dc20a302Sas *
1092dc20a302Sas * B_TRUE and (new_mark == 0):
1093dc20a302Sas * This is the last or only lock left to be unlocked
1094dc20a302Sas *
1095dc20a302Sas * B_TRUE and (new_mark > 0):
1096dc20a302Sas * The range from start to new_mark can be unlocked
1097dc20a302Sas *
1098dc20a302Sas * B_FALSE and (new_mark == 0):
1099dc20a302Sas * The unlock can't be performed and we are done
1100dc20a302Sas *
1101dc20a302Sas * B_FALSE and (new_mark > 0),
1102dc20a302Sas * The range from start to new_mark can't be unlocked
1103dc20a302Sas * Start should be reset to new_mark for the next pass
1104dc20a302Sas */
1105dc20a302Sas
1106dc20a302Sas static boolean_t
smb_is_range_unlocked(uint64_t start,uint64_t end,uint32_t uniqid,smb_llist_t * llist_head,uint64_t * new_mark)1107c8ec8eeaSjose borrego smb_is_range_unlocked(uint64_t start, uint64_t end, uint32_t uniqid,
1108c8ec8eeaSjose borrego smb_llist_t *llist_head, uint64_t *new_mark)
1109dc20a302Sas {
1110c8ec8eeaSjose borrego struct smb_lock *lk = NULL;
1111dc20a302Sas uint64_t low_water_mark = MAXOFFSET_T;
1112dc20a302Sas uint64_t lk_start;
1113dc20a302Sas uint64_t lk_end;
1114dc20a302Sas
1115dc20a302Sas *new_mark = 0;
1116dc20a302Sas lk = smb_llist_head(llist_head);
1117dc20a302Sas while (lk) {
1118c8ec8eeaSjose borrego if (lk->l_length == 0) {
1119c8ec8eeaSjose borrego lk = smb_llist_next(llist_head, lk);
1120c8ec8eeaSjose borrego continue;
1121c8ec8eeaSjose borrego }
1122c8ec8eeaSjose borrego
1123c8ec8eeaSjose borrego if (lk->l_file->f_uniqid != uniqid) {
1124c8ec8eeaSjose borrego lk = smb_llist_next(llist_head, lk);
1125c8ec8eeaSjose borrego continue;
1126c8ec8eeaSjose borrego }
1127dc20a302Sas
1128dc20a302Sas lk_end = lk->l_start + lk->l_length - 1;
1129dc20a302Sas lk_start = lk->l_start;
1130dc20a302Sas
1131dc20a302Sas /*
1132dc20a302Sas * there is no overlap for the first 2 cases
1133dc20a302Sas * check next node
1134dc20a302Sas */
1135dc20a302Sas if (lk_end < start) {
1136dc20a302Sas lk = smb_llist_next(llist_head, lk);
1137dc20a302Sas continue;
1138dc20a302Sas }
1139dc20a302Sas if (lk_start > end) {
1140dc20a302Sas lk = smb_llist_next(llist_head, lk);
1141dc20a302Sas continue;
1142dc20a302Sas }
1143dc20a302Sas
1144dc20a302Sas /* this range is completely locked */
1145dc20a302Sas if ((lk_start <= start) && (lk_end >= end)) {
1146dc20a302Sas return (B_FALSE);
1147dc20a302Sas }
1148dc20a302Sas
1149dc20a302Sas /* the first part of this range is locked */
1150dc20a302Sas if ((start >= lk_start) && (start <= lk_end)) {
1151dc20a302Sas if (end > lk_end)
1152dc20a302Sas *new_mark = lk_end + 1;
1153dc20a302Sas return (B_FALSE);
1154dc20a302Sas }
1155dc20a302Sas
1156dc20a302Sas /* this piece is unlocked */
1157dc20a302Sas if ((lk_start >= start) && (lk_start <= end)) {
1158dc20a302Sas if (low_water_mark > lk_start)
1159dc20a302Sas low_water_mark = lk_start;
1160dc20a302Sas }
1161dc20a302Sas
1162dc20a302Sas lk = smb_llist_next(llist_head, lk);
1163dc20a302Sas }
1164dc20a302Sas
1165dc20a302Sas if (low_water_mark != MAXOFFSET_T) {
1166dc20a302Sas *new_mark = low_water_mark;
1167dc20a302Sas return (B_TRUE);
1168da6c28aaSamw }
1169dc20a302Sas /* the range is completely unlocked */
1170dc20a302Sas return (B_TRUE);
1171da6c28aaSamw }
11720897f7fbSGordon Ross
11730897f7fbSGordon Ross #ifdef DEBUG
11740897f7fbSGordon Ross static void
smb_lock_dump1(smb_lock_t * lock)11750897f7fbSGordon Ross smb_lock_dump1(smb_lock_t *lock)
11760897f7fbSGordon Ross {
11770897f7fbSGordon Ross cmn_err(CE_CONT, "\t0x%p: 0x%llx, 0x%llx, %p, %d\n",
11780897f7fbSGordon Ross (void *)lock,
11790897f7fbSGordon Ross (long long)lock->l_start,
11800897f7fbSGordon Ross (long long)lock->l_length,
11810897f7fbSGordon Ross (void *)lock->l_file,
11820897f7fbSGordon Ross lock->l_pid);
11830897f7fbSGordon Ross
11840897f7fbSGordon Ross }
11850897f7fbSGordon Ross
11860897f7fbSGordon Ross static void
smb_lock_dumplist(smb_llist_t * llist)11870897f7fbSGordon Ross smb_lock_dumplist(smb_llist_t *llist)
11880897f7fbSGordon Ross {
11890897f7fbSGordon Ross smb_lock_t *lock;
11900897f7fbSGordon Ross
11910897f7fbSGordon Ross for (lock = smb_llist_head(llist);
11920897f7fbSGordon Ross lock != NULL;
11930897f7fbSGordon Ross lock = smb_llist_next(llist, lock)) {
11940897f7fbSGordon Ross smb_lock_dump1(lock);
11950897f7fbSGordon Ross }
11960897f7fbSGordon Ross }
11970897f7fbSGordon Ross
11980897f7fbSGordon Ross static void
smb_lock_dumpnode(smb_node_t * node)11990897f7fbSGordon Ross smb_lock_dumpnode(smb_node_t *node)
12000897f7fbSGordon Ross {
12010897f7fbSGordon Ross cmn_err(CE_CONT, "Granted Locks on %p (%d)\n",
12020897f7fbSGordon Ross (void *)node, node->n_lock_list.ll_count);
12030897f7fbSGordon Ross smb_lock_dumplist(&node->n_lock_list);
12040897f7fbSGordon Ross
12050897f7fbSGordon Ross cmn_err(CE_CONT, "Waiting Locks on %p (%d)\n",
12060897f7fbSGordon Ross (void *)node, node->n_wlock_list.ll_count);
12070897f7fbSGordon Ross smb_lock_dumplist(&node->n_wlock_list);
12080897f7fbSGordon Ross }
12090897f7fbSGordon Ross
12100897f7fbSGordon Ross #endif
1211