1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2016 Nexenta Systems, Inc. All rights reserved. 14 */ 15 16 /* 17 * Dispatch function for SMB2_LOCK 18 */ 19 20 #include <smbsrv/smb2_kproto.h> 21 22 typedef struct SMB2_LOCK_ELEMENT { 23 uint64_t Offset; 24 uint64_t Length; 25 uint32_t Flags; 26 uint32_t reserved; 27 } lock_elem_t; 28 29 static uint32_t smb2_unlock(smb_request_t *); 30 static uint32_t smb2_locks(smb_request_t *); 31 static smb_sdrc_t smb2_lock_async(smb_request_t *); 32 33 /* 34 * This is a somewhat arbitrary sanity limit on the length of the 35 * SMB2_LOCK_ELEMENT array. It usually has length one or two. 36 */ 37 int smb2_lock_max_elem = 1024; 38 39 smb_sdrc_t 40 smb2_lock(smb_request_t *sr) 41 { 42 lock_elem_t *lvec, *lk; 43 smb2fid_t smb2fid; 44 uint32_t LockSequence; 45 uint32_t status; 46 uint16_t StructSize; 47 uint16_t LockCount; 48 uint16_t i; 49 int rc; 50 51 /* 52 * SMB2 Lock request 53 */ 54 rc = smb_mbc_decodef( 55 &sr->smb_data, "wwlqq", 56 &StructSize, /* w */ 57 &LockCount, /* w */ 58 &LockSequence, /* l */ 59 &smb2fid.persistent, /* q */ 60 &smb2fid.temporal); /* q */ 61 if (rc || StructSize != 48) 62 return (SDRC_ERROR); 63 64 status = smb2sr_lookup_fid(sr, &smb2fid); 65 if (status) 66 goto errout; 67 if (sr->fid_ofile->f_node == NULL || LockCount == 0) { 68 status = NT_STATUS_INVALID_PARAMETER; 69 goto errout; 70 } 71 if (LockCount > smb2_lock_max_elem) { 72 status = NT_STATUS_INSUFFICIENT_RESOURCES; 73 goto errout; 74 } 75 76 /* 77 * Parse the array of SMB2_LOCK_ELEMENT structs. 78 * This array is free'd in smb_srm_fini. 79 */ 80 lvec = smb_srm_zalloc(sr, LockCount * sizeof (*lvec)); 81 for (i = 0; i < LockCount; i++) { 82 lk = &lvec[i]; 83 rc = smb_mbc_decodef( 84 &sr->smb_data, "qqll", 85 &lk->Offset, /* q */ 86 &lk->Length, /* q */ 87 &lk->Flags, /* l */ 88 &lk->reserved); /* l */ 89 if (rc) { 90 status = NT_STATUS_INVALID_PARAMETER; 91 goto errout; 92 } 93 } 94 95 /* 96 * [MS-SMB2] 3.3.5.14 97 * If the flags of the [first element of] the Locks array 98 * [has] SMB2_LOCKFLAG_UNLOCK set, the server MUST process 99 * the lock array as a series of unlocks. Otherwise, it 100 * MUST process the lock array as a series of lock requests. 101 */ 102 sr->arg.lock.lvec = lvec; 103 sr->arg.lock.lcnt = LockCount; 104 sr->arg.lock.lseq = LockSequence; 105 if (lvec[0].Flags & SMB2_LOCKFLAG_UNLOCK) { 106 status = smb2_unlock(sr); 107 } else { 108 status = smb2_locks(sr); 109 } 110 if (status) 111 goto errout; 112 113 /* 114 * SMB2 Lock reply (sync) 115 */ 116 (void) smb_mbc_encodef( 117 &sr->reply, "w..", 118 4); /* StructSize w */ 119 /* reserved .. */ 120 return (SDRC_SUCCESS); 121 122 errout: 123 smb2sr_put_error(sr, status); 124 return (SDRC_SUCCESS); 125 } 126 127 /* 128 * Process what should be an array of unlock requests. 129 */ 130 static uint32_t 131 smb2_unlock(smb_request_t *sr) 132 { 133 lock_elem_t *lk; 134 lock_elem_t *lvec = sr->arg.lock.lvec; 135 uint32_t LockCount = sr->arg.lock.lcnt; 136 uint32_t LockSequence = sr->arg.lock.lseq; 137 uint32_t status = 0; 138 uint32_t pid = 0; /* SMB2 ignores lock PIDs. */ 139 int i; 140 141 for (i = 0; i < LockCount; i++) { 142 lk = &lvec[i]; 143 144 if (lk->Flags != SMB2_LOCKFLAG_UNLOCK) { 145 status = NT_STATUS_INVALID_PARAMETER; 146 break; 147 } 148 149 status = smb_unlock_range(sr, lk->Offset, lk->Length, pid); 150 if (status != 0) 151 break; 152 } 153 (void) LockSequence; /* todo */ 154 155 return (status); 156 } 157 158 /* 159 * Process what should be an array of lock requests. 160 */ 161 static uint32_t 162 smb2_locks(smb_request_t *sr) 163 { 164 lock_elem_t *lk; 165 lock_elem_t *lvec = sr->arg.lock.lvec; 166 uint32_t LockCount = sr->arg.lock.lcnt; 167 uint32_t i; 168 uint32_t ltype; 169 uint32_t pid = 0; /* SMB2 ignores lock PIDs */ 170 uint32_t timeout = 0; 171 uint32_t status = 0; 172 173 for (i = 0; i < LockCount; i++) { 174 lk = &lvec[i]; 175 176 switch (lk->Flags) { 177 178 case SMB2_LOCKFLAG_SHARED_LOCK: 179 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: 180 /* 181 * Blocking locks have special rules: 182 * Must be exactly one element, else 183 * invalid parameter. 184 */ 185 if (i == 0 && LockCount == 1) { 186 status = smb2sr_go_async(sr, smb2_lock_async); 187 return (status); 188 } 189 /* FALLTHROUGH */ 190 case SMB2_LOCKFLAG_UNLOCK: 191 default: 192 status = NT_STATUS_INVALID_PARAMETER; 193 goto end_loop; 194 195 /* BEGIN CSTYLED */ 196 case SMB2_LOCKFLAG_SHARED_LOCK | 197 SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 198 /* END CSTYLED */ 199 ltype = SMB_LOCK_TYPE_READONLY; 200 break; 201 202 /* BEGIN CSTYLED */ 203 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK | 204 SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 205 /* END CSTYLED */ 206 ltype = SMB_LOCK_TYPE_READWRITE; 207 break; 208 } 209 210 status = smb_lock_range(sr, lk->Offset, lk->Length, pid, 211 ltype, timeout); 212 if (status != 0) { 213 goto end_loop; 214 } 215 } 216 217 end_loop: 218 if (status != 0) { 219 /* 220 * Oh... we have to rollback. 221 */ 222 while (i > 0) { 223 --i; 224 lk = &lvec[i]; 225 (void) smb_unlock_range(sr, 226 lk->Offset, lk->Length, pid); 227 } 228 } 229 230 return (status); 231 } 232 233 /* 234 * Async handler for blocking lock requests. 235 * Always exactly one lock request here. 236 */ 237 static smb_sdrc_t 238 smb2_lock_async(smb_request_t *sr) 239 { 240 lock_elem_t *lk = sr->arg.lock.lvec; 241 uint32_t LockCount = sr->arg.lock.lcnt; 242 uint32_t status; 243 uint32_t ltype; 244 uint32_t pid = 0; /* SMB2 ignores lock PIDs */ 245 uint32_t timeout = UINT_MAX; 246 247 ASSERT(sr->fid_ofile->f_node != NULL); 248 ASSERT(LockCount == 1); 249 250 switch (lk->Flags) { 251 case SMB2_LOCKFLAG_SHARED_LOCK: 252 ltype = SMB_LOCK_TYPE_READONLY; 253 break; 254 255 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: 256 ltype = SMB_LOCK_TYPE_READWRITE; 257 break; 258 259 default: 260 ASSERT(0); 261 status = NT_STATUS_INTERNAL_ERROR; 262 goto errout; 263 } 264 265 status = smb_lock_range(sr, lk->Offset, lk->Length, pid, 266 ltype, timeout); 267 if (status != 0) 268 goto errout; 269 270 /* 271 * SMB2 Lock reply (async) 272 */ 273 (void) smb_mbc_encodef( 274 &sr->reply, "w..", 275 4); /* StructSize w */ 276 /* reserved .. */ 277 return (SDRC_SUCCESS); 278 279 errout: 280 smb2sr_put_error(sr, status); 281 return (SDRC_SUCCESS); 282 } 283