xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_lock.c (revision 5db531e3faa94427746eae754b11770fd8416b6d)
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 }
294