xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_oplock.c (revision a92282e44f968185a6bba094d1e5fece2da819cf)
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 2019 Nexenta by DDN, Inc.  All rights reserved.
14  * Copyright 2019 RackTop Systems.
15  */
16 
17 /*
18  * Dispatch function for SMB2_OPLOCK_BREAK
19  */
20 
21 #include <smbsrv/smb2_kproto.h>
22 
23 #define	BATCH_OR_EXCL	(OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE)
24 
25 /* StructSize for the two "break" message formats. */
26 #define	SSZ_OPLOCK	24
27 #define	SSZ_LEASE	36
28 
29 /*
30  * SMB2 Oplock Break Acknowledgement
31  * [MS-SMB2] 3.3.5.22.1 Processing an Oplock Acknowledgment
32  * Called via smb2_disp_table[]
33  */
34 smb_sdrc_t
35 smb2_oplock_break_ack(smb_request_t *sr)
36 {
37 	smb_ofile_t *ofile;
38 	smb2fid_t smb2fid;
39 	uint32_t status;
40 	uint32_t NewLevel;
41 	uint8_t smbOplockLevel;
42 	int rc = 0;
43 	uint16_t StructSize;
44 
45 	/*
46 	 * Decode the SMB2 Oplock Break Ack (24 bytes) or
47 	 * Lease Break Ack (36 bytes), starting with just
48 	 * the StructSize, which tells us what this is.
49 	 */
50 	rc = smb_mbc_decodef(&sr->smb_data, "w", &StructSize);
51 	if (rc != 0)
52 		return (SDRC_ERROR);
53 
54 	if (StructSize == SSZ_LEASE) {
55 		/* See smb2_lease.c */
56 		return (smb2_lease_break_ack(sr));
57 	}
58 	if (StructSize != SSZ_OPLOCK)
59 		return (SDRC_ERROR);
60 
61 	/*
62 	 * Decode an SMB2 Oplock Break Ack.
63 	 * [MS-SMB2] 2.2.24.1
64 	 * Note: Struct size decoded above.
65 	 */
66 	rc = smb_mbc_decodef(
67 	    &sr->smb_data, "b5.qq",
68 	    &smbOplockLevel,		/* b */
69 	    /* reserved			  5. */
70 	    &smb2fid.persistent,	/* q */
71 	    &smb2fid.temporal);		/* q */
72 	if (rc != 0)
73 		return (SDRC_ERROR);
74 
75 	/* Find the ofile */
76 	status = smb2sr_lookup_fid(sr, &smb2fid);
77 	/* Success or NT_STATUS_FILE_CLOSED */
78 
79 	DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr);
80 	if (status != 0)
81 		goto errout;
82 
83 	/*
84 	 * Process an (old-style) oplock break ack.
85 	 */
86 	switch (smbOplockLevel) {
87 	case SMB2_OPLOCK_LEVEL_NONE:	/* 0x00 */
88 		NewLevel = OPLOCK_LEVEL_NONE;
89 		break;
90 	case SMB2_OPLOCK_LEVEL_II:	/* 0x01 */
91 		NewLevel = OPLOCK_LEVEL_TWO;
92 		break;
93 	case SMB2_OPLOCK_LEVEL_EXCLUSIVE: /* 0x08 */
94 		NewLevel = OPLOCK_LEVEL_ONE;
95 		break;
96 	case SMB2_OPLOCK_LEVEL_BATCH:	/* 0x09 */
97 		NewLevel = OPLOCK_LEVEL_BATCH;
98 		break;
99 	case SMB2_OPLOCK_LEVEL_LEASE:	/* 0xFF */
100 		NewLevel = OPLOCK_LEVEL_NONE;
101 		break;
102 	default:
103 		status = NT_STATUS_INVALID_PARAMETER;
104 		goto errout;
105 	}
106 
107 	ofile = sr->fid_ofile;
108 	if (ofile->f_oplock.og_breaking == 0) {
109 		/*
110 		 * This is an unsolicited Ack. (There is no
111 		 * outstanding oplock break in progress now.)
112 		 * There are WPTS tests that care which error
113 		 * is returned.  See [MS-SMB2] 3.3.5.22.1
114 		 */
115 		if (smbOplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
116 			status = NT_STATUS_INVALID_PARAMETER;
117 			goto errout;
118 		}
119 		if (NewLevel >= (ofile->f_oplock.og_state &
120 		    OPLOCK_LEVEL_TYPE_MASK)) {
121 			status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
122 			goto errout;
123 		}
124 		status = NT_STATUS_INVALID_DEVICE_STATE;
125 		goto errout;
126 	}
127 	ofile->f_oplock.og_breaking = 0;
128 
129 	status = smb_oplock_ack_break(sr, ofile, &NewLevel);
130 	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
131 		status = smb2sr_go_async(sr);
132 		if (status != 0)
133 			goto errout;
134 		(void) smb_oplock_wait_break(ofile->f_node, 0);
135 		status = 0;
136 	}
137 	if (status != 0) {
138 		NewLevel = OPLOCK_LEVEL_NONE;
139 		goto errout;
140 	}
141 
142 	ofile->f_oplock.og_state = NewLevel;
143 	switch (NewLevel & OPLOCK_LEVEL_TYPE_MASK) {
144 	case OPLOCK_LEVEL_NONE:
145 		smbOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
146 		break;
147 	case OPLOCK_LEVEL_TWO:
148 		smbOplockLevel = SMB2_OPLOCK_LEVEL_II;
149 		break;
150 	case OPLOCK_LEVEL_ONE:
151 		smbOplockLevel = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
152 		break;
153 	case OPLOCK_LEVEL_BATCH:
154 		smbOplockLevel = SMB2_OPLOCK_LEVEL_BATCH;
155 		break;
156 	case OPLOCK_LEVEL_GRANULAR:
157 	default:
158 		smbOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
159 		break;
160 	}
161 
162 errout:
163 	sr->smb2_status = status;
164 	DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr);
165 	if (status) {
166 		smb2sr_put_error(sr, status);
167 		return (SDRC_SUCCESS);
168 	}
169 
170 	/*
171 	 * Encode an SMB2 Oplock Break Ack response
172 	 * [MS-SMB2] 2.2.25.1
173 	 */
174 	(void) smb_mbc_encodef(
175 	    &sr->reply, "wb5.qq",
176 	    SSZ_OPLOCK,			/* w */
177 	    smbOplockLevel,		/* b */
178 	    /* reserved			  5. */
179 	    smb2fid.persistent,		/* q */
180 	    smb2fid.temporal);		/* q */
181 
182 	return (SDRC_SUCCESS);
183 }
184 
185 /*
186  * Compose an SMB2 Oplock Break Notification packet, including
187  * the SMB2 header and everything, in sr->reply.
188  * The caller will send it and free the request.
189  */
190 void
191 smb2_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel)
192 {
193 	smb_ofile_t *ofile = sr->fid_ofile;
194 	smb2fid_t smb2fid;
195 	uint16_t StructSize;
196 	uint8_t OplockLevel;
197 
198 	/*
199 	 * Convert internal level to SMB2
200 	 */
201 	switch (NewLevel) {
202 	default:
203 		ASSERT(0);
204 		/* FALLTHROUGH */
205 	case OPLOCK_LEVEL_NONE:
206 		OplockLevel = SMB2_OPLOCK_LEVEL_NONE;
207 		break;
208 	case OPLOCK_LEVEL_TWO:
209 		OplockLevel = SMB2_OPLOCK_LEVEL_II;
210 		break;
211 	}
212 
213 	/*
214 	 * SMB2 Header
215 	 */
216 	sr->smb2_cmd_code = SMB2_OPLOCK_BREAK;
217 	sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR;
218 	sr->smb_tid = 0;
219 	sr->smb_pid = 0;
220 	sr->smb2_ssnid = 0;
221 	sr->smb2_messageid = UINT64_MAX;
222 	(void) smb2_encode_header(sr, B_FALSE);
223 
224 	/*
225 	 * SMB2 Oplock Break, variable part
226 	 */
227 	StructSize = 24;
228 	smb2fid.persistent = ofile->f_persistid;
229 	smb2fid.temporal = ofile->f_fid;
230 	(void) smb_mbc_encodef(
231 	    &sr->reply, "wb5.qq",
232 	    StructSize,		/* w */
233 	    OplockLevel,	/* b */
234 	    /* reserved		  5. */
235 	    smb2fid.persistent,	/* q */
236 	    smb2fid.temporal);	/* q */
237 }
238 
239 /*
240  * Client has an open handle and requests an oplock.
241  * Convert SMB2 oplock request info in to internal form,
242  * call common oplock code, convert result to SMB2.
243  *
244  * If necessary, "go async" here.
245  */
246 void
247 smb2_oplock_acquire(smb_request_t *sr)
248 {
249 	smb_arg_open_t *op = &sr->arg.open;
250 	smb_ofile_t *ofile = sr->fid_ofile;
251 	uint32_t status;
252 
253 	/* Only disk trees get oplocks. */
254 	ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE);
255 
256 	/* Only plain files... */
257 	if (!smb_node_is_file(ofile->f_node)) {
258 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
259 		return;
260 	}
261 
262 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
263 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
264 		return;
265 	}
266 
267 	/*
268 	 * SMB2: Convert to internal form.
269 	 */
270 	switch (op->op_oplock_level) {
271 	case SMB2_OPLOCK_LEVEL_BATCH:
272 		op->op_oplock_state = OPLOCK_LEVEL_BATCH;
273 		break;
274 	case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
275 		op->op_oplock_state = OPLOCK_LEVEL_ONE;
276 		break;
277 	case SMB2_OPLOCK_LEVEL_II:
278 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
279 		break;
280 	case SMB2_OPLOCK_LEVEL_LEASE:
281 		ASSERT(0); /* Handled elsewhere */
282 		/* FALLTHROUGH */
283 	case SMB2_OPLOCK_LEVEL_NONE:
284 	default:
285 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
286 		return;
287 	}
288 
289 	/*
290 	 * Tree options may force shared oplocks,
291 	 * in which case we reduce the request.
292 	 */
293 	if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
294 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
295 	}
296 
297 	/*
298 	 * Try exclusive first, if requested
299 	 */
300 	if ((op->op_oplock_state & BATCH_OR_EXCL) != 0) {
301 		status = smb_oplock_request(sr, ofile,
302 		    &op->op_oplock_state);
303 	} else {
304 		status = NT_STATUS_OPLOCK_NOT_GRANTED;
305 	}
306 
307 	/*
308 	 * If exclusive failed (or the tree forced shared oplocks)
309 	 * try for a shared oplock (Level II)
310 	 */
311 	if (status == NT_STATUS_OPLOCK_NOT_GRANTED) {
312 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
313 		status = smb_oplock_request(sr, ofile,
314 		    &op->op_oplock_state);
315 	}
316 
317 	/*
318 	 * Either of the above may have returned the
319 	 * status code that says we should wait.
320 	 */
321 	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
322 		(void) smb2sr_go_async(sr);
323 		(void) smb_oplock_wait_break(ofile->f_node, 0);
324 		status = 0;
325 	}
326 
327 	/*
328 	 * Keep track of what we got (in ofile->f_oplock.og_state)
329 	 * so we'll know what we had when sending a break later.
330 	 * The og_dialect here is the oplock dialect, not the
331 	 * SMB dialect.  No lease here, so SMB 2.0.
332 	 */
333 	ofile->f_oplock.og_dialect = SMB_VERS_2_002;
334 	switch (status) {
335 	case NT_STATUS_SUCCESS:
336 		ofile->f_oplock.og_state = op->op_oplock_state;
337 		break;
338 	case NT_STATUS_OPLOCK_NOT_GRANTED:
339 		ofile->f_oplock.og_state = 0;
340 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
341 		return;
342 	default:
343 		/* Caller did not check args sufficiently? */
344 		cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x",
345 		    sr->session->ip_addr_str, status);
346 		ofile->f_oplock.og_state = 0;
347 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
348 		return;
349 	}
350 
351 	/*
352 	 * Have STATUS_SUCCESS
353 	 * Convert internal oplock state to SMB2
354 	 */
355 	if (op->op_oplock_state & OPLOCK_LEVEL_GRANULAR) {
356 		ASSERT(0);
357 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
358 	} else if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
359 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
360 	} else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
361 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
362 	} else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
363 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
364 	} else {
365 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
366 	}
367 }
368 
369 /*
370  * smb2_oplock_reconnect()  Helper for smb2_dh_reconnect
371  * Get oplock state into op->op_oplock_level etc.
372  *
373  * Similar to the end of smb2_lease_acquire (for leases) or
374  * the end of smb2_oplock_acquire (for old-style oplocks).
375  */
376 void
377 smb2_oplock_reconnect(smb_request_t *sr)
378 {
379 	smb_arg_open_t *op = &sr->arg.open;
380 	smb_ofile_t *ofile = sr->fid_ofile;
381 
382 	op->op_oplock_state = ofile->f_oplock.og_state;
383 	if (ofile->f_lease != NULL) {
384 		smb_lease_t *ls = ofile->f_lease;
385 
386 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
387 		op->lease_state = ls->ls_state &
388 		    OPLOCK_LEVEL_CACHE_MASK;
389 		op->lease_flags = (ls->ls_breaking != 0) ?
390 		    SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0;
391 		op->lease_epoch = ls->ls_epoch;
392 		op->lease_version = ls->ls_version;
393 	} else {
394 		switch (op->op_oplock_state & OPLOCK_LEVEL_TYPE_MASK) {
395 		default:
396 		case OPLOCK_LEVEL_NONE:
397 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
398 			break;
399 		case OPLOCK_LEVEL_TWO:
400 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
401 			break;
402 		case OPLOCK_LEVEL_ONE:
403 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
404 			break;
405 		case OPLOCK_LEVEL_BATCH:
406 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
407 			break;
408 		}
409 	}
410 }
411