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 2014 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 struct SMB2_LOCK_ELEMENT {
23 uint64_t Offset;
24 uint64_t Length;
25 uint32_t Flags;
26 uint32_t reserved;
27 };
28
29 static smb_sdrc_t smb2_lock_async(smb_request_t *);
30 static uint32_t smb2_lock_exec(smb_request_t *, uint16_t);
31 static uint32_t smb2_lock_elem(smb_request_t *, struct SMB2_LOCK_ELEMENT *);
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 struct SMB2_LOCK_ELEMENT elem;
43 smb2fid_t smb2fid;
44 uint32_t save_offset;
45 uint32_t LockSequence;
46 uint32_t status;
47 uint16_t StructSize;
48 uint16_t LockCount;
49 uint16_t i;
50 boolean_t MayBlock = B_FALSE;
51 int rc = 0;
52
53 /*
54 * SMB2 Lock request
55 */
56 rc = smb_mbc_decodef(
57 &sr->smb_data, "wwlqq",
58 &StructSize, /* w */
59 &LockCount, /* w */
60 &LockSequence, /* l */
61 &smb2fid.persistent, /* q */
62 &smb2fid.temporal); /* q */
63 if (rc || StructSize != 48)
64 return (SDRC_ERROR);
65
66 status = smb2sr_lookup_fid(sr, &smb2fid);
67 if (status)
68 goto errout;
69 if (sr->fid_ofile->f_node == NULL || LockCount == 0) {
70 status = NT_STATUS_INVALID_PARAMETER;
71 goto errout;
72 }
73 if (LockCount > smb2_lock_max_elem) {
74 status = NT_STATUS_INSUFFICIENT_RESOURCES;
75 goto errout;
76 }
77
78 /*
79 * Process the array of SMB2_LOCK_ELEMENT structs
80 * We do this twice. (it's always a short list)
81 * The first time, just validate the flags, and check
82 * if any of the locking request might need to block.
83 * The second time (either here, or in the async
84 * handler function) process the locks for real.
85 */
86 save_offset = sr->smb_data.chain_offset;
87 for (i = 0; i < LockCount; i++) {
88 rc = smb_mbc_decodef(
89 &sr->smb_data, "qqll",
90 &elem.Offset, /* q */
91 &elem.Length, /* q */
92 &elem.Flags, /* l */
93 &elem.reserved); /* l */
94 if (rc) {
95 status = NT_STATUS_INVALID_PARAMETER;
96 goto errout;
97 }
98
99 /*
100 * Make sure the flags are valid;
101 * Find out if we might block.
102 */
103 switch (elem.Flags) {
104 case SMB2_LOCKFLAG_SHARED_LOCK:
105 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
106 MayBlock = B_TRUE;
107 break;
108
109 /* BEGIN CSTYLED */
110 case SMB2_LOCKFLAG_SHARED_LOCK |
111 SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
112 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK |
113 SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
114 case SMB2_LOCKFLAG_UNLOCK:
115 /* END CSTYLED */
116 break;
117
118 default:
119 status = NT_STATUS_INVALID_PARAMETER;
120 goto errout;
121 }
122 }
123
124 if (MayBlock) {
125 /*
126 * May need to block. "Go async".
127 */
128 status = smb2sr_go_async(sr, smb2_lock_async);
129 goto errout;
130 }
131
132 sr->smb_data.chain_offset = save_offset;
133 status = smb2_lock_exec(sr, LockCount);
134 if (status)
135 goto errout;
136
137 /*
138 * SMB2 Lock reply (sync)
139 */
140 StructSize = 4;
141 (void) smb_mbc_encodef(
142 &sr->reply, "w..",
143 StructSize); /* w */
144 /* reserved .. */
145 return (SDRC_SUCCESS);
146
147 errout:
148 smb2sr_put_error(sr, status);
149 return (SDRC_SUCCESS);
150 }
151
152 static smb_sdrc_t
153 smb2_lock_async(smb_request_t *sr)
154 {
155 smb2fid_t smb2fid;
156 uint32_t LockSequence;
157 uint32_t status;
158 uint16_t StructSize;
159 uint16_t LockCount;
160 int rc = 0;
161
162 /*
163 * Decode the lock request again. It should all decode
164 * exactly the same as the first time we saw it. If not,
165 * report an "internal error".
166 */
167 rc = smb_mbc_decodef(
168 &sr->smb_data, "wwlqq",
169 &StructSize, /* w */
170 &LockCount, /* w */
171 &LockSequence, /* l */
172 &smb2fid.persistent, /* q */
173 &smb2fid.temporal); /* q */
174 if (rc || StructSize != 48)
175 return (SDRC_ERROR);
176
177 status = smb2sr_lookup_fid(sr, &smb2fid);
178 if (status)
179 goto errout;
180 if (sr->fid_ofile->f_node == NULL || LockCount == 0) {
181 status = NT_STATUS_INTERNAL_ERROR;
182 goto errout;
183 }
184
185 status = smb2_lock_exec(sr, LockCount);
186 if (status)
187 goto errout;
188
189 /*
190 * SMB2 Lock reply (async)
191 */
192 StructSize = 4;
193 (void) smb_mbc_encodef(
194 &sr->reply, "w..",
195 StructSize); /* w */
196 /* reserved .. */
197 return (SDRC_SUCCESS);
198
199 errout:
200 smb2sr_put_error(sr, status);
201 return (SDRC_SUCCESS);
202 }
203
204 /*
205 * Execute the vector of locks. This is the common function called by
206 * either the sync or async code paths. We've already decoded this
207 * request once when we get here, so if there are any decode errors
208 * then it's some kind of internal error.
209 */
210 static uint32_t
211 smb2_lock_exec(smb_request_t *sr, uint16_t LockCount)
212 {
213 struct SMB2_LOCK_ELEMENT elem;
214 uint32_t status = 0;
215 uint16_t i;
216 int rc;
217
218 /*
219 * On entry, out position in the input data should be
220 * after both the SMB2 header and the fixed part of
221 * the SMB Lock request header (24).
222 */
223 ASSERT(sr->smb_data.chain_offset ==
224 (sr->smb2_cmd_hdr + SMB2_HDR_SIZE + 24));
225
226 /*
227 * This is checked by our callers, but let's make sure.
228 */
229 ASSERT(sr->fid_ofile->f_node != NULL);
230
231 for (i = 0; i < LockCount; i++) {
232 rc = smb_mbc_decodef(
233 &sr->smb_data, "qqll",
234 &elem.Offset, /* q */
235 &elem.Length, /* q */
236 &elem.Flags, /* l */
237 &elem.reserved); /* l */
238 if (rc) {
239 status = NT_STATUS_INTERNAL_ERROR;
240 break;
241 }
242 status = smb2_lock_elem(sr, &elem);
243 if (status)
244 break;
245 }
246 return (status);
247 }
248
249 static uint32_t
250 smb2_lock_elem(smb_request_t *sr, struct SMB2_LOCK_ELEMENT *elem)
251 {
252 smb_node_t *node = sr->fid_ofile->f_node;
253 uint32_t status;
254 uint32_t ltype;
255 uint32_t timeout = 0;
256
257 switch (elem->Flags) {
258 case SMB2_LOCKFLAG_SHARED_LOCK:
259 timeout = UINT_MAX;
260 /* FALLTHROUGH */
261 case SMB2_LOCKFLAG_SHARED_LOCK | SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
262 ltype = SMB_LOCK_TYPE_READONLY;
263 status = smb_lock_range(sr,
264 elem->Offset, elem->Length,
265 timeout, ltype);
266 break;
267
268 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
269 timeout = UINT_MAX;
270 /* FALLTHROUGH */
271 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK | SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
272 ltype = SMB_LOCK_TYPE_READWRITE;
273 status = smb_lock_range(sr,
274 elem->Offset, elem->Length,
275 timeout, ltype);
276 break;
277
278 case SMB2_LOCKFLAG_UNLOCK:
279 status = smb_unlock_range(sr, node,
280 elem->Offset, elem->Length);
281 break;
282
283 /*
284 * We've already checked the flags previously, so any
285 * surprises here are some kind of internal error.
286 */
287 default:
288 status = NT_STATUS_INTERNAL_ERROR;
289 break;
290 }
291
292 return (status);
293 }
|
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__lock(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__lock(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__lock(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 }
|