xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_lock.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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 	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_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
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 }
411