xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_oplock.c (revision 1d0ec46fafb49266ae79840a692bb48af60ade70)
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 		 * Note: this can also fail for other reasons
358 		 * such as client disconnect or server shutdown.
359 		 */
360 		status = smb_oplock_wait_ack(sr, NewLevel);
361 		if (status == 0)
362 			return;
363 
364 		DTRACE_PROBE2(wait__ack__failed, smb_request_t *, sr,
365 		    uint32_t, status);
366 
367 		/*
368 		 * Will do local ack below.  Note, after timeout,
369 		 * do a break to none or "no caching" regardless
370 		 * of what the passed in cache level was.
371 		 */
372 		NewLevel = OPLOCK_LEVEL_NONE;
373 	}
374 
375 	/*
376 	 * Do the ack locally.
377 	 */
378 	smb_llist_enter(&node->n_ofile_list, RW_READER);
379 	mutex_enter(&node->n_oplock.ol_mutex);
380 
381 	ofile->f_oplock.og_breaking = B_FALSE;
382 	cv_broadcast(&ofile->f_oplock.og_ack_cv);
383 
384 	status = smb_oplock_ack_break(sr, ofile, &NewLevel);
385 
386 	ofile->f_oplock.og_state = NewLevel;
387 	if (ofile->dh_persist)
388 		smb2_dh_update_oplock(sr, ofile);
389 
390 	mutex_exit(&node->n_oplock.ol_mutex);
391 	smb_llist_exit(&node->n_ofile_list);
392 
393 #ifdef	DEBUG
394 	if (status != 0) {
395 		cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x",
396 		    sr->session->ip_addr_str, status);
397 	}
398 #endif
399 }
400 
401 /*
402  * Client has an open handle and requests an oplock.
403  * Convert SMB2 oplock request info in to internal form,
404  * call common oplock code, convert result to SMB2.
405  *
406  * If necessary, "go async" here.
407  */
408 void
409 smb2_oplock_acquire(smb_request_t *sr)
410 {
411 	smb_arg_open_t *op = &sr->arg.open;
412 	smb_ofile_t *ofile = sr->fid_ofile;
413 	uint32_t status;
414 
415 	/* Only disk trees get oplocks. */
416 	ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE);
417 
418 	/* Only plain files... */
419 	if (!smb_node_is_file(ofile->f_node)) {
420 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
421 		return;
422 	}
423 
424 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
425 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
426 		return;
427 	}
428 
429 	/*
430 	 * SMB2: Convert to internal form.
431 	 */
432 	switch (op->op_oplock_level) {
433 	case SMB2_OPLOCK_LEVEL_BATCH:
434 		op->op_oplock_state = OPLOCK_LEVEL_BATCH;
435 		break;
436 	case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
437 		op->op_oplock_state = OPLOCK_LEVEL_ONE;
438 		break;
439 	case SMB2_OPLOCK_LEVEL_II:
440 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
441 		break;
442 	case SMB2_OPLOCK_LEVEL_LEASE:
443 		ASSERT(0); /* Handled elsewhere */
444 		/* FALLTHROUGH */
445 	case SMB2_OPLOCK_LEVEL_NONE:
446 	default:
447 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
448 		return;
449 	}
450 
451 	/*
452 	 * Tree options may force shared oplocks,
453 	 * in which case we reduce the request.
454 	 * Can't get here with LEVEL_NONE, so
455 	 * this can only decrease the level.
456 	 */
457 	if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
458 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
459 	}
460 
461 	/*
462 	 * Try exclusive first, if requested
463 	 */
464 	if ((op->op_oplock_state & BATCH_OR_EXCL) != 0) {
465 		status = smb_oplock_request(sr, ofile,
466 		    &op->op_oplock_state);
467 	} else {
468 		status = NT_STATUS_OPLOCK_NOT_GRANTED;
469 	}
470 
471 	/*
472 	 * If exclusive failed (or the tree forced shared oplocks)
473 	 * try for a shared oplock (Level II)
474 	 */
475 	if (status == NT_STATUS_OPLOCK_NOT_GRANTED) {
476 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
477 		status = smb_oplock_request(sr, ofile,
478 		    &op->op_oplock_state);
479 	}
480 
481 	/*
482 	 * Keep track of what we got (ofile->f_oplock.og_state etc)
483 	 * so we'll know what we had when sending a break later.
484 	 * The og_dialect here is the oplock dialect, not the
485 	 * SMB dialect.  No lease here, so SMB 2.0.
486 	 */
487 	switch (status) {
488 	case NT_STATUS_SUCCESS:
489 	case NT_STATUS_OPLOCK_BREAK_IN_PROGRESS:
490 		ofile->f_oplock.og_dialect = SMB_VERS_2_002;
491 		ofile->f_oplock.og_state   = op->op_oplock_state;
492 		ofile->f_oplock.og_breakto = op->op_oplock_state;
493 		ofile->f_oplock.og_breaking = B_FALSE;
494 		if (ofile->dh_persist) {
495 			smb2_dh_update_oplock(sr, ofile);
496 		}
497 		break;
498 
499 	case NT_STATUS_OPLOCK_NOT_GRANTED:
500 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
501 		return;
502 
503 	default:
504 		/* Caller did not check args sufficiently? */
505 		cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x",
506 		    sr->session->ip_addr_str, status);
507 		DTRACE_PROBE2(other__error, smb_request_t *, sr,
508 		    uint32_t, status);
509 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
510 		return;
511 	}
512 
513 	/*
514 	 * Only success cases get here
515 	 * Convert internal oplock state to SMB2
516 	 */
517 	if (op->op_oplock_state & OPLOCK_LEVEL_GRANULAR) {
518 		ASSERT(0);
519 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
520 	} else if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
521 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
522 	} else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
523 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
524 	} else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
525 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
526 	} else {
527 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
528 	}
529 
530 	/*
531 	 * An smb_oplock_reqest call may have returned the
532 	 * status code that says we should wait.
533 	 */
534 	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
535 		(void) smb2sr_go_async(sr);
536 		(void) smb_oplock_wait_break(sr, ofile->f_node, 0);
537 	}
538 }
539 
540 /*
541  * smb2_oplock_reconnect()  Helper for smb2_dh_reconnect
542  * Get oplock state into op->op_oplock_level etc.
543  *
544  * Similar to the end of smb2_lease_acquire (for leases) or
545  * the end of smb2_oplock_acquire (for old-style oplocks).
546  */
547 void
548 smb2_oplock_reconnect(smb_request_t *sr)
549 {
550 	smb_arg_open_t *op = &sr->arg.open;
551 	smb_ofile_t *ofile = sr->fid_ofile;
552 
553 	op->op_oplock_state = ofile->f_oplock.og_state;
554 	if (ofile->f_lease != NULL) {
555 		smb_lease_t *ls = ofile->f_lease;
556 
557 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
558 		op->lease_state = ls->ls_state &
559 		    OPLOCK_LEVEL_CACHE_MASK;
560 		op->lease_flags = (ls->ls_breaking != 0) ?
561 		    SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0;
562 		op->lease_epoch = ls->ls_epoch;
563 		op->lease_version = ls->ls_version;
564 	} else {
565 		switch (op->op_oplock_state & OPLOCK_LEVEL_TYPE_MASK) {
566 		default:
567 		case OPLOCK_LEVEL_NONE:
568 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
569 			break;
570 		case OPLOCK_LEVEL_TWO:
571 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
572 			break;
573 		case OPLOCK_LEVEL_ONE:
574 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
575 			break;
576 		case OPLOCK_LEVEL_BATCH:
577 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
578 			break;
579 		}
580 	}
581 }
582