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