xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_lock.c (revision 93bc28dbaee6387120d48b12b3dc1ba5f7418e6e)
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 /*
130897f7fbSGordon 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 
220897f7fbSGordon 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;
270897f7fbSGordon Ross } lock_elem_t;
28a90cf9f2SGordon Ross 
290897f7fbSGordon Ross static uint32_t smb2_unlock(smb_request_t *);
300897f7fbSGordon 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 {
420897f7fbSGordon 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;
490897f7fbSGordon Ross 	int rc;
50a90cf9f2SGordon Ross 
51a90cf9f2SGordon Ross 	/*
52*93bc28dbSGordon Ross 	 * Decode 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 
64*93bc28dbSGordon Ross 	/*
65*93bc28dbSGordon Ross 	 * Want FID lookup before the start probe.
66*93bc28dbSGordon Ross 	 */
67a90cf9f2SGordon Ross 	status = smb2sr_lookup_fid(sr, &smb2fid);
68*93bc28dbSGordon Ross 	DTRACE_SMB2_START(op__Lock, smb_request_t *, sr);
69*93bc28dbSGordon Ross 
70a90cf9f2SGordon Ross 	if (status)
71*93bc28dbSGordon Ross 		goto errout; /* Bad FID */
72a90cf9f2SGordon Ross 	if (sr->fid_ofile->f_node == NULL || LockCount == 0) {
73a90cf9f2SGordon Ross 		status = NT_STATUS_INVALID_PARAMETER;
74a90cf9f2SGordon Ross 		goto errout;
75a90cf9f2SGordon Ross 	}
76a90cf9f2SGordon Ross 	if (LockCount > smb2_lock_max_elem) {
77a90cf9f2SGordon Ross 		status = NT_STATUS_INSUFFICIENT_RESOURCES;
78a90cf9f2SGordon Ross 		goto errout;
79a90cf9f2SGordon Ross 	}
80a90cf9f2SGordon Ross 
81a90cf9f2SGordon Ross 	/*
820897f7fbSGordon Ross 	 * Parse the array of SMB2_LOCK_ELEMENT structs.
830897f7fbSGordon Ross 	 * This array is free'd in smb_srm_fini.
84a90cf9f2SGordon Ross 	 */
850897f7fbSGordon Ross 	lvec = smb_srm_zalloc(sr, LockCount * sizeof (*lvec));
86a90cf9f2SGordon Ross 	for (i = 0; i < LockCount; i++) {
870897f7fbSGordon Ross 		lk = &lvec[i];
88a90cf9f2SGordon Ross 		rc = smb_mbc_decodef(
89a90cf9f2SGordon Ross 		    &sr->smb_data, "qqll",
900897f7fbSGordon Ross 		    &lk->Offset,	/* q */
910897f7fbSGordon Ross 		    &lk->Length,	/* q */
920897f7fbSGordon Ross 		    &lk->Flags,		/* l */
930897f7fbSGordon Ross 		    &lk->reserved);	/* l */
94a90cf9f2SGordon Ross 		if (rc) {
95a90cf9f2SGordon Ross 			status = NT_STATUS_INVALID_PARAMETER;
96a90cf9f2SGordon Ross 			goto errout;
97a90cf9f2SGordon Ross 		}
980897f7fbSGordon Ross 	}
99a90cf9f2SGordon Ross 
100a90cf9f2SGordon Ross 	/*
1010897f7fbSGordon Ross 	 * [MS-SMB2] 3.3.5.14
1020897f7fbSGordon Ross 	 * If the flags of the [first element of] the Locks array
1030897f7fbSGordon Ross 	 * [has] SMB2_LOCKFLAG_UNLOCK set, the server MUST process
1040897f7fbSGordon Ross 	 * the lock array as a series of unlocks. Otherwise, it
1050897f7fbSGordon Ross 	 * MUST process the lock array as a series of lock requests.
106a90cf9f2SGordon Ross 	 */
1070897f7fbSGordon Ross 	sr->arg.lock.lvec = lvec;
1080897f7fbSGordon Ross 	sr->arg.lock.lcnt = LockCount;
1090897f7fbSGordon Ross 	sr->arg.lock.lseq = LockSequence;
1100897f7fbSGordon Ross 	if (lvec[0].Flags & SMB2_LOCKFLAG_UNLOCK) {
1110897f7fbSGordon Ross 		status = smb2_unlock(sr);
1120897f7fbSGordon Ross 	} else {
1130897f7fbSGordon Ross 		status = smb2_locks(sr);
114a90cf9f2SGordon Ross 	}
115*93bc28dbSGordon Ross 
116*93bc28dbSGordon Ross errout:
117*93bc28dbSGordon Ross 	sr->smb2_status = status;
118*93bc28dbSGordon Ross 	if (status != NT_STATUS_PENDING) {
119*93bc28dbSGordon Ross 		DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr);
120*93bc28dbSGordon Ross 	}
121*93bc28dbSGordon Ross 
122*93bc28dbSGordon Ross 	if (status) {
123*93bc28dbSGordon Ross 		smb2sr_put_error(sr, status);
124*93bc28dbSGordon Ross 		return (SDRC_SUCCESS);
125*93bc28dbSGordon Ross 	}
126a90cf9f2SGordon Ross 
127a90cf9f2SGordon Ross 	/*
128*93bc28dbSGordon Ross 	 * Encode SMB2 Lock reply (sync)
129a90cf9f2SGordon Ross 	 */
130a90cf9f2SGordon Ross 	(void) smb_mbc_encodef(
131a90cf9f2SGordon Ross 	    &sr->reply, "w..",
1320897f7fbSGordon Ross 	    4); /* StructSize	w */
133a90cf9f2SGordon Ross 	    /* reserved		.. */
134a90cf9f2SGordon Ross 	return (SDRC_SUCCESS);
135a90cf9f2SGordon Ross }
136a90cf9f2SGordon Ross 
1370897f7fbSGordon Ross /*
1380897f7fbSGordon Ross  * Process what should be an array of unlock requests.
1390897f7fbSGordon Ross  */
1400897f7fbSGordon Ross static uint32_t
1410897f7fbSGordon Ross smb2_unlock(smb_request_t *sr)
1420897f7fbSGordon Ross {
1430897f7fbSGordon Ross 	lock_elem_t *lk;
1440897f7fbSGordon Ross 	lock_elem_t *lvec = sr->arg.lock.lvec;
1450897f7fbSGordon Ross 	uint32_t LockCount = sr->arg.lock.lcnt;
1460897f7fbSGordon Ross 	uint32_t LockSequence = sr->arg.lock.lseq;
1470897f7fbSGordon Ross 	uint32_t status = 0;
1480897f7fbSGordon Ross 	uint32_t pid = 0;	/* SMB2 ignores lock PIDs. */
1490897f7fbSGordon Ross 	int i;
1500897f7fbSGordon Ross 
1510897f7fbSGordon Ross 	for (i = 0; i < LockCount; i++) {
1520897f7fbSGordon Ross 		lk = &lvec[i];
1530897f7fbSGordon Ross 
1540897f7fbSGordon Ross 		if (lk->Flags != SMB2_LOCKFLAG_UNLOCK) {
1550897f7fbSGordon Ross 			status = NT_STATUS_INVALID_PARAMETER;
1560897f7fbSGordon Ross 			break;
1570897f7fbSGordon Ross 		}
1580897f7fbSGordon Ross 
1590897f7fbSGordon Ross 		status = smb_unlock_range(sr, lk->Offset, lk->Length, pid);
1600897f7fbSGordon Ross 		if (status != 0)
1610897f7fbSGordon Ross 			break;
1620897f7fbSGordon Ross 	}
1630897f7fbSGordon Ross 	(void) LockSequence; /* todo */
1640897f7fbSGordon Ross 
1650897f7fbSGordon Ross 	return (status);
1660897f7fbSGordon Ross }
1670897f7fbSGordon Ross 
1680897f7fbSGordon Ross /*
1690897f7fbSGordon Ross  * Process what should be an array of lock requests.
1700897f7fbSGordon Ross  */
1710897f7fbSGordon Ross static uint32_t
1720897f7fbSGordon Ross smb2_locks(smb_request_t *sr)
1730897f7fbSGordon Ross {
1740897f7fbSGordon Ross 	lock_elem_t *lk;
1750897f7fbSGordon Ross 	lock_elem_t *lvec = sr->arg.lock.lvec;
1760897f7fbSGordon Ross 	uint32_t LockCount = sr->arg.lock.lcnt;
1770897f7fbSGordon Ross 	uint32_t i;
1780897f7fbSGordon Ross 	uint32_t ltype;
1790897f7fbSGordon Ross 	uint32_t pid = 0;	/* SMB2 ignores lock PIDs */
1800897f7fbSGordon Ross 	uint32_t timeout = 0;
1810897f7fbSGordon Ross 	uint32_t status = 0;
1820897f7fbSGordon Ross 
1830897f7fbSGordon Ross 	for (i = 0; i < LockCount; i++) {
1840897f7fbSGordon Ross 		lk = &lvec[i];
1850897f7fbSGordon Ross 
1860897f7fbSGordon Ross 		switch (lk->Flags) {
1870897f7fbSGordon Ross 
1880897f7fbSGordon Ross 		case SMB2_LOCKFLAG_SHARED_LOCK:
1890897f7fbSGordon Ross 		case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
1900897f7fbSGordon Ross 			/*
1910897f7fbSGordon Ross 			 * Blocking locks have special rules:
1920897f7fbSGordon Ross 			 * Must be exactly one element, else
1930897f7fbSGordon Ross 			 * invalid parameter.
1940897f7fbSGordon Ross 			 */
1950897f7fbSGordon Ross 			if (i == 0 && LockCount == 1) {
1960897f7fbSGordon Ross 				status = smb2sr_go_async(sr, smb2_lock_async);
1970897f7fbSGordon Ross 				return (status);
1980897f7fbSGordon Ross 			}
1990897f7fbSGordon Ross 			/* FALLTHROUGH */
2000897f7fbSGordon Ross 		case SMB2_LOCKFLAG_UNLOCK:
2010897f7fbSGordon Ross 		default:
2020897f7fbSGordon Ross 			status = NT_STATUS_INVALID_PARAMETER;
2030897f7fbSGordon Ross 			goto end_loop;
2040897f7fbSGordon Ross 
2050897f7fbSGordon Ross 		/* BEGIN CSTYLED */
2060897f7fbSGordon Ross 		case SMB2_LOCKFLAG_SHARED_LOCK |
2070897f7fbSGordon Ross 		     SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
2080897f7fbSGordon Ross 		/* END CSTYLED */
2090897f7fbSGordon Ross 			ltype = SMB_LOCK_TYPE_READONLY;
2100897f7fbSGordon Ross 			break;
2110897f7fbSGordon Ross 
2120897f7fbSGordon Ross 		/* BEGIN CSTYLED */
2130897f7fbSGordon Ross 		case SMB2_LOCKFLAG_EXCLUSIVE_LOCK |
2140897f7fbSGordon Ross 		     SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
2150897f7fbSGordon Ross 		/* END CSTYLED */
2160897f7fbSGordon Ross 			ltype = SMB_LOCK_TYPE_READWRITE;
2170897f7fbSGordon Ross 			break;
2180897f7fbSGordon Ross 		}
2190897f7fbSGordon Ross 
2200897f7fbSGordon Ross 		status = smb_lock_range(sr, lk->Offset, lk->Length, pid,
2210897f7fbSGordon Ross 		    ltype, timeout);
2220897f7fbSGordon Ross 		if (status != 0) {
2230897f7fbSGordon Ross 			goto end_loop;
2240897f7fbSGordon Ross 		}
2250897f7fbSGordon Ross 	}
2260897f7fbSGordon Ross 
2270897f7fbSGordon Ross end_loop:
2280897f7fbSGordon Ross 	if (status != 0) {
2290897f7fbSGordon Ross 		/*
2300897f7fbSGordon Ross 		 * Oh... we have to rollback.
2310897f7fbSGordon Ross 		 */
2320897f7fbSGordon Ross 		while (i > 0) {
2330897f7fbSGordon Ross 			--i;
2340897f7fbSGordon Ross 			lk = &lvec[i];
2350897f7fbSGordon Ross 			(void) smb_unlock_range(sr,
2360897f7fbSGordon Ross 			    lk->Offset, lk->Length, pid);
2370897f7fbSGordon Ross 		}
2380897f7fbSGordon Ross 	}
2390897f7fbSGordon Ross 
2400897f7fbSGordon Ross 	return (status);
2410897f7fbSGordon Ross }
2420897f7fbSGordon Ross 
2430897f7fbSGordon Ross /*
2440897f7fbSGordon Ross  * Async handler for blocking lock requests.
2450897f7fbSGordon Ross  * Always exactly one lock request here.
2460897f7fbSGordon Ross  */
247a90cf9f2SGordon Ross static smb_sdrc_t
248a90cf9f2SGordon Ross smb2_lock_async(smb_request_t *sr)
249a90cf9f2SGordon Ross {
2500897f7fbSGordon Ross 	lock_elem_t *lk = sr->arg.lock.lvec;
2510897f7fbSGordon Ross 	uint32_t LockCount = sr->arg.lock.lcnt;
252a90cf9f2SGordon Ross 	uint32_t status;
2530897f7fbSGordon Ross 	uint32_t ltype;
2540897f7fbSGordon Ross 	uint32_t pid = 0;	/* SMB2 ignores lock PIDs */
2550897f7fbSGordon Ross 	uint32_t timeout = UINT_MAX;
256a90cf9f2SGordon Ross 
2570897f7fbSGordon Ross 	ASSERT(sr->fid_ofile->f_node != NULL);
2580897f7fbSGordon Ross 	ASSERT(LockCount == 1);
259a90cf9f2SGordon Ross 
2600897f7fbSGordon Ross 	switch (lk->Flags) {
2610897f7fbSGordon Ross 	case SMB2_LOCKFLAG_SHARED_LOCK:
2620897f7fbSGordon Ross 		ltype = SMB_LOCK_TYPE_READONLY;
2630897f7fbSGordon Ross 		break;
2640897f7fbSGordon Ross 
2650897f7fbSGordon Ross 	case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
2660897f7fbSGordon Ross 		ltype = SMB_LOCK_TYPE_READWRITE;
2670897f7fbSGordon Ross 		break;
2680897f7fbSGordon Ross 
2690897f7fbSGordon Ross 	default:
2700897f7fbSGordon Ross 		ASSERT(0);
271a90cf9f2SGordon Ross 		status = NT_STATUS_INTERNAL_ERROR;
272a90cf9f2SGordon Ross 		goto errout;
273a90cf9f2SGordon Ross 	}
274a90cf9f2SGordon Ross 
2750897f7fbSGordon Ross 	status = smb_lock_range(sr, lk->Offset, lk->Length, pid,
2760897f7fbSGordon Ross 	    ltype, timeout);
277*93bc28dbSGordon Ross 
278*93bc28dbSGordon Ross errout:
279*93bc28dbSGordon Ross 	sr->smb2_status = status;
280*93bc28dbSGordon Ross 	DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr);
281*93bc28dbSGordon Ross 
282*93bc28dbSGordon Ross 	if (status != 0) {
283*93bc28dbSGordon Ross 		smb2sr_put_error(sr, status);
284*93bc28dbSGordon Ross 		return (SDRC_SUCCESS);
285*93bc28dbSGordon Ross 	}
286a90cf9f2SGordon Ross 
287a90cf9f2SGordon Ross 	/*
288a90cf9f2SGordon Ross 	 * SMB2 Lock reply (async)
289a90cf9f2SGordon Ross 	 */
290a90cf9f2SGordon Ross 	(void) smb_mbc_encodef(
291a90cf9f2SGordon Ross 	    &sr->reply, "w..",
2920897f7fbSGordon Ross 	    4); /* StructSize	w */
293a90cf9f2SGordon Ross 	    /* reserved		.. */
294a90cf9f2SGordon Ross 	return (SDRC_SUCCESS);
295a90cf9f2SGordon Ross }
296