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 2017 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 /* 23 * [MS-SMB2] 2.2.26 LockSequenceIndex, LockSequenceNumber. 24 */ 25 #define SMB2_LSN_SHIFT 4 26 #define SMB2_LSN_MASK 0xf 27 28 typedef struct SMB2_LOCK_ELEMENT { 29 uint64_t Offset; 30 uint64_t Length; 31 uint32_t Flags; 32 uint32_t reserved; 33 } lock_elem_t; 34 35 static uint32_t smb2_unlock(smb_request_t *); 36 static uint32_t smb2_locks(smb_request_t *); 37 static uint32_t smb2_lock_blocking(smb_request_t *); 38 39 static boolean_t smb2_lock_chk_lockseq(smb_ofile_t *, uint32_t); 40 static void smb2_lock_set_lockseq(smb_ofile_t *, uint32_t); 41 42 /* 43 * This is a somewhat arbitrary sanity limit on the length of the 44 * SMB2_LOCK_ELEMENT array. It usually has length one or two. 45 */ 46 int smb2_lock_max_elem = 1024; 47 48 smb_sdrc_t 49 smb2_lock(smb_request_t *sr) 50 { 51 lock_elem_t *lvec, *lk; 52 smb2fid_t smb2fid; 53 uint32_t LockSequence; 54 uint32_t status; 55 uint16_t StructSize; 56 uint16_t LockCount; 57 uint16_t i; 58 int rc; 59 60 /* 61 * Decode SMB2 Lock request 62 */ 63 rc = smb_mbc_decodef( 64 &sr->smb_data, "wwlqq", 65 &StructSize, /* w */ 66 &LockCount, /* w */ 67 &LockSequence, /* l */ 68 &smb2fid.persistent, /* q */ 69 &smb2fid.temporal); /* q */ 70 if (rc || StructSize != 48) 71 return (SDRC_ERROR); 72 73 /* 74 * Want FID lookup before the start probe. 75 */ 76 status = smb2sr_lookup_fid(sr, &smb2fid); 77 DTRACE_SMB2_START(op__Lock, smb_request_t *, sr); 78 79 if (status) 80 goto errout; /* Bad FID */ 81 if (sr->fid_ofile->f_node == NULL || LockCount == 0) { 82 status = NT_STATUS_INVALID_PARAMETER; 83 goto errout; 84 } 85 if (LockCount > smb2_lock_max_elem) { 86 status = NT_STATUS_INSUFFICIENT_RESOURCES; 87 goto errout; 88 } 89 90 /* 91 * Check the LockSequence to determine whether a previous 92 * lock request succeeded, but the client disconnected 93 * (retaining a durable or resilient handle). If so, this 94 * is a lock "replay". We'll find the lock sequence here 95 * and return success without processing the lock again. 96 */ 97 if (sr->session->dialect < SMB_VERS_2_1) 98 LockSequence = 0; 99 if ((sr->session->dialect == SMB_VERS_2_1) && 100 sr->fid_ofile->dh_vers != SMB2_RESILIENT) 101 LockSequence = 0; 102 /* dialect 3.0 or later can always use LockSequence */ 103 104 if (LockSequence != 0 && 105 smb2_lock_chk_lockseq(sr->fid_ofile, LockSequence)) { 106 status = NT_STATUS_SUCCESS; 107 goto errout; 108 } 109 110 /* 111 * Parse the array of SMB2_LOCK_ELEMENT structs. 112 * This array is free'd in smb_srm_fini. 113 */ 114 lvec = smb_srm_zalloc(sr, LockCount * sizeof (*lvec)); 115 for (i = 0; i < LockCount; i++) { 116 lk = &lvec[i]; 117 rc = smb_mbc_decodef( 118 &sr->smb_data, "qqll", 119 &lk->Offset, /* q */ 120 &lk->Length, /* q */ 121 &lk->Flags, /* l */ 122 &lk->reserved); /* l */ 123 if (rc) { 124 status = NT_STATUS_INVALID_PARAMETER; 125 goto errout; 126 } 127 } 128 129 /* 130 * [MS-SMB2] 3.3.5.14 131 * If the flags of the [first element of] the Locks array 132 * [has] SMB2_LOCKFLAG_UNLOCK set, the server MUST process 133 * the lock array as a series of unlocks. Otherwise, it 134 * MUST process the lock array as a series of lock requests. 135 */ 136 sr->arg.lock.lvec = lvec; 137 sr->arg.lock.lcnt = LockCount; 138 sr->arg.lock.lseq = LockSequence; 139 if (lvec[0].Flags & SMB2_LOCKFLAG_UNLOCK) { 140 status = smb2_unlock(sr); 141 } else { 142 status = smb2_locks(sr); 143 } 144 145 errout: 146 sr->smb2_status = status; 147 DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr); 148 149 if (status) { 150 smb2sr_put_error(sr, status); 151 return (SDRC_SUCCESS); 152 } 153 154 /* 155 * Encode SMB2 Lock reply 156 */ 157 (void) smb_mbc_encodef( 158 &sr->reply, "w..", 159 4); /* StructSize w */ 160 /* reserved .. */ 161 return (SDRC_SUCCESS); 162 } 163 164 /* 165 * Process what should be an array of unlock requests. 166 */ 167 static uint32_t 168 smb2_unlock(smb_request_t *sr) 169 { 170 lock_elem_t *lk; 171 lock_elem_t *lvec = sr->arg.lock.lvec; 172 uint32_t LockCount = sr->arg.lock.lcnt; 173 uint32_t LockSequence = sr->arg.lock.lseq; 174 uint32_t status = 0; 175 uint32_t pid = 0; /* SMB2 ignores lock PIDs. */ 176 int i; 177 178 for (i = 0; i < LockCount; i++) { 179 lk = &lvec[i]; 180 181 if (lk->Flags != SMB2_LOCKFLAG_UNLOCK) { 182 status = NT_STATUS_INVALID_PARAMETER; 183 break; 184 } 185 186 status = smb_unlock_range(sr, lk->Offset, lk->Length, pid); 187 if (status != 0) 188 break; 189 } 190 if (status == 0 && LockSequence != 0) { 191 smb2_lock_set_lockseq(sr->fid_ofile, LockSequence); 192 } 193 194 return (status); 195 } 196 197 /* 198 * Process what should be an array of lock requests. 199 */ 200 static uint32_t 201 smb2_locks(smb_request_t *sr) 202 { 203 lock_elem_t *lk; 204 lock_elem_t *lvec = sr->arg.lock.lvec; 205 uint32_t LockCount = sr->arg.lock.lcnt; 206 uint32_t LockSequence = sr->arg.lock.lseq; 207 uint32_t i; 208 uint32_t ltype; 209 uint32_t pid = 0; /* SMB2 ignores lock PIDs */ 210 uint32_t timeout = 0; 211 uint32_t status = 0; 212 213 for (i = 0; i < LockCount; i++) { 214 lk = &lvec[i]; 215 216 switch (lk->Flags) { 217 218 case SMB2_LOCKFLAG_SHARED_LOCK: 219 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: 220 /* 221 * Blocking locks have special rules: 222 * Must be exactly one element, else 223 * invalid parameter. 224 */ 225 if (i == 0 && LockCount == 1) { 226 status = smb2_lock_blocking(sr); 227 return (status); 228 } 229 /* FALLTHROUGH */ 230 case SMB2_LOCKFLAG_UNLOCK: 231 default: 232 status = NT_STATUS_INVALID_PARAMETER; 233 goto end_loop; 234 235 /* BEGIN CSTYLED */ 236 case SMB2_LOCKFLAG_SHARED_LOCK | 237 SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 238 /* END CSTYLED */ 239 ltype = SMB_LOCK_TYPE_READONLY; 240 break; 241 242 /* BEGIN CSTYLED */ 243 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK | 244 SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 245 /* END CSTYLED */ 246 ltype = SMB_LOCK_TYPE_READWRITE; 247 break; 248 } 249 250 status = smb_lock_range(sr, lk->Offset, lk->Length, pid, 251 ltype, timeout); 252 if (status != 0) { 253 goto end_loop; 254 } 255 } 256 257 end_loop: 258 if (status != 0) { 259 /* 260 * Oh... we have to rollback. 261 */ 262 while (i > 0) { 263 --i; 264 lk = &lvec[i]; 265 (void) smb_unlock_range(sr, 266 lk->Offset, lk->Length, pid); 267 } 268 } 269 if (status == 0 && LockSequence != 0) 270 smb2_lock_set_lockseq(sr->fid_ofile, LockSequence); 271 272 return (status); 273 } 274 275 /* 276 * Handler for blocking lock requests, which may "go async". 277 * Always exactly one lock request here. 278 */ 279 static uint32_t 280 smb2_lock_blocking(smb_request_t *sr) 281 { 282 lock_elem_t *lk = sr->arg.lock.lvec; 283 uint32_t LockCount = sr->arg.lock.lcnt; 284 uint32_t LockSequence = sr->arg.lock.lseq; 285 uint32_t status; 286 uint32_t ltype; 287 uint32_t pid = 0; /* SMB2 ignores lock PIDs */ 288 uint32_t timeout = UINT_MAX; 289 290 ASSERT(sr->fid_ofile->f_node != NULL); 291 ASSERT(LockCount == 1); 292 293 switch (lk->Flags) { 294 case SMB2_LOCKFLAG_SHARED_LOCK: 295 ltype = SMB_LOCK_TYPE_READONLY; 296 break; 297 298 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: 299 ltype = SMB_LOCK_TYPE_READWRITE; 300 break; 301 302 default: 303 ASSERT(0); 304 return (NT_STATUS_INTERNAL_ERROR); 305 } 306 307 /* 308 * Try the lock first with timeout=0 as we can often 309 * get a lock without going async and avoid an extra 310 * round trip with the client. Also, only go async 311 * for status returns that mean we will block. 312 */ 313 status = smb_lock_range(sr, lk->Offset, lk->Length, pid, ltype, 0); 314 if (status == NT_STATUS_LOCK_NOT_GRANTED || 315 status == NT_STATUS_FILE_LOCK_CONFLICT) { 316 status = smb2sr_go_async(sr); 317 if (status != 0) 318 return (status); 319 status = smb_lock_range(sr, lk->Offset, lk->Length, 320 pid, ltype, timeout); 321 } 322 323 if (status == 0 && LockSequence != 0) 324 smb2_lock_set_lockseq(sr->fid_ofile, LockSequence); 325 326 return (status); 327 } 328 329 /* 330 * Check whether we've stored a given LockSequence 331 * 332 * [MS-SMB2] 3.3.5.14 333 * 334 * The server verifies the LockSequence by performing the following steps: 335 * 336 * 1. The server MUST use LockSequenceIndex as an index into the 337 * Open.LockSequenceArray in order to locate the sequence number entry. 338 * If the index exceeds the maximum extent of the Open.LockSequenceArray, 339 * or LockSequenceIndex is 0, or if the sequence number entry is empty, 340 * the server MUST skip step 2 and continue lock/unlock processing. 341 * 342 * 2. The server MUST compare LockSequenceNumber to the SequenceNumber of 343 * the entry located in step 1. If the sequence numbers are equal, the 344 * server MUST complete the lock/unlock request with success. Otherwise, 345 * the server MUST reset the entry value to empty and continue lock/unlock 346 * processing. 347 */ 348 boolean_t 349 smb2_lock_chk_lockseq(smb_ofile_t *ofile, uint32_t lockseq) 350 { 351 uint32_t lsi; 352 uint8_t lsn; 353 boolean_t rv; 354 355 /* 356 * LockSequenceNumber is the low four bits. 357 * LockSequenceIndex is the remaining 28 bits. 358 * valid range is 1..64, which we convert to an 359 * array index in the range 0..63 360 */ 361 lsn = lockseq & SMB2_LSN_MASK; 362 lsi = (lockseq >> SMB2_LSN_SHIFT); 363 if (lsi == 0 || lsi > SMB_OFILE_LSEQ_MAX) 364 return (B_FALSE); 365 --lsi; 366 367 mutex_enter(&ofile->f_mutex); 368 369 if (ofile->f_lock_seq[lsi] == lsn) { 370 rv = B_TRUE; 371 } else { 372 ofile->f_lock_seq[lsi] = (uint8_t)-1; /* "Empty" */ 373 rv = B_FALSE; 374 } 375 376 mutex_exit(&ofile->f_mutex); 377 378 return (rv); 379 } 380 381 static void 382 smb2_lock_set_lockseq(smb_ofile_t *ofile, uint32_t lockseq) 383 { 384 uint32_t lsi; 385 uint8_t lsn; 386 387 /* 388 * LockSequenceNumber is the low four bits. 389 * LockSequenceIndex is the remaining 28 bits. 390 * valid range is 1..64, which we convert to an 391 * array index in the range 0..63 392 */ 393 lsn = lockseq & SMB2_LSN_MASK; 394 lsi = (lockseq >> SMB2_LSN_SHIFT); 395 if (lsi == 0 || lsi > SMB_OFILE_LSEQ_MAX) { 396 cmn_err(CE_NOTE, "smb2_lock_set_lockseq, index=%u", lsi); 397 return; 398 } 399 --lsi; 400 401 mutex_enter(&ofile->f_mutex); 402 403 ofile->f_lock_seq[lsi] = lsn; 404 405 mutex_exit(&ofile->f_mutex); 406 } 407