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 * Decode 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 /* 65 * Want FID lookup before the start probe. 66 */ 67 status = smb2sr_lookup_fid(sr, &smb2fid); 68 DTRACE_SMB2_START(op__Lock, smb_request_t *, sr); 69 70 if (status) 71 goto errout; /* Bad FID */ 72 if (sr->fid_ofile->f_node == NULL || LockCount == 0) { 73 status = NT_STATUS_INVALID_PARAMETER; 74 goto errout; 75 } 76 if (LockCount > smb2_lock_max_elem) { 77 status = NT_STATUS_INSUFFICIENT_RESOURCES; 78 goto errout; 79 } 80 81 /* 82 * Parse the array of SMB2_LOCK_ELEMENT structs. 83 * This array is free'd in smb_srm_fini. 84 */ 85 lvec = smb_srm_zalloc(sr, LockCount * sizeof (*lvec)); 86 for (i = 0; i < LockCount; i++) { 87 lk = &lvec[i]; 88 rc = smb_mbc_decodef( 89 &sr->smb_data, "qqll", 90 &lk->Offset, /* q */ 91 &lk->Length, /* q */ 92 &lk->Flags, /* l */ 93 &lk->reserved); /* l */ 94 if (rc) { 95 status = NT_STATUS_INVALID_PARAMETER; 96 goto errout; 97 } 98 } 99 100 /* 101 * [MS-SMB2] 3.3.5.14 102 * If the flags of the [first element of] the Locks array 103 * [has] SMB2_LOCKFLAG_UNLOCK set, the server MUST process 104 * the lock array as a series of unlocks. Otherwise, it 105 * MUST process the lock array as a series of lock requests. 106 */ 107 sr->arg.lock.lvec = lvec; 108 sr->arg.lock.lcnt = LockCount; 109 sr->arg.lock.lseq = LockSequence; 110 if (lvec[0].Flags & SMB2_LOCKFLAG_UNLOCK) { 111 status = smb2_unlock(sr); 112 } else { 113 status = smb2_locks(sr); 114 } 115 116 errout: 117 sr->smb2_status = status; 118 if (status != NT_STATUS_PENDING) { 119 DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr); 120 } 121 122 if (status) { 123 smb2sr_put_error(sr, status); 124 return (SDRC_SUCCESS); 125 } 126 127 /* 128 * Encode SMB2 Lock reply (sync) 129 */ 130 (void) smb_mbc_encodef( 131 &sr->reply, "w..", 132 4); /* StructSize w */ 133 /* reserved .. */ 134 return (SDRC_SUCCESS); 135 } 136 137 /* 138 * Process what should be an array of unlock requests. 139 */ 140 static uint32_t 141 smb2_unlock(smb_request_t *sr) 142 { 143 lock_elem_t *lk; 144 lock_elem_t *lvec = sr->arg.lock.lvec; 145 uint32_t LockCount = sr->arg.lock.lcnt; 146 uint32_t LockSequence = sr->arg.lock.lseq; 147 uint32_t status = 0; 148 uint32_t pid = 0; /* SMB2 ignores lock PIDs. */ 149 int i; 150 151 for (i = 0; i < LockCount; i++) { 152 lk = &lvec[i]; 153 154 if (lk->Flags != SMB2_LOCKFLAG_UNLOCK) { 155 status = NT_STATUS_INVALID_PARAMETER; 156 break; 157 } 158 159 status = smb_unlock_range(sr, lk->Offset, lk->Length, pid); 160 if (status != 0) 161 break; 162 } 163 (void) LockSequence; /* todo */ 164 165 return (status); 166 } 167 168 /* 169 * Process what should be an array of lock requests. 170 */ 171 static uint32_t 172 smb2_locks(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 i; 178 uint32_t ltype; 179 uint32_t pid = 0; /* SMB2 ignores lock PIDs */ 180 uint32_t timeout = 0; 181 uint32_t status = 0; 182 183 for (i = 0; i < LockCount; i++) { 184 lk = &lvec[i]; 185 186 switch (lk->Flags) { 187 188 case SMB2_LOCKFLAG_SHARED_LOCK: 189 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: 190 /* 191 * Blocking locks have special rules: 192 * Must be exactly one element, else 193 * invalid parameter. 194 */ 195 if (i == 0 && LockCount == 1) { 196 status = smb2sr_go_async(sr, smb2_lock_async); 197 return (status); 198 } 199 /* FALLTHROUGH */ 200 case SMB2_LOCKFLAG_UNLOCK: 201 default: 202 status = NT_STATUS_INVALID_PARAMETER; 203 goto end_loop; 204 205 /* BEGIN CSTYLED */ 206 case SMB2_LOCKFLAG_SHARED_LOCK | 207 SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 208 /* END CSTYLED */ 209 ltype = SMB_LOCK_TYPE_READONLY; 210 break; 211 212 /* BEGIN CSTYLED */ 213 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK | 214 SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 215 /* END CSTYLED */ 216 ltype = SMB_LOCK_TYPE_READWRITE; 217 break; 218 } 219 220 status = smb_lock_range(sr, lk->Offset, lk->Length, pid, 221 ltype, timeout); 222 if (status != 0) { 223 goto end_loop; 224 } 225 } 226 227 end_loop: 228 if (status != 0) { 229 /* 230 * Oh... we have to rollback. 231 */ 232 while (i > 0) { 233 --i; 234 lk = &lvec[i]; 235 (void) smb_unlock_range(sr, 236 lk->Offset, lk->Length, pid); 237 } 238 } 239 240 return (status); 241 } 242 243 /* 244 * Async handler for blocking lock requests. 245 * Always exactly one lock request here. 246 */ 247 static smb_sdrc_t 248 smb2_lock_async(smb_request_t *sr) 249 { 250 lock_elem_t *lk = sr->arg.lock.lvec; 251 uint32_t LockCount = sr->arg.lock.lcnt; 252 uint32_t status; 253 uint32_t ltype; 254 uint32_t pid = 0; /* SMB2 ignores lock PIDs */ 255 uint32_t timeout = UINT_MAX; 256 257 ASSERT(sr->fid_ofile->f_node != NULL); 258 ASSERT(LockCount == 1); 259 260 switch (lk->Flags) { 261 case SMB2_LOCKFLAG_SHARED_LOCK: 262 ltype = SMB_LOCK_TYPE_READONLY; 263 break; 264 265 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: 266 ltype = SMB_LOCK_TYPE_READWRITE; 267 break; 268 269 default: 270 ASSERT(0); 271 status = NT_STATUS_INTERNAL_ERROR; 272 goto errout; 273 } 274 275 status = smb_lock_range(sr, lk->Offset, lk->Length, pid, 276 ltype, timeout); 277 278 errout: 279 sr->smb2_status = status; 280 DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr); 281 282 if (status != 0) { 283 smb2sr_put_error(sr, status); 284 return (SDRC_SUCCESS); 285 } 286 287 /* 288 * SMB2 Lock reply (async) 289 */ 290 (void) smb_mbc_encodef( 291 &sr->reply, "w..", 292 4); /* StructSize w */ 293 /* reserved .. */ 294 return (SDRC_SUCCESS); 295 } 296