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
smb2_lock(smb_request_t * sr)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
smb2_lock_async(smb_request_t * sr)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
smb2_lock_exec(smb_request_t * sr,uint16_t LockCount)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
smb2_lock_elem(smb_request_t * sr,struct SMB2_LOCK_ELEMENT * elem)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