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