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