xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_lock.c (revision 0897f7fbb62326e60e858c62a1654b2ca3e2667e)
1a90cf9f2SGordon Ross /*
2a90cf9f2SGordon Ross  * This file and its contents are supplied under the terms of the
3a90cf9f2SGordon Ross  * Common Development and Distribution License ("CDDL"), version 1.0.
4a90cf9f2SGordon Ross  * You may only use this file in accordance with the terms of version
5a90cf9f2SGordon Ross  * 1.0 of the CDDL.
6a90cf9f2SGordon Ross  *
7a90cf9f2SGordon Ross  * A full copy of the text of the CDDL should have accompanied this
8a90cf9f2SGordon Ross  * source.  A copy of the CDDL is also available via the Internet at
9a90cf9f2SGordon Ross  * http://www.illumos.org/license/CDDL.
10a90cf9f2SGordon Ross  */
11a90cf9f2SGordon Ross 
12a90cf9f2SGordon Ross /*
13*0897f7fbSGordon Ross  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
14a90cf9f2SGordon Ross  */
15a90cf9f2SGordon Ross 
16a90cf9f2SGordon Ross /*
17a90cf9f2SGordon Ross  * Dispatch function for SMB2_LOCK
18a90cf9f2SGordon Ross  */
19a90cf9f2SGordon Ross 
20a90cf9f2SGordon Ross #include <smbsrv/smb2_kproto.h>
21a90cf9f2SGordon Ross 
22*0897f7fbSGordon Ross typedef struct SMB2_LOCK_ELEMENT {
23a90cf9f2SGordon Ross 	uint64_t Offset;
24a90cf9f2SGordon Ross 	uint64_t Length;
25a90cf9f2SGordon Ross 	uint32_t Flags;
26a90cf9f2SGordon Ross 	uint32_t reserved;
27*0897f7fbSGordon Ross } lock_elem_t;
28a90cf9f2SGordon Ross 
29*0897f7fbSGordon Ross static uint32_t smb2_unlock(smb_request_t *);
30*0897f7fbSGordon Ross static uint32_t smb2_locks(smb_request_t *);
31a90cf9f2SGordon Ross static smb_sdrc_t smb2_lock_async(smb_request_t *);
32a90cf9f2SGordon Ross 
33a90cf9f2SGordon Ross /*
34a90cf9f2SGordon Ross  * This is a somewhat arbitrary sanity limit on the length of the
35a90cf9f2SGordon Ross  * SMB2_LOCK_ELEMENT array.  It usually has length one or two.
36a90cf9f2SGordon Ross  */
37a90cf9f2SGordon Ross int smb2_lock_max_elem = 1024;
38a90cf9f2SGordon Ross 
39a90cf9f2SGordon Ross smb_sdrc_t
40a90cf9f2SGordon Ross smb2_lock(smb_request_t *sr)
41a90cf9f2SGordon Ross {
42*0897f7fbSGordon Ross 	lock_elem_t *lvec, *lk;
43a90cf9f2SGordon Ross 	smb2fid_t smb2fid;
44a90cf9f2SGordon Ross 	uint32_t LockSequence;
45a90cf9f2SGordon Ross 	uint32_t status;
46a90cf9f2SGordon Ross 	uint16_t StructSize;
47a90cf9f2SGordon Ross 	uint16_t LockCount;
48a90cf9f2SGordon Ross 	uint16_t i;
49*0897f7fbSGordon Ross 	int rc;
50a90cf9f2SGordon Ross 
51a90cf9f2SGordon Ross 	/*
52a90cf9f2SGordon Ross 	 * SMB2 Lock request
53a90cf9f2SGordon Ross 	 */
54a90cf9f2SGordon Ross 	rc = smb_mbc_decodef(
55a90cf9f2SGordon Ross 	    &sr->smb_data, "wwlqq",
56a90cf9f2SGordon Ross 	    &StructSize,		/* w */
57a90cf9f2SGordon Ross 	    &LockCount,			/* w */
58a90cf9f2SGordon Ross 	    &LockSequence,		/* l */
59a90cf9f2SGordon Ross 	    &smb2fid.persistent,	/* q */
60a90cf9f2SGordon Ross 	    &smb2fid.temporal);		/* q */
61a90cf9f2SGordon Ross 	if (rc || StructSize != 48)
62a90cf9f2SGordon Ross 		return (SDRC_ERROR);
63a90cf9f2SGordon Ross 
64a90cf9f2SGordon Ross 	status = smb2sr_lookup_fid(sr, &smb2fid);
65a90cf9f2SGordon Ross 	if (status)
66a90cf9f2SGordon Ross 		goto errout;
67a90cf9f2SGordon Ross 	if (sr->fid_ofile->f_node == NULL || LockCount == 0) {
68a90cf9f2SGordon Ross 		status = NT_STATUS_INVALID_PARAMETER;
69a90cf9f2SGordon Ross 		goto errout;
70a90cf9f2SGordon Ross 	}
71a90cf9f2SGordon Ross 	if (LockCount > smb2_lock_max_elem) {
72a90cf9f2SGordon Ross 		status = NT_STATUS_INSUFFICIENT_RESOURCES;
73a90cf9f2SGordon Ross 		goto errout;
74a90cf9f2SGordon Ross 	}
75a90cf9f2SGordon Ross 
76a90cf9f2SGordon Ross 	/*
77*0897f7fbSGordon Ross 	 * Parse the array of SMB2_LOCK_ELEMENT structs.
78*0897f7fbSGordon Ross 	 * This array is free'd in smb_srm_fini.
79a90cf9f2SGordon Ross 	 */
80*0897f7fbSGordon Ross 	lvec = smb_srm_zalloc(sr, LockCount * sizeof (*lvec));
81a90cf9f2SGordon Ross 	for (i = 0; i < LockCount; i++) {
82*0897f7fbSGordon Ross 		lk = &lvec[i];
83a90cf9f2SGordon Ross 		rc = smb_mbc_decodef(
84a90cf9f2SGordon Ross 		    &sr->smb_data, "qqll",
85*0897f7fbSGordon Ross 		    &lk->Offset,	/* q */
86*0897f7fbSGordon Ross 		    &lk->Length,	/* q */
87*0897f7fbSGordon Ross 		    &lk->Flags,		/* l */
88*0897f7fbSGordon Ross 		    &lk->reserved);	/* l */
89a90cf9f2SGordon Ross 		if (rc) {
90a90cf9f2SGordon Ross 			status = NT_STATUS_INVALID_PARAMETER;
91a90cf9f2SGordon Ross 			goto errout;
92a90cf9f2SGordon Ross 		}
93*0897f7fbSGordon Ross 	}
94a90cf9f2SGordon Ross 
95a90cf9f2SGordon Ross 	/*
96*0897f7fbSGordon Ross 	 * [MS-SMB2] 3.3.5.14
97*0897f7fbSGordon Ross 	 * If the flags of the [first element of] the Locks array
98*0897f7fbSGordon Ross 	 * [has] SMB2_LOCKFLAG_UNLOCK set, the server MUST process
99*0897f7fbSGordon Ross 	 * the lock array as a series of unlocks. Otherwise, it
100*0897f7fbSGordon Ross 	 * MUST process the lock array as a series of lock requests.
101a90cf9f2SGordon Ross 	 */
102*0897f7fbSGordon Ross 	sr->arg.lock.lvec = lvec;
103*0897f7fbSGordon Ross 	sr->arg.lock.lcnt = LockCount;
104*0897f7fbSGordon Ross 	sr->arg.lock.lseq = LockSequence;
105*0897f7fbSGordon Ross 	if (lvec[0].Flags & SMB2_LOCKFLAG_UNLOCK) {
106*0897f7fbSGordon Ross 		status = smb2_unlock(sr);
107*0897f7fbSGordon Ross 	} else {
108*0897f7fbSGordon Ross 		status = smb2_locks(sr);
109a90cf9f2SGordon Ross 	}
110a90cf9f2SGordon Ross 	if (status)
111a90cf9f2SGordon Ross 		goto errout;
112a90cf9f2SGordon Ross 
113a90cf9f2SGordon Ross 	/*
114a90cf9f2SGordon Ross 	 * SMB2 Lock reply (sync)
115a90cf9f2SGordon Ross 	 */
116a90cf9f2SGordon Ross 	(void) smb_mbc_encodef(
117a90cf9f2SGordon Ross 	    &sr->reply, "w..",
118*0897f7fbSGordon Ross 	    4); /* StructSize	w */
119a90cf9f2SGordon Ross 	    /* reserved		.. */
120a90cf9f2SGordon Ross 	return (SDRC_SUCCESS);
121a90cf9f2SGordon Ross 
122a90cf9f2SGordon Ross errout:
123a90cf9f2SGordon Ross 	smb2sr_put_error(sr, status);
124a90cf9f2SGordon Ross 	return (SDRC_SUCCESS);
125a90cf9f2SGordon Ross }
126a90cf9f2SGordon Ross 
127*0897f7fbSGordon Ross /*
128*0897f7fbSGordon Ross  * Process what should be an array of unlock requests.
129*0897f7fbSGordon Ross  */
130*0897f7fbSGordon Ross static uint32_t
131*0897f7fbSGordon Ross smb2_unlock(smb_request_t *sr)
132*0897f7fbSGordon Ross {
133*0897f7fbSGordon Ross 	lock_elem_t *lk;
134*0897f7fbSGordon Ross 	lock_elem_t *lvec = sr->arg.lock.lvec;
135*0897f7fbSGordon Ross 	uint32_t LockCount = sr->arg.lock.lcnt;
136*0897f7fbSGordon Ross 	uint32_t LockSequence = sr->arg.lock.lseq;
137*0897f7fbSGordon Ross 	uint32_t status = 0;
138*0897f7fbSGordon Ross 	uint32_t pid = 0;	/* SMB2 ignores lock PIDs. */
139*0897f7fbSGordon Ross 	int i;
140*0897f7fbSGordon Ross 
141*0897f7fbSGordon Ross 	for (i = 0; i < LockCount; i++) {
142*0897f7fbSGordon Ross 		lk = &lvec[i];
143*0897f7fbSGordon Ross 
144*0897f7fbSGordon Ross 		if (lk->Flags != SMB2_LOCKFLAG_UNLOCK) {
145*0897f7fbSGordon Ross 			status = NT_STATUS_INVALID_PARAMETER;
146*0897f7fbSGordon Ross 			break;
147*0897f7fbSGordon Ross 		}
148*0897f7fbSGordon Ross 
149*0897f7fbSGordon Ross 		status = smb_unlock_range(sr, lk->Offset, lk->Length, pid);
150*0897f7fbSGordon Ross 		if (status != 0)
151*0897f7fbSGordon Ross 			break;
152*0897f7fbSGordon Ross 	}
153*0897f7fbSGordon Ross 	(void) LockSequence; /* todo */
154*0897f7fbSGordon Ross 
155*0897f7fbSGordon Ross 	return (status);
156*0897f7fbSGordon Ross }
157*0897f7fbSGordon Ross 
158*0897f7fbSGordon Ross /*
159*0897f7fbSGordon Ross  * Process what should be an array of lock requests.
160*0897f7fbSGordon Ross  */
161*0897f7fbSGordon Ross static uint32_t
162*0897f7fbSGordon Ross smb2_locks(smb_request_t *sr)
163*0897f7fbSGordon Ross {
164*0897f7fbSGordon Ross 	lock_elem_t *lk;
165*0897f7fbSGordon Ross 	lock_elem_t *lvec = sr->arg.lock.lvec;
166*0897f7fbSGordon Ross 	uint32_t LockCount = sr->arg.lock.lcnt;
167*0897f7fbSGordon Ross 	uint32_t i;
168*0897f7fbSGordon Ross 	uint32_t ltype;
169*0897f7fbSGordon Ross 	uint32_t pid = 0;	/* SMB2 ignores lock PIDs */
170*0897f7fbSGordon Ross 	uint32_t timeout = 0;
171*0897f7fbSGordon Ross 	uint32_t status = 0;
172*0897f7fbSGordon Ross 
173*0897f7fbSGordon Ross 	for (i = 0; i < LockCount; i++) {
174*0897f7fbSGordon Ross 		lk = &lvec[i];
175*0897f7fbSGordon Ross 
176*0897f7fbSGordon Ross 		switch (lk->Flags) {
177*0897f7fbSGordon Ross 
178*0897f7fbSGordon Ross 		case SMB2_LOCKFLAG_SHARED_LOCK:
179*0897f7fbSGordon Ross 		case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
180*0897f7fbSGordon Ross 			/*
181*0897f7fbSGordon Ross 			 * Blocking locks have special rules:
182*0897f7fbSGordon Ross 			 * Must be exactly one element, else
183*0897f7fbSGordon Ross 			 * invalid parameter.
184*0897f7fbSGordon Ross 			 */
185*0897f7fbSGordon Ross 			if (i == 0 && LockCount == 1) {
186*0897f7fbSGordon Ross 				status = smb2sr_go_async(sr, smb2_lock_async);
187*0897f7fbSGordon Ross 				return (status);
188*0897f7fbSGordon Ross 			}
189*0897f7fbSGordon Ross 			/* FALLTHROUGH */
190*0897f7fbSGordon Ross 		case SMB2_LOCKFLAG_UNLOCK:
191*0897f7fbSGordon Ross 		default:
192*0897f7fbSGordon Ross 			status = NT_STATUS_INVALID_PARAMETER;
193*0897f7fbSGordon Ross 			goto end_loop;
194*0897f7fbSGordon Ross 
195*0897f7fbSGordon Ross 		/* BEGIN CSTYLED */
196*0897f7fbSGordon Ross 		case SMB2_LOCKFLAG_SHARED_LOCK |
197*0897f7fbSGordon Ross 		     SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
198*0897f7fbSGordon Ross 		/* END CSTYLED */
199*0897f7fbSGordon Ross 			ltype = SMB_LOCK_TYPE_READONLY;
200*0897f7fbSGordon Ross 			break;
201*0897f7fbSGordon Ross 
202*0897f7fbSGordon Ross 		/* BEGIN CSTYLED */
203*0897f7fbSGordon Ross 		case SMB2_LOCKFLAG_EXCLUSIVE_LOCK |
204*0897f7fbSGordon Ross 		     SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
205*0897f7fbSGordon Ross 		/* END CSTYLED */
206*0897f7fbSGordon Ross 			ltype = SMB_LOCK_TYPE_READWRITE;
207*0897f7fbSGordon Ross 			break;
208*0897f7fbSGordon Ross 		}
209*0897f7fbSGordon Ross 
210*0897f7fbSGordon Ross 		status = smb_lock_range(sr, lk->Offset, lk->Length, pid,
211*0897f7fbSGordon Ross 		    ltype, timeout);
212*0897f7fbSGordon Ross 		if (status != 0) {
213*0897f7fbSGordon Ross 			goto end_loop;
214*0897f7fbSGordon Ross 		}
215*0897f7fbSGordon Ross 	}
216*0897f7fbSGordon Ross 
217*0897f7fbSGordon Ross end_loop:
218*0897f7fbSGordon Ross 	if (status != 0) {
219*0897f7fbSGordon Ross 		/*
220*0897f7fbSGordon Ross 		 * Oh... we have to rollback.
221*0897f7fbSGordon Ross 		 */
222*0897f7fbSGordon Ross 		while (i > 0) {
223*0897f7fbSGordon Ross 			--i;
224*0897f7fbSGordon Ross 			lk = &lvec[i];
225*0897f7fbSGordon Ross 			(void) smb_unlock_range(sr,
226*0897f7fbSGordon Ross 			    lk->Offset, lk->Length, pid);
227*0897f7fbSGordon Ross 		}
228*0897f7fbSGordon Ross 	}
229*0897f7fbSGordon Ross 
230*0897f7fbSGordon Ross 	return (status);
231*0897f7fbSGordon Ross }
232*0897f7fbSGordon Ross 
233*0897f7fbSGordon Ross /*
234*0897f7fbSGordon Ross  * Async handler for blocking lock requests.
235*0897f7fbSGordon Ross  * Always exactly one lock request here.
236*0897f7fbSGordon Ross  */
237a90cf9f2SGordon Ross static smb_sdrc_t
238a90cf9f2SGordon Ross smb2_lock_async(smb_request_t *sr)
239a90cf9f2SGordon Ross {
240*0897f7fbSGordon Ross 	lock_elem_t *lk = sr->arg.lock.lvec;
241*0897f7fbSGordon Ross 	uint32_t LockCount = sr->arg.lock.lcnt;
242a90cf9f2SGordon Ross 	uint32_t status;
243*0897f7fbSGordon Ross 	uint32_t ltype;
244*0897f7fbSGordon Ross 	uint32_t pid = 0;	/* SMB2 ignores lock PIDs */
245*0897f7fbSGordon Ross 	uint32_t timeout = UINT_MAX;
246a90cf9f2SGordon Ross 
247*0897f7fbSGordon Ross 	ASSERT(sr->fid_ofile->f_node != NULL);
248*0897f7fbSGordon Ross 	ASSERT(LockCount == 1);
249a90cf9f2SGordon Ross 
250*0897f7fbSGordon Ross 	switch (lk->Flags) {
251*0897f7fbSGordon Ross 	case SMB2_LOCKFLAG_SHARED_LOCK:
252*0897f7fbSGordon Ross 		ltype = SMB_LOCK_TYPE_READONLY;
253*0897f7fbSGordon Ross 		break;
254*0897f7fbSGordon Ross 
255*0897f7fbSGordon Ross 	case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
256*0897f7fbSGordon Ross 		ltype = SMB_LOCK_TYPE_READWRITE;
257*0897f7fbSGordon Ross 		break;
258*0897f7fbSGordon Ross 
259*0897f7fbSGordon Ross 	default:
260*0897f7fbSGordon Ross 		ASSERT(0);
261a90cf9f2SGordon Ross 		status = NT_STATUS_INTERNAL_ERROR;
262a90cf9f2SGordon Ross 		goto errout;
263a90cf9f2SGordon Ross 	}
264a90cf9f2SGordon Ross 
265*0897f7fbSGordon Ross 	status = smb_lock_range(sr, lk->Offset, lk->Length, pid,
266*0897f7fbSGordon Ross 	    ltype, timeout);
267*0897f7fbSGordon Ross 	if (status != 0)
268a90cf9f2SGordon Ross 		goto errout;
269a90cf9f2SGordon Ross 
270a90cf9f2SGordon Ross 	/*
271a90cf9f2SGordon Ross 	 * SMB2 Lock reply (async)
272a90cf9f2SGordon Ross 	 */
273a90cf9f2SGordon Ross 	(void) smb_mbc_encodef(
274a90cf9f2SGordon Ross 	    &sr->reply, "w..",
275*0897f7fbSGordon Ross 	    4); /* StructSize	w */
276a90cf9f2SGordon Ross 	    /* reserved		.. */
277a90cf9f2SGordon Ross 	return (SDRC_SUCCESS);
278a90cf9f2SGordon Ross 
279a90cf9f2SGordon Ross errout:
280a90cf9f2SGordon Ross 	smb2sr_put_error(sr, status);
281a90cf9f2SGordon Ross 	return (SDRC_SUCCESS);
282a90cf9f2SGordon Ross }
283