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  */
21*bbe876c0SMarcel Telka 
227c478bd9Sstevel@tonic-gate /*
23*bbe876c0SMarcel Telka  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
24d7de0ceaSRobert Harris  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  *	Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
297c478bd9Sstevel@tonic-gate  *	All Rights Reserved
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include <sys/param.h>
337c478bd9Sstevel@tonic-gate #include <sys/types.h>
347c478bd9Sstevel@tonic-gate #include <sys/systm.h>
357c478bd9Sstevel@tonic-gate #include <sys/cred.h>
367c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
37aa59c4cbSrsb #include <sys/vfs_opreg.h>
387c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
397c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
407c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
417c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
427c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
437c478bd9Sstevel@tonic-gate #include <sys/mount.h>
447c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
457c478bd9Sstevel@tonic-gate #include <sys/errno.h>
467c478bd9Sstevel@tonic-gate #include <sys/debug.h>
477c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
487c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
497c478bd9Sstevel@tonic-gate #include <sys/bootconf.h>
507c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
517c478bd9Sstevel@tonic-gate #include <sys/acl.h>
527c478bd9Sstevel@tonic-gate #include <sys/flock.h>
537c478bd9Sstevel@tonic-gate #include <sys/time.h>
547c478bd9Sstevel@tonic-gate #include <sys/disp.h>
557c478bd9Sstevel@tonic-gate #include <sys/policy.h>
567c478bd9Sstevel@tonic-gate #include <sys/socket.h>
577c478bd9Sstevel@tonic-gate #include <sys/netconfig.h>
587c478bd9Sstevel@tonic-gate #include <sys/dnlc.h>
597c478bd9Sstevel@tonic-gate #include <sys/list.h>
6045916cd2Sjpk #include <sys/mntent.h>
6145916cd2Sjpk #include <sys/tsol/label.h>
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate #include <rpc/types.h>
647c478bd9Sstevel@tonic-gate #include <rpc/auth.h>
657c478bd9Sstevel@tonic-gate #include <rpc/rpcsec_gss.h>
667c478bd9Sstevel@tonic-gate #include <rpc/clnt.h>
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate #include <nfs/nfs.h>
697c478bd9Sstevel@tonic-gate #include <nfs/nfs_clnt.h>
707c478bd9Sstevel@tonic-gate #include <nfs/mount.h>
717c478bd9Sstevel@tonic-gate #include <nfs/nfs_acl.h>
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h>
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate #include <nfs/nfs4.h>
767c478bd9Sstevel@tonic-gate #include <nfs/rnode4.h>
777c478bd9Sstevel@tonic-gate #include <nfs/nfs4_clnt.h>
7839d3e169Sevanl #include <sys/fs/autofs.h>
7939d3e169Sevanl 
802f172c55SRobert Thurlow #include <sys/sdt.h>
812f172c55SRobert Thurlow 
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate /*
847c478bd9Sstevel@tonic-gate  * Arguments passed to thread to free data structures from forced unmount.
857c478bd9Sstevel@tonic-gate  */
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate typedef struct {
88b9238976Sth 	vfs_t	*fm_vfsp;
89b9238976Sth 	int	fm_flag;
90b9238976Sth 	cred_t	*fm_cr;
917c478bd9Sstevel@tonic-gate } freemountargs_t;
927c478bd9Sstevel@tonic-gate 
93b9238976Sth static void	async_free_mount(vfs_t *, int, cred_t *);
94b9238976Sth static void	nfs4_free_mount(vfs_t *, int, cred_t *);
957c478bd9Sstevel@tonic-gate static void	nfs4_free_mount_thread(freemountargs_t *);
967c478bd9Sstevel@tonic-gate static int nfs4_chkdup_servinfo4(servinfo4_t *, servinfo4_t *);
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate /*
997c478bd9Sstevel@tonic-gate  * From rpcsec module (common/rpcsec).
1007c478bd9Sstevel@tonic-gate  */
1017c478bd9Sstevel@tonic-gate extern int sec_clnt_loadinfo(struct sec_data *, struct sec_data **, model_t);
1027c478bd9Sstevel@tonic-gate extern void sec_clnt_freeinfo(struct sec_data *);
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate /*
1057c478bd9Sstevel@tonic-gate  * The order and contents of this structure must be kept in sync with that of
1067c478bd9Sstevel@tonic-gate  * rfsreqcnt_v4_tmpl in nfs_stats.c
1077c478bd9Sstevel@tonic-gate  */
1087c478bd9Sstevel@tonic-gate static char *rfsnames_v4[] = {
1097c478bd9Sstevel@tonic-gate 	"null", "compound", "reserved",	"access", "close", "commit", "create",
1107c478bd9Sstevel@tonic-gate 	"delegpurge", "delegreturn", "getattr",	"getfh", "link", "lock",
1117c478bd9Sstevel@tonic-gate 	"lockt", "locku", "lookup", "lookupp", "nverify", "open", "openattr",
1127c478bd9Sstevel@tonic-gate 	"open_confirm",	"open_downgrade", "putfh", "putpubfh", "putrootfh",
1137c478bd9Sstevel@tonic-gate 	"read", "readdir", "readlink", "remove", "rename", "renew",
1147c478bd9Sstevel@tonic-gate 	"restorefh", "savefh", "secinfo", "setattr", "setclientid",
1157c478bd9Sstevel@tonic-gate 	"setclientid_confirm", "verify", "write"
1167c478bd9Sstevel@tonic-gate };
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate /*
1197c478bd9Sstevel@tonic-gate  * nfs4_max_mount_retry is the number of times the client will redrive
1207c478bd9Sstevel@tonic-gate  * a mount compound before giving up and returning failure.  The intent
1217c478bd9Sstevel@tonic-gate  * is to redrive mount compounds which fail NFS4ERR_STALE so that
1227c478bd9Sstevel@tonic-gate  * if a component of the server path being mounted goes stale, it can
1237c478bd9Sstevel@tonic-gate  * "recover" by redriving the mount compund (LOOKUP ops).  This recovery
1247c478bd9Sstevel@tonic-gate  * code is needed outside of the recovery framework because mount is a
1257c478bd9Sstevel@tonic-gate  * special case.  The client doesn't create vnodes/rnodes for components
1267c478bd9Sstevel@tonic-gate  * of the server path being mounted.  The recovery code recovers real
1277c478bd9Sstevel@tonic-gate  * client objects, not STALE FHs which map to components of the server
1287c478bd9Sstevel@tonic-gate  * path being mounted.
1297c478bd9Sstevel@tonic-gate  *
1307c478bd9Sstevel@tonic-gate  * We could just fail the mount on the first time, but that would
1317c478bd9Sstevel@tonic-gate  * instantly trigger failover (from nfs4_mount), and the client should
1327c478bd9Sstevel@tonic-gate  * try to re-lookup the STALE FH before doing failover.  The easiest
1337c478bd9Sstevel@tonic-gate  * way to "re-lookup" is to simply redrive the mount compound.
1347c478bd9Sstevel@tonic-gate  */
1357c478bd9Sstevel@tonic-gate static int nfs4_max_mount_retry = 2;
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate /*
1387c478bd9Sstevel@tonic-gate  * nfs4 vfs operations.
1397c478bd9Sstevel@tonic-gate  */
140b9238976Sth int		nfs4_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *);
1417c478bd9Sstevel@tonic-gate static int	nfs4_unmount(vfs_t *, int, cred_t *);
1427c478bd9Sstevel@tonic-gate static int	nfs4_root(vfs_t *, vnode_t **);
1437c478bd9Sstevel@tonic-gate static int	nfs4_statvfs(vfs_t *, struct statvfs64 *);
1447c478bd9Sstevel@tonic-gate static int	nfs4_sync(vfs_t *, short, cred_t *);
1457c478bd9Sstevel@tonic-gate static int	nfs4_vget(vfs_t *, vnode_t **, fid_t *);
1467c478bd9Sstevel@tonic-gate static int	nfs4_mountroot(vfs_t *, whymountroot_t);
1477c478bd9Sstevel@tonic-gate static void	nfs4_freevfs(vfs_t *);
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate static int	nfs4rootvp(vnode_t **, vfs_t *, struct servinfo4 *,
1507c478bd9Sstevel@tonic-gate 		    int, cred_t *, zone_t *);
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate vfsops_t	*nfs4_vfsops;
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate int nfs4_vfsinit(void);
1557c478bd9Sstevel@tonic-gate void nfs4_vfsfini(void);
1567c478bd9Sstevel@tonic-gate static void nfs4setclientid_init(void);
1577c478bd9Sstevel@tonic-gate static void nfs4setclientid_fini(void);
1587c478bd9Sstevel@tonic-gate static void nfs4setclientid_otw(mntinfo4_t *, servinfo4_t *,  cred_t *,
1597c478bd9Sstevel@tonic-gate 		struct nfs4_server *, nfs4_error_t *, int *);
1607c478bd9Sstevel@tonic-gate static void	destroy_nfs4_server(nfs4_server_t *);
1617c478bd9Sstevel@tonic-gate static void	remove_mi(nfs4_server_t *, mntinfo4_t *);
1627c478bd9Sstevel@tonic-gate 
163b9238976Sth extern void nfs4_ephemeral_init(void);
164b9238976Sth extern void nfs4_ephemeral_fini(void);
165b9238976Sth 
1662f172c55SRobert Thurlow /* referral related routines */
1672f172c55SRobert Thurlow static servinfo4_t *copy_svp(servinfo4_t *);
1682f172c55SRobert Thurlow static void free_knconf_contents(struct knetconfig *k);
1692f172c55SRobert Thurlow static char *extract_referral_point(const char *, int);
1702f172c55SRobert Thurlow static void setup_newsvpath(servinfo4_t *, int);
1712f172c55SRobert Thurlow static void update_servinfo4(servinfo4_t *, fs_location4 *,
1722f172c55SRobert Thurlow 		struct nfs_fsl_info *, char *, int);
1732f172c55SRobert Thurlow 
1747c478bd9Sstevel@tonic-gate /*
1757c478bd9Sstevel@tonic-gate  * Initialize the vfs structure
1767c478bd9Sstevel@tonic-gate  */
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate static int nfs4fstyp;
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate /*
1827c478bd9Sstevel@tonic-gate  * Debug variable to check for rdma based
1837c478bd9Sstevel@tonic-gate  * transport startup and cleanup. Controlled
1847c478bd9Sstevel@tonic-gate  * through /etc/system. Off by default.
1857c478bd9Sstevel@tonic-gate  */
1867c478bd9Sstevel@tonic-gate extern int rdma_debug;
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate int
1897c478bd9Sstevel@tonic-gate nfs4init(int fstyp, char *name)
1907c478bd9Sstevel@tonic-gate {
1917c478bd9Sstevel@tonic-gate 	static const fs_operation_def_t nfs4_vfsops_template[] = {
192aa59c4cbSrsb 		VFSNAME_MOUNT,		{ .vfs_mount = nfs4_mount },
193aa59c4cbSrsb 		VFSNAME_UNMOUNT,	{ .vfs_unmount = nfs4_unmount },
194aa59c4cbSrsb 		VFSNAME_ROOT,		{ .vfs_root = nfs4_root },
195aa59c4cbSrsb 		VFSNAME_STATVFS,	{ .vfs_statvfs = nfs4_statvfs },
196aa59c4cbSrsb 		VFSNAME_SYNC,		{ .vfs_sync = nfs4_sync },
197aa59c4cbSrsb 		VFSNAME_VGET,		{ .vfs_vget = nfs4_vget },
198aa59c4cbSrsb 		VFSNAME_MOUNTROOT,	{ .vfs_mountroot = nfs4_mountroot },
199aa59c4cbSrsb 		VFSNAME_FREEVFS,	{ .vfs_freevfs = nfs4_freevfs },
200aa59c4cbSrsb 		NULL,			NULL
2017c478bd9Sstevel@tonic-gate 	};
2027c478bd9Sstevel@tonic-gate 	int error;
2037c478bd9Sstevel@tonic-gate 
204b9238976Sth 	nfs4_vfsops = NULL;
205b9238976Sth 	nfs4_vnodeops = NULL;
206b9238976Sth 	nfs4_trigger_vnodeops = NULL;
207b9238976Sth 
2087c478bd9Sstevel@tonic-gate 	error = vfs_setfsops(fstyp, nfs4_vfsops_template, &nfs4_vfsops);
2097c478bd9Sstevel@tonic-gate 	if (error != 0) {
2107c478bd9Sstevel@tonic-gate 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
2117c478bd9Sstevel@tonic-gate 		    "nfs4init: bad vfs ops template");
212b9238976Sth 		goto out;
2137c478bd9Sstevel@tonic-gate 	}
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate 	error = vn_make_ops(name, nfs4_vnodeops_template, &nfs4_vnodeops);
2167c478bd9Sstevel@tonic-gate 	if (error != 0) {
2177c478bd9Sstevel@tonic-gate 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
2187c478bd9Sstevel@tonic-gate 		    "nfs4init: bad vnode ops template");
219b9238976Sth 		goto out;
2207c478bd9Sstevel@tonic-gate 	}
2217c478bd9Sstevel@tonic-gate 
222b9238976Sth 	error = vn_make_ops("nfs4_trigger", nfs4_trigger_vnodeops_template,
223b9238976Sth 	    &nfs4_trigger_vnodeops);
224b9238976Sth 	if (error != 0) {
225b9238976Sth 		zcmn_err(GLOBAL_ZONEID, CE_WARN,
226b9238976Sth 		    "nfs4init: bad trigger vnode ops template");
227b9238976Sth 		goto out;
228b9238976Sth 	}
2297c478bd9Sstevel@tonic-gate 
230b9238976Sth 	nfs4fstyp = fstyp;
2317c478bd9Sstevel@tonic-gate 	(void) nfs4_vfsinit();
2327c478bd9Sstevel@tonic-gate 	(void) nfs4_init_dot_entries();
2337c478bd9Sstevel@tonic-gate 
234b9238976Sth out:
235b9238976Sth 	if (error) {
236b9238976Sth 		if (nfs4_trigger_vnodeops != NULL)
237b9238976Sth 			vn_freevnodeops(nfs4_trigger_vnodeops);
238b9238976Sth 
239b9238976Sth 		if (nfs4_vnodeops != NULL)
240b9238976Sth 			vn_freevnodeops(nfs4_vnodeops);
241b9238976Sth 
242b9238976Sth 		(void) vfs_freevfsops_by_type(fstyp);
243b9238976Sth 	}
244b9238976Sth 
245b9238976Sth 	return (error);
2467c478bd9Sstevel@tonic-gate }
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate void
2497c478bd9Sstevel@tonic-gate nfs4fini(void)
2507c478bd9Sstevel@tonic-gate {
2517c478bd9Sstevel@tonic-gate 	(void) nfs4_destroy_dot_entries();
2527c478bd9Sstevel@tonic-gate 	nfs4_vfsfini();
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate /*
2567c478bd9Sstevel@tonic-gate  * Create a new sec_data structure to store AUTH_DH related data:
2577c478bd9Sstevel@tonic-gate  * netname, syncaddr, knetconfig. There is no AUTH_F_RPCTIMESYNC
2587c478bd9Sstevel@tonic-gate  * flag set for NFS V4 since we are avoiding to contact the rpcbind
2597c478bd9Sstevel@tonic-gate  * daemon and is using the IP time service (IPPORT_TIMESERVER).
2607c478bd9Sstevel@tonic-gate  *
2617c478bd9Sstevel@tonic-gate  * sec_data can be freed by sec_clnt_freeinfo().
2627c478bd9Sstevel@tonic-gate  */
263b9238976Sth static struct sec_data *
2647c478bd9Sstevel@tonic-gate create_authdh_data(char *netname, int nlen, struct netbuf *syncaddr,
2657c478bd9Sstevel@tonic-gate 		struct knetconfig *knconf) {
2667c478bd9Sstevel@tonic-gate 	struct sec_data *secdata;
2677c478bd9Sstevel@tonic-gate 	dh_k4_clntdata_t *data;
2687c478bd9Sstevel@tonic-gate 	char *pf, *p;
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 	if (syncaddr == NULL || syncaddr->buf == NULL || nlen == 0)
2717c478bd9Sstevel@tonic-gate 		return (NULL);
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate 	secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP);
2747c478bd9Sstevel@tonic-gate 	secdata->flags = 0;
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	data = kmem_alloc(sizeof (*data), KM_SLEEP);
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 	data->syncaddr.maxlen = syncaddr->maxlen;
2797c478bd9Sstevel@tonic-gate 	data->syncaddr.len = syncaddr->len;
2807c478bd9Sstevel@tonic-gate 	data->syncaddr.buf = (char *)kmem_alloc(syncaddr->len, KM_SLEEP);
2817c478bd9Sstevel@tonic-gate 	bcopy(syncaddr->buf, data->syncaddr.buf, syncaddr->len);
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 	/*
2847c478bd9Sstevel@tonic-gate 	 * duplicate the knconf information for the
2857c478bd9Sstevel@tonic-gate 	 * new opaque data.
2867c478bd9Sstevel@tonic-gate 	 */
2877c478bd9Sstevel@tonic-gate 	data->knconf = kmem_alloc(sizeof (*knconf), KM_SLEEP);
2887c478bd9Sstevel@tonic-gate 	*data->knconf = *knconf;
2897c478bd9Sstevel@tonic-gate 	pf = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
2907c478bd9Sstevel@tonic-gate 	p = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
2917c478bd9Sstevel@tonic-gate 	bcopy(knconf->knc_protofmly, pf, KNC_STRSIZE);
2927c478bd9Sstevel@tonic-gate 	bcopy(knconf->knc_proto, p, KNC_STRSIZE);
2937c478bd9Sstevel@tonic-gate 	data->knconf->knc_protofmly = pf;
2947c478bd9Sstevel@tonic-gate 	data->knconf->knc_proto = p;
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	/* move server netname to the sec_data structure */
2977c478bd9Sstevel@tonic-gate 	data->netname = kmem_alloc(nlen, KM_SLEEP);
2987c478bd9Sstevel@tonic-gate 	bcopy(netname, data->netname, nlen);
2997c478bd9Sstevel@tonic-gate 	data->netnamelen = (int)nlen;
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 	secdata->secmod = AUTH_DH;
3027c478bd9Sstevel@tonic-gate 	secdata->rpcflavor = AUTH_DH;
3037c478bd9Sstevel@tonic-gate 	secdata->data = (caddr_t)data;
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	return (secdata);
3067c478bd9Sstevel@tonic-gate }
3077c478bd9Sstevel@tonic-gate 
308b9238976Sth /*
309b9238976Sth  * Returns (deep) copy of sec_data_t. Allocates all memory required; caller
310b9238976Sth  * is responsible for freeing.
311b9238976Sth  */
312b9238976Sth sec_data_t *
313b9238976Sth copy_sec_data(sec_data_t *fsecdata) {
314b9238976Sth 	sec_data_t *tsecdata;
315b9238976Sth 
316b9238976Sth 	if (fsecdata == NULL)
317b9238976Sth 		return (NULL);
318b9238976Sth 
319b9238976Sth 	if (fsecdata->rpcflavor == AUTH_DH) {
320b9238976Sth 		dh_k4_clntdata_t *fdata = (dh_k4_clntdata_t *)fsecdata->data;
321b9238976Sth 
322b9238976Sth 		if (fdata == NULL)
323b9238976Sth 			return (NULL);
324b9238976Sth 
325b9238976Sth 		tsecdata = (sec_data_t *)create_authdh_data(fdata->netname,
326b9238976Sth 		    fdata->netnamelen, &fdata->syncaddr, fdata->knconf);
327b9238976Sth 
328b9238976Sth 		return (tsecdata);
329b9238976Sth 	}
330b9238976Sth 
331b9238976Sth 	tsecdata = kmem_zalloc(sizeof (sec_data_t), KM_SLEEP);
332b9238976Sth 
333b9238976Sth 	tsecdata->secmod = fsecdata->secmod;
334b9238976Sth 	tsecdata->rpcflavor = fsecdata->rpcflavor;
335b9238976Sth 	tsecdata->flags = fsecdata->flags;
336b9238976Sth 	tsecdata->uid = fsecdata->uid;
337b9238976Sth 
338b9238976Sth 	if (fsecdata->rpcflavor == RPCSEC_GSS) {
339b9238976Sth 		gss_clntdata_t *gcd = (gss_clntdata_t *)fsecdata->data;
340b9238976Sth 
341b9238976Sth 		tsecdata->data = (caddr_t)copy_sec_data_gss(gcd);
342b9238976Sth 	} else {
343b9238976Sth 		tsecdata->data = NULL;
344b9238976Sth 	}
345b9238976Sth 
346b9238976Sth 	return (tsecdata);
347b9238976Sth }
348b9238976Sth 
349b9238976Sth gss_clntdata_t *
350b9238976Sth copy_sec_data_gss(gss_clntdata_t *fdata)
351b9238976Sth {
352b9238976Sth 	gss_clntdata_t *tdata;
353b9238976Sth 
354b9238976Sth 	if (fdata == NULL)
355b9238976Sth 		return (NULL);
356b9238976Sth 
357b9238976Sth 	tdata = kmem_zalloc(sizeof (gss_clntdata_t), KM_SLEEP);
358b9238976Sth 
359b9238976Sth 	tdata->mechanism.length = fdata->mechanism.length;
360b9238976Sth 	tdata->mechanism.elements = kmem_zalloc(fdata->mechanism.length,
361b9238976Sth 	    KM_SLEEP);
362b9238976Sth 	bcopy(fdata->mechanism.elements, tdata->mechanism.elements,
363b9238976Sth 	    fdata->mechanism.length);
364b9238976Sth 
365b9238976Sth 	tdata->service = fdata->service;
366b9238976Sth 
367b9238976Sth 	(void) strcpy(tdata->uname, fdata->uname);
368b9238976Sth 	(void) strcpy(tdata->inst, fdata->inst);
369b9238976Sth 	(void) strcpy(tdata->realm, fdata->realm);
370b9238976Sth 
371b9238976Sth 	tdata->qop = fdata->qop;
372b9238976Sth 
373b9238976Sth 	return (tdata);
374b9238976Sth }
375b9238976Sth 
3767c478bd9Sstevel@tonic-gate static int
3777c478bd9Sstevel@tonic-gate nfs4_chkdup_servinfo4(servinfo4_t *svp_head, servinfo4_t *svp)
3787c478bd9Sstevel@tonic-gate {
3797c478bd9Sstevel@tonic-gate 	servinfo4_t *si;
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	/*
3827c478bd9Sstevel@tonic-gate 	 * Iterate over the servinfo4 list to make sure
3837c478bd9Sstevel@tonic-gate 	 * we do not have a duplicate. Skip any servinfo4
3847c478bd9Sstevel@tonic-gate 	 * that has been marked "NOT IN USE"
3857c478bd9Sstevel@tonic-gate 	 */
3867c478bd9Sstevel@tonic-gate 	for (si = svp_head; si; si = si->sv_next) {
3877c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&si->sv_lock, RW_READER, 0);
3887c478bd9Sstevel@tonic-gate 		if (si->sv_flags & SV4_NOTINUSE) {
3897c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&si->sv_lock);
3907c478bd9Sstevel@tonic-gate 			continue;
3917c478bd9Sstevel@tonic-gate 		}
3927c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&si->sv_lock);
3937c478bd9Sstevel@tonic-gate 		if (si == svp)
3947c478bd9Sstevel@tonic-gate 			continue;
3957c478bd9Sstevel@tonic-gate 		if (si->sv_addr.len == svp->sv_addr.len &&
3967c478bd9Sstevel@tonic-gate 		    strcmp(si->sv_knconf->knc_protofmly,
397b9238976Sth 		    svp->sv_knconf->knc_protofmly) == 0 &&
3987c478bd9Sstevel@tonic-gate 		    bcmp(si->sv_addr.buf, svp->sv_addr.buf,
399b9238976Sth 		    si->sv_addr.len) == 0) {
4007c478bd9Sstevel@tonic-gate 			/* it's a duplicate */
4017c478bd9Sstevel@tonic-gate 			return (1);
4027c478bd9Sstevel@tonic-gate 		}
4037c478bd9Sstevel@tonic-gate 	}
4047c478bd9Sstevel@tonic-gate 	/* it's not a duplicate */
4057c478bd9Sstevel@tonic-gate 	return (0);
4067c478bd9Sstevel@tonic-gate }
4077c478bd9Sstevel@tonic-gate 
40839d3e169Sevanl void
40939d3e169Sevanl nfs4_free_args(struct nfs_args *nargs)
41039d3e169Sevanl {
41139d3e169Sevanl 	if (nargs->knconf) {
41239d3e169Sevanl 		if (nargs->knconf->knc_protofmly)
41339d3e169Sevanl 			kmem_free(nargs->knconf->knc_protofmly,
414b9238976Sth 			    KNC_STRSIZE);
41539d3e169Sevanl 		if (nargs->knconf->knc_proto)
41639d3e169Sevanl 			kmem_free(nargs->knconf->knc_proto, KNC_STRSIZE);
41739d3e169Sevanl 		kmem_free(nargs->knconf, sizeof (*nargs->knconf));
41839d3e169Sevanl 		nargs->knconf = NULL;
41939d3e169Sevanl 	}
42039d3e169Sevanl 
42139d3e169Sevanl 	if (nargs->fh) {
42239d3e169Sevanl 		kmem_free(nargs->fh, strlen(nargs->fh) + 1);
42339d3e169Sevanl 		nargs->fh = NULL;
42439d3e169Sevanl 	}
42539d3e169Sevanl 
42639d3e169Sevanl 	if (nargs->hostname) {
42739d3e169Sevanl 		kmem_free(nargs->hostname, strlen(nargs->hostname) + 1);
42839d3e169Sevanl 		nargs->hostname = NULL;
42939d3e169Sevanl 	}
43039d3e169Sevanl 
43139d3e169Sevanl 	if (nargs->addr) {
43239d3e169Sevanl 		if (nargs->addr->buf) {
43339d3e169Sevanl 			ASSERT(nargs->addr->len);
43439d3e169Sevanl 			kmem_free(nargs->addr->buf, nargs->addr->len);
43539d3e169Sevanl 		}
43639d3e169Sevanl 		kmem_free(nargs->addr, sizeof (struct netbuf));
43739d3e169Sevanl 		nargs->addr = NULL;
43839d3e169Sevanl 	}
43939d3e169Sevanl 
44039d3e169Sevanl 	if (nargs->syncaddr) {
44139d3e169Sevanl 		ASSERT(nargs->syncaddr->len);
44239d3e169Sevanl 		if (nargs->syncaddr->buf) {
44339d3e169Sevanl 			ASSERT(nargs->syncaddr->len);
44439d3e169Sevanl 			kmem_free(nargs->syncaddr->buf, nargs->syncaddr->len);
44539d3e169Sevanl 		}
44639d3e169Sevanl 		kmem_free(nargs->syncaddr, sizeof (struct netbuf));
44739d3e169Sevanl 		nargs->syncaddr = NULL;
44839d3e169Sevanl 	}
44939d3e169Sevanl 
45039d3e169Sevanl 	if (nargs->netname) {
45139d3e169Sevanl 		kmem_free(nargs->netname, strlen(nargs->netname) + 1);
45239d3e169Sevanl 		nargs->netname = NULL;
45339d3e169Sevanl 	}
45439d3e169Sevanl 
45539d3e169Sevanl 	if (nargs->nfs_ext_u.nfs_extA.secdata) {
45639d3e169Sevanl 		sec_clnt_freeinfo(
457b9238976Sth 		    nargs->nfs_ext_u.nfs_extA.secdata);
45839d3e169Sevanl 		nargs->nfs_ext_u.nfs_extA.secdata = NULL;
45939d3e169Sevanl 	}
46039d3e169Sevanl }
46139d3e169Sevanl 
46239d3e169Sevanl 
46339d3e169Sevanl int
46439d3e169Sevanl nfs4_copyin(char *data, int datalen, struct nfs_args *nargs)
46539d3e169Sevanl {
46639d3e169Sevanl 
46739d3e169Sevanl 	int error;
46839d3e169Sevanl 	size_t hlen;			/* length of hostname */
46939d3e169Sevanl 	size_t nlen;			/* length of netname */
47039d3e169Sevanl 	char netname[MAXNETNAMELEN+1];	/* server's netname */
47139d3e169Sevanl 	struct netbuf addr;		/* server's address */
47239d3e169Sevanl 	struct netbuf syncaddr;		/* AUTH_DES time sync addr */
47339d3e169Sevanl 	struct knetconfig *knconf;		/* transport structure */
47439d3e169Sevanl 	struct sec_data *secdata = NULL;	/* security data */
47539d3e169Sevanl 	STRUCT_DECL(nfs_args, args);		/* nfs mount arguments */
47639d3e169Sevanl 	STRUCT_DECL(knetconfig, knconf_tmp);
47739d3e169Sevanl 	STRUCT_DECL(netbuf, addr_tmp);
47839d3e169Sevanl 	int flags;
47939d3e169Sevanl 	char *p, *pf;
48039d3e169Sevanl 	struct pathname pn;
48139d3e169Sevanl 	char *userbufptr;
48239d3e169Sevanl 
48339d3e169Sevanl 
48439d3e169Sevanl 	bzero(nargs, sizeof (*nargs));
48539d3e169Sevanl 
48639d3e169Sevanl 	STRUCT_INIT(args, get_udatamodel());
48739d3e169Sevanl 	bzero(STRUCT_BUF(args), SIZEOF_STRUCT(nfs_args, DATAMODEL_NATIVE));
48839d3e169Sevanl 	if (copyin(data, STRUCT_BUF(args), MIN(datalen,
48939d3e169Sevanl 	    STRUCT_SIZE(args))))
49039d3e169Sevanl 		return (EFAULT);
49139d3e169Sevanl 
49239d3e169Sevanl 	nargs->wsize = STRUCT_FGET(args, wsize);
49339d3e169Sevanl 	nargs->rsize = STRUCT_FGET(args, rsize);
49439d3e169Sevanl 	nargs->timeo = STRUCT_FGET(args, timeo);
49539d3e169Sevanl 	nargs->retrans = STRUCT_FGET(args, retrans);
49639d3e169Sevanl 	nargs->acregmin = STRUCT_FGET(args, acregmin);
49739d3e169Sevanl 	nargs->acregmax = STRUCT_FGET(args, acregmax);
49839d3e169Sevanl 	nargs->acdirmin = STRUCT_FGET(args, acdirmin);
49939d3e169Sevanl 	nargs->acdirmax = STRUCT_FGET(args, acdirmax);
50039d3e169Sevanl 
50139d3e169Sevanl 	flags = STRUCT_FGET(args, flags);
50239d3e169Sevanl 	nargs->flags = flags;
50339d3e169Sevanl 
50439d3e169Sevanl 	addr.buf = NULL;
50539d3e169Sevanl 	syncaddr.buf = NULL;
50639d3e169Sevanl 
50739d3e169Sevanl 
50839d3e169Sevanl 	/*
50939d3e169Sevanl 	 * Allocate space for a knetconfig structure and
51039d3e169Sevanl 	 * its strings and copy in from user-land.
51139d3e169Sevanl 	 */
51239d3e169Sevanl 	knconf = kmem_zalloc(sizeof (*knconf), KM_SLEEP);
51339d3e169Sevanl 	STRUCT_INIT(knconf_tmp, get_udatamodel());
51439d3e169Sevanl 	if (copyin(STRUCT_FGETP(args, knconf), STRUCT_BUF(knconf_tmp),
51539d3e169Sevanl 	    STRUCT_SIZE(knconf_tmp))) {
51639d3e169Sevanl 		kmem_free(knconf, sizeof (*knconf));
51739d3e169Sevanl 		return (EFAULT);
51839d3e169Sevanl 	}
51939d3e169Sevanl 
52039d3e169Sevanl 	knconf->knc_semantics = STRUCT_FGET(knconf_tmp, knc_semantics);
52139d3e169Sevanl 	knconf->knc_protofmly = STRUCT_FGETP(knconf_tmp, knc_protofmly);
52239d3e169Sevanl 	knconf->knc_proto = STRUCT_FGETP(knconf_tmp, knc_proto);
52339d3e169Sevanl 	if (get_udatamodel() != DATAMODEL_LP64) {
52439d3e169Sevanl 		knconf->knc_rdev = expldev(STRUCT_FGET(knconf_tmp, knc_rdev));
52539d3e169Sevanl 	} else {
52639d3e169Sevanl 		knconf->knc_rdev = STRUCT_FGET(knconf_tmp, knc_rdev);
52739d3e169Sevanl 	}
52839d3e169Sevanl 
52939d3e169Sevanl 	pf = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
53039d3e169Sevanl 	p = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
53139d3e169Sevanl 	error = copyinstr(knconf->knc_protofmly, pf, KNC_STRSIZE, NULL);
53239d3e169Sevanl 	if (error) {
53339d3e169Sevanl 		kmem_free(pf, KNC_STRSIZE);
53439d3e169Sevanl 		kmem_free(p, KNC_STRSIZE);
53539d3e169Sevanl 		kmem_free(knconf, sizeof (*knconf));
53639d3e169Sevanl 		return (error);
53739d3e169Sevanl 	}
53839d3e169Sevanl 
53939d3e169Sevanl 	error = copyinstr(knconf->knc_proto, p, KNC_STRSIZE, NULL);
54039d3e169Sevanl 	if (error) {
54139d3e169Sevanl 		kmem_free(pf, KNC_STRSIZE);
54239d3e169Sevanl 		kmem_free(p, KNC_STRSIZE);
54339d3e169Sevanl 		kmem_free(knconf, sizeof (*knconf));
54439d3e169Sevanl 		return (error);
54539d3e169Sevanl 	}
54639d3e169Sevanl 
54739d3e169Sevanl 
54839d3e169Sevanl 	knconf->knc_protofmly = pf;
54939d3e169Sevanl 	knconf->knc_proto = p;
55039d3e169Sevanl 
55139d3e169Sevanl 	nargs->knconf = knconf;
55239d3e169Sevanl 
55339d3e169Sevanl 	/*
55439d3e169Sevanl 	 * Get server address
55539d3e169Sevanl 	 */
55639d3e169Sevanl 	STRUCT_INIT(addr_tmp, get_udatamodel());
55739d3e169Sevanl 	if (copyin(STRUCT_FGETP(args, addr), STRUCT_BUF(addr_tmp),
55839d3e169Sevanl 	    STRUCT_SIZE(addr_tmp))) {
55939d3e169Sevanl 		error = EFAULT;
56039d3e169Sevanl 		goto errout;
56139d3e169Sevanl 	}
56239d3e169Sevanl 
5637e450cbcSJames Wahlig 	nargs->addr = kmem_zalloc(sizeof (struct netbuf), KM_SLEEP);
56439d3e169Sevanl 	userbufptr = STRUCT_FGETP(addr_tmp, buf);
56539d3e169Sevanl 	addr.len = STRUCT_FGET(addr_tmp, len);
56639d3e169Sevanl 	addr.buf = kmem_alloc(addr.len, KM_SLEEP);
56739d3e169Sevanl 	addr.maxlen = addr.len;
56839d3e169Sevanl 	if (copyin(userbufptr, addr.buf, addr.len)) {
56939d3e169Sevanl 		kmem_free(addr.buf, addr.len);
57039d3e169Sevanl 		error = EFAULT;
57139d3e169Sevanl 		goto errout;
57239d3e169Sevanl 	}
57339d3e169Sevanl 	bcopy(&addr, nargs->addr, sizeof (struct netbuf));
57439d3e169Sevanl 
57539d3e169Sevanl 	/*
57639d3e169Sevanl 	 * Get the root fhandle
57739d3e169Sevanl 	 */
57839d3e169Sevanl 	error = pn_get(STRUCT_FGETP(args, fh), UIO_USERSPACE, &pn);
57939d3e169Sevanl 	if (error)
58039d3e169Sevanl 		goto errout;
58139d3e169Sevanl 
58239d3e169Sevanl 	/* Volatile fh: keep server paths, so use actual-size strings */
58339d3e169Sevanl 	nargs->fh = kmem_alloc(pn.pn_pathlen + 1, KM_SLEEP);
58439d3e169Sevanl 	bcopy(pn.pn_path, nargs->fh, pn.pn_pathlen);
58539d3e169Sevanl 	nargs->fh[pn.pn_pathlen] = '\0';
58639d3e169Sevanl 	pn_free(&pn);
58739d3e169Sevanl 
58839d3e169Sevanl 
58939d3e169Sevanl 	/*
59039d3e169Sevanl 	 * Get server's hostname
59139d3e169Sevanl 	 */
59239d3e169Sevanl 	if (flags & NFSMNT_HOSTNAME) {
59339d3e169Sevanl 		error = copyinstr(STRUCT_FGETP(args, hostname),
594b9238976Sth 		    netname, sizeof (netname), &hlen);
59539d3e169Sevanl 		if (error)
59639d3e169Sevanl 			goto errout;
59739d3e169Sevanl 		nargs->hostname = kmem_zalloc(hlen, KM_SLEEP);
59839d3e169Sevanl 		(void) strcpy(nargs->hostname, netname);
59939d3e169Sevanl 
60039d3e169Sevanl 	} else {
60139d3e169Sevanl 		nargs->hostname = NULL;
60239d3e169Sevanl 	}
60339d3e169Sevanl 
60439d3e169Sevanl 
60539d3e169Sevanl 	/*
60639d3e169Sevanl 	 * If there are syncaddr and netname data, load them in. This is
60739d3e169Sevanl 	 * to support data needed for NFSV4 when AUTH_DH is the negotiated
60839d3e169Sevanl 	 * flavor via SECINFO. (instead of using MOUNT protocol in V3).
60939d3e169Sevanl 	 */
61039d3e169Sevanl 	netname[0] = '\0';
61139d3e169Sevanl 	if (flags & NFSMNT_SECURE) {
61239d3e169Sevanl 
61339d3e169Sevanl 		/* get syncaddr */
61439d3e169Sevanl 		STRUCT_INIT(addr_tmp, get_udatamodel());
61539d3e169Sevanl 		if (copyin(STRUCT_FGETP(args, syncaddr), STRUCT_BUF(addr_tmp),
616b9238976Sth 		    STRUCT_SIZE(addr_tmp))) {
61739d3e169Sevanl 			error = EINVAL;
61839d3e169Sevanl 			goto errout;
61939d3e169Sevanl 		}
62039d3e169Sevanl 		userbufptr = STRUCT_FGETP(addr_tmp, buf);
62139d3e169Sevanl 		syncaddr.len = STRUCT_FGET(addr_tmp, len);
62239d3e169Sevanl 		syncaddr.buf = kmem_alloc(syncaddr.len, KM_SLEEP);
62339d3e169Sevanl 		syncaddr.maxlen = syncaddr.len;
62439d3e169Sevanl 		if (copyin(userbufptr, syncaddr.buf, syncaddr.len)) {
62539d3e169Sevanl 			kmem_free(syncaddr.buf, syncaddr.len);
62639d3e169Sevanl 			error = EFAULT;
62739d3e169Sevanl 			goto errout;
62839d3e169Sevanl 		}
62939d3e169Sevanl 
63039d3e169Sevanl 		nargs->syncaddr = kmem_alloc(sizeof (struct netbuf), KM_SLEEP);
63139d3e169Sevanl 		bcopy(&syncaddr, nargs->syncaddr, sizeof (struct netbuf));
63239d3e169Sevanl 
63322d5e933Skr 		/* get server's netname */
63422d5e933Skr 		if (copyinstr(STRUCT_FGETP(args, netname), netname,
635b9238976Sth 		    sizeof (netname), &nlen)) {
63622d5e933Skr 			error = EFAULT;
63722d5e933Skr 			goto errout;
63822d5e933Skr 		}
63922d5e933Skr 
64022d5e933Skr 		netname[nlen] = '\0';
64122d5e933Skr 		nargs->netname = kmem_zalloc(nlen, KM_SLEEP);
64222d5e933Skr 		(void) strcpy(nargs->netname, netname);
64322d5e933Skr 	}
64439d3e169Sevanl 
64539d3e169Sevanl 	/*
64639d3e169Sevanl 	 * Get the extention data which has the security data structure.
64739d3e169Sevanl 	 * This includes data for AUTH_SYS as well.
64839d3e169Sevanl 	 */
64939d3e169Sevanl 	if (flags & NFSMNT_NEWARGS) {
65039d3e169Sevanl 		nargs->nfs_args_ext = STRUCT_FGET(args, nfs_args_ext);
65139d3e169Sevanl 		if (nargs->nfs_args_ext == NFS_ARGS_EXTA ||
652b9238976Sth 		    nargs->nfs_args_ext == NFS_ARGS_EXTB) {
65339d3e169Sevanl 			/*
65439d3e169Sevanl 			 * Indicating the application is using the new
65539d3e169Sevanl 			 * sec_data structure to pass in the security
65639d3e169Sevanl 			 * data.
65739d3e169Sevanl 			 */
65839d3e169Sevanl 			if (STRUCT_FGETP(args,
65939d3e169Sevanl 			    nfs_ext_u.nfs_extA.secdata) != NULL) {
66039d3e169Sevanl 				error = sec_clnt_loadinfo(
66139d3e169Sevanl 				    (struct sec_data *)STRUCT_FGETP(args,
662b9238976Sth 				    nfs_ext_u.nfs_extA.secdata),
66339d3e169Sevanl 				    &secdata, get_udatamodel());
66439d3e169Sevanl 			}
66539d3e169Sevanl 			nargs->nfs_ext_u.nfs_extA.secdata = secdata;
66639d3e169Sevanl 		}
66739d3e169Sevanl 	}
66839d3e169Sevanl 
66939d3e169Sevanl 	if (error)
67039d3e169Sevanl 		goto errout;
67139d3e169Sevanl 
67239d3e169Sevanl 	/*
67339d3e169Sevanl 	 * Failover support:
67439d3e169Sevanl 	 *
67539d3e169Sevanl 	 * We may have a linked list of nfs_args structures,
67639d3e169Sevanl 	 * which means the user is looking for failover.  If
67739d3e169Sevanl 	 * the mount is either not "read-only" or "soft",
67839d3e169Sevanl 	 * we want to bail out with EINVAL.
67939d3e169Sevanl 	 */
68039d3e169Sevanl 	if (nargs->nfs_args_ext == NFS_ARGS_EXTB)
68139d3e169Sevanl 		nargs->nfs_ext_u.nfs_extB.next =
682b9238976Sth 		    STRUCT_FGETP(args, nfs_ext_u.nfs_extB.next);
68339d3e169Sevanl 
68439d3e169Sevanl errout:
68539d3e169Sevanl 	if (error)
68639d3e169Sevanl 		nfs4_free_args(nargs);
68739d3e169Sevanl 
68839d3e169Sevanl 	return (error);
68939d3e169Sevanl }
69039d3e169Sevanl 
69139d3e169Sevanl 
6927c478bd9Sstevel@tonic-gate /*
6937c478bd9Sstevel@tonic-gate  * nfs mount vfsop
6947c478bd9Sstevel@tonic-gate  * Set up mount info record and attach it to vfs struct.
6957c478bd9Sstevel@tonic-gate  */
696b9238976Sth int
6977c478bd9Sstevel@tonic-gate nfs4_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
6987c478bd9Sstevel@tonic-gate {
6997c478bd9Sstevel@tonic-gate 	char *data = uap->dataptr;
7007c478bd9Sstevel@tonic-gate 	int error;
7017c478bd9Sstevel@tonic-gate 	vnode_t *rtvp;			/* the server's root */
7027c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;			/* mount info, pointed at by vfs */
7037c478bd9Sstevel@tonic-gate 	struct knetconfig *rdma_knconf;	/* rdma transport structure */
7047c478bd9Sstevel@tonic-gate 	rnode4_t *rp;
7057c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp;		/* nfs server info */
7067c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp_tail = NULL; /* previous nfs server info */
7077c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp_head;	/* first nfs server info */
7087c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp_2ndlast;	/* 2nd last in server info list */
7097c478bd9Sstevel@tonic-gate 	struct sec_data *secdata;	/* security data */
71039d3e169Sevanl 	struct nfs_args *args = NULL;
71150a83466Sjwahlig 	int flags, addr_type, removed;
712108322fbScarlsonj 	zone_t *zone = nfs_zone();
7137c478bd9Sstevel@tonic-gate 	nfs4_error_t n4e;
71445916cd2Sjpk 	zone_t *mntzone = NULL;
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
7177c478bd9Sstevel@tonic-gate 		return (EPERM);
7187c478bd9Sstevel@tonic-gate 	if (mvp->v_type != VDIR)
7197c478bd9Sstevel@tonic-gate 		return (ENOTDIR);
720b9238976Sth 
7217c478bd9Sstevel@tonic-gate 	/*
7227c478bd9Sstevel@tonic-gate 	 * get arguments
7237c478bd9Sstevel@tonic-gate 	 *
7247c478bd9Sstevel@tonic-gate 	 * nfs_args is now versioned and is extensible, so
7257c478bd9Sstevel@tonic-gate 	 * uap->datalen might be different from sizeof (args)
7267c478bd9Sstevel@tonic-gate 	 * in a compatible situation.
7277c478bd9Sstevel@tonic-gate 	 */
7287c478bd9Sstevel@tonic-gate more:
72939d3e169Sevanl 	if (!(uap->flags & MS_SYSSPACE)) {
73039d3e169Sevanl 		if (args == NULL)
73139d3e169Sevanl 			args = kmem_zalloc(sizeof (struct nfs_args), KM_SLEEP);
73239d3e169Sevanl 		else
73339d3e169Sevanl 			nfs4_free_args(args);
73439d3e169Sevanl 		error = nfs4_copyin(data, uap->datalen, args);
73539d3e169Sevanl 		if (error) {
73639d3e169Sevanl 			if (args) {
73739d3e169Sevanl 				kmem_free(args, sizeof (*args));
73839d3e169Sevanl 			}
73939d3e169Sevanl 			return (error);
74039d3e169Sevanl 		}
74139d3e169Sevanl 	} else {
74239d3e169Sevanl 		args = (struct nfs_args *)data;
74339d3e169Sevanl 	}
7447c478bd9Sstevel@tonic-gate 
74539d3e169Sevanl 	flags = args->flags;
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate 	/*
7487c478bd9Sstevel@tonic-gate 	 * If the request changes the locking type, disallow the remount,
7497c478bd9Sstevel@tonic-gate 	 * because it's questionable whether we can transfer the
7507c478bd9Sstevel@tonic-gate 	 * locking state correctly.
7517c478bd9Sstevel@tonic-gate 	 */
7527c478bd9Sstevel@tonic-gate 	if (uap->flags & MS_REMOUNT) {
75339d3e169Sevanl 		if (!(uap->flags & MS_SYSSPACE)) {
75439d3e169Sevanl 			nfs4_free_args(args);
75539d3e169Sevanl 			kmem_free(args, sizeof (*args));
75639d3e169Sevanl 		}
7577c478bd9Sstevel@tonic-gate 		if ((mi = VFTOMI4(vfsp)) != NULL) {
7587c478bd9Sstevel@tonic-gate 			uint_t new_mi_llock;
7597c478bd9Sstevel@tonic-gate 			uint_t old_mi_llock;
7607c478bd9Sstevel@tonic-gate 			new_mi_llock = (flags & NFSMNT_LLOCK) ? 1 : 0;
7617c478bd9Sstevel@tonic-gate 			old_mi_llock = (mi->mi_flags & MI4_LLOCK) ? 1 : 0;
7627c478bd9Sstevel@tonic-gate 			if (old_mi_llock != new_mi_llock)
7637c478bd9Sstevel@tonic-gate 				return (EBUSY);
7647c478bd9Sstevel@tonic-gate 		}
7657c478bd9Sstevel@tonic-gate 		return (0);
7667c478bd9Sstevel@tonic-gate 	}
7677c478bd9Sstevel@tonic-gate 
768b9238976Sth 	/*
769b9238976Sth 	 * For ephemeral mount trigger stub vnodes, we have two problems
770b9238976Sth 	 * to solve: racing threads will likely fail the v_count check, and
771b9238976Sth 	 * we want only one to proceed with the mount.
772b9238976Sth 	 *
773b9238976Sth 	 * For stubs, if the mount has already occurred (via a racing thread),
774b9238976Sth 	 * just return success. If not, skip the v_count check and proceed.
775b9238976Sth 	 * Note that we are already serialised at this point.
776b9238976Sth 	 */
7777c478bd9Sstevel@tonic-gate 	mutex_enter(&mvp->v_lock);
778b9238976Sth 	if (vn_matchops(mvp, nfs4_trigger_vnodeops)) {
779b9238976Sth 		/* mntpt is a v4 stub vnode */
780b9238976Sth 		ASSERT(RP_ISSTUB(VTOR4(mvp)));
781b9238976Sth 		ASSERT(!(uap->flags & MS_OVERLAY));
782b9238976Sth 		ASSERT(!(mvp->v_flag & VROOT));
783b9238976Sth 		if (vn_mountedvfs(mvp) != NULL) {
784b9238976Sth 			/* ephemeral mount has already occurred */
785b9238976Sth 			ASSERT(uap->flags & MS_SYSSPACE);
786b9238976Sth 			mutex_exit(&mvp->v_lock);
787b9238976Sth 			return (0);
788b9238976Sth 		}
789b9238976Sth 	} else {
790b9238976Sth 		/* mntpt is a non-v4 or v4 non-stub vnode */
791b9238976Sth 		if (!(uap->flags & MS_OVERLAY) &&
792b9238976Sth 		    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
793b9238976Sth 			mutex_exit(&mvp->v_lock);
794b9238976Sth 			if (!(uap->flags & MS_SYSSPACE)) {
795b9238976Sth 				nfs4_free_args(args);
796b9238976Sth 				kmem_free(args, sizeof (*args));
797b9238976Sth 			}
798b9238976Sth 			return (EBUSY);
79939d3e169Sevanl 		}
8007c478bd9Sstevel@tonic-gate 	}
8017c478bd9Sstevel@tonic-gate 	mutex_exit(&mvp->v_lock);
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 	/* make sure things are zeroed for errout: */
8047c478bd9Sstevel@tonic-gate 	rtvp = NULL;
8057c478bd9Sstevel@tonic-gate 	mi = NULL;
8067c478bd9Sstevel@tonic-gate 	secdata = NULL;
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate 	/*
8097c478bd9Sstevel@tonic-gate 	 * A valid knetconfig structure is required.
8107c478bd9Sstevel@tonic-gate 	 */
81139d3e169Sevanl 	if (!(flags & NFSMNT_KNCONF) ||
812b9238976Sth 	    args->knconf == NULL || args->knconf->knc_protofmly == NULL ||
813b9238976Sth 	    args->knconf->knc_proto == NULL ||
814b9238976Sth 	    (strcmp(args->knconf->knc_proto, NC_UDP) == 0)) {
81539d3e169Sevanl 		if (!(uap->flags & MS_SYSSPACE)) {
81639d3e169Sevanl 			nfs4_free_args(args);
81739d3e169Sevanl 			kmem_free(args, sizeof (*args));
81839d3e169Sevanl 		}
8197c478bd9Sstevel@tonic-gate 		return (EINVAL);
82039d3e169Sevanl 	}
82139d3e169Sevanl 
82239d3e169Sevanl 	if ((strlen(args->knconf->knc_protofmly) >= KNC_STRSIZE) ||
823b9238976Sth 	    (strlen(args->knconf->knc_proto) >= KNC_STRSIZE)) {
82439d3e169Sevanl 		if (!(uap->flags & MS_SYSSPACE)) {
82539d3e169Sevanl 			nfs4_free_args(args);
82639d3e169Sevanl 			kmem_free(args, sizeof (*args));
82739d3e169Sevanl 		}
82839d3e169Sevanl 		return (EINVAL);
82939d3e169Sevanl 	}
83039d3e169Sevanl 
8317c478bd9Sstevel@tonic-gate 	/*
8327c478bd9Sstevel@tonic-gate 	 * Allocate a servinfo4 struct.
8337c478bd9Sstevel@tonic-gate 	 */
8347c478bd9Sstevel@tonic-gate 	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
8357c478bd9Sstevel@tonic-gate 	nfs_rw_init(&svp->sv_lock, NULL, RW_DEFAULT, NULL);
8367c478bd9Sstevel@tonic-gate 	if (svp_tail) {
8377c478bd9Sstevel@tonic-gate 		svp_2ndlast = svp_tail;
8387c478bd9Sstevel@tonic-gate 		svp_tail->sv_next = svp;
8397c478bd9Sstevel@tonic-gate 	} else {
8407c478bd9Sstevel@tonic-gate 		svp_head = svp;
8417c478bd9Sstevel@tonic-gate 		svp_2ndlast = svp;
8427c478bd9Sstevel@tonic-gate 	}
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 	svp_tail = svp;
84539d3e169Sevanl 	svp->sv_knconf = args->knconf;
84639d3e169Sevanl 	args->knconf = NULL;
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 	/*
8497c478bd9Sstevel@tonic-gate 	 * Get server address
8507c478bd9Sstevel@tonic-gate 	 */
85139d3e169Sevanl 	if (args->addr == NULL || args->addr->buf == NULL) {
85239d3e169Sevanl 		error = EINVAL;
8537c478bd9Sstevel@tonic-gate 		goto errout;
8547c478bd9Sstevel@tonic-gate 	}
8557c478bd9Sstevel@tonic-gate 
85639d3e169Sevanl 	svp->sv_addr.maxlen = args->addr->maxlen;
85739d3e169Sevanl 	svp->sv_addr.len = args->addr->len;
85839d3e169Sevanl 	svp->sv_addr.buf = args->addr->buf;
85939d3e169Sevanl 	args->addr->buf = NULL;
86039d3e169Sevanl 
8617c478bd9Sstevel@tonic-gate 	/*
8627c478bd9Sstevel@tonic-gate 	 * Get the root fhandle
8637c478bd9Sstevel@tonic-gate 	 */
86439d3e169Sevanl 	if (args->fh == NULL || (strlen(args->fh) >= MAXPATHLEN)) {
86539d3e169Sevanl 		error = EINVAL;
8667c478bd9Sstevel@tonic-gate 		goto errout;
86739d3e169Sevanl 	}
8687c478bd9Sstevel@tonic-gate 
86939d3e169Sevanl 	svp->sv_path = args->fh;
87039d3e169Sevanl 	svp->sv_pathlen = strlen(args->fh) + 1;
87139d3e169Sevanl 	args->fh = NULL;
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	/*
8747c478bd9Sstevel@tonic-gate 	 * Get server's hostname
8757c478bd9Sstevel@tonic-gate 	 */
8767c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_HOSTNAME) {
87739d3e169Sevanl 		if (args->hostname == NULL || (strlen(args->hostname) >
878b9238976Sth 		    MAXNETNAMELEN)) {
87939d3e169Sevanl 			error = EINVAL;
8807c478bd9Sstevel@tonic-gate 			goto errout;
88139d3e169Sevanl 		}
88239d3e169Sevanl 		svp->sv_hostnamelen = strlen(args->hostname) + 1;
88339d3e169Sevanl 		svp->sv_hostname = args->hostname;
88439d3e169Sevanl 		args->hostname = NULL;
8857c478bd9Sstevel@tonic-gate 	} else {
8867c478bd9Sstevel@tonic-gate 		char *p = "unknown-host";
88739d3e169Sevanl 		svp->sv_hostnamelen = strlen(p) + 1;
88839d3e169Sevanl 		svp->sv_hostname = kmem_zalloc(svp->sv_hostnamelen, KM_SLEEP);
88939d3e169Sevanl 		(void) strcpy(svp->sv_hostname, p);
8907c478bd9Sstevel@tonic-gate 	}
8917c478bd9Sstevel@tonic-gate 
8927c478bd9Sstevel@tonic-gate 	/*
8937c478bd9Sstevel@tonic-gate 	 * RDMA MOUNT SUPPORT FOR NFS v4.
8947c478bd9Sstevel@tonic-gate 	 * Establish, is it possible to use RDMA, if so overload the
8957c478bd9Sstevel@tonic-gate 	 * knconf with rdma specific knconf and free the orignal knconf.
8967c478bd9Sstevel@tonic-gate 	 */
8977c478bd9Sstevel@tonic-gate 	if ((flags & NFSMNT_TRYRDMA) || (flags & NFSMNT_DORDMA)) {
8987c478bd9Sstevel@tonic-gate 		/*
8997c478bd9Sstevel@tonic-gate 		 * Determine the addr type for RDMA, IPv4 or v6.
9007c478bd9Sstevel@tonic-gate 		 */
9017c478bd9Sstevel@tonic-gate 		if (strcmp(svp->sv_knconf->knc_protofmly, NC_INET) == 0)
9027c478bd9Sstevel@tonic-gate 			addr_type = AF_INET;
9037c478bd9Sstevel@tonic-gate 		else if (strcmp(svp->sv_knconf->knc_protofmly, NC_INET6) == 0)
9047c478bd9Sstevel@tonic-gate 			addr_type = AF_INET6;
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 		if (rdma_reachable(addr_type, &svp->sv_addr,
907b9238976Sth 		    &rdma_knconf) == 0) {
9087c478bd9Sstevel@tonic-gate 			/*
9097c478bd9Sstevel@tonic-gate 			 * If successful, hijack the orignal knconf and
9107c478bd9Sstevel@tonic-gate 			 * replace with the new one, depending on the flags.
9117c478bd9Sstevel@tonic-gate 			 */
9127c478bd9Sstevel@tonic-gate 			svp->sv_origknconf = svp->sv_knconf;
9137c478bd9Sstevel@tonic-gate 			svp->sv_knconf = rdma_knconf;
9147c478bd9Sstevel@tonic-gate 		} else {
9157c478bd9Sstevel@tonic-gate 			if (flags & NFSMNT_TRYRDMA) {
9167c478bd9Sstevel@tonic-gate #ifdef	DEBUG
9177c478bd9Sstevel@tonic-gate 				if (rdma_debug)
9187c478bd9Sstevel@tonic-gate 					zcmn_err(getzoneid(), CE_WARN,
9197c478bd9Sstevel@tonic-gate 					    "no RDMA onboard, revert\n");
9207c478bd9Sstevel@tonic-gate #endif
9217c478bd9Sstevel@tonic-gate 			}
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 			if (flags & NFSMNT_DORDMA) {
9247c478bd9Sstevel@tonic-gate 				/*
9257c478bd9Sstevel@tonic-gate 				 * If proto=rdma is specified and no RDMA
9267c478bd9Sstevel@tonic-gate 				 * path to this server is avialable then
9277c478bd9Sstevel@tonic-gate 				 * ditch this server.
9287c478bd9Sstevel@tonic-gate 				 * This is not included in the mountable
9297c478bd9Sstevel@tonic-gate 				 * server list or the replica list.
9307c478bd9Sstevel@tonic-gate 				 * Check if more servers are specified;
9317c478bd9Sstevel@tonic-gate 				 * Failover case, otherwise bail out of mount.
9327c478bd9Sstevel@tonic-gate 				 */
933b9238976Sth 				if (args->nfs_args_ext == NFS_ARGS_EXTB &&
934b9238976Sth 				    args->nfs_ext_u.nfs_extB.next != NULL) {
93539d3e169Sevanl 					data = (char *)
936b9238976Sth 					    args->nfs_ext_u.nfs_extB.next;
9377c478bd9Sstevel@tonic-gate 					if (uap->flags & MS_RDONLY &&
9387c478bd9Sstevel@tonic-gate 					    !(flags & NFSMNT_SOFT)) {
9397c478bd9Sstevel@tonic-gate 						if (svp_head->sv_next == NULL) {
9407c478bd9Sstevel@tonic-gate 							svp_tail = NULL;
9417c478bd9Sstevel@tonic-gate 							svp_2ndlast = NULL;
9427c478bd9Sstevel@tonic-gate 							sv4_free(svp_head);
9437c478bd9Sstevel@tonic-gate 							goto more;
9447c478bd9Sstevel@tonic-gate 						} else {
9457c478bd9Sstevel@tonic-gate 							svp_tail = svp_2ndlast;
9467c478bd9Sstevel@tonic-gate 							svp_2ndlast->sv_next =
9477c478bd9Sstevel@tonic-gate 							    NULL;
9487c478bd9Sstevel@tonic-gate 							sv4_free(svp);
9497c478bd9Sstevel@tonic-gate 							goto more;
9507c478bd9Sstevel@tonic-gate 						}
9517c478bd9Sstevel@tonic-gate 					}
9527c478bd9Sstevel@tonic-gate 				} else {
9537c478bd9Sstevel@tonic-gate 					/*
9547c478bd9Sstevel@tonic-gate 					 * This is the last server specified
9557c478bd9Sstevel@tonic-gate 					 * in the nfs_args list passed down
9567c478bd9Sstevel@tonic-gate 					 * and its not rdma capable.
9577c478bd9Sstevel@tonic-gate 					 */
9587c478bd9Sstevel@tonic-gate 					if (svp_head->sv_next == NULL) {
9597c478bd9Sstevel@tonic-gate 						/*
9607c478bd9Sstevel@tonic-gate 						 * Is this the only one
9617c478bd9Sstevel@tonic-gate 						 */
9627c478bd9Sstevel@tonic-gate 						error = EINVAL;
9637c478bd9Sstevel@tonic-gate #ifdef	DEBUG
9647c478bd9Sstevel@tonic-gate 						if (rdma_debug)
9657c478bd9Sstevel@tonic-gate 							zcmn_err(getzoneid(),
9667c478bd9Sstevel@tonic-gate 							    CE_WARN,
9677c478bd9Sstevel@tonic-gate 							    "No RDMA srv");
9687c478bd9Sstevel@tonic-gate #endif
9697c478bd9Sstevel@tonic-gate 						goto errout;
9707c478bd9Sstevel@tonic-gate 					} else {
9717c478bd9Sstevel@tonic-gate 						/*
9727c478bd9Sstevel@tonic-gate 						 * There is list, since some
9737c478bd9Sstevel@tonic-gate 						 * servers specified before
9747c478bd9Sstevel@tonic-gate 						 * this passed all requirements
9757c478bd9Sstevel@tonic-gate 						 */
9767c478bd9Sstevel@tonic-gate 						svp_tail = svp_2ndlast;
9777c478bd9Sstevel@tonic-gate 						svp_2ndlast->sv_next = NULL;
9787c478bd9Sstevel@tonic-gate 						sv4_free(svp);
9797c478bd9Sstevel@tonic-gate 						goto proceed;
9807c478bd9Sstevel@tonic-gate 					}
9817c478bd9Sstevel@tonic-gate 				}
9827c478bd9Sstevel@tonic-gate 			}
9837c478bd9Sstevel@tonic-gate 		}
9847c478bd9Sstevel@tonic-gate 	}
9857c478bd9Sstevel@tonic-gate 
9867c478bd9Sstevel@tonic-gate 	/*
9877c478bd9Sstevel@tonic-gate 	 * If there are syncaddr and netname data, load them in. This is
9887c478bd9Sstevel@tonic-gate 	 * to support data needed for NFSV4 when AUTH_DH is the negotiated
9897c478bd9Sstevel@tonic-gate 	 * flavor via SECINFO. (instead of using MOUNT protocol in V3).
9907c478bd9Sstevel@tonic-gate 	 */
99139d3e169Sevanl 	if (args->flags & NFSMNT_SECURE) {
99239d3e169Sevanl 		svp->sv_dhsec = create_authdh_data(args->netname,
993b9238976Sth 		    strlen(args->netname),
994b9238976Sth 		    args->syncaddr, svp->sv_knconf);
9957c478bd9Sstevel@tonic-gate 	}
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	/*
9987c478bd9Sstevel@tonic-gate 	 * Get the extention data which has the security data structure.
9997c478bd9Sstevel@tonic-gate 	 * This includes data for AUTH_SYS as well.
10007c478bd9Sstevel@tonic-gate 	 */
10017c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_NEWARGS) {
100239d3e169Sevanl 		switch (args->nfs_args_ext) {
10037c478bd9Sstevel@tonic-gate 		case NFS_ARGS_EXTA:
10047c478bd9Sstevel@tonic-gate 		case NFS_ARGS_EXTB:
10057c478bd9Sstevel@tonic-gate 			/*
10067c478bd9Sstevel@tonic-gate 			 * Indicating the application is using the new
10077c478bd9Sstevel@tonic-gate 			 * sec_data structure to pass in the security
10087c478bd9Sstevel@tonic-gate 			 * data.
10097c478bd9Sstevel@tonic-gate 			 */
101039d3e169Sevanl 			secdata = args->nfs_ext_u.nfs_extA.secdata;
101139d3e169Sevanl 			if (secdata == NULL) {
10127c478bd9Sstevel@tonic-gate 				error = EINVAL;
101339d3e169Sevanl 			} else if (uap->flags & MS_SYSSPACE) {
101439d3e169Sevanl 				/*
101539d3e169Sevanl 				 * Need to validate the flavor here if
101639d3e169Sevanl 				 * sysspace, userspace was already
101739d3e169Sevanl 				 * validate from the nfs_copyin function.
101839d3e169Sevanl 				 */
101939d3e169Sevanl 				switch (secdata->rpcflavor) {
102039d3e169Sevanl 				case AUTH_NONE:
102139d3e169Sevanl 				case AUTH_UNIX:
102239d3e169Sevanl 				case AUTH_LOOPBACK:
102339d3e169Sevanl 				case AUTH_DES:
102439d3e169Sevanl 				case RPCSEC_GSS:
102539d3e169Sevanl 					break;
102639d3e169Sevanl 				default:
102739d3e169Sevanl 					error = EINVAL;
102839d3e169Sevanl 					goto errout;
102939d3e169Sevanl 				}
10307c478bd9Sstevel@tonic-gate 			}
103139d3e169Sevanl 			args->nfs_ext_u.nfs_extA.secdata = NULL;
10327c478bd9Sstevel@tonic-gate 			break;
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 		default:
10357c478bd9Sstevel@tonic-gate 			error = EINVAL;
10367c478bd9Sstevel@tonic-gate 			break;
10377c478bd9Sstevel@tonic-gate 		}
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate 	} else if (flags & NFSMNT_SECURE) {
10407c478bd9Sstevel@tonic-gate 		/*
10417c478bd9Sstevel@tonic-gate 		 * NFSMNT_SECURE is deprecated but we keep it
1042b9238976Sth 		 * to support the rogue user-generated application
10437c478bd9Sstevel@tonic-gate 		 * that may use this undocumented interface to do
1044b9238976Sth 		 * AUTH_DH security, e.g. our own rexd.
1045b9238976Sth 		 *
1046b9238976Sth 		 * Also note that NFSMNT_SECURE is used for passing
1047b9238976Sth 		 * AUTH_DH info to be used in negotiation.
10487c478bd9Sstevel@tonic-gate 		 */
104939d3e169Sevanl 		secdata = create_authdh_data(args->netname,
1050b9238976Sth 		    strlen(args->netname), args->syncaddr, svp->sv_knconf);
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate 	} else {
10537c478bd9Sstevel@tonic-gate 		secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP);
10547c478bd9Sstevel@tonic-gate 		secdata->secmod = secdata->rpcflavor = AUTH_SYS;
10557c478bd9Sstevel@tonic-gate 		secdata->data = NULL;
10567c478bd9Sstevel@tonic-gate 	}
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate 	svp->sv_secdata = secdata;
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 	/*
10617c478bd9Sstevel@tonic-gate 	 * User does not explictly specify a flavor, and a user
10627c478bd9Sstevel@tonic-gate 	 * defined default flavor is passed down.
10637c478bd9Sstevel@tonic-gate 	 */
10647c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_SECDEFAULT) {
10657c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
10667c478bd9Sstevel@tonic-gate 		svp->sv_flags |= SV4_TRYSECDEFAULT;
10677c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
10687c478bd9Sstevel@tonic-gate 	}
10697c478bd9Sstevel@tonic-gate 
10707c478bd9Sstevel@tonic-gate 	/*
10717c478bd9Sstevel@tonic-gate 	 * Failover support:
10727c478bd9Sstevel@tonic-gate 	 *
10737c478bd9Sstevel@tonic-gate 	 * We may have a linked list of nfs_args structures,
10747c478bd9Sstevel@tonic-gate 	 * which means the user is looking for failover.  If
10757c478bd9Sstevel@tonic-gate 	 * the mount is either not "read-only" or "soft",
10767c478bd9Sstevel@tonic-gate 	 * we want to bail out with EINVAL.
10777c478bd9Sstevel@tonic-gate 	 */
107839d3e169Sevanl 	if (args->nfs_args_ext == NFS_ARGS_EXTB &&
107939d3e169Sevanl 	    args->nfs_ext_u.nfs_extB.next != NULL) {
10807c478bd9Sstevel@tonic-gate 		if (uap->flags & MS_RDONLY && !(flags & NFSMNT_SOFT)) {
108139d3e169Sevanl 			data = (char *)args->nfs_ext_u.nfs_extB.next;
10827c478bd9Sstevel@tonic-gate 			goto more;
10837c478bd9Sstevel@tonic-gate 		}
10847c478bd9Sstevel@tonic-gate 		error = EINVAL;
10857c478bd9Sstevel@tonic-gate 		goto errout;
10867c478bd9Sstevel@tonic-gate 	}
10877c478bd9Sstevel@tonic-gate 
10887c478bd9Sstevel@tonic-gate 	/*
10897c478bd9Sstevel@tonic-gate 	 * Determine the zone we're being mounted into.
10907c478bd9Sstevel@tonic-gate 	 */
109145916cd2Sjpk 	zone_hold(mntzone = zone);		/* start with this assumption */
10927c478bd9Sstevel@tonic-gate 	if (getzoneid() == GLOBAL_ZONEID) {
109345916cd2Sjpk 		zone_rele(mntzone);
10947c478bd9Sstevel@tonic-gate 		mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
10957c478bd9Sstevel@tonic-gate 		ASSERT(mntzone != NULL);
10967c478bd9Sstevel@tonic-gate 		if (mntzone != zone) {
10977c478bd9Sstevel@tonic-gate 			error = EBUSY;
10987c478bd9Sstevel@tonic-gate 			goto errout;
10997c478bd9Sstevel@tonic-gate 		}
11007c478bd9Sstevel@tonic-gate 	}
11017c478bd9Sstevel@tonic-gate 
110245916cd2Sjpk 	if (is_system_labeled()) {
110345916cd2Sjpk 		error = nfs_mount_label_policy(vfsp, &svp->sv_addr,
110445916cd2Sjpk 		    svp->sv_knconf, cr);
110545916cd2Sjpk 
110645916cd2Sjpk 		if (error > 0)
110745916cd2Sjpk 			goto errout;
110845916cd2Sjpk 
110945916cd2Sjpk 		if (error == -1) {
111045916cd2Sjpk 			/* change mount to read-only to prevent write-down */
111145916cd2Sjpk 			vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
111245916cd2Sjpk 		}
111345916cd2Sjpk 	}
111445916cd2Sjpk 
11157c478bd9Sstevel@tonic-gate 	/*
11167c478bd9Sstevel@tonic-gate 	 * Stop the mount from going any further if the zone is going away.
11177c478bd9Sstevel@tonic-gate 	 */
111845916cd2Sjpk 	if (zone_status_get(mntzone) >= ZONE_IS_SHUTTING_DOWN) {
11197c478bd9Sstevel@tonic-gate 		error = EBUSY;
11207c478bd9Sstevel@tonic-gate 		goto errout;
11217c478bd9Sstevel@tonic-gate 	}
11227c478bd9Sstevel@tonic-gate 
11237c478bd9Sstevel@tonic-gate 	/*
11247c478bd9Sstevel@tonic-gate 	 * Get root vnode.
11257c478bd9Sstevel@tonic-gate 	 */
11267c478bd9Sstevel@tonic-gate proceed:
112745916cd2Sjpk 	error = nfs4rootvp(&rtvp, vfsp, svp_head, flags, cr, mntzone);
112850a83466Sjwahlig 	if (error) {
112950a83466Sjwahlig 		/* if nfs4rootvp failed, it will free svp_head */
113050a83466Sjwahlig 		svp_head = NULL;
11317c478bd9Sstevel@tonic-gate 		goto errout;
113250a83466Sjwahlig 	}
11337c478bd9Sstevel@tonic-gate 
11347c478bd9Sstevel@tonic-gate 	mi = VTOMI4(rtvp);
11357c478bd9Sstevel@tonic-gate 
11367c478bd9Sstevel@tonic-gate 	/*
11377c478bd9Sstevel@tonic-gate 	 * Send client id to the server, if necessary
11387c478bd9Sstevel@tonic-gate 	 */
11397c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(&n4e);
11407c478bd9Sstevel@tonic-gate 	nfs4setclientid(mi, cr, FALSE, &n4e);
1141b9238976Sth 
11427c478bd9Sstevel@tonic-gate 	error = n4e.error;
11437c478bd9Sstevel@tonic-gate 
11447c478bd9Sstevel@tonic-gate 	if (error)
11457c478bd9Sstevel@tonic-gate 		goto errout;
11467c478bd9Sstevel@tonic-gate 
11477c478bd9Sstevel@tonic-gate 	/*
11487c478bd9Sstevel@tonic-gate 	 * Set option fields in the mount info record
11497c478bd9Sstevel@tonic-gate 	 */
11507c478bd9Sstevel@tonic-gate 
11517c478bd9Sstevel@tonic-gate 	if (svp_head->sv_next) {
11527c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_lock);
11537c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_LLOCK;
11547c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_lock);
11557c478bd9Sstevel@tonic-gate 	}
115639d3e169Sevanl 	error = nfs4_setopts(rtvp, DATAMODEL_NATIVE, args);
1157b9238976Sth 	if (error)
1158b9238976Sth 		goto errout;
1159b9238976Sth 
1160b9238976Sth 	/*
1161b9238976Sth 	 * Time to tie in the mirror mount info at last!
1162b9238976Sth 	 */
1163b9238976Sth 	if (flags & NFSMNT_EPHEMERAL)
1164d3a14591SThomas Haynes 		error = nfs4_record_ephemeral_mount(mi, mvp);
11657c478bd9Sstevel@tonic-gate 
11667c478bd9Sstevel@tonic-gate errout:
11677c478bd9Sstevel@tonic-gate 	if (error) {
11687c478bd9Sstevel@tonic-gate 		if (rtvp != NULL) {
11697c478bd9Sstevel@tonic-gate 			rp = VTOR4(rtvp);
11707c478bd9Sstevel@tonic-gate 			if (rp->r_flags & R4HASHED)
11717c478bd9Sstevel@tonic-gate 				rp4_rmhash(rp);
11727c478bd9Sstevel@tonic-gate 		}
11737c478bd9Sstevel@tonic-gate 		if (mi != NULL) {
11747c478bd9Sstevel@tonic-gate 			nfs4_async_stop(vfsp);
11757c478bd9Sstevel@tonic-gate 			nfs4_async_manager_stop(vfsp);
11767c478bd9Sstevel@tonic-gate 			nfs4_remove_mi_from_server(mi, NULL);
117750a83466Sjwahlig 			if (rtvp != NULL)
11787c478bd9Sstevel@tonic-gate 				VN_RELE(rtvp);
117945916cd2Sjpk 			if (mntzone != NULL)
118045916cd2Sjpk 				zone_rele(mntzone);
118150a83466Sjwahlig 			/* need to remove it from the zone */
118250a83466Sjwahlig 			removed = nfs4_mi_zonelist_remove(mi);
118350a83466Sjwahlig 			if (removed)
1184a19609f8Sjv 				zone_rele_ref(&mi->mi_zone_ref,
1185a19609f8Sjv 				    ZONE_REF_NFSV4);
118650a83466Sjwahlig 			MI4_RELE(mi);
118739d3e169Sevanl 			if (!(uap->flags & MS_SYSSPACE) && args) {
118839d3e169Sevanl 				nfs4_free_args(args);
118939d3e169Sevanl 				kmem_free(args, sizeof (*args));
119039d3e169Sevanl 			}
11917c478bd9Sstevel@tonic-gate 			return (error);
11927c478bd9Sstevel@tonic-gate 		}
119350a83466Sjwahlig 		if (svp_head)
119450a83466Sjwahlig 			sv4_free(svp_head);
11957c478bd9Sstevel@tonic-gate 	}
11967c478bd9Sstevel@tonic-gate 
119739d3e169Sevanl 	if (!(uap->flags & MS_SYSSPACE) && args) {
119839d3e169Sevanl 		nfs4_free_args(args);
119939d3e169Sevanl 		kmem_free(args, sizeof (*args));
120039d3e169Sevanl 	}
12017c478bd9Sstevel@tonic-gate 	if (rtvp != NULL)
12027c478bd9Sstevel@tonic-gate 		VN_RELE(rtvp);
12037c478bd9Sstevel@tonic-gate 
120445916cd2Sjpk 	if (mntzone != NULL)
120545916cd2Sjpk 		zone_rele(mntzone);
120645916cd2Sjpk 
12077c478bd9Sstevel@tonic-gate 	return (error);
12087c478bd9Sstevel@tonic-gate }
12097c478bd9Sstevel@tonic-gate 
121039d3e169Sevanl #ifdef  DEBUG
12117c478bd9Sstevel@tonic-gate #define	VERS_MSG	"NFS4 server "
12127c478bd9Sstevel@tonic-gate #else
12137c478bd9Sstevel@tonic-gate #define	VERS_MSG	"NFS server "
12147c478bd9Sstevel@tonic-gate #endif
12157c478bd9Sstevel@tonic-gate 
121639d3e169Sevanl #define	READ_MSG        \
12177c478bd9Sstevel@tonic-gate 	VERS_MSG "%s returned 0 for read transfer size"
121839d3e169Sevanl #define	WRITE_MSG       \
12197c478bd9Sstevel@tonic-gate 	VERS_MSG "%s returned 0 for write transfer size"
122039d3e169Sevanl #define	SIZE_MSG        \
12217c478bd9Sstevel@tonic-gate 	VERS_MSG "%s returned 0 for maximum file size"
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate /*
12247c478bd9Sstevel@tonic-gate  * Get the symbolic link text from the server for a given filehandle
12257c478bd9Sstevel@tonic-gate  * of that symlink.
12267c478bd9Sstevel@tonic-gate  *
122739d3e169Sevanl  *      (get symlink text) PUTFH READLINK
12287c478bd9Sstevel@tonic-gate  */
12297c478bd9Sstevel@tonic-gate static int
12307c478bd9Sstevel@tonic-gate getlinktext_otw(mntinfo4_t *mi, nfs_fh4 *fh, char **linktextp, cred_t *cr,
1231b9238976Sth     int flags)
12327c478bd9Sstevel@tonic-gate {
12337c478bd9Sstevel@tonic-gate 	COMPOUND4args_clnt args;
12347c478bd9Sstevel@tonic-gate 	COMPOUND4res_clnt res;
12357c478bd9Sstevel@tonic-gate 	int doqueue;
12367c478bd9Sstevel@tonic-gate 	nfs_argop4 argop[2];
12377c478bd9Sstevel@tonic-gate 	nfs_resop4 *resop;
12387c478bd9Sstevel@tonic-gate 	READLINK4res *lr_res;
12397c478bd9Sstevel@tonic-gate 	uint_t len;
12407c478bd9Sstevel@tonic-gate 	bool_t needrecov = FALSE;
12417c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
12427c478bd9Sstevel@tonic-gate 	nfs4_sharedfh_t *sfh;
12437c478bd9Sstevel@tonic-gate 	nfs4_error_t e;
12447c478bd9Sstevel@tonic-gate 	int num_retry = nfs4_max_mount_retry;
12457c478bd9Sstevel@tonic-gate 	int recovery = !(flags & NFS4_GETFH_NEEDSOP);
12467c478bd9Sstevel@tonic-gate 
12477c478bd9Sstevel@tonic-gate 	sfh = sfh4_get(fh, mi);
12487c478bd9Sstevel@tonic-gate 	recov_state.rs_flags = 0;
12497c478bd9Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
12507c478bd9Sstevel@tonic-gate 
12517c478bd9Sstevel@tonic-gate recov_retry:
12527c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(&e);
12537c478bd9Sstevel@tonic-gate 
12547c478bd9Sstevel@tonic-gate 	args.array_len = 2;
12557c478bd9Sstevel@tonic-gate 	args.array = argop;
12567c478bd9Sstevel@tonic-gate 	args.ctag = TAG_GET_SYMLINK;
12577c478bd9Sstevel@tonic-gate 
12587c478bd9Sstevel@tonic-gate 	if (! recovery) {
12597c478bd9Sstevel@tonic-gate 		e.error = nfs4_start_op(mi, NULL, NULL, &recov_state);
12607c478bd9Sstevel@tonic-gate 		if (e.error) {
12617c478bd9Sstevel@tonic-gate 			sfh4_rele(&sfh);
12627c478bd9Sstevel@tonic-gate 			return (e.error);
12637c478bd9Sstevel@tonic-gate 		}
12647c478bd9Sstevel@tonic-gate 	}
12657c478bd9Sstevel@tonic-gate 
12667c478bd9Sstevel@tonic-gate 	/* 0. putfh symlink fh */
12677c478bd9Sstevel@tonic-gate 	argop[0].argop = OP_CPUTFH;
12687c478bd9Sstevel@tonic-gate 	argop[0].nfs_argop4_u.opcputfh.sfh = sfh;
12697c478bd9Sstevel@tonic-gate 
12707c478bd9Sstevel@tonic-gate 	/* 1. readlink */
12717c478bd9Sstevel@tonic-gate 	argop[1].argop = OP_READLINK;
12727c478bd9Sstevel@tonic-gate 
12737c478bd9Sstevel@tonic-gate 	doqueue = 1;
12747c478bd9Sstevel@tonic-gate 
12757c478bd9Sstevel@tonic-gate 	rfs4call(mi, &args, &res, cr, &doqueue, 0, &e);
12767c478bd9Sstevel@tonic-gate 
12777c478bd9Sstevel@tonic-gate 	needrecov = nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp);
12787c478bd9Sstevel@tonic-gate 
12797c478bd9Sstevel@tonic-gate 	if (needrecov && !recovery && num_retry-- > 0) {
12807c478bd9Sstevel@tonic-gate 
12817c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
1282b9238976Sth 		    "getlinktext_otw: initiating recovery\n"));
12837c478bd9Sstevel@tonic-gate 
12847c478bd9Sstevel@tonic-gate 		if (nfs4_start_recovery(&e, mi, NULL, NULL, NULL, NULL,
12852f172c55SRobert Thurlow 		    OP_READLINK, NULL, NULL, NULL) == FALSE) {
1286b9238976Sth 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
1287b9238976Sth 			if (!e.error)
1288b9238976Sth 				(void) xdr_free(xdr_COMPOUND4res_clnt,
1289b9238976Sth 				    (caddr_t)&res);
12907c478bd9Sstevel@tonic-gate 			goto recov_retry;
12917c478bd9Sstevel@tonic-gate 		}
12927c478bd9Sstevel@tonic-gate 	}
12937c478bd9Sstevel@tonic-gate 
12947c478bd9Sstevel@tonic-gate 	/*
12957c478bd9Sstevel@tonic-gate 	 * If non-NFS4 pcol error and/or we weren't able to recover.
12967c478bd9Sstevel@tonic-gate 	 */
12977c478bd9Sstevel@tonic-gate 	if (e.error != 0) {
12987c478bd9Sstevel@tonic-gate 		if (! recovery)
12997c478bd9Sstevel@tonic-gate 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
13007c478bd9Sstevel@tonic-gate 		sfh4_rele(&sfh);
13017c478bd9Sstevel@tonic-gate 		return (e.error);
13027c478bd9Sstevel@tonic-gate 	}
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate 	if (res.status) {
13057c478bd9Sstevel@tonic-gate 		e.error = geterrno4(res.status);
13067c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
13077c478bd9Sstevel@tonic-gate 		if (! recovery)
13087c478bd9Sstevel@tonic-gate 			nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
13097c478bd9Sstevel@tonic-gate 		sfh4_rele(&sfh);
13107c478bd9Sstevel@tonic-gate 		return (e.error);
13117c478bd9Sstevel@tonic-gate 	}
13127c478bd9Sstevel@tonic-gate 
13137c478bd9Sstevel@tonic-gate 	/* res.status == NFS4_OK */
13147c478bd9Sstevel@tonic-gate 	ASSERT(res.status == NFS4_OK);
13157c478bd9Sstevel@tonic-gate 
131639d3e169Sevanl 	resop = &res.array[1];  /* readlink res */
13177c478bd9Sstevel@tonic-gate 	lr_res = &resop->nfs_resop4_u.opreadlink;
13187c478bd9Sstevel@tonic-gate 
13197c478bd9Sstevel@tonic-gate 	/* treat symlink name as data */
1320*bbe876c0SMarcel Telka 	*linktextp = utf8_to_str((utf8string *)&lr_res->link, &len, NULL);
13217c478bd9Sstevel@tonic-gate 
13227c478bd9Sstevel@tonic-gate 	if (! recovery)
13237c478bd9Sstevel@tonic-gate 		nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov);
13247c478bd9Sstevel@tonic-gate 	sfh4_rele(&sfh);
13257c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
13267c478bd9Sstevel@tonic-gate 	return (0);
13277c478bd9Sstevel@tonic-gate }
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate /*
13307c478bd9Sstevel@tonic-gate  * Skip over consecutive slashes and "/./" in a pathname.
13317c478bd9Sstevel@tonic-gate  */
13327c478bd9Sstevel@tonic-gate void
13337c478bd9Sstevel@tonic-gate pathname_skipslashdot(struct pathname *pnp)
13347c478bd9Sstevel@tonic-gate {
13357c478bd9Sstevel@tonic-gate 	char *c1, *c2;
13367c478bd9Sstevel@tonic-gate 
13377c478bd9Sstevel@tonic-gate 	while (pnp->pn_pathlen > 0 && *pnp->pn_path == '/') {
13387c478bd9Sstevel@tonic-gate 
13397c478bd9Sstevel@tonic-gate 		c1 = pnp->pn_path + 1;
13407c478bd9Sstevel@tonic-gate 		c2 = pnp->pn_path + 2;
13417c478bd9Sstevel@tonic-gate 
13427c478bd9Sstevel@tonic-gate 		if (*c1 == '.' && (*c2 == '/' || *c2 == '\0')) {
13437c478bd9Sstevel@tonic-gate 			pnp->pn_path = pnp->pn_path + 2; /* skip "/." */
13447c478bd9Sstevel@tonic-gate 			pnp->pn_pathlen = pnp->pn_pathlen - 2;
13457c478bd9Sstevel@tonic-gate 		} else {
13467c478bd9Sstevel@tonic-gate 			pnp->pn_path++;
13477c478bd9Sstevel@tonic-gate 			pnp->pn_pathlen--;
13487c478bd9Sstevel@tonic-gate 		}
13497c478bd9Sstevel@tonic-gate 	}
13507c478bd9Sstevel@tonic-gate }
13517c478bd9Sstevel@tonic-gate 
13527c478bd9Sstevel@tonic-gate /*
13537c478bd9Sstevel@tonic-gate  * Resolve a symbolic link path. The symlink is in the nth component of
13547c478bd9Sstevel@tonic-gate  * svp->sv_path and has an nfs4 file handle "fh".
13557c478bd9Sstevel@tonic-gate  * Upon return, the sv_path will point to the new path that has the nth
13567c478bd9Sstevel@tonic-gate  * component resolved to its symlink text.
13577c478bd9Sstevel@tonic-gate  */
13587c478bd9Sstevel@tonic-gate int
13597c478bd9Sstevel@tonic-gate resolve_sympath(mntinfo4_t *mi, servinfo4_t *svp, int nth, nfs_fh4 *fh,
1360b9238976Sth     cred_t *cr, int flags)
13617c478bd9Sstevel@tonic-gate {
13627c478bd9Sstevel@tonic-gate 	char *oldpath;
13637c478bd9Sstevel@tonic-gate 	char *symlink, *newpath;
13647c478bd9Sstevel@tonic-gate 	struct pathname oldpn, newpn;
13657c478bd9Sstevel@tonic-gate 	char component[MAXNAMELEN];
13667c478bd9Sstevel@tonic-gate 	int i, addlen, error = 0;
13677c478bd9Sstevel@tonic-gate 	int oldpathlen;
13687c478bd9Sstevel@tonic-gate 
13697c478bd9Sstevel@tonic-gate 	/* Get the symbolic link text over the wire. */
13707c478bd9Sstevel@tonic-gate 	error = getlinktext_otw(mi, fh, &symlink, cr, flags);
13717c478bd9Sstevel@tonic-gate 
13727c478bd9Sstevel@tonic-gate 	if (error || symlink == NULL || strlen(symlink) == 0)
13737c478bd9Sstevel@tonic-gate 		return (error);
13747c478bd9Sstevel@tonic-gate 
13757c478bd9Sstevel@tonic-gate 	/*
13767c478bd9Sstevel@tonic-gate 	 * Compose the new pathname.
13777c478bd9Sstevel@tonic-gate 	 * Note:
13787c478bd9Sstevel@tonic-gate 	 *    - only the nth component is resolved for the pathname.
13797c478bd9Sstevel@tonic-gate 	 *    - pathname.pn_pathlen does not count the ending null byte.
13807c478bd9Sstevel@tonic-gate 	 */
13817c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
13827c478bd9Sstevel@tonic-gate 	oldpath = svp->sv_path;
13837c478bd9Sstevel@tonic-gate 	oldpathlen = svp->sv_pathlen;
13847c478bd9Sstevel@tonic-gate 	if (error = pn_get(oldpath, UIO_SYSSPACE, &oldpn)) {
13857c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
13867c478bd9Sstevel@tonic-gate 		kmem_free(symlink, strlen(symlink) + 1);
13877c478bd9Sstevel@tonic-gate 		return (error);
13887c478bd9Sstevel@tonic-gate 	}
13897c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
13907c478bd9Sstevel@tonic-gate 	pn_alloc(&newpn);
13917c478bd9Sstevel@tonic-gate 
13927c478bd9Sstevel@tonic-gate 	/*
13937c478bd9Sstevel@tonic-gate 	 * Skip over previous components from the oldpath so that the
13947c478bd9Sstevel@tonic-gate 	 * oldpn.pn_path will point to the symlink component. Skip
13957c478bd9Sstevel@tonic-gate 	 * leading slashes and "/./" (no OP_LOOKUP on ".") so that
13967c478bd9Sstevel@tonic-gate 	 * pn_getcompnent can get the component.
13977c478bd9Sstevel@tonic-gate 	 */
13987c478bd9Sstevel@tonic-gate 	for (i = 1; i < nth; i++) {
13997c478bd9Sstevel@tonic-gate 		pathname_skipslashdot(&oldpn);
14007c478bd9Sstevel@tonic-gate 		error = pn_getcomponent(&oldpn, component);
14017c478bd9Sstevel@tonic-gate 		if (error)
14027c478bd9Sstevel@tonic-gate 			goto out;
14037c478bd9Sstevel@tonic-gate 	}
14047c478bd9Sstevel@tonic-gate 
14057c478bd9Sstevel@tonic-gate 	/*
14067c478bd9Sstevel@tonic-gate 	 * Copy the old path upto the component right before the symlink
14077c478bd9Sstevel@tonic-gate 	 * if the symlink is not an absolute path.
14087c478bd9Sstevel@tonic-gate 	 */
14097c478bd9Sstevel@tonic-gate 	if (symlink[0] != '/') {
14107c478bd9Sstevel@tonic-gate 		addlen = oldpn.pn_path - oldpn.pn_buf;
14117c478bd9Sstevel@tonic-gate 		bcopy(oldpn.pn_buf, newpn.pn_path, addlen);
14127c478bd9Sstevel@tonic-gate 		newpn.pn_pathlen += addlen;
14137c478bd9Sstevel@tonic-gate 		newpn.pn_path += addlen;
14147c478bd9Sstevel@tonic-gate 		newpn.pn_buf[newpn.pn_pathlen] = '/';
14157c478bd9Sstevel@tonic-gate 		newpn.pn_pathlen++;
14167c478bd9Sstevel@tonic-gate 		newpn.pn_path++;
14177c478bd9Sstevel@tonic-gate 	}
14187c478bd9Sstevel@tonic-gate 
14197c478bd9Sstevel@tonic-gate 	/* copy the resolved symbolic link text */
14207c478bd9Sstevel@tonic-gate 	addlen = strlen(symlink);
14217c478bd9Sstevel@tonic-gate 	if (newpn.pn_pathlen + addlen >= newpn.pn_bufsize) {
14227c478bd9Sstevel@tonic-gate 		error = ENAMETOOLONG;
14237c478bd9Sstevel@tonic-gate 		goto out;
14247c478bd9Sstevel@tonic-gate 	}
14257c478bd9Sstevel@tonic-gate 	bcopy(symlink, newpn.pn_path, addlen);
14267c478bd9Sstevel@tonic-gate 	newpn.pn_pathlen += addlen;
14277c478bd9Sstevel@tonic-gate 	newpn.pn_path += addlen;
14287c478bd9Sstevel@tonic-gate 
14297c478bd9Sstevel@tonic-gate 	/*
14307c478bd9Sstevel@tonic-gate 	 * Check if there is any remaining path after the symlink component.
14317c478bd9Sstevel@tonic-gate 	 * First, skip the symlink component.
14327c478bd9Sstevel@tonic-gate 	 */
14337c478bd9Sstevel@tonic-gate 	pathname_skipslashdot(&oldpn);
14347c478bd9Sstevel@tonic-gate 	if (error = pn_getcomponent(&oldpn, component))
14357c478bd9Sstevel@tonic-gate 		goto out;
14367c478bd9Sstevel@tonic-gate 
14377c478bd9Sstevel@tonic-gate 	addlen = pn_pathleft(&oldpn); /* includes counting the slash */
14387c478bd9Sstevel@tonic-gate 
14397c478bd9Sstevel@tonic-gate 	/*
14407c478bd9Sstevel@tonic-gate 	 * Copy the remaining path to the new pathname if there is any.
14417c478bd9Sstevel@tonic-gate 	 */
14427c478bd9Sstevel@tonic-gate 	if (addlen > 0) {
14437c478bd9Sstevel@tonic-gate 		if (newpn.pn_pathlen + addlen >= newpn.pn_bufsize) {
14447c478bd9Sstevel@tonic-gate 			error = ENAMETOOLONG;
14457c478bd9Sstevel@tonic-gate 			goto out;
14467c478bd9Sstevel@tonic-gate 		}
14477c478bd9Sstevel@tonic-gate 		bcopy(oldpn.pn_path, newpn.pn_path, addlen);
14487c478bd9Sstevel@tonic-gate 		newpn.pn_pathlen += addlen;
14497c478bd9Sstevel@tonic-gate 	}
14507c478bd9Sstevel@tonic-gate 	newpn.pn_buf[newpn.pn_pathlen] = '\0';
14517c478bd9Sstevel@tonic-gate 
14527c478bd9Sstevel@tonic-gate 	/* get the newpath and store it in the servinfo4_t */
14537c478bd9Sstevel@tonic-gate 	newpath = kmem_alloc(newpn.pn_pathlen + 1, KM_SLEEP);
14547c478bd9Sstevel@tonic-gate 	bcopy(newpn.pn_buf, newpath, newpn.pn_pathlen);
14557c478bd9Sstevel@tonic-gate 	newpath[newpn.pn_pathlen] = '\0';
14567c478bd9Sstevel@tonic-gate 
14577c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
14587c478bd9Sstevel@tonic-gate 	svp->sv_path = newpath;
14597c478bd9Sstevel@tonic-gate 	svp->sv_pathlen = strlen(newpath) + 1;
14607c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
14617c478bd9Sstevel@tonic-gate 
14627c478bd9Sstevel@tonic-gate 	kmem_free(oldpath, oldpathlen);
14637c478bd9Sstevel@tonic-gate out:
14647c478bd9Sstevel@tonic-gate 	kmem_free(symlink, strlen(symlink) + 1);
14657c478bd9Sstevel@tonic-gate 	pn_free(&newpn);
14667c478bd9Sstevel@tonic-gate 	pn_free(&oldpn);
14677c478bd9Sstevel@tonic-gate 
14687c478bd9Sstevel@tonic-gate 	return (error);
14697c478bd9Sstevel@tonic-gate }
14707c478bd9Sstevel@tonic-gate 
14712f172c55SRobert Thurlow /*
14722f172c55SRobert Thurlow  * This routine updates servinfo4 structure with the new referred server
14732f172c55SRobert Thurlow  * info.
14742f172c55SRobert Thurlow  * nfsfsloc has the location related information
14752f172c55SRobert Thurlow  * fsp has the hostname and pathname info.
14762f172c55SRobert Thurlow  * new path = pathname from referral + part of orig pathname(based on nth).
14772f172c55SRobert Thurlow  */
14782f172c55SRobert Thurlow static void
14792f172c55SRobert Thurlow update_servinfo4(servinfo4_t *svp, fs_location4 *fsp,
14802f172c55SRobert Thurlow     struct nfs_fsl_info *nfsfsloc, char *orig_path, int nth)
14812f172c55SRobert Thurlow {
14822f172c55SRobert Thurlow 	struct knetconfig *knconf, *svknconf;
14832f172c55SRobert Thurlow 	struct netbuf *saddr;
14842f172c55SRobert Thurlow 	sec_data_t	*secdata;
14852f172c55SRobert Thurlow 	utf8string *host;
14862f172c55SRobert Thurlow 	int i = 0, num_slashes = 0;
14872f172c55SRobert Thurlow 	char *p, *spath, *op, *new_path;
14882f172c55SRobert Thurlow 
14892f172c55SRobert Thurlow 	/* Update knconf */
14902f172c55SRobert Thurlow 	knconf = svp->sv_knconf;
14912f172c55SRobert Thurlow 	free_knconf_contents(knconf);
14922f172c55SRobert Thurlow 	bzero(knconf, sizeof (struct knetconfig));
14932f172c55SRobert Thurlow 	svknconf = nfsfsloc->knconf;
14942f172c55SRobert Thurlow 	knconf->knc_semantics = svknconf->knc_semantics;
14952f172c55SRobert Thurlow 	knconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
14962f172c55SRobert Thurlow 	knconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
14972f172c55SRobert Thurlow 	knconf->knc_rdev = svknconf->knc_rdev;
14982f172c55SRobert Thurlow 	bcopy(svknconf->knc_protofmly, knconf->knc_protofmly, KNC_STRSIZE);
14992f172c55SRobert Thurlow 	bcopy(svknconf->knc_proto, knconf->knc_proto, KNC_STRSIZE);
15002f172c55SRobert Thurlow 
15012f172c55SRobert Thurlow 	/* Update server address */
15022f172c55SRobert Thurlow 	saddr = &svp->sv_addr;
15032f172c55SRobert Thurlow 	if (saddr->buf != NULL)
15042f172c55SRobert Thurlow 		kmem_free(saddr->buf, saddr->maxlen);
15052f172c55SRobert Thurlow 	saddr->buf  = kmem_alloc(nfsfsloc->addr->maxlen, KM_SLEEP);
15062f172c55SRobert Thurlow 	saddr->len = nfsfsloc->addr->len;
15072f172c55SRobert Thurlow 	saddr->maxlen = nfsfsloc->addr->maxlen;
15082f172c55SRobert Thurlow 	bcopy(nfsfsloc->addr->buf, saddr->buf, nfsfsloc->addr->len);
15092f172c55SRobert Thurlow 
15102f172c55SRobert Thurlow 	/* Update server name */
15112f172c55SRobert Thurlow 	host = fsp->server_val;
15122f172c55SRobert Thurlow 	kmem_free(svp->sv_hostname, svp->sv_hostnamelen);
15132f172c55SRobert Thurlow 	svp->sv_hostname = kmem_zalloc(host->utf8string_len + 1, KM_SLEEP);
15142f172c55SRobert Thurlow 	bcopy(host->utf8string_val, svp->sv_hostname, host->utf8string_len);
15152f172c55SRobert Thurlow 	svp->sv_hostname[host->utf8string_len] = '\0';
15162f172c55SRobert Thurlow 	svp->sv_hostnamelen = host->utf8string_len + 1;
15172f172c55SRobert Thurlow 
15182f172c55SRobert Thurlow 	/*
15192f172c55SRobert Thurlow 	 * Update server path.
15202f172c55SRobert Thurlow 	 * We need to setup proper path here.
15212f172c55SRobert Thurlow 	 * For ex., If we got a path name serv1:/rp/aaa/bbb
15222f172c55SRobert Thurlow 	 * where aaa is a referral and points to serv2:/rpool/aa
15232f172c55SRobert Thurlow 	 * we need to set the path to serv2:/rpool/aa/bbb
15242f172c55SRobert Thurlow 	 * The first part of this below code generates /rpool/aa
15252f172c55SRobert Thurlow 	 * and the second part appends /bbb to the server path.
15262f172c55SRobert Thurlow 	 */
15272f172c55SRobert Thurlow 	spath = p = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
15282f172c55SRobert Thurlow 	*p++ = '/';
15292f172c55SRobert Thurlow 	for (i = 0; i < fsp->rootpath.pathname4_len; i++) {
15302f172c55SRobert Thurlow 		component4 *comp;
15312f172c55SRobert Thurlow 
15322f172c55SRobert Thurlow 		comp = &fsp->rootpath.pathname4_val[i];
15332f172c55SRobert Thurlow 		/* If no space, null the string and bail */
15342f172c55SRobert Thurlow 		if ((p - spath) + comp->utf8string_len + 1 > MAXPATHLEN) {
15352f172c55SRobert Thurlow 			p = spath + MAXPATHLEN - 1;
15362f172c55SRobert Thurlow 			spath[0] = '\0';
15372f172c55SRobert Thurlow 			break;
15382f172c55SRobert Thurlow 		}
15392f172c55SRobert Thurlow 		bcopy(comp->utf8string_val, p, comp->utf8string_len);
15402f172c55SRobert Thurlow 		p += comp->utf8string_len;
15412f172c55SRobert Thurlow 		*p++ = '/';
15422f172c55SRobert Thurlow 	}
15432f172c55SRobert Thurlow 	if (fsp->rootpath.pathname4_len != 0)
15442f172c55SRobert Thurlow 		*(p - 1) = '\0';
15452f172c55SRobert Thurlow 	else
15462f172c55SRobert Thurlow 		*p = '\0';
15472f172c55SRobert Thurlow 	p = spath;
15482f172c55SRobert Thurlow 
15492f172c55SRobert Thurlow 	new_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
15502f172c55SRobert Thurlow 	(void) strlcpy(new_path, p, MAXPATHLEN);
15512f172c55SRobert Thurlow 	kmem_free(p, MAXPATHLEN);
15522f172c55SRobert Thurlow 	i = strlen(new_path);
15532f172c55SRobert Thurlow 
15542f172c55SRobert Thurlow 	for (op = orig_path; *op; op++) {
15552f172c55SRobert Thurlow 		if (*op == '/')
15562f172c55SRobert Thurlow 			num_slashes++;
15572f172c55SRobert Thurlow 		if (num_slashes == nth + 2) {
15582f172c55SRobert Thurlow 			while (*op != '\0') {
15592f172c55SRobert Thurlow 				new_path[i] = *op;
15602f172c55SRobert Thurlow 				i++;
15612f172c55SRobert Thurlow 				op++;
15622f172c55SRobert Thurlow 			}
15632f172c55SRobert Thurlow 			break;
15642f172c55SRobert Thurlow 		}
15652f172c55SRobert Thurlow 	}
15662f172c55SRobert Thurlow 	new_path[i] = '\0';
15672f172c55SRobert Thurlow 
15682f172c55SRobert Thurlow 	kmem_free(svp->sv_path, svp->sv_pathlen);
15692f172c55SRobert Thurlow 	svp->sv_pathlen = strlen(new_path) + 1;
15702f172c55SRobert Thurlow 	svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP);
15712f172c55SRobert Thurlow 	bcopy(new_path, svp->sv_path, svp->sv_pathlen);
15722f172c55SRobert Thurlow 	kmem_free(new_path, MAXPATHLEN);
15732f172c55SRobert Thurlow 
15742f172c55SRobert Thurlow 	/*
15752f172c55SRobert Thurlow 	 * All the security data is specific to old server.
15762f172c55SRobert Thurlow 	 * Clean it up except secdata which deals with mount options.
15772f172c55SRobert Thurlow 	 * We need to inherit that data. Copy secdata into our new servinfo4.
15782f172c55SRobert Thurlow 	 */
15792f172c55SRobert Thurlow 	if (svp->sv_dhsec) {
15802f172c55SRobert Thurlow 		sec_clnt_freeinfo(svp->sv_dhsec);
15812f172c55SRobert Thurlow 		svp->sv_dhsec = NULL;
15822f172c55SRobert Thurlow 	}
15832f172c55SRobert Thurlow 	if (svp->sv_save_secinfo &&
15842f172c55SRobert Thurlow 	    svp->sv_save_secinfo != svp->sv_secinfo) {
15852f172c55SRobert Thurlow 		secinfo_free(svp->sv_save_secinfo);
15862f172c55SRobert Thurlow 		svp->sv_save_secinfo = NULL;
15872f172c55SRobert Thurlow 	}
15882f172c55SRobert Thurlow 	if (svp->sv_secinfo) {
15892f172c55SRobert Thurlow 		secinfo_free(svp->sv_secinfo);
15902f172c55SRobert Thurlow 		svp->sv_secinfo = NULL;
15912f172c55SRobert Thurlow 	}
15922f172c55SRobert Thurlow 	svp->sv_currsec = NULL;
15932f172c55SRobert Thurlow 
15942f172c55SRobert Thurlow 	secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP);
15952f172c55SRobert Thurlow 	*secdata = *svp->sv_secdata;
15962f172c55SRobert Thurlow 	secdata->data = NULL;
15972f172c55SRobert Thurlow 	if (svp->sv_secdata) {
15982f172c55SRobert Thurlow 		sec_clnt_freeinfo(svp->sv_secdata);
15992f172c55SRobert Thurlow 		svp->sv_secdata = NULL;
16002f172c55SRobert Thurlow 	}
16012f172c55SRobert Thurlow 	svp->sv_secdata = secdata;
16022f172c55SRobert Thurlow }
16032f172c55SRobert Thurlow 
16042f172c55SRobert Thurlow /*
16052f172c55SRobert Thurlow  * Resolve a referral. The referral is in the n+1th component of
16062f172c55SRobert Thurlow  * svp->sv_path and has a parent nfs4 file handle "fh".
16072f172c55SRobert Thurlow  * Upon return, the sv_path will point to the new path that has referral
16082f172c55SRobert Thurlow  * component resolved to its referred path and part of original path.
16092f172c55SRobert Thurlow  * Hostname and other address information is also updated.
16102f172c55SRobert Thurlow  */
16112f172c55SRobert Thurlow int
16122f172c55SRobert Thurlow resolve_referral(mntinfo4_t *mi, servinfo4_t *svp, cred_t *cr, int nth,
16132f172c55SRobert Thurlow     nfs_fh4 *fh)
16142f172c55SRobert Thurlow {
16152f172c55SRobert Thurlow 	nfs4_sharedfh_t	*sfh;
16162f172c55SRobert Thurlow 	struct nfs_fsl_info nfsfsloc;
16172f172c55SRobert Thurlow 	nfs4_ga_res_t garp;
16182f172c55SRobert Thurlow 	COMPOUND4res_clnt callres;
16192f172c55SRobert Thurlow 	fs_location4	*fsp;
16202f172c55SRobert Thurlow 	char *nm, *orig_path;
16212f172c55SRobert Thurlow 	int orig_pathlen = 0, ret = -1, index;
16222f172c55SRobert Thurlow 
16232f172c55SRobert Thurlow 	if (svp->sv_pathlen <= 0)
16242f172c55SRobert Thurlow 		return (ret);
16252f172c55SRobert Thurlow 
16262f172c55SRobert Thurlow 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
16272f172c55SRobert Thurlow 	orig_pathlen = svp->sv_pathlen;
16282f172c55SRobert Thurlow 	orig_path = kmem_alloc(orig_pathlen, KM_SLEEP);
16292f172c55SRobert Thurlow 	bcopy(svp->sv_path, orig_path, orig_pathlen);
16302f172c55SRobert Thurlow 	nm = extract_referral_point(svp->sv_path, nth);
16312f172c55SRobert Thurlow 	setup_newsvpath(svp, nth);
16322f172c55SRobert Thurlow 	nfs_rw_exit(&svp->sv_lock);
16332f172c55SRobert Thurlow 
16342f172c55SRobert Thurlow 	sfh = sfh4_get(fh, mi);
16352f172c55SRobert Thurlow 	index = nfs4_process_referral(mi, sfh, nm, cr,
16362f172c55SRobert Thurlow 	    &garp, &callres, &nfsfsloc);
16372f172c55SRobert Thurlow 	sfh4_rele(&sfh);
16382f172c55SRobert Thurlow 	kmem_free(nm, MAXPATHLEN);
16392f172c55SRobert Thurlow 	if (index < 0) {
16402f172c55SRobert Thurlow 		kmem_free(orig_path, orig_pathlen);
16412f172c55SRobert Thurlow 		return (index);
16422f172c55SRobert Thurlow 	}
16432f172c55SRobert Thurlow 
16442f172c55SRobert Thurlow 	fsp =  &garp.n4g_ext_res->n4g_fslocations.locations_val[index];
16452f172c55SRobert Thurlow 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
16462f172c55SRobert Thurlow 	update_servinfo4(svp, fsp, &nfsfsloc, orig_path, nth);
16472f172c55SRobert Thurlow 	nfs_rw_exit(&svp->sv_lock);
16482f172c55SRobert Thurlow 
16492f172c55SRobert Thurlow 	mutex_enter(&mi->mi_lock);
16502f172c55SRobert Thurlow 	mi->mi_vfs_referral_loop_cnt++;
16512f172c55SRobert Thurlow 	mutex_exit(&mi->mi_lock);
16522f172c55SRobert Thurlow 
16532f172c55SRobert Thurlow 	ret = 0;
16542f172c55SRobert Thurlow bad:
16552f172c55SRobert Thurlow 	/* Free up XDR memory allocated in nfs4_process_referral() */
16562f172c55SRobert Thurlow 	xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc);
16572f172c55SRobert Thurlow 	xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
16582f172c55SRobert Thurlow 	kmem_free(orig_path, orig_pathlen);
16592f172c55SRobert Thurlow 
16602f172c55SRobert Thurlow 	return (ret);
16612f172c55SRobert Thurlow }
16622f172c55SRobert Thurlow 
16637c478bd9Sstevel@tonic-gate /*
16647c478bd9Sstevel@tonic-gate  * Get the root filehandle for the given filesystem and server, and update
16657c478bd9Sstevel@tonic-gate  * svp.
16667c478bd9Sstevel@tonic-gate  *
16677c478bd9Sstevel@tonic-gate  * If NFS4_GETFH_NEEDSOP is set, then use nfs4_start_fop and nfs4_end_fop
16687c478bd9Sstevel@tonic-gate  * to coordinate with recovery.  Otherwise, the caller is assumed to be
16697c478bd9Sstevel@tonic-gate  * the recovery thread or have already done a start_fop.
16707c478bd9Sstevel@tonic-gate  *
16717c478bd9Sstevel@tonic-gate  * Errors are returned by the nfs4_error_t parameter.
16727c478bd9Sstevel@tonic-gate  */
16737c478bd9Sstevel@tonic-gate static void
16747c478bd9Sstevel@tonic-gate nfs4getfh_otw(struct mntinfo4 *mi, servinfo4_t *svp, vtype_t *vtp,
1675b9238976Sth     int flags, cred_t *cr, nfs4_error_t *ep)
16767c478bd9Sstevel@tonic-gate {
16777c478bd9Sstevel@tonic-gate 	COMPOUND4args_clnt args;
16787c478bd9Sstevel@tonic-gate 	COMPOUND4res_clnt res;
16797c478bd9Sstevel@tonic-gate 	int doqueue = 1;
16807c478bd9Sstevel@tonic-gate 	nfs_argop4 *argop;
16817c478bd9Sstevel@tonic-gate 	nfs_resop4 *resop;
16827c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t *garp;
16837c478bd9Sstevel@tonic-gate 	int num_argops;
16847c478bd9Sstevel@tonic-gate 	lookup4_param_t lookuparg;
16857c478bd9Sstevel@tonic-gate 	nfs_fh4 *tmpfhp;
16867c478bd9Sstevel@tonic-gate 	nfs_fh4 *resfhp;
16877c478bd9Sstevel@tonic-gate 	bool_t needrecov = FALSE;
16887c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
16897c478bd9Sstevel@tonic-gate 	int llndx;
16907c478bd9Sstevel@tonic-gate 	int nthcomp;
16917c478bd9Sstevel@tonic-gate 	int recovery = !(flags & NFS4_GETFH_NEEDSOP);
16927c478bd9Sstevel@tonic-gate 
16937c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
16947c478bd9Sstevel@tonic-gate 	ASSERT(svp->sv_path != NULL);
16957c478bd9Sstevel@tonic-gate 	if (svp->sv_path[0] == '\0') {
16967c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
16977c478bd9Sstevel@tonic-gate 		nfs4_error_init(ep, EINVAL);
16987c478bd9Sstevel@tonic-gate 		return;
16997c478bd9Sstevel@tonic-gate 	}
17007c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
17017c478bd9Sstevel@tonic-gate 
17027c478bd9Sstevel@tonic-gate 	recov_state.rs_flags = 0;
17037c478bd9Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
17042f172c55SRobert Thurlow 
17057c478bd9Sstevel@tonic-gate recov_retry:
17062f172c55SRobert Thurlow 	if (mi->mi_vfs_referral_loop_cnt >= NFS4_REFERRAL_LOOP_MAX) {
17072f172c55SRobert Thurlow 		DTRACE_PROBE3(nfs4clnt__debug__referral__loop, mntinfo4 *,
17082f172c55SRobert Thurlow 		    mi, servinfo4_t *, svp, char *, "nfs4getfh_otw");
17092f172c55SRobert Thurlow 		nfs4_error_init(ep, EINVAL);
17102f172c55SRobert Thurlow 		return;
17112f172c55SRobert Thurlow 	}
17127c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(ep);
17137c478bd9Sstevel@tonic-gate 
17147c478bd9Sstevel@tonic-gate 	if (!recovery) {
17157c478bd9Sstevel@tonic-gate 		ep->error = nfs4_start_fop(mi, NULL, NULL, OH_MOUNT,
1716b9238976Sth 		    &recov_state, NULL);
17177c478bd9Sstevel@tonic-gate 
17187c478bd9Sstevel@tonic-gate 		/*
17197c478bd9Sstevel@tonic-gate 		 * If recovery has been started and this request as
17207c478bd9Sstevel@tonic-gate 		 * initiated by a mount, then we must wait for recovery
17217c478bd9Sstevel@tonic-gate 		 * to finish before proceeding, otherwise, the error
17227c478bd9Sstevel@tonic-gate 		 * cleanup would remove data structures needed by the
17237c478bd9Sstevel@tonic-gate 		 * recovery thread.
17247c478bd9Sstevel@tonic-gate 		 */
17257c478bd9Sstevel@tonic-gate 		if (ep->error) {
17267c478bd9Sstevel@tonic-gate 			mutex_enter(&mi->mi_lock);
17277c478bd9Sstevel@tonic-gate 			if (mi->mi_flags & MI4_MOUNTING) {
17287c478bd9Sstevel@tonic-gate 				mi->mi_flags |= MI4_RECOV_FAIL;
17297c478bd9Sstevel@tonic-gate 				mi->mi_error = EIO;
17307c478bd9Sstevel@tonic-gate 
17317c478bd9Sstevel@tonic-gate 				NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE,
17327c478bd9Sstevel@tonic-gate 				    "nfs4getfh_otw: waiting 4 recovery\n"));
17337c478bd9Sstevel@tonic-gate 
17347c478bd9Sstevel@tonic-gate 				while (mi->mi_flags & MI4_RECOV_ACTIV)
17357c478bd9Sstevel@tonic-gate 					cv_wait(&mi->mi_failover_cv,
17367c478bd9Sstevel@tonic-gate 					    &mi->mi_lock);
17377c478bd9Sstevel@tonic-gate 			}
17387c478bd9Sstevel@tonic-gate 			mutex_exit(&mi->mi_lock);
17397c478bd9Sstevel@tonic-gate 			return;
17407c478bd9Sstevel@tonic-gate 		}
17417c478bd9Sstevel@tonic-gate 
17427c478bd9Sstevel@tonic-gate 		/*
17437c478bd9Sstevel@tonic-gate 		 * If the client does not specify a specific flavor to use
17447c478bd9Sstevel@tonic-gate 		 * and has not gotten a secinfo list from the server yet,
17457c478bd9Sstevel@tonic-gate 		 * retrieve the secinfo list from the server and use a
17467c478bd9Sstevel@tonic-gate 		 * flavor from the list to mount.
17477c478bd9Sstevel@tonic-gate 		 *
17487c478bd9Sstevel@tonic-gate 		 * If fail to get the secinfo list from the server, then
17497c478bd9Sstevel@tonic-gate 		 * try the default flavor.
17507c478bd9Sstevel@tonic-gate 		 */
17517c478bd9Sstevel@tonic-gate 		if ((svp->sv_flags & SV4_TRYSECDEFAULT) &&
17527c478bd9Sstevel@tonic-gate 		    svp->sv_secinfo == NULL) {
17537c478bd9Sstevel@tonic-gate 			(void) nfs4_secinfo_path(mi, cr, FALSE);
17547c478bd9Sstevel@tonic-gate 		}
17557c478bd9Sstevel@tonic-gate 	}
17567c478bd9Sstevel@tonic-gate 
17577c478bd9Sstevel@tonic-gate 	if (recovery)
17587c478bd9Sstevel@tonic-gate 		args.ctag = TAG_REMAP_MOUNT;
17597c478bd9Sstevel@tonic-gate 	else
17607c478bd9Sstevel@tonic-gate 		args.ctag = TAG_MOUNT;
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate 	lookuparg.l4_getattrs = LKP4_ALL_ATTRIBUTES;
17637c478bd9Sstevel@tonic-gate 	lookuparg.argsp = &args;
17647c478bd9Sstevel@tonic-gate 	lookuparg.resp = &res;
17657c478bd9Sstevel@tonic-gate 	lookuparg.header_len = 2;	/* Putrootfh, getfh */
17667c478bd9Sstevel@tonic-gate 	lookuparg.trailer_len = 0;
17677c478bd9Sstevel@tonic-gate 	lookuparg.ga_bits = FATTR4_FSINFO_MASK;
17687c478bd9Sstevel@tonic-gate 	lookuparg.mi = mi;
17697c478bd9Sstevel@tonic-gate 
17707c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
17717c478bd9Sstevel@tonic-gate 	ASSERT(svp->sv_path != NULL);
17727c478bd9Sstevel@tonic-gate 	llndx = nfs4lookup_setup(svp->sv_path, &lookuparg, 0);
17737c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
17747c478bd9Sstevel@tonic-gate 
17757c478bd9Sstevel@tonic-gate 	argop = args.array;
17767c478bd9Sstevel@tonic-gate 	num_argops = args.array_len;
17777c478bd9Sstevel@tonic-gate 
17787c478bd9Sstevel@tonic-gate 	/* choose public or root filehandle */
17797c478bd9Sstevel@tonic-gate 	if (flags & NFS4_GETFH_PUBLIC)
17807c478bd9Sstevel@tonic-gate 		argop[0].argop = OP_PUTPUBFH;
17817c478bd9Sstevel@tonic-gate 	else
17827c478bd9Sstevel@tonic-gate 		argop[0].argop = OP_PUTROOTFH;
17837c478bd9Sstevel@tonic-gate 
17847c478bd9Sstevel@tonic-gate 	/* get fh */
17857c478bd9Sstevel@tonic-gate 	argop[1].argop = OP_GETFH;
17867c478bd9Sstevel@tonic-gate 
17877c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_call_debug, (CE_NOTE,
17887c478bd9Sstevel@tonic-gate 	    "nfs4getfh_otw: %s call, mi 0x%p",
17897c478bd9Sstevel@tonic-gate 	    needrecov ? "recov" : "first", (void *)mi));
17907c478bd9Sstevel@tonic-gate 
17917c478bd9Sstevel@tonic-gate 	rfs4call(mi, &args, &res, cr, &doqueue, RFSCALL_SOFT, ep);
17927c478bd9Sstevel@tonic-gate 
17937c478bd9Sstevel@tonic-gate 	needrecov = nfs4_needs_recovery(ep, FALSE, mi->mi_vfsp);
17947c478bd9Sstevel@tonic-gate 
17957c478bd9Sstevel@tonic-gate 	if (needrecov) {
17967c478bd9Sstevel@tonic-gate 		bool_t abort;
17977c478bd9Sstevel@tonic-gate 
17987c478bd9Sstevel@tonic-gate 		if (recovery) {
17997c478bd9Sstevel@tonic-gate 			nfs4args_lookup_free(argop, num_argops);
18007c478bd9Sstevel@tonic-gate 			kmem_free(argop,
1801b9238976Sth 			    lookuparg.arglen * sizeof (nfs_argop4));
18027c478bd9Sstevel@tonic-gate 			if (!ep->error)
18037c478bd9Sstevel@tonic-gate 				(void) xdr_free(xdr_COMPOUND4res_clnt,
1804b9238976Sth 				    (caddr_t)&res);
18057c478bd9Sstevel@tonic-gate 			return;
18067c478bd9Sstevel@tonic-gate 		}
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_recov_debug,
18097c478bd9Sstevel@tonic-gate 		    (CE_NOTE, "nfs4getfh_otw: initiating recovery\n"));
18107c478bd9Sstevel@tonic-gate 
18117c478bd9Sstevel@tonic-gate 		abort = nfs4_start_recovery(ep, mi, NULL,
18122f172c55SRobert Thurlow 		    NULL, NULL, NULL, OP_GETFH, NULL, NULL, NULL);
18137c478bd9Sstevel@tonic-gate 		if (!ep->error) {
18147c478bd9Sstevel@tonic-gate 			ep->error = geterrno4(res.status);
18157c478bd9Sstevel@tonic-gate 			(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
18167c478bd9Sstevel@tonic-gate 		}
18177c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
18187c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
18197c478bd9Sstevel@tonic-gate 		nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state, needrecov);
18207c478bd9Sstevel@tonic-gate 		/* have another go? */
18217c478bd9Sstevel@tonic-gate 		if (abort == FALSE)
18227c478bd9Sstevel@tonic-gate 			goto recov_retry;
18237c478bd9Sstevel@tonic-gate 		return;
18247c478bd9Sstevel@tonic-gate 	}
18257c478bd9Sstevel@tonic-gate 
18267c478bd9Sstevel@tonic-gate 	/*
18277c478bd9Sstevel@tonic-gate 	 * No recovery, but check if error is set.
18287c478bd9Sstevel@tonic-gate 	 */
18297c478bd9Sstevel@tonic-gate 	if (ep->error)  {
18307c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
18317c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
18327c478bd9Sstevel@tonic-gate 		if (!recovery)
18337c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
1834b9238976Sth 			    needrecov);
18357c478bd9Sstevel@tonic-gate 		return;
18367c478bd9Sstevel@tonic-gate 	}
18377c478bd9Sstevel@tonic-gate 
18387c478bd9Sstevel@tonic-gate is_link_err:
18397c478bd9Sstevel@tonic-gate 
18407c478bd9Sstevel@tonic-gate 	/* for non-recovery errors */
18412f172c55SRobert Thurlow 	if (res.status && res.status != NFS4ERR_SYMLINK &&
18422f172c55SRobert Thurlow 	    res.status != NFS4ERR_MOVED) {
18437c478bd9Sstevel@tonic-gate 		if (!recovery) {
18447c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
1845b9238976Sth 			    needrecov);
18467c478bd9Sstevel@tonic-gate 		}
18477c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
18487c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
18497c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
18507c478bd9Sstevel@tonic-gate 		return;
18517c478bd9Sstevel@tonic-gate 	}
18527c478bd9Sstevel@tonic-gate 
18537c478bd9Sstevel@tonic-gate 	/*
18547c478bd9Sstevel@tonic-gate 	 * If any intermediate component in the path is a symbolic link,
18557c478bd9Sstevel@tonic-gate 	 * resolve the symlink, then try mount again using the new path.
18567c478bd9Sstevel@tonic-gate 	 */
18572f172c55SRobert Thurlow 	if (res.status == NFS4ERR_SYMLINK || res.status == NFS4ERR_MOVED) {
18587c478bd9Sstevel@tonic-gate 		int where;
18597c478bd9Sstevel@tonic-gate 
18602f172c55SRobert Thurlow 		/*
18612f172c55SRobert Thurlow 		 * Need to call nfs4_end_op before resolve_sympath to avoid
18622f172c55SRobert Thurlow 		 * potential nfs4_start_op deadlock.
18632f172c55SRobert Thurlow 		 */
18642f172c55SRobert Thurlow 		if (!recovery)
18652f172c55SRobert Thurlow 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
18662f172c55SRobert Thurlow 			    needrecov);
18672f172c55SRobert Thurlow 
18687c478bd9Sstevel@tonic-gate 		/*
18697c478bd9Sstevel@tonic-gate 		 * This must be from OP_LOOKUP failure. The (cfh) for this
18707c478bd9Sstevel@tonic-gate 		 * OP_LOOKUP is a symlink node. Found out where the
18717c478bd9Sstevel@tonic-gate 		 * OP_GETFH is for the (cfh) that is a symlink node.
18727c478bd9Sstevel@tonic-gate 		 *
18737c478bd9Sstevel@tonic-gate 		 * Example:
18747c478bd9Sstevel@tonic-gate 		 * (mount) PUTROOTFH, GETFH, LOOKUP comp1, GETFH, GETATTR,
18757c478bd9Sstevel@tonic-gate 		 * LOOKUP comp2, GETFH, GETATTR, LOOKUP comp3, GETFH, GETATTR
18767c478bd9Sstevel@tonic-gate 		 *
18777c478bd9Sstevel@tonic-gate 		 * LOOKUP comp3 fails with SYMLINK because comp2 is a symlink.
18787c478bd9Sstevel@tonic-gate 		 * In this case, where = 7, nthcomp = 2.
18797c478bd9Sstevel@tonic-gate 		 */
18807c478bd9Sstevel@tonic-gate 		where = res.array_len - 2;
18817c478bd9Sstevel@tonic-gate 		ASSERT(where > 0);
18827c478bd9Sstevel@tonic-gate 
18832f172c55SRobert Thurlow 		if (res.status == NFS4ERR_SYMLINK) {
18847c478bd9Sstevel@tonic-gate 
18852f172c55SRobert Thurlow 			resop = &res.array[where - 1];
18862f172c55SRobert Thurlow 			ASSERT(resop->resop == OP_GETFH);
18872f172c55SRobert Thurlow 			tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
18882f172c55SRobert Thurlow 			nthcomp = res.array_len/3 - 1;
18892f172c55SRobert Thurlow 			ep->error = resolve_sympath(mi, svp, nthcomp,
18902f172c55SRobert Thurlow 			    tmpfhp, cr, flags);
18917c478bd9Sstevel@tonic-gate 
18922f172c55SRobert Thurlow 		} else if (res.status == NFS4ERR_MOVED) {
18932f172c55SRobert Thurlow 
18942f172c55SRobert Thurlow 			resop = &res.array[where - 2];
18952f172c55SRobert Thurlow 			ASSERT(resop->resop == OP_GETFH);
18962f172c55SRobert Thurlow 			tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
18972f172c55SRobert Thurlow 			nthcomp = res.array_len/3 - 1;
18982f172c55SRobert Thurlow 			ep->error = resolve_referral(mi, svp, cr, nthcomp,
18992f172c55SRobert Thurlow 			    tmpfhp);
19002f172c55SRobert Thurlow 		}
19017c478bd9Sstevel@tonic-gate 
19027c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
19037c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
19047c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
19057c478bd9Sstevel@tonic-gate 
19067c478bd9Sstevel@tonic-gate 		if (ep->error)
19077c478bd9Sstevel@tonic-gate 			return;
19087c478bd9Sstevel@tonic-gate 
19097c478bd9Sstevel@tonic-gate 		goto recov_retry;
19107c478bd9Sstevel@tonic-gate 	}
19117c478bd9Sstevel@tonic-gate 
19127c478bd9Sstevel@tonic-gate 	/* getfh */
19137c478bd9Sstevel@tonic-gate 	resop = &res.array[res.array_len - 2];
19147c478bd9Sstevel@tonic-gate 	ASSERT(resop->resop == OP_GETFH);
19157c478bd9Sstevel@tonic-gate 	resfhp = &resop->nfs_resop4_u.opgetfh.object;
19167c478bd9Sstevel@tonic-gate 
19177c478bd9Sstevel@tonic-gate 	/* getattr fsinfo res */
19187c478bd9Sstevel@tonic-gate 	resop++;
19197c478bd9Sstevel@tonic-gate 	garp = &resop->nfs_resop4_u.opgetattr.ga_res;
19207c478bd9Sstevel@tonic-gate 
19217c478bd9Sstevel@tonic-gate 	*vtp = garp->n4g_va.va_type;
19227c478bd9Sstevel@tonic-gate 
19237c478bd9Sstevel@tonic-gate 	mi->mi_fh_expire_type = garp->n4g_ext_res->n4g_fet;
19247c478bd9Sstevel@tonic-gate 
19257c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
19267c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_pc4.pc4_link_support)
19277c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_LINK;
19287c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_pc4.pc4_symlink_support)
19297c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_SYMLINK;
19307c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_suppattrs & FATTR4_ACL_MASK)
19317c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_ACL;
19327c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
19337c478bd9Sstevel@tonic-gate 
19347c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_maxread == 0)
19357c478bd9Sstevel@tonic-gate 		mi->mi_tsize =
1936b9238976Sth 		    MIN(MAXBSIZE, mi->mi_tsize);
19377c478bd9Sstevel@tonic-gate 	else
19387c478bd9Sstevel@tonic-gate 		mi->mi_tsize =
1939b9238976Sth 		    MIN(garp->n4g_ext_res->n4g_maxread,
1940b9238976Sth 		    mi->mi_tsize);
19417c478bd9Sstevel@tonic-gate 
19427c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_maxwrite == 0)
19437c478bd9Sstevel@tonic-gate 		mi->mi_stsize =
1944b9238976Sth 		    MIN(MAXBSIZE, mi->mi_stsize);
19457c478bd9Sstevel@tonic-gate 	else
19467c478bd9Sstevel@tonic-gate 		mi->mi_stsize =
1947b9238976Sth 		    MIN(garp->n4g_ext_res->n4g_maxwrite,
1948b9238976Sth 		    mi->mi_stsize);
19497c478bd9Sstevel@tonic-gate 
19507c478bd9Sstevel@tonic-gate 	if (garp->n4g_ext_res->n4g_maxfilesize != 0)
19517c478bd9Sstevel@tonic-gate 		mi->mi_maxfilesize =
1952b9238976Sth 		    MIN(garp->n4g_ext_res->n4g_maxfilesize,
1953b9238976Sth 		    mi->mi_maxfilesize);
19547c478bd9Sstevel@tonic-gate 
19557c478bd9Sstevel@tonic-gate 	/*
19567c478bd9Sstevel@tonic-gate 	 * If the final component is a a symbolic link, resolve the symlink,
19577c478bd9Sstevel@tonic-gate 	 * then try mount again using the new path.
19587c478bd9Sstevel@tonic-gate 	 *
19597c478bd9Sstevel@tonic-gate 	 * Assume no symbolic link for root filesysm "/".
19607c478bd9Sstevel@tonic-gate 	 */
19617c478bd9Sstevel@tonic-gate 	if (*vtp == VLNK) {
19627c478bd9Sstevel@tonic-gate 		/*
19637c478bd9Sstevel@tonic-gate 		 * nthcomp is the total result length minus
19647c478bd9Sstevel@tonic-gate 		 * the 1st 2 OPs (PUTROOTFH, GETFH),
19657c478bd9Sstevel@tonic-gate 		 * then divided by 3 (LOOKUP,GETFH,GETATTR)
19667c478bd9Sstevel@tonic-gate 		 *
19677c478bd9Sstevel@tonic-gate 		 * e.g. PUTROOTFH GETFH LOOKUP 1st-comp GETFH GETATTR
19687c478bd9Sstevel@tonic-gate 		 *	LOOKUP 2nd-comp GETFH GETATTR
19697c478bd9Sstevel@tonic-gate 		 *
19707c478bd9Sstevel@tonic-gate 		 *	(8 - 2)/3 = 2
19717c478bd9Sstevel@tonic-gate 		 */
19727c478bd9Sstevel@tonic-gate 		nthcomp = (res.array_len - 2)/3;
19737c478bd9Sstevel@tonic-gate 
19747c478bd9Sstevel@tonic-gate 		/*
19757c478bd9Sstevel@tonic-gate 		 * Need to call nfs4_end_op before resolve_sympath to avoid
19767c478bd9Sstevel@tonic-gate 		 * potential nfs4_start_op deadlock. See RFE 4777612.
19777c478bd9Sstevel@tonic-gate 		 */
19787c478bd9Sstevel@tonic-gate 		if (!recovery)
19797c478bd9Sstevel@tonic-gate 			nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state,
1980b9238976Sth 			    needrecov);
19817c478bd9Sstevel@tonic-gate 
19827c478bd9Sstevel@tonic-gate 		ep->error = resolve_sympath(mi, svp, nthcomp, resfhp, cr,
1983b9238976Sth 		    flags);
19847c478bd9Sstevel@tonic-gate 
19857c478bd9Sstevel@tonic-gate 		nfs4args_lookup_free(argop, num_argops);
19867c478bd9Sstevel@tonic-gate 		kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
19877c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
19887c478bd9Sstevel@tonic-gate 
19897c478bd9Sstevel@tonic-gate 		if (ep->error)
19907c478bd9Sstevel@tonic-gate 			return;
19917c478bd9Sstevel@tonic-gate 
19927c478bd9Sstevel@tonic-gate 		goto recov_retry;
19937c478bd9Sstevel@tonic-gate 	}
19947c478bd9Sstevel@tonic-gate 
19957c478bd9Sstevel@tonic-gate 	/*
19967c478bd9Sstevel@tonic-gate 	 * We need to figure out where in the compound the getfh
19977c478bd9Sstevel@tonic-gate 	 * for the parent directory is. If the object to be mounted is
19987c478bd9Sstevel@tonic-gate 	 * the root, then there is no lookup at all:
19997c478bd9Sstevel@tonic-gate 	 * PUTROOTFH, GETFH.
20007c478bd9Sstevel@tonic-gate 	 * If the object to be mounted is in the root, then the compound is:
20017c478bd9Sstevel@tonic-gate 	 * PUTROOTFH, GETFH, LOOKUP, GETFH, GETATTR.
20027c478bd9Sstevel@tonic-gate 	 * In either of these cases, the index of the GETFH is 1.
20037c478bd9Sstevel@tonic-gate 	 * If it is not at the root, then it's something like:
20047c478bd9Sstevel@tonic-gate 	 * PUTROOTFH, GETFH, LOOKUP, GETFH, GETATTR,
20057c478bd9Sstevel@tonic-gate 	 * LOOKUP, GETFH, GETATTR
20067c478bd9Sstevel@tonic-gate 	 * In this case, the index is llndx (last lookup index) - 2.
20077c478bd9Sstevel@tonic-gate 	 */
20087c478bd9Sstevel@tonic-gate 	if (llndx == -1 || llndx == 2)
20097c478bd9Sstevel@tonic-gate 		resop = &res.array[1];
20107c478bd9Sstevel@tonic-gate 	else {
20117c478bd9Sstevel@tonic-gate 		ASSERT(llndx > 2);
20127c478bd9Sstevel@tonic-gate 		resop = &res.array[llndx-2];
20137c478bd9Sstevel@tonic-gate 	}
20147c478bd9Sstevel@tonic-gate 
20157c478bd9Sstevel@tonic-gate 	ASSERT(resop->resop == OP_GETFH);
20167c478bd9Sstevel@tonic-gate 	tmpfhp = &resop->nfs_resop4_u.opgetfh.object;
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 	/* save the filehandles for the replica */
20197c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
20207c478bd9Sstevel@tonic-gate 	ASSERT(tmpfhp->nfs_fh4_len <= NFS4_FHSIZE);
20217c478bd9Sstevel@tonic-gate 	svp->sv_pfhandle.fh_len = tmpfhp->nfs_fh4_len;
20227c478bd9Sstevel@tonic-gate 	bcopy(tmpfhp->nfs_fh4_val, svp->sv_pfhandle.fh_buf,
20237c478bd9Sstevel@tonic-gate 	    tmpfhp->nfs_fh4_len);
20247c478bd9Sstevel@tonic-gate 	ASSERT(resfhp->nfs_fh4_len <= NFS4_FHSIZE);
20257c478bd9Sstevel@tonic-gate 	svp->sv_fhandle.fh_len = resfhp->nfs_fh4_len;
20267c478bd9Sstevel@tonic-gate 	bcopy(resfhp->nfs_fh4_val, svp->sv_fhandle.fh_buf, resfhp->nfs_fh4_len);
20277c478bd9Sstevel@tonic-gate 
20287c478bd9Sstevel@tonic-gate 	/* initialize fsid and supp_attrs for server fs */
20297c478bd9Sstevel@tonic-gate 	svp->sv_fsid = garp->n4g_fsid;
20307c478bd9Sstevel@tonic-gate 	svp->sv_supp_attrs =
2031b9238976Sth 	    garp->n4g_ext_res->n4g_suppattrs | FATTR4_MANDATTR_MASK;
20327c478bd9Sstevel@tonic-gate 
20337c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
20347c478bd9Sstevel@tonic-gate 	nfs4args_lookup_free(argop, num_argops);
20357c478bd9Sstevel@tonic-gate 	kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4));
20367c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
20377c478bd9Sstevel@tonic-gate 	if (!recovery)
20387c478bd9Sstevel@tonic-gate 		nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state, needrecov);
20397c478bd9Sstevel@tonic-gate }
20407c478bd9Sstevel@tonic-gate 
20412f172c55SRobert Thurlow /*
20422f172c55SRobert Thurlow  * Save a copy of Servinfo4_t structure.
20432f172c55SRobert Thurlow  * We might need when there is a failure in getting file handle
20442f172c55SRobert Thurlow  * in case of a referral to replace servinfo4 struct and try again.
20452f172c55SRobert Thurlow  */
20462f172c55SRobert Thurlow static struct servinfo4 *
20472f172c55SRobert Thurlow copy_svp(servinfo4_t *nsvp)
20482f172c55SRobert Thurlow {
20492f172c55SRobert Thurlow 	servinfo4_t *svp = NULL;
20502f172c55SRobert Thurlow 	struct knetconfig *sknconf, *tknconf;
20512f172c55SRobert Thurlow 	struct netbuf *saddr, *taddr;
20522f172c55SRobert Thurlow 
20532f172c55SRobert Thurlow 	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
20542f172c55SRobert Thurlow 	nfs_rw_init(&svp->sv_lock, NULL, RW_DEFAULT, NULL);
20552f172c55SRobert Thurlow 	svp->sv_flags = nsvp->sv_flags;
20562f172c55SRobert Thurlow 	svp->sv_fsid = nsvp->sv_fsid;
20572f172c55SRobert Thurlow 	svp->sv_hostnamelen = nsvp->sv_hostnamelen;
20582f172c55SRobert Thurlow 	svp->sv_pathlen = nsvp->sv_pathlen;
20592f172c55SRobert Thurlow 	svp->sv_supp_attrs = nsvp->sv_supp_attrs;
20602f172c55SRobert Thurlow 
20612f172c55SRobert Thurlow 	svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP);
20622f172c55SRobert Thurlow 	svp->sv_hostname = kmem_alloc(svp->sv_hostnamelen, KM_SLEEP);
20632f172c55SRobert Thurlow 	bcopy(nsvp->sv_hostname, svp->sv_hostname, svp->sv_hostnamelen);
20642f172c55SRobert Thurlow 	bcopy(nsvp->sv_path, svp->sv_path, svp->sv_pathlen);
20652f172c55SRobert Thurlow 
20662f172c55SRobert Thurlow 	saddr = &nsvp->sv_addr;
20672f172c55SRobert Thurlow 	taddr = &svp->sv_addr;
20682f172c55SRobert Thurlow 	taddr->maxlen = saddr->maxlen;
20692f172c55SRobert Thurlow 	taddr->len = saddr->len;
20702f172c55SRobert Thurlow 	if (saddr->len > 0) {
20712f172c55SRobert Thurlow 		taddr->buf = kmem_zalloc(saddr->maxlen, KM_SLEEP);
20722f172c55SRobert Thurlow 		bcopy(saddr->buf, taddr->buf, saddr->len);
20732f172c55SRobert Thurlow 	}
20742f172c55SRobert Thurlow 
20752f172c55SRobert Thurlow 	svp->sv_knconf = kmem_zalloc(sizeof (struct knetconfig), KM_SLEEP);
20762f172c55SRobert Thurlow 	sknconf = nsvp->sv_knconf;
20772f172c55SRobert Thurlow 	tknconf = svp->sv_knconf;
20782f172c55SRobert Thurlow 	tknconf->knc_semantics = sknconf->knc_semantics;
20792f172c55SRobert Thurlow 	tknconf->knc_rdev = sknconf->knc_rdev;
20802f172c55SRobert Thurlow 	if (sknconf->knc_proto != NULL) {
20812f172c55SRobert Thurlow 		tknconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
20822f172c55SRobert Thurlow 		bcopy(sknconf->knc_proto, (char *)tknconf->knc_proto,
20832f172c55SRobert Thurlow 		    KNC_STRSIZE);
20842f172c55SRobert Thurlow 	}
20852f172c55SRobert Thurlow 	if (sknconf->knc_protofmly != NULL) {
20862f172c55SRobert Thurlow 		tknconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
20872f172c55SRobert Thurlow 		bcopy(sknconf->knc_protofmly, (char *)tknconf->knc_protofmly,
20882f172c55SRobert Thurlow 		    KNC_STRSIZE);
20892f172c55SRobert Thurlow 	}
20902f172c55SRobert Thurlow 
20912f172c55SRobert Thurlow 	if (nsvp->sv_origknconf != NULL) {
20922f172c55SRobert Thurlow 		svp->sv_origknconf = kmem_zalloc(sizeof (struct knetconfig),
20932f172c55SRobert Thurlow 		    KM_SLEEP);
20942f172c55SRobert Thurlow 		sknconf = nsvp->sv_origknconf;
20952f172c55SRobert Thurlow 		tknconf = svp->sv_origknconf;
20962f172c55SRobert Thurlow 		tknconf->knc_semantics = sknconf->knc_semantics;
20972f172c55SRobert Thurlow 		tknconf->knc_rdev = sknconf->knc_rdev;
20982f172c55SRobert Thurlow 		if (sknconf->knc_proto != NULL) {
20992f172c55SRobert Thurlow 			tknconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
21002f172c55SRobert Thurlow 			bcopy(sknconf->knc_proto, (char *)tknconf->knc_proto,
21012f172c55SRobert Thurlow 			    KNC_STRSIZE);
21022f172c55SRobert Thurlow 		}
21032f172c55SRobert Thurlow 		if (sknconf->knc_protofmly != NULL) {
21042f172c55SRobert Thurlow 			tknconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE,
21052f172c55SRobert Thurlow 			    KM_SLEEP);
21062f172c55SRobert Thurlow 			bcopy(sknconf->knc_protofmly,
21072f172c55SRobert Thurlow 			    (char *)tknconf->knc_protofmly, KNC_STRSIZE);
21082f172c55SRobert Thurlow 		}
21092f172c55SRobert Thurlow 	}
21102f172c55SRobert Thurlow 
21112f172c55SRobert Thurlow 	svp->sv_secdata = copy_sec_data(nsvp->sv_secdata);
21122f172c55SRobert Thurlow 	svp->sv_dhsec = copy_sec_data(svp->sv_dhsec);
21132f172c55SRobert Thurlow 	/*
21142f172c55SRobert Thurlow 	 * Rest of the security information is not copied as they are built
21152f172c55SRobert Thurlow 	 * with the information available from secdata and dhsec.
21162f172c55SRobert Thurlow 	 */
21172f172c55SRobert Thurlow 	svp->sv_next = NULL;
21182f172c55SRobert Thurlow 
21192f172c55SRobert Thurlow 	return (svp);
21202f172c55SRobert Thurlow }
21212f172c55SRobert Thurlow 
21222f172c55SRobert Thurlow servinfo4_t *
21232f172c55SRobert Thurlow restore_svp(mntinfo4_t *mi, servinfo4_t *svp, servinfo4_t *origsvp)
21242f172c55SRobert Thurlow {
21252f172c55SRobert Thurlow 	servinfo4_t *srvnext, *tmpsrv;
21262f172c55SRobert Thurlow 
21272f172c55SRobert Thurlow 	if (strcmp(svp->sv_hostname, origsvp->sv_hostname) != 0) {
21282f172c55SRobert Thurlow 		/*
21292f172c55SRobert Thurlow 		 * Since the hostname changed, we must be dealing
21302f172c55SRobert Thurlow 		 * with a referral, and the lookup failed.  We will
21312f172c55SRobert Thurlow 		 * restore the whole servinfo4_t to what it was before.
21322f172c55SRobert Thurlow 		 */
21332f172c55SRobert Thurlow 		srvnext = svp->sv_next;
21342f172c55SRobert Thurlow 		svp->sv_next = NULL;
21352f172c55SRobert Thurlow 		tmpsrv = copy_svp(origsvp);
21362f172c55SRobert Thurlow 		sv4_free(svp);
21372f172c55SRobert Thurlow 		svp = tmpsrv;
21382f172c55SRobert Thurlow 		svp->sv_next = srvnext;
21392f172c55SRobert Thurlow 		mutex_enter(&mi->mi_lock);
21402f172c55SRobert Thurlow 		mi->mi_servers = svp;
21412f172c55SRobert Thurlow 		mi->mi_curr_serv = svp;
21422f172c55SRobert Thurlow 		mutex_exit(&mi->mi_lock);
21432f172c55SRobert Thurlow 
21442f172c55SRobert Thurlow 	} else if (origsvp->sv_pathlen != svp->sv_pathlen) {
21452f172c55SRobert Thurlow 
21462f172c55SRobert Thurlow 		/*
21472f172c55SRobert Thurlow 		 * For symlink case: restore original path because
21482f172c55SRobert Thurlow 		 * it might have contained symlinks that were
21492f172c55SRobert Thurlow 		 * expanded by nfsgetfh_otw before the failure occurred.
21502f172c55SRobert Thurlow 		 */
21515301ec54SRobert Thurlow 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
21522f172c55SRobert Thurlow 		kmem_free(svp->sv_path, svp->sv_pathlen);
21532f172c55SRobert Thurlow 		svp->sv_path =
21542f172c55SRobert Thurlow 		    kmem_alloc(origsvp->sv_pathlen, KM_SLEEP);
21552f172c55SRobert Thurlow 		svp->sv_pathlen = origsvp->sv_pathlen;
21562f172c55SRobert Thurlow 		bcopy(origsvp->sv_path, svp->sv_path,
21572f172c55SRobert Thurlow 		    origsvp->sv_pathlen);
21582f172c55SRobert Thurlow 		nfs_rw_exit(&svp->sv_lock);
21592f172c55SRobert Thurlow 	}
21602f172c55SRobert Thurlow 	return (svp);
21612f172c55SRobert Thurlow }
21622f172c55SRobert Thurlow 
21637c478bd9Sstevel@tonic-gate static ushort_t nfs4_max_threads = 8;	/* max number of active async threads */
2164c242f9a0Schunli zhang - Sun Microsystems - Irvine United States uint_t nfs4_bsize = 32 * 1024;	/* client `block' size */
21657c478bd9Sstevel@tonic-gate static uint_t nfs4_async_clusters = 1;	/* # of reqs from each async queue */
21667c478bd9Sstevel@tonic-gate static uint_t nfs4_cots_timeo = NFS_COTS_TIMEO;
21677c478bd9Sstevel@tonic-gate 
21687c478bd9Sstevel@tonic-gate /*
21697c478bd9Sstevel@tonic-gate  * Remap the root filehandle for the given filesystem.
21707c478bd9Sstevel@tonic-gate  *
21717c478bd9Sstevel@tonic-gate  * results returned via the nfs4_error_t parameter.
21727c478bd9Sstevel@tonic-gate  */
21737c478bd9Sstevel@tonic-gate void
21747c478bd9Sstevel@tonic-gate nfs4_remap_root(mntinfo4_t *mi, nfs4_error_t *ep, int flags)
21757c478bd9Sstevel@tonic-gate {
21762f172c55SRobert Thurlow 	struct servinfo4 *svp, *origsvp;
21777c478bd9Sstevel@tonic-gate 	vtype_t vtype;
21787c478bd9Sstevel@tonic-gate 	nfs_fh4 rootfh;
21797c478bd9Sstevel@tonic-gate 	int getfh_flags;
21802f172c55SRobert Thurlow 	int num_retry;
21817c478bd9Sstevel@tonic-gate 
21827c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
21838c9e5ad2Saalok 
21848c9e5ad2Saalok remap_retry:
21857c478bd9Sstevel@tonic-gate 	svp = mi->mi_curr_serv;
21867c478bd9Sstevel@tonic-gate 	getfh_flags =
2187b9238976Sth 	    (flags & NFS4_REMAP_NEEDSOP) ? NFS4_GETFH_NEEDSOP : 0;
21887c478bd9Sstevel@tonic-gate 	getfh_flags |=
2189b9238976Sth 	    (mi->mi_flags & MI4_PUBLIC) ? NFS4_GETFH_PUBLIC : 0;
21907c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
21917c478bd9Sstevel@tonic-gate 
21927c478bd9Sstevel@tonic-gate 	/*
21937c478bd9Sstevel@tonic-gate 	 * Just in case server path being mounted contains
21947c478bd9Sstevel@tonic-gate 	 * symlinks and fails w/STALE, save the initial sv_path
21957c478bd9Sstevel@tonic-gate 	 * so we can redrive the initial mount compound with the
21967c478bd9Sstevel@tonic-gate 	 * initial sv_path -- not a symlink-expanded version.
21977c478bd9Sstevel@tonic-gate 	 *
21987c478bd9Sstevel@tonic-gate 	 * This could only happen if a symlink was expanded
21997c478bd9Sstevel@tonic-gate 	 * and the expanded mount compound failed stale.  Because
22007c478bd9Sstevel@tonic-gate 	 * it could be the case that the symlink was removed at
22017c478bd9Sstevel@tonic-gate 	 * the server (and replaced with another symlink/dir,
22027c478bd9Sstevel@tonic-gate 	 * we need to use the initial sv_path when attempting
22037c478bd9Sstevel@tonic-gate 	 * to re-lookup everything and recover.
22047c478bd9Sstevel@tonic-gate 	 */
22057c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
22062f172c55SRobert Thurlow 	origsvp = copy_svp(svp);
22077c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
22087c478bd9Sstevel@tonic-gate 
22097c478bd9Sstevel@tonic-gate 	num_retry = nfs4_max_mount_retry;
22107c478bd9Sstevel@tonic-gate 
22117c478bd9Sstevel@tonic-gate 	do {
22127c478bd9Sstevel@tonic-gate 		/*
22137c478bd9Sstevel@tonic-gate 		 * Get the root fh from the server.  Retry nfs4_max_mount_retry
22147c478bd9Sstevel@tonic-gate 		 * (2) times if it fails with STALE since the recovery
22157c478bd9Sstevel@tonic-gate 		 * infrastructure doesn't do STALE recovery for components
22167c478bd9Sstevel@tonic-gate 		 * of the server path to the object being mounted.
22177c478bd9Sstevel@tonic-gate 		 */
22187c478bd9Sstevel@tonic-gate 		nfs4getfh_otw(mi, svp, &vtype, getfh_flags, CRED(), ep);
22197c478bd9Sstevel@tonic-gate 
22207c478bd9Sstevel@tonic-gate 		if (ep->error == 0 && ep->stat == NFS4_OK)
22217c478bd9Sstevel@tonic-gate 			break;
22227c478bd9Sstevel@tonic-gate 
22237c478bd9Sstevel@tonic-gate 		/*
22247c478bd9Sstevel@tonic-gate 		 * For some reason, the mount compound failed.  Before
22252f172c55SRobert Thurlow 		 * retrying, we need to restore original conditions.
22267c478bd9Sstevel@tonic-gate 		 */
22272f172c55SRobert Thurlow 		svp = restore_svp(mi, svp, origsvp);
22287c478bd9Sstevel@tonic-gate 
22297c478bd9Sstevel@tonic-gate 	} while (num_retry-- > 0);
22307c478bd9Sstevel@tonic-gate 
22312f172c55SRobert Thurlow 	sv4_free(origsvp);
22327c478bd9Sstevel@tonic-gate 
22337c478bd9Sstevel@tonic-gate 	if (ep->error != 0 || ep->stat != 0) {
22347c478bd9Sstevel@tonic-gate 		return;
22357c478bd9Sstevel@tonic-gate 	}
22367c478bd9Sstevel@tonic-gate 
22377c478bd9Sstevel@tonic-gate 	if (vtype != VNON && vtype != mi->mi_type) {
22387c478bd9Sstevel@tonic-gate 		/* shouldn't happen */
22397c478bd9Sstevel@tonic-gate 		zcmn_err(mi->mi_zone->zone_id, CE_WARN,
2240b9238976Sth 		    "nfs4_remap_root: server root vnode type (%d) doesn't "
2241b9238976Sth 		    "match mount info (%d)", vtype, mi->mi_type);
22427c478bd9Sstevel@tonic-gate 	}
22437c478bd9Sstevel@tonic-gate 
22447c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
22457c478bd9Sstevel@tonic-gate 	rootfh.nfs_fh4_val = svp->sv_fhandle.fh_buf;
22467c478bd9Sstevel@tonic-gate 	rootfh.nfs_fh4_len = svp->sv_fhandle.fh_len;
22477c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
22487c478bd9Sstevel@tonic-gate 	sfh4_update(mi->mi_rootfh, &rootfh);
22497c478bd9Sstevel@tonic-gate 
22507c478bd9Sstevel@tonic-gate 	/*
22518c9e5ad2Saalok 	 * It's possible that recovery took place on the filesystem
22528c9e5ad2Saalok 	 * and the server has been updated between the time we did
22538c9e5ad2Saalok 	 * the nfs4getfh_otw and now. Re-drive the otw operation
22548c9e5ad2Saalok 	 * to make sure we have a good fh.
22557c478bd9Sstevel@tonic-gate 	 */
22567c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
22578c9e5ad2Saalok 	if (mi->mi_curr_serv != svp)
22588c9e5ad2Saalok 		goto remap_retry;
22598c9e5ad2Saalok 
22607c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
22617c478bd9Sstevel@tonic-gate }
22627c478bd9Sstevel@tonic-gate 
22637c478bd9Sstevel@tonic-gate static int
22647c478bd9Sstevel@tonic-gate nfs4rootvp(vnode_t **rtvpp, vfs_t *vfsp, struct servinfo4 *svp_head,
2265b9238976Sth     int flags, cred_t *cr, zone_t *zone)
22667c478bd9Sstevel@tonic-gate {
22677c478bd9Sstevel@tonic-gate 	vnode_t *rtvp = NULL;
22687c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
22697c478bd9Sstevel@tonic-gate 	dev_t nfs_dev;
22707c478bd9Sstevel@tonic-gate 	int error = 0;
22717c478bd9Sstevel@tonic-gate 	rnode4_t *rp;
22722f172c55SRobert Thurlow 	int i, len;
22737c478bd9Sstevel@tonic-gate 	struct vattr va;
22747c478bd9Sstevel@tonic-gate 	vtype_t vtype = VNON;
22757c478bd9Sstevel@tonic-gate 	vtype_t tmp_vtype = VNON;
22767c478bd9Sstevel@tonic-gate 	struct servinfo4 *firstsvp = NULL, *svp = svp_head;
22777c478bd9Sstevel@tonic-gate 	nfs4_oo_hash_bucket_t *bucketp;
22787c478bd9Sstevel@tonic-gate 	nfs_fh4 fh;
22797c478bd9Sstevel@tonic-gate 	char *droptext = "";
22807c478bd9Sstevel@tonic-gate 	struct nfs_stats *nfsstatsp;
22817c478bd9Sstevel@tonic-gate 	nfs4_fname_t *mfname;
22827c478bd9Sstevel@tonic-gate 	nfs4_error_t e;
22832f172c55SRobert Thurlow 	int num_retry, removed;
22847c478bd9Sstevel@tonic-gate 	cred_t *lcr = NULL, *tcr = cr;
22852f172c55SRobert Thurlow 	struct servinfo4 *origsvp;
22862f172c55SRobert Thurlow 	char *resource;
22877c478bd9Sstevel@tonic-gate 
2288108322fbScarlsonj 	nfsstatsp = zone_getspecific(nfsstat_zone_key, nfs_zone());
22897c478bd9Sstevel@tonic-gate 	ASSERT(nfsstatsp != NULL);
22907c478bd9Sstevel@tonic-gate 
2291108322fbScarlsonj 	ASSERT(nfs_zone() == zone);
22927c478bd9Sstevel@tonic-gate 	ASSERT(crgetref(cr));
22937c478bd9Sstevel@tonic-gate 
22947c478bd9Sstevel@tonic-gate 	/*
22957c478bd9Sstevel@tonic-gate 	 * Create a mount record and link it to the vfs struct.
22967c478bd9Sstevel@tonic-gate 	 */
22977c478bd9Sstevel@tonic-gate 	mi = kmem_zalloc(sizeof (*mi), KM_SLEEP);
22987c478bd9Sstevel@tonic-gate 	mutex_init(&mi->mi_lock, NULL, MUTEX_DEFAULT, NULL);
22997c478bd9Sstevel@tonic-gate 	nfs_rw_init(&mi->mi_recovlock, NULL, RW_DEFAULT, NULL);
23007c478bd9Sstevel@tonic-gate 	nfs_rw_init(&mi->mi_rename_lock, NULL, RW_DEFAULT, NULL);
23017c478bd9Sstevel@tonic-gate 	nfs_rw_init(&mi->mi_fh_lock, NULL, RW_DEFAULT, NULL);
23027c478bd9Sstevel@tonic-gate 
23037c478bd9Sstevel@tonic-gate 	if (!(flags & NFSMNT_SOFT))
23047c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_HARD;
23057c478bd9Sstevel@tonic-gate 	if ((flags & NFSMNT_NOPRINT))
23067c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_NOPRINT;
23077c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_INT)
23087c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_INT;
23097c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_PUBLIC)
23107c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_PUBLIC;
2311b9238976Sth 	if (flags & NFSMNT_MIRRORMOUNT)
2312b9238976Sth 		mi->mi_flags |= MI4_MIRRORMOUNT;
23132f172c55SRobert Thurlow 	if (flags & NFSMNT_REFERRAL)
23142f172c55SRobert Thurlow 		mi->mi_flags |= MI4_REFERRAL;
23157c478bd9Sstevel@tonic-gate 	mi->mi_retrans = NFS_RETRIES;
23167c478bd9Sstevel@tonic-gate 	if (svp->sv_knconf->knc_semantics == NC_TPI_COTS_ORD ||
23177c478bd9Sstevel@tonic-gate 	    svp->sv_knconf->knc_semantics == NC_TPI_COTS)
23187c478bd9Sstevel@tonic-gate 		mi->mi_timeo = nfs4_cots_timeo;
23197c478bd9Sstevel@tonic-gate 	else
23207c478bd9Sstevel@tonic-gate 		mi->mi_timeo = NFS_TIMEO;
23217c478bd9Sstevel@tonic-gate 	mi->mi_prog = NFS_PROGRAM;
23227c478bd9Sstevel@tonic-gate 	mi->mi_vers = NFS_V4;
23237c478bd9Sstevel@tonic-gate 	mi->mi_rfsnames = rfsnames_v4;
23247c478bd9Sstevel@tonic-gate 	mi->mi_reqs = nfsstatsp->nfs_stats_v4.rfsreqcnt_ptr;
23257c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_failover_cv, NULL, CV_DEFAULT, NULL);
23267c478bd9Sstevel@tonic-gate 	mi->mi_servers = svp;
23277c478bd9Sstevel@tonic-gate 	mi->mi_curr_serv = svp;
23287c478bd9Sstevel@tonic-gate 	mi->mi_acregmin = SEC2HR(ACREGMIN);
23297c478bd9Sstevel@tonic-gate 	mi->mi_acregmax = SEC2HR(ACREGMAX);
23307c478bd9Sstevel@tonic-gate 	mi->mi_acdirmin = SEC2HR(ACDIRMIN);
23317c478bd9Sstevel@tonic-gate 	mi->mi_acdirmax = SEC2HR(ACDIRMAX);
23327c478bd9Sstevel@tonic-gate 	mi->mi_fh_expire_type = FH4_PERSISTENT;
23337c478bd9Sstevel@tonic-gate 	mi->mi_clientid_next = NULL;
23347c478bd9Sstevel@tonic-gate 	mi->mi_clientid_prev = NULL;
23353b895386SPavel Filipensky 	mi->mi_srv = NULL;
23367c478bd9Sstevel@tonic-gate 	mi->mi_grace_wait = 0;
23377c478bd9Sstevel@tonic-gate 	mi->mi_error = 0;
23387c478bd9Sstevel@tonic-gate 	mi->mi_srvsettime = 0;
23393b895386SPavel Filipensky 	mi->mi_srvset_cnt = 0;
23407c478bd9Sstevel@tonic-gate 
234150a83466Sjwahlig 	mi->mi_count = 1;
234250a83466Sjwahlig 
23437c478bd9Sstevel@tonic-gate 	mi->mi_tsize = nfs4_tsize(svp->sv_knconf);
23447c478bd9Sstevel@tonic-gate 	mi->mi_stsize = mi->mi_tsize;
23457c478bd9Sstevel@tonic-gate 
23467c478bd9Sstevel@tonic-gate 	if (flags & NFSMNT_DIRECTIO)
23477c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_DIRECTIO;
23487c478bd9Sstevel@tonic-gate 
23497c478bd9Sstevel@tonic-gate 	mi->mi_flags |= MI4_MOUNTING;
23507c478bd9Sstevel@tonic-gate 
23517c478bd9Sstevel@tonic-gate 	/*
23527c478bd9Sstevel@tonic-gate 	 * Make a vfs struct for nfs.  We do this here instead of below
23537c478bd9Sstevel@tonic-gate 	 * because rtvp needs a vfs before we can do a getattr on it.
23547c478bd9Sstevel@tonic-gate 	 *
23557c478bd9Sstevel@tonic-gate 	 * Assign a unique device id to the mount
23567c478bd9Sstevel@tonic-gate 	 */
23577c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs_minor_lock);
23587c478bd9Sstevel@tonic-gate 	do {
23597c478bd9Sstevel@tonic-gate 		nfs_minor = (nfs_minor + 1) & MAXMIN32;
23607c478bd9Sstevel@tonic-gate 		nfs_dev = makedevice(nfs_major, nfs_minor);
23617c478bd9Sstevel@tonic-gate 	} while (vfs_devismounted(nfs_dev));
23627c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs_minor_lock);
23637c478bd9Sstevel@tonic-gate 
23647c478bd9Sstevel@tonic-gate 	vfsp->vfs_dev = nfs_dev;
23657c478bd9Sstevel@tonic-gate 	vfs_make_fsid(&vfsp->vfs_fsid, nfs_dev, nfs4fstyp);
23667c478bd9Sstevel@tonic-gate 	vfsp->vfs_data = (caddr_t)mi;
23677c478bd9Sstevel@tonic-gate 	vfsp->vfs_fstype = nfsfstyp;
23687c478bd9Sstevel@tonic-gate 	vfsp->vfs_bsize = nfs4_bsize;
23697c478bd9Sstevel@tonic-gate 
23707c478bd9Sstevel@tonic-gate 	/*
23717c478bd9Sstevel@tonic-gate 	 * Initialize fields used to support async putpage operations.
23727c478bd9Sstevel@tonic-gate 	 */
23737c478bd9Sstevel@tonic-gate 	for (i = 0; i < NFS4_ASYNC_TYPES; i++)
23747c478bd9Sstevel@tonic-gate 		mi->mi_async_clusters[i] = nfs4_async_clusters;
23757c478bd9Sstevel@tonic-gate 	mi->mi_async_init_clusters = nfs4_async_clusters;
23760776f5e6SVallish Vaidyeshwara 	mi->mi_async_curr[NFS4_ASYNC_QUEUE] =
23770776f5e6SVallish Vaidyeshwara 	    mi->mi_async_curr[NFS4_ASYNC_PGOPS_QUEUE] = &mi->mi_async_reqs[0];
23787c478bd9Sstevel@tonic-gate 	mi->mi_max_threads = nfs4_max_threads;
23797c478bd9Sstevel@tonic-gate 	mutex_init(&mi->mi_async_lock, NULL, MUTEX_DEFAULT, NULL);
23807c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_async_reqs_cv, NULL, CV_DEFAULT, NULL);
23810776f5e6SVallish Vaidyeshwara 	cv_init(&mi->mi_async_work_cv[NFS4_ASYNC_QUEUE], NULL, CV_DEFAULT,
23820776f5e6SVallish Vaidyeshwara 	    NULL);
23830776f5e6SVallish Vaidyeshwara 	cv_init(&mi->mi_async_work_cv[NFS4_ASYNC_PGOPS_QUEUE], NULL,
23840776f5e6SVallish Vaidyeshwara 	    CV_DEFAULT, NULL);
23857c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_async_cv, NULL, CV_DEFAULT, NULL);
23867c478bd9Sstevel@tonic-gate 	cv_init(&mi->mi_inact_req_cv, NULL, CV_DEFAULT, NULL);
23877c478bd9Sstevel@tonic-gate 
23887c478bd9Sstevel@tonic-gate 	mi->mi_vfsp = vfsp;
2389a19609f8Sjv 	mi->mi_zone = zone;
2390a19609f8Sjv 	zone_init_ref(&mi->mi_zone_ref);
2391a19609f8Sjv 	zone_hold_ref(zone, &mi->mi_zone_ref, ZONE_REF_NFSV4);
23927c478bd9Sstevel@tonic-gate 	nfs4_mi_zonelist_add(mi);
23937c478bd9Sstevel@tonic-gate 
23947c478bd9Sstevel@tonic-gate 	/*
23957c478bd9Sstevel@tonic-gate 	 * Initialize the <open owner/cred> hash table.
23967c478bd9Sstevel@tonic-gate 	 */
23977c478bd9Sstevel@tonic-gate 	for (i = 0; i < NFS4_NUM_OO_BUCKETS; i++) {
23987c478bd9Sstevel@tonic-gate 		bucketp = &(mi->mi_oo_list[i]);
23997c478bd9Sstevel@tonic-gate 		mutex_init(&bucketp->b_lock, NULL, MUTEX_DEFAULT, NULL);
24007c478bd9Sstevel@tonic-gate 		list_create(&bucketp->b_oo_hash_list,
24017c478bd9Sstevel@tonic-gate 		    sizeof (nfs4_open_owner_t),
24027c478bd9Sstevel@tonic-gate 		    offsetof(nfs4_open_owner_t, oo_hash_node));
24037c478bd9Sstevel@tonic-gate 	}
24047c478bd9Sstevel@tonic-gate 
24057c478bd9Sstevel@tonic-gate 	/*
24067c478bd9Sstevel@tonic-gate 	 * Initialize the freed open owner list.
24077c478bd9Sstevel@tonic-gate 	 */
24087c478bd9Sstevel@tonic-gate 	mi->mi_foo_num = 0;
24097c478bd9Sstevel@tonic-gate 	mi->mi_foo_max = NFS4_NUM_FREED_OPEN_OWNERS;
24107c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_foo_list, sizeof (nfs4_open_owner_t),
24117c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_open_owner_t, oo_foo_node));
24127c478bd9Sstevel@tonic-gate 
24137c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_lost_state, sizeof (nfs4_lost_rqst_t),
24147c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_lost_rqst_t, lr_node));
24157c478bd9Sstevel@tonic-gate 
24167c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_bseqid_list, sizeof (nfs4_bseqid_entry_t),
24177c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_bseqid_entry_t, bs_node));
24187c478bd9Sstevel@tonic-gate 
24197c478bd9Sstevel@tonic-gate 	/*
24207c478bd9Sstevel@tonic-gate 	 * Initialize the msg buffer.
24217c478bd9Sstevel@tonic-gate 	 */
24227c478bd9Sstevel@tonic-gate 	list_create(&mi->mi_msg_list, sizeof (nfs4_debug_msg_t),
24237c478bd9Sstevel@tonic-gate 	    offsetof(nfs4_debug_msg_t, msg_node));
24247c478bd9Sstevel@tonic-gate 	mi->mi_msg_count = 0;
24257c478bd9Sstevel@tonic-gate 	mutex_init(&mi->mi_msg_list_lock, NULL, MUTEX_DEFAULT, NULL);
24267c478bd9Sstevel@tonic-gate 
24277c478bd9Sstevel@tonic-gate 	/*
24287c478bd9Sstevel@tonic-gate 	 * Initialize kstats
24297c478bd9Sstevel@tonic-gate 	 */
24307c478bd9Sstevel@tonic-gate 	nfs4_mnt_kstat_init(vfsp);
24317c478bd9Sstevel@tonic-gate 
24327c478bd9Sstevel@tonic-gate 	/*
2433bbf2a467SNagakiran Rajashekar 	 * Initialize the shared filehandle pool.
24347c478bd9Sstevel@tonic-gate 	 */
24357c478bd9Sstevel@tonic-gate 	sfh4_createtab(&mi->mi_filehandles);
24367c478bd9Sstevel@tonic-gate 
24377c478bd9Sstevel@tonic-gate 	/*
24387c478bd9Sstevel@tonic-gate 	 * Save server path we're attempting to mount.
24397c478bd9Sstevel@tonic-gate 	 */
24407c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
24412f172c55SRobert Thurlow 	origsvp = copy_svp(svp);
24427c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
24437c478bd9Sstevel@tonic-gate 
24447c478bd9Sstevel@tonic-gate 	/*
24457c478bd9Sstevel@tonic-gate 	 * Make the GETFH call to get root fh for each replica.
24467c478bd9Sstevel@tonic-gate 	 */
24477c478bd9Sstevel@tonic-gate 	if (svp_head->sv_next)
24487c478bd9Sstevel@tonic-gate 		droptext = ", dropping replica";
24497c478bd9Sstevel@tonic-gate 
24507c478bd9Sstevel@tonic-gate 	/*
24517c478bd9Sstevel@tonic-gate 	 * If the uid is set then set the creds for secure mounts
24527c478bd9Sstevel@tonic-gate 	 * by proxy processes such as automountd.
24537c478bd9Sstevel@tonic-gate 	 */
24547c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
2455f722863dSSiyamaladevi Santhana Krishnan 	if (svp->sv_secdata->uid != 0 &&
2456f722863dSSiyamaladevi Santhana Krishnan 	    svp->sv_secdata->rpcflavor == RPCSEC_GSS) {
24577c478bd9Sstevel@tonic-gate 		lcr = crdup(cr);
24587c478bd9Sstevel@tonic-gate 		(void) crsetugid(lcr, svp->sv_secdata->uid, crgetgid(cr));
24597c478bd9Sstevel@tonic-gate 		tcr = lcr;
24607c478bd9Sstevel@tonic-gate 	}
24617c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
24627c478bd9Sstevel@tonic-gate 	for (svp = svp_head; svp; svp = svp->sv_next) {
24637c478bd9Sstevel@tonic-gate 		if (nfs4_chkdup_servinfo4(svp_head, svp)) {
24647c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN,
2465b9238976Sth 			    VERS_MSG "Host %s is a duplicate%s",
2466b9238976Sth 			    svp->sv_hostname, droptext);
24677c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
24687c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
24697c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
24707c478bd9Sstevel@tonic-gate 			continue;
24717c478bd9Sstevel@tonic-gate 		}
24727c478bd9Sstevel@tonic-gate 		mi->mi_curr_serv = svp;
24737c478bd9Sstevel@tonic-gate 
24747c478bd9Sstevel@tonic-gate 		/*
24757c478bd9Sstevel@tonic-gate 		 * Just in case server path being mounted contains
24767c478bd9Sstevel@tonic-gate 		 * symlinks and fails w/STALE, save the initial sv_path
24777c478bd9Sstevel@tonic-gate 		 * so we can redrive the initial mount compound with the
24787c478bd9Sstevel@tonic-gate 		 * initial sv_path -- not a symlink-expanded version.
24797c478bd9Sstevel@tonic-gate 		 *
24807c478bd9Sstevel@tonic-gate 		 * This could only happen if a symlink was expanded
24817c478bd9Sstevel@tonic-gate 		 * and the expanded mount compound failed stale.  Because
24827c478bd9Sstevel@tonic-gate 		 * it could be the case that the symlink was removed at
24837c478bd9Sstevel@tonic-gate 		 * the server (and replaced with another symlink/dir,
24847c478bd9Sstevel@tonic-gate 		 * we need to use the initial sv_path when attempting
24857c478bd9Sstevel@tonic-gate 		 * to re-lookup everything and recover.
24867c478bd9Sstevel@tonic-gate 		 *
24877c478bd9Sstevel@tonic-gate 		 * Other mount errors should evenutally be handled here also
24887c478bd9Sstevel@tonic-gate 		 * (NFS4ERR_DELAY, NFS4ERR_RESOURCE).  For now, all mount
24897c478bd9Sstevel@tonic-gate 		 * failures will result in mount being redriven a few times.
24907c478bd9Sstevel@tonic-gate 		 */
24917c478bd9Sstevel@tonic-gate 		num_retry = nfs4_max_mount_retry;
24927c478bd9Sstevel@tonic-gate 		do {
24937c478bd9Sstevel@tonic-gate 			nfs4getfh_otw(mi, svp, &tmp_vtype,
24947c478bd9Sstevel@tonic-gate 			    ((flags & NFSMNT_PUBLIC) ? NFS4_GETFH_PUBLIC : 0) |
24957c478bd9Sstevel@tonic-gate 			    NFS4_GETFH_NEEDSOP, tcr, &e);
24967c478bd9Sstevel@tonic-gate 
24977c478bd9Sstevel@tonic-gate 			if (e.error == 0 && e.stat == NFS4_OK)
24987c478bd9Sstevel@tonic-gate 				break;
24997c478bd9Sstevel@tonic-gate 
25007c478bd9Sstevel@tonic-gate 			/*
25012f172c55SRobert Thurlow 			 * For some reason, the mount compound failed.  Before
25022f172c55SRobert Thurlow 			 * retrying, we need to restore original conditions.
25037c478bd9Sstevel@tonic-gate 			 */
25042f172c55SRobert Thurlow 			svp = restore_svp(mi, svp, origsvp);
25052f172c55SRobert Thurlow 			svp_head = svp;
25067c478bd9Sstevel@tonic-gate 
25077c478bd9Sstevel@tonic-gate 		} while (num_retry-- > 0);
25087c478bd9Sstevel@tonic-gate 		error = e.error ? e.error : geterrno4(e.stat);
25097c478bd9Sstevel@tonic-gate 		if (error) {
25107c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN,
2511b9238976Sth 			    VERS_MSG "initial call to %s failed%s: %m",
2512b9238976Sth 			    svp->sv_hostname, droptext);
25137c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
25147c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
25157c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
25167c478bd9Sstevel@tonic-gate 			mi->mi_flags &= ~MI4_RECOV_FAIL;
25177c478bd9Sstevel@tonic-gate 			mi->mi_error = 0;
25187c478bd9Sstevel@tonic-gate 			continue;
25197c478bd9Sstevel@tonic-gate 		}
25207c478bd9Sstevel@tonic-gate 
25217c478bd9Sstevel@tonic-gate 		if (tmp_vtype == VBAD) {
25227c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_WARN,
2523b9238976Sth 			    VERS_MSG "%s returned a bad file type for "
2524b9238976Sth 			    "root%s", svp->sv_hostname, droptext);
25257c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
25267c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
25277c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
25287c478bd9Sstevel@tonic-gate 			continue;
25297c478bd9Sstevel@tonic-gate 		}
25307c478bd9Sstevel@tonic-gate 
25317c478bd9Sstevel@tonic-gate 		if (vtype == VNON) {
25327c478bd9Sstevel@tonic-gate 			vtype = tmp_vtype;
25337c478bd9Sstevel@tonic-gate 		} else if (vtype != tmp_vtype) {
25347c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_WARN,
2535b9238976Sth 			    VERS_MSG "%s returned a different file type "
2536b9238976Sth 			    "for root%s", svp->sv_hostname, droptext);
25377c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
25387c478bd9Sstevel@tonic-gate 			svp->sv_flags |= SV4_NOTINUSE;
25397c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
25407c478bd9Sstevel@tonic-gate 			continue;
25417c478bd9Sstevel@tonic-gate 		}
25427c478bd9Sstevel@tonic-gate 		if (firstsvp == NULL)
25437c478bd9Sstevel@tonic-gate 			firstsvp = svp;
25447c478bd9Sstevel@tonic-gate 	}
25457c478bd9Sstevel@tonic-gate 
25467c478bd9Sstevel@tonic-gate 	if (firstsvp == NULL) {
25477c478bd9Sstevel@tonic-gate 		if (error == 0)
25487c478bd9Sstevel@tonic-gate 			error = ENOENT;
25497c478bd9Sstevel@tonic-gate 		goto bad;
25507c478bd9Sstevel@tonic-gate 	}
25517c478bd9Sstevel@tonic-gate 
25527c478bd9Sstevel@tonic-gate 	mi->mi_curr_serv = svp = firstsvp;
25537c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
25547c478bd9Sstevel@tonic-gate 	ASSERT((mi->mi_curr_serv->sv_flags & SV4_NOTINUSE) == 0);
25557c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_len = svp->sv_fhandle.fh_len;
25567c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_val = svp->sv_fhandle.fh_buf;
25577c478bd9Sstevel@tonic-gate 	mi->mi_rootfh = sfh4_get(&fh, mi);
25587c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_len = svp->sv_pfhandle.fh_len;
25597c478bd9Sstevel@tonic-gate 	fh.nfs_fh4_val = svp->sv_pfhandle.fh_buf;
25607c478bd9Sstevel@tonic-gate 	mi->mi_srvparentfh = sfh4_get(&fh, mi);
25617c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
25627c478bd9Sstevel@tonic-gate 
25637c478bd9Sstevel@tonic-gate 	/*
2564bbf2a467SNagakiran Rajashekar 	 * Get the fname for filesystem root.
25657c478bd9Sstevel@tonic-gate 	 */
2566bbf2a467SNagakiran Rajashekar 	mi->mi_fname = fn_get(NULL, ".", mi->mi_rootfh);
25677c478bd9Sstevel@tonic-gate 	mfname = mi->mi_fname;
25687c478bd9Sstevel@tonic-gate 	fn_hold(mfname);
2569bbf2a467SNagakiran Rajashekar 
2570bbf2a467SNagakiran Rajashekar 	/*
2571bbf2a467SNagakiran Rajashekar 	 * Make the root vnode without attributes.
2572bbf2a467SNagakiran Rajashekar 	 */
25737c478bd9Sstevel@tonic-gate 	rtvp = makenfs4node_by_fh(mi->mi_rootfh, NULL,
25747c478bd9Sstevel@tonic-gate 	    &mfname, NULL, mi, cr, gethrtime());
25757c478bd9Sstevel@tonic-gate 	rtvp->v_type = vtype;
25767c478bd9Sstevel@tonic-gate 
25777c478bd9Sstevel@tonic-gate 	mi->mi_curread = mi->mi_tsize;
25787c478bd9Sstevel@tonic-gate 	mi->mi_curwrite = mi->mi_stsize;
25797c478bd9Sstevel@tonic-gate 
25807c478bd9Sstevel@tonic-gate 	/*
25817c478bd9Sstevel@tonic-gate 	 * Start the manager thread responsible for handling async worker
25827c478bd9Sstevel@tonic-gate 	 * threads.
25837c478bd9Sstevel@tonic-gate 	 */
258450a83466Sjwahlig 	MI4_HOLD(mi);
25857c478bd9Sstevel@tonic-gate 	VFS_HOLD(vfsp);	/* add reference for thread */
25867c478bd9Sstevel@tonic-gate 	mi->mi_manager_thread = zthread_create(NULL, 0, nfs4_async_manager,
2587b9238976Sth 	    vfsp, 0, minclsyspri);
25887c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_manager_thread != NULL);
258950a83466Sjwahlig 
25907c478bd9Sstevel@tonic-gate 	/*
25917c478bd9Sstevel@tonic-gate 	 * Create the thread that handles over-the-wire calls for
25927c478bd9Sstevel@tonic-gate 	 * VOP_INACTIVE.
25937c478bd9Sstevel@tonic-gate 	 * This needs to happen after the manager thread is created.
25947c478bd9Sstevel@tonic-gate 	 */
259550a83466Sjwahlig 	MI4_HOLD(mi);
25967c478bd9Sstevel@tonic-gate 	mi->mi_inactive_thread = zthread_create(NULL, 0, nfs4_inactive_thread,
2597b9238976Sth 	    mi, 0, minclsyspri);
25987c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_inactive_thread != NULL);
25997c478bd9Sstevel@tonic-gate 
26007c478bd9Sstevel@tonic-gate 	/* If we didn't get a type, get one now */
26017c478bd9Sstevel@tonic-gate 	if (rtvp->v_type == VNON) {
26027c478bd9Sstevel@tonic-gate 		va.va_mask = AT_TYPE;
26037c478bd9Sstevel@tonic-gate 		error = nfs4getattr(rtvp, &va, tcr);
26047c478bd9Sstevel@tonic-gate 		if (error)
26057c478bd9Sstevel@tonic-gate 			goto bad;
26067c478bd9Sstevel@tonic-gate 		rtvp->v_type = va.va_type;
26077c478bd9Sstevel@tonic-gate 	}
26087c478bd9Sstevel@tonic-gate 
26097c478bd9Sstevel@tonic-gate 	mi->mi_type = rtvp->v_type;
26107c478bd9Sstevel@tonic-gate 
26117c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
26127c478bd9Sstevel@tonic-gate 	mi->mi_flags &= ~MI4_MOUNTING;
26137c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
26147c478bd9Sstevel@tonic-gate 
26152f172c55SRobert Thurlow 	/* Update VFS with new server and path info */
26162f172c55SRobert Thurlow 	if ((strcmp(svp->sv_hostname, origsvp->sv_hostname) != 0) ||
26172f172c55SRobert Thurlow 	    (strcmp(svp->sv_path, origsvp->sv_path) != 0)) {
26182f172c55SRobert Thurlow 		len = svp->sv_hostnamelen + svp->sv_pathlen;
26192f172c55SRobert Thurlow 		resource = kmem_zalloc(len, KM_SLEEP);
26202f172c55SRobert Thurlow 		(void) strcat(resource, svp->sv_hostname);
26212f172c55SRobert Thurlow 		(void) strcat(resource, ":");
26222f172c55SRobert Thurlow 		(void) strcat(resource, svp->sv_path);
2623d7de0ceaSRobert Harris 		vfs_setresource(vfsp, resource, 0);
26242f172c55SRobert Thurlow 		kmem_free(resource, len);
26252f172c55SRobert Thurlow 	}
26262f172c55SRobert Thurlow 
26272f172c55SRobert Thurlow 	sv4_free(origsvp);
26287c478bd9Sstevel@tonic-gate 	*rtvpp = rtvp;
26297c478bd9Sstevel@tonic-gate 	if (lcr != NULL)
26307c478bd9Sstevel@tonic-gate 		crfree(lcr);
26317c478bd9Sstevel@tonic-gate 
26327c478bd9Sstevel@tonic-gate 	return (0);
26337c478bd9Sstevel@tonic-gate bad:
26347c478bd9Sstevel@tonic-gate 	/*
26357c478bd9Sstevel@tonic-gate 	 * An error occurred somewhere, need to clean up...
26367c478bd9Sstevel@tonic-gate 	 */
26377c478bd9Sstevel@tonic-gate 	if (lcr != NULL)
26387c478bd9Sstevel@tonic-gate 		crfree(lcr);
2639b9238976Sth 
26407c478bd9Sstevel@tonic-gate 	if (rtvp != NULL) {
26417c478bd9Sstevel@tonic-gate 		/*
26427c478bd9Sstevel@tonic-gate 		 * We need to release our reference to the root vnode and
26437c478bd9Sstevel@tonic-gate 		 * destroy the mntinfo4 struct that we just created.
26447c478bd9Sstevel@tonic-gate 		 */
26457c478bd9Sstevel@tonic-gate 		rp = VTOR4(rtvp);
26467c478bd9Sstevel@tonic-gate 		if (rp->r_flags & R4HASHED)
26477c478bd9Sstevel@tonic-gate 			rp4_rmhash(rp);
26487c478bd9Sstevel@tonic-gate 		VN_RELE(rtvp);
26497c478bd9Sstevel@tonic-gate 	}
26507c478bd9Sstevel@tonic-gate 	nfs4_async_stop(vfsp);
26517c478bd9Sstevel@tonic-gate 	nfs4_async_manager_stop(vfsp);
265250a83466Sjwahlig 	removed = nfs4_mi_zonelist_remove(mi);
265350a83466Sjwahlig 	if (removed)
2654a19609f8Sjv 		zone_rele_ref(&mi->mi_zone_ref, ZONE_REF_NFSV4);
265550a83466Sjwahlig 
265650a83466Sjwahlig 	/*
265750a83466Sjwahlig 	 * This releases the initial "hold" of the mi since it will never
265850a83466Sjwahlig 	 * be referenced by the vfsp.  Also, when mount returns to vfs.c
265950a83466Sjwahlig 	 * with an error, the vfsp will be destroyed, not rele'd.
266050a83466Sjwahlig 	 */
266150a83466Sjwahlig 	MI4_RELE(mi);
266250a83466Sjwahlig 
26632f172c55SRobert Thurlow 	if (origsvp != NULL)
26642f172c55SRobert Thurlow 		sv4_free(origsvp);
26652f172c55SRobert Thurlow 
26667c478bd9Sstevel@tonic-gate 	*rtvpp = NULL;
26677c478bd9Sstevel@tonic-gate 	return (error);
26687c478bd9Sstevel@tonic-gate }
26697c478bd9Sstevel@tonic-gate 
26707c478bd9Sstevel@tonic-gate /*
26717c478bd9Sstevel@tonic-gate  * vfs operations
26727c478bd9Sstevel@tonic-gate  */
26737c478bd9Sstevel@tonic-gate static int
26747c478bd9Sstevel@tonic-gate nfs4_unmount(vfs_t *vfsp, int flag, cred_t *cr)
26757c478bd9Sstevel@tonic-gate {
2676b9238976Sth 	mntinfo4_t		*mi;
2677b9238976Sth 	ushort_t		omax;
2678b9238976Sth 	int			removed;
2679b9238976Sth 
2680d3a14591SThomas Haynes 	bool_t			must_unlock;
2681b9238976Sth 
2682b9238976Sth 	nfs4_ephemeral_tree_t	*eph_tree;
26837c478bd9Sstevel@tonic-gate 
26847c478bd9Sstevel@tonic-gate 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
26857c478bd9Sstevel@tonic-gate 		return (EPERM);
26867c478bd9Sstevel@tonic-gate 
26877c478bd9Sstevel@tonic-gate 	mi = VFTOMI4(vfsp);
26887c478bd9Sstevel@tonic-gate 
26897c478bd9Sstevel@tonic-gate 	if (flag & MS_FORCE) {
26907c478bd9Sstevel@tonic-gate 		vfsp->vfs_flag |= VFS_UNMOUNTED;
2691108322fbScarlsonj 		if (nfs_zone() != mi->mi_zone) {
26927c478bd9Sstevel@tonic-gate 			/*
26937c478bd9Sstevel@tonic-gate 			 * If the request is coming from the wrong zone,
26947c478bd9Sstevel@tonic-gate 			 * we don't want to create any new threads, and
26957c478bd9Sstevel@tonic-gate 			 * performance is not a concern.  Do everything
26967c478bd9Sstevel@tonic-gate 			 * inline.
26977c478bd9Sstevel@tonic-gate 			 */
26987c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_client_zone_debug, (CE_NOTE,
26997c478bd9Sstevel@tonic-gate 			    "nfs4_unmount x-zone forced unmount of vfs %p\n",
27007c478bd9Sstevel@tonic-gate 			    (void *)vfsp));
2701b9238976Sth 			nfs4_free_mount(vfsp, flag, cr);
27027c478bd9Sstevel@tonic-gate 		} else {
27037c478bd9Sstevel@tonic-gate 			/*
27047c478bd9Sstevel@tonic-gate 			 * Free data structures asynchronously, to avoid
27057c478bd9Sstevel@tonic-gate 			 * blocking the current thread (for performance
27067c478bd9Sstevel@tonic-gate 			 * reasons only).
27077c478bd9Sstevel@tonic-gate 			 */
2708b9238976Sth 			async_free_mount(vfsp, flag, cr);
27097c478bd9Sstevel@tonic-gate 		}
2710b9238976Sth 
27117c478bd9Sstevel@tonic-gate 		return (0);
27127c478bd9Sstevel@tonic-gate 	}
2713b9238976Sth 
27147c478bd9Sstevel@tonic-gate 	/*
27157c478bd9Sstevel@tonic-gate 	 * Wait until all asynchronous putpage operations on
27167c478bd9Sstevel@tonic-gate 	 * this file system are complete before flushing rnodes
27177c478bd9Sstevel@tonic-gate 	 * from the cache.
27187c478bd9Sstevel@tonic-gate 	 */
27197c478bd9Sstevel@tonic-gate 	omax = mi->mi_max_threads;
2720b9238976Sth 	if (nfs4_async_stop_sig(vfsp))
27217c478bd9Sstevel@tonic-gate 		return (EINTR);
2722b9238976Sth 
27237c478bd9Sstevel@tonic-gate 	r4flush(vfsp, cr);
2724b9238976Sth 
2725eabd0450Sth 	/*
2726eabd0450Sth 	 * About the only reason that this would fail would be
2727eabd0450Sth 	 * that the harvester is already busy tearing down this
2728eabd0450Sth 	 * node. So we fail back to the caller and let them try
2729eabd0450Sth 	 * again when needed.
2730eabd0450Sth 	 */
2731eabd0450Sth 	if (nfs4_ephemeral_umount(mi, flag, cr,
27322f172c55SRobert Thurlow 	    &must_unlock, &eph_tree)) {
2733d3a14591SThomas Haynes 		ASSERT(must_unlock == FALSE);
2734eabd0450Sth 		mutex_enter(&mi->mi_async_lock);
2735eabd0450Sth 		mi->mi_max_threads = omax;
2736eabd0450Sth 		mutex_exit(&mi->mi_async_lock);
2737eabd0450Sth 
2738eabd0450Sth 		return (EBUSY);
2739eabd0450Sth 	}
2740b9238976Sth 
27417c478bd9Sstevel@tonic-gate 	/*
27427c478bd9Sstevel@tonic-gate 	 * If there are any active vnodes on this file system,
2743b9238976Sth 	 * then the file system is busy and can't be unmounted.
27447c478bd9Sstevel@tonic-gate 	 */
27457c478bd9Sstevel@tonic-gate 	if (check_rtable4(vfsp)) {
27462f172c55SRobert Thurlow 		nfs4_ephemeral_umount_unlock(&must_unlock, &eph_tree);
2747b9238976Sth 
27487c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_async_lock);
27497c478bd9Sstevel@tonic-gate 		mi->mi_max_threads = omax;
27507c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_async_lock);
2751b9238976Sth 
27527c478bd9Sstevel@tonic-gate 		return (EBUSY);
27537c478bd9Sstevel@tonic-gate 	}
2754b9238976Sth 
2755b9238976Sth 	/*
2756b9238976Sth 	 * The unmount can't fail from now on, so record any
2757b9238976Sth 	 * ephemeral changes.
2758b9238976Sth 	 */
27592f172c55SRobert Thurlow 	nfs4_ephemeral_umount_activate(mi, &must_unlock, &eph_tree);
2760b9238976Sth 
27617c478bd9Sstevel@tonic-gate 	/*
2762b9238976Sth 	 * There are no active files that could require over-the-wire
2763b9238976Sth 	 * calls to the server, so stop the async manager and the
2764b9238976Sth 	 * inactive thread.
27657c478bd9Sstevel@tonic-gate 	 */
27667c478bd9Sstevel@tonic-gate 	nfs4_async_manager_stop(vfsp);
2767b9238976Sth 
27687c478bd9Sstevel@tonic-gate 	/*
27697c478bd9Sstevel@tonic-gate 	 * Destroy all rnodes belonging to this file system from the
27707c478bd9Sstevel@tonic-gate 	 * rnode hash queues and purge any resources allocated to
27717c478bd9Sstevel@tonic-gate 	 * them.
27727c478bd9Sstevel@tonic-gate 	 */
27737c478bd9Sstevel@tonic-gate 	destroy_rtable4(vfsp, cr);
27747c478bd9Sstevel@tonic-gate 	vfsp->vfs_flag |= VFS_UNMOUNTED;
277550a83466Sjwahlig 
27767c478bd9Sstevel@tonic-gate 	nfs4_remove_mi_from_server(mi, NULL);
277750a83466Sjwahlig 	removed = nfs4_mi_zonelist_remove(mi);
277850a83466Sjwahlig 	if (removed)
2779a19609f8Sjv 		zone_rele_ref(&mi->mi_zone_ref, ZONE_REF_NFSV4);
278050a83466Sjwahlig 
27817c478bd9Sstevel@tonic-gate 	return (0);
27827c478bd9Sstevel@tonic-gate }
27837c478bd9Sstevel@tonic-gate 
27847c478bd9Sstevel@tonic-gate /*
27857c478bd9Sstevel@tonic-gate  * find root of nfs
27867c478bd9Sstevel@tonic-gate  */
27877c478bd9Sstevel@tonic-gate static int
27887c478bd9Sstevel@tonic-gate nfs4_root(vfs_t *vfsp, vnode_t **vpp)
27897c478bd9Sstevel@tonic-gate {
27907c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
27917c478bd9Sstevel@tonic-gate 	vnode_t *vp;
27927c478bd9Sstevel@tonic-gate 	nfs4_fname_t *mfname;
27937c478bd9Sstevel@tonic-gate 	servinfo4_t *svp;
27947c478bd9Sstevel@tonic-gate 
27957c478bd9Sstevel@tonic-gate 	mi = VFTOMI4(vfsp);
27967c478bd9Sstevel@tonic-gate 
2797108322fbScarlsonj 	if (nfs_zone() != mi->mi_zone)
27987c478bd9Sstevel@tonic-gate 		return (EPERM);
27997c478bd9Sstevel@tonic-gate 
28007c478bd9Sstevel@tonic-gate 	svp = mi->mi_curr_serv;
28017c478bd9Sstevel@tonic-gate 	if (svp) {
28027c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
28037c478bd9Sstevel@tonic-gate 		if (svp->sv_flags & SV4_ROOT_STALE) {
28047c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
28057c478bd9Sstevel@tonic-gate 
28067c478bd9Sstevel@tonic-gate 			(void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0);
28077c478bd9Sstevel@tonic-gate 			if (svp->sv_flags & SV4_ROOT_STALE) {
28087c478bd9Sstevel@tonic-gate 				svp->sv_flags &= ~SV4_ROOT_STALE;
28097c478bd9Sstevel@tonic-gate 				nfs_rw_exit(&svp->sv_lock);
28107c478bd9Sstevel@tonic-gate 				return (ENOENT);
28117c478bd9Sstevel@tonic-gate 			}
28127c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
28137c478bd9Sstevel@tonic-gate 		} else
28147c478bd9Sstevel@tonic-gate 			nfs_rw_exit(&svp->sv_lock);
28157c478bd9Sstevel@tonic-gate 	}
28167c478bd9Sstevel@tonic-gate 
28177c478bd9Sstevel@tonic-gate 	mfname = mi->mi_fname;
28187c478bd9Sstevel@tonic-gate 	fn_hold(mfname);
28197c478bd9Sstevel@tonic-gate 	vp = makenfs4node_by_fh(mi->mi_rootfh, NULL, &mfname, NULL,
28207c478bd9Sstevel@tonic-gate 	    VFTOMI4(vfsp), CRED(), gethrtime());
28217c478bd9Sstevel@tonic-gate 
28227c478bd9Sstevel@tonic-gate 	if (VTOR4(vp)->r_flags & R4STALE) {
28237c478bd9Sstevel@tonic-gate 		VN_RELE(vp);
28247c478bd9Sstevel@tonic-gate 		return (ENOENT);
28257c478bd9Sstevel@tonic-gate 	}
28267c478bd9Sstevel@tonic-gate 
28277c478bd9Sstevel@tonic-gate 	ASSERT(vp->v_type == VNON || vp->v_type == mi->mi_type);
28287c478bd9Sstevel@tonic-gate 
28297c478bd9Sstevel@tonic-gate 	vp->v_type = mi->mi_type;
28307c478bd9Sstevel@tonic-gate 
28317c478bd9Sstevel@tonic-gate 	*vpp = vp;
28327c478bd9Sstevel@tonic-gate 
28337c478bd9Sstevel@tonic-gate 	return (0);
28347c478bd9Sstevel@tonic-gate }
28357c478bd9Sstevel@tonic-gate 
28367c478bd9Sstevel@tonic-gate static int
28377c478bd9Sstevel@tonic-gate nfs4_statfs_otw(vnode_t *vp, struct statvfs64 *sbp, cred_t *cr)
28387c478bd9Sstevel@tonic-gate {
28397c478bd9Sstevel@tonic-gate 	int error;
28407c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t gar;
28417c478bd9Sstevel@tonic-gate 	nfs4_ga_ext_res_t ger;
28427c478bd9Sstevel@tonic-gate 
28437c478bd9Sstevel@tonic-gate 	gar.n4g_ext_res = &ger;
28447c478bd9Sstevel@tonic-gate 
28457c478bd9Sstevel@tonic-gate 	if (error = nfs4_attr_otw(vp, TAG_FSINFO, &gar,
28467c478bd9Sstevel@tonic-gate 	    NFS4_STATFS_ATTR_MASK, cr))
28477c478bd9Sstevel@tonic-gate 		return (error);
28487c478bd9Sstevel@tonic-gate 
28497c478bd9Sstevel@tonic-gate 	*sbp = gar.n4g_ext_res->n4g_sb;
28507c478bd9Sstevel@tonic-gate 
28517c478bd9Sstevel@tonic-gate 	return (0);
28527c478bd9Sstevel@tonic-gate }
28537c478bd9Sstevel@tonic-gate 
28547c478bd9Sstevel@tonic-gate /*
28557c478bd9Sstevel@tonic-gate  * Get file system statistics.
28567c478bd9Sstevel@tonic-gate  */
28577c478bd9Sstevel@tonic-gate static int
28587c478bd9Sstevel@tonic-gate nfs4_statvfs(vfs_t *vfsp, struct statvfs64 *sbp)
28597c478bd9Sstevel@tonic-gate {
28607c478bd9Sstevel@tonic-gate 	int error;
28617c478bd9Sstevel@tonic-gate 	vnode_t *vp;
28627c478bd9Sstevel@tonic-gate 	cred_t *cr;
28637c478bd9Sstevel@tonic-gate 
28647c478bd9Sstevel@tonic-gate 	error = nfs4_root(vfsp, &vp);
28657c478bd9Sstevel@tonic-gate 	if (error)
28667c478bd9Sstevel@tonic-gate 		return (error);
28677c478bd9Sstevel@tonic-gate 
28687c478bd9Sstevel@tonic-gate 	cr = CRED();
28697c478bd9Sstevel@tonic-gate 
28707c478bd9Sstevel@tonic-gate 	error = nfs4_statfs_otw(vp, sbp, cr);
28717c478bd9Sstevel@tonic-gate 	if (!error) {
28727c478bd9Sstevel@tonic-gate 		(void) strncpy(sbp->f_basetype,
2873b9238976Sth 		    vfssw[vfsp->vfs_fstype].vsw_name, FSTYPSZ);
28747c478bd9Sstevel@tonic-gate 		sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
28757c478bd9Sstevel@tonic-gate 	} else {
28767c478bd9Sstevel@tonic-gate 		nfs4_purge_stale_fh(error, vp, cr);
28777c478bd9Sstevel@tonic-gate 	}
28787c478bd9Sstevel@tonic-gate 
28797c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
28807c478bd9Sstevel@tonic-gate 
28817c478bd9Sstevel@tonic-gate 	return (error);
28827c478bd9Sstevel@tonic-gate }
28837c478bd9Sstevel@tonic-gate 
28847c478bd9Sstevel@tonic-gate static kmutex_t nfs4_syncbusy;
28857c478bd9Sstevel@tonic-gate 
28867c478bd9Sstevel@tonic-gate /*
28877c478bd9Sstevel@tonic-gate  * Flush dirty nfs files for file system vfsp.
28887c478bd9Sstevel@tonic-gate  * If vfsp == NULL, all nfs files are flushed.
28897c478bd9Sstevel@tonic-gate  *
28907c478bd9Sstevel@tonic-gate  * SYNC_CLOSE in flag is passed to us to
28917c478bd9Sstevel@tonic-gate  * indicate that we are shutting down and or
28927c478bd9Sstevel@tonic-gate  * rebooting.
28937c478bd9Sstevel@tonic-gate  */
28947c478bd9Sstevel@tonic-gate static int
28957c478bd9Sstevel@tonic-gate nfs4_sync(vfs_t *vfsp, short flag, cred_t *cr)
28967c478bd9Sstevel@tonic-gate {
28977c478bd9Sstevel@tonic-gate 	/*
28987c478bd9Sstevel@tonic-gate 	 * Cross-zone calls are OK here, since this translates to a
28997c478bd9Sstevel@tonic-gate 	 * VOP_PUTPAGE(B_ASYNC), which gets picked up by the right zone.
29007c478bd9Sstevel@tonic-gate 	 */
29017c478bd9Sstevel@tonic-gate 	if (!(flag & SYNC_ATTR) && mutex_tryenter(&nfs4_syncbusy) != 0) {
29027c478bd9Sstevel@tonic-gate 		r4flush(vfsp, cr);
29037c478bd9Sstevel@tonic-gate 		mutex_exit(&nfs4_syncbusy);
29047c478bd9Sstevel@tonic-gate 	}
29057c478bd9Sstevel@tonic-gate 
29067c478bd9Sstevel@tonic-gate 	/*
29077c478bd9Sstevel@tonic-gate 	 * if SYNC_CLOSE is set then we know that
29087c478bd9Sstevel@tonic-gate 	 * the system is rebooting, mark the mntinfo
29097c478bd9Sstevel@tonic-gate 	 * for later examination.
29107c478bd9Sstevel@tonic-gate 	 */
29117c478bd9Sstevel@tonic-gate 	if (vfsp && (flag & SYNC_CLOSE)) {
29127c478bd9Sstevel@tonic-gate 		mntinfo4_t *mi;
29137c478bd9Sstevel@tonic-gate 
29147c478bd9Sstevel@tonic-gate 		mi = VFTOMI4(vfsp);
29157c478bd9Sstevel@tonic-gate 		if (!(mi->mi_flags & MI4_SHUTDOWN)) {
29167c478bd9Sstevel@tonic-gate 			mutex_enter(&mi->mi_lock);
29177c478bd9Sstevel@tonic-gate 			mi->mi_flags |= MI4_SHUTDOWN;
29187c478bd9Sstevel@tonic-gate 			mutex_exit(&mi->mi_lock);
29197c478bd9Sstevel@tonic-gate 		}
29207c478bd9Sstevel@tonic-gate 	}
29217c478bd9Sstevel@tonic-gate 	return (0);
29227c478bd9Sstevel@tonic-gate }
29237c478bd9Sstevel@tonic-gate 
29247c478bd9Sstevel@tonic-gate /*
29257c478bd9Sstevel@tonic-gate  * vget is difficult, if not impossible, to support in v4 because we don't
29267c478bd9Sstevel@tonic-gate  * know the parent directory or name, which makes it impossible to create a
29277c478bd9Sstevel@tonic-gate  * useful shadow vnode.  And we need the shadow vnode for things like
29287c478bd9Sstevel@tonic-gate  * OPEN.
29297c478bd9Sstevel@tonic-gate  */
29307c478bd9Sstevel@tonic-gate 
29317c478bd9Sstevel@tonic-gate /* ARGSUSED */
29327c478bd9Sstevel@tonic-gate /*
29337c478bd9Sstevel@tonic-gate  * XXX Check nfs4_vget_pseudo() for dependency.
29347c478bd9Sstevel@tonic-gate  */
29357c478bd9Sstevel@tonic-gate static int
29367c478bd9Sstevel@tonic-gate nfs4_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
29377c478bd9Sstevel@tonic-gate {
29387c478bd9Sstevel@tonic-gate 	return (EREMOTE);
29397c478bd9Sstevel@tonic-gate }
29407c478bd9Sstevel@tonic-gate 
29417c478bd9Sstevel@tonic-gate /*
29427c478bd9Sstevel@tonic-gate  * nfs4_mountroot get called in the case where we are diskless booting.  All
29437c478bd9Sstevel@tonic-gate  * we need from here is the ability to get the server info and from there we
29447c478bd9Sstevel@tonic-gate  * can simply call nfs4_rootvp.
29457c478bd9Sstevel@tonic-gate  */
29467c478bd9Sstevel@tonic-gate /* ARGSUSED */
29477c478bd9Sstevel@tonic-gate static int
29487c478bd9Sstevel@tonic-gate nfs4_mountroot(vfs_t *vfsp, whymountroot_t why)
29497c478bd9Sstevel@tonic-gate {
29507c478bd9Sstevel@tonic-gate 	vnode_t *rtvp;
29517c478bd9Sstevel@tonic-gate 	char root_hostname[SYS_NMLN+1];
29527c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp;
29537c478bd9Sstevel@tonic-gate 	int error;
29547c478bd9Sstevel@tonic-gate 	int vfsflags;
29557c478bd9Sstevel@tonic-gate 	size_t size;
29567c478bd9Sstevel@tonic-gate 	char *root_path;
29577c478bd9Sstevel@tonic-gate 	struct pathname pn;
29587c478bd9Sstevel@tonic-gate 	char *name;
29597c478bd9Sstevel@tonic-gate 	cred_t *cr;
29607c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
29617c478bd9Sstevel@tonic-gate 	struct nfs_args args;		/* nfs mount arguments */
29627c478bd9Sstevel@tonic-gate 	static char token[10];
29637c478bd9Sstevel@tonic-gate 	nfs4_error_t n4e;
29647c478bd9Sstevel@tonic-gate 
29657c478bd9Sstevel@tonic-gate 	bzero(&args, sizeof (args));
29667c478bd9Sstevel@tonic-gate 
29677c478bd9Sstevel@tonic-gate 	/* do this BEFORE getfile which causes xid stamps to be initialized */
29687c478bd9Sstevel@tonic-gate 	clkset(-1L);		/* hack for now - until we get time svc? */
29697c478bd9Sstevel@tonic-gate 
29707c478bd9Sstevel@tonic-gate 	if (why == ROOT_REMOUNT) {
29717c478bd9Sstevel@tonic-gate 		/*
29727c478bd9Sstevel@tonic-gate 		 * Shouldn't happen.
29737c478bd9Sstevel@tonic-gate 		 */
29747c478bd9Sstevel@tonic-gate 		panic("nfs4_mountroot: why == ROOT_REMOUNT");
29757c478bd9Sstevel@tonic-gate 	}
29767c478bd9Sstevel@tonic-gate 
29777c478bd9Sstevel@tonic-gate 	if (why == ROOT_UNMOUNT) {
29787c478bd9Sstevel@tonic-gate 		/*
29797c478bd9Sstevel@tonic-gate 		 * Nothing to do for NFS.
29807c478bd9Sstevel@tonic-gate 		 */
29817c478bd9Sstevel@tonic-gate 		return (0);
29827c478bd9Sstevel@tonic-gate 	}
29837c478bd9Sstevel@tonic-gate 
29847c478bd9Sstevel@tonic-gate 	/*
29857c478bd9Sstevel@tonic-gate 	 * why == ROOT_INIT
29867c478bd9Sstevel@tonic-gate 	 */
29877c478bd9Sstevel@tonic-gate 
29887c478bd9Sstevel@tonic-gate 	name = token;
29897c478bd9Sstevel@tonic-gate 	*name = 0;
29907c478bd9Sstevel@tonic-gate 	(void) getfsname("root", name, sizeof (token));
29917c478bd9Sstevel@tonic-gate 
29927c478bd9Sstevel@tonic-gate 	pn_alloc(&pn);
29937c478bd9Sstevel@tonic-gate 	root_path = pn.pn_path;
29947c478bd9Sstevel@tonic-gate 
29957c478bd9Sstevel@tonic-gate 	svp = kmem_zalloc(sizeof (*svp), KM_SLEEP);
29967c478bd9Sstevel@tonic-gate 	nfs_rw_init(&svp->sv_lock, NULL, RW_DEFAULT, NULL);
29977c478bd9Sstevel@tonic-gate 	svp->sv_knconf = kmem_zalloc(sizeof (*svp->sv_knconf), KM_SLEEP);
29987c478bd9Sstevel@tonic-gate 	svp->sv_knconf->knc_protofmly = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
29997c478bd9Sstevel@tonic-gate 	svp->sv_knconf->knc_proto = kmem_alloc(KNC_STRSIZE, KM_SLEEP);
30007c478bd9Sstevel@tonic-gate 
30017c478bd9Sstevel@tonic-gate 	/*
30027c478bd9Sstevel@tonic-gate 	 * Get server address
30037c478bd9Sstevel@tonic-gate 	 * Get the root path
30047c478bd9Sstevel@tonic-gate 	 * Get server's transport
30057c478bd9Sstevel@tonic-gate 	 * Get server's hostname
30067c478bd9Sstevel@tonic-gate 	 * Get options
30077c478bd9Sstevel@tonic-gate 	 */
30087c478bd9Sstevel@tonic-gate 	args.addr = &svp->sv_addr;
30097c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
30107c478bd9Sstevel@tonic-gate 	args.fh = (char *)&svp->sv_fhandle;
30117c478bd9Sstevel@tonic-gate 	args.knconf = svp->sv_knconf;
30127c478bd9Sstevel@tonic-gate 	args.hostname = root_hostname;
30137c478bd9Sstevel@tonic-gate 	vfsflags = 0;
30147c478bd9Sstevel@tonic-gate 	if (error = mount_root(*name ? name : "root", root_path, NFS_V4,
30157c478bd9Sstevel@tonic-gate 	    &args, &vfsflags)) {
30167c478bd9Sstevel@tonic-gate 		if (error == EPROTONOSUPPORT)
30177c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN, "nfs4_mountroot: "
30187c478bd9Sstevel@tonic-gate 			    "mount_root failed: server doesn't support NFS V4");
30197c478bd9Sstevel@tonic-gate 		else
30207c478bd9Sstevel@tonic-gate 			nfs_cmn_err(error, CE_WARN,
30217c478bd9Sstevel@tonic-gate 			    "nfs4_mountroot: mount_root failed: %m");
30227c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
30237c478bd9Sstevel@tonic-gate 		sv4_free(svp);
30247c478bd9Sstevel@tonic-gate 		pn_free(&pn);
30257c478bd9Sstevel@tonic-gate 		return (error);
30267c478bd9Sstevel@tonic-gate 	}
30277c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&svp->sv_lock);
30287c478bd9Sstevel@tonic-gate 	svp->sv_hostnamelen = (int)(strlen(root_hostname) + 1);
30297c478bd9Sstevel@tonic-gate 	svp->sv_hostname = kmem_alloc(svp->sv_hostnamelen, KM_SLEEP);
30307c478bd9Sstevel@tonic-gate 	(void) strcpy(svp->sv_hostname, root_hostname);
30317c478bd9Sstevel@tonic-gate 
30327c478bd9Sstevel@tonic-gate 	svp->sv_pathlen = (int)(strlen(root_path) + 1);
30337c478bd9Sstevel@tonic-gate 	svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP);
30347c478bd9Sstevel@tonic-gate 	(void) strcpy(svp->sv_path, root_path);
30357c478bd9Sstevel@tonic-gate 
30367c478bd9Sstevel@tonic-gate 	/*
30377c478bd9Sstevel@tonic-gate 	 * Force root partition to always be mounted with AUTH_UNIX for now
30387c478bd9Sstevel@tonic-gate 	 */
30397c478bd9Sstevel@tonic-gate 	svp->sv_secdata = kmem_alloc(sizeof (*svp->sv_secdata), KM_SLEEP);
30407c478bd9Sstevel@tonic-gate 	svp->sv_secdata->secmod = AUTH_UNIX;
30417c478bd9Sstevel@tonic-gate 	svp->sv_secdata->rpcflavor = AUTH_UNIX;
30427c478bd9Sstevel@tonic-gate 	svp->sv_secdata->data = NULL;
30437c478bd9Sstevel@tonic-gate 
30447c478bd9Sstevel@tonic-gate 	cr = crgetcred();
30457c478bd9Sstevel@tonic-gate 	rtvp = NULL;
30467c478bd9Sstevel@tonic-gate 
30477c478bd9Sstevel@tonic-gate 	error = nfs4rootvp(&rtvp, vfsp, svp, args.flags, cr, global_zone);
30487c478bd9Sstevel@tonic-gate 
30497c478bd9Sstevel@tonic-gate 	if (error) {
30507c478bd9Sstevel@tonic-gate 		crfree(cr);
30517c478bd9Sstevel@tonic-gate 		pn_free(&pn);
3052ab7762b6Smaheshvs 		sv4_free(svp);
3053ab7762b6Smaheshvs 		return (error);
30547c478bd9Sstevel@tonic-gate 	}
30557c478bd9Sstevel@tonic-gate 
30567c478bd9Sstevel@tonic-gate 	mi = VTOMI4(rtvp);
30577c478bd9Sstevel@tonic-gate 
30587c478bd9Sstevel@tonic-gate 	/*
30597c478bd9Sstevel@tonic-gate 	 * Send client id to the server, if necessary
30607c478bd9Sstevel@tonic-gate 	 */
30617c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(&n4e);
30627c478bd9Sstevel@tonic-gate 	nfs4setclientid(mi, cr, FALSE, &n4e);
30637c478bd9Sstevel@tonic-gate 	error = n4e.error;
30647c478bd9Sstevel@tonic-gate 
30657c478bd9Sstevel@tonic-gate 	crfree(cr);
30667c478bd9Sstevel@tonic-gate 
30677c478bd9Sstevel@tonic-gate 	if (error) {
30687c478bd9Sstevel@tonic-gate 		pn_free(&pn);
30697c478bd9Sstevel@tonic-gate 		goto errout;
30707c478bd9Sstevel@tonic-gate 	}
30717c478bd9Sstevel@tonic-gate 
30727c478bd9Sstevel@tonic-gate 	error = nfs4_setopts(rtvp, DATAMODEL_NATIVE, &args);
30737c478bd9Sstevel@tonic-gate 	if (error) {
30747c478bd9Sstevel@tonic-gate 		nfs_cmn_err(error, CE_WARN,
30757c478bd9Sstevel@tonic-gate 		    "nfs4_mountroot: invalid root mount options");
30767c478bd9Sstevel@tonic-gate 		pn_free(&pn);
30777c478bd9Sstevel@tonic-gate 		goto errout;
30787c478bd9Sstevel@tonic-gate 	}
30797c478bd9Sstevel@tonic-gate 
30807c478bd9Sstevel@tonic-gate 	(void) vfs_lock_wait(vfsp);
30817c478bd9Sstevel@tonic-gate 	vfs_add(NULL, vfsp, vfsflags);
30827c478bd9Sstevel@tonic-gate 	vfs_unlock(vfsp);
30837c478bd9Sstevel@tonic-gate 
30847c478bd9Sstevel@tonic-gate 	size = strlen(svp->sv_hostname);
30857c478bd9Sstevel@tonic-gate 	(void) strcpy(rootfs.bo_name, svp->sv_hostname);
30867c478bd9Sstevel@tonic-gate 	rootfs.bo_name[size] = ':';
30877c478bd9Sstevel@tonic-gate 	(void) strcpy(&rootfs.bo_name[size + 1], root_path);
30887c478bd9Sstevel@tonic-gate 
30897c478bd9Sstevel@tonic-gate 	pn_free(&pn);
30907c478bd9Sstevel@tonic-gate 
30917c478bd9Sstevel@tonic-gate errout:
30927c478bd9Sstevel@tonic-gate 	if (error) {
30937c478bd9Sstevel@tonic-gate 		sv4_free(svp);
30947c478bd9Sstevel@tonic-gate 		nfs4_async_stop(vfsp);
30957c478bd9Sstevel@tonic-gate 		nfs4_async_manager_stop(vfsp);
30967c478bd9Sstevel@tonic-gate 	}
30977c478bd9Sstevel@tonic-gate 
30987c478bd9Sstevel@tonic-gate 	if (rtvp != NULL)
30997c478bd9Sstevel@tonic-gate 		VN_RELE(rtvp);
31007c478bd9Sstevel@tonic-gate 
31017c478bd9Sstevel@tonic-gate 	return (error);
31027c478bd9Sstevel@tonic-gate }
31037c478bd9Sstevel@tonic-gate 
31047c478bd9Sstevel@tonic-gate /*
31057c478bd9Sstevel@tonic-gate  * Initialization routine for VFS routines.  Should only be called once
31067c478bd9Sstevel@tonic-gate  */
31077c478bd9Sstevel@tonic-gate int
31087c478bd9Sstevel@tonic-gate nfs4_vfsinit(void)
31097c478bd9Sstevel@tonic-gate {
31107c478bd9Sstevel@tonic-gate 	mutex_init(&nfs4_syncbusy, NULL, MUTEX_DEFAULT, NULL);
31117c478bd9Sstevel@tonic-gate 	nfs4setclientid_init();
3112b9238976Sth 	nfs4_ephemeral_init();
31137c478bd9Sstevel@tonic-gate 	return (0);
31147c478bd9Sstevel@tonic-gate }
31157c478bd9Sstevel@tonic-gate 
31167c478bd9Sstevel@tonic-gate void
31177c478bd9Sstevel@tonic-gate nfs4_vfsfini(void)
31187c478bd9Sstevel@tonic-gate {
3119b9238976Sth 	nfs4_ephemeral_fini();
31207c478bd9Sstevel@tonic-gate 	nfs4setclientid_fini();
31217c478bd9Sstevel@tonic-gate 	mutex_destroy(&nfs4_syncbusy);
31227c478bd9Sstevel@tonic-gate }
31237c478bd9Sstevel@tonic-gate 
31247c478bd9Sstevel@tonic-gate void
31257c478bd9Sstevel@tonic-gate nfs4_freevfs(vfs_t *vfsp)
31267c478bd9Sstevel@tonic-gate {
31277c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
31287c478bd9Sstevel@tonic-gate 
312950a83466Sjwahlig 	/* need to release the initial hold */
31307c478bd9Sstevel@tonic-gate 	mi = VFTOMI4(vfsp);
3131b87f76edSThomas Haynes 
3132b87f76edSThomas Haynes 	/*
3133b87f76edSThomas Haynes 	 * At this point, we can no longer reference the vfs
3134b87f76edSThomas Haynes 	 * and need to inform other holders of the reference
3135b87f76edSThomas Haynes 	 * to the mntinfo4_t.
3136b87f76edSThomas Haynes 	 */
3137b87f76edSThomas Haynes 	mi->mi_vfsp = NULL;
3138b87f76edSThomas Haynes 
313950a83466Sjwahlig 	MI4_RELE(mi);
31407c478bd9Sstevel@tonic-gate }
31417c478bd9Sstevel@tonic-gate 
31427c478bd9Sstevel@tonic-gate /*
31437c478bd9Sstevel@tonic-gate  * Client side SETCLIENTID and SETCLIENTID_CONFIRM
31447c478bd9Sstevel@tonic-gate  */
31457c478bd9Sstevel@tonic-gate struct nfs4_server nfs4_server_lst =
31467c478bd9Sstevel@tonic-gate 	{ &nfs4_server_lst, &nfs4_server_lst };
31477c478bd9Sstevel@tonic-gate 
31487c478bd9Sstevel@tonic-gate kmutex_t nfs4_server_lst_lock;
31497c478bd9Sstevel@tonic-gate 
31507c478bd9Sstevel@tonic-gate static void
31517c478bd9Sstevel@tonic-gate nfs4setclientid_init(void)
31527c478bd9Sstevel@tonic-gate {
31537c478bd9Sstevel@tonic-gate 	mutex_init(&nfs4_server_lst_lock, NULL, MUTEX_DEFAULT, NULL);
31547c478bd9Sstevel@tonic-gate }
31557c478bd9Sstevel@tonic-gate 
31567c478bd9Sstevel@tonic-gate static void
31577c478bd9Sstevel@tonic-gate nfs4setclientid_fini(void)
31587c478bd9Sstevel@tonic-gate {
31597c478bd9Sstevel@tonic-gate 	mutex_destroy(&nfs4_server_lst_lock);
31607c478bd9Sstevel@tonic-gate }
31617c478bd9Sstevel@tonic-gate 
31627c478bd9Sstevel@tonic-gate int nfs4_retry_sclid_delay = NFS4_RETRY_SCLID_DELAY;
31637c478bd9Sstevel@tonic-gate int nfs4_num_sclid_retries = NFS4_NUM_SCLID_RETRIES;
31647c478bd9Sstevel@tonic-gate 
31657c478bd9Sstevel@tonic-gate /*
31667c478bd9Sstevel@tonic-gate  * Set the clientid for the server for "mi".  No-op if the clientid is
31677c478bd9Sstevel@tonic-gate  * already set.
31687c478bd9Sstevel@tonic-gate  *
31697c478bd9Sstevel@tonic-gate  * The recovery boolean should be set to TRUE if this function was called
3170a092743bSek  * by the recovery code, and FALSE otherwise.  This is used to determine
3171a092743bSek  * if we need to call nfs4_start/end_op as well as grab the mi_recovlock
3172a092743bSek  * for adding a mntinfo4_t to a nfs4_server_t.
31737c478bd9Sstevel@tonic-gate  *
31747c478bd9Sstevel@tonic-gate  * Error is returned via 'n4ep'.  If there was a 'n4ep->stat' error, then
31757c478bd9Sstevel@tonic-gate  * 'n4ep->error' is set to geterrno4(n4ep->stat).
31767c478bd9Sstevel@tonic-gate  */
31777c478bd9Sstevel@tonic-gate void
31787c478bd9Sstevel@tonic-gate nfs4setclientid(mntinfo4_t *mi, cred_t *cr, bool_t recovery, nfs4_error_t *n4ep)
31797c478bd9Sstevel@tonic-gate {
31807c478bd9Sstevel@tonic-gate 	struct nfs4_server *np;
31817c478bd9Sstevel@tonic-gate 	struct servinfo4 *svp = mi->mi_curr_serv;
31827c478bd9Sstevel@tonic-gate 	nfs4_recov_state_t recov_state;
31837c478bd9Sstevel@tonic-gate 	int num_retries = 0;
3184f64c4ae1Sdm 	bool_t retry;
31857c478bd9Sstevel@tonic-gate 	cred_t *lcr = NULL;
31867c478bd9Sstevel@tonic-gate 	int retry_inuse = 1; /* only retry once on NFS4ERR_CLID_INUSE */
31877c478bd9Sstevel@tonic-gate 	time_t lease_time = 0;
31887c478bd9Sstevel@tonic-gate 
31897c478bd9Sstevel@tonic-gate 	recov_state.rs_flags = 0;
31907c478bd9Sstevel@tonic-gate 	recov_state.rs_num_retry_despite_err = 0;
31917c478bd9Sstevel@tonic-gate 	ASSERT(n4ep != NULL);
31927c478bd9Sstevel@tonic-gate 
31937c478bd9Sstevel@tonic-gate recov_retry:
3194f64c4ae1Sdm 	retry = FALSE;
31957c478bd9Sstevel@tonic-gate 	nfs4_error_zinit(n4ep);
3196a092743bSek 	if (!recovery)
3197a092743bSek 		(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, 0);
3198a092743bSek 
3199f86c6ccaSdm 	mutex_enter(&nfs4_server_lst_lock);
3200f86c6ccaSdm 	np = servinfo4_to_nfs4_server(svp); /* This locks np if it is found */
3201f86c6ccaSdm 	mutex_exit(&nfs4_server_lst_lock);
3202f86c6ccaSdm 	if (!np) {
3203f86c6ccaSdm 		struct nfs4_server *tnp;
3204f86c6ccaSdm 		np = new_nfs4_server(svp, cr);
320516237317Sdm 		mutex_enter(&np->s_lock);
32067c478bd9Sstevel@tonic-gate 
3207f86c6ccaSdm 		mutex_enter(&nfs4_server_lst_lock);
3208f86c6ccaSdm 		tnp = servinfo4_to_nfs4_server(svp);
3209f86c6ccaSdm 		if (tnp) {
3210f86c6ccaSdm 			/*
3211f86c6ccaSdm 			 * another thread snuck in and put server on list.
3212f86c6ccaSdm 			 * since we aren't adding it to the nfs4_server_list
3213f86c6ccaSdm 			 * we need to set the ref count to 0 and destroy it.
3214f86c6ccaSdm 			 */
3215f86c6ccaSdm 			np->s_refcnt = 0;
3216f86c6ccaSdm 			destroy_nfs4_server(np);
3217f86c6ccaSdm 			np = tnp;
3218f86c6ccaSdm 		} else {
3219f86c6ccaSdm 			/*
3220f86c6ccaSdm 			 * do not give list a reference until everything
3221f86c6ccaSdm 			 * succeeds
3222f86c6ccaSdm 			 */
3223f86c6ccaSdm 			insque(np, &nfs4_server_lst);
3224f86c6ccaSdm 		}
3225f86c6ccaSdm 		mutex_exit(&nfs4_server_lst_lock);
3226f86c6ccaSdm 	}
3227f86c6ccaSdm 	ASSERT(MUTEX_HELD(&np->s_lock));
32287c478bd9Sstevel@tonic-gate 	/*
3229f86c6ccaSdm 	 * If we find the server already has N4S_CLIENTID_SET, then
3230f86c6ccaSdm 	 * just return, we've already done SETCLIENTID to that server
32317c478bd9Sstevel@tonic-gate 	 */
3232f86c6ccaSdm 	if (np->s_flags & N4S_CLIENTID_SET) {
32337c478bd9Sstevel@tonic-gate 		/* add mi to np's mntinfo4_list */
32347c478bd9Sstevel@tonic-gate 		nfs4_add_mi_to_server(np, mi);
3235a092743bSek 		if (!recovery)
3236a092743bSek 			nfs_rw_exit(&mi->mi_recovlock);
32377c478bd9Sstevel@tonic-gate 		mutex_exit(&np->s_lock);
32387c478bd9Sstevel@tonic-gate 		nfs4_server_rele(np);
32397c478bd9Sstevel@tonic-gate 		return;
32407c478bd9Sstevel@tonic-gate 	}
3241f86c6ccaSdm 	mutex_exit(&np->s_lock);
3242f86c6ccaSdm 
32437c478bd9Sstevel@tonic-gate 
3244a092743bSek 	/*
3245a092743bSek 	 * Drop the mi_recovlock since nfs4_start_op will
3246a092743bSek 	 * acquire it again for us.
3247a092743bSek 	 */
3248f86c6ccaSdm 	if (!recovery) {
3249a092743bSek 		nfs_rw_exit(&mi->mi_recovlock);
3250a092743bSek 
32517c478bd9Sstevel@tonic-gate 		n4ep->error = nfs4_start_op(mi, NULL, NULL, &recov_state);
32527c478bd9Sstevel@tonic-gate 		if (n4ep->error) {
32537c478bd9Sstevel@tonic-gate 			nfs4_server_rele(np);
32547c478bd9Sstevel@tonic-gate 			return;
32557c478bd9Sstevel@tonic-gate 		}
32567c478bd9Sstevel@tonic-gate 	}
32577c478bd9Sstevel@tonic-gate 
32587c478bd9Sstevel@tonic-gate 	mutex_enter(&np->s_lock);
3259f86c6ccaSdm 	while (np->s_flags & N4S_CLIENTID_PEND) {
3260f86c6ccaSdm 		if (!cv_wait_sig(&np->s_clientid_pend, &np->s_lock)) {
3261f86c6ccaSdm 			mutex_exit(&np->s_lock);
3262f86c6ccaSdm 			nfs4_server_rele(np);
3263f86c6ccaSdm 			if (!recovery)
3264f86c6ccaSdm 				nfs4_end_op(mi, NULL, NULL, &recov_state,
3265f86c6ccaSdm 				    recovery);
3266f86c6ccaSdm 			n4ep->error = EINTR;
3267f86c6ccaSdm 			return;
3268f86c6ccaSdm 		}
3269f86c6ccaSdm 	}
32707c478bd9Sstevel@tonic-gate 
32717c478bd9Sstevel@tonic-gate 	if (np->s_flags & N4S_CLIENTID_SET) {
32727c478bd9Sstevel@tonic-gate 		/* XXX copied/pasted from above */
32737c478bd9Sstevel@tonic-gate 		/* add mi to np's mntinfo4_list */
32747c478bd9Sstevel@tonic-gate 		nfs4_add_mi_to_server(np, mi);
32757c478bd9Sstevel@tonic-gate 		mutex_exit(&np->s_lock);
32767c478bd9Sstevel@tonic-gate 		nfs4_server_rele(np);
32777c478bd9Sstevel@tonic-gate 		if (!recovery)
32787c478bd9Sstevel@tonic-gate 			nfs4_end_op(mi, NULL, NULL, &recov_state, recovery);
32797c478bd9Sstevel@tonic-gate 		return;
32807c478bd9Sstevel@tonic-gate 	}
32817c478bd9Sstevel@tonic-gate 
3282f86c6ccaSdm 	/*
3283f86c6ccaSdm 	 * Reset the N4S_CB_PINGED flag. This is used to
3284f86c6ccaSdm 	 * indicate if we have received a CB_NULL from the
3285f86c6ccaSdm 	 * server. Also we reset the waiter flag.
3286f86c6ccaSdm 	 */
3287f86c6ccaSdm 	np->s_flags &= ~(N4S_CB_PINGED | N4S_CB_WAITER);
3288f86c6ccaSdm 	/* any failure must now clear this flag */
3289f86c6ccaSdm 	np->s_flags |= N4S_CLIENTID_PEND;
3290f86c6ccaSdm 	mutex_exit(&np->s_lock);
32917c478bd9Sstevel@tonic-gate 	nfs4setclientid_otw(mi, svp, cr, np, n4ep, &retry_inuse);
32927c478bd9Sstevel@tonic-gate 
32937c478bd9Sstevel@tonic-gate 	if (n4ep->error == EACCES) {
32947c478bd9Sstevel@tonic-gate 		/*
32957c478bd9Sstevel@tonic-gate 		 * If the uid is set then set the creds for secure mounts
32967c478bd9Sstevel@tonic-gate 		 * by proxy processes such as automountd.
32977c478bd9Sstevel@tonic-gate 		 */
32987c478bd9Sstevel@tonic-gate 		(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
32997c478bd9Sstevel@tonic-gate 		if (svp->sv_secdata->uid != 0) {
33007c478bd9Sstevel@tonic-gate 			lcr = crdup(cr);
33017c478bd9Sstevel@tonic-gate 			(void) crsetugid(lcr, svp->sv_secdata->uid,
33027c478bd9Sstevel@tonic-gate 			    crgetgid(cr));
33037c478bd9Sstevel@tonic-gate 		}
33047c478bd9Sstevel@tonic-gate 		nfs_rw_exit(&svp->sv_lock);
33057c478bd9Sstevel@tonic-gate 
3306f86c6ccaSdm 		if (lcr != NULL) {
3307f86c6ccaSdm 			mutex_enter(&np->s_lock);
3308f86c6ccaSdm 			crfree(np->s_cred);
3309f86c6ccaSdm 			np->s_cred = lcr;
3310f86c6ccaSdm 			mutex_exit(&np->s_lock);
33117c478bd9Sstevel@tonic-gate 			nfs4setclientid_otw(mi, svp, lcr, np, n4ep,
3312b9238976Sth 			    &retry_inuse);
3313f86c6ccaSdm 		}
33147c478bd9Sstevel@tonic-gate 	}
3315f86c6ccaSdm 	mutex_enter(&np->s_lock);
33167c478bd9Sstevel@tonic-gate 	lease_time = np->s_lease_time;
3317f86c6ccaSdm 	np->s_flags &= ~N4S_CLIENTID_PEND;
33187c478bd9Sstevel@tonic-gate 	mutex_exit(&np->s_lock);
33197c478bd9Sstevel@tonic-gate 
33207c478bd9Sstevel@tonic-gate 	if (n4ep->error != 0 || n4ep->stat != NFS4_OK) {
33217c478bd9Sstevel@tonic-gate 		/*
33227c478bd9Sstevel@tonic-gate 		 * Start recovery if failover is a possibility.  If
33237c478bd9Sstevel@tonic-gate 		 * invoked by the recovery thread itself, then just
33247c478bd9Sstevel@tonic-gate 		 * return and let it handle the failover first.  NB:
33257c478bd9Sstevel@tonic-gate 		 * recovery is not allowed if the mount is in progress
33267c478bd9Sstevel@tonic-gate 		 * since the infrastructure is not sufficiently setup
33277c478bd9Sstevel@tonic-gate 		 * to allow it.  Just return the error (after suitable
33287c478bd9Sstevel@tonic-gate 		 * retries).
33297c478bd9Sstevel@tonic-gate 		 */
33307c478bd9Sstevel@tonic-gate 		if (FAILOVER_MOUNT4(mi) && nfs4_try_failover(n4ep)) {
33317c478bd9Sstevel@tonic-gate 			(void) nfs4_start_recovery(n4ep, mi, NULL,
33322f172c55SRobert Thurlow 			    NULL, NULL, NULL, OP_SETCLIENTID, NULL, NULL, NULL);
33337c478bd9Sstevel@tonic-gate 			/*
33347c478bd9Sstevel@tonic-gate 			 * Don't retry here, just return and let
33357c478bd9Sstevel@tonic-gate 			 * recovery take over.
33367c478bd9Sstevel@tonic-gate 			 */
33377c478bd9Sstevel@tonic-gate 			if (recovery)
33387c478bd9Sstevel@tonic-gate 				retry = FALSE;
33397c478bd9Sstevel@tonic-gate 		} else if (nfs4_rpc_retry_error(n4ep->error) ||
3340b9238976Sth 		    n4ep->stat == NFS4ERR_RESOURCE ||
3341b9238976Sth 		    n4ep->stat == NFS4ERR_STALE_CLIENTID) {
33427c478bd9Sstevel@tonic-gate 
3343b9238976Sth 			retry = TRUE;
3344b9238976Sth 			/*
3345b9238976Sth 			 * Always retry if in recovery or once had
3346b9238976Sth 			 * contact with the server (but now it's
3347b9238976Sth 			 * overloaded).
3348b9238976Sth 			 */
3349b9238976Sth 			if (recovery == TRUE ||
3350b9238976Sth 			    n4ep->error == ETIMEDOUT ||
3351b9238976Sth 			    n4ep->error == ECONNRESET)
33527c478bd9Sstevel@tonic-gate 				num_retries = 0;
3353b9238976Sth 		} else if (retry_inuse && n4ep->error == 0 &&
3354b9238976Sth 		    n4ep->stat == NFS4ERR_CLID_INUSE) {
3355b9238976Sth 			retry = TRUE;
3356b9238976Sth 			num_retries = 0;
33577c478bd9Sstevel@tonic-gate 		}
3358f86c6ccaSdm 	} else {
3359f64c4ae1Sdm 		/*
3360f64c4ae1Sdm 		 * Since everything succeeded give the list a reference count if
3361f64c4ae1Sdm 		 * it hasn't been given one by add_new_nfs4_server() or if this
3362f64c4ae1Sdm 		 * is not a recovery situation in which case it is already on
3363f64c4ae1Sdm 		 * the list.
3364f64c4ae1Sdm 		 */
3365f86c6ccaSdm 		mutex_enter(&np->s_lock);
3366f64c4ae1Sdm 		if ((np->s_flags & N4S_INSERTED) == 0) {
3367f64c4ae1Sdm 			np->s_refcnt++;
3368f64c4ae1Sdm 			np->s_flags |= N4S_INSERTED;
3369f64c4ae1Sdm 		}
3370f86c6ccaSdm 		mutex_exit(&np->s_lock);
33717c478bd9Sstevel@tonic-gate 	}
33727c478bd9Sstevel@tonic-gate 
33737c478bd9Sstevel@tonic-gate 	if (!recovery)
33747c478bd9Sstevel@tonic-gate 		nfs4_end_op(mi, NULL, NULL, &recov_state, recovery);
3375f86c6ccaSdm 
33767c478bd9Sstevel@tonic-gate 
33777c478bd9Sstevel@tonic-gate 	if (retry && num_retries++ < nfs4_num_sclid_retries) {
33787c478bd9Sstevel@tonic-gate 		if (retry_inuse) {
33797c478bd9Sstevel@tonic-gate 			delay(SEC_TO_TICK(lease_time + nfs4_retry_sclid_delay));
33807c478bd9Sstevel@tonic-gate 			retry_inuse = 0;
33817c478bd9Sstevel@tonic-gate 		} else
33827c478bd9Sstevel@tonic-gate 			delay(SEC_TO_TICK(nfs4_retry_sclid_delay));
3383f86c6ccaSdm 
3384f86c6ccaSdm 		nfs4_server_rele(np);
33857c478bd9Sstevel@tonic-gate 		goto recov_retry;
33867c478bd9Sstevel@tonic-gate 	}
33877c478bd9Sstevel@tonic-gate 
3388f86c6ccaSdm 
33897c478bd9Sstevel@tonic-gate 	if (n4ep->error == 0)
33907c478bd9Sstevel@tonic-gate 		n4ep->error = geterrno4(n4ep->stat);
3391f86c6ccaSdm 
3392f86c6ccaSdm 	/* broadcast before release in case no other threads are waiting */
3393f86c6ccaSdm 	cv_broadcast(&np->s_clientid_pend);
3394f86c6ccaSdm 	nfs4_server_rele(np);
33957c478bd9Sstevel@tonic-gate }
33967c478bd9Sstevel@tonic-gate 
33977c478bd9Sstevel@tonic-gate int nfs4setclientid_otw_debug = 0;
33987c478bd9Sstevel@tonic-gate 
33997c478bd9Sstevel@tonic-gate /*
34007c478bd9Sstevel@tonic-gate  * This function handles the recovery of STALE_CLIENTID for SETCLIENTID_CONFRIM,
34017c478bd9Sstevel@tonic-gate  * but nothing else; the calling function must be designed to handle those
34027c478bd9Sstevel@tonic-gate  * other errors.
34037c478bd9Sstevel@tonic-gate  */
34047c478bd9Sstevel@tonic-gate static void
34057c478bd9Sstevel@tonic-gate nfs4setclientid_otw(mntinfo4_t *mi, struct servinfo4 *svp,  cred_t *cr,
3406b9238976Sth     struct nfs4_server *np, nfs4_error_t *ep, int *retry_inusep)
34077c478bd9Sstevel@tonic-gate {
34087c478bd9Sstevel@tonic-gate 	COMPOUND4args_clnt args;
34097c478bd9Sstevel@tonic-gate 	COMPOUND4res_clnt res;
34107c478bd9Sstevel@tonic-gate 	nfs_argop4 argop[3];
34117c478bd9Sstevel@tonic-gate 	SETCLIENTID4args *s_args;
34127c478bd9Sstevel@tonic-gate 	SETCLIENTID4resok *s_resok;
34137c478bd9Sstevel@tonic-gate 	int doqueue = 1;
34147c478bd9Sstevel@tonic-gate 	nfs4_ga_res_t *garp = NULL;
34157c478bd9Sstevel@tonic-gate 	timespec_t prop_time, after_time;
34167c478bd9Sstevel@tonic-gate 	verifier4 verf;
34177c478bd9Sstevel@tonic-gate 	clientid4 tmp_clientid;
34187c478bd9Sstevel@tonic-gate 
3419f86c6ccaSdm 	ASSERT(!MUTEX_HELD(&np->s_lock));
34207c478bd9Sstevel@tonic-gate 
34217c478bd9Sstevel@tonic-gate 	args.ctag = TAG_SETCLIENTID;
34227c478bd9Sstevel@tonic-gate 
34237c478bd9Sstevel@tonic-gate 	args.array = argop;
34247c478bd9Sstevel@tonic-gate 	args.array_len = 3;
34257c478bd9Sstevel@tonic-gate 
34267c478bd9Sstevel@tonic-gate 	/* PUTROOTFH */
34277c478bd9Sstevel@tonic-gate 	argop[0].argop = OP_PUTROOTFH;
34287c478bd9Sstevel@tonic-gate 
34297c478bd9Sstevel@tonic-gate 	/* GETATTR */
34307c478bd9Sstevel@tonic-gate 	argop[1].argop = OP_GETATTR;
34317c478bd9Sstevel@tonic-gate 	argop[1].nfs_argop4_u.opgetattr.attr_request = FATTR4_LEASE_TIME_MASK;
34327c478bd9Sstevel@tonic-gate 	argop[1].nfs_argop4_u.opgetattr.mi = mi;
34337c478bd9Sstevel@tonic-gate 
34347c478bd9Sstevel@tonic-gate 	/* SETCLIENTID */
34357c478bd9Sstevel@tonic-gate 	argop[2].argop = OP_SETCLIENTID;
34367c478bd9Sstevel@tonic-gate 
34377c478bd9Sstevel@tonic-gate 	s_args = &argop[2].nfs_argop4_u.opsetclientid;
34387c478bd9Sstevel@tonic-gate 
3439f86c6ccaSdm 	mutex_enter(&np->s_lock);
3440f86c6ccaSdm 
34417c478bd9Sstevel@tonic-gate 	s_args->client.verifier = np->clidtosend.verifier;
34427c478bd9Sstevel@tonic-gate 	s_args->client.id_len = np->clidtosend.id_len;
34437c478bd9Sstevel@tonic-gate 	ASSERT(s_args->client.id_len <= NFS4_OPAQUE_LIMIT);
34447c478bd9Sstevel@tonic-gate 	s_args->client.id_val = np->clidtosend.id_val;
34457c478bd9Sstevel@tonic-gate 
34467c478bd9Sstevel@tonic-gate 	/*
34477c478bd9Sstevel@tonic-gate 	 * Callback needs to happen on non-RDMA transport
34487c478bd9Sstevel@tonic-gate 	 * Check if we have saved the original knetconfig
34497c478bd9Sstevel@tonic-gate 	 * if so, use that instead.
34507c478bd9Sstevel@tonic-gate 	 */
34517c478bd9Sstevel@tonic-gate 	if (svp->sv_origknconf != NULL)
34527c478bd9Sstevel@tonic-gate 		nfs4_cb_args(np, svp->sv_origknconf, s_args);
34537c478bd9Sstevel@tonic-gate 	else
34547c478bd9Sstevel@tonic-gate 		nfs4_cb_args(np, svp->sv_knconf, s_args);
34557c478bd9Sstevel@tonic-gate 
3456f86c6ccaSdm 	mutex_exit(&np->s_lock);
3457f86c6ccaSdm 
3458f86c6ccaSdm 	rfs4call(mi, &args, &res, cr, &doqueue, 0, ep);
34597c478bd9Sstevel@tonic-gate 
34607c478bd9Sstevel@tonic-gate 	if (ep->error)
34617c478bd9Sstevel@tonic-gate 		return;
34627c478bd9Sstevel@tonic-gate 
34637c478bd9Sstevel@tonic-gate 	/* getattr lease_time res */
3464e557fb2cSDai Ngo 	if ((res.array_len >= 2) &&
3465e557fb2cSDai Ngo 	    (res.array[1].nfs_resop4_u.opgetattr.status == NFS4_OK)) {
34667c478bd9Sstevel@tonic-gate 		garp = &res.array[1].nfs_resop4_u.opgetattr.ga_res;
34677c478bd9Sstevel@tonic-gate 
34687c478bd9Sstevel@tonic-gate #ifndef _LP64
34697c478bd9Sstevel@tonic-gate 		/*
34707c478bd9Sstevel@tonic-gate 		 * The 32 bit client cannot handle a lease time greater than
34717c478bd9Sstevel@tonic-gate 		 * (INT32_MAX/1000000).  This is due to the use of the
34727c478bd9Sstevel@tonic-gate 		 * lease_time in calls to drv_usectohz() in
34737c478bd9Sstevel@tonic-gate 		 * nfs4_renew_lease_thread().  The problem is that
34747c478bd9Sstevel@tonic-gate 		 * drv_usectohz() takes a time_t (which is just a long = 4
34757c478bd9Sstevel@tonic-gate 		 * bytes) as its parameter.  The lease_time is multiplied by
34767c478bd9Sstevel@tonic-gate 		 * 1000000 to convert seconds to usecs for the parameter.  If
34777c478bd9Sstevel@tonic-gate 		 * a number bigger than (INT32_MAX/1000000) is used then we
34787c478bd9Sstevel@tonic-gate 		 * overflow on the 32bit client.
34797c478bd9Sstevel@tonic-gate 		 */
34807c478bd9Sstevel@tonic-gate 		if (garp->n4g_ext_res->n4g_leasetime > (INT32_MAX/1000000)) {
34817c478bd9Sstevel@tonic-gate 			garp->n4g_ext_res->n4g_leasetime = INT32_MAX/1000000;
34827c478bd9Sstevel@tonic-gate 		}
34837c478bd9Sstevel@tonic-gate #endif
34847c478bd9Sstevel@tonic-gate 
3485f86c6ccaSdm 		mutex_enter(&np->s_lock);
34867c478bd9Sstevel@tonic-gate 		np->s_lease_time = garp->n4g_ext_res->n4g_leasetime;
34877c478bd9Sstevel@tonic-gate 
34887c478bd9Sstevel@tonic-gate 		/*
34897c478bd9Sstevel@tonic-gate 		 * Keep track of the lease period for the mi's
34907c478bd9Sstevel@tonic-gate 		 * mi_msg_list.  We need an appropiate time
34917c478bd9Sstevel@tonic-gate 		 * bound to associate past facts with a current
34927c478bd9Sstevel@tonic-gate 		 * event.  The lease period is perfect for this.
34937c478bd9Sstevel@tonic-gate 		 */
34947c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_msg_list_lock);
34957c478bd9Sstevel@tonic-gate 		mi->mi_lease_period = np->s_lease_time;
34967c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_msg_list_lock);
3497f86c6ccaSdm 		mutex_exit(&np->s_lock);
34987c478bd9Sstevel@tonic-gate 	}
34997c478bd9Sstevel@tonic-gate 
35007c478bd9Sstevel@tonic-gate 
35017c478bd9Sstevel@tonic-gate 	if (res.status == NFS4ERR_CLID_INUSE) {
35027c478bd9Sstevel@tonic-gate 		clientaddr4 *clid_inuse;
35037c478bd9Sstevel@tonic-gate 
35047c478bd9Sstevel@tonic-gate 		if (!(*retry_inusep)) {
35057c478bd9Sstevel@tonic-gate 			clid_inuse = &res.array->nfs_resop4_u.
3506b9238976Sth 			    opsetclientid.SETCLIENTID4res_u.client_using;
35077c478bd9Sstevel@tonic-gate 
35087c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_NOTE,
35097c478bd9Sstevel@tonic-gate 			    "NFS4 mount (SETCLIENTID failed)."
35107c478bd9Sstevel@tonic-gate 			    "  nfs4_client_id.id is in"
35117c478bd9Sstevel@tonic-gate 			    "use already by: r_netid<%s> r_addr<%s>",
35127c478bd9Sstevel@tonic-gate 			    clid_inuse->r_netid, clid_inuse->r_addr);
35137c478bd9Sstevel@tonic-gate 		}
35147c478bd9Sstevel@tonic-gate 
35157c478bd9Sstevel@tonic-gate 		/*
35167c478bd9Sstevel@tonic-gate 		 * XXX - The client should be more robust in its
35177c478bd9Sstevel@tonic-gate 		 * handling of clientid in use errors (regen another
35187c478bd9Sstevel@tonic-gate 		 * clientid and try again?)
35197c478bd9Sstevel@tonic-gate 		 */
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 	if (res.status) {
35257c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
35267c478bd9Sstevel@tonic-gate 		return;
35277c478bd9Sstevel@tonic-gate 	}
35287c478bd9Sstevel@tonic-gate 
35297c478bd9Sstevel@tonic-gate 	s_resok = &res.array[2].nfs_resop4_u.
3530b9238976Sth 	    opsetclientid.SETCLIENTID4res_u.resok4;
35317c478bd9Sstevel@tonic-gate 
35327c478bd9Sstevel@tonic-gate 	tmp_clientid = s_resok->clientid;
35337c478bd9Sstevel@tonic-gate 
35347c478bd9Sstevel@tonic-gate 	verf = s_resok->setclientid_confirm;
35357c478bd9Sstevel@tonic-gate 
35367c478bd9Sstevel@tonic-gate #ifdef	DEBUG
35377c478bd9Sstevel@tonic-gate 	if (nfs4setclientid_otw_debug) {
35387c478bd9Sstevel@tonic-gate 		union {
35397c478bd9Sstevel@tonic-gate 			clientid4	clientid;
35407c478bd9Sstevel@tonic-gate 			int		foo[2];
35417c478bd9Sstevel@tonic-gate 		} cid;
35427c478bd9Sstevel@tonic-gate 
35437c478bd9Sstevel@tonic-gate 		cid.clientid = s_resok->clientid;
35447c478bd9Sstevel@tonic-gate 
35457c478bd9Sstevel@tonic-gate 		zcmn_err(mi->mi_zone->zone_id, CE_NOTE,
35467c478bd9Sstevel@tonic-gate 		"nfs4setclientid_otw: OK, clientid = %x,%x, "
35477c478bd9Sstevel@tonic-gate 		"verifier = %" PRIx64 "\n", cid.foo[0], cid.foo[1], verf);
35487c478bd9Sstevel@tonic-gate 	}
35497c478bd9Sstevel@tonic-gate #endif
35507c478bd9Sstevel@tonic-gate 
35517c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
35527c478bd9Sstevel@tonic-gate 
35537c478bd9Sstevel@tonic-gate 	/* Confirm the client id and get the lease_time attribute */
35547c478bd9Sstevel@tonic-gate 
35557c478bd9Sstevel@tonic-gate 	args.ctag = TAG_SETCLIENTID_CF;
35567c478bd9Sstevel@tonic-gate 
35577c478bd9Sstevel@tonic-gate 	args.array = argop;
35587c478bd9Sstevel@tonic-gate 	args.array_len = 1;
35597c478bd9Sstevel@tonic-gate 
35607c478bd9Sstevel@tonic-gate 	argop[0].argop = OP_SETCLIENTID_CONFIRM;
35617c478bd9Sstevel@tonic-gate 
35627c478bd9Sstevel@tonic-gate 	argop[0].nfs_argop4_u.opsetclientid_confirm.clientid = tmp_clientid;
35637c478bd9Sstevel@tonic-gate 	argop[0].nfs_argop4_u.opsetclientid_confirm.setclientid_confirm = verf;
35647c478bd9Sstevel@tonic-gate 
35657c478bd9Sstevel@tonic-gate 	/* used to figure out RTT for np */
35667c478bd9Sstevel@tonic-gate 	gethrestime(&prop_time);
35677c478bd9Sstevel@tonic-gate 
35687c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4setlientid_otw: "
3569b9238976Sth 	    "start time: %ld sec %ld nsec", prop_time.tv_sec,
3570b9238976Sth 	    prop_time.tv_nsec));
35717c478bd9Sstevel@tonic-gate 
35727c478bd9Sstevel@tonic-gate 	rfs4call(mi, &args, &res, cr, &doqueue, 0, ep);
35737c478bd9Sstevel@tonic-gate 
35747c478bd9Sstevel@tonic-gate 	gethrestime(&after_time);
3575f86c6ccaSdm 	mutex_enter(&np->s_lock);
35767c478bd9Sstevel@tonic-gate 	np->propagation_delay.tv_sec =
3577b9238976Sth 	    MAX(1, after_time.tv_sec - prop_time.tv_sec);
3578f86c6ccaSdm 	mutex_exit(&np->s_lock);
35797c478bd9Sstevel@tonic-gate 
35807c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4setlcientid_otw: "
3581b9238976Sth 	    "finish time: %ld sec ", after_time.tv_sec));
35827c478bd9Sstevel@tonic-gate 
35837c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4setclientid_otw: "
3584b9238976Sth 	    "propagation delay set to %ld sec",
3585b9238976Sth 	    np->propagation_delay.tv_sec));
35867c478bd9Sstevel@tonic-gate 
35877c478bd9Sstevel@tonic-gate 	if (ep->error)
35887c478bd9Sstevel@tonic-gate 		return;
35897c478bd9Sstevel@tonic-gate 
35907c478bd9Sstevel@tonic-gate 	if (res.status == NFS4ERR_CLID_INUSE) {
35917c478bd9Sstevel@tonic-gate 		clientaddr4 *clid_inuse;
35927c478bd9Sstevel@tonic-gate 
35937c478bd9Sstevel@tonic-gate 		if (!(*retry_inusep)) {
35947c478bd9Sstevel@tonic-gate 			clid_inuse = &res.array->nfs_resop4_u.
3595b9238976Sth 			    opsetclientid.SETCLIENTID4res_u.client_using;
35967c478bd9Sstevel@tonic-gate 
35977c478bd9Sstevel@tonic-gate 			zcmn_err(mi->mi_zone->zone_id, CE_NOTE,
35987c478bd9Sstevel@tonic-gate 			    "SETCLIENTID_CONFIRM failed.  "
35997c478bd9Sstevel@tonic-gate 			    "nfs4_client_id.id is in use already by: "
36007c478bd9Sstevel@tonic-gate 			    "r_netid<%s> r_addr<%s>",
36017c478bd9Sstevel@tonic-gate 			    clid_inuse->r_netid, clid_inuse->r_addr);
36027c478bd9Sstevel@tonic-gate 		}
36037c478bd9Sstevel@tonic-gate 
36047c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
36057c478bd9Sstevel@tonic-gate 		return;
36067c478bd9Sstevel@tonic-gate 	}
36077c478bd9Sstevel@tonic-gate 
36087c478bd9Sstevel@tonic-gate 	if (res.status) {
36097c478bd9Sstevel@tonic-gate 		(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
36107c478bd9Sstevel@tonic-gate 		return;
36117c478bd9Sstevel@tonic-gate 	}
36127c478bd9Sstevel@tonic-gate 
3613f86c6ccaSdm 	mutex_enter(&np->s_lock);
36147c478bd9Sstevel@tonic-gate 	np->clientid = tmp_clientid;
36157c478bd9Sstevel@tonic-gate 	np->s_flags |= N4S_CLIENTID_SET;
36167c478bd9Sstevel@tonic-gate 
36177c478bd9Sstevel@tonic-gate 	/* Add mi to np's mntinfo4 list */
36187c478bd9Sstevel@tonic-gate 	nfs4_add_mi_to_server(np, mi);
36197c478bd9Sstevel@tonic-gate 
36207c478bd9Sstevel@tonic-gate 	if (np->lease_valid == NFS4_LEASE_NOT_STARTED) {
36217c478bd9Sstevel@tonic-gate 		/*
36227c478bd9Sstevel@tonic-gate 		 * Start lease management thread.
36237c478bd9Sstevel@tonic-gate 		 * Keep trying until we succeed.
36247c478bd9Sstevel@tonic-gate 		 */
36257c478bd9Sstevel@tonic-gate 
36267c478bd9Sstevel@tonic-gate 		np->s_refcnt++;		/* pass reference to thread */
36277c478bd9Sstevel@tonic-gate 		(void) zthread_create(NULL, 0, nfs4_renew_lease_thread, np, 0,
3628b9238976Sth 		    minclsyspri);
36297c478bd9Sstevel@tonic-gate 	}
3630f86c6ccaSdm 	mutex_exit(&np->s_lock);
36317c478bd9Sstevel@tonic-gate 
36327c478bd9Sstevel@tonic-gate 	(void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
36337c478bd9Sstevel@tonic-gate }
36347c478bd9Sstevel@tonic-gate 
36357c478bd9Sstevel@tonic-gate /*
36367c478bd9Sstevel@tonic-gate  * Add mi to sp's mntinfo4_list if it isn't already in the list.  Makes
36377c478bd9Sstevel@tonic-gate  * mi's clientid the same as sp's.
36387c478bd9Sstevel@tonic-gate  * Assumes sp is locked down.
36397c478bd9Sstevel@tonic-gate  */
36407c478bd9Sstevel@tonic-gate void
36417c478bd9Sstevel@tonic-gate nfs4_add_mi_to_server(nfs4_server_t *sp, mntinfo4_t *mi)
36427c478bd9Sstevel@tonic-gate {
36437c478bd9Sstevel@tonic-gate 	mntinfo4_t *tmi;
36447c478bd9Sstevel@tonic-gate 	int in_list = 0;
36457c478bd9Sstevel@tonic-gate 
3646a092743bSek 	ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) ||
3647a092743bSek 	    nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER));
36487c478bd9Sstevel@tonic-gate 	ASSERT(sp != &nfs4_server_lst);
36497c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
36507c478bd9Sstevel@tonic-gate 
36517c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
3652b9238976Sth 	    "nfs4_add_mi_to_server: add mi %p to sp %p",
3653b9238976Sth 	    (void*)mi, (void*)sp));
36547c478bd9Sstevel@tonic-gate 
36557c478bd9Sstevel@tonic-gate 	for (tmi = sp->mntinfo4_list;
36567c478bd9Sstevel@tonic-gate 	    tmi != NULL;
36577c478bd9Sstevel@tonic-gate 	    tmi = tmi->mi_clientid_next) {
36587c478bd9Sstevel@tonic-gate 		if (tmi == mi) {
36597c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_client_lease_debug,
3660b9238976Sth 			    (CE_NOTE,
3661b9238976Sth 			    "nfs4_add_mi_to_server: mi in list"));
36627c478bd9Sstevel@tonic-gate 			in_list = 1;
36637c478bd9Sstevel@tonic-gate 		}
36647c478bd9Sstevel@tonic-gate 	}
36657c478bd9Sstevel@tonic-gate 
36667c478bd9Sstevel@tonic-gate 	/*
36677c478bd9Sstevel@tonic-gate 	 * First put a hold on the mntinfo4's vfsp so that references via
36687c478bd9Sstevel@tonic-gate 	 * mntinfo4_list will be valid.
36697c478bd9Sstevel@tonic-gate 	 */
36707c478bd9Sstevel@tonic-gate 	if (!in_list)
36717c478bd9Sstevel@tonic-gate 		VFS_HOLD(mi->mi_vfsp);
36727c478bd9Sstevel@tonic-gate 
36737c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE, "nfs4_add_mi_to_server: "
3674b9238976Sth 	    "hold vfs %p for mi: %p", (void*)mi->mi_vfsp, (void*)mi));
36757c478bd9Sstevel@tonic-gate 
36767c478bd9Sstevel@tonic-gate 	if (!in_list) {
36777c478bd9Sstevel@tonic-gate 		if (sp->mntinfo4_list)
36787c478bd9Sstevel@tonic-gate 			sp->mntinfo4_list->mi_clientid_prev = mi;
36797c478bd9Sstevel@tonic-gate 		mi->mi_clientid_next = sp->mntinfo4_list;
36803b895386SPavel Filipensky 		mi->mi_srv = sp;
36817c478bd9Sstevel@tonic-gate 		sp->mntinfo4_list = mi;
36827c478bd9Sstevel@tonic-gate 		mi->mi_srvsettime = gethrestime_sec();
36833b895386SPavel Filipensky 		mi->mi_srvset_cnt++;
36847c478bd9Sstevel@tonic-gate 	}
36857c478bd9Sstevel@tonic-gate 
36867c478bd9Sstevel@tonic-gate 	/* set mi's clientid to that of sp's for later matching */
36877c478bd9Sstevel@tonic-gate 	mi->mi_clientid = sp->clientid;
36887c478bd9Sstevel@tonic-gate 
36897c478bd9Sstevel@tonic-gate 	/*
36907c478bd9Sstevel@tonic-gate 	 * Update the clientid for any other mi's belonging to sp.  This
36917c478bd9Sstevel@tonic-gate 	 * must be done here while we hold sp->s_lock, so that
36927c478bd9Sstevel@tonic-gate 	 * find_nfs4_server() continues to work.
36937c478bd9Sstevel@tonic-gate 	 */
36947c478bd9Sstevel@tonic-gate 
36957c478bd9Sstevel@tonic-gate 	for (tmi = sp->mntinfo4_list;
36967c478bd9Sstevel@tonic-gate 	    tmi != NULL;
36977c478bd9Sstevel@tonic-gate 	    tmi = tmi->mi_clientid_next) {
36987c478bd9Sstevel@tonic-gate 		if (tmi != mi) {
36997c478bd9Sstevel@tonic-gate 			tmi->mi_clientid = sp->clientid;
37007c478bd9Sstevel@tonic-gate 		}
37017c478bd9Sstevel@tonic-gate 	}
37027c478bd9Sstevel@tonic-gate }
37037c478bd9Sstevel@tonic-gate 
37047c478bd9Sstevel@tonic-gate /*
37057c478bd9Sstevel@tonic-gate  * Remove the mi from sp's mntinfo4_list and release its reference.
37067c478bd9Sstevel@tonic-gate  * Exception: if mi still has open files, flag it for later removal (when
37077c478bd9Sstevel@tonic-gate  * all the files are closed).
37087c478bd9Sstevel@tonic-gate  *
37097c478bd9Sstevel@tonic-gate  * If this is the last mntinfo4 in sp's list then tell the lease renewal
37107c478bd9Sstevel@tonic-gate  * thread to exit.
37117c478bd9Sstevel@tonic-gate  */
37127c478bd9Sstevel@tonic-gate static void
37137c478bd9Sstevel@tonic-gate nfs4_remove_mi_from_server_nolock(mntinfo4_t *mi, nfs4_server_t *sp)
37147c478bd9Sstevel@tonic-gate {
37157c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
3716b9238976Sth 	    "nfs4_remove_mi_from_server_nolock: remove mi %p from sp %p",
3717b9238976Sth 	    (void*)mi, (void*)sp));
37187c478bd9Sstevel@tonic-gate 
37197c478bd9Sstevel@tonic-gate 	ASSERT(sp != NULL);
37207c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
37217c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_open_files >= 0);
37227c478bd9Sstevel@tonic-gate 
37237c478bd9Sstevel@tonic-gate 	/*
37247c478bd9Sstevel@tonic-gate 	 * First make sure this mntinfo4 can be taken off of the list,
37257c478bd9Sstevel@tonic-gate 	 * ie: it doesn't have any open files remaining.
37267c478bd9Sstevel@tonic-gate 	 */
37277c478bd9Sstevel@tonic-gate 	if (mi->mi_open_files > 0) {
37287c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
3729b9238976Sth 		    "nfs4_remove_mi_from_server_nolock: don't "
3730b9238976Sth 		    "remove mi since it still has files open"));
37317c478bd9Sstevel@tonic-gate 
37327c478bd9Sstevel@tonic-gate 		mutex_enter(&mi->mi_lock);
37337c478bd9Sstevel@tonic-gate 		mi->mi_flags |= MI4_REMOVE_ON_LAST_CLOSE;
37347c478bd9Sstevel@tonic-gate 		mutex_exit(&mi->mi_lock);
37357c478bd9Sstevel@tonic-gate 		return;
37367c478bd9Sstevel@tonic-gate 	}
37377c478bd9Sstevel@tonic-gate 
373850a83466Sjwahlig 	VFS_HOLD(mi->mi_vfsp);
37397c478bd9Sstevel@tonic-gate 	remove_mi(sp, mi);
374050a83466Sjwahlig 	VFS_RELE(mi->mi_vfsp);
37417c478bd9Sstevel@tonic-gate 
37427c478bd9Sstevel@tonic-gate 	if (sp->mntinfo4_list == NULL) {
37437c478bd9Sstevel@tonic-gate 		/* last fs unmounted, kill the thread */
37447c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_lease_debug, (CE_NOTE,
3745b9238976Sth 		    "remove_mi_from_nfs4_server_nolock: kill the thread"));
37467c478bd9Sstevel@tonic-gate 		nfs4_mark_srv_dead(sp);
37477c478bd9Sstevel@tonic-gate 	}
37487c478bd9Sstevel@tonic-gate }
37497c478bd9Sstevel@tonic-gate 
37507c478bd9Sstevel@tonic-gate /*
37517c478bd9Sstevel@tonic-gate  * Remove mi from sp's mntinfo4_list and release the vfs reference.
37527c478bd9Sstevel@tonic-gate  */
37537c478bd9Sstevel@tonic-gate static void
37547c478bd9Sstevel@tonic-gate remove_mi(nfs4_server_t *sp, mntinfo4_t *mi)
37557c478bd9Sstevel@tonic-gate {
37567c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
37577c478bd9Sstevel@tonic-gate 
37587c478bd9Sstevel@tonic-gate 	/*
37597c478bd9Sstevel@tonic-gate 	 * We release a reference, and the caller must still have a
37607c478bd9Sstevel@tonic-gate 	 * reference.
37617c478bd9Sstevel@tonic-gate 	 */
37627c478bd9Sstevel@tonic-gate 	ASSERT(mi->mi_vfsp->vfs_count >= 2);
37637c478bd9Sstevel@tonic-gate 
37647c478bd9Sstevel@tonic-gate 	if (mi->mi_clientid_prev) {
37657c478bd9Sstevel@tonic-gate 		mi->mi_clientid_prev->mi_clientid_next = mi->mi_clientid_next;
37667c478bd9Sstevel@tonic-gate 	} else {
37677c478bd9Sstevel@tonic-gate 		/* This is the first mi in sp's mntinfo4_list */
37687c478bd9Sstevel@tonic-gate 		/*
37697c478bd9Sstevel@tonic-gate 		 * Make sure the first mntinfo4 in the list is the actual
37707c478bd9Sstevel@tonic-gate 		 * mntinfo4 passed in.
37717c478bd9Sstevel@tonic-gate 		 */
37727c478bd9Sstevel@tonic-gate 		ASSERT(sp->mntinfo4_list == mi);
37737c478bd9Sstevel@tonic-gate 
37747c478bd9Sstevel@tonic-gate 		sp->mntinfo4_list = mi->mi_clientid_next;
37757c478bd9Sstevel@tonic-gate 	}
37767c478bd9Sstevel@tonic-gate 	if (mi->mi_clientid_next)
37777c478bd9Sstevel@tonic-gate 		mi->mi_clientid_next->mi_clientid_prev = mi->mi_clientid_prev;
37787c478bd9Sstevel@tonic-gate 
37797c478bd9Sstevel@tonic-gate 	/* Now mark the mntinfo4's links as being removed */
37807c478bd9Sstevel@tonic-gate 	mi->mi_clientid_prev = mi->mi_clientid_next = NULL;
37813b895386SPavel Filipensky 	mi->mi_srv = NULL;
378222dc8f51SPavel Filipensky 	mi->mi_srvset_cnt++;
37837c478bd9Sstevel@tonic-gate 
37847c478bd9Sstevel@tonic-gate 	VFS_RELE(mi->mi_vfsp);
37857c478bd9Sstevel@tonic-gate }
37867c478bd9Sstevel@tonic-gate 
37877c478bd9Sstevel@tonic-gate /*
37887c478bd9Sstevel@tonic-gate  * Free all the entries in sp's mntinfo4_list.
37897c478bd9Sstevel@tonic-gate  */
37907c478bd9Sstevel@tonic-gate static void
37917c478bd9Sstevel@tonic-gate remove_all_mi(nfs4_server_t *sp)
37927c478bd9Sstevel@tonic-gate {
37937c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
37947c478bd9Sstevel@tonic-gate 
37957c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
37967c478bd9Sstevel@tonic-gate 
37977c478bd9Sstevel@tonic-gate 	while (sp->mntinfo4_list != NULL) {
37987c478bd9Sstevel@tonic-gate 		mi = sp->mntinfo4_list;
37997c478bd9Sstevel@tonic-gate 		/*
38007c478bd9Sstevel@tonic-gate 		 * Grab a reference in case there is only one left (which
38017c478bd9Sstevel@tonic-gate 		 * remove_mi() frees).
38027c478bd9Sstevel@tonic-gate 		 */
38037c478bd9Sstevel@tonic-gate 		VFS_HOLD(mi->mi_vfsp);
38047c478bd9Sstevel@tonic-gate 		remove_mi(sp, mi);
38057c478bd9Sstevel@tonic-gate 		VFS_RELE(mi->mi_vfsp);
38067c478bd9Sstevel@tonic-gate 	}
38077c478bd9Sstevel@tonic-gate }
38087c478bd9Sstevel@tonic-gate 
38097c478bd9Sstevel@tonic-gate /*
38107c478bd9Sstevel@tonic-gate  * Remove the mi from sp's mntinfo4_list as above, and rele the vfs.
38117c478bd9Sstevel@tonic-gate  *
38127c478bd9Sstevel@tonic-gate  * This version can be called with a null nfs4_server_t arg,
38137c478bd9Sstevel@tonic-gate  * and will either find the right one and handle locking, or
38147c478bd9Sstevel@tonic-gate  * do nothing because the mi wasn't added to an sp's mntinfo4_list.
38157c478bd9Sstevel@tonic-gate  */
38167c478bd9Sstevel@tonic-gate void
38177c478bd9Sstevel@tonic-gate nfs4_remove_mi_from_server(mntinfo4_t *mi, nfs4_server_t *esp)
38187c478bd9Sstevel@tonic-gate {
38197c478bd9Sstevel@tonic-gate 	nfs4_server_t	*sp;
38207c478bd9Sstevel@tonic-gate 
38213b895386SPavel Filipensky 	if (esp) {
38223b895386SPavel Filipensky 		nfs4_remove_mi_from_server_nolock(mi, esp);
38233b895386SPavel Filipensky 		return;
38243b895386SPavel Filipensky 	}
38257c478bd9Sstevel@tonic-gate 
38263b895386SPavel Filipensky 	(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, 0);
382722dc8f51SPavel Filipensky 	if (sp = find_nfs4_server_all(mi, 1)) {
38287c478bd9Sstevel@tonic-gate 		nfs4_remove_mi_from_server_nolock(mi, sp);
38293b895386SPavel Filipensky 		mutex_exit(&sp->s_lock);
383022dc8f51SPavel Filipensky 		nfs4_server_rele(sp);
38317c478bd9Sstevel@tonic-gate 	}
38323b895386SPavel Filipensky 	nfs_rw_exit(&mi->mi_recovlock);
38337c478bd9Sstevel@tonic-gate }
38347c478bd9Sstevel@tonic-gate 
38357c478bd9Sstevel@tonic-gate /*
38367c478bd9Sstevel@tonic-gate  * Return TRUE if the given server has any non-unmounted filesystems.
38377c478bd9Sstevel@tonic-gate  */
38387c478bd9Sstevel@tonic-gate 
38397c478bd9Sstevel@tonic-gate bool_t
38407c478bd9Sstevel@tonic-gate nfs4_fs_active(nfs4_server_t *sp)
38417c478bd9Sstevel@tonic-gate {
38427c478bd9Sstevel@tonic-gate 	mntinfo4_t *mi;
38437c478bd9Sstevel@tonic-gate 
38447c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
38457c478bd9Sstevel@tonic-gate 
38467c478bd9Sstevel@tonic-gate 	for (mi = sp->mntinfo4_list; mi != NULL; mi = mi->mi_clientid_next) {
38477c478bd9Sstevel@tonic-gate 		if (!(mi->mi_vfsp->vfs_flag & VFS_UNMOUNTED))
38487c478bd9Sstevel@tonic-gate 			return (TRUE);
38497c478bd9Sstevel@tonic-gate 	}
38507c478bd9Sstevel@tonic-gate 
38517c478bd9Sstevel@tonic-gate 	return (FALSE);
38527c478bd9Sstevel@tonic-gate }
38537c478bd9Sstevel@tonic-gate 
38547c478bd9Sstevel@tonic-gate /*
38557c478bd9Sstevel@tonic-gate  * Mark sp as finished and notify any waiters.
38567c478bd9Sstevel@tonic-gate  */
38577c478bd9Sstevel@tonic-gate 
38587c478bd9Sstevel@tonic-gate void
38597c478bd9Sstevel@tonic-gate nfs4_mark_srv_dead(nfs4_server_t *sp)
38607c478bd9Sstevel@tonic-gate {
38617c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
38627c478bd9Sstevel@tonic-gate 
38637c478bd9Sstevel@tonic-gate 	sp->s_thread_exit = NFS4_THREAD_EXIT;
38647c478bd9Sstevel@tonic-gate 	cv_broadcast(&sp->cv_thread_exit);
38657c478bd9Sstevel@tonic-gate }
38667c478bd9Sstevel@tonic-gate 
38677c478bd9Sstevel@tonic-gate /*
38687c478bd9Sstevel@tonic-gate  * Create a new nfs4_server_t structure.
38697c478bd9Sstevel@tonic-gate  * Returns new node unlocked and not in list, but with a reference count of
38707c478bd9Sstevel@tonic-gate  * 1.
38717c478bd9Sstevel@tonic-gate  */
38727c478bd9Sstevel@tonic-gate struct nfs4_server *
38737c478bd9Sstevel@tonic-gate new_nfs4_server(struct servinfo4 *svp, cred_t *cr)
38747c478bd9Sstevel@tonic-gate {
38757c478bd9Sstevel@tonic-gate 	struct nfs4_server *np;
38767c478bd9Sstevel@tonic-gate 	timespec_t tt;
38777c478bd9Sstevel@tonic-gate 	union {
38787c478bd9Sstevel@tonic-gate 		struct {
38797c478bd9Sstevel@tonic-gate 			uint32_t sec;
38807c478bd9Sstevel@tonic-gate 			uint32_t subsec;
38817c478bd9Sstevel@tonic-gate 		} un_curtime;
38827c478bd9Sstevel@tonic-gate 		verifier4	un_verifier;
38837c478bd9Sstevel@tonic-gate 	} nfs4clientid_verifier;
38842f172c55SRobert Thurlow 	/*
38852f172c55SRobert Thurlow 	 * We change this ID string carefully and with the Solaris
38862f172c55SRobert Thurlow 	 * NFS server behaviour in mind.  "+referrals" indicates
38872f172c55SRobert Thurlow 	 * a client that can handle an NFSv4 referral.
38882f172c55SRobert Thurlow 	 */
38892f172c55SRobert Thurlow 	char id_val[] = "Solaris: %s, NFSv4 kernel client +referrals";
38907c478bd9Sstevel@tonic-gate 	int len;
38917c478bd9Sstevel@tonic-gate 
38927c478bd9Sstevel@tonic-gate 	np = kmem_zalloc(sizeof (struct nfs4_server), KM_SLEEP);
38937c478bd9Sstevel@tonic-gate 	np->saddr.len = svp->sv_addr.len;
38947c478bd9Sstevel@tonic-gate 	np->saddr.maxlen = svp->sv_addr.maxlen;
38957c478bd9Sstevel@tonic-gate 	np->saddr.buf = kmem_alloc(svp->sv_addr.maxlen, KM_SLEEP);
38967c478bd9Sstevel@tonic-gate 	bcopy(svp->sv_addr.buf, np->saddr.buf, svp->sv_addr.len);
38977c478bd9Sstevel@tonic-gate 	np->s_refcnt = 1;
38987c478bd9Sstevel@tonic-gate 
38997c478bd9Sstevel@tonic-gate 	/*
39007c478bd9Sstevel@tonic-gate 	 * Build the nfs_client_id4 for this server mount.  Ensure
39017c478bd9Sstevel@tonic-gate 	 * the verifier is useful and that the identification is
39027c478bd9Sstevel@tonic-gate 	 * somehow based on the server's address for the case of
39037c478bd9Sstevel@tonic-gate 	 * multi-homed servers.
39047c478bd9Sstevel@tonic-gate 	 */
39057c478bd9Sstevel@tonic-gate 	nfs4clientid_verifier.un_verifier = 0;
39067c478bd9Sstevel@tonic-gate 	gethrestime(&tt);
39077c478bd9Sstevel@tonic-gate 	nfs4clientid_verifier.un_curtime.sec = (uint32_t)tt.tv_sec;
39087c478bd9Sstevel@tonic-gate 	nfs4clientid_verifier.un_curtime.subsec = (uint32_t)tt.tv_nsec;
39097c478bd9Sstevel@tonic-gate 	np->clidtosend.verifier = nfs4clientid_verifier.un_verifier;
39107c478bd9Sstevel@tonic-gate 
39117c478bd9Sstevel@tonic-gate 	/*
39127c478bd9Sstevel@tonic-gate 	 * calculate the length of the opaque identifier.  Subtract 2
39137c478bd9Sstevel@tonic-gate 	 * for the "%s" and add the traditional +1 for null
39147c478bd9Sstevel@tonic-gate 	 * termination.
39157c478bd9Sstevel@tonic-gate 	 */
39167c478bd9Sstevel@tonic-gate 	len = strlen(id_val) - 2 + strlen(uts_nodename()) + 1;
39177c478bd9Sstevel@tonic-gate 	np->clidtosend.id_len = len + np->saddr.maxlen;
39187c478bd9Sstevel@tonic-gate 
39197c478bd9Sstevel@tonic-gate 	np->clidtosend.id_val = kmem_alloc(np->clidtosend.id_len, KM_SLEEP);
39207c478bd9Sstevel@tonic-gate 	(void) sprintf(np->clidtosend.id_val, id_val, uts_nodename());
39217c478bd9Sstevel@tonic-gate 	bcopy(np->saddr.buf, &np->clidtosend.id_val[len], np->saddr.len);
39227c478bd9Sstevel@tonic-gate 
39237c478bd9Sstevel@tonic-gate 	np->s_flags = 0;
39247c478bd9Sstevel@tonic-gate 	np->mntinfo4_list = NULL;
39257c478bd9Sstevel@tonic-gate 	/* save cred for issuing rfs4calls inside the renew thread */
39267c478bd9Sstevel@tonic-gate 	crhold(cr);
39277c478bd9Sstevel@tonic-gate 	np->s_cred = cr;
39287c478bd9Sstevel@tonic-gate 	cv_init(&np->cv_thread_exit, NULL, CV_DEFAULT, NULL);
39297c478bd9Sstevel@tonic-gate 	mutex_init(&np->s_lock, NULL, MUTEX_DEFAULT, NULL);
39307c478bd9Sstevel@tonic-gate 	nfs_rw_init(&np->s_recovlock, NULL, RW_DEFAULT, NULL);
39317c478bd9Sstevel@tonic-gate 	list_create(&np->s_deleg_list, sizeof (rnode4_t),
39327c478bd9Sstevel@tonic-gate 	    offsetof(rnode4_t, r_deleg_link));
39337c478bd9Sstevel@tonic-gate 	np->s_thread_exit = 0;
39347c478bd9Sstevel@tonic-gate 	np->state_ref_count = 0;
39357c478bd9Sstevel@tonic-gate 	np->lease_valid = NFS4_LEASE_NOT_STARTED;
39367c478bd9Sstevel@tonic-gate 	cv_init(&np->s_cv_otw_count, NULL, CV_DEFAULT, NULL);
3937f86c6ccaSdm 	cv_init(&np->s_clientid_pend, NULL, CV_DEFAULT, NULL);
39387c478bd9Sstevel@tonic-gate 	np->s_otw_call_count = 0;
39397c478bd9Sstevel@tonic-gate 	cv_init(&np->wait_cb_null, NULL, CV_DEFAULT, NULL);
39407c478bd9Sstevel@tonic-gate 	np->zoneid = getzoneid();
39417c478bd9Sstevel@tonic-gate 	np->zone_globals = nfs4_get_callback_globals();
39427c478bd9Sstevel@tonic-gate 	ASSERT(np->zone_globals != NULL);
39437c478bd9Sstevel@tonic-gate 	return (np);
39447c478bd9Sstevel@tonic-gate }
39457c478bd9Sstevel@tonic-gate 
39467c478bd9Sstevel@tonic-gate /*
39477c478bd9Sstevel@tonic-gate  * Create a new nfs4_server_t structure and add it to the list.
39487c478bd9Sstevel@tonic-gate  * Returns new node locked; reference must eventually be freed.
39497c478bd9Sstevel@tonic-gate  */
39507c478bd9Sstevel@tonic-gate static struct nfs4_server *
39517c478bd9Sstevel@tonic-gate add_new_nfs4_server(struct servinfo4 *svp, cred_t *cr)
39527c478bd9Sstevel@tonic-gate {
39537c478bd9Sstevel@tonic-gate 	nfs4_server_t *sp;
39547c478bd9Sstevel@tonic-gate 
39557c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&nfs4_server_lst_lock));
39567c478bd9Sstevel@tonic-gate 	sp = new_nfs4_server(svp, cr);
39577c478bd9Sstevel@tonic-gate 	mutex_enter(&sp->s_lock);
39587c478bd9Sstevel@tonic-gate 	insque(sp, &nfs4_server_lst);
39597c478bd9Sstevel@tonic-gate 	sp->s_refcnt++;			/* list gets a reference */
3960f64c4ae1Sdm 	sp->s_flags |= N4S_INSERTED;
39617c478bd9Sstevel@tonic-gate 	sp->clientid = 0;
39627c478bd9Sstevel@tonic-gate 	return (sp);
39637c478bd9Sstevel@tonic-gate }
39647c478bd9Sstevel@tonic-gate 
39657c478bd9Sstevel@tonic-gate int nfs4_server_t_debug = 0;
39667c478bd9Sstevel@tonic-gate 
39677c478bd9Sstevel@tonic-gate #ifdef lint
39687c478bd9Sstevel@tonic-gate extern void
39697c478bd9Sstevel@tonic-gate dumpnfs4slist(char *, mntinfo4_t *, clientid4, servinfo4_t *);
39707c478bd9Sstevel@tonic-gate #endif
39717c478bd9Sstevel@tonic-gate 
39727c478bd9Sstevel@tonic-gate #ifndef lint
39737c478bd9Sstevel@tonic-gate #ifdef DEBUG
39747c478bd9Sstevel@tonic-gate void
39757c478bd9Sstevel@tonic-gate dumpnfs4slist(char *txt, mntinfo4_t *mi, clientid4 clientid, servinfo4_t *srv_p)
39767c478bd9Sstevel@tonic-gate {
39777c478bd9Sstevel@tonic-gate 	int hash16(void *p, int len);
39787c478bd9Sstevel@tonic-gate 	nfs4_server_t *np;
39797c478bd9Sstevel@tonic-gate 
39807c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_server_t_debug, (CE_NOTE,
39817c478bd9Sstevel@tonic-gate 	    "dumping nfs4_server_t list in %s", txt));
39827c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
39837c478bd9Sstevel@tonic-gate 	    "mi 0x%p, want clientid %llx, addr %d/%04X",
39847c478bd9Sstevel@tonic-gate 	    mi, (longlong_t)clientid, srv_p->sv_addr.len,
39857c478bd9Sstevel@tonic-gate 	    hash16((void *)srv_p->sv_addr.buf, srv_p->sv_addr.len)));
39867c478bd9Sstevel@tonic-gate 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst;
39877c478bd9Sstevel@tonic-gate 	    np = np->forw) {
39887c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
39897c478bd9Sstevel@tonic-gate 		    "node 0x%p,    clientid %llx, addr %d/%04X, cnt %d",
39907c478bd9Sstevel@tonic-gate 		    np, (longlong_t)np->clientid, np->saddr.len,
39917c478bd9Sstevel@tonic-gate 		    hash16((void *)np->saddr.buf, np->saddr.len),
39927c478bd9Sstevel@tonic-gate 		    np->state_ref_count));
39937c478bd9Sstevel@tonic-gate 		if (np->saddr.len == srv_p->sv_addr.len &&
39947c478bd9Sstevel@tonic-gate 		    bcmp(np->saddr.buf, srv_p->sv_addr.buf,
39957c478bd9Sstevel@tonic-gate 		    np->saddr.len) == 0)
39967c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
39977c478bd9Sstevel@tonic-gate 			    " - address matches"));
39987c478bd9Sstevel@tonic-gate 		if (np->clientid == clientid || np->clientid == 0)
39997c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
40007c478bd9Sstevel@tonic-gate 			    " - clientid matches"));
40017c478bd9Sstevel@tonic-gate 		if (np->s_thread_exit != NFS4_THREAD_EXIT)
40027c478bd9Sstevel@tonic-gate 			NFS4_DEBUG(nfs4_server_t_debug, (CE_CONT,
40037c478bd9Sstevel@tonic-gate 			    " - thread not exiting"));
40047c478bd9Sstevel@tonic-gate 	}
40057c478bd9Sstevel@tonic-gate 	delay(hz);
40067c478bd9Sstevel@tonic-gate }
40077c478bd9Sstevel@tonic-gate #endif
40087c478bd9Sstevel@tonic-gate #endif
40097c478bd9Sstevel@tonic-gate 
40107c478bd9Sstevel@tonic-gate 
40117c478bd9Sstevel@tonic-gate /*
40127c478bd9Sstevel@tonic-gate  * Move a mntinfo4_t from one server list to another.
40137c478bd9Sstevel@tonic-gate  * Locking of the two nfs4_server_t nodes will be done in list order.
40147c478bd9Sstevel@tonic-gate  *
40157c478bd9Sstevel@tonic-gate  * Returns NULL if the current nfs4_server_t for the filesystem could not
40167c478bd9Sstevel@tonic-gate  * be found (e.g., due to forced unmount).  Otherwise returns a reference
40177c478bd9Sstevel@tonic-gate  * to the new nfs4_server_t, which must eventually be freed.
40187c478bd9Sstevel@tonic-gate  */
40197c478bd9Sstevel@tonic-gate nfs4_server_t *
40207c478bd9Sstevel@tonic-gate nfs4_move_mi(mntinfo4_t *mi, servinfo4_t *old, servinfo4_t *new)
40217c478bd9Sstevel@tonic-gate {
40227c478bd9Sstevel@tonic-gate 	nfs4_server_t *p, *op = NULL, *np = NULL;
40237c478bd9Sstevel@tonic-gate 	int num_open;
4024108322fbScarlsonj 	zoneid_t zoneid = nfs_zoneid();
40257c478bd9Sstevel@tonic-gate 
4026108322fbScarlsonj 	ASSERT(nfs_zone() == mi->mi_zone);
40277c478bd9Sstevel@tonic-gate 
40287c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_server_lst_lock);
40297c478bd9Sstevel@tonic-gate #ifdef DEBUG
40307c478bd9Sstevel@tonic-gate 	if (nfs4_server_t_debug)
40317c478bd9Sstevel@tonic-gate 		dumpnfs4slist("nfs4_move_mi", mi, (clientid4)0, new);
40327c478bd9Sstevel@tonic-gate #endif
40337c478bd9Sstevel@tonic-gate 	for (p = nfs4_server_lst.forw; p != &nfs4_server_lst; p = p->forw) {
40347c478bd9Sstevel@tonic-gate 		if (p->zoneid != zoneid)
40357c478bd9Sstevel@tonic-gate 			continue;
40367c478bd9Sstevel@tonic-gate 		if (p->saddr.len == old->sv_addr.len &&
40377c478bd9Sstevel@tonic-gate 		    bcmp(p->saddr.buf, old->sv_addr.buf, p->saddr.len) == 0 &&
40387c478bd9Sstevel@tonic-gate 		    p->s_thread_exit != NFS4_THREAD_EXIT) {
40397c478bd9Sstevel@tonic-gate 			op = p;
40407c478bd9Sstevel@tonic-gate 			mutex_enter(&op->s_lock);
40417c478bd9Sstevel@tonic-gate 			op->s_refcnt++;
40427c478bd9Sstevel@tonic-gate 		}
40437c478bd9Sstevel@tonic-gate 		if (p->saddr.len == new->sv_addr.len &&
40447c478bd9Sstevel@tonic-gate 		    bcmp(p->saddr.buf, new->sv_addr.buf, p->saddr.len) == 0 &&
40457c478bd9Sstevel@tonic-gate 		    p->s_thread_exit != NFS4_THREAD_EXIT) {
40467c478bd9Sstevel@tonic-gate 			np = p;
40477c478bd9Sstevel@tonic-gate 			mutex_enter(&np->s_lock);
40487c478bd9Sstevel@tonic-gate 		}
40497c478bd9Sstevel@tonic-gate 		if (op != NULL && np != NULL)
40507c478bd9Sstevel@tonic-gate 			break;
40517c478bd9Sstevel@tonic-gate 	}
40527c478bd9Sstevel@tonic-gate 	if (op == NULL) {
40537c478bd9Sstevel@tonic-gate 		/*
40547c478bd9Sstevel@tonic-gate 		 * Filesystem has been forcibly unmounted.  Bail out.
40557c478bd9Sstevel@tonic-gate 		 */
40567c478bd9Sstevel@tonic-gate 		if (np != NULL)
40577c478bd9Sstevel@tonic-gate 			mutex_exit(&np->s_lock);
40587c478bd9Sstevel@tonic-gate 		mutex_exit(&nfs4_server_lst_lock);
40597c478bd9Sstevel@tonic-gate 		return (NULL);
40607c478bd9Sstevel@tonic-gate 	}
40617c478bd9Sstevel@tonic-gate 	if (np != NULL) {
40627c478bd9Sstevel@tonic-gate 		np->s_refcnt++;
40637c478bd9Sstevel@tonic-gate 	} else {
40647c478bd9Sstevel@tonic-gate #ifdef DEBUG
40657c478bd9Sstevel@tonic-gate 		NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
40667c478bd9Sstevel@tonic-gate 		    "nfs4_move_mi: no target nfs4_server, will create."));
40677c478bd9Sstevel@tonic-gate #endif
40687c478bd9Sstevel@tonic-gate 		np = add_new_nfs4_server(new, kcred);
40697c478bd9Sstevel@tonic-gate 	}
40707c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_server_lst_lock);
40717c478bd9Sstevel@tonic-gate 
40727c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
40737c478bd9Sstevel@tonic-gate 	    "nfs4_move_mi: for mi 0x%p, "
40747c478bd9Sstevel@tonic-gate 	    "old servinfo4 0x%p, new servinfo4 0x%p, "
40757c478bd9Sstevel@tonic-gate 	    "old nfs4_server 0x%p, new nfs4_server 0x%p, ",
40767c478bd9Sstevel@tonic-gate 	    (void*)mi, (void*)old, (void*)new,
40777c478bd9Sstevel@tonic-gate 	    (void*)op, (void*)np));
40787c478bd9Sstevel@tonic-gate 	ASSERT(op != NULL && np != NULL);
40797c478bd9Sstevel@tonic-gate 
40807c478bd9Sstevel@tonic-gate 	/* discard any delegations */
40817c478bd9Sstevel@tonic-gate 	nfs4_deleg_discard(mi, op);
40827c478bd9Sstevel@tonic-gate 
40837c478bd9Sstevel@tonic-gate 	num_open = mi->mi_open_files;
40847c478bd9Sstevel@tonic-gate 	mi->mi_open_files = 0;
40857c478bd9Sstevel@tonic-gate 	op->state_ref_count -= num_open;
40867c478bd9Sstevel@tonic-gate 	ASSERT(op->state_ref_count >= 0);
40877c478bd9Sstevel@tonic-gate 	np->state_ref_count += num_open;
40887c478bd9Sstevel@tonic-gate 	nfs4_remove_mi_from_server_nolock(mi, op);
40897c478bd9Sstevel@tonic-gate 	mi->mi_open_files = num_open;
40907c478bd9Sstevel@tonic-gate 	NFS4_DEBUG(nfs4_client_failover_debug, (CE_NOTE,
40917c478bd9Sstevel@tonic-gate 	    "nfs4_move_mi: mi_open_files %d, op->cnt %d, np->cnt %d",
40927c478bd9Sstevel@tonic-gate 	    mi->mi_open_files, op->state_ref_count, np->state_ref_count));
40937c478bd9Sstevel@tonic-gate 
40947c478bd9Sstevel@tonic-gate 	nfs4_add_mi_to_server(np, mi);
40957c478bd9Sstevel@tonic-gate 
40967c478bd9Sstevel@tonic-gate 	mutex_exit(&op->s_lock);
40977c478bd9Sstevel@tonic-gate 	mutex_exit(&np->s_lock);
409895c7fa91SPavel Filipensky 	nfs4_server_rele(op);
40997c478bd9Sstevel@tonic-gate 
41007c478bd9Sstevel@tonic-gate 	return (np);
41017c478bd9Sstevel@tonic-gate }
41027c478bd9Sstevel@tonic-gate 
41037c478bd9Sstevel@tonic-gate /*
4104f86c6ccaSdm  * Need to have the nfs4_server_lst_lock.
41057c478bd9Sstevel@tonic-gate  * Search the nfs4_server list to find a match on this servinfo4
41067c478bd9Sstevel@tonic-gate  * based on its address.
41077c478bd9Sstevel@tonic-gate  *
41087c478bd9Sstevel@tonic-gate  * Returns NULL if no match is found.  Otherwise returns a reference (which
41097c478bd9Sstevel@tonic-gate  * must eventually be freed) to a locked nfs4_server.
41107c478bd9Sstevel@tonic-gate  */
41117c478bd9Sstevel@tonic-gate nfs4_server_t *
41127c478bd9Sstevel@tonic-gate servinfo4_to_nfs4_server(servinfo4_t *srv_p)
41137c478bd9Sstevel@tonic-gate {
41147c478bd9Sstevel@tonic-gate 	nfs4_server_t *np;
4115108322fbScarlsonj 	zoneid_t zoneid = nfs_zoneid();
41167c478bd9Sstevel@tonic-gate 
4117f86c6ccaSdm 	ASSERT(MUTEX_HELD(&nfs4_server_lst_lock));
41187c478bd9Sstevel@tonic-gate 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst; np = np->forw) {
41197c478bd9Sstevel@tonic-gate 		if (np->zoneid == zoneid &&
41207c478bd9Sstevel@tonic-gate 		    np->saddr.len == srv_p->sv_addr.len &&
41217c478bd9Sstevel@tonic-gate 		    bcmp(np->saddr.buf, srv_p->sv_addr.buf,
4122b9238976Sth 		    np->saddr.len) == 0 &&
41237c478bd9Sstevel@tonic-gate 		    np->s_thread_exit != NFS4_THREAD_EXIT) {
41247c478bd9Sstevel@tonic-gate 			mutex_enter(&np->s_lock);
41257c478bd9Sstevel@tonic-gate 			np->s_refcnt++;
41267c478bd9Sstevel@tonic-gate 			return (np);
41277c478bd9Sstevel@tonic-gate 		}
41287c478bd9Sstevel@tonic-gate 	}
41297c478bd9Sstevel@tonic-gate 	return (NULL);
41307c478bd9Sstevel@tonic-gate }
41317c478bd9Sstevel@tonic-gate 
41327c478bd9Sstevel@tonic-gate /*
41337c478bd9Sstevel@tonic-gate  * Locks the nfs4_server down if it is found and returns a reference that
41347c478bd9Sstevel@tonic-gate  * must eventually be freed.
413522dc8f51SPavel Filipensky  */
413622dc8f51SPavel Filipensky static nfs4_server_t *
413722dc8f51SPavel Filipensky lookup_nfs4_server(nfs4_server_t *sp, int any_state)
413822dc8f51SPavel Filipensky {
413922dc8f51SPavel Filipensky 	nfs4_server_t *np;
414022dc8f51SPavel Filipensky 
414122dc8f51SPavel Filipensky 	mutex_enter(&nfs4_server_lst_lock);
414222dc8f51SPavel Filipensky 	for (np = nfs4_server_lst.forw; np != &nfs4_server_lst; np = np->forw) {
414322dc8f51SPavel Filipensky 		mutex_enter(&np->s_lock);
414422dc8f51SPavel Filipensky 		if (np == sp && np->s_refcnt > 0 &&
414522dc8f51SPavel Filipensky 		    (np->s_thread_exit != NFS4_THREAD_EXIT || any_state)) {
414622dc8f51SPavel Filipensky 			mutex_exit(&nfs4_server_lst_lock);
414722dc8f51SPavel Filipensky 			np->s_refcnt++;
414822dc8f51SPavel Filipensky 			return (np);
414922dc8f51SPavel Filipensky 		}
415022dc8f51SPavel Filipensky 		mutex_exit(&np->s_lock);
415122dc8f51SPavel Filipensky 	}
415222dc8f51SPavel Filipensky 	mutex_exit(&nfs4_server_lst_lock);
415322dc8f51SPavel Filipensky 
415422dc8f51SPavel Filipensky 	return (NULL);
415522dc8f51SPavel Filipensky }
415622dc8f51SPavel Filipensky 
415722dc8f51SPavel Filipensky /*
41587c478bd9Sstevel@tonic-gate  * The caller should be holding mi->mi_recovlock, and it should continue to
41597c478bd9Sstevel@tonic-gate  * hold the lock until done with the returned nfs4_server_t.  Once
41607c478bd9Sstevel@tonic-gate  * mi->mi_recovlock is released, there is no guarantee that the returned
41617c478bd9Sstevel@tonic-gate  * mi->nfs4_server_t will continue to correspond to mi.
41627c478bd9Sstevel@tonic-gate  */
41637c478bd9Sstevel@tonic-gate nfs4_server_t *
41647c478bd9Sstevel@tonic-gate find_nfs4_server(mntinfo4_t *mi)
41657c478bd9Sstevel@tonic-gate {
416622dc8f51SPavel Filipensky 	ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) ||
416722dc8f51SPavel Filipensky 	    nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER));
416822dc8f51SPavel Filipensky 
416922dc8f51SPavel Filipensky 	return (lookup_nfs4_server(mi->mi_srv, 0));
41707c478bd9Sstevel@tonic-gate }
41717c478bd9Sstevel@tonic-gate 
41727c478bd9Sstevel@tonic-gate /*
417322dc8f51SPavel Filipensky  * Same as above, but takes an "any_state" parameter which can be
41747c478bd9Sstevel@tonic-gate  * set to 1 if the caller wishes to find nfs4_server_t's which
41757c478bd9Sstevel@tonic-gate  * have been marked for termination by the exit of the renew
41767c478bd9Sstevel@tonic-gate  * thread.  This should only be used by operations which are
41777c478bd9Sstevel@tonic-gate  * cleaning up and will not cause an OTW op.
41787c478bd9Sstevel@tonic-gate  */
41797c478bd9Sstevel@tonic-gate nfs4_server_t *
418022dc8f51SPavel Filipensky find_nfs4_server_all(mntinfo4_t *mi, int any_state)
41817c478bd9Sstevel@tonic-gate {
41827c478bd9Sstevel@tonic-gate 	ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) ||
41837c478bd9Sstevel@tonic-gate 	    nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER));
41847c478bd9Sstevel@tonic-gate 
418522dc8f51SPavel Filipensky 	return (lookup_nfs4_server(mi->mi_srv, any_state));
418622dc8f51SPavel Filipensky }
418722dc8f51SPavel Filipensky 
418822dc8f51SPavel Filipensky /*
418922dc8f51SPavel Filipensky  * Lock sp, but only if it's still active (in the list and hasn't been
419022dc8f51SPavel Filipensky  * flagged as exiting) or 'any_state' is non-zero.
419122dc8f51SPavel Filipensky  * Returns TRUE if sp got locked and adds a reference to sp.
419222dc8f51SPavel Filipensky  */
419322dc8f51SPavel Filipensky bool_t
419422dc8f51SPavel Filipensky nfs4_server_vlock(nfs4_server_t *sp, int any_state)
419522dc8f51SPavel Filipensky {
419622dc8f51SPavel Filipensky 	return (lookup_nfs4_server(sp, any_state) != NULL);
41977c478bd9Sstevel@tonic-gate }
41987c478bd9Sstevel@tonic-gate 
41997c478bd9Sstevel@tonic-gate /*
42007c478bd9Sstevel@tonic-gate  * Release the reference to sp and destroy it if that's the last one.
42017c478bd9Sstevel@tonic-gate  */
42027c478bd9Sstevel@tonic-gate 
42037c478bd9Sstevel@tonic-gate void
42047c478bd9Sstevel@tonic-gate nfs4_server_rele(nfs4_server_t *sp)
42057c478bd9Sstevel@tonic-gate {
42067c478bd9Sstevel@tonic-gate 	mutex_enter(&sp->s_lock);
42077c478bd9Sstevel@tonic-gate 	ASSERT(sp->s_refcnt > 0);
42087c478bd9Sstevel@tonic-gate 	sp->s_refcnt--;
42097c478bd9Sstevel@tonic-gate 	if (sp->s_refcnt > 0) {
42107c478bd9Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
42117c478bd9Sstevel@tonic-gate 		return;
42127c478bd9Sstevel@tonic-gate 	}
42137c478bd9Sstevel@tonic-gate 	mutex_exit(&sp->s_lock);
4214f86c6ccaSdm 
42157c478bd9Sstevel@tonic-gate 	mutex_enter(&nfs4_server_lst_lock);
42167c478bd9Sstevel@tonic-gate 	mutex_enter(&sp->s_lock);
42177c478bd9Sstevel@tonic-gate 	if (sp->s_refcnt > 0) {
42187c478bd9Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
42197c478bd9Sstevel@tonic-gate 		mutex_exit(&nfs4_server_lst_lock);
42207c478bd9Sstevel@tonic-gate 		return;
42217c478bd9Sstevel@tonic-gate 	}
4222f86c6ccaSdm 	remque(sp);
4223f86c6ccaSdm 	sp->forw = sp->back = NULL;
42247c478bd9Sstevel@tonic-gate 	mutex_exit(&nfs4_server_lst_lock);
42257c478bd9Sstevel@tonic-gate 	destroy_nfs4_server(sp);
42267c478bd9Sstevel@tonic-gate }
42277c478bd9Sstevel@tonic-gate 
42287c478bd9Sstevel@tonic-gate static void
42297c478bd9Sstevel@tonic-gate destroy_nfs4_server(nfs4_server_t *sp)
42307c478bd9Sstevel@tonic-gate {
42317c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sp->s_lock));
42327c478bd9Sstevel@tonic-gate 	ASSERT(sp->s_refcnt == 0);
42337c478bd9Sstevel@tonic-gate 	ASSERT(sp->s_otw_call_count == 0);
42347c478bd9Sstevel@tonic-gate 
42357c478bd9Sstevel@tonic-gate 	remove_all_mi(sp);
42367c478bd9Sstevel@tonic-gate 
42377c478bd9Sstevel@tonic-gate 	crfree(sp->s_cred);
42387c478bd9Sstevel@tonic-gate 	kmem_free(sp->saddr.buf, sp->saddr.maxlen);
42397c478bd9Sstevel@tonic-gate 	kmem_free(sp->clidtosend.id_val, sp->clidtosend.id_len);
42407c478bd9Sstevel@tonic-gate 	mutex_exit(&sp->s_lock);
42417c478bd9Sstevel@tonic-gate 
42427c478bd9Sstevel@tonic-gate 	/* destroy the nfs4_server */
42437c478bd9Sstevel@tonic-gate 	nfs4callback_destroy(sp);
42447c478bd9Sstevel@tonic-gate 	list_destroy(&sp->s_deleg_list);
42457c478bd9Sstevel@tonic-gate 	mutex_destroy(&sp->s_lock);
42467c478bd9Sstevel@tonic-gate 	cv_destroy(&sp->cv_thread_exit);
42477c478bd9Sstevel@tonic-gate 	cv_destroy(&sp->s_cv_otw_count);
4248f86c6ccaSdm 	cv_destroy(&sp->s_clientid_pend);
42497c478bd9Sstevel@tonic-gate 	cv_destroy(&sp->wait_cb_null);
42507c478bd9Sstevel@tonic-gate 	nfs_rw_destroy(&sp->s_recovlock);
42517c478bd9Sstevel@tonic-gate 	kmem_free(sp, sizeof (*sp));
42527c478bd9Sstevel@tonic-gate }
42537c478bd9Sstevel@tonic-gate 
42547c478bd9Sstevel@tonic-gate /*
42557c478bd9Sstevel@tonic-gate  * Fork off a thread to free the data structures for a mount.
42567c478bd9Sstevel@tonic-gate  */
42577c478bd9Sstevel@tonic-gate 
42587c478bd9Sstevel@tonic-gate static void
4259b9238976Sth async_free_mount(vfs_t *vfsp, int flag, cred_t *cr)
42607c478bd9Sstevel@tonic-gate {
42617c478bd9Sstevel@tonic-gate 	freemountargs_t *args;
42627c478bd9Sstevel@tonic-gate 	args = kmem_alloc(sizeof (freemountargs_t), KM_SLEEP);
42637c478bd9Sstevel@tonic-gate 	args->fm_vfsp = vfsp;
42647c478bd9Sstevel@tonic-gate 	VFS_HOLD(vfsp);
426550a83466Sjwahlig 	MI4_HOLD(VFTOMI4(vfsp));
4266b9238976Sth 	args->fm_flag = flag;
42677c478bd9Sstevel@tonic-gate 	args->fm_cr = cr;
42687c478bd9Sstevel@tonic-gate 	crhold(cr);
42697c478bd9Sstevel@tonic-gate 	(void) zthread_create(NULL, 0, nfs4_free_mount_thread, args, 0,
42707c478bd9Sstevel@tonic-gate 	    minclsyspri);
42717c478bd9Sstevel@tonic-gate }
42727c478bd9Sstevel@tonic-gate 
42737c478bd9Sstevel@tonic-gate static void
42747c478bd9Sstevel@tonic-gate nfs4_free_mount_thread(freemountargs_t *args)
42757c478bd9Sstevel@tonic-gate {
427650a83466Sjwahlig 	mntinfo4_t *mi;
4277b9238976Sth 	nfs4_free_mount(args->fm_vfsp, args->fm_flag, args->fm_cr);
427850a83466Sjwahlig 	mi = VFTOMI4(args->fm_vfsp);
42797c478bd9Sstevel@tonic-gate 	crfree(args->fm_cr);
428050a83466Sjwahlig 	VFS_RELE(args->fm_vfsp);
428150a83466Sjwahlig 	MI4_RELE(mi);
42827c478bd9Sstevel@tonic-gate 	kmem_free(args, sizeof (freemountargs_t));
42837c478bd9Sstevel@tonic-gate 	zthread_exit();
42847c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
42857c478bd9Sstevel@tonic-gate }
42867c478bd9Sstevel@tonic-gate 
42877c478bd9Sstevel@tonic-gate /*
42887c478bd9Sstevel@tonic-gate  * Thread to free the data structures for a given filesystem.
42897c478bd9Sstevel@tonic-gate  */
42907c478bd9Sstevel@tonic-gate static void
4291b9238976Sth nfs4_free_mount(vfs_t *vfsp, int flag, cred_t *cr)
42927c478bd9Sstevel@tonic-gate {
4293b9238976Sth 	mntinfo4_t		*mi = VFTOMI4(vfsp);
4294b9238976Sth 	nfs4_server_t		*sp;
4295b9238976Sth 	callb_cpr_t		cpr_info;
4296b9238976Sth 	kmutex_t		cpr_lock;
4297b9238976Sth 	boolean_t		async_thread;
4298b9238976Sth 	int			removed;
4299b9238976Sth 
4300d3a14591SThomas Haynes 	bool_t			must_unlock;
4301b9238976Sth 	nfs4_ephemeral_tree_t	*eph_tree;
43027c478bd9Sstevel@tonic-gate 
43037c478bd9Sstevel@tonic-gate 	/*
43047c478bd9Sstevel@tonic-gate 	 * We need to participate in the CPR framework if this is a kernel
43057c478bd9Sstevel@tonic-gate 	 * thread.
43067c478bd9Sstevel@tonic-gate 	 */
4307108322fbScarlsonj 	async_thread = (curproc == nfs_zone()->zone_zsched);
43087c478bd9Sstevel@tonic-gate 	if (async_thread) {
43097c478bd9Sstevel@tonic-gate 		mutex_init(&cpr_lock, NULL, MUTEX_DEFAULT, NULL);
43107c478bd9Sstevel@tonic-gate 		CALLB_CPR_INIT(&cpr_info, &cpr_lock, callb_generic_cpr,
43117c478bd9Sstevel@tonic-gate 		    "nfsv4AsyncUnmount");
43127c478bd9Sstevel@tonic-gate 	}
43137c478bd9Sstevel@tonic-gate 
43147c478bd9Sstevel@tonic-gate 	/*
43157c478bd9Sstevel@tonic-gate 	 * We need to wait for all outstanding OTW calls
43167c478bd9Sstevel@tonic-gate 	 * and recovery to finish before we remove the mi
43177c478bd9Sstevel@tonic-gate 	 * from the nfs4_server_t, as current pending
43187c478bd9Sstevel@tonic-gate 	 * calls might still need this linkage (in order
43197c478bd9Sstevel@tonic-gate 	 * to find a nfs4_server_t from a mntinfo4_t).
43207c478bd9Sstevel@tonic-gate 	 */
43217c478bd9Sstevel@tonic-gate 	(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, FALSE);
43227c478bd9Sstevel@tonic-gate 	sp = find_nfs4_server(mi);
43237c478bd9Sstevel@tonic-gate 	nfs_rw_exit(&mi->mi_recovlock);
43247c478bd9Sstevel@tonic-gate 
43257c478bd9Sstevel@tonic-gate 	if (sp) {
43267c478bd9Sstevel@tonic-gate 		while (sp->s_otw_call_count != 0) {
43277c478bd9Sstevel@tonic-gate 			if (async_thread) {
43287c478bd9Sstevel@tonic-gate 				mutex_enter(&cpr_lock);
43297c478bd9Sstevel@tonic-gate 				CALLB_CPR_SAFE_BEGIN(&cpr_info);
43307c478bd9Sstevel@tonic-gate 				mutex_exit(&cpr_lock);
43317c478bd9Sstevel@tonic-gate 			}
43327c478bd9Sstevel@tonic-gate 			cv_wait(&sp->s_cv_otw_count, &sp->s_lock);
43337c478bd9Sstevel@tonic-gate 			if (async_thread) {
43347c478bd9Sstevel@tonic-gate 				mutex_enter(&cpr_lock);
43357c478bd9Sstevel@tonic-gate 				CALLB_CPR_SAFE_END(&cpr_info, &cpr_lock);
43367c478bd9Sstevel@tonic-gate 				mutex_exit(&cpr_lock);
43377c478bd9Sstevel@tonic-gate 			}
43387c478bd9Sstevel@tonic-gate 		}
43397c478bd9Sstevel@tonic-gate 		mutex_exit(&sp->s_lock);
43407c478bd9Sstevel@tonic-gate 		nfs4_server_rele(sp);
43417c478bd9Sstevel@tonic-gate 		sp = NULL;
43427c478bd9Sstevel@tonic-gate 	}
43437c478bd9Sstevel@tonic-gate 
43447c478bd9Sstevel@tonic-gate 	mutex_enter(&mi->mi_lock);
43457c478bd9Sstevel@tonic-gate 	while (mi->mi_in_recovery != 0) {
43467c478bd9Sstevel@tonic-gate 		if (async_thread) {
43477c478bd9Sstevel@tonic-gate 			mutex_enter(&cpr_lock);
43487c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_BEGIN(&cpr_info);
43497c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_lock);
43507c478bd9Sstevel@tonic-gate 		}
43517c478bd9Sstevel@tonic-gate 		cv_wait(&mi->mi_cv_in_recov, &mi->mi_lock);
43527c478bd9Sstevel@tonic-gate 		if (async_thread) {
43537c478bd9Sstevel@tonic-gate 			mutex_enter(&cpr_lock);
43547c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(&cpr_info, &cpr_lock);
43557c478bd9Sstevel@tonic-gate 			mutex_exit(&cpr_lock);
43567c478bd9Sstevel@tonic-gate 		}
43577c478bd9Sstevel@tonic-gate 	}
43587c478bd9Sstevel@tonic-gate 	mutex_exit(&mi->mi_lock);
43597c478bd9Sstevel@tonic-gate 
4360eabd0450Sth 	/*
4361eabd0450Sth 	 * If we got an error, then do not nuke the
4362eabd0450Sth 	 * tree. Either the harvester is busy reclaiming
4363eabd0450Sth 	 * this node or we ran into some busy condition.
4364eabd0450Sth 	 *
4365eabd0450Sth 	 * The harvester will eventually come along and cleanup.
4366eabd0450Sth 	 * The only problem would be the root mount point.
4367eabd0450Sth 	 *
4368eabd0450Sth 	 * Since the busy node can occur for a variety
4369eabd0450Sth 	 * of reasons and can result in an entry staying
4370eabd0450Sth 	 * in df output but no longer accessible from the
4371eabd0450Sth 	 * directory tree, we are okay.
4372eabd0450Sth 	 */
4373eabd0450Sth 	if (!nfs4_ephemeral_umount(mi, flag, cr,
43742f172c55SRobert Thurlow 	    &must_unlock, &eph_tree))
4375eabd0450Sth 		nfs4_ephemeral_umount_activate(mi, &must_unlock,
43762f172c55SRobert Thurlow 		    &eph_tree);
4377b9238976Sth 
43787c478bd9Sstevel@tonic-gate 	/*
43797c478bd9Sstevel@tonic-gate 	 * The original purge of the dnlc via 'dounmount'
43807c478bd9Sstevel@tonic-gate 	 * doesn't guarantee that another dnlc entry was not
43817c478bd9Sstevel@tonic-gate 	 * added while we waitied for all outstanding OTW
43827c478bd9Sstevel@tonic-gate 	 * and recovery calls to finish.  So re-purge the
43837c478bd9Sstevel@tonic-gate 	 * dnlc now.
43847c478bd9Sstevel@tonic-gate 	 */
43857c478bd9Sstevel@tonic-gate 	(void) dnlc_purge_vfsp(vfsp, 0);
43867c478bd9Sstevel@tonic-gate 
43877c478bd9Sstevel@tonic-gate 	/*
43887c478bd9Sstevel@tonic-gate 	 * We need to explicitly stop the manager thread; the asyc worker
43897c478bd9Sstevel@tonic-gate 	 * threads can timeout and exit on their own.
43907c478bd9Sstevel@tonic-gate 	 */
439150a83466Sjwahlig 	mutex_enter(&mi->mi_async_lock);
439250a83466Sjwahlig 	mi->mi_max_threads = 0;
43930776f5e6SVallish Vaidyeshwara 	NFS4_WAKEALL_ASYNC_WORKERS(mi->mi_async_work_cv);
439450a83466Sjwahlig 	mutex_exit(&mi->mi_async_lock);
439550a83466Sjwahlig 	if (mi->mi_manager_thread)
439650a83466Sjwahlig 		nfs4_async_manager_stop(vfsp);
43977c478bd9Sstevel@tonic-gate 
43987c478bd9Sstevel@tonic-gate 	destroy_rtable4(vfsp, cr);
43997c478bd9Sstevel@tonic-gate 
44007c478bd9Sstevel@tonic-gate 	nfs4_remove_mi_from_server(mi, NULL);
44017c478bd9Sstevel@tonic-gate 
44027c478bd9Sstevel@tonic-gate 	if (async_thread) {
44037c478bd9Sstevel@tonic-gate 		mutex_enter(&cpr_lock);
44047c478bd9Sstevel@tonic-gate 		CALLB_CPR_EXIT(&cpr_info);	/* drops cpr_lock */
44057c478bd9Sstevel@tonic-gate 		mutex_destroy(&cpr_lock);
44067c478bd9Sstevel@tonic-gate 	}
440750a83466Sjwahlig 
440850a83466Sjwahlig 	removed = nfs4_mi_zonelist_remove(mi);
440950a83466Sjwahlig 	if (removed)
4410a19609f8Sjv 		zone_rele_ref(&mi->mi_zone_ref, ZONE_REF_NFSV4);
44117c478bd9Sstevel@tonic-gate }
44122f172c55SRobert Thurlow 
44132f172c55SRobert Thurlow /* Referral related sub-routines */
44142f172c55SRobert Thurlow 
44152f172c55SRobert Thurlow /* Freeup knetconfig */
44162f172c55SRobert Thurlow static void
44172f172c55SRobert Thurlow free_knconf_contents(struct knetconfig *k)
44182f172c55SRobert Thurlow {
44192f172c55SRobert Thurlow 	if (k == NULL)
44202f172c55SRobert Thurlow 		return;
44212f172c55SRobert Thurlow 	if (k->knc_protofmly)
44222f172c55SRobert Thurlow 		kmem_free(k->knc_protofmly, KNC_STRSIZE);
44232f172c55SRobert Thurlow 	if (k->knc_proto)
44242f172c55SRobert Thurlow 		kmem_free(k->knc_proto, KNC_STRSIZE);
44252f172c55SRobert Thurlow }
44262f172c55SRobert Thurlow 
44272f172c55SRobert Thurlow /*
44282f172c55SRobert Thurlow  * This updates newpath variable with exact name component from the
44292f172c55SRobert Thurlow  * path which gave us a NFS4ERR_MOVED error.
44302f172c55SRobert Thurlow  * If the path is /rp/aaa/bbb and nth value is 1, aaa is returned.
44312f172c55SRobert Thurlow  */
44322f172c55SRobert Thurlow static char *
44332f172c55SRobert Thurlow extract_referral_point(const char *svp, int nth)
44342f172c55SRobert Thurlow {
44352f172c55SRobert Thurlow 	int num_slashes = 0;
44362f172c55SRobert Thurlow 	const char *p;
44372f172c55SRobert Thurlow 	char *newpath = NULL;
44382f172c55SRobert Thurlow 	int i = 0;
44392f172c55SRobert Thurlow 
44402f172c55SRobert Thurlow 	newpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
44412f172c55SRobert Thurlow 	for (p = svp; *p; p++) {
44422f172c55SRobert Thurlow 		if (*p == '/')
44432f172c55SRobert Thurlow 			num_slashes++;
44442f172c55SRobert Thurlow 		if (num_slashes == nth + 1) {
44452f172c55SRobert Thurlow 			p++;
44462f172c55SRobert Thurlow 			while (*p != '/') {
44472f172c55SRobert Thurlow 				if (*p == '\0')
44482f172c55SRobert Thurlow 					break;
44492f172c55SRobert Thurlow 				newpath[i] = *p;
44502f172c55SRobert Thurlow 				i++;
44512f172c55SRobert Thurlow 				p++;
44522f172c55SRobert Thurlow 			}
44532f172c55SRobert Thurlow 			newpath[i++] = '\0';
44542f172c55SRobert Thurlow 			break;
44552f172c55SRobert Thurlow 		}
44562f172c55SRobert Thurlow 	}
44572f172c55SRobert Thurlow 	return (newpath);
44582f172c55SRobert Thurlow }
44592f172c55SRobert Thurlow 
44602f172c55SRobert Thurlow /*
44612f172c55SRobert Thurlow  * This sets up a new path in sv_path to do a lookup of the referral point.
44622f172c55SRobert Thurlow  * If the path is /rp/aaa/bbb and the referral point is aaa,
44632f172c55SRobert Thurlow  * this updates /rp/aaa. This path will be used to get referral
44642f172c55SRobert Thurlow  * location.
44652f172c55SRobert Thurlow  */
44662f172c55SRobert Thurlow static void
44672f172c55SRobert Thurlow setup_newsvpath(servinfo4_t *svp, int nth)
44682f172c55SRobert Thurlow {
44692f172c55SRobert Thurlow 	int num_slashes = 0, pathlen, i = 0;
44702f172c55SRobert Thurlow 	char *newpath, *p;
44712f172c55SRobert Thurlow 
44722f172c55SRobert Thurlow 	newpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
44732f172c55SRobert Thurlow 	for (p = svp->sv_path; *p; p++) {
44742f172c55SRobert Thurlow 		newpath[i] =  *p;
44752f172c55SRobert Thurlow 		if (*p == '/')
44762f172c55SRobert Thurlow 			num_slashes++;
44772f172c55SRobert Thurlow 		if (num_slashes == nth + 1) {
44782f172c55SRobert Thurlow 			newpath[i] = '\0';
44792f172c55SRobert Thurlow 			pathlen = strlen(newpath) + 1;
44802f172c55SRobert Thurlow 			kmem_free(svp->sv_path, svp->sv_pathlen);
44812f172c55SRobert Thurlow 			svp->sv_path = kmem_alloc(pathlen, KM_SLEEP);
44822f172c55SRobert Thurlow 			svp->sv_pathlen = pathlen;
44832f172c55SRobert Thurlow 			bcopy(newpath, svp->sv_path, pathlen);
44842f172c55SRobert Thurlow 			break;
44852f172c55SRobert Thurlow 		}
44862f172c55SRobert Thurlow 		i++;
44872f172c55SRobert Thurlow 	}
44882f172c55SRobert Thurlow 	kmem_free(newpath, MAXPATHLEN);
44892f172c55SRobert Thurlow }
4490