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 if (sr->fid_ofile->dh_persist) { 146 smb2_dh_update_locks(sr, sr->fid_ofile); 147 } 148 149 errout: 150 sr->smb2_status = status; 151 DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr); 152 153 if (status) { 154 smb2sr_put_error(sr, status); 155 return (SDRC_SUCCESS); 156 } 157 158 /* 159 * Encode SMB2 Lock reply 160 */ 161 (void) smb_mbc_encodef( 162 &sr->reply, "w..", 163 4); /* StructSize w */ 164 /* reserved .. */ 165 return (SDRC_SUCCESS); 166 } 167 168 /* 169 * Process what should be an array of unlock requests. 170 */ 171 static uint32_t 172 smb2_unlock(smb_request_t *sr) 173 { 174 lock_elem_t *lk; 175 lock_elem_t *lvec = sr->arg.lock.lvec; 176 uint32_t LockCount = sr->arg.lock.lcnt; 177 uint32_t LockSequence = sr->arg.lock.lseq; 178 uint32_t status = 0; 179 uint32_t pid = 0; /* SMB2 ignores lock PIDs. */ 180 int i; 181 182 for (i = 0; i < LockCount; i++) { 183 lk = &lvec[i]; 184 185 if (lk->Flags != SMB2_LOCKFLAG_UNLOCK) { 186 status = NT_STATUS_INVALID_PARAMETER; 187 break; 188 } 189 190 status = smb_unlock_range(sr, lk->Offset, lk->Length, pid); 191 if (status != 0) 192 break; 193 } 194 if (status == 0 && LockSequence != 0) { 195 smb2_lock_set_lockseq(sr->fid_ofile, LockSequence); 196 } 197 198 return (status); 199 } 200 201 /* 202 * Process what should be an array of lock requests. 203 */ 204 static uint32_t 205 smb2_locks(smb_request_t *sr) 206 { 207 lock_elem_t *lk; 208 lock_elem_t *lvec = sr->arg.lock.lvec; 209 uint32_t LockCount = sr->arg.lock.lcnt; 210 uint32_t LockSequence = sr->arg.lock.lseq; 211 uint32_t i; 212 uint32_t ltype; 213 uint32_t pid = 0; /* SMB2 ignores lock PIDs */ 214 uint32_t timeout = 0; 215 uint32_t status = 0; 216 217 for (i = 0; i < LockCount; i++) { 218 lk = &lvec[i]; 219 220 switch (lk->Flags) { 221 222 case SMB2_LOCKFLAG_SHARED_LOCK: 223 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: 224 /* 225 * Blocking locks have special rules: 226 * Must be exactly one element, else 227 * invalid parameter. 228 */ 229 if (i == 0 && LockCount == 1) { 230 status = smb2_lock_blocking(sr); 231 return (status); 232 } 233 /* FALLTHROUGH */ 234 case SMB2_LOCKFLAG_UNLOCK: 235 default: 236 status = NT_STATUS_INVALID_PARAMETER; 237 goto end_loop; 238 239 /* BEGIN CSTYLED */ 240 case SMB2_LOCKFLAG_SHARED_LOCK | 241 SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 242 /* END CSTYLED */ 243 ltype = SMB_LOCK_TYPE_READONLY; 244 break; 245 246 /* BEGIN CSTYLED */ 247 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK | 248 SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 249 /* END CSTYLED */ 250 ltype = SMB_LOCK_TYPE_READWRITE; 251 break; 252 } 253 254 status = smb_lock_range(sr, lk->Offset, lk->Length, pid, 255 ltype, timeout); 256 if (status != 0) { 257 goto end_loop; 258 } 259 } 260 261 end_loop: 262 if (status != 0) { 263 /* 264 * Oh... we have to rollback. 265 */ 266 while (i > 0) { 267 --i; 268 lk = &lvec[i]; 269 (void) smb_unlock_range(sr, 270 lk->Offset, lk->Length, pid); 271 } 272 } 273 if (status == 0 && LockSequence != 0) 274 smb2_lock_set_lockseq(sr->fid_ofile, LockSequence); 275 276 return (status); 277 } 278 279 /* 280 * Handler for blocking lock requests, which may "go async". 281 * Always exactly one lock request here. 282 */ 283 static uint32_t 284 smb2_lock_blocking(smb_request_t *sr) 285 { 286 lock_elem_t *lk = sr->arg.lock.lvec; 287 uint32_t LockCount = sr->arg.lock.lcnt; 288 uint32_t LockSequence = sr->arg.lock.lseq; 289 uint32_t status; 290 uint32_t ltype; 291 uint32_t pid = 0; /* SMB2 ignores lock PIDs */ 292 uint32_t timeout = UINT_MAX; 293 294 ASSERT(sr->fid_ofile->f_node != NULL); 295 ASSERT(LockCount == 1); 296 297 switch (lk->Flags) { 298 case SMB2_LOCKFLAG_SHARED_LOCK: 299 ltype = SMB_LOCK_TYPE_READONLY; 300 break; 301 302 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: 303 ltype = SMB_LOCK_TYPE_READWRITE; 304 break; 305 306 default: 307 ASSERT(0); 308 return (NT_STATUS_INTERNAL_ERROR); 309 } 310 311 /* 312 * Try the lock first with timeout=0 as we can often 313 * get a lock without going async and avoid an extra 314 * round trip with the client. Also, only go async 315 * for status returns that mean we will block. 316 */ 317 status = smb_lock_range(sr, lk->Offset, lk->Length, pid, ltype, 0); 318 if (status == NT_STATUS_LOCK_NOT_GRANTED || 319 status == NT_STATUS_FILE_LOCK_CONFLICT) { 320 status = smb2sr_go_async(sr); 321 if (status != 0) 322 return (status); 323 status = smb_lock_range(sr, lk->Offset, lk->Length, 324 pid, ltype, timeout); 325 } 326 327 if (status == 0 && LockSequence != 0) 328 smb2_lock_set_lockseq(sr->fid_ofile, LockSequence); 329 330 return (status); 331 } 332 333 /* 334 * Check whether we've stored a given LockSequence 335 * 336 * [MS-SMB2] 3.3.5.14 337 * 338 * The server verifies the LockSequence by performing the following steps: 339 * 340 * 1. The server MUST use LockSequenceIndex as an index into the 341 * Open.LockSequenceArray in order to locate the sequence number entry. 342 * If the index exceeds the maximum extent of the Open.LockSequenceArray, 343 * or LockSequenceIndex is 0, or if the sequence number entry is empty, 344 * the server MUST skip step 2 and continue lock/unlock processing. 345 * 346 * 2. The server MUST compare LockSequenceNumber to the SequenceNumber of 347 * the entry located in step 1. If the sequence numbers are equal, the 348 * server MUST complete the lock/unlock request with success. Otherwise, 349 * the server MUST reset the entry value to empty and continue lock/unlock 350 * processing. 351 */ 352 boolean_t 353 smb2_lock_chk_lockseq(smb_ofile_t *ofile, uint32_t lockseq) 354 { 355 uint32_t lsi; 356 uint8_t lsn; 357 boolean_t rv; 358 359 /* 360 * LockSequenceNumber is the low four bits. 361 * LockSequenceIndex is the remaining 28 bits. 362 * valid range is 1..64, which we convert to an 363 * array index in the range 0..63 364 */ 365 lsn = lockseq & SMB2_LSN_MASK; 366 lsi = (lockseq >> SMB2_LSN_SHIFT); 367 if (lsi == 0 || lsi > SMB_OFILE_LSEQ_MAX) 368 return (B_FALSE); 369 --lsi; 370 371 mutex_enter(&ofile->f_mutex); 372 373 if (ofile->f_lock_seq[lsi] == lsn) { 374 rv = B_TRUE; 375 } else { 376 ofile->f_lock_seq[lsi] = (uint8_t)-1; /* "Empty" */ 377 rv = B_FALSE; 378 } 379 380 mutex_exit(&ofile->f_mutex); 381 382 return (rv); 383 } 384 385 static void 386 smb2_lock_set_lockseq(smb_ofile_t *ofile, uint32_t lockseq) 387 { 388 uint32_t lsi; 389 uint8_t lsn; 390 391 /* 392 * LockSequenceNumber is the low four bits. 393 * LockSequenceIndex is the remaining 28 bits. 394 * valid range is 1..64, which we convert to an 395 * array index in the range 0..63 396 */ 397 lsn = lockseq & SMB2_LSN_MASK; 398 lsi = (lockseq >> SMB2_LSN_SHIFT); 399 if (lsi == 0 || lsi > SMB_OFILE_LSEQ_MAX) { 400 cmn_err(CE_NOTE, "smb2_lock_set_lockseq, index=%u", lsi); 401 return; 402 } 403 --lsi; 404 405 mutex_enter(&ofile->f_mutex); 406 407 ofile->f_lock_seq[lsi] = lsn; 408 409 mutex_exit(&ofile->f_mutex); 410 } 411