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