xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_lock.c (revision 17a5fa85fe0c34b1146222e40a80b42f2aae8500)
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 2016 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 typedef struct SMB2_LOCK_ELEMENT {
23 	uint64_t Offset;
24 	uint64_t Length;
25 	uint32_t Flags;
26 	uint32_t reserved;
27 } lock_elem_t;
28 
29 static uint32_t smb2_unlock(smb_request_t *);
30 static uint32_t smb2_locks(smb_request_t *);
31 static smb_sdrc_t smb2_lock_async(smb_request_t *);
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 	lock_elem_t *lvec, *lk;
43 	smb2fid_t smb2fid;
44 	uint32_t LockSequence;
45 	uint32_t status;
46 	uint16_t StructSize;
47 	uint16_t LockCount;
48 	uint16_t i;
49 	int rc;
50 
51 	/*
52 	 * Decode SMB2 Lock request
53 	 */
54 	rc = smb_mbc_decodef(
55 	    &sr->smb_data, "wwlqq",
56 	    &StructSize,		/* w */
57 	    &LockCount,			/* w */
58 	    &LockSequence,		/* l */
59 	    &smb2fid.persistent,	/* q */
60 	    &smb2fid.temporal);		/* q */
61 	if (rc || StructSize != 48)
62 		return (SDRC_ERROR);
63 
64 	/*
65 	 * Want FID lookup before the start probe.
66 	 */
67 	status = smb2sr_lookup_fid(sr, &smb2fid);
68 	DTRACE_SMB2_START(op__Lock, smb_request_t *, sr);
69 
70 	if (status)
71 		goto errout; /* Bad FID */
72 	if (sr->fid_ofile->f_node == NULL || LockCount == 0) {
73 		status = NT_STATUS_INVALID_PARAMETER;
74 		goto errout;
75 	}
76 	if (LockCount > smb2_lock_max_elem) {
77 		status = NT_STATUS_INSUFFICIENT_RESOURCES;
78 		goto errout;
79 	}
80 
81 	/*
82 	 * Parse the array of SMB2_LOCK_ELEMENT structs.
83 	 * This array is free'd in smb_srm_fini.
84 	 */
85 	lvec = smb_srm_zalloc(sr, LockCount * sizeof (*lvec));
86 	for (i = 0; i < LockCount; i++) {
87 		lk = &lvec[i];
88 		rc = smb_mbc_decodef(
89 		    &sr->smb_data, "qqll",
90 		    &lk->Offset,	/* q */
91 		    &lk->Length,	/* q */
92 		    &lk->Flags,		/* l */
93 		    &lk->reserved);	/* l */
94 		if (rc) {
95 			status = NT_STATUS_INVALID_PARAMETER;
96 			goto errout;
97 		}
98 	}
99 
100 	/*
101 	 * [MS-SMB2] 3.3.5.14
102 	 * If the flags of the [first element of] the Locks array
103 	 * [has] SMB2_LOCKFLAG_UNLOCK set, the server MUST process
104 	 * the lock array as a series of unlocks. Otherwise, it
105 	 * MUST process the lock array as a series of lock requests.
106 	 */
107 	sr->arg.lock.lvec = lvec;
108 	sr->arg.lock.lcnt = LockCount;
109 	sr->arg.lock.lseq = LockSequence;
110 	if (lvec[0].Flags & SMB2_LOCKFLAG_UNLOCK) {
111 		status = smb2_unlock(sr);
112 	} else {
113 		status = smb2_locks(sr);
114 	}
115 
116 errout:
117 	sr->smb2_status = status;
118 	if (status != NT_STATUS_PENDING) {
119 		DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr);
120 	}
121 
122 	if (status) {
123 		smb2sr_put_error(sr, status);
124 		return (SDRC_SUCCESS);
125 	}
126 
127 	/*
128 	 * Encode SMB2 Lock reply (sync)
129 	 */
130 	(void) smb_mbc_encodef(
131 	    &sr->reply, "w..",
132 	    4); /* StructSize	w */
133 	    /* reserved		.. */
134 	return (SDRC_SUCCESS);
135 }
136 
137 /*
138  * Process what should be an array of unlock requests.
139  */
140 static uint32_t
141 smb2_unlock(smb_request_t *sr)
142 {
143 	lock_elem_t *lk;
144 	lock_elem_t *lvec = sr->arg.lock.lvec;
145 	uint32_t LockCount = sr->arg.lock.lcnt;
146 	uint32_t LockSequence = sr->arg.lock.lseq;
147 	uint32_t status = 0;
148 	uint32_t pid = 0;	/* SMB2 ignores lock PIDs. */
149 	int i;
150 
151 	for (i = 0; i < LockCount; i++) {
152 		lk = &lvec[i];
153 
154 		if (lk->Flags != SMB2_LOCKFLAG_UNLOCK) {
155 			status = NT_STATUS_INVALID_PARAMETER;
156 			break;
157 		}
158 
159 		status = smb_unlock_range(sr, lk->Offset, lk->Length, pid);
160 		if (status != 0)
161 			break;
162 	}
163 	(void) LockSequence; /* todo */
164 
165 	return (status);
166 }
167 
168 /*
169  * Process what should be an array of lock requests.
170  */
171 static uint32_t
172 smb2_locks(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 i;
178 	uint32_t ltype;
179 	uint32_t pid = 0;	/* SMB2 ignores lock PIDs */
180 	uint32_t timeout = 0;
181 	uint32_t status = 0;
182 
183 	for (i = 0; i < LockCount; i++) {
184 		lk = &lvec[i];
185 
186 		switch (lk->Flags) {
187 
188 		case SMB2_LOCKFLAG_SHARED_LOCK:
189 		case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
190 			/*
191 			 * Blocking locks have special rules:
192 			 * Must be exactly one element, else
193 			 * invalid parameter.
194 			 */
195 			if (i == 0 && LockCount == 1) {
196 				status = smb2sr_go_async(sr, smb2_lock_async);
197 				return (status);
198 			}
199 			/* FALLTHROUGH */
200 		case SMB2_LOCKFLAG_UNLOCK:
201 		default:
202 			status = NT_STATUS_INVALID_PARAMETER;
203 			goto end_loop;
204 
205 		/* BEGIN CSTYLED */
206 		case SMB2_LOCKFLAG_SHARED_LOCK |
207 		     SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
208 		/* END CSTYLED */
209 			ltype = SMB_LOCK_TYPE_READONLY;
210 			break;
211 
212 		/* BEGIN CSTYLED */
213 		case SMB2_LOCKFLAG_EXCLUSIVE_LOCK |
214 		     SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
215 		/* END CSTYLED */
216 			ltype = SMB_LOCK_TYPE_READWRITE;
217 			break;
218 		}
219 
220 		status = smb_lock_range(sr, lk->Offset, lk->Length, pid,
221 		    ltype, timeout);
222 		if (status != 0) {
223 			goto end_loop;
224 		}
225 	}
226 
227 end_loop:
228 	if (status != 0) {
229 		/*
230 		 * Oh... we have to rollback.
231 		 */
232 		while (i > 0) {
233 			--i;
234 			lk = &lvec[i];
235 			(void) smb_unlock_range(sr,
236 			    lk->Offset, lk->Length, pid);
237 		}
238 	}
239 
240 	return (status);
241 }
242 
243 /*
244  * Async handler for blocking lock requests.
245  * Always exactly one lock request here.
246  */
247 static smb_sdrc_t
248 smb2_lock_async(smb_request_t *sr)
249 {
250 	lock_elem_t *lk = sr->arg.lock.lvec;
251 	uint32_t LockCount = sr->arg.lock.lcnt;
252 	uint32_t status;
253 	uint32_t ltype;
254 	uint32_t pid = 0;	/* SMB2 ignores lock PIDs */
255 	uint32_t timeout = UINT_MAX;
256 
257 	ASSERT(sr->fid_ofile->f_node != NULL);
258 	ASSERT(LockCount == 1);
259 
260 	switch (lk->Flags) {
261 	case SMB2_LOCKFLAG_SHARED_LOCK:
262 		ltype = SMB_LOCK_TYPE_READONLY;
263 		break;
264 
265 	case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
266 		ltype = SMB_LOCK_TYPE_READWRITE;
267 		break;
268 
269 	default:
270 		ASSERT(0);
271 		status = NT_STATUS_INTERNAL_ERROR;
272 		goto errout;
273 	}
274 
275 	status = smb_lock_range(sr, lk->Offset, lk->Length, pid,
276 	    ltype, timeout);
277 
278 errout:
279 	sr->smb2_status = status;
280 	DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr);
281 
282 	if (status != 0) {
283 		smb2sr_put_error(sr, status);
284 		return (SDRC_SUCCESS);
285 	}
286 
287 	/*
288 	 * SMB2 Lock reply (async)
289 	 */
290 	(void) smb_mbc_encodef(
291 	    &sr->reply, "w..",
292 	    4); /* StructSize	w */
293 	    /* reserved		.. */
294 	return (SDRC_SUCCESS);
295 }
296