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