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