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