1a90cf9f2SGordon Ross /* 2a90cf9f2SGordon Ross * This file and its contents are supplied under the terms of the 3a90cf9f2SGordon Ross * Common Development and Distribution License ("CDDL"), version 1.0. 4a90cf9f2SGordon Ross * You may only use this file in accordance with the terms of version 5a90cf9f2SGordon Ross * 1.0 of the CDDL. 6a90cf9f2SGordon Ross * 7a90cf9f2SGordon Ross * A full copy of the text of the CDDL should have accompanied this 8a90cf9f2SGordon Ross * source. A copy of the CDDL is also available via the Internet at 9a90cf9f2SGordon Ross * http://www.illumos.org/license/CDDL. 10a90cf9f2SGordon Ross */ 11a90cf9f2SGordon Ross 12a90cf9f2SGordon Ross /* 130897f7fbSGordon Ross * Copyright 2016 Nexenta Systems, Inc. All rights reserved. 14a90cf9f2SGordon Ross */ 15a90cf9f2SGordon Ross 16a90cf9f2SGordon Ross /* 17a90cf9f2SGordon Ross * Dispatch function for SMB2_LOCK 18a90cf9f2SGordon Ross */ 19a90cf9f2SGordon Ross 20a90cf9f2SGordon Ross #include <smbsrv/smb2_kproto.h> 21a90cf9f2SGordon Ross 220897f7fbSGordon Ross typedef struct SMB2_LOCK_ELEMENT { 23a90cf9f2SGordon Ross uint64_t Offset; 24a90cf9f2SGordon Ross uint64_t Length; 25a90cf9f2SGordon Ross uint32_t Flags; 26a90cf9f2SGordon Ross uint32_t reserved; 270897f7fbSGordon Ross } lock_elem_t; 28a90cf9f2SGordon Ross 290897f7fbSGordon Ross static uint32_t smb2_unlock(smb_request_t *); 300897f7fbSGordon Ross static uint32_t smb2_locks(smb_request_t *); 31a90cf9f2SGordon Ross static smb_sdrc_t smb2_lock_async(smb_request_t *); 32a90cf9f2SGordon Ross 33a90cf9f2SGordon Ross /* 34a90cf9f2SGordon Ross * This is a somewhat arbitrary sanity limit on the length of the 35a90cf9f2SGordon Ross * SMB2_LOCK_ELEMENT array. It usually has length one or two. 36a90cf9f2SGordon Ross */ 37a90cf9f2SGordon Ross int smb2_lock_max_elem = 1024; 38a90cf9f2SGordon Ross 39a90cf9f2SGordon Ross smb_sdrc_t 40a90cf9f2SGordon Ross smb2_lock(smb_request_t *sr) 41a90cf9f2SGordon Ross { 420897f7fbSGordon Ross lock_elem_t *lvec, *lk; 43a90cf9f2SGordon Ross smb2fid_t smb2fid; 44a90cf9f2SGordon Ross uint32_t LockSequence; 45a90cf9f2SGordon Ross uint32_t status; 46a90cf9f2SGordon Ross uint16_t StructSize; 47a90cf9f2SGordon Ross uint16_t LockCount; 48a90cf9f2SGordon Ross uint16_t i; 490897f7fbSGordon Ross int rc; 50a90cf9f2SGordon Ross 51a90cf9f2SGordon Ross /* 52*93bc28dbSGordon Ross * Decode SMB2 Lock request 53a90cf9f2SGordon Ross */ 54a90cf9f2SGordon Ross rc = smb_mbc_decodef( 55a90cf9f2SGordon Ross &sr->smb_data, "wwlqq", 56a90cf9f2SGordon Ross &StructSize, /* w */ 57a90cf9f2SGordon Ross &LockCount, /* w */ 58a90cf9f2SGordon Ross &LockSequence, /* l */ 59a90cf9f2SGordon Ross &smb2fid.persistent, /* q */ 60a90cf9f2SGordon Ross &smb2fid.temporal); /* q */ 61a90cf9f2SGordon Ross if (rc || StructSize != 48) 62a90cf9f2SGordon Ross return (SDRC_ERROR); 63a90cf9f2SGordon Ross 64*93bc28dbSGordon Ross /* 65*93bc28dbSGordon Ross * Want FID lookup before the start probe. 66*93bc28dbSGordon Ross */ 67a90cf9f2SGordon Ross status = smb2sr_lookup_fid(sr, &smb2fid); 68*93bc28dbSGordon Ross DTRACE_SMB2_START(op__Lock, smb_request_t *, sr); 69*93bc28dbSGordon Ross 70a90cf9f2SGordon Ross if (status) 71*93bc28dbSGordon Ross goto errout; /* Bad FID */ 72a90cf9f2SGordon Ross if (sr->fid_ofile->f_node == NULL || LockCount == 0) { 73a90cf9f2SGordon Ross status = NT_STATUS_INVALID_PARAMETER; 74a90cf9f2SGordon Ross goto errout; 75a90cf9f2SGordon Ross } 76a90cf9f2SGordon Ross if (LockCount > smb2_lock_max_elem) { 77a90cf9f2SGordon Ross status = NT_STATUS_INSUFFICIENT_RESOURCES; 78a90cf9f2SGordon Ross goto errout; 79a90cf9f2SGordon Ross } 80a90cf9f2SGordon Ross 81a90cf9f2SGordon Ross /* 820897f7fbSGordon Ross * Parse the array of SMB2_LOCK_ELEMENT structs. 830897f7fbSGordon Ross * This array is free'd in smb_srm_fini. 84a90cf9f2SGordon Ross */ 850897f7fbSGordon Ross lvec = smb_srm_zalloc(sr, LockCount * sizeof (*lvec)); 86a90cf9f2SGordon Ross for (i = 0; i < LockCount; i++) { 870897f7fbSGordon Ross lk = &lvec[i]; 88a90cf9f2SGordon Ross rc = smb_mbc_decodef( 89a90cf9f2SGordon Ross &sr->smb_data, "qqll", 900897f7fbSGordon Ross &lk->Offset, /* q */ 910897f7fbSGordon Ross &lk->Length, /* q */ 920897f7fbSGordon Ross &lk->Flags, /* l */ 930897f7fbSGordon Ross &lk->reserved); /* l */ 94a90cf9f2SGordon Ross if (rc) { 95a90cf9f2SGordon Ross status = NT_STATUS_INVALID_PARAMETER; 96a90cf9f2SGordon Ross goto errout; 97a90cf9f2SGordon Ross } 980897f7fbSGordon Ross } 99a90cf9f2SGordon Ross 100a90cf9f2SGordon Ross /* 1010897f7fbSGordon Ross * [MS-SMB2] 3.3.5.14 1020897f7fbSGordon Ross * If the flags of the [first element of] the Locks array 1030897f7fbSGordon Ross * [has] SMB2_LOCKFLAG_UNLOCK set, the server MUST process 1040897f7fbSGordon Ross * the lock array as a series of unlocks. Otherwise, it 1050897f7fbSGordon Ross * MUST process the lock array as a series of lock requests. 106a90cf9f2SGordon Ross */ 1070897f7fbSGordon Ross sr->arg.lock.lvec = lvec; 1080897f7fbSGordon Ross sr->arg.lock.lcnt = LockCount; 1090897f7fbSGordon Ross sr->arg.lock.lseq = LockSequence; 1100897f7fbSGordon Ross if (lvec[0].Flags & SMB2_LOCKFLAG_UNLOCK) { 1110897f7fbSGordon Ross status = smb2_unlock(sr); 1120897f7fbSGordon Ross } else { 1130897f7fbSGordon Ross status = smb2_locks(sr); 114a90cf9f2SGordon Ross } 115*93bc28dbSGordon Ross 116*93bc28dbSGordon Ross errout: 117*93bc28dbSGordon Ross sr->smb2_status = status; 118*93bc28dbSGordon Ross if (status != NT_STATUS_PENDING) { 119*93bc28dbSGordon Ross DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr); 120*93bc28dbSGordon Ross } 121*93bc28dbSGordon Ross 122*93bc28dbSGordon Ross if (status) { 123*93bc28dbSGordon Ross smb2sr_put_error(sr, status); 124*93bc28dbSGordon Ross return (SDRC_SUCCESS); 125*93bc28dbSGordon Ross } 126a90cf9f2SGordon Ross 127a90cf9f2SGordon Ross /* 128*93bc28dbSGordon Ross * Encode SMB2 Lock reply (sync) 129a90cf9f2SGordon Ross */ 130a90cf9f2SGordon Ross (void) smb_mbc_encodef( 131a90cf9f2SGordon Ross &sr->reply, "w..", 1320897f7fbSGordon Ross 4); /* StructSize w */ 133a90cf9f2SGordon Ross /* reserved .. */ 134a90cf9f2SGordon Ross return (SDRC_SUCCESS); 135a90cf9f2SGordon Ross } 136a90cf9f2SGordon Ross 1370897f7fbSGordon Ross /* 1380897f7fbSGordon Ross * Process what should be an array of unlock requests. 1390897f7fbSGordon Ross */ 1400897f7fbSGordon Ross static uint32_t 1410897f7fbSGordon Ross smb2_unlock(smb_request_t *sr) 1420897f7fbSGordon Ross { 1430897f7fbSGordon Ross lock_elem_t *lk; 1440897f7fbSGordon Ross lock_elem_t *lvec = sr->arg.lock.lvec; 1450897f7fbSGordon Ross uint32_t LockCount = sr->arg.lock.lcnt; 1460897f7fbSGordon Ross uint32_t LockSequence = sr->arg.lock.lseq; 1470897f7fbSGordon Ross uint32_t status = 0; 1480897f7fbSGordon Ross uint32_t pid = 0; /* SMB2 ignores lock PIDs. */ 1490897f7fbSGordon Ross int i; 1500897f7fbSGordon Ross 1510897f7fbSGordon Ross for (i = 0; i < LockCount; i++) { 1520897f7fbSGordon Ross lk = &lvec[i]; 1530897f7fbSGordon Ross 1540897f7fbSGordon Ross if (lk->Flags != SMB2_LOCKFLAG_UNLOCK) { 1550897f7fbSGordon Ross status = NT_STATUS_INVALID_PARAMETER; 1560897f7fbSGordon Ross break; 1570897f7fbSGordon Ross } 1580897f7fbSGordon Ross 1590897f7fbSGordon Ross status = smb_unlock_range(sr, lk->Offset, lk->Length, pid); 1600897f7fbSGordon Ross if (status != 0) 1610897f7fbSGordon Ross break; 1620897f7fbSGordon Ross } 1630897f7fbSGordon Ross (void) LockSequence; /* todo */ 1640897f7fbSGordon Ross 1650897f7fbSGordon Ross return (status); 1660897f7fbSGordon Ross } 1670897f7fbSGordon Ross 1680897f7fbSGordon Ross /* 1690897f7fbSGordon Ross * Process what should be an array of lock requests. 1700897f7fbSGordon Ross */ 1710897f7fbSGordon Ross static uint32_t 1720897f7fbSGordon Ross smb2_locks(smb_request_t *sr) 1730897f7fbSGordon Ross { 1740897f7fbSGordon Ross lock_elem_t *lk; 1750897f7fbSGordon Ross lock_elem_t *lvec = sr->arg.lock.lvec; 1760897f7fbSGordon Ross uint32_t LockCount = sr->arg.lock.lcnt; 1770897f7fbSGordon Ross uint32_t i; 1780897f7fbSGordon Ross uint32_t ltype; 1790897f7fbSGordon Ross uint32_t pid = 0; /* SMB2 ignores lock PIDs */ 1800897f7fbSGordon Ross uint32_t timeout = 0; 1810897f7fbSGordon Ross uint32_t status = 0; 1820897f7fbSGordon Ross 1830897f7fbSGordon Ross for (i = 0; i < LockCount; i++) { 1840897f7fbSGordon Ross lk = &lvec[i]; 1850897f7fbSGordon Ross 1860897f7fbSGordon Ross switch (lk->Flags) { 1870897f7fbSGordon Ross 1880897f7fbSGordon Ross case SMB2_LOCKFLAG_SHARED_LOCK: 1890897f7fbSGordon Ross case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: 1900897f7fbSGordon Ross /* 1910897f7fbSGordon Ross * Blocking locks have special rules: 1920897f7fbSGordon Ross * Must be exactly one element, else 1930897f7fbSGordon Ross * invalid parameter. 1940897f7fbSGordon Ross */ 1950897f7fbSGordon Ross if (i == 0 && LockCount == 1) { 1960897f7fbSGordon Ross status = smb2sr_go_async(sr, smb2_lock_async); 1970897f7fbSGordon Ross return (status); 1980897f7fbSGordon Ross } 1990897f7fbSGordon Ross /* FALLTHROUGH */ 2000897f7fbSGordon Ross case SMB2_LOCKFLAG_UNLOCK: 2010897f7fbSGordon Ross default: 2020897f7fbSGordon Ross status = NT_STATUS_INVALID_PARAMETER; 2030897f7fbSGordon Ross goto end_loop; 2040897f7fbSGordon Ross 2050897f7fbSGordon Ross /* BEGIN CSTYLED */ 2060897f7fbSGordon Ross case SMB2_LOCKFLAG_SHARED_LOCK | 2070897f7fbSGordon Ross SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 2080897f7fbSGordon Ross /* END CSTYLED */ 2090897f7fbSGordon Ross ltype = SMB_LOCK_TYPE_READONLY; 2100897f7fbSGordon Ross break; 2110897f7fbSGordon Ross 2120897f7fbSGordon Ross /* BEGIN CSTYLED */ 2130897f7fbSGordon Ross case SMB2_LOCKFLAG_EXCLUSIVE_LOCK | 2140897f7fbSGordon Ross SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 2150897f7fbSGordon Ross /* END CSTYLED */ 2160897f7fbSGordon Ross ltype = SMB_LOCK_TYPE_READWRITE; 2170897f7fbSGordon Ross break; 2180897f7fbSGordon Ross } 2190897f7fbSGordon Ross 2200897f7fbSGordon Ross status = smb_lock_range(sr, lk->Offset, lk->Length, pid, 2210897f7fbSGordon Ross ltype, timeout); 2220897f7fbSGordon Ross if (status != 0) { 2230897f7fbSGordon Ross goto end_loop; 2240897f7fbSGordon Ross } 2250897f7fbSGordon Ross } 2260897f7fbSGordon Ross 2270897f7fbSGordon Ross end_loop: 2280897f7fbSGordon Ross if (status != 0) { 2290897f7fbSGordon Ross /* 2300897f7fbSGordon Ross * Oh... we have to rollback. 2310897f7fbSGordon Ross */ 2320897f7fbSGordon Ross while (i > 0) { 2330897f7fbSGordon Ross --i; 2340897f7fbSGordon Ross lk = &lvec[i]; 2350897f7fbSGordon Ross (void) smb_unlock_range(sr, 2360897f7fbSGordon Ross lk->Offset, lk->Length, pid); 2370897f7fbSGordon Ross } 2380897f7fbSGordon Ross } 2390897f7fbSGordon Ross 2400897f7fbSGordon Ross return (status); 2410897f7fbSGordon Ross } 2420897f7fbSGordon Ross 2430897f7fbSGordon Ross /* 2440897f7fbSGordon Ross * Async handler for blocking lock requests. 2450897f7fbSGordon Ross * Always exactly one lock request here. 2460897f7fbSGordon Ross */ 247a90cf9f2SGordon Ross static smb_sdrc_t 248a90cf9f2SGordon Ross smb2_lock_async(smb_request_t *sr) 249a90cf9f2SGordon Ross { 2500897f7fbSGordon Ross lock_elem_t *lk = sr->arg.lock.lvec; 2510897f7fbSGordon Ross uint32_t LockCount = sr->arg.lock.lcnt; 252a90cf9f2SGordon Ross uint32_t status; 2530897f7fbSGordon Ross uint32_t ltype; 2540897f7fbSGordon Ross uint32_t pid = 0; /* SMB2 ignores lock PIDs */ 2550897f7fbSGordon Ross uint32_t timeout = UINT_MAX; 256a90cf9f2SGordon Ross 2570897f7fbSGordon Ross ASSERT(sr->fid_ofile->f_node != NULL); 2580897f7fbSGordon Ross ASSERT(LockCount == 1); 259a90cf9f2SGordon Ross 2600897f7fbSGordon Ross switch (lk->Flags) { 2610897f7fbSGordon Ross case SMB2_LOCKFLAG_SHARED_LOCK: 2620897f7fbSGordon Ross ltype = SMB_LOCK_TYPE_READONLY; 2630897f7fbSGordon Ross break; 2640897f7fbSGordon Ross 2650897f7fbSGordon Ross case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: 2660897f7fbSGordon Ross ltype = SMB_LOCK_TYPE_READWRITE; 2670897f7fbSGordon Ross break; 2680897f7fbSGordon Ross 2690897f7fbSGordon Ross default: 2700897f7fbSGordon Ross ASSERT(0); 271a90cf9f2SGordon Ross status = NT_STATUS_INTERNAL_ERROR; 272a90cf9f2SGordon Ross goto errout; 273a90cf9f2SGordon Ross } 274a90cf9f2SGordon Ross 2750897f7fbSGordon Ross status = smb_lock_range(sr, lk->Offset, lk->Length, pid, 2760897f7fbSGordon Ross ltype, timeout); 277*93bc28dbSGordon Ross 278*93bc28dbSGordon Ross errout: 279*93bc28dbSGordon Ross sr->smb2_status = status; 280*93bc28dbSGordon Ross DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr); 281*93bc28dbSGordon Ross 282*93bc28dbSGordon Ross if (status != 0) { 283*93bc28dbSGordon Ross smb2sr_put_error(sr, status); 284*93bc28dbSGordon Ross return (SDRC_SUCCESS); 285*93bc28dbSGordon Ross } 286a90cf9f2SGordon Ross 287a90cf9f2SGordon Ross /* 288a90cf9f2SGordon Ross * SMB2 Lock reply (async) 289a90cf9f2SGordon Ross */ 290a90cf9f2SGordon Ross (void) smb_mbc_encodef( 291a90cf9f2SGordon Ross &sr->reply, "w..", 2920897f7fbSGordon Ross 4); /* StructSize w */ 293a90cf9f2SGordon Ross /* reserved .. */ 294a90cf9f2SGordon Ross return (SDRC_SUCCESS); 295a90cf9f2SGordon Ross } 296