xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_lease.c (revision 74e12c43fe52f2c30f36e65a4d0fb0e8dfd7068a)
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  */
15 
16 /*
17  * Dispatch function for SMB2_OPLOCK_BREAK
18  */
19 
20 #include <smbsrv/smb2_kproto.h>
21 #include <smbsrv/smb_oplock.h>
22 
23 /* StructSize for the two "break" message formats. */
24 #define	SSZ_OPLOCK	24
25 #define	SSZ_LEASE_ACK	36
26 #define	SSZ_LEASE_BRK	44
27 
28 #define	NODE_FLAGS_DELETING	(NODE_FLAGS_DELETE_ON_CLOSE |\
29 				NODE_FLAGS_DELETE_COMMITTED)
30 
31 static const char lease_zero[UUID_LEN] = { 0 };
32 
33 static kmem_cache_t	*smb_lease_cache = NULL;
34 
35 void
36 smb2_lease_init()
37 {
38 	if (smb_lease_cache != NULL)
39 		return;
40 
41 	smb_lease_cache = kmem_cache_create("smb_lease_cache",
42 	    sizeof (smb_lease_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
43 }
44 
45 void
46 smb2_lease_fini()
47 {
48 	if (smb_lease_cache != NULL) {
49 		kmem_cache_destroy(smb_lease_cache);
50 		smb_lease_cache = NULL;
51 	}
52 }
53 
54 static void
55 smb2_lease_hold(smb_lease_t *ls)
56 {
57 	mutex_enter(&ls->ls_mutex);
58 	ls->ls_refcnt++;
59 	mutex_exit(&ls->ls_mutex);
60 }
61 
62 void
63 smb2_lease_rele(smb_lease_t *ls)
64 {
65 	smb_llist_t *bucket;
66 
67 	mutex_enter(&ls->ls_mutex);
68 	ls->ls_refcnt--;
69 	if (ls->ls_refcnt != 0) {
70 		mutex_exit(&ls->ls_mutex);
71 		return;
72 	}
73 	mutex_exit(&ls->ls_mutex);
74 
75 	/*
76 	 * Get the list lock, then re-check the refcnt
77 	 * and if it's still zero, unlink & destroy.
78 	 */
79 	bucket = ls->ls_bucket;
80 	smb_llist_enter(bucket, RW_WRITER);
81 
82 	mutex_enter(&ls->ls_mutex);
83 	if (ls->ls_refcnt == 0)
84 		smb_llist_remove(bucket, ls);
85 	mutex_exit(&ls->ls_mutex);
86 
87 	if (ls->ls_refcnt == 0) {
88 		mutex_destroy(&ls->ls_mutex);
89 		kmem_cache_free(smb_lease_cache, ls);
90 	}
91 
92 	smb_llist_exit(bucket);
93 }
94 
95 /*
96  * Compute a hash from a uuid
97  * Based on mod_hash_bystr()
98  */
99 static uint_t
100 smb_hash_uuid(const uint8_t *uuid)
101 {
102 	char *k = (char *)uuid;
103 	uint_t hash = 0;
104 	uint_t g;
105 	int i;
106 
107 	ASSERT(k);
108 	for (i = 0; i < UUID_LEN; i++) {
109 		hash = (hash << 4) + k[i];
110 		if ((g = (hash & 0xf0000000)) != 0) {
111 			hash ^= (g >> 24);
112 			hash ^= g;
113 		}
114 	}
115 	return (hash);
116 }
117 
118 /*
119  * Add or update a lease table entry for a new ofile.
120  * (in the per-session lease table)
121  * See [MS-SMB2] 3.3.5.9.8
122  * Handling the SMB2_CREATE_REQUEST_LEASE Create Context
123  */
124 uint32_t
125 smb2_lease_create(smb_request_t *sr, uint8_t *clnt)
126 {
127 	smb_arg_open_t *op = &sr->arg.open;
128 	uint8_t *key = op->lease_key;
129 	smb_ofile_t *of = sr->fid_ofile;
130 	smb_hash_t *ht = sr->sr_server->sv_lease_ht;
131 	smb_llist_t *bucket;
132 	smb_lease_t *lease;
133 	smb_lease_t *newlease;
134 	size_t hashkey;
135 	uint32_t status = NT_STATUS_INVALID_PARAMETER;
136 
137 	if (bcmp(key, lease_zero, UUID_LEN) == 0)
138 		return (status);
139 
140 	/*
141 	 * Find or create, and add a ref for the new ofile.
142 	 */
143 	hashkey = smb_hash_uuid(key);
144 	hashkey &= (ht->num_buckets - 1);
145 	bucket = &ht->buckets[hashkey].b_list;
146 
147 	newlease = kmem_cache_alloc(smb_lease_cache, KM_SLEEP);
148 	bzero(newlease, sizeof (smb_lease_t));
149 	mutex_init(&newlease->ls_mutex, NULL, MUTEX_DEFAULT, NULL);
150 	newlease->ls_bucket = bucket;
151 	newlease->ls_node = of->f_node;
152 	newlease->ls_refcnt = 1;
153 	newlease->ls_epoch = op->lease_epoch;
154 	newlease->ls_version = op->lease_version;
155 	bcopy(key, newlease->ls_key, UUID_LEN);
156 	bcopy(clnt, newlease->ls_clnt, UUID_LEN);
157 
158 	smb_llist_enter(bucket, RW_WRITER);
159 	for (lease = smb_llist_head(bucket); lease != NULL;
160 	    lease = smb_llist_next(bucket, lease)) {
161 		/*
162 		 * Looking for this lease ID, on a node
163 		 * that's not being deleted.
164 		 */
165 		if (bcmp(lease->ls_key, key, UUID_LEN) == 0 &&
166 		    bcmp(lease->ls_clnt, clnt, UUID_LEN) == 0 &&
167 		    (lease->ls_node->flags & NODE_FLAGS_DELETING) == 0)
168 			break;
169 	}
170 	if (lease != NULL) {
171 		/*
172 		 * Found existing lease.  Make sure it refers to
173 		 * the same node...
174 		 */
175 		if (lease->ls_node == of->f_node) {
176 			smb2_lease_hold(lease);
177 		} else {
178 			/* Same lease ID, different node! */
179 #ifdef DEBUG
180 			cmn_err(CE_NOTE, "new lease on node %p (%s) "
181 			    "conflicts with existing node %p (%s)",
182 			    (void *) of->f_node,
183 			    of->f_node->od_name,
184 			    (void *) lease->ls_node,
185 			    lease->ls_node->od_name);
186 #endif
187 			DTRACE_PROBE2(dup_lease, smb_request_t, sr,
188 			    smb_lease_t, lease);
189 			lease = NULL; /* error */
190 		}
191 	} else {
192 		lease = newlease;
193 		smb_llist_insert_head(bucket, lease);
194 		newlease = NULL; /* don't free */
195 	}
196 	smb_llist_exit(bucket);
197 
198 	if (newlease != NULL) {
199 		mutex_destroy(&newlease->ls_mutex);
200 		kmem_cache_free(smb_lease_cache, newlease);
201 	}
202 
203 	if (lease != NULL) {
204 		of->f_lease = lease;
205 		status = NT_STATUS_SUCCESS;
206 	}
207 
208 	return (status);
209 }
210 
211 /*
212  * Find the lease for a given: client_uuid, lease_key
213  * Returns the lease with a new ref.
214  */
215 smb_lease_t *
216 smb2_lease_lookup(smb_server_t *sv, uint8_t *clnt_uuid, uint8_t *lease_key)
217 {
218 	smb_hash_t *ht = sv->sv_lease_ht;
219 	smb_llist_t *bucket;
220 	smb_lease_t *lease;
221 	size_t hashkey;
222 
223 	hashkey = smb_hash_uuid(lease_key);
224 	hashkey &= (ht->num_buckets - 1);
225 	bucket = &ht->buckets[hashkey].b_list;
226 
227 	smb_llist_enter(bucket, RW_READER);
228 	lease = smb_llist_head(bucket);
229 	while (lease != NULL) {
230 		if (bcmp(lease->ls_key, lease_key, UUID_LEN) == 0 &&
231 		    bcmp(lease->ls_clnt, clnt_uuid, UUID_LEN) == 0) {
232 			smb2_lease_hold(lease);
233 			break;
234 		}
235 		lease = smb_llist_next(bucket, lease);
236 	}
237 	smb_llist_exit(bucket);
238 
239 	return (lease);
240 }
241 
242 /*
243  * Find an smb_ofile_t in the current tree that shares the
244  * specified lease and has some oplock breaking flags set.
245  * If lease not found, NT_STATUS_OBJECT_NAME_NOT_FOUND.
246  * If ofile not breaking NT_STATUS_UNSUCCESSFUL.
247  * On success, ofile (held) in sr->fid_ofile.
248  */
249 static uint32_t
250 find_breaking_ofile(smb_request_t *sr, uint8_t *lease_key)
251 {
252 	smb_tree_t	*tree = sr->tid_tree;
253 	smb_lease_t	*lease;
254 	smb_llist_t	*of_list;
255 	smb_ofile_t	*o;
256 	uint32_t	status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
257 
258 	SMB_TREE_VALID(tree);
259 	of_list = &tree->t_ofile_list;
260 
261 	smb_llist_enter(of_list, RW_READER);
262 	for (o = smb_llist_head(of_list); o != NULL;
263 	    o = smb_llist_next(of_list, o)) {
264 
265 		ASSERT(o->f_magic == SMB_OFILE_MAGIC);
266 		ASSERT(o->f_tree == tree);
267 
268 		if ((lease = o->f_lease) == NULL)
269 			continue; // no lease
270 
271 		if (bcmp(lease->ls_key, lease_key, UUID_LEN) != 0)
272 			continue; // wrong lease
273 
274 		/*
275 		 * Now we know the lease exists, so if we don't
276 		 * find an ofile with breaking flags, return:
277 		 */
278 		status = NT_STATUS_UNSUCCESSFUL;
279 
280 		if (o->f_oplock.og_breaking == 0)
281 			continue; // not breaking
282 
283 		/* Found breaking ofile. */
284 		if (smb_ofile_hold(o)) {
285 			sr->fid_ofile = o;
286 			status = NT_STATUS_SUCCESS;
287 			break;
288 		}
289 	}
290 	smb_llist_exit(of_list);
291 
292 	return (status);
293 }
294 
295 /*
296  * This is called by smb2_oplock_break_ack when the struct size
297  * indicates this is a lease break (SZ_LEASE).  See:
298  * [MS-SMB2] 3.3.5.22.2 Processing a Lease Acknowledgment
299  */
300 smb_sdrc_t
301 smb2_lease_break_ack(smb_request_t *sr)
302 {
303 	smb_lease_t *lease;
304 	smb_ofile_t *ofile;
305 	uint8_t LeaseKey[UUID_LEN];
306 	uint32_t LeaseState;
307 	uint32_t LeaseBreakTo;
308 	uint32_t status;
309 	int rc = 0;
310 
311 	if (sr->session->dialect < SMB_VERS_2_1)
312 		return (SDRC_ERROR);
313 
314 	/*
315 	 * Decode an SMB2 Lease Acknowldgement
316 	 * [MS-SMB2] 2.2.24.2
317 	 * Note: Struct size decoded by caller.
318 	 */
319 	rc = smb_mbc_decodef(
320 	    &sr->smb_data, "6.#cl8.",
321 	    /* reserved		  6. */
322 	    UUID_LEN,		/* # */
323 	    LeaseKey,		/* c */
324 	    &LeaseState);	/* l */
325 	    /* duration		  8. */
326 	if (rc != 0)
327 		return (SDRC_ERROR);
328 
329 	status = find_breaking_ofile(sr, LeaseKey);
330 
331 	DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr);
332 	if (status != 0)
333 		goto errout;
334 
335 	/* Success, so have sr->fid_ofile and lease */
336 	ofile = sr->fid_ofile;
337 	lease = ofile->f_lease;
338 
339 	/*
340 	 * Process the lease break ack.
341 	 *
342 	 * If the new LeaseState has any bits in excess of
343 	 * the lease state we sent in the break, error...
344 	 */
345 	LeaseBreakTo = (lease->ls_breaking >> BREAK_SHIFT) &
346 	    OPLOCK_LEVEL_CACHE_MASK;
347 	if ((LeaseState & ~LeaseBreakTo) != 0) {
348 		status = NT_STATUS_REQUEST_NOT_ACCEPTED;
349 		goto errout;
350 	}
351 
352 	ofile->f_oplock.og_breaking = 0;
353 	lease->ls_breaking = 0;
354 
355 	LeaseState |= OPLOCK_LEVEL_GRANULAR;
356 	status = smb_oplock_ack_break(sr, ofile, &LeaseState);
357 	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
358 		(void) smb2sr_go_async(sr);
359 		(void) smb_oplock_wait_break(ofile->f_node, 0);
360 		status = NT_STATUS_SUCCESS;
361 	}
362 
363 	ofile->f_oplock.og_state = LeaseState;
364 	lease->ls_state = LeaseState &
365 	    OPLOCK_LEVEL_CACHE_MASK;
366 
367 errout:
368 	sr->smb2_status = status;
369 	DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr);
370 	if (status) {
371 		smb2sr_put_error(sr, status);
372 		return (SDRC_SUCCESS);
373 	}
374 
375 	/*
376 	 * Encode an SMB2 Lease Ack. response
377 	 * [MS-SMB2] 2.2.25.2
378 	 */
379 	LeaseState &= OPLOCK_LEVEL_CACHE_MASK;
380 	(void) smb_mbc_encodef(
381 	    &sr->reply, "w6.#cl8.",
382 	    SSZ_LEASE_ACK,	/* w */
383 	    /* reserved		  6. */
384 	    UUID_LEN,		/* # */
385 	    LeaseKey,		/* c */
386 	    LeaseState);	/* l */
387 	    /* duration		  8. */
388 
389 	return (SDRC_SUCCESS);
390 
391 }
392 
393 /*
394  * Compose an SMB2 Lease Break Notification packet, including
395  * the SMB2 header and everything, in sr->reply.
396  * The caller will send it and free the request.
397  *
398  * [MS-SMB2] 2.2.23.2 Lease Break Notification
399  */
400 void
401 smb2_lease_break_notification(smb_request_t *sr, uint32_t NewLevel,
402     boolean_t AckReq)
403 {
404 	smb_ofile_t *ofile = sr->fid_ofile;
405 	smb_oplock_grant_t *og = &ofile->f_oplock;
406 	smb_lease_t *ls = ofile->f_lease;
407 	uint32_t oldcache;
408 	uint32_t newcache;
409 	uint16_t Epoch;
410 	uint16_t Flags;
411 
412 	/*
413 	 * Convert internal level to SMB2
414 	 */
415 	oldcache = og->og_state & OPLOCK_LEVEL_CACHE_MASK;
416 	newcache = NewLevel & OPLOCK_LEVEL_CACHE_MASK;
417 	if (ls->ls_version < 2)
418 		Epoch = 0;
419 	else
420 		Epoch = ls->ls_epoch;
421 
422 	/*
423 	 * SMB2 Header
424 	 */
425 	sr->smb2_cmd_code = SMB2_OPLOCK_BREAK;
426 	sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR;
427 	sr->smb_tid = 0;
428 	sr->smb_pid = 0;
429 	sr->smb2_ssnid = 0;
430 	sr->smb2_messageid = UINT64_MAX;
431 	(void) smb2_encode_header(sr, B_FALSE);
432 
433 	/*
434 	 * SMB2 Oplock Break, variable part
435 	 *
436 	 * [MS-SMB2] says the current lease state preceeds the
437 	 * new lease state, but that looks like an error...
438 	 */
439 	Flags = AckReq ? SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED : 0;
440 	(void) smb_mbc_encodef(
441 	    &sr->reply, "wwl#cll4.4.4.",
442 	    SSZ_LEASE_BRK,		/* w */
443 	    Epoch,			/* w */
444 	    Flags,			/* l */
445 	    SMB_LEASE_KEY_SZ,		/* # */
446 	    ls->ls_key,			/* c */
447 	    oldcache,		/* cur.st  l */
448 	    newcache);		/* new.st  l */
449 	    /* reserved (4.4.4.) */
450 }
451 
452 /*
453  * Client has an open handle and requests a lease.
454  * Convert SMB2 lease request info in to internal form,
455  * call common oplock code, convert result to SMB2.
456  *
457  * If necessary, "go async" here.
458  */
459 void
460 smb2_lease_acquire(smb_request_t *sr)
461 {
462 	smb_arg_open_t *op = &sr->arg.open;
463 	smb_ofile_t *ofile = sr->fid_ofile;
464 	smb_lease_t *lease = ofile->f_lease;
465 	uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED;
466 	uint32_t have, want; /* lease flags */
467 	boolean_t NewGrant = B_FALSE;
468 
469 	/* Only disk trees get oplocks. */
470 	ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE);
471 
472 	/*
473 	 * Only plain files (for now).
474 	 * Later, test SMB2_CAP_DIRECTORY_LEASING
475 	 */
476 	if (!smb_node_is_file(ofile->f_node)) {
477 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
478 		return;
479 	}
480 
481 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
482 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
483 		return;
484 	}
485 
486 	/*
487 	 * SMB2: Convert to internal form.
488 	 * Caller should have setup the lease.
489 	 */
490 	ASSERT(op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE);
491 	ASSERT(lease != NULL);
492 	if (lease == NULL) {
493 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
494 		return;
495 	}
496 	op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
497 	    (op->lease_state & CACHE_RWH);
498 
499 	/*
500 	 * Tree options may force shared oplocks,
501 	 * in which case we reduce the request.
502 	 */
503 	if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
504 		op->op_oplock_state &= ~WRITE_CACHING;
505 	}
506 
507 	/*
508 	 * Disallow downgrade
509 	 *
510 	 * Note that open with a lease is not allowed to turn off
511 	 * any cache rights.  If the client tries to "downgrade",
512 	 * any bits, just return the existing lease cache bits.
513 	 */
514 	have = lease->ls_state & CACHE_RWH;
515 	want = op->op_oplock_state & CACHE_RWH;
516 	if ((have & ~want) != 0) {
517 		op->op_oplock_state = have |
518 		    OPLOCK_LEVEL_GRANULAR;
519 		goto done;
520 	}
521 
522 	/*
523 	 * Handle oplock requests in three parts:
524 	 *	a: Requests with WRITE_CACHING
525 	 *	b: Requests with HANDLE_CACHING
526 	 *	c: Requests with READ_CACHING
527 	 * reducing the request before b and c.
528 	 *
529 	 * In each: first check if the lease grants the
530 	 * (possibly reduced) request, in which case we
531 	 * leave the lease unchanged and return what's
532 	 * granted by the lease.  Otherwise, try to get
533 	 * the oplock, and if the succeeds, wait for any
534 	 * breaks, update the lease, and return.
535 	 */
536 
537 	/*
538 	 * Try exclusive (request is RW or RWH)
539 	 */
540 	if ((op->op_oplock_state & WRITE_CACHING) != 0) {
541 		want = op->op_oplock_state & CACHE_RWH;
542 		if (have == want)
543 			goto done;
544 
545 		status = smb_oplock_request(sr, ofile,
546 		    &op->op_oplock_state);
547 		if (status == NT_STATUS_SUCCESS ||
548 		    status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
549 			NewGrant = B_TRUE;
550 			goto done;
551 		}
552 
553 		/*
554 		 * We did not get the exclusive oplock.
555 		 *
556 		 * There are odd rules about lease upgrade.
557 		 * If the existing lease grants R and the
558 		 * client fails to upgrade it to "RWH"
559 		 * (presumably due to handle conflicts)
560 		 * then just return the existing lease,
561 		 * even though upgrade to RH would work.
562 		 */
563 		if (have != 0) {
564 			op->op_oplock_state = have |
565 			    OPLOCK_LEVEL_GRANULAR;
566 			goto done;
567 		}
568 
569 		/*
570 		 * Keep trying without write.
571 		 * Need to re-init op_oplock_state
572 		 */
573 		op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
574 		    (op->lease_state & CACHE_RH);
575 	}
576 
577 	/*
578 	 * Try shared ("RH")
579 	 */
580 	if ((op->op_oplock_state & HANDLE_CACHING) != 0) {
581 		want = op->op_oplock_state & CACHE_RWH;
582 		if (have == want)
583 			goto done;
584 
585 		status = smb_oplock_request(sr, ofile,
586 		    &op->op_oplock_state);
587 		if (status == NT_STATUS_SUCCESS ||
588 		    status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
589 			NewGrant = B_TRUE;
590 			goto done;
591 		}
592 
593 		/*
594 		 * We did not get "RH", probably because
595 		 * ther were (old style) Level II oplocks.
596 		 * Continue, try for just read.
597 		 */
598 		op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
599 		    (op->lease_state & CACHE_R);
600 	}
601 
602 	/*
603 	 * Try shared ("R")
604 	 */
605 	if ((op->op_oplock_state & READ_CACHING) != 0) {
606 		want = op->op_oplock_state & CACHE_RWH;
607 		if (have == want)
608 			goto done;
609 
610 		status = smb_oplock_request(sr, ofile,
611 		    &op->op_oplock_state);
612 		if (status == NT_STATUS_SUCCESS ||
613 		    status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
614 			NewGrant = B_TRUE;
615 			goto done;
616 		}
617 
618 		/*
619 		 * We did not get "R".
620 		 * Fall into "none".
621 		 */
622 	}
623 
624 	/*
625 	 * None of the above were able to get an oplock.
626 	 * The lease has no caching rights, and we didn't
627 	 * add any in this request.  Return it as-is.
628 	 */
629 	op->op_oplock_state = OPLOCK_LEVEL_GRANULAR;
630 
631 done:
632 	if (NewGrant) {
633 		/*
634 		 * After a new oplock grant, the status return
635 		 * may indicate we need to wait for breaks.
636 		 */
637 		if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
638 			(void) smb2sr_go_async(sr);
639 			(void) smb_oplock_wait_break(ofile->f_node, 0);
640 			status = NT_STATUS_SUCCESS;
641 		}
642 		ASSERT(status == NT_STATUS_SUCCESS);
643 
644 		/*
645 		 * Keep track of what we got (in ofile->f_oplock.og_state)
646 		 * so we'll know what we had when sending a break later.
647 		 * Also update the lease with the new oplock state.
648 		 * Also track which ofile on the lease owns the oplock.
649 		 * The og_dialect here is the oplock dialect, not the
650 		 * SMB dialect.  Leasing, so SMB 2.1 (or later).
651 		 */
652 		ofile->f_oplock.og_dialect = SMB_VERS_2_1;
653 		ofile->f_oplock.og_state = op->op_oplock_state;
654 		mutex_enter(&lease->ls_mutex);
655 		lease->ls_state = op->op_oplock_state & CACHE_RWH;
656 		lease->ls_epoch++;
657 		mutex_exit(&lease->ls_mutex);
658 	}
659 
660 	/*
661 	 * Convert internal oplock state to SMB2
662 	 */
663 	op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
664 	op->lease_state = lease->ls_state & CACHE_RWH;
665 	op->lease_flags = (lease->ls_breaking != 0) ?
666 	    SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0;
667 	op->lease_epoch = lease->ls_epoch;
668 	op->lease_version = lease->ls_version;
669 }
670 
671 /*
672  * This ofile has a lease and is about to close.
673  * Called by smb_ofile_close when there's a lease.
674  *
675  * With leases, just one ofile on a lease owns the oplock.
676  * If an ofile with a lease is closed and it's the one that
677  * owns the oplock, try to move the oplock to another ofile
678  * on the same lease.
679  */
680 void
681 smb2_lease_ofile_close(smb_ofile_t *ofile)
682 {
683 	smb_node_t *node = ofile->f_node;
684 	smb_lease_t *lease = ofile->f_lease;
685 	smb_ofile_t *o;
686 
687 	ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
688 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
689 
690 	/*
691 	 * If this ofile was not the oplock owner for this lease,
692 	 * we can leave things as they are.
693 	 */
694 	if (lease->ls_oplock_ofile != ofile)
695 		return;
696 
697 	/*
698 	 * Find another ofile to which we can move the oplock.
699 	 * The ofile must be open and allow a new ref.
700 	 */
701 	FOREACH_NODE_OFILE(node, o) {
702 		if (o == ofile)
703 			continue;
704 		if (o->f_lease != lease)
705 			continue;
706 		if (o->f_oplock.og_closing)
707 			continue;
708 		/* If we can get a hold, use this ofile. */
709 		if (smb_ofile_hold(o))
710 			break;
711 	}
712 	if (o == NULL) {
713 		/* Normal for last close on a lease. */
714 		return;
715 	}
716 	smb_oplock_move(node, ofile, o);
717 
718 	smb_ofile_release(o);
719 }
720