xref: /onnv-gate/usr/src/uts/common/fs/autofs/auto_subr.c (revision 1153:b0029fa0379d)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
23*1153Snr123932  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <sys/param.h>
300Sstevel@tonic-gate #include <sys/kmem.h>
310Sstevel@tonic-gate #include <sys/errno.h>
320Sstevel@tonic-gate #include <sys/proc.h>
330Sstevel@tonic-gate #include <sys/disp.h>
340Sstevel@tonic-gate #include <sys/vfs.h>
350Sstevel@tonic-gate #include <sys/vnode.h>
360Sstevel@tonic-gate #include <sys/pathname.h>
370Sstevel@tonic-gate #include <sys/cred.h>
380Sstevel@tonic-gate #include <sys/mount.h>
390Sstevel@tonic-gate #include <sys/cmn_err.h>
400Sstevel@tonic-gate #include <sys/debug.h>
410Sstevel@tonic-gate #include <sys/systm.h>
420Sstevel@tonic-gate #include <rpc/types.h>
430Sstevel@tonic-gate #include <rpc/xdr.h>
440Sstevel@tonic-gate #include <rpc/auth.h>
450Sstevel@tonic-gate #include <rpc/clnt.h>
460Sstevel@tonic-gate #include <sys/ticotsord.h>
470Sstevel@tonic-gate #include <sys/dirent.h>
480Sstevel@tonic-gate #include <fs/fs_subr.h>
490Sstevel@tonic-gate #include <rpcsvc/autofs_prot.h>
500Sstevel@tonic-gate #include <sys/fs/autofs.h>
510Sstevel@tonic-gate #include <sys/callb.h>
520Sstevel@tonic-gate #include <sys/sysmacros.h>
530Sstevel@tonic-gate #include <sys/zone.h>
540Sstevel@tonic-gate #include <sys/fs/mntdata.h>
550Sstevel@tonic-gate 
560Sstevel@tonic-gate /*
570Sstevel@tonic-gate  * Autofs and Zones:
580Sstevel@tonic-gate  *
590Sstevel@tonic-gate  * Zones are delegated the responsibility of managing their own autofs mounts
600Sstevel@tonic-gate  * and maps.  Each zone runs its own copy of automountd, with its own timeouts,
610Sstevel@tonic-gate  * and other logically "global" parameters.  kRPC and virtualization in the
620Sstevel@tonic-gate  * loopback transport (tl) will prevent a zone from communicating with another
630Sstevel@tonic-gate  * zone's automountd.
640Sstevel@tonic-gate  *
650Sstevel@tonic-gate  * Each zone has its own "rootfnnode" and associated tree of auto nodes.
660Sstevel@tonic-gate  *
670Sstevel@tonic-gate  * Each zone also has its own set of "unmounter" kernel threads; these are
680Sstevel@tonic-gate  * created and run within the zone's context (ie, they are created via
690Sstevel@tonic-gate  * zthread_create()).
700Sstevel@tonic-gate  *
710Sstevel@tonic-gate  * Cross-zone mount triggers are disallowed.  There is a check in
720Sstevel@tonic-gate  * auto_trigger_mount() to this effect; EPERM is returned to indicate that the
730Sstevel@tonic-gate  * mount is not owned by the caller.
740Sstevel@tonic-gate  *
750Sstevel@tonic-gate  * autofssys() enables a caller in the global zone to clean up in-kernel (as
760Sstevel@tonic-gate  * well as regular) autofs mounts via the unmount_tree() mechanism.  This is
770Sstevel@tonic-gate  * routinely done when all mounts are removed as part of zone shutdown.
780Sstevel@tonic-gate  */
790Sstevel@tonic-gate #define	TYPICALMAXPATHLEN	64
800Sstevel@tonic-gate 
810Sstevel@tonic-gate static kmutex_t autofs_nodeid_lock;
820Sstevel@tonic-gate 
830Sstevel@tonic-gate static int auto_perform_link(fnnode_t *, struct linka *, cred_t *);
840Sstevel@tonic-gate static int auto_perform_actions(fninfo_t *, fnnode_t *,
850Sstevel@tonic-gate     action_list *, cred_t *);
860Sstevel@tonic-gate static int auto_getmntpnt(vnode_t *, char *, vnode_t **, cred_t *);
870Sstevel@tonic-gate static int auto_lookup_request(fninfo_t *, char *, struct linka *,
880Sstevel@tonic-gate     cred_t *, bool_t, bool_t *);
890Sstevel@tonic-gate static int auto_mount_request(fninfo_t *, char *, action_list **,
900Sstevel@tonic-gate     cred_t *, bool_t);
910Sstevel@tonic-gate 
920Sstevel@tonic-gate /*
930Sstevel@tonic-gate  * Clears the MF_INPROG flag, and wakes up those threads sleeping on
940Sstevel@tonic-gate  * fn_cv_mount if MF_WAITING is set.
950Sstevel@tonic-gate  */
960Sstevel@tonic-gate void
970Sstevel@tonic-gate auto_unblock_others(
980Sstevel@tonic-gate 	fnnode_t *fnp,
990Sstevel@tonic-gate 	uint_t operation)		/* either MF_INPROG or MF_LOOKUP */
1000Sstevel@tonic-gate {
1010Sstevel@tonic-gate 	ASSERT(operation & (MF_INPROG | MF_LOOKUP));
1020Sstevel@tonic-gate 	fnp->fn_flags &= ~operation;
1030Sstevel@tonic-gate 	if (fnp->fn_flags & MF_WAITING) {
1040Sstevel@tonic-gate 		fnp->fn_flags &= ~MF_WAITING;
1050Sstevel@tonic-gate 		cv_broadcast(&fnp->fn_cv_mount);
1060Sstevel@tonic-gate 	}
1070Sstevel@tonic-gate }
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate int
1100Sstevel@tonic-gate auto_wait4mount(fnnode_t *fnp)
1110Sstevel@tonic-gate {
1120Sstevel@tonic-gate 	int error;
1130Sstevel@tonic-gate 	k_sigset_t smask;
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_wait4mount: fnp=%p\n", (void *)fnp));
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
1180Sstevel@tonic-gate 	while (fnp->fn_flags & (MF_INPROG | MF_LOOKUP)) {
1190Sstevel@tonic-gate 		/*
1200Sstevel@tonic-gate 		 * There is a mount or a lookup in progress.
1210Sstevel@tonic-gate 		 */
1220Sstevel@tonic-gate 		fnp->fn_flags |= MF_WAITING;
1230Sstevel@tonic-gate 		sigintr(&smask, 1);
1240Sstevel@tonic-gate 		if (!cv_wait_sig(&fnp->fn_cv_mount, &fnp->fn_lock)) {
1250Sstevel@tonic-gate 			/*
1260Sstevel@tonic-gate 			 * Decided not to wait for operation to
1270Sstevel@tonic-gate 			 * finish after all.
1280Sstevel@tonic-gate 			 */
1290Sstevel@tonic-gate 			sigunintr(&smask);
1300Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
1310Sstevel@tonic-gate 			return (EINTR);
1320Sstevel@tonic-gate 		}
1330Sstevel@tonic-gate 		sigunintr(&smask);
1340Sstevel@tonic-gate 	}
1350Sstevel@tonic-gate 	error = fnp->fn_error;
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate 	if (error == EINTR) {
1380Sstevel@tonic-gate 		/*
1390Sstevel@tonic-gate 		 * The thread doing the mount got interrupted, we need to
1400Sstevel@tonic-gate 		 * try again, by returning EAGAIN.
1410Sstevel@tonic-gate 		 */
1420Sstevel@tonic-gate 		error = EAGAIN;
1430Sstevel@tonic-gate 	}
1440Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_wait4mount: fnp=%p error=%d\n", (void *)fnp,
1470Sstevel@tonic-gate 	    error));
1480Sstevel@tonic-gate 	return (error);
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate int
1520Sstevel@tonic-gate auto_lookup_aux(fnnode_t *fnp, char *name, cred_t *cred)
1530Sstevel@tonic-gate {
1540Sstevel@tonic-gate 	struct fninfo *fnip;
1550Sstevel@tonic-gate 	struct linka link;
1560Sstevel@tonic-gate 	bool_t mountreq = FALSE;
1570Sstevel@tonic-gate 	int error = 0;
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 	fnip = vfstofni(fntovn(fnp)->v_vfsp);
1600Sstevel@tonic-gate 	bzero(&link, sizeof (link));
1610Sstevel@tonic-gate 	error = auto_lookup_request(fnip, name, &link, cred, TRUE, &mountreq);
1620Sstevel@tonic-gate 	if (!error) {
1630Sstevel@tonic-gate 		if (link.link != NULL) {
1640Sstevel@tonic-gate 			/*
1650Sstevel@tonic-gate 			 * This node should be a symlink
1660Sstevel@tonic-gate 			 */
1670Sstevel@tonic-gate 			error = auto_perform_link(fnp, &link, cred);
1680Sstevel@tonic-gate 			kmem_free(link.dir, strlen(link.dir) + 1);
1690Sstevel@tonic-gate 			kmem_free(link.link, strlen(link.link) + 1);
1700Sstevel@tonic-gate 		} else if (mountreq) {
1710Sstevel@tonic-gate 			/*
1720Sstevel@tonic-gate 			 * The automount daemon is requesting a mount,
1730Sstevel@tonic-gate 			 * implying this entry must be a wildcard match and
1740Sstevel@tonic-gate 			 * therefore in need of verification that the entry
1750Sstevel@tonic-gate 			 * exists on the server.
1760Sstevel@tonic-gate 			 */
1770Sstevel@tonic-gate 			mutex_enter(&fnp->fn_lock);
1780Sstevel@tonic-gate 			AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG);
1790Sstevel@tonic-gate 			fnp->fn_error = 0;
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate 			/*
1820Sstevel@tonic-gate 			 * Unblock other lookup requests on this node,
1830Sstevel@tonic-gate 			 * this is needed to let the lookup generated by
1840Sstevel@tonic-gate 			 * the mount call to complete. The caveat is
1850Sstevel@tonic-gate 			 * other lookups on this node can also get by,
1860Sstevel@tonic-gate 			 * i.e., another lookup on this node that occurs
1870Sstevel@tonic-gate 			 * while this lookup is attempting the mount
1880Sstevel@tonic-gate 			 * would return a positive result no matter what.
1890Sstevel@tonic-gate 			 * Therefore two lookups on the this node could
1900Sstevel@tonic-gate 			 * potentially get disparate results.
1910Sstevel@tonic-gate 			 */
1920Sstevel@tonic-gate 			AUTOFS_UNBLOCK_OTHERS(fnp, MF_LOOKUP);
1930Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
1940Sstevel@tonic-gate 			/*
1950Sstevel@tonic-gate 			 * auto_new_mount_thread fires up a new thread which
1960Sstevel@tonic-gate 			 * calls automountd finishing up the work
1970Sstevel@tonic-gate 			 */
1980Sstevel@tonic-gate 			auto_new_mount_thread(fnp, name, cred);
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 			/*
2010Sstevel@tonic-gate 			 * At this point, we are simply another thread
2020Sstevel@tonic-gate 			 * waiting for the mount to complete
2030Sstevel@tonic-gate 			 */
2040Sstevel@tonic-gate 			error = auto_wait4mount(fnp);
2050Sstevel@tonic-gate 			if (error == AUTOFS_SHUTDOWN)
2060Sstevel@tonic-gate 				error = ENOENT;
2070Sstevel@tonic-gate 		}
2080Sstevel@tonic-gate 	}
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
2110Sstevel@tonic-gate 	fnp->fn_error = error;
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 	/*
2140Sstevel@tonic-gate 	 * Notify threads waiting for lookup/mount that
2150Sstevel@tonic-gate 	 * it's done.
2160Sstevel@tonic-gate 	 */
2170Sstevel@tonic-gate 	if (mountreq) {
2180Sstevel@tonic-gate 		AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
2190Sstevel@tonic-gate 	} else {
2200Sstevel@tonic-gate 		AUTOFS_UNBLOCK_OTHERS(fnp, MF_LOOKUP);
2210Sstevel@tonic-gate 	}
2220Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
2230Sstevel@tonic-gate 	return (error);
2240Sstevel@tonic-gate }
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate /*
2270Sstevel@tonic-gate  * Starting point for thread to handle mount requests with automountd.
2280Sstevel@tonic-gate  * XXX auto_mount_thread() is not suspend-safe within the scope of
2290Sstevel@tonic-gate  * the present model defined for cpr to suspend the system. Calls
2300Sstevel@tonic-gate  * made by the auto_mount_thread() that have been identified to be unsafe
2310Sstevel@tonic-gate  * are (1) RPC client handle setup and client calls to automountd which
2320Sstevel@tonic-gate  * can block deep down in the RPC library, (2) kmem_alloc() calls with the
2330Sstevel@tonic-gate  * KM_SLEEP flag which can block if memory is low, and (3) VFS_*(), and
2340Sstevel@tonic-gate  * lookuppnvp() calls which can result in over the wire calls to servers.
2350Sstevel@tonic-gate  * The thread should be completely reevaluated to make it suspend-safe in
2360Sstevel@tonic-gate  * case of future updates to the cpr model.
2370Sstevel@tonic-gate  */
2380Sstevel@tonic-gate static void
2390Sstevel@tonic-gate auto_mount_thread(struct autofs_callargs *argsp)
2400Sstevel@tonic-gate {
2410Sstevel@tonic-gate 	struct fninfo *fnip;
2420Sstevel@tonic-gate 	fnnode_t *fnp;
2430Sstevel@tonic-gate 	vnode_t *vp;
2440Sstevel@tonic-gate 	char *name;
2450Sstevel@tonic-gate 	size_t namelen;
2460Sstevel@tonic-gate 	cred_t *cred;
2470Sstevel@tonic-gate 	action_list *alp = NULL;
2480Sstevel@tonic-gate 	int error;
2490Sstevel@tonic-gate 	callb_cpr_t cprinfo;
2500Sstevel@tonic-gate 	kmutex_t auto_mount_thread_cpr_lock;
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 	mutex_init(&auto_mount_thread_cpr_lock, NULL, MUTEX_DEFAULT, NULL);
2530Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, &auto_mount_thread_cpr_lock, callb_generic_cpr,
2540Sstevel@tonic-gate 		"auto_mount_thread");
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	fnp = argsp->fnc_fnp;
2570Sstevel@tonic-gate 	vp = fntovn(fnp);
2580Sstevel@tonic-gate 	fnip = vfstofni(vp->v_vfsp);
2590Sstevel@tonic-gate 	name = argsp->fnc_name;
2600Sstevel@tonic-gate 	cred = argsp->fnc_cred;
2610Sstevel@tonic-gate 	ASSERT(crgetzoneid(argsp->fnc_cred) == fnip->fi_zoneid);
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 	error = auto_mount_request(fnip, name, &alp, cred, TRUE);
2640Sstevel@tonic-gate 	if (!error)
2650Sstevel@tonic-gate 		error = auto_perform_actions(fnip, fnp, alp, cred);
2660Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
2670Sstevel@tonic-gate 	fnp->fn_error = error;
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 	/*
2700Sstevel@tonic-gate 	 * Notify threads waiting for mount that
2710Sstevel@tonic-gate 	 * it's done.
2720Sstevel@tonic-gate 	 */
2730Sstevel@tonic-gate 	AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
2740Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate 	VN_RELE(vp);
2770Sstevel@tonic-gate 	crfree(argsp->fnc_cred);
2780Sstevel@tonic-gate 	namelen = strlen(argsp->fnc_name) + 1;
2790Sstevel@tonic-gate 	kmem_free(argsp->fnc_name, namelen);
2800Sstevel@tonic-gate 	kmem_free(argsp, sizeof (*argsp));
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	mutex_enter(&auto_mount_thread_cpr_lock);
2830Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cprinfo);
2840Sstevel@tonic-gate 	mutex_destroy(&auto_mount_thread_cpr_lock);
2850Sstevel@tonic-gate 	zthread_exit();
2860Sstevel@tonic-gate 	/* NOTREACHED */
2870Sstevel@tonic-gate }
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate static int autofs_thr_success = 0;
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate /*
2920Sstevel@tonic-gate  * Creates new thread which calls auto_mount_thread which does
2930Sstevel@tonic-gate  * the bulk of the work calling automountd, via 'auto_perform_actions'.
2940Sstevel@tonic-gate  */
2950Sstevel@tonic-gate void
2960Sstevel@tonic-gate auto_new_mount_thread(fnnode_t *fnp, char *name, cred_t *cred)
2970Sstevel@tonic-gate {
2980Sstevel@tonic-gate 	struct autofs_callargs *argsp;
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 	argsp = kmem_alloc(sizeof (*argsp), KM_SLEEP);
3010Sstevel@tonic-gate 	VN_HOLD(fntovn(fnp));
3020Sstevel@tonic-gate 	argsp->fnc_fnp = fnp;
3030Sstevel@tonic-gate 	argsp->fnc_name = kmem_alloc(strlen(name) + 1, KM_SLEEP);
3040Sstevel@tonic-gate 	(void) strcpy(argsp->fnc_name, name);
3050Sstevel@tonic-gate 	argsp->fnc_origin = curthread;
3060Sstevel@tonic-gate 	crhold(cred);
3070Sstevel@tonic-gate 	argsp->fnc_cred = cred;
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	(void) zthread_create(NULL, 0, auto_mount_thread, argsp, 0,
3100Sstevel@tonic-gate 	    minclsyspri);
3110Sstevel@tonic-gate 	autofs_thr_success++;
3120Sstevel@tonic-gate }
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate int
3150Sstevel@tonic-gate auto_calldaemon(
3160Sstevel@tonic-gate 	fninfo_t *fnip,
3170Sstevel@tonic-gate 	rpcproc_t which,
3180Sstevel@tonic-gate 	xdrproc_t xdrargs,
3190Sstevel@tonic-gate 	void *argsp,
3200Sstevel@tonic-gate 	xdrproc_t xdrres,
3210Sstevel@tonic-gate 	void *resp,
3220Sstevel@tonic-gate 	cred_t *cred,
3230Sstevel@tonic-gate 	bool_t hard)				/* retry forever? */
3240Sstevel@tonic-gate {
3250Sstevel@tonic-gate 	CLIENT *client;
3260Sstevel@tonic-gate 	enum clnt_stat status;
3270Sstevel@tonic-gate 	struct rpc_err rpcerr;
3280Sstevel@tonic-gate 	struct timeval wait;
3290Sstevel@tonic-gate 	bool_t tryagain;
3300Sstevel@tonic-gate 	int error = 0;
3310Sstevel@tonic-gate 	k_sigset_t smask;
3320Sstevel@tonic-gate 	struct autofs_globals *fngp = vntofn(fnip->fi_rootvp)->fn_globals;
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_calldaemon\n"));
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 	error = clnt_tli_kcreate(&fnip->fi_knconf, &fnip->fi_addr,
3370Sstevel@tonic-gate 	    AUTOFS_PROG, AUTOFS_VERS, 0, INT_MAX, cred, &client);
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	if (error) {
3400Sstevel@tonic-gate 		auto_log(fngp, CE_WARN, "autofs: clnt_tli_kcreate: error %d",
3410Sstevel@tonic-gate 		    error);
3420Sstevel@tonic-gate 		goto done;
3430Sstevel@tonic-gate 	}
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	/*
3460Sstevel@tonic-gate 	 * Release the old authentication handle.  It was probably
3470Sstevel@tonic-gate 	 * AUTH_UNIX.
3480Sstevel@tonic-gate 	 */
3490Sstevel@tonic-gate 	auth_destroy(client->cl_auth);
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate 	/*
3520Sstevel@tonic-gate 	 * Create a new authentication handle for AUTH_LOOPBACK.  This
3530Sstevel@tonic-gate 	 * will allow us to correctly handle the entire groups list.
3540Sstevel@tonic-gate 	 */
3550Sstevel@tonic-gate 	client->cl_auth = authloopback_create();
3560Sstevel@tonic-gate 	if (client->cl_auth == NULL) {
3570Sstevel@tonic-gate 		clnt_destroy(client);
3580Sstevel@tonic-gate 		error = EINTR;
3590Sstevel@tonic-gate 		auto_log(fngp, CE_WARN,
3600Sstevel@tonic-gate 		    "autofs: authloopback_create: error %d", error);
3610Sstevel@tonic-gate 		goto done;
3620Sstevel@tonic-gate 	}
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	wait.tv_sec = fnip->fi_rpc_to;
3650Sstevel@tonic-gate 	wait.tv_usec = 0;
3660Sstevel@tonic-gate 	do {
3670Sstevel@tonic-gate 		tryagain = FALSE;
3680Sstevel@tonic-gate 		error = 0;
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 		/*
3710Sstevel@tonic-gate 		 * Mask out all signals except SIGHUP, SIGINT, SIGQUIT
3720Sstevel@tonic-gate 		 * and SIGTERM. (Preserving the existing masks)
3730Sstevel@tonic-gate 		 */
3740Sstevel@tonic-gate 		sigintr(&smask, 1);
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 		status = CLNT_CALL(client, which, xdrargs, argsp,
3770Sstevel@tonic-gate 		    xdrres, resp, wait);
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 		/*
3800Sstevel@tonic-gate 		 * Restore original signal mask
3810Sstevel@tonic-gate 		 */
3820Sstevel@tonic-gate 		sigunintr(&smask);
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 		switch (status) {
3850Sstevel@tonic-gate 		case RPC_SUCCESS:
3860Sstevel@tonic-gate 			break;
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 		case RPC_INTR:
3890Sstevel@tonic-gate 			error = EINTR;
3900Sstevel@tonic-gate 			break;
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 		case RPC_TIMEDOUT:
3930Sstevel@tonic-gate 			tryagain = TRUE;
3940Sstevel@tonic-gate 			error = ETIMEDOUT;
3950Sstevel@tonic-gate 			break;
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 		case RPC_CANTCONNECT:
3980Sstevel@tonic-gate 		case RPC_CANTCREATESTREAM:
3990Sstevel@tonic-gate 			/*
4000Sstevel@tonic-gate 			 * The connection could not be established
4010Sstevel@tonic-gate 			 */
4020Sstevel@tonic-gate 			/* fall thru */
4030Sstevel@tonic-gate 		case RPC_XPRTFAILED:
4040Sstevel@tonic-gate 			/*
4050Sstevel@tonic-gate 			 * The connection could not be established or
4060Sstevel@tonic-gate 			 * was dropped, we differentiate between the two
4070Sstevel@tonic-gate 			 * conditions by calling CLNT_GETERR and look at
4080Sstevel@tonic-gate 			 * rpcerror.re_errno.
4090Sstevel@tonic-gate 			 * If rpcerr.re_errno == ECONNREFUSED, then the
4100Sstevel@tonic-gate 			 * connection could not be established at all.
4110Sstevel@tonic-gate 			 */
4120Sstevel@tonic-gate 			error = ECONNREFUSED;
4130Sstevel@tonic-gate 			if (status == RPC_XPRTFAILED) {
4140Sstevel@tonic-gate 				CLNT_GETERR(client, &rpcerr);
4150Sstevel@tonic-gate 				if (rpcerr.re_errno != ECONNREFUSED) {
4160Sstevel@tonic-gate 					/*
4170Sstevel@tonic-gate 					 * The connection was dropped, return
4180Sstevel@tonic-gate 					 * to the caller if hard is not set.
4190Sstevel@tonic-gate 					 * It is the responsability of the
4200Sstevel@tonic-gate 					 * caller to retry the call if
4210Sstevel@tonic-gate 					 * appropriate.
4220Sstevel@tonic-gate 					 */
4230Sstevel@tonic-gate 					error = ECONNRESET;
4240Sstevel@tonic-gate 				}
4250Sstevel@tonic-gate 			}
4260Sstevel@tonic-gate 			/*
4270Sstevel@tonic-gate 			 * We know that the current thread is doing work on
4280Sstevel@tonic-gate 			 * behalf of its own zone, so it's ok to use
4290Sstevel@tonic-gate 			 * curproc->p_zone.
4300Sstevel@tonic-gate 			 */
4310Sstevel@tonic-gate 			ASSERT(fngp->fng_zoneid == getzoneid());
4320Sstevel@tonic-gate 			if (zone_status_get(curproc->p_zone) >=
4330Sstevel@tonic-gate 			    ZONE_IS_SHUTTING_DOWN) {
4340Sstevel@tonic-gate 				/*
4350Sstevel@tonic-gate 				 * There's no point in trying to talk to
4360Sstevel@tonic-gate 				 * automountd.  Plus, zone_shutdown() is
4370Sstevel@tonic-gate 				 * waiting for us.
4380Sstevel@tonic-gate 				 */
4390Sstevel@tonic-gate 				tryagain = FALSE;
4400Sstevel@tonic-gate 				break;
4410Sstevel@tonic-gate 			}
4420Sstevel@tonic-gate 			tryagain = hard;
4430Sstevel@tonic-gate 			if (!fngp->fng_printed_not_running_msg) {
4440Sstevel@tonic-gate 				if (tryagain) {
4450Sstevel@tonic-gate 					fngp->fng_printed_not_running_msg = 1;
4460Sstevel@tonic-gate 					zprintf(fngp->fng_zoneid,
4470Sstevel@tonic-gate 					"automountd not running, retrying\n");
4480Sstevel@tonic-gate 				}
4490Sstevel@tonic-gate 			}
4500Sstevel@tonic-gate 			break;
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 		default:
4530Sstevel@tonic-gate 			auto_log(fngp, CE_WARN, "autofs: %s",
4540Sstevel@tonic-gate 			    clnt_sperrno(status));
4550Sstevel@tonic-gate 			error = ENOENT;
4560Sstevel@tonic-gate 			break;
4570Sstevel@tonic-gate 		}
4580Sstevel@tonic-gate 	} while (tryagain);
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 	if (status == RPC_SUCCESS) {
4610Sstevel@tonic-gate 		if (fngp->fng_printed_not_running_msg == 1) {
4620Sstevel@tonic-gate 			fngp->fng_printed_not_running_msg = 0;
4630Sstevel@tonic-gate 			zprintf(fngp->fng_zoneid, "automountd OK\n");
4640Sstevel@tonic-gate 		}
4650Sstevel@tonic-gate 	}
4660Sstevel@tonic-gate 	auth_destroy(client->cl_auth);
4670Sstevel@tonic-gate 	clnt_destroy(client);
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate done:
4700Sstevel@tonic-gate 	ASSERT(status == RPC_SUCCESS || error != 0);
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_calldaemon error=%d\n", error));
4730Sstevel@tonic-gate 	return (error);
4740Sstevel@tonic-gate }
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate static int
4770Sstevel@tonic-gate auto_null_request(fninfo_t *fnip, cred_t *cred, bool_t hard)
4780Sstevel@tonic-gate {
4790Sstevel@tonic-gate 	int error;
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tauto_null_request\n"));
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 	error = auto_calldaemon(fnip, NULLPROC, xdr_void, NULL, xdr_void, NULL,
4840Sstevel@tonic-gate 	    cred, hard);
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tauto_null_request: error=%d\n", error));
4870Sstevel@tonic-gate 	return (error);
4880Sstevel@tonic-gate }
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate static int
4910Sstevel@tonic-gate auto_lookup_request(
4920Sstevel@tonic-gate 	fninfo_t *fnip,
4930Sstevel@tonic-gate 	char *key,
4940Sstevel@tonic-gate 	struct linka *lnp,
4950Sstevel@tonic-gate 	cred_t *cred,
4960Sstevel@tonic-gate 	bool_t hard,
4970Sstevel@tonic-gate 	bool_t *mountreq)
4980Sstevel@tonic-gate {
4990Sstevel@tonic-gate 	int error;
5000Sstevel@tonic-gate 	struct autofs_globals *fngp;
5010Sstevel@tonic-gate 	struct autofs_lookupargs request;
5020Sstevel@tonic-gate 	struct autofs_lookupres result;
5030Sstevel@tonic-gate 	struct linka *p;
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_lookup_request: path=%s name=%s\n",
5060Sstevel@tonic-gate 	    fnip->fi_path, key));
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	fngp = vntofn(fnip->fi_rootvp)->fn_globals;
5090Sstevel@tonic-gate 	request.map = fnip->fi_map;
5100Sstevel@tonic-gate 	request.path = fnip->fi_path;
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 	if (fnip->fi_flags & MF_DIRECT)
5130Sstevel@tonic-gate 		request.name = fnip->fi_key;
5140Sstevel@tonic-gate 	else
5150Sstevel@tonic-gate 		request.name = key;
5160Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_lookup_request: using key=%s\n", request.name));
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 	request.subdir = fnip->fi_subdir;
5190Sstevel@tonic-gate 	request.opts = fnip->fi_opts;
5200Sstevel@tonic-gate 	request.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE;
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 	bzero(&result, sizeof (result));
5230Sstevel@tonic-gate 	error = auto_calldaemon(fnip, AUTOFS_LOOKUP,
5240Sstevel@tonic-gate 	    xdr_autofs_lookupargs, &request,
5250Sstevel@tonic-gate 	    xdr_autofs_lookupres, &result,
5260Sstevel@tonic-gate 	    cred, hard);
5270Sstevel@tonic-gate 	if (!error) {
5280Sstevel@tonic-gate 		fngp->fng_verbose = result.lu_verbose;
5290Sstevel@tonic-gate 		switch (result.lu_res) {
5300Sstevel@tonic-gate 		case AUTOFS_OK:
5310Sstevel@tonic-gate 			switch (result.lu_type.action) {
5320Sstevel@tonic-gate 			case AUTOFS_MOUNT_RQ:
5330Sstevel@tonic-gate 				lnp->link = NULL;
5340Sstevel@tonic-gate 				lnp->dir = NULL;
5350Sstevel@tonic-gate 				*mountreq = TRUE;
5360Sstevel@tonic-gate 				break;
5370Sstevel@tonic-gate 			case AUTOFS_LINK_RQ:
5380Sstevel@tonic-gate 				p =
5390Sstevel@tonic-gate 				&result.lu_type.lookup_result_type_u.lt_linka;
5400Sstevel@tonic-gate 				lnp->dir = kmem_alloc(strlen(p->dir) + 1,
5410Sstevel@tonic-gate 				    KM_SLEEP);
5420Sstevel@tonic-gate 				(void) strcpy(lnp->dir, p->dir);
5430Sstevel@tonic-gate 				lnp->link = kmem_alloc(strlen(p->link) + 1,
5440Sstevel@tonic-gate 				    KM_SLEEP);
5450Sstevel@tonic-gate 				(void) strcpy(lnp->link, p->link);
5460Sstevel@tonic-gate 				break;
5470Sstevel@tonic-gate 			case AUTOFS_NONE:
5480Sstevel@tonic-gate 				lnp->link = NULL;
5490Sstevel@tonic-gate 				lnp->dir = NULL;
5500Sstevel@tonic-gate 				break;
5510Sstevel@tonic-gate 			default:
5520Sstevel@tonic-gate 				auto_log(fngp, CE_WARN,
5530Sstevel@tonic-gate 				    "auto_lookup_request: bad action type %d",
5540Sstevel@tonic-gate 				    result.lu_res);
5550Sstevel@tonic-gate 				error = ENOENT;
5560Sstevel@tonic-gate 			}
5570Sstevel@tonic-gate 			break;
5580Sstevel@tonic-gate 		case AUTOFS_NOENT:
5590Sstevel@tonic-gate 			error = ENOENT;
5600Sstevel@tonic-gate 			break;
5610Sstevel@tonic-gate 		default:
5620Sstevel@tonic-gate 			error = ENOENT;
5630Sstevel@tonic-gate 			auto_log(fngp, CE_WARN,
5640Sstevel@tonic-gate 			    "auto_lookup_request: unknown result: %d",
5650Sstevel@tonic-gate 			    result.lu_res);
5660Sstevel@tonic-gate 			break;
5670Sstevel@tonic-gate 		}
5680Sstevel@tonic-gate 	}
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate done:
5710Sstevel@tonic-gate 	xdr_free(xdr_autofs_lookupres, (char *)&result);
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_lookup_request: path=%s name=%s error=%d\n",
5740Sstevel@tonic-gate 	    fnip->fi_path, key, error));
5750Sstevel@tonic-gate 	return (error);
5760Sstevel@tonic-gate }
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate static int
5790Sstevel@tonic-gate auto_mount_request(
5800Sstevel@tonic-gate 	fninfo_t *fnip,
5810Sstevel@tonic-gate 	char *key,
5820Sstevel@tonic-gate 	action_list **alpp,
5830Sstevel@tonic-gate 	cred_t *cred,
5840Sstevel@tonic-gate 	bool_t hard)
5850Sstevel@tonic-gate {
5860Sstevel@tonic-gate 	int error;
5870Sstevel@tonic-gate 	struct autofs_globals *fngp;
5880Sstevel@tonic-gate 	struct autofs_lookupargs request;
5890Sstevel@tonic-gate 	struct autofs_mountres *result;
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_mount_request: path=%s name=%s\n",
5920Sstevel@tonic-gate 	    fnip->fi_path, key));
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate 	fngp = vntofn(fnip->fi_rootvp)->fn_globals;
5950Sstevel@tonic-gate 	request.map = fnip->fi_map;
5960Sstevel@tonic-gate 	request.path = fnip->fi_path;
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	if (fnip->fi_flags & MF_DIRECT)
5990Sstevel@tonic-gate 		request.name = fnip->fi_key;
6000Sstevel@tonic-gate 	else
6010Sstevel@tonic-gate 		request.name = key;
6020Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_mount_request: using key=%s\n", request.name));
6030Sstevel@tonic-gate 
6040Sstevel@tonic-gate 	request.subdir = fnip->fi_subdir;
6050Sstevel@tonic-gate 	request.opts = fnip->fi_opts;
6060Sstevel@tonic-gate 	request.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE;
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate 	*alpp = NULL;
6090Sstevel@tonic-gate 	result = kmem_zalloc(sizeof (*result), KM_SLEEP);
6100Sstevel@tonic-gate 	error = auto_calldaemon(fnip, AUTOFS_MOUNT,
6110Sstevel@tonic-gate 	    xdr_autofs_lookupargs, &request,
6120Sstevel@tonic-gate 	    xdr_autofs_mountres, result,
6130Sstevel@tonic-gate 	    cred, hard);
6140Sstevel@tonic-gate 	if (!error) {
6150Sstevel@tonic-gate 		fngp->fng_verbose = result->mr_verbose;
6160Sstevel@tonic-gate 		switch (result->mr_type.status) {
6170Sstevel@tonic-gate 		case AUTOFS_ACTION:
6180Sstevel@tonic-gate 			error = 0;
6190Sstevel@tonic-gate 			/*
6200Sstevel@tonic-gate 			 * Save the action list since it is used by
6210Sstevel@tonic-gate 			 * the caller. We NULL the action list pointer
6220Sstevel@tonic-gate 			 * in 'result' so that xdr_free() will not free
6230Sstevel@tonic-gate 			 * the list.
6240Sstevel@tonic-gate 			 */
6250Sstevel@tonic-gate 			*alpp = result->mr_type.mount_result_type_u.list;
6260Sstevel@tonic-gate 			result->mr_type.mount_result_type_u.list = NULL;
6270Sstevel@tonic-gate 			break;
6280Sstevel@tonic-gate 		case AUTOFS_DONE:
6290Sstevel@tonic-gate 			error = result->mr_type.mount_result_type_u.error;
6300Sstevel@tonic-gate 			break;
6310Sstevel@tonic-gate 		default:
6320Sstevel@tonic-gate 			error = ENOENT;
6330Sstevel@tonic-gate 			auto_log(fngp, CE_WARN,
6340Sstevel@tonic-gate 			    "auto_mount_request: unknown status %d",
6350Sstevel@tonic-gate 			    result->mr_type.status);
6360Sstevel@tonic-gate 			break;
6370Sstevel@tonic-gate 		}
6380Sstevel@tonic-gate 	}
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	xdr_free(xdr_autofs_mountres, (char *)result);
6410Sstevel@tonic-gate 	kmem_free(result, sizeof (*result));
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_mount_request: path=%s name=%s error=%d\n",
6440Sstevel@tonic-gate 	    fnip->fi_path, key, error));
6450Sstevel@tonic-gate 	return (error);
6460Sstevel@tonic-gate }
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate static int
6500Sstevel@tonic-gate auto_send_unmount_request(
6510Sstevel@tonic-gate 	fninfo_t *fnip,
6520Sstevel@tonic-gate 	umntrequest *ul,
6530Sstevel@tonic-gate 	cred_t *cred,
6540Sstevel@tonic-gate 	bool_t hard)
6550Sstevel@tonic-gate {
6560Sstevel@tonic-gate 	int error;
6570Sstevel@tonic-gate 	umntres result;
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tauto_send_unmount_request: fstype=%s "
6600Sstevel@tonic-gate 			" mntpnt=%s\n", ul->fstype, ul->mntpnt));
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 	error = auto_calldaemon(fnip, AUTOFS_UNMOUNT,
6630Sstevel@tonic-gate 	    xdr_umntrequest, ul,
6640Sstevel@tonic-gate 	    xdr_umntres, &result,
6650Sstevel@tonic-gate 	    cred, hard);
6660Sstevel@tonic-gate 	if (!error)
6670Sstevel@tonic-gate 		error = result.status;
6680Sstevel@tonic-gate 
6690Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tauto_send_unmount_request: error=%d\n", error));
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate 	return (error);
6720Sstevel@tonic-gate }
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate static int
6750Sstevel@tonic-gate auto_perform_link(fnnode_t *fnp, struct linka *linkp, cred_t *cred)
6760Sstevel@tonic-gate {
6770Sstevel@tonic-gate 	vnode_t *vp;
6780Sstevel@tonic-gate 	size_t len;
6790Sstevel@tonic-gate 	char *tmp;
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 	AUTOFS_DPRINT((3, "auto_perform_link: fnp=%p dir=%s link=%s\n",
6820Sstevel@tonic-gate 	    (void *)fnp, linkp->dir, linkp->link));
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate 	len = strlen(linkp->link) + 1;		/* include '\0' */
6850Sstevel@tonic-gate 	tmp = kmem_zalloc(len, KM_SLEEP);
6860Sstevel@tonic-gate 	(void) kcopy(linkp->link, tmp, len);
6870Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
6880Sstevel@tonic-gate 	fnp->fn_symlink = tmp;
6890Sstevel@tonic-gate 	fnp->fn_symlinklen = (uint_t)len;
6900Sstevel@tonic-gate 	fnp->fn_flags |= MF_THISUID_MATCH_RQD;
6910Sstevel@tonic-gate 	crhold(cred);
6920Sstevel@tonic-gate 	fnp->fn_cred = cred;
6930Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate 	vp = fntovn(fnp);
6960Sstevel@tonic-gate 	vp->v_type = VLNK;
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate 	return (0);
6990Sstevel@tonic-gate }
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate static boolean_t
7020Sstevel@tonic-gate auto_invalid_action(fninfo_t *dfnip, fnnode_t *dfnp, action_list *p)
7030Sstevel@tonic-gate {
7040Sstevel@tonic-gate 	struct mounta *m;
7050Sstevel@tonic-gate 	struct autofs_args *argsp;
7060Sstevel@tonic-gate 	vnode_t *dvp;
7070Sstevel@tonic-gate 	char buff[AUTOFS_MAXPATHLEN];
7080Sstevel@tonic-gate 	size_t len;
7090Sstevel@tonic-gate 	struct autofs_globals *fngp;
7100Sstevel@tonic-gate 
7110Sstevel@tonic-gate 	fngp = dfnp->fn_globals;
7120Sstevel@tonic-gate 	dvp = fntovn(dfnp);
7130Sstevel@tonic-gate 	/*
7140Sstevel@tonic-gate 	 * Before we go any further, this better be a mount request.
7150Sstevel@tonic-gate 	 */
7160Sstevel@tonic-gate 	if (p->action.action != AUTOFS_MOUNT_RQ)
7170Sstevel@tonic-gate 		return (B_TRUE);
7180Sstevel@tonic-gate 	m = &p->action.action_list_entry_u.mounta;
7190Sstevel@tonic-gate 	/*
7200Sstevel@tonic-gate 	 * Make sure we aren't geting passed NULL values or a "dir" that
7210Sstevel@tonic-gate 	 * isn't "." and doesn't begin with "./".
7220Sstevel@tonic-gate 	 *
7230Sstevel@tonic-gate 	 * We also only want to perform autofs mounts, so make sure
7240Sstevel@tonic-gate 	 * no-one is trying to trick us into doing anything else.
7250Sstevel@tonic-gate 	 */
7260Sstevel@tonic-gate 	if (m->spec == NULL || m->dir == NULL || m->dir[0] != '.' ||
7270Sstevel@tonic-gate 	    (m->dir[1] != '/' && m->dir[1] != '\0') ||
7280Sstevel@tonic-gate 	    m->fstype == NULL || strcmp(m->fstype, "autofs") != 0 ||
7290Sstevel@tonic-gate 	    m->dataptr == NULL || m->datalen != sizeof (struct autofs_args) ||
7300Sstevel@tonic-gate 	    m->optptr == NULL)
7310Sstevel@tonic-gate 		return (B_TRUE);
7320Sstevel@tonic-gate 	/*
7330Sstevel@tonic-gate 	 * We also don't like ".."s in the pathname.  Symlinks are
7340Sstevel@tonic-gate 	 * handled by the fact that we'll use NOFOLLOW when we do
7350Sstevel@tonic-gate 	 * lookup()s.
7360Sstevel@tonic-gate 	 */
7370Sstevel@tonic-gate 	if (strstr(m->dir, "/../") != NULL ||
7380Sstevel@tonic-gate 	    (len = strlen(m->dir)) > sizeof ("/..") - 1 &&
7390Sstevel@tonic-gate 	    m->dir[len] == '.' && m->dir[len - 1] == '.' &&
7400Sstevel@tonic-gate 	    m->dir[len - 2] == '/')
7410Sstevel@tonic-gate 		return (B_TRUE);
7420Sstevel@tonic-gate 	argsp = (struct autofs_args *)m->dataptr;
7430Sstevel@tonic-gate 	/*
7440Sstevel@tonic-gate 	 * We don't want NULL values here either.
7450Sstevel@tonic-gate 	 */
7460Sstevel@tonic-gate 	if (argsp->addr.buf == NULL || argsp->path == NULL ||
7470Sstevel@tonic-gate 	    argsp->opts == NULL || argsp->map == NULL || argsp->subdir == NULL)
7480Sstevel@tonic-gate 		return (B_TRUE);
7490Sstevel@tonic-gate 	/*
7500Sstevel@tonic-gate 	 * We know what the claimed pathname *should* look like:
7510Sstevel@tonic-gate 	 *
7520Sstevel@tonic-gate 	 * If the parent (dfnp) is a mount point (VROOT), then
7530Sstevel@tonic-gate 	 * the path should be (dfnip->fi_path + m->dir).
7540Sstevel@tonic-gate 	 *
7550Sstevel@tonic-gate 	 * Else, we know we're only two levels deep, so we use
7560Sstevel@tonic-gate 	 * (dfnip->fi_path + dfnp->fn_name + m->dir).
7570Sstevel@tonic-gate 	 *
7580Sstevel@tonic-gate 	 * Furthermore, "." only makes sense if dfnp is a
7590Sstevel@tonic-gate 	 * trigger node.
7600Sstevel@tonic-gate 	 *
7610Sstevel@tonic-gate 	 * At this point it seems like the passed-in path is
7620Sstevel@tonic-gate 	 * redundant.
7630Sstevel@tonic-gate 	 */
7640Sstevel@tonic-gate 	if (dvp->v_flag & VROOT) {
7650Sstevel@tonic-gate 		if (m->dir[1] == '\0' && !(dfnp->fn_flags & MF_TRIGGER))
7660Sstevel@tonic-gate 			return (B_TRUE);
7670Sstevel@tonic-gate 		(void) snprintf(buff, sizeof (buff), "%s%s",
7680Sstevel@tonic-gate 		    dfnip->fi_path, m->dir + 1);
7690Sstevel@tonic-gate 	} else {
7700Sstevel@tonic-gate 		(void) snprintf(buff, sizeof (buff), "%s/%s%s",
7710Sstevel@tonic-gate 		    dfnip->fi_path, dfnp->fn_name, m->dir + 1);
7720Sstevel@tonic-gate 	}
7730Sstevel@tonic-gate 	if (strcmp(argsp->path, buff) != 0) {
7740Sstevel@tonic-gate 		auto_log(fngp, CE_WARN, "autofs: expected path of '%s', "
7750Sstevel@tonic-gate 		    "got '%s' instead.", buff, argsp->path);
7760Sstevel@tonic-gate 		return (B_TRUE);
7770Sstevel@tonic-gate 	}
7780Sstevel@tonic-gate 	return (B_FALSE); /* looks OK */
7790Sstevel@tonic-gate }
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate static int
7820Sstevel@tonic-gate auto_perform_actions(
7830Sstevel@tonic-gate 	fninfo_t *dfnip,
7840Sstevel@tonic-gate 	fnnode_t *dfnp,
7850Sstevel@tonic-gate 	action_list *alp,
7860Sstevel@tonic-gate 	cred_t *cred)	/* Credentials of the caller */
7870Sstevel@tonic-gate {
7880Sstevel@tonic-gate 	action_list *p;
7890Sstevel@tonic-gate 	struct mounta *m, margs;
7900Sstevel@tonic-gate 	struct autofs_args *argsp;
7910Sstevel@tonic-gate 	int error, success = 0;
7920Sstevel@tonic-gate 	vnode_t *mvp, *dvp, *newvp;
7930Sstevel@tonic-gate 	fnnode_t *newfnp, *mfnp;
7940Sstevel@tonic-gate 	int auto_mount = 0;
7950Sstevel@tonic-gate 	int save_triggers = 0;		/* set when we need to save at least */
7960Sstevel@tonic-gate 					/* one trigger node */
7970Sstevel@tonic-gate 	int update_times = 0;
7980Sstevel@tonic-gate 	char *mntpnt;
7990Sstevel@tonic-gate 	char buff[AUTOFS_MAXPATHLEN];
8000Sstevel@tonic-gate 	timestruc_t now;
8010Sstevel@tonic-gate 	struct autofs_globals *fngp;
8020Sstevel@tonic-gate 	cred_t *zcred;	/* kcred-like credentials limited by our zone */
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_perform_actions: alp=%p\n", (void *)alp));
8050Sstevel@tonic-gate 
8060Sstevel@tonic-gate 	fngp = dfnp->fn_globals;
8070Sstevel@tonic-gate 	dvp = fntovn(dfnp);
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 	/*
8100Sstevel@tonic-gate 	 * As automountd running in a zone may be compromised, and this may be
8110Sstevel@tonic-gate 	 * an attack, we can't trust everything passed in by automountd, and we
8120Sstevel@tonic-gate 	 * need to do argument verification.  We'll issue a warning and drop
8130Sstevel@tonic-gate 	 * the request if it doesn't seem right.
8140Sstevel@tonic-gate 	 */
8150Sstevel@tonic-gate 	for (p = alp; p != NULL; p = p->next) {
8160Sstevel@tonic-gate 		if (auto_invalid_action(dfnip, dfnp, p)) {
8170Sstevel@tonic-gate 			/*
8180Sstevel@tonic-gate 			 * This warning should be sent to the global zone,
8190Sstevel@tonic-gate 			 * since presumably the zone administrator is the same
8200Sstevel@tonic-gate 			 * as the attacker.
8210Sstevel@tonic-gate 			 */
8220Sstevel@tonic-gate 			cmn_err(CE_WARN, "autofs: invalid action list received "
8230Sstevel@tonic-gate 			    "by automountd in zone %s.",
8240Sstevel@tonic-gate 			    curproc->p_zone->zone_name);
8250Sstevel@tonic-gate 			/*
8260Sstevel@tonic-gate 			 * This conversation is over.
8270Sstevel@tonic-gate 			 */
8280Sstevel@tonic-gate 			xdr_free(xdr_action_list, (char *)alp);
8290Sstevel@tonic-gate 			return (EINVAL);
8300Sstevel@tonic-gate 		}
8310Sstevel@tonic-gate 	}
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate 	zcred = zone_get_kcred(getzoneid());
8340Sstevel@tonic-gate 	ASSERT(zcred != NULL);
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 	if (vn_mountedvfs(dvp) != NULL) {
8370Sstevel@tonic-gate 		/*
8380Sstevel@tonic-gate 		 * The daemon successfully mounted a filesystem
8390Sstevel@tonic-gate 		 * on the AUTOFS root node.
8400Sstevel@tonic-gate 		 */
8410Sstevel@tonic-gate 		mutex_enter(&dfnp->fn_lock);
8420Sstevel@tonic-gate 		dfnp->fn_flags |= MF_MOUNTPOINT;
8430Sstevel@tonic-gate 		ASSERT(dfnp->fn_dirents == NULL);
8440Sstevel@tonic-gate 		mutex_exit(&dfnp->fn_lock);
8450Sstevel@tonic-gate 		success++;
8460Sstevel@tonic-gate 	} else {
8470Sstevel@tonic-gate 		/*
8480Sstevel@tonic-gate 		 * Clear MF_MOUNTPOINT.
8490Sstevel@tonic-gate 		 */
8500Sstevel@tonic-gate 		mutex_enter(&dfnp->fn_lock);
8510Sstevel@tonic-gate 		if (dfnp->fn_flags & MF_MOUNTPOINT) {
8520Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "autofs: clearing mountpoint "
8530Sstevel@tonic-gate 			    "flag on %s.", dfnp->fn_name));
8540Sstevel@tonic-gate 			ASSERT(dfnp->fn_dirents == NULL);
8550Sstevel@tonic-gate 			ASSERT(dfnp->fn_trigger == NULL);
8560Sstevel@tonic-gate 		}
8570Sstevel@tonic-gate 		dfnp->fn_flags &= ~MF_MOUNTPOINT;
8580Sstevel@tonic-gate 		mutex_exit(&dfnp->fn_lock);
8590Sstevel@tonic-gate 	}
8600Sstevel@tonic-gate 
8610Sstevel@tonic-gate 	for (p = alp; p != NULL; p = p->next) {
8620Sstevel@tonic-gate 		vfs_t *vfsp;	/* dummy argument */
8630Sstevel@tonic-gate 		vfs_t *mvfsp;
8640Sstevel@tonic-gate 
8650Sstevel@tonic-gate 		auto_mount = 0;
8660Sstevel@tonic-gate 
8670Sstevel@tonic-gate 		m = &p->action.action_list_entry_u.mounta;
8680Sstevel@tonic-gate 		argsp = (struct autofs_args *)m->dataptr;
8690Sstevel@tonic-gate 		/*
8700Sstevel@tonic-gate 		 * use the parent directory's timeout since it's the
8710Sstevel@tonic-gate 		 * one specified/inherited by automount.
8720Sstevel@tonic-gate 		 */
8730Sstevel@tonic-gate 		argsp->mount_to = dfnip->fi_mount_to;
8740Sstevel@tonic-gate 		/*
8750Sstevel@tonic-gate 		 * The mountpoint is relative, and it is guaranteed to
8760Sstevel@tonic-gate 		 * begin with "."
8770Sstevel@tonic-gate 		 *
8780Sstevel@tonic-gate 		 */
8790Sstevel@tonic-gate 		ASSERT(m->dir[0] == '.');
8800Sstevel@tonic-gate 		if (m->dir[0] == '.' && m->dir[1] == '\0') {
8810Sstevel@tonic-gate 			/*
8820Sstevel@tonic-gate 			 * mounting on the trigger node
8830Sstevel@tonic-gate 			 */
8840Sstevel@tonic-gate 			mvp = dvp;
8850Sstevel@tonic-gate 			VN_HOLD(mvp);
8860Sstevel@tonic-gate 			goto mount;
8870Sstevel@tonic-gate 		}
8880Sstevel@tonic-gate 		/*
8890Sstevel@tonic-gate 		 * ignore "./" in front of mountpoint
8900Sstevel@tonic-gate 		 */
8910Sstevel@tonic-gate 		ASSERT(m->dir[1] == '/');
8920Sstevel@tonic-gate 		mntpnt = m->dir + 2;
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tdfnip->fi_path=%s\n", dfnip->fi_path));
8950Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tdfnip->fi_flags=%x\n", dfnip->fi_flags));
8960Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tmntpnt=%s\n", mntpnt));
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 		if (dfnip->fi_flags & MF_DIRECT) {
8990Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tDIRECT\n"));
9000Sstevel@tonic-gate 			(void) sprintf(buff, "%s/%s", dfnip->fi_path, mntpnt);
9010Sstevel@tonic-gate 		} else {
9020Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tINDIRECT\n"));
9030Sstevel@tonic-gate 			(void) sprintf(buff, "%s/%s/%s", dfnip->fi_path,
9040Sstevel@tonic-gate 			    dfnp->fn_name, mntpnt);
9050Sstevel@tonic-gate 		}
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate 		if (vn_mountedvfs(dvp) == NULL) {
9080Sstevel@tonic-gate 			/*
9090Sstevel@tonic-gate 			 * Daemon didn't mount anything on the root
9100Sstevel@tonic-gate 			 * We have to create the mountpoint if it doesn't
9110Sstevel@tonic-gate 			 * exist already
9120Sstevel@tonic-gate 			 *
9130Sstevel@tonic-gate 			 * We use the caller's credentials in case a UID-match
9140Sstevel@tonic-gate 			 * is required (MF_THISUID_MATCH_RQD).
9150Sstevel@tonic-gate 			 */
9160Sstevel@tonic-gate 			rw_enter(&dfnp->fn_rwlock, RW_WRITER);
9170Sstevel@tonic-gate 			error = auto_search(dfnp, mntpnt, &mfnp, cred);
9180Sstevel@tonic-gate 			if (error == 0) {
9190Sstevel@tonic-gate 				/*
9200Sstevel@tonic-gate 				 * AUTOFS mountpoint exists
9210Sstevel@tonic-gate 				 */
9220Sstevel@tonic-gate 				if (vn_mountedvfs(fntovn(mfnp)) != NULL) {
9230Sstevel@tonic-gate 					cmn_err(CE_PANIC,
9240Sstevel@tonic-gate 					    "auto_perform_actions: "
9250Sstevel@tonic-gate 					    "mfnp=%p covered", (void *)mfnp);
9260Sstevel@tonic-gate 				}
9270Sstevel@tonic-gate 			} else {
9280Sstevel@tonic-gate 				/*
9290Sstevel@tonic-gate 				 * Create AUTOFS mountpoint
9300Sstevel@tonic-gate 				 */
9310Sstevel@tonic-gate 				ASSERT((dfnp->fn_flags & MF_MOUNTPOINT) == 0);
9320Sstevel@tonic-gate 				error = auto_enter(dfnp, mntpnt, &mfnp, cred);
9330Sstevel@tonic-gate 				ASSERT(mfnp->fn_linkcnt == 1);
9340Sstevel@tonic-gate 				mfnp->fn_linkcnt++;
9350Sstevel@tonic-gate 			}
9360Sstevel@tonic-gate 			if (!error)
9370Sstevel@tonic-gate 				update_times = 1;
9380Sstevel@tonic-gate 			rw_exit(&dfnp->fn_rwlock);
9390Sstevel@tonic-gate 			ASSERT(error != EEXIST);
9400Sstevel@tonic-gate 			if (!error) {
9410Sstevel@tonic-gate 				/*
9420Sstevel@tonic-gate 				 * mfnp is already held.
9430Sstevel@tonic-gate 				 */
9440Sstevel@tonic-gate 				mvp = fntovn(mfnp);
9450Sstevel@tonic-gate 			} else {
9460Sstevel@tonic-gate 				auto_log(fngp, CE_WARN, "autofs: mount of %s "
9470Sstevel@tonic-gate 				    "failed - can't create mountpoint.", buff);
9480Sstevel@tonic-gate 				continue;
9490Sstevel@tonic-gate 			}
9500Sstevel@tonic-gate 		} else {
9510Sstevel@tonic-gate 			/*
9520Sstevel@tonic-gate 			 * Find mountpoint in VFS mounted here. If not found,
9530Sstevel@tonic-gate 			 * fail the submount, though the overall mount has
9540Sstevel@tonic-gate 			 * succeeded since the root is mounted.
9550Sstevel@tonic-gate 			 */
9560Sstevel@tonic-gate 			if (error = auto_getmntpnt(dvp, mntpnt, &mvp, kcred)) {
9570Sstevel@tonic-gate 				auto_log(fngp, CE_WARN, "autofs: mount of %s "
9580Sstevel@tonic-gate 				    "failed - mountpoint doesn't exist.", buff);
9590Sstevel@tonic-gate 				continue;
9600Sstevel@tonic-gate 			}
9610Sstevel@tonic-gate 			if (mvp->v_type == VLNK) {
9620Sstevel@tonic-gate 				auto_log(fngp, CE_WARN, "autofs: %s symbolic "
9630Sstevel@tonic-gate 				    "link: not a valid mountpoint "
9640Sstevel@tonic-gate 				    "- mount failed", buff);
9650Sstevel@tonic-gate 				VN_RELE(mvp);
9660Sstevel@tonic-gate 				error = ENOENT;
9670Sstevel@tonic-gate 				continue;
9680Sstevel@tonic-gate 			}
9690Sstevel@tonic-gate 		}
9700Sstevel@tonic-gate mount:
9710Sstevel@tonic-gate 		m->flags |= MS_SYSSPACE | MS_OPTIONSTR;
9720Sstevel@tonic-gate 		/*
9730Sstevel@tonic-gate 		 * Copy mounta struct here so we can substitute a buffer
9740Sstevel@tonic-gate 		 * that is large enough to hold the returned option string,
9750Sstevel@tonic-gate 		 * if that string is longer that the input option string.
9760Sstevel@tonic-gate 		 * This can happen if there are default options enabled
9770Sstevel@tonic-gate 		 * that were not in the input option string.
9780Sstevel@tonic-gate 		 */
9790Sstevel@tonic-gate 		bcopy(m, &margs, sizeof (*m));
9800Sstevel@tonic-gate 		margs.optptr = kmem_alloc(MAX_MNTOPT_STR, KM_SLEEP);
9810Sstevel@tonic-gate 		margs.optlen = MAX_MNTOPT_STR;
9820Sstevel@tonic-gate 		(void) strcpy(margs.optptr, m->optptr);
9830Sstevel@tonic-gate 		margs.dir = argsp->path;
9840Sstevel@tonic-gate 		/*
9850Sstevel@tonic-gate 		 * We use the zone's kcred because we don't want the zone to be
9860Sstevel@tonic-gate 		 * able to thus do something it wouldn't normally be able to.
9870Sstevel@tonic-gate 		 */
9880Sstevel@tonic-gate 		error = domount(NULL, &margs, mvp, zcred, &vfsp);
9890Sstevel@tonic-gate 		kmem_free(margs.optptr, MAX_MNTOPT_STR);
9900Sstevel@tonic-gate 		if (error != 0) {
9910Sstevel@tonic-gate 			auto_log(fngp, CE_WARN,
9920Sstevel@tonic-gate 			    "autofs: domount of %s failed error=%d",
9930Sstevel@tonic-gate 			    buff, error);
9940Sstevel@tonic-gate 			VN_RELE(mvp);
9950Sstevel@tonic-gate 			continue;
9960Sstevel@tonic-gate 		}
9970Sstevel@tonic-gate 		VFS_RELE(vfsp);
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate 		/*
10000Sstevel@tonic-gate 		 * If mountpoint is an AUTOFS node, then I'm going to
10010Sstevel@tonic-gate 		 * flag it that the Filesystem mounted on top was mounted
10020Sstevel@tonic-gate 		 * in the kernel so that the unmount can be done inside the
10030Sstevel@tonic-gate 		 * kernel as well.
10040Sstevel@tonic-gate 		 * I don't care to flag non-AUTOFS mountpoints when an AUTOFS
10050Sstevel@tonic-gate 		 * in-kernel mount was done on top, because the unmount
10060Sstevel@tonic-gate 		 * routine already knows that such case was done in the kernel.
10070Sstevel@tonic-gate 		 */
10080Sstevel@tonic-gate 		if (vfs_matchops(dvp->v_vfsp, vfs_getops(mvp->v_vfsp))) {
10090Sstevel@tonic-gate 			mfnp = vntofn(mvp);
10100Sstevel@tonic-gate 			mutex_enter(&mfnp->fn_lock);
10110Sstevel@tonic-gate 			mfnp->fn_flags |= MF_IK_MOUNT;
10120Sstevel@tonic-gate 			mutex_exit(&mfnp->fn_lock);
10130Sstevel@tonic-gate 		}
10140Sstevel@tonic-gate 
1015*1153Snr123932 		(void) vn_vfsrlock_wait(mvp);
10160Sstevel@tonic-gate 		mvfsp = vn_mountedvfs(mvp);
10170Sstevel@tonic-gate 		if (mvfsp != NULL) {
1018*1153Snr123932 			error = VFS_ROOT(mvfsp, &newvp);
10190Sstevel@tonic-gate 			vn_vfsunlock(mvp);
10200Sstevel@tonic-gate 			if (error) {
10210Sstevel@tonic-gate 				/*
10220Sstevel@tonic-gate 				 * We've dropped the locks, so let's get
10230Sstevel@tonic-gate 				 * the mounted vfs again in case it changed.
10240Sstevel@tonic-gate 				 */
10250Sstevel@tonic-gate 				(void) vn_vfswlock_wait(mvp);
10260Sstevel@tonic-gate 				mvfsp = vn_mountedvfs(mvp);
10270Sstevel@tonic-gate 				if (mvfsp != NULL) {
10280Sstevel@tonic-gate 					error = dounmount(mvfsp, 0, CRED());
10290Sstevel@tonic-gate 					if (error) {
10300Sstevel@tonic-gate 						cmn_err(CE_WARN,
10310Sstevel@tonic-gate 						    "autofs: could not "
10320Sstevel@tonic-gate 						    "unmount vfs=%p",
10330Sstevel@tonic-gate 						(void *)mvfsp);
10340Sstevel@tonic-gate 					}
10350Sstevel@tonic-gate 				} else
10360Sstevel@tonic-gate 					vn_vfsunlock(mvp);
10370Sstevel@tonic-gate 				VN_RELE(mvp);
10380Sstevel@tonic-gate 				continue;
10390Sstevel@tonic-gate 			}
10400Sstevel@tonic-gate 		} else {
10410Sstevel@tonic-gate 			vn_vfsunlock(mvp);
10420Sstevel@tonic-gate 			VN_RELE(mvp);
10430Sstevel@tonic-gate 			continue;
10440Sstevel@tonic-gate 		}
10450Sstevel@tonic-gate 
10460Sstevel@tonic-gate 		auto_mount = vfs_matchops(dvp->v_vfsp,
10470Sstevel@tonic-gate 						vfs_getops(newvp->v_vfsp));
10480Sstevel@tonic-gate 		newfnp = vntofn(newvp);
10490Sstevel@tonic-gate 		newfnp->fn_parent = dfnp;
10500Sstevel@tonic-gate 
10510Sstevel@tonic-gate 		/*
10520Sstevel@tonic-gate 		 * At this time we want to save the AUTOFS filesystem as
10530Sstevel@tonic-gate 		 * a trigger node. (We only do this if the mount occured
10540Sstevel@tonic-gate 		 * on a node different from the root.
10550Sstevel@tonic-gate 		 * We look at the trigger nodes during
10560Sstevel@tonic-gate 		 * the automatic unmounting to make sure we remove them
10570Sstevel@tonic-gate 		 * as a unit and remount them as a unit if the filesystem
10580Sstevel@tonic-gate 		 * mounted at the root could not be unmounted.
10590Sstevel@tonic-gate 		 */
10600Sstevel@tonic-gate 		if (auto_mount && (error == 0) && (mvp != dvp)) {
10610Sstevel@tonic-gate 			save_triggers++;
10620Sstevel@tonic-gate 			/*
10630Sstevel@tonic-gate 			 * Add AUTOFS mount to hierarchy
10640Sstevel@tonic-gate 			 */
10650Sstevel@tonic-gate 			newfnp->fn_flags |= MF_TRIGGER;
10660Sstevel@tonic-gate 			rw_enter(&newfnp->fn_rwlock, RW_WRITER);
10670Sstevel@tonic-gate 			newfnp->fn_next = dfnp->fn_trigger;
10680Sstevel@tonic-gate 			rw_exit(&newfnp->fn_rwlock);
10690Sstevel@tonic-gate 			rw_enter(&dfnp->fn_rwlock, RW_WRITER);
10700Sstevel@tonic-gate 			dfnp->fn_trigger = newfnp;
10710Sstevel@tonic-gate 			rw_exit(&dfnp->fn_rwlock);
10720Sstevel@tonic-gate 			/*
10730Sstevel@tonic-gate 			 * Don't VN_RELE(newvp) here since dfnp now holds
10740Sstevel@tonic-gate 			 * reference to it as its trigger node.
10750Sstevel@tonic-gate 			 */
10760Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tadding trigger %s to %s\n",
10770Sstevel@tonic-gate 			    newfnp->fn_name, dfnp->fn_name));
10780Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tfirst trigger is %s\n",
10790Sstevel@tonic-gate 			    dfnp->fn_trigger->fn_name));
10800Sstevel@tonic-gate 			if (newfnp->fn_next != NULL)
10810Sstevel@tonic-gate 				AUTOFS_DPRINT((10, "\tnext trigger is %s\n",
10820Sstevel@tonic-gate 				    newfnp->fn_next->fn_name));
10830Sstevel@tonic-gate 			else
10840Sstevel@tonic-gate 				AUTOFS_DPRINT((10, "\tno next trigger\n"));
10850Sstevel@tonic-gate 		} else
10860Sstevel@tonic-gate 			VN_RELE(newvp);
10870Sstevel@tonic-gate 
10880Sstevel@tonic-gate 		if (!error)
10890Sstevel@tonic-gate 			success++;
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 		if (update_times) {
10920Sstevel@tonic-gate 			gethrestime(&now);
10930Sstevel@tonic-gate 			dfnp->fn_atime = dfnp->fn_mtime = now;
10940Sstevel@tonic-gate 		}
10950Sstevel@tonic-gate 
10960Sstevel@tonic-gate 		VN_RELE(mvp);
10970Sstevel@tonic-gate 	}
10980Sstevel@tonic-gate 
10990Sstevel@tonic-gate 	if (save_triggers) {
11000Sstevel@tonic-gate 		/*
11010Sstevel@tonic-gate 		 * Make sure the parent can't be freed while it has triggers.
11020Sstevel@tonic-gate 		 */
11030Sstevel@tonic-gate 		VN_HOLD(dvp);
11040Sstevel@tonic-gate 	}
11050Sstevel@tonic-gate 
11060Sstevel@tonic-gate 	crfree(zcred);
11070Sstevel@tonic-gate 
11080Sstevel@tonic-gate done:
11090Sstevel@tonic-gate 	/*
11100Sstevel@tonic-gate 	 * Return failure if daemon didn't mount anything, and all
11110Sstevel@tonic-gate 	 * kernel mounts attempted failed.
11120Sstevel@tonic-gate 	 */
11130Sstevel@tonic-gate 	error = success ? 0 : ENOENT;
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 	if (alp != NULL) {
11160Sstevel@tonic-gate 		if ((error == 0) && save_triggers) {
11170Sstevel@tonic-gate 			/*
11180Sstevel@tonic-gate 			 * Save action_list information, so that we can use it
11190Sstevel@tonic-gate 			 * when it comes time to remount the trigger nodes
11200Sstevel@tonic-gate 			 * The action list is freed when the directory node
11210Sstevel@tonic-gate 			 * containing the reference to it is unmounted in
11220Sstevel@tonic-gate 			 * unmount_tree().
11230Sstevel@tonic-gate 			 */
11240Sstevel@tonic-gate 			mutex_enter(&dfnp->fn_lock);
11250Sstevel@tonic-gate 			ASSERT(dfnp->fn_alp == NULL);
11260Sstevel@tonic-gate 			dfnp->fn_alp = alp;
11270Sstevel@tonic-gate 			mutex_exit(&dfnp->fn_lock);
11280Sstevel@tonic-gate 		} else {
11290Sstevel@tonic-gate 			/*
11300Sstevel@tonic-gate 			 * free the action list now,
11310Sstevel@tonic-gate 			 */
11320Sstevel@tonic-gate 			xdr_free(xdr_action_list, (char *)alp);
11330Sstevel@tonic-gate 		}
11340Sstevel@tonic-gate 	}
11350Sstevel@tonic-gate 
11360Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_perform_actions: error=%d\n", error));
11370Sstevel@tonic-gate 	return (error);
11380Sstevel@tonic-gate }
11390Sstevel@tonic-gate 
11400Sstevel@tonic-gate fnnode_t *
11410Sstevel@tonic-gate auto_makefnnode(
11420Sstevel@tonic-gate 	vtype_t type,
11430Sstevel@tonic-gate 	vfs_t *vfsp,
11440Sstevel@tonic-gate 	char *name,
11450Sstevel@tonic-gate 	cred_t *cred,
11460Sstevel@tonic-gate 	struct autofs_globals *fngp)
11470Sstevel@tonic-gate {
11480Sstevel@tonic-gate 	fnnode_t *fnp;
11490Sstevel@tonic-gate 	vnode_t *vp;
11500Sstevel@tonic-gate 	char *tmpname;
11510Sstevel@tonic-gate 	timestruc_t now;
11520Sstevel@tonic-gate 	/*
11530Sstevel@tonic-gate 	 * autofs uses odd inode numbers
11540Sstevel@tonic-gate 	 * automountd uses even inode numbers
11550Sstevel@tonic-gate 	 *
11560Sstevel@tonic-gate 	 * To preserve the age-old semantics that inum+devid is unique across
11570Sstevel@tonic-gate 	 * the system, this variable must be global across zones.
11580Sstevel@tonic-gate 	 */
11590Sstevel@tonic-gate 	static ino_t nodeid = 3;
11600Sstevel@tonic-gate 
11610Sstevel@tonic-gate 	fnp = kmem_zalloc(sizeof (*fnp), KM_SLEEP);
11620Sstevel@tonic-gate 	fnp->fn_vnode = vn_alloc(KM_SLEEP);
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 	vp = fntovn(fnp);
11650Sstevel@tonic-gate 	tmpname = kmem_alloc(strlen(name) + 1, KM_SLEEP);
11660Sstevel@tonic-gate 	(void) strcpy(tmpname, name);
11670Sstevel@tonic-gate 	fnp->fn_name = &tmpname[0];
11680Sstevel@tonic-gate 	fnp->fn_namelen = (int)strlen(tmpname) + 1;	/* include '\0' */
11690Sstevel@tonic-gate 	fnp->fn_uid = crgetuid(cred);
11700Sstevel@tonic-gate 	fnp->fn_gid = crgetgid(cred);
11710Sstevel@tonic-gate 	/*
11720Sstevel@tonic-gate 	 * ".." is added in auto_enter and auto_mount.
11730Sstevel@tonic-gate 	 * "." is added in auto_mkdir and auto_mount.
11740Sstevel@tonic-gate 	 */
11750Sstevel@tonic-gate 	/*
11760Sstevel@tonic-gate 	 * Note that fn_size and fn_linkcnt are already 0 since
11770Sstevel@tonic-gate 	 * we used kmem_zalloc to allocated fnp
11780Sstevel@tonic-gate 	 */
11790Sstevel@tonic-gate 	fnp->fn_mode = AUTOFS_MODE;
11800Sstevel@tonic-gate 	gethrestime(&now);
11810Sstevel@tonic-gate 	fnp->fn_atime = fnp->fn_mtime = fnp->fn_ctime = now;
11820Sstevel@tonic-gate 	fnp->fn_ref_time = now.tv_sec;
11830Sstevel@tonic-gate 	mutex_enter(&autofs_nodeid_lock);
11840Sstevel@tonic-gate 	fnp->fn_nodeid = nodeid;
11850Sstevel@tonic-gate 	nodeid += 2;
11860Sstevel@tonic-gate 	fnp->fn_globals = fngp;
11870Sstevel@tonic-gate 	fngp->fng_fnnode_count++;
11880Sstevel@tonic-gate 	mutex_exit(&autofs_nodeid_lock);
11890Sstevel@tonic-gate 	vn_setops(vp, auto_vnodeops);
11900Sstevel@tonic-gate 	vp->v_type = type;
11910Sstevel@tonic-gate 	vp->v_data = (void *)fnp;
11920Sstevel@tonic-gate 	vp->v_vfsp = vfsp;
11930Sstevel@tonic-gate 	mutex_init(&fnp->fn_lock, NULL, MUTEX_DEFAULT, NULL);
11940Sstevel@tonic-gate 	rw_init(&fnp->fn_rwlock, NULL, RW_DEFAULT, NULL);
11950Sstevel@tonic-gate 	cv_init(&fnp->fn_cv_mount, NULL, CV_DEFAULT, NULL);
11960Sstevel@tonic-gate 	vn_exists(vp);
11970Sstevel@tonic-gate 	return (fnp);
11980Sstevel@tonic-gate }
11990Sstevel@tonic-gate 
12000Sstevel@tonic-gate 
12010Sstevel@tonic-gate void
12020Sstevel@tonic-gate auto_freefnnode(fnnode_t *fnp)
12030Sstevel@tonic-gate {
12040Sstevel@tonic-gate 	vnode_t *vp = fntovn(fnp);
12050Sstevel@tonic-gate 
12060Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_freefnnode: fnp=%p\n", (void *)fnp));
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate 	ASSERT(fnp->fn_linkcnt == 0);
12090Sstevel@tonic-gate 	ASSERT(vp->v_count == 0);
12100Sstevel@tonic-gate 	ASSERT(fnp->fn_dirents == NULL);
12110Sstevel@tonic-gate 	ASSERT(fnp->fn_parent == NULL);
12120Sstevel@tonic-gate 
12130Sstevel@tonic-gate 	vn_invalid(vp);
12140Sstevel@tonic-gate 	kmem_free(fnp->fn_name, fnp->fn_namelen);
12150Sstevel@tonic-gate 	if (fnp->fn_symlink) {
12160Sstevel@tonic-gate 		ASSERT(fnp->fn_flags & MF_THISUID_MATCH_RQD);
12170Sstevel@tonic-gate 		kmem_free(fnp->fn_symlink, fnp->fn_symlinklen);
12180Sstevel@tonic-gate 	}
12190Sstevel@tonic-gate 	if (fnp->fn_cred)
12200Sstevel@tonic-gate 		crfree(fnp->fn_cred);
12210Sstevel@tonic-gate 	mutex_destroy(&fnp->fn_lock);
12220Sstevel@tonic-gate 	rw_destroy(&fnp->fn_rwlock);
12230Sstevel@tonic-gate 	cv_destroy(&fnp->fn_cv_mount);
12240Sstevel@tonic-gate 	vn_free(vp);
12250Sstevel@tonic-gate 
12260Sstevel@tonic-gate 	mutex_enter(&autofs_nodeid_lock);
12270Sstevel@tonic-gate 	fnp->fn_globals->fng_fnnode_count--;
12280Sstevel@tonic-gate 	mutex_exit(&autofs_nodeid_lock);
12290Sstevel@tonic-gate 	kmem_free(fnp, sizeof (*fnp));
12300Sstevel@tonic-gate }
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate void
12330Sstevel@tonic-gate auto_disconnect(
12340Sstevel@tonic-gate 	fnnode_t *dfnp,
12350Sstevel@tonic-gate 	fnnode_t *fnp)
12360Sstevel@tonic-gate {
12370Sstevel@tonic-gate 	fnnode_t *tmp, **fnpp;
12380Sstevel@tonic-gate 	vnode_t *vp = fntovn(fnp);
12390Sstevel@tonic-gate 	timestruc_t now;
12400Sstevel@tonic-gate 
12410Sstevel@tonic-gate 	AUTOFS_DPRINT((4,
12420Sstevel@tonic-gate 	    "auto_disconnect: dfnp=%p fnp=%p linkcnt=%d\n v_count=%d",
12430Sstevel@tonic-gate 	    (void *)dfnp, (void *)fnp, fnp->fn_linkcnt, vp->v_count));
12440Sstevel@tonic-gate 
12450Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock));
12460Sstevel@tonic-gate 	ASSERT(fnp->fn_linkcnt == 1);
12470Sstevel@tonic-gate 
12480Sstevel@tonic-gate 	if (vn_mountedvfs(vp) != NULL) {
12490Sstevel@tonic-gate 		cmn_err(CE_PANIC, "auto_disconnect: vp %p mounted on",
12500Sstevel@tonic-gate 		    (void *)vp);
12510Sstevel@tonic-gate 	}
12520Sstevel@tonic-gate 
12530Sstevel@tonic-gate 	/*
12540Sstevel@tonic-gate 	 * Decrement by 1 because we're removing the entry in dfnp.
12550Sstevel@tonic-gate 	 */
12560Sstevel@tonic-gate 	fnp->fn_linkcnt--;
12570Sstevel@tonic-gate 	fnp->fn_size--;
12580Sstevel@tonic-gate 
12590Sstevel@tonic-gate 	/*
12600Sstevel@tonic-gate 	 * only changed while holding parent's (dfnp) rw_lock
12610Sstevel@tonic-gate 	 */
12620Sstevel@tonic-gate 	fnp->fn_parent = NULL;
12630Sstevel@tonic-gate 
12640Sstevel@tonic-gate 	fnpp = &dfnp->fn_dirents;
12650Sstevel@tonic-gate 	for (;;) {
12660Sstevel@tonic-gate 		tmp = *fnpp;
12670Sstevel@tonic-gate 		if (tmp == NULL) {
12680Sstevel@tonic-gate 			cmn_err(CE_PANIC,
12690Sstevel@tonic-gate 			    "auto_disconnect: %p not in %p dirent list",
12700Sstevel@tonic-gate 			    (void *)fnp, (void *)dfnp);
12710Sstevel@tonic-gate 		}
12720Sstevel@tonic-gate 		if (tmp == fnp) {
12730Sstevel@tonic-gate 			*fnpp = tmp->fn_next; 	/* remove it from the list */
12740Sstevel@tonic-gate 			ASSERT(vp->v_count == 0);
12750Sstevel@tonic-gate 			/* child had a pointer to parent ".." */
12760Sstevel@tonic-gate 			dfnp->fn_linkcnt--;
12770Sstevel@tonic-gate 			dfnp->fn_size--;
12780Sstevel@tonic-gate 			break;
12790Sstevel@tonic-gate 		}
12800Sstevel@tonic-gate 		fnpp = &tmp->fn_next;
12810Sstevel@tonic-gate 	}
12820Sstevel@tonic-gate 
12830Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
12840Sstevel@tonic-gate 	gethrestime(&now);
12850Sstevel@tonic-gate 	fnp->fn_atime = fnp->fn_mtime = now;
12860Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
12870Sstevel@tonic-gate 
12880Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_disconnect: done\n"));
12890Sstevel@tonic-gate }
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate int
12920Sstevel@tonic-gate auto_enter(fnnode_t *dfnp, char *name, fnnode_t **fnpp, cred_t *cred)
12930Sstevel@tonic-gate {
12940Sstevel@tonic-gate 	struct fnnode *cfnp, **spp;
12950Sstevel@tonic-gate 	vnode_t *dvp = fntovn(dfnp);
12960Sstevel@tonic-gate 	ushort_t offset = 0;
12970Sstevel@tonic-gate 	ushort_t diff;
12980Sstevel@tonic-gate 
12990Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_enter: dfnp=%p, name=%s ", (void *)dfnp, name));
13000Sstevel@tonic-gate 
13010Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock));
13020Sstevel@tonic-gate 
13030Sstevel@tonic-gate 	cfnp = dfnp->fn_dirents;
13040Sstevel@tonic-gate 	if (cfnp == NULL) {
13050Sstevel@tonic-gate 		/*
13060Sstevel@tonic-gate 		 * offset = 0 for '.' and offset = 1 for '..'
13070Sstevel@tonic-gate 		 */
13080Sstevel@tonic-gate 		spp = &dfnp->fn_dirents;
13090Sstevel@tonic-gate 		offset = 2;
13100Sstevel@tonic-gate 	}
13110Sstevel@tonic-gate 
13120Sstevel@tonic-gate 	for (; cfnp; cfnp = cfnp->fn_next) {
13130Sstevel@tonic-gate 		if (strcmp(cfnp->fn_name, name) == 0) {
13140Sstevel@tonic-gate 			mutex_enter(&cfnp->fn_lock);
13150Sstevel@tonic-gate 			if (cfnp->fn_flags & MF_THISUID_MATCH_RQD) {
13160Sstevel@tonic-gate 				/*
13170Sstevel@tonic-gate 				 * "thisuser" kind of node, need to
13180Sstevel@tonic-gate 				 * match CREDs as well
13190Sstevel@tonic-gate 				 */
13200Sstevel@tonic-gate 				mutex_exit(&cfnp->fn_lock);
13210Sstevel@tonic-gate 				if (crcmp(cfnp->fn_cred, cred) == 0)
13220Sstevel@tonic-gate 					return (EEXIST);
13230Sstevel@tonic-gate 			} else {
13240Sstevel@tonic-gate 				mutex_exit(&cfnp->fn_lock);
13250Sstevel@tonic-gate 				return (EEXIST);
13260Sstevel@tonic-gate 			}
13270Sstevel@tonic-gate 		}
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate 		if (cfnp->fn_next != NULL) {
13300Sstevel@tonic-gate 			diff = (ushort_t)
13310Sstevel@tonic-gate 			    (cfnp->fn_next->fn_offset - cfnp->fn_offset);
13320Sstevel@tonic-gate 			ASSERT(diff != 0);
13330Sstevel@tonic-gate 			if (diff > 1 && offset == 0) {
13340Sstevel@tonic-gate 				offset = (ushort_t)cfnp->fn_offset + 1;
13350Sstevel@tonic-gate 				spp = &cfnp->fn_next;
13360Sstevel@tonic-gate 			}
13370Sstevel@tonic-gate 		} else if (offset == 0) {
13380Sstevel@tonic-gate 			offset = (ushort_t)cfnp->fn_offset + 1;
13390Sstevel@tonic-gate 			spp = &cfnp->fn_next;
13400Sstevel@tonic-gate 		}
13410Sstevel@tonic-gate 	}
13420Sstevel@tonic-gate 
13430Sstevel@tonic-gate 	*fnpp = auto_makefnnode(VDIR, dvp->v_vfsp, name, cred,
13440Sstevel@tonic-gate 	    dfnp->fn_globals);
13450Sstevel@tonic-gate 	if (*fnpp == NULL)
13460Sstevel@tonic-gate 		return (ENOMEM);
13470Sstevel@tonic-gate 
13480Sstevel@tonic-gate 	/*
13490Sstevel@tonic-gate 	 * I don't hold the mutex on fnpp because I created it, and
13500Sstevel@tonic-gate 	 * I'm already holding the writers lock for it's parent
13510Sstevel@tonic-gate 	 * directory, therefore nobody can reference it without me first
13520Sstevel@tonic-gate 	 * releasing the writers lock.
13530Sstevel@tonic-gate 	 */
13540Sstevel@tonic-gate 	(*fnpp)->fn_offset = offset;
13550Sstevel@tonic-gate 	(*fnpp)->fn_next = *spp;
13560Sstevel@tonic-gate 	*spp = *fnpp;
13570Sstevel@tonic-gate 	(*fnpp)->fn_parent = dfnp;
13580Sstevel@tonic-gate 	(*fnpp)->fn_linkcnt++;	/* parent now holds reference to entry */
13590Sstevel@tonic-gate 	(*fnpp)->fn_size++;
13600Sstevel@tonic-gate 
13610Sstevel@tonic-gate 	/*
13620Sstevel@tonic-gate 	 * dfnp->fn_linkcnt and dfnp->fn_size protected by dfnp->rw_lock
13630Sstevel@tonic-gate 	 */
13640Sstevel@tonic-gate 	dfnp->fn_linkcnt++;	/* child now holds reference to parent '..' */
13650Sstevel@tonic-gate 	dfnp->fn_size++;
13660Sstevel@tonic-gate 
13670Sstevel@tonic-gate 	dfnp->fn_ref_time = gethrestime_sec();
13680Sstevel@tonic-gate 
13690Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "*fnpp=%p\n", (void *)*fnpp));
13700Sstevel@tonic-gate 	return (0);
13710Sstevel@tonic-gate }
13720Sstevel@tonic-gate 
13730Sstevel@tonic-gate int
13740Sstevel@tonic-gate auto_search(fnnode_t *dfnp, char *name, fnnode_t **fnpp, cred_t *cred)
13750Sstevel@tonic-gate {
13760Sstevel@tonic-gate 	vnode_t *dvp;
13770Sstevel@tonic-gate 	fnnode_t *p;
13780Sstevel@tonic-gate 	int error = ENOENT, match = 0;
13790Sstevel@tonic-gate 
13800Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_search: dfnp=%p, name=%s...\n",
13810Sstevel@tonic-gate 	    (void *)dfnp, name));
13820Sstevel@tonic-gate 
13830Sstevel@tonic-gate 	dvp = fntovn(dfnp);
13840Sstevel@tonic-gate 	if (dvp->v_type != VDIR) {
13850Sstevel@tonic-gate 		cmn_err(CE_PANIC, "auto_search: dvp=%p not a directory",
13860Sstevel@tonic-gate 		    (void *)dvp);
13870Sstevel@tonic-gate 	}
13880Sstevel@tonic-gate 
13890Sstevel@tonic-gate 	ASSERT(RW_LOCK_HELD(&dfnp->fn_rwlock));
13900Sstevel@tonic-gate 	for (p = dfnp->fn_dirents; p != NULL; p = p->fn_next) {
13910Sstevel@tonic-gate 		if (strcmp(p->fn_name, name) == 0) {
13920Sstevel@tonic-gate 			mutex_enter(&p->fn_lock);
13930Sstevel@tonic-gate 			if (p->fn_flags & MF_THISUID_MATCH_RQD) {
13940Sstevel@tonic-gate 				/*
13950Sstevel@tonic-gate 				 * "thisuser" kind of node
13960Sstevel@tonic-gate 				 * Need to match CREDs as well
13970Sstevel@tonic-gate 				 */
13980Sstevel@tonic-gate 				mutex_exit(&p->fn_lock);
13990Sstevel@tonic-gate 				match = crcmp(p->fn_cred, cred) == 0;
14000Sstevel@tonic-gate 			} else {
14010Sstevel@tonic-gate 				/*
14020Sstevel@tonic-gate 				 * No need to check CRED
14030Sstevel@tonic-gate 				 */
14040Sstevel@tonic-gate 				mutex_exit(&p->fn_lock);
14050Sstevel@tonic-gate 				match = 1;
14060Sstevel@tonic-gate 			}
14070Sstevel@tonic-gate 		}
14080Sstevel@tonic-gate 		if (match) {
14090Sstevel@tonic-gate 			error = 0;
14100Sstevel@tonic-gate 			if (fnpp) {
14110Sstevel@tonic-gate 				*fnpp = p;
14120Sstevel@tonic-gate 				VN_HOLD(fntovn(*fnpp));
14130Sstevel@tonic-gate 			}
14140Sstevel@tonic-gate 			break;
14150Sstevel@tonic-gate 		}
14160Sstevel@tonic-gate 	}
14170Sstevel@tonic-gate 
14180Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_search: error=%d\n", error));
14190Sstevel@tonic-gate 	return (error);
14200Sstevel@tonic-gate }
14210Sstevel@tonic-gate 
14220Sstevel@tonic-gate /*
14230Sstevel@tonic-gate  * If dvp is mounted on, get path's vnode in the mounted on
14240Sstevel@tonic-gate  * filesystem.  Path is relative to dvp, ie "./path".
14250Sstevel@tonic-gate  * If successful, *mvp points to a the held mountpoint vnode.
14260Sstevel@tonic-gate  */
14270Sstevel@tonic-gate /* ARGSUSED */
14280Sstevel@tonic-gate static int
14290Sstevel@tonic-gate auto_getmntpnt(
14300Sstevel@tonic-gate 	vnode_t *dvp,
14310Sstevel@tonic-gate 	char *path,
14320Sstevel@tonic-gate 	vnode_t **mvpp,		/* vnode for mountpoint */
14330Sstevel@tonic-gate 	cred_t *cred)
14340Sstevel@tonic-gate {
14350Sstevel@tonic-gate 	int error = 0;
14360Sstevel@tonic-gate 	vnode_t *newvp;
14370Sstevel@tonic-gate 	char namebuf[TYPICALMAXPATHLEN];
14380Sstevel@tonic-gate 	struct pathname lookpn;
14390Sstevel@tonic-gate 	vfs_t *vfsp;
14400Sstevel@tonic-gate 
14410Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_getmntpnt: path=%s\n", path));
14420Sstevel@tonic-gate 
1443*1153Snr123932 	if (error = vn_vfsrlock_wait(dvp))
14440Sstevel@tonic-gate 		return (error);
14450Sstevel@tonic-gate 
14460Sstevel@tonic-gate 	/*
14470Sstevel@tonic-gate 	 * Now that we have the vfswlock, check to see if dvp
14480Sstevel@tonic-gate 	 * is still mounted on.  If not, then just bail out as
14490Sstevel@tonic-gate 	 * there is no need to remount the triggers since the
14500Sstevel@tonic-gate 	 * higher level mount point has gotten unmounted.
14510Sstevel@tonic-gate 	 */
14520Sstevel@tonic-gate 	vfsp = vn_mountedvfs(dvp);
14530Sstevel@tonic-gate 	if (vfsp == NULL) {
14540Sstevel@tonic-gate 		vn_vfsunlock(dvp);
14550Sstevel@tonic-gate 		error = EBUSY;
14560Sstevel@tonic-gate 		goto done;
14570Sstevel@tonic-gate 	}
14580Sstevel@tonic-gate 	/*
14590Sstevel@tonic-gate 	 * Since mounted on, lookup "path" in the new filesystem,
14600Sstevel@tonic-gate 	 * it is important that we do the filesystem jump here to
14610Sstevel@tonic-gate 	 * avoid lookuppn() calling auto_lookup on dvp and deadlock.
14620Sstevel@tonic-gate 	 */
1463*1153Snr123932 	error = VFS_ROOT(vfsp, &newvp);
14640Sstevel@tonic-gate 	vn_vfsunlock(dvp);
14650Sstevel@tonic-gate 	if (error)
14660Sstevel@tonic-gate 		goto done;
14670Sstevel@tonic-gate 
14680Sstevel@tonic-gate 	/*
14690Sstevel@tonic-gate 	 * We do a VN_HOLD on newvp just in case the first call to
14700Sstevel@tonic-gate 	 * lookuppnvp() fails with ENAMETOOLONG.  We should still have a
14710Sstevel@tonic-gate 	 * reference to this vnode for the second call to lookuppnvp().
14720Sstevel@tonic-gate 	 */
14730Sstevel@tonic-gate 	VN_HOLD(newvp);
14740Sstevel@tonic-gate 
14750Sstevel@tonic-gate 	/*
14760Sstevel@tonic-gate 	 * Now create the pathname struct so we can make use of lookuppnvp,
14770Sstevel@tonic-gate 	 * and pn_getcomponent.
14780Sstevel@tonic-gate 	 * This code is similar to lookupname() in fs/lookup.c.
14790Sstevel@tonic-gate 	 */
14800Sstevel@tonic-gate 	error = pn_get_buf(path, UIO_SYSSPACE, &lookpn,
14810Sstevel@tonic-gate 		namebuf, sizeof (namebuf));
14820Sstevel@tonic-gate 	if (error == 0) {
14830Sstevel@tonic-gate 		error = lookuppnvp(&lookpn, NULL, NO_FOLLOW, NULLVPP,
14840Sstevel@tonic-gate 		    mvpp, rootdir, newvp, cred);
14850Sstevel@tonic-gate 	} else
14860Sstevel@tonic-gate 		VN_RELE(newvp);
14870Sstevel@tonic-gate 	if (error == ENAMETOOLONG) {
14880Sstevel@tonic-gate 		/*
14890Sstevel@tonic-gate 		 * This thread used a pathname > TYPICALMAXPATHLEN bytes long.
14900Sstevel@tonic-gate 		 * newvp is VN_RELE'd by this call to lookuppnvp.
14910Sstevel@tonic-gate 		 *
14920Sstevel@tonic-gate 		 * Using 'rootdir' in a zone's context is OK here: we already
14930Sstevel@tonic-gate 		 * ascertained that there are no '..'s in the path, and we're
14940Sstevel@tonic-gate 		 * not following symlinks.
14950Sstevel@tonic-gate 		 */
14960Sstevel@tonic-gate 		if ((error = pn_get(path, UIO_SYSSPACE, &lookpn)) == 0) {
14970Sstevel@tonic-gate 			error = lookuppnvp(&lookpn, NULL, NO_FOLLOW, NULLVPP,
14980Sstevel@tonic-gate 			    mvpp, rootdir, newvp, cred);
14990Sstevel@tonic-gate 			pn_free(&lookpn);
15000Sstevel@tonic-gate 		} else
15010Sstevel@tonic-gate 			VN_RELE(newvp);
15020Sstevel@tonic-gate 	} else {
15030Sstevel@tonic-gate 		/*
15040Sstevel@tonic-gate 		 * Need to release newvp here since we held it.
15050Sstevel@tonic-gate 		 */
15060Sstevel@tonic-gate 		VN_RELE(newvp);
15070Sstevel@tonic-gate 	}
15080Sstevel@tonic-gate 
15090Sstevel@tonic-gate done:
15100Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_getmntpnt: path=%s *mvpp=%p error=%d\n",
15110Sstevel@tonic-gate 	    path, (void *)*mvpp, error));
15120Sstevel@tonic-gate 	return (error);
15130Sstevel@tonic-gate }
15140Sstevel@tonic-gate 
15150Sstevel@tonic-gate #define	DEEPER(x) (((x)->fn_dirents != NULL) || \
15160Sstevel@tonic-gate 			(vn_mountedvfs(fntovn((x)))) != NULL)
15170Sstevel@tonic-gate 
15180Sstevel@tonic-gate /*
15190Sstevel@tonic-gate  * The caller, should have already VN_RELE'd its reference to the
15200Sstevel@tonic-gate  * root vnode of this filesystem.
15210Sstevel@tonic-gate  */
15220Sstevel@tonic-gate static int
15230Sstevel@tonic-gate auto_inkernel_unmount(vfs_t *vfsp)
15240Sstevel@tonic-gate {
15250Sstevel@tonic-gate 	vnode_t *cvp = vfsp->vfs_vnodecovered;
15260Sstevel@tonic-gate 	int error;
15270Sstevel@tonic-gate 
15280Sstevel@tonic-gate 	AUTOFS_DPRINT((4,
15290Sstevel@tonic-gate 	    "auto_inkernel_unmount: devid=%lx mntpnt(%p) count %u\n",
15300Sstevel@tonic-gate 	    vfsp->vfs_dev, (void *)cvp, cvp->v_count));
15310Sstevel@tonic-gate 
15320Sstevel@tonic-gate 	ASSERT(vn_vfswlock_held(cvp));
15330Sstevel@tonic-gate 
15340Sstevel@tonic-gate 	/*
15350Sstevel@tonic-gate 	 * Perform the unmount
15360Sstevel@tonic-gate 	 * The mountpoint has already been locked by the caller.
15370Sstevel@tonic-gate 	 */
15380Sstevel@tonic-gate 	error = dounmount(vfsp, 0, kcred);
15390Sstevel@tonic-gate 
15400Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_inkernel_unmount: exit count %u\n",
15410Sstevel@tonic-gate 	    cvp->v_count));
15420Sstevel@tonic-gate 	return (error);
15430Sstevel@tonic-gate }
15440Sstevel@tonic-gate 
15450Sstevel@tonic-gate /*
15460Sstevel@tonic-gate  * unmounts trigger nodes in the kernel.
15470Sstevel@tonic-gate  */
15480Sstevel@tonic-gate static void
15490Sstevel@tonic-gate unmount_triggers(fnnode_t *fnp, action_list **alp)
15500Sstevel@tonic-gate {
15510Sstevel@tonic-gate 	fnnode_t *tp, *next;
15520Sstevel@tonic-gate 	int error = 0;
15530Sstevel@tonic-gate 	vfs_t *vfsp;
15540Sstevel@tonic-gate 	vnode_t *tvp;
15550Sstevel@tonic-gate 
15560Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "unmount_triggers: fnp=%p\n", (void *)fnp));
15570Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock));
15580Sstevel@tonic-gate 
15590Sstevel@tonic-gate 	*alp = fnp->fn_alp;
15600Sstevel@tonic-gate 	next = fnp->fn_trigger;
15610Sstevel@tonic-gate 	while ((tp = next) != NULL) {
15620Sstevel@tonic-gate 		tvp = fntovn(tp);
15630Sstevel@tonic-gate 		ASSERT(tvp->v_count >= 2);
15640Sstevel@tonic-gate 		next = tp->fn_next;
15650Sstevel@tonic-gate 		/*
15660Sstevel@tonic-gate 		 * drop writer's lock since the unmount will end up
15670Sstevel@tonic-gate 		 * disconnecting this node from fnp and needs to acquire
15680Sstevel@tonic-gate 		 * the writer's lock again.
15690Sstevel@tonic-gate 		 * next has at least a reference count >= 2 since it's
15700Sstevel@tonic-gate 		 * a trigger node, therefore can not be accidentally freed
15710Sstevel@tonic-gate 		 * by a VN_RELE
15720Sstevel@tonic-gate 		 */
15730Sstevel@tonic-gate 		rw_exit(&fnp->fn_rwlock);
15740Sstevel@tonic-gate 
15750Sstevel@tonic-gate 		vfsp = tvp->v_vfsp;
15760Sstevel@tonic-gate 
15770Sstevel@tonic-gate 		/*
15780Sstevel@tonic-gate 		 * Its parent was holding a reference to it, since this
15790Sstevel@tonic-gate 		 * is a trigger vnode.
15800Sstevel@tonic-gate 		 */
15810Sstevel@tonic-gate 		VN_RELE(tvp);
15820Sstevel@tonic-gate 		if (error = auto_inkernel_unmount(vfsp)) {
15830Sstevel@tonic-gate 			cmn_err(CE_PANIC, "unmount_triggers: "
15840Sstevel@tonic-gate 			    "unmount of vp=%p failed error=%d",
15850Sstevel@tonic-gate 			    (void *)tvp, error);
15860Sstevel@tonic-gate 		}
15870Sstevel@tonic-gate 		/*
15880Sstevel@tonic-gate 		 * reacquire writer's lock
15890Sstevel@tonic-gate 		 */
15900Sstevel@tonic-gate 		rw_enter(&fnp->fn_rwlock, RW_WRITER);
15910Sstevel@tonic-gate 	}
15920Sstevel@tonic-gate 
15930Sstevel@tonic-gate 	/*
15940Sstevel@tonic-gate 	 * We were holding a reference to our parent.  Drop that.
15950Sstevel@tonic-gate 	 */
15960Sstevel@tonic-gate 	VN_RELE(fntovn(fnp));
15970Sstevel@tonic-gate 	fnp->fn_trigger = NULL;
15980Sstevel@tonic-gate 	fnp->fn_alp = NULL;
15990Sstevel@tonic-gate 
16000Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "unmount_triggers: finished\n"));
16010Sstevel@tonic-gate }
16020Sstevel@tonic-gate 
16030Sstevel@tonic-gate /*
16040Sstevel@tonic-gate  * This routine locks the mountpoint of every trigger node if they're
16050Sstevel@tonic-gate  * not busy, or returns EBUSY if any node is busy. If a trigger node should
16060Sstevel@tonic-gate  * be unmounted first, then it sets nfnp to point to it, otherwise nfnp
16070Sstevel@tonic-gate  * points to NULL.
16080Sstevel@tonic-gate  */
16090Sstevel@tonic-gate static int
16100Sstevel@tonic-gate triggers_busy(fnnode_t *fnp, fnnode_t **nfnp)
16110Sstevel@tonic-gate {
16120Sstevel@tonic-gate 	int error = 0, done;
16130Sstevel@tonic-gate 	int lck_error = 0;
16140Sstevel@tonic-gate 	fnnode_t *tp, *t1p;
16150Sstevel@tonic-gate 	vfs_t *vfsp;
16160Sstevel@tonic-gate 
16170Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock));
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate 	*nfnp = NULL;
16200Sstevel@tonic-gate 	for (tp = fnp->fn_trigger; tp != NULL; tp = tp->fn_next) {
16210Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\ttrigger: %s\n", tp->fn_name));
16220Sstevel@tonic-gate 		vfsp = fntovn(tp)->v_vfsp;
16230Sstevel@tonic-gate 		error = 0;
16240Sstevel@tonic-gate 		/*
16250Sstevel@tonic-gate 		 * The vn_vfsunlock will be done in auto_inkernel_unmount.
16260Sstevel@tonic-gate 		 */
16270Sstevel@tonic-gate 		lck_error = vn_vfswlock(vfsp->vfs_vnodecovered);
16280Sstevel@tonic-gate 		if (lck_error == 0) {
16290Sstevel@tonic-gate 			mutex_enter(&tp->fn_lock);
16300Sstevel@tonic-gate 			ASSERT((tp->fn_flags & MF_LOOKUP) == 0);
16310Sstevel@tonic-gate 			if (tp->fn_flags & MF_INPROG) {
16320Sstevel@tonic-gate 				/*
16330Sstevel@tonic-gate 				 * a mount is in progress
16340Sstevel@tonic-gate 				 */
16350Sstevel@tonic-gate 				error = EBUSY;
16360Sstevel@tonic-gate 			}
16370Sstevel@tonic-gate 			mutex_exit(&tp->fn_lock);
16380Sstevel@tonic-gate 		}
16390Sstevel@tonic-gate 		if (lck_error || error || DEEPER(tp) ||
16400Sstevel@tonic-gate 		    ((fntovn(tp))->v_count) > 2) {
16410Sstevel@tonic-gate 			/*
16420Sstevel@tonic-gate 			 * couldn't lock it because it's busy,
16430Sstevel@tonic-gate 			 * It is mounted on or has dirents?
16440Sstevel@tonic-gate 			 * If reference count is greater than two, then
16450Sstevel@tonic-gate 			 * somebody else is holding a reference to this vnode.
16460Sstevel@tonic-gate 			 * One reference is for the mountpoint, and the second
16470Sstevel@tonic-gate 			 * is for the trigger node.
16480Sstevel@tonic-gate 			 */
16490Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\ttrigger busy\n"));
16500Sstevel@tonic-gate 			if ((lck_error == 0) && (error == 0)) {
16510Sstevel@tonic-gate 				*nfnp = tp;
16520Sstevel@tonic-gate 				/*
16530Sstevel@tonic-gate 				 * The matching VN_RELE is done in
16540Sstevel@tonic-gate 				 * unmount_tree().
16550Sstevel@tonic-gate 				 */
16560Sstevel@tonic-gate 				VN_HOLD(fntovn(*nfnp));
16570Sstevel@tonic-gate 			}
16580Sstevel@tonic-gate 			/*
16590Sstevel@tonic-gate 			 * Unlock previously locked mountpoints
16600Sstevel@tonic-gate 			 */
16610Sstevel@tonic-gate 			for (done = 0, t1p = fnp->fn_trigger; !done;
16620Sstevel@tonic-gate 			    t1p = t1p->fn_next) {
16630Sstevel@tonic-gate 				/*
16640Sstevel@tonic-gate 				 * Unlock all nodes previously
16650Sstevel@tonic-gate 				 * locked. All nodes up to 'tp'
16660Sstevel@tonic-gate 				 * were successfully locked. If 'lck_err' is
16670Sstevel@tonic-gate 				 * set, then 'tp' was not locked, and thus
16680Sstevel@tonic-gate 				 * should not be unlocked. If
16690Sstevel@tonic-gate 				 * 'lck_err' is not set, then 'tp' was
16700Sstevel@tonic-gate 				 * successfully locked, and it should
16710Sstevel@tonic-gate 				 * be unlocked.
16720Sstevel@tonic-gate 				 */
16730Sstevel@tonic-gate 				if (t1p != tp || !lck_error) {
16740Sstevel@tonic-gate 					vfsp = fntovn(t1p)->v_vfsp;
16750Sstevel@tonic-gate 					vn_vfsunlock(vfsp->vfs_vnodecovered);
16760Sstevel@tonic-gate 				}
16770Sstevel@tonic-gate 				done = (t1p == tp);
16780Sstevel@tonic-gate 			}
16790Sstevel@tonic-gate 			error = EBUSY;
16800Sstevel@tonic-gate 			break;
16810Sstevel@tonic-gate 		}
16820Sstevel@tonic-gate 	}
16830Sstevel@tonic-gate 
16840Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "triggers_busy: error=%d\n", error));
16850Sstevel@tonic-gate 	return (error);
16860Sstevel@tonic-gate }
16870Sstevel@tonic-gate 
16880Sstevel@tonic-gate /*
16890Sstevel@tonic-gate  * Unlock previously locked trigger nodes.
16900Sstevel@tonic-gate  */
16910Sstevel@tonic-gate static int
16920Sstevel@tonic-gate triggers_unlock(fnnode_t *fnp)
16930Sstevel@tonic-gate {
16940Sstevel@tonic-gate 	fnnode_t *tp;
16950Sstevel@tonic-gate 	vfs_t *vfsp;
16960Sstevel@tonic-gate 
16970Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock));
16980Sstevel@tonic-gate 
16990Sstevel@tonic-gate 	for (tp = fnp->fn_trigger; tp != NULL; tp = tp->fn_next) {
17000Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tunlock trigger: %s\n", tp->fn_name));
17010Sstevel@tonic-gate 		vfsp = fntovn(tp)->v_vfsp;
17020Sstevel@tonic-gate 		vn_vfsunlock(vfsp->vfs_vnodecovered);
17030Sstevel@tonic-gate 	}
17040Sstevel@tonic-gate 
17050Sstevel@tonic-gate 	return (0);
17060Sstevel@tonic-gate }
17070Sstevel@tonic-gate 
17080Sstevel@tonic-gate /*
17090Sstevel@tonic-gate  * It is the caller's responsibility to grab the VVFSLOCK.
17100Sstevel@tonic-gate  * Releases the VVFSLOCK upon return.
17110Sstevel@tonic-gate  */
17120Sstevel@tonic-gate static int
17130Sstevel@tonic-gate unmount_node(vnode_t *cvp, int force)
17140Sstevel@tonic-gate {
17150Sstevel@tonic-gate 	int error = 0;
17160Sstevel@tonic-gate 	fnnode_t *cfnp;
17170Sstevel@tonic-gate 	vfs_t *vfsp;
17180Sstevel@tonic-gate 	umntrequest ul;
17190Sstevel@tonic-gate 	fninfo_t *fnip;
17200Sstevel@tonic-gate 
17210Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tunmount_node cvp=%p\n", (void *)cvp));
17220Sstevel@tonic-gate 
17230Sstevel@tonic-gate 	ASSERT(vn_vfswlock_held(cvp));
17240Sstevel@tonic-gate 	cfnp = vntofn(cvp);
17250Sstevel@tonic-gate 	vfsp = vn_mountedvfs(cvp);
17260Sstevel@tonic-gate 
17270Sstevel@tonic-gate 	if (force || cfnp->fn_flags & MF_IK_MOUNT) {
17280Sstevel@tonic-gate 		/*
17290Sstevel@tonic-gate 		 * Mount was performed in the kernel, so
17300Sstevel@tonic-gate 		 * do an in-kernel unmount. auto_inkernel_unmount()
17310Sstevel@tonic-gate 		 * will vn_vfsunlock(cvp).
17320Sstevel@tonic-gate 		 */
17330Sstevel@tonic-gate 		error = auto_inkernel_unmount(vfsp);
17340Sstevel@tonic-gate 	} else {
17350Sstevel@tonic-gate 		zone_t *zone = NULL;
17360Sstevel@tonic-gate 		refstr_t *mntpt, *resource;
17370Sstevel@tonic-gate 		size_t mntoptslen;
17380Sstevel@tonic-gate 
17390Sstevel@tonic-gate 		/*
17400Sstevel@tonic-gate 		 * Get the mnttab information of the node
17410Sstevel@tonic-gate 		 * and ask the daemon to unmount it.
17420Sstevel@tonic-gate 		 */
17430Sstevel@tonic-gate 		bzero(&ul, sizeof (ul));
17440Sstevel@tonic-gate 		mntfs_getmntopts(vfsp, &ul.mntopts, &mntoptslen);
17450Sstevel@tonic-gate 		if (ul.mntopts == NULL) {
17460Sstevel@tonic-gate 			auto_log(cfnp->fn_globals, CE_WARN, "unmount_node: "
17470Sstevel@tonic-gate 			    "no memory");
17480Sstevel@tonic-gate 			vn_vfsunlock(cvp);
17490Sstevel@tonic-gate 			error = ENOMEM;
17500Sstevel@tonic-gate 			goto done;
17510Sstevel@tonic-gate 		}
17520Sstevel@tonic-gate 		if (mntoptslen > AUTOFS_MAXOPTSLEN)
17530Sstevel@tonic-gate 			ul.mntopts[AUTOFS_MAXOPTSLEN - 1] = '\0';
17540Sstevel@tonic-gate 
17550Sstevel@tonic-gate 		mntpt = vfs_getmntpoint(vfsp);
17560Sstevel@tonic-gate 		ul.mntpnt = (char *)refstr_value(mntpt);
17570Sstevel@tonic-gate 		resource = vfs_getresource(vfsp);
17580Sstevel@tonic-gate 		ul.mntresource = (char *)refstr_value(resource);
17590Sstevel@tonic-gate 
17600Sstevel@tonic-gate 		fnip = vfstofni(cvp->v_vfsp);
17610Sstevel@tonic-gate 		ul.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE;
17620Sstevel@tonic-gate 
17630Sstevel@tonic-gate 		/*
17640Sstevel@tonic-gate 		 * Since a zone'd automountd's view of the autofs mount points
17650Sstevel@tonic-gate 		 * differs from those in the kernel, we need to make sure we
17660Sstevel@tonic-gate 		 * give it consistent mount points.
17670Sstevel@tonic-gate 		 */
17680Sstevel@tonic-gate 		ASSERT(fnip->fi_zoneid == getzoneid());
17690Sstevel@tonic-gate 		zone = curproc->p_zone;
17700Sstevel@tonic-gate 
17710Sstevel@tonic-gate 		if (fnip->fi_zoneid != GLOBAL_ZONEID) {
17720Sstevel@tonic-gate 			if (ZONE_PATH_VISIBLE(ul.mntpnt, zone)) {
17730Sstevel@tonic-gate 				ul.mntpnt =
17740Sstevel@tonic-gate 				    ZONE_PATH_TRANSLATE(ul.mntpnt, zone);
17750Sstevel@tonic-gate 			}
17760Sstevel@tonic-gate 			if (ZONE_PATH_VISIBLE(ul.mntresource, zone)) {
17770Sstevel@tonic-gate 				ul.mntresource =
17780Sstevel@tonic-gate 				    ZONE_PATH_TRANSLATE(ul.mntresource, zone);
17790Sstevel@tonic-gate 			}
17800Sstevel@tonic-gate 		}
17810Sstevel@tonic-gate 		ul.fstype = vfssw[vfsp->vfs_fstype].vsw_name;
17820Sstevel@tonic-gate 		vn_vfsunlock(cvp);
17830Sstevel@tonic-gate 
17840Sstevel@tonic-gate 		error = auto_send_unmount_request(fnip, &ul, CRED(), FALSE);
17850Sstevel@tonic-gate 		kmem_free(ul.mntopts, mntoptslen);
17860Sstevel@tonic-gate 		refstr_rele(mntpt);
17870Sstevel@tonic-gate 		refstr_rele(resource);
17880Sstevel@tonic-gate 	}
17890Sstevel@tonic-gate 
17900Sstevel@tonic-gate done:
17910Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tunmount_node cvp=%p error=%d\n", (void *)cvp,
17920Sstevel@tonic-gate 	    error));
17930Sstevel@tonic-gate 	return (error);
17940Sstevel@tonic-gate }
17950Sstevel@tonic-gate 
17960Sstevel@tonic-gate /*
17970Sstevel@tonic-gate  * vp is the "root" of the AUTOFS filesystem.
17980Sstevel@tonic-gate  * return EBUSY if any thread is holding a reference to this vnode
17990Sstevel@tonic-gate  * other than us.
18000Sstevel@tonic-gate  */
18010Sstevel@tonic-gate static int
18020Sstevel@tonic-gate check_auto_node(vnode_t *vp)
18030Sstevel@tonic-gate {
18040Sstevel@tonic-gate 	fnnode_t *fnp;
18050Sstevel@tonic-gate 	int error = 0;
18060Sstevel@tonic-gate 	/*
18070Sstevel@tonic-gate 	 * number of references to expect for
18080Sstevel@tonic-gate 	 * a non-busy vnode.
18090Sstevel@tonic-gate 	 */
18100Sstevel@tonic-gate 	uint_t count;
18110Sstevel@tonic-gate 
18120Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tcheck_auto_node vp=%p ", (void *)vp));
18130Sstevel@tonic-gate 	fnp = vntofn(vp);
18140Sstevel@tonic-gate 	ASSERT(fnp->fn_flags & MF_INPROG);
18150Sstevel@tonic-gate 	ASSERT((fnp->fn_flags & MF_LOOKUP) == 0);
18160Sstevel@tonic-gate 
18170Sstevel@tonic-gate 	count = 1;		/* we are holding a reference to vp */
18180Sstevel@tonic-gate 	if (fnp->fn_flags & MF_TRIGGER) {
18190Sstevel@tonic-gate 		/*
18200Sstevel@tonic-gate 		 * parent holds a pointer to us (trigger)
18210Sstevel@tonic-gate 		 */
18220Sstevel@tonic-gate 		count++;
18230Sstevel@tonic-gate 	}
18240Sstevel@tonic-gate 	if (fnp->fn_trigger != NULL) {
18250Sstevel@tonic-gate 		/*
18260Sstevel@tonic-gate 		 * The trigger nodes have a hold on us.
18270Sstevel@tonic-gate 		 */
18280Sstevel@tonic-gate 		count++;
18290Sstevel@tonic-gate 	}
18300Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
18310Sstevel@tonic-gate 	if (vp->v_flag & VROOT)
18320Sstevel@tonic-gate 		count++;
18330Sstevel@tonic-gate 	ASSERT(vp->v_count > 0);
18340Sstevel@tonic-gate 	AUTOFS_DPRINT((10, "\tcount=%u ", vp->v_count));
18350Sstevel@tonic-gate 	if (vp->v_count > count)
18360Sstevel@tonic-gate 		error = EBUSY;
18370Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
18380Sstevel@tonic-gate 
18390Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tcheck_auto_node error=%d ", error));
18400Sstevel@tonic-gate 	return (error);
18410Sstevel@tonic-gate }
18420Sstevel@tonic-gate 
18430Sstevel@tonic-gate /*
18440Sstevel@tonic-gate  * rootvp is the root of the AUTOFS filesystem.
18450Sstevel@tonic-gate  * If rootvp is busy (v_count > 1) returns EBUSY.
18460Sstevel@tonic-gate  * else removes every vnode under this tree.
18470Sstevel@tonic-gate  * ASSUMPTION: Assumes that the only node which can be busy is
18480Sstevel@tonic-gate  * the root vnode. This filesystem better be two levels deep only,
18490Sstevel@tonic-gate  * the root and its immediate subdirs.
18500Sstevel@tonic-gate  * The daemon will "AUTOFS direct-mount" only one level below the root.
18510Sstevel@tonic-gate  */
18520Sstevel@tonic-gate static int
18530Sstevel@tonic-gate unmount_autofs(vnode_t *rootvp)
18540Sstevel@tonic-gate {
18550Sstevel@tonic-gate 	fnnode_t *fnp, *rootfnp, *nfnp;
18560Sstevel@tonic-gate 	int error;
18570Sstevel@tonic-gate 
18580Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tunmount_autofs rootvp=%p ", (void *)rootvp));
18590Sstevel@tonic-gate 
18600Sstevel@tonic-gate 	error = check_auto_node(rootvp);
18610Sstevel@tonic-gate 	if (error == 0) {
18620Sstevel@tonic-gate 		/*
18630Sstevel@tonic-gate 		 * Remove all its immediate subdirectories.
18640Sstevel@tonic-gate 		 */
18650Sstevel@tonic-gate 		rootfnp = vntofn(rootvp);
18660Sstevel@tonic-gate 		rw_enter(&rootfnp->fn_rwlock, RW_WRITER);
18670Sstevel@tonic-gate 		nfnp = NULL;	/* lint clean */
18680Sstevel@tonic-gate 		for (fnp = rootfnp->fn_dirents; fnp != NULL; fnp = nfnp) {
18690Sstevel@tonic-gate 			ASSERT(fntovn(fnp)->v_count == 0);
18700Sstevel@tonic-gate 			ASSERT(fnp->fn_dirents == NULL);
18710Sstevel@tonic-gate 			ASSERT(fnp->fn_linkcnt == 2);
18720Sstevel@tonic-gate 			fnp->fn_linkcnt--;
18730Sstevel@tonic-gate 			auto_disconnect(rootfnp, fnp);
18740Sstevel@tonic-gate 			nfnp = fnp->fn_next;
18750Sstevel@tonic-gate 			auto_freefnnode(fnp);
18760Sstevel@tonic-gate 		}
18770Sstevel@tonic-gate 		rw_exit(&rootfnp->fn_rwlock);
18780Sstevel@tonic-gate 	}
18790Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tunmount_autofs error=%d ", error));
18800Sstevel@tonic-gate 	return (error);
18810Sstevel@tonic-gate }
18820Sstevel@tonic-gate 
18830Sstevel@tonic-gate /*
18840Sstevel@tonic-gate  * max number of unmount threads running
18850Sstevel@tonic-gate  */
18860Sstevel@tonic-gate static int autofs_unmount_threads = 5;
18870Sstevel@tonic-gate 
18880Sstevel@tonic-gate /*
18890Sstevel@tonic-gate  * XXX unmount_tree() is not suspend-safe within the scope of
18900Sstevel@tonic-gate  * the present model defined for cpr to suspend the system. Calls made
18910Sstevel@tonic-gate  * by the unmount_tree() that have been identified to be unsafe are
18920Sstevel@tonic-gate  * (1) RPC client handle setup and client calls to automountd which can
18930Sstevel@tonic-gate  * block deep down in the RPC library, (2) kmem_alloc() calls with the
18940Sstevel@tonic-gate  * KM_SLEEP flag which can block if memory is low, and (3) VFS_*() and
18950Sstevel@tonic-gate  * VOP_*() calls which can result in over the wire calls to servers.
18960Sstevel@tonic-gate  * The thread should be completely reevaluated to make it suspend-safe in
18970Sstevel@tonic-gate  * case of future updates to the cpr model.
18980Sstevel@tonic-gate  */
18990Sstevel@tonic-gate void
19000Sstevel@tonic-gate unmount_tree(struct autofs_globals *fngp, int force)
19010Sstevel@tonic-gate {
19020Sstevel@tonic-gate 	vnode_t *vp, *newvp;
19030Sstevel@tonic-gate 	vfs_t *vfsp;
19040Sstevel@tonic-gate 	fnnode_t *fnp, *nfnp, *pfnp;
19050Sstevel@tonic-gate 	action_list *alp;
19060Sstevel@tonic-gate 	int error, ilocked_it = 0;
19070Sstevel@tonic-gate 	fninfo_t *fnip;
19080Sstevel@tonic-gate 	time_t ref_time;
19090Sstevel@tonic-gate 	int autofs_busy_root, unmount_as_unit, unmount_done = 0;
19100Sstevel@tonic-gate 	timestruc_t now;
19110Sstevel@tonic-gate 
19120Sstevel@tonic-gate 	callb_cpr_t cprinfo;
19130Sstevel@tonic-gate 	kmutex_t unmount_tree_cpr_lock;
19140Sstevel@tonic-gate 
19150Sstevel@tonic-gate 	mutex_init(&unmount_tree_cpr_lock, NULL, MUTEX_DEFAULT, NULL);
19160Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, &unmount_tree_cpr_lock, callb_generic_cpr,
19170Sstevel@tonic-gate 		"unmount_tree");
19180Sstevel@tonic-gate 
19190Sstevel@tonic-gate 	/*
19200Sstevel@tonic-gate 	 * Got to release lock before attempting unmount in case
19210Sstevel@tonic-gate 	 * it hangs.
19220Sstevel@tonic-gate 	 */
19230Sstevel@tonic-gate 	rw_enter(&fngp->fng_rootfnnodep->fn_rwlock, RW_READER);
19240Sstevel@tonic-gate 	if ((fnp = fngp->fng_rootfnnodep->fn_dirents) == NULL) {
19250Sstevel@tonic-gate 		ASSERT(fngp->fng_fnnode_count == 1);
19260Sstevel@tonic-gate 		/*
19270Sstevel@tonic-gate 		 * no autofs mounted, done.
19280Sstevel@tonic-gate 		 */
19290Sstevel@tonic-gate 		rw_exit(&fngp->fng_rootfnnodep->fn_rwlock);
19300Sstevel@tonic-gate 		goto done;
19310Sstevel@tonic-gate 	}
19320Sstevel@tonic-gate 	VN_HOLD(fntovn(fnp));
19330Sstevel@tonic-gate 	rw_exit(&fngp->fng_rootfnnodep->fn_rwlock);
19340Sstevel@tonic-gate 
19350Sstevel@tonic-gate 	vp = fntovn(fnp);
19360Sstevel@tonic-gate 	fnip = vfstofni(vp->v_vfsp);
19370Sstevel@tonic-gate 	/*
19380Sstevel@tonic-gate 	 * autofssys() will be calling in from the global zone and doing
19390Sstevel@tonic-gate 	 * work on the behalf of the given zone, hence we can't always assert
19400Sstevel@tonic-gate 	 * that we have the right credentials, nor that the caller is always in
19410Sstevel@tonic-gate 	 * the correct zone.
19420Sstevel@tonic-gate 	 *
19430Sstevel@tonic-gate 	 * We do, however, know that if this is a "forced unmount" operation
19440Sstevel@tonic-gate 	 * (which autofssys() does), then we won't go down to the krpc layers,
19450Sstevel@tonic-gate 	 * so we don't need to fudge with the credentials.
19460Sstevel@tonic-gate 	 */
19470Sstevel@tonic-gate 	ASSERT(force || fnip->fi_zoneid == getzoneid());
19480Sstevel@tonic-gate 	if (!force && auto_null_request(fnip, kcred, FALSE) != 0) {
19490Sstevel@tonic-gate 		/*
19500Sstevel@tonic-gate 		 * automountd not running in this zone,
19510Sstevel@tonic-gate 		 * don't attempt unmounting this round.
19520Sstevel@tonic-gate 		 */
19530Sstevel@tonic-gate 		VN_RELE(vp);
19540Sstevel@tonic-gate 		goto done;
19550Sstevel@tonic-gate 	}
19560Sstevel@tonic-gate 	/* reference time for this unmount round */
19570Sstevel@tonic-gate 	ref_time = gethrestime_sec();
19580Sstevel@tonic-gate 	/*
19590Sstevel@tonic-gate 	 * If this an autofssys() call, we need to make sure we don't skip
19600Sstevel@tonic-gate 	 * nodes because we think we saw them recently.
19610Sstevel@tonic-gate 	 */
19620Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
19630Sstevel@tonic-gate 	if (force && fnp->fn_unmount_ref_time >= ref_time)
19640Sstevel@tonic-gate 		ref_time = fnp->fn_unmount_ref_time + 1;
19650Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
19660Sstevel@tonic-gate 
19670Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "unmount_tree (ID=%ld)\n", ref_time));
19680Sstevel@tonic-gate top:
19690Sstevel@tonic-gate 	AUTOFS_DPRINT((10, "unmount_tree: %s\n", fnp->fn_name));
19700Sstevel@tonic-gate 	ASSERT(fnp);
19710Sstevel@tonic-gate 	vp = fntovn(fnp);
19720Sstevel@tonic-gate 	if (vp->v_type == VLNK) {
19730Sstevel@tonic-gate 		/*
19740Sstevel@tonic-gate 		 * can't unmount symbolic links
19750Sstevel@tonic-gate 		 */
19760Sstevel@tonic-gate 		goto next;
19770Sstevel@tonic-gate 	}
19780Sstevel@tonic-gate 	fnip = vfstofni(vp->v_vfsp);
19790Sstevel@tonic-gate 	ASSERT(vp->v_count > 0);
19800Sstevel@tonic-gate 	error = 0;
19810Sstevel@tonic-gate 	autofs_busy_root = unmount_as_unit = 0;
19820Sstevel@tonic-gate 	alp = NULL;
19830Sstevel@tonic-gate 
19840Sstevel@tonic-gate 	ilocked_it = 0;
19850Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
19860Sstevel@tonic-gate 	if (fnp->fn_flags & (MF_INPROG | MF_LOOKUP)) {
19870Sstevel@tonic-gate 		/*
19880Sstevel@tonic-gate 		 * Either a mount, lookup or another unmount of this
19890Sstevel@tonic-gate 		 * subtree is in progress, don't attempt to unmount at
19900Sstevel@tonic-gate 		 * this time.
19910Sstevel@tonic-gate 		 */
19920Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
19930Sstevel@tonic-gate 		error = EBUSY;
19940Sstevel@tonic-gate 		goto next;
19950Sstevel@tonic-gate 	}
19960Sstevel@tonic-gate 	if (fnp->fn_unmount_ref_time >= ref_time) {
19970Sstevel@tonic-gate 		/*
19980Sstevel@tonic-gate 		 * Already been here, try next node.
19990Sstevel@tonic-gate 		 */
20000Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
20010Sstevel@tonic-gate 		error = EBUSY;
20020Sstevel@tonic-gate 		goto next;
20030Sstevel@tonic-gate 	}
20040Sstevel@tonic-gate 	fnp->fn_unmount_ref_time = ref_time;
20050Sstevel@tonic-gate 
20060Sstevel@tonic-gate 	/*
20070Sstevel@tonic-gate 	 * If forced operation ignore timeout values
20080Sstevel@tonic-gate 	 */
20090Sstevel@tonic-gate 	if (!force && fnp->fn_ref_time + fnip->fi_mount_to >
20100Sstevel@tonic-gate 	    gethrestime_sec()) {
20110Sstevel@tonic-gate 		/*
20120Sstevel@tonic-gate 		 * Node has been referenced recently, try the
20130Sstevel@tonic-gate 		 * unmount of its children if any.
20140Sstevel@tonic-gate 		 */
20150Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
20160Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "fn_ref_time within range\n"));
20170Sstevel@tonic-gate 		rw_enter(&fnp->fn_rwlock, RW_READER);
20180Sstevel@tonic-gate 		if (fnp->fn_dirents) {
20190Sstevel@tonic-gate 			/*
20200Sstevel@tonic-gate 			 * Has subdirectory, attempt their
20210Sstevel@tonic-gate 			 * unmount first
20220Sstevel@tonic-gate 			 */
20230Sstevel@tonic-gate 			nfnp = fnp->fn_dirents;
20240Sstevel@tonic-gate 			VN_HOLD(fntovn(nfnp));
20250Sstevel@tonic-gate 			rw_exit(&fnp->fn_rwlock);
20260Sstevel@tonic-gate 
20270Sstevel@tonic-gate 			VN_RELE(vp);
20280Sstevel@tonic-gate 			fnp = nfnp;
20290Sstevel@tonic-gate 			goto top;
20300Sstevel@tonic-gate 		}
20310Sstevel@tonic-gate 		rw_exit(&fnp->fn_rwlock);
20320Sstevel@tonic-gate 		/*
20330Sstevel@tonic-gate 		 * No children, try next node.
20340Sstevel@tonic-gate 		 */
20350Sstevel@tonic-gate 		error = EBUSY;
20360Sstevel@tonic-gate 		goto next;
20370Sstevel@tonic-gate 	}
20380Sstevel@tonic-gate 
20390Sstevel@tonic-gate 	AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG);
20400Sstevel@tonic-gate 	fnp->fn_error = 0;
20410Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
20420Sstevel@tonic-gate 	ilocked_it = 1;
20430Sstevel@tonic-gate 
20440Sstevel@tonic-gate 	rw_enter(&fnp->fn_rwlock, RW_WRITER);
20450Sstevel@tonic-gate 	if (fnp->fn_trigger != NULL) {
20460Sstevel@tonic-gate 		unmount_as_unit = 1;
20470Sstevel@tonic-gate 		if ((vn_mountedvfs(vp) == NULL) && (check_auto_node(vp))) {
20480Sstevel@tonic-gate 			/*
20490Sstevel@tonic-gate 			 * AUTOFS mountpoint is busy, there's
20500Sstevel@tonic-gate 			 * no point trying to unmount. Fall through
20510Sstevel@tonic-gate 			 * to attempt to unmount subtrees rooted
20520Sstevel@tonic-gate 			 * at a possible trigger node, but remember
20530Sstevel@tonic-gate 			 * not to unmount this tree.
20540Sstevel@tonic-gate 			 */
20550Sstevel@tonic-gate 			autofs_busy_root = 1;
20560Sstevel@tonic-gate 		}
20570Sstevel@tonic-gate 
20580Sstevel@tonic-gate 		if (triggers_busy(fnp, &nfnp)) {
20590Sstevel@tonic-gate 			rw_exit(&fnp->fn_rwlock);
20600Sstevel@tonic-gate 			if (nfnp == NULL) {
20610Sstevel@tonic-gate 				error = EBUSY;
20620Sstevel@tonic-gate 				goto next;
20630Sstevel@tonic-gate 			}
20640Sstevel@tonic-gate 			/*
20650Sstevel@tonic-gate 			 * nfnp is busy, try to unmount it first
20660Sstevel@tonic-gate 			 */
20670Sstevel@tonic-gate 			mutex_enter(&fnp->fn_lock);
20680Sstevel@tonic-gate 			AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
20690Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
20700Sstevel@tonic-gate 			VN_RELE(vp);
20710Sstevel@tonic-gate 			ASSERT(fntovn(nfnp)->v_count > 1);
20720Sstevel@tonic-gate 			fnp = nfnp;
20730Sstevel@tonic-gate 			goto top;
20740Sstevel@tonic-gate 		}
20750Sstevel@tonic-gate 
20760Sstevel@tonic-gate 		/*
20770Sstevel@tonic-gate 		 * At this point, we know all trigger nodes are locked,
20780Sstevel@tonic-gate 		 * and they're not busy or mounted on.
20790Sstevel@tonic-gate 		 */
20800Sstevel@tonic-gate 
20810Sstevel@tonic-gate 		if (autofs_busy_root) {
20820Sstevel@tonic-gate 			/*
20830Sstevel@tonic-gate 			 * Got to unlock the the trigger nodes since
20840Sstevel@tonic-gate 			 * I'm not really going to unmount the filesystem.
20850Sstevel@tonic-gate 			 */
20860Sstevel@tonic-gate 			(void) triggers_unlock(fnp);
20870Sstevel@tonic-gate 		} else {
20880Sstevel@tonic-gate 			/*
20890Sstevel@tonic-gate 			 * Attempt to unmount all the trigger nodes,
20900Sstevel@tonic-gate 			 * save the action_list in case we need to
20910Sstevel@tonic-gate 			 * remount them later. The action_list will be XDR
20920Sstevel@tonic-gate 			 * freed later if there was no need to remount the
20930Sstevel@tonic-gate 			 * trigger nodes.
20940Sstevel@tonic-gate 			 */
20950Sstevel@tonic-gate 			unmount_triggers(fnp, &alp);
20960Sstevel@tonic-gate 		}
20970Sstevel@tonic-gate 	}
20980Sstevel@tonic-gate 	rw_exit(&fnp->fn_rwlock);
20990Sstevel@tonic-gate 
21000Sstevel@tonic-gate 	if (autofs_busy_root)
21010Sstevel@tonic-gate 		goto next;
21020Sstevel@tonic-gate 
21030Sstevel@tonic-gate 	(void) vn_vfswlock_wait(vp);
21040Sstevel@tonic-gate 
21050Sstevel@tonic-gate 	vfsp = vn_mountedvfs(vp);
21060Sstevel@tonic-gate 	if (vfsp != NULL) {
21070Sstevel@tonic-gate 		/*
21080Sstevel@tonic-gate 		 * Node is mounted on.
21090Sstevel@tonic-gate 		 */
21100Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tNode is mounted on\n"));
21110Sstevel@tonic-gate 
21120Sstevel@tonic-gate 		/*
21130Sstevel@tonic-gate 		 * Deal with /xfn/host/jurassic alikes here...
21140Sstevel@tonic-gate 		 */
21150Sstevel@tonic-gate 		if (vfs_matchops(vfsp, vfs_getops(vp->v_vfsp))) {
21160Sstevel@tonic-gate 			/*
21170Sstevel@tonic-gate 			 * If the filesystem mounted here is AUTOFS, and it
21180Sstevel@tonic-gate 			 * is busy, try to unmount the tree rooted on it
21190Sstevel@tonic-gate 			 * first. We know this call to VFS_ROOT is safe to
21200Sstevel@tonic-gate 			 * call while holding VVFSLOCK, since it resolves
21210Sstevel@tonic-gate 			 * to a call to auto_root().
21220Sstevel@tonic-gate 			 */
21230Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\t\tAUTOFS mounted here\n"));
21240Sstevel@tonic-gate 			if (VFS_ROOT(vfsp, &newvp)) {
21250Sstevel@tonic-gate 				cmn_err(CE_PANIC,
21260Sstevel@tonic-gate 				    "unmount_tree: VFS_ROOT(vfs=%p) failed",
21270Sstevel@tonic-gate 				    (void *)vfsp);
21280Sstevel@tonic-gate 			}
21290Sstevel@tonic-gate 			nfnp = vntofn(newvp);
21300Sstevel@tonic-gate 			if (DEEPER(nfnp)) {
21310Sstevel@tonic-gate 				vn_vfsunlock(vp);
21320Sstevel@tonic-gate 				mutex_enter(&fnp->fn_lock);
21330Sstevel@tonic-gate 				AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
21340Sstevel@tonic-gate 				mutex_exit(&fnp->fn_lock);
21350Sstevel@tonic-gate 				VN_RELE(vp);
21360Sstevel@tonic-gate 				fnp = nfnp;
21370Sstevel@tonic-gate 				goto top;
21380Sstevel@tonic-gate 			}
21390Sstevel@tonic-gate 			/*
21400Sstevel@tonic-gate 			 * Fall through to unmount this filesystem
21410Sstevel@tonic-gate 			 */
21420Sstevel@tonic-gate 			VN_RELE(newvp);
21430Sstevel@tonic-gate 		}
21440Sstevel@tonic-gate 
21450Sstevel@tonic-gate 		/*
21460Sstevel@tonic-gate 		 * vn_vfsunlock(vp) is done inside unmount_node()
21470Sstevel@tonic-gate 		 */
21480Sstevel@tonic-gate 		error = unmount_node(vp, force);
21490Sstevel@tonic-gate 		if (error == ECONNRESET) {
21500Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tConnection dropped\n"));
21510Sstevel@tonic-gate 			if (vn_mountedvfs(vp) == NULL) {
21520Sstevel@tonic-gate 				/*
21530Sstevel@tonic-gate 				 * The filesystem was unmounted before the
21540Sstevel@tonic-gate 				 * daemon died. Unfortunately we can not
21550Sstevel@tonic-gate 				 * determine whether all the cleanup work was
21560Sstevel@tonic-gate 				 * successfully finished (i.e. update mnttab,
21570Sstevel@tonic-gate 				 * or notify NFS server of the unmount).
21580Sstevel@tonic-gate 				 * We should not retry the operation since the
21590Sstevel@tonic-gate 				 * filesystem has already been unmounted, and
21600Sstevel@tonic-gate 				 * may have already been removed from mnttab,
21610Sstevel@tonic-gate 				 * in such case the devid/rdevid we send to
21620Sstevel@tonic-gate 				 * the daemon will not be matched. So we have
21630Sstevel@tonic-gate 				 * to be contempt with the partial unmount.
21640Sstevel@tonic-gate 				 * Since the mountpoint is no longer covered, we
21650Sstevel@tonic-gate 				 * clear the error condition.
21660Sstevel@tonic-gate 				 */
21670Sstevel@tonic-gate 				error = 0;
21680Sstevel@tonic-gate 				auto_log(fngp, CE_WARN,
21690Sstevel@tonic-gate 				    "unmount_tree: automountd connection "
21700Sstevel@tonic-gate 				    "dropped");
21710Sstevel@tonic-gate 				if (fnip->fi_flags & MF_DIRECT) {
21720Sstevel@tonic-gate 					auto_log(fngp, CE_WARN, "unmount_tree: "
21730Sstevel@tonic-gate 					    "%s successfully unmounted - "
21740Sstevel@tonic-gate 					    "do not remount triggers",
21750Sstevel@tonic-gate 					    fnip->fi_path);
21760Sstevel@tonic-gate 				} else {
21770Sstevel@tonic-gate 					auto_log(fngp, CE_WARN, "unmount_tree: "
21780Sstevel@tonic-gate 					    "%s/%s successfully unmounted - "
21790Sstevel@tonic-gate 					    "do not remount triggers",
21800Sstevel@tonic-gate 					    fnip->fi_path, fnp->fn_name);
21810Sstevel@tonic-gate 				}
21820Sstevel@tonic-gate 			}
21830Sstevel@tonic-gate 		}
21840Sstevel@tonic-gate 	} else {
21850Sstevel@tonic-gate 		vn_vfsunlock(vp);
21860Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tNode is AUTOFS\n"));
21870Sstevel@tonic-gate 		if (unmount_as_unit) {
21880Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tunmount as unit\n"));
21890Sstevel@tonic-gate 			error = unmount_autofs(vp);
21900Sstevel@tonic-gate 		} else {
21910Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tunmount one at a time\n"));
21920Sstevel@tonic-gate 			rw_enter(&fnp->fn_rwlock, RW_READER);
21930Sstevel@tonic-gate 			if (fnp->fn_dirents != NULL) {
21940Sstevel@tonic-gate 				/*
21950Sstevel@tonic-gate 				 * Has subdirectory, attempt their
21960Sstevel@tonic-gate 				 * unmount first
21970Sstevel@tonic-gate 				 */
21980Sstevel@tonic-gate 				nfnp = fnp->fn_dirents;
21990Sstevel@tonic-gate 				VN_HOLD(fntovn(nfnp));
22000Sstevel@tonic-gate 				rw_exit(&fnp->fn_rwlock);
22010Sstevel@tonic-gate 
22020Sstevel@tonic-gate 				mutex_enter(&fnp->fn_lock);
22030Sstevel@tonic-gate 				AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
22040Sstevel@tonic-gate 				mutex_exit(&fnp->fn_lock);
22050Sstevel@tonic-gate 				VN_RELE(vp);
22060Sstevel@tonic-gate 				fnp = nfnp;
22070Sstevel@tonic-gate 				goto top;
22080Sstevel@tonic-gate 			}
22090Sstevel@tonic-gate 			rw_exit(&fnp->fn_rwlock);
22100Sstevel@tonic-gate 			goto next;
22110Sstevel@tonic-gate 		}
22120Sstevel@tonic-gate 	}
22130Sstevel@tonic-gate 
22140Sstevel@tonic-gate 	if (error) {
22150Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tUnmount failed\n"));
22160Sstevel@tonic-gate 		if (alp != NULL) {
22170Sstevel@tonic-gate 			/*
22180Sstevel@tonic-gate 			 * Unmount failed, got to remount triggers.
22190Sstevel@tonic-gate 			 */
22200Sstevel@tonic-gate 			ASSERT((fnp->fn_flags & MF_THISUID_MATCH_RQD) == 0);
22210Sstevel@tonic-gate 			error = auto_perform_actions(fnip, fnp, alp, CRED());
22220Sstevel@tonic-gate 			if (error) {
22230Sstevel@tonic-gate 				auto_log(fngp, CE_WARN, "autofs: can't remount "
22240Sstevel@tonic-gate 				    "triggers fnp=%p error=%d", (void *)fnp,
22250Sstevel@tonic-gate 				    error);
22260Sstevel@tonic-gate 				error = 0;
22270Sstevel@tonic-gate 				/*
22280Sstevel@tonic-gate 				 * The action list should have been
22290Sstevel@tonic-gate 				 * xdr_free'd by auto_perform_actions
22300Sstevel@tonic-gate 				 * since an error occured
22310Sstevel@tonic-gate 				 */
22320Sstevel@tonic-gate 				alp = NULL;
22330Sstevel@tonic-gate 			}
22340Sstevel@tonic-gate 		}
22350Sstevel@tonic-gate 	} else {
22360Sstevel@tonic-gate 		/*
22370Sstevel@tonic-gate 		 * The unmount succeeded, which will cause this node to
22380Sstevel@tonic-gate 		 * be removed from its parent if its an indirect mount,
22390Sstevel@tonic-gate 		 * therefore update the parent's atime and mtime now.
22400Sstevel@tonic-gate 		 * I don't update them in auto_disconnect() because I
22410Sstevel@tonic-gate 		 * don't want atime and mtime changing every time a
22420Sstevel@tonic-gate 		 * lookup goes to the daemon and creates a new node.
22430Sstevel@tonic-gate 		 */
22440Sstevel@tonic-gate 		unmount_done = 1;
22450Sstevel@tonic-gate 		if ((fnip->fi_flags & MF_DIRECT) == 0) {
22460Sstevel@tonic-gate 			gethrestime(&now);
22470Sstevel@tonic-gate 			if (fnp->fn_parent == fngp->fng_rootfnnodep)
22480Sstevel@tonic-gate 				fnp->fn_atime = fnp->fn_mtime = now;
22490Sstevel@tonic-gate 			else
22500Sstevel@tonic-gate 				fnp->fn_parent->fn_atime =
22510Sstevel@tonic-gate 					fnp->fn_parent->fn_mtime = now;
22520Sstevel@tonic-gate 		}
22530Sstevel@tonic-gate 
22540Sstevel@tonic-gate 		/*
22550Sstevel@tonic-gate 		 * Free the action list here
22560Sstevel@tonic-gate 		 */
22570Sstevel@tonic-gate 		if (alp != NULL) {
22580Sstevel@tonic-gate 			xdr_free(xdr_action_list, (char *)alp);
22590Sstevel@tonic-gate 			alp = NULL;
22600Sstevel@tonic-gate 		}
22610Sstevel@tonic-gate 	}
22620Sstevel@tonic-gate 
22630Sstevel@tonic-gate 	fnp->fn_ref_time = gethrestime_sec();
22640Sstevel@tonic-gate 
22650Sstevel@tonic-gate next:
22660Sstevel@tonic-gate 	/*
22670Sstevel@tonic-gate 	 * Obtain parent's readers lock before grabbing
22680Sstevel@tonic-gate 	 * reference to next sibling.
22690Sstevel@tonic-gate 	 * XXX Note that nodes in the top level list (mounted
22700Sstevel@tonic-gate 	 * in user space not by the daemon in the kernel) parent is itself,
22710Sstevel@tonic-gate 	 * therefore grabbing the lock makes no sense, but doesn't
22720Sstevel@tonic-gate 	 * hurt either.
22730Sstevel@tonic-gate 	 */
22740Sstevel@tonic-gate 	pfnp = fnp->fn_parent;
22750Sstevel@tonic-gate 	ASSERT(pfnp != NULL);
22760Sstevel@tonic-gate 	rw_enter(&pfnp->fn_rwlock, RW_READER);
22770Sstevel@tonic-gate 	if ((nfnp = fnp->fn_next) != NULL)
22780Sstevel@tonic-gate 		VN_HOLD(fntovn(nfnp));
22790Sstevel@tonic-gate 	rw_exit(&pfnp->fn_rwlock);
22800Sstevel@tonic-gate 
22810Sstevel@tonic-gate 	if (ilocked_it) {
22820Sstevel@tonic-gate 		mutex_enter(&fnp->fn_lock);
22830Sstevel@tonic-gate 		if (unmount_done) {
22840Sstevel@tonic-gate 			/*
22850Sstevel@tonic-gate 			 * Other threads may be waiting for this unmount to
22860Sstevel@tonic-gate 			 * finish. We must let it know that in order to
22870Sstevel@tonic-gate 			 * proceed, it must trigger the mount itself.
22880Sstevel@tonic-gate 			 */
22890Sstevel@tonic-gate 			fnp->fn_flags &= ~MF_IK_MOUNT;
22900Sstevel@tonic-gate 			if (fnp->fn_flags & MF_WAITING)
22910Sstevel@tonic-gate 				fnp->fn_error = EAGAIN;
22920Sstevel@tonic-gate 			unmount_done = 0;
22930Sstevel@tonic-gate 		}
22940Sstevel@tonic-gate 		AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
22950Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
22960Sstevel@tonic-gate 		ilocked_it = 0;
22970Sstevel@tonic-gate 	}
22980Sstevel@tonic-gate 
22990Sstevel@tonic-gate 	if (nfnp != NULL) {
23000Sstevel@tonic-gate 		VN_RELE(vp);
23010Sstevel@tonic-gate 		fnp = nfnp;
23020Sstevel@tonic-gate 		/*
23030Sstevel@tonic-gate 		 * Unmount next element
23040Sstevel@tonic-gate 		 */
23050Sstevel@tonic-gate 		goto top;
23060Sstevel@tonic-gate 	}
23070Sstevel@tonic-gate 
23080Sstevel@tonic-gate 	/*
23090Sstevel@tonic-gate 	 * We don't want to unmount rootfnnodep, so the check is made here
23100Sstevel@tonic-gate 	 */
23110Sstevel@tonic-gate 	ASSERT(pfnp != fnp);
23120Sstevel@tonic-gate 	if (pfnp != fngp->fng_rootfnnodep) {
23130Sstevel@tonic-gate 		/*
23140Sstevel@tonic-gate 		 * Now attempt to unmount my parent
23150Sstevel@tonic-gate 		 */
23160Sstevel@tonic-gate 		VN_HOLD(fntovn(pfnp));
23170Sstevel@tonic-gate 		VN_RELE(vp);
23180Sstevel@tonic-gate 		fnp = pfnp;
23190Sstevel@tonic-gate 
23200Sstevel@tonic-gate 		goto top;
23210Sstevel@tonic-gate 	}
23220Sstevel@tonic-gate 
23230Sstevel@tonic-gate 	VN_RELE(vp);
23240Sstevel@tonic-gate 
23250Sstevel@tonic-gate 	/*
23260Sstevel@tonic-gate 	 * At this point we've walked the entire tree and attempted to unmount
23270Sstevel@tonic-gate 	 * as much as we can one level at a time.
23280Sstevel@tonic-gate 	 */
23290Sstevel@tonic-gate done:
23300Sstevel@tonic-gate 	mutex_enter(&unmount_tree_cpr_lock);
23310Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cprinfo);
23320Sstevel@tonic-gate 	mutex_destroy(&unmount_tree_cpr_lock);
23330Sstevel@tonic-gate }
23340Sstevel@tonic-gate 
23350Sstevel@tonic-gate static void
23360Sstevel@tonic-gate unmount_zone_tree(struct autofs_globals *fngp)
23370Sstevel@tonic-gate {
23380Sstevel@tonic-gate 	unmount_tree(fngp, 0);
23390Sstevel@tonic-gate 	mutex_enter(&fngp->fng_unmount_threads_lock);
23400Sstevel@tonic-gate 	fngp->fng_unmount_threads--;
23410Sstevel@tonic-gate 	mutex_exit(&fngp->fng_unmount_threads_lock);
23420Sstevel@tonic-gate 
23430Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "unmount_tree done. Thread exiting.\n"));
23440Sstevel@tonic-gate 
23450Sstevel@tonic-gate 	zthread_exit();
23460Sstevel@tonic-gate 	/* NOTREACHED */
23470Sstevel@tonic-gate }
23480Sstevel@tonic-gate 
23490Sstevel@tonic-gate static int autofs_unmount_thread_timer = 120;	/* in seconds */
23500Sstevel@tonic-gate 
23510Sstevel@tonic-gate void
23520Sstevel@tonic-gate auto_do_unmount(struct autofs_globals *fngp)
23530Sstevel@tonic-gate {
23540Sstevel@tonic-gate 	callb_cpr_t cprinfo;
23550Sstevel@tonic-gate 	clock_t timeleft;
23560Sstevel@tonic-gate 	zone_t *zone = curproc->p_zone;
23570Sstevel@tonic-gate 
23580Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, &fngp->fng_unmount_threads_lock,
23590Sstevel@tonic-gate 		callb_generic_cpr, "auto_do_unmount");
23600Sstevel@tonic-gate 
23610Sstevel@tonic-gate 	for (;;) {	/* forever */
23620Sstevel@tonic-gate 		mutex_enter(&fngp->fng_unmount_threads_lock);
23630Sstevel@tonic-gate 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
23640Sstevel@tonic-gate newthread:
23650Sstevel@tonic-gate 		mutex_exit(&fngp->fng_unmount_threads_lock);
23660Sstevel@tonic-gate 		timeleft = zone_status_timedwait(zone, lbolt +
23670Sstevel@tonic-gate 		    autofs_unmount_thread_timer * hz, ZONE_IS_SHUTTING_DOWN);
23680Sstevel@tonic-gate 		mutex_enter(&fngp->fng_unmount_threads_lock);
23690Sstevel@tonic-gate 
23700Sstevel@tonic-gate 		if (timeleft != -1) {	/* didn't time out */
23710Sstevel@tonic-gate 			ASSERT(zone_status_get(zone) >= ZONE_IS_SHUTTING_DOWN);
23720Sstevel@tonic-gate 			/*
23730Sstevel@tonic-gate 			 * zone is exiting... don't create any new threads.
23740Sstevel@tonic-gate 			 * fng_unmount_threads_lock is released implicitly by
23750Sstevel@tonic-gate 			 * the below.
23760Sstevel@tonic-gate 			 */
23770Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(&cprinfo,
23780Sstevel@tonic-gate 				&fngp->fng_unmount_threads_lock);
23790Sstevel@tonic-gate 			CALLB_CPR_EXIT(&cprinfo);
23800Sstevel@tonic-gate 			zthread_exit();
23810Sstevel@tonic-gate 			/* NOTREACHED */
23820Sstevel@tonic-gate 		}
23830Sstevel@tonic-gate 		if (fngp->fng_unmount_threads < autofs_unmount_threads) {
23840Sstevel@tonic-gate 			fngp->fng_unmount_threads++;
23850Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(&cprinfo,
23860Sstevel@tonic-gate 				&fngp->fng_unmount_threads_lock);
23870Sstevel@tonic-gate 			mutex_exit(&fngp->fng_unmount_threads_lock);
23880Sstevel@tonic-gate 
23890Sstevel@tonic-gate 			(void) zthread_create(NULL, 0, unmount_zone_tree, fngp,
23900Sstevel@tonic-gate 			    0, minclsyspri);
23910Sstevel@tonic-gate 		} else
23920Sstevel@tonic-gate 			goto newthread;
23930Sstevel@tonic-gate 	}
23940Sstevel@tonic-gate 	/* NOTREACHED */
23950Sstevel@tonic-gate }
23960Sstevel@tonic-gate 
23970Sstevel@tonic-gate /*
23980Sstevel@tonic-gate  * Is nobrowse specified in option string?
23990Sstevel@tonic-gate  * opts should be a null ('\0') terminated string.
24000Sstevel@tonic-gate  * Returns non-zero if nobrowse has been specified.
24010Sstevel@tonic-gate  */
24020Sstevel@tonic-gate int
24030Sstevel@tonic-gate auto_nobrowse_option(char *opts)
24040Sstevel@tonic-gate {
24050Sstevel@tonic-gate 	char *buf;
24060Sstevel@tonic-gate 	char *p;
24070Sstevel@tonic-gate 	char *t;
24080Sstevel@tonic-gate 	int nobrowse = 0;
24090Sstevel@tonic-gate 	int last_opt = 0;
24100Sstevel@tonic-gate 	size_t len;
24110Sstevel@tonic-gate 
24120Sstevel@tonic-gate 	len = strlen(opts) + 1;
24130Sstevel@tonic-gate 	p = buf = kmem_alloc(len, KM_SLEEP);
24140Sstevel@tonic-gate 	(void) strcpy(buf, opts);
24150Sstevel@tonic-gate 	do {
24160Sstevel@tonic-gate 		if (t = strchr(p, ','))
24170Sstevel@tonic-gate 			*t++ = '\0';
24180Sstevel@tonic-gate 		else
24190Sstevel@tonic-gate 			last_opt++;
24200Sstevel@tonic-gate 		if (strcmp(p, MNTOPT_NOBROWSE) == 0)
24210Sstevel@tonic-gate 			nobrowse = 1;
24220Sstevel@tonic-gate 		else if (strcmp(p, MNTOPT_BROWSE) == 0)
24230Sstevel@tonic-gate 			nobrowse = 0;
24240Sstevel@tonic-gate 		p = t;
24250Sstevel@tonic-gate 	} while (!last_opt);
24260Sstevel@tonic-gate 	kmem_free(buf, len);
24270Sstevel@tonic-gate 
24280Sstevel@tonic-gate 	return (nobrowse);
24290Sstevel@tonic-gate }
24300Sstevel@tonic-gate 
24310Sstevel@tonic-gate /*
24320Sstevel@tonic-gate  * used to log warnings only if automountd is running
24330Sstevel@tonic-gate  * with verbose mode set
24340Sstevel@tonic-gate  */
24350Sstevel@tonic-gate void
24360Sstevel@tonic-gate auto_log(struct autofs_globals *fngp, int level, const char *fmt, ...)
24370Sstevel@tonic-gate {
24380Sstevel@tonic-gate 	va_list args;
24390Sstevel@tonic-gate 
24400Sstevel@tonic-gate 	if (fngp->fng_verbose > 0) {
24410Sstevel@tonic-gate 		va_start(args, fmt);
24420Sstevel@tonic-gate 		vzcmn_err(fngp->fng_zoneid, level, fmt, args);
24430Sstevel@tonic-gate 		va_end(args);
24440Sstevel@tonic-gate 	}
24450Sstevel@tonic-gate }
24460Sstevel@tonic-gate 
24470Sstevel@tonic-gate #ifdef DEBUG
24480Sstevel@tonic-gate static int autofs_debug = 0;
24490Sstevel@tonic-gate 
24500Sstevel@tonic-gate /*
24510Sstevel@tonic-gate  * Utilities used by both client and server
24520Sstevel@tonic-gate  * Standard levels:
24530Sstevel@tonic-gate  * 0) no debugging
24540Sstevel@tonic-gate  * 1) hard failures
24550Sstevel@tonic-gate  * 2) soft failures
24560Sstevel@tonic-gate  * 3) current test software
24570Sstevel@tonic-gate  * 4) main procedure entry points
24580Sstevel@tonic-gate  * 5) main procedure exit points
24590Sstevel@tonic-gate  * 6) utility procedure entry points
24600Sstevel@tonic-gate  * 7) utility procedure exit points
24610Sstevel@tonic-gate  * 8) obscure procedure entry points
24620Sstevel@tonic-gate  * 9) obscure procedure exit points
24630Sstevel@tonic-gate  * 10) random stuff
24640Sstevel@tonic-gate  * 11) all <= 1
24650Sstevel@tonic-gate  * 12) all <= 2
24660Sstevel@tonic-gate  * 13) all <= 3
24670Sstevel@tonic-gate  * ...
24680Sstevel@tonic-gate  */
24690Sstevel@tonic-gate /* PRINTFLIKE2 */
24700Sstevel@tonic-gate void
24710Sstevel@tonic-gate auto_dprint(int level, const char *fmt, ...)
24720Sstevel@tonic-gate {
24730Sstevel@tonic-gate 	va_list args;
24740Sstevel@tonic-gate 
24750Sstevel@tonic-gate 	if (autofs_debug == level ||
24760Sstevel@tonic-gate 	    (autofs_debug > 10 && (autofs_debug - 10) >= level)) {
24770Sstevel@tonic-gate 		va_start(args, fmt);
24780Sstevel@tonic-gate 		(void) vprintf(fmt, args);
24790Sstevel@tonic-gate 		va_end(args);
24800Sstevel@tonic-gate 	}
24810Sstevel@tonic-gate }
24820Sstevel@tonic-gate #endif /* DEBUG */
2483