17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5f86c6ccaSdm  * Common Development and Distribution License (the "License").
6f86c6ccaSdm  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*d7de0ceaSRobert Harris  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate /*
267c478bd9Sstevel@tonic-gate  *	Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
277c478bd9Sstevel@tonic-gate  *	All Rights Reserved
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <sys/param.h>
317c478bd9Sstevel@tonic-gate #include <sys/types.h>
327c478bd9Sstevel@tonic-gate #include <sys/systm.h>
337c478bd9Sstevel@tonic-gate #include <sys/cred.h>
347c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
35aa59c4cbSrsb #include <sys/vfs_opreg.h>
367c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
377c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
387c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
397c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
407c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
417c478bd9Sstevel@tonic-gate #include <sys/mount.h>
427c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
437c478bd9Sstevel@tonic-gate #include <sys/errno.h>
447c478bd9Sstevel@tonic-gate #include <sys/debug.h>
457c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
467c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
477c478bd9Sstevel@tonic-gate #include <sys/bootconf.h>
487c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
497c478bd9Sstevel@tonic-gate #include <sys/acl.h>
507c478bd9Sstevel@tonic-gate #include <sys/flock.h>
517c478bd9Sstevel@tonic-gate #include <sys/time.h>
527c478bd9Sstevel@tonic-gate #include <sys/disp.h>
537c478bd9Sstevel@tonic-gate #include <sys/policy.h>
547c478bd9Sstevel@tonic-gate #include <sys/socket.h>
557c478bd9Sstevel@tonic-gate #include <sys/netconfig.h>
567c478bd9Sstevel@tonic-gate #include <sys/dnlc.h>
577c478bd9Sstevel@tonic-gate #include <sys/list.h>
5845916cd2Sjpk #include <sys/mntent.h>
5945916cd2Sjpk #include <sys/tsol/label.h>
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate #include <rpc/types.h>
627c478bd9Sstevel@tonic-gate #include <rpc/auth.h>
637c478bd9Sstevel@tonic-gate #include <rpc/rpcsec_gss.h>
647c478bd9Sstevel@tonic-gate #include <rpc/clnt.h>
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate #include <nfs/nfs.h>
677c478bd9Sstevel@tonic-gate #include <nfs/nfs_clnt.h>
687c478bd9Sstevel@tonic-gate #include <nfs/mount.h>
697c478bd9Sstevel@tonic-gate #include <nfs/nfs_acl.h>
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h>
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate #include <nfs/nfs4.h>
747c478bd9Sstevel@tonic-gate #include <nfs/rnode4.h>
757c478bd9Sstevel@tonic-gate #include <nfs/nfs4_clnt.h>
7639d3e169Sevanl #include <sys/fs/autofs.h>
7739d3e169Sevanl 
782f172c55SRobert Thurlow #include <sys/sdt.h>
792f172c55SRobert Thurlow 
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate /*
827c478bd9Sstevel@tonic-gate  * Arguments passed to thread to free data structures from forced unmount.
837c478bd9Sstevel@tonic-gate  */
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate typedef struct {
86b9238976Sth 	vfs_t	*fm_vfsp;
87b9238976Sth 	int	fm_flag;
88b9238976Sth 	cred_t	*fm_cr;
897c478bd9Sstevel@tonic-gate } freemountargs_t;
907c478bd9Sstevel@tonic-gate 
91b9238976Sth static void	async_free_mount(vfs_t *, int, cred_t *);
92b9238976Sth static void	nfs4_free_mount(vfs_t *, int, cred_t *);
937c478bd9Sstevel@tonic-gate static void	nfs4_free_mount_thread(freemountargs_t *);
947c478bd9Sstevel@tonic-gate static int nfs4_chkdup_servinfo4(servinfo4_t *, servinfo4_t *);
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate /*
977c478bd9Sstevel@tonic-gate  * From rpcsec module (common/rpcsec).
987c478bd9Sstevel@tonic-gate  */
997c478bd9Sstevel@tonic-gate extern int sec_clnt_loadinfo(struct sec_data *, struct sec_data **, model_t);
1007c478bd9Sstevel@tonic-gate extern void sec_clnt_freeinfo(struct sec_data *);
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate /*
1037c478bd9Sstevel@tonic-gate  * The order and contents of this structure must be kept in sync with that of
1047c478bd9Sstevel@tonic-gate  * rfsreqcnt_v4_tmpl in nfs_stats.c
1057c478bd9Sstevel@tonic-gate  */
1067c478bd9Sstevel@tonic-gate static char *rfsnames_v4[] = {
1077c478bd9Sstevel@tonic-gate 	"null", "compound", "reserved",	"access", "close", "commit", "create",
1087c478bd9Sstevel@tonic-gate 	"delegpurge", "delegreturn", "getattr",	"getfh", "link", "lock",
1097c478bd9Sstevel@tonic-gate 	"lockt", "locku", "lookup", "lookupp", "nverify", "open", "openattr",
1107c478bd9Sstevel@tonic-gate 	"open_confirm",	"open_downgrade", "putfh", "putpubfh", "putrootfh",
1117c478bd9Sstevel@tonic-gate 	"read", "readdir", "readlink", "remove", "rename", "renew",
1127c478bd9Sstevel@tonic-gate 	"restorefh", "savefh", "secinfo", "setattr", "setclientid",
1137c478bd9Sstevel@tonic-gate 	"setclientid_confirm", "verify", "write"
1147c478bd9Sstevel@tonic-gate };
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate /*
1177c478bd9Sstevel@tonic-gate  * nfs4_max_mount_retry is the number of times the client will redrive
1187c478bd9Sstevel@tonic-gate  * a mount compound before giving up and returning failure.  The intent
1197c478bd9Sstevel@tonic-gate  * is to redrive mount compounds which fail NFS4ERR_STALE so that
1207c478bd9Sstevel@tonic-gate  * if a component of the server path being mounted goes stale, it can
1217c478bd9Sstevel@tonic-gate  * "recover" by redriving the mount compund (LOOKUP ops).  This recovery
1227c478bd9Sstevel@tonic-gate  * code is needed outside of the recovery framework because mount is a
1237c478bd9Sstevel@tonic-gate  * special case.  The client doesn't create vnodes/rnodes for components
1247c478bd9Sstevel@tonic-gate  * of the server path being mounted.  The recovery code recovers real
1257c478bd9Sstevel@tonic-gate  * client objects, not STALE FHs which map to components of the server
1267c478bd9Sstevel@tonic-gate  * path being mounted.
1277c478bd9Sstevel@tonic-gate  *
1287c478bd9Sstevel@tonic-gate  * We could just fail the mount on the first time, but that would
1297c478bd9Sstevel@tonic-gate  * instantly trigger failover (from nfs4_mount), and the client should
1307c478bd9Sstevel@tonic-gate  * try to re-lookup the STALE FH before doing failover.  The easiest
1317c478bd9Sstevel@tonic-gate  * way to "re-lookup" is to simply redrive the mount compound.
1327c478bd9Sstevel@tonic-gate  */
1337c478bd9Sstevel@tonic-gate static int nfs4_max_mount_retry = 2;
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate /*
1367c478bd9Sstevel@tonic-gate  * nfs4 vfs operations.
1377c478bd9Sstevel@tonic-gate  */
138b9238976Sth int		nfs4_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *);
1397c478bd9Sstevel@tonic-gate static int	nfs4_unmount(vfs_t *, int, cred_t *);
1407c478bd9Sstevel@tonic-gate static int	nfs4_root(vfs_t *, vnode_t **);
1417c478bd9Sstevel@tonic-gate static int	nfs4_statvfs(vfs_t *, struct statvfs64 *);
1427c478bd9Sstevel@tonic-gate static int	nfs4_sync(vfs_t *, short, cred_t *);
1437c478bd9Sstevel@tonic-gate static int	nfs4_vget(vfs_t *, vnode_t **, fid_t *);
1447c478bd9Sstevel@tonic-gate static int	nfs4_mountroot(vfs_t *, whymountroot_t);
1457c478bd9Sstevel@tonic-gate static void	nfs4_freevfs(vfs_t *);
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate static int	nfs4rootvp(vnode_t **, vfs_t *, struct servinfo4 *,
1487c478bd9Sstevel@tonic-gate 		    int, cred_t *, zone_t *);
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate vfsops_t	*nfs4_vfsops;
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate int nfs4_vfsinit(void);
1537c478bd9Sstevel@tonic-gate void nfs4_vfsfini(void);
1547c478bd9Sstevel@tonic-gate static void nfs4setclientid_init(void);
1557c478bd9Sstevel@tonic-gate static void nfs4setclientid_fini(void);
1567c478bd9Sstevel@tonic-gate static void nfs4setclientid_otw(mntinfo4_t *, servinfo4_t *,  cred_t *,
1577c478bd9Sstevel@tonic-gate 		struct nfs4_server *, nfs4_error_t *, int *);
1587c478bd9Sstevel@tonic-gate static void	destroy_nfs4_server(nfs4_server_t *);
1597c478bd9Sstevel@tonic-gate static void	remove_mi(nfs4_server_t *, mntinfo4_t *);
1607c478bd9Sstevel@tonic-gate 
161b9238976Sth extern void nfs4_ephemeral_init(void);
162b9238976Sth extern void nfs4_ephemeral_fini(void);
163b9238976Sth 
1642f172c55SRobert Thurlow /* referral related routines */
1652f172c55SRobert Thurlow static servinfo4_t *copy_svp(servinfo4_t *);
1662f172c55SRobert Thurlow static void free_knconf_contents(struct knetconfig *k);
1672f172c55SRobert Thurlow static char *extract_referral_point(const char *, int);
1682f172c55SRobert Thurlow static void setup_newsvpath(servinfo4_t *, int);
1692f172c55SRobert Thurlow static void update_servinfo4(servinfo4_t *, fs_location4 *,
1702f172c55SRobert Thurlow 		struct nfs_fsl_info *, char *, int);
1712f172c55SRobert Thurlow 
1727c478bd9Sstevel@tonic-gate /*
1737c478bd9Sstevel@tonic-gate  * Initialize the vfs structure
1747c478bd9Sstevel@tonic-gate  */
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate static int nfs4fstyp;
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate /*
1807c478bd9Sstevel@tonic-gate  * Debug variable to check for rdma based
1817c478bd9Sstevel@tonic-gate  * transport startup and cleanup. Controlled
1827c478bd9Sstevel@tonic-gate  * through /etc/system. Off by default.
1837c478bd9Sstevel@tonic-gate  */
1847c478bd9Sstevel@tonic-gate extern int rdma_debug;
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate int
1877c478bd9Sstevel@tonic-gate nfs4init(int fstyp, char *name)
1887c478bd9Sstevel@tonic-gate {
1897c478bd9Sstevel@tonic-gate 	static const fs_operation_def_t nfs4_vfsops_template[] = {
190aa59c4cbSrsb 		VFSNAME_MOUNT,		{ .vfs_mount = nfs4_mount },
191aa59c4cbSrsb 		VFSNAME_UNMOUNT,	{ .vfs_unmount = nfs4_unmount },
192aa59c4cbSrsb 		VFSNAME_ROOT,		{ .vfs_root = nfs4_root },
193aa59c4cbSrsb 		VFSNAME_STATVFS,	{ .vfs_statvfs = nfs4_statvfs },
194aa59c4cbSrsb 		VFSNAME_SYNC,		{ .vfs_sync = nfs4_sync },
195aa59c4cbSrsb 		VFSNAME_VGET,		{ .vfs_vget = nfs4_vget },
196aa59c4cbSrsb 		VFSNAME_MOUNTROOT,	{ .vfs_mountroot = nfs4_mountroot },
197aa59c4cbSrsb 		VFSNAME_FREEVFS,	{ .vfs_freevfs = nfs4_freevfs },
198aa59c4cbSrsb 		NULL,			NULL
1997c478bd9Sstevel@tonic-gate 	};
2007c478bd9Sstevel@tonic-gate 	int error;
2017c478bd9Sstevel@tonic-gate 
202b9238976Sth 	nfs4_vfsops = NULL;
203b9238976Sth 	nfs4_vnodeops = NULL;
204b9238976Sth 	nfs4_trigger_vnodeops = NULL;
205b9238976Sth 
2067c478bd9Sstevel@tonic-gate 	error = vfs_setfsops(fstyp, nfs4_vfsops_template, &nfs4_vfsops);
2077c478bd9Sstevel@tonic-gate 	if (error != 0) {
2087c478bd9Sstevel@tonic-gate 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
2097c478bd9Sstevel@tonic-gate 		    "nfs4init: bad vfs ops template");
210b9238976Sth 		goto out;
2117c478bd9Sstevel@tonic-gate 	}
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	error = vn_make_ops(name, nfs4_vnodeops_template, &nfs4_vnodeops);
2147c478bd9Sstevel@tonic-gate 	if (error != 0) {
2157c478bd9Sstevel@tonic-gate 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
2167c478bd9Sstevel@tonic-gate 		    "nfs4init: bad vnode ops template");
217b9238976Sth 		goto out;
2187c478bd9Sstevel@tonic-gate 	}
2197c478bd9Sstevel@tonic-gate 
220b9238976Sth 	error = vn_make_ops("nfs4_trigger", nfs4_trigger_vnodeops_template,
221b9238976Sth 	    &nfs4_trigger_vnodeops);
222b9238976Sth 	if (error != 0) {
223b9238976Sth 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
224b9238976Sth 		    "nfs4init: bad trigger vnode ops template");
225b9238976Sth 		goto out;
226b9238976Sth 	}
2277c478bd9Sstevel@tonic-gate 
228b9238976Sth 	nfs4fstyp = fstyp;
2297c478bd9Sstevel@tonic-gate 	(void) nfs4_vfsinit();
2307c478bd9Sstevel@tonic-gate 	(void) nfs4_init_dot_entries();
2317c478bd9Sstevel@tonic-gate 
232b9238976Sth out:
233b9238976Sth 	if (error) {
234b9238976Sth 		if (nfs4_trigger_vnodeops != NULL)
235b9238976Sth 			vn_freevnodeops(nfs4_trigger_vnodeops);
236b9238976Sth 
237b9238976Sth 		if (nfs4_vnodeops != NULL)
238b9238976Sth 			vn_freevnodeops(nfs4_vnodeops);
239b9238976Sth 
240b9238976Sth 		(void) vfs_freevfsops_by_type(fstyp);
241b9238976Sth 	}
242b9238976Sth 
243b9238976Sth 	return (error);
2447c478bd9Sstevel@tonic-gate }
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate void
2477c478bd9Sstevel@tonic-gate nfs4fini(void)
2487c478bd9Sstevel@tonic-gate {
2497c478bd9Sstevel@tonic-gate 	(void) nfs4_destroy_dot_entries();
2507c478bd9Sstevel@tonic-gate 	nfs4_vfsfini();
2517c478bd9Sstevel@tonic-gate }
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate /*
2547c478bd9Sstevel@tonic-gate  * Create a new sec_data structure to store AUTH_DH related data:
2557c478bd9Sstevel@tonic-gate  * netname, syncaddr, knetconfig. There is no AUTH_F_RPCTIMESYNC
2567c478bd9Sstevel@tonic-gate  * flag set for NFS V4 since we are avoiding to contact the rpcbind
2577c478bd9Sstevel@tonic-gate  * daemon and is using the IP time service (IPPORT_TIMESERVER).
2587c478bd9Sstevel@tonic-gate  *
2597c478bd9Sstevel@tonic-gate  * sec_data can be freed by sec_clnt_freeinfo().
2607c478bd9Sstevel@tonic-gate  */
261b9238976Sth static struct sec_data *
2627c478bd9Sstevel@tonic-gate create_authdh_data(char *netname, int nlen, struct netbuf *syncaddr,
2637c478bd9Sstevel@tonic-gate 		struct knetconfig *knconf) {
2647c478bd9Sstevel@tonic-gate 	struct sec_data *secdata;
2657c478bd9Sstevel@tonic-gate 	dh_k4_clntdata_t *data;
2667c478bd9Sstevel@tonic-gate 	char *pf, *p;
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate 	if (syncaddr == NULL || syncaddr->buf == NULL || nlen == 0)
2697c478bd9Sstevel@tonic-gate 		return (NULL);
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP);
2727c478bd9Sstevel@tonic-gate 	secdata->flags = 0;
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	data = kmem_alloc(sizeof (*data), KM_SLEEP);
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	data->syncaddr.maxlen = syncaddr->maxlen;
2777c478bd9Sstevel@tonic-gate 	data->syncaddr.len = syncaddr->len;
2787c478bd9Sstevel@tonic-gate 	data->syncaddr.buf = (char *)kmem_alloc(syncaddr->len, KM_SLEEP);
2797c478bd9Sstevel@tonic-gate 	bcopy(syncaddr->buf, data->syncaddr.buf, syncaddr->len);
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 	/*
2827c478bd9Sstevel@tonic-gate 	 * duplicate the knconf information for the
2837c478bd9Sstevel@tonic-gate 	 * new opaque data.
2847c478bd9Sstevel@tonic-gate 	 */
2857c478bd9Sstevel@tonic-gate 	data->knconf = kmem_alloc(sizeof (*knconf), KM_SLEEP);
2867c478bd9Sstevel@tonic-gate 	*data->knconf = *knconf;
2877c478bd9Sstevel@tonic-gate 	pf = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
2887c478bd9Sstevel@tonic-gate 	p = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
2897c478bd9Sstevel@tonic-gate 	bcopy(knconf->knc_protofmly, pf, KNC_STRSIZE);
2907c478bd9Sstevel@tonic-gate 	bcopy(knconf->knc_proto, p, KNC_STRSIZE);
2917c478bd9Sstevel@tonic-gate 	data->knconf->knc_protofmly = pf;
2927c478bd9Sstevel@tonic-gate 	data->knconf->knc_proto = p;
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 	/* move server netname to the sec_data structure */
2957c478bd9Sstevel@tonic-gate 	data->netname = kmem_alloc(nlen, KM_SLEEP);
2967c478bd9Sstevel@tonic-gate 	bcopy(netname, data->netname, nlen);
2977c478bd9Sstevel@tonic-gate 	data->netnamelen = (int)nlen;
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 	secdata->secmod = AUTH_DH;
3007c478bd9Sstevel@tonic-gate 	secdata->rpcflavor = AUTH_DH;
3017c478bd9Sstevel@tonic-gate 	secdata->data = (caddr_t)data;
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	return (secdata);
3047c478bd9Sstevel@tonic-gate }
3057c478bd9Sstevel@tonic-gate 
306b9238976Sth /*
307b9238976Sth  * Returns (deep) copy of sec_data_t. Allocates all memory required; caller
308b9238976Sth  * is responsible for freeing.
309b9238976Sth  */
310b9238976Sth sec_data_t *
311b9238976Sth copy_sec_data(sec_data_t *fsecdata) {
312b9238976Sth 	sec_data_t *tsecdata;
313b9238976Sth 
314b9238976Sth 	if (fsecdata == NULL)
315b9238976Sth 		return (NULL);
316b9238976Sth 
317b9238976Sth 	if (fsecdata->rpcflavor == AUTH_DH) {
318b9238976Sth 		dh_k4_clntdata_t *fdata = (dh_k4_clntdata_t *)fsecdata->data;
319b9238976Sth 
320b9238976Sth 		if (fdata == NULL)
321b9238976Sth 			return (NULL);
322b9238976Sth 
323b9238976Sth 		tsecdata = (sec_data_t *)create_authdh_data(fdata->netname,
324b9238976Sth 		    fdata->netnamelen, &fdata->syncaddr, fdata->knconf);
325b9238976Sth 
326b9238976Sth 		return (tsecdata);
327b9238976Sth 	}
328b9238976Sth 
329b9238976Sth 	tsecdata = kmem_zalloc(sizeof (sec_data_t), KM_SLEEP);
330b9238976Sth 
331b9238976Sth 	tsecdata->secmod = fsecdata->secmod;
332b9238976Sth 	tsecdata->rpcflavor = fsecdata->rpcflavor;
333b9238976Sth 	tsecdata->flags = fsecdata->flags;
334b9238976Sth 	tsecdata->uid = fsecdata->uid;
335b9238976Sth 
336b9238976Sth 	if (fsecdata->rpcflavor == RPCSEC_GSS) {
337b9238976Sth 		gss_clntdata_t *gcd = (gss_clntdata_t *)fsecdata->data;
338b9238976Sth 
339b9238976Sth 		tsecdata->data = (caddr_t)copy_sec_data_gss(gcd);
340b9238976Sth 	} else {
341b9238976Sth 		tsecdata->data = NULL;
342b9238976Sth 	}
343b9238976Sth 
344b9238976Sth 	return (tsecdata);
345b9238976Sth }
346b9238976Sth 
347b9238976Sth gss_clntdata_t *
348b9238976Sth copy_sec_data_gss(gss_clntdata_t *fdata)
349b9238976Sth {
350b9238976Sth 	gss_clntdata_t *tdata;
351b9238976Sth 
352b9238976Sth 	if (fdata == NULL)
353b9238976Sth 		return (NULL);
354b9238976Sth 
355b9238976Sth 	tdata = kmem_zalloc(sizeof (gss_clntdata_t), KM_SLEEP);
356b9238976Sth 
357b9238976Sth 	tdata->mechanism.length = fdata->mechanism.length;
358b9238976Sth 	tdata->mechanism.elements = kmem_zalloc(fdata->mechanism.length,
359b9238976Sth 	    KM_SLEEP);
360b9238976Sth 	bcopy(fdata->mechanism.elements, tdata->mechanism.elements,
361b9238976Sth 	    fdata->mechanism.length);
362b9238976Sth 
363b9238976Sth 	tdata->service = fdata->service;
364b9238976Sth 
365b9238976Sth 	(void) strcpy(tdata->uname, fdata->uname);
366b9238976Sth 	(void) strcpy(tdata->inst, fdata->inst);
367b9238976Sth 	(void) strcpy(tdata->realm, fdata->realm);
368b9238976Sth 
369b9238976Sth 	tdata->qop = fdata->qop;
370b9238976Sth 
371b9238976Sth 	return (tdata);
372b9238976Sth }
373b9238976Sth 
3747c478bd9Sstevel@tonic-gate static int
3757c478bd9Sstevel@tonic-gate nfs4_chkdup_servinfo4(servinfo4_t *svp_head, servinfo4_t *svp)
3767c478bd9Sstevel@tonic-gate {
3777c478bd9Sstevel@tonic-gate 	servinfo4_t *si;
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 	/*
3807c478bd9Sstevel@tonic-gate 	 * Iterate over the servinfo4 list to make sure
3817c478bd9Sstevel@tonic-gate 	 * we do not have a duplicate. Skip any servinfo4
3827c478bd9Sstevel@tonic-gate 	 * that has been marked "NOT IN USE"
3837c478bd9Sstevel@tonic-gate 	 */
3847c478bd9Sstevel@tonic-gate 	for (si = svp_head; si; si = si->sv_next) {
3857c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&si->sv_lock, RW_READER, 0);
3867c478bd9Sstevel@tonic-gate 		if (si->sv_flags & SV4_NOTINUSE) {
3877c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&si->sv_lock);
3887c478bd9Sstevel@tonic-gate 			continue;
3897c478bd9Sstevel@tonic-gate 		}
3907c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&si->sv_lock);
3917c478bd9Sstevel@tonic-gate 		if (si == svp)
3927c478bd9Sstevel@tonic-gate 			continue;
3937c478bd9Sstevel@tonic-gate 		if (si->sv_addr.len == svp->sv_addr.len &&
3947c478bd9Sstevel@tonic-gate 		    strcmp(si->sv_knconf->knc_protofmly,
395b9238976Sth 		    svp->sv_knconf->knc_protofmly) == 0 &&
3967c478bd9Sstevel@tonic-gate 		    bcmp(si->sv_addr.buf, svp->sv_addr.buf,
397b9238976Sth 		    si->sv_addr.len) == 0) {
3987c478bd9Sstevel@tonic-gate 			/* it's a duplicate */
3997c478bd9Sstevel@tonic-gate 			return (1);
4007c478bd9Sstevel@tonic-gate 		}
4017c478bd9Sstevel@tonic-gate 	}
4027c478bd9Sstevel@tonic-gate 	/* it's not a duplicate */
4037c478bd9Sstevel@tonic-gate 	return (0);
4047c478bd9Sstevel@tonic-gate }
4057c478bd9Sstevel@tonic-gate 
40639d3e169Sevanl void
40739d3e169Sevanl nfs4_free_args(struct nfs_args *nargs)
40839d3e169Sevanl {
40939d3e169Sevanl 	if (nargs->knconf) {
41039d3e169Sevanl 		if (nargs->knconf->knc_protofmly)
41139d3e169Sevanl 			kmem_free(nargs->knconf->knc_protofmly,
412b9238976Sth 			    KNC_STRSIZE);
41339d3e169Sevanl 		if (nargs->knconf->knc_proto)
41439d3e169Sevanl 			kmem_free(nargs->knconf->knc_proto, KNC_STRSIZE);
41539d3e169Sevanl 		kmem_free(nargs->knconf, sizeof (*nargs->knconf));
41639d3e169Sevanl 		nargs->knconf = NULL;
41739d3e169Sevanl 	}
41839d3e169Sevanl 
41939d3e169Sevanl 	if (nargs->fh) {
42039d3e169Sevanl 		kmem_free(nargs->fh, strlen(nargs->fh) + 1);
42139d3e169Sevanl 		nargs->fh = NULL;
42239d3e169Sevanl 	}
42339d3e169Sevanl 
42439d3e169Sevanl 	if (nargs->hostname) {
42539d3e169Sevanl 		kmem_free(nargs->hostname, strlen(nargs->hostname) + 1);
42639d3e169Sevanl 		nargs->hostname = NULL;
42739d3e169Sevanl 	}
42839d3e169Sevanl 
42939d3e169Sevanl 	if (nargs->addr) {
43039d3e169Sevanl 		if (nargs->addr->buf) {
43139d3e169Sevanl 			ASSERT(nargs->addr->len);
43239d3e169Sevanl 			kmem_free(nargs->addr->buf, nargs->addr->len);
43339d3e169Sevanl 		}
43439d3e169Sevanl 		kmem_free(nargs->addr, sizeof (struct netbuf));
43539d3e169Sevanl 		nargs->addr = NULL;
43639d3e169Sevanl 	}
43739d3e169Sevanl 
43839d3e169Sevanl 	if (nargs->syncaddr) {
43939d3e169Sevanl 		ASSERT(nargs->syncaddr->len);
44039d3e169Sevanl 		if (nargs->syncaddr->buf) {
44139d3e169Sevanl 			ASSERT(nargs->syncaddr->len);
44239d3e169Sevanl 			kmem_free(nargs->syncaddr->buf, nargs->syncaddr->len);
44339d3e169Sevanl 		}
44439d3e169Sevanl 		kmem_free(nargs->syncaddr, sizeof (struct netbuf));
44539d3e169Sevanl 		nargs->syncaddr = NULL;
44639d3e169Sevanl 	}
44739d3e169Sevanl 
44839d3e169Sevanl 	if (nargs->netname) {
44939d3e169Sevanl 		kmem_free(nargs->netname, strlen(nargs->netname) + 1);
45039d3e169Sevanl 		nargs->netname = NULL;
45139d3e169Sevanl 	}
45239d3e169Sevanl 
45339d3e169Sevanl 	if (nargs->nfs_ext_u.nfs_extA.secdata) {
45439d3e169Sevanl 		sec_clnt_freeinfo(
455b9238976Sth 		    nargs->nfs_ext_u.nfs_extA.secdata);
45639d3e169Sevanl 		nargs->nfs_ext_u.nfs_extA.secdata = NULL;
45739d3e169Sevanl 	}
45839d3e169Sevanl }
45939d3e169Sevanl 
46039d3e169Sevanl 
46139d3e169Sevanl int
46239d3e169Sevanl nfs4_copyin(char *data, int datalen, struct nfs_args *nargs)
46339d3e169Sevanl {
46439d3e169Sevanl 
46539d3e169Sevanl 	int error;
46639d3e169Sevanl 	size_t hlen;			/* length of hostname */
46739d3e169Sevanl 	size_t nlen;			/* length of netname */
46839d3e169Sevanl 	char netname[MAXNETNAMELEN+1];	/* server's netname */
46939d3e169Sevanl 	struct netbuf addr;		/* server's address */
47039d3e169Sevanl 	struct netbuf syncaddr;		/* AUTH_DES time sync addr */
47139d3e169Sevanl 	struct knetconfig *knconf;		/* transport structure */
47239d3e169Sevanl 	struct sec_data *secdata = NULL;	/* security data */
47339d3e169Sevanl 	STRUCT_DECL(nfs_args, args);		/* nfs mount arguments */
47439d3e169Sevanl 	STRUCT_DECL(knetconfig, knconf_tmp);
47539d3e169Sevanl 	STRUCT_DECL(netbuf, addr_tmp);
47639d3e169Sevanl 	int flags;
47739d3e169Sevanl 	char *p, *pf;
47839d3e169Sevanl 	struct pathname pn;
47939d3e169Sevanl 	char *userbufptr;
48039d3e169Sevanl 
48139d3e169Sevanl 
48239d3e169Sevanl 	bzero(nargs, sizeof (*nargs));
48339d3e169Sevanl 
48439d3e169Sevanl 	STRUCT_INIT(args, get_udatamodel());
48539d3e169Sevanl 	bzero(STRUCT_BUF(args), SIZEOF_STRUCT(nfs_args, DATAMODEL_NATIVE));
48639d3e169Sevanl 	if (copyin(data, STRUCT_BUF(args), MIN(datalen,
48739d3e169Sevanl 	    STRUCT_SIZE(args))))
48839d3e169Sevanl 		return (EFAULT);
48939d3e169Sevanl 
49039d3e169Sevanl 	nargs->wsize = STRUCT_FGET(args, wsize);
49139d3e169Sevanl 	nargs->rsize = STRUCT_FGET(args, rsize);
49239d3e169Sevanl 	nargs->timeo = STRUCT_FGET(args, timeo);
49339d3e169Sevanl 	nargs->retrans = STRUCT_FGET(args, retrans);
49439d3e169Sevanl 	nargs->acregmin = STRUCT_FGET(args, acregmin);
49539d3e169Sevanl 	nargs->acregmax = STRUCT_FGET(args, acregmax);
49639d3e169Sevanl 	nargs->acdirmin = STRUCT_FGET(args, acdirmin);
49739d3e169Sevanl 	nargs->acdirmax = STRUCT_FGET(args, acdirmax);
49839d3e169Sevanl 
49939d3e169Sevanl 	flags = STRUCT_FGET(args, flags);
50039d3e169Sevanl 	nargs->flags = flags;
50139d3e169Sevanl 
50239d3e169Sevanl 	addr.buf = NULL;
50339d3e169Sevanl 	syncaddr.buf = NULL;
50439d3e169Sevanl 
50539d3e169Sevanl 
50639d3e169Sevanl 	/*
50739d3e169Sevanl 	 * Allocate space for a knetconfig structure and
50839d3e169Sevanl 	 * its strings and copy in from user-land.
50939d3e169Sevanl 	 */
51039d3e169Sevanl 	knconf = kmem_zalloc(sizeof (*knconf), KM_SLEEP);
51139d3e169Sevanl 	STRUCT_INIT(knconf_tmp, get_udatamodel());
51239d3e169Sevanl 	if (copyin(STRUCT_FGETP(args, knconf), STRUCT_BUF(knconf_tmp),
51339d3e169Sevanl 	    STRUCT_SIZE(knconf_tmp))) {
51439d3e169Sevanl 		kmem_free(knconf, sizeof (*knconf));
51539d3e169Sevanl 		return (EFAULT);
51639d3e169Sevanl 	}
51739d3e169Sevanl 
51839d3e169Sevanl 	knconf->knc_semantics = STRUCT_FGET(knconf_tmp, knc_semantics);
51939d3e169Sevanl 	knconf->knc_protofmly = STRUCT_FGETP(knconf_tmp, knc_protofmly);
52039d3e169Sevanl 	knconf->knc_proto = STRUCT_FGETP(knconf_tmp, knc_proto);
52139d3e169Sevanl 	if (get_udatamodel() != DATAMODEL_LP64) {
52239d3e169Sevanl 		knconf->knc_rdev = expldev(STRUCT_FGET(knconf_tmp, knc_rdev));
52339d3e169Sevanl 	} else {
52439d3e169Sevanl 		knconf->knc_rdev = STRUCT_FGET(knconf_tmp, knc_rdev);
52539d3e169Sevanl 	}
52639d3e169Sevanl 
52739d3e169Sevanl 	pf = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
52839d3e169Sevanl 	p = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
52939d3e169Sevanl 	error = copyinstr(knconf->knc_protofmly, pf, KNC_STRSIZE, NULL);
53039d3e169Sevanl 	if (error) {
53139d3e169Sevanl 		kmem_free(pf, KNC_STRSIZE);
53239d3e169Sevanl 		kmem_free(p, KNC_STRSIZE);
53339d3e169Sevanl 		kmem_free(knconf, sizeof (*knconf));
53439d3e169Sevanl 		return (error);
53539d3e169Sevanl 	}
53639d3e169Sevanl 
53739d3e169Sevanl 	error = copyinstr(knconf->knc_proto, p, KNC_STRSIZE, NULL);
53839d3e169Sevanl 	if (error) {
53939d3e169Sevanl 		kmem_free(pf, KNC_STRSIZE);
54039d3e169Sevanl 		kmem_free(p, KNC_STRSIZE);
54139d3e169Sevanl 		kmem_free(knconf, sizeof (*knconf));
54239d3e169Sevanl 		return (error);
54339d3e169Sevanl 	}
54439d3e169Sevanl 
54539d3e169Sevanl 
54639d3e169Sevanl 	knconf->knc_protofmly = pf;
54739d3e169Sevanl 	knconf->knc_proto = p;
54839d3e169Sevanl 
54939d3e169Sevanl 	nargs->knconf = knconf;
55039d3e169Sevanl 
55139d3e169Sevanl 	/*
55239d3e169Sevanl 	 * Get server address
55339d3e169Sevanl 	 */
55439d3e169Sevanl 	STRUCT_INIT(addr_tmp, get_udatamodel());
55539d3e169Sevanl 	if (copyin(STRUCT_FGETP(args, addr), STRUCT_BUF(addr_tmp),
55639d3e169Sevanl 	    STRUCT_SIZE(addr_tmp))) {
55739d3e169Sevanl 		error = EFAULT;
55839d3e169Sevanl 		goto errout;
55939d3e169Sevanl 	}
56039d3e169Sevanl 
5617e450cbcSJames Wahlig 	nargs->addr = kmem_zalloc(sizeof (struct netbuf), KM_SLEEP);
56239d3e169Sevanl 	userbufptr = STRUCT_FGETP(addr_tmp, buf);
56339d3e169Sevanl 	addr.len = STRUCT_FGET(addr_tmp, len);
56439d3e169Sevanl 	addr.buf = kmem_alloc(addr.len, KM_SLEEP);
56539d3e169Sevanl 	addr.maxlen = addr.len;
56639d3e169Sevanl 	if (copyin(userbufptr, addr.buf, addr.len)) {
56739d3e169Sevanl 		kmem_free(addr.buf, addr.len);
56839d3e169Sevanl 		error = EFAULT;
56939d3e169Sevanl 		goto errout;
57039d3e169Sevanl 	}
57139d3e169Sevanl 	bcopy(&addr, nargs->addr, sizeof (struct netbuf));
57239d3e169Sevanl 
57339d3e169Sevanl 	/*
57439d3e169Sevanl 	 * Get the root fhandle
57539d3e169Sevanl 	 */
57639d3e169Sevanl 	error = pn_get(STRUCT_FGETP(args, fh), UIO_USERSPACE, &pn);
57739d3e169Sevanl 	if (error)
57839d3e169Sevanl 		goto errout;
57939d3e169Sevanl 
58039d3e169Sevanl 	/* Volatile fh: keep server paths, so use actual-size strings */
58139d3e169Sevanl 	nargs->fh = kmem_alloc(pn.pn_pathlen + 1, KM_SLEEP);
58239d3e169Sevanl 	bcopy(pn.pn_path, nargs->fh, pn.pn_pathlen);
58339d3e169Sevanl 	nargs->fh[pn.pn_pathlen] = '\0';
58439d3e169Sevanl 	pn_free(&pn);
58539d3e169Sevanl 
58639d3e169Sevanl 
58739d3e169Sevanl 	/*
58839d3e169Sevanl 	 * Get server's hostname
58939d3e169Sevanl 	 */
59039d3e169Sevanl 	if (flags & NFSMNT_HOSTNAME) {
59139d3e169Sevanl 		error = copyinstr(STRUCT_FGETP(args, hostname),
592b9238976Sth 		    netname, sizeof (netname), &hlen);
59339d3e169Sevanl 		if (error)
59439d3e169Sevanl 			goto errout;
59539d3e169Sevanl 		nargs->hostname = kmem_zalloc(hlen, KM_SLEEP);
59639d3e169Sevanl 		(void) strcpy(nargs->hostname, netname);
59739d3e169Sevanl 
59839d3e169Sevanl 	} else {
59939d3e169Sevanl 		nargs->hostname = NULL;
60039d3e169Sevanl 	}
60139d3e169Sevanl 
60239d3e169Sevanl 
60339d3e169Sevanl 	/*
60439d3e169Sevanl 	 * If there are syncaddr and netname data, load them in. This is
60539d3e169Sevanl 	 * to support data needed for NFSV4 when AUTH_DH is the negotiated
60639d3e169Sevanl 	 * flavor via SECINFO. (instead of using MOUNT protocol in V3).
60739d3e169Sevanl 	 */
60839d3e169Sevanl 	netname[0] = '\0';
60939d3e169Sevanl 	if (flags & NFSMNT_SECURE) {
61039d3e169Sevanl 
61139d3e169Sevanl 		/* get syncaddr */
61239d3e169Sevanl 		STRUCT_INIT(addr_tmp, get_udatamodel());
61339d3e169Sevanl 		if (copyin(STRUCT_FGETP(args, syncaddr), STRUCT_BUF(addr_tmp),
614b9238976Sth 		    STRUCT_SIZE(addr_tmp))) {
61539d3e169Sevanl 			error = EINVAL;
61639d3e169Sevanl 			goto errout;
61739d3e169Sevanl 		}
61839d3e169Sevanl 		userbufptr = STRUCT_FGETP(addr_tmp, buf);
61939d3e169Sevanl 		syncaddr.len = STRUCT_FGET(addr_tmp, len);
62039d3e169Sevanl 		syncaddr.buf = kmem_alloc(syncaddr.len, KM_SLEEP);
62139d3e169Sevanl 		syncaddr.maxlen = syncaddr.len;
62239d3e169Sevanl 		if (copyin(userbufptr, syncaddr.buf, syncaddr.len)) {
62339d3e169Sevanl 			kmem_free(syncaddr.buf, syncaddr.len);
62439d3e169Sevanl 			error = EFAULT;
62539d3e169Sevanl 			goto errout;
62639d3e169Sevanl 		}
62739d3e169Sevanl 
62839d3e169Sevanl 		nargs->syncaddr = kmem_alloc(sizeof (struct netbuf), KM_SLEEP);
62939d3e169Sevanl 		bcopy(&syncaddr, nargs->syncaddr, sizeof (struct netbuf));
63039d3e169Sevanl 
63122d5e933Skr 		/* get server's netname */
63222d5e933Skr 		if (copyinstr(STRUCT_FGETP(args, netname), netname,
633b9238976Sth 		    sizeof (netname), &nlen)) {
63422d5e933Skr 			error = EFAULT;
63522d5e933Skr 			goto errout;
63622d5e933Skr 		}
63722d5e933Skr 
63822d5e933Skr 		netname[nlen] = '\0';
63922d5e933Skr 		nargs->netname = kmem_zalloc(nlen, KM_SLEEP);
64022d5e933Skr 		(void) strcpy(nargs->netname, netname);
64122d5e933Skr 	}
64239d3e169Sevanl 
64339d3e169Sevanl 	/*
64439d3e169Sevanl 	 * Get the extention data which has the security data structure.
64539d3e169Sevanl 	 * This includes data for AUTH_SYS as well.
64639d3e169Sevanl 	 */
64739d3e169Sevanl 	if (flags & NFSMNT_NEWARGS) {
64839d3e169Sevanl 		nargs->nfs_args_ext = STRUCT_FGET(args, nfs_args_ext);
64939d3e169Sevanl 		if (nargs->nfs_args_ext == NFS_ARGS_EXTA ||
650b9238976Sth 		    nargs->nfs_args_ext == NFS_ARGS_EXTB) {
65139d3e169Sevanl 			/*
65239d3e169Sevanl 			 * Indicating the application is using the new
65339d3e169Sevanl 			 * sec_data structure to pass in the security
65439d3e169Sevanl 			 * data.
65539d3e169Sevanl 			 */
65639d3e169Sevanl 			if (STRUCT_FGETP(args,
65739d3e169Sevanl 			    nfs_ext_u.nfs_extA.secdata) != NULL) {
65839d3e169Sevanl 				error = sec_clnt_loadinfo(
65939d3e169Sevanl 				    (struct sec_data *)STRUCT_FGETP(args,
660b9238976Sth 				    nfs_ext_u.nfs_extA.secdata),
66139d3e169Sevanl 				    &secdata, get_udatamodel());
66239d3e169Sevanl 			}
66339d3e169Sevanl 			nargs->nfs_ext_u.nfs_extA.secdata = secdata;
66439d3e169Sevanl 		}
66539d3e169Sevanl 	}
66639d3e169Sevanl 
66739d3e169Sevanl 	if (error)
66839d3e169Sevanl 		goto errout;
66939d3e169Sevanl 
67039d3e169Sevanl 	/*
67139d3e169Sevanl 	 * Failover support:
67239d3e169Sevanl 	 *
67339d3e169Sevanl 	 * We may have a linked list of nfs_args structures,
67439d3e169Sevanl 	 * which means the user is looking for failover.  If
67539d3e169Sevanl 	 * the mount is either not "read-only" or "soft",
67639d3e169Sevanl 	 * we want to bail out with EINVAL.
67739d3e169Sevanl 	 */
67839d3e169Sevanl 	if (nargs->nfs_args_ext == NFS_ARGS_EXTB)
67939d3e169Sevanl 		nargs->nfs_ext_u.nfs_extB.next =
680b9238976Sth 		    STRUCT_FGETP(args, nfs_ext_u.nfs_extB.next);
68139d3e169Sevanl 
68239d3e169Sevanl errout:
68339d3e169Sevanl 	if (error)
68439d3e169Sevanl 		nfs4_free_args(nargs);
68539d3e169Sevanl 
68639d3e169Sevanl 	return (error);
68739d3e169Sevanl }
68839d3e169Sevanl 
68939d3e169Sevanl 
6907c478bd9Sstevel@tonic-gate /*
6917c478bd9Sstevel@tonic-gate  * nfs mount vfsop
6927c478bd9Sstevel@tonic-gate  * Set up mount info record and attach it to vfs struct.
6937c478bd9Sstevel@tonic-gate  */
694b9238976Sth int
6957c478bd9Sstevel@tonic-gate nfs4_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
6967c478bd9Sstevel@tonic-gate {
6977c478bd9Sstevel@tonic-gate 	char *data = uap->dataptr;
6987c478bd9Sstevel@tonic-gate 	int error;
6997c478bd9Sstevel@tonic-gate 	vnode_t *rtvp;			/* the server's root */
7007c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;			/* mount info, pointed at by vfs */
7017c478bd9Sstevel@tonic-gate 	struct knetconfig *rdma_knconf;	/* rdma transport structure */
7027c478bd9Sstevel@tonic-gate 	rnode4_t *rp;
7037c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp;		/* nfs server info */
7047c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp_tail = NULL; /* previous nfs server info */
7057c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp_head;	/* first nfs server info */
7067c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp_2ndlast;	/* 2nd last in server info list */
7077c478bd9Sstevel@tonic-gate 	struct sec_data *secdata;	/* security data */
70839d3e169Sevanl 	struct nfs_args *args = NULL;
70950a83466Sjwahlig 	int flags, addr_type, removed;
710108322fbScarlsonj 	zone_t *zone = nfs_zone();
7117c478bd9Sstevel@tonic-gate 	nfs4_error_t n4e;
71245916cd2Sjpk 	zone_t *mntzone = NULL;
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
7157c478bd9Sstevel@tonic-gate 		return (EPERM);
7167c478bd9Sstevel@tonic-gate 	if (mvp->v_type != VDIR)
7177c478bd9Sstevel@tonic-gate 		return (ENOTDIR);
718b9238976Sth 
7197c478bd9Sstevel@tonic-gate 	/*
7207c478bd9Sstevel@tonic-gate 	 * get arguments
7217c478bd9Sstevel@tonic-gate 	 *
7227c478bd9Sstevel@tonic-gate 	 * nfs_args is now versioned and is extensible, so
7237c478bd9Sstevel@tonic-gate 	 * uap->datalen might be different from sizeof (args)
7247c478bd9Sstevel@tonic-gate 	 * in a compatible situation.
7257c478bd9Sstevel@tonic-gate 	 */
7267c478bd9Sstevel@tonic-gate more:
72739d3e169Sevanl 	if (!(uap->flags & MS_SYSSPACE)) {
72839d3e169Sevanl 		if (args == NULL)
72939d3e169Sevanl 			args = kmem_zalloc(sizeof (struct nfs_args), KM_SLEEP);
73039d3e169Sevanl 		else
73139d3e169Sevanl 			nfs4_free_args(args);
73239d3e169Sevanl 		error = nfs4_copyin(data, uap->datalen, args);
73339d3e169Sevanl 		if (error) {
73439d3e169Sevanl 			if (args) {
73539d3e169Sevanl 				kmem_free(args, sizeof (*args));
73639d3e169Sevanl 			}
73739d3e169Sevanl 			return (error);
73839d3e169Sevanl 		}
73939d3e169Sevanl 	} else {
74039d3e169Sevanl 		args = (struct nfs_args *)data;
74139d3e169Sevanl 	}
7427c478bd9Sstevel@tonic-gate 
74339d3e169Sevanl 	flags = args->flags;
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate 	/*
7467c478bd9Sstevel@tonic-gate 	 * If the request changes the locking type, disallow the remount,
7477c478bd9Sstevel@tonic-gate 	 * because it's questionable whether we can transfer the
7487c478bd9Sstevel@tonic-gate 	 * locking state correctly.
7497c478bd9Sstevel@tonic-gate 	 */
7507c478bd9Sstevel@tonic-gate 	if (uap->flags & MS_REMOUNT) {
75139d3e169Sevanl 		if (!(uap->flags & MS_SYSSPACE)) {
75239d3e169Sevanl 			nfs4_free_args(args);
75339d3e169Sevanl 			kmem_free(args, sizeof (*args));
75439d3e169Sevanl 		}
7557c478bd9Sstevel@tonic-gate 		if ((mi = VFTOMI4(vfsp)) != NULL) {
7567c478bd9Sstevel@tonic-gate 			uint_t new_mi_llock;
7577c478bd9Sstevel@tonic-gate 			uint_t old_mi_llock;
7587c478bd9Sstevel@tonic-gate 			new_mi_llock = (flags & NFSMNT_LLOCK) ? 1 : 0;
7597c478bd9Sstevel@tonic-gate 			old_mi_llock = (mi->mi_flags & MI4_LLOCK) ? 1 : 0;
7607c478bd9Sstevel@tonic-gate 			if (old_mi_llock != new_mi_llock)
7617c478bd9Sstevel@tonic-gate 				return (EBUSY);
7627c478bd9Sstevel@tonic-gate 		}
7637c478bd9Sstevel@tonic-gate 		return (0);
7647c478bd9Sstevel@tonic-gate 	}
7657c478bd9Sstevel@tonic-gate 
766b9238976Sth 	/*
767b9238976Sth 	 * For ephemeral mount trigger stub vnodes, we have two problems
768b9238976Sth 	 * to solve: racing threads will likely fail the v_count check, and
769b9238976Sth 	 * we want only one to proceed with the mount.
770b9238976Sth 	 *
771b9238976Sth 	 * For stubs, if the mount has already occurred (via a racing thread),
772b9238976Sth 	 * just return success. If not, skip the v_count check and proceed.
773b9238976Sth 	 * Note that we are already serialised at this point.
774b9238976Sth 	 */
7757c478bd9Sstevel@tonic-gate 	mutex_enter(&mvp->v_lock);
776b9238976Sth 	if (vn_matchops(mvp, nfs4_trigger_vnodeops)) {
777b9238976Sth 		/* mntpt is a v4 stub vnode */
778b9238976Sth 		ASSERT(RP_ISSTUB(VTOR4(mvp)));
779b9238976Sth 		ASSERT(!(uap->flags & MS_OVERLAY));
780b9238976Sth 		ASSERT(!(mvp->v_flag & VROOT));
781b9238976Sth 		if (vn_mountedvfs(mvp) != NULL) {
782b9238976Sth 			/* ephemeral mount has already occurred */
783b9238976Sth 			ASSERT(uap->flags & MS_SYSSPACE);
784b9238976Sth 			mutex_exit(&mvp->v_lock);
785b9238976Sth 			return (0);
786b9238976Sth 		}
787b9238976Sth 	} else {
788b9238976Sth 		/* mntpt is a non-v4 or v4 non-stub vnode */
789b9238976Sth 		if (!(uap->flags & MS_OVERLAY) &&
790b9238976Sth 		    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
791b9238976Sth 			mutex_exit(&mvp->v_lock);
792b9238976Sth 			if (!(uap->flags & MS_SYSSPACE)) {
793b9238976Sth 				nfs4_free_args(args);
794b9238976Sth 				kmem_free(args, sizeof (*args));
795b9238976Sth 			}
796b9238976Sth 			return (EBUSY);
79739d3e169Sevanl 		}
7987c478bd9Sstevel@tonic-gate 	}
7997c478bd9Sstevel@tonic-gate 	mutex_exit(&mvp->v_lock);
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 	/* make sure things are zeroed for errout: */
8027c478bd9Sstevel@tonic-gate 	rtvp = NULL;
8037c478bd9Sstevel@tonic-gate 	mi = NULL;
8047c478bd9Sstevel@tonic-gate 	secdata = NULL;
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate 	/*
8077c478bd9Sstevel@tonic-gate 	 * A valid knetconfig structure is required.
8087c478bd9Sstevel@tonic-gate 	 */
80939d3e169Sevanl 	if (!(flags & NFSMNT_KNCONF) ||
810b9238976Sth 	    args->knconf == NULL || args->knconf->knc_protofmly == NULL ||
811b9238976Sth 	    args->knconf->knc_proto == NULL ||
812b9238976Sth 	    (strcmp(args->knconf->knc_proto, NC_UDP) == 0)) {
81339d3e169Sevanl 		if (!(uap->flags & MS_SYSSPACE)) {
81439d3e169Sevanl 			nfs4_free_args(args);
81539d3e169Sevanl 			kmem_free(args, sizeof (*args));
81639d3e169Sevanl 		}
8177c478bd9Sstevel@tonic-gate 		return (EINVAL);
81839d3e169Sevanl 	}
81939d3e169Sevanl 
82039d3e169Sevanl 	if ((strlen(args->knconf->knc_protofmly) >= KNC_STRSIZE) ||
821b9238976Sth 	    (strlen(args->knconf->knc_proto) >= KNC_STRSIZE)) {
82239d3e169Sevanl 		if (!(uap->flags & MS_SYSSPACE)) {
82339d3e169Sevanl 			nfs4_free_args(args);
82439d3e169Sevanl 			kmem_free(args, sizeof (*args));
82539d3e169Sevanl 		}
82639d3e169Sevanl 		return (EINVAL);
82739d3e169Sevanl 	}
82839d3e169Sevanl 
8297c478bd9Sstevel@tonic-gate 	/*
8307c478bd9Sstevel@tonic-gate 	 * Allocate a servinfo4 struct.
8317c478bd9Sstevel@tonic-gate 	 */
8327c478bd9Sstevel@tonic-gate 	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
8337c478bd9Sstevel@tonic-gate 	nfs_rw_init(&svp->sv_lock, NULL, RW_DEFAULT, NULL);
8347c478bd9Sstevel@tonic-gate 	if (svp_tail) {
8357c478bd9Sstevel@tonic-gate 		svp_2ndlast = svp_tail;
8367c478bd9Sstevel@tonic-gate 		svp_tail->sv_next = svp;
8377c478bd9Sstevel@tonic-gate 	} else {
8387c478bd9Sstevel@tonic-gate 		svp_head = svp;
8397c478bd9Sstevel@tonic-gate 		svp_2ndlast = svp;
8407c478bd9Sstevel@tonic-gate 	}
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate 	svp_tail = svp;
84339d3e169Sevanl 	svp->sv_knconf = args->knconf;
84439d3e169Sevanl 	args->knconf = NULL;
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 	/*
8477c478bd9Sstevel@tonic-gate 	 * Get server address
8487c478bd9Sstevel@tonic-gate 	 */
84939d3e169Sevanl 	if (args->addr == NULL || args->addr->buf == NULL) {
85039d3e169Sevanl 		error = EINVAL;
8517c478bd9Sstevel@tonic-gate 		goto errout;
8527c478bd9Sstevel@tonic-gate 	}
8537c478bd9Sstevel@tonic-gate 
85439d3e169Sevanl 	svp->sv_addr.maxlen = args->addr->maxlen;
85539d3e169Sevanl 	svp->sv_addr.len = args->addr->len;
85639d3e169Sevanl 	svp->sv_addr.buf = args->addr->buf;
85739d3e169Sevanl 	args->addr->buf = NULL;
85839d3e169Sevanl 
8597c478bd9Sstevel@tonic-gate 	/*
8607c478bd9Sstevel@tonic-gate 	 * Get the root fhandle
8617c478bd9Sstevel@tonic-gate 	 */
86239d3e169Sevanl 	if (args->fh == NULL || (strlen(args->fh) >= MAXPATHLEN)) {
86339d3e169Sevanl 		error = EINVAL;
8647c478bd9Sstevel@tonic-gate 		goto errout;
86539d3e169Sevanl 	}
8667c478bd9Sstevel@tonic-gate 
86739d3e169Sevanl 	svp->sv_path = args->fh;
86839d3e169Sevanl 	svp->sv_pathlen = strlen(args->fh) + 1;
86939d3e169Sevanl 	args->fh = NULL;
8707c478bd9Sstevel@tonic-gate 
8717c478bd9Sstevel@tonic-gate 	/*
8727c478bd9Sstevel@tonic-gate 	 * Get server's hostname
8737c478bd9Sstevel@tonic-gate 	 */
8747c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_HOSTNAME) {
87539d3e169Sevanl 		if (args->hostname == NULL || (strlen(args->hostname) >
876b9238976Sth 		    MAXNETNAMELEN)) {
87739d3e169Sevanl 			error = EINVAL;
8787c478bd9Sstevel@tonic-gate 			goto errout;
87939d3e169Sevanl 		}
88039d3e169Sevanl 		svp->sv_hostnamelen = strlen(args->hostname) + 1;
88139d3e169Sevanl 		svp->sv_hostname = args->hostname;
88239d3e169Sevanl 		args->hostname = NULL;
8837c478bd9Sstevel@tonic-gate 	} else {
8847c478bd9Sstevel@tonic-gate 		char *p = "unknown-host";
88539d3e169Sevanl 		svp->sv_hostnamelen = strlen(p) + 1;
88639d3e169Sevanl 		svp->sv_hostname = kmem_zalloc(svp->sv_hostnamelen, KM_SLEEP);
88739d3e169Sevanl 		(void) strcpy(svp->sv_hostname, p);
8887c478bd9Sstevel@tonic-gate 	}
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 	/*
8917c478bd9Sstevel@tonic-gate 	 * RDMA MOUNT SUPPORT FOR NFS v4.
8927c478bd9Sstevel@tonic-gate 	 * Establish, is it possible to use RDMA, if so overload the
8937c478bd9Sstevel@tonic-gate 	 * knconf with rdma specific knconf and free the orignal knconf.
8947c478bd9Sstevel@tonic-gate 	 */
8957c478bd9Sstevel@tonic-gate 	if ((flags & NFSMNT_TRYRDMA) || (flags & NFSMNT_DORDMA)) {
8967c478bd9Sstevel@tonic-gate 		/*
8977c478bd9Sstevel@tonic-gate 		 * Determine the addr type for RDMA, IPv4 or v6.
8987c478bd9Sstevel@tonic-gate 		 */
8997c478bd9Sstevel@tonic-gate 		if (strcmp(svp->sv_knconf->knc_protofmly, NC_INET) == 0)
9007c478bd9Sstevel@tonic-gate 			addr_type = AF_INET;
9017c478bd9Sstevel@tonic-gate 		else if (strcmp(svp->sv_knconf->knc_protofmly, NC_INET6) == 0)
9027c478bd9Sstevel@tonic-gate 			addr_type = AF_INET6;
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate 		if (rdma_reachable(addr_type, &svp->sv_addr,
905b9238976Sth 		    &rdma_knconf) == 0) {
9067c478bd9Sstevel@tonic-gate 			/*
9077c478bd9Sstevel@tonic-gate 			 * If successful, hijack the orignal knconf and
9087c478bd9Sstevel@tonic-gate 			 * replace with the new one, depending on the flags.
9097c478bd9Sstevel@tonic-gate 			 */
9107c478bd9Sstevel@tonic-gate 			svp->sv_origknconf = svp->sv_knconf;
9117c478bd9Sstevel@tonic-gate 			svp->sv_knconf = rdma_knconf;
9127c478bd9Sstevel@tonic-gate 		} else {
9137c478bd9Sstevel@tonic-gate 			if (flags & NFSMNT_TRYRDMA) {
9147c478bd9Sstevel@tonic-gate #ifdef	DEBUG
9157c478bd9Sstevel@tonic-gate 				if (rdma_debug)
9167c478bd9Sstevel@tonic-gate 					zcmn_err(getzoneid(), CE_WARN,
9177c478bd9Sstevel@tonic-gate 					    "no RDMA onboard, revert\n");
9187c478bd9Sstevel@tonic-gate #endif
9197c478bd9Sstevel@tonic-gate 			}
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate 			if (flags & NFSMNT_DORDMA) {
9227c478bd9Sstevel@tonic-gate 				/*
9237c478bd9Sstevel@tonic-gate 				 * If proto=rdma is specified and no RDMA
9247c478bd9Sstevel@tonic-gate 				 * path to this server is avialable then
9257c478bd9Sstevel@tonic-gate 				 * ditch this server.
9267c478bd9Sstevel@tonic-gate 				 * This is not included in the mountable
9277c478bd9Sstevel@tonic-gate 				 * server list or the replica list.
9287c478bd9Sstevel@tonic-gate 				 * Check if more servers are specified;
9297c478bd9Sstevel@tonic-gate 				 * Failover case, otherwise bail out of mount.
9307c478bd9Sstevel@tonic-gate 				 */
931b9238976Sth 				if (args->nfs_args_ext == NFS_ARGS_EXTB &&
932b9238976Sth 				    args->nfs_ext_u.nfs_extB.next != NULL) {
93339d3e169Sevanl 					data = (char *)
934b9238976Sth 					    args->nfs_ext_u.nfs_extB.next;
9357c478bd9Sstevel@tonic-gate 					if (uap->flags & MS_RDONLY &&
9367c478bd9Sstevel@tonic-gate 					    !(flags & NFSMNT_SOFT)) {
9377c478bd9Sstevel@tonic-gate 						if (svp_head->sv_next == NULL) {
9387c478bd9Sstevel@tonic-gate 							svp_tail = NULL;
9397c478bd9Sstevel@tonic-gate 							svp_2ndlast = NULL;
9407c478bd9Sstevel@tonic-gate 							sv4_free(svp_head);
9417c478bd9Sstevel@tonic-gate 							goto more;
9427c478bd9Sstevel@tonic-gate 						} else {
9437c478bd9Sstevel@tonic-gate 							svp_tail = svp_2ndlast;
9447c478bd9Sstevel@tonic-gate 							svp_2ndlast->sv_next =
9457c478bd9Sstevel@tonic-gate 							    NULL;
9467c478bd9Sstevel@tonic-gate 							sv4_free(svp);
9477c478bd9Sstevel@tonic-gate 							goto more;
9487c478bd9Sstevel@tonic-gate 						}
9497c478bd9Sstevel@tonic-gate 					}
9507c478bd9Sstevel@tonic-gate 				} else {
9517c478bd9Sstevel@tonic-gate 					/*
9527c478bd9Sstevel@tonic-gate 					 * This is the last server specified
9537c478bd9Sstevel@tonic-gate 					 * in the nfs_args list passed down
9547c478bd9Sstevel@tonic-gate 					 * and its not rdma capable.
9557c478bd9Sstevel@tonic-gate 					 */
9567c478bd9Sstevel@tonic-gate 					if (svp_head->sv_next == NULL) {
9577c478bd9Sstevel@tonic-gate 						/*
9587c478bd9Sstevel@tonic-gate 						 * Is this the only one
9597c478bd9Sstevel@tonic-gate 						 */
9607c478bd9Sstevel@tonic-gate 						error = EINVAL;
9617c478bd9Sstevel@tonic-gate #ifdef	DEBUG
9627c478bd9Sstevel@tonic-gate 						if (rdma_debug)
9637c478bd9Sstevel@tonic-gate 							zcmn_err(getzoneid(),
9647c478bd9Sstevel@tonic-gate 							    CE_WARN,
9657c478bd9Sstevel@tonic-gate 							    "No RDMA srv");
9667c478bd9Sstevel@tonic-gate #endif
9677c478bd9Sstevel@tonic-gate 						goto errout;
9687c478bd9Sstevel@tonic-gate 					} else {
9697c478bd9Sstevel@tonic-gate 						/*
9707c478bd9Sstevel@tonic-gate 						 * There is list, since some
9717c478bd9Sstevel@tonic-gate 						 * servers specified before
9727c478bd9Sstevel@tonic-gate 						 * this passed all requirements
9737c478bd9Sstevel@tonic-gate 						 */
9747c478bd9Sstevel@tonic-gate 						svp_tail = svp_2ndlast;
9757c478bd9Sstevel@tonic-gate 						svp_2ndlast->sv_next = NULL;
9767c478bd9Sstevel@tonic-gate 						sv4_free(svp);
9777c478bd9Sstevel@tonic-gate 						goto proceed;
9787c478bd9Sstevel@tonic-gate 					}
9797c478bd9Sstevel@tonic-gate 				}
9807c478bd9Sstevel@tonic-gate 			}
9817c478bd9Sstevel@tonic-gate 		}
9827c478bd9Sstevel@tonic-gate 	}
9837c478bd9Sstevel@tonic-gate 
9847c478bd9Sstevel@tonic-gate 	/*
9857c478bd9Sstevel@tonic-gate 	 * If there are syncaddr and netname data, load them in. This is
9867c478bd9Sstevel@tonic-gate 	 * to support data needed for NFSV4 when AUTH_DH is the negotiated
9877c478bd9Sstevel@tonic-gate 	 * flavor via SECINFO. (instead of using MOUNT protocol in V3).
9887c478bd9Sstevel@tonic-gate 	 */
98939d3e169Sevanl 	if (args->flags & NFSMNT_SECURE) {
99039d3e169Sevanl 		svp->sv_dhsec = create_authdh_data(args->netname,
991b9238976Sth 		    strlen(args->netname),
992b9238976Sth 		    args->syncaddr, svp->sv_knconf);
9937c478bd9Sstevel@tonic-gate 	}
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 	/*
9967c478bd9Sstevel@tonic-gate 	 * Get the extention data which has the security data structure.
9977c478bd9Sstevel@tonic-gate 	 * This includes data for AUTH_SYS as well.
9987c478bd9Sstevel@tonic-gate 	 */
9997c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_NEWARGS) {
100039d3e169Sevanl 		switch (args->nfs_args_ext) {
10017c478bd9Sstevel@tonic-gate 		case NFS_ARGS_EXTA:
10027c478bd9Sstevel@tonic-gate 		case NFS_ARGS_EXTB:
10037c478bd9Sstevel@tonic-gate 			/*
10047c478bd9Sstevel@tonic-gate 			 * Indicating the application is using the new
10057c478bd9Sstevel@tonic-gate 			 * sec_data structure to pass in the security
10067c478bd9Sstevel@tonic-gate 			 * data.
10077c478bd9Sstevel@tonic-gate 			 */
100839d3e169Sevanl 			secdata = args->nfs_ext_u.nfs_extA.secdata;
100939d3e169Sevanl 			if (secdata == NULL) {
10107c478bd9Sstevel@tonic-gate 				error = EINVAL;
101139d3e169Sevanl 			} else if (uap->flags & MS_SYSSPACE) {
101239d3e169Sevanl 				/*
101339d3e169Sevanl 				 * Need to validate the flavor here if
101439d3e169Sevanl 				 * sysspace, userspace was already
101539d3e169Sevanl 				 * validate from the nfs_copyin function.
101639d3e169Sevanl 				 */
101739d3e169Sevanl 				switch (secdata->rpcflavor) {
101839d3e169Sevanl 				case AUTH_NONE:
101939d3e169Sevanl 				case AUTH_UNIX:
102039d3e169Sevanl 				case AUTH_LOOPBACK:
102139d3e169Sevanl 				case AUTH_DES:
102239d3e169Sevanl 				case RPCSEC_GSS:
102339d3e169Sevanl 					break;
102439d3e169Sevanl 				default:
102539d3e169Sevanl 					error = EINVAL;
102639d3e169Sevanl 					goto errout;
102739d3e169Sevanl 				}
10287c478bd9Sstevel@tonic-gate 			}
102939d3e169Sevanl 			args->nfs_ext_u.nfs_extA.secdata = NULL;
10307c478bd9Sstevel@tonic-gate 			break;
10317c478bd9Sstevel@tonic-gate 
10327c478bd9Sstevel@tonic-gate 		default:
10337c478bd9Sstevel@tonic-gate 			error = EINVAL;
10347c478bd9Sstevel@tonic-gate 			break;
10357c478bd9Sstevel@tonic-gate 		}
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate 	} else if (flags & NFSMNT_SECURE) {
10387c478bd9Sstevel@tonic-gate 		/*
10397c478bd9Sstevel@tonic-gate 		 * NFSMNT_SECURE is deprecated but we keep it
1040b9238976Sth 		 * to support the rogue user-generated application
10417c478bd9Sstevel@tonic-gate 		 * that may use this undocumented interface to do
1042b9238976Sth 		 * AUTH_DH security, e.g. our own rexd.
1043b9238976Sth 		 *
1044b9238976Sth 		 * Also note that NFSMNT_SECURE is used for passing
1045b9238976Sth 		 * AUTH_DH info to be used in negotiation.
10467c478bd9Sstevel@tonic-gate 		 */
104739d3e169Sevanl 		secdata = create_authdh_data(args->netname,
1048b9238976Sth 		    strlen(args->netname), args->syncaddr, svp->sv_knconf);
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate 	} else {
10517c478bd9Sstevel@tonic-gate 		secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP);
10527c478bd9Sstevel@tonic-gate 		secdata->secmod = secdata->rpcflavor = AUTH_SYS;
10537c478bd9Sstevel@tonic-gate 		secdata->data = NULL;
10547c478bd9Sstevel@tonic-gate 	}
10557c478bd9Sstevel@tonic-gate 
10567c478bd9Sstevel@tonic-gate 	svp->sv_secdata = secdata;
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate 	/*
10597c478bd9Sstevel@tonic-gate 	 * User does not explictly specify a flavor, and a user
10607c478bd9Sstevel@tonic-gate 	 * defined default flavor is passed down.
10617c478bd9Sstevel@tonic-gate 	 */
10627c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_SECDEFAULT) {
10637c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
10647c478bd9Sstevel@tonic-gate 		svp->sv_flags |= SV4_TRYSECDEFAULT;
10657c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
10667c478bd9Sstevel@tonic-gate 	}
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate 	/*
10697c478bd9Sstevel@tonic-gate 	 * Failover support:
10707c478bd9Sstevel@tonic-gate 	 *
10717c478bd9Sstevel@tonic-gate 	 * We may have a linked list of nfs_args structures,
10727c478bd9Sstevel@tonic-gate 	 * which means the user is looking for failover.  If
10737c478bd9Sstevel@tonic-gate 	 * the mount is either not "read-only" or "soft",
10747c478bd9Sstevel@tonic-gate 	 * we want to bail out with EINVAL.
10757c478bd9Sstevel@tonic-gate 	 */
107639d3e169Sevanl 	if (args->nfs_args_ext == NFS_ARGS_EXTB &&
107739d3e169Sevanl 	    args->nfs_ext_u.nfs_extB.next != NULL) {
10787c478bd9Sstevel@tonic-gate 		if (uap->flags & MS_RDONLY && !(flags & NFSMNT_SOFT)) {
107939d3e169Sevanl 			data = (char *)args->nfs_ext_u.nfs_extB.next;
10807c478bd9Sstevel@tonic-gate 			goto more;
10817c478bd9Sstevel@tonic-gate 		}
10827c478bd9Sstevel@tonic-gate 		error = EINVAL;
10837c478bd9Sstevel@tonic-gate 		goto errout;
10847c478bd9Sstevel@tonic-gate 	}
10857c478bd9Sstevel@tonic-gate 
10867c478bd9Sstevel@tonic-gate 	/*
10877c478bd9Sstevel@tonic-gate 	 * Determine the zone we're being mounted into.
10887c478bd9Sstevel@tonic-gate 	 */
108945916cd2Sjpk 	zone_hold(mntzone = zone);		/* start with this assumption */
10907c478bd9Sstevel@tonic-gate 	if (getzoneid() == GLOBAL_ZONEID) {
109145916cd2Sjpk 		zone_rele(mntzone);
10927c478bd9Sstevel@tonic-gate 		mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
10937c478bd9Sstevel@tonic-gate 		ASSERT(mntzone != NULL);
10947c478bd9Sstevel@tonic-gate 		if (mntzone != zone) {
10957c478bd9Sstevel@tonic-gate 			error = EBUSY;
10967c478bd9Sstevel@tonic-gate 			goto errout;
10977c478bd9Sstevel@tonic-gate 		}
10987c478bd9Sstevel@tonic-gate 	}
10997c478bd9Sstevel@tonic-gate 
110045916cd2Sjpk 	if (is_system_labeled()) {
110145916cd2Sjpk 		error = nfs_mount_label_policy(vfsp, &svp->sv_addr,
110245916cd2Sjpk 		    svp->sv_knconf, cr);
110345916cd2Sjpk 
110445916cd2Sjpk 		if (error > 0)
110545916cd2Sjpk 			goto errout;
110645916cd2Sjpk 
110745916cd2Sjpk 		if (error == -1) {
110845916cd2Sjpk 			/* change mount to read-only to prevent write-down */
110945916cd2Sjpk 			vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
111045916cd2Sjpk 		}
111145916cd2Sjpk 	}
111245916cd2Sjpk 
11137c478bd9Sstevel@tonic-gate 	/*
11147c478bd9Sstevel@tonic-gate 	 * Stop the mount from going any further if the zone is going away.
11157c478bd9Sstevel@tonic-gate 	 */
111645916cd2Sjpk 	if (zone_status_get(mntzone) >= ZONE_IS_SHUTTING_DOWN) {
11177c478bd9Sstevel@tonic-gate 		error = EBUSY;
11187c478bd9Sstevel@tonic-gate 		goto errout;
11197c478bd9Sstevel@tonic-gate 	}
11207c478bd9Sstevel@tonic-gate 
11217c478bd9Sstevel@tonic-gate 	/*
11227c478bd9Sstevel@tonic-gate 	 * Get root vnode.
11237c478bd9Sstevel@tonic-gate 	 */
11247c478bd9Sstevel@tonic-gate proceed:
112545916cd2Sjpk 	error = nfs4rootvp(&rtvp, vfsp, svp_head, flags, cr, mntzone);
112650a83466Sjwahlig 	if (error) {
112750a83466Sjwahlig 		/* if nfs4rootvp failed, it will free svp_head */
112850a83466Sjwahlig 		svp_head = NULL;
11297c478bd9Sstevel@tonic-gate 		goto errout;
113050a83466Sjwahlig 	}
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate 	mi = VTOMI4(rtvp);
11337c478bd9Sstevel@tonic-gate 
11347c478bd9Sstevel@tonic-gate 	/*
11357c478bd9Sstevel@tonic-gate 	 * Send client id to the server, if necessary
11367c478bd9Sstevel@tonic-gate 	 */
11377c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(&n4e);
11387c478bd9Sstevel@tonic-gate 	nfs4setclientid(mi, cr, FALSE, &n4e);
1139b9238976Sth 
11407c478bd9Sstevel@tonic-gate 	error = n4e.error;
11417c478bd9Sstevel@tonic-gate 
11427c478bd9Sstevel@tonic-gate 	if (error)
11437c478bd9Sstevel@tonic-gate 		goto errout;
11447c478bd9Sstevel@tonic-gate 
11457c478bd9Sstevel@tonic-gate 	/*
11467c478bd9Sstevel@tonic-gate 	 * Set option fields in the mount info record
11477c478bd9Sstevel@tonic-gate 	 */
11487c478bd9Sstevel@tonic-gate 
11497c478bd9Sstevel@tonic-gate 	if (svp_head->sv_next) {
11507c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_lock);
11517c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_LLOCK;
11527c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_lock);
11537c478bd9Sstevel@tonic-gate 	}
115439d3e169Sevanl 	error = nfs4_setopts(rtvp, DATAMODEL_NATIVE, args);
1155b9238976Sth 	if (error)
1156b9238976Sth 		goto errout;
1157b9238976Sth 
1158b9238976Sth 	/*
1159b9238976Sth 	 * Time to tie in the mirror mount info at last!
1160b9238976Sth 	 */
1161b9238976Sth 	if (flags & NFSMNT_EPHEMERAL)
1162d3a14591SThomas Haynes 		error = nfs4_record_ephemeral_mount(mi, mvp);
11637c478bd9Sstevel@tonic-gate 
11647c478bd9Sstevel@tonic-gate errout:
11657c478bd9Sstevel@tonic-gate 	if (error) {
11667c478bd9Sstevel@tonic-gate 		if (rtvp != NULL) {
11677c478bd9Sstevel@tonic-gate 			rp = VTOR4(rtvp);
11687c478bd9Sstevel@tonic-gate 			if (rp->r_flags & R4HASHED)
11697c478bd9Sstevel@tonic-gate 				rp4_rmhash(rp);
11707c478bd9Sstevel@tonic-gate 		}
11717c478bd9Sstevel@tonic-gate 		if (mi != NULL) {
11727c478bd9Sstevel@tonic-gate 			nfs4_async_stop(vfsp);
11737c478bd9Sstevel@tonic-gate 			nfs4_async_manager_stop(vfsp);
11747c478bd9Sstevel@tonic-gate 			nfs4_remove_mi_from_server(mi, NULL);
117550a83466Sjwahlig 			if (rtvp != NULL)
11767c478bd9Sstevel@tonic-gate 				VN_RELE(rtvp);
117745916cd2Sjpk 			if (mntzone != NULL)
117845916cd2Sjpk 				zone_rele(mntzone);
117950a83466Sjwahlig 			/* need to remove it from the zone */
118050a83466Sjwahlig 			removed = nfs4_mi_zonelist_remove(mi);
118150a83466Sjwahlig 			if (removed)
118250a83466Sjwahlig 				zone_rele(mi->mi_zone);
118350a83466Sjwahlig 			MI4_RELE(mi);
118439d3e169Sevanl 			if (!(uap->flags & MS_SYSSPACE) && args) {
118539d3e169Sevanl 				nfs4_free_args(args);
118639d3e169Sevanl 				kmem_free(args, sizeof (*args));
118739d3e169Sevanl 			}
11887c478bd9Sstevel@tonic-gate 			return (error);
11897c478bd9Sstevel@tonic-gate 		}
119050a83466Sjwahlig 		if (svp_head)
119150a83466Sjwahlig 			sv4_free(svp_head);
11927c478bd9Sstevel@tonic-gate 	}
11937c478bd9Sstevel@tonic-gate 
119439d3e169Sevanl 	if (!(uap->flags & MS_SYSSPACE) && args) {
119539d3e169Sevanl 		nfs4_free_args(args);
119639d3e169Sevanl 		kmem_free(args, sizeof (*args));
119739d3e169Sevanl 	}
11987c478bd9Sstevel@tonic-gate 	if (rtvp != NULL)
11997c478bd9Sstevel@tonic-gate 		VN_RELE(rtvp);
12007c478bd9Sstevel@tonic-gate 
120145916cd2Sjpk 	if (mntzone != NULL)
120245916cd2Sjpk 		zone_rele(mntzone);
120345916cd2Sjpk 
12047c478bd9Sstevel@tonic-gate 	return (error);
12057c478bd9Sstevel@tonic-gate }
12067c478bd9Sstevel@tonic-gate 
120739d3e169Sevanl #ifdef  DEBUG
12087c478bd9Sstevel@tonic-gate #define	VERS_MSG	"NFS4 server "
12097c478bd9Sstevel@tonic-gate #else
12107c478bd9Sstevel@tonic-gate #define	VERS_MSG	"NFS server "
12117c478bd9Sstevel@tonic-gate #endif
12127c478bd9Sstevel@tonic-gate 
121339d3e169Sevanl #define	READ_MSG        \
12147c478bd9Sstevel@tonic-gate 	VERS_MSG "%s returned 0 for read transfer size"
121539d3e169Sevanl #define	WRITE_MSG       \
12167c478bd9Sstevel@tonic-gate 	VERS_MSG "%s returned 0 for write transfer size"
121739d3e169Sevanl #define	SIZE_MSG        \
12187c478bd9Sstevel@tonic-gate 	VERS_MSG "%s returned 0 for maximum file size"
12197c478bd9Sstevel@tonic-gate 
12207c478bd9Sstevel@tonic-gate /*
12217c478bd9Sstevel@tonic-gate  * Get the symbolic link text from the server for a given filehandle
12227c478bd9Sstevel@tonic-gate  * of that symlink.
12237c478bd9Sstevel@tonic-gate  *
122439d3e169Sevanl  *      (get symlink text) PUTFH READLINK
12257c478bd9Sstevel@tonic-gate  */
12267c478bd9Sstevel@tonic-gate static int
12277c478bd9Sstevel@tonic-gate getlinktext_otw(mntinfo4_t *mi, nfs_fh4 *fh, char **linktextp, cred_t *cr,
1228b9238976Sth     int flags)
12297c478bd9Sstevel@tonic-gate {
12307c478bd9Sstevel@tonic-gate 	COMPOUND4args_clnt args;
12317c478bd9Sstevel@tonic-gate 	COMPOUND4res_clnt res;
12327c478bd9Sstevel@tonic-gate 	int doqueue;
12337c478bd9Sstevel@tonic-gate 	nfs_argop4 argop[2];
12347c478bd9Sstevel@tonic-gate 	nfs_resop4 *resop;
12357c478bd9Sstevel@tonic-gate 	READLINK4res *lr_res;
12367c478bd9Sstevel@tonic-gate 	uint_t len;
12377c478bd9Sstevel@tonic-gate 	bool_t needrecov = FALSE;
12387c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
12397c478bd9Sstevel@tonic-gate 	nfs4_sharedfh_t *sfh;
12407c478bd9Sstevel@tonic-gate 	nfs4_error_t e;
12417c478bd9Sstevel@tonic-gate 	int num_retry = nfs4_max_mount_retry;
12427c478bd9Sstevel@tonic-gate 	int recovery = !(flags & NFS4_GETFH_NEEDSOP);
12437c478bd9Sstevel@tonic-gate 
12447c478bd9Sstevel@tonic-gate 	sfh = sfh4_get(fh, mi);
12457c478bd9Sstevel@tonic-gate 	recov_state.rs_flags = 0;
12467c478bd9Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
12477c478bd9Sstevel@tonic-gate 
12487c478bd9Sstevel@tonic-gate recov_retry:
12497c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(&e);
12507c478bd9Sstevel@tonic-gate 
12517c478bd9Sstevel@tonic-gate 	args.array_len = 2;
12527c478bd9Sstevel@tonic-gate 	args.array = argop;
12537c478bd9Sstevel@tonic-gate 	args.ctag = TAG_GET_SYMLINK;
12547c478bd9Sstevel@tonic-gate 
12557c478bd9Sstevel@tonic-gate 	if (! recovery) {
12567c478bd9Sstevel@tonic-gate 		e.error = nfs4_start_op(mi, NULL, NULL, &recov_state);
12577c478bd9Sstevel@tonic-gate 		if (e.error) {
12587c478bd9Sstevel@tonic-gate 			sfh4_rele(&sfh);
12597c478bd9Sstevel@tonic-gate 			return (e.error);
12607c478bd9Sstevel@tonic-gate 		}
12617c478bd9Sstevel@tonic-gate 	}
12627c478bd9Sstevel@tonic-gate 
12637c478bd9Sstevel@tonic-gate 	/* 0. putfh symlink fh */
12647c478bd9Sstevel@tonic-gate 	argop[0].argop = OP_CPUTFH;
12657c478bd9Sstevel@tonic-gate 	argop[0].nfs_argop4_u.opcputfh.sfh = sfh;
12667c478bd9Sstevel@tonic-gate 
12677c478bd9Sstevel@tonic-gate 	/* 1. readlink */
12687c478bd9Sstevel@tonic-gate 	argop[1].argop = OP_READLINK;
12697c478bd9Sstevel@tonic-gate 
12707c478bd9Sstevel@tonic-gate 	doqueue = 1;
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate 	rfs4call(mi, &args, &res, cr, &doqueue, 0, &e);
12737c478bd9Sstevel@tonic-gate 
12747c478bd9Sstevel@tonic-gate 	needrecov = nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp);
12757c478bd9Sstevel@tonic-gate 
12767c478bd9Sstevel@tonic-gate 	if (needrecov && !recovery && num_retry-- > 0) {
12777c478bd9Sstevel@tonic-gate 
12787c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
1279b9238976Sth 		    "getlinktext_otw: initiating recovery\n"));
12807c478bd9Sstevel@tonic-gate 
12817c478bd9Sstevel@tonic-gate 		if (nfs4_start_recovery(&e, mi, NULL, NULL, NULL, NULL,
12822f172c55SRobert Thurlow 		    OP_READLINK, NULL, NULL, NULL) == FALSE) {
1283b9238976Sth 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
1284b9238976Sth 			if (!e.error)
1285b9238976Sth 				(void) xdr_free(xdr_COMPOUND4res_clnt,
1286b9238976Sth 				    (caddr_t)&res);
12877c478bd9Sstevel@tonic-gate 			goto recov_retry;
12887c478bd9Sstevel@tonic-gate 		}
12897c478bd9Sstevel@tonic-gate 	}
12907c478bd9Sstevel@tonic-gate 
12917c478bd9Sstevel@tonic-gate 	/*
12927c478bd9Sstevel@tonic-gate 	 * If non-NFS4 pcol error and/or we weren't able to recover.
12937c478bd9Sstevel@tonic-gate 	 */
12947c478bd9Sstevel@tonic-gate 	if (e.error != 0) {
12957c478bd9Sstevel@tonic-gate 		if (! recovery)
12967c478bd9Sstevel@tonic-gate 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
12977c478bd9Sstevel@tonic-gate 		sfh4_rele(&sfh);
12987c478bd9Sstevel@tonic-gate 		return (e.error);
12997c478bd9Sstevel@tonic-gate 	}
13007c478bd9Sstevel@tonic-gate 
13017c478bd9Sstevel@tonic-gate 	if (res.status) {
13027c478bd9Sstevel@tonic-gate 		e.error = geterrno4(res.status);
13037c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
13047c478bd9Sstevel@tonic-gate 		if (! recovery)
13057c478bd9Sstevel@tonic-gate 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
13067c478bd9Sstevel@tonic-gate 		sfh4_rele(&sfh);
13077c478bd9Sstevel@tonic-gate 		return (e.error);
13087c478bd9Sstevel@tonic-gate 	}
13097c478bd9Sstevel@tonic-gate 
13107c478bd9Sstevel@tonic-gate 	/* res.status == NFS4_OK */
13117c478bd9Sstevel@tonic-gate 	ASSERT(res.status == NFS4_OK);
13127c478bd9Sstevel@tonic-gate 
131339d3e169Sevanl 	resop = &res.array[1];  /* readlink res */
13147c478bd9Sstevel@tonic-gate 	lr_res = &resop->nfs_resop4_u.opreadlink;
13157c478bd9Sstevel@tonic-gate 
13167c478bd9Sstevel@tonic-gate 	/* treat symlink name as data */
13177c478bd9Sstevel@tonic-gate 	*linktextp = utf8_to_str(&lr_res->link, &len, NULL);
13187c478bd9Sstevel@tonic-gate 
13197c478bd9Sstevel@tonic-gate 	if (! recovery)
13207c478bd9Sstevel@tonic-gate 		nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
13217c478bd9Sstevel@tonic-gate 	sfh4_rele(&sfh);
13227c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
13237c478bd9Sstevel@tonic-gate 	return (0);
13247c478bd9Sstevel@tonic-gate }
13257c478bd9Sstevel@tonic-gate 
13267c478bd9Sstevel@tonic-gate /*
13277c478bd9Sstevel@tonic-gate  * Skip over consecutive slashes and "/./" in a pathname.
13287c478bd9Sstevel@tonic-gate  */
13297c478bd9Sstevel@tonic-gate void
13307c478bd9Sstevel@tonic-gate pathname_skipslashdot(struct pathname *pnp)
13317c478bd9Sstevel@tonic-gate {
13327c478bd9Sstevel@tonic-gate 	char *c1, *c2;
13337c478bd9Sstevel@tonic-gate 
13347c478bd9Sstevel@tonic-gate 	while (pnp->pn_pathlen > 0 && *pnp->pn_path == '/') {
13357c478bd9Sstevel@tonic-gate 
13367c478bd9Sstevel@tonic-gate 		c1 = pnp->pn_path + 1;
13377c478bd9Sstevel@tonic-gate 		c2 = pnp->pn_path + 2;
13387c478bd9Sstevel@tonic-gate 
13397c478bd9Sstevel@tonic-gate 		if (*c1 == '.' && (*c2 == '/' || *c2 == '\0')) {
13407c478bd9Sstevel@tonic-gate 			pnp->pn_path = pnp->pn_path + 2; /* skip "/." */
13417c478bd9Sstevel@tonic-gate 			pnp->pn_pathlen = pnp->pn_pathlen - 2;
13427c478bd9Sstevel@tonic-gate 		} else {
13437c478bd9Sstevel@tonic-gate 			pnp->pn_path++;
13447c478bd9Sstevel@tonic-gate 			pnp->pn_pathlen--;
13457c478bd9Sstevel@tonic-gate 		}
13467c478bd9Sstevel@tonic-gate 	}
13477c478bd9Sstevel@tonic-gate }
13487c478bd9Sstevel@tonic-gate 
13497c478bd9Sstevel@tonic-gate /*
13507c478bd9Sstevel@tonic-gate  * Resolve a symbolic link path. The symlink is in the nth component of
13517c478bd9Sstevel@tonic-gate  * svp->sv_path and has an nfs4 file handle "fh".
13527c478bd9Sstevel@tonic-gate  * Upon return, the sv_path will point to the new path that has the nth
13537c478bd9Sstevel@tonic-gate  * component resolved to its symlink text.
13547c478bd9Sstevel@tonic-gate  */
13557c478bd9Sstevel@tonic-gate int
13567c478bd9Sstevel@tonic-gate resolve_sympath(mntinfo4_t *mi, servinfo4_t *svp, int nth, nfs_fh4 *fh,
1357b9238976Sth     cred_t *cr, int flags)
13587c478bd9Sstevel@tonic-gate {
13597c478bd9Sstevel@tonic-gate 	char *oldpath;
13607c478bd9Sstevel@tonic-gate 	char *symlink, *newpath;
13617c478bd9Sstevel@tonic-gate 	struct pathname oldpn, newpn;
13627c478bd9Sstevel@tonic-gate 	char component[MAXNAMELEN];
13637c478bd9Sstevel@tonic-gate 	int i, addlen, error = 0;
13647c478bd9Sstevel@tonic-gate 	int oldpathlen;
13657c478bd9Sstevel@tonic-gate 
13667c478bd9Sstevel@tonic-gate 	/* Get the symbolic link text over the wire. */
13677c478bd9Sstevel@tonic-gate 	error = getlinktext_otw(mi, fh, &symlink, cr, flags);
13687c478bd9Sstevel@tonic-gate 
13697c478bd9Sstevel@tonic-gate 	if (error || symlink == NULL || strlen(symlink) == 0)
13707c478bd9Sstevel@tonic-gate 		return (error);
13717c478bd9Sstevel@tonic-gate 
13727c478bd9Sstevel@tonic-gate 	/*
13737c478bd9Sstevel@tonic-gate 	 * Compose the new pathname.
13747c478bd9Sstevel@tonic-gate 	 * Note:
13757c478bd9Sstevel@tonic-gate 	 *    - only the nth component is resolved for the pathname.
13767c478bd9Sstevel@tonic-gate 	 *    - pathname.pn_pathlen does not count the ending null byte.
13777c478bd9Sstevel@tonic-gate 	 */
13787c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
13797c478bd9Sstevel@tonic-gate 	oldpath = svp->sv_path;
13807c478bd9Sstevel@tonic-gate 	oldpathlen = svp->sv_pathlen;
13817c478bd9Sstevel@tonic-gate 	if (error = pn_get(oldpath, UIO_SYSSPACE, &oldpn)) {
13827c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
13837c478bd9Sstevel@tonic-gate 		kmem_free(symlink, strlen(symlink) + 1);
13847c478bd9Sstevel@tonic-gate 		return (error);
13857c478bd9Sstevel@tonic-gate 	}
13867c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
13877c478bd9Sstevel@tonic-gate 	pn_alloc(&newpn);
13887c478bd9Sstevel@tonic-gate 
13897c478bd9Sstevel@tonic-gate 	/*
13907c478bd9Sstevel@tonic-gate 	 * Skip over previous components from the oldpath so that the
13917c478bd9Sstevel@tonic-gate 	 * oldpn.pn_path will point to the symlink component. Skip
13927c478bd9Sstevel@tonic-gate 	 * leading slashes and "/./" (no OP_LOOKUP on ".") so that
13937c478bd9Sstevel@tonic-gate 	 * pn_getcompnent can get the component.
13947c478bd9Sstevel@tonic-gate 	 */
13957c478bd9Sstevel@tonic-gate 	for (i = 1; i < nth; i++) {
13967c478bd9Sstevel@tonic-gate 		pathname_skipslashdot(&oldpn);
13977c478bd9Sstevel@tonic-gate 		error = pn_getcomponent(&oldpn, component);
13987c478bd9Sstevel@tonic-gate 		if (error)
13997c478bd9Sstevel@tonic-gate 			goto out;
14007c478bd9Sstevel@tonic-gate 	}
14017c478bd9Sstevel@tonic-gate 
14027c478bd9Sstevel@tonic-gate 	/*
14037c478bd9Sstevel@tonic-gate 	 * Copy the old path upto the component right before the symlink
14047c478bd9Sstevel@tonic-gate 	 * if the symlink is not an absolute path.
14057c478bd9Sstevel@tonic-gate 	 */
14067c478bd9Sstevel@tonic-gate 	if (symlink[0] != '/') {
14077c478bd9Sstevel@tonic-gate 		addlen = oldpn.pn_path - oldpn.pn_buf;
14087c478bd9Sstevel@tonic-gate 		bcopy(oldpn.pn_buf, newpn.pn_path, addlen);
14097c478bd9Sstevel@tonic-gate 		newpn.pn_pathlen += addlen;
14107c478bd9Sstevel@tonic-gate 		newpn.pn_path += addlen;
14117c478bd9Sstevel@tonic-gate 		newpn.pn_buf[newpn.pn_pathlen] = '/';
14127c478bd9Sstevel@tonic-gate 		newpn.pn_pathlen++;
14137c478bd9Sstevel@tonic-gate 		newpn.pn_path++;
14147c478bd9Sstevel@tonic-gate 	}
14157c478bd9Sstevel@tonic-gate 
14167c478bd9Sstevel@tonic-gate 	/* copy the resolved symbolic link text */
14177c478bd9Sstevel@tonic-gate 	addlen = strlen(symlink);
14187c478bd9Sstevel@tonic-gate 	if (newpn.pn_pathlen + addlen >= newpn.pn_bufsize) {
14197c478bd9Sstevel@tonic-gate 		error = ENAMETOOLONG;
14207c478bd9Sstevel@tonic-gate 		goto out;
14217c478bd9Sstevel@tonic-gate 	}
14227c478bd9Sstevel@tonic-gate 	bcopy(symlink, newpn.pn_path, addlen);
14237c478bd9Sstevel@tonic-gate 	newpn.pn_pathlen += addlen;
14247c478bd9Sstevel@tonic-gate 	newpn.pn_path += addlen;
14257c478bd9Sstevel@tonic-gate 
14267c478bd9Sstevel@tonic-gate 	/*
14277c478bd9Sstevel@tonic-gate 	 * Check if there is any remaining path after the symlink component.
14287c478bd9Sstevel@tonic-gate 	 * First, skip the symlink component.
14297c478bd9Sstevel@tonic-gate 	 */
14307c478bd9Sstevel@tonic-gate 	pathname_skipslashdot(&oldpn);
14317c478bd9Sstevel@tonic-gate 	if (error = pn_getcomponent(&oldpn, component))
14327c478bd9Sstevel@tonic-gate 		goto out;
14337c478bd9Sstevel@tonic-gate 
14347c478bd9Sstevel@tonic-gate 	addlen = pn_pathleft(&oldpn); /* includes counting the slash */
14357c478bd9Sstevel@tonic-gate 
14367c478bd9Sstevel@tonic-gate 	/*
14377c478bd9Sstevel@tonic-gate 	 * Copy the remaining path to the new pathname if there is any.
14387c478bd9Sstevel@tonic-gate 	 */
14397c478bd9Sstevel@tonic-gate 	if (addlen > 0) {
14407c478bd9Sstevel@tonic-gate 		if (newpn.pn_pathlen + addlen >= newpn.pn_bufsize) {
14417c478bd9Sstevel@tonic-gate 			error = ENAMETOOLONG;
14427c478bd9Sstevel@tonic-gate 			goto out;
14437c478bd9Sstevel@tonic-gate 		}
14447c478bd9Sstevel@tonic-gate 		bcopy(oldpn.pn_path, newpn.pn_path, addlen);
14457c478bd9Sstevel@tonic-gate 		newpn.pn_pathlen += addlen;
14467c478bd9Sstevel@tonic-gate 	}
14477c478bd9Sstevel@tonic-gate 	newpn.pn_buf[newpn.pn_pathlen] = '\0';
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate 	/* get the newpath and store it in the servinfo4_t */
14507c478bd9Sstevel@tonic-gate 	newpath = kmem_alloc(newpn.pn_pathlen + 1, KM_SLEEP);
14517c478bd9Sstevel@tonic-gate 	bcopy(newpn.pn_buf, newpath, newpn.pn_pathlen);
14527c478bd9Sstevel@tonic-gate 	newpath[newpn.pn_pathlen] = '\0';
14537c478bd9Sstevel@tonic-gate 
14547c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
14557c478bd9Sstevel@tonic-gate 	svp->sv_path = newpath;
14567c478bd9Sstevel@tonic-gate 	svp->sv_pathlen = strlen(newpath) + 1;
14577c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
14587c478bd9Sstevel@tonic-gate 
14597c478bd9Sstevel@tonic-gate 	kmem_free(oldpath, oldpathlen);
14607c478bd9Sstevel@tonic-gate out:
14617c478bd9Sstevel@tonic-gate 	kmem_free(symlink, strlen(symlink) + 1);
14627c478bd9Sstevel@tonic-gate 	pn_free(&newpn);
14637c478bd9Sstevel@tonic-gate 	pn_free(&oldpn);
14647c478bd9Sstevel@tonic-gate 
14657c478bd9Sstevel@tonic-gate 	return (error);
14667c478bd9Sstevel@tonic-gate }
14677c478bd9Sstevel@tonic-gate 
14682f172c55SRobert Thurlow /*
14692f172c55SRobert Thurlow  * This routine updates servinfo4 structure with the new referred server
14702f172c55SRobert Thurlow  * info.
14712f172c55SRobert Thurlow  * nfsfsloc has the location related information
14722f172c55SRobert Thurlow  * fsp has the hostname and pathname info.
14732f172c55SRobert Thurlow  * new path = pathname from referral + part of orig pathname(based on nth).
14742f172c55SRobert Thurlow  */
14752f172c55SRobert Thurlow static void
14762f172c55SRobert Thurlow update_servinfo4(servinfo4_t *svp, fs_location4 *fsp,
14772f172c55SRobert Thurlow     struct nfs_fsl_info *nfsfsloc, char *orig_path, int nth)
14782f172c55SRobert Thurlow {
14792f172c55SRobert Thurlow 	struct knetconfig *knconf, *svknconf;
14802f172c55SRobert Thurlow 	struct netbuf *saddr;
14812f172c55SRobert Thurlow 	sec_data_t	*secdata;
14822f172c55SRobert Thurlow 	utf8string *host;
14832f172c55SRobert Thurlow 	int i = 0, num_slashes = 0;
14842f172c55SRobert Thurlow 	char *p, *spath, *op, *new_path;
14852f172c55SRobert Thurlow 
14862f172c55SRobert Thurlow 	/* Update knconf */
14872f172c55SRobert Thurlow 	knconf = svp->sv_knconf;
14882f172c55SRobert Thurlow 	free_knconf_contents(knconf);
14892f172c55SRobert Thurlow 	bzero(knconf, sizeof (struct knetconfig));
14902f172c55SRobert Thurlow 	svknconf = nfsfsloc->knconf;
14912f172c55SRobert Thurlow 	knconf->knc_semantics = svknconf->knc_semantics;
14922f172c55SRobert Thurlow 	knconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
14932f172c55SRobert Thurlow 	knconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
14942f172c55SRobert Thurlow 	knconf->knc_rdev = svknconf->knc_rdev;
14952f172c55SRobert Thurlow 	bcopy(svknconf->knc_protofmly, knconf->knc_protofmly, KNC_STRSIZE);
14962f172c55SRobert Thurlow 	bcopy(svknconf->knc_proto, knconf->knc_proto, KNC_STRSIZE);
14972f172c55SRobert Thurlow 
14982f172c55SRobert Thurlow 	/* Update server address */
14992f172c55SRobert Thurlow 	saddr = &svp->sv_addr;
15002f172c55SRobert Thurlow 	if (saddr->buf != NULL)
15012f172c55SRobert Thurlow 		kmem_free(saddr->buf, saddr->maxlen);
15022f172c55SRobert Thurlow 	saddr->buf  = kmem_alloc(nfsfsloc->addr->maxlen, KM_SLEEP);
15032f172c55SRobert Thurlow 	saddr->len = nfsfsloc->addr->len;
15042f172c55SRobert Thurlow 	saddr->maxlen = nfsfsloc->addr->maxlen;
15052f172c55SRobert Thurlow 	bcopy(nfsfsloc->addr->buf, saddr->buf, nfsfsloc->addr->len);
15062f172c55SRobert Thurlow 
15072f172c55SRobert Thurlow 	/* Update server name */
15082f172c55SRobert Thurlow 	host = fsp->server_val;
15092f172c55SRobert Thurlow 	kmem_free(svp->sv_hostname, svp->sv_hostnamelen);
15102f172c55SRobert Thurlow 	svp->sv_hostname = kmem_zalloc(host->utf8string_len + 1, KM_SLEEP);
15112f172c55SRobert Thurlow 	bcopy(host->utf8string_val, svp->sv_hostname, host->utf8string_len);
15122f172c55SRobert Thurlow 	svp->sv_hostname[host->utf8string_len] = '\0';
15132f172c55SRobert Thurlow 	svp->sv_hostnamelen = host->utf8string_len + 1;
15142f172c55SRobert Thurlow 
15152f172c55SRobert Thurlow 	/*
15162f172c55SRobert Thurlow 	 * Update server path.
15172f172c55SRobert Thurlow 	 * We need to setup proper path here.
15182f172c55SRobert Thurlow 	 * For ex., If we got a path name serv1:/rp/aaa/bbb
15192f172c55SRobert Thurlow 	 * where aaa is a referral and points to serv2:/rpool/aa
15202f172c55SRobert Thurlow 	 * we need to set the path to serv2:/rpool/aa/bbb
15212f172c55SRobert Thurlow 	 * The first part of this below code generates /rpool/aa
15222f172c55SRobert Thurlow 	 * and the second part appends /bbb to the server path.
15232f172c55SRobert Thurlow 	 */
15242f172c55SRobert Thurlow 	spath = p = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
15252f172c55SRobert Thurlow 	*p++ = '/';
15262f172c55SRobert Thurlow 	for (i = 0; i < fsp->rootpath.pathname4_len; i++) {
15272f172c55SRobert Thurlow 		component4 *comp;
15282f172c55SRobert Thurlow 
15292f172c55SRobert Thurlow 		comp = &fsp->rootpath.pathname4_val[i];
15302f172c55SRobert Thurlow 		/* If no space, null the string and bail */
15312f172c55SRobert Thurlow 		if ((p - spath) + comp->utf8string_len + 1 > MAXPATHLEN) {
15322f172c55SRobert Thurlow 			p = spath + MAXPATHLEN - 1;
15332f172c55SRobert Thurlow 			spath[0] = '\0';
15342f172c55SRobert Thurlow 			break;
15352f172c55SRobert Thurlow 		}
15362f172c55SRobert Thurlow 		bcopy(comp->utf8string_val, p, comp->utf8string_len);
15372f172c55SRobert Thurlow 		p += comp->utf8string_len;
15382f172c55SRobert Thurlow 		*p++ = '/';
15392f172c55SRobert Thurlow 	}
15402f172c55SRobert Thurlow 	if (fsp->rootpath.pathname4_len != 0)
15412f172c55SRobert Thurlow 		*(p - 1) = '\0';
15422f172c55SRobert Thurlow 	else
15432f172c55SRobert Thurlow 		*p = '\0';
15442f172c55SRobert Thurlow 	p = spath;
15452f172c55SRobert Thurlow 
15462f172c55SRobert Thurlow 	new_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
15472f172c55SRobert Thurlow 	(void) strlcpy(new_path, p, MAXPATHLEN);
15482f172c55SRobert Thurlow 	kmem_free(p, MAXPATHLEN);
15492f172c55SRobert Thurlow 	i = strlen(new_path);
15502f172c55SRobert Thurlow 
15512f172c55SRobert Thurlow 	for (op = orig_path; *op; op++) {
15522f172c55SRobert Thurlow 		if (*op == '/')
15532f172c55SRobert Thurlow 			num_slashes++;
15542f172c55SRobert Thurlow 		if (num_slashes == nth + 2) {
15552f172c55SRobert Thurlow 			while (*op != '\0') {
15562f172c55SRobert Thurlow 				new_path[i] = *op;
15572f172c55SRobert Thurlow 				i++;
15582f172c55SRobert Thurlow 				op++;
15592f172c55SRobert Thurlow 			}
15602f172c55SRobert Thurlow 			break;
15612f172c55SRobert Thurlow 		}
15622f172c55SRobert Thurlow 	}
15632f172c55SRobert Thurlow 	new_path[i] = '\0';
15642f172c55SRobert Thurlow 
15652f172c55SRobert Thurlow 	kmem_free(svp->sv_path, svp->sv_pathlen);
15662f172c55SRobert Thurlow 	svp->sv_pathlen = strlen(new_path) + 1;
15672f172c55SRobert Thurlow 	svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP);
15682f172c55SRobert Thurlow 	bcopy(new_path, svp->sv_path, svp->sv_pathlen);
15692f172c55SRobert Thurlow 	kmem_free(new_path, MAXPATHLEN);
15702f172c55SRobert Thurlow 
15712f172c55SRobert Thurlow 	/*
15722f172c55SRobert Thurlow 	 * All the security data is specific to old server.
15732f172c55SRobert Thurlow 	 * Clean it up except secdata which deals with mount options.
15742f172c55SRobert Thurlow 	 * We need to inherit that data. Copy secdata into our new servinfo4.
15752f172c55SRobert Thurlow 	 */
15762f172c55SRobert Thurlow 	if (svp->sv_dhsec) {
15772f172c55SRobert Thurlow 		sec_clnt_freeinfo(svp->sv_dhsec);
15782f172c55SRobert Thurlow 		svp->sv_dhsec = NULL;
15792f172c55SRobert Thurlow 	}
15802f172c55SRobert Thurlow 	if (svp->sv_save_secinfo &&
15812f172c55SRobert Thurlow 	    svp->sv_save_secinfo != svp->sv_secinfo) {
15822f172c55SRobert Thurlow 		secinfo_free(svp->sv_save_secinfo);
15832f172c55SRobert Thurlow 		svp->sv_save_secinfo = NULL;
15842f172c55SRobert Thurlow 	}
15852f172c55SRobert Thurlow 	if (svp->sv_secinfo) {
15862f172c55SRobert Thurlow 		secinfo_free(svp->sv_secinfo);
15872f172c55SRobert Thurlow 		svp->sv_secinfo = NULL;
15882f172c55SRobert Thurlow 	}
15892f172c55SRobert Thurlow 	svp->sv_currsec = NULL;
15902f172c55SRobert Thurlow 
15912f172c55SRobert Thurlow 	secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP);
15922f172c55SRobert Thurlow 	*secdata = *svp->sv_secdata;
15932f172c55SRobert Thurlow 	secdata->data = NULL;
15942f172c55SRobert Thurlow 	if (svp->sv_secdata) {
15952f172c55SRobert Thurlow 		sec_clnt_freeinfo(svp->sv_secdata);
15962f172c55SRobert Thurlow 		svp->sv_secdata = NULL;
15972f172c55SRobert Thurlow 	}
15982f172c55SRobert Thurlow 	svp->sv_secdata = secdata;
15992f172c55SRobert Thurlow }
16002f172c55SRobert Thurlow 
16012f172c55SRobert Thurlow /*
16022f172c55SRobert Thurlow  * Resolve a referral. The referral is in the n+1th component of
16032f172c55SRobert Thurlow  * svp->sv_path and has a parent nfs4 file handle "fh".
16042f172c55SRobert Thurlow  * Upon return, the sv_path will point to the new path that has referral
16052f172c55SRobert Thurlow  * component resolved to its referred path and part of original path.
16062f172c55SRobert Thurlow  * Hostname and other address information is also updated.
16072f172c55SRobert Thurlow  */
16082f172c55SRobert Thurlow int
16092f172c55SRobert Thurlow resolve_referral(mntinfo4_t *mi, servinfo4_t *svp, cred_t *cr, int nth,
16102f172c55SRobert Thurlow     nfs_fh4 *fh)
16112f172c55SRobert Thurlow {
16122f172c55SRobert Thurlow 	nfs4_sharedfh_t	*sfh;
16132f172c55SRobert Thurlow 	struct nfs_fsl_info nfsfsloc;
16142f172c55SRobert Thurlow 	nfs4_ga_res_t garp;
16152f172c55SRobert Thurlow 	COMPOUND4res_clnt callres;
16162f172c55SRobert Thurlow 	fs_location4	*fsp;
16172f172c55SRobert Thurlow 	char *nm, *orig_path;
16182f172c55SRobert Thurlow 	int orig_pathlen = 0, ret = -1, index;
16192f172c55SRobert Thurlow 
16202f172c55SRobert Thurlow 	if (svp->sv_pathlen <= 0)
16212f172c55SRobert Thurlow 		return (ret);
16222f172c55SRobert Thurlow 
16232f172c55SRobert Thurlow 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
16242f172c55SRobert Thurlow 	orig_pathlen = svp->sv_pathlen;
16252f172c55SRobert Thurlow 	orig_path = kmem_alloc(orig_pathlen, KM_SLEEP);
16262f172c55SRobert Thurlow 	bcopy(svp->sv_path, orig_path, orig_pathlen);
16272f172c55SRobert Thurlow 	nm = extract_referral_point(svp->sv_path, nth);
16282f172c55SRobert Thurlow 	setup_newsvpath(svp, nth);
16292f172c55SRobert Thurlow 	nfs_rw_exit(&svp->sv_lock);
16302f172c55SRobert Thurlow 
16312f172c55SRobert Thurlow 	sfh = sfh4_get(fh, mi);
16322f172c55SRobert Thurlow 	index = nfs4_process_referral(mi, sfh, nm, cr,
16332f172c55SRobert Thurlow 	    &garp, &callres, &nfsfsloc);
16342f172c55SRobert Thurlow 	sfh4_rele(&sfh);
16352f172c55SRobert Thurlow 	kmem_free(nm, MAXPATHLEN);
16362f172c55SRobert Thurlow 	if (index < 0) {
16372f172c55SRobert Thurlow 		kmem_free(orig_path, orig_pathlen);
16382f172c55SRobert Thurlow 		return (index);
16392f172c55SRobert Thurlow 	}
16402f172c55SRobert Thurlow 
16412f172c55SRobert Thurlow 	fsp =  &garp.n4g_ext_res->n4g_fslocations.locations_val[index];
16422f172c55SRobert Thurlow 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
16432f172c55SRobert Thurlow 	update_servinfo4(svp, fsp, &nfsfsloc, orig_path, nth);
16442f172c55SRobert Thurlow 	nfs_rw_exit(&svp->sv_lock);
16452f172c55SRobert Thurlow 
16462f172c55SRobert Thurlow 	mutex_enter(&mi->mi_lock);
16472f172c55SRobert Thurlow 	mi->mi_vfs_referral_loop_cnt++;
16482f172c55SRobert Thurlow 	mutex_exit(&mi->mi_lock);
16492f172c55SRobert Thurlow 
16502f172c55SRobert Thurlow 	ret = 0;
16512f172c55SRobert Thurlow bad:
16522f172c55SRobert Thurlow 	/* Free up XDR memory allocated in nfs4_process_referral() */
16532f172c55SRobert Thurlow 	xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc);
16542f172c55SRobert Thurlow 	xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
16552f172c55SRobert Thurlow 	kmem_free(orig_path, orig_pathlen);
16562f172c55SRobert Thurlow 
16572f172c55SRobert Thurlow 	return (ret);
16582f172c55SRobert Thurlow }
16592f172c55SRobert Thurlow 
16607c478bd9Sstevel@tonic-gate /*
16617c478bd9Sstevel@tonic-gate  * Get the root filehandle for the given filesystem and server, and update
16627c478bd9Sstevel@tonic-gate  * svp.
16637c478bd9Sstevel@tonic-gate  *
16647c478bd9Sstevel@tonic-gate  * If NFS4_GETFH_NEEDSOP is set, then use nfs4_start_fop and nfs4_end_fop
16657c478bd9Sstevel@tonic-gate  * to coordinate with recovery.  Otherwise, the caller is assumed to be
16667c478bd9Sstevel@tonic-gate  * the recovery thread or have already done a start_fop.
16677c478bd9Sstevel@tonic-gate  *
16687c478bd9Sstevel@tonic-gate  * Errors are returned by the nfs4_error_t parameter.
16697c478bd9Sstevel@tonic-gate  */
16707c478bd9Sstevel@tonic-gate static void
16717c478bd9Sstevel@tonic-gate nfs4getfh_otw(struct mntinfo4 *mi, servinfo4_t *svp, vtype_t *vtp,
1672b9238976Sth     int flags, cred_t *cr, nfs4_error_t *ep)
16737c478bd9Sstevel@tonic-gate {
16747c478bd9Sstevel@tonic-gate 	COMPOUND4args_clnt args;
16757c478bd9Sstevel@tonic-gate 	COMPOUND4res_clnt res;
16767c478bd9Sstevel@tonic-gate 	int doqueue = 1;
16777c478bd9Sstevel@tonic-gate 	nfs_argop4 *argop;
16787c478bd9Sstevel@tonic-gate 	nfs_resop4 *resop;
16797c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t *garp;
16807c478bd9Sstevel@tonic-gate 	int num_argops;
16817c478bd9Sstevel@tonic-gate 	lookup4_param_t lookuparg;
16827c478bd9Sstevel@tonic-gate 	nfs_fh4 *tmpfhp;
16837c478bd9Sstevel@tonic-gate 	nfs_fh4 *resfhp;
16847c478bd9Sstevel@tonic-gate 	bool_t needrecov = FALSE;
16857c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
16867c478bd9Sstevel@tonic-gate 	int llndx;
16877c478bd9Sstevel@tonic-gate 	int nthcomp;
16887c478bd9Sstevel@tonic-gate 	int recovery = !(flags & NFS4_GETFH_NEEDSOP);
16897c478bd9Sstevel@tonic-gate 
16907c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
16917c478bd9Sstevel@tonic-gate 	ASSERT(svp->sv_path != NULL);
16927c478bd9Sstevel@tonic-gate 	if (svp->sv_path[0] == '\0') {
16937c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
16947c478bd9Sstevel@tonic-gate 		nfs4_error_init(ep, EINVAL);
16957c478bd9Sstevel@tonic-gate 		return;
16967c478bd9Sstevel@tonic-gate 	}
16977c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 	recov_state.rs_flags = 0;
17007c478bd9Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
17012f172c55SRobert Thurlow 
17027c478bd9Sstevel@tonic-gate recov_retry:
17032f172c55SRobert Thurlow 	if (mi->mi_vfs_referral_loop_cnt >= NFS4_REFERRAL_LOOP_MAX) {
17042f172c55SRobert Thurlow 		DTRACE_PROBE3(nfs4clnt__debug__referral__loop, mntinfo4 *,
17052f172c55SRobert Thurlow 		    mi, servinfo4_t *, svp, char *, "nfs4getfh_otw");
17062f172c55SRobert Thurlow 		nfs4_error_init(ep, EINVAL);
17072f172c55SRobert Thurlow 		return;
17082f172c55SRobert Thurlow 	}
17097c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(ep);
17107c478bd9Sstevel@tonic-gate 
17117c478bd9Sstevel@tonic-gate 	if (!recovery) {
17127c478bd9Sstevel@tonic-gate 		ep->error = nfs4_start_fop(mi, NULL, NULL, OH_MOUNT,
1713b9238976Sth 		    &recov_state, NULL);
17147c478bd9Sstevel@tonic-gate 
17157c478bd9Sstevel@tonic-gate 		/*
17167c478bd9Sstevel@tonic-gate 		 * If recovery has been started and this request as
17177c478bd9Sstevel@tonic-gate 		 * initiated by a mount, then we must wait for recovery
17187c478bd9Sstevel@tonic-gate 		 * to finish before proceeding, otherwise, the error
17197c478bd9Sstevel@tonic-gate 		 * cleanup would remove data structures needed by the
17207c478bd9Sstevel@tonic-gate 		 * recovery thread.
17217c478bd9Sstevel@tonic-gate 		 */
17227c478bd9Sstevel@tonic-gate 		if (ep->error) {
17237c478bd9Sstevel@tonic-gate 			mutex_enter(&mi->mi_lock);
17247c478bd9Sstevel@tonic-gate 			if (mi->mi_flags & MI4_MOUNTING) {
17257c478bd9Sstevel@tonic-gate 				mi->mi_flags |= MI4_RECOV_FAIL;
17267c478bd9Sstevel@tonic-gate 				mi->mi_error = EIO;
17277c478bd9Sstevel@tonic-gate 
17287c478bd9Sstevel@tonic-gate 				NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
17297c478bd9Sstevel@tonic-gate 				    "nfs4getfh_otw: waiting 4 recovery\n"));
17307c478bd9Sstevel@tonic-gate 
17317c478bd9Sstevel@tonic-gate 				while (mi->mi_flags & MI4_RECOV_ACTIV)
17327c478bd9Sstevel@tonic-gate 					cv_wait(&mi->mi_failover_cv,
17337c478bd9Sstevel@tonic-gate 					    &mi->mi_lock);
17347c478bd9Sstevel@tonic-gate 			}
17357c478bd9Sstevel@tonic-gate 			mutex_exit(&mi->mi_lock);
17367c478bd9Sstevel@tonic-gate 			return;
17377c478bd9Sstevel@tonic-gate 		}
17387c478bd9Sstevel@tonic-gate 
17397c478bd9Sstevel@tonic-gate 		/*
17407c478bd9Sstevel@tonic-gate 		 * If the client does not specify a specific flavor to use
17417c478bd9Sstevel@tonic-gate 		 * and has not gotten a secinfo list from the server yet,
17427c478bd9Sstevel@tonic-gate 		 * retrieve the secinfo list from the server and use a
17437c478bd9Sstevel@tonic-gate 		 * flavor from the list to mount.
17447c478bd9Sstevel@tonic-gate 		 *
17457c478bd9Sstevel@tonic-gate 		 * If fail to get the secinfo list from the server, then
17467c478bd9Sstevel@tonic-gate 		 * try the default flavor.
17477c478bd9Sstevel@tonic-gate 		 */
17487c478bd9Sstevel@tonic-gate 		if ((svp->sv_flags & SV4_TRYSECDEFAULT) &&
17497c478bd9Sstevel@tonic-gate 		    svp->sv_secinfo == NULL) {
17507c478bd9Sstevel@tonic-gate 			(void) nfs4_secinfo_path(mi, cr, FALSE);
17517c478bd9Sstevel@tonic-gate 		}
17527c478bd9Sstevel@tonic-gate 	}
17537c478bd9Sstevel@tonic-gate 
17547c478bd9Sstevel@tonic-gate 	if (recovery)
17557c478bd9Sstevel@tonic-gate 		args.ctag = TAG_REMAP_MOUNT;
17567c478bd9Sstevel@tonic-gate 	else
17577c478bd9Sstevel@tonic-gate 		args.ctag = TAG_MOUNT;
17587c478bd9Sstevel@tonic-gate 
17597c478bd9Sstevel@tonic-gate 	lookuparg.l4_getattrs = LKP4_ALL_ATTRIBUTES;
17607c478bd9Sstevel@tonic-gate 	lookuparg.argsp = &args;
17617c478bd9Sstevel@tonic-gate 	lookuparg.resp = &res;
17627c478bd9Sstevel@tonic-gate 	lookuparg.header_len = 2;	/* Putrootfh, getfh */
17637c478bd9Sstevel@tonic-gate 	lookuparg.trailer_len = 0;
17647c478bd9Sstevel@tonic-gate 	lookuparg.ga_bits = FATTR4_FSINFO_MASK;
17657c478bd9Sstevel@tonic-gate 	lookuparg.mi = mi;
17667c478bd9Sstevel@tonic-gate 
17677c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
17687c478bd9Sstevel@tonic-gate 	ASSERT(svp->sv_path != NULL);
17697c478bd9Sstevel@tonic-gate 	llndx = nfs4lookup_setup(svp->sv_path, &lookuparg, 0);
17707c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
17717c478bd9Sstevel@tonic-gate 
17727c478bd9Sstevel@tonic-gate 	argop = args.array;
17737c478bd9Sstevel@tonic-gate 	num_argops = args.array_len;
17747c478bd9Sstevel@tonic-gate 
17757c478bd9Sstevel@tonic-gate 	/* choose public or root filehandle */
17767c478bd9Sstevel@tonic-gate 	if (flags & NFS4_GETFH_PUBLIC)
17777c478bd9Sstevel@tonic-gate 		argop[0].argop = OP_PUTPUBFH;
17787c478bd9Sstevel@tonic-gate 	else
17797c478bd9Sstevel@tonic-gate 		argop[0].argop = OP_PUTROOTFH;
17807c478bd9Sstevel@tonic-gate 
17817c478bd9Sstevel@tonic-gate 	/* get fh */
17827c478bd9Sstevel@tonic-gate 	argop[1].argop = OP_GETFH;
17837c478bd9Sstevel@tonic-gate 
17847c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_call_debug, (CE_NOTE,
17857c478bd9Sstevel@tonic-gate 	    "nfs4getfh_otw: %s call, mi 0x%p",
17867c478bd9Sstevel@tonic-gate 	    needrecov ? "recov" : "first", (void *)mi));
17877c478bd9Sstevel@tonic-gate 
17887c478bd9Sstevel@tonic-gate 	rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, ep);
17897c478bd9Sstevel@tonic-gate 
17907c478bd9Sstevel@tonic-gate 	needrecov = nfs4_needs_recovery(ep, FALSE, mi->mi_vfsp);
17917c478bd9Sstevel@tonic-gate 
17927c478bd9Sstevel@tonic-gate 	if (needrecov) {
17937c478bd9Sstevel@tonic-gate 		bool_t abort;
17947c478bd9Sstevel@tonic-gate 
17957c478bd9Sstevel@tonic-gate 		if (recovery) {
17967c478bd9Sstevel@tonic-gate 			nfs4args_lookup_free(argop, num_argops);
17977c478bd9Sstevel@tonic-gate 			kmem_free(argop,
1798b9238976Sth 			    lookuparg.arglen * sizeof (nfs_argop4));
17997c478bd9Sstevel@tonic-gate 			if (!ep->error)
18007c478bd9Sstevel@tonic-gate 				(void) xdr_free(xdr_COMPOUND4res_clnt,
1801b9238976Sth 				    (caddr_t)&res);
18027c478bd9Sstevel@tonic-gate 			return;
18037c478bd9Sstevel@tonic-gate 		}
18047c478bd9Sstevel@tonic-gate 
18057c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_recov_debug,
18067c478bd9Sstevel@tonic-gate 		    (CE_NOTE, "nfs4getfh_otw: initiating recovery\n"));
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate 		abort = nfs4_start_recovery(ep, mi, NULL,
18092f172c55SRobert Thurlow 		    NULL, NULL, NULL, OP_GETFH, NULL, NULL, NULL);
18107c478bd9Sstevel@tonic-gate 		if (!ep->error) {
18117c478bd9Sstevel@tonic-gate 			ep->error = geterrno4(res.status);
18127c478bd9Sstevel@tonic-gate 			(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
18137c478bd9Sstevel@tonic-gate 		}
18147c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
18157c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
18167c478bd9Sstevel@tonic-gate 		nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state, needrecov);
18177c478bd9Sstevel@tonic-gate 		/* have another go? */
18187c478bd9Sstevel@tonic-gate 		if (abort == FALSE)
18197c478bd9Sstevel@tonic-gate 			goto recov_retry;
18207c478bd9Sstevel@tonic-gate 		return;
18217c478bd9Sstevel@tonic-gate 	}
18227c478bd9Sstevel@tonic-gate 
18237c478bd9Sstevel@tonic-gate 	/*
18247c478bd9Sstevel@tonic-gate 	 * No recovery, but check if error is set.
18257c478bd9Sstevel@tonic-gate 	 */
18267c478bd9Sstevel@tonic-gate 	if (ep->error)  {
18277c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
18287c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
18297c478bd9Sstevel@tonic-gate 		if (!recovery)
18307c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
1831b9238976Sth 			    needrecov);
18327c478bd9Sstevel@tonic-gate 		return;
18337c478bd9Sstevel@tonic-gate 	}
18347c478bd9Sstevel@tonic-gate 
18357c478bd9Sstevel@tonic-gate is_link_err:
18367c478bd9Sstevel@tonic-gate 
18377c478bd9Sstevel@tonic-gate 	/* for non-recovery errors */
18382f172c55SRobert Thurlow 	if (res.status && res.status != NFS4ERR_SYMLINK &&
18392f172c55SRobert Thurlow 	    res.status != NFS4ERR_MOVED) {
18407c478bd9Sstevel@tonic-gate 		if (!recovery) {
18417c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
1842b9238976Sth 			    needrecov);
18437c478bd9Sstevel@tonic-gate 		}
18447c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
18457c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
18467c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
18477c478bd9Sstevel@tonic-gate 		return;
18487c478bd9Sstevel@tonic-gate 	}
18497c478bd9Sstevel@tonic-gate 
18507c478bd9Sstevel@tonic-gate 	/*
18517c478bd9Sstevel@tonic-gate 	 * If any intermediate component in the path is a symbolic link,
18527c478bd9Sstevel@tonic-gate 	 * resolve the symlink, then try mount again using the new path.
18537c478bd9Sstevel@tonic-gate 	 */
18542f172c55SRobert Thurlow 	if (res.status == NFS4ERR_SYMLINK || res.status == NFS4ERR_MOVED) {
18557c478bd9Sstevel@tonic-gate 		int where;
18567c478bd9Sstevel@tonic-gate 
18572f172c55SRobert Thurlow 		/*
18582f172c55SRobert Thurlow 		 * Need to call nfs4_end_op before resolve_sympath to avoid
18592f172c55SRobert Thurlow 		 * potential nfs4_start_op deadlock.
18602f172c55SRobert Thurlow 		 */
18612f172c55SRobert Thurlow 		if (!recovery)
18622f172c55SRobert Thurlow 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
18632f172c55SRobert Thurlow 			    needrecov);
18642f172c55SRobert Thurlow 
18657c478bd9Sstevel@tonic-gate 		/*
18667c478bd9Sstevel@tonic-gate 		 * This must be from OP_LOOKUP failure. The (cfh) for this
18677c478bd9Sstevel@tonic-gate 		 * OP_LOOKUP is a symlink node. Found out where the
18687c478bd9Sstevel@tonic-gate 		 * OP_GETFH is for the (cfh) that is a symlink node.
18697c478bd9Sstevel@tonic-gate 		 *
18707c478bd9Sstevel@tonic-gate 		 * Example:
18717c478bd9Sstevel@tonic-gate 		 * (mount) PUTROOTFH, GETFH, LOOKUP comp1, GETFH, GETATTR,
18727c478bd9Sstevel@tonic-gate 		 * LOOKUP comp2, GETFH, GETATTR, LOOKUP comp3, GETFH, GETATTR
18737c478bd9Sstevel@tonic-gate 		 *
18747c478bd9Sstevel@tonic-gate 		 * LOOKUP comp3 fails with SYMLINK because comp2 is a symlink.
18757c478bd9Sstevel@tonic-gate 		 * In this case, where = 7, nthcomp = 2.
18767c478bd9Sstevel@tonic-gate 		 */
18777c478bd9Sstevel@tonic-gate 		where = res.array_len - 2;
18787c478bd9Sstevel@tonic-gate 		ASSERT(where > 0);
18797c478bd9Sstevel@tonic-gate 
18802f172c55SRobert Thurlow 		if (res.status == NFS4ERR_SYMLINK) {
18817c478bd9Sstevel@tonic-gate 
18822f172c55SRobert Thurlow 			resop = &res.array[where - 1];
18832f172c55SRobert Thurlow 			ASSERT(resop->resop == OP_GETFH);
18842f172c55SRobert Thurlow 			tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
18852f172c55SRobert Thurlow 			nthcomp = res.array_len/3 - 1;
18862f172c55SRobert Thurlow 			ep->error = resolve_sympath(mi, svp, nthcomp,
18872f172c55SRobert Thurlow 			    tmpfhp, cr, flags);
18887c478bd9Sstevel@tonic-gate 
18892f172c55SRobert Thurlow 		} else if (res.status == NFS4ERR_MOVED) {
18902f172c55SRobert Thurlow 
18912f172c55SRobert Thurlow 			resop = &res.array[where - 2];
18922f172c55SRobert Thurlow 			ASSERT(resop->resop == OP_GETFH);
18932f172c55SRobert Thurlow 			tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
18942f172c55SRobert Thurlow 			nthcomp = res.array_len/3 - 1;
18952f172c55SRobert Thurlow 			ep->error = resolve_referral(mi, svp, cr, nthcomp,
18962f172c55SRobert Thurlow 			    tmpfhp);
18972f172c55SRobert Thurlow 		}
18987c478bd9Sstevel@tonic-gate 
18997c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
19007c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
19017c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
19027c478bd9Sstevel@tonic-gate 
19037c478bd9Sstevel@tonic-gate 		if (ep->error)
19047c478bd9Sstevel@tonic-gate 			return;
19057c478bd9Sstevel@tonic-gate 
19067c478bd9Sstevel@tonic-gate 		goto recov_retry;
19077c478bd9Sstevel@tonic-gate 	}
19087c478bd9Sstevel@tonic-gate 
19097c478bd9Sstevel@tonic-gate 	/* getfh */
19107c478bd9Sstevel@tonic-gate 	resop = &res.array[res.array_len - 2];
19117c478bd9Sstevel@tonic-gate 	ASSERT(resop->resop == OP_GETFH);
19127c478bd9Sstevel@tonic-gate 	resfhp = &resop->nfs_resop4_u.opgetfh.object;
19137c478bd9Sstevel@tonic-gate 
19147c478bd9Sstevel@tonic-gate 	/* getattr fsinfo res */
19157c478bd9Sstevel@tonic-gate 	resop++;
19167c478bd9Sstevel@tonic-gate 	garp = &resop->nfs_resop4_u.opgetattr.ga_res;
19177c478bd9Sstevel@tonic-gate 
19187c478bd9Sstevel@tonic-gate 	*vtp = garp->n4g_va.va_type;
19197c478bd9Sstevel@tonic-gate 
19207c478bd9Sstevel@tonic-gate 	mi->mi_fh_expire_type = garp->n4g_ext_res->n4g_fet;
19217c478bd9Sstevel@tonic-gate 
19227c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
19237c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_pc4.pc4_link_support)
19247c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_LINK;
19257c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_pc4.pc4_symlink_support)
19267c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_SYMLINK;
19277c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_suppattrs & FATTR4_ACL_MASK)
19287c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_ACL;
19297c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
19307c478bd9Sstevel@tonic-gate 
19317c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_maxread == 0)
19327c478bd9Sstevel@tonic-gate 		mi->mi_tsize =
1933b9238976Sth 		    MIN(MAXBSIZE, mi->mi_tsize);
19347c478bd9Sstevel@tonic-gate 	else
19357c478bd9Sstevel@tonic-gate 		mi->mi_tsize =
1936b9238976Sth 		    MIN(garp->n4g_ext_res->n4g_maxread,
1937b9238976Sth 		    mi->mi_tsize);
19387c478bd9Sstevel@tonic-gate 
19397c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_maxwrite == 0)
19407c478bd9Sstevel@tonic-gate 		mi->mi_stsize =
1941b9238976Sth 		    MIN(MAXBSIZE, mi->mi_stsize);
19427c478bd9Sstevel@tonic-gate 	else
19437c478bd9Sstevel@tonic-gate 		mi->mi_stsize =
1944b9238976Sth 		    MIN(garp->n4g_ext_res->n4g_maxwrite,
1945b9238976Sth 		    mi->mi_stsize);
19467c478bd9Sstevel@tonic-gate 
19477c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_maxfilesize != 0)
19487c478bd9Sstevel@tonic-gate 		mi->mi_maxfilesize =
1949b9238976Sth 		    MIN(garp->n4g_ext_res->n4g_maxfilesize,
1950b9238976Sth 		    mi->mi_maxfilesize);
19517c478bd9Sstevel@tonic-gate 
19527c478bd9Sstevel@tonic-gate 	/*
19537c478bd9Sstevel@tonic-gate 	 * If the final component is a a symbolic link, resolve the symlink,
19547c478bd9Sstevel@tonic-gate 	 * then try mount again using the new path.
19557c478bd9Sstevel@tonic-gate 	 *
19567c478bd9Sstevel@tonic-gate 	 * Assume no symbolic link for root filesysm "/".
19577c478bd9Sstevel@tonic-gate 	 */
19587c478bd9Sstevel@tonic-gate 	if (*vtp == VLNK) {
19597c478bd9Sstevel@tonic-gate 		/*
19607c478bd9Sstevel@tonic-gate 		 * nthcomp is the total result length minus
19617c478bd9Sstevel@tonic-gate 		 * the 1st 2 OPs (PUTROOTFH, GETFH),
19627c478bd9Sstevel@tonic-gate 		 * then divided by 3 (LOOKUP,GETFH,GETATTR)
19637c478bd9Sstevel@tonic-gate 		 *
19647c478bd9Sstevel@tonic-gate 		 * e.g. PUTROOTFH GETFH LOOKUP 1st-comp GETFH GETATTR
19657c478bd9Sstevel@tonic-gate 		 *	LOOKUP 2nd-comp GETFH GETATTR
19667c478bd9Sstevel@tonic-gate 		 *
19677c478bd9Sstevel@tonic-gate 		 *	(8 - 2)/3 = 2
19687c478bd9Sstevel@tonic-gate 		 */
19697c478bd9Sstevel@tonic-gate 		nthcomp = (res.array_len - 2)/3;
19707c478bd9Sstevel@tonic-gate 
19717c478bd9Sstevel@tonic-gate 		/*
19727c478bd9Sstevel@tonic-gate 		 * Need to call nfs4_end_op before resolve_sympath to avoid
19737c478bd9Sstevel@tonic-gate 		 * potential nfs4_start_op deadlock. See RFE 4777612.
19747c478bd9Sstevel@tonic-gate 		 */
19757c478bd9Sstevel@tonic-gate 		if (!recovery)
19767c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
1977b9238976Sth 			    needrecov);
19787c478bd9Sstevel@tonic-gate 
19797c478bd9Sstevel@tonic-gate 		ep->error = resolve_sympath(mi, svp, nthcomp, resfhp, cr,
1980b9238976Sth 		    flags);
19817c478bd9Sstevel@tonic-gate 
19827c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
19837c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
19847c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
19857c478bd9Sstevel@tonic-gate 
19867c478bd9Sstevel@tonic-gate 		if (ep->error)
19877c478bd9Sstevel@tonic-gate 			return;
19887c478bd9Sstevel@tonic-gate 
19897c478bd9Sstevel@tonic-gate 		goto recov_retry;
19907c478bd9Sstevel@tonic-gate 	}
19917c478bd9Sstevel@tonic-gate 
19927c478bd9Sstevel@tonic-gate 	/*
19937c478bd9Sstevel@tonic-gate 	 * We need to figure out where in the compound the getfh
19947c478bd9Sstevel@tonic-gate 	 * for the parent directory is. If the object to be mounted is
19957c478bd9Sstevel@tonic-gate 	 * the root, then there is no lookup at all:
19967c478bd9Sstevel@tonic-gate 	 * PUTROOTFH, GETFH.
19977c478bd9Sstevel@tonic-gate 	 * If the object to be mounted is in the root, then the compound is:
19987c478bd9Sstevel@tonic-gate 	 * PUTROOTFH, GETFH, LOOKUP, GETFH, GETATTR.
19997c478bd9Sstevel@tonic-gate 	 * In either of these cases, the index of the GETFH is 1.
20007c478bd9Sstevel@tonic-gate 	 * If it is not at the root, then it's something like:
20017c478bd9Sstevel@tonic-gate 	 * PUTROOTFH, GETFH, LOOKUP, GETFH, GETATTR,
20027c478bd9Sstevel@tonic-gate 	 * LOOKUP, GETFH, GETATTR
20037c478bd9Sstevel@tonic-gate 	 * In this case, the index is llndx (last lookup index) - 2.
20047c478bd9Sstevel@tonic-gate 	 */
20057c478bd9Sstevel@tonic-gate 	if (llndx == -1 || llndx == 2)
20067c478bd9Sstevel@tonic-gate 		resop = &res.array[1];
20077c478bd9Sstevel@tonic-gate 	else {
20087c478bd9Sstevel@tonic-gate 		ASSERT(llndx > 2);
20097c478bd9Sstevel@tonic-gate 		resop = &res.array[llndx-2];
20107c478bd9Sstevel@tonic-gate 	}
20117c478bd9Sstevel@tonic-gate 
20127c478bd9Sstevel@tonic-gate 	ASSERT(resop->resop == OP_GETFH);
20137c478bd9Sstevel@tonic-gate 	tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
20147c478bd9Sstevel@tonic-gate 
20157c478bd9Sstevel@tonic-gate 	/* save the filehandles for the replica */
20167c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
20177c478bd9Sstevel@tonic-gate 	ASSERT(tmpfhp->nfs_fh4_len <= NFS4_FHSIZE);
20187c478bd9Sstevel@tonic-gate 	svp->sv_pfhandle.fh_len = tmpfhp->nfs_fh4_len;
20197c478bd9Sstevel@tonic-gate 	bcopy(tmpfhp->nfs_fh4_val, svp->sv_pfhandle.fh_buf,
20207c478bd9Sstevel@tonic-gate 	    tmpfhp->nfs_fh4_len);
20217c478bd9Sstevel@tonic-gate 	ASSERT(resfhp->nfs_fh4_len <= NFS4_FHSIZE);
20227c478bd9Sstevel@tonic-gate 	svp->sv_fhandle.fh_len = resfhp->nfs_fh4_len;
20237c478bd9Sstevel@tonic-gate 	bcopy(resfhp->nfs_fh4_val, svp->sv_fhandle.fh_buf, resfhp->nfs_fh4_len);
20247c478bd9Sstevel@tonic-gate 
20257c478bd9Sstevel@tonic-gate 	/* initialize fsid and supp_attrs for server fs */
20267c478bd9Sstevel@tonic-gate 	svp->sv_fsid = garp->n4g_fsid;
20277c478bd9Sstevel@tonic-gate 	svp->sv_supp_attrs =
2028b9238976Sth 	    garp->n4g_ext_res->n4g_suppattrs | FATTR4_MANDATTR_MASK;
20297c478bd9Sstevel@tonic-gate 
20307c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
20317c478bd9Sstevel@tonic-gate 	nfs4args_lookup_free(argop, num_argops);
20327c478bd9Sstevel@tonic-gate 	kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
20337c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
20347c478bd9Sstevel@tonic-gate 	if (!recovery)
20357c478bd9Sstevel@tonic-gate 		nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state, needrecov);
20367c478bd9Sstevel@tonic-gate }
20377c478bd9Sstevel@tonic-gate 
20382f172c55SRobert Thurlow /*
20392f172c55SRobert Thurlow  * Save a copy of Servinfo4_t structure.
20402f172c55SRobert Thurlow  * We might need when there is a failure in getting file handle
20412f172c55SRobert Thurlow  * in case of a referral to replace servinfo4 struct and try again.
20422f172c55SRobert Thurlow  */
20432f172c55SRobert Thurlow static struct servinfo4 *
20442f172c55SRobert Thurlow copy_svp(servinfo4_t *nsvp)
20452f172c55SRobert Thurlow {
20462f172c55SRobert Thurlow 	servinfo4_t *svp = NULL;
20472f172c55SRobert Thurlow 	struct knetconfig *sknconf, *tknconf;
20482f172c55SRobert Thurlow 	struct netbuf *saddr, *taddr;
20492f172c55SRobert Thurlow 
20502f172c55SRobert Thurlow 	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
20512f172c55SRobert Thurlow 	nfs_rw_init(&svp->sv_lock, NULL, RW_DEFAULT, NULL);
20522f172c55SRobert Thurlow 	svp->sv_flags = nsvp->sv_flags;
20532f172c55SRobert Thurlow 	svp->sv_fsid = nsvp->sv_fsid;
20542f172c55SRobert Thurlow 	svp->sv_hostnamelen = nsvp->sv_hostnamelen;
20552f172c55SRobert Thurlow 	svp->sv_pathlen = nsvp->sv_pathlen;
20562f172c55SRobert Thurlow 	svp->sv_supp_attrs = nsvp->sv_supp_attrs;
20572f172c55SRobert Thurlow 
20582f172c55SRobert Thurlow 	svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP);
20592f172c55SRobert Thurlow 	svp->sv_hostname = kmem_alloc(svp->sv_hostnamelen, KM_SLEEP);
20602f172c55SRobert Thurlow 	bcopy(nsvp->sv_hostname, svp->sv_hostname, svp->sv_hostnamelen);
20612f172c55SRobert Thurlow 	bcopy(nsvp->sv_path, svp->sv_path, svp->sv_pathlen);
20622f172c55SRobert Thurlow 
20632f172c55SRobert Thurlow 	saddr = &nsvp->sv_addr;
20642f172c55SRobert Thurlow 	taddr = &svp->sv_addr;
20652f172c55SRobert Thurlow 	taddr->maxlen = saddr->maxlen;
20662f172c55SRobert Thurlow 	taddr->len = saddr->len;
20672f172c55SRobert Thurlow 	if (saddr->len > 0) {
20682f172c55SRobert Thurlow 		taddr->buf = kmem_zalloc(saddr->maxlen, KM_SLEEP);
20692f172c55SRobert Thurlow 		bcopy(saddr->buf, taddr->buf, saddr->len);
20702f172c55SRobert Thurlow 	}
20712f172c55SRobert Thurlow 
20722f172c55SRobert Thurlow 	svp->sv_knconf = kmem_zalloc(sizeof (struct knetconfig), KM_SLEEP);
20732f172c55SRobert Thurlow 	sknconf = nsvp->sv_knconf;
20742f172c55SRobert Thurlow 	tknconf = svp->sv_knconf;
20752f172c55SRobert Thurlow 	tknconf->knc_semantics = sknconf->knc_semantics;
20762f172c55SRobert Thurlow 	tknconf->knc_rdev = sknconf->knc_rdev;
20772f172c55SRobert Thurlow 	if (sknconf->knc_proto != NULL) {
20782f172c55SRobert Thurlow 		tknconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
20792f172c55SRobert Thurlow 		bcopy(sknconf->knc_proto, (char *)tknconf->knc_proto,
20802f172c55SRobert Thurlow 		    KNC_STRSIZE);
20812f172c55SRobert Thurlow 	}
20822f172c55SRobert Thurlow 	if (sknconf->knc_protofmly != NULL) {
20832f172c55SRobert Thurlow 		tknconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
20842f172c55SRobert Thurlow 		bcopy(sknconf->knc_protofmly, (char *)tknconf->knc_protofmly,
20852f172c55SRobert Thurlow 		    KNC_STRSIZE);
20862f172c55SRobert Thurlow 	}
20872f172c55SRobert Thurlow 
20882f172c55SRobert Thurlow 	if (nsvp->sv_origknconf != NULL) {
20892f172c55SRobert Thurlow 		svp->sv_origknconf = kmem_zalloc(sizeof (struct knetconfig),
20902f172c55SRobert Thurlow 		    KM_SLEEP);
20912f172c55SRobert Thurlow 		sknconf = nsvp->sv_origknconf;
20922f172c55SRobert Thurlow 		tknconf = svp->sv_origknconf;
20932f172c55SRobert Thurlow 		tknconf->knc_semantics = sknconf->knc_semantics;
20942f172c55SRobert Thurlow 		tknconf->knc_rdev = sknconf->knc_rdev;
20952f172c55SRobert Thurlow 		if (sknconf->knc_proto != NULL) {
20962f172c55SRobert Thurlow 			tknconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
20972f172c55SRobert Thurlow 			bcopy(sknconf->knc_proto, (char *)tknconf->knc_proto,
20982f172c55SRobert Thurlow 			    KNC_STRSIZE);
20992f172c55SRobert Thurlow 		}
21002f172c55SRobert Thurlow 		if (sknconf->knc_protofmly != NULL) {
21012f172c55SRobert Thurlow 			tknconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE,
21022f172c55SRobert Thurlow 			    KM_SLEEP);
21032f172c55SRobert Thurlow 			bcopy(sknconf->knc_protofmly,
21042f172c55SRobert Thurlow 			    (char *)tknconf->knc_protofmly, KNC_STRSIZE);
21052f172c55SRobert Thurlow 		}
21062f172c55SRobert Thurlow 	}
21072f172c55SRobert Thurlow 
21082f172c55SRobert Thurlow 	svp->sv_secdata = copy_sec_data(nsvp->sv_secdata);
21092f172c55SRobert Thurlow 	svp->sv_dhsec = copy_sec_data(svp->sv_dhsec);
21102f172c55SRobert Thurlow 	/*
21112f172c55SRobert Thurlow 	 * Rest of the security information is not copied as they are built
21122f172c55SRobert Thurlow 	 * with the information available from secdata and dhsec.
21132f172c55SRobert Thurlow 	 */
21142f172c55SRobert Thurlow 	svp->sv_next = NULL;
21152f172c55SRobert Thurlow 
21162f172c55SRobert Thurlow 	return (svp);
21172f172c55SRobert Thurlow }
21182f172c55SRobert Thurlow 
21192f172c55SRobert Thurlow servinfo4_t *
21202f172c55SRobert Thurlow restore_svp(mntinfo4_t *mi, servinfo4_t *svp, servinfo4_t *origsvp)
21212f172c55SRobert Thurlow {
21222f172c55SRobert Thurlow 	servinfo4_t *srvnext, *tmpsrv;
21232f172c55SRobert Thurlow 
21242f172c55SRobert Thurlow 	if (strcmp(svp->sv_hostname, origsvp->sv_hostname) != 0) {
21252f172c55SRobert Thurlow 		/*
21262f172c55SRobert Thurlow 		 * Since the hostname changed, we must be dealing
21272f172c55SRobert Thurlow 		 * with a referral, and the lookup failed.  We will
21282f172c55SRobert Thurlow 		 * restore the whole servinfo4_t to what it was before.
21292f172c55SRobert Thurlow 		 */
21302f172c55SRobert Thurlow 		srvnext = svp->sv_next;
21312f172c55SRobert Thurlow 		svp->sv_next = NULL;
21322f172c55SRobert Thurlow 		tmpsrv = copy_svp(origsvp);
21332f172c55SRobert Thurlow 		sv4_free(svp);
21342f172c55SRobert Thurlow 		svp = tmpsrv;
21352f172c55SRobert Thurlow 		svp->sv_next = srvnext;
21362f172c55SRobert Thurlow 		mutex_enter(&mi->mi_lock);
21372f172c55SRobert Thurlow 		mi->mi_servers = svp;
21382f172c55SRobert Thurlow 		mi->mi_curr_serv = svp;
21392f172c55SRobert Thurlow 		mutex_exit(&mi->mi_lock);
21402f172c55SRobert Thurlow 
21412f172c55SRobert Thurlow 	} else if (origsvp->sv_pathlen != svp->sv_pathlen) {
21422f172c55SRobert Thurlow 
21432f172c55SRobert Thurlow 		/*
21442f172c55SRobert Thurlow 		 * For symlink case: restore original path because
21452f172c55SRobert Thurlow 		 * it might have contained symlinks that were
21462f172c55SRobert Thurlow 		 * expanded by nfsgetfh_otw before the failure occurred.
21472f172c55SRobert Thurlow 		 */
21485301ec54SRobert Thurlow 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
21492f172c55SRobert Thurlow 		kmem_free(svp->sv_path, svp->sv_pathlen);
21502f172c55SRobert Thurlow 		svp->sv_path =
21512f172c55SRobert Thurlow 		    kmem_alloc(origsvp->sv_pathlen, KM_SLEEP);
21522f172c55SRobert Thurlow 		svp->sv_pathlen = origsvp->sv_pathlen;
21532f172c55SRobert Thurlow 		bcopy(origsvp->sv_path, svp->sv_path,
21542f172c55SRobert Thurlow 		    origsvp->sv_pathlen);
21552f172c55SRobert Thurlow 		nfs_rw_exit(&svp->sv_lock);
21562f172c55SRobert Thurlow 	}
21572f172c55SRobert Thurlow 	return (svp);
21582f172c55SRobert Thurlow }
21592f172c55SRobert Thurlow 
21607c478bd9Sstevel@tonic-gate static ushort_t nfs4_max_threads = 8;	/* max number of active async threads */
2161c242f9a0Schunli zhang - Sun Microsystems - Irvine United States uint_t nfs4_bsize = 32 * 1024;	/* client `block' size */
21627c478bd9Sstevel@tonic-gate static uint_t nfs4_async_clusters = 1;	/* # of reqs from each async queue */
21637c478bd9Sstevel@tonic-gate static uint_t nfs4_cots_timeo = NFS_COTS_TIMEO;
21647c478bd9Sstevel@tonic-gate 
21657c478bd9Sstevel@tonic-gate /*
21667c478bd9Sstevel@tonic-gate  * Remap the root filehandle for the given filesystem.
21677c478bd9Sstevel@tonic-gate  *
21687c478bd9Sstevel@tonic-gate  * results returned via the nfs4_error_t parameter.
21697c478bd9Sstevel@tonic-gate  */
21707c478bd9Sstevel@tonic-gate void
21717c478bd9Sstevel@tonic-gate nfs4_remap_root(mntinfo4_t *mi, nfs4_error_t *ep, int flags)
21727c478bd9Sstevel@tonic-gate {
21732f172c55SRobert Thurlow 	struct servinfo4 *svp, *origsvp;
21747c478bd9Sstevel@tonic-gate 	vtype_t vtype;
21757c478bd9Sstevel@tonic-gate 	nfs_fh4 rootfh;
21767c478bd9Sstevel@tonic-gate 	int getfh_flags;
21772f172c55SRobert Thurlow 	int num_retry;
21787c478bd9Sstevel@tonic-gate 
21797c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
21808c9e5ad2Saalok 
21818c9e5ad2Saalok remap_retry:
21827c478bd9Sstevel@tonic-gate 	svp = mi->mi_curr_serv;
21837c478bd9Sstevel@tonic-gate 	getfh_flags =
2184b9238976Sth 	    (flags & NFS4_REMAP_NEEDSOP) ? NFS4_GETFH_NEEDSOP : 0;
21857c478bd9Sstevel@tonic-gate 	getfh_flags |=
2186b9238976Sth 	    (mi->mi_flags & MI4_PUBLIC) ? NFS4_GETFH_PUBLIC : 0;
21877c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
21887c478bd9Sstevel@tonic-gate 
21897c478bd9Sstevel@tonic-gate 	/*
21907c478bd9Sstevel@tonic-gate 	 * Just in case server path being mounted contains
21917c478bd9Sstevel@tonic-gate 	 * symlinks and fails w/STALE, save the initial sv_path
21927c478bd9Sstevel@tonic-gate 	 * so we can redrive the initial mount compound with the
21937c478bd9Sstevel@tonic-gate 	 * initial sv_path -- not a symlink-expanded version.
21947c478bd9Sstevel@tonic-gate 	 *
21957c478bd9Sstevel@tonic-gate 	 * This could only happen if a symlink was expanded
21967c478bd9Sstevel@tonic-gate 	 * and the expanded mount compound failed stale.  Because
21977c478bd9Sstevel@tonic-gate 	 * it could be the case that the symlink was removed at
21987c478bd9Sstevel@tonic-gate 	 * the server (and replaced with another symlink/dir,
21997c478bd9Sstevel@tonic-gate 	 * we need to use the initial sv_path when attempting
22007c478bd9Sstevel@tonic-gate 	 * to re-lookup everything and recover.
22017c478bd9Sstevel@tonic-gate 	 */
22027c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
22032f172c55SRobert Thurlow 	origsvp = copy_svp(svp);
22047c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
22057c478bd9Sstevel@tonic-gate 
22067c478bd9Sstevel@tonic-gate 	num_retry = nfs4_max_mount_retry;
22077c478bd9Sstevel@tonic-gate 
22087c478bd9Sstevel@tonic-gate 	do {
22097c478bd9Sstevel@tonic-gate 		/*
22107c478bd9Sstevel@tonic-gate 		 * Get the root fh from the server.  Retry nfs4_max_mount_retry
22117c478bd9Sstevel@tonic-gate 		 * (2) times if it fails with STALE since the recovery
22127c478bd9Sstevel@tonic-gate 		 * infrastructure doesn't do STALE recovery for components
22137c478bd9Sstevel@tonic-gate 		 * of the server path to the object being mounted.
22147c478bd9Sstevel@tonic-gate 		 */
22157c478bd9Sstevel@tonic-gate 		nfs4getfh_otw(mi, svp, &vtype, getfh_flags, CRED(), ep);
22167c478bd9Sstevel@tonic-gate 
22177c478bd9Sstevel@tonic-gate 		if (ep->error == 0 && ep->stat == NFS4_OK)
22187c478bd9Sstevel@tonic-gate 			break;
22197c478bd9Sstevel@tonic-gate 
22207c478bd9Sstevel@tonic-gate 		/*
22217c478bd9Sstevel@tonic-gate 		 * For some reason, the mount compound failed.  Before
22222f172c55SRobert Thurlow 		 * retrying, we need to restore original conditions.
22237c478bd9Sstevel@tonic-gate 		 */
22242f172c55SRobert Thurlow 		svp = restore_svp(mi, svp, origsvp);
22257c478bd9Sstevel@tonic-gate 
22267c478bd9Sstevel@tonic-gate 	} while (num_retry-- > 0);
22277c478bd9Sstevel@tonic-gate 
22282f172c55SRobert Thurlow 	sv4_free(origsvp);
22297c478bd9Sstevel@tonic-gate 
22307c478bd9Sstevel@tonic-gate 	if (ep->error != 0 || ep->stat != 0) {
22317c478bd9Sstevel@tonic-gate 		return;
22327c478bd9Sstevel@tonic-gate 	}
22337c478bd9Sstevel@tonic-gate 
22347c478bd9Sstevel@tonic-gate 	if (vtype != VNON && vtype != mi->mi_type) {
22357c478bd9Sstevel@tonic-gate 		/* shouldn't happen */
22367c478bd9Sstevel@tonic-gate 		zcmn_err(mi->mi_zone->zone_id, CE_WARN,
2237b9238976Sth 		    "nfs4_remap_root: server root vnode type (%d) doesn't "
2238b9238976Sth 		    "match mount info (%d)", vtype, mi->mi_type);
22397c478bd9Sstevel@tonic-gate 	}
22407c478bd9Sstevel@tonic-gate 
22417c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
22427c478bd9Sstevel@tonic-gate 	rootfh.nfs_fh4_val = svp->sv_fhandle.fh_buf;
22437c478bd9Sstevel@tonic-gate 	rootfh.nfs_fh4_len = svp->sv_fhandle.fh_len;
22447c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
22457c478bd9Sstevel@tonic-gate 	sfh4_update(mi->mi_rootfh, &rootfh);
22467c478bd9Sstevel@tonic-gate 
22477c478bd9Sstevel@tonic-gate 	/*
22488c9e5ad2Saalok 	 * It's possible that recovery took place on the filesystem
22498c9e5ad2Saalok 	 * and the server has been updated between the time we did
22508c9e5ad2Saalok 	 * the nfs4getfh_otw and now. Re-drive the otw operation
22518c9e5ad2Saalok 	 * to make sure we have a good fh.
22527c478bd9Sstevel@tonic-gate 	 */
22537c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
22548c9e5ad2Saalok 	if (mi->mi_curr_serv != svp)
22558c9e5ad2Saalok 		goto remap_retry;
22568c9e5ad2Saalok 
22577c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
22587c478bd9Sstevel@tonic-gate }
22597c478bd9Sstevel@tonic-gate 
22607c478bd9Sstevel@tonic-gate static int
22617c478bd9Sstevel@tonic-gate nfs4rootvp(vnode_t **rtvpp, vfs_t *vfsp, struct servinfo4 *svp_head,
2262b9238976Sth     int flags, cred_t *cr, zone_t *zone)
22637c478bd9Sstevel@tonic-gate {
22647c478bd9Sstevel@tonic-gate 	vnode_t *rtvp = NULL;
22657c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
22667c478bd9Sstevel@tonic-gate 	dev_t nfs_dev;
22677c478bd9Sstevel@tonic-gate 	int error = 0;
22687c478bd9Sstevel@tonic-gate 	rnode4_t *rp;
22692f172c55SRobert Thurlow 	int i, len;
22707c478bd9Sstevel@tonic-gate 	struct vattr va;
22717c478bd9Sstevel@tonic-gate 	vtype_t vtype = VNON;
22727c478bd9Sstevel@tonic-gate 	vtype_t tmp_vtype = VNON;
22737c478bd9Sstevel@tonic-gate 	struct servinfo4 *firstsvp = NULL, *svp = svp_head;
22747c478bd9Sstevel@tonic-gate 	nfs4_oo_hash_bucket_t *bucketp;
22757c478bd9Sstevel@tonic-gate 	nfs_fh4 fh;
22767c478bd9Sstevel@tonic-gate 	char *droptext = "";
22777c478bd9Sstevel@tonic-gate 	struct nfs_stats *nfsstatsp;
22787c478bd9Sstevel@tonic-gate 	nfs4_fname_t *mfname;
22797c478bd9Sstevel@tonic-gate 	nfs4_error_t e;
22802f172c55SRobert Thurlow 	int num_retry, removed;
22817c478bd9Sstevel@tonic-gate 	cred_t *lcr = NULL, *tcr = cr;
22822f172c55SRobert Thurlow 	struct servinfo4 *origsvp;
22832f172c55SRobert Thurlow 	char *resource;
22847c478bd9Sstevel@tonic-gate 
2285108322fbScarlsonj 	nfsstatsp = zone_getspecific(nfsstat_zone_key, nfs_zone());
22867c478bd9Sstevel@tonic-gate 	ASSERT(nfsstatsp != NULL);
22877c478bd9Sstevel@tonic-gate 
2288108322fbScarlsonj 	ASSERT(nfs_zone() == zone);
22897c478bd9Sstevel@tonic-gate 	ASSERT(crgetref(cr));
22907c478bd9Sstevel@tonic-gate 
22917c478bd9Sstevel@tonic-gate 	/*
22927c478bd9Sstevel@tonic-gate 	 * Create a mount record and link it to the vfs struct.
22937c478bd9Sstevel@tonic-gate 	 */
22947c478bd9Sstevel@tonic-gate 	mi = kmem_zalloc(sizeof (*mi), KM_SLEEP);
22957c478bd9Sstevel@tonic-gate 	mutex_init(&mi->mi_lock, NULL, MUTEX_DEFAULT, NULL);
22967c478bd9Sstevel@tonic-gate 	nfs_rw_init(&mi->mi_recovlock, NULL, RW_DEFAULT, NULL);
22977c478bd9Sstevel@tonic-gate 	nfs_rw_init(&mi->mi_rename_lock, NULL, RW_DEFAULT, NULL);
22987c478bd9Sstevel@tonic-gate 	nfs_rw_init(&mi->mi_fh_lock, NULL, RW_DEFAULT, NULL);
22997c478bd9Sstevel@tonic-gate 
23007c478bd9Sstevel@tonic-gate 	if (!(flags & NFSMNT_SOFT))
23017c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_HARD;
23027c478bd9Sstevel@tonic-gate 	if ((flags & NFSMNT_NOPRINT))
23037c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_NOPRINT;
23047c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_INT)
23057c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_INT;
23067c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_PUBLIC)
23077c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_PUBLIC;
2308b9238976Sth 	if (flags & NFSMNT_MIRRORMOUNT)
2309b9238976Sth 		mi->mi_flags |= MI4_MIRRORMOUNT;
23102f172c55SRobert Thurlow 	if (flags & NFSMNT_REFERRAL)
23112f172c55SRobert Thurlow 		mi->mi_flags |= MI4_REFERRAL;
23127c478bd9Sstevel@tonic-gate 	mi->mi_retrans = NFS_RETRIES;
23137c478bd9Sstevel@tonic-gate 	if (svp->sv_knconf->knc_semantics == NC_TPI_COTS_ORD ||
23147c478bd9Sstevel@tonic-gate 	    svp->sv_knconf->knc_semantics == NC_TPI_COTS)
23157c478bd9Sstevel@tonic-gate 		mi->mi_timeo = nfs4_cots_timeo;
23167c478bd9Sstevel@tonic-gate 	else
23177c478bd9Sstevel@tonic-gate 		mi->mi_timeo = NFS_TIMEO;
23187c478bd9Sstevel@tonic-gate 	mi->mi_prog = NFS_PROGRAM;
23197c478bd9Sstevel@tonic-gate 	mi->mi_vers = NFS_V4;
23207c478bd9Sstevel@tonic-gate 	mi->mi_rfsnames = rfsnames_v4;
23217c478bd9Sstevel@tonic-gate 	mi->mi_reqs = nfsstatsp->nfs_stats_v4.rfsreqcnt_ptr;
23227c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_failover_cv, NULL, CV_DEFAULT, NULL);
23237c478bd9Sstevel@tonic-gate 	mi->mi_servers = svp;
23247c478bd9Sstevel@tonic-gate 	mi->mi_curr_serv = svp;
23257c478bd9Sstevel@tonic-gate 	mi->mi_acregmin = SEC2HR(ACREGMIN);
23267c478bd9Sstevel@tonic-gate 	mi->mi_acregmax = SEC2HR(ACREGMAX);
23277c478bd9Sstevel@tonic-gate 	mi->mi_acdirmin = SEC2HR(ACDIRMIN);
23287c478bd9Sstevel@tonic-gate 	mi->mi_acdirmax = SEC2HR(ACDIRMAX);
23297c478bd9Sstevel@tonic-gate 	mi->mi_fh_expire_type = FH4_PERSISTENT;
23307c478bd9Sstevel@tonic-gate 	mi->mi_clientid_next = NULL;
23317c478bd9Sstevel@tonic-gate 	mi->mi_clientid_prev = NULL;
23323b895386SPavel Filipensky 	mi->mi_srv = NULL;
23337c478bd9Sstevel@tonic-gate 	mi->mi_grace_wait = 0;
23347c478bd9Sstevel@tonic-gate 	mi->mi_error = 0;
23357c478bd9Sstevel@tonic-gate 	mi->mi_srvsettime = 0;
23363b895386SPavel Filipensky 	mi->mi_srvset_cnt = 0;
23377c478bd9Sstevel@tonic-gate 
233850a83466Sjwahlig 	mi->mi_count = 1;
233950a83466Sjwahlig 
23407c478bd9Sstevel@tonic-gate 	mi->mi_tsize = nfs4_tsize(svp->sv_knconf);
23417c478bd9Sstevel@tonic-gate 	mi->mi_stsize = mi->mi_tsize;
23427c478bd9Sstevel@tonic-gate 
23437c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_DIRECTIO)
23447c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_DIRECTIO;
23457c478bd9Sstevel@tonic-gate 
23467c478bd9Sstevel@tonic-gate 	mi->mi_flags |= MI4_MOUNTING;
23477c478bd9Sstevel@tonic-gate 
23487c478bd9Sstevel@tonic-gate 	/*
23497c478bd9Sstevel@tonic-gate 	 * Make a vfs struct for nfs.  We do this here instead of below
23507c478bd9Sstevel@tonic-gate 	 * because rtvp needs a vfs before we can do a getattr on it.
23517c478bd9Sstevel@tonic-gate 	 *
23527c478bd9Sstevel@tonic-gate 	 * Assign a unique device id to the mount
23537c478bd9Sstevel@tonic-gate 	 */
23547c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs_minor_lock);
23557c478bd9Sstevel@tonic-gate 	do {
23567c478bd9Sstevel@tonic-gate 		nfs_minor = (nfs_minor + 1) & MAXMIN32;
23577c478bd9Sstevel@tonic-gate 		nfs_dev = makedevice(nfs_major, nfs_minor);
23587c478bd9Sstevel@tonic-gate 	} while (vfs_devismounted(nfs_dev));
23597c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs_minor_lock);
23607c478bd9Sstevel@tonic-gate 
23617c478bd9Sstevel@tonic-gate 	vfsp->vfs_dev = nfs_dev;
23627c478bd9Sstevel@tonic-gate 	vfs_make_fsid(&vfsp->vfs_fsid, nfs_dev, nfs4fstyp);
23637c478bd9Sstevel@tonic-gate 	vfsp->vfs_data = (caddr_t)mi;
23647c478bd9Sstevel@tonic-gate 	vfsp->vfs_fstype = nfsfstyp;
23657c478bd9Sstevel@tonic-gate 	vfsp->vfs_bsize = nfs4_bsize;
23667c478bd9Sstevel@tonic-gate 
23677c478bd9Sstevel@tonic-gate 	/*
23687c478bd9Sstevel@tonic-gate 	 * Initialize fields used to support async putpage operations.
23697c478bd9Sstevel@tonic-gate 	 */
23707c478bd9Sstevel@tonic-gate 	for (i = 0; i < NFS4_ASYNC_TYPES; i++)
23717c478bd9Sstevel@tonic-gate 		mi->mi_async_clusters[i] = nfs4_async_clusters;
23727c478bd9Sstevel@tonic-gate 	mi->mi_async_init_clusters = nfs4_async_clusters;
23730776f5e6SVallish Vaidyeshwara 	mi->mi_async_curr[NFS4_ASYNC_QUEUE] =
23740776f5e6SVallish Vaidyeshwara 	    mi->mi_async_curr[NFS4_ASYNC_PGOPS_QUEUE] = &mi->mi_async_reqs[0];
23757c478bd9Sstevel@tonic-gate 	mi->mi_max_threads = nfs4_max_threads;
23767c478bd9Sstevel@tonic-gate 	mutex_init(&mi->mi_async_lock, NULL, MUTEX_DEFAULT, NULL);
23777c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_async_reqs_cv, NULL, CV_DEFAULT, NULL);
23780776f5e6SVallish Vaidyeshwara 	cv_init(&mi->mi_async_work_cv[NFS4_ASYNC_QUEUE], NULL, CV_DEFAULT,
23790776f5e6SVallish Vaidyeshwara 	    NULL);
23800776f5e6SVallish Vaidyeshwara 	cv_init(&mi->mi_async_work_cv[NFS4_ASYNC_PGOPS_QUEUE], NULL,
23810776f5e6SVallish Vaidyeshwara 	    CV_DEFAULT, NULL);
23827c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_async_cv, NULL, CV_DEFAULT, NULL);
23837c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_inact_req_cv, NULL, CV_DEFAULT, NULL);
23847c478bd9Sstevel@tonic-gate 
23857c478bd9Sstevel@tonic-gate 	mi->mi_vfsp = vfsp;
23867c478bd9Sstevel@tonic-gate 	zone_hold(mi->mi_zone = zone);
23877c478bd9Sstevel@tonic-gate 	nfs4_mi_zonelist_add(mi);
23887c478bd9Sstevel@tonic-gate 
23897c478bd9Sstevel@tonic-gate 	/*
23907c478bd9Sstevel@tonic-gate 	 * Initialize the <open owner/cred> hash table.
23917c478bd9Sstevel@tonic-gate 	 */
23927c478bd9Sstevel@tonic-gate 	for (i = 0; i < NFS4_NUM_OO_BUCKETS; i++) {
23937c478bd9Sstevel@tonic-gate 		bucketp = &(mi->mi_oo_list[i]);
23947c478bd9Sstevel@tonic-gate 		mutex_init(&bucketp->b_lock, NULL, MUTEX_DEFAULT, NULL);
23957c478bd9Sstevel@tonic-gate 		list_create(&bucketp->b_oo_hash_list,
23967c478bd9Sstevel@tonic-gate 		    sizeof (nfs4_open_owner_t),
23977c478bd9Sstevel@tonic-gate 		    offsetof(nfs4_open_owner_t, oo_hash_node));
23987c478bd9Sstevel@tonic-gate 	}
23997c478bd9Sstevel@tonic-gate 
24007c478bd9Sstevel@tonic-gate 	/*
24017c478bd9Sstevel@tonic-gate 	 * Initialize the freed open owner list.
24027c478bd9Sstevel@tonic-gate 	 */
24037c478bd9Sstevel@tonic-gate 	mi->mi_foo_num = 0;
24047c478bd9Sstevel@tonic-gate 	mi->mi_foo_max = NFS4_NUM_FREED_OPEN_OWNERS;
24057c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_foo_list, sizeof (nfs4_open_owner_t),
24067c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_open_owner_t, oo_foo_node));
24077c478bd9Sstevel@tonic-gate 
24087c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_lost_state, sizeof (nfs4_lost_rqst_t),
24097c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_lost_rqst_t, lr_node));
24107c478bd9Sstevel@tonic-gate 
24117c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_bseqid_list, sizeof (nfs4_bseqid_entry_t),
24127c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_bseqid_entry_t, bs_node));
24137c478bd9Sstevel@tonic-gate 
24147c478bd9Sstevel@tonic-gate 	/*
24157c478bd9Sstevel@tonic-gate 	 * Initialize the msg buffer.
24167c478bd9Sstevel@tonic-gate 	 */
24177c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_msg_list, sizeof (nfs4_debug_msg_t),
24187c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_debug_msg_t, msg_node));
24197c478bd9Sstevel@tonic-gate 	mi->mi_msg_count = 0;
24207c478bd9Sstevel@tonic-gate 	mutex_init(&mi->mi_msg_list_lock, NULL, MUTEX_DEFAULT, NULL);
24217c478bd9Sstevel@tonic-gate 
24227c478bd9Sstevel@tonic-gate 	/*
24237c478bd9Sstevel@tonic-gate 	 * Initialize kstats
24247c478bd9Sstevel@tonic-gate 	 */
24257c478bd9Sstevel@tonic-gate 	nfs4_mnt_kstat_init(vfsp);
24267c478bd9Sstevel@tonic-gate 
24277c478bd9Sstevel@tonic-gate 	/*
2428bbf2a467SNagakiran Rajashekar 	 * Initialize the shared filehandle pool.
24297c478bd9Sstevel@tonic-gate 	 */
24307c478bd9Sstevel@tonic-gate 	sfh4_createtab(&mi->mi_filehandles);
24317c478bd9Sstevel@tonic-gate 
24327c478bd9Sstevel@tonic-gate 	/*
24337c478bd9Sstevel@tonic-gate 	 * Save server path we're attempting to mount.
24347c478bd9Sstevel@tonic-gate 	 */
24357c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
24362f172c55SRobert Thurlow 	origsvp = copy_svp(svp);
24377c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
24387c478bd9Sstevel@tonic-gate 
24397c478bd9Sstevel@tonic-gate 	/*
24407c478bd9Sstevel@tonic-gate 	 * Make the GETFH call to get root fh for each replica.
24417c478bd9Sstevel@tonic-gate 	 */
24427c478bd9Sstevel@tonic-gate 	if (svp_head->sv_next)
24437c478bd9Sstevel@tonic-gate 		droptext = ", dropping replica";
24447c478bd9Sstevel@tonic-gate 
24457c478bd9Sstevel@tonic-gate 	/*
24467c478bd9Sstevel@tonic-gate 	 * If the uid is set then set the creds for secure mounts
24477c478bd9Sstevel@tonic-gate 	 * by proxy processes such as automountd.
24487c478bd9Sstevel@tonic-gate 	 */
24497c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
2450f722863dSSiyamaladevi Santhana Krishnan 	if (svp->sv_secdata->uid != 0 &&
2451f722863dSSiyamaladevi Santhana Krishnan 	    svp->sv_secdata->rpcflavor == RPCSEC_GSS) {
24527c478bd9Sstevel@tonic-gate 		lcr = crdup(cr);
24537c478bd9Sstevel@tonic-gate 		(void) crsetugid(lcr, svp->sv_secdata->uid, crgetgid(cr));
24547c478bd9Sstevel@tonic-gate 		tcr = lcr;
24557c478bd9Sstevel@tonic-gate 	}
24567c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
24577c478bd9Sstevel@tonic-gate 	for (svp = svp_head; svp; svp = svp->sv_next) {
24587c478bd9Sstevel@tonic-gate 		if (nfs4_chkdup_servinfo4(svp_head, svp)) {
24597c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN,
2460b9238976Sth 			    VERS_MSG "Host %s is a duplicate%s",
2461b9238976Sth 			    svp->sv_hostname, droptext);
24627c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
24637c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
24647c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
24657c478bd9Sstevel@tonic-gate 			continue;
24667c478bd9Sstevel@tonic-gate 		}
24677c478bd9Sstevel@tonic-gate 		mi->mi_curr_serv = svp;
24687c478bd9Sstevel@tonic-gate 
24697c478bd9Sstevel@tonic-gate 		/*
24707c478bd9Sstevel@tonic-gate 		 * Just in case server path being mounted contains
24717c478bd9Sstevel@tonic-gate 		 * symlinks and fails w/STALE, save the initial sv_path
24727c478bd9Sstevel@tonic-gate 		 * so we can redrive the initial mount compound with the
24737c478bd9Sstevel@tonic-gate 		 * initial sv_path -- not a symlink-expanded version.
24747c478bd9Sstevel@tonic-gate 		 *
24757c478bd9Sstevel@tonic-gate 		 * This could only happen if a symlink was expanded
24767c478bd9Sstevel@tonic-gate 		 * and the expanded mount compound failed stale.  Because
24777c478bd9Sstevel@tonic-gate 		 * it could be the case that the symlink was removed at
24787c478bd9Sstevel@tonic-gate 		 * the server (and replaced with another symlink/dir,
24797c478bd9Sstevel@tonic-gate 		 * we need to use the initial sv_path when attempting
24807c478bd9Sstevel@tonic-gate 		 * to re-lookup everything and recover.
24817c478bd9Sstevel@tonic-gate 		 *
24827c478bd9Sstevel@tonic-gate 		 * Other mount errors should evenutally be handled here also
24837c478bd9Sstevel@tonic-gate 		 * (NFS4ERR_DELAY, NFS4ERR_RESOURCE).  For now, all mount
24847c478bd9Sstevel@tonic-gate 		 * failures will result in mount being redriven a few times.
24857c478bd9Sstevel@tonic-gate 		 */
24867c478bd9Sstevel@tonic-gate 		num_retry = nfs4_max_mount_retry;
24877c478bd9Sstevel@tonic-gate 		do {
24887c478bd9Sstevel@tonic-gate 			nfs4getfh_otw(mi, svp, &tmp_vtype,
24897c478bd9Sstevel@tonic-gate 			    ((flags & NFSMNT_PUBLIC) ? NFS4_GETFH_PUBLIC : 0) |
24907c478bd9Sstevel@tonic-gate 			    NFS4_GETFH_NEEDSOP, tcr, &e);
24917c478bd9Sstevel@tonic-gate 
24927c478bd9Sstevel@tonic-gate 			if (e.error == 0 && e.stat == NFS4_OK)
24937c478bd9Sstevel@tonic-gate 				break;
24947c478bd9Sstevel@tonic-gate 
24957c478bd9Sstevel@tonic-gate 			/*
24962f172c55SRobert Thurlow 			 * For some reason, the mount compound failed.  Before
24972f172c55SRobert Thurlow 			 * retrying, we need to restore original conditions.
24987c478bd9Sstevel@tonic-gate 			 */
24992f172c55SRobert Thurlow 			svp = restore_svp(mi, svp, origsvp);
25002f172c55SRobert Thurlow 			svp_head = svp;
25017c478bd9Sstevel@tonic-gate 
25027c478bd9Sstevel@tonic-gate 		} while (num_retry-- > 0);
25037c478bd9Sstevel@tonic-gate 		error = e.error ? e.error : geterrno4(e.stat);
25047c478bd9Sstevel@tonic-gate 		if (error) {
25057c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN,
2506b9238976Sth 			    VERS_MSG "initial call to %s failed%s: %m",
2507b9238976Sth 			    svp->sv_hostname, droptext);
25087c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
25097c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
25107c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
25117c478bd9Sstevel@tonic-gate 			mi->mi_flags &= ~MI4_RECOV_FAIL;
25127c478bd9Sstevel@tonic-gate 			mi->mi_error = 0;
25137c478bd9Sstevel@tonic-gate 			continue;
25147c478bd9Sstevel@tonic-gate 		}
25157c478bd9Sstevel@tonic-gate 
25167c478bd9Sstevel@tonic-gate 		if (tmp_vtype == VBAD) {
25177c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_WARN,
2518b9238976Sth 			    VERS_MSG "%s returned a bad file type for "
2519b9238976Sth 			    "root%s", svp->sv_hostname, droptext);
25207c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
25217c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
25227c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
25237c478bd9Sstevel@tonic-gate 			continue;
25247c478bd9Sstevel@tonic-gate 		}
25257c478bd9Sstevel@tonic-gate 
25267c478bd9Sstevel@tonic-gate 		if (vtype == VNON) {
25277c478bd9Sstevel@tonic-gate 			vtype = tmp_vtype;
25287c478bd9Sstevel@tonic-gate 		} else if (vtype != tmp_vtype) {
25297c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_WARN,
2530b9238976Sth 			    VERS_MSG "%s returned a different file type "
2531b9238976Sth 			    "for root%s", svp->sv_hostname, droptext);
25327c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
25337c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
25347c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
25357c478bd9Sstevel@tonic-gate 			continue;
25367c478bd9Sstevel@tonic-gate 		}
25377c478bd9Sstevel@tonic-gate 		if (firstsvp == NULL)
25387c478bd9Sstevel@tonic-gate 			firstsvp = svp;
25397c478bd9Sstevel@tonic-gate 	}
25407c478bd9Sstevel@tonic-gate 
25417c478bd9Sstevel@tonic-gate 	if (firstsvp == NULL) {
25427c478bd9Sstevel@tonic-gate 		if (error == 0)
25437c478bd9Sstevel@tonic-gate 			error = ENOENT;
25447c478bd9Sstevel@tonic-gate 		goto bad;
25457c478bd9Sstevel@tonic-gate 	}
25467c478bd9Sstevel@tonic-gate 
25477c478bd9Sstevel@tonic-gate 	mi->mi_curr_serv = svp = firstsvp;
25487c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
25497c478bd9Sstevel@tonic-gate 	ASSERT((mi->mi_curr_serv->sv_flags & SV4_NOTINUSE) == 0);
25507c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_len = svp->sv_fhandle.fh_len;
25517c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_val = svp->sv_fhandle.fh_buf;
25527c478bd9Sstevel@tonic-gate 	mi->mi_rootfh = sfh4_get(&fh, mi);
25537c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_len = svp->sv_pfhandle.fh_len;
25547c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_val = svp->sv_pfhandle.fh_buf;
25557c478bd9Sstevel@tonic-gate 	mi->mi_srvparentfh = sfh4_get(&fh, mi);
25567c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
25577c478bd9Sstevel@tonic-gate 
25587c478bd9Sstevel@tonic-gate 	/*
2559bbf2a467SNagakiran Rajashekar 	 * Get the fname for filesystem root.
25607c478bd9Sstevel@tonic-gate 	 */
2561bbf2a467SNagakiran Rajashekar 	mi->mi_fname = fn_get(NULL, ".", mi->mi_rootfh);
25627c478bd9Sstevel@tonic-gate 	mfname = mi->mi_fname;
25637c478bd9Sstevel@tonic-gate 	fn_hold(mfname);
2564bbf2a467SNagakiran Rajashekar 
2565bbf2a467SNagakiran Rajashekar 	/*
2566bbf2a467SNagakiran Rajashekar 	 * Make the root vnode without attributes.
2567bbf2a467SNagakiran Rajashekar 	 */
25687c478bd9Sstevel@tonic-gate 	rtvp = makenfs4node_by_fh(mi->mi_rootfh, NULL,
25697c478bd9Sstevel@tonic-gate 	    &mfname, NULL, mi, cr, gethrtime());
25707c478bd9Sstevel@tonic-gate 	rtvp->v_type = vtype;
25717c478bd9Sstevel@tonic-gate 
25727c478bd9Sstevel@tonic-gate 	mi->mi_curread = mi->mi_tsize;
25737c478bd9Sstevel@tonic-gate 	mi->mi_curwrite = mi->mi_stsize;
25747c478bd9Sstevel@tonic-gate 
25757c478bd9Sstevel@tonic-gate 	/*
25767c478bd9Sstevel@tonic-gate 	 * Start the manager thread responsible for handling async worker
25777c478bd9Sstevel@tonic-gate 	 * threads.
25787c478bd9Sstevel@tonic-gate 	 */
257950a83466Sjwahlig 	MI4_HOLD(mi);
25807c478bd9Sstevel@tonic-gate 	VFS_HOLD(vfsp);	/* add reference for thread */
25817c478bd9Sstevel@tonic-gate 	mi->mi_manager_thread = zthread_create(NULL, 0, nfs4_async_manager,
2582b9238976Sth 	    vfsp, 0, minclsyspri);
25837c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_manager_thread != NULL);
258450a83466Sjwahlig 
25857c478bd9Sstevel@tonic-gate 	/*
25867c478bd9Sstevel@tonic-gate 	 * Create the thread that handles over-the-wire calls for
25877c478bd9Sstevel@tonic-gate 	 * VOP_INACTIVE.
25887c478bd9Sstevel@tonic-gate 	 * This needs to happen after the manager thread is created.
25897c478bd9Sstevel@tonic-gate 	 */
259050a83466Sjwahlig 	MI4_HOLD(mi);
25917c478bd9Sstevel@tonic-gate 	mi->mi_inactive_thread = zthread_create(NULL, 0, nfs4_inactive_thread,
2592b9238976Sth 	    mi, 0, minclsyspri);
25937c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_inactive_thread != NULL);
25947c478bd9Sstevel@tonic-gate 
25957c478bd9Sstevel@tonic-gate 	/* If we didn't get a type, get one now */
25967c478bd9Sstevel@tonic-gate 	if (rtvp->v_type == VNON) {
25977c478bd9Sstevel@tonic-gate 		va.va_mask = AT_TYPE;
25987c478bd9Sstevel@tonic-gate 		error = nfs4getattr(rtvp, &va, tcr);
25997c478bd9Sstevel@tonic-gate 		if (error)
26007c478bd9Sstevel@tonic-gate 			goto bad;
26017c478bd9Sstevel@tonic-gate 		rtvp->v_type = va.va_type;
26027c478bd9Sstevel@tonic-gate 	}
26037c478bd9Sstevel@tonic-gate 
26047c478bd9Sstevel@tonic-gate 	mi->mi_type = rtvp->v_type;
26057c478bd9Sstevel@tonic-gate 
26067c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
26077c478bd9Sstevel@tonic-gate 	mi->mi_flags &= ~MI4_MOUNTING;
26087c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
26097c478bd9Sstevel@tonic-gate 
26102f172c55SRobert Thurlow 	/* Update VFS with new server and path info */
26112f172c55SRobert Thurlow 	if ((strcmp(svp->sv_hostname, origsvp->sv_hostname) != 0) ||
26122f172c55SRobert Thurlow 	    (strcmp(svp->sv_path, origsvp->sv_path) != 0)) {
26132f172c55SRobert Thurlow 		len = svp->sv_hostnamelen + svp->sv_pathlen;
26142f172c55SRobert Thurlow 		resource = kmem_zalloc(len, KM_SLEEP);
26152f172c55SRobert Thurlow 		(void) strcat(resource, svp->sv_hostname);
26162f172c55SRobert Thurlow 		(void) strcat(resource, ":");
26172f172c55SRobert Thurlow 		(void) strcat(resource, svp->sv_path);
2618*d7de0ceaSRobert Harris 		vfs_setresource(vfsp, resource, 0);
26192f172c55SRobert Thurlow 		kmem_free(resource, len);
26202f172c55SRobert Thurlow 	}
26212f172c55SRobert Thurlow 
26222f172c55SRobert Thurlow 	sv4_free(origsvp);
26237c478bd9Sstevel@tonic-gate 	*rtvpp = rtvp;
26247c478bd9Sstevel@tonic-gate 	if (lcr != NULL)
26257c478bd9Sstevel@tonic-gate 		crfree(lcr);
26267c478bd9Sstevel@tonic-gate 
26277c478bd9Sstevel@tonic-gate 	return (0);
26287c478bd9Sstevel@tonic-gate bad:
26297c478bd9Sstevel@tonic-gate 	/*
26307c478bd9Sstevel@tonic-gate 	 * An error occurred somewhere, need to clean up...
26317c478bd9Sstevel@tonic-gate 	 */
26327c478bd9Sstevel@tonic-gate 	if (lcr != NULL)
26337c478bd9Sstevel@tonic-gate 		crfree(lcr);
2634b9238976Sth 
26357c478bd9Sstevel@tonic-gate 	if (rtvp != NULL) {
26367c478bd9Sstevel@tonic-gate 		/*
26377c478bd9Sstevel@tonic-gate 		 * We need to release our reference to the root vnode and
26387c478bd9Sstevel@tonic-gate 		 * destroy the mntinfo4 struct that we just created.
26397c478bd9Sstevel@tonic-gate 		 */
26407c478bd9Sstevel@tonic-gate 		rp = VTOR4(rtvp);
26417c478bd9Sstevel@tonic-gate 		if (rp->r_flags & R4HASHED)
26427c478bd9Sstevel@tonic-gate 			rp4_rmhash(rp);
26437c478bd9Sstevel@tonic-gate 		VN_RELE(rtvp);
26447c478bd9Sstevel@tonic-gate 	}
26457c478bd9Sstevel@tonic-gate 	nfs4_async_stop(vfsp);
26467c478bd9Sstevel@tonic-gate 	nfs4_async_manager_stop(vfsp);
264750a83466Sjwahlig 	removed = nfs4_mi_zonelist_remove(mi);
264850a83466Sjwahlig 	if (removed)
264950a83466Sjwahlig 		zone_rele(mi->mi_zone);
265050a83466Sjwahlig 
265150a83466Sjwahlig 	/*
265250a83466Sjwahlig 	 * This releases the initial "hold" of the mi since it will never
265350a83466Sjwahlig 	 * be referenced by the vfsp.  Also, when mount returns to vfs.c
265450a83466Sjwahlig 	 * with an error, the vfsp will be destroyed, not rele'd.
265550a83466Sjwahlig 	 */
265650a83466Sjwahlig 	MI4_RELE(mi);
265750a83466Sjwahlig 
26582f172c55SRobert Thurlow 	if (origsvp != NULL)
26592f172c55SRobert Thurlow 		sv4_free(origsvp);
26602f172c55SRobert Thurlow 
26617c478bd9Sstevel@tonic-gate 	*rtvpp = NULL;
26627c478bd9Sstevel@tonic-gate 	return (error);
26637c478bd9Sstevel@tonic-gate }
26647c478bd9Sstevel@tonic-gate 
26657c478bd9Sstevel@tonic-gate /*
26667c478bd9Sstevel@tonic-gate  * vfs operations
26677c478bd9Sstevel@tonic-gate  */
26687c478bd9Sstevel@tonic-gate static int
26697c478bd9Sstevel@tonic-gate nfs4_unmount(vfs_t *vfsp, int flag, cred_t *cr)
26707c478bd9Sstevel@tonic-gate {
2671b9238976Sth 	mntinfo4_t		*mi;
2672b9238976Sth 	ushort_t		omax;
2673b9238976Sth 	int			removed;
2674b9238976Sth 
2675d3a14591SThomas Haynes 	bool_t			must_unlock;
2676b9238976Sth 
2677b9238976Sth 	nfs4_ephemeral_tree_t	*eph_tree;
26787c478bd9Sstevel@tonic-gate 
26797c478bd9Sstevel@tonic-gate 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
26807c478bd9Sstevel@tonic-gate 		return (EPERM);
26817c478bd9Sstevel@tonic-gate 
26827c478bd9Sstevel@tonic-gate 	mi = VFTOMI4(vfsp);
26837c478bd9Sstevel@tonic-gate 
26847c478bd9Sstevel@tonic-gate 	if (flag & MS_FORCE) {
26857c478bd9Sstevel@tonic-gate 		vfsp->vfs_flag |= VFS_UNMOUNTED;
2686108322fbScarlsonj 		if (nfs_zone() != mi->mi_zone) {
26877c478bd9Sstevel@tonic-gate 			/*
26887c478bd9Sstevel@tonic-gate 			 * If the request is coming from the wrong zone,
26897c478bd9Sstevel@tonic-gate 			 * we don't want to create any new threads, and
26907c478bd9Sstevel@tonic-gate 			 * performance is not a concern.  Do everything
26917c478bd9Sstevel@tonic-gate 			 * inline.
26927c478bd9Sstevel@tonic-gate 			 */
26937c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_client_zone_debug, (CE_NOTE,
26947c478bd9Sstevel@tonic-gate 			    "nfs4_unmount x-zone forced unmount of vfs %p\n",
26957c478bd9Sstevel@tonic-gate 			    (void *)vfsp));
2696b9238976Sth 			nfs4_free_mount(vfsp, flag, cr);
26977c478bd9Sstevel@tonic-gate 		} else {
26987c478bd9Sstevel@tonic-gate 			/*
26997c478bd9Sstevel@tonic-gate 			 * Free data structures asynchronously, to avoid
27007c478bd9Sstevel@tonic-gate 			 * blocking the current thread (for performance
27017c478bd9Sstevel@tonic-gate 			 * reasons only).
27027c478bd9Sstevel@tonic-gate 			 */
2703b9238976Sth 			async_free_mount(vfsp, flag, cr);
27047c478bd9Sstevel@tonic-gate 		}
2705b9238976Sth 
27067c478bd9Sstevel@tonic-gate 		return (0);
27077c478bd9Sstevel@tonic-gate 	}
2708b9238976Sth 
27097c478bd9Sstevel@tonic-gate 	/*
27107c478bd9Sstevel@tonic-gate 	 * Wait until all asynchronous putpage operations on
27117c478bd9Sstevel@tonic-gate 	 * this file system are complete before flushing rnodes
27127c478bd9Sstevel@tonic-gate 	 * from the cache.
27137c478bd9Sstevel@tonic-gate 	 */
27147c478bd9Sstevel@tonic-gate 	omax = mi->mi_max_threads;
2715b9238976Sth 	if (nfs4_async_stop_sig(vfsp))
27167c478bd9Sstevel@tonic-gate 		return (EINTR);
2717b9238976Sth 
27187c478bd9Sstevel@tonic-gate 	r4flush(vfsp, cr);
2719b9238976Sth 
2720eabd0450Sth 	/*
2721eabd0450Sth 	 * About the only reason that this would fail would be
2722eabd0450Sth 	 * that the harvester is already busy tearing down this
2723eabd0450Sth 	 * node. So we fail back to the caller and let them try
2724eabd0450Sth 	 * again when needed.
2725eabd0450Sth 	 */
2726eabd0450Sth 	if (nfs4_ephemeral_umount(mi, flag, cr,
27272f172c55SRobert Thurlow 	    &must_unlock, &eph_tree)) {
2728d3a14591SThomas Haynes 		ASSERT(must_unlock == FALSE);
2729eabd0450Sth 		mutex_enter(&mi->mi_async_lock);
2730eabd0450Sth 		mi->mi_max_threads = omax;
2731eabd0450Sth 		mutex_exit(&mi->mi_async_lock);
2732eabd0450Sth 
2733eabd0450Sth 		return (EBUSY);
2734eabd0450Sth 	}
2735b9238976Sth 
27367c478bd9Sstevel@tonic-gate 	/*
27377c478bd9Sstevel@tonic-gate 	 * If there are any active vnodes on this file system,
2738b9238976Sth 	 * then the file system is busy and can't be unmounted.
27397c478bd9Sstevel@tonic-gate 	 */
27407c478bd9Sstevel@tonic-gate 	if (check_rtable4(vfsp)) {
27412f172c55SRobert Thurlow 		nfs4_ephemeral_umount_unlock(&must_unlock, &eph_tree);
2742b9238976Sth 
27437c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_async_lock);
27447c478bd9Sstevel@tonic-gate 		mi->mi_max_threads = omax;
27457c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_async_lock);
2746b9238976Sth 
27477c478bd9Sstevel@tonic-gate 		return (EBUSY);
27487c478bd9Sstevel@tonic-gate 	}
2749b9238976Sth 
2750b9238976Sth 	/*
2751b9238976Sth 	 * The unmount can't fail from now on, so record any
2752b9238976Sth 	 * ephemeral changes.
2753b9238976Sth 	 */
27542f172c55SRobert Thurlow 	nfs4_ephemeral_umount_activate(mi, &must_unlock, &eph_tree);
2755b9238976Sth 
27567c478bd9Sstevel@tonic-gate 	/*
2757b9238976Sth 	 * There are no active files that could require over-the-wire
2758b9238976Sth 	 * calls to the server, so stop the async manager and the
2759b9238976Sth 	 * inactive thread.
27607c478bd9Sstevel@tonic-gate 	 */
27617c478bd9Sstevel@tonic-gate 	nfs4_async_manager_stop(vfsp);
2762b9238976Sth 
27637c478bd9Sstevel@tonic-gate 	/*
27647c478bd9Sstevel@tonic-gate 	 * Destroy all rnodes belonging to this file system from the
27657c478bd9Sstevel@tonic-gate 	 * rnode hash queues and purge any resources allocated to
27667c478bd9Sstevel@tonic-gate 	 * them.
27677c478bd9Sstevel@tonic-gate 	 */
27687c478bd9Sstevel@tonic-gate 	destroy_rtable4(vfsp, cr);
27697c478bd9Sstevel@tonic-gate 	vfsp->vfs_flag |= VFS_UNMOUNTED;
277050a83466Sjwahlig 
27717c478bd9Sstevel@tonic-gate 	nfs4_remove_mi_from_server(mi, NULL);
277250a83466Sjwahlig 	removed = nfs4_mi_zonelist_remove(mi);
277350a83466Sjwahlig 	if (removed)
277450a83466Sjwahlig 		zone_rele(mi->mi_zone);
277550a83466Sjwahlig 
27767c478bd9Sstevel@tonic-gate 	return (0);
27777c478bd9Sstevel@tonic-gate }
27787c478bd9Sstevel@tonic-gate 
27797c478bd9Sstevel@tonic-gate /*
27807c478bd9Sstevel@tonic-gate  * find root of nfs
27817c478bd9Sstevel@tonic-gate  */
27827c478bd9Sstevel@tonic-gate static int
27837c478bd9Sstevel@tonic-gate nfs4_root(vfs_t *vfsp, vnode_t **vpp)
27847c478bd9Sstevel@tonic-gate {
27857c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
27867c478bd9Sstevel@tonic-gate 	vnode_t *vp;
27877c478bd9Sstevel@tonic-gate 	nfs4_fname_t *mfname;
27887c478bd9Sstevel@tonic-gate 	servinfo4_t *svp;
27897c478bd9Sstevel@tonic-gate 
27907c478bd9Sstevel@tonic-gate 	mi = VFTOMI4(vfsp);
27917c478bd9Sstevel@tonic-gate 
2792108322fbScarlsonj 	if (nfs_zone() != mi->mi_zone)
27937c478bd9Sstevel@tonic-gate 		return (EPERM);
27947c478bd9Sstevel@tonic-gate 
27957c478bd9Sstevel@tonic-gate 	svp = mi->mi_curr_serv;
27967c478bd9Sstevel@tonic-gate 	if (svp) {
27977c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
27987c478bd9Sstevel@tonic-gate 		if (svp->sv_flags & SV4_ROOT_STALE) {
27997c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
28007c478bd9Sstevel@tonic-gate 
28017c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
28027c478bd9Sstevel@tonic-gate 			if (svp->sv_flags & SV4_ROOT_STALE) {
28037c478bd9Sstevel@tonic-gate 				svp->sv_flags &= ~SV4_ROOT_STALE;
28047c478bd9Sstevel@tonic-gate 				nfs_rw_exit(&svp->sv_lock);
28057c478bd9Sstevel@tonic-gate 				return (ENOENT);
28067c478bd9Sstevel@tonic-gate 			}
28077c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
28087c478bd9Sstevel@tonic-gate 		} else
28097c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
28107c478bd9Sstevel@tonic-gate 	}
28117c478bd9Sstevel@tonic-gate 
28127c478bd9Sstevel@tonic-gate 	mfname = mi->mi_fname;
28137c478bd9Sstevel@tonic-gate 	fn_hold(mfname);
28147c478bd9Sstevel@tonic-gate 	vp = makenfs4node_by_fh(mi->mi_rootfh, NULL, &mfname, NULL,
28157c478bd9Sstevel@tonic-gate 	    VFTOMI4(vfsp), CRED(), gethrtime());
28167c478bd9Sstevel@tonic-gate 
28177c478bd9Sstevel@tonic-gate 	if (VTOR4(vp)->r_flags & R4STALE) {
28187c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
28197c478bd9Sstevel@tonic-gate 		return (ENOENT);
28207c478bd9Sstevel@tonic-gate 	}
28217c478bd9Sstevel@tonic-gate 
28227c478bd9Sstevel@tonic-gate 	ASSERT(vp->v_type == VNON || vp->v_type == mi->mi_type);
28237c478bd9Sstevel@tonic-gate 
28247c478bd9Sstevel@tonic-gate 	vp->v_type = mi->mi_type;
28257c478bd9Sstevel@tonic-gate 
28267c478bd9Sstevel@tonic-gate 	*vpp = vp;
28277c478bd9Sstevel@tonic-gate 
28287c478bd9Sstevel@tonic-gate 	return (0);
28297c478bd9Sstevel@tonic-gate }
28307c478bd9Sstevel@tonic-gate 
28317c478bd9Sstevel@tonic-gate static int
28327c478bd9Sstevel@tonic-gate nfs4_statfs_otw(vnode_t *vp, struct statvfs64 *sbp, cred_t *cr)
28337c478bd9Sstevel@tonic-gate {
28347c478bd9Sstevel@tonic-gate 	int error;
28357c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t gar;
28367c478bd9Sstevel@tonic-gate 	nfs4_ga_ext_res_t ger;
28377c478bd9Sstevel@tonic-gate 
28387c478bd9Sstevel@tonic-gate 	gar.n4g_ext_res = &ger;
28397c478bd9Sstevel@tonic-gate 
28407c478bd9Sstevel@tonic-gate 	if (error = nfs4_attr_otw(vp, TAG_FSINFO, &gar,
28417c478bd9Sstevel@tonic-gate 	    NFS4_STATFS_ATTR_MASK, cr))
28427c478bd9Sstevel@tonic-gate 		return (error);
28437c478bd9Sstevel@tonic-gate 
28447c478bd9Sstevel@tonic-gate 	*sbp = gar.n4g_ext_res->n4g_sb;
28457c478bd9Sstevel@tonic-gate 
28467c478bd9Sstevel@tonic-gate 	return (0);
28477c478bd9Sstevel@tonic-gate }
28487c478bd9Sstevel@tonic-gate 
28497c478bd9Sstevel@tonic-gate /*
28507c478bd9Sstevel@tonic-gate  * Get file system statistics.
28517c478bd9Sstevel@tonic-gate  */
28527c478bd9Sstevel@tonic-gate static int
28537c478bd9Sstevel@tonic-gate nfs4_statvfs(vfs_t *vfsp, struct statvfs64 *sbp)
28547c478bd9Sstevel@tonic-gate {
28557c478bd9Sstevel@tonic-gate 	int error;
28567c478bd9Sstevel@tonic-gate 	vnode_t *vp;
28577c478bd9Sstevel@tonic-gate 	cred_t *cr;
28587c478bd9Sstevel@tonic-gate 
28597c478bd9Sstevel@tonic-gate 	error = nfs4_root(vfsp, &vp);
28607c478bd9Sstevel@tonic-gate 	if (error)
28617c478bd9Sstevel@tonic-gate 		return (error);
28627c478bd9Sstevel@tonic-gate 
28637c478bd9Sstevel@tonic-gate 	cr = CRED();
28647c478bd9Sstevel@tonic-gate 
28657c478bd9Sstevel@tonic-gate 	error = nfs4_statfs_otw(vp, sbp, cr);
28667c478bd9Sstevel@tonic-gate 	if (!error) {
28677c478bd9Sstevel@tonic-gate 		(void) strncpy(sbp->f_basetype,
2868b9238976Sth 		    vfssw[vfsp->vfs_fstype].vsw_name, FSTYPSZ);
28697c478bd9Sstevel@tonic-gate 		sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
28707c478bd9Sstevel@tonic-gate 	} else {
28717c478bd9Sstevel@tonic-gate 		nfs4_purge_stale_fh(error, vp, cr);
28727c478bd9Sstevel@tonic-gate 	}
28737c478bd9Sstevel@tonic-gate 
28747c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
28757c478bd9Sstevel@tonic-gate 
28767c478bd9Sstevel@tonic-gate 	return (error);
28777c478bd9Sstevel@tonic-gate }
28787c478bd9Sstevel@tonic-gate 
28797c478bd9Sstevel@tonic-gate static kmutex_t nfs4_syncbusy;
28807c478bd9Sstevel@tonic-gate 
28817c478bd9Sstevel@tonic-gate /*
28827c478bd9Sstevel@tonic-gate  * Flush dirty nfs files for file system vfsp.
28837c478bd9Sstevel@tonic-gate  * If vfsp == NULL, all nfs files are flushed.
28847c478bd9Sstevel@tonic-gate  *
28857c478bd9Sstevel@tonic-gate  * SYNC_CLOSE in flag is passed to us to
28867c478bd9Sstevel@tonic-gate  * indicate that we are shutting down and or
28877c478bd9Sstevel@tonic-gate  * rebooting.
28887c478bd9Sstevel@tonic-gate  */
28897c478bd9Sstevel@tonic-gate static int
28907c478bd9Sstevel@tonic-gate nfs4_sync(vfs_t *vfsp, short flag, cred_t *cr)
28917c478bd9Sstevel@tonic-gate {
28927c478bd9Sstevel@tonic-gate 	/*
28937c478bd9Sstevel@tonic-gate 	 * Cross-zone calls are OK here, since this translates to a
28947c478bd9Sstevel@tonic-gate 	 * VOP_PUTPAGE(B_ASYNC), which gets picked up by the right zone.
28957c478bd9Sstevel@tonic-gate 	 */
28967c478bd9Sstevel@tonic-gate 	if (!(flag & SYNC_ATTR) && mutex_tryenter(&nfs4_syncbusy) != 0) {
28977c478bd9Sstevel@tonic-gate 		r4flush(vfsp, cr);
28987c478bd9Sstevel@tonic-gate 		mutex_exit(&nfs4_syncbusy);
28997c478bd9Sstevel@tonic-gate 	}
29007c478bd9Sstevel@tonic-gate 
29017c478bd9Sstevel@tonic-gate 	/*
29027c478bd9Sstevel@tonic-gate 	 * if SYNC_CLOSE is set then we know that
29037c478bd9Sstevel@tonic-gate 	 * the system is rebooting, mark the mntinfo
29047c478bd9Sstevel@tonic-gate 	 * for later examination.
29057c478bd9Sstevel@tonic-gate 	 */
29067c478bd9Sstevel@tonic-gate 	if (vfsp && (flag & SYNC_CLOSE)) {
29077c478bd9Sstevel@tonic-gate 		mntinfo4_t *mi;
29087c478bd9Sstevel@tonic-gate 
29097c478bd9Sstevel@tonic-gate 		mi = VFTOMI4(vfsp);
29107c478bd9Sstevel@tonic-gate 		if (!(mi->mi_flags & MI4_SHUTDOWN)) {
29117c478bd9Sstevel@tonic-gate 			mutex_enter(&mi->mi_lock);
29127c478bd9Sstevel@tonic-gate 			mi->mi_flags |= MI4_SHUTDOWN;
29137c478bd9Sstevel@tonic-gate 			mutex_exit(&mi->mi_lock);
29147c478bd9Sstevel@tonic-gate 		}
29157c478bd9Sstevel@tonic-gate 	}
29167c478bd9Sstevel@tonic-gate 	return (0);
29177c478bd9Sstevel@tonic-gate }
29187c478bd9Sstevel@tonic-gate 
29197c478bd9Sstevel@tonic-gate /*
29207c478bd9Sstevel@tonic-gate  * vget is difficult, if not impossible, to support in v4 because we don't
29217c478bd9Sstevel@tonic-gate  * know the parent directory or name, which makes it impossible to create a
29227c478bd9Sstevel@tonic-gate  * useful shadow vnode.  And we need the shadow vnode for things like
29237c478bd9Sstevel@tonic-gate  * OPEN.
29247c478bd9Sstevel@tonic-gate  */
29257c478bd9Sstevel@tonic-gate 
29267c478bd9Sstevel@tonic-gate /* ARGSUSED */
29277c478bd9Sstevel@tonic-gate /*
29287c478bd9Sstevel@tonic-gate  * XXX Check nfs4_vget_pseudo() for dependency.
29297c478bd9Sstevel@tonic-gate  */
29307c478bd9Sstevel@tonic-gate static int
29317c478bd9Sstevel@tonic-gate nfs4_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
29327c478bd9Sstevel@tonic-gate {
29337c478bd9Sstevel@tonic-gate 	return (EREMOTE);
29347c478bd9Sstevel@tonic-gate }
29357c478bd9Sstevel@tonic-gate 
29367c478bd9Sstevel@tonic-gate /*
29377c478bd9Sstevel@tonic-gate  * nfs4_mountroot get called in the case where we are diskless booting.  All
29387c478bd9Sstevel@tonic-gate  * we need from here is the ability to get the server info and from there we
29397c478bd9Sstevel@tonic-gate  * can simply call nfs4_rootvp.
29407c478bd9Sstevel@tonic-gate  */
29417c478bd9Sstevel@tonic-gate /* ARGSUSED */
29427c478bd9Sstevel@tonic-gate static int
29437c478bd9Sstevel@tonic-gate nfs4_mountroot(vfs_t *vfsp, whymountroot_t why)
29447c478bd9Sstevel@tonic-gate {
29457c478bd9Sstevel@tonic-gate 	vnode_t *rtvp;
29467c478bd9Sstevel@tonic-gate 	char root_hostname[SYS_NMLN+1];
29477c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp;
29487c478bd9Sstevel@tonic-gate 	int error;
29497c478bd9Sstevel@tonic-gate 	int vfsflags;
29507c478bd9Sstevel@tonic-gate 	size_t size;
29517c478bd9Sstevel@tonic-gate 	char *root_path;
29527c478bd9Sstevel@tonic-gate 	struct pathname pn;
29537c478bd9Sstevel@tonic-gate 	char *name;
29547c478bd9Sstevel@tonic-gate 	cred_t *cr;
29557c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
29567c478bd9Sstevel@tonic-gate 	struct nfs_args args;		/* nfs mount arguments */
29577c478bd9Sstevel@tonic-gate 	static char token[10];
29587c478bd9Sstevel@tonic-gate 	nfs4_error_t n4e;
29597c478bd9Sstevel@tonic-gate 
29607c478bd9Sstevel@tonic-gate 	bzero(&args, sizeof (args));
29617c478bd9Sstevel@tonic-gate 
29627c478bd9Sstevel@tonic-gate 	/* do this BEFORE getfile which causes xid stamps to be initialized */
29637c478bd9Sstevel@tonic-gate 	clkset(-1L);		/* hack for now - until we get time svc? */
29647c478bd9Sstevel@tonic-gate 
29657c478bd9Sstevel@tonic-gate 	if (why == ROOT_REMOUNT) {
29667c478bd9Sstevel@tonic-gate 		/*
29677c478bd9Sstevel@tonic-gate 		 * Shouldn't happen.
29687c478bd9Sstevel@tonic-gate 		 */
29697c478bd9Sstevel@tonic-gate 		panic("nfs4_mountroot: why == ROOT_REMOUNT");
29707c478bd9Sstevel@tonic-gate 	}
29717c478bd9Sstevel@tonic-gate 
29727c478bd9Sstevel@tonic-gate 	if (why == ROOT_UNMOUNT) {
29737c478bd9Sstevel@tonic-gate 		/*
29747c478bd9Sstevel@tonic-gate 		 * Nothing to do for NFS.
29757c478bd9Sstevel@tonic-gate 		 */
29767c478bd9Sstevel@tonic-gate 		return (0);
29777c478bd9Sstevel@tonic-gate 	}
29787c478bd9Sstevel@tonic-gate 
29797c478bd9Sstevel@tonic-gate 	/*
29807c478bd9Sstevel@tonic-gate 	 * why == ROOT_INIT
29817c478bd9Sstevel@tonic-gate 	 */
29827c478bd9Sstevel@tonic-gate 
29837c478bd9Sstevel@tonic-gate 	name = token;
29847c478bd9Sstevel@tonic-gate 	*name = 0;
29857c478bd9Sstevel@tonic-gate 	(void) getfsname("root", name, sizeof (token));
29867c478bd9Sstevel@tonic-gate 
29877c478bd9Sstevel@tonic-gate 	pn_alloc(&pn);
29887c478bd9Sstevel@tonic-gate 	root_path = pn.pn_path;
29897c478bd9Sstevel@tonic-gate 
29907c478bd9Sstevel@tonic-gate 	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
29917c478bd9Sstevel@tonic-gate 	nfs_rw_init(&svp->sv_lock, NULL, RW_DEFAULT, NULL);
29927c478bd9Sstevel@tonic-gate 	svp->sv_knconf = kmem_zalloc(sizeof (*svp->sv_knconf), KM_SLEEP);
29937c478bd9Sstevel@tonic-gate 	svp->sv_knconf->knc_protofmly = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
29947c478bd9Sstevel@tonic-gate 	svp->sv_knconf->knc_proto = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
29957c478bd9Sstevel@tonic-gate 
29967c478bd9Sstevel@tonic-gate 	/*
29977c478bd9Sstevel@tonic-gate 	 * Get server address
29987c478bd9Sstevel@tonic-gate 	 * Get the root path
29997c478bd9Sstevel@tonic-gate 	 * Get server's transport
30007c478bd9Sstevel@tonic-gate 	 * Get server's hostname
30017c478bd9Sstevel@tonic-gate 	 * Get options
30027c478bd9Sstevel@tonic-gate 	 */
30037c478bd9Sstevel@tonic-gate 	args.addr = &svp->sv_addr;
30047c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
30057c478bd9Sstevel@tonic-gate 	args.fh = (char *)&svp->sv_fhandle;
30067c478bd9Sstevel@tonic-gate 	args.knconf = svp->sv_knconf;
30077c478bd9Sstevel@tonic-gate 	args.hostname = root_hostname;
30087c478bd9Sstevel@tonic-gate 	vfsflags = 0;
30097c478bd9Sstevel@tonic-gate 	if (error = mount_root(*name ? name : "root", root_path, NFS_V4,
30107c478bd9Sstevel@tonic-gate 	    &args, &vfsflags)) {
30117c478bd9Sstevel@tonic-gate 		if (error == EPROTONOSUPPORT)
30127c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN, "nfs4_mountroot: "
30137c478bd9Sstevel@tonic-gate 			    "mount_root failed: server doesn't support NFS V4");
30147c478bd9Sstevel@tonic-gate 		else
30157c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN,
30167c478bd9Sstevel@tonic-gate 			    "nfs4_mountroot: mount_root failed: %m");
30177c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
30187c478bd9Sstevel@tonic-gate 		sv4_free(svp);
30197c478bd9Sstevel@tonic-gate 		pn_free(&pn);
30207c478bd9Sstevel@tonic-gate 		return (error);
30217c478bd9Sstevel@tonic-gate 	}
30227c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
30237c478bd9Sstevel@tonic-gate 	svp->sv_hostnamelen = (int)(strlen(root_hostname) + 1);
30247c478bd9Sstevel@tonic-gate 	svp->sv_hostname = kmem_alloc(svp->sv_hostnamelen, KM_SLEEP);
30257c478bd9Sstevel@tonic-gate 	(void) strcpy(svp->sv_hostname, root_hostname);
30267c478bd9Sstevel@tonic-gate 
30277c478bd9Sstevel@tonic-gate 	svp->sv_pathlen = (int)(strlen(root_path) + 1);
30287c478bd9Sstevel@tonic-gate 	svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP);
30297c478bd9Sstevel@tonic-gate 	(void) strcpy(svp->sv_path, root_path);
30307c478bd9Sstevel@tonic-gate 
30317c478bd9Sstevel@tonic-gate 	/*
30327c478bd9Sstevel@tonic-gate 	 * Force root partition to always be mounted with AUTH_UNIX for now
30337c478bd9Sstevel@tonic-gate 	 */
30347c478bd9Sstevel@tonic-gate 	svp->sv_secdata = kmem_alloc(sizeof (*svp->sv_secdata), KM_SLEEP);
30357c478bd9Sstevel@tonic-gate 	svp->sv_secdata->secmod = AUTH_UNIX;
30367c478bd9Sstevel@tonic-gate 	svp->sv_secdata->rpcflavor = AUTH_UNIX;
30377c478bd9Sstevel@tonic-gate 	svp->sv_secdata->data = NULL;
30387c478bd9Sstevel@tonic-gate 
30397c478bd9Sstevel@tonic-gate 	cr = crgetcred();
30407c478bd9Sstevel@tonic-gate 	rtvp = NULL;
30417c478bd9Sstevel@tonic-gate 
30427c478bd9Sstevel@tonic-gate 	error = nfs4rootvp(&rtvp, vfsp, svp, args.flags, cr, global_zone);
30437c478bd9Sstevel@tonic-gate 
30447c478bd9Sstevel@tonic-gate 	if (error) {
30457c478bd9Sstevel@tonic-gate 		crfree(cr);
30467c478bd9Sstevel@tonic-gate 		pn_free(&pn);
3047ab7762b6Smaheshvs 		sv4_free(svp);
3048ab7762b6Smaheshvs 		return (error);
30497c478bd9Sstevel@tonic-gate 	}
30507c478bd9Sstevel@tonic-gate 
30517c478bd9Sstevel@tonic-gate 	mi = VTOMI4(rtvp);
30527c478bd9Sstevel@tonic-gate 
30537c478bd9Sstevel@tonic-gate 	/*
30547c478bd9Sstevel@tonic-gate 	 * Send client id to the server, if necessary
30557c478bd9Sstevel@tonic-gate 	 */
30567c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(&n4e);
30577c478bd9Sstevel@tonic-gate 	nfs4setclientid(mi, cr, FALSE, &n4e);
30587c478bd9Sstevel@tonic-gate 	error = n4e.error;
30597c478bd9Sstevel@tonic-gate 
30607c478bd9Sstevel@tonic-gate 	crfree(cr);
30617c478bd9Sstevel@tonic-gate 
30627c478bd9Sstevel@tonic-gate 	if (error) {
30637c478bd9Sstevel@tonic-gate 		pn_free(&pn);
30647c478bd9Sstevel@tonic-gate 		goto errout;
30657c478bd9Sstevel@tonic-gate 	}
30667c478bd9Sstevel@tonic-gate 
30677c478bd9Sstevel@tonic-gate 	error = nfs4_setopts(rtvp, DATAMODEL_NATIVE, &args);
30687c478bd9Sstevel@tonic-gate 	if (error) {
30697c478bd9Sstevel@tonic-gate 		nfs_cmn_err(error, CE_WARN,
30707c478bd9Sstevel@tonic-gate 		    "nfs4_mountroot: invalid root mount options");
30717c478bd9Sstevel@tonic-gate 		pn_free(&pn);
30727c478bd9Sstevel@tonic-gate 		goto errout;
30737c478bd9Sstevel@tonic-gate 	}
30747c478bd9Sstevel@tonic-gate 
30757c478bd9Sstevel@tonic-gate 	(void) vfs_lock_wait(vfsp);
30767c478bd9Sstevel@tonic-gate 	vfs_add(NULL, vfsp, vfsflags);
30777c478bd9Sstevel@tonic-gate 	vfs_unlock(vfsp);
30787c478bd9Sstevel@tonic-gate 
30797c478bd9Sstevel@tonic-gate 	size = strlen(svp->sv_hostname);
30807c478bd9Sstevel@tonic-gate 	(void) strcpy(rootfs.bo_name, svp->sv_hostname);
30817c478bd9Sstevel@tonic-gate 	rootfs.bo_name[size] = ':';
30827c478bd9Sstevel@tonic-gate 	(void) strcpy(&rootfs.bo_name[size + 1], root_path);
30837c478bd9Sstevel@tonic-gate 
30847c478bd9Sstevel@tonic-gate 	pn_free(&pn);
30857c478bd9Sstevel@tonic-gate 
30867c478bd9Sstevel@tonic-gate errout:
30877c478bd9Sstevel@tonic-gate 	if (error) {
30887c478bd9Sstevel@tonic-gate 		sv4_free(svp);
30897c478bd9Sstevel@tonic-gate 		nfs4_async_stop(vfsp);
30907c478bd9Sstevel@tonic-gate 		nfs4_async_manager_stop(vfsp);
30917c478bd9Sstevel@tonic-gate 	}
30927c478bd9Sstevel@tonic-gate 
30937c478bd9Sstevel@tonic-gate 	if (rtvp != NULL)
30947c478bd9Sstevel@tonic-gate 		VN_RELE(rtvp);
30957c478bd9Sstevel@tonic-gate 
30967c478bd9Sstevel@tonic-gate 	return (error);
30977c478bd9Sstevel@tonic-gate }
30987c478bd9Sstevel@tonic-gate 
30997c478bd9Sstevel@tonic-gate /*
31007c478bd9Sstevel@tonic-gate  * Initialization routine for VFS routines.  Should only be called once
31017c478bd9Sstevel@tonic-gate  */
31027c478bd9Sstevel@tonic-gate int
31037c478bd9Sstevel@tonic-gate nfs4_vfsinit(void)
31047c478bd9Sstevel@tonic-gate {
31057c478bd9Sstevel@tonic-gate 	mutex_init(&nfs4_syncbusy, NULL, MUTEX_DEFAULT, NULL);
31067c478bd9Sstevel@tonic-gate 	nfs4setclientid_init();
3107b9238976Sth 	nfs4_ephemeral_init();
31087c478bd9Sstevel@tonic-gate 	return (0);
31097c478bd9Sstevel@tonic-gate }
31107c478bd9Sstevel@tonic-gate 
31117c478bd9Sstevel@tonic-gate void
31127c478bd9Sstevel@tonic-gate nfs4_vfsfini(void)
31137c478bd9Sstevel@tonic-gate {
3114b9238976Sth 	nfs4_ephemeral_fini();
31157c478bd9Sstevel@tonic-gate 	nfs4setclientid_fini();
31167c478bd9Sstevel@tonic-gate 	mutex_destroy(&nfs4_syncbusy);
31177c478bd9Sstevel@tonic-gate }
31187c478bd9Sstevel@tonic-gate 
31197c478bd9Sstevel@tonic-gate void
31207c478bd9Sstevel@tonic-gate nfs4_freevfs(vfs_t *vfsp)
31217c478bd9Sstevel@tonic-gate {
31227c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
31237c478bd9Sstevel@tonic-gate 
312450a83466Sjwahlig 	/* need to release the initial hold */
31257c478bd9Sstevel@tonic-gate 	mi = VFTOMI4(vfsp);
3126b87f76edSThomas Haynes 
3127b87f76edSThomas Haynes 	/*
3128b87f76edSThomas Haynes 	 * At this point, we can no longer reference the vfs
3129b87f76edSThomas Haynes 	 * and need to inform other holders of the reference
3130b87f76edSThomas Haynes 	 * to the mntinfo4_t.
3131b87f76edSThomas Haynes 	 */
3132b87f76edSThomas Haynes 	mi->mi_vfsp = NULL;
3133b87f76edSThomas Haynes 
313450a83466Sjwahlig 	MI4_RELE(mi);
31357c478bd9Sstevel@tonic-gate }
31367c478bd9Sstevel@tonic-gate 
31377c478bd9Sstevel@tonic-gate /*
31387c478bd9Sstevel@tonic-gate  * Client side SETCLIENTID and SETCLIENTID_CONFIRM
31397c478bd9Sstevel@tonic-gate  */
31407c478bd9Sstevel@tonic-gate struct nfs4_server nfs4_server_lst =
31417c478bd9Sstevel@tonic-gate 	{ &nfs4_server_lst, &nfs4_server_lst };
31427c478bd9Sstevel@tonic-gate 
31437c478bd9Sstevel@tonic-gate kmutex_t nfs4_server_lst_lock;
31447c478bd9Sstevel@tonic-gate 
31457c478bd9Sstevel@tonic-gate static void
31467c478bd9Sstevel@tonic-gate nfs4setclientid_init(void)
31477c478bd9Sstevel@tonic-gate {
31487c478bd9Sstevel@tonic-gate 	mutex_init(&nfs4_server_lst_lock, NULL, MUTEX_DEFAULT, NULL);
31497c478bd9Sstevel@tonic-gate }
31507c478bd9Sstevel@tonic-gate 
31517c478bd9Sstevel@tonic-gate static void
31527c478bd9Sstevel@tonic-gate nfs4setclientid_fini(void)
31537c478bd9Sstevel@tonic-gate {
31547c478bd9Sstevel@tonic-gate 	mutex_destroy(&nfs4_server_lst_lock);
31557c478bd9Sstevel@tonic-gate }
31567c478bd9Sstevel@tonic-gate 
31577c478bd9Sstevel@tonic-gate int nfs4_retry_sclid_delay = NFS4_RETRY_SCLID_DELAY;
31587c478bd9Sstevel@tonic-gate int nfs4_num_sclid_retries = NFS4_NUM_SCLID_RETRIES;
31597c478bd9Sstevel@tonic-gate 
31607c478bd9Sstevel@tonic-gate /*
31617c478bd9Sstevel@tonic-gate  * Set the clientid for the server for "mi".  No-op if the clientid is
31627c478bd9Sstevel@tonic-gate  * already set.
31637c478bd9Sstevel@tonic-gate  *
31647c478bd9Sstevel@tonic-gate  * The recovery boolean should be set to TRUE if this function was called
3165a092743bSek  * by the recovery code, and FALSE otherwise.  This is used to determine
3166a092743bSek  * if we need to call nfs4_start/end_op as well as grab the mi_recovlock
3167a092743bSek  * for adding a mntinfo4_t to a nfs4_server_t.
31687c478bd9Sstevel@tonic-gate  *
31697c478bd9Sstevel@tonic-gate  * Error is returned via 'n4ep'.  If there was a 'n4ep->stat' error, then
31707c478bd9Sstevel@tonic-gate  * 'n4ep->error' is set to geterrno4(n4ep->stat).
31717c478bd9Sstevel@tonic-gate  */
31727c478bd9Sstevel@tonic-gate void
31737c478bd9Sstevel@tonic-gate nfs4setclientid(mntinfo4_t *mi, cred_t *cr, bool_t recovery, nfs4_error_t *n4ep)
31747c478bd9Sstevel@tonic-gate {
31757c478bd9Sstevel@tonic-gate 	struct nfs4_server *np;
31767c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp = mi->mi_curr_serv;
31777c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
31787c478bd9Sstevel@tonic-gate 	int num_retries = 0;
3179f64c4ae1Sdm 	bool_t retry;
31807c478bd9Sstevel@tonic-gate 	cred_t *lcr = NULL;
31817c478bd9Sstevel@tonic-gate 	int retry_inuse = 1; /* only retry once on NFS4ERR_CLID_INUSE */
31827c478bd9Sstevel@tonic-gate 	time_t lease_time = 0;
31837c478bd9Sstevel@tonic-gate 
31847c478bd9Sstevel@tonic-gate 	recov_state.rs_flags = 0;
31857c478bd9Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
31867c478bd9Sstevel@tonic-gate 	ASSERT(n4ep != NULL);
31877c478bd9Sstevel@tonic-gate 
31887c478bd9Sstevel@tonic-gate recov_retry:
3189f64c4ae1Sdm 	retry = FALSE;
31907c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(n4ep);
3191a092743bSek 	if (!recovery)
3192a092743bSek 		(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, 0);
3193a092743bSek 
3194f86c6ccaSdm 	mutex_enter(&nfs4_server_lst_lock);
3195f86c6ccaSdm 	np = servinfo4_to_nfs4_server(svp); /* This locks np if it is found */
3196f86c6ccaSdm 	mutex_exit(&nfs4_server_lst_lock);
3197f86c6ccaSdm 	if (!np) {
3198f86c6ccaSdm 		struct nfs4_server *tnp;
3199f86c6ccaSdm 		np = new_nfs4_server(svp, cr);
320016237317Sdm 		mutex_enter(&np->s_lock);
32017c478bd9Sstevel@tonic-gate 
3202f86c6ccaSdm 		mutex_enter(&nfs4_server_lst_lock);
3203f86c6ccaSdm 		tnp = servinfo4_to_nfs4_server(svp);
3204f86c6ccaSdm 		if (tnp) {
3205f86c6ccaSdm 			/*
3206f86c6ccaSdm 			 * another thread snuck in and put server on list.
3207f86c6ccaSdm 			 * since we aren't adding it to the nfs4_server_list
3208f86c6ccaSdm 			 * we need to set the ref count to 0 and destroy it.
3209f86c6ccaSdm 			 */
3210f86c6ccaSdm 			np->s_refcnt = 0;
3211f86c6ccaSdm 			destroy_nfs4_server(np);
3212f86c6ccaSdm 			np = tnp;
3213f86c6ccaSdm 		} else {
3214f86c6ccaSdm 			/*
3215f86c6ccaSdm 			 * do not give list a reference until everything
3216f86c6ccaSdm 			 * succeeds
3217f86c6ccaSdm 			 */
3218f86c6ccaSdm 			insque(np, &nfs4_server_lst);
3219f86c6ccaSdm 		}
3220f86c6ccaSdm 		mutex_exit(&nfs4_server_lst_lock);
3221f86c6ccaSdm 	}
3222f86c6ccaSdm 	ASSERT(MUTEX_HELD(&np->s_lock));
32237c478bd9Sstevel@tonic-gate 	/*
3224f86c6ccaSdm 	 * If we find the server already has N4S_CLIENTID_SET, then
3225f86c6ccaSdm 	 * just return, we've already done SETCLIENTID to that server
32267c478bd9Sstevel@tonic-gate 	 */
3227f86c6ccaSdm 	if (np->s_flags & N4S_CLIENTID_SET) {
32287c478bd9Sstevel@tonic-gate 		/* add mi to np's mntinfo4_list */
32297c478bd9Sstevel@tonic-gate 		nfs4_add_mi_to_server(np, mi);
3230a092743bSek 		if (!recovery)
3231a092743bSek 			nfs_rw_exit(&mi->mi_recovlock);
32327c478bd9Sstevel@tonic-gate 		mutex_exit(&np->s_lock);
32337c478bd9Sstevel@tonic-gate 		nfs4_server_rele(np);
32347c478bd9Sstevel@tonic-gate 		return;
32357c478bd9Sstevel@tonic-gate 	}
3236f86c6ccaSdm 	mutex_exit(&np->s_lock);
3237f86c6ccaSdm 
32387c478bd9Sstevel@tonic-gate 
3239a092743bSek 	/*
3240a092743bSek 	 * Drop the mi_recovlock since nfs4_start_op will
3241a092743bSek 	 * acquire it again for us.
3242a092743bSek 	 */
3243f86c6ccaSdm 	if (!recovery) {
3244a092743bSek 		nfs_rw_exit(&mi->mi_recovlock);
3245a092743bSek 
32467c478bd9Sstevel@tonic-gate 		n4ep->error = nfs4_start_op(mi, NULL, NULL, &recov_state);
32477c478bd9Sstevel@tonic-gate 		if (n4ep->error) {
32487c478bd9Sstevel@tonic-gate 			nfs4_server_rele(np);
32497c478bd9Sstevel@tonic-gate 			return;
32507c478bd9Sstevel@tonic-gate 		}
32517c478bd9Sstevel@tonic-gate 	}
32527c478bd9Sstevel@tonic-gate 
32537c478bd9Sstevel@tonic-gate 	mutex_enter(&np->s_lock);
3254f86c6ccaSdm 	while (np->s_flags & N4S_CLIENTID_PEND) {
3255f86c6ccaSdm 		if (!cv_wait_sig(&np->s_clientid_pend, &np->s_lock)) {
3256f86c6ccaSdm 			mutex_exit(&np->s_lock);
3257f86c6ccaSdm 			nfs4_server_rele(np);
3258f86c6ccaSdm 			if (!recovery)
3259f86c6ccaSdm 				nfs4_end_op(mi, NULL, NULL, &recov_state,
3260f86c6ccaSdm 				    recovery);
3261f86c6ccaSdm 			n4ep->error = EINTR;
3262f86c6ccaSdm 			return;
3263f86c6ccaSdm 		}
3264f86c6ccaSdm 	}
32657c478bd9Sstevel@tonic-gate 
32667c478bd9Sstevel@tonic-gate 	if (np->s_flags & N4S_CLIENTID_SET) {
32677c478bd9Sstevel@tonic-gate 		/* XXX copied/pasted from above */
32687c478bd9Sstevel@tonic-gate 		/* add mi to np's mntinfo4_list */
32697c478bd9Sstevel@tonic-gate 		nfs4_add_mi_to_server(np, mi);
32707c478bd9Sstevel@tonic-gate 		mutex_exit(&np->s_lock);
32717c478bd9Sstevel@tonic-gate 		nfs4_server_rele(np);
32727c478bd9Sstevel@tonic-gate 		if (!recovery)
32737c478bd9Sstevel@tonic-gate 			nfs4_end_op(mi, NULL, NULL, &recov_state, recovery);
32747c478bd9Sstevel@tonic-gate 		return;
32757c478bd9Sstevel@tonic-gate 	}
32767c478bd9Sstevel@tonic-gate 
3277f86c6ccaSdm 	/*
3278f86c6ccaSdm 	 * Reset the N4S_CB_PINGED flag. This is used to
3279f86c6ccaSdm 	 * indicate if we have received a CB_NULL from the
3280f86c6ccaSdm 	 * server. Also we reset the waiter flag.
3281f86c6ccaSdm 	 */
3282f86c6ccaSdm 	np->s_flags &= ~(N4S_CB_PINGED | N4S_CB_WAITER);
3283f86c6ccaSdm 	/* any failure must now clear this flag */
3284f86c6ccaSdm 	np->s_flags |= N4S_CLIENTID_PEND;
3285f86c6ccaSdm 	mutex_exit(&np->s_lock);
32867c478bd9Sstevel@tonic-gate 	nfs4setclientid_otw(mi, svp, cr, np, n4ep, &retry_inuse);
32877c478bd9Sstevel@tonic-gate 
32887c478bd9Sstevel@tonic-gate 	if (n4ep->error == EACCES) {
32897c478bd9Sstevel@tonic-gate 		/*
32907c478bd9Sstevel@tonic-gate 		 * If the uid is set then set the creds for secure mounts
32917c478bd9Sstevel@tonic-gate 		 * by proxy processes such as automountd.
32927c478bd9Sstevel@tonic-gate 		 */
32937c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
32947c478bd9Sstevel@tonic-gate 		if (svp->sv_secdata->uid != 0) {
32957c478bd9Sstevel@tonic-gate 			lcr = crdup(cr);
32967c478bd9Sstevel@tonic-gate 			(void) crsetugid(lcr, svp->sv_secdata->uid,
32977c478bd9Sstevel@tonic-gate 			    crgetgid(cr));
32987c478bd9Sstevel@tonic-gate 		}
32997c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
33007c478bd9Sstevel@tonic-gate 
3301f86c6ccaSdm 		if (lcr != NULL) {
3302f86c6ccaSdm 			mutex_enter(&np->s_lock);
3303f86c6ccaSdm 			crfree(np->s_cred);
3304f86c6ccaSdm 			np->s_cred = lcr;
3305f86c6ccaSdm 			mutex_exit(&np->s_lock);
33067c478bd9Sstevel@tonic-gate 			nfs4setclientid_otw(mi, svp, lcr, np, n4ep,
3307b9238976Sth 			    &retry_inuse);
3308f86c6ccaSdm 		}
33097c478bd9Sstevel@tonic-gate 	}
3310f86c6ccaSdm 	mutex_enter(&np->s_lock);
33117c478bd9Sstevel@tonic-gate 	lease_time = np->s_lease_time;
3312f86c6ccaSdm 	np->s_flags &= ~N4S_CLIENTID_PEND;
33137c478bd9Sstevel@tonic-gate 	mutex_exit(&np->s_lock);
33147c478bd9Sstevel@tonic-gate 
33157c478bd9Sstevel@tonic-gate 	if (n4ep->error != 0 || n4ep->stat != NFS4_OK) {
33167c478bd9Sstevel@tonic-gate 		/*
33177c478bd9Sstevel@tonic-gate 		 * Start recovery if failover is a possibility.  If
33187c478bd9Sstevel@tonic-gate 		 * invoked by the recovery thread itself, then just
33197c478bd9Sstevel@tonic-gate 		 * return and let it handle the failover first.  NB:
33207c478bd9Sstevel@tonic-gate 		 * recovery is not allowed if the mount is in progress
33217c478bd9Sstevel@tonic-gate 		 * since the infrastructure is not sufficiently setup
33227c478bd9Sstevel@tonic-gate 		 * to allow it.  Just return the error (after suitable
33237c478bd9Sstevel@tonic-gate 		 * retries).
33247c478bd9Sstevel@tonic-gate 		 */
33257c478bd9Sstevel@tonic-gate 		if (FAILOVER_MOUNT4(mi) && nfs4_try_failover(n4ep)) {
33267c478bd9Sstevel@tonic-gate 			(void) nfs4_start_recovery(n4ep, mi, NULL,
33272f172c55SRobert Thurlow 			    NULL, NULL, NULL, OP_SETCLIENTID, NULL, NULL, NULL);
33287c478bd9Sstevel@tonic-gate 			/*
33297c478bd9Sstevel@tonic-gate 			 * Don't retry here, just return and let
33307c478bd9Sstevel@tonic-gate 			 * recovery take over.
33317c478bd9Sstevel@tonic-gate 			 */
33327c478bd9Sstevel@tonic-gate 			if (recovery)
33337c478bd9Sstevel@tonic-gate 				retry = FALSE;
33347c478bd9Sstevel@tonic-gate 		} else if (nfs4_rpc_retry_error(n4ep->error) ||
3335b9238976Sth 		    n4ep->stat == NFS4ERR_RESOURCE ||
3336b9238976Sth 		    n4ep->stat == NFS4ERR_STALE_CLIENTID) {
33377c478bd9Sstevel@tonic-gate 
3338b9238976Sth 			retry = TRUE;
3339b9238976Sth 			/*
3340b9238976Sth 			 * Always retry if in recovery or once had
3341b9238976Sth 			 * contact with the server (but now it's
3342b9238976Sth 			 * overloaded).
3343b9238976Sth 			 */
3344b9238976Sth 			if (recovery == TRUE ||
3345b9238976Sth 			    n4ep->error == ETIMEDOUT ||
3346b9238976Sth 			    n4ep->error == ECONNRESET)
33477c478bd9Sstevel@tonic-gate 				num_retries = 0;
3348b9238976Sth 		} else if (retry_inuse && n4ep->error == 0 &&
3349b9238976Sth 		    n4ep->stat == NFS4ERR_CLID_INUSE) {
3350b9238976Sth 			retry = TRUE;
3351b9238976Sth 			num_retries = 0;
33527c478bd9Sstevel@tonic-gate 		}
3353f86c6ccaSdm 	} else {
3354f64c4ae1Sdm 		/*
3355f64c4ae1Sdm 		 * Since everything succeeded give the list a reference count if
3356f64c4ae1Sdm 		 * it hasn't been given one by add_new_nfs4_server() or if this
3357f64c4ae1Sdm 		 * is not a recovery situation in which case it is already on
3358f64c4ae1Sdm 		 * the list.
3359f64c4ae1Sdm 		 */
3360f86c6ccaSdm 		mutex_enter(&np->s_lock);
3361f64c4ae1Sdm 		if ((np->s_flags & N4S_INSERTED) == 0) {
3362f64c4ae1Sdm 			np->s_refcnt++;
3363f64c4ae1Sdm 			np->s_flags |= N4S_INSERTED;
3364f64c4ae1Sdm 		}
3365f86c6ccaSdm 		mutex_exit(&np->s_lock);
33667c478bd9Sstevel@tonic-gate 	}
33677c478bd9Sstevel@tonic-gate 
33687c478bd9Sstevel@tonic-gate 	if (!recovery)
33697c478bd9Sstevel@tonic-gate 		nfs4_end_op(mi, NULL, NULL, &recov_state, recovery);
3370f86c6ccaSdm 
33717c478bd9Sstevel@tonic-gate 
33727c478bd9Sstevel@tonic-gate 	if (retry && num_retries++ < nfs4_num_sclid_retries) {
33737c478bd9Sstevel@tonic-gate 		if (retry_inuse) {
33747c478bd9Sstevel@tonic-gate 			delay(SEC_TO_TICK(lease_time + nfs4_retry_sclid_delay));
33757c478bd9Sstevel@tonic-gate 			retry_inuse = 0;
33767c478bd9Sstevel@tonic-gate 		} else
33777c478bd9Sstevel@tonic-gate 			delay(SEC_TO_TICK(nfs4_retry_sclid_delay));
3378f86c6ccaSdm 
3379f86c6ccaSdm 		nfs4_server_rele(np);
33807c478bd9Sstevel@tonic-gate 		goto recov_retry;
33817c478bd9Sstevel@tonic-gate 	}
33827c478bd9Sstevel@tonic-gate 
3383f86c6ccaSdm 
33847c478bd9Sstevel@tonic-gate 	if (n4ep->error == 0)
33857c478bd9Sstevel@tonic-gate 		n4ep->error = geterrno4(n4ep->stat);
3386f86c6ccaSdm 
3387f86c6ccaSdm 	/* broadcast before release in case no other threads are waiting */
3388f86c6ccaSdm 	cv_broadcast(&np->s_clientid_pend);
3389f86c6ccaSdm 	nfs4_server_rele(np);
33907c478bd9Sstevel@tonic-gate }
33917c478bd9Sstevel@tonic-gate 
33927c478bd9Sstevel@tonic-gate int nfs4setclientid_otw_debug = 0;
33937c478bd9Sstevel@tonic-gate 
33947c478bd9Sstevel@tonic-gate /*
33957c478bd9Sstevel@tonic-gate  * This function handles the recovery of STALE_CLIENTID for SETCLIENTID_CONFRIM,
33967c478bd9Sstevel@tonic-gate  * but nothing else; the calling function must be designed to handle those
33977c478bd9Sstevel@tonic-gate  * other errors.
33987c478bd9Sstevel@tonic-gate  */
33997c478bd9Sstevel@tonic-gate static void
34007c478bd9Sstevel@tonic-gate nfs4setclientid_otw(mntinfo4_t *mi, struct servinfo4 *svp,  cred_t *cr,
3401b9238976Sth     struct nfs4_server *np, nfs4_error_t *ep, int *retry_inusep)
34027c478bd9Sstevel@tonic-gate {
34037c478bd9Sstevel@tonic-gate 	COMPOUND4args_clnt args;
34047c478bd9Sstevel@tonic-gate 	COMPOUND4res_clnt res;
34057c478bd9Sstevel@tonic-gate 	nfs_argop4 argop[3];
34067c478bd9Sstevel@tonic-gate 	SETCLIENTID4args *s_args;
34077c478bd9Sstevel@tonic-gate 	SETCLIENTID4resok *s_resok;
34087c478bd9Sstevel@tonic-gate 	int doqueue = 1;
34097c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t *garp = NULL;
34107c478bd9Sstevel@tonic-gate 	timespec_t prop_time, after_time;
34117c478bd9Sstevel@tonic-gate 	verifier4 verf;
34127c478bd9Sstevel@tonic-gate 	clientid4 tmp_clientid;
34137c478bd9Sstevel@tonic-gate 
3414f86c6ccaSdm 	ASSERT(!MUTEX_HELD(&np->s_lock));
34157c478bd9Sstevel@tonic-gate 
34167c478bd9Sstevel@tonic-gate 	args.ctag = TAG_SETCLIENTID;
34177c478bd9Sstevel@tonic-gate 
34187c478bd9Sstevel@tonic-gate 	args.array = argop;
34197c478bd9Sstevel@tonic-gate 	args.array_len = 3;
34207c478bd9Sstevel@tonic-gate 
34217c478bd9Sstevel@tonic-gate 	/* PUTROOTFH */
34227c478bd9Sstevel@tonic-gate 	argop[0].argop = OP_PUTROOTFH;
34237c478bd9Sstevel@tonic-gate 
34247c478bd9Sstevel@tonic-gate 	/* GETATTR */
34257c478bd9Sstevel@tonic-gate 	argop[1].argop = OP_GETATTR;
34267c478bd9Sstevel@tonic-gate 	argop[1].nfs_argop4_u.opgetattr.attr_request = FATTR4_LEASE_TIME_MASK;
34277c478bd9Sstevel@tonic-gate 	argop[1].nfs_argop4_u.opgetattr.mi = mi;
34287c478bd9Sstevel@tonic-gate 
34297c478bd9Sstevel@tonic-gate 	/* SETCLIENTID */
34307c478bd9Sstevel@tonic-gate 	argop[2].argop = OP_SETCLIENTID;
34317c478bd9Sstevel@tonic-gate 
34327c478bd9Sstevel@tonic-gate 	s_args = &argop[2].nfs_argop4_u.opsetclientid;
34337c478bd9Sstevel@tonic-gate 
3434f86c6ccaSdm 	mutex_enter(&np->s_lock);
3435f86c6ccaSdm 
34367c478bd9Sstevel@tonic-gate 	s_args->client.verifier = np->clidtosend.verifier;
34377c478bd9Sstevel@tonic-gate 	s_args->client.id_len = np->clidtosend.id_len;
34387c478bd9Sstevel@tonic-gate 	ASSERT(s_args->client.id_len <= NFS4_OPAQUE_LIMIT);
34397c478bd9Sstevel@tonic-gate 	s_args->client.id_val = np->clidtosend.id_val;
34407c478bd9Sstevel@tonic-gate 
34417c478bd9Sstevel@tonic-gate 	/*
34427c478bd9Sstevel@tonic-gate 	 * Callback needs to happen on non-RDMA transport
34437c478bd9Sstevel@tonic-gate 	 * Check if we have saved the original knetconfig
34447c478bd9Sstevel@tonic-gate 	 * if so, use that instead.
34457c478bd9Sstevel@tonic-gate 	 */
34467c478bd9Sstevel@tonic-gate 	if (svp->sv_origknconf != NULL)
34477c478bd9Sstevel@tonic-gate 		nfs4_cb_args(np, svp->sv_origknconf, s_args);
34487c478bd9Sstevel@tonic-gate 	else
34497c478bd9Sstevel@tonic-gate 		nfs4_cb_args(np, svp->sv_knconf, s_args);
34507c478bd9Sstevel@tonic-gate 
3451f86c6ccaSdm 	mutex_exit(&np->s_lock);
3452f86c6ccaSdm 
3453f86c6ccaSdm 	rfs4call(mi, &args, &res, cr, &doqueue, 0, ep);
34547c478bd9Sstevel@tonic-gate 
34557c478bd9Sstevel@tonic-gate 	if (ep->error)
34567c478bd9Sstevel@tonic-gate 		return;
34577c478bd9Sstevel@tonic-gate 
34587c478bd9Sstevel@tonic-gate 	/* getattr lease_time res */
3459e557fb2cSDai Ngo 	if ((res.array_len >= 2) &&
3460e557fb2cSDai Ngo 	    (res.array[1].nfs_resop4_u.opgetattr.status == NFS4_OK)) {
34617c478bd9Sstevel@tonic-gate 		garp = &res.array[1].nfs_resop4_u.opgetattr.ga_res;
34627c478bd9Sstevel@tonic-gate 
34637c478bd9Sstevel@tonic-gate #ifndef _LP64
34647c478bd9Sstevel@tonic-gate 		/*
34657c478bd9Sstevel@tonic-gate 		 * The 32 bit client cannot handle a lease time greater than
34667c478bd9Sstevel@tonic-gate 		 * (INT32_MAX/1000000).  This is due to the use of the
34677c478bd9Sstevel@tonic-gate 		 * lease_time in calls to drv_usectohz() in
34687c478bd9Sstevel@tonic-gate 		 * nfs4_renew_lease_thread().  The problem is that
34697c478bd9Sstevel@tonic-gate 		 * drv_usectohz() takes a time_t (which is just a long = 4
34707c478bd9Sstevel@tonic-gate 		 * bytes) as its parameter.  The lease_time is multiplied by
34717c478bd9Sstevel@tonic-gate 		 * 1000000 to convert seconds to usecs for the parameter.  If
34727c478bd9Sstevel@tonic-gate 		 * a number bigger than (INT32_MAX/1000000) is used then we
34737c478bd9Sstevel@tonic-gate 		 * overflow on the 32bit client.
34747c478bd9Sstevel@tonic-gate 		 */
34757c478bd9Sstevel@tonic-gate 		if (garp->n4g_ext_res->n4g_leasetime > (INT32_MAX/1000000)) {
34767c478bd9Sstevel@tonic-gate 			garp->n4g_ext_res->n4g_leasetime = INT32_MAX/1000000;
34777c478bd9Sstevel@tonic-gate 		}
34787c478bd9Sstevel@tonic-gate #endif
34797c478bd9Sstevel@tonic-gate 
3480f86c6ccaSdm 		mutex_enter(&np->s_lock);
34817c478bd9Sstevel@tonic-gate 		np->s_lease_time = garp->n4g_ext_res->n4g_leasetime;
34827c478bd9Sstevel@tonic-gate 
34837c478bd9Sstevel@tonic-gate 		/*
34847c478bd9Sstevel@tonic-gate 		 * Keep track of the lease period for the mi's
34857c478bd9Sstevel@tonic-gate 		 * mi_msg_list.  We need an appropiate time
34867c478bd9Sstevel@tonic-gate 		 * bound to associate past facts with a current
34877c478bd9Sstevel@tonic-gate 		 * event.  The lease period is perfect for this.
34887c478bd9Sstevel@tonic-gate 		 */
34897c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_msg_list_lock);
34907c478bd9Sstevel@tonic-gate 		mi->mi_lease_period = np->s_lease_time;
34917c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_msg_list_lock);
3492f86c6ccaSdm 		mutex_exit(&np->s_lock);
34937c478bd9Sstevel@tonic-gate 	}
34947c478bd9Sstevel@tonic-gate 
34957c478bd9Sstevel@tonic-gate 
34967c478bd9Sstevel@tonic-gate 	if (res.status == NFS4ERR_CLID_INUSE) {
34977c478bd9Sstevel@tonic-gate 		clientaddr4 *clid_inuse;
34987c478bd9Sstevel@tonic-gate 
34997c478bd9Sstevel@tonic-gate 		if (!(*retry_inusep)) {
35007c478bd9Sstevel@tonic-gate 			clid_inuse = &res.array->nfs_resop4_u.
3501b9238976Sth 			    opsetclientid.SETCLIENTID4res_u.client_using;
35027c478bd9Sstevel@tonic-gate 
35037c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_NOTE,
35047c478bd9Sstevel@tonic-gate 			    "NFS4 mount (SETCLIENTID failed)."
35057c478bd9Sstevel@tonic-gate 			    "  nfs4_client_id.id is in"
35067c478bd9Sstevel@tonic-gate 			    "use already by: r_netid<%s> r_addr<%s>",
35077c478bd9Sstevel@tonic-gate 			    clid_inuse->r_netid, clid_inuse->r_addr);
35087c478bd9Sstevel@tonic-gate 		}
35097c478bd9Sstevel@tonic-gate 
35107c478bd9Sstevel@tonic-gate 		/*
35117c478bd9Sstevel@tonic-gate 		 * XXX - The client should be more robust in its
35127c478bd9Sstevel@tonic-gate 		 * handling of clientid in use errors (regen another
35137c478bd9Sstevel@tonic-gate 		 * clientid and try again?)
35147c478bd9Sstevel@tonic-gate 		 */
35157c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
35167c478bd9Sstevel@tonic-gate 		return;
35177c478bd9Sstevel@tonic-gate 	}
35187c478bd9Sstevel@tonic-gate 
35197c478bd9Sstevel@tonic-gate 	if (res.status) {
35207c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
35217c478bd9Sstevel@tonic-gate 		return;
35227c478bd9Sstevel@tonic-gate 	}
35237c478bd9Sstevel@tonic-gate 
35247c478bd9Sstevel@tonic-gate 	s_resok = &res.array[2].nfs_resop4_u.
3525b9238976Sth 	    opsetclientid.SETCLIENTID4res_u.resok4;
35267c478bd9Sstevel@tonic-gate 
35277c478bd9Sstevel@tonic-gate 	tmp_clientid = s_resok->clientid;
35287c478bd9Sstevel@tonic-gate 
35297c478bd9Sstevel@tonic-gate 	verf = s_resok->setclientid_confirm;
35307c478bd9Sstevel@tonic-gate 
35317c478bd9Sstevel@tonic-gate #ifdef	DEBUG
35327c478bd9Sstevel@tonic-gate 	if (nfs4setclientid_otw_debug) {
35337c478bd9Sstevel@tonic-gate 		union {
35347c478bd9Sstevel@tonic-gate 			clientid4	clientid;
35357c478bd9Sstevel@tonic-gate 			int		foo[2];
35367c478bd9Sstevel@tonic-gate 		} cid;
35377c478bd9Sstevel@tonic-gate 
35387c478bd9Sstevel@tonic-gate 		cid.clientid = s_resok->clientid;
35397c478bd9Sstevel@tonic-gate 
35407c478bd9Sstevel@tonic-gate 		zcmn_err(mi->mi_zone->zone_id, CE_NOTE,
35417c478bd9Sstevel@tonic-gate 		"nfs4setclientid_otw: OK, clientid = %x,%x, "
35427c478bd9Sstevel@tonic-gate 		"verifier = %" PRIx64 "\n", cid.foo[0], cid.foo[1], verf);
35437c478bd9Sstevel@tonic-gate 	}
35447c478bd9Sstevel@tonic-gate #endif
35457c478bd9Sstevel@tonic-gate 
35467c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
35477c478bd9Sstevel@tonic-gate 
35487c478bd9Sstevel@tonic-gate 	/* Confirm the client id and get the lease_time attribute */
35497c478bd9Sstevel@tonic-gate 
35507c478bd9Sstevel@tonic-gate 	args.ctag = TAG_SETCLIENTID_CF;
35517c478bd9Sstevel@tonic-gate 
35527c478bd9Sstevel@tonic-gate 	args.array = argop;
35537c478bd9Sstevel@tonic-gate 	args.array_len = 1;
35547c478bd9Sstevel@tonic-gate 
35557c478bd9Sstevel@tonic-gate 	argop[0].argop = OP_SETCLIENTID_CONFIRM;
35567c478bd9Sstevel@tonic-gate 
35577c478bd9Sstevel@tonic-gate 	argop[0].nfs_argop4_u.opsetclientid_confirm.clientid = tmp_clientid;
35587c478bd9Sstevel@tonic-gate 	argop[0].nfs_argop4_u.opsetclientid_confirm.setclientid_confirm = verf;
35597c478bd9Sstevel@tonic-gate 
35607c478bd9Sstevel@tonic-gate 	/* used to figure out RTT for np */
35617c478bd9Sstevel@tonic-gate 	gethrestime(&prop_time);
35627c478bd9Sstevel@tonic-gate 
35637c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4setlientid_otw: "
3564b9238976Sth 	    "start time: %ld sec %ld nsec", prop_time.tv_sec,
3565b9238976Sth 	    prop_time.tv_nsec));
35667c478bd9Sstevel@tonic-gate 
35677c478bd9Sstevel@tonic-gate 	rfs4call(mi, &args, &res, cr, &doqueue, 0, ep);
35687c478bd9Sstevel@tonic-gate 
35697c478bd9Sstevel@tonic-gate 	gethrestime(&after_time);
3570f86c6ccaSdm 	mutex_enter(&np->s_lock);
35717c478bd9Sstevel@tonic-gate 	np->propagation_delay.tv_sec =
3572b9238976Sth 	    MAX(1, after_time.tv_sec - prop_time.tv_sec);
3573f86c6ccaSdm 	mutex_exit(&np->s_lock);
35747c478bd9Sstevel@tonic-gate 
35757c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4setlcientid_otw: "
3576b9238976Sth 	    "finish time: %ld sec ", after_time.tv_sec));
35777c478bd9Sstevel@tonic-gate 
35787c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4setclientid_otw: "
3579b9238976Sth 	    "propagation delay set to %ld sec",
3580b9238976Sth 	    np->propagation_delay.tv_sec));
35817c478bd9Sstevel@tonic-gate 
35827c478bd9Sstevel@tonic-gate 	if (ep->error)
35837c478bd9Sstevel@tonic-gate 		return;
35847c478bd9Sstevel@tonic-gate 
35857c478bd9Sstevel@tonic-gate 	if (res.status == NFS4ERR_CLID_INUSE) {
35867c478bd9Sstevel@tonic-gate 		clientaddr4 *clid_inuse;
35877c478bd9Sstevel@tonic-gate 
35887c478bd9Sstevel@tonic-gate 		if (!(*retry_inusep)) {
35897c478bd9Sstevel@tonic-gate 			clid_inuse = &res.array->nfs_resop4_u.
3590b9238976Sth 			    opsetclientid.SETCLIENTID4res_u.client_using;
35917c478bd9Sstevel@tonic-gate 
35927c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_NOTE,
35937c478bd9Sstevel@tonic-gate 			    "SETCLIENTID_CONFIRM failed.  "
35947c478bd9Sstevel@tonic-gate 			    "nfs4_client_id.id is in use already by: "
35957c478bd9Sstevel@tonic-gate 			    "r_netid<%s> r_addr<%s>",
35967c478bd9Sstevel@tonic-gate 			    clid_inuse->r_netid, clid_inuse->r_addr);
35977c478bd9Sstevel@tonic-gate 		}
35987c478bd9Sstevel@tonic-gate 
35997c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
36007c478bd9Sstevel@tonic-gate 		return;
36017c478bd9Sstevel@tonic-gate 	}
36027c478bd9Sstevel@tonic-gate 
36037c478bd9Sstevel@tonic-gate 	if (res.status) {
36047c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
36057c478bd9Sstevel@tonic-gate 		return;
36067c478bd9Sstevel@tonic-gate 	}
36077c478bd9Sstevel@tonic-gate 
3608f86c6ccaSdm 	mutex_enter(&np->s_lock);
36097c478bd9Sstevel@tonic-gate 	np->clientid = tmp_clientid;
36107c478bd9Sstevel@tonic-gate 	np->s_flags |= N4S_CLIENTID_SET;
36117c478bd9Sstevel@tonic-gate 
36127c478bd9Sstevel@tonic-gate 	/* Add mi to np's mntinfo4 list */
36137c478bd9Sstevel@tonic-gate 	nfs4_add_mi_to_server(np, mi);
36147c478bd9Sstevel@tonic-gate 
36157c478bd9Sstevel@tonic-gate 	if (np->lease_valid == NFS4_LEASE_NOT_STARTED) {
36167c478bd9Sstevel@tonic-gate 		/*
36177c478bd9Sstevel@tonic-gate 		 * Start lease management thread.
36187c478bd9Sstevel@tonic-gate 		 * Keep trying until we succeed.
36197c478bd9Sstevel@tonic-gate 		 */
36207c478bd9Sstevel@tonic-gate 
36217c478bd9Sstevel@tonic-gate 		np->s_refcnt++;		/* pass reference to thread */
36227c478bd9Sstevel@tonic-gate 		(void) zthread_create(NULL, 0, nfs4_renew_lease_thread, np, 0,
3623b9238976Sth 		    minclsyspri);
36247c478bd9Sstevel@tonic-gate 	}
3625f86c6ccaSdm 	mutex_exit(&np->s_lock);
36267c478bd9Sstevel@tonic-gate 
36277c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
36287c478bd9Sstevel@tonic-gate }
36297c478bd9Sstevel@tonic-gate 
36307c478bd9Sstevel@tonic-gate /*
36317c478bd9Sstevel@tonic-gate  * Add mi to sp's mntinfo4_list if it isn't already in the list.  Makes
36327c478bd9Sstevel@tonic-gate  * mi's clientid the same as sp's.
36337c478bd9Sstevel@tonic-gate  * Assumes sp is locked down.
36347c478bd9Sstevel@tonic-gate  */
36357c478bd9Sstevel@tonic-gate void
36367c478bd9Sstevel@tonic-gate nfs4_add_mi_to_server(nfs4_server_t *sp, mntinfo4_t *mi)
36377c478bd9Sstevel@tonic-gate {
36387c478bd9Sstevel@tonic-gate 	mntinfo4_t *tmi;
36397c478bd9Sstevel@tonic-gate 	int in_list = 0;
36407c478bd9Sstevel@tonic-gate 
3641a092743bSek 	ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) ||
3642a092743bSek 	    nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER));
36437c478bd9Sstevel@tonic-gate 	ASSERT(sp != &nfs4_server_lst);
36447c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
36457c478bd9Sstevel@tonic-gate 
36467c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
3647b9238976Sth 	    "nfs4_add_mi_to_server: add mi %p to sp %p",
3648b9238976Sth 	    (void*)mi, (void*)sp));
36497c478bd9Sstevel@tonic-gate 
36507c478bd9Sstevel@tonic-gate 	for (tmi = sp->mntinfo4_list;
36517c478bd9Sstevel@tonic-gate 	    tmi != NULL;
36527c478bd9Sstevel@tonic-gate 	    tmi = tmi->mi_clientid_next) {
36537c478bd9Sstevel@tonic-gate 		if (tmi == mi) {
36547c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_client_lease_debug,
3655b9238976Sth 			    (CE_NOTE,
3656b9238976Sth 			    "nfs4_add_mi_to_server: mi in list"));
36577c478bd9Sstevel@tonic-gate 			in_list = 1;
36587c478bd9Sstevel@tonic-gate 		}
36597c478bd9Sstevel@tonic-gate 	}
36607c478bd9Sstevel@tonic-gate 
36617c478bd9Sstevel@tonic-gate 	/*
36627c478bd9Sstevel@tonic-gate 	 * First put a hold on the mntinfo4's vfsp so that references via
36637c478bd9Sstevel@tonic-gate 	 * mntinfo4_list will be valid.
36647c478bd9Sstevel@tonic-gate 	 */
36657c478bd9Sstevel@tonic-gate 	if (!in_list)
36667c478bd9Sstevel@tonic-gate 		VFS_HOLD(mi->mi_vfsp);
36677c478bd9Sstevel@tonic-gate 
36687c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4_add_mi_to_server: "
3669b9238976Sth 	    "hold vfs %p for mi: %p", (void*)mi->mi_vfsp, (void*)mi));
36707c478bd9Sstevel@tonic-gate 
36717c478bd9Sstevel@tonic-gate 	if (!in_list) {
36727c478bd9Sstevel@tonic-gate 		if (sp->mntinfo4_list)
36737c478bd9Sstevel@tonic-gate 			sp->mntinfo4_list->mi_clientid_prev = mi;
36747c478bd9Sstevel@tonic-gate 		mi->mi_clientid_next = sp->mntinfo4_list;
36753b895386SPavel Filipensky 		mi->mi_srv = sp;
36767c478bd9Sstevel@tonic-gate 		sp->mntinfo4_list = mi;
36777c478bd9Sstevel@tonic-gate 		mi->mi_srvsettime = gethrestime_sec();
36783b895386SPavel Filipensky 		mi->mi_srvset_cnt++;
36797c478bd9Sstevel@tonic-gate 	}
36807c478bd9Sstevel@tonic-gate 
36817c478bd9Sstevel@tonic-gate 	/* set mi's clientid to that of sp's for later matching */
36827c478bd9Sstevel@tonic-gate 	mi->mi_clientid = sp->clientid;
36837c478bd9Sstevel@tonic-gate 
36847c478bd9Sstevel@tonic-gate 	/*
36857c478bd9Sstevel@tonic-gate 	 * Update the clientid for any other mi's belonging to sp.  This
36867c478bd9Sstevel@tonic-gate 	 * must be done here while we hold sp->s_lock, so that
36877c478bd9Sstevel@tonic-gate 	 * find_nfs4_server() continues to work.
36887c478bd9Sstevel@tonic-gate 	 */
36897c478bd9Sstevel@tonic-gate 
36907c478bd9Sstevel@tonic-gate 	for (tmi = sp->mntinfo4_list;
36917c478bd9Sstevel@tonic-gate 	    tmi != NULL;
36927c478bd9Sstevel@tonic-gate 	    tmi = tmi->mi_clientid_next) {
36937c478bd9Sstevel@tonic-gate 		if (tmi != mi) {
36947c478bd9Sstevel@tonic-gate 			tmi->mi_clientid = sp->clientid;
36957c478bd9Sstevel@tonic-gate 		}
36967c478bd9Sstevel@tonic-gate 	}
36977c478bd9Sstevel@tonic-gate }
36987c478bd9Sstevel@tonic-gate 
36997c478bd9Sstevel@tonic-gate /*
37007c478bd9Sstevel@tonic-gate  * Remove the mi from sp's mntinfo4_list and release its reference.
37017c478bd9Sstevel@tonic-gate  * Exception: if mi still has open files, flag it for later removal (when
37027c478bd9Sstevel@tonic-gate  * all the files are closed).
37037c478bd9Sstevel@tonic-gate  *
37047c478bd9Sstevel@tonic-gate  * If this is the last mntinfo4 in sp's list then tell the lease renewal
37057c478bd9Sstevel@tonic-gate  * thread to exit.
37067c478bd9Sstevel@tonic-gate  */
37077c478bd9Sstevel@tonic-gate static void
37087c478bd9Sstevel@tonic-gate nfs4_remove_mi_from_server_nolock(mntinfo4_t *mi, nfs4_server_t *sp)
37097c478bd9Sstevel@tonic-gate {
37107c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
3711b9238976Sth 	    "nfs4_remove_mi_from_server_nolock: remove mi %p from sp %p",
3712b9238976Sth 	    (void*)mi, (void*)sp));
37137c478bd9Sstevel@tonic-gate 
37147c478bd9Sstevel@tonic-gate 	ASSERT(sp != NULL);
37157c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
37167c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_open_files >= 0);
37177c478bd9Sstevel@tonic-gate 
37187c478bd9Sstevel@tonic-gate 	/*
37197c478bd9Sstevel@tonic-gate 	 * First make sure this mntinfo4 can be taken off of the list,
37207c478bd9Sstevel@tonic-gate 	 * ie: it doesn't have any open files remaining.
37217c478bd9Sstevel@tonic-gate 	 */
37227c478bd9Sstevel@tonic-gate 	if (mi->mi_open_files > 0) {
37237c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
3724b9238976Sth 		    "nfs4_remove_mi_from_server_nolock: don't "
3725b9238976Sth 		    "remove mi since it still has files open"));
37267c478bd9Sstevel@tonic-gate 
37277c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_lock);
37287c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_REMOVE_ON_LAST_CLOSE;
37297c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_lock);
37307c478bd9Sstevel@tonic-gate 		return;
37317c478bd9Sstevel@tonic-gate 	}
37327c478bd9Sstevel@tonic-gate 
373350a83466Sjwahlig 	VFS_HOLD(mi->mi_vfsp);
37347c478bd9Sstevel@tonic-gate 	remove_mi(sp, mi);
373550a83466Sjwahlig 	VFS_RELE(mi->mi_vfsp);
37367c478bd9Sstevel@tonic-gate 
37377c478bd9Sstevel@tonic-gate 	if (sp->mntinfo4_list == NULL) {
37387c478bd9Sstevel@tonic-gate 		/* last fs unmounted, kill the thread */
37397c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
3740b9238976Sth 		    "remove_mi_from_nfs4_server_nolock: kill the thread"));
37417c478bd9Sstevel@tonic-gate 		nfs4_mark_srv_dead(sp);
37427c478bd9Sstevel@tonic-gate 	}
37437c478bd9Sstevel@tonic-gate }
37447c478bd9Sstevel@tonic-gate 
37457c478bd9Sstevel@tonic-gate /*
37467c478bd9Sstevel@tonic-gate  * Remove mi from sp's mntinfo4_list and release the vfs reference.
37477c478bd9Sstevel@tonic-gate  */
37487c478bd9Sstevel@tonic-gate static void
37497c478bd9Sstevel@tonic-gate remove_mi(nfs4_server_t *sp, mntinfo4_t *mi)
37507c478bd9Sstevel@tonic-gate {
37517c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
37527c478bd9Sstevel@tonic-gate 
37537c478bd9Sstevel@tonic-gate 	/*
37547c478bd9Sstevel@tonic-gate 	 * We release a reference, and the caller must still have a
37557c478bd9Sstevel@tonic-gate 	 * reference.
37567c478bd9Sstevel@tonic-gate 	 */
37577c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_vfsp->vfs_count >= 2);
37587c478bd9Sstevel@tonic-gate 
37597c478bd9Sstevel@tonic-gate 	if (mi->mi_clientid_prev) {
37607c478bd9Sstevel@tonic-gate 		mi->mi_clientid_prev->mi_clientid_next = mi->mi_clientid_next;
37617c478bd9Sstevel@tonic-gate 	} else {
37627c478bd9Sstevel@tonic-gate 		/* This is the first mi in sp's mntinfo4_list */
37637c478bd9Sstevel@tonic-gate 		/*
37647c478bd9Sstevel@tonic-gate 		 * Make sure the first mntinfo4 in the list is the actual
37657c478bd9Sstevel@tonic-gate 		 * mntinfo4 passed in.
37667c478bd9Sstevel@tonic-gate 		 */
37677c478bd9Sstevel@tonic-gate 		ASSERT(sp->mntinfo4_list == mi);
37687c478bd9Sstevel@tonic-gate 
37697c478bd9Sstevel@tonic-gate 		sp->mntinfo4_list = mi->mi_clientid_next;
37707c478bd9Sstevel@tonic-gate 	}
37717c478bd9Sstevel@tonic-gate 	if (mi->mi_clientid_next)
37727c478bd9Sstevel@tonic-gate 		mi->mi_clientid_next->mi_clientid_prev = mi->mi_clientid_prev;
37737c478bd9Sstevel@tonic-gate 
37747c478bd9Sstevel@tonic-gate 	/* Now mark the mntinfo4's links as being removed */
37757c478bd9Sstevel@tonic-gate 	mi->mi_clientid_prev = mi->mi_clientid_next = NULL;
37763b895386SPavel Filipensky 	mi->mi_srv = NULL;
377722dc8f51SPavel Filipensky 	mi->mi_srvset_cnt++;
37787c478bd9Sstevel@tonic-gate 
37797c478bd9Sstevel@tonic-gate 	VFS_RELE(mi->mi_vfsp);
37807c478bd9Sstevel@tonic-gate }
37817c478bd9Sstevel@tonic-gate 
37827c478bd9Sstevel@tonic-gate /*
37837c478bd9Sstevel@tonic-gate  * Free all the entries in sp's mntinfo4_list.
37847c478bd9Sstevel@tonic-gate  */
37857c478bd9Sstevel@tonic-gate static void
37867c478bd9Sstevel@tonic-gate remove_all_mi(nfs4_server_t *sp)
37877c478bd9Sstevel@tonic-gate {
37887c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
37897c478bd9Sstevel@tonic-gate 
37907c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
37917c478bd9Sstevel@tonic-gate 
37927c478bd9Sstevel@tonic-gate 	while (sp->mntinfo4_list != NULL) {
37937c478bd9Sstevel@tonic-gate 		mi = sp->mntinfo4_list;
37947c478bd9Sstevel@tonic-gate 		/*
37957c478bd9Sstevel@tonic-gate 		 * Grab a reference in case there is only one left (which
37967c478bd9Sstevel@tonic-gate 		 * remove_mi() frees).
37977c478bd9Sstevel@tonic-gate 		 */
37987c478bd9Sstevel@tonic-gate 		VFS_HOLD(mi->mi_vfsp);
37997c478bd9Sstevel@tonic-gate 		remove_mi(sp, mi);
38007c478bd9Sstevel@tonic-gate 		VFS_RELE(mi->mi_vfsp);
38017c478bd9Sstevel@tonic-gate 	}
38027c478bd9Sstevel@tonic-gate }
38037c478bd9Sstevel@tonic-gate 
38047c478bd9Sstevel@tonic-gate /*
38057c478bd9Sstevel@tonic-gate  * Remove the mi from sp's mntinfo4_list as above, and rele the vfs.
38067c478bd9Sstevel@tonic-gate  *
38077c478bd9Sstevel@tonic-gate  * This version can be called with a null nfs4_server_t arg,
38087c478bd9Sstevel@tonic-gate  * and will either find the right one and handle locking, or
38097c478bd9Sstevel@tonic-gate  * do nothing because the mi wasn't added to an sp's mntinfo4_list.
38107c478bd9Sstevel@tonic-gate  */
38117c478bd9Sstevel@tonic-gate void
38127c478bd9Sstevel@tonic-gate nfs4_remove_mi_from_server(mntinfo4_t *mi, nfs4_server_t *esp)
38137c478bd9Sstevel@tonic-gate {
38147c478bd9Sstevel@tonic-gate 	nfs4_server_t	*sp;
38157c478bd9Sstevel@tonic-gate 
38163b895386SPavel Filipensky 	if (esp) {
38173b895386SPavel Filipensky 		nfs4_remove_mi_from_server_nolock(mi, esp);
38183b895386SPavel Filipensky 		return;
38193b895386SPavel Filipensky 	}
38207c478bd9Sstevel@tonic-gate 
38213b895386SPavel Filipensky 	(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, 0);
382222dc8f51SPavel Filipensky 	if (sp = find_nfs4_server_all(mi, 1)) {
38237c478bd9Sstevel@tonic-gate 		nfs4_remove_mi_from_server_nolock(mi, sp);
38243b895386SPavel Filipensky 		mutex_exit(&sp->s_lock);
382522dc8f51SPavel Filipensky 		nfs4_server_rele(sp);
38267c478bd9Sstevel@tonic-gate 	}
38273b895386SPavel Filipensky 	nfs_rw_exit(&mi->mi_recovlock);
38287c478bd9Sstevel@tonic-gate }
38297c478bd9Sstevel@tonic-gate 
38307c478bd9Sstevel@tonic-gate /*
38317c478bd9Sstevel@tonic-gate  * Return TRUE if the given server has any non-unmounted filesystems.
38327c478bd9Sstevel@tonic-gate  */
38337c478bd9Sstevel@tonic-gate 
38347c478bd9Sstevel@tonic-gate bool_t
38357c478bd9Sstevel@tonic-gate nfs4_fs_active(nfs4_server_t *sp)
38367c478bd9Sstevel@tonic-gate {
38377c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
38387c478bd9Sstevel@tonic-gate 
38397c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
38407c478bd9Sstevel@tonic-gate 
38417c478bd9Sstevel@tonic-gate 	for (mi = sp->mntinfo4_list; mi != NULL; mi = mi->mi_clientid_next) {
38427c478bd9Sstevel@tonic-gate 		if (!(mi->mi_vfsp->vfs_flag & VFS_UNMOUNTED))
38437c478bd9Sstevel@tonic-gate 			return (TRUE);
38447c478bd9Sstevel@tonic-gate 	}
38457c478bd9Sstevel@tonic-gate 
38467c478bd9Sstevel@tonic-gate 	return (FALSE);
38477c478bd9Sstevel@tonic-gate }
38487c478bd9Sstevel@tonic-gate 
38497c478bd9Sstevel@tonic-gate /*
38507c478bd9Sstevel@tonic-gate  * Mark sp as finished and notify any waiters.
38517c478bd9Sstevel@tonic-gate  */
38527c478bd9Sstevel@tonic-gate 
38537c478bd9Sstevel@tonic-gate void
38547c478bd9Sstevel@tonic-gate nfs4_mark_srv_dead(nfs4_server_t *sp)
38557c478bd9Sstevel@tonic-gate {
38567c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
38577c478bd9Sstevel@tonic-gate 
38587c478bd9Sstevel@tonic-gate 	sp->s_thread_exit = NFS4_THREAD_EXIT;
38597c478bd9Sstevel@tonic-gate 	cv_broadcast(&sp->cv_thread_exit);
38607c478bd9Sstevel@tonic-gate }
38617c478bd9Sstevel@tonic-gate 
38627c478bd9Sstevel@tonic-gate /*
38637c478bd9Sstevel@tonic-gate  * Create a new nfs4_server_t structure.
38647c478bd9Sstevel@tonic-gate  * Returns new node unlocked and not in list, but with a reference count of
38657c478bd9Sstevel@tonic-gate  * 1.
38667c478bd9Sstevel@tonic-gate  */
38677c478bd9Sstevel@tonic-gate struct nfs4_server *
38687c478bd9Sstevel@tonic-gate new_nfs4_server(struct servinfo4 *svp, cred_t *cr)
38697c478bd9Sstevel@tonic-gate {
38707c478bd9Sstevel@tonic-gate 	struct nfs4_server *np;
38717c478bd9Sstevel@tonic-gate 	timespec_t tt;
38727c478bd9Sstevel@tonic-gate 	union {
38737c478bd9Sstevel@tonic-gate 		struct {
38747c478bd9Sstevel@tonic-gate 			uint32_t sec;
38757c478bd9Sstevel@tonic-gate 			uint32_t subsec;
38767c478bd9Sstevel@tonic-gate 		} un_curtime;
38777c478bd9Sstevel@tonic-gate 		verifier4	un_verifier;
38787c478bd9Sstevel@tonic-gate 	} nfs4clientid_verifier;
38792f172c55SRobert Thurlow 	/*
38802f172c55SRobert Thurlow 	 * We change this ID string carefully and with the Solaris
38812f172c55SRobert Thurlow 	 * NFS server behaviour in mind.  "+referrals" indicates
38822f172c55SRobert Thurlow 	 * a client that can handle an NFSv4 referral.
38832f172c55SRobert Thurlow 	 */
38842f172c55SRobert Thurlow 	char id_val[] = "Solaris: %s, NFSv4 kernel client +referrals";
38857c478bd9Sstevel@tonic-gate 	int len;
38867c478bd9Sstevel@tonic-gate 
38877c478bd9Sstevel@tonic-gate 	np = kmem_zalloc(sizeof (struct nfs4_server), KM_SLEEP);
38887c478bd9Sstevel@tonic-gate 	np->saddr.len = svp->sv_addr.len;
38897c478bd9Sstevel@tonic-gate 	np->saddr.maxlen = svp->sv_addr.maxlen;
38907c478bd9Sstevel@tonic-gate 	np->saddr.buf = kmem_alloc(svp->sv_addr.maxlen, KM_SLEEP);
38917c478bd9Sstevel@tonic-gate 	bcopy(svp->sv_addr.buf, np->saddr.buf, svp->sv_addr.len);
38927c478bd9Sstevel@tonic-gate 	np->s_refcnt = 1;
38937c478bd9Sstevel@tonic-gate 
38947c478bd9Sstevel@tonic-gate 	/*
38957c478bd9Sstevel@tonic-gate 	 * Build the nfs_client_id4 for this server mount.  Ensure
38967c478bd9Sstevel@tonic-gate 	 * the verifier is useful and that the identification is
38977c478bd9Sstevel@tonic-gate 	 * somehow based on the server's address for the case of
38987c478bd9Sstevel@tonic-gate 	 * multi-homed servers.
38997c478bd9Sstevel@tonic-gate 	 */
39007c478bd9Sstevel@tonic-gate 	nfs4clientid_verifier.un_verifier = 0;
39017c478bd9Sstevel@tonic-gate 	gethrestime(&tt);
39027c478bd9Sstevel@tonic-gate 	nfs4clientid_verifier.un_curtime.sec = (uint32_t)tt.tv_sec;
39037c478bd9Sstevel@tonic-gate 	nfs4clientid_verifier.un_curtime.subsec = (uint32_t)tt.tv_nsec;
39047c478bd9Sstevel@tonic-gate 	np->clidtosend.verifier = nfs4clientid_verifier.un_verifier;
39057c478bd9Sstevel@tonic-gate 
39067c478bd9Sstevel@tonic-gate 	/*
39077c478bd9Sstevel@tonic-gate 	 * calculate the length of the opaque identifier.  Subtract 2
39087c478bd9Sstevel@tonic-gate 	 * for the "%s" and add the traditional +1 for null
39097c478bd9Sstevel@tonic-gate 	 * termination.
39107c478bd9Sstevel@tonic-gate 	 */
39117c478bd9Sstevel@tonic-gate 	len = strlen(id_val) - 2 + strlen(uts_nodename()) + 1;
39127c478bd9Sstevel@tonic-gate 	np->clidtosend.id_len = len + np->saddr.maxlen;
39137c478bd9Sstevel@tonic-gate 
39147c478bd9Sstevel@tonic-gate 	np->clidtosend.id_val = kmem_alloc(np->clidtosend.id_len, KM_SLEEP);
39157c478bd9Sstevel@tonic-gate 	(void) sprintf(np->clidtosend.id_val, id_val, uts_nodename());
39167c478bd9Sstevel@tonic-gate 	bcopy(np->saddr.buf, &np->clidtosend.id_val[len], np->saddr.len);
39177c478bd9Sstevel@tonic-gate 
39187c478bd9Sstevel@tonic-gate 	np->s_flags = 0;
39197c478bd9Sstevel@tonic-gate 	np->mntinfo4_list = NULL;
39207c478bd9Sstevel@tonic-gate 	/* save cred for issuing rfs4calls inside the renew thread */
39217c478bd9Sstevel@tonic-gate 	crhold(cr);
39227c478bd9Sstevel@tonic-gate 	np->s_cred = cr;
39237c478bd9Sstevel@tonic-gate 	cv_init(&np->cv_thread_exit, NULL, CV_DEFAULT, NULL);
39247c478bd9Sstevel@tonic-gate 	mutex_init(&np->s_lock, NULL, MUTEX_DEFAULT, NULL);
39257c478bd9Sstevel@tonic-gate 	nfs_rw_init(&np->s_recovlock, NULL, RW_DEFAULT, NULL);
39267c478bd9Sstevel@tonic-gate 	list_create(&np->s_deleg_list, sizeof (rnode4_t),
39277c478bd9Sstevel@tonic-gate 	    offsetof(rnode4_t, r_deleg_link));
39287c478bd9Sstevel@tonic-gate 	np->s_thread_exit = 0;
39297c478bd9Sstevel@tonic-gate 	np->state_ref_count = 0;
39307c478bd9Sstevel@tonic-gate 	np->lease_valid = NFS4_LEASE_NOT_STARTED;
39317c478bd9Sstevel@tonic-gate 	cv_init(&np->s_cv_otw_count, NULL, CV_DEFAULT, NULL);
3932f86c6ccaSdm 	cv_init(&np->s_clientid_pend, NULL, CV_DEFAULT, NULL);
39337c478bd9Sstevel@tonic-gate 	np->s_otw_call_count = 0;
39347c478bd9Sstevel@tonic-gate 	cv_init(&np->wait_cb_null, NULL, CV_DEFAULT, NULL);
39357c478bd9Sstevel@tonic-gate 	np->zoneid = getzoneid();
39367c478bd9Sstevel@tonic-gate 	np->zone_globals = nfs4_get_callback_globals();
39377c478bd9Sstevel@tonic-gate 	ASSERT(np->zone_globals != NULL);
39387c478bd9Sstevel@tonic-gate 	return (np);
39397c478bd9Sstevel@tonic-gate }
39407c478bd9Sstevel@tonic-gate 
39417c478bd9Sstevel@tonic-gate /*
39427c478bd9Sstevel@tonic-gate  * Create a new nfs4_server_t structure and add it to the list.
39437c478bd9Sstevel@tonic-gate  * Returns new node locked; reference must eventually be freed.
39447c478bd9Sstevel@tonic-gate  */
39457c478bd9Sstevel@tonic-gate static struct nfs4_server *
39467c478bd9Sstevel@tonic-gate add_new_nfs4_server(struct servinfo4 *svp, cred_t *cr)
39477c478bd9Sstevel@tonic-gate {
39487c478bd9Sstevel@tonic-gate 	nfs4_server_t *sp;
39497c478bd9Sstevel@tonic-gate 
39507c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&nfs4_server_lst_lock));
39517c478bd9Sstevel@tonic-gate 	sp = new_nfs4_server(svp, cr);
39527c478bd9Sstevel@tonic-gate 	mutex_enter(&sp->s_lock);
39537c478bd9Sstevel@tonic-gate 	insque(sp, &nfs4_server_lst);
39547c478bd9Sstevel@tonic-gate 	sp->s_refcnt++;			/* list gets a reference */
3955f64c4ae1Sdm 	sp->s_flags |= N4S_INSERTED;
39567c478bd9Sstevel@tonic-gate 	sp->clientid = 0;
39577c478bd9Sstevel@tonic-gate 	return (sp);
39587c478bd9Sstevel@tonic-gate }
39597c478bd9Sstevel@tonic-gate 
39607c478bd9Sstevel@tonic-gate int nfs4_server_t_debug = 0;
39617c478bd9Sstevel@tonic-gate 
39627c478bd9Sstevel@tonic-gate #ifdef lint
39637c478bd9Sstevel@tonic-gate extern void
39647c478bd9Sstevel@tonic-gate dumpnfs4slist(char *, mntinfo4_t *, clientid4, servinfo4_t *);
39657c478bd9Sstevel@tonic-gate #endif
39667c478bd9Sstevel@tonic-gate 
39677c478bd9Sstevel@tonic-gate #ifndef lint
39687c478bd9Sstevel@tonic-gate #ifdef DEBUG
39697c478bd9Sstevel@tonic-gate void
39707c478bd9Sstevel@tonic-gate dumpnfs4slist(char *txt, mntinfo4_t *mi, clientid4 clientid, servinfo4_t *srv_p)
39717c478bd9Sstevel@tonic-gate {
39727c478bd9Sstevel@tonic-gate 	int hash16(void *p, int len);
39737c478bd9Sstevel@tonic-gate 	nfs4_server_t *np;
39747c478bd9Sstevel@tonic-gate 
39757c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_server_t_debug, (CE_NOTE,
39767c478bd9Sstevel@tonic-gate 	    "dumping nfs4_server_t list in %s", txt));
39777c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
39787c478bd9Sstevel@tonic-gate 	    "mi 0x%p, want clientid %llx, addr %d/%04X",
39797c478bd9Sstevel@tonic-gate 	    mi, (longlong_t)clientid, srv_p->sv_addr.len,
39807c478bd9Sstevel@tonic-gate 	    hash16((void *)srv_p->sv_addr.buf, srv_p->sv_addr.len)));
39817c478bd9Sstevel@tonic-gate 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst;
39827c478bd9Sstevel@tonic-gate 	    np = np->forw) {
39837c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
39847c478bd9Sstevel@tonic-gate 		    "node 0x%p,    clientid %llx, addr %d/%04X, cnt %d",
39857c478bd9Sstevel@tonic-gate 		    np, (longlong_t)np->clientid, np->saddr.len,
39867c478bd9Sstevel@tonic-gate 		    hash16((void *)np->saddr.buf, np->saddr.len),
39877c478bd9Sstevel@tonic-gate 		    np->state_ref_count));
39887c478bd9Sstevel@tonic-gate 		if (np->saddr.len == srv_p->sv_addr.len &&
39897c478bd9Sstevel@tonic-gate 		    bcmp(np->saddr.buf, srv_p->sv_addr.buf,
39907c478bd9Sstevel@tonic-gate 		    np->saddr.len) == 0)
39917c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
39927c478bd9Sstevel@tonic-gate 			    " - address matches"));
39937c478bd9Sstevel@tonic-gate 		if (np->clientid == clientid || np->clientid == 0)
39947c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
39957c478bd9Sstevel@tonic-gate 			    " - clientid matches"));
39967c478bd9Sstevel@tonic-gate 		if (np->s_thread_exit != NFS4_THREAD_EXIT)
39977c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
39987c478bd9Sstevel@tonic-gate 			    " - thread not exiting"));
39997c478bd9Sstevel@tonic-gate 	}
40007c478bd9Sstevel@tonic-gate 	delay(hz);
40017c478bd9Sstevel@tonic-gate }
40027c478bd9Sstevel@tonic-gate #endif
40037c478bd9Sstevel@tonic-gate #endif
40047c478bd9Sstevel@tonic-gate 
40057c478bd9Sstevel@tonic-gate 
40067c478bd9Sstevel@tonic-gate /*
40077c478bd9Sstevel@tonic-gate  * Move a mntinfo4_t from one server list to another.
40087c478bd9Sstevel@tonic-gate  * Locking of the two nfs4_server_t nodes will be done in list order.
40097c478bd9Sstevel@tonic-gate  *
40107c478bd9Sstevel@tonic-gate  * Returns NULL if the current nfs4_server_t for the filesystem could not
40117c478bd9Sstevel@tonic-gate  * be found (e.g., due to forced unmount).  Otherwise returns a reference
40127c478bd9Sstevel@tonic-gate  * to the new nfs4_server_t, which must eventually be freed.
40137c478bd9Sstevel@tonic-gate  */
40147c478bd9Sstevel@tonic-gate nfs4_server_t *
40157c478bd9Sstevel@tonic-gate nfs4_move_mi(mntinfo4_t *mi, servinfo4_t *old, servinfo4_t *new)
40167c478bd9Sstevel@tonic-gate {
40177c478bd9Sstevel@tonic-gate 	nfs4_server_t *p, *op = NULL, *np = NULL;
40187c478bd9Sstevel@tonic-gate 	int num_open;
4019108322fbScarlsonj 	zoneid_t zoneid = nfs_zoneid();
40207c478bd9Sstevel@tonic-gate 
4021108322fbScarlsonj 	ASSERT(nfs_zone() == mi->mi_zone);
40227c478bd9Sstevel@tonic-gate 
40237c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_server_lst_lock);
40247c478bd9Sstevel@tonic-gate #ifdef DEBUG
40257c478bd9Sstevel@tonic-gate 	if (nfs4_server_t_debug)
40267c478bd9Sstevel@tonic-gate 		dumpnfs4slist("nfs4_move_mi", mi, (clientid4)0, new);
40277c478bd9Sstevel@tonic-gate #endif
40287c478bd9Sstevel@tonic-gate 	for (p = nfs4_server_lst.forw; p != &nfs4_server_lst; p = p->forw) {
40297c478bd9Sstevel@tonic-gate 		if (p->zoneid != zoneid)
40307c478bd9Sstevel@tonic-gate 			continue;
40317c478bd9Sstevel@tonic-gate 		if (p->saddr.len == old->sv_addr.len &&
40327c478bd9Sstevel@tonic-gate 		    bcmp(p->saddr.buf, old->sv_addr.buf, p->saddr.len) == 0 &&
40337c478bd9Sstevel@tonic-gate 		    p->s_thread_exit != NFS4_THREAD_EXIT) {
40347c478bd9Sstevel@tonic-gate 			op = p;
40357c478bd9Sstevel@tonic-gate 			mutex_enter(&op->s_lock);
40367c478bd9Sstevel@tonic-gate 			op->s_refcnt++;
40377c478bd9Sstevel@tonic-gate 		}
40387c478bd9Sstevel@tonic-gate 		if (p->saddr.len == new->sv_addr.len &&
40397c478bd9Sstevel@tonic-gate 		    bcmp(p->saddr.buf, new->sv_addr.buf, p->saddr.len) == 0 &&
40407c478bd9Sstevel@tonic-gate 		    p->s_thread_exit != NFS4_THREAD_EXIT) {
40417c478bd9Sstevel@tonic-gate 			np = p;
40427c478bd9Sstevel@tonic-gate 			mutex_enter(&np->s_lock);
40437c478bd9Sstevel@tonic-gate 		}
40447c478bd9Sstevel@tonic-gate 		if (op != NULL && np != NULL)
40457c478bd9Sstevel@tonic-gate 			break;
40467c478bd9Sstevel@tonic-gate 	}
40477c478bd9Sstevel@tonic-gate 	if (op == NULL) {
40487c478bd9Sstevel@tonic-gate 		/*
40497c478bd9Sstevel@tonic-gate 		 * Filesystem has been forcibly unmounted.  Bail out.
40507c478bd9Sstevel@tonic-gate 		 */
40517c478bd9Sstevel@tonic-gate 		if (np != NULL)
40527c478bd9Sstevel@tonic-gate 			mutex_exit(&np->s_lock);
40537c478bd9Sstevel@tonic-gate 		mutex_exit(&nfs4_server_lst_lock);
40547c478bd9Sstevel@tonic-gate 		return (NULL);
40557c478bd9Sstevel@tonic-gate 	}
40567c478bd9Sstevel@tonic-gate 	if (np != NULL) {
40577c478bd9Sstevel@tonic-gate 		np->s_refcnt++;
40587c478bd9Sstevel@tonic-gate 	} else {
40597c478bd9Sstevel@tonic-gate #ifdef DEBUG
40607c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
40617c478bd9Sstevel@tonic-gate 		    "nfs4_move_mi: no target nfs4_server, will create."));
40627c478bd9Sstevel@tonic-gate #endif
40637c478bd9Sstevel@tonic-gate 		np = add_new_nfs4_server(new, kcred);
40647c478bd9Sstevel@tonic-gate 	}
40657c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_server_lst_lock);
40667c478bd9Sstevel@tonic-gate 
40677c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
40687c478bd9Sstevel@tonic-gate 	    "nfs4_move_mi: for mi 0x%p, "
40697c478bd9Sstevel@tonic-gate 	    "old servinfo4 0x%p, new servinfo4 0x%p, "
40707c478bd9Sstevel@tonic-gate 	    "old nfs4_server 0x%p, new nfs4_server 0x%p, ",
40717c478bd9Sstevel@tonic-gate 	    (void*)mi, (void*)old, (void*)new,
40727c478bd9Sstevel@tonic-gate 	    (void*)op, (void*)np));
40737c478bd9Sstevel@tonic-gate 	ASSERT(op != NULL && np != NULL);
40747c478bd9Sstevel@tonic-gate 
40757c478bd9Sstevel@tonic-gate 	/* discard any delegations */
40767c478bd9Sstevel@tonic-gate 	nfs4_deleg_discard(mi, op);
40777c478bd9Sstevel@tonic-gate 
40787c478bd9Sstevel@tonic-gate 	num_open = mi->mi_open_files;
40797c478bd9Sstevel@tonic-gate 	mi->mi_open_files = 0;
40807c478bd9Sstevel@tonic-gate 	op->state_ref_count -= num_open;
40817c478bd9Sstevel@tonic-gate 	ASSERT(op->state_ref_count >= 0);
40827c478bd9Sstevel@tonic-gate 	np->state_ref_count += num_open;
40837c478bd9Sstevel@tonic-gate 	nfs4_remove_mi_from_server_nolock(mi, op);
40847c478bd9Sstevel@tonic-gate 	mi->mi_open_files = num_open;
40857c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
40867c478bd9Sstevel@tonic-gate 	    "nfs4_move_mi: mi_open_files %d, op->cnt %d, np->cnt %d",
40877c478bd9Sstevel@tonic-gate 	    mi->mi_open_files, op->state_ref_count, np->state_ref_count));
40887c478bd9Sstevel@tonic-gate 
40897c478bd9Sstevel@tonic-gate 	nfs4_add_mi_to_server(np, mi);
40907c478bd9Sstevel@tonic-gate 
40917c478bd9Sstevel@tonic-gate 	mutex_exit(&op->s_lock);
40927c478bd9Sstevel@tonic-gate 	mutex_exit(&np->s_lock);
409395c7fa91SPavel Filipensky 	nfs4_server_rele(op);
40947c478bd9Sstevel@tonic-gate 
40957c478bd9Sstevel@tonic-gate 	return (np);
40967c478bd9Sstevel@tonic-gate }
40977c478bd9Sstevel@tonic-gate 
40987c478bd9Sstevel@tonic-gate /*
4099f86c6ccaSdm  * Need to have the nfs4_server_lst_lock.
41007c478bd9Sstevel@tonic-gate  * Search the nfs4_server list to find a match on this servinfo4
41017c478bd9Sstevel@tonic-gate  * based on its address.
41027c478bd9Sstevel@tonic-gate  *
41037c478bd9Sstevel@tonic-gate  * Returns NULL if no match is found.  Otherwise returns a reference (which
41047c478bd9Sstevel@tonic-gate  * must eventually be freed) to a locked nfs4_server.
41057c478bd9Sstevel@tonic-gate  */
41067c478bd9Sstevel@tonic-gate nfs4_server_t *
41077c478bd9Sstevel@tonic-gate servinfo4_to_nfs4_server(servinfo4_t *srv_p)
41087c478bd9Sstevel@tonic-gate {
41097c478bd9Sstevel@tonic-gate 	nfs4_server_t *np;
4110108322fbScarlsonj 	zoneid_t zoneid = nfs_zoneid();
41117c478bd9Sstevel@tonic-gate 
4112f86c6ccaSdm 	ASSERT(MUTEX_HELD(&nfs4_server_lst_lock));
41137c478bd9Sstevel@tonic-gate 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst; np = np->forw) {
41147c478bd9Sstevel@tonic-gate 		if (np->zoneid == zoneid &&
41157c478bd9Sstevel@tonic-gate 		    np->saddr.len == srv_p->sv_addr.len &&
41167c478bd9Sstevel@tonic-gate 		    bcmp(np->saddr.buf, srv_p->sv_addr.buf,
4117b9238976Sth 		    np->saddr.len) == 0 &&
41187c478bd9Sstevel@tonic-gate 		    np->s_thread_exit != NFS4_THREAD_EXIT) {
41197c478bd9Sstevel@tonic-gate 			mutex_enter(&np->s_lock);
41207c478bd9Sstevel@tonic-gate 			np->s_refcnt++;
41217c478bd9Sstevel@tonic-gate 			return (np);
41227c478bd9Sstevel@tonic-gate 		}
41237c478bd9Sstevel@tonic-gate 	}
41247c478bd9Sstevel@tonic-gate 	return (NULL);
41257c478bd9Sstevel@tonic-gate }
41267c478bd9Sstevel@tonic-gate 
41277c478bd9Sstevel@tonic-gate /*
41287c478bd9Sstevel@tonic-gate  * Locks the nfs4_server down if it is found and returns a reference that
41297c478bd9Sstevel@tonic-gate  * must eventually be freed.
413022dc8f51SPavel Filipensky  */
413122dc8f51SPavel Filipensky static nfs4_server_t *
413222dc8f51SPavel Filipensky lookup_nfs4_server(nfs4_server_t *sp, int any_state)
413322dc8f51SPavel Filipensky {
413422dc8f51SPavel Filipensky 	nfs4_server_t *np;
413522dc8f51SPavel Filipensky 
413622dc8f51SPavel Filipensky 	mutex_enter(&nfs4_server_lst_lock);
413722dc8f51SPavel Filipensky 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst; np = np->forw) {
413822dc8f51SPavel Filipensky 		mutex_enter(&np->s_lock);
413922dc8f51SPavel Filipensky 		if (np == sp && np->s_refcnt > 0 &&
414022dc8f51SPavel Filipensky 		    (np->s_thread_exit != NFS4_THREAD_EXIT || any_state)) {
414122dc8f51SPavel Filipensky 			mutex_exit(&nfs4_server_lst_lock);
414222dc8f51SPavel Filipensky 			np->s_refcnt++;
414322dc8f51SPavel Filipensky 			return (np);
414422dc8f51SPavel Filipensky 		}
414522dc8f51SPavel Filipensky 		mutex_exit(&np->s_lock);
414622dc8f51SPavel Filipensky 	}
414722dc8f51SPavel Filipensky 	mutex_exit(&nfs4_server_lst_lock);
414822dc8f51SPavel Filipensky 
414922dc8f51SPavel Filipensky 	return (NULL);
415022dc8f51SPavel Filipensky }
415122dc8f51SPavel Filipensky 
415222dc8f51SPavel Filipensky /*
41537c478bd9Sstevel@tonic-gate  * The caller should be holding mi->mi_recovlock, and it should continue to
41547c478bd9Sstevel@tonic-gate  * hold the lock until done with the returned nfs4_server_t.  Once
41557c478bd9Sstevel@tonic-gate  * mi->mi_recovlock is released, there is no guarantee that the returned
41567c478bd9Sstevel@tonic-gate  * mi->nfs4_server_t will continue to correspond to mi.
41577c478bd9Sstevel@tonic-gate  */
41587c478bd9Sstevel@tonic-gate nfs4_server_t *
41597c478bd9Sstevel@tonic-gate find_nfs4_server(mntinfo4_t *mi)
41607c478bd9Sstevel@tonic-gate {
416122dc8f51SPavel Filipensky 	ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) ||
416222dc8f51SPavel Filipensky 	    nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER));
416322dc8f51SPavel Filipensky 
416422dc8f51SPavel Filipensky 	return (lookup_nfs4_server(mi->mi_srv, 0));
41657c478bd9Sstevel@tonic-gate }
41667c478bd9Sstevel@tonic-gate 
41677c478bd9Sstevel@tonic-gate /*
416822dc8f51SPavel Filipensky  * Same as above, but takes an "any_state" parameter which can be
41697c478bd9Sstevel@tonic-gate  * set to 1 if the caller wishes to find nfs4_server_t's which
41707c478bd9Sstevel@tonic-gate  * have been marked for termination by the exit of the renew
41717c478bd9Sstevel@tonic-gate  * thread.  This should only be used by operations which are
41727c478bd9Sstevel@tonic-gate  * cleaning up and will not cause an OTW op.
41737c478bd9Sstevel@tonic-gate  */
41747c478bd9Sstevel@tonic-gate nfs4_server_t *
417522dc8f51SPavel Filipensky find_nfs4_server_all(mntinfo4_t *mi, int any_state)
41767c478bd9Sstevel@tonic-gate {
41777c478bd9Sstevel@tonic-gate 	ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) ||
41787c478bd9Sstevel@tonic-gate 	    nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER));
41797c478bd9Sstevel@tonic-gate 
418022dc8f51SPavel Filipensky 	return (lookup_nfs4_server(mi->mi_srv, any_state));
418122dc8f51SPavel Filipensky }
418222dc8f51SPavel Filipensky 
418322dc8f51SPavel Filipensky /*
418422dc8f51SPavel Filipensky  * Lock sp, but only if it's still active (in the list and hasn't been
418522dc8f51SPavel Filipensky  * flagged as exiting) or 'any_state' is non-zero.
418622dc8f51SPavel Filipensky  * Returns TRUE if sp got locked and adds a reference to sp.
418722dc8f51SPavel Filipensky  */
418822dc8f51SPavel Filipensky bool_t
418922dc8f51SPavel Filipensky nfs4_server_vlock(nfs4_server_t *sp, int any_state)
419022dc8f51SPavel Filipensky {
419122dc8f51SPavel Filipensky 	return (lookup_nfs4_server(sp, any_state) != NULL);
41927c478bd9Sstevel@tonic-gate }
41937c478bd9Sstevel@tonic-gate 
41947c478bd9Sstevel@tonic-gate /*
41957c478bd9Sstevel@tonic-gate  * Release the reference to sp and destroy it if that's the last one.
41967c478bd9Sstevel@tonic-gate  */
41977c478bd9Sstevel@tonic-gate 
41987c478bd9Sstevel@tonic-gate void
41997c478bd9Sstevel@tonic-gate nfs4_server_rele(nfs4_server_t *sp)
42007c478bd9Sstevel@tonic-gate {
42017c478bd9Sstevel@tonic-gate 	mutex_enter(&sp->s_lock);
42027c478bd9Sstevel@tonic-gate 	ASSERT(sp->s_refcnt > 0);
42037c478bd9Sstevel@tonic-gate 	sp->s_refcnt--;
42047c478bd9Sstevel@tonic-gate 	if (sp->s_refcnt > 0) {
42057c478bd9Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
42067c478bd9Sstevel@tonic-gate 		return;
42077c478bd9Sstevel@tonic-gate 	}
42087c478bd9Sstevel@tonic-gate 	mutex_exit(&sp->s_lock);
4209f86c6ccaSdm 
42107c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_server_lst_lock);
42117c478bd9Sstevel@tonic-gate 	mutex_enter(&sp->s_lock);
42127c478bd9Sstevel@tonic-gate 	if (sp->s_refcnt > 0) {
42137c478bd9Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
42147c478bd9Sstevel@tonic-gate 		mutex_exit(&nfs4_server_lst_lock);
42157c478bd9Sstevel@tonic-gate 		return;
42167c478bd9Sstevel@tonic-gate 	}
4217f86c6ccaSdm 	remque(sp);
4218f86c6ccaSdm 	sp->forw = sp->back = NULL;
42197c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_server_lst_lock);
42207c478bd9Sstevel@tonic-gate 	destroy_nfs4_server(sp);
42217c478bd9Sstevel@tonic-gate }
42227c478bd9Sstevel@tonic-gate 
42237c478bd9Sstevel@tonic-gate static void
42247c478bd9Sstevel@tonic-gate destroy_nfs4_server(nfs4_server_t *sp)
42257c478bd9Sstevel@tonic-gate {
42267c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
42277c478bd9Sstevel@tonic-gate 	ASSERT(sp->s_refcnt == 0);
42287c478bd9Sstevel@tonic-gate 	ASSERT(sp->s_otw_call_count == 0);
42297c478bd9Sstevel@tonic-gate 
42307c478bd9Sstevel@tonic-gate 	remove_all_mi(sp);
42317c478bd9Sstevel@tonic-gate 
42327c478bd9Sstevel@tonic-gate 	crfree(sp->s_cred);
42337c478bd9Sstevel@tonic-gate 	kmem_free(sp->saddr.buf, sp->saddr.maxlen);
42347c478bd9Sstevel@tonic-gate 	kmem_free(sp->clidtosend.id_val, sp->clidtosend.id_len);
42357c478bd9Sstevel@tonic-gate 	mutex_exit(&sp->s_lock);
42367c478bd9Sstevel@tonic-gate 
42377c478bd9Sstevel@tonic-gate 	/* destroy the nfs4_server */
42387c478bd9Sstevel@tonic-gate 	nfs4callback_destroy(sp);
42397c478bd9Sstevel@tonic-gate 	list_destroy(&sp->s_deleg_list);
42407c478bd9Sstevel@tonic-gate 	mutex_destroy(&sp->s_lock);
42417c478bd9Sstevel@tonic-gate 	cv_destroy(&sp->cv_thread_exit);
42427c478bd9Sstevel@tonic-gate 	cv_destroy(&sp->s_cv_otw_count);
4243f86c6ccaSdm 	cv_destroy(&sp->s_clientid_pend);
42447c478bd9Sstevel@tonic-gate 	cv_destroy(&sp->wait_cb_null);
42457c478bd9Sstevel@tonic-gate 	nfs_rw_destroy(&sp->s_recovlock);
42467c478bd9Sstevel@tonic-gate 	kmem_free(sp, sizeof (*sp));
42477c478bd9Sstevel@tonic-gate }
42487c478bd9Sstevel@tonic-gate 
42497c478bd9Sstevel@tonic-gate /*
42507c478bd9Sstevel@tonic-gate  * Fork off a thread to free the data structures for a mount.
42517c478bd9Sstevel@tonic-gate  */
42527c478bd9Sstevel@tonic-gate 
42537c478bd9Sstevel@tonic-gate static void
4254b9238976Sth async_free_mount(vfs_t *vfsp, int flag, cred_t *cr)
42557c478bd9Sstevel@tonic-gate {
42567c478bd9Sstevel@tonic-gate 	freemountargs_t *args;
42577c478bd9Sstevel@tonic-gate 	args = kmem_alloc(sizeof (freemountargs_t), KM_SLEEP);
42587c478bd9Sstevel@tonic-gate 	args->fm_vfsp = vfsp;
42597c478bd9Sstevel@tonic-gate 	VFS_HOLD(vfsp);
426050a83466Sjwahlig 	MI4_HOLD(VFTOMI4(vfsp));
4261b9238976Sth 	args->fm_flag = flag;
42627c478bd9Sstevel@tonic-gate 	args->fm_cr = cr;
42637c478bd9Sstevel@tonic-gate 	crhold(cr);
42647c478bd9Sstevel@tonic-gate 	(void) zthread_create(NULL, 0, nfs4_free_mount_thread, args, 0,
42657c478bd9Sstevel@tonic-gate 	    minclsyspri);
42667c478bd9Sstevel@tonic-gate }
42677c478bd9Sstevel@tonic-gate 
42687c478bd9Sstevel@tonic-gate static void
42697c478bd9Sstevel@tonic-gate nfs4_free_mount_thread(freemountargs_t *args)
42707c478bd9Sstevel@tonic-gate {
427150a83466Sjwahlig 	mntinfo4_t *mi;
4272b9238976Sth 	nfs4_free_mount(args->fm_vfsp, args->fm_flag, args->fm_cr);
427350a83466Sjwahlig 	mi = VFTOMI4(args->fm_vfsp);
42747c478bd9Sstevel@tonic-gate 	crfree(args->fm_cr);
427550a83466Sjwahlig 	VFS_RELE(args->fm_vfsp);
427650a83466Sjwahlig 	MI4_RELE(mi);
42777c478bd9Sstevel@tonic-gate 	kmem_free(args, sizeof (freemountargs_t));
42787c478bd9Sstevel@tonic-gate 	zthread_exit();
42797c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
42807c478bd9Sstevel@tonic-gate }
42817c478bd9Sstevel@tonic-gate 
42827c478bd9Sstevel@tonic-gate /*
42837c478bd9Sstevel@tonic-gate  * Thread to free the data structures for a given filesystem.
42847c478bd9Sstevel@tonic-gate  */
42857c478bd9Sstevel@tonic-gate static void
4286b9238976Sth nfs4_free_mount(vfs_t *vfsp, int flag, cred_t *cr)
42877c478bd9Sstevel@tonic-gate {
4288b9238976Sth 	mntinfo4_t		*mi = VFTOMI4(vfsp);
4289b9238976Sth 	nfs4_server_t		*sp;
4290b9238976Sth 	callb_cpr_t		cpr_info;
4291b9238976Sth 	kmutex_t		cpr_lock;
4292b9238976Sth 	boolean_t		async_thread;
4293b9238976Sth 	int			removed;
4294b9238976Sth 
4295d3a14591SThomas Haynes 	bool_t			must_unlock;
4296b9238976Sth 	nfs4_ephemeral_tree_t	*eph_tree;
42977c478bd9Sstevel@tonic-gate 
42987c478bd9Sstevel@tonic-gate 	/*
42997c478bd9Sstevel@tonic-gate 	 * We need to participate in the CPR framework if this is a kernel
43007c478bd9Sstevel@tonic-gate 	 * thread.
43017c478bd9Sstevel@tonic-gate 	 */
4302108322fbScarlsonj 	async_thread = (curproc == nfs_zone()->zone_zsched);
43037c478bd9Sstevel@tonic-gate 	if (async_thread) {
43047c478bd9Sstevel@tonic-gate 		mutex_init(&cpr_lock, NULL, MUTEX_DEFAULT, NULL);
43057c478bd9Sstevel@tonic-gate 		CALLB_CPR_INIT(&cpr_info, &cpr_lock, callb_generic_cpr,
43067c478bd9Sstevel@tonic-gate 		    "nfsv4AsyncUnmount");
43077c478bd9Sstevel@tonic-gate 	}
43087c478bd9Sstevel@tonic-gate 
43097c478bd9Sstevel@tonic-gate 	/*
43107c478bd9Sstevel@tonic-gate 	 * We need to wait for all outstanding OTW calls
43117c478bd9Sstevel@tonic-gate 	 * and recovery to finish before we remove the mi
43127c478bd9Sstevel@tonic-gate 	 * from the nfs4_server_t, as current pending
43137c478bd9Sstevel@tonic-gate 	 * calls might still need this linkage (in order
43147c478bd9Sstevel@tonic-gate 	 * to find a nfs4_server_t from a mntinfo4_t).
43157c478bd9Sstevel@tonic-gate 	 */
43167c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, FALSE);
43177c478bd9Sstevel@tonic-gate 	sp = find_nfs4_server(mi);
43187c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&mi->mi_recovlock);
43197c478bd9Sstevel@tonic-gate 
43207c478bd9Sstevel@tonic-gate 	if (sp) {
43217c478bd9Sstevel@tonic-gate 		while (sp->s_otw_call_count != 0) {
43227c478bd9Sstevel@tonic-gate 			if (async_thread) {
43237c478bd9Sstevel@tonic-gate 				mutex_enter(&cpr_lock);
43247c478bd9Sstevel@tonic-gate 				CALLB_CPR_SAFE_BEGIN(&cpr_info);
43257c478bd9Sstevel@tonic-gate 				mutex_exit(&cpr_lock);
43267c478bd9Sstevel@tonic-gate 			}
43277c478bd9Sstevel@tonic-gate 			cv_wait(&sp->s_cv_otw_count, &sp->s_lock);
43287c478bd9Sstevel@tonic-gate 			if (async_thread) {
43297c478bd9Sstevel@tonic-gate 				mutex_enter(&cpr_lock);
43307c478bd9Sstevel@tonic-gate 				CALLB_CPR_SAFE_END(&cpr_info, &cpr_lock);
43317c478bd9Sstevel@tonic-gate 				mutex_exit(&cpr_lock);
43327c478bd9Sstevel@tonic-gate 			}
43337c478bd9Sstevel@tonic-gate 		}
43347c478bd9Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
43357c478bd9Sstevel@tonic-gate 		nfs4_server_rele(sp);
43367c478bd9Sstevel@tonic-gate 		sp = NULL;
43377c478bd9Sstevel@tonic-gate 	}
43387c478bd9Sstevel@tonic-gate 
43397c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
43407c478bd9Sstevel@tonic-gate 	while (mi->mi_in_recovery != 0) {
43417c478bd9Sstevel@tonic-gate 		if (async_thread) {
43427c478bd9Sstevel@tonic-gate 			mutex_enter(&cpr_lock);
43437c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_BEGIN(&cpr_info);
43447c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_lock);
43457c478bd9Sstevel@tonic-gate 		}
43467c478bd9Sstevel@tonic-gate 		cv_wait(&mi->mi_cv_in_recov, &mi->mi_lock);
43477c478bd9Sstevel@tonic-gate 		if (async_thread) {
43487c478bd9Sstevel@tonic-gate 			mutex_enter(&cpr_lock);
43497c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(&cpr_info, &cpr_lock);
43507c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_lock);
43517c478bd9Sstevel@tonic-gate 		}
43527c478bd9Sstevel@tonic-gate 	}
43537c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
43547c478bd9Sstevel@tonic-gate 
4355eabd0450Sth 	/*
4356eabd0450Sth 	 * If we got an error, then do not nuke the
4357eabd0450Sth 	 * tree. Either the harvester is busy reclaiming
4358eabd0450Sth 	 * this node or we ran into some busy condition.
4359eabd0450Sth 	 *
4360eabd0450Sth 	 * The harvester will eventually come along and cleanup.
4361eabd0450Sth 	 * The only problem would be the root mount point.
4362eabd0450Sth 	 *
4363eabd0450Sth 	 * Since the busy node can occur for a variety
4364eabd0450Sth 	 * of reasons and can result in an entry staying
4365eabd0450Sth 	 * in df output but no longer accessible from the
4366eabd0450Sth 	 * directory tree, we are okay.
4367eabd0450Sth 	 */
4368eabd0450Sth 	if (!nfs4_ephemeral_umount(mi, flag, cr,
43692f172c55SRobert Thurlow 	    &must_unlock, &eph_tree))
4370eabd0450Sth 		nfs4_ephemeral_umount_activate(mi, &must_unlock,
43712f172c55SRobert Thurlow 		    &eph_tree);
4372b9238976Sth 
43737c478bd9Sstevel@tonic-gate 	/*
43747c478bd9Sstevel@tonic-gate 	 * The original purge of the dnlc via 'dounmount'
43757c478bd9Sstevel@tonic-gate 	 * doesn't guarantee that another dnlc entry was not
43767c478bd9Sstevel@tonic-gate 	 * added while we waitied for all outstanding OTW
43777c478bd9Sstevel@tonic-gate 	 * and recovery calls to finish.  So re-purge the
43787c478bd9Sstevel@tonic-gate 	 * dnlc now.
43797c478bd9Sstevel@tonic-gate 	 */
43807c478bd9Sstevel@tonic-gate 	(void) dnlc_purge_vfsp(vfsp, 0);
43817c478bd9Sstevel@tonic-gate 
43827c478bd9Sstevel@tonic-gate 	/*
43837c478bd9Sstevel@tonic-gate 	 * We need to explicitly stop the manager thread; the asyc worker
43847c478bd9Sstevel@tonic-gate 	 * threads can timeout and exit on their own.
43857c478bd9Sstevel@tonic-gate 	 */
438650a83466Sjwahlig 	mutex_enter(&mi->mi_async_lock);
438750a83466Sjwahlig 	mi->mi_max_threads = 0;
43880776f5e6SVallish Vaidyeshwara 	NFS4_WAKEALL_ASYNC_WORKERS(mi->mi_async_work_cv);
438950a83466Sjwahlig 	mutex_exit(&mi->mi_async_lock);
439050a83466Sjwahlig 	if (mi->mi_manager_thread)
439150a83466Sjwahlig 		nfs4_async_manager_stop(vfsp);
43927c478bd9Sstevel@tonic-gate 
43937c478bd9Sstevel@tonic-gate 	destroy_rtable4(vfsp, cr);
43947c478bd9Sstevel@tonic-gate 
43957c478bd9Sstevel@tonic-gate 	nfs4_remove_mi_from_server(mi, NULL);
43967c478bd9Sstevel@tonic-gate 
43977c478bd9Sstevel@tonic-gate 	if (async_thread) {
43987c478bd9Sstevel@tonic-gate 		mutex_enter(&cpr_lock);
43997c478bd9Sstevel@tonic-gate 		CALLB_CPR_EXIT(&cpr_info);	/* drops cpr_lock */
44007c478bd9Sstevel@tonic-gate 		mutex_destroy(&cpr_lock);
44017c478bd9Sstevel@tonic-gate 	}
440250a83466Sjwahlig 
440350a83466Sjwahlig 	removed = nfs4_mi_zonelist_remove(mi);
440450a83466Sjwahlig 	if (removed)
440550a83466Sjwahlig 		zone_rele(mi->mi_zone);
44067c478bd9Sstevel@tonic-gate }
44072f172c55SRobert Thurlow 
44082f172c55SRobert Thurlow /* Referral related sub-routines */
44092f172c55SRobert Thurlow 
44102f172c55SRobert Thurlow /* Freeup knetconfig */
44112f172c55SRobert Thurlow static void
44122f172c55SRobert Thurlow free_knconf_contents(struct knetconfig *k)
44132f172c55SRobert Thurlow {
44142f172c55SRobert Thurlow 	if (k == NULL)
44152f172c55SRobert Thurlow 		return;
44162f172c55SRobert Thurlow 	if (k->knc_protofmly)
44172f172c55SRobert Thurlow 		kmem_free(k->knc_protofmly, KNC_STRSIZE);
44182f172c55SRobert Thurlow 	if (k->knc_proto)
44192f172c55SRobert Thurlow 		kmem_free(k->knc_proto, KNC_STRSIZE);
44202f172c55SRobert Thurlow }
44212f172c55SRobert Thurlow 
44222f172c55SRobert Thurlow /*
44232f172c55SRobert Thurlow  * This updates newpath variable with exact name component from the
44242f172c55SRobert Thurlow  * path which gave us a NFS4ERR_MOVED error.
44252f172c55SRobert Thurlow  * If the path is /rp/aaa/bbb and nth value is 1, aaa is returned.
44262f172c55SRobert Thurlow  */
44272f172c55SRobert Thurlow static char *
44282f172c55SRobert Thurlow extract_referral_point(const char *svp, int nth)
44292f172c55SRobert Thurlow {
44302f172c55SRobert Thurlow 	int num_slashes = 0;
44312f172c55SRobert Thurlow 	const char *p;
44322f172c55SRobert Thurlow 	char *newpath = NULL;
44332f172c55SRobert Thurlow 	int i = 0;
44342f172c55SRobert Thurlow 
44352f172c55SRobert Thurlow 	newpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
44362f172c55SRobert Thurlow 	for (p = svp; *p; p++) {
44372f172c55SRobert Thurlow 		if (*p == '/')
44382f172c55SRobert Thurlow 			num_slashes++;
44392f172c55SRobert Thurlow 		if (num_slashes == nth + 1) {
44402f172c55SRobert Thurlow 			p++;
44412f172c55SRobert Thurlow 			while (*p != '/') {
44422f172c55SRobert Thurlow 				if (*p == '\0')
44432f172c55SRobert Thurlow 					break;
44442f172c55SRobert Thurlow 				newpath[i] = *p;
44452f172c55SRobert Thurlow 				i++;
44462f172c55SRobert Thurlow 				p++;
44472f172c55SRobert Thurlow 			}
44482f172c55SRobert Thurlow 			newpath[i++] = '\0';
44492f172c55SRobert Thurlow 			break;
44502f172c55SRobert Thurlow 		}
44512f172c55SRobert Thurlow 	}
44522f172c55SRobert Thurlow 	return (newpath);
44532f172c55SRobert Thurlow }
44542f172c55SRobert Thurlow 
44552f172c55SRobert Thurlow /*
44562f172c55SRobert Thurlow  * This sets up a new path in sv_path to do a lookup of the referral point.
44572f172c55SRobert Thurlow  * If the path is /rp/aaa/bbb and the referral point is aaa,
44582f172c55SRobert Thurlow  * this updates /rp/aaa. This path will be used to get referral
44592f172c55SRobert Thurlow  * location.
44602f172c55SRobert Thurlow  */
44612f172c55SRobert Thurlow static void
44622f172c55SRobert Thurlow setup_newsvpath(servinfo4_t *svp, int nth)
44632f172c55SRobert Thurlow {
44642f172c55SRobert Thurlow 	int num_slashes = 0, pathlen, i = 0;
44652f172c55SRobert Thurlow 	char *newpath, *p;
44662f172c55SRobert Thurlow 
44672f172c55SRobert Thurlow 	newpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
44682f172c55SRobert Thurlow 	for (p = svp->sv_path; *p; p++) {
44692f172c55SRobert Thurlow 		newpath[i] =  *p;
44702f172c55SRobert Thurlow 		if (*p == '/')
44712f172c55SRobert Thurlow 			num_slashes++;
44722f172c55SRobert Thurlow 		if (num_slashes == nth + 1) {
44732f172c55SRobert Thurlow 			newpath[i] = '\0';
44742f172c55SRobert Thurlow 			pathlen = strlen(newpath) + 1;
44752f172c55SRobert Thurlow 			kmem_free(svp->sv_path, svp->sv_pathlen);
44762f172c55SRobert Thurlow 			svp->sv_path = kmem_alloc(pathlen, KM_SLEEP);
44772f172c55SRobert Thurlow 			svp->sv_pathlen = pathlen;
44782f172c55SRobert Thurlow 			bcopy(newpath, svp->sv_path, pathlen);
44792f172c55SRobert Thurlow 			break;
44802f172c55SRobert Thurlow 		}
44812f172c55SRobert Thurlow 		i++;
44822f172c55SRobert Thurlow 	}
44832f172c55SRobert Thurlow 	kmem_free(newpath, MAXPATHLEN);
44842f172c55SRobert Thurlow }
4485