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