xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_durable.c (revision 34bbc83afbf22a6f8e504cb99d76c97c017cb5f4)
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 2017-2022 Tintri by DDN, Inc. All rights reserved.
14  * Copyright 2021-2024 RackTop Systems, Inc.
15  */
16 
17 /*
18  * SMB2 Durable Handle support
19  */
20 
21 #include <sys/types.h>
22 #include <sys/cmn_err.h>
23 #include <sys/fcntl.h>
24 #include <sys/nbmlock.h>
25 #include <sys/sid.h>
26 #include <smbsrv/string.h>
27 #include <smbsrv/smb_kproto.h>
28 #include <smbsrv/smb_fsops.h>
29 #include <smbsrv/smbinfo.h>
30 #include <smbsrv/smb2_kproto.h>
31 
32 /* Windows default values from [MS-SMB2] */
33 /*
34  * (times in seconds)
35  * resilient:
36  * MaxTimeout = 300 (win7+)
37  * if timeout > MaxTimeout, ERROR
38  * if timeout != 0, timeout = req.timeout
39  * if timeout == 0, timeout = (infinity) (Win7/w2k8r2)
40  * if timeout == 0, timeout = 120 (Win8+)
41  * v2:
42  * if timeout != 0, timeout = MIN(timeout, 300) (spec)
43  * if timeout != 0, timeout = timeout (win8/2k12)
44  * if timeout == 0, timeout = Share.CATimeout. \
45  *	if Share.CATimeout == 0, timeout = 60 (win8/w2k12)
46  * if timeout == 0, timeout = 180 (win8.1/w2k12r2)
47  * open.timeout = 60 (win8/w2k12r2) (i.e. we ignore the request)
48  * v1:
49  * open.timeout = 16 minutes
50  */
51 
52 uint32_t smb2_dh_def_timeout = 60 * MILLISEC;	/* mSec. */
53 uint32_t smb2_dh_max_timeout = 300 * MILLISEC;	/* mSec. */
54 
55 uint32_t smb2_res_def_timeout = 120 * MILLISEC;	/* mSec. */
56 uint32_t smb2_res_max_timeout = 300 * MILLISEC;	/* mSec. */
57 
58 uint32_t smb2_persist_timeout = 300 * MILLISEC;	/* mSec. */
59 
60 /*
61  * Escape hatch in case persistent handles on directories cause problems.
62  * We don't have directory leases, so there's not really much point in
63  * persistence for directory handles, but Hyper-V wants them.
64  */
65 int smb2_dh_allow_dir = 1;
66 
67 /*
68  * Max. size of the file used to store a CA handle.
69  * Don't adjust this while the server is running.
70  */
71 static uint32_t smb2_dh_max_cah_size = 64 * 1024;
72 static uint32_t smb2_ca_info_version = 1;
73 
74 /*
75  * Want this to have invariant layout on disk, where the
76  * last two uint32_t values are stored as a uint64_t
77  */
78 struct nvlk {
79 	uint64_t lk_start;
80 	uint64_t lk_len;
81 	/* (lk_pid << 32) | lk_type */
82 #ifdef	_BIG_ENDIAN
83 	uint32_t lk_pid, lk_type;
84 #else
85 	uint32_t lk_type, lk_pid;
86 #endif
87 };
88 
89 static void smb2_dh_import_share(void *);
90 static smb_ofile_t *smb2_dh_import_handle(smb_request_t *, smb_node_t *,
91     char *, uint64_t);
92 static int smb2_dh_read_nvlist(smb_request_t *, smb_node_t *,
93     char *, struct nvlist **);
94 static int smb2_dh_import_cred(smb_ofile_t *, char *);
95 
96 #define	DH_SN_SIZE 24	/* size of DH stream name buffers */
97 /*
98  * Build the stream name used to store a CA handle.
99  * i.e. ":0123456789abcdef:$CA"
100  * Note: smb_fsop_create adds the SUNWsmb prefix,
101  * so we compose the name without the prefix.
102  */
103 static inline void
104 smb2_dh_make_stream_name(char *buf, size_t buflen, uint64_t id)
105 {
106 	ASSERT(buflen >= DH_SN_SIZE);
107 	(void) snprintf(buf, buflen,
108 	    ":%016" PRIx64 ":$CA", id);
109 }
110 
111 /*
112  * smb_dh_create_allowed
113  *
114  * Helper for smb2_create to decide whether the open/create call should
115  * allow this handle to become durable.
116  *
117  * For files:
118  * 1. op_oplock_level == SMB2_OPLOCK_LEVEL_BATCH
119  * 2. A lease is requested with handle caching
120  *    - for v1, the lease must not be on a directory
121  * 3. For v2, flags has "persistent" (tree is CA)
122  *    (when tree not CA, turned off persist earlier)
123  *
124  * For directories, we don't do leases, but allow persistent.
125  */
126 boolean_t
127 smb_dh_create_allowed(smb_request_t *sr, smb_ofile_t *of)
128 {
129 	smb_arg_open_t *op = &sr->arg.open;
130 
131 	if (smb2_dh_allow_dir && smb_node_is_dir(of->f_node) &&
132 	    (op->dh_v2_flags & SMB2_DHANDLE_FLAG_PERSISTENT) != 0)
133 		return (B_TRUE);
134 
135 	if (smb_node_is_file(of->f_node) &&
136 	    ((op->dh_v2_flags & SMB2_DHANDLE_FLAG_PERSISTENT) != 0 ||
137 	    (op->op_oplock_level == SMB2_OPLOCK_LEVEL_BATCH) ||
138 	    (op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE &&
139 	    (op->lease_state & OPLOCK_LEVEL_CACHE_HANDLE) != 0)))
140 		return (B_TRUE);
141 
142 	return (B_FALSE);
143 }
144 
145 
146 /*
147  * smb_dh_should_save
148  *
149  * During session tear-down, decide whether to keep a durable handle.
150  *
151  * There are two cases where we save durable handles:
152  * 1. An SMB2 LOGOFF request was received
153  * 2. An unexpected disconnect from the client
154  *    Note: Specifying a PrevSessionID in session setup
155  *    is considered a disconnect (we just haven't learned about it yet)
156  * In every other case, we close durable handles.
157  *
158  * [MS-SMB2] 3.3.5.6 SMB2_LOGOFF
159  * [MS-SMB2] 3.3.7.1 Handling Loss of a Connection
160  *
161  * If any of the following are true, preserve for reconnect:
162  *
163  * - Open.IsResilient is TRUE.
164  *
165  * - Open.OplockLevel == SMB2_OPLOCK_LEVEL_BATCH and
166  *   Open.OplockState == Held, and Open.IsDurable is TRUE.
167  *
168  * - Open.OplockLevel == SMB2_OPLOCK_LEVEL_LEASE,
169  *   Lease.LeaseState SMB2_LEASE_HANDLE_CACHING,
170  *   Open.OplockState == Held, and Open.IsDurable is TRUE.
171  *
172  * - Open.IsPersistent is TRUE.
173  *
174  * We also deal with some special cases for shutdown of the
175  * server, session, user, tree (in that order). Other than
176  * the cases above, shutdown (or forced termination) should
177  * destroy durable handles.
178  */
179 boolean_t
180 smb_dh_should_save(smb_ofile_t *of)
181 {
182 	ASSERT(MUTEX_HELD(&of->f_mutex));
183 	ASSERT(of->dh_vers != SMB2_NOT_DURABLE);
184 
185 	/* SMB service shutting down, destroy DH */
186 	if (of->f_server->sv_state == SMB_SERVER_STATE_STOPPING)
187 		return (B_FALSE);
188 
189 	/*
190 	 * SMB Session (connection) going away (server up).
191 	 * If server initiated disconnect, destroy DH
192 	 * If client initiated disconnect, save all DH.
193 	 */
194 	if (of->f_session->s_state == SMB_SESSION_STATE_TERMINATED)
195 		return (B_FALSE);
196 	if (of->f_session->s_state == SMB_SESSION_STATE_DISCONNECTED)
197 		return (B_TRUE);
198 
199 	/*
200 	 * SMB User logoff, session still "up".
201 	 * Action depends on why/how this logoff happened,
202 	 * determined based on user->preserve_opens
203 	 */
204 	if (of->f_user->u_state == SMB_USER_STATE_LOGGING_OFF) {
205 		switch (of->f_user->preserve_opens) {
206 		case SMB2_DH_PRESERVE_NONE:
207 			/* Server-initiated */
208 			return (B_FALSE);
209 		case SMB2_DH_PRESERVE_SOME:
210 			/* Previous session logoff. */
211 			goto preserve_some;
212 		case SMB2_DH_PRESERVE_ALL:
213 			/* Protocol logoff request */
214 			return (B_TRUE);
215 		}
216 	}
217 
218 	/*
219 	 * SMB tree disconnecting (user still logged on)
220 	 * i.e. when kshare export forces disconnection.
221 	 */
222 	if (of->f_tree->t_state == SMB_TREE_STATE_DISCONNECTING)
223 		return (B_FALSE);
224 
225 preserve_some:
226 	/* preserve_opens == SMB2_DH_PRESERVE_SOME */
227 
228 	switch (of->dh_vers) {
229 		uint32_t ol_state;
230 
231 	case SMB2_RESILIENT:
232 		return (B_TRUE);
233 
234 	case SMB2_DURABLE_V2:
235 		if (of->dh_persist)
236 			return (B_TRUE);
237 		/* FALLTHROUGH */
238 	case SMB2_DURABLE_V1:
239 		/* IS durable (v1 or v2) */
240 		if (of->f_lease != NULL)
241 			ol_state = of->f_lease->ls_state;
242 		else
243 			ol_state = of->f_oplock.og_state;
244 		if ((ol_state & (OPLOCK_LEVEL_BATCH |
245 		    OPLOCK_LEVEL_CACHE_HANDLE)) != 0)
246 			return (B_TRUE);
247 		/* FALLTHROUGH */
248 	case SMB2_NOT_DURABLE:
249 	default:
250 		break;
251 	}
252 
253 	return (B_FALSE);
254 }
255 
256 /*
257  * Is this stream name a CA handle? i.e.
258  * ":0123456789abcdef:$CA"
259  */
260 static boolean_t
261 smb2_dh_match_ca_name(const char *name, uint64_t *idp)
262 {
263 	static const char suffix[] = ":$CA";
264 	u_longlong_t ull;
265 	const char *p = name;
266 	char *p2 = NULL;
267 	int len, rc;
268 
269 	if (*p++ != ':')
270 		return (B_FALSE);
271 
272 	rc = ddi_strtoull(p, &p2, 16, &ull);
273 	if (rc != 0 || p2 != (p + 16))
274 		return (B_FALSE);
275 	p += 16;
276 
277 	len = sizeof (suffix) - 1;
278 	if (strncmp(p, suffix, len) != 0)
279 		return (B_FALSE);
280 	p += len;
281 
282 	if (*p != '\0')
283 		return (B_FALSE);
284 
285 	*idp = (uint64_t)ull;
286 	return (B_TRUE);
287 }
288 
289 /*
290  * smb2_dh_new_ca_share
291  *
292  * Called when a new share has ca=true.  Find or create the CA dir,
293  * and start a thread to import persistent handles.
294  */
295 int
296 smb2_dh_new_ca_share(smb_server_t *sv, smb_kshare_t *shr)
297 {
298 	smb_kshare_t	*shr2;
299 	smb_request_t	*sr;
300 	taskqid_t	tqid;
301 
302 	ASSERT(STYPE_ISDSK(shr->shr_type));
303 
304 	/*
305 	 * Need to lookup the kshare again, to get a hold.
306 	 * Add a function to just get the hold?
307 	 */
308 	shr2 = smb_kshare_lookup(sv, shr->shr_name);
309 	if (shr2 != shr)
310 		return (EINVAL);
311 
312 	sr = smb_request_alloc(sv->sv_session, 0);
313 	if (sr == NULL) {
314 		/* shutting down? */
315 		smb_kshare_release(sv, shr);
316 		return (EINTR);
317 	}
318 	sr->sr_state = SMB_REQ_STATE_SUBMITTED;
319 
320 	/*
321 	 * Mark this share as "busy importing persistent handles"
322 	 * so we can hold off tree connect until that's done.
323 	 * Will clear and wakeup below.
324 	 */
325 	mutex_enter(&shr->shr_mutex);
326 	shr->shr_import_busy = sr;
327 	mutex_exit(&shr->shr_mutex);
328 
329 	/*
330 	 * Start a taskq job to import any CA handles.
331 	 * The hold on the kshare is given to this job,
332 	 * which releases it when it's done.
333 	 */
334 	sr->arg.tcon.si = shr;	/* hold from above */
335 	tqid = taskq_dispatch(sv->sv_worker_pool,
336 	    smb2_dh_import_share, sr, TQ_SLEEP);
337 	VERIFY(tqid != TASKQID_INVALID);
338 
339 	return (0);
340 }
341 
342 int smb2_dh_import_delay = 0;
343 
344 static void
345 smb2_dh_import_share(void *arg)
346 {
347 	smb_request_t	*sr = arg;
348 	smb_kshare_t	*shr = sr->arg.tcon.si;
349 	smb_node_t	*snode;
350 	cred_t		*kcr = zone_kcred();
351 	smb_streaminfo_t *str_info = NULL;
352 	char		*nvl_buf = NULL;
353 	uint64_t	id;
354 	smb_node_t	*str_node;
355 	smb_odir_t	*od = NULL;
356 	smb_ofile_t	*of;
357 	int		rc;
358 	boolean_t	eof;
359 
360 	sr->sr_state = SMB_REQ_STATE_ACTIVE;
361 
362 	if (smb2_dh_import_delay > 0)
363 		delay(SEC_TO_TICK(smb2_dh_import_delay));
364 
365 	/*
366 	 * Borrow the server's "root" user.
367 	 *
368 	 * This takes the place of smb_session_lookup_ssnid()
369 	 * that would happen in smb2_dispatch for a normal SR.
370 	 * As usual, this hold is released in smb_request_free.
371 	 */
372 	sr->uid_user = sr->sr_server->sv_rootuser;
373 	smb_user_hold_internal(sr->uid_user);
374 	sr->user_cr = sr->uid_user->u_cred;
375 
376 	/*
377 	 * Create a temporary tree connect
378 	 */
379 	sr->tid_tree = smb_tree_alloc(sr, shr, shr->shr_root_node,
380 	    ACE_ALL_PERMS, 0);
381 	if (sr->tid_tree == NULL) {
382 		cmn_err(CE_NOTE, "smb2_dh_import_share: "
383 		    "failed connect share <%s>", shr->shr_name);
384 		goto out;
385 	}
386 	snode = sr->tid_tree->t_snode;
387 
388 	/*
389 	 * Get the buffers we'll use to read CA handle data.
390 	 * Also get a buffer for the stream name info.
391 	 */
392 	nvl_buf = kmem_alloc(smb2_dh_max_cah_size, KM_SLEEP);
393 	str_info = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP);
394 
395 	/*
396 	 * Open the ext. attr dir under the share root and
397 	 * import CA handles for this share.
398 	 */
399 	if (smb_odir_openat(sr, snode, &od, B_FALSE) != 0) {
400 		cmn_err(CE_NOTE, "!Share [%s] CA import, no xattr dir?",
401 		    shr->shr_name);
402 		goto out;
403 	}
404 
405 	eof = B_FALSE;
406 	do {
407 		/*
408 		 * If the kshare gets unshared before we finish,
409 		 * bail out so we don't hold things up.
410 		 */
411 		if (shr->shr_flags & SMB_SHRF_REMOVED)
412 			break;
413 
414 		/*
415 		 * If the server's stopping, no point importing.
416 		 */
417 		if (smb_server_is_stopping(sr->sr_server))
418 			break;
419 
420 		/*
421 		 * Read a stream name and info
422 		 */
423 		rc = smb_odir_read_streaminfo(sr, od, str_info, &eof);
424 		if ((rc != 0) || (eof))
425 			break;
426 
427 		/*
428 		 * Skip anything not a CA handle.
429 		 */
430 		if (!smb2_dh_match_ca_name(str_info->si_name, &id)) {
431 			continue;
432 		}
433 
434 		/*
435 		 * Lookup stream node and import
436 		 */
437 		str_node = NULL;
438 		rc = smb_fsop_lookup_name(sr, kcr, SMB_CASE_SENSITIVE,
439 		    snode, snode, str_info->si_name, &str_node);
440 		if (rc != 0) {
441 			cmn_err(CE_NOTE, "Share [%s] CA import, "
442 			    "lookup <%s> failed rc=%d",
443 			    shr->shr_name, str_info->si_name, rc);
444 			continue;
445 		}
446 		of = smb2_dh_import_handle(sr, str_node, nvl_buf, id);
447 		smb_node_release(str_node);
448 		if (of != NULL) {
449 			smb_ofile_release(of);
450 			of = NULL;
451 		}
452 		sr->fid_ofile = NULL;
453 		smb_lavl_flush(&sr->tid_tree->t_ofile_list);
454 
455 	} while (!eof);
456 
457 out:
458 	if (od != NULL) {
459 		smb_odir_close(od);
460 		smb_odir_release(od);
461 	}
462 
463 	if (str_info != NULL)
464 		kmem_free(str_info, sizeof (smb_streaminfo_t));
465 	if (nvl_buf != NULL)
466 		kmem_free(nvl_buf, smb2_dh_max_cah_size);
467 
468 	/*
469 	 * We did a (temporary, internal) tree connect above,
470 	 * which we need to undo before we return.  Note that
471 	 * smb_request_free will do the final release of
472 	 * sr->tid_tree, sr->uid_user
473 	 */
474 	if (sr->tid_tree != NULL)
475 		smb_tree_disconnect(sr->tid_tree, B_FALSE);
476 
477 	/*
478 	 * Wake up any waiting tree connect(s).
479 	 * See smb_tree_connect_disk().
480 	 */
481 	mutex_enter(&shr->shr_mutex);
482 	shr->shr_import_busy = NULL;
483 	cv_broadcast(&shr->shr_cv);
484 	mutex_exit(&shr->shr_mutex);
485 
486 	smb_kshare_release(sr->sr_server, shr);
487 	smb_request_free(sr);
488 }
489 
490 /*
491  * This returns the new ofile mostly for dtrace.
492  */
493 static smb_ofile_t *
494 smb2_dh_import_handle(smb_request_t *sr, smb_node_t *str_node,
495     char *nvl_buf, uint64_t persist_id)
496 {
497 	uint8_t		client_uuid[UUID_LEN];
498 	smb_tree_t	*tree = sr->tid_tree;
499 	smb_arg_open_t	*op = &sr->arg.open;
500 	smb_pathname_t	*pn = &op->fqi.fq_path;
501 	cred_t		*kcr = zone_kcred();
502 	struct nvlist	*nvl = NULL;
503 	char		*sidstr = NULL;
504 	smb_ofile_t	*of = NULL;
505 	smb_attr_t	*pa;
506 	boolean_t	did_open = B_FALSE;
507 	boolean_t	have_lease = B_FALSE;
508 	hrtime_t	hrt;
509 	uint64_t	*u64p;
510 	uint64_t	u64;
511 	uint32_t	u32;
512 	uint32_t	status;
513 	char		*s;
514 	uint8_t		*u8p;
515 	uint_t		alen;
516 	int		rc;
517 
518 	/*
519 	 * While we're called with arg.tcon, we now want to use
520 	 * smb_arg_open for the rest of import, so clear it.
521 	 */
522 	bzero(op, sizeof (*op));
523 	op->create_disposition = FILE_OPEN;
524 
525 	/*
526 	 * Read and unpack the NVL
527 	 */
528 	rc = smb2_dh_read_nvlist(sr, str_node, nvl_buf, &nvl);
529 	if (rc != 0)
530 		return (NULL);
531 
532 	/*
533 	 * Known CA info version?
534 	 */
535 	u32 = 0;
536 	rc = nvlist_lookup_uint32(nvl, "info_version", &u32);
537 	if (rc != 0 || u32 != smb2_ca_info_version) {
538 		cmn_err(CE_NOTE, "CA import (%s/%s) bad vers=%d",
539 		    tree->t_resource, str_node->od_name, u32);
540 		goto errout;
541 	}
542 
543 	/*
544 	 * The persist ID in the nvlist should match the one
545 	 * encoded in the file name. (not enforced)
546 	 */
547 	u64 = 0;
548 	rc = nvlist_lookup_uint64(nvl, "file_persistid", &u64);
549 	if (rc != 0 || u64 != persist_id) {
550 		cmn_err(CE_WARN, "CA import (%s/%s) bad id=%016" PRIx64,
551 		    tree->t_resource, str_node->od_name, u64);
552 		/* goto errout? (allow) */
553 	}
554 
555 	/*
556 	 * Does it belong in the share being imported?
557 	 */
558 	s = NULL;
559 	rc = nvlist_lookup_string(nvl, "share_name", &s);
560 	if (rc != 0) {
561 		cmn_err(CE_NOTE, "CA import (%s/%s) no share_name",
562 		    tree->t_resource, str_node->od_name);
563 		goto errout;
564 	}
565 	if (smb_strcasecmp(s, tree->t_sharename, 0) != 0) {
566 		/* Normal (not an error) */
567 #ifdef DEBUG
568 		cmn_err(CE_NOTE, "CA import (%s/%s) other share",
569 		    tree->t_resource, str_node->od_name);
570 #endif
571 		goto errout;
572 	}
573 
574 	/*
575 	 * Get the path name (for lookup)
576 	 */
577 	rc = nvlist_lookup_string(nvl, "path_name", &pn->pn_path);
578 	if (rc != 0) {
579 		cmn_err(CE_NOTE, "CA import (%s/%s) no path_name",
580 		    tree->t_resource, str_node->od_name);
581 		goto errout;
582 	}
583 
584 	/*
585 	 * owner sid
586 	 */
587 	rc = nvlist_lookup_string(nvl, "owner_sid", &sidstr);
588 	if (rc != 0) {
589 		cmn_err(CE_NOTE, "CA import (%s/%s) no owner_sid",
590 		    tree->t_resource, str_node->od_name);
591 		goto errout;
592 	}
593 
594 	/*
595 	 * granted access
596 	 */
597 	rc = nvlist_lookup_uint32(nvl,
598 	    "granted_access", &op->desired_access);
599 	if (rc != 0) {
600 		cmn_err(CE_NOTE, "CA import (%s/%s) no granted_access",
601 		    tree->t_resource, str_node->od_name);
602 		goto errout;
603 	}
604 
605 	/*
606 	 * share access
607 	 */
608 	rc = nvlist_lookup_uint32(nvl,
609 	    "share_access", &op->share_access);
610 	if (rc != 0) {
611 		cmn_err(CE_NOTE, "CA import (%s/%s) no share_access",
612 		    tree->t_resource, str_node->od_name);
613 		goto errout;
614 	}
615 
616 	/*
617 	 * create options
618 	 */
619 	rc = nvlist_lookup_uint32(nvl,
620 	    "create_options", &op->create_options);
621 	if (rc != 0) {
622 		cmn_err(CE_NOTE, "CA import (%s/%s) no create_options",
623 		    tree->t_resource, str_node->od_name);
624 		goto errout;
625 	}
626 
627 	/*
628 	 * create guid (client-assigned)
629 	 */
630 	alen = UUID_LEN;
631 	u8p = NULL;
632 	rc = nvlist_lookup_uint8_array(nvl, "file_guid", &u8p, &alen);
633 	if (rc != 0 || alen != UUID_LEN) {
634 		cmn_err(CE_NOTE, "CA import (%s/%s) bad file_guid",
635 		    tree->t_resource, str_node->od_name);
636 		goto errout;
637 	}
638 	bcopy(u8p, op->create_guid, UUID_LEN);
639 
640 	/*
641 	 * client uuid (identifies the client)
642 	 */
643 	alen = UUID_LEN;
644 	u8p = NULL;
645 	rc = nvlist_lookup_uint8_array(nvl, "client_uuid", &u8p, &alen);
646 	if (rc != 0 || alen != UUID_LEN) {
647 		cmn_err(CE_NOTE, "CA import (%s/%s) no client_uuid",
648 		    tree->t_resource, str_node->od_name);
649 		goto errout;
650 	}
651 	bcopy(u8p, client_uuid, UUID_LEN);
652 
653 	/*
654 	 * Lease key (optional)
655 	 */
656 	alen = SMB_LEASE_KEY_SZ;
657 	u8p = NULL;
658 	rc = nvlist_lookup_uint8_array(nvl, "lease_uuid", &u8p, &alen);
659 	if (rc == 0) {
660 		bcopy(u8p, op->lease_key, UUID_LEN);
661 		(void) nvlist_lookup_uint32(nvl,
662 		    "lease_state", &op->lease_state);
663 		(void) nvlist_lookup_uint16(nvl,
664 		    "lease_epoch", &op->lease_epoch);
665 		(void) nvlist_lookup_uint16(nvl,
666 		    "lease_version", &op->lease_version);
667 		have_lease = B_TRUE;
668 	} else {
669 		(void) nvlist_lookup_uint32(nvl,
670 		    "oplock_state", &op->op_oplock_state);
671 	}
672 
673 	/*
674 	 * Done getting what we need from the NV list.
675 	 * (re)open the file
676 	 */
677 	status = smb_common_open(sr);
678 	if (status != 0) {
679 		cmn_err(CE_NOTE, "CA import (%s/%s) open failed 0x%x",
680 		    tree->t_resource, str_node->od_name, status);
681 		(void) smb_node_set_delete_on_close(str_node, kcr, 0);
682 		goto errout;
683 	}
684 	of = sr->fid_ofile;
685 	did_open = B_TRUE;
686 
687 	/*
688 	 * Now restore the rest of the SMB2 level state.
689 	 * See smb2_create after smb_common_open
690 	 */
691 
692 	/*
693 	 * Setup of->f_cr with owner SID
694 	 */
695 	rc = smb2_dh_import_cred(of, sidstr);
696 	if (rc != 0) {
697 		cmn_err(CE_NOTE, "CA import (%s/%s) import cred failed",
698 		    tree->t_resource, str_node->od_name);
699 		goto errout;
700 	}
701 
702 	/*
703 	 * Use the persist ID we previously assigned.
704 	 * Like smb_ofile_set_persistid_ph()
705 	 */
706 	rc = smb_ofile_insert_persistid(of, persist_id);
707 	if (rc != 0) {
708 		cmn_err(CE_NOTE, "CA import (%s/%s) "
709 		    "insert_persistid rc=%d",
710 		    tree->t_resource, str_node->od_name, rc);
711 		goto errout;
712 	}
713 
714 	/*
715 	 * Like smb2_lease_create()
716 	 *
717 	 * Lease state is stored in each persistent handle, but
718 	 * only one handle has the state we want.  As we import
719 	 * each handle, "upgrade" the lease if the handle we're
720 	 * importing has a "better" lease state (higher epoch or
721 	 * more cache rights).  After all handles are imported,
722 	 * that will get the lease to the right state.
723 	 */
724 	if (have_lease) {
725 		smb_lease_t *ls;
726 		status = smb2_lease_create(sr, client_uuid);
727 		if (status != 0) {
728 			cmn_err(CE_NOTE, "CA import (%s/%s) get lease 0x%x",
729 			    tree->t_resource, str_node->od_name, status);
730 			goto errout;
731 		}
732 		ls = of->f_lease;
733 
734 		/* Use most current "epoch". */
735 		mutex_enter(&ls->ls_mutex);
736 		if (ls->ls_epoch < op->lease_epoch)
737 			ls->ls_epoch = op->lease_epoch;
738 		mutex_exit(&ls->ls_mutex);
739 
740 		/*
741 		 * Get the lease (and oplock)
742 		 * uses op->lease_state
743 		 */
744 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
745 		smb2_lease_acquire(sr);
746 
747 	} else {
748 		/*
749 		 * No lease; maybe get an oplock
750 		 * uses: op->op_oplock_level
751 		 */
752 		if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
753 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
754 		} else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
755 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
756 		} else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
757 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
758 		} else {
759 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
760 		}
761 		smb2_oplock_acquire(sr);
762 	}
763 
764 	/*
765 	 * Byte range locks
766 	 */
767 	alen = 0;
768 	u64p = NULL;
769 	if (nvlist_lookup_uint64_array(nvl, "locks", &u64p, &alen) == 0) {
770 		uint_t	i;
771 		uint_t nlocks = alen / 3;
772 		struct nvlk	*nlp;
773 
774 		nlp = (struct nvlk *)u64p;
775 		for (i = 0; i < nlocks; i++) {
776 			status = smb_lock_range(
777 			    sr,
778 			    nlp->lk_start,
779 			    nlp->lk_len,
780 			    nlp->lk_pid,
781 			    nlp->lk_type,
782 			    0);
783 			if (status != 0) {
784 				cmn_err(CE_NOTE, "CA import (%s/%s) "
785 				    "get lock %d failed 0x%x",
786 				    tree->t_resource,
787 				    str_node->od_name,
788 				    i, status);
789 			}
790 			nlp++;
791 		}
792 	}
793 	alen = SMB_OFILE_LSEQ_MAX;
794 	u8p = NULL;
795 	if (nvlist_lookup_uint8_array(nvl, "lockseq", &u8p, &alen) == 0) {
796 		if (alen != SMB_OFILE_LSEQ_MAX) {
797 			cmn_err(CE_NOTE, "CA import (%s/%s) "
798 			    "get lockseq bad len=%d",
799 			    tree->t_resource,
800 			    str_node->od_name,
801 			    alen);
802 		} else {
803 			mutex_enter(&of->f_mutex);
804 			bcopy(u8p, of->f_lock_seq, alen);
805 			mutex_exit(&of->f_mutex);
806 		}
807 	}
808 
809 	/*
810 	 * Optional "sticky" times (set pending attributes)
811 	 */
812 	mutex_enter(&of->f_mutex);
813 	pa = &of->f_pending_attr;
814 	if (nvlist_lookup_hrtime(nvl, "atime", &hrt) == 0) {
815 		hrt2ts(hrt, &pa->sa_vattr.va_atime);
816 		pa->sa_mask |= SMB_AT_ATIME;
817 	}
818 	if (nvlist_lookup_hrtime(nvl, "mtime", &hrt) == 0) {
819 		hrt2ts(hrt, &pa->sa_vattr.va_mtime);
820 		pa->sa_mask |= SMB_AT_MTIME;
821 	}
822 	if (nvlist_lookup_hrtime(nvl, "ctime", &hrt) == 0) {
823 		hrt2ts(hrt, &pa->sa_vattr.va_ctime);
824 		pa->sa_mask |= SMB_AT_CTIME;
825 	}
826 	mutex_exit(&of->f_mutex);
827 
828 	/*
829 	 * Make durable and persistent.
830 	 * See smb2_dh_make_persistent()
831 	 */
832 	of->dh_vers = SMB2_DURABLE_V2;
833 	bcopy(op->create_guid, of->dh_create_guid, UUID_LEN);
834 	of->dh_persist = B_TRUE;
835 	of->dh_nvfile = str_node;
836 	smb_node_ref(str_node);
837 	of->dh_nvlist = nvl;
838 	nvl = NULL;
839 
840 	/*
841 	 * Now make it state orphaned...
842 	 * See smb_ofile_drop(), then
843 	 * smb_ofile_save_dh()
844 	 */
845 	mutex_enter(&of->f_mutex);
846 	of->f_state = SMB_OFILE_STATE_SAVE_DH;
847 	of->dh_timeout_offset = MSEC2NSEC(smb2_persist_timeout);
848 	mutex_exit(&of->f_mutex);
849 
850 	/*
851 	 * Finished!
852 	 */
853 	return (of);
854 
855 errout:
856 	if (did_open) {
857 		smb_ofile_close(of, 0);
858 		smb_ofile_release(of);
859 	} else {
860 		ASSERT(of == NULL);
861 	}
862 
863 	if (nvl != NULL)
864 		nvlist_free(nvl);
865 
866 	return (NULL);
867 }
868 
869 static int
870 smb2_dh_read_nvlist(smb_request_t *sr, smb_node_t *node,
871     char *fbuf, struct nvlist **nvlpp)
872 {
873 	smb_attr_t	attr;
874 	iovec_t		iov;
875 	uio_t		uio;
876 	smb_tree_t	*tree = sr->tid_tree;
877 	cred_t		*kcr = zone_kcred();
878 	size_t		flen;
879 	int		rc;
880 
881 	bzero(&attr, sizeof (attr));
882 	attr.sa_mask = SMB_AT_SIZE;
883 	rc = smb_node_getattr(NULL, node, kcr, NULL, &attr);
884 	if (rc != 0) {
885 		cmn_err(CE_NOTE, "CA import (%s/%s) getattr rc=%d",
886 		    tree->t_resource, node->od_name, rc);
887 		return (rc);
888 	}
889 
890 	if (attr.sa_vattr.va_size < 4 ||
891 	    attr.sa_vattr.va_size > smb2_dh_max_cah_size) {
892 		cmn_err(CE_NOTE, "CA import (%s/%s) bad size=%" PRIu64,
893 		    tree->t_resource, node->od_name,
894 		    (uint64_t)attr.sa_vattr.va_size);
895 		return (EINVAL);
896 	}
897 	flen = (size_t)attr.sa_vattr.va_size;
898 
899 	bzero(&uio, sizeof (uio));
900 	iov.iov_base = fbuf;
901 	iov.iov_len = flen;
902 	uio.uio_iov = &iov;
903 	uio.uio_iovcnt = 1;
904 	uio.uio_resid = flen;
905 	uio.uio_segflg = UIO_SYSSPACE;
906 	uio.uio_extflg = UIO_COPY_DEFAULT;
907 	rc = smb_fsop_read(sr, kcr, node, NULL, &uio, 0);
908 	if (rc != 0) {
909 		cmn_err(CE_NOTE, "CA import (%s/%s) read, rc=%d",
910 		    tree->t_resource, node->od_name, rc);
911 		return (rc);
912 	}
913 	if (uio.uio_resid != 0) {
914 		cmn_err(CE_NOTE, "CA import (%s/%s) short read",
915 		    tree->t_resource, node->od_name);
916 		return (EIO);
917 	}
918 
919 	rc = nvlist_unpack(fbuf, flen, nvlpp, KM_SLEEP);
920 	if (rc != 0) {
921 		cmn_err(CE_NOTE, "CA import (%s/%s) unpack, rc=%d",
922 		    tree->t_resource, node->od_name, rc);
923 		return (rc);
924 	}
925 
926 	return (0);
927 }
928 
929 /*
930  * Setup a vestigial credential in of->f_cr just good enough for
931  * smb_is_same_user to determine if the caller owned this ofile.
932  * At reconnect, of->f_cr will be replaced with the caller's.
933  */
934 static int
935 smb2_dh_import_cred(smb_ofile_t *of, char *sidstr)
936 {
937 #ifdef	_FAKE_KERNEL
938 	_NOTE(ARGUNUSED(sidstr))
939 	/* fksmbd doesn't have real credentials. */
940 	of->f_cr = CRED();
941 	crhold(of->f_cr);
942 #else
943 	char tmpstr[SMB_SID_STRSZ];
944 	ksid_t		ksid;
945 	cred_t		*cr, *oldcr;
946 	int		rc;
947 
948 	(void) strlcpy(tmpstr, sidstr, sizeof (tmpstr));
949 	bzero(&ksid, sizeof (ksid));
950 
951 	rc = smb_sid_splitstr(tmpstr, &ksid.ks_rid);
952 	if (rc != 0)
953 		return (rc);
954 	cr = crget();
955 
956 	ksid.ks_domain = ksid_lookupdomain(tmpstr);
957 	crsetsid(cr, &ksid, KSID_USER);
958 	ksiddomain_hold(ksid.ks_domain);
959 	crsetsid(cr, &ksid, KSID_OWNER);
960 
961 	/*
962 	 * Just to avoid leaving the KSID_GROUP slot NULL,
963 	 * put the "everyone" SID there (S-1-1-0).
964 	 */
965 	ksid.ks_domain = ksid_lookupdomain("S-1-1");
966 	ksid.ks_rid = 0;
967 	crsetsid(cr, &ksid, KSID_GROUP);
968 
969 	oldcr = of->f_cr;
970 	of->f_cr = cr;
971 	if (oldcr != NULL)
972 		crfree(oldcr);
973 #endif
974 
975 	return (0);
976 }
977 
978 /*
979  * Set Delete-on-Close (DoC) on the persistent state file so it will be
980  * removed when the last ref. goes away (in smb2_dh_close_persistent).
981  *
982  * This is called in just two places:
983  * (1) SMB2_close request -- client tells us to destroy the handle.
984  * (2) smb2_dh_expire -- client has forgotten about this handle.
985  * All other (server-initiated) close calls should leave these
986  * persistent state files in the file system.
987  */
988 void
989 smb2_dh_setdoc_persistent(smb_ofile_t *of)
990 {
991 	smb_node_t *strnode;
992 	uint32_t status;
993 
994 	mutex_enter(&of->dh_nvlock);
995 	if ((strnode = of->dh_nvfile) != NULL)
996 		smb_node_ref(strnode);
997 	mutex_exit(&of->dh_nvlock);
998 
999 	if (strnode != NULL) {
1000 		status = smb_node_set_delete_on_close(strnode,
1001 		    zone_kcred(), SMB_CASE_SENSITIVE);
1002 		if (status != 0) {
1003 			cmn_err(CE_WARN, "Can't set DoC on CA file: %s",
1004 			    strnode->od_name);
1005 			DTRACE_PROBE1(rm__ca__err, smb_ofile_t *, of);
1006 		}
1007 		smb_node_release(strnode);
1008 	}
1009 }
1010 
1011 /*
1012  * During ofile close, free the persistent handle state nvlist and
1013  * drop our reference to the state file node (which may unlink it
1014  * if smb2_dh_setdoc_persistent was called).
1015  */
1016 void
1017 smb2_dh_close_persistent(smb_ofile_t *of)
1018 {
1019 	smb_node_t	*strnode;
1020 	struct nvlist	*nvl;
1021 
1022 	/*
1023 	 * Clear out nvlist and stream linkage
1024 	 */
1025 	mutex_enter(&of->dh_nvlock);
1026 	strnode = of->dh_nvfile;
1027 	of->dh_nvfile = NULL;
1028 	nvl = of->dh_nvlist;
1029 	of->dh_nvlist = NULL;
1030 	mutex_exit(&of->dh_nvlock);
1031 
1032 	if (nvl != NULL)
1033 		nvlist_free(nvl);
1034 
1035 	if (strnode != NULL)
1036 		smb_node_release(strnode);
1037 }
1038 
1039 /*
1040  * Make this durable handle persistent.
1041  * If we succeed, set of->dh_persist = TRUE.
1042  */
1043 int
1044 smb2_dh_make_persistent(smb_request_t *sr, smb_ofile_t *of)
1045 {
1046 	char		fname[DH_SN_SIZE];
1047 	char		sidstr[SMB_SID_STRSZ];
1048 	smb_attr_t	attr;
1049 	smb_arg_open_t	*op = &sr->arg.open;
1050 	cred_t		*kcr = zone_kcred();
1051 	smb_node_t	*dnode = of->f_tree->t_snode;
1052 	smb_node_t	*fnode = NULL;
1053 	ksid_t		*ksid;
1054 	int		rc;
1055 
1056 	ASSERT(of->dh_nvfile == NULL);
1057 
1058 	/*
1059 	 * Create the persistent handle nvlist file.
1060 	 * It's a named stream in the share root.
1061 	 */
1062 	smb2_dh_make_stream_name(fname, sizeof (fname), of->f_persistid);
1063 
1064 	bzero(&attr, sizeof (attr));
1065 	attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE | SMB_AT_SIZE;
1066 	attr.sa_vattr.va_type = VREG;
1067 	attr.sa_vattr.va_mode = 0640;
1068 	attr.sa_vattr.va_size = 4;
1069 	rc = smb_fsop_create(sr, kcr, dnode, fname, &attr, &fnode);
1070 	if (rc != 0)
1071 		return (rc);
1072 
1073 	mutex_enter(&of->dh_nvlock);
1074 
1075 	/* fnode is held. rele in smb2_dh_close_persistent */
1076 	of->dh_nvfile = fnode;
1077 	(void) nvlist_alloc(&of->dh_nvlist, NV_UNIQUE_NAME, KM_SLEEP);
1078 
1079 	/*
1080 	 * Want the ksid as a string
1081 	 */
1082 	ksid = crgetsid(of->f_user->u_cred, KSID_USER);
1083 	(void) snprintf(sidstr, sizeof (sidstr), "%s-%u",
1084 	    ksid->ks_domain->kd_name, ksid->ks_rid);
1085 
1086 	/*
1087 	 * Fill in the fixed parts of the nvlist
1088 	 */
1089 	(void) nvlist_add_uint32(of->dh_nvlist,
1090 	    "info_version", smb2_ca_info_version);
1091 	(void) nvlist_add_string(of->dh_nvlist,
1092 	    "owner_sid", sidstr);
1093 	(void) nvlist_add_string(of->dh_nvlist,
1094 	    "share_name", of->f_tree->t_sharename);
1095 	(void) nvlist_add_uint64(of->dh_nvlist,
1096 	    "file_persistid", of->f_persistid);
1097 	(void) nvlist_add_uint8_array(of->dh_nvlist,
1098 	    "file_guid", of->dh_create_guid, UUID_LEN);
1099 	(void) nvlist_add_string(of->dh_nvlist,
1100 	    "client_ipaddr", sr->session->ip_addr_str);
1101 	(void) nvlist_add_uint8_array(of->dh_nvlist,
1102 	    "client_uuid", sr->session->clnt_uuid, UUID_LEN);
1103 	(void) nvlist_add_string(of->dh_nvlist,
1104 	    "path_name", op->fqi.fq_path.pn_path);
1105 	(void) nvlist_add_uint32(of->dh_nvlist,
1106 	    "granted_access", of->f_granted_access);
1107 	(void) nvlist_add_uint32(of->dh_nvlist,
1108 	    "share_access", of->f_share_access);
1109 	(void) nvlist_add_uint32(of->dh_nvlist,
1110 	    "create_options", of->f_create_options);
1111 	if (of->f_lease != NULL) {
1112 		smb_lease_t *ls = of->f_lease;
1113 		(void) nvlist_add_uint8_array(of->dh_nvlist,
1114 		    "lease_uuid", ls->ls_key, 16);
1115 		(void) nvlist_add_uint32(of->dh_nvlist,
1116 		    "lease_state", ls->ls_state);
1117 		(void) nvlist_add_uint16(of->dh_nvlist,
1118 		    "lease_epoch", ls->ls_epoch);
1119 		(void) nvlist_add_uint16(of->dh_nvlist,
1120 		    "lease_version", ls->ls_version);
1121 	} else {
1122 		(void) nvlist_add_uint32(of->dh_nvlist,
1123 		    "oplock_state", of->f_oplock.og_state);
1124 	}
1125 	mutex_exit(&of->dh_nvlock);
1126 
1127 	smb2_dh_update_locks(sr, of);
1128 
1129 	/* Tell sr update nvlist file */
1130 	sr->dh_nvl_dirty = B_TRUE;
1131 
1132 	return (0);
1133 }
1134 
1135 void
1136 smb2_dh_update_nvfile(smb_request_t *sr)
1137 {
1138 	smb_attr_t	attr;
1139 	iovec_t		iov;
1140 	uio_t		uio;
1141 	smb_ofile_t	*of = sr->fid_ofile;
1142 	cred_t		*kcr = zone_kcred();
1143 	char		*buf = NULL;
1144 	size_t		buflen = 0;
1145 	uint32_t	wcnt;
1146 	int		rc;
1147 
1148 	if (of == NULL || of->dh_persist == B_FALSE)
1149 		return;
1150 
1151 	mutex_enter(&of->dh_nvlock);
1152 	if (of->dh_nvlist == NULL || of->dh_nvfile == NULL) {
1153 		mutex_exit(&of->dh_nvlock);
1154 		return;
1155 	}
1156 
1157 	rc = nvlist_size(of->dh_nvlist, &buflen, NV_ENCODE_XDR);
1158 	if (rc != 0)
1159 		goto out;
1160 	buf = kmem_zalloc(buflen, KM_SLEEP);
1161 
1162 	rc = nvlist_pack(of->dh_nvlist, &buf, &buflen,
1163 	    NV_ENCODE_XDR, KM_SLEEP);
1164 	if (rc != 0)
1165 		goto out;
1166 
1167 	bzero(&attr, sizeof (attr));
1168 	attr.sa_mask = SMB_AT_SIZE;
1169 	attr.sa_vattr.va_size = buflen;
1170 	rc = smb_node_setattr(sr, of->dh_nvfile, kcr, NULL, &attr);
1171 	if (rc != 0)
1172 		goto out;
1173 
1174 	bzero(&uio, sizeof (uio));
1175 	iov.iov_base = (void *) buf;
1176 	iov.iov_len = buflen;
1177 	uio.uio_iov = &iov;
1178 	uio.uio_iovcnt = 1;
1179 	uio.uio_resid = buflen;
1180 	uio.uio_segflg = UIO_SYSSPACE;
1181 	uio.uio_extflg = UIO_COPY_DEFAULT;
1182 	rc = smb_fsop_write(sr, kcr, of->dh_nvfile,
1183 	    NULL, &uio, &wcnt, 0);
1184 	if (rc == 0 && wcnt != buflen)
1185 		rc = EIO;
1186 
1187 out:
1188 	mutex_exit(&of->dh_nvlock);
1189 
1190 	if (rc != 0) {
1191 		cmn_err(CE_WARN,
1192 		    "clnt(%s) failed to update persistent handle, rc=%d",
1193 		    sr->session->ip_addr_str, rc);
1194 	}
1195 
1196 	if (buf != NULL) {
1197 		kmem_free(buf, buflen);
1198 	}
1199 }
1200 
1201 /*
1202  * Called after f_oplock (and lease) changes
1203  * If lease, update: lease_state, lease_epoch
1204  * else (oplock) update: oplock_state
1205  */
1206 void
1207 smb2_dh_update_oplock(smb_request_t *sr, smb_ofile_t *of)
1208 {
1209 	smb_lease_t *ls;
1210 
1211 	mutex_enter(&of->dh_nvlock);
1212 	if (of->dh_nvlist == NULL) {
1213 		mutex_exit(&of->dh_nvlock);
1214 		return;
1215 	}
1216 
1217 	if (of->f_lease != NULL) {
1218 		ls = of->f_lease;
1219 		(void) nvlist_add_uint32(of->dh_nvlist,
1220 		    "lease_state", ls->ls_state);
1221 		(void) nvlist_add_uint16(of->dh_nvlist,
1222 		    "lease_epoch", ls->ls_epoch);
1223 	} else {
1224 		(void) nvlist_add_uint32(of->dh_nvlist,
1225 		    "oplock_state", of->f_oplock.og_state);
1226 	}
1227 	mutex_exit(&of->dh_nvlock);
1228 
1229 	sr->dh_nvl_dirty = B_TRUE;
1230 }
1231 
1232 /*
1233  * Save locks from this ofile as an array of uint64_t, where the
1234  * elements are triplets: (start, length, (pid << 32) | type)
1235  * Note pid should always be zero for SMB2, so we could use
1236  * that 32-bit spot for something else if needed.
1237  */
1238 void
1239 smb2_dh_update_locks(smb_request_t *sr, smb_ofile_t *of)
1240 {
1241 	uint8_t		lseq[SMB_OFILE_LSEQ_MAX];
1242 	smb_node_t	*node = of->f_node;
1243 	smb_llist_t	*llist = &node->n_lock_list;
1244 	size_t		vec_sz;	// storage size
1245 	uint_t		my_cnt = 0;
1246 	uint64_t	*vec = NULL;
1247 	struct nvlk	*nlp;
1248 	smb_lock_t	*lock;
1249 
1250 	smb_llist_enter(llist, RW_READER);
1251 	vec_sz = (llist->ll_count + 1) * sizeof (struct nvlk);
1252 	vec = kmem_alloc(vec_sz, KM_SLEEP);
1253 	nlp = (struct nvlk *)vec;
1254 	for (lock = smb_llist_head(llist);
1255 	    lock != NULL;
1256 	    lock = smb_llist_next(llist, lock)) {
1257 		if (lock->l_file != of)
1258 			continue;
1259 		nlp->lk_start = lock->l_start;
1260 		nlp->lk_len = lock->l_length;
1261 		nlp->lk_pid = lock->l_pid;
1262 		nlp->lk_type = lock->l_type;
1263 		nlp++;
1264 		my_cnt++;
1265 	}
1266 	smb_llist_exit(llist);
1267 
1268 	mutex_enter(&of->f_mutex);
1269 	bcopy(of->f_lock_seq, lseq, sizeof (lseq));
1270 	mutex_exit(&of->f_mutex);
1271 
1272 	mutex_enter(&of->dh_nvlock);
1273 	if (of->dh_nvlist != NULL) {
1274 
1275 		(void) nvlist_add_uint64_array(of->dh_nvlist,
1276 		    "locks", vec, my_cnt * 3);
1277 
1278 		(void) nvlist_add_uint8_array(of->dh_nvlist,
1279 		    "lockseq", lseq, sizeof (lseq));
1280 	}
1281 	mutex_exit(&of->dh_nvlock);
1282 
1283 	kmem_free(vec, vec_sz);
1284 
1285 	sr->dh_nvl_dirty = B_TRUE;
1286 }
1287 
1288 /*
1289  * Save "sticky" times.
1290  */
1291 void
1292 smb2_dh_update_times(smb_request_t *sr, smb_ofile_t *of, smb_attr_t *attr)
1293 {
1294 	hrtime_t t;
1295 
1296 	mutex_enter(&of->dh_nvlock);
1297 	if (of->dh_nvlist == NULL) {
1298 		mutex_exit(&of->dh_nvlock);
1299 		return;
1300 	}
1301 
1302 	if (attr->sa_mask & SMB_AT_ATIME) {
1303 		t = ts2hrt(&attr->sa_vattr.va_atime);
1304 		(void) nvlist_add_hrtime(of->dh_nvlist, "atime", t);
1305 	} else {
1306 		(void) nvlist_remove_all(of->dh_nvlist, "atime");
1307 	}
1308 
1309 	if (attr->sa_mask & SMB_AT_MTIME) {
1310 		t = ts2hrt(&attr->sa_vattr.va_mtime);
1311 		(void) nvlist_add_hrtime(of->dh_nvlist, "mtime", t);
1312 	} else {
1313 		(void) nvlist_remove_all(of->dh_nvlist, "mtime");
1314 	}
1315 
1316 	if (attr->sa_mask & SMB_AT_CTIME) {
1317 		t = ts2hrt(&attr->sa_vattr.va_ctime);
1318 		(void) nvlist_add_hrtime(of->dh_nvlist, "ctime", t);
1319 	} else {
1320 		(void) nvlist_remove_all(of->dh_nvlist, "ctime");
1321 	}
1322 
1323 	mutex_exit(&of->dh_nvlock);
1324 
1325 	sr->dh_nvl_dirty = B_TRUE;
1326 }
1327 
1328 
1329 /*
1330  * Requirements for ofile found during reconnect (MS-SMB2 3.3.5.9.7):
1331  * - security descriptor must match provided descriptor
1332  *
1333  * If file is leased:
1334  * - lease must be requested
1335  * - client guid must match session guid
1336  * - file name must match given name
1337  * - lease key must match provided lease key
1338  * If file is not leased:
1339  * - Lease must not be requested
1340  *
1341  * dh_v2 only:
1342  * - SMB2_DHANDLE_FLAG_PERSISTENT must be set if dh_persist is true
1343  * - SMB2_DHANDLE_FLAG_PERSISTENT must not be set if dh_persist is false
1344  * - desired access, share access, and create_options must be ignored
1345  * - createguid must match
1346  */
1347 static uint32_t
1348 smb2_dh_reconnect_checks(smb_request_t *sr, smb_ofile_t *of)
1349 {
1350 	smb_arg_open_t	*op = &sr->sr_open;
1351 	char *fname;
1352 
1353 	if (of->f_lease != NULL) {
1354 		if (bcmp(sr->session->clnt_uuid,
1355 		    of->f_lease->ls_clnt, 16) != 0)
1356 			return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
1357 
1358 		if (op->op_oplock_level != SMB2_OPLOCK_LEVEL_LEASE)
1359 			return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
1360 		if (bcmp(op->lease_key, of->f_lease->ls_key,
1361 		    SMB_LEASE_KEY_SZ) != 0)
1362 			return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
1363 
1364 		/*
1365 		 * We're supposed to check the name is the same.
1366 		 * Not really necessary to do this, so just do
1367 		 * minimal effort (check last component)
1368 		 */
1369 		fname = strrchr(op->fqi.fq_path.pn_path, '\\');
1370 		if (fname != NULL)
1371 			fname++;
1372 		else
1373 			fname = op->fqi.fq_path.pn_path;
1374 		if (smb_strcasecmp(fname, of->f_node->od_name, 0) != 0) {
1375 #ifdef	DEBUG
1376 			cmn_err(CE_NOTE, "reconnect name <%s> of name <%s>",
1377 			    fname, of->f_node->od_name);
1378 #endif
1379 			return (NT_STATUS_INVALID_PARAMETER);
1380 		}
1381 	} else {
1382 		if (op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE)
1383 			return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
1384 	}
1385 
1386 	if (op->dh_vers == SMB2_DURABLE_V2) {
1387 		boolean_t op_persist =
1388 		    ((op->dh_v2_flags & SMB2_DHANDLE_FLAG_PERSISTENT) != 0);
1389 		if (of->dh_persist != op_persist)
1390 			return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
1391 		if (memcmp(op->create_guid, of->dh_create_guid, UUID_LEN))
1392 			return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
1393 	}
1394 
1395 	if (!smb_is_same_user(sr->user_cr, of->f_cr))
1396 		return (NT_STATUS_ACCESS_DENIED);
1397 
1398 	return (NT_STATUS_SUCCESS);
1399 }
1400 
1401 /*
1402  * [MS-SMB2] 3.3.5.9.7 and 3.3.5.9.12 (durable reconnect v1/v2)
1403  *
1404  * Looks up an ofile on the server's sv_dh_list by the persistid.
1405  * If found, it validates the request.
1406  * (see smb2_dh_reconnect_checks() for details)
1407  * If the checks are passed, add it onto the new tree's list.
1408  *
1409  * Note that the oplock break code path can get to an ofile via the node
1410  * ofile list.  It starts with a ref taken in smb_ofile_hold_olbrk, which
1411  * waits if the ofile is found in state RECONNECT.  That wait happens with
1412  * the node ofile list lock held as reader, and the oplock mutex held.
1413  * Implications of that are: While we're in state RECONNECT, we shoud NOT
1414  * block (at least, not for long) and must not try to enter any of the
1415  * node ofile list lock or oplock mutex.  Thankfully, we don't need to
1416  * enter those while reclaiming an orphaned ofile.
1417  */
1418 uint32_t
1419 smb2_dh_reconnect(smb_request_t *sr)
1420 {
1421 	smb_arg_open_t	*op = &sr->sr_open;
1422 	smb_tree_t *tree = sr->tid_tree;
1423 	smb_ofile_t *of;
1424 	cred_t *old_cr;
1425 	uint32_t status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1426 	uint16_t fid = 0;
1427 
1428 	if (smb_idpool_alloc(&tree->t_fid_pool, &fid))
1429 		return (NT_STATUS_TOO_MANY_OPENED_FILES);
1430 
1431 	/* Find orphaned handle. */
1432 	of = smb_ofile_lookup_by_persistid(sr, op->dh_fileid.persistent);
1433 	if (of == NULL)
1434 		goto errout;
1435 
1436 	mutex_enter(&of->f_mutex);
1437 	if (of->f_state != SMB_OFILE_STATE_ORPHANED) {
1438 		mutex_exit(&of->f_mutex);
1439 		goto errout;
1440 	}
1441 
1442 	status = smb2_dh_reconnect_checks(sr, of);
1443 	if (status != NT_STATUS_SUCCESS) {
1444 		mutex_exit(&of->f_mutex);
1445 		goto errout;
1446 	}
1447 
1448 	/*
1449 	 * Note: cv_broadcast(&of->f_cv) when we're
1450 	 * done messing around in this state.
1451 	 * See: smb_ofile_hold_olbrk()
1452 	 */
1453 	of->f_state = SMB_OFILE_STATE_RECONNECT;
1454 	mutex_exit(&of->f_mutex);
1455 
1456 	/*
1457 	 * At this point, we should be the only thread with a ref on the
1458 	 * ofile, and the RECONNECT state should prevent new refs from
1459 	 * being granted, or other durable threads from observing or
1460 	 * reclaiming it. Put this ofile in the new tree, similar to
1461 	 * the last part of smb_ofile_open.
1462 	 */
1463 
1464 	old_cr = of->f_cr;
1465 	of->f_cr = sr->user_cr;
1466 	crhold(of->f_cr);
1467 	crfree(old_cr);
1468 
1469 	of->f_session = sr->session; /* hold is via user and tree */
1470 	smb_user_hold_internal(sr->uid_user);
1471 	of->f_user = sr->uid_user;
1472 	smb_tree_hold_internal(tree);
1473 	of->f_tree = tree;
1474 	of->f_fid = fid;
1475 
1476 	smb_lavl_enter(&tree->t_ofile_list, RW_WRITER);
1477 	smb_lavl_insert(&tree->t_ofile_list, of);
1478 	smb_lavl_exit(&tree->t_ofile_list);
1479 	atomic_inc_32(&tree->t_open_files);
1480 	atomic_inc_32(&sr->session->s_file_cnt);
1481 
1482 	/*
1483 	 * The ofile is now in the caller's session & tree.
1484 	 *
1485 	 * In case smb_ofile_hold or smb_oplock_send_break() are
1486 	 * waiting for state RECONNECT to complete, wakeup.
1487 	 */
1488 	mutex_enter(&of->f_mutex);
1489 	of->dh_expire_time = 0;
1490 	of->f_state = SMB_OFILE_STATE_OPEN;
1491 	cv_broadcast(&of->f_cv);
1492 	mutex_exit(&of->f_mutex);
1493 
1494 	/*
1495 	 * The ofile is now visible in the new session.
1496 	 * From here, this is similar to the last part of
1497 	 * smb_common_open().
1498 	 */
1499 	op->fqi.fq_fattr.sa_mask = SMB_AT_ALL;
1500 	(void) smb_node_getattr(sr, of->f_node, zone_kcred(), of,
1501 	    &op->fqi.fq_fattr);
1502 
1503 	/*
1504 	 * Set up the fileid and dosattr in open_param for response
1505 	 */
1506 	op->fileid = op->fqi.fq_fattr.sa_vattr.va_nodeid;
1507 	op->dattr = op->fqi.fq_fattr.sa_dosattr;
1508 
1509 	/*
1510 	 * Set up the file type in open_param for the response
1511 	 * The ref. from ofile lookup is "given" to fid_ofile.
1512 	 */
1513 	op->ftype = SMB_FTYPE_DISK;
1514 	sr->smb_fid = of->f_fid;
1515 	sr->fid_ofile = of;
1516 
1517 	if (smb_node_is_file(of->f_node)) {
1518 		op->dsize = op->fqi.fq_fattr.sa_vattr.va_size;
1519 	} else {
1520 		/* directory or symlink */
1521 		op->dsize = 0;
1522 	}
1523 
1524 	op->create_options = 0; /* no more modifications wanted */
1525 	op->action_taken = SMB_OACT_OPENED;
1526 	return (NT_STATUS_SUCCESS);
1527 
1528 errout:
1529 	if (of != NULL)
1530 		smb_ofile_release(of);
1531 	if (fid != 0)
1532 		smb_idpool_free(&tree->t_fid_pool, fid);
1533 
1534 	return (status);
1535 }
1536 
1537 /*
1538  * Durable handle expiration
1539  * ofile state is _EXPIRED
1540  */
1541 static void
1542 smb2_dh_expire(void *arg)
1543 {
1544 	smb_ofile_t *of = (smb_ofile_t *)arg;
1545 
1546 	if (of->dh_persist)
1547 		smb2_dh_setdoc_persistent(of);
1548 	smb_ofile_close(of, 0);
1549 	smb_ofile_release(of);
1550 }
1551 
1552 /*
1553  * Called once a minute to do expiration of durable handles.
1554  *
1555  * Normally expired durable handles should be in state "orphaned",
1556  * having transitioned from state SAVE_DH through SAVING to state
1557  * ORPHANED after all ofile references go away.  If an ofile has
1558  * leaked references and the client disconnects, it will be found
1559  * here still in state SAVE_DH and past it's expiration time.
1560  * Call smb2_dh_expire for these as well, which will move them
1561  * from state SAVE_DH to state CLOSING, so they can no longer
1562  * cause sharing violations for new opens.
1563  */
1564 void
1565 smb2_durable_timers(smb_server_t *sv)
1566 {
1567 	smb_hash_t *hash;
1568 	smb_llist_t *bucket;
1569 	smb_ofile_t *of;
1570 	hrtime_t now;
1571 	int i;
1572 
1573 	hash = sv->sv_persistid_ht;
1574 	now = gethrtime();
1575 
1576 	for (i = 0; i < hash->num_buckets; i++) {
1577 		bucket = &hash->buckets[i].b_list;
1578 		smb_llist_enter(bucket, RW_READER);
1579 		for (of = smb_llist_head(bucket);
1580 		    of != NULL;
1581 		    of = smb_llist_next(bucket, of)) {
1582 			SMB_OFILE_VALID(of);
1583 
1584 			/*
1585 			 * Check outside the mutex first to avoid some
1586 			 * mutex_enter work in this loop.  If the state
1587 			 * changes under foot, the worst that happens
1588 			 * is we either enter the mutex when we might
1589 			 * not have needed to, or we miss some DH in
1590 			 * this pass and get it on the next.
1591 			 */
1592 			if (of->f_state != SMB_OFILE_STATE_ORPHANED &&
1593 			    of->f_state != SMB_OFILE_STATE_SAVE_DH)
1594 				continue;
1595 
1596 			mutex_enter(&of->f_mutex);
1597 			if ((of->f_state == SMB_OFILE_STATE_ORPHANED ||
1598 			    of->f_state == SMB_OFILE_STATE_SAVE_DH) &&
1599 			    of->dh_expire_time != 0 &&
1600 			    of->dh_expire_time <= now) {
1601 
1602 				of->f_state = SMB_OFILE_STATE_EXPIRED;
1603 				/* inline smb_ofile_hold_internal() */
1604 				of->f_refcnt++;
1605 				smb_llist_post(bucket, of, smb2_dh_expire);
1606 			}
1607 			mutex_exit(&of->f_mutex);
1608 		}
1609 		smb_llist_exit(bucket);
1610 	}
1611 }
1612 
1613 /*
1614  * This is called when we're about to add a new open to some node.
1615  * If we still have orphaned durable handles on this node, let's
1616  * assume the client has lost interest in those and close them,
1617  * otherwise we might conflict with our own orphaned handles.
1618  *
1619  * We need this because we import persistent handles "speculatively"
1620  * during share import (before the client ever asks for reconnect).
1621  * That allows us to avoid any need for a "create blackout" (or
1622  * "grace period") because the imported handles prevent unwanted
1623  * conflicting opens from other clients.  However, if some client
1624  * "forgets" about a persistent handle (*cough* Hyper-V) and tries
1625  * a new (conflicting) open instead of a reconnect, that might
1626  * fail unless we expire our orphaned durables handle first.
1627  *
1628  * Logic similar to smb_node_open_check()
1629  */
1630 void
1631 smb2_dh_close_my_orphans(smb_request_t *sr, smb_ofile_t *new_of)
1632 {
1633 	smb_node_t *node = new_of->f_node;
1634 	smb_ofile_t *of;
1635 
1636 	SMB_NODE_VALID(node);
1637 
1638 	smb_llist_enter(&node->n_ofile_list, RW_READER);
1639 	for (of = smb_llist_head(&node->n_ofile_list);
1640 	    of != NULL;
1641 	    of = smb_llist_next(&node->n_ofile_list, of)) {
1642 
1643 		/* Same client? */
1644 		if (of->f_lease != NULL &&
1645 		    bcmp(sr->session->clnt_uuid,
1646 		    of->f_lease->ls_clnt, 16) != 0)
1647 			continue;
1648 
1649 		if (!smb_is_same_user(sr->user_cr, of->f_cr))
1650 			continue;
1651 
1652 		mutex_enter(&of->f_mutex);
1653 		if (of->f_state == SMB_OFILE_STATE_ORPHANED ||
1654 		    of->f_state == SMB_OFILE_STATE_SAVE_DH) {
1655 			of->f_state = SMB_OFILE_STATE_EXPIRED;
1656 			/* inline smb_ofile_hold_internal() */
1657 			of->f_refcnt++;
1658 			smb_llist_post(&node->n_ofile_list,
1659 			    of, smb2_dh_expire);
1660 		}
1661 		mutex_exit(&of->f_mutex);
1662 	}
1663 
1664 	smb_llist_exit(&node->n_ofile_list);
1665 }
1666 
1667 /*
1668  * Called for each orphaned DH during shutdown.
1669  * Clean out any in-memory state, but leave any
1670  * on-disk persistent handle state in place.
1671  */
1672 static void
1673 smb2_dh_cleanup(void *arg)
1674 {
1675 	smb_ofile_t *of = (smb_ofile_t *)arg;
1676 	smb_node_t *strnode;
1677 	struct nvlist *nvl;
1678 
1679 	/*
1680 	 * Intentionally skip smb2_dh_close_persistent by
1681 	 * clearing dh_nvfile before smb_ofile_close().
1682 	 */
1683 	mutex_enter(&of->dh_nvlock);
1684 	strnode = of->dh_nvfile;
1685 	of->dh_nvfile = NULL;
1686 	nvl = of->dh_nvlist;
1687 	of->dh_nvlist = NULL;
1688 	mutex_exit(&of->dh_nvlock);
1689 
1690 	if (nvl != NULL)
1691 		nvlist_free(nvl);
1692 
1693 	if (strnode != NULL)
1694 		smb_node_release(strnode);
1695 
1696 	smb_ofile_close(of, 0);
1697 	smb_ofile_release(of);
1698 }
1699 
1700 /*
1701  * Clean out durable handles during shutdown.
1702  *
1703  * Like, smb2_durable_timers but cleanup only in-memory state,
1704  * and leave any persistent state there for later reconnect.
1705  */
1706 void
1707 smb2_dh_shutdown(smb_server_t *sv)
1708 {
1709 	static const smb_oplock_grant_t og0 = { 0 };
1710 	smb_hash_t *hash;
1711 	smb_llist_t *bucket;
1712 	smb_ofile_t *of;
1713 	int i;
1714 
1715 	hash = sv->sv_persistid_ht;
1716 
1717 	for (i = 0; i < hash->num_buckets; i++) {
1718 		bucket = &hash->buckets[i].b_list;
1719 		smb_llist_enter(bucket, RW_READER);
1720 		of = smb_llist_head(bucket);
1721 		while (of != NULL) {
1722 			SMB_OFILE_VALID(of);
1723 			mutex_enter(&of->f_mutex);
1724 
1725 			switch (of->f_state) {
1726 			case SMB_OFILE_STATE_ORPHANED:
1727 			case SMB_OFILE_STATE_SAVE_DH:
1728 				of->f_state = SMB_OFILE_STATE_EXPIRED;
1729 				/* inline smb_ofile_hold_internal() */
1730 				of->f_refcnt++;
1731 				smb_llist_post(bucket, of, smb2_dh_cleanup);
1732 				break;
1733 
1734 			default:
1735 				/*
1736 				 * Should not be possible, but try to
1737 				 * make this zombie ofile harmless.
1738 				 */
1739 				cmn_err(CE_NOTE, "!dh_shutdown found "
1740 				    "of = %p with invalid state = %d",
1741 				    (void *)of, of->f_state);
1742 				DTRACE_PROBE1(bad_ofile, smb_ofile_t *, of);
1743 				ASSERT(0);
1744 				of->f_oplock = og0;
1745 				break;
1746 			}
1747 			mutex_exit(&of->f_mutex);
1748 			of = smb_llist_next(bucket, of);
1749 		}
1750 		smb_llist_exit(bucket);
1751 	}
1752 
1753 #ifdef	DEBUG
1754 	for (i = 0; i < hash->num_buckets; i++) {
1755 		bucket = &hash->buckets[i].b_list;
1756 		smb_llist_enter(bucket, RW_READER);
1757 		of = smb_llist_head(bucket);
1758 		while (of != NULL) {
1759 			SMB_OFILE_VALID(of);
1760 			cmn_err(CE_NOTE, "dh_shutdown leaked of=%p",
1761 			    (void *)of);
1762 			of = smb_llist_next(bucket, of);
1763 		}
1764 		smb_llist_exit(bucket);
1765 	}
1766 #endif	// DEBUG
1767 }
1768 
1769 uint32_t
1770 smb2_fsctl_set_resilient(smb_request_t *sr, smb_fsctl_t *fsctl)
1771 {
1772 	uint32_t timeout;
1773 	smb_ofile_t *of = sr->fid_ofile;
1774 
1775 	/*
1776 	 * Note: The spec does not explicitly prohibit resilient directories
1777 	 * the same way it prohibits durable directories. We prohibit them
1778 	 * anyway as a simplifying assumption, as there doesn't seem to be
1779 	 * much use for it. (HYPER-V only seems to use it on files anyway)
1780 	 */
1781 	if (fsctl->InputCount < 8 || !smb_node_is_file(of->f_node))
1782 		return (NT_STATUS_INVALID_PARAMETER);
1783 
1784 	(void) smb_mbc_decodef(fsctl->in_mbc, "l4.",
1785 	    &timeout); /* milliseconds */
1786 
1787 	if (smb2_enable_dh == 0)
1788 		return (NT_STATUS_NOT_SUPPORTED);
1789 
1790 	/*
1791 	 * The spec wants us to return INVALID_PARAMETER if the timeout
1792 	 * is too large, but we have no way of informing the client
1793 	 * what an appropriate timeout is, so just set the timeout to
1794 	 * our max and return SUCCESS.
1795 	 */
1796 	if (timeout == 0)
1797 		timeout = smb2_res_def_timeout;
1798 	if (timeout > smb2_res_max_timeout)
1799 		timeout = smb2_res_max_timeout;
1800 
1801 	mutex_enter(&of->f_mutex);
1802 	of->dh_vers = SMB2_RESILIENT;
1803 	of->dh_timeout_offset = MSEC2NSEC(timeout);
1804 	mutex_exit(&of->f_mutex);
1805 
1806 	return (NT_STATUS_SUCCESS);
1807 }
1808