xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_lock.c (revision 7f3d7c9289dee6488b3cd2848a68c0b8580d750c)
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  * Copyright 2022-2024 RackTop Systems, Inc.
15  */
16 
17 /*
18  * Dispatch function for SMB2_LOCK
19  */
20 
21 #include <smbsrv/smb2_kproto.h>
22 
23 /*
24  * [MS-SMB2] 2.2.26 LockSequenceIndex, LockSequenceNumber.
25  */
26 #define	SMB2_LSN_SHIFT	4
27 #define	SMB2_LSN_MASK	0xf
28 
29 typedef struct SMB2_LOCK_ELEMENT {
30 	uint64_t Offset;
31 	uint64_t Length;
32 	uint32_t Flags;
33 	uint32_t reserved;
34 } lock_elem_t;
35 
36 static uint32_t smb2_unlock(smb_request_t *);
37 static uint32_t smb2_locks(smb_request_t *);
38 static uint32_t smb2_lock_blocking(smb_request_t *);
39 
40 static boolean_t smb2_lock_chk_lockseq(smb_ofile_t *, uint32_t);
41 static void smb2_lock_set_lockseq(smb_ofile_t *, uint32_t);
42 
43 /*
44  * This is a somewhat arbitrary sanity limit on the length of the
45  * SMB2_LOCK_ELEMENT array.  It usually has length one or two.
46  */
47 int smb2_lock_max_elem = 1024;
48 
49 smb_sdrc_t
50 smb2_lock(smb_request_t *sr)
51 {
52 	lock_elem_t *lvec, *lk;
53 	smb2fid_t smb2fid;
54 	uint32_t LockSequence;
55 	uint32_t status;
56 	uint16_t StructSize;
57 	uint16_t LockCount;
58 	uint16_t i;
59 	int rc;
60 
61 	/*
62 	 * Decode SMB2 Lock request
63 	 */
64 	rc = smb_mbc_decodef(
65 	    &sr->smb_data, "wwlqq",
66 	    &StructSize,		/* w */
67 	    &LockCount,			/* w */
68 	    &LockSequence,		/* l */
69 	    &smb2fid.persistent,	/* q */
70 	    &smb2fid.temporal);		/* q */
71 	if (rc || StructSize != 48)
72 		return (SDRC_ERROR);
73 
74 	/*
75 	 * Want FID lookup before the start probe.
76 	 */
77 	status = smb2sr_lookup_fid(sr, &smb2fid);
78 	DTRACE_SMB2_START(op__Lock, smb_request_t *, sr);
79 
80 	if (status)
81 		goto errout; /* Bad FID */
82 	if (sr->fid_ofile->f_node == NULL || LockCount == 0) {
83 		status = NT_STATUS_INVALID_PARAMETER;
84 		goto errout;
85 	}
86 	if (LockCount > smb2_lock_max_elem) {
87 		status = NT_STATUS_INSUFFICIENT_RESOURCES;
88 		goto errout;
89 	}
90 
91 	/*
92 	 * Check the LockSequence to determine whether a previous
93 	 * lock request succeeded, but the client disconnected
94 	 * (retaining a durable or resilient handle).  If so, this
95 	 * is a lock "replay".  We'll find the lock sequence here
96 	 * and return success without processing the lock again.
97 	 */
98 	if (sr->session->dialect < SMB_VERS_2_1)
99 		LockSequence = 0;
100 	if ((sr->session->dialect == SMB_VERS_2_1) &&
101 	    sr->fid_ofile->dh_vers != SMB2_RESILIENT)
102 		LockSequence = 0;
103 	/* dialect 3.0 or later can always use LockSequence */
104 
105 	if (LockSequence != 0 &&
106 	    smb2_lock_chk_lockseq(sr->fid_ofile, LockSequence)) {
107 		status = NT_STATUS_SUCCESS;
108 		goto errout;
109 	}
110 
111 	/*
112 	 * Parse the array of SMB2_LOCK_ELEMENT structs.
113 	 * This array is free'd in smb_srm_fini.
114 	 */
115 	lvec = smb_srm_zalloc(sr, LockCount * sizeof (*lvec));
116 	for (i = 0; i < LockCount; i++) {
117 		lk = &lvec[i];
118 		rc = smb_mbc_decodef(
119 		    &sr->smb_data, "qqll",
120 		    &lk->Offset,	/* q */
121 		    &lk->Length,	/* q */
122 		    &lk->Flags,		/* l */
123 		    &lk->reserved);	/* l */
124 		if (rc) {
125 			status = NT_STATUS_INVALID_PARAMETER;
126 			goto errout;
127 		}
128 	}
129 
130 	/*
131 	 * [MS-SMB2] 3.3.5.14
132 	 * If the flags of the [first element of] the Locks array
133 	 * [has] SMB2_LOCKFLAG_UNLOCK set, the server MUST process
134 	 * the lock array as a series of unlocks. Otherwise, it
135 	 * MUST process the lock array as a series of lock requests.
136 	 */
137 	sr->arg.lock.lvec = lvec;
138 	sr->arg.lock.lcnt = LockCount;
139 	sr->arg.lock.lseq = LockSequence;
140 	if (lvec[0].Flags & SMB2_LOCKFLAG_UNLOCK) {
141 		status = smb2_unlock(sr);
142 	} else {
143 		status = smb2_locks(sr);
144 	}
145 
146 	if (sr->fid_ofile->dh_persist) {
147 		smb2_dh_update_locks(sr, sr->fid_ofile);
148 	}
149 
150 errout:
151 	sr->smb2_status = status;
152 	DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr);
153 
154 	if (status) {
155 		smb2sr_put_error(sr, status);
156 		return (SDRC_SUCCESS);
157 	}
158 
159 	/*
160 	 * Encode SMB2 Lock reply
161 	 */
162 	(void) smb_mbc_encodef(
163 	    &sr->reply, "w..",
164 	    4); /* StructSize	w */
165 	    /* reserved		.. */
166 	return (SDRC_SUCCESS);
167 }
168 
169 /*
170  * Process what should be an array of unlock requests.
171  */
172 static uint32_t
173 smb2_unlock(smb_request_t *sr)
174 {
175 	lock_elem_t *lk;
176 	lock_elem_t *lvec = sr->arg.lock.lvec;
177 	uint32_t LockCount = sr->arg.lock.lcnt;
178 	uint32_t LockSequence = sr->arg.lock.lseq;
179 	uint32_t status = 0;
180 	uint32_t pid = 0;	/* SMB2 ignores lock PIDs. */
181 	int i;
182 
183 	for (i = 0; i < LockCount; i++) {
184 		lk = &lvec[i];
185 
186 		if (lk->Flags != SMB2_LOCKFLAG_UNLOCK) {
187 			status = NT_STATUS_INVALID_PARAMETER;
188 			break;
189 		}
190 
191 		status = smb_unlock_range(sr, lk->Offset, lk->Length, pid);
192 		if (status != 0)
193 			break;
194 	}
195 	if (status == 0 && LockSequence != 0) {
196 		smb2_lock_set_lockseq(sr->fid_ofile, LockSequence);
197 	}
198 
199 	return (status);
200 }
201 
202 /*
203  * Process what should be an array of lock requests.
204  */
205 static uint32_t
206 smb2_locks(smb_request_t *sr)
207 {
208 	lock_elem_t *lk;
209 	lock_elem_t *lvec = sr->arg.lock.lvec;
210 	uint32_t LockCount = sr->arg.lock.lcnt;
211 	uint32_t LockSequence = sr->arg.lock.lseq;
212 	uint32_t i;
213 	uint32_t ltype;
214 	uint32_t pid = 0;	/* SMB2 ignores lock PIDs */
215 	uint32_t timeout = 0;
216 	uint32_t status = 0;
217 
218 	for (i = 0; i < LockCount; i++) {
219 		lk = &lvec[i];
220 
221 		switch (lk->Flags) {
222 
223 		case SMB2_LOCKFLAG_SHARED_LOCK:
224 		case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
225 			/*
226 			 * Blocking locks have special rules:
227 			 * Must be exactly one element, else
228 			 * invalid parameter.
229 			 */
230 			if (i == 0 && LockCount == 1) {
231 				status = smb2_lock_blocking(sr);
232 				return (status);
233 			}
234 			/* FALLTHROUGH */
235 		case SMB2_LOCKFLAG_UNLOCK:
236 		default:
237 			status = NT_STATUS_INVALID_PARAMETER;
238 			goto end_loop;
239 
240 		/* BEGIN CSTYLED */
241 		case SMB2_LOCKFLAG_SHARED_LOCK |
242 		     SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
243 		/* END CSTYLED */
244 			ltype = SMB_LOCK_TYPE_READONLY;
245 			break;
246 
247 		/* BEGIN CSTYLED */
248 		case SMB2_LOCKFLAG_EXCLUSIVE_LOCK |
249 		     SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
250 		/* END CSTYLED */
251 			ltype = SMB_LOCK_TYPE_READWRITE;
252 			break;
253 		}
254 
255 		status = smb_lock_range(sr, lk->Offset, lk->Length, pid,
256 		    ltype, timeout);
257 		if (status != 0) {
258 			goto end_loop;
259 		}
260 	}
261 
262 end_loop:
263 	if (status != 0) {
264 		/*
265 		 * Oh... we have to rollback.
266 		 */
267 		while (i > 0) {
268 			--i;
269 			lk = &lvec[i];
270 			(void) smb_unlock_range(sr,
271 			    lk->Offset, lk->Length, pid);
272 		}
273 	}
274 	if (status == 0 && LockSequence != 0)
275 		smb2_lock_set_lockseq(sr->fid_ofile, LockSequence);
276 
277 	return (status);
278 }
279 
280 /*
281  * Handler for blocking lock requests, which may "go async".
282  * Always exactly one lock request here.
283  */
284 static uint32_t
285 smb2_lock_blocking(smb_request_t *sr)
286 {
287 	lock_elem_t *lk = sr->arg.lock.lvec;
288 	uint32_t LockCount = sr->arg.lock.lcnt;
289 	uint32_t LockSequence = sr->arg.lock.lseq;
290 	uint32_t status;
291 	uint32_t ltype;
292 	uint32_t pid = 0;	/* SMB2 ignores lock PIDs */
293 	uint32_t timeout = UINT_MAX;
294 
295 	ASSERT(sr->fid_ofile->f_node != NULL);
296 	ASSERT(LockCount == 1);
297 
298 	switch (lk->Flags) {
299 	case SMB2_LOCKFLAG_SHARED_LOCK:
300 		ltype = SMB_LOCK_TYPE_READONLY;
301 		break;
302 
303 	case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
304 		ltype = SMB_LOCK_TYPE_READWRITE;
305 		break;
306 
307 	default:
308 		ASSERT(0);
309 		return (NT_STATUS_INTERNAL_ERROR);
310 	}
311 
312 	/*
313 	 * Try the lock first with timeout=0 as we can often
314 	 * get a lock without going async and avoid an extra
315 	 * round trip with the client.  Also, only go async
316 	 * for status returns that mean we will block.
317 	 */
318 	status = smb_lock_range(sr, lk->Offset, lk->Length, pid, ltype, 0);
319 	if (status == NT_STATUS_LOCK_NOT_GRANTED ||
320 	    status == NT_STATUS_FILE_LOCK_CONFLICT) {
321 		status = smb2sr_go_async_indefinite(sr);
322 		if (status != 0)
323 			return (status);
324 		status = smb_lock_range(sr, lk->Offset, lk->Length,
325 		    pid, ltype, timeout);
326 	}
327 
328 	if (status == 0 && LockSequence != 0)
329 		smb2_lock_set_lockseq(sr->fid_ofile, LockSequence);
330 
331 	return (status);
332 }
333 
334 /*
335  * Check whether we've stored a given LockSequence
336  *
337  * [MS-SMB2] 3.3.5.14
338  *
339  * The server verifies the LockSequence by performing the following steps:
340  *
341  * 1. The server MUST use LockSequenceIndex as an index into the
342  * Open.LockSequenceArray in order to locate the sequence number entry.
343  * If the index exceeds the maximum extent of the Open.LockSequenceArray,
344  * or LockSequenceIndex is 0, or if the sequence number entry is empty,
345  * the server MUST skip step 2 and continue lock/unlock processing.
346  *
347  * 2. The server MUST compare LockSequenceNumber to the SequenceNumber of
348  * the entry located in step 1. If the sequence numbers are equal, the
349  * server MUST complete the lock/unlock request with success. Otherwise,
350  * the server MUST reset the entry value to empty and continue lock/unlock
351  * processing.
352  */
353 boolean_t
354 smb2_lock_chk_lockseq(smb_ofile_t *ofile, uint32_t lockseq)
355 {
356 	uint32_t lsi;
357 	uint8_t lsn;
358 	boolean_t rv;
359 
360 	/*
361 	 * LockSequenceNumber is the low four bits.
362 	 * LockSequenceIndex is the remaining 28 bits.
363 	 * valid range is 1..64, which we convert to an
364 	 * array index in the range 0..63
365 	 */
366 	lsn = lockseq & SMB2_LSN_MASK;
367 	lsi = (lockseq >> SMB2_LSN_SHIFT);
368 	if (lsi == 0 || lsi > SMB_OFILE_LSEQ_MAX)
369 		return (B_FALSE);
370 	--lsi;
371 
372 	mutex_enter(&ofile->f_mutex);
373 
374 	if (ofile->f_lock_seq[lsi] == lsn) {
375 		rv = B_TRUE;
376 	} else {
377 		ofile->f_lock_seq[lsi] = (uint8_t)-1;	/* "Empty" */
378 		rv = B_FALSE;
379 	}
380 
381 	mutex_exit(&ofile->f_mutex);
382 
383 	return (rv);
384 }
385 
386 static void
387 smb2_lock_set_lockseq(smb_ofile_t *ofile, uint32_t lockseq)
388 {
389 	uint32_t lsi;
390 	uint8_t lsn;
391 
392 	/*
393 	 * LockSequenceNumber is the low four bits.
394 	 * LockSequenceIndex is the remaining 28 bits.
395 	 * valid range is 1..64, which we convert to an
396 	 * array index in the range 0..63
397 	 */
398 	lsn = lockseq & SMB2_LSN_MASK;
399 	lsi = (lockseq >> SMB2_LSN_SHIFT);
400 	if (lsi == 0 || lsi > SMB_OFILE_LSEQ_MAX) {
401 		cmn_err(CE_NOTE, "smb2_lock_set_lockseq, index=%u", lsi);
402 		return;
403 	}
404 	--lsi;
405 
406 	mutex_enter(&ofile->f_mutex);
407 
408 	ofile->f_lock_seq[lsi] = lsn;
409 
410 	mutex_exit(&ofile->f_mutex);
411 }
412