1811599a4SMatt Barden /*
2811599a4SMatt Barden  * This file and its contents are supplied under the terms of the
3811599a4SMatt Barden  * Common Development and Distribution License ("CDDL"), version 1.0.
4811599a4SMatt Barden  * You may only use this file in accordance with the terms of version
5811599a4SMatt Barden  * 1.0 of the CDDL.
6811599a4SMatt Barden  *
7811599a4SMatt Barden  * A full copy of the text of the CDDL should have accompanied this
8811599a4SMatt Barden  * source.  A copy of the CDDL is also available via the Internet at
9811599a4SMatt Barden  * http://www.illumos.org/license/CDDL.
10811599a4SMatt Barden  */
11811599a4SMatt Barden 
12811599a4SMatt Barden /*
1356a2adb6SPrashanth Badari  * Copyright 2017-2022 Tintri by DDN, Inc. All rights reserved.
14*19fa1385SGordon Ross  * Copyright 2021-2024 RackTop Systems, Inc.
15811599a4SMatt Barden  */
16811599a4SMatt Barden 
17811599a4SMatt Barden /*
18811599a4SMatt Barden  * SMB2 Durable Handle support
19811599a4SMatt Barden  */
20811599a4SMatt Barden 
21811599a4SMatt Barden #include <sys/types.h>
22811599a4SMatt Barden #include <sys/cmn_err.h>
23811599a4SMatt Barden #include <sys/fcntl.h>
24811599a4SMatt Barden #include <sys/nbmlock.h>
258d94f651SGordon Ross #include <sys/sid.h>
26811599a4SMatt Barden #include <smbsrv/string.h>
27811599a4SMatt Barden #include <smbsrv/smb_kproto.h>
28811599a4SMatt Barden #include <smbsrv/smb_fsops.h>
29811599a4SMatt Barden #include <smbsrv/smbinfo.h>
30811599a4SMatt Barden #include <smbsrv/smb2_kproto.h>
31811599a4SMatt Barden 
32811599a4SMatt Barden /* Windows default values from [MS-SMB2] */
33811599a4SMatt Barden /*
34811599a4SMatt Barden  * (times in seconds)
35811599a4SMatt Barden  * resilient:
36811599a4SMatt Barden  * MaxTimeout = 300 (win7+)
37811599a4SMatt Barden  * if timeout > MaxTimeout, ERROR
38811599a4SMatt Barden  * if timeout != 0, timeout = req.timeout
39811599a4SMatt Barden  * if timeout == 0, timeout = (infinity) (Win7/w2k8r2)
40811599a4SMatt Barden  * if timeout == 0, timeout = 120 (Win8+)
41811599a4SMatt Barden  * v2:
42811599a4SMatt Barden  * if timeout != 0, timeout = MIN(timeout, 300) (spec)
43811599a4SMatt Barden  * if timeout != 0, timeout = timeout (win8/2k12)
44811599a4SMatt Barden  * if timeout == 0, timeout = Share.CATimeout. \
45811599a4SMatt Barden  *	if Share.CATimeout == 0, timeout = 60 (win8/w2k12)
46811599a4SMatt Barden  * if timeout == 0, timeout = 180 (win8.1/w2k12r2)
47811599a4SMatt Barden  * open.timeout = 60 (win8/w2k12r2) (i.e. we ignore the request)
48811599a4SMatt Barden  * v1:
49811599a4SMatt Barden  * open.timeout = 16 minutes
50811599a4SMatt Barden  */
51811599a4SMatt Barden 
52811599a4SMatt Barden uint32_t smb2_dh_def_timeout = 60 * MILLISEC;	/* mSec. */
53811599a4SMatt Barden uint32_t smb2_dh_max_timeout = 300 * MILLISEC;	/* mSec. */
54811599a4SMatt Barden 
55811599a4SMatt Barden uint32_t smb2_res_def_timeout = 120 * MILLISEC;	/* mSec. */
56811599a4SMatt Barden uint32_t smb2_res_max_timeout = 300 * MILLISEC;	/* mSec. */
57811599a4SMatt Barden 
588d94f651SGordon Ross uint32_t smb2_persist_timeout = 300 * MILLISEC;	/* mSec. */
598d94f651SGordon Ross 
60*19fa1385SGordon Ross /*
61*19fa1385SGordon Ross  * Escape hatch in case persistent handles on directories cause problems.
62*19fa1385SGordon Ross  * We don't have directory leases, so there's not really much point in
63*19fa1385SGordon Ross  * persistence for directory handles, but Hyper-V wants them.
64*19fa1385SGordon Ross  */
65*19fa1385SGordon Ross int smb2_dh_allow_dir = 1;
66*19fa1385SGordon Ross 
67897907ceSGordon Ross /*
68897907ceSGordon Ross  * Max. size of the file used to store a CA handle.
69897907ceSGordon Ross  * Don't adjust this while the server is running.
70897907ceSGordon Ross  */
718d94f651SGordon Ross static uint32_t smb2_dh_max_cah_size = 64 * 1024;
728d94f651SGordon Ross static uint32_t smb2_ca_info_version = 1;
738d94f651SGordon Ross 
748d94f651SGordon Ross /*
758d94f651SGordon Ross  * Want this to have invariant layout on disk, where the
768d94f651SGordon Ross  * last two uint32_t values are stored as a uint64_t
778d94f651SGordon Ross  */
788d94f651SGordon Ross struct nvlk {
798d94f651SGordon Ross 	uint64_t lk_start;
808d94f651SGordon Ross 	uint64_t lk_len;
818d94f651SGordon Ross 	/* (lk_pid << 32) | lk_type */
828d94f651SGordon Ross #ifdef	_BIG_ENDIAN
838d94f651SGordon Ross 	uint32_t lk_pid, lk_type;
848d94f651SGordon Ross #else
858d94f651SGordon Ross 	uint32_t lk_type, lk_pid;
868d94f651SGordon Ross #endif
878d94f651SGordon Ross };
888d94f651SGordon Ross 
898d94f651SGordon Ross static void smb2_dh_import_share(void *);
908d94f651SGordon Ross static smb_ofile_t *smb2_dh_import_handle(smb_request_t *, smb_node_t *,
91897907ceSGordon Ross     char *, uint64_t);
92897907ceSGordon Ross static int smb2_dh_read_nvlist(smb_request_t *, smb_node_t *,
93897907ceSGordon Ross     char *, struct nvlist **);
948d94f651SGordon Ross static int smb2_dh_import_cred(smb_ofile_t *, char *);
958d94f651SGordon Ross 
968d94f651SGordon Ross #define	DH_SN_SIZE 24	/* size of DH stream name buffers */
978d94f651SGordon Ross /*
988d94f651SGordon Ross  * Build the stream name used to store a CA handle.
998d94f651SGordon Ross  * i.e. ":0123456789abcdef:$CA"
1008d94f651SGordon Ross  * Note: smb_fsop_create adds the SUNWsmb prefix,
1018d94f651SGordon Ross  * so we compose the name without the prefix.
1028d94f651SGordon Ross  */
1038d94f651SGordon Ross static inline void
smb2_dh_make_stream_name(char * buf,size_t buflen,uint64_t id)1048d94f651SGordon Ross smb2_dh_make_stream_name(char *buf, size_t buflen, uint64_t id)
1058d94f651SGordon Ross {
1068d94f651SGordon Ross 	ASSERT(buflen >= DH_SN_SIZE);
1078d94f651SGordon Ross 	(void) snprintf(buf, buflen,
1088d94f651SGordon Ross 	    ":%016" PRIx64 ":$CA", id);
1098d94f651SGordon Ross }
1108d94f651SGordon Ross 
111*19fa1385SGordon Ross /*
112*19fa1385SGordon Ross  * smb_dh_create_allowed
113*19fa1385SGordon Ross  *
114*19fa1385SGordon Ross  * Helper for smb2_create to decide whether the open/create call should
115*19fa1385SGordon Ross  * allow this handle to become durable.
116*19fa1385SGordon Ross  *
117*19fa1385SGordon Ross  * For files:
118*19fa1385SGordon Ross  * 1. op_oplock_level == SMB2_OPLOCK_LEVEL_BATCH
119*19fa1385SGordon Ross  * 2. A lease is requested with handle caching
120*19fa1385SGordon Ross  *    - for v1, the lease must not be on a directory
121*19fa1385SGordon Ross  * 3. For v2, flags has "persistent" (tree is CA)
122*19fa1385SGordon Ross  *    (when tree not CA, turned off persist earlier)
123*19fa1385SGordon Ross  *
124*19fa1385SGordon Ross  * For directories, we don't do leases, but allow persistent.
125*19fa1385SGordon Ross  */
126*19fa1385SGordon Ross boolean_t
smb_dh_create_allowed(smb_request_t * sr,smb_ofile_t * of)127*19fa1385SGordon Ross smb_dh_create_allowed(smb_request_t *sr, smb_ofile_t *of)
128*19fa1385SGordon Ross {
129*19fa1385SGordon Ross 	smb_arg_open_t *op = &sr->arg.open;
130*19fa1385SGordon Ross 
131*19fa1385SGordon Ross 	if (smb2_dh_allow_dir && smb_node_is_dir(of->f_node) &&
132*19fa1385SGordon Ross 	    (op->dh_v2_flags & SMB2_DHANDLE_FLAG_PERSISTENT) != 0)
133*19fa1385SGordon Ross 		return (B_TRUE);
134*19fa1385SGordon Ross 
135*19fa1385SGordon Ross 	if (smb_node_is_file(of->f_node) &&
136*19fa1385SGordon Ross 	    ((op->dh_v2_flags & SMB2_DHANDLE_FLAG_PERSISTENT) != 0 ||
137*19fa1385SGordon Ross 	    (op->op_oplock_level == SMB2_OPLOCK_LEVEL_BATCH) ||
138*19fa1385SGordon Ross 	    (op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE &&
139*19fa1385SGordon Ross 	    (op->lease_state & OPLOCK_LEVEL_CACHE_HANDLE) != 0)))
140*19fa1385SGordon Ross 		return (B_TRUE);
141*19fa1385SGordon Ross 
142*19fa1385SGordon Ross 	return (B_FALSE);
143*19fa1385SGordon Ross }
144*19fa1385SGordon Ross 
145*19fa1385SGordon Ross 
146811599a4SMatt Barden /*
147811599a4SMatt Barden  * smb_dh_should_save
148811599a4SMatt Barden  *
149811599a4SMatt Barden  * During session tear-down, decide whether to keep a durable handle.
150811599a4SMatt Barden  *
151811599a4SMatt Barden  * There are two cases where we save durable handles:
152811599a4SMatt Barden  * 1. An SMB2 LOGOFF request was received
153811599a4SMatt Barden  * 2. An unexpected disconnect from the client
154811599a4SMatt Barden  *    Note: Specifying a PrevSessionID in session setup
155811599a4SMatt Barden  *    is considered a disconnect (we just haven't learned about it yet)
156811599a4SMatt Barden  * In every other case, we close durable handles.
157811599a4SMatt Barden  *
158811599a4SMatt Barden  * [MS-SMB2] 3.3.5.6 SMB2_LOGOFF
159811599a4SMatt Barden  * [MS-SMB2] 3.3.7.1 Handling Loss of a Connection
160811599a4SMatt Barden  *
161811599a4SMatt Barden  * If any of the following are true, preserve for reconnect:
162811599a4SMatt Barden  *
163811599a4SMatt Barden  * - Open.IsResilient is TRUE.
164811599a4SMatt Barden  *
165811599a4SMatt Barden  * - Open.OplockLevel == SMB2_OPLOCK_LEVEL_BATCH and
166811599a4SMatt Barden  *   Open.OplockState == Held, and Open.IsDurable is TRUE.
167811599a4SMatt Barden  *
168811599a4SMatt Barden  * - Open.OplockLevel == SMB2_OPLOCK_LEVEL_LEASE,
169811599a4SMatt Barden  *   Lease.LeaseState SMB2_LEASE_HANDLE_CACHING,
170811599a4SMatt Barden  *   Open.OplockState == Held, and Open.IsDurable is TRUE.
171811599a4SMatt Barden  *
172811599a4SMatt Barden  * - Open.IsPersistent is TRUE.
1738d94f651SGordon Ross  *
1748d94f651SGordon Ross  * We also deal with some special cases for shutdown of the
1758d94f651SGordon Ross  * server, session, user, tree (in that order). Other than
1768d94f651SGordon Ross  * the cases above, shutdown (or forced termination) should
1778d94f651SGordon Ross  * destroy durable handles.
178811599a4SMatt Barden  */
179811599a4SMatt Barden boolean_t
smb_dh_should_save(smb_ofile_t * of)180811599a4SMatt Barden smb_dh_should_save(smb_ofile_t *of)
181811599a4SMatt Barden {
182811599a4SMatt Barden 	ASSERT(MUTEX_HELD(&of->f_mutex));
183811599a4SMatt Barden 	ASSERT(of->dh_vers != SMB2_NOT_DURABLE);
184811599a4SMatt Barden 
1858d94f651SGordon Ross 	/* SMB service shutting down, destroy DH */
1868d94f651SGordon Ross 	if (of->f_server->sv_state == SMB_SERVER_STATE_STOPPING)
187811599a4SMatt Barden 		return (B_FALSE);
188811599a4SMatt Barden 
1898d94f651SGordon Ross 	/*
1908d94f651SGordon Ross 	 * SMB Session (connection) going away (server up).
1918d94f651SGordon Ross 	 * If server initiated disconnect, destroy DH
1928d94f651SGordon Ross 	 * If client initiated disconnect, save all DH.
1938d94f651SGordon Ross 	 */
1948d94f651SGordon Ross 	if (of->f_session->s_state == SMB_SESSION_STATE_TERMINATED)
1958d94f651SGordon Ross 		return (B_FALSE);
1968d94f651SGordon Ross 	if (of->f_session->s_state == SMB_SESSION_STATE_DISCONNECTED)
197811599a4SMatt Barden 		return (B_TRUE);
198811599a4SMatt Barden 
1998d94f651SGordon Ross 	/*
2008d94f651SGordon Ross 	 * SMB User logoff, session still "up".
2018d94f651SGordon Ross 	 * Action depends on why/how this logoff happened,
2028d94f651SGordon Ross 	 * determined based on user->preserve_opens
2038d94f651SGordon Ross 	 */
2048d94f651SGordon Ross 	if (of->f_user->u_state == SMB_USER_STATE_LOGGING_OFF) {
2058d94f651SGordon Ross 		switch (of->f_user->preserve_opens) {
2068d94f651SGordon Ross 		case SMB2_DH_PRESERVE_NONE:
2078d94f651SGordon Ross 			/* Server-initiated */
2088d94f651SGordon Ross 			return (B_FALSE);
2098d94f651SGordon Ross 		case SMB2_DH_PRESERVE_SOME:
2108d94f651SGordon Ross 			/* Previous session logoff. */
2118d94f651SGordon Ross 			goto preserve_some;
2128d94f651SGordon Ross 		case SMB2_DH_PRESERVE_ALL:
2138d94f651SGordon Ross 			/* Protocol logoff request */
2148d94f651SGordon Ross 			return (B_TRUE);
2158d94f651SGordon Ross 		}
2168d94f651SGordon Ross 	}
2178d94f651SGordon Ross 
2188d94f651SGordon Ross 	/*
2198d94f651SGordon Ross 	 * SMB tree disconnecting (user still logged on)
2208d94f651SGordon Ross 	 * i.e. when kshare export forces disconnection.
2218d94f651SGordon Ross 	 */
2228d94f651SGordon Ross 	if (of->f_tree->t_state == SMB_TREE_STATE_DISCONNECTING)
2238d94f651SGordon Ross 		return (B_FALSE);
2248d94f651SGordon Ross 
2258d94f651SGordon Ross preserve_some:
2268d94f651SGordon Ross 	/* preserve_opens == SMB2_DH_PRESERVE_SOME */
2278d94f651SGordon Ross 
22894047d49SGordon Ross 	switch (of->dh_vers) {
2290ce4dc6fSGordon Ross 		uint32_t ol_state;
2300ce4dc6fSGordon Ross 
23194047d49SGordon Ross 	case SMB2_RESILIENT:
232811599a4SMatt Barden 		return (B_TRUE);
233811599a4SMatt Barden 
23494047d49SGordon Ross 	case SMB2_DURABLE_V2:
23594047d49SGordon Ross 		if (of->dh_persist)
23694047d49SGordon Ross 			return (B_TRUE);
23794047d49SGordon Ross 		/* FALLTHROUGH */
23894047d49SGordon Ross 	case SMB2_DURABLE_V1:
23994047d49SGordon Ross 		/* IS durable (v1 or v2) */
2400ce4dc6fSGordon Ross 		if (of->f_lease != NULL)
2410ce4dc6fSGordon Ross 			ol_state = of->f_lease->ls_state;
2420ce4dc6fSGordon Ross 		else
2430ce4dc6fSGordon Ross 			ol_state = of->f_oplock.og_state;
2440ce4dc6fSGordon Ross 		if ((ol_state & (OPLOCK_LEVEL_BATCH |
24594047d49SGordon Ross 		    OPLOCK_LEVEL_CACHE_HANDLE)) != 0)
24694047d49SGordon Ross 			return (B_TRUE);
24794047d49SGordon Ross 		/* FALLTHROUGH */
24894047d49SGordon Ross 	case SMB2_NOT_DURABLE:
24994047d49SGordon Ross 	default:
25094047d49SGordon Ross 		break;
25194047d49SGordon Ross 	}
252811599a4SMatt Barden 
253811599a4SMatt Barden 	return (B_FALSE);
254811599a4SMatt Barden }
255811599a4SMatt Barden 
2568d94f651SGordon Ross /*
2578d94f651SGordon Ross  * Is this stream name a CA handle? i.e.
2588d94f651SGordon Ross  * ":0123456789abcdef:$CA"
2598d94f651SGordon Ross  */
2608d94f651SGordon Ross static boolean_t
smb2_dh_match_ca_name(const char * name,uint64_t * idp)2618d94f651SGordon Ross smb2_dh_match_ca_name(const char *name, uint64_t *idp)
2628d94f651SGordon Ross {
2638d94f651SGordon Ross 	static const char suffix[] = ":$CA";
2648d94f651SGordon Ross 	u_longlong_t ull;
2658d94f651SGordon Ross 	const char *p = name;
2668d94f651SGordon Ross 	char *p2 = NULL;
2678d94f651SGordon Ross 	int len, rc;
2688d94f651SGordon Ross 
2698d94f651SGordon Ross 	if (*p++ != ':')
2708d94f651SGordon Ross 		return (B_FALSE);
2718d94f651SGordon Ross 
2728d94f651SGordon Ross 	rc = ddi_strtoull(p, &p2, 16, &ull);
2738d94f651SGordon Ross 	if (rc != 0 || p2 != (p + 16))
2748d94f651SGordon Ross 		return (B_FALSE);
2758d94f651SGordon Ross 	p += 16;
2768d94f651SGordon Ross 
2778d94f651SGordon Ross 	len = sizeof (suffix) - 1;
2788d94f651SGordon Ross 	if (strncmp(p, suffix, len) != 0)
2798d94f651SGordon Ross 		return (B_FALSE);
2808d94f651SGordon Ross 	p += len;
2818d94f651SGordon Ross 
2828d94f651SGordon Ross 	if (*p != '\0')
2838d94f651SGordon Ross 		return (B_FALSE);
2848d94f651SGordon Ross 
2858d94f651SGordon Ross 	*idp = (uint64_t)ull;
2868d94f651SGordon Ross 	return (B_TRUE);
2878d94f651SGordon Ross }
2888d94f651SGordon Ross 
2898d94f651SGordon Ross /*
2908d94f651SGordon Ross  * smb2_dh_new_ca_share
2918d94f651SGordon Ross  *
2928d94f651SGordon Ross  * Called when a new share has ca=true.  Find or create the CA dir,
2938d94f651SGordon Ross  * and start a thread to import persistent handles.
2948d94f651SGordon Ross  */
2958d94f651SGordon Ross int
smb2_dh_new_ca_share(smb_server_t * sv,smb_kshare_t * shr)2968d94f651SGordon Ross smb2_dh_new_ca_share(smb_server_t *sv, smb_kshare_t *shr)
2978d94f651SGordon Ross {
2988d94f651SGordon Ross 	smb_kshare_t	*shr2;
2998d94f651SGordon Ross 	smb_request_t	*sr;
3009788d6deSGordon Ross 	taskqid_t	tqid;
3018d94f651SGordon Ross 
3028d94f651SGordon Ross 	ASSERT(STYPE_ISDSK(shr->shr_type));
3038d94f651SGordon Ross 
3048d94f651SGordon Ross 	/*
3058d94f651SGordon Ross 	 * Need to lookup the kshare again, to get a hold.
3068d94f651SGordon Ross 	 * Add a function to just get the hold?
3078d94f651SGordon Ross 	 */
3088d94f651SGordon Ross 	shr2 = smb_kshare_lookup(sv, shr->shr_name);
3098d94f651SGordon Ross 	if (shr2 != shr)
3108d94f651SGordon Ross 		return (EINVAL);
3118d94f651SGordon Ross 
3128d94f651SGordon Ross 	sr = smb_request_alloc(sv->sv_session, 0);
3138d94f651SGordon Ross 	if (sr == NULL) {
3148d94f651SGordon Ross 		/* shutting down? */
3158d94f651SGordon Ross 		smb_kshare_release(sv, shr);
3168d94f651SGordon Ross 		return (EINTR);
3178d94f651SGordon Ross 	}
3188d94f651SGordon Ross 	sr->sr_state = SMB_REQ_STATE_SUBMITTED;
3198d94f651SGordon Ross 
3208d94f651SGordon Ross 	/*
3218d94f651SGordon Ross 	 * Mark this share as "busy importing persistent handles"
3228d94f651SGordon Ross 	 * so we can hold off tree connect until that's done.
3238d94f651SGordon Ross 	 * Will clear and wakeup below.
3248d94f651SGordon Ross 	 */
3258d94f651SGordon Ross 	mutex_enter(&shr->shr_mutex);
3268d94f651SGordon Ross 	shr->shr_import_busy = sr;
3278d94f651SGordon Ross 	mutex_exit(&shr->shr_mutex);
3288d94f651SGordon Ross 
3298d94f651SGordon Ross 	/*
3308d94f651SGordon Ross 	 * Start a taskq job to import any CA handles.
3318d94f651SGordon Ross 	 * The hold on the kshare is given to this job,
3328d94f651SGordon Ross 	 * which releases it when it's done.
3338d94f651SGordon Ross 	 */
3348d94f651SGordon Ross 	sr->arg.tcon.si = shr;	/* hold from above */
3359788d6deSGordon Ross 	tqid = taskq_dispatch(sv->sv_worker_pool,
3368d94f651SGordon Ross 	    smb2_dh_import_share, sr, TQ_SLEEP);
3379788d6deSGordon Ross 	VERIFY(tqid != TASKQID_INVALID);
3388d94f651SGordon Ross 
3398d94f651SGordon Ross 	return (0);
3408d94f651SGordon Ross }
3418d94f651SGordon Ross 
3428d94f651SGordon Ross int smb2_dh_import_delay = 0;
3438d94f651SGordon Ross 
3448d94f651SGordon Ross static void
smb2_dh_import_share(void * arg)3458d94f651SGordon Ross smb2_dh_import_share(void *arg)
3468d94f651SGordon Ross {
3478d94f651SGordon Ross 	smb_request_t	*sr = arg;
3488d94f651SGordon Ross 	smb_kshare_t	*shr = sr->arg.tcon.si;
3498d94f651SGordon Ross 	smb_node_t	*snode;
3508d94f651SGordon Ross 	cred_t		*kcr = zone_kcred();
3518d94f651SGordon Ross 	smb_streaminfo_t *str_info = NULL;
352897907ceSGordon Ross 	char		*nvl_buf = NULL;
3538d94f651SGordon Ross 	uint64_t	id;
3548d94f651SGordon Ross 	smb_node_t	*str_node;
3558d94f651SGordon Ross 	smb_odir_t	*od = NULL;
3568d94f651SGordon Ross 	smb_ofile_t	*of;
3578d94f651SGordon Ross 	int		rc;
3588d94f651SGordon Ross 	boolean_t	eof;
3598d94f651SGordon Ross 
3608d94f651SGordon Ross 	sr->sr_state = SMB_REQ_STATE_ACTIVE;
3618d94f651SGordon Ross 
3628d94f651SGordon Ross 	if (smb2_dh_import_delay > 0)
3638d94f651SGordon Ross 		delay(SEC_TO_TICK(smb2_dh_import_delay));
3648d94f651SGordon Ross 
3658d94f651SGordon Ross 	/*
3668d94f651SGordon Ross 	 * Borrow the server's "root" user.
3678d94f651SGordon Ross 	 *
3688d94f651SGordon Ross 	 * This takes the place of smb_session_lookup_ssnid()
3698d94f651SGordon Ross 	 * that would happen in smb2_dispatch for a normal SR.
3708d94f651SGordon Ross 	 * As usual, this hold is released in smb_request_free.
3718d94f651SGordon Ross 	 */
3728d94f651SGordon Ross 	sr->uid_user = sr->sr_server->sv_rootuser;
3738d94f651SGordon Ross 	smb_user_hold_internal(sr->uid_user);
3748d94f651SGordon Ross 	sr->user_cr = sr->uid_user->u_cred;
3758d94f651SGordon Ross 
3768d94f651SGordon Ross 	/*
3778d94f651SGordon Ross 	 * Create a temporary tree connect
3788d94f651SGordon Ross 	 */
3798d94f651SGordon Ross 	sr->tid_tree = smb_tree_alloc(sr, shr, shr->shr_root_node,
3808d94f651SGordon Ross 	    ACE_ALL_PERMS, 0);
3818d94f651SGordon Ross 	if (sr->tid_tree == NULL) {
3828d94f651SGordon Ross 		cmn_err(CE_NOTE, "smb2_dh_import_share: "
3838d94f651SGordon Ross 		    "failed connect share <%s>", shr->shr_name);
3848d94f651SGordon Ross 		goto out;
3858d94f651SGordon Ross 	}
3868d94f651SGordon Ross 	snode = sr->tid_tree->t_snode;
3878d94f651SGordon Ross 
3888d94f651SGordon Ross 	/*
3898d94f651SGordon Ross 	 * Get the buffers we'll use to read CA handle data.
390897907ceSGordon Ross 	 * Also get a buffer for the stream name info.
3918d94f651SGordon Ross 	 */
392897907ceSGordon Ross 	nvl_buf = kmem_alloc(smb2_dh_max_cah_size, KM_SLEEP);
3938d94f651SGordon Ross 	str_info = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP);
3948d94f651SGordon Ross 
3958d94f651SGordon Ross 	/*
3968d94f651SGordon Ross 	 * Open the ext. attr dir under the share root and
3978d94f651SGordon Ross 	 * import CA handles for this share.
3988d94f651SGordon Ross 	 */
39956a2adb6SPrashanth Badari 	if (smb_odir_openat(sr, snode, &od, B_FALSE) != 0) {
40056a2adb6SPrashanth Badari 		cmn_err(CE_NOTE, "!Share [%s] CA import, no xattr dir?",
4018d94f651SGordon Ross 		    shr->shr_name);
4028d94f651SGordon Ross 		goto out;
4038d94f651SGordon Ross 	}
4048d94f651SGordon Ross 
4058d94f651SGordon Ross 	eof = B_FALSE;
4068d94f651SGordon Ross 	do {
4078d94f651SGordon Ross 		/*
4088d94f651SGordon Ross 		 * If the kshare gets unshared before we finish,
4098d94f651SGordon Ross 		 * bail out so we don't hold things up.
4108d94f651SGordon Ross 		 */
4118d94f651SGordon Ross 		if (shr->shr_flags & SMB_SHRF_REMOVED)
4128d94f651SGordon Ross 			break;
4138d94f651SGordon Ross 
414af7caad8SPrashanth Badari 		/*
415af7caad8SPrashanth Badari 		 * If the server's stopping, no point importing.
416af7caad8SPrashanth Badari 		 */
417af7caad8SPrashanth Badari 		if (smb_server_is_stopping(sr->sr_server))
418af7caad8SPrashanth Badari 			break;
419af7caad8SPrashanth Badari 
4208d94f651SGordon Ross 		/*
4218d94f651SGordon Ross 		 * Read a stream name and info
4228d94f651SGordon Ross 		 */
4238d94f651SGordon Ross 		rc = smb_odir_read_streaminfo(sr, od, str_info, &eof);
4248d94f651SGordon Ross 		if ((rc != 0) || (eof))
4258d94f651SGordon Ross 			break;
4268d94f651SGordon Ross 
4278d94f651SGordon Ross 		/*
4288d94f651SGordon Ross 		 * Skip anything not a CA handle.
4298d94f651SGordon Ross 		 */
4308d94f651SGordon Ross 		if (!smb2_dh_match_ca_name(str_info->si_name, &id)) {
4318d94f651SGordon Ross 			continue;
4328d94f651SGordon Ross 		}
4338d94f651SGordon Ross 
4348d94f651SGordon Ross 		/*
4358d94f651SGordon Ross 		 * Lookup stream node and import
4368d94f651SGordon Ross 		 */
4378d94f651SGordon Ross 		str_node = NULL;
4388d94f651SGordon Ross 		rc = smb_fsop_lookup_name(sr, kcr, SMB_CASE_SENSITIVE,
4398d94f651SGordon Ross 		    snode, snode, str_info->si_name, &str_node);
4408d94f651SGordon Ross 		if (rc != 0) {
4418d94f651SGordon Ross 			cmn_err(CE_NOTE, "Share [%s] CA import, "
4428d94f651SGordon Ross 			    "lookup <%s> failed rc=%d",
4438d94f651SGordon Ross 			    shr->shr_name, str_info->si_name, rc);
4448d94f651SGordon Ross 			continue;
4458d94f651SGordon Ross 		}
446897907ceSGordon Ross 		of = smb2_dh_import_handle(sr, str_node, nvl_buf, id);
4478d94f651SGordon Ross 		smb_node_release(str_node);
4488d94f651SGordon Ross 		if (of != NULL) {
4498d94f651SGordon Ross 			smb_ofile_release(of);
4508d94f651SGordon Ross 			of = NULL;
4518d94f651SGordon Ross 		}
4528d94f651SGordon Ross 		sr->fid_ofile = NULL;
4532cf6b79fSGordon Ross 		smb_lavl_flush(&sr->tid_tree->t_ofile_list);
4548d94f651SGordon Ross 
4558d94f651SGordon Ross 	} while (!eof);
4568d94f651SGordon Ross 
4578d94f651SGordon Ross out:
4588d94f651SGordon Ross 	if (od != NULL) {
4598d94f651SGordon Ross 		smb_odir_close(od);
4608d94f651SGordon Ross 		smb_odir_release(od);
4618d94f651SGordon Ross 	}
4628d94f651SGordon Ross 
4638d94f651SGordon Ross 	if (str_info != NULL)
4648d94f651SGordon Ross 		kmem_free(str_info, sizeof (smb_streaminfo_t));
465897907ceSGordon Ross 	if (nvl_buf != NULL)
466897907ceSGordon Ross 		kmem_free(nvl_buf, smb2_dh_max_cah_size);
4678d94f651SGordon Ross 
4688d94f651SGordon Ross 	/*
4698d94f651SGordon Ross 	 * We did a (temporary, internal) tree connect above,
4708d94f651SGordon Ross 	 * which we need to undo before we return.  Note that
4718d94f651SGordon Ross 	 * smb_request_free will do the final release of
4728d94f651SGordon Ross 	 * sr->tid_tree, sr->uid_user
4738d94f651SGordon Ross 	 */
4748d94f651SGordon Ross 	if (sr->tid_tree != NULL)
4758d94f651SGordon Ross 		smb_tree_disconnect(sr->tid_tree, B_FALSE);
4768d94f651SGordon Ross 
4778d94f651SGordon Ross 	/*
4788d94f651SGordon Ross 	 * Wake up any waiting tree connect(s).
4798d94f651SGordon Ross 	 * See smb_tree_connect_disk().
4808d94f651SGordon Ross 	 */
4818d94f651SGordon Ross 	mutex_enter(&shr->shr_mutex);
4828d94f651SGordon Ross 	shr->shr_import_busy = NULL;
4838d94f651SGordon Ross 	cv_broadcast(&shr->shr_cv);
4848d94f651SGordon Ross 	mutex_exit(&shr->shr_mutex);
4858d94f651SGordon Ross 
4868d94f651SGordon Ross 	smb_kshare_release(sr->sr_server, shr);
4878d94f651SGordon Ross 	smb_request_free(sr);
4888d94f651SGordon Ross }
4898d94f651SGordon Ross 
4908d94f651SGordon Ross /*
4918d94f651SGordon Ross  * This returns the new ofile mostly for dtrace.
4928d94f651SGordon Ross  */
4938d94f651SGordon Ross static smb_ofile_t *
smb2_dh_import_handle(smb_request_t * sr,smb_node_t * str_node,char * nvl_buf,uint64_t persist_id)4948d94f651SGordon Ross smb2_dh_import_handle(smb_request_t *sr, smb_node_t *str_node,
495897907ceSGordon Ross     char *nvl_buf, uint64_t persist_id)
4968d94f651SGordon Ross {
4978d94f651SGordon Ross 	uint8_t		client_uuid[UUID_LEN];
4988d94f651SGordon Ross 	smb_tree_t	*tree = sr->tid_tree;
4998d94f651SGordon Ross 	smb_arg_open_t	*op = &sr->arg.open;
5008d94f651SGordon Ross 	smb_pathname_t	*pn = &op->fqi.fq_path;
5018d94f651SGordon Ross 	cred_t		*kcr = zone_kcred();
5028d94f651SGordon Ross 	struct nvlist	*nvl = NULL;
5038d94f651SGordon Ross 	char		*sidstr = NULL;
5048d94f651SGordon Ross 	smb_ofile_t	*of = NULL;
5058d94f651SGordon Ross 	smb_attr_t	*pa;
5068d94f651SGordon Ross 	boolean_t	did_open = B_FALSE;
5078d94f651SGordon Ross 	boolean_t	have_lease = B_FALSE;
5088d94f651SGordon Ross 	hrtime_t	hrt;
5098d94f651SGordon Ross 	uint64_t	*u64p;
5108d94f651SGordon Ross 	uint64_t	u64;
5118d94f651SGordon Ross 	uint32_t	u32;
5128d94f651SGordon Ross 	uint32_t	status;
5138d94f651SGordon Ross 	char		*s;
5148d94f651SGordon Ross 	uint8_t		*u8p;
5158d94f651SGordon Ross 	uint_t		alen;
5168d94f651SGordon Ross 	int		rc;
5178d94f651SGordon Ross 
5188d94f651SGordon Ross 	/*
5198d94f651SGordon Ross 	 * While we're called with arg.tcon, we now want to use
5208d94f651SGordon Ross 	 * smb_arg_open for the rest of import, so clear it.
5218d94f651SGordon Ross 	 */
5228d94f651SGordon Ross 	bzero(op, sizeof (*op));
5238d94f651SGordon Ross 	op->create_disposition = FILE_OPEN;
5248d94f651SGordon Ross 
5258d94f651SGordon Ross 	/*
5268d94f651SGordon Ross 	 * Read and unpack the NVL
5278d94f651SGordon Ross 	 */
528897907ceSGordon Ross 	rc = smb2_dh_read_nvlist(sr, str_node, nvl_buf, &nvl);
5298d94f651SGordon Ross 	if (rc != 0)
5308d94f651SGordon Ross 		return (NULL);
5318d94f651SGordon Ross 
5328d94f651SGordon Ross 	/*
5338d94f651SGordon Ross 	 * Known CA info version?
5348d94f651SGordon Ross 	 */
5358d94f651SGordon Ross 	u32 = 0;
5368d94f651SGordon Ross 	rc = nvlist_lookup_uint32(nvl, "info_version", &u32);
5378d94f651SGordon Ross 	if (rc != 0 || u32 != smb2_ca_info_version) {
5388d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) bad vers=%d",
5398d94f651SGordon Ross 		    tree->t_resource, str_node->od_name, u32);
5408d94f651SGordon Ross 		goto errout;
5418d94f651SGordon Ross 	}
5428d94f651SGordon Ross 
5438d94f651SGordon Ross 	/*
5448d94f651SGordon Ross 	 * The persist ID in the nvlist should match the one
5458d94f651SGordon Ross 	 * encoded in the file name. (not enforced)
5468d94f651SGordon Ross 	 */
5478d94f651SGordon Ross 	u64 = 0;
5488d94f651SGordon Ross 	rc = nvlist_lookup_uint64(nvl, "file_persistid", &u64);
5498d94f651SGordon Ross 	if (rc != 0 || u64 != persist_id) {
5508d94f651SGordon Ross 		cmn_err(CE_WARN, "CA import (%s/%s) bad id=%016" PRIx64,
5518d94f651SGordon Ross 		    tree->t_resource, str_node->od_name, u64);
5528d94f651SGordon Ross 		/* goto errout? (allow) */
5538d94f651SGordon Ross 	}
5548d94f651SGordon Ross 
5558d94f651SGordon Ross 	/*
5568d94f651SGordon Ross 	 * Does it belong in the share being imported?
5578d94f651SGordon Ross 	 */
5588d94f651SGordon Ross 	s = NULL;
5598d94f651SGordon Ross 	rc = nvlist_lookup_string(nvl, "share_name", &s);
5608d94f651SGordon Ross 	if (rc != 0) {
5618d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) no share_name",
5628d94f651SGordon Ross 		    tree->t_resource, str_node->od_name);
5638d94f651SGordon Ross 		goto errout;
5648d94f651SGordon Ross 	}
5658d94f651SGordon Ross 	if (smb_strcasecmp(s, tree->t_sharename, 0) != 0) {
5668d94f651SGordon Ross 		/* Normal (not an error) */
5678d94f651SGordon Ross #ifdef DEBUG
5688d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) other share",
5698d94f651SGordon Ross 		    tree->t_resource, str_node->od_name);
5708d94f651SGordon Ross #endif
5718d94f651SGordon Ross 		goto errout;
5728d94f651SGordon Ross 	}
5738d94f651SGordon Ross 
5748d94f651SGordon Ross 	/*
5758d94f651SGordon Ross 	 * Get the path name (for lookup)
5768d94f651SGordon Ross 	 */
5778d94f651SGordon Ross 	rc = nvlist_lookup_string(nvl, "path_name", &pn->pn_path);
5788d94f651SGordon Ross 	if (rc != 0) {
5798d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) no path_name",
5808d94f651SGordon Ross 		    tree->t_resource, str_node->od_name);
5818d94f651SGordon Ross 		goto errout;
5828d94f651SGordon Ross 	}
5838d94f651SGordon Ross 
5848d94f651SGordon Ross 	/*
5858d94f651SGordon Ross 	 * owner sid
5868d94f651SGordon Ross 	 */
5878d94f651SGordon Ross 	rc = nvlist_lookup_string(nvl, "owner_sid", &sidstr);
5888d94f651SGordon Ross 	if (rc != 0) {
5898d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) no owner_sid",
5908d94f651SGordon Ross 		    tree->t_resource, str_node->od_name);
5918d94f651SGordon Ross 		goto errout;
5928d94f651SGordon Ross 	}
5938d94f651SGordon Ross 
5948d94f651SGordon Ross 	/*
5958d94f651SGordon Ross 	 * granted access
5968d94f651SGordon Ross 	 */
5978d94f651SGordon Ross 	rc = nvlist_lookup_uint32(nvl,
5988d94f651SGordon Ross 	    "granted_access", &op->desired_access);
5998d94f651SGordon Ross 	if (rc != 0) {
6008d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) no granted_access",
6018d94f651SGordon Ross 		    tree->t_resource, str_node->od_name);
6028d94f651SGordon Ross 		goto errout;
6038d94f651SGordon Ross 	}
6048d94f651SGordon Ross 
6058d94f651SGordon Ross 	/*
6068d94f651SGordon Ross 	 * share access
6078d94f651SGordon Ross 	 */
6088d94f651SGordon Ross 	rc = nvlist_lookup_uint32(nvl,
6098d94f651SGordon Ross 	    "share_access", &op->share_access);
6108d94f651SGordon Ross 	if (rc != 0) {
6118d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) no share_access",
6128d94f651SGordon Ross 		    tree->t_resource, str_node->od_name);
6138d94f651SGordon Ross 		goto errout;
6148d94f651SGordon Ross 	}
6158d94f651SGordon Ross 
6168d94f651SGordon Ross 	/*
6178d94f651SGordon Ross 	 * create options
6188d94f651SGordon Ross 	 */
6198d94f651SGordon Ross 	rc = nvlist_lookup_uint32(nvl,
6208d94f651SGordon Ross 	    "create_options", &op->create_options);
6218d94f651SGordon Ross 	if (rc != 0) {
6228d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) no create_options",
6238d94f651SGordon Ross 		    tree->t_resource, str_node->od_name);
6248d94f651SGordon Ross 		goto errout;
6258d94f651SGordon Ross 	}
6268d94f651SGordon Ross 
6278d94f651SGordon Ross 	/*
6288d94f651SGordon Ross 	 * create guid (client-assigned)
6298d94f651SGordon Ross 	 */
6308d94f651SGordon Ross 	alen = UUID_LEN;
6318d94f651SGordon Ross 	u8p = NULL;
6328d94f651SGordon Ross 	rc = nvlist_lookup_uint8_array(nvl, "file_guid", &u8p, &alen);
6338d94f651SGordon Ross 	if (rc != 0 || alen != UUID_LEN) {
6348d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) bad file_guid",
6358d94f651SGordon Ross 		    tree->t_resource, str_node->od_name);
6368d94f651SGordon Ross 		goto errout;
6378d94f651SGordon Ross 	}
6388d94f651SGordon Ross 	bcopy(u8p, op->create_guid, UUID_LEN);
6398d94f651SGordon Ross 
6408d94f651SGordon Ross 	/*
6418d94f651SGordon Ross 	 * client uuid (identifies the client)
6428d94f651SGordon Ross 	 */
6438d94f651SGordon Ross 	alen = UUID_LEN;
6448d94f651SGordon Ross 	u8p = NULL;
6458d94f651SGordon Ross 	rc = nvlist_lookup_uint8_array(nvl, "client_uuid", &u8p, &alen);
6468d94f651SGordon Ross 	if (rc != 0 || alen != UUID_LEN) {
6478d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) no client_uuid",
6488d94f651SGordon Ross 		    tree->t_resource, str_node->od_name);
6498d94f651SGordon Ross 		goto errout;
6508d94f651SGordon Ross 	}
6518d94f651SGordon Ross 	bcopy(u8p, client_uuid, UUID_LEN);
6528d94f651SGordon Ross 
6538d94f651SGordon Ross 	/*
6548d94f651SGordon Ross 	 * Lease key (optional)
6558d94f651SGordon Ross 	 */
6568d94f651SGordon Ross 	alen = SMB_LEASE_KEY_SZ;
6578d94f651SGordon Ross 	u8p = NULL;
6588d94f651SGordon Ross 	rc = nvlist_lookup_uint8_array(nvl, "lease_uuid", &u8p, &alen);
6598d94f651SGordon Ross 	if (rc == 0) {
6608d94f651SGordon Ross 		bcopy(u8p, op->lease_key, UUID_LEN);
6618d94f651SGordon Ross 		(void) nvlist_lookup_uint32(nvl,
6628d94f651SGordon Ross 		    "lease_state", &op->lease_state);
6638d94f651SGordon Ross 		(void) nvlist_lookup_uint16(nvl,
6648d94f651SGordon Ross 		    "lease_epoch", &op->lease_epoch);
6658d94f651SGordon Ross 		(void) nvlist_lookup_uint16(nvl,
6668d94f651SGordon Ross 		    "lease_version", &op->lease_version);
6678d94f651SGordon Ross 		have_lease = B_TRUE;
6688d94f651SGordon Ross 	} else {
6698d94f651SGordon Ross 		(void) nvlist_lookup_uint32(nvl,
6708d94f651SGordon Ross 		    "oplock_state", &op->op_oplock_state);
6718d94f651SGordon Ross 	}
6728d94f651SGordon Ross 
6738d94f651SGordon Ross 	/*
6748d94f651SGordon Ross 	 * Done getting what we need from the NV list.
6758d94f651SGordon Ross 	 * (re)open the file
6768d94f651SGordon Ross 	 */
6778d94f651SGordon Ross 	status = smb_common_open(sr);
6788d94f651SGordon Ross 	if (status != 0) {
6798d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) open failed 0x%x",
6808d94f651SGordon Ross 		    tree->t_resource, str_node->od_name, status);
6818d94f651SGordon Ross 		(void) smb_node_set_delete_on_close(str_node, kcr, 0);
6828d94f651SGordon Ross 		goto errout;
6838d94f651SGordon Ross 	}
6848d94f651SGordon Ross 	of = sr->fid_ofile;
6858d94f651SGordon Ross 	did_open = B_TRUE;
6868d94f651SGordon Ross 
6878d94f651SGordon Ross 	/*
6888d94f651SGordon Ross 	 * Now restore the rest of the SMB2 level state.
6898d94f651SGordon Ross 	 * See smb2_create after smb_common_open
6908d94f651SGordon Ross 	 */
6918d94f651SGordon Ross 
6928d94f651SGordon Ross 	/*
6938d94f651SGordon Ross 	 * Setup of->f_cr with owner SID
6948d94f651SGordon Ross 	 */
6958d94f651SGordon Ross 	rc = smb2_dh_import_cred(of, sidstr);
6968d94f651SGordon Ross 	if (rc != 0) {
6978d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) import cred failed",
6988d94f651SGordon Ross 		    tree->t_resource, str_node->od_name);
6998d94f651SGordon Ross 		goto errout;
7008d94f651SGordon Ross 	}
7018d94f651SGordon Ross 
7028d94f651SGordon Ross 	/*
7038d94f651SGordon Ross 	 * Use the persist ID we previously assigned.
7048d94f651SGordon Ross 	 * Like smb_ofile_set_persistid_ph()
7058d94f651SGordon Ross 	 */
7068d94f651SGordon Ross 	rc = smb_ofile_insert_persistid(of, persist_id);
7078d94f651SGordon Ross 	if (rc != 0) {
7088d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) "
7098d94f651SGordon Ross 		    "insert_persistid rc=%d",
7108d94f651SGordon Ross 		    tree->t_resource, str_node->od_name, rc);
7118d94f651SGordon Ross 		goto errout;
7128d94f651SGordon Ross 	}
7138d94f651SGordon Ross 
7148d94f651SGordon Ross 	/*
7158d94f651SGordon Ross 	 * Like smb2_lease_create()
7168d94f651SGordon Ross 	 *
7178d94f651SGordon Ross 	 * Lease state is stored in each persistent handle, but
7188d94f651SGordon Ross 	 * only one handle has the state we want.  As we import
7198d94f651SGordon Ross 	 * each handle, "upgrade" the lease if the handle we're
7208d94f651SGordon Ross 	 * importing has a "better" lease state (higher epoch or
7218d94f651SGordon Ross 	 * more cache rights).  After all handles are imported,
7228d94f651SGordon Ross 	 * that will get the lease to the right state.
7238d94f651SGordon Ross 	 */
7248d94f651SGordon Ross 	if (have_lease) {
7258d94f651SGordon Ross 		smb_lease_t *ls;
7268d94f651SGordon Ross 		status = smb2_lease_create(sr, client_uuid);
7278d94f651SGordon Ross 		if (status != 0) {
7288d94f651SGordon Ross 			cmn_err(CE_NOTE, "CA import (%s/%s) get lease 0x%x",
7298d94f651SGordon Ross 			    tree->t_resource, str_node->od_name, status);
7308d94f651SGordon Ross 			goto errout;
7318d94f651SGordon Ross 		}
7328d94f651SGordon Ross 		ls = of->f_lease;
7338d94f651SGordon Ross 
7348d94f651SGordon Ross 		/* Use most current "epoch". */
7358d94f651SGordon Ross 		mutex_enter(&ls->ls_mutex);
7368d94f651SGordon Ross 		if (ls->ls_epoch < op->lease_epoch)
7378d94f651SGordon Ross 			ls->ls_epoch = op->lease_epoch;
7388d94f651SGordon Ross 		mutex_exit(&ls->ls_mutex);
7398d94f651SGordon Ross 
7408d94f651SGordon Ross 		/*
7418d94f651SGordon Ross 		 * Get the lease (and oplock)
7428d94f651SGordon Ross 		 * uses op->lease_state
7438d94f651SGordon Ross 		 */
7448d94f651SGordon Ross 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
7458d94f651SGordon Ross 		smb2_lease_acquire(sr);
7468d94f651SGordon Ross 
7478d94f651SGordon Ross 	} else {
7488d94f651SGordon Ross 		/*
7498d94f651SGordon Ross 		 * No lease; maybe get an oplock
7508d94f651SGordon Ross 		 * uses: op->op_oplock_level
7518d94f651SGordon Ross 		 */
7528d94f651SGordon Ross 		if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
7538d94f651SGordon Ross 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
7548d94f651SGordon Ross 		} else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
7558d94f651SGordon Ross 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
7568d94f651SGordon Ross 		} else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
7578d94f651SGordon Ross 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
7588d94f651SGordon Ross 		} else {
7598d94f651SGordon Ross 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
7608d94f651SGordon Ross 		}
7618d94f651SGordon Ross 		smb2_oplock_acquire(sr);
7628d94f651SGordon Ross 	}
7638d94f651SGordon Ross 
7648d94f651SGordon Ross 	/*
7658d94f651SGordon Ross 	 * Byte range locks
7668d94f651SGordon Ross 	 */
7678d94f651SGordon Ross 	alen = 0;
7688d94f651SGordon Ross 	u64p = NULL;
7698d94f651SGordon Ross 	if (nvlist_lookup_uint64_array(nvl, "locks", &u64p, &alen) == 0) {
7708d94f651SGordon Ross 		uint_t	i;
7718d94f651SGordon Ross 		uint_t nlocks = alen / 3;
7728d94f651SGordon Ross 		struct nvlk	*nlp;
7738d94f651SGordon Ross 
7748d94f651SGordon Ross 		nlp = (struct nvlk *)u64p;
7758d94f651SGordon Ross 		for (i = 0; i < nlocks; i++) {
7768d94f651SGordon Ross 			status = smb_lock_range(
7778d94f651SGordon Ross 			    sr,
7788d94f651SGordon Ross 			    nlp->lk_start,
7798d94f651SGordon Ross 			    nlp->lk_len,
7808d94f651SGordon Ross 			    nlp->lk_pid,
7818d94f651SGordon Ross 			    nlp->lk_type,
7828d94f651SGordon Ross 			    0);
7838d94f651SGordon Ross 			if (status != 0) {
7848d94f651SGordon Ross 				cmn_err(CE_NOTE, "CA import (%s/%s) "
7858d94f651SGordon Ross 				    "get lock %d failed 0x%x",
7868d94f651SGordon Ross 				    tree->t_resource,
7878d94f651SGordon Ross 				    str_node->od_name,
7888d94f651SGordon Ross 				    i, status);
7898d94f651SGordon Ross 			}
7908d94f651SGordon Ross 			nlp++;
7918d94f651SGordon Ross 		}
7928d94f651SGordon Ross 	}
7938d94f651SGordon Ross 	alen = SMB_OFILE_LSEQ_MAX;
7948d94f651SGordon Ross 	u8p = NULL;
7958d94f651SGordon Ross 	if (nvlist_lookup_uint8_array(nvl, "lockseq", &u8p, &alen) == 0) {
7968d94f651SGordon Ross 		if (alen != SMB_OFILE_LSEQ_MAX) {
7978d94f651SGordon Ross 			cmn_err(CE_NOTE, "CA import (%s/%s) "
7988d94f651SGordon Ross 			    "get lockseq bad len=%d",
7998d94f651SGordon Ross 			    tree->t_resource,
8008d94f651SGordon Ross 			    str_node->od_name,
8018d94f651SGordon Ross 			    alen);
8028d94f651SGordon Ross 		} else {
8038d94f651SGordon Ross 			mutex_enter(&of->f_mutex);
8048d94f651SGordon Ross 			bcopy(u8p, of->f_lock_seq, alen);
8058d94f651SGordon Ross 			mutex_exit(&of->f_mutex);
8068d94f651SGordon Ross 		}
8078d94f651SGordon Ross 	}
8088d94f651SGordon Ross 
8098d94f651SGordon Ross 	/*
8108d94f651SGordon Ross 	 * Optional "sticky" times (set pending attributes)
8118d94f651SGordon Ross 	 */
8128d94f651SGordon Ross 	mutex_enter(&of->f_mutex);
8138d94f651SGordon Ross 	pa = &of->f_pending_attr;
8148d94f651SGordon Ross 	if (nvlist_lookup_hrtime(nvl, "atime", &hrt) == 0) {
8158d94f651SGordon Ross 		hrt2ts(hrt, &pa->sa_vattr.va_atime);
8168d94f651SGordon Ross 		pa->sa_mask |= SMB_AT_ATIME;
8178d94f651SGordon Ross 	}
8188d94f651SGordon Ross 	if (nvlist_lookup_hrtime(nvl, "mtime", &hrt) == 0) {
8198d94f651SGordon Ross 		hrt2ts(hrt, &pa->sa_vattr.va_mtime);
8208d94f651SGordon Ross 		pa->sa_mask |= SMB_AT_MTIME;
8218d94f651SGordon Ross 	}
8228d94f651SGordon Ross 	if (nvlist_lookup_hrtime(nvl, "ctime", &hrt) == 0) {
8238d94f651SGordon Ross 		hrt2ts(hrt, &pa->sa_vattr.va_ctime);
8248d94f651SGordon Ross 		pa->sa_mask |= SMB_AT_CTIME;
8258d94f651SGordon Ross 	}
8268d94f651SGordon Ross 	mutex_exit(&of->f_mutex);
8278d94f651SGordon Ross 
8288d94f651SGordon Ross 	/*
8298d94f651SGordon Ross 	 * Make durable and persistent.
8308d94f651SGordon Ross 	 * See smb2_dh_make_persistent()
8318d94f651SGordon Ross 	 */
8328d94f651SGordon Ross 	of->dh_vers = SMB2_DURABLE_V2;
8338d94f651SGordon Ross 	bcopy(op->create_guid, of->dh_create_guid, UUID_LEN);
8348d94f651SGordon Ross 	of->dh_persist = B_TRUE;
8358d94f651SGordon Ross 	of->dh_nvfile = str_node;
8368d94f651SGordon Ross 	smb_node_ref(str_node);
8378d94f651SGordon Ross 	of->dh_nvlist = nvl;
8388d94f651SGordon Ross 	nvl = NULL;
8398d94f651SGordon Ross 
8408d94f651SGordon Ross 	/*
8418d94f651SGordon Ross 	 * Now make it state orphaned...
8428d94f651SGordon Ross 	 * See smb_ofile_drop(), then
8438d94f651SGordon Ross 	 * smb_ofile_save_dh()
8448d94f651SGordon Ross 	 */
8458d94f651SGordon Ross 	mutex_enter(&of->f_mutex);
8468d94f651SGordon Ross 	of->f_state = SMB_OFILE_STATE_SAVE_DH;
8478d94f651SGordon Ross 	of->dh_timeout_offset = MSEC2NSEC(smb2_persist_timeout);
8488d94f651SGordon Ross 	mutex_exit(&of->f_mutex);
8498d94f651SGordon Ross 
8508d94f651SGordon Ross 	/*
8518d94f651SGordon Ross 	 * Finished!
8528d94f651SGordon Ross 	 */
8538d94f651SGordon Ross 	return (of);
8548d94f651SGordon Ross 
8558d94f651SGordon Ross errout:
8568d94f651SGordon Ross 	if (did_open) {
8578d94f651SGordon Ross 		smb_ofile_close(of, 0);
8588d94f651SGordon Ross 		smb_ofile_release(of);
8598d94f651SGordon Ross 	} else {
8608d94f651SGordon Ross 		ASSERT(of == NULL);
8618d94f651SGordon Ross 	}
8628d94f651SGordon Ross 
8638d94f651SGordon Ross 	if (nvl != NULL)
8648d94f651SGordon Ross 		nvlist_free(nvl);
8658d94f651SGordon Ross 
8668d94f651SGordon Ross 	return (NULL);
8678d94f651SGordon Ross }
8688d94f651SGordon Ross 
8698d94f651SGordon Ross static int
smb2_dh_read_nvlist(smb_request_t * sr,smb_node_t * node,char * fbuf,struct nvlist ** nvlpp)8708d94f651SGordon Ross smb2_dh_read_nvlist(smb_request_t *sr, smb_node_t *node,
871897907ceSGordon Ross     char *fbuf, struct nvlist **nvlpp)
8728d94f651SGordon Ross {
8738d94f651SGordon Ross 	smb_attr_t	attr;
8748d94f651SGordon Ross 	iovec_t		iov;
8758d94f651SGordon Ross 	uio_t		uio;
876174aa483SGordon Ross 	smb_tree_t	*tree = sr->tid_tree;
8778d94f651SGordon Ross 	cred_t		*kcr = zone_kcred();
8788d94f651SGordon Ross 	size_t		flen;
8798d94f651SGordon Ross 	int		rc;
8808d94f651SGordon Ross 
8818d94f651SGordon Ross 	bzero(&attr, sizeof (attr));
8828d94f651SGordon Ross 	attr.sa_mask = SMB_AT_SIZE;
8838d94f651SGordon Ross 	rc = smb_node_getattr(NULL, node, kcr, NULL, &attr);
8848d94f651SGordon Ross 	if (rc != 0) {
8858d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) getattr rc=%d",
886174aa483SGordon Ross 		    tree->t_resource, node->od_name, rc);
8878d94f651SGordon Ross 		return (rc);
8888d94f651SGordon Ross 	}
8898d94f651SGordon Ross 
8908d94f651SGordon Ross 	if (attr.sa_vattr.va_size < 4 ||
891897907ceSGordon Ross 	    attr.sa_vattr.va_size > smb2_dh_max_cah_size) {
8928d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) bad size=%" PRIu64,
893174aa483SGordon Ross 		    tree->t_resource, node->od_name,
8948d94f651SGordon Ross 		    (uint64_t)attr.sa_vattr.va_size);
8958d94f651SGordon Ross 		return (EINVAL);
8968d94f651SGordon Ross 	}
8978d94f651SGordon Ross 	flen = (size_t)attr.sa_vattr.va_size;
8988d94f651SGordon Ross 
8998d94f651SGordon Ross 	bzero(&uio, sizeof (uio));
900897907ceSGordon Ross 	iov.iov_base = fbuf;
9018d94f651SGordon Ross 	iov.iov_len = flen;
9028d94f651SGordon Ross 	uio.uio_iov = &iov;
9038d94f651SGordon Ross 	uio.uio_iovcnt = 1;
9048d94f651SGordon Ross 	uio.uio_resid = flen;
9058d94f651SGordon Ross 	uio.uio_segflg = UIO_SYSSPACE;
9068d94f651SGordon Ross 	uio.uio_extflg = UIO_COPY_DEFAULT;
907dfa42fabSMatt Barden 	rc = smb_fsop_read(sr, kcr, node, NULL, &uio, 0);
9088d94f651SGordon Ross 	if (rc != 0) {
9098d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) read, rc=%d",
910174aa483SGordon Ross 		    tree->t_resource, node->od_name, rc);
9118d94f651SGordon Ross 		return (rc);
9128d94f651SGordon Ross 	}
9138d94f651SGordon Ross 	if (uio.uio_resid != 0) {
9148d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) short read",
915174aa483SGordon Ross 		    tree->t_resource, node->od_name);
9168d94f651SGordon Ross 		return (EIO);
9178d94f651SGordon Ross 	}
9188d94f651SGordon Ross 
919897907ceSGordon Ross 	rc = nvlist_unpack(fbuf, flen, nvlpp, KM_SLEEP);
9208d94f651SGordon Ross 	if (rc != 0) {
9218d94f651SGordon Ross 		cmn_err(CE_NOTE, "CA import (%s/%s) unpack, rc=%d",
922174aa483SGordon Ross 		    tree->t_resource, node->od_name, rc);
9238d94f651SGordon Ross 		return (rc);
9248d94f651SGordon Ross 	}
9258d94f651SGordon Ross 
9268d94f651SGordon Ross 	return (0);
9278d94f651SGordon Ross }
9288d94f651SGordon Ross 
9298d94f651SGordon Ross /*
9308d94f651SGordon Ross  * Setup a vestigial credential in of->f_cr just good enough for
9318d94f651SGordon Ross  * smb_is_same_user to determine if the caller owned this ofile.
9328d94f651SGordon Ross  * At reconnect, of->f_cr will be replaced with the caller's.
9338d94f651SGordon Ross  */
9348d94f651SGordon Ross static int
smb2_dh_import_cred(smb_ofile_t * of,char * sidstr)9358d94f651SGordon Ross smb2_dh_import_cred(smb_ofile_t *of, char *sidstr)
9368d94f651SGordon Ross {
9378d94f651SGordon Ross #ifdef	_FAKE_KERNEL
9388d94f651SGordon Ross 	_NOTE(ARGUNUSED(sidstr))
9398d94f651SGordon Ross 	/* fksmbd doesn't have real credentials. */
9408d94f651SGordon Ross 	of->f_cr = CRED();
9418d94f651SGordon Ross 	crhold(of->f_cr);
9428d94f651SGordon Ross #else
9438d94f651SGordon Ross 	char tmpstr[SMB_SID_STRSZ];
9448d94f651SGordon Ross 	ksid_t		ksid;
9458d94f651SGordon Ross 	cred_t		*cr, *oldcr;
9468d94f651SGordon Ross 	int		rc;
9478d94f651SGordon Ross 
9488d94f651SGordon Ross 	(void) strlcpy(tmpstr, sidstr, sizeof (tmpstr));
9498d94f651SGordon Ross 	bzero(&ksid, sizeof (ksid));
9508d94f651SGordon Ross 
9518d94f651SGordon Ross 	rc = smb_sid_splitstr(tmpstr, &ksid.ks_rid);
9528d94f651SGordon Ross 	if (rc != 0)
9538d94f651SGordon Ross 		return (rc);
9548d94f651SGordon Ross 	cr = crget();
9558d94f651SGordon Ross 
9568d94f651SGordon Ross 	ksid.ks_domain = ksid_lookupdomain(tmpstr);
9578d94f651SGordon Ross 	crsetsid(cr, &ksid, KSID_USER);
9588d94f651SGordon Ross 	ksiddomain_hold(ksid.ks_domain);
9598d94f651SGordon Ross 	crsetsid(cr, &ksid, KSID_OWNER);
9608d94f651SGordon Ross 
9618d94f651SGordon Ross 	/*
9628d94f651SGordon Ross 	 * Just to avoid leaving the KSID_GROUP slot NULL,
9638d94f651SGordon Ross 	 * put the "everyone" SID there (S-1-1-0).
9648d94f651SGordon Ross 	 */
9658d94f651SGordon Ross 	ksid.ks_domain = ksid_lookupdomain("S-1-1");
9668d94f651SGordon Ross 	ksid.ks_rid = 0;
9678d94f651SGordon Ross 	crsetsid(cr, &ksid, KSID_GROUP);
9688d94f651SGordon Ross 
9698d94f651SGordon Ross 	oldcr = of->f_cr;
9708d94f651SGordon Ross 	of->f_cr = cr;
9718d94f651SGordon Ross 	if (oldcr != NULL)
9728d94f651SGordon Ross 		crfree(oldcr);
9738d94f651SGordon Ross #endif
9748d94f651SGordon Ross 
9758d94f651SGordon Ross 	return (0);
9768d94f651SGordon Ross }
9778d94f651SGordon Ross 
9788d94f651SGordon Ross /*
9798d94f651SGordon Ross  * Set Delete-on-Close (DoC) on the persistent state file so it will be
9808d94f651SGordon Ross  * removed when the last ref. goes away (in smb2_dh_close_persistent).
9818d94f651SGordon Ross  *
9828d94f651SGordon Ross  * This is called in just two places:
9838d94f651SGordon Ross  * (1) SMB2_close request -- client tells us to destroy the handle.
9848d94f651SGordon Ross  * (2) smb2_dh_expire -- client has forgotten about this handle.
9858d94f651SGordon Ross  * All other (server-initiated) close calls should leave these
9868d94f651SGordon Ross  * persistent state files in the file system.
9878d94f651SGordon Ross  */
9888d94f651SGordon Ross void
smb2_dh_setdoc_persistent(smb_ofile_t * of)9898d94f651SGordon Ross smb2_dh_setdoc_persistent(smb_ofile_t *of)
9908d94f651SGordon Ross {
9918d94f651SGordon Ross 	smb_node_t *strnode;
9928d94f651SGordon Ross 	uint32_t status;
9938d94f651SGordon Ross 
9948d94f651SGordon Ross 	mutex_enter(&of->dh_nvlock);
9958d94f651SGordon Ross 	if ((strnode = of->dh_nvfile) != NULL)
9968d94f651SGordon Ross 		smb_node_ref(strnode);
9978d94f651SGordon Ross 	mutex_exit(&of->dh_nvlock);
9988d94f651SGordon Ross 
9998d94f651SGordon Ross 	if (strnode != NULL) {
10008d94f651SGordon Ross 		status = smb_node_set_delete_on_close(strnode,
10018d94f651SGordon Ross 		    zone_kcred(), SMB_CASE_SENSITIVE);
10028d94f651SGordon Ross 		if (status != 0) {
10038d94f651SGordon Ross 			cmn_err(CE_WARN, "Can't set DoC on CA file: %s",
10048d94f651SGordon Ross 			    strnode->od_name);
10058d94f651SGordon Ross 			DTRACE_PROBE1(rm__ca__err, smb_ofile_t *, of);
10068d94f651SGordon Ross 		}
10078d94f651SGordon Ross 		smb_node_release(strnode);
10088d94f651SGordon Ross 	}
10098d94f651SGordon Ross }
10108d94f651SGordon Ross 
10118d94f651SGordon Ross /*
10128d94f651SGordon Ross  * During ofile close, free the persistent handle state nvlist and
10138d94f651SGordon Ross  * drop our reference to the state file node (which may unlink it
10148d94f651SGordon Ross  * if smb2_dh_setdoc_persistent was called).
10158d94f651SGordon Ross  */
10168d94f651SGordon Ross void
smb2_dh_close_persistent(smb_ofile_t * of)10178d94f651SGordon Ross smb2_dh_close_persistent(smb_ofile_t *of)
10188d94f651SGordon Ross {
10198d94f651SGordon Ross 	smb_node_t	*strnode;
10208d94f651SGordon Ross 	struct nvlist	*nvl;
10218d94f651SGordon Ross 
10228d94f651SGordon Ross 	/*
10238d94f651SGordon Ross 	 * Clear out nvlist and stream linkage
10248d94f651SGordon Ross 	 */
10258d94f651SGordon Ross 	mutex_enter(&of->dh_nvlock);
10268d94f651SGordon Ross 	strnode = of->dh_nvfile;
10278d94f651SGordon Ross 	of->dh_nvfile = NULL;
10288d94f651SGordon Ross 	nvl = of->dh_nvlist;
10298d94f651SGordon Ross 	of->dh_nvlist = NULL;
10308d94f651SGordon Ross 	mutex_exit(&of->dh_nvlock);
10318d94f651SGordon Ross 
10328d94f651SGordon Ross 	if (nvl != NULL)
10338d94f651SGordon Ross 		nvlist_free(nvl);
10348d94f651SGordon Ross 
10358d94f651SGordon Ross 	if (strnode != NULL)
10368d94f651SGordon Ross 		smb_node_release(strnode);
10378d94f651SGordon Ross }
10388d94f651SGordon Ross 
10398d94f651SGordon Ross /*
10408d94f651SGordon Ross  * Make this durable handle persistent.
10418d94f651SGordon Ross  * If we succeed, set of->dh_persist = TRUE.
10428d94f651SGordon Ross  */
10438d94f651SGordon Ross int
smb2_dh_make_persistent(smb_request_t * sr,smb_ofile_t * of)10448d94f651SGordon Ross smb2_dh_make_persistent(smb_request_t *sr, smb_ofile_t *of)
10458d94f651SGordon Ross {
10468d94f651SGordon Ross 	char		fname[DH_SN_SIZE];
10478d94f651SGordon Ross 	char		sidstr[SMB_SID_STRSZ];
10488d94f651SGordon Ross 	smb_attr_t	attr;
10498d94f651SGordon Ross 	smb_arg_open_t	*op = &sr->arg.open;
10508d94f651SGordon Ross 	cred_t		*kcr = zone_kcred();
10518d94f651SGordon Ross 	smb_node_t	*dnode = of->f_tree->t_snode;
10528d94f651SGordon Ross 	smb_node_t	*fnode = NULL;
10538d94f651SGordon Ross 	ksid_t		*ksid;
10548d94f651SGordon Ross 	int		rc;
10558d94f651SGordon Ross 
10568d94f651SGordon Ross 	ASSERT(of->dh_nvfile == NULL);
10578d94f651SGordon Ross 
10588d94f651SGordon Ross 	/*
10598d94f651SGordon Ross 	 * Create the persistent handle nvlist file.
10608d94f651SGordon Ross 	 * It's a named stream in the share root.
10618d94f651SGordon Ross 	 */
10628d94f651SGordon Ross 	smb2_dh_make_stream_name(fname, sizeof (fname), of->f_persistid);
10638d94f651SGordon Ross 
10648d94f651SGordon Ross 	bzero(&attr, sizeof (attr));
10658d94f651SGordon Ross 	attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE | SMB_AT_SIZE;
10668d94f651SGordon Ross 	attr.sa_vattr.va_type = VREG;
10678d94f651SGordon Ross 	attr.sa_vattr.va_mode = 0640;
10688d94f651SGordon Ross 	attr.sa_vattr.va_size = 4;
10698d94f651SGordon Ross 	rc = smb_fsop_create(sr, kcr, dnode, fname, &attr, &fnode);
10708d94f651SGordon Ross 	if (rc != 0)
10718d94f651SGordon Ross 		return (rc);
10728d94f651SGordon Ross 
10738d94f651SGordon Ross 	mutex_enter(&of->dh_nvlock);
10748d94f651SGordon Ross 
10758d94f651SGordon Ross 	/* fnode is held. rele in smb2_dh_close_persistent */
10768d94f651SGordon Ross 	of->dh_nvfile = fnode;
10778d94f651SGordon Ross 	(void) nvlist_alloc(&of->dh_nvlist, NV_UNIQUE_NAME, KM_SLEEP);
10788d94f651SGordon Ross 
10798d94f651SGordon Ross 	/*
10808d94f651SGordon Ross 	 * Want the ksid as a string
10818d94f651SGordon Ross 	 */
10828d94f651SGordon Ross 	ksid = crgetsid(of->f_user->u_cred, KSID_USER);
10838d94f651SGordon Ross 	(void) snprintf(sidstr, sizeof (sidstr), "%s-%u",
10848d94f651SGordon Ross 	    ksid->ks_domain->kd_name, ksid->ks_rid);
10858d94f651SGordon Ross 
10868d94f651SGordon Ross 	/*
10878d94f651SGordon Ross 	 * Fill in the fixed parts of the nvlist
10888d94f651SGordon Ross 	 */
10898d94f651SGordon Ross 	(void) nvlist_add_uint32(of->dh_nvlist,
10908d94f651SGordon Ross 	    "info_version", smb2_ca_info_version);
10918d94f651SGordon Ross 	(void) nvlist_add_string(of->dh_nvlist,
10928d94f651SGordon Ross 	    "owner_sid", sidstr);
10938d94f651SGordon Ross 	(void) nvlist_add_string(of->dh_nvlist,
10948d94f651SGordon Ross 	    "share_name", of->f_tree->t_sharename);
10958d94f651SGordon Ross 	(void) nvlist_add_uint64(of->dh_nvlist,
10968d94f651SGordon Ross 	    "file_persistid", of->f_persistid);
10978d94f651SGordon Ross 	(void) nvlist_add_uint8_array(of->dh_nvlist,
10988d94f651SGordon Ross 	    "file_guid", of->dh_create_guid, UUID_LEN);
10998d94f651SGordon Ross 	(void) nvlist_add_string(of->dh_nvlist,
11008d94f651SGordon Ross 	    "client_ipaddr", sr->session->ip_addr_str);
11018d94f651SGordon Ross 	(void) nvlist_add_uint8_array(of->dh_nvlist,
11028d94f651SGordon Ross 	    "client_uuid", sr->session->clnt_uuid, UUID_LEN);
11038d94f651SGordon Ross 	(void) nvlist_add_string(of->dh_nvlist,
11048d94f651SGordon Ross 	    "path_name", op->fqi.fq_path.pn_path);
11058d94f651SGordon Ross 	(void) nvlist_add_uint32(of->dh_nvlist,
11068d94f651SGordon Ross 	    "granted_access", of->f_granted_access);
11078d94f651SGordon Ross 	(void) nvlist_add_uint32(of->dh_nvlist,
11088d94f651SGordon Ross 	    "share_access", of->f_share_access);
11098d94f651SGordon Ross 	(void) nvlist_add_uint32(of->dh_nvlist,
11108d94f651SGordon Ross 	    "create_options", of->f_create_options);
11118d94f651SGordon Ross 	if (of->f_lease != NULL) {
11128d94f651SGordon Ross 		smb_lease_t *ls = of->f_lease;
11138d94f651SGordon Ross 		(void) nvlist_add_uint8_array(of->dh_nvlist,
11148d94f651SGordon Ross 		    "lease_uuid", ls->ls_key, 16);
11158d94f651SGordon Ross 		(void) nvlist_add_uint32(of->dh_nvlist,
11168d94f651SGordon Ross 		    "lease_state", ls->ls_state);
11178d94f651SGordon Ross 		(void) nvlist_add_uint16(of->dh_nvlist,
11188d94f651SGordon Ross 		    "lease_epoch", ls->ls_epoch);
11198d94f651SGordon Ross 		(void) nvlist_add_uint16(of->dh_nvlist,
11208d94f651SGordon Ross 		    "lease_version", ls->ls_version);
11218d94f651SGordon Ross 	} else {
11228d94f651SGordon Ross 		(void) nvlist_add_uint32(of->dh_nvlist,
11238d94f651SGordon Ross 		    "oplock_state", of->f_oplock.og_state);
11248d94f651SGordon Ross 	}
11258d94f651SGordon Ross 	mutex_exit(&of->dh_nvlock);
11268d94f651SGordon Ross 
11278d94f651SGordon Ross 	smb2_dh_update_locks(sr, of);
11288d94f651SGordon Ross 
11298d94f651SGordon Ross 	/* Tell sr update nvlist file */
11308d94f651SGordon Ross 	sr->dh_nvl_dirty = B_TRUE;
11318d94f651SGordon Ross 
11328d94f651SGordon Ross 	return (0);
11338d94f651SGordon Ross }
11348d94f651SGordon Ross 
11358d94f651SGordon Ross void
smb2_dh_update_nvfile(smb_request_t * sr)11368d94f651SGordon Ross smb2_dh_update_nvfile(smb_request_t *sr)
11378d94f651SGordon Ross {
11388d94f651SGordon Ross 	smb_attr_t	attr;
11398d94f651SGordon Ross 	iovec_t		iov;
11408d94f651SGordon Ross 	uio_t		uio;
11418d94f651SGordon Ross 	smb_ofile_t	*of = sr->fid_ofile;
11428d94f651SGordon Ross 	cred_t		*kcr = zone_kcred();
11438d94f651SGordon Ross 	char		*buf = NULL;
11448d94f651SGordon Ross 	size_t		buflen = 0;
11458d94f651SGordon Ross 	uint32_t	wcnt;
11468d94f651SGordon Ross 	int		rc;
11478d94f651SGordon Ross 
11488d94f651SGordon Ross 	if (of == NULL || of->dh_persist == B_FALSE)
11498d94f651SGordon Ross 		return;
11508d94f651SGordon Ross 
11518d94f651SGordon Ross 	mutex_enter(&of->dh_nvlock);
11528d94f651SGordon Ross 	if (of->dh_nvlist == NULL || of->dh_nvfile == NULL) {
11538d94f651SGordon Ross 		mutex_exit(&of->dh_nvlock);
11548d94f651SGordon Ross 		return;
11558d94f651SGordon Ross 	}
11568d94f651SGordon Ross 
11578d94f651SGordon Ross 	rc = nvlist_size(of->dh_nvlist, &buflen, NV_ENCODE_XDR);
11588d94f651SGordon Ross 	if (rc != 0)
11598d94f651SGordon Ross 		goto out;
11608d94f651SGordon Ross 	buf = kmem_zalloc(buflen, KM_SLEEP);
11618d94f651SGordon Ross 
11628d94f651SGordon Ross 	rc = nvlist_pack(of->dh_nvlist, &buf, &buflen,
11638d94f651SGordon Ross 	    NV_ENCODE_XDR, KM_SLEEP);
11648d94f651SGordon Ross 	if (rc != 0)
11658d94f651SGordon Ross 		goto out;
11668d94f651SGordon Ross 
11678d94f651SGordon Ross 	bzero(&attr, sizeof (attr));
11688d94f651SGordon Ross 	attr.sa_mask = SMB_AT_SIZE;
11698d94f651SGordon Ross 	attr.sa_vattr.va_size = buflen;
11708d94f651SGordon Ross 	rc = smb_node_setattr(sr, of->dh_nvfile, kcr, NULL, &attr);
11718d94f651SGordon Ross 	if (rc != 0)
11728d94f651SGordon Ross 		goto out;
11738d94f651SGordon Ross 
11748d94f651SGordon Ross 	bzero(&uio, sizeof (uio));
11758d94f651SGordon Ross 	iov.iov_base = (void *) buf;
11768d94f651SGordon Ross 	iov.iov_len = buflen;
11778d94f651SGordon Ross 	uio.uio_iov = &iov;
11788d94f651SGordon Ross 	uio.uio_iovcnt = 1;
11798d94f651SGordon Ross 	uio.uio_resid = buflen;
11808d94f651SGordon Ross 	uio.uio_segflg = UIO_SYSSPACE;
11818d94f651SGordon Ross 	uio.uio_extflg = UIO_COPY_DEFAULT;
11828d94f651SGordon Ross 	rc = smb_fsop_write(sr, kcr, of->dh_nvfile,
11838d94f651SGordon Ross 	    NULL, &uio, &wcnt, 0);
11848d94f651SGordon Ross 	if (rc == 0 && wcnt != buflen)
11858d94f651SGordon Ross 		rc = EIO;
11868d94f651SGordon Ross 
11878d94f651SGordon Ross out:
11888d94f651SGordon Ross 	mutex_exit(&of->dh_nvlock);
11898d94f651SGordon Ross 
11908d94f651SGordon Ross 	if (rc != 0) {
11918d94f651SGordon Ross 		cmn_err(CE_WARN,
11928d94f651SGordon Ross 		    "clnt(%s) failed to update persistent handle, rc=%d",
11938d94f651SGordon Ross 		    sr->session->ip_addr_str, rc);
11948d94f651SGordon Ross 	}
11958d94f651SGordon Ross 
11968d94f651SGordon Ross 	if (buf != NULL) {
11978d94f651SGordon Ross 		kmem_free(buf, buflen);
11988d94f651SGordon Ross 	}
11998d94f651SGordon Ross }
12008d94f651SGordon Ross 
12018d94f651SGordon Ross /*
12028d94f651SGordon Ross  * Called after f_oplock (and lease) changes
12038d94f651SGordon Ross  * If lease, update: lease_state, lease_epoch
12048d94f651SGordon Ross  * else (oplock) update: oplock_state
12058d94f651SGordon Ross  */
12068d94f651SGordon Ross void
smb2_dh_update_oplock(smb_request_t * sr,smb_ofile_t * of)12078d94f651SGordon Ross smb2_dh_update_oplock(smb_request_t *sr, smb_ofile_t *of)
12088d94f651SGordon Ross {
12098d94f651SGordon Ross 	smb_lease_t *ls;
12108d94f651SGordon Ross 
12118d94f651SGordon Ross 	mutex_enter(&of->dh_nvlock);
12128d94f651SGordon Ross 	if (of->dh_nvlist == NULL) {
12138d94f651SGordon Ross 		mutex_exit(&of->dh_nvlock);
12148d94f651SGordon Ross 		return;
12158d94f651SGordon Ross 	}
12168d94f651SGordon Ross 
12178d94f651SGordon Ross 	if (of->f_lease != NULL) {
12188d94f651SGordon Ross 		ls = of->f_lease;
12198d94f651SGordon Ross 		(void) nvlist_add_uint32(of->dh_nvlist,
12208d94f651SGordon Ross 		    "lease_state", ls->ls_state);
12218d94f651SGordon Ross 		(void) nvlist_add_uint16(of->dh_nvlist,
12228d94f651SGordon Ross 		    "lease_epoch", ls->ls_epoch);
12238d94f651SGordon Ross 	} else {
12248d94f651SGordon Ross 		(void) nvlist_add_uint32(of->dh_nvlist,
12258d94f651SGordon Ross 		    "oplock_state", of->f_oplock.og_state);
12268d94f651SGordon Ross 	}
12278d94f651SGordon Ross 	mutex_exit(&of->dh_nvlock);
12288d94f651SGordon Ross 
12298d94f651SGordon Ross 	sr->dh_nvl_dirty = B_TRUE;
12308d94f651SGordon Ross }
12318d94f651SGordon Ross 
12328d94f651SGordon Ross /*
12338d94f651SGordon Ross  * Save locks from this ofile as an array of uint64_t, where the
12348d94f651SGordon Ross  * elements are triplets: (start, length, (pid << 32) | type)
12358d94f651SGordon Ross  * Note pid should always be zero for SMB2, so we could use
12368d94f651SGordon Ross  * that 32-bit spot for something else if needed.
12378d94f651SGordon Ross  */
12388d94f651SGordon Ross void
smb2_dh_update_locks(smb_request_t * sr,smb_ofile_t * of)12398d94f651SGordon Ross smb2_dh_update_locks(smb_request_t *sr, smb_ofile_t *of)
12408d94f651SGordon Ross {
12418d94f651SGordon Ross 	uint8_t		lseq[SMB_OFILE_LSEQ_MAX];
12428d94f651SGordon Ross 	smb_node_t	*node = of->f_node;
12438d94f651SGordon Ross 	smb_llist_t	*llist = &node->n_lock_list;
12448d94f651SGordon Ross 	size_t		vec_sz;	// storage size
12458d94f651SGordon Ross 	uint_t		my_cnt = 0;
12468d94f651SGordon Ross 	uint64_t	*vec = NULL;
12478d94f651SGordon Ross 	struct nvlk	*nlp;
12488d94f651SGordon Ross 	smb_lock_t	*lock;
12498d94f651SGordon Ross 
12508d94f651SGordon Ross 	smb_llist_enter(llist, RW_READER);
12518d94f651SGordon Ross 	vec_sz = (llist->ll_count + 1) * sizeof (struct nvlk);
12528d94f651SGordon Ross 	vec = kmem_alloc(vec_sz, KM_SLEEP);
12538d94f651SGordon Ross 	nlp = (struct nvlk *)vec;
12548d94f651SGordon Ross 	for (lock = smb_llist_head(llist);
12558d94f651SGordon Ross 	    lock != NULL;
12568d94f651SGordon Ross 	    lock = smb_llist_next(llist, lock)) {
12578d94f651SGordon Ross 		if (lock->l_file != of)
12588d94f651SGordon Ross 			continue;
12598d94f651SGordon Ross 		nlp->lk_start = lock->l_start;
12608d94f651SGordon Ross 		nlp->lk_len = lock->l_length;
12618d94f651SGordon Ross 		nlp->lk_pid = lock->l_pid;
12628d94f651SGordon Ross 		nlp->lk_type = lock->l_type;
12638d94f651SGordon Ross 		nlp++;
12648d94f651SGordon Ross 		my_cnt++;
12658d94f651SGordon Ross 	}
12668d94f651SGordon Ross 	smb_llist_exit(llist);
12678d94f651SGordon Ross 
12688d94f651SGordon Ross 	mutex_enter(&of->f_mutex);
12698d94f651SGordon Ross 	bcopy(of->f_lock_seq, lseq, sizeof (lseq));
12708d94f651SGordon Ross 	mutex_exit(&of->f_mutex);
12718d94f651SGordon Ross 
12728d94f651SGordon Ross 	mutex_enter(&of->dh_nvlock);
12738d94f651SGordon Ross 	if (of->dh_nvlist != NULL) {
12748d94f651SGordon Ross 
12758d94f651SGordon Ross 		(void) nvlist_add_uint64_array(of->dh_nvlist,
12768d94f651SGordon Ross 		    "locks", vec, my_cnt * 3);
12778d94f651SGordon Ross 
12788d94f651SGordon Ross 		(void) nvlist_add_uint8_array(of->dh_nvlist,
12798d94f651SGordon Ross 		    "lockseq", lseq, sizeof (lseq));
12808d94f651SGordon Ross 	}
12818d94f651SGordon Ross 	mutex_exit(&of->dh_nvlock);
12828d94f651SGordon Ross 
12838d94f651SGordon Ross 	kmem_free(vec, vec_sz);
12848d94f651SGordon Ross 
12858d94f651SGordon Ross 	sr->dh_nvl_dirty = B_TRUE;
12868d94f651SGordon Ross }
12878d94f651SGordon Ross 
12888d94f651SGordon Ross /*
1289b62fa64bSToomas Soome  * Save "sticky" times.
12908d94f651SGordon Ross  */
12918d94f651SGordon Ross void
smb2_dh_update_times(smb_request_t * sr,smb_ofile_t * of,smb_attr_t * attr)12928d94f651SGordon Ross smb2_dh_update_times(smb_request_t *sr, smb_ofile_t *of, smb_attr_t *attr)
12938d94f651SGordon Ross {
12948d94f651SGordon Ross 	hrtime_t t;
12958d94f651SGordon Ross 
12968d94f651SGordon Ross 	mutex_enter(&of->dh_nvlock);
12978d94f651SGordon Ross 	if (of->dh_nvlist == NULL) {
12988d94f651SGordon Ross 		mutex_exit(&of->dh_nvlock);
12998d94f651SGordon Ross 		return;
13008d94f651SGordon Ross 	}
13018d94f651SGordon Ross 
13028d94f651SGordon Ross 	if (attr->sa_mask & SMB_AT_ATIME) {
13038d94f651SGordon Ross 		t = ts2hrt(&attr->sa_vattr.va_atime);
13048d94f651SGordon Ross 		(void) nvlist_add_hrtime(of->dh_nvlist, "atime", t);
1305b62fa64bSToomas Soome 	} else {
1306b62fa64bSToomas Soome 		(void) nvlist_remove_all(of->dh_nvlist, "atime");
13078d94f651SGordon Ross 	}
1308b62fa64bSToomas Soome 
13098d94f651SGordon Ross 	if (attr->sa_mask & SMB_AT_MTIME) {
13108d94f651SGordon Ross 		t = ts2hrt(&attr->sa_vattr.va_mtime);
13118d94f651SGordon Ross 		(void) nvlist_add_hrtime(of->dh_nvlist, "mtime", t);
1312b62fa64bSToomas Soome 	} else {
1313b62fa64bSToomas Soome 		(void) nvlist_remove_all(of->dh_nvlist, "mtime");
13148d94f651SGordon Ross 	}
1315b62fa64bSToomas Soome 
13168d94f651SGordon Ross 	if (attr->sa_mask & SMB_AT_CTIME) {
13178d94f651SGordon Ross 		t = ts2hrt(&attr->sa_vattr.va_ctime);
13188d94f651SGordon Ross 		(void) nvlist_add_hrtime(of->dh_nvlist, "ctime", t);
1319b62fa64bSToomas Soome 	} else {
1320b62fa64bSToomas Soome 		(void) nvlist_remove_all(of->dh_nvlist, "ctime");
13218d94f651SGordon Ross 	}
1322b62fa64bSToomas Soome 
13238d94f651SGordon Ross 	mutex_exit(&of->dh_nvlock);
13248d94f651SGordon Ross 
13258d94f651SGordon Ross 	sr->dh_nvl_dirty = B_TRUE;
13268d94f651SGordon Ross }
13278d94f651SGordon Ross 
13288d94f651SGordon Ross 
1329811599a4SMatt Barden /*
1330811599a4SMatt Barden  * Requirements for ofile found during reconnect (MS-SMB2 3.3.5.9.7):
1331811599a4SMatt Barden  * - security descriptor must match provided descriptor
1332811599a4SMatt Barden  *
1333811599a4SMatt Barden  * If file is leased:
1334811599a4SMatt Barden  * - lease must be requested
1335811599a4SMatt Barden  * - client guid must match session guid
1336811599a4SMatt Barden  * - file name must match given name
1337811599a4SMatt Barden  * - lease key must match provided lease key
1338811599a4SMatt Barden  * If file is not leased:
1339811599a4SMatt Barden  * - Lease must not be requested
1340811599a4SMatt Barden  *
1341811599a4SMatt Barden  * dh_v2 only:
1342811599a4SMatt Barden  * - SMB2_DHANDLE_FLAG_PERSISTENT must be set if dh_persist is true
1343811599a4SMatt Barden  * - SMB2_DHANDLE_FLAG_PERSISTENT must not be set if dh_persist is false
1344811599a4SMatt Barden  * - desired access, share access, and create_options must be ignored
1345811599a4SMatt Barden  * - createguid must match
1346811599a4SMatt Barden  */
1347811599a4SMatt Barden static uint32_t
smb2_dh_reconnect_checks(smb_request_t * sr,smb_ofile_t * of)1348811599a4SMatt Barden smb2_dh_reconnect_checks(smb_request_t *sr, smb_ofile_t *of)
1349811599a4SMatt Barden {
1350811599a4SMatt Barden 	smb_arg_open_t	*op = &sr->sr_open;
135194047d49SGordon Ross 	char *fname;
135294047d49SGordon Ross 
135394047d49SGordon Ross 	if (of->f_lease != NULL) {
135494047d49SGordon Ross 		if (bcmp(sr->session->clnt_uuid,
135594047d49SGordon Ross 		    of->f_lease->ls_clnt, 16) != 0)
135694047d49SGordon Ross 			return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
135794047d49SGordon Ross 
135894047d49SGordon Ross 		if (op->op_oplock_level != SMB2_OPLOCK_LEVEL_LEASE)
135994047d49SGordon Ross 			return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
136094047d49SGordon Ross 		if (bcmp(op->lease_key, of->f_lease->ls_key,
136194047d49SGordon Ross 		    SMB_LEASE_KEY_SZ) != 0)
136294047d49SGordon Ross 			return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
136394047d49SGordon Ross 
136494047d49SGordon Ross 		/*
136594047d49SGordon Ross 		 * We're supposed to check the name is the same.
136694047d49SGordon Ross 		 * Not really necessary to do this, so just do
136794047d49SGordon Ross 		 * minimal effort (check last component)
136894047d49SGordon Ross 		 */
136994047d49SGordon Ross 		fname = strrchr(op->fqi.fq_path.pn_path, '\\');
137094047d49SGordon Ross 		if (fname != NULL)
137194047d49SGordon Ross 			fname++;
137294047d49SGordon Ross 		else
137394047d49SGordon Ross 			fname = op->fqi.fq_path.pn_path;
137494047d49SGordon Ross 		if (smb_strcasecmp(fname, of->f_node->od_name, 0) != 0) {
137594047d49SGordon Ross #ifdef	DEBUG
137694047d49SGordon Ross 			cmn_err(CE_NOTE, "reconnect name <%s> of name <%s>",
137794047d49SGordon Ross 			    fname, of->f_node->od_name);
137894047d49SGordon Ross #endif
137994047d49SGordon Ross 			return (NT_STATUS_INVALID_PARAMETER);
138094047d49SGordon Ross 		}
138194047d49SGordon Ross 	} else {
138294047d49SGordon Ross 		if (op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE)
138394047d49SGordon Ross 			return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
138494047d49SGordon Ross 	}
1385811599a4SMatt Barden 
1386811599a4SMatt Barden 	if (op->dh_vers == SMB2_DURABLE_V2) {
1387811599a4SMatt Barden 		boolean_t op_persist =
1388811599a4SMatt Barden 		    ((op->dh_v2_flags & SMB2_DHANDLE_FLAG_PERSISTENT) != 0);
1389811599a4SMatt Barden 		if (of->dh_persist != op_persist)
1390811599a4SMatt Barden 			return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
1391811599a4SMatt Barden 		if (memcmp(op->create_guid, of->dh_create_guid, UUID_LEN))
1392811599a4SMatt Barden 			return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
1393811599a4SMatt Barden 	}
1394811599a4SMatt Barden 
1395811599a4SMatt Barden 	if (!smb_is_same_user(sr->user_cr, of->f_cr))
1396811599a4SMatt Barden 		return (NT_STATUS_ACCESS_DENIED);
1397811599a4SMatt Barden 
1398811599a4SMatt Barden 	return (NT_STATUS_SUCCESS);
1399811599a4SMatt Barden }
1400811599a4SMatt Barden 
1401811599a4SMatt Barden /*
1402811599a4SMatt Barden  * [MS-SMB2] 3.3.5.9.7 and 3.3.5.9.12 (durable reconnect v1/v2)
1403811599a4SMatt Barden  *
1404811599a4SMatt Barden  * Looks up an ofile on the server's sv_dh_list by the persistid.
1405811599a4SMatt Barden  * If found, it validates the request.
1406811599a4SMatt Barden  * (see smb2_dh_reconnect_checks() for details)
1407811599a4SMatt Barden  * If the checks are passed, add it onto the new tree's list.
1408811599a4SMatt Barden  *
1409811599a4SMatt Barden  * Note that the oplock break code path can get to an ofile via the node
1410811599a4SMatt Barden  * ofile list.  It starts with a ref taken in smb_ofile_hold_olbrk, which
1411811599a4SMatt Barden  * waits if the ofile is found in state RECONNECT.  That wait happens with
1412811599a4SMatt Barden  * the node ofile list lock held as reader, and the oplock mutex held.
1413811599a4SMatt Barden  * Implications of that are: While we're in state RECONNECT, we shoud NOT
1414811599a4SMatt Barden  * block (at least, not for long) and must not try to enter any of the
1415811599a4SMatt Barden  * node ofile list lock or oplock mutex.  Thankfully, we don't need to
1416811599a4SMatt Barden  * enter those while reclaiming an orphaned ofile.
1417811599a4SMatt Barden  */
1418811599a4SMatt Barden uint32_t
smb2_dh_reconnect(smb_request_t * sr)1419811599a4SMatt Barden smb2_dh_reconnect(smb_request_t *sr)
1420811599a4SMatt Barden {
1421811599a4SMatt Barden 	smb_arg_open_t	*op = &sr->sr_open;
1422811599a4SMatt Barden 	smb_tree_t *tree = sr->tid_tree;
1423811599a4SMatt Barden 	smb_ofile_t *of;
1424811599a4SMatt Barden 	cred_t *old_cr;
1425811599a4SMatt Barden 	uint32_t status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1426811599a4SMatt Barden 	uint16_t fid = 0;
1427811599a4SMatt Barden 
1428811599a4SMatt Barden 	if (smb_idpool_alloc(&tree->t_fid_pool, &fid))
1429811599a4SMatt Barden 		return (NT_STATUS_TOO_MANY_OPENED_FILES);
1430811599a4SMatt Barden 
1431811599a4SMatt Barden 	/* Find orphaned handle. */
1432811599a4SMatt Barden 	of = smb_ofile_lookup_by_persistid(sr, op->dh_fileid.persistent);
1433811599a4SMatt Barden 	if (of == NULL)
1434811599a4SMatt Barden 		goto errout;
1435811599a4SMatt Barden 
1436811599a4SMatt Barden 	mutex_enter(&of->f_mutex);
1437811599a4SMatt Barden 	if (of->f_state != SMB_OFILE_STATE_ORPHANED) {
1438811599a4SMatt Barden 		mutex_exit(&of->f_mutex);
1439811599a4SMatt Barden 		goto errout;
1440811599a4SMatt Barden 	}
1441811599a4SMatt Barden 
1442811599a4SMatt Barden 	status = smb2_dh_reconnect_checks(sr, of);
1443811599a4SMatt Barden 	if (status != NT_STATUS_SUCCESS) {
1444811599a4SMatt Barden 		mutex_exit(&of->f_mutex);
1445811599a4SMatt Barden 		goto errout;
1446811599a4SMatt Barden 	}
1447811599a4SMatt Barden 
1448811599a4SMatt Barden 	/*
1449811599a4SMatt Barden 	 * Note: cv_broadcast(&of->f_cv) when we're
1450811599a4SMatt Barden 	 * done messing around in this state.
1451811599a4SMatt Barden 	 * See: smb_ofile_hold_olbrk()
1452811599a4SMatt Barden 	 */
1453811599a4SMatt Barden 	of->f_state = SMB_OFILE_STATE_RECONNECT;
1454811599a4SMatt Barden 	mutex_exit(&of->f_mutex);
1455811599a4SMatt Barden 
1456811599a4SMatt Barden 	/*
1457811599a4SMatt Barden 	 * At this point, we should be the only thread with a ref on the
1458811599a4SMatt Barden 	 * ofile, and the RECONNECT state should prevent new refs from
1459811599a4SMatt Barden 	 * being granted, or other durable threads from observing or
1460811599a4SMatt Barden 	 * reclaiming it. Put this ofile in the new tree, similar to
1461811599a4SMatt Barden 	 * the last part of smb_ofile_open.
1462811599a4SMatt Barden 	 */
1463811599a4SMatt Barden 
1464811599a4SMatt Barden 	old_cr = of->f_cr;
1465811599a4SMatt Barden 	of->f_cr = sr->user_cr;
1466811599a4SMatt Barden 	crhold(of->f_cr);
1467811599a4SMatt Barden 	crfree(old_cr);
1468811599a4SMatt Barden 
1469811599a4SMatt Barden 	of->f_session = sr->session; /* hold is via user and tree */
1470811599a4SMatt Barden 	smb_user_hold_internal(sr->uid_user);
1471811599a4SMatt Barden 	of->f_user = sr->uid_user;
1472811599a4SMatt Barden 	smb_tree_hold_internal(tree);
1473811599a4SMatt Barden 	of->f_tree = tree;
1474811599a4SMatt Barden 	of->f_fid = fid;
1475811599a4SMatt Barden 
14762cf6b79fSGordon Ross 	smb_lavl_enter(&tree->t_ofile_list, RW_WRITER);
14772cf6b79fSGordon Ross 	smb_lavl_insert(&tree->t_ofile_list, of);
14782cf6b79fSGordon Ross 	smb_lavl_exit(&tree->t_ofile_list);
1479811599a4SMatt Barden 	atomic_inc_32(&tree->t_open_files);
1480811599a4SMatt Barden 	atomic_inc_32(&sr->session->s_file_cnt);
1481811599a4SMatt Barden 
1482811599a4SMatt Barden 	/*
1483811599a4SMatt Barden 	 * The ofile is now in the caller's session & tree.
1484811599a4SMatt Barden 	 *
148572b35b05SGordon Ross 	 * In case smb_ofile_hold or smb_oplock_send_break() are
1486811599a4SMatt Barden 	 * waiting for state RECONNECT to complete, wakeup.
1487811599a4SMatt Barden 	 */
1488811599a4SMatt Barden 	mutex_enter(&of->f_mutex);
1489811599a4SMatt Barden 	of->dh_expire_time = 0;
1490811599a4SMatt Barden 	of->f_state = SMB_OFILE_STATE_OPEN;
1491811599a4SMatt Barden 	cv_broadcast(&of->f_cv);
1492811599a4SMatt Barden 	mutex_exit(&of->f_mutex);
1493811599a4SMatt Barden 
1494811599a4SMatt Barden 	/*
1495811599a4SMatt Barden 	 * The ofile is now visible in the new session.
1496811599a4SMatt Barden 	 * From here, this is similar to the last part of
1497811599a4SMatt Barden 	 * smb_common_open().
1498811599a4SMatt Barden 	 */
1499811599a4SMatt Barden 	op->fqi.fq_fattr.sa_mask = SMB_AT_ALL;
1500811599a4SMatt Barden 	(void) smb_node_getattr(sr, of->f_node, zone_kcred(), of,
1501811599a4SMatt Barden 	    &op->fqi.fq_fattr);
1502811599a4SMatt Barden 
1503811599a4SMatt Barden 	/*
1504811599a4SMatt Barden 	 * Set up the fileid and dosattr in open_param for response
1505811599a4SMatt Barden 	 */
1506811599a4SMatt Barden 	op->fileid = op->fqi.fq_fattr.sa_vattr.va_nodeid;
1507811599a4SMatt Barden 	op->dattr = op->fqi.fq_fattr.sa_dosattr;
1508811599a4SMatt Barden 
1509811599a4SMatt Barden 	/*
1510811599a4SMatt Barden 	 * Set up the file type in open_param for the response
1511811599a4SMatt Barden 	 * The ref. from ofile lookup is "given" to fid_ofile.
1512811599a4SMatt Barden 	 */
1513811599a4SMatt Barden 	op->ftype = SMB_FTYPE_DISK;
1514811599a4SMatt Barden 	sr->smb_fid = of->f_fid;
1515811599a4SMatt Barden 	sr->fid_ofile = of;
1516811599a4SMatt Barden 
1517811599a4SMatt Barden 	if (smb_node_is_file(of->f_node)) {
1518811599a4SMatt Barden 		op->dsize = op->fqi.fq_fattr.sa_vattr.va_size;
1519811599a4SMatt Barden 	} else {
1520811599a4SMatt Barden 		/* directory or symlink */
1521811599a4SMatt Barden 		op->dsize = 0;
1522811599a4SMatt Barden 	}
1523811599a4SMatt Barden 
1524811599a4SMatt Barden 	op->create_options = 0; /* no more modifications wanted */
1525811599a4SMatt Barden 	op->action_taken = SMB_OACT_OPENED;
1526811599a4SMatt Barden 	return (NT_STATUS_SUCCESS);
1527811599a4SMatt Barden 
1528811599a4SMatt Barden errout:
1529811599a4SMatt Barden 	if (of != NULL)
1530811599a4SMatt Barden 		smb_ofile_release(of);
1531811599a4SMatt Barden 	if (fid != 0)
1532811599a4SMatt Barden 		smb_idpool_free(&tree->t_fid_pool, fid);
1533811599a4SMatt Barden 
1534811599a4SMatt Barden 	return (status);
1535811599a4SMatt Barden }
1536811599a4SMatt Barden 
1537811599a4SMatt Barden /*
1538811599a4SMatt Barden  * Durable handle expiration
1539811599a4SMatt Barden  * ofile state is _EXPIRED
1540811599a4SMatt Barden  */
1541811599a4SMatt Barden static void
smb2_dh_expire(void * arg)1542811599a4SMatt Barden smb2_dh_expire(void *arg)
1543811599a4SMatt Barden {
1544811599a4SMatt Barden 	smb_ofile_t *of = (smb_ofile_t *)arg;
1545811599a4SMatt Barden 
15468d94f651SGordon Ross 	if (of->dh_persist)
15478d94f651SGordon Ross 		smb2_dh_setdoc_persistent(of);
1548811599a4SMatt Barden 	smb_ofile_close(of, 0);
1549811599a4SMatt Barden 	smb_ofile_release(of);
1550811599a4SMatt Barden }
1551811599a4SMatt Barden 
155211eb4918SGordon Ross /*
155311eb4918SGordon Ross  * Called once a minute to do expiration of durable handles.
155411eb4918SGordon Ross  *
155511eb4918SGordon Ross  * Normally expired durable handles should be in state "orphaned",
155611eb4918SGordon Ross  * having transitioned from state SAVE_DH through SAVING to state
155711eb4918SGordon Ross  * ORPHANED after all ofile references go away.  If an ofile has
155811eb4918SGordon Ross  * leaked references and the client disconnects, it will be found
155911eb4918SGordon Ross  * here still in state SAVE_DH and past it's expiration time.
156011eb4918SGordon Ross  * Call smb2_dh_expire for these as well, which will move them
156111eb4918SGordon Ross  * from state SAVE_DH to state CLOSING, so they can no longer
156211eb4918SGordon Ross  * cause sharing violations for new opens.
156311eb4918SGordon Ross  */
1564811599a4SMatt Barden void
smb2_durable_timers(smb_server_t * sv)1565811599a4SMatt Barden smb2_durable_timers(smb_server_t *sv)
1566811599a4SMatt Barden {
1567811599a4SMatt Barden 	smb_hash_t *hash;
1568811599a4SMatt Barden 	smb_llist_t *bucket;
1569811599a4SMatt Barden 	smb_ofile_t *of;
1570811599a4SMatt Barden 	hrtime_t now;
1571811599a4SMatt Barden 	int i;
1572811599a4SMatt Barden 
1573811599a4SMatt Barden 	hash = sv->sv_persistid_ht;
1574811599a4SMatt Barden 	now = gethrtime();
1575811599a4SMatt Barden 
1576811599a4SMatt Barden 	for (i = 0; i < hash->num_buckets; i++) {
1577811599a4SMatt Barden 		bucket = &hash->buckets[i].b_list;
1578811599a4SMatt Barden 		smb_llist_enter(bucket, RW_READER);
1579811599a4SMatt Barden 		for (of = smb_llist_head(bucket);
1580811599a4SMatt Barden 		    of != NULL;
1581811599a4SMatt Barden 		    of = smb_llist_next(bucket, of)) {
1582811599a4SMatt Barden 			SMB_OFILE_VALID(of);
1583811599a4SMatt Barden 
1584811599a4SMatt Barden 			/*
1585811599a4SMatt Barden 			 * Check outside the mutex first to avoid some
1586811599a4SMatt Barden 			 * mutex_enter work in this loop.  If the state
1587811599a4SMatt Barden 			 * changes under foot, the worst that happens
1588811599a4SMatt Barden 			 * is we either enter the mutex when we might
1589811599a4SMatt Barden 			 * not have needed to, or we miss some DH in
1590811599a4SMatt Barden 			 * this pass and get it on the next.
1591811599a4SMatt Barden 			 */
159211eb4918SGordon Ross 			if (of->f_state != SMB_OFILE_STATE_ORPHANED &&
159311eb4918SGordon Ross 			    of->f_state != SMB_OFILE_STATE_SAVE_DH)
1594811599a4SMatt Barden 				continue;
1595811599a4SMatt Barden 
1596811599a4SMatt Barden 			mutex_enter(&of->f_mutex);
159711eb4918SGordon Ross 			if ((of->f_state == SMB_OFILE_STATE_ORPHANED ||
159811eb4918SGordon Ross 			    of->f_state == SMB_OFILE_STATE_SAVE_DH) &&
159911eb4918SGordon Ross 			    of->dh_expire_time != 0 &&
1600811599a4SMatt Barden 			    of->dh_expire_time <= now) {
160111eb4918SGordon Ross 
1602811599a4SMatt Barden 				of->f_state = SMB_OFILE_STATE_EXPIRED;
1603811599a4SMatt Barden 				/* inline smb_ofile_hold_internal() */
1604811599a4SMatt Barden 				of->f_refcnt++;
1605811599a4SMatt Barden 				smb_llist_post(bucket, of, smb2_dh_expire);
1606811599a4SMatt Barden 			}
1607811599a4SMatt Barden 			mutex_exit(&of->f_mutex);
1608811599a4SMatt Barden 		}
1609811599a4SMatt Barden 		smb_llist_exit(bucket);
1610811599a4SMatt Barden 	}
1611811599a4SMatt Barden }
1612811599a4SMatt Barden 
16138d94f651SGordon Ross /*
16148d94f651SGordon Ross  * This is called when we're about to add a new open to some node.
16158d94f651SGordon Ross  * If we still have orphaned durable handles on this node, let's
16168d94f651SGordon Ross  * assume the client has lost interest in those and close them,
16178d94f651SGordon Ross  * otherwise we might conflict with our own orphaned handles.
16188d94f651SGordon Ross  *
16198d94f651SGordon Ross  * We need this because we import persistent handles "speculatively"
16208d94f651SGordon Ross  * during share import (before the client ever asks for reconnect).
16218d94f651SGordon Ross  * That allows us to avoid any need for a "create blackout" (or
16228d94f651SGordon Ross  * "grace period") because the imported handles prevent unwanted
16238d94f651SGordon Ross  * conflicting opens from other clients.  However, if some client
16248d94f651SGordon Ross  * "forgets" about a persistent handle (*cough* Hyper-V) and tries
16258d94f651SGordon Ross  * a new (conflicting) open instead of a reconnect, that might
16268d94f651SGordon Ross  * fail unless we expire our orphaned durables handle first.
16278d94f651SGordon Ross  *
16288d94f651SGordon Ross  * Logic similar to smb_node_open_check()
16298d94f651SGordon Ross  */
16308d94f651SGordon Ross void
smb2_dh_close_my_orphans(smb_request_t * sr,smb_ofile_t * new_of)16318d94f651SGordon Ross smb2_dh_close_my_orphans(smb_request_t *sr, smb_ofile_t *new_of)
16328d94f651SGordon Ross {
16338d94f651SGordon Ross 	smb_node_t *node = new_of->f_node;
16348d94f651SGordon Ross 	smb_ofile_t *of;
16358d94f651SGordon Ross 
16368d94f651SGordon Ross 	SMB_NODE_VALID(node);
16378d94f651SGordon Ross 
16388d94f651SGordon Ross 	smb_llist_enter(&node->n_ofile_list, RW_READER);
16398d94f651SGordon Ross 	for (of = smb_llist_head(&node->n_ofile_list);
16408d94f651SGordon Ross 	    of != NULL;
16418d94f651SGordon Ross 	    of = smb_llist_next(&node->n_ofile_list, of)) {
16428d94f651SGordon Ross 
16438d94f651SGordon Ross 		/* Same client? */
16448d94f651SGordon Ross 		if (of->f_lease != NULL &&
16458d94f651SGordon Ross 		    bcmp(sr->session->clnt_uuid,
16468d94f651SGordon Ross 		    of->f_lease->ls_clnt, 16) != 0)
16478d94f651SGordon Ross 			continue;
16488d94f651SGordon Ross 
16498d94f651SGordon Ross 		if (!smb_is_same_user(sr->user_cr, of->f_cr))
16508d94f651SGordon Ross 			continue;
16518d94f651SGordon Ross 
16528d94f651SGordon Ross 		mutex_enter(&of->f_mutex);
165311eb4918SGordon Ross 		if (of->f_state == SMB_OFILE_STATE_ORPHANED ||
165411eb4918SGordon Ross 		    of->f_state == SMB_OFILE_STATE_SAVE_DH) {
16558d94f651SGordon Ross 			of->f_state = SMB_OFILE_STATE_EXPIRED;
16568d94f651SGordon Ross 			/* inline smb_ofile_hold_internal() */
16578d94f651SGordon Ross 			of->f_refcnt++;
16588d94f651SGordon Ross 			smb_llist_post(&node->n_ofile_list,
16598d94f651SGordon Ross 			    of, smb2_dh_expire);
16608d94f651SGordon Ross 		}
16618d94f651SGordon Ross 		mutex_exit(&of->f_mutex);
16628d94f651SGordon Ross 	}
16638d94f651SGordon Ross 
16648d94f651SGordon Ross 	smb_llist_exit(&node->n_ofile_list);
16658d94f651SGordon Ross }
16668d94f651SGordon Ross 
16678d94f651SGordon Ross /*
16688d94f651SGordon Ross  * Called for each orphaned DH during shutdown.
16698d94f651SGordon Ross  * Clean out any in-memory state, but leave any
16708d94f651SGordon Ross  * on-disk persistent handle state in place.
16718d94f651SGordon Ross  */
16728d94f651SGordon Ross static void
smb2_dh_cleanup(void * arg)16738d94f651SGordon Ross smb2_dh_cleanup(void *arg)
16748d94f651SGordon Ross {
16758d94f651SGordon Ross 	smb_ofile_t *of = (smb_ofile_t *)arg;
16768d94f651SGordon Ross 	smb_node_t *strnode;
16778d94f651SGordon Ross 	struct nvlist *nvl;
16788d94f651SGordon Ross 
16798d94f651SGordon Ross 	/*
16808d94f651SGordon Ross 	 * Intentionally skip smb2_dh_close_persistent by
16818d94f651SGordon Ross 	 * clearing dh_nvfile before smb_ofile_close().
16828d94f651SGordon Ross 	 */
16838d94f651SGordon Ross 	mutex_enter(&of->dh_nvlock);
16848d94f651SGordon Ross 	strnode = of->dh_nvfile;
16858d94f651SGordon Ross 	of->dh_nvfile = NULL;
16868d94f651SGordon Ross 	nvl = of->dh_nvlist;
16878d94f651SGordon Ross 	of->dh_nvlist = NULL;
16888d94f651SGordon Ross 	mutex_exit(&of->dh_nvlock);
16898d94f651SGordon Ross 
16908d94f651SGordon Ross 	if (nvl != NULL)
16918d94f651SGordon Ross 		nvlist_free(nvl);
16928d94f651SGordon Ross 
16938d94f651SGordon Ross 	if (strnode != NULL)
16948d94f651SGordon Ross 		smb_node_release(strnode);
16958d94f651SGordon Ross 
16968d94f651SGordon Ross 	smb_ofile_close(of, 0);
16978d94f651SGordon Ross 	smb_ofile_release(of);
16988d94f651SGordon Ross }
16998d94f651SGordon Ross 
1700811599a4SMatt Barden /*
1701811599a4SMatt Barden  * Clean out durable handles during shutdown.
17028d94f651SGordon Ross  *
17038d94f651SGordon Ross  * Like, smb2_durable_timers but cleanup only in-memory state,
17048d94f651SGordon Ross  * and leave any persistent state there for later reconnect.
1705811599a4SMatt Barden  */
1706811599a4SMatt Barden void
smb2_dh_shutdown(smb_server_t * sv)1707811599a4SMatt Barden smb2_dh_shutdown(smb_server_t *sv)
1708811599a4SMatt Barden {
170986184067SGordon Ross 	static const smb_oplock_grant_t og0 = { 0 };
1710811599a4SMatt Barden 	smb_hash_t *hash;
1711811599a4SMatt Barden 	smb_llist_t *bucket;
1712811599a4SMatt Barden 	smb_ofile_t *of;
1713811599a4SMatt Barden 	int i;
1714811599a4SMatt Barden 
1715811599a4SMatt Barden 	hash = sv->sv_persistid_ht;
1716811599a4SMatt Barden 
1717811599a4SMatt Barden 	for (i = 0; i < hash->num_buckets; i++) {
1718811599a4SMatt Barden 		bucket = &hash->buckets[i].b_list;
1719811599a4SMatt Barden 		smb_llist_enter(bucket, RW_READER);
1720811599a4SMatt Barden 		of = smb_llist_head(bucket);
1721811599a4SMatt Barden 		while (of != NULL) {
1722811599a4SMatt Barden 			SMB_OFILE_VALID(of);
1723811599a4SMatt Barden 			mutex_enter(&of->f_mutex);
1724811599a4SMatt Barden 
1725811599a4SMatt Barden 			switch (of->f_state) {
1726811599a4SMatt Barden 			case SMB_OFILE_STATE_ORPHANED:
172711eb4918SGordon Ross 			case SMB_OFILE_STATE_SAVE_DH:
1728811599a4SMatt Barden 				of->f_state = SMB_OFILE_STATE_EXPIRED;
1729811599a4SMatt Barden 				/* inline smb_ofile_hold_internal() */
1730811599a4SMatt Barden 				of->f_refcnt++;
17318d94f651SGordon Ross 				smb_llist_post(bucket, of, smb2_dh_cleanup);
1732811599a4SMatt Barden 				break;
173386184067SGordon Ross 
1734811599a4SMatt Barden 			default:
173586184067SGordon Ross 				/*
173686184067SGordon Ross 				 * Should not be possible, but try to
173786184067SGordon Ross 				 * make this zombie ofile harmless.
173886184067SGordon Ross 				 */
173986184067SGordon Ross 				cmn_err(CE_NOTE, "!dh_shutdown found "
174086184067SGordon Ross 				    "of = %p with invalid state = %d",
174186184067SGordon Ross 				    (void *)of, of->f_state);
174286184067SGordon Ross 				DTRACE_PROBE1(bad_ofile, smb_ofile_t *, of);
174386184067SGordon Ross 				ASSERT(0);
174486184067SGordon Ross 				of->f_oplock = og0;
1745811599a4SMatt Barden 				break;
1746811599a4SMatt Barden 			}
1747811599a4SMatt Barden 			mutex_exit(&of->f_mutex);
1748811599a4SMatt Barden 			of = smb_llist_next(bucket, of);
1749811599a4SMatt Barden 		}
1750811599a4SMatt Barden 		smb_llist_exit(bucket);
1751811599a4SMatt Barden 	}
1752811599a4SMatt Barden 
1753811599a4SMatt Barden #ifdef	DEBUG
1754811599a4SMatt Barden 	for (i = 0; i < hash->num_buckets; i++) {
1755811599a4SMatt Barden 		bucket = &hash->buckets[i].b_list;
1756811599a4SMatt Barden 		smb_llist_enter(bucket, RW_READER);
1757811599a4SMatt Barden 		of = smb_llist_head(bucket);
1758811599a4SMatt Barden 		while (of != NULL) {
1759811599a4SMatt Barden 			SMB_OFILE_VALID(of);
1760811599a4SMatt Barden 			cmn_err(CE_NOTE, "dh_shutdown leaked of=%p",
1761811599a4SMatt Barden 			    (void *)of);
1762811599a4SMatt Barden 			of = smb_llist_next(bucket, of);
1763811599a4SMatt Barden 		}
1764811599a4SMatt Barden 		smb_llist_exit(bucket);
1765811599a4SMatt Barden 	}
1766811599a4SMatt Barden #endif	// DEBUG
1767811599a4SMatt Barden }
1768811599a4SMatt Barden 
1769811599a4SMatt Barden uint32_t
smb2_fsctl_set_resilient(smb_request_t * sr,smb_fsctl_t * fsctl)177055f0a249SGordon Ross smb2_fsctl_set_resilient(smb_request_t *sr, smb_fsctl_t *fsctl)
1771811599a4SMatt Barden {
1772811599a4SMatt Barden 	uint32_t timeout;
1773811599a4SMatt Barden 	smb_ofile_t *of = sr->fid_ofile;
1774811599a4SMatt Barden 
1775811599a4SMatt Barden 	/*
1776811599a4SMatt Barden 	 * Note: The spec does not explicitly prohibit resilient directories
1777811599a4SMatt Barden 	 * the same way it prohibits durable directories. We prohibit them
1778811599a4SMatt Barden 	 * anyway as a simplifying assumption, as there doesn't seem to be
1779811599a4SMatt Barden 	 * much use for it. (HYPER-V only seems to use it on files anyway)
1780811599a4SMatt Barden 	 */
1781811599a4SMatt Barden 	if (fsctl->InputCount < 8 || !smb_node_is_file(of->f_node))
1782811599a4SMatt Barden 		return (NT_STATUS_INVALID_PARAMETER);
1783811599a4SMatt Barden 
1784811599a4SMatt Barden 	(void) smb_mbc_decodef(fsctl->in_mbc, "l4.",
1785811599a4SMatt Barden 	    &timeout); /* milliseconds */
1786811599a4SMatt Barden 
1787811599a4SMatt Barden 	if (smb2_enable_dh == 0)
1788811599a4SMatt Barden 		return (NT_STATUS_NOT_SUPPORTED);
1789811599a4SMatt Barden 
1790811599a4SMatt Barden 	/*
1791811599a4SMatt Barden 	 * The spec wants us to return INVALID_PARAMETER if the timeout
1792811599a4SMatt Barden 	 * is too large, but we have no way of informing the client
1793811599a4SMatt Barden 	 * what an appropriate timeout is, so just set the timeout to
1794811599a4SMatt Barden 	 * our max and return SUCCESS.
1795811599a4SMatt Barden 	 */
1796811599a4SMatt Barden 	if (timeout == 0)
1797811599a4SMatt Barden 		timeout = smb2_res_def_timeout;
1798811599a4SMatt Barden 	if (timeout > smb2_res_max_timeout)
1799811599a4SMatt Barden 		timeout = smb2_res_max_timeout;
1800811599a4SMatt Barden 
1801811599a4SMatt Barden 	mutex_enter(&of->f_mutex);
1802811599a4SMatt Barden 	of->dh_vers = SMB2_RESILIENT;
1803811599a4SMatt Barden 	of->dh_timeout_offset = MSEC2NSEC(timeout);
1804811599a4SMatt Barden 	mutex_exit(&of->f_mutex);
1805811599a4SMatt Barden 
1806811599a4SMatt Barden 	return (NT_STATUS_SUCCESS);
1807811599a4SMatt Barden }
1808