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