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
smb2_lock(smb_request_t * sr)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
smb2_unlock(smb_request_t * sr)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
smb2_locks(smb_request_t * sr)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
smb2_lock_blocking(smb_request_t * sr)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
smb2_lock_chk_lockseq(smb_ofile_t * ofile,uint32_t lockseq)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
smb2_lock_set_lockseq(smb_ofile_t * ofile,uint32_t lockseq)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