xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_oplock.c (revision d48be21240dfd051b689384ce2b23479d757f2d8)
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 2020 Tintri by DDN, Inc.  All rights reserved.
14  * Copyright 2022 RackTop Systems, Inc.
15  */
16 
17 /*
18  * Dispatch function for SMB2_OPLOCK_BREAK
19  */
20 
21 #include <smbsrv/smb2_kproto.h>
22 #include <smbsrv/smb_oplock.h>
23 
24 #define	BATCH_OR_EXCL	(OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE)
25 
26 /* StructSize for the two "break" message formats. */
27 #define	SSZ_OPLOCK	24
28 #define	SSZ_LEASE	36
29 
30 /*
31  * SMB2 Oplock Break Acknowledgement
32  * [MS-SMB2] 3.3.5.22.1 Processing an Oplock Acknowledgment
33  * Called via smb2_disp_table[]
34  * This is an "Ack" from the client.
35  */
36 smb_sdrc_t
37 smb2_oplock_break_ack(smb_request_t *sr)
38 {
39 	smb_arg_olbrk_t	*olbrk = &sr->arg.olbrk;
40 	smb_node_t  *node;
41 	smb_ofile_t *ofile;
42 	smb_oplock_grant_t *og;
43 	smb2fid_t smb2fid;
44 	uint32_t status;
45 	uint32_t NewLevel;
46 	uint8_t smbOplockLevel;
47 	int rc = 0;
48 	uint16_t StructSize;
49 
50 	/*
51 	 * Decode the SMB2 Oplock Break Ack (24 bytes) or
52 	 * Lease Break Ack (36 bytes), starting with just
53 	 * the StructSize, which tells us what this is.
54 	 */
55 	rc = smb_mbc_decodef(&sr->smb_data, "w", &StructSize);
56 	if (rc != 0)
57 		return (SDRC_ERROR);
58 
59 	if (StructSize == SSZ_LEASE) {
60 		/* See smb2_lease.c */
61 		return (smb2_lease_break_ack(sr));
62 	}
63 	if (StructSize != SSZ_OPLOCK)
64 		return (SDRC_ERROR);
65 
66 	/*
67 	 * Decode an SMB2 Oplock Break Ack.
68 	 * [MS-SMB2] 2.2.24.1
69 	 * Note: Struct size decoded above.
70 	 */
71 	rc = smb_mbc_decodef(
72 	    &sr->smb_data, "b5.qq",
73 	    &smbOplockLevel,		/* b */
74 	    /* reserved			  5. */
75 	    &smb2fid.persistent,	/* q */
76 	    &smb2fid.temporal);		/* q */
77 	if (rc != 0)
78 		return (SDRC_ERROR);
79 
80 	/*
81 	 * Convert SMB oplock level to internal form.
82 	 */
83 	switch (smbOplockLevel) {
84 	case SMB2_OPLOCK_LEVEL_NONE:	/* 0x00 */
85 		NewLevel = OPLOCK_LEVEL_NONE;
86 		break;
87 	case SMB2_OPLOCK_LEVEL_II:	/* 0x01 */
88 		NewLevel = OPLOCK_LEVEL_TWO;
89 		break;
90 	case SMB2_OPLOCK_LEVEL_EXCLUSIVE: /* 0x08 */
91 		NewLevel = OPLOCK_LEVEL_ONE;
92 		break;
93 	case SMB2_OPLOCK_LEVEL_BATCH:	/* 0x09 */
94 		NewLevel = OPLOCK_LEVEL_BATCH;
95 		break;
96 
97 	/* Note: _LEVEL_LEASE is not valid here. */
98 	case SMB2_OPLOCK_LEVEL_LEASE:	/* 0xFF */
99 	default:
100 		/*
101 		 * Impossible NewLevel here, will cause
102 		 * NT_STATUS_INVALID_PARAMETER below.
103 		 */
104 		NewLevel = OPLOCK_LEVEL_GRANULAR;
105 		break;
106 	}
107 
108 	/* for dtrace */
109 	olbrk->NewLevel = NewLevel;
110 
111 	/* Find the ofile */
112 	status = smb2sr_lookup_fid(sr, &smb2fid);
113 	/* Success or NT_STATUS_FILE_CLOSED */
114 
115 	DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr);
116 
117 	if (status != 0) {
118 		/* lookup fid failed */
119 		goto errout;
120 	}
121 
122 	if (NewLevel == OPLOCK_LEVEL_GRANULAR) {
123 		/* Switch above got invalid smbOplockLevel */
124 		status = NT_STATUS_INVALID_PARAMETER;
125 		goto errout;
126 	}
127 
128 	/* Success, so have sr->fid_ofile */
129 	ofile = sr->fid_ofile;
130 	og = &ofile->f_oplock;
131 	node = ofile->f_node;
132 
133 	smb_llist_enter(&node->n_ofile_list, RW_READER);
134 	mutex_enter(&node->n_oplock.ol_mutex);
135 
136 	if (og->og_breaking == B_FALSE) {
137 		/*
138 		 * This is an unsolicited Ack. (There is no
139 		 * outstanding oplock break in progress now.)
140 		 * There are WPTS tests that care which error
141 		 * is returned.  See [MS-SMB2] 3.3.5.22.1
142 		 */
143 		if (NewLevel >= (og->og_state & OPLOCK_LEVEL_TYPE_MASK)) {
144 			status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
145 			goto unlock_out;
146 		}
147 		status = NT_STATUS_INVALID_DEVICE_STATE;
148 		goto unlock_out;
149 	}
150 
151 	/*
152 	 * Process the oplock break ack.
153 	 *
154 	 * Clear breaking flags before we ack,
155 	 * because ack might set those.
156 	 */
157 	ofile->f_oplock.og_breaking = B_FALSE;
158 	cv_broadcast(&ofile->f_oplock.og_ack_cv);
159 
160 	status = smb_oplock_ack_break(sr, ofile, &NewLevel);
161 
162 	ofile->f_oplock.og_state = NewLevel;
163 	if (ofile->dh_persist)
164 		smb2_dh_update_oplock(sr, ofile);
165 
166 unlock_out:
167 	mutex_exit(&node->n_oplock.ol_mutex);
168 	smb_llist_exit(&node->n_ofile_list);
169 
170 errout:
171 	sr->smb2_status = status;
172 	DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr);
173 	if (status) {
174 		smb2sr_put_error(sr, status);
175 		return (SDRC_SUCCESS);
176 	}
177 
178 	/*
179 	 * Convert internal oplock state back to SMB form.
180 	 */
181 	switch (NewLevel & OPLOCK_LEVEL_TYPE_MASK) {
182 	case OPLOCK_LEVEL_NONE:
183 		smbOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
184 		break;
185 	case OPLOCK_LEVEL_TWO:
186 		smbOplockLevel = SMB2_OPLOCK_LEVEL_II;
187 		break;
188 	case OPLOCK_LEVEL_ONE:
189 		smbOplockLevel = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
190 		break;
191 	case OPLOCK_LEVEL_BATCH:
192 		smbOplockLevel = SMB2_OPLOCK_LEVEL_BATCH;
193 		break;
194 	case OPLOCK_LEVEL_GRANULAR:
195 	default:
196 		smbOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
197 		break;
198 	}
199 
200 	/*
201 	 * Encode an SMB2 Oplock Break Ack response
202 	 * [MS-SMB2] 2.2.25.1
203 	 */
204 	(void) smb_mbc_encodef(
205 	    &sr->reply, "wb5.qq",
206 	    SSZ_OPLOCK,			/* w */
207 	    smbOplockLevel,		/* b */
208 	    /* reserved			  5. */
209 	    smb2fid.persistent,		/* q */
210 	    smb2fid.temporal);		/* q */
211 
212 	return (SDRC_SUCCESS);
213 }
214 
215 /*
216  * Compose an SMB2 Oplock Break Notification packet, including
217  * the SMB2 header and everything, in sr->reply.
218  * The caller will send it and free the request.
219  */
220 static void
221 smb2_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel)
222 {
223 	smb_ofile_t *ofile = sr->fid_ofile;
224 	smb2fid_t smb2fid;
225 	uint16_t StructSize;
226 	uint8_t OplockLevel;
227 
228 	/*
229 	 * Convert internal level to SMB2
230 	 */
231 	switch (NewLevel) {
232 	default:
233 		ASSERT(0);
234 		/* FALLTHROUGH */
235 	case OPLOCK_LEVEL_NONE:
236 		OplockLevel = SMB2_OPLOCK_LEVEL_NONE;
237 		break;
238 	case OPLOCK_LEVEL_TWO:
239 		OplockLevel = SMB2_OPLOCK_LEVEL_II;
240 		break;
241 	}
242 
243 	/*
244 	 * SMB2 Header
245 	 */
246 	sr->smb2_cmd_code = SMB2_OPLOCK_BREAK;
247 	sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR;
248 	sr->smb_tid = 0;
249 	sr->smb_pid = 0;
250 	sr->smb2_ssnid = 0;
251 	sr->smb2_messageid = UINT64_MAX;
252 	(void) smb2_encode_header(sr, B_FALSE);
253 
254 	/*
255 	 * SMB2 Oplock Break, variable part
256 	 */
257 	StructSize = 24;
258 	smb2fid.persistent = ofile->f_persistid;
259 	smb2fid.temporal = ofile->f_fid;
260 	(void) smb_mbc_encodef(
261 	    &sr->reply, "wb5.qq",
262 	    StructSize,		/* w */
263 	    OplockLevel,	/* b */
264 	    /* reserved		  5. */
265 	    smb2fid.persistent,	/* q */
266 	    smb2fid.temporal);	/* q */
267 }
268 
269 /*
270  * Send an oplock break over the wire, or if we can't,
271  * then process the oplock break locally.
272  *
273  * [MS-SMB2] 3.3.4.6 Object Store Indicates an Oplock Break
274  *
275  * Note: When "AckRequired" is set, and we're for any reason
276  * unable to communicate with the client so that they do an
277  * "oplock break ACK", then we absolutely MUST do a local ACK
278  * for this break indication (or close the ofile).
279  *
280  * The file-system level oplock code (smb_cmn_oplock.c)
281  * requires these ACK calls to clear "breaking" flags.
282  *
283  * This is called either from smb_oplock_async_break via a
284  * taskq job scheduled in smb_oplock_ind_break, or from the
285  * smb2sr_append_postwork() mechanism when we're doing a
286  * "break in ack", via smb_oplock_ind_break_in_ack.
287  *
288  * This runs much like other smb_request_t handlers, in the
289  * context of a worker task that calls with no locks held.
290  *
291  * Note that we have sr->fid_ofile here but all the other
292  * normal sr members may be NULL:  uid_user, tid_tree.
293  * Also sr->session may or may not be the same session as
294  * the ofile came from (ofile->f_session) depending on
295  * whether this is a "live" open or an orphaned DH,
296  * where ofile->f_session will be NULL.
297  */
298 void
299 smb2_oplock_send_break(smb_request_t *sr)
300 {
301 	smb_ofile_t	*ofile = sr->fid_ofile;
302 	smb_node_t	*node = ofile->f_node;
303 	uint32_t	NewLevel = sr->arg.olbrk.NewLevel;
304 	boolean_t	AckReq = sr->arg.olbrk.AckRequired;
305 	uint32_t	status;
306 	int		rc;
307 
308 	/*
309 	 * Build the break message in sr->reply.
310 	 * It's free'd in smb_request_free().
311 	 * Always SMB2 oplock here (no lease)
312 	 */
313 	sr->reply.max_bytes = MLEN;
314 	smb2_oplock_break_notification(sr, NewLevel);
315 
316 	/*
317 	 * Try to send the break message to the client.
318 	 * If connected, this IF body will be true.
319 	 */
320 	if (sr->session == ofile->f_session)
321 		rc = smb_session_send(sr->session, 0, &sr->reply);
322 	else
323 		rc = ENOTCONN;
324 
325 	if (rc != 0) {
326 		/*
327 		 * We were unable to send the oplock break request,
328 		 * presumably because the connection is gone.
329 		 *
330 		 * [MS-SMB2] 3.3.4.6 Object Store Indicates an Oplock Break
331 		 * If no connection is available, Open.IsResilient is FALSE,
332 		 * Open.IsDurable is FALSE, and Open.IsPersistent is FALSE,
333 		 * the server SHOULD close the Open as specified in...
334 		 */
335 		if (ofile->dh_persist == B_FALSE &&
336 		    ofile->dh_vers != SMB2_RESILIENT &&
337 		    (ofile->dh_vers == SMB2_NOT_DURABLE ||
338 		    (NewLevel & OPLOCK_LEVEL_BATCH) == 0)) {
339 			smb_ofile_close(ofile, 0);
340 			return;
341 		}
342 		/* Keep this (durable) open. */
343 		if (!AckReq)
344 			return;
345 		/* Do local Ack below. */
346 	} else {
347 		/*
348 		 * OK, we were able to send the break message.
349 		 * If no ack. required, we're done.
350 		 */
351 		if (!AckReq)
352 			return;
353 
354 		/*
355 		 * We're expecting an ACK.  Wait in this thread
356 		 * so we can log clients that don't respond.
357 		 */
358 		status = smb_oplock_wait_ack(sr, NewLevel);
359 		if (status == 0)
360 			return;
361 
362 		cmn_err(CE_NOTE, "clnt %s oplock break timeout",
363 		    sr->session->ip_addr_str);
364 		DTRACE_PROBE1(ack_timeout, smb_request_t *, sr);
365 
366 		/*
367 		 * Will do local ack below.  Note, after timeout,
368 		 * do a break to none or "no caching" regardless
369 		 * of what the passed in cache level was.
370 		 */
371 		NewLevel = OPLOCK_LEVEL_NONE;
372 	}
373 
374 	/*
375 	 * Do the ack locally.
376 	 */
377 	smb_llist_enter(&node->n_ofile_list, RW_READER);
378 	mutex_enter(&node->n_oplock.ol_mutex);
379 
380 	ofile->f_oplock.og_breaking = B_FALSE;
381 	cv_broadcast(&ofile->f_oplock.og_ack_cv);
382 
383 	status = smb_oplock_ack_break(sr, ofile, &NewLevel);
384 
385 	ofile->f_oplock.og_state = NewLevel;
386 	if (ofile->dh_persist)
387 		smb2_dh_update_oplock(sr, ofile);
388 
389 	mutex_exit(&node->n_oplock.ol_mutex);
390 	smb_llist_exit(&node->n_ofile_list);
391 
392 #ifdef	DEBUG
393 	if (status != 0) {
394 		cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x",
395 		    sr->session->ip_addr_str, status);
396 	}
397 #endif
398 }
399 
400 /*
401  * Client has an open handle and requests an oplock.
402  * Convert SMB2 oplock request info in to internal form,
403  * call common oplock code, convert result to SMB2.
404  *
405  * If necessary, "go async" here.
406  */
407 void
408 smb2_oplock_acquire(smb_request_t *sr)
409 {
410 	smb_arg_open_t *op = &sr->arg.open;
411 	smb_ofile_t *ofile = sr->fid_ofile;
412 	uint32_t status;
413 
414 	/* Only disk trees get oplocks. */
415 	ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE);
416 
417 	/* Only plain files... */
418 	if (!smb_node_is_file(ofile->f_node)) {
419 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
420 		return;
421 	}
422 
423 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
424 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
425 		return;
426 	}
427 
428 	/*
429 	 * SMB2: Convert to internal form.
430 	 */
431 	switch (op->op_oplock_level) {
432 	case SMB2_OPLOCK_LEVEL_BATCH:
433 		op->op_oplock_state = OPLOCK_LEVEL_BATCH;
434 		break;
435 	case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
436 		op->op_oplock_state = OPLOCK_LEVEL_ONE;
437 		break;
438 	case SMB2_OPLOCK_LEVEL_II:
439 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
440 		break;
441 	case SMB2_OPLOCK_LEVEL_LEASE:
442 		ASSERT(0); /* Handled elsewhere */
443 		/* FALLTHROUGH */
444 	case SMB2_OPLOCK_LEVEL_NONE:
445 	default:
446 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
447 		return;
448 	}
449 
450 	/*
451 	 * Tree options may force shared oplocks,
452 	 * in which case we reduce the request.
453 	 * Can't get here with LEVEL_NONE, so
454 	 * this can only decrease the level.
455 	 */
456 	if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
457 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
458 	}
459 
460 	/*
461 	 * Try exclusive first, if requested
462 	 */
463 	if ((op->op_oplock_state & BATCH_OR_EXCL) != 0) {
464 		status = smb_oplock_request(sr, ofile,
465 		    &op->op_oplock_state);
466 	} else {
467 		status = NT_STATUS_OPLOCK_NOT_GRANTED;
468 	}
469 
470 	/*
471 	 * If exclusive failed (or the tree forced shared oplocks)
472 	 * try for a shared oplock (Level II)
473 	 */
474 	if (status == NT_STATUS_OPLOCK_NOT_GRANTED) {
475 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
476 		status = smb_oplock_request(sr, ofile,
477 		    &op->op_oplock_state);
478 	}
479 
480 	/*
481 	 * Keep track of what we got (ofile->f_oplock.og_state etc)
482 	 * so we'll know what we had when sending a break later.
483 	 * The og_dialect here is the oplock dialect, not the
484 	 * SMB dialect.  No lease here, so SMB 2.0.
485 	 */
486 	switch (status) {
487 	case NT_STATUS_SUCCESS:
488 	case NT_STATUS_OPLOCK_BREAK_IN_PROGRESS:
489 		ofile->f_oplock.og_dialect = SMB_VERS_2_002;
490 		ofile->f_oplock.og_state   = op->op_oplock_state;
491 		ofile->f_oplock.og_breakto = op->op_oplock_state;
492 		ofile->f_oplock.og_breaking = B_FALSE;
493 		if (ofile->dh_persist) {
494 			smb2_dh_update_oplock(sr, ofile);
495 		}
496 		break;
497 
498 	case NT_STATUS_OPLOCK_NOT_GRANTED:
499 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
500 		return;
501 
502 	default:
503 		/* Caller did not check args sufficiently? */
504 		cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x",
505 		    sr->session->ip_addr_str, status);
506 		DTRACE_PROBE2(other__error, smb_request_t *, sr,
507 		    uint32_t, status);
508 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
509 		return;
510 	}
511 
512 	/*
513 	 * Only success cases get here
514 	 * Convert internal oplock state to SMB2
515 	 */
516 	if (op->op_oplock_state & OPLOCK_LEVEL_GRANULAR) {
517 		ASSERT(0);
518 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
519 	} else if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
520 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
521 	} else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
522 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
523 	} else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
524 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
525 	} else {
526 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
527 	}
528 
529 	/*
530 	 * An smb_oplock_reqest call may have returned the
531 	 * status code that says we should wait.
532 	 */
533 	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
534 		(void) smb2sr_go_async(sr);
535 		(void) smb_oplock_wait_break(sr, ofile->f_node, 0);
536 	}
537 }
538 
539 /*
540  * smb2_oplock_reconnect()  Helper for smb2_dh_reconnect
541  * Get oplock state into op->op_oplock_level etc.
542  *
543  * Similar to the end of smb2_lease_acquire (for leases) or
544  * the end of smb2_oplock_acquire (for old-style oplocks).
545  */
546 void
547 smb2_oplock_reconnect(smb_request_t *sr)
548 {
549 	smb_arg_open_t *op = &sr->arg.open;
550 	smb_ofile_t *ofile = sr->fid_ofile;
551 
552 	op->op_oplock_state = ofile->f_oplock.og_state;
553 	if (ofile->f_lease != NULL) {
554 		smb_lease_t *ls = ofile->f_lease;
555 
556 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
557 		op->lease_state = ls->ls_state &
558 		    OPLOCK_LEVEL_CACHE_MASK;
559 		op->lease_flags = (ls->ls_breaking != 0) ?
560 		    SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0;
561 		op->lease_epoch = ls->ls_epoch;
562 		op->lease_version = ls->ls_version;
563 	} else {
564 		switch (op->op_oplock_state & OPLOCK_LEVEL_TYPE_MASK) {
565 		default:
566 		case OPLOCK_LEVEL_NONE:
567 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
568 			break;
569 		case OPLOCK_LEVEL_TWO:
570 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
571 			break;
572 		case OPLOCK_LEVEL_ONE:
573 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
574 			break;
575 		case OPLOCK_LEVEL_BATCH:
576 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
577 			break;
578 		}
579 	}
580 }
581