xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_lease.c (revision 7f6a299e282ed51917878b84744774a6634e5dc6)
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 2021 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 /* StructSize for the two "break" message formats. */
25 #define	SSZ_OPLOCK	24
26 #define	SSZ_LEASE_ACK	36
27 #define	SSZ_LEASE_BRK	44
28 
29 #define	NODE_FLAGS_DELETING	(NODE_FLAGS_DELETE_ON_CLOSE |\
30 				NODE_FLAGS_DELETE_COMMITTED)
31 
32 static const char lease_zero[UUID_LEN] = { 0 };
33 
34 static kmem_cache_t	*smb_lease_cache = NULL;
35 
36 void
37 smb2_lease_init()
38 {
39 	if (smb_lease_cache != NULL)
40 		return;
41 
42 	smb_lease_cache = kmem_cache_create("smb_lease_cache",
43 	    sizeof (smb_lease_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
44 }
45 
46 void
47 smb2_lease_fini()
48 {
49 	if (smb_lease_cache != NULL) {
50 		kmem_cache_destroy(smb_lease_cache);
51 		smb_lease_cache = NULL;
52 	}
53 }
54 
55 static void
56 smb2_lease_hold(smb_lease_t *ls)
57 {
58 	mutex_enter(&ls->ls_mutex);
59 	ls->ls_refcnt++;
60 	mutex_exit(&ls->ls_mutex);
61 }
62 
63 static void
64 lease_destroy(smb_lease_t *ls)
65 {
66 	smb_node_release(ls->ls_node);
67 	mutex_destroy(&ls->ls_mutex);
68 	kmem_cache_free(smb_lease_cache, ls);
69 }
70 
71 void
72 smb2_lease_rele(smb_lease_t *ls)
73 {
74 	smb_llist_t *bucket;
75 	boolean_t destroy = B_FALSE;
76 
77 	mutex_enter(&ls->ls_mutex);
78 	ls->ls_refcnt--;
79 	if (ls->ls_refcnt != 0) {
80 		mutex_exit(&ls->ls_mutex);
81 		return;
82 	}
83 	mutex_exit(&ls->ls_mutex);
84 
85 	/*
86 	 * Get the list lock, then re-check the refcnt
87 	 * and if it's still zero, unlink & destroy.
88 	 */
89 	bucket = ls->ls_bucket;
90 	smb_llist_enter(bucket, RW_WRITER);
91 
92 	mutex_enter(&ls->ls_mutex);
93 	if (ls->ls_refcnt == 0) {
94 		smb_llist_remove(bucket, ls);
95 		destroy = B_TRUE;
96 	}
97 	mutex_exit(&ls->ls_mutex);
98 
99 	smb_llist_exit(bucket);
100 
101 	if (destroy) {
102 		lease_destroy(ls);
103 	}
104 }
105 
106 /*
107  * Compute a hash from a uuid
108  * Based on mod_hash_bystr()
109  */
110 static uint_t
111 smb_hash_uuid(const uint8_t *uuid)
112 {
113 	char *k = (char *)uuid;
114 	uint_t hash = 0;
115 	uint_t g;
116 	int i;
117 
118 	ASSERT(k);
119 	for (i = 0; i < UUID_LEN; i++) {
120 		hash = (hash << 4) + k[i];
121 		if ((g = (hash & 0xf0000000)) != 0) {
122 			hash ^= (g >> 24);
123 			hash ^= g;
124 		}
125 	}
126 	return (hash);
127 }
128 
129 /*
130  * Add or update a lease table entry for a new ofile.
131  * (in the per-session lease table)
132  * See [MS-SMB2] 3.3.5.9.8
133  * Handling the SMB2_CREATE_REQUEST_LEASE Create Context
134  */
135 uint32_t
136 smb2_lease_create(smb_request_t *sr, uint8_t *clnt)
137 {
138 	smb_arg_open_t *op = &sr->arg.open;
139 	uint8_t *key = op->lease_key;
140 	smb_ofile_t *of = sr->fid_ofile;
141 	smb_hash_t *ht = sr->sr_server->sv_lease_ht;
142 	smb_llist_t *bucket;
143 	smb_lease_t *lease;
144 	smb_lease_t *newlease;
145 	size_t hashkey;
146 	uint32_t status = NT_STATUS_INVALID_PARAMETER;
147 
148 	if (bcmp(key, lease_zero, UUID_LEN) == 0)
149 		return (status);
150 
151 	/*
152 	 * Find or create, and add a ref for the new ofile.
153 	 */
154 	hashkey = smb_hash_uuid(key);
155 	hashkey &= (ht->num_buckets - 1);
156 	bucket = &ht->buckets[hashkey].b_list;
157 
158 	newlease = kmem_cache_alloc(smb_lease_cache, KM_SLEEP);
159 	bzero(newlease, sizeof (smb_lease_t));
160 	mutex_init(&newlease->ls_mutex, NULL, MUTEX_DEFAULT, NULL);
161 	newlease->ls_bucket = bucket;
162 	newlease->ls_node = of->f_node;
163 	smb_node_ref(newlease->ls_node);
164 	newlease->ls_refcnt = 1;
165 	newlease->ls_epoch = op->lease_epoch;
166 	newlease->ls_version = op->lease_version;
167 	bcopy(key, newlease->ls_key, UUID_LEN);
168 	bcopy(clnt, newlease->ls_clnt, UUID_LEN);
169 
170 	smb_llist_enter(bucket, RW_WRITER);
171 	for (lease = smb_llist_head(bucket); lease != NULL;
172 	    lease = smb_llist_next(bucket, lease)) {
173 		/*
174 		 * Looking for this lease ID, on a node
175 		 * that's not being deleted.
176 		 */
177 		if (bcmp(lease->ls_key, key, UUID_LEN) == 0 &&
178 		    bcmp(lease->ls_clnt, clnt, UUID_LEN) == 0 &&
179 		    (lease->ls_node->flags & NODE_FLAGS_DELETING) == 0)
180 			break;
181 	}
182 	if (lease != NULL) {
183 		/*
184 		 * Found existing lease.  Make sure it refers to
185 		 * the same node...
186 		 */
187 		if (lease->ls_node == of->f_node) {
188 			smb2_lease_hold(lease);
189 		} else {
190 			/* Same lease ID, different node! */
191 #ifdef DEBUG
192 			cmn_err(CE_NOTE, "new lease on node %p (%s) "
193 			    "conflicts with existing node %p (%s)",
194 			    (void *) of->f_node,
195 			    of->f_node->od_name,
196 			    (void *) lease->ls_node,
197 			    lease->ls_node->od_name);
198 #endif
199 			DTRACE_PROBE2(dup_lease, smb_request_t *, sr,
200 			    smb_lease_t *, lease);
201 			lease = NULL; /* error */
202 		}
203 	} else {
204 		lease = newlease;
205 		smb_llist_insert_head(bucket, lease);
206 		newlease = NULL; /* don't free */
207 	}
208 	smb_llist_exit(bucket);
209 
210 	if (newlease != NULL) {
211 		lease_destroy(newlease);
212 	}
213 
214 	if (lease != NULL) {
215 		of->f_lease = lease;
216 		status = NT_STATUS_SUCCESS;
217 	}
218 
219 	return (status);
220 }
221 
222 /*
223  * Find the lease for a given: client_uuid, lease_key
224  * Returns the lease with a new ref.
225  */
226 static smb_lease_t *
227 lease_lookup(smb_request_t *sr, uint8_t *lease_key)
228 {
229 	smb_server_t *sv = sr->sr_server;
230 	uint8_t *clnt_uuid = sr->session->clnt_uuid;
231 	smb_hash_t *ht = sv->sv_lease_ht;
232 	smb_llist_t *bucket;
233 	smb_lease_t *lease;
234 	size_t hashkey;
235 
236 	hashkey = smb_hash_uuid(lease_key);
237 	hashkey &= (ht->num_buckets - 1);
238 	bucket = &ht->buckets[hashkey].b_list;
239 
240 	smb_llist_enter(bucket, RW_READER);
241 	lease = smb_llist_head(bucket);
242 	while (lease != NULL) {
243 		if (bcmp(lease->ls_key, lease_key, UUID_LEN) == 0 &&
244 		    bcmp(lease->ls_clnt, clnt_uuid, UUID_LEN) == 0) {
245 			smb2_lease_hold(lease);
246 			break;
247 		}
248 		lease = smb_llist_next(bucket, lease);
249 	}
250 	smb_llist_exit(bucket);
251 
252 	return (lease);
253 }
254 
255 /*
256  * Find the oplock smb_ofile_t for the specified lease.
257  * If no such ofile, NT_STATUS_UNSUCCESSFUL.
258  * On success, ofile (held) in sr->fid_ofile.
259  */
260 static uint32_t
261 lease_find_oplock(smb_request_t *sr, smb_lease_t *lease)
262 {
263 	smb_node_t	*node = lease->ls_node;
264 	smb_ofile_t	*o;
265 	uint32_t	status = NT_STATUS_UNSUCCESSFUL;
266 
267 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
268 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
269 	ASSERT(sr->fid_ofile == NULL);
270 
271 	FOREACH_NODE_OFILE(node, o) {
272 		if (o->f_lease != lease)
273 			continue;
274 		if (o != lease->ls_oplock_ofile)
275 			continue;
276 		/*
277 		 * Found the ofile holding the oplock
278 		 * This hold released in smb_request_free
279 		 */
280 		if (smb_ofile_hold_olbrk(o)) {
281 			sr->fid_ofile = o;
282 			status = NT_STATUS_SUCCESS;
283 			break;
284 		}
285 	}
286 
287 	return (status);
288 }
289 
290 /*
291  * This is called by smb2_oplock_break_ack when the struct size
292  * indicates this is a lease break (SZ_LEASE).  See:
293  * [MS-SMB2] 3.3.5.22.2 Processing a Lease Acknowledgment
294  * This is an "Ack" from the client.
295  */
296 smb_sdrc_t
297 smb2_lease_break_ack(smb_request_t *sr)
298 {
299 	smb_arg_olbrk_t	*olbrk = &sr->arg.olbrk;
300 	smb_lease_t *lease;
301 	smb_node_t  *node;
302 	smb_ofile_t *ofile;
303 	uint32_t LeaseState;
304 	uint32_t status;
305 	int rc = 0;
306 
307 	if (sr->session->dialect < SMB_VERS_2_1)
308 		return (SDRC_ERROR);
309 
310 	/*
311 	 * Decode an SMB2 Lease Acknowldgement
312 	 * [MS-SMB2] 2.2.24.2
313 	 * Note: Struct size decoded by caller.
314 	 */
315 	rc = smb_mbc_decodef(
316 	    &sr->smb_data, "6.#cl8.",
317 	    /* reserved		  6. */
318 	    UUID_LEN,		/* # */
319 	    olbrk->LeaseKey,	/* c */
320 	    &olbrk->NewLevel);	/* l */
321 	    /* duration		  8. */
322 	if (rc != 0)
323 		return (SDRC_ERROR);
324 	LeaseState = olbrk->NewLevel;
325 
326 	/*
327 	 * Find the lease via the given key.
328 	 */
329 	lease = lease_lookup(sr, olbrk->LeaseKey);
330 	if (lease == NULL) {
331 		/*
332 		 * It's unusual to skip the dtrace start/done
333 		 * probes like this, but trying to run them
334 		 * with no lease->node would be complex and
335 		 * would not show anything particularly useful.
336 		 * Do the start probe after we find the ofile.
337 		 */
338 		status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
339 		smb2sr_put_error(sr, status);
340 		return (SDRC_SUCCESS);
341 	}
342 	// Note: lease ref; smb_lease_rele() below.
343 	node = lease->ls_node;
344 
345 	/*
346 	 * Find the leased oplock.  Hold locks so it can't move
347 	 * until we're done with ACK-break processing.
348 	 */
349 	smb_llist_enter(&node->n_ofile_list, RW_READER);
350 	mutex_enter(&node->n_oplock.ol_mutex);
351 
352 	status = lease_find_oplock(sr, lease);
353 	/* Normally have sr->fid_ofile now. */
354 
355 	DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr);
356 
357 	if (status != 0) {
358 		/* Leased oplock not found.  Must have closed. */
359 		goto errout;
360 	}
361 
362 	/* Success, so have sr->fid_ofile */
363 	ofile = sr->fid_ofile;
364 
365 	if (lease->ls_breaking == B_FALSE) {
366 		/*
367 		 * This ACK is either unsolicited or too late,
368 		 * eg. we timed out the ACK and did it locally.
369 		 */
370 		status = NT_STATUS_UNSUCCESSFUL;
371 		goto errout;
372 	}
373 
374 	/*
375 	 * If the new LeaseState has any bits in excess of
376 	 * the lease state we sent in the break, error...
377 	 */
378 	if ((LeaseState & ~(lease->ls_breakto)) != 0) {
379 		status = NT_STATUS_REQUEST_NOT_ACCEPTED;
380 		goto errout;
381 	}
382 
383 	/*
384 	 * Process the lease break ack.
385 	 *
386 	 * Clear breaking flags before we ack,
387 	 * because ack might set those.
388 	 * Signal both CVs, out of paranoia.
389 	 */
390 	ofile->f_oplock.og_breaking = B_FALSE;
391 	cv_broadcast(&ofile->f_oplock.og_ack_cv);
392 	lease->ls_breaking = B_FALSE;
393 	cv_broadcast(&lease->ls_ack_cv);
394 
395 	LeaseState |= OPLOCK_LEVEL_GRANULAR;
396 	status = smb_oplock_ack_break(sr, ofile, &LeaseState);
397 
398 	ofile->f_oplock.og_state = LeaseState;
399 	lease->ls_state = LeaseState;
400 	/* ls_epoch does not change here */
401 
402 	if (ofile->dh_persist)
403 		smb2_dh_update_oplock(sr, ofile);
404 
405 errout:
406 	sr->smb2_status = status;
407 	DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr);
408 
409 	mutex_exit(&node->n_oplock.ol_mutex);
410 	smb_llist_exit(&node->n_ofile_list);
411 
412 	smb2_lease_rele(lease);
413 
414 	if (status) {
415 		smb2sr_put_error(sr, status);
416 		return (SDRC_SUCCESS);
417 	}
418 
419 	/*
420 	 * Encode an SMB2 Lease Ack. response
421 	 * [MS-SMB2] 2.2.25.2
422 	 */
423 	LeaseState &= OPLOCK_LEVEL_CACHE_MASK;
424 	(void) smb_mbc_encodef(
425 	    &sr->reply, "w6.#cl8.",
426 	    SSZ_LEASE_ACK,	/* w */
427 	    /* reserved		  6. */
428 	    UUID_LEN,		/* # */
429 	    olbrk->LeaseKey,	/* c */
430 	    LeaseState);	/* l */
431 	    /* duration		  8. */
432 
433 	return (SDRC_SUCCESS);
434 
435 }
436 
437 /*
438  * Compose an SMB2 Lease Break Notification packet, including
439  * the SMB2 header and everything, in sr->reply.
440  * The caller will send it and free the request.
441  *
442  * [MS-SMB2] 2.2.23.2 Lease Break Notification
443  */
444 static void
445 smb2_lease_break_notification(smb_request_t *sr,
446     uint32_t OldLevel, uint32_t NewLevel,
447     uint16_t Epoch, boolean_t AckReq)
448 {
449 	smb_lease_t *ls = sr->fid_ofile->f_lease;
450 	uint16_t Flags = 0;
451 
452 	/*
453 	 * Convert internal lease info to SMB2
454 	 */
455 	if (AckReq)
456 		Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
457 	if (ls->ls_version < 2)
458 		Epoch = 0;
459 	OldLevel &= OPLOCK_LEVEL_CACHE_MASK;
460 	NewLevel &= OPLOCK_LEVEL_CACHE_MASK;
461 
462 	/*
463 	 * SMB2 Header
464 	 */
465 	sr->smb2_cmd_code = SMB2_OPLOCK_BREAK;
466 	sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR;
467 	sr->smb_tid = 0;
468 	sr->smb_pid = 0;
469 	sr->smb2_ssnid = 0;
470 	sr->smb2_messageid = UINT64_MAX;
471 	(void) smb2_encode_header(sr, B_FALSE);
472 
473 	/*
474 	 * SMB2 Oplock Break, variable part
475 	 *
476 	 * [MS-SMB2] says the current lease state preceeds the
477 	 * new lease state, but that looks like an error...
478 	 */
479 	(void) smb_mbc_encodef(
480 	    &sr->reply, "wwl#cll4.4.4.",
481 	    SSZ_LEASE_BRK,		/* w */
482 	    Epoch,			/* w */
483 	    Flags,			/* l */
484 	    SMB_LEASE_KEY_SZ,		/* # */
485 	    ls->ls_key,			/* c */
486 	    OldLevel,		/* cur.st  l */
487 	    NewLevel);		/* new.st  l */
488 	    /* reserved (4.4.4.) */
489 }
490 
491 /*
492  * Do our best to send a lease break message to the client.
493  * When we get to multi-channel, this is supposed to try
494  * every channel before giving up.  For now, try every
495  * connected session with an ofile sharing this lease.
496  *
497  * If this ofile has a valid session, try that first.
498  * Otherwise look on the node list for other ofiles with
499  * the same lease and a connected session.
500  */
501 static int
502 lease_send_any_cn(smb_request_t *sr)
503 {
504 	smb_ofile_t	*o;
505 	smb_ofile_t	*ofile = sr->fid_ofile;
506 	smb_lease_t	*lease = ofile->f_lease;
507 	smb_node_t	*node = ofile->f_node;
508 	int		rc = ENOTCONN;
509 
510 	/*
511 	 * If the passed oplock ofile has a session,
512 	 * this IF expression will be true.
513 	 */
514 	if (sr->session == ofile->f_session) {
515 		rc = smb_session_send(sr->session, 0, &sr->reply);
516 		if (rc == 0)
517 			return (rc);
518 	}
519 
520 	smb_llist_enter(&node->n_ofile_list, RW_READER);
521 	FOREACH_NODE_OFILE(node, o) {
522 		if (o->f_lease != lease)
523 			continue;
524 		if (smb_ofile_hold(o)) {
525 			/* Has a session. */
526 			rc = smb_session_send(o->f_session, 0, &sr->reply);
527 			smb_llist_post(&node->n_ofile_list, o,
528 			    smb_ofile_release_LL);
529 		}
530 		if (rc == 0)
531 			break;
532 	}
533 	smb_llist_exit(&node->n_ofile_list);
534 
535 	return (rc);
536 }
537 
538 /*
539  * See smb_llist_post on node->n_ofile_list below.
540  * Can't call smb_ofile_close with that list entered.
541  */
542 static void
543 lease_ofile_close_rele(void *arg)
544 {
545 	smb_ofile_t *of = (smb_ofile_t *)arg;
546 
547 	smb_ofile_close(of, 0);
548 	smb_ofile_release(of);
549 }
550 
551 /*
552  * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break
553  * If no connection, for each Open in Lease.LeaseOpens,
554  * the server MUST close the Open as specified in sec...
555  * for the following cases:
556  * - Open.IsDurable, Open.IsResilient, and
557  *   Open.IsPersistent are all FALSE.
558  * - Open.IsDurable is TRUE and Lease.BreakToLeaseState
559  *   does not contain SMB2_LEASE_HANDLE_CACHING and
560  */
561 static void
562 lease_close_notconn(smb_request_t *sr, uint32_t NewLevel)
563 {
564 	smb_ofile_t	*o;
565 	smb_ofile_t	*ofile = sr->fid_ofile;
566 	smb_lease_t	*lease = ofile->f_lease;
567 	smb_node_t	*node = ofile->f_node;
568 
569 	smb_llist_enter(&node->n_ofile_list, RW_READER);
570 	FOREACH_NODE_OFILE(node, o) {
571 		if (o->f_lease != lease)
572 			continue;
573 		if (o->f_oplock_closing)
574 			continue;
575 		if (o->dh_persist)
576 			continue;
577 		if (o->dh_vers == SMB2_RESILIENT)
578 			continue;
579 		if (o->dh_vers == SMB2_NOT_DURABLE ||
580 		    (NewLevel & OPLOCK_LEVEL_CACHE_HANDLE) == 0) {
581 			if (smb_ofile_hold_olbrk(o)) {
582 				smb_llist_post(&node->n_ofile_list, o,
583 				    lease_ofile_close_rele);
584 			}
585 		}
586 	}
587 	smb_llist_exit(&node->n_ofile_list);
588 }
589 
590 /*
591  * Send a lease break over the wire, or if we can't,
592  * then process the lease break locally.
593  *
594  * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break
595  *
596  * This is mostly similar to smb2_oplock_send_break()
597  * See top comment there about the design.
598  *
599  * Differences beween a lease break and oplock break:
600  *
601  * Leases are an SMB-level mechanism whereby multiple open
602  * SMB file handles can share an oplock.  All SMB handles
603  * on the lease enjoy the same caching rights.  Down at the
604  * file-system level, just one oplock holds the cache rights
605  * for a lease, but (this is the tricky part) that oplock can
606  * MOVE among the SMB file handles sharing the lease. Such
607  * oplock moves can happen when a handle is closed (if that
608  * handle is the one with the oplock) or when a new open on
609  * the lease causes an upgrade of the caching rights.
610  *
611  * We have to deal here with lease movement because this call
612  * happens asynchronously after the smb_oplock_ind_break call,
613  * meaning that the oplock for the lease may have moved by the
614  * time this runs.  In addition, the ofile holding the oplock
615  * might not be the best one to use to send a lease break.
616  * If the oplock is held by a handle that's "orphaned" and
617  * there are other handles on the lease with active sessions,
618  * we want to send the lease break on an active session.
619  *
620  * Also note: NewLevel (as provided by smb_oplock_ind_break etc.)
621  * does NOT include the GRANULAR flag.  This level is expected to
622  * keep track of how each oplock was acquired (by lease or not)
623  * and put the GRANULAR flag back in when appropriate.
624  */
625 void
626 smb2_lease_send_break(smb_request_t *sr)
627 {
628 	smb_ofile_t	*old_ofile;
629 	smb_ofile_t	*ofile = sr->fid_ofile;
630 	smb_node_t	*node = ofile->f_node;
631 	smb_lease_t	*lease = ofile->f_lease;
632 	smb_arg_olbrk_t	*olbrk = &sr->arg.olbrk;
633 	boolean_t	AckReq = olbrk->AckRequired;
634 	uint32_t	OldLevel = olbrk->OldLevel;
635 	uint32_t	NewLevel = olbrk->NewLevel;
636 	uint32_t	status;
637 	int		rc;
638 
639 	NewLevel |= OPLOCK_LEVEL_GRANULAR;
640 
641 	/*
642 	 * Build the break message in sr->reply.
643 	 * It's free'd in smb_request_free().
644 	 * Always an SMB2 lease here.
645 	 */
646 	sr->reply.max_bytes = MLEN;
647 	smb2_lease_break_notification(sr,
648 	    OldLevel, NewLevel, lease->ls_epoch, AckReq);
649 
650 	/*
651 	 * Try to send the break message to the client,
652 	 * on any connection with this lease.
653 	 */
654 	rc = lease_send_any_cn(sr);
655 	if (rc != 0) {
656 		/*
657 		 * We were unable to send the oplock break request,
658 		 * presumably because the connection is gone.
659 		 * Close uninteresting handles.
660 		 */
661 		lease_close_notconn(sr, NewLevel);
662 		/* Note: some handles may remain on the lease. */
663 		if (!AckReq)
664 			return;
665 		/* Do local Ack below. */
666 	} else {
667 		/*
668 		 * OK, we were able to send the break message.
669 		 * If no ack. required, we're done.
670 		 */
671 		if (!AckReq)
672 			return;
673 
674 		/*
675 		 * We're expecting an ACK.  Wait in this thread
676 		 * so we can log clients that don't respond.
677 		 */
678 		status = smb_oplock_wait_ack(sr, NewLevel);
679 		if (status == 0)
680 			return;
681 
682 		cmn_err(CE_NOTE, "clnt %s oplock break timeout",
683 		    sr->session->ip_addr_str);
684 		DTRACE_PROBE1(ack_timeout, smb_request_t *, sr);
685 
686 		/*
687 		 * Will do local ack below.  Note, after timeout,
688 		 * do a break to none or "no caching" regardless
689 		 * of what the passed in cache level was.
690 		 * That means: clear all except GRANULAR.
691 		 */
692 		NewLevel = OPLOCK_LEVEL_GRANULAR;
693 	}
694 
695 	/*
696 	 * Do the ack locally.
697 	 *
698 	 * Find the ofile with the leased oplock
699 	 * (may have moved before we took locks)
700 	 */
701 	smb_llist_enter(&node->n_ofile_list, RW_READER);
702 	mutex_enter(&node->n_oplock.ol_mutex);
703 
704 	old_ofile = ofile;
705 	sr->fid_ofile = NULL;
706 	status = lease_find_oplock(sr, lease);
707 	if (status != 0) {
708 		/* put back old_ofile */
709 		sr->fid_ofile = old_ofile;
710 		goto unlock_out;
711 	}
712 	smb_llist_post(&node->n_ofile_list, old_ofile,
713 	    smb_ofile_release_LL);
714 
715 	ofile = sr->fid_ofile;
716 
717 	/*
718 	 * Now continue like the non-lease code
719 	 */
720 	ofile->f_oplock.og_breaking = B_FALSE;
721 	lease->ls_breaking = B_FALSE;
722 	cv_broadcast(&lease->ls_ack_cv);
723 
724 	status = smb_oplock_ack_break(sr, ofile, &NewLevel);
725 
726 	ofile->f_oplock.og_state = NewLevel;
727 	lease->ls_state = NewLevel;
728 	/* ls_epoch does not change here */
729 
730 	if (ofile->dh_persist)
731 		smb2_dh_update_oplock(sr, ofile);
732 
733 unlock_out:
734 	mutex_exit(&node->n_oplock.ol_mutex);
735 	smb_llist_exit(&node->n_ofile_list);
736 
737 #ifdef	DEBUG
738 	if (status != 0) {
739 		cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x",
740 		    sr->session->ip_addr_str, status);
741 	}
742 #endif
743 }
744 
745 /*
746  * Client has an open handle and requests a lease.
747  * Convert SMB2 lease request info in to internal form,
748  * call common oplock code, convert result to SMB2.
749  *
750  * If necessary, "go async" here (at the end).
751  */
752 void
753 smb2_lease_acquire(smb_request_t *sr)
754 {
755 	smb_arg_open_t *op = &sr->arg.open;
756 	smb_ofile_t *ofile = sr->fid_ofile;
757 	smb_lease_t *lease = ofile->f_lease;
758 	smb_node_t *node = ofile->f_node;
759 	uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED;
760 	uint32_t have, want; /* lease flags */
761 	boolean_t NewGrant = B_FALSE;
762 
763 	/* Only disk trees get oplocks. */
764 	ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE);
765 
766 	/*
767 	 * Only plain files (for now).
768 	 * Later, test SMB2_CAP_DIRECTORY_LEASING
769 	 */
770 	if (!smb_node_is_file(ofile->f_node)) {
771 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
772 		return;
773 	}
774 
775 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
776 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
777 		return;
778 	}
779 
780 	/*
781 	 * SMB2: Convert to internal form.
782 	 * Caller should have setup the lease.
783 	 */
784 	ASSERT(op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE);
785 	ASSERT(lease != NULL);
786 	if (lease == NULL) {
787 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
788 		return;
789 	}
790 	op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
791 	    (op->lease_state & CACHE_RWH);
792 
793 	/*
794 	 * Tree options may force shared oplocks,
795 	 * in which case we reduce the request.
796 	 */
797 	if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
798 		op->op_oplock_state &= ~WRITE_CACHING;
799 	}
800 
801 	/*
802 	 * Using the "Locks Held" (LH) variant of smb_oplock_request
803 	 * below so things won't change underfoot.
804 	 */
805 	smb_llist_enter(&node->n_ofile_list, RW_READER);
806 	mutex_enter(&node->n_oplock.ol_mutex);
807 
808 	/*
809 	 * MS-SMB2 3.3.5.9.8 and 3.3.5.9.11 Lease (V2) create contexts
810 	 *
811 	 * If the caching state requested in LeaseState of the (create ctx)
812 	 * is not a superset of Lease.LeaseState or if Lease.Breaking is TRUE,
813 	 * the server MUST NOT promote Lease.LeaseState. If the lease state
814 	 * requested is a superset of Lease.LeaseState and Lease.Breaking is
815 	 * FALSE, the server MUST request promotion of the lease state from
816 	 * the underlying object store to the new caching state.
817 	 */
818 	have = lease->ls_state & CACHE_RWH;
819 	want = op->op_oplock_state & CACHE_RWH;
820 	if ((have & ~want) != 0 || lease->ls_breaking) {
821 		op->op_oplock_state = have |
822 		    OPLOCK_LEVEL_GRANULAR;
823 		goto done;
824 	}
825 
826 	/*
827 	 * Handle oplock requests in three parts:
828 	 *	a: Requests with WRITE_CACHING
829 	 *	b: Requests with HANDLE_CACHING
830 	 *	c: Requests with READ_CACHING
831 	 * reducing the request before b and c.
832 	 *
833 	 * In each: first check if the lease grants the
834 	 * (possibly reduced) request, in which case we
835 	 * leave the lease unchanged and return what's
836 	 * granted by the lease.  Otherwise, try to get
837 	 * the oplock, and if the succeeds, wait for any
838 	 * breaks, update the lease, and return.
839 	 */
840 
841 	/*
842 	 * Try exclusive (request is RW or RWH)
843 	 */
844 	if ((op->op_oplock_state & WRITE_CACHING) != 0) {
845 		/* Alread checked (want & ~have) */
846 
847 		status = smb_oplock_request_LH(sr, ofile,
848 		    &op->op_oplock_state);
849 		if (status == NT_STATUS_SUCCESS ||
850 		    status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
851 			NewGrant = B_TRUE;
852 			goto done;
853 		}
854 
855 		/*
856 		 * We did not get the exclusive oplock.
857 		 *
858 		 * There are odd rules about lease upgrade.
859 		 * If the existing lease grants R and the
860 		 * client fails to upgrade it to "RWH"
861 		 * (presumably due to handle conflicts)
862 		 * then just return the existing lease,
863 		 * even though upgrade to RH would work.
864 		 */
865 		if (have != 0) {
866 			op->op_oplock_state = have |
867 			    OPLOCK_LEVEL_GRANULAR;
868 			goto done;
869 		}
870 
871 		/*
872 		 * Keep trying without write.
873 		 * Need to re-init op_oplock_state
874 		 */
875 		op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
876 		    (op->lease_state & CACHE_RH);
877 	}
878 
879 	/*
880 	 * Try shared ("RH")
881 	 */
882 	if ((op->op_oplock_state & HANDLE_CACHING) != 0) {
883 		want = op->op_oplock_state & CACHE_RWH;
884 		if ((want & ~have) == 0)
885 			goto done;
886 
887 		status = smb_oplock_request_LH(sr, ofile,
888 		    &op->op_oplock_state);
889 		if (status == NT_STATUS_SUCCESS ||
890 		    status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
891 			NewGrant = B_TRUE;
892 			goto done;
893 		}
894 
895 		/*
896 		 * We did not get "RH", probably because
897 		 * ther were (old style) Level II oplocks.
898 		 * Continue, try for just read.
899 		 * Again, re-init op_oplock_state
900 		 */
901 		op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
902 		    (op->lease_state & CACHE_R);
903 	}
904 
905 	/*
906 	 * Try shared ("R")
907 	 */
908 	if ((op->op_oplock_state & READ_CACHING) != 0) {
909 		want = op->op_oplock_state & CACHE_RWH;
910 		if ((want & ~have) == 0)
911 			goto done;
912 
913 		status = smb_oplock_request_LH(sr, ofile,
914 		    &op->op_oplock_state);
915 		if (status == NT_STATUS_SUCCESS ||
916 		    status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
917 			NewGrant = B_TRUE;
918 			goto done;
919 		}
920 
921 		/*
922 		 * We did not get "R".
923 		 * Fall into "none".
924 		 */
925 	}
926 
927 	/*
928 	 * None of the above were able to get an oplock.
929 	 * The lease has no caching rights, and we didn't
930 	 * add any in this request.  Return it as-is.
931 	 */
932 	op->op_oplock_state = OPLOCK_LEVEL_GRANULAR;
933 
934 done:
935 	/*
936 	 * Only success cases get here
937 	 */
938 
939 	/*
940 	 * Keep track of what we got (ofile->f_oplock.og_state etc)
941 	 * so we'll know what we had when sending a break later.
942 	 * Also keep a copy of some things in the lease.
943 	 *
944 	 * Not using og_dialect here, as ofile->f_lease tells us
945 	 * this has to be using granular oplocks.
946 	 */
947 	if (NewGrant) {
948 		ofile->f_oplock.og_state   = op->op_oplock_state;
949 		ofile->f_oplock.og_breakto = op->op_oplock_state;
950 		ofile->f_oplock.og_breaking = B_FALSE;
951 
952 		lease->ls_oplock_ofile = ofile;
953 		lease->ls_state   = ofile->f_oplock.og_state;
954 		lease->ls_breakto = ofile->f_oplock.og_breakto;
955 		lease->ls_breaking = B_FALSE;
956 		lease->ls_epoch++;
957 
958 		if (ofile->dh_persist) {
959 			smb2_dh_update_oplock(sr, ofile);
960 		}
961 	}
962 
963 	/*
964 	 * Convert internal oplock state to SMB2
965 	 */
966 	op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
967 	op->lease_state = lease->ls_state & CACHE_RWH;
968 	op->lease_flags = (lease->ls_breaking != 0) ?
969 	    SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0;
970 	op->lease_epoch = lease->ls_epoch;
971 	op->lease_version = lease->ls_version;
972 
973 	/*
974 	 * End of lock-held region
975 	 */
976 	mutex_exit(&node->n_oplock.ol_mutex);
977 	smb_llist_exit(&node->n_ofile_list);
978 
979 	/*
980 	 * After a new oplock grant, the status return
981 	 * may indicate we need to wait for breaks.
982 	 */
983 	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
984 		(void) smb2sr_go_async(sr);
985 		(void) smb_oplock_wait_break(sr, ofile->f_node, 0);
986 	}
987 }
988 
989 /*
990  * This ofile has a lease and is about to close.
991  * Called by smb_ofile_close when there's a lease.
992  *
993  * Note that a client may close an ofile in response to an
994  * oplock break or lease break intead of doing an Ack break,
995  * so this must wake anything that might be waiting on an ack
996  * when the last close of a lease happens.
997  *
998  * With leases, just one ofile on a lease owns the oplock.
999  * If an ofile with a lease is closed and it's the one that
1000  * owns the oplock, try to move the oplock to another ofile
1001  * on the same lease.
1002  *
1003  * Would prefer that we could just use smb_ofile_hold_olbrk
1004  * to select a suitable destination for the move, but this
1005  * is called while holding the owning tree ofile list etc
1006  * which can cause deadlock as described in illumos 13850
1007  * when smb_ofile_hold_olbrk has to wait.  XXX todo
1008  */
1009 void
1010 smb2_lease_ofile_close(smb_ofile_t *ofile)
1011 {
1012 	smb_node_t *node = ofile->f_node;
1013 	smb_lease_t *lease = ofile->f_lease;
1014 	smb_ofile_t *o;
1015 
1016 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
1017 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
1018 
1019 #ifdef	DEBUG
1020 	FOREACH_NODE_OFILE(node, o) {
1021 		DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
1022 	}
1023 #endif
1024 
1025 	/*
1026 	 * If this ofile was not the oplock owner for this lease,
1027 	 * we can leave things as they are.
1028 	 */
1029 	if (lease->ls_oplock_ofile != ofile)
1030 		return;
1031 
1032 	/*
1033 	 * Find another ofile to which we can move the oplock.
1034 	 * First try for one that's open.  Usually find one.
1035 	 */
1036 	FOREACH_NODE_OFILE(node, o) {
1037 		if (o == ofile)
1038 			continue;
1039 		if (o->f_lease != lease)
1040 			continue;
1041 		if (o->f_oplock_closing)
1042 			continue;
1043 
1044 		mutex_enter(&o->f_mutex);
1045 		if (o->f_state == SMB_OFILE_STATE_OPEN) {
1046 			smb_oplock_move(node, ofile, o);
1047 			lease->ls_oplock_ofile = o;
1048 			mutex_exit(&o->f_mutex);
1049 			return;
1050 		}
1051 		mutex_exit(&o->f_mutex);
1052 	}
1053 
1054 	/*
1055 	 * Now try for one that's orphaned etc.
1056 	 */
1057 	FOREACH_NODE_OFILE(node, o) {
1058 		if (o == ofile)
1059 			continue;
1060 		if (o->f_lease != lease)
1061 			continue;
1062 		if (o->f_oplock_closing)
1063 			continue;
1064 
1065 		/*
1066 		 * Allow most states as seen in smb_ofile_hold_olbrk
1067 		 * without waiting for "_reconnect" or "_saving".
1068 		 * Skip "_expired" because that's about to close.
1069 		 * This is OK because just swapping the oplock state
1070 		 * between two ofiles does not interfere with the
1071 		 * dh_save or reconnect code paths.
1072 		 */
1073 		mutex_enter(&o->f_mutex);
1074 		switch (o->f_state) {
1075 		case SMB_OFILE_STATE_OPEN:
1076 		case SMB_OFILE_STATE_SAVE_DH:
1077 		case SMB_OFILE_STATE_SAVING:
1078 		case SMB_OFILE_STATE_ORPHANED:
1079 		case SMB_OFILE_STATE_RECONNECT:
1080 			smb_oplock_move(node, ofile, o);
1081 			lease->ls_oplock_ofile = o;
1082 			mutex_exit(&o->f_mutex);
1083 			return;
1084 		}
1085 		mutex_exit(&o->f_mutex);
1086 	}
1087 
1088 	/*
1089 	 * Normal for last close on a lease.
1090 	 * Wakeup ACK waiters too.
1091 	 */
1092 	lease->ls_state = 0;
1093 	lease->ls_breakto = 0;
1094 	lease->ls_breaking = B_FALSE;
1095 	cv_broadcast(&lease->ls_ack_cv);
1096 
1097 	lease->ls_oplock_ofile = NULL;
1098 }
1099