xref: /titanic_51/usr/src/uts/common/fs/autofs/auto_subr.c (revision 6bf33d39c4f690c6cbac80609ced3a7495a38f94)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
539d3e169Sevanl  * Common Development and Distribution License (the "License").
639d3e169Sevanl  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21a574db85Sraf 
227c478bd9Sstevel@tonic-gate /*
23f798ee53SJan Kryl  * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <sys/param.h>
277c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
287c478bd9Sstevel@tonic-gate #include <sys/errno.h>
297c478bd9Sstevel@tonic-gate #include <sys/proc.h>
307c478bd9Sstevel@tonic-gate #include <sys/disp.h>
317c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
327c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
337c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
347c478bd9Sstevel@tonic-gate #include <sys/cred.h>
357c478bd9Sstevel@tonic-gate #include <sys/mount.h>
367c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
377c478bd9Sstevel@tonic-gate #include <sys/debug.h>
387c478bd9Sstevel@tonic-gate #include <sys/systm.h>
397c478bd9Sstevel@tonic-gate #include <sys/dirent.h>
407c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h>
417c478bd9Sstevel@tonic-gate #include <sys/fs/autofs.h>
427c478bd9Sstevel@tonic-gate #include <sys/callb.h>
437c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
447c478bd9Sstevel@tonic-gate #include <sys/zone.h>
4539d3e169Sevanl #include <sys/door.h>
467c478bd9Sstevel@tonic-gate #include <sys/fs/mntdata.h>
4739d3e169Sevanl #include <nfs/mount.h>
4839d3e169Sevanl #include <rpc/clnt.h>
4939d3e169Sevanl #include <rpcsvc/autofs_prot.h>
5039d3e169Sevanl #include <nfs/rnode.h>
5139d3e169Sevanl #include <sys/utsname.h>
52a574db85Sraf #include <sys/schedctl.h>
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate /*
557c478bd9Sstevel@tonic-gate  * Autofs and Zones:
567c478bd9Sstevel@tonic-gate  *
577c478bd9Sstevel@tonic-gate  * Zones are delegated the responsibility of managing their own autofs mounts
587c478bd9Sstevel@tonic-gate  * and maps.  Each zone runs its own copy of automountd, with its own timeouts,
597c478bd9Sstevel@tonic-gate  * and other logically "global" parameters.  kRPC and virtualization in the
607c478bd9Sstevel@tonic-gate  * loopback transport (tl) will prevent a zone from communicating with another
617c478bd9Sstevel@tonic-gate  * zone's automountd.
627c478bd9Sstevel@tonic-gate  *
637c478bd9Sstevel@tonic-gate  * Each zone has its own "rootfnnode" and associated tree of auto nodes.
647c478bd9Sstevel@tonic-gate  *
657c478bd9Sstevel@tonic-gate  * Each zone also has its own set of "unmounter" kernel threads; these are
667c478bd9Sstevel@tonic-gate  * created and run within the zone's context (ie, they are created via
677c478bd9Sstevel@tonic-gate  * zthread_create()).
687c478bd9Sstevel@tonic-gate  *
697c478bd9Sstevel@tonic-gate  * Cross-zone mount triggers are disallowed.  There is a check in
707c478bd9Sstevel@tonic-gate  * auto_trigger_mount() to this effect; EPERM is returned to indicate that the
717c478bd9Sstevel@tonic-gate  * mount is not owned by the caller.
727c478bd9Sstevel@tonic-gate  *
737c478bd9Sstevel@tonic-gate  * autofssys() enables a caller in the global zone to clean up in-kernel (as
747c478bd9Sstevel@tonic-gate  * well as regular) autofs mounts via the unmount_tree() mechanism.  This is
757c478bd9Sstevel@tonic-gate  * routinely done when all mounts are removed as part of zone shutdown.
767c478bd9Sstevel@tonic-gate  */
777c478bd9Sstevel@tonic-gate #define	TYPICALMAXPATHLEN	64
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate static kmutex_t autofs_nodeid_lock;
807c478bd9Sstevel@tonic-gate 
81f798ee53SJan Kryl /* max number of unmount threads running */
82f798ee53SJan Kryl static int autofs_unmount_threads = 5;
83f798ee53SJan Kryl static int autofs_unmount_thread_timer = 120;	/* in seconds */
84f798ee53SJan Kryl 
857c478bd9Sstevel@tonic-gate static int auto_perform_link(fnnode_t *, struct linka *, cred_t *);
867c478bd9Sstevel@tonic-gate static int auto_perform_actions(fninfo_t *, fnnode_t *,
877c478bd9Sstevel@tonic-gate     action_list *, cred_t *);
887c478bd9Sstevel@tonic-gate static int auto_getmntpnt(vnode_t *, char *, vnode_t **, cred_t *);
897c478bd9Sstevel@tonic-gate static int auto_lookup_request(fninfo_t *, char *, struct linka *,
903bfb48feSsemery     bool_t, bool_t *, cred_t *);
913bfb48feSsemery static int auto_mount_request(fninfo_t *, char *, action_list **, cred_t *,
9239d3e169Sevanl     bool_t);
9339d3e169Sevanl 
947c478bd9Sstevel@tonic-gate /*
957c478bd9Sstevel@tonic-gate  * Clears the MF_INPROG flag, and wakes up those threads sleeping on
967c478bd9Sstevel@tonic-gate  * fn_cv_mount if MF_WAITING is set.
977c478bd9Sstevel@tonic-gate  */
987c478bd9Sstevel@tonic-gate void
997c478bd9Sstevel@tonic-gate auto_unblock_others(
1007c478bd9Sstevel@tonic-gate 	fnnode_t *fnp,
1017c478bd9Sstevel@tonic-gate 	uint_t operation)		/* either MF_INPROG or MF_LOOKUP */
1027c478bd9Sstevel@tonic-gate {
1037c478bd9Sstevel@tonic-gate 	ASSERT(operation & (MF_INPROG | MF_LOOKUP));
1047c478bd9Sstevel@tonic-gate 	fnp->fn_flags &= ~operation;
1057c478bd9Sstevel@tonic-gate 	if (fnp->fn_flags & MF_WAITING) {
1067c478bd9Sstevel@tonic-gate 		fnp->fn_flags &= ~MF_WAITING;
1077c478bd9Sstevel@tonic-gate 		cv_broadcast(&fnp->fn_cv_mount);
1087c478bd9Sstevel@tonic-gate 	}
1097c478bd9Sstevel@tonic-gate }
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate int
1127c478bd9Sstevel@tonic-gate auto_wait4mount(fnnode_t *fnp)
1137c478bd9Sstevel@tonic-gate {
1147c478bd9Sstevel@tonic-gate 	int error;
1157c478bd9Sstevel@tonic-gate 	k_sigset_t smask;
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_wait4mount: fnp=%p\n", (void *)fnp));
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
1207c478bd9Sstevel@tonic-gate 	while (fnp->fn_flags & (MF_INPROG | MF_LOOKUP)) {
1217c478bd9Sstevel@tonic-gate 		/*
1227c478bd9Sstevel@tonic-gate 		 * There is a mount or a lookup in progress.
1237c478bd9Sstevel@tonic-gate 		 */
1247c478bd9Sstevel@tonic-gate 		fnp->fn_flags |= MF_WAITING;
1257c478bd9Sstevel@tonic-gate 		sigintr(&smask, 1);
1267c478bd9Sstevel@tonic-gate 		if (!cv_wait_sig(&fnp->fn_cv_mount, &fnp->fn_lock)) {
1277c478bd9Sstevel@tonic-gate 			/*
1287c478bd9Sstevel@tonic-gate 			 * Decided not to wait for operation to
1297c478bd9Sstevel@tonic-gate 			 * finish after all.
1307c478bd9Sstevel@tonic-gate 			 */
1317c478bd9Sstevel@tonic-gate 			sigunintr(&smask);
1327c478bd9Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
1337c478bd9Sstevel@tonic-gate 			return (EINTR);
1347c478bd9Sstevel@tonic-gate 		}
1357c478bd9Sstevel@tonic-gate 		sigunintr(&smask);
1367c478bd9Sstevel@tonic-gate 	}
1377c478bd9Sstevel@tonic-gate 	error = fnp->fn_error;
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate 	if (error == EINTR) {
1407c478bd9Sstevel@tonic-gate 		/*
1417c478bd9Sstevel@tonic-gate 		 * The thread doing the mount got interrupted, we need to
1427c478bd9Sstevel@tonic-gate 		 * try again, by returning EAGAIN.
1437c478bd9Sstevel@tonic-gate 		 */
1447c478bd9Sstevel@tonic-gate 		error = EAGAIN;
1457c478bd9Sstevel@tonic-gate 	}
1467c478bd9Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_wait4mount: fnp=%p error=%d\n", (void *)fnp,
1497c478bd9Sstevel@tonic-gate 	    error));
1507c478bd9Sstevel@tonic-gate 	return (error);
1517c478bd9Sstevel@tonic-gate }
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate int
1547c478bd9Sstevel@tonic-gate auto_lookup_aux(fnnode_t *fnp, char *name, cred_t *cred)
1557c478bd9Sstevel@tonic-gate {
1567c478bd9Sstevel@tonic-gate 	struct fninfo *fnip;
1577c478bd9Sstevel@tonic-gate 	struct linka link;
1587c478bd9Sstevel@tonic-gate 	bool_t mountreq = FALSE;
1597c478bd9Sstevel@tonic-gate 	int error = 0;
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	fnip = vfstofni(fntovn(fnp)->v_vfsp);
1627c478bd9Sstevel@tonic-gate 	bzero(&link, sizeof (link));
1633bfb48feSsemery 	error = auto_lookup_request(fnip, name, &link, TRUE, &mountreq, cred);
1647c478bd9Sstevel@tonic-gate 	if (!error) {
16539d3e169Sevanl 		if (link.link != NULL || link.link != '\0') {
1667c478bd9Sstevel@tonic-gate 			/*
1677c478bd9Sstevel@tonic-gate 			 * This node should be a symlink
1687c478bd9Sstevel@tonic-gate 			 */
1697c478bd9Sstevel@tonic-gate 			error = auto_perform_link(fnp, &link, cred);
1707c478bd9Sstevel@tonic-gate 		} else if (mountreq) {
1717c478bd9Sstevel@tonic-gate 			/*
1727c478bd9Sstevel@tonic-gate 			 * The automount daemon is requesting a mount,
1737c478bd9Sstevel@tonic-gate 			 * implying this entry must be a wildcard match and
1747c478bd9Sstevel@tonic-gate 			 * therefore in need of verification that the entry
1757c478bd9Sstevel@tonic-gate 			 * exists on the server.
1767c478bd9Sstevel@tonic-gate 			 */
1777c478bd9Sstevel@tonic-gate 			mutex_enter(&fnp->fn_lock);
1787c478bd9Sstevel@tonic-gate 			AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG);
1797c478bd9Sstevel@tonic-gate 			fnp->fn_error = 0;
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 			/*
1827c478bd9Sstevel@tonic-gate 			 * Unblock other lookup requests on this node,
1837c478bd9Sstevel@tonic-gate 			 * this is needed to let the lookup generated by
1847c478bd9Sstevel@tonic-gate 			 * the mount call to complete. The caveat is
1857c478bd9Sstevel@tonic-gate 			 * other lookups on this node can also get by,
1867c478bd9Sstevel@tonic-gate 			 * i.e., another lookup on this node that occurs
1877c478bd9Sstevel@tonic-gate 			 * while this lookup is attempting the mount
1887c478bd9Sstevel@tonic-gate 			 * would return a positive result no matter what.
1897c478bd9Sstevel@tonic-gate 			 * Therefore two lookups on the this node could
1907c478bd9Sstevel@tonic-gate 			 * potentially get disparate results.
1917c478bd9Sstevel@tonic-gate 			 */
1927c478bd9Sstevel@tonic-gate 			AUTOFS_UNBLOCK_OTHERS(fnp, MF_LOOKUP);
1937c478bd9Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
1947c478bd9Sstevel@tonic-gate 			/*
1957c478bd9Sstevel@tonic-gate 			 * auto_new_mount_thread fires up a new thread which
1967c478bd9Sstevel@tonic-gate 			 * calls automountd finishing up the work
1977c478bd9Sstevel@tonic-gate 			 */
1987c478bd9Sstevel@tonic-gate 			auto_new_mount_thread(fnp, name, cred);
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate 			/*
2017c478bd9Sstevel@tonic-gate 			 * At this point, we are simply another thread
2027c478bd9Sstevel@tonic-gate 			 * waiting for the mount to complete
2037c478bd9Sstevel@tonic-gate 			 */
2047c478bd9Sstevel@tonic-gate 			error = auto_wait4mount(fnp);
2057c478bd9Sstevel@tonic-gate 			if (error == AUTOFS_SHUTDOWN)
2067c478bd9Sstevel@tonic-gate 				error = ENOENT;
2077c478bd9Sstevel@tonic-gate 		}
2087c478bd9Sstevel@tonic-gate 	}
2097c478bd9Sstevel@tonic-gate 
21039d3e169Sevanl 	if (link.link)
21139d3e169Sevanl 		kmem_free(link.link, strlen(link.link) + 1);
21239d3e169Sevanl 	if (link.dir)
21339d3e169Sevanl 		kmem_free(link.dir, strlen(link.dir) + 1);
2147c478bd9Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
2157c478bd9Sstevel@tonic-gate 	fnp->fn_error = error;
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 	/*
2187c478bd9Sstevel@tonic-gate 	 * Notify threads waiting for lookup/mount that
2197c478bd9Sstevel@tonic-gate 	 * it's done.
2207c478bd9Sstevel@tonic-gate 	 */
2217c478bd9Sstevel@tonic-gate 	if (mountreq) {
2227c478bd9Sstevel@tonic-gate 		AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
2237c478bd9Sstevel@tonic-gate 	} else {
2247c478bd9Sstevel@tonic-gate 		AUTOFS_UNBLOCK_OTHERS(fnp, MF_LOOKUP);
2257c478bd9Sstevel@tonic-gate 	}
2267c478bd9Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
2277c478bd9Sstevel@tonic-gate 	return (error);
2287c478bd9Sstevel@tonic-gate }
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate /*
2317c478bd9Sstevel@tonic-gate  * Starting point for thread to handle mount requests with automountd.
2327c478bd9Sstevel@tonic-gate  * XXX auto_mount_thread() is not suspend-safe within the scope of
2337c478bd9Sstevel@tonic-gate  * the present model defined for cpr to suspend the system. Calls
2347c478bd9Sstevel@tonic-gate  * made by the auto_mount_thread() that have been identified to be unsafe
2357c478bd9Sstevel@tonic-gate  * are (1) RPC client handle setup and client calls to automountd which
2367c478bd9Sstevel@tonic-gate  * can block deep down in the RPC library, (2) kmem_alloc() calls with the
2377c478bd9Sstevel@tonic-gate  * KM_SLEEP flag which can block if memory is low, and (3) VFS_*(), and
2387c478bd9Sstevel@tonic-gate  * lookuppnvp() calls which can result in over the wire calls to servers.
2397c478bd9Sstevel@tonic-gate  * The thread should be completely reevaluated to make it suspend-safe in
2407c478bd9Sstevel@tonic-gate  * case of future updates to the cpr model.
2417c478bd9Sstevel@tonic-gate  */
2427c478bd9Sstevel@tonic-gate static void
2437c478bd9Sstevel@tonic-gate auto_mount_thread(struct autofs_callargs *argsp)
2447c478bd9Sstevel@tonic-gate {
2457c478bd9Sstevel@tonic-gate 	struct fninfo 		*fnip;
2467c478bd9Sstevel@tonic-gate 	fnnode_t 		*fnp;
2477c478bd9Sstevel@tonic-gate 	vnode_t 		*vp;
2487c478bd9Sstevel@tonic-gate 	char 			*name;
2497c478bd9Sstevel@tonic-gate 	size_t 			namelen;
2507c478bd9Sstevel@tonic-gate 	cred_t 			*cred;
2517c478bd9Sstevel@tonic-gate 	action_list		*alp = NULL;
2527c478bd9Sstevel@tonic-gate 	int 			error;
2537c478bd9Sstevel@tonic-gate 	callb_cpr_t 		cprinfo;
2547c478bd9Sstevel@tonic-gate 	kmutex_t 		auto_mount_thread_cpr_lock;
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 	mutex_init(&auto_mount_thread_cpr_lock, NULL, MUTEX_DEFAULT, NULL);
25739d3e169Sevanl 	CALLB_CPR_INIT(&cprinfo, &auto_mount_thread_cpr_lock,
25839d3e169Sevanl 	    callb_generic_cpr, "auto_mount_thread");
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate 	fnp = argsp->fnc_fnp;
2617c478bd9Sstevel@tonic-gate 	vp = fntovn(fnp);
2627c478bd9Sstevel@tonic-gate 	fnip = vfstofni(vp->v_vfsp);
2637c478bd9Sstevel@tonic-gate 	name = argsp->fnc_name;
2647c478bd9Sstevel@tonic-gate 	cred = argsp->fnc_cred;
2657c478bd9Sstevel@tonic-gate 	ASSERT(crgetzoneid(argsp->fnc_cred) == fnip->fi_zoneid);
2667c478bd9Sstevel@tonic-gate 
2673bfb48feSsemery 	error = auto_mount_request(fnip, name, &alp, cred, TRUE);
2687c478bd9Sstevel@tonic-gate 	if (!error)
2697c478bd9Sstevel@tonic-gate 		error = auto_perform_actions(fnip, fnp, alp, cred);
2707c478bd9Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
2717c478bd9Sstevel@tonic-gate 	fnp->fn_error = error;
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate 	/*
2747c478bd9Sstevel@tonic-gate 	 * Notify threads waiting for mount that
2757c478bd9Sstevel@tonic-gate 	 * it's done.
2767c478bd9Sstevel@tonic-gate 	 */
2777c478bd9Sstevel@tonic-gate 	AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
2787c478bd9Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
2817c478bd9Sstevel@tonic-gate 	crfree(argsp->fnc_cred);
2827c478bd9Sstevel@tonic-gate 	namelen = strlen(argsp->fnc_name) + 1;
2837c478bd9Sstevel@tonic-gate 	kmem_free(argsp->fnc_name, namelen);
2847c478bd9Sstevel@tonic-gate 	kmem_free(argsp, sizeof (*argsp));
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	mutex_enter(&auto_mount_thread_cpr_lock);
2877c478bd9Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cprinfo);
2887c478bd9Sstevel@tonic-gate 	mutex_destroy(&auto_mount_thread_cpr_lock);
2897c478bd9Sstevel@tonic-gate 	zthread_exit();
2907c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
2917c478bd9Sstevel@tonic-gate }
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate static int autofs_thr_success = 0;
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate /*
2967c478bd9Sstevel@tonic-gate  * Creates new thread which calls auto_mount_thread which does
2977c478bd9Sstevel@tonic-gate  * the bulk of the work calling automountd, via 'auto_perform_actions'.
2987c478bd9Sstevel@tonic-gate  */
2997c478bd9Sstevel@tonic-gate void
3007c478bd9Sstevel@tonic-gate auto_new_mount_thread(fnnode_t *fnp, char *name, cred_t *cred)
3017c478bd9Sstevel@tonic-gate {
3027c478bd9Sstevel@tonic-gate 	struct autofs_callargs *argsp;
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 	argsp = kmem_alloc(sizeof (*argsp), KM_SLEEP);
3057c478bd9Sstevel@tonic-gate 	VN_HOLD(fntovn(fnp));
3067c478bd9Sstevel@tonic-gate 	argsp->fnc_fnp = fnp;
3077c478bd9Sstevel@tonic-gate 	argsp->fnc_name = kmem_alloc(strlen(name) + 1, KM_SLEEP);
3087c478bd9Sstevel@tonic-gate 	(void) strcpy(argsp->fnc_name, name);
3097c478bd9Sstevel@tonic-gate 	argsp->fnc_origin = curthread;
3107c478bd9Sstevel@tonic-gate 	crhold(cred);
3117c478bd9Sstevel@tonic-gate 	argsp->fnc_cred = cred;
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	(void) zthread_create(NULL, 0, auto_mount_thread, argsp, 0,
3147c478bd9Sstevel@tonic-gate 	    minclsyspri);
3157c478bd9Sstevel@tonic-gate 	autofs_thr_success++;
3167c478bd9Sstevel@tonic-gate }
3177c478bd9Sstevel@tonic-gate 
318d6c8399bSrmesta #define	DOOR_BUF_ALIGN		(1024*1024)
319d6c8399bSrmesta #define	DOOR_BUF_MULTIPLIER	3
320d6c8399bSrmesta #define	DOOR_BUF_DEFAULT_SZ	(DOOR_BUF_MULTIPLIER * DOOR_BUF_ALIGN)
321d6c8399bSrmesta int	doorbuf_defsz = DOOR_BUF_DEFAULT_SZ;
322d6c8399bSrmesta 
323d6c8399bSrmesta /*ARGSUSED*/
3247c478bd9Sstevel@tonic-gate int
3257c478bd9Sstevel@tonic-gate auto_calldaemon(
32639d3e169Sevanl 	zoneid_t 		zoneid,
32739d3e169Sevanl 	int			which,
32839d3e169Sevanl 	xdrproc_t		xarg_func,
3297c478bd9Sstevel@tonic-gate 	void 			*argsp,
33039d3e169Sevanl 	xdrproc_t		xresp_func,
3317c478bd9Sstevel@tonic-gate 	void 			*resp,
33239d3e169Sevanl 	int			reslen,
3337c478bd9Sstevel@tonic-gate 	bool_t 			hard)	/* retry forever? */
3347c478bd9Sstevel@tonic-gate {
335d6c8399bSrmesta 	int			 retry;
336d6c8399bSrmesta 	int			 error = 0;
3377c478bd9Sstevel@tonic-gate 	k_sigset_t		 smask;
33839d3e169Sevanl 	door_arg_t		 door_args;
33939d3e169Sevanl 	door_handle_t		 dh;
340d6c8399bSrmesta 	XDR			 xdrarg;
341d6c8399bSrmesta 	XDR			 xdrres;
34239d3e169Sevanl 	struct autofs_globals 	*fngp = NULL;
343d6c8399bSrmesta 	void			*orp = NULL;
344d6c8399bSrmesta 	int			 orl;
3459d40d374Srmesta 	int			 rlen = 0;	/* MUST be initialized */
34639d3e169Sevanl 	autofs_door_args_t	*xdr_argsp;
34739d3e169Sevanl 	int			 xdr_len = 0;
348780213fbSevanl 	int			 printed_not_running_msg = 0;
349780213fbSevanl 	klwp_t			*lwp = ttolwp(curthread);
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	/*
3527c478bd9Sstevel@tonic-gate 	 * We know that the current thread is doing work on
3537c478bd9Sstevel@tonic-gate 	 * behalf of its own zone, so it's ok to use
3547c478bd9Sstevel@tonic-gate 	 * curproc->p_zone.
3557c478bd9Sstevel@tonic-gate 	 */
35639d3e169Sevanl 	ASSERT(zoneid == getzoneid());
357d6c8399bSrmesta 	if (zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN) {
3587c478bd9Sstevel@tonic-gate 		/*
3597c478bd9Sstevel@tonic-gate 		 * There's no point in trying to talk to
3607c478bd9Sstevel@tonic-gate 		 * automountd.  Plus, zone_shutdown() is
3617c478bd9Sstevel@tonic-gate 		 * waiting for us.
3627c478bd9Sstevel@tonic-gate 		 */
36339d3e169Sevanl 		return (ECONNREFUSED);
3647c478bd9Sstevel@tonic-gate 	}
36539d3e169Sevanl 
366780213fbSevanl 	do {
367780213fbSevanl 		retry = 0;
368780213fbSevanl 		mutex_enter(&autofs_minor_lock);
369780213fbSevanl 		fngp = zone_getspecific(autofs_key, curproc->p_zone);
370780213fbSevanl 		mutex_exit(&autofs_minor_lock);
371780213fbSevanl 		if (fngp == NULL) {
372780213fbSevanl 			if (hard) {
373780213fbSevanl 				AUTOFS_DPRINT((5,
374780213fbSevanl 				    "auto_calldaemon: "\
375780213fbSevanl 				    "failed to get door handle\n"));
376780213fbSevanl 				if (!printed_not_running_msg) {
377780213fbSevanl 					printed_not_running_msg = 1;
378780213fbSevanl 					zprintf(zoneid, "automountd not "\
379780213fbSevanl 					    "running, retrying\n");
380780213fbSevanl 				}
381780213fbSevanl 				delay(hz);
382780213fbSevanl 				retry = 1;
383780213fbSevanl 			} else {
384780213fbSevanl 				/*
385780213fbSevanl 				 * There is no global data so no door.
386780213fbSevanl 				 * There's no point in attempting to talk
387780213fbSevanl 				 * to automountd if we can't get the door
388780213fbSevanl 				 * handle.
389780213fbSevanl 				 */
390780213fbSevanl 				return (ECONNREFUSED);
391780213fbSevanl 			}
392780213fbSevanl 		}
393780213fbSevanl 	} while (retry);
394780213fbSevanl 
395780213fbSevanl 	if (printed_not_running_msg) {
396780213fbSevanl 		fngp->fng_printed_not_running_msg = printed_not_running_msg;
39739d3e169Sevanl 	}
39839d3e169Sevanl 
39939d3e169Sevanl 	ASSERT(fngp != NULL);
40039d3e169Sevanl 
40139d3e169Sevanl 	if (argsp != NULL && (xdr_len = xdr_sizeof(xarg_func, argsp)) == 0)
40239d3e169Sevanl 		return (EINVAL);
40339d3e169Sevanl 	xdr_argsp = kmem_zalloc(xdr_len + sizeof (*xdr_argsp), KM_SLEEP);
40439d3e169Sevanl 	xdr_argsp->xdr_len = xdr_len;
40539d3e169Sevanl 	xdr_argsp->cmd = which;
40639d3e169Sevanl 
40739d3e169Sevanl 	if (argsp) {
40839d3e169Sevanl 		xdrmem_create(&xdrarg, (char *)&xdr_argsp->xdr_arg,
40939d3e169Sevanl 		    xdr_argsp->xdr_len, XDR_ENCODE);
41039d3e169Sevanl 
41139d3e169Sevanl 		if (!(*xarg_func)(&xdrarg, argsp)) {
41239d3e169Sevanl 			kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp));
41339d3e169Sevanl 			return (EINVAL);
41439d3e169Sevanl 		}
41539d3e169Sevanl 	}
41639d3e169Sevanl 
41739d3e169Sevanl 	/*
41839d3e169Sevanl 	 * We're saving off the original pointer and length due to the
41939d3e169Sevanl 	 * possibility that the results buffer returned by the door
42039d3e169Sevanl 	 * upcall can be different then what we passed in. This is because
42139d3e169Sevanl 	 * the door will allocate new memory if the results buffer passed
42239d3e169Sevanl 	 * in isn't large enough to hold what we need to send back.
42339d3e169Sevanl 	 * In this case we need to free the memory originally allocated
42439d3e169Sevanl 	 * for that buffer.
42539d3e169Sevanl 	 */
426d6c8399bSrmesta 	if (resp)
427d6c8399bSrmesta 		rlen = xdr_sizeof(xresp_func, resp);
428d6c8399bSrmesta 	orl = (rlen == 0) ? doorbuf_defsz : MAX(rlen, doorbuf_defsz);
429d6c8399bSrmesta 	orp = kmem_zalloc(orl, KM_SLEEP);
43039d3e169Sevanl 
43139d3e169Sevanl 	do {
43239d3e169Sevanl 		retry = 0;
43339d3e169Sevanl 		mutex_enter(&fngp->fng_autofs_daemon_lock);
43439d3e169Sevanl 		dh = fngp->fng_autofs_daemon_dh;
43539d3e169Sevanl 		if (dh)
43639d3e169Sevanl 			door_ki_hold(dh);
43739d3e169Sevanl 		mutex_exit(&fngp->fng_autofs_daemon_lock);
43839d3e169Sevanl 
43939d3e169Sevanl 		if (dh == NULL) {
440d6c8399bSrmesta 			if (orp)
441d6c8399bSrmesta 				kmem_free(orp, orl);
44239d3e169Sevanl 			kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp));
44339d3e169Sevanl 			return (ENOENT);
44439d3e169Sevanl 		}
44539d3e169Sevanl 		door_args.data_ptr = (char *)xdr_argsp;
44639d3e169Sevanl 		door_args.data_size = sizeof (*xdr_argsp) + xdr_argsp->xdr_len;
44739d3e169Sevanl 		door_args.desc_ptr = NULL;
44839d3e169Sevanl 		door_args.desc_num = 0;
449d6c8399bSrmesta 		door_args.rbuf = orp ? (char *)orp : NULL;
450d6c8399bSrmesta 		door_args.rsize = orl;
45139d3e169Sevanl 
45239d3e169Sevanl 		sigintr(&smask, 1);
453323a81d9Sjwadams 		error =
454323a81d9Sjwadams 		    door_ki_upcall_limited(dh, &door_args, NULL, SIZE_MAX, 0);
45539d3e169Sevanl 		sigunintr(&smask);
45639d3e169Sevanl 
45739d3e169Sevanl 		door_ki_rele(dh);
45839d3e169Sevanl 
459d6c8399bSrmesta 		/*
460d6c8399bSrmesta 		 * Handle daemon errors
461d6c8399bSrmesta 		 */
46239d3e169Sevanl 		if (!error) {
463d6c8399bSrmesta 			/*
464d6c8399bSrmesta 			 * Upcall successful. Let's check for soft errors
465d6c8399bSrmesta 			 * from the daemon. We only recover from overflow
466d6c8399bSrmesta 			 * type scenarios. Any other errors, we return to
467d6c8399bSrmesta 			 * the caller.
468d6c8399bSrmesta 			 */
46939d3e169Sevanl 			autofs_door_res_t *adr =
47039d3e169Sevanl 			    (autofs_door_res_t *)door_args.rbuf;
471d6c8399bSrmesta 
472d6c8399bSrmesta 			if (door_args.rbuf != NULL) {
473d6c8399bSrmesta 				int	 nl;
474d6c8399bSrmesta 
475d6c8399bSrmesta 				switch (error = adr->res_status) {
476d6c8399bSrmesta 				case 0:	/* no error; continue */
477d6c8399bSrmesta 					break;
478d6c8399bSrmesta 
479d6c8399bSrmesta 				case EOVERFLOW:
480d6c8399bSrmesta 					/*
481d6c8399bSrmesta 					 * orig landing buf not big enough.
482d6c8399bSrmesta 					 * xdr_len in XDR_BYTES_PER_UNIT
483d6c8399bSrmesta 					 */
484d6c8399bSrmesta 					if ((nl = adr->xdr_len) > 0 &&
485d6c8399bSrmesta 					    (btopr(nl) < freemem/64)) {
486d6c8399bSrmesta 						if (orp)
487d6c8399bSrmesta 							kmem_free(orp, orl);
488d6c8399bSrmesta 						orp = kmem_zalloc(nl, KM_SLEEP);
489d6c8399bSrmesta 						orl = nl;
490d6c8399bSrmesta 						retry = 1;
491d6c8399bSrmesta 						break;
492d6c8399bSrmesta 					}
493d6c8399bSrmesta 					/*FALLTHROUGH*/
494d6c8399bSrmesta 
495d6c8399bSrmesta 				default:
49639d3e169Sevanl 					kmem_free(xdr_argsp,
49739d3e169Sevanl 					    xdr_len + sizeof (*xdr_argsp));
498d6c8399bSrmesta 					if (orp)
499d6c8399bSrmesta 						kmem_free(orp, orl);
50039d3e169Sevanl 					return (error);
50139d3e169Sevanl 				}
502d6c8399bSrmesta 			}
50339d3e169Sevanl 			continue;
50439d3e169Sevanl 		}
505d6c8399bSrmesta 
506d6c8399bSrmesta 		/*
507d6c8399bSrmesta 		 * no daemon errors; now process door/comm errors (if any)
508d6c8399bSrmesta 		 */
50939d3e169Sevanl 		switch (error) {
51039d3e169Sevanl 		case EINTR:
51139d3e169Sevanl 			/*
51239d3e169Sevanl 			 * interrupts should be handled properly by the
513780213fbSevanl 			 * door upcall. If the door doesn't handle the
514780213fbSevanl 			 * interupt completely then we need to bail out.
515780213fbSevanl 			 */
516780213fbSevanl 			if (lwp && (ISSIG(curthread,
517780213fbSevanl 			    JUSTLOOKING) || MUSTRETURN(curproc, curthread))) {
518780213fbSevanl 				if (ISSIG(curthread, FORREAL) ||
519780213fbSevanl 				    lwp->lwp_sysabort ||
520780213fbSevanl 				    MUSTRETURN(curproc, curthread)) {
521780213fbSevanl 					lwp->lwp_sysabort = 0;
522780213fbSevanl 					return (EINTR);
523780213fbSevanl 				}
524780213fbSevanl 			}
525780213fbSevanl 			/*
52639d3e169Sevanl 			 * We may have gotten EINTR for other reasons
52739d3e169Sevanl 			 * like the door being revoked on us. Instead
52839d3e169Sevanl 			 * of trying to extract this out of the door
52939d3e169Sevanl 			 * handle, sleep and try again, if still
53039d3e169Sevanl 			 * revoked we will get EBADF next time
53139d3e169Sevanl 			 * through.
532a574db85Sraf 			 *
533a574db85Sraf 			 * If we have a pending cancellation and we don't
534a574db85Sraf 			 * have cancellation disabled, we will get EINTR
535a574db85Sraf 			 * forever, no matter how many times we retry,
536a574db85Sraf 			 * so just get out now if this is the case.
53739d3e169Sevanl 			 */
538a574db85Sraf 			if (schedctl_cancel_pending())
539a574db85Sraf 				break;
540780213fbSevanl 			/* FALLTHROUGH */
54139d3e169Sevanl 		case EAGAIN:    /* process may be forking */
54239d3e169Sevanl 			/*
54339d3e169Sevanl 			 * Back off for a bit
54439d3e169Sevanl 			 */
54539d3e169Sevanl 			delay(hz);
54639d3e169Sevanl 			retry = 1;
54739d3e169Sevanl 			break;
54839d3e169Sevanl 		case EBADF:	/* Invalid door */
54939d3e169Sevanl 		case EINVAL:    /* Not a door, wrong target */
55039d3e169Sevanl 			/*
55139d3e169Sevanl 			 * A fatal door error, if our failing door
55239d3e169Sevanl 			 * handle is the current door handle, clean
55339d3e169Sevanl 			 * up our state.
55439d3e169Sevanl 			 */
55539d3e169Sevanl 			mutex_enter(&fngp->fng_autofs_daemon_lock);
55639d3e169Sevanl 			if (dh == fngp->fng_autofs_daemon_dh) {
55739d3e169Sevanl 				door_ki_rele(fngp->fng_autofs_daemon_dh);
55839d3e169Sevanl 				fngp->fng_autofs_daemon_dh = NULL;
55939d3e169Sevanl 			}
56039d3e169Sevanl 			mutex_exit(&fngp->fng_autofs_daemon_lock);
561d6c8399bSrmesta 			AUTOFS_DPRINT((5, "auto_calldaemon error=%d\n", error));
56239d3e169Sevanl 			if (hard) {
5637c478bd9Sstevel@tonic-gate 				if (!fngp->fng_printed_not_running_msg) {
5647c478bd9Sstevel@tonic-gate 					fngp->fng_printed_not_running_msg = 1;
565d6c8399bSrmesta 					zprintf(zoneid, "automountd not "
56639d3e169Sevanl 					    "running, retrying\n");
5677c478bd9Sstevel@tonic-gate 				}
56839d3e169Sevanl 				delay(hz);
56939d3e169Sevanl 				retry = 1;
5707c478bd9Sstevel@tonic-gate 				break;
57139d3e169Sevanl 			} else {
57239d3e169Sevanl 				error = ECONNREFUSED;
57339d3e169Sevanl 				kmem_free(xdr_argsp,
57439d3e169Sevanl 				    xdr_len + sizeof (*xdr_argsp));
575d6c8399bSrmesta 				if (orp)
576d6c8399bSrmesta 					kmem_free(orp, orl);
57739d3e169Sevanl 				return (error);
57839d3e169Sevanl 			}
57939d3e169Sevanl 		default:	/* Unknown must be fatal */
5807c478bd9Sstevel@tonic-gate 			error = ENOENT;
58139d3e169Sevanl 			kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp));
582d6c8399bSrmesta 			if (orp)
583d6c8399bSrmesta 				kmem_free(orp, orl);
58439d3e169Sevanl 			return (error);
5857c478bd9Sstevel@tonic-gate 		}
58639d3e169Sevanl 	} while (retry);
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	if (fngp->fng_printed_not_running_msg == 1) {
5897c478bd9Sstevel@tonic-gate 		fngp->fng_printed_not_running_msg = 0;
59039d3e169Sevanl 		zprintf(zoneid, "automountd OK\n");
5917c478bd9Sstevel@tonic-gate 	}
59239d3e169Sevanl 
593d6c8399bSrmesta 	if (orp && orl) {
59439d3e169Sevanl 		autofs_door_res_t	*door_resp;
595d6c8399bSrmesta 		door_resp = (autofs_door_res_t *)door_args.rbuf;
596d6c8399bSrmesta 
597d6c8399bSrmesta 		if ((void *)door_args.rbuf != orp)
598d6c8399bSrmesta 			kmem_free(orp, orl);
599d6c8399bSrmesta 
60039d3e169Sevanl 		xdrmem_create(&xdrres, (char *)&door_resp->xdr_res,
60139d3e169Sevanl 		    door_resp->xdr_len, XDR_DECODE);
602d6c8399bSrmesta 
60339d3e169Sevanl 		if (!((*xresp_func)(&xdrres, resp)))
60439d3e169Sevanl 			error = EINVAL;
60539d3e169Sevanl 		kmem_free(door_args.rbuf, door_args.rsize);
6067c478bd9Sstevel@tonic-gate 	}
60739d3e169Sevanl 	kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp));
6087c478bd9Sstevel@tonic-gate 	return (error);
6097c478bd9Sstevel@tonic-gate }
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate static int
612f798ee53SJan Kryl auto_null_request(zoneid_t zoneid, bool_t hard)
6137c478bd9Sstevel@tonic-gate {
6147c478bd9Sstevel@tonic-gate 	int error;
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tauto_null_request\n"));
6177c478bd9Sstevel@tonic-gate 
618f798ee53SJan Kryl 	error = auto_calldaemon(zoneid, NULLPROC,
619d6c8399bSrmesta 	    xdr_void, NULL, xdr_void, NULL, 0, hard);
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tauto_null_request: error=%d\n", error));
6227c478bd9Sstevel@tonic-gate 	return (error);
6237c478bd9Sstevel@tonic-gate }
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate static int
6267c478bd9Sstevel@tonic-gate auto_lookup_request(
6277c478bd9Sstevel@tonic-gate 	fninfo_t *fnip,
6287c478bd9Sstevel@tonic-gate 	char *key,
6297c478bd9Sstevel@tonic-gate 	struct linka *lnp,
6307c478bd9Sstevel@tonic-gate 	bool_t hard,
6313bfb48feSsemery 	bool_t *mountreq,
6323bfb48feSsemery 	cred_t *cred)
6337c478bd9Sstevel@tonic-gate {
6347c478bd9Sstevel@tonic-gate 	int 				error;
6357c478bd9Sstevel@tonic-gate 	struct autofs_globals 		*fngp;
63639d3e169Sevanl 	struct autofs_lookupargs	 reqst;
63739d3e169Sevanl 	autofs_lookupres 		*resp;
6387c478bd9Sstevel@tonic-gate 	struct linka 			*p;
6397c478bd9Sstevel@tonic-gate 
64039d3e169Sevanl 
64139d3e169Sevanl 	AUTOFS_DPRINT((4, "auto_lookup_equest: path=%s name=%s\n",
6427c478bd9Sstevel@tonic-gate 	    fnip->fi_path, key));
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate 	fngp = vntofn(fnip->fi_rootvp)->fn_globals;
64539d3e169Sevanl 
64639d3e169Sevanl 	reqst.map = fnip->fi_map;
64739d3e169Sevanl 	reqst.path = fnip->fi_path;
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 	if (fnip->fi_flags & MF_DIRECT)
65039d3e169Sevanl 		reqst.name = fnip->fi_key;
6517c478bd9Sstevel@tonic-gate 	else
65239d3e169Sevanl 		reqst.name = key;
65339d3e169Sevanl 	AUTOFS_DPRINT((4, "auto_lookup_request: using key=%s\n", reqst.name));
6547c478bd9Sstevel@tonic-gate 
65539d3e169Sevanl 	reqst.subdir = fnip->fi_subdir;
65639d3e169Sevanl 	reqst.opts = fnip->fi_opts;
65739d3e169Sevanl 	reqst.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE;
6583bfb48feSsemery 	reqst.uid = crgetuid(cred);
6597c478bd9Sstevel@tonic-gate 
66039d3e169Sevanl 	resp = kmem_zalloc(sizeof (*resp), KM_SLEEP);
66139d3e169Sevanl 
662d6c8399bSrmesta 	error = auto_calldaemon(fngp->fng_zoneid, AUTOFS_LOOKUP,
663d6c8399bSrmesta 	    xdr_autofs_lookupargs, &reqst, xdr_autofs_lookupres,
664d6c8399bSrmesta 	    (void *)resp, sizeof (autofs_lookupres), hard);
66539d3e169Sevanl 
66639d3e169Sevanl 	if (error) {
66739d3e169Sevanl 		xdr_free(xdr_autofs_lookupres, (char *)resp);
66839d3e169Sevanl 		kmem_free(resp, sizeof (*resp));
66939d3e169Sevanl 		return (error);
67039d3e169Sevanl 	}
67139d3e169Sevanl 
6727c478bd9Sstevel@tonic-gate 	if (!error) {
67339d3e169Sevanl 		fngp->fng_verbose = resp->lu_verbose;
67439d3e169Sevanl 		switch (resp->lu_res) {
6757c478bd9Sstevel@tonic-gate 		case AUTOFS_OK:
67639d3e169Sevanl 			switch (resp->lu_type.action) {
6777c478bd9Sstevel@tonic-gate 			case AUTOFS_MOUNT_RQ:
6787c478bd9Sstevel@tonic-gate 				lnp->link = NULL;
6797c478bd9Sstevel@tonic-gate 				lnp->dir = NULL;
6807c478bd9Sstevel@tonic-gate 				*mountreq = TRUE;
6817c478bd9Sstevel@tonic-gate 				break;
682d6c8399bSrmesta 
6837c478bd9Sstevel@tonic-gate 			case AUTOFS_LINK_RQ:
684d6c8399bSrmesta 			p = &resp->lu_type.lookup_result_type_u.lt_linka;
6857c478bd9Sstevel@tonic-gate 				lnp->dir = kmem_alloc(strlen(p->dir) + 1,
6867c478bd9Sstevel@tonic-gate 				    KM_SLEEP);
6877c478bd9Sstevel@tonic-gate 				(void) strcpy(lnp->dir, p->dir);
6887c478bd9Sstevel@tonic-gate 				lnp->link = kmem_alloc(strlen(p->link) + 1,
6897c478bd9Sstevel@tonic-gate 				    KM_SLEEP);
6907c478bd9Sstevel@tonic-gate 				(void) strcpy(lnp->link, p->link);
6917c478bd9Sstevel@tonic-gate 				break;
692d6c8399bSrmesta 
6937c478bd9Sstevel@tonic-gate 			case AUTOFS_NONE:
6947c478bd9Sstevel@tonic-gate 				lnp->link = NULL;
6957c478bd9Sstevel@tonic-gate 				lnp->dir = NULL;
6967c478bd9Sstevel@tonic-gate 				break;
697d6c8399bSrmesta 
6987c478bd9Sstevel@tonic-gate 			default:
699d6c8399bSrmesta 				auto_log(fngp->fng_verbose, fngp->fng_zoneid,
700d6c8399bSrmesta 				    CE_WARN, "auto_lookup_request: bad action "
701d6c8399bSrmesta 				    "type %d", resp->lu_res);
7027c478bd9Sstevel@tonic-gate 				error = ENOENT;
7037c478bd9Sstevel@tonic-gate 			}
7047c478bd9Sstevel@tonic-gate 			break;
705d6c8399bSrmesta 
7067c478bd9Sstevel@tonic-gate 		case AUTOFS_NOENT:
7077c478bd9Sstevel@tonic-gate 			error = ENOENT;
7087c478bd9Sstevel@tonic-gate 			break;
709d6c8399bSrmesta 
7107c478bd9Sstevel@tonic-gate 		default:
7117c478bd9Sstevel@tonic-gate 			error = ENOENT;
71239d3e169Sevanl 			auto_log(fngp->fng_verbose, fngp->fng_zoneid, CE_WARN,
7137c478bd9Sstevel@tonic-gate 			    "auto_lookup_request: unknown result: %d",
71439d3e169Sevanl 			    resp->lu_res);
7157c478bd9Sstevel@tonic-gate 			break;
7167c478bd9Sstevel@tonic-gate 		}
7177c478bd9Sstevel@tonic-gate 	}
7187c478bd9Sstevel@tonic-gate done:
71939d3e169Sevanl 	xdr_free(xdr_autofs_lookupres, (char *)resp);
72039d3e169Sevanl 	kmem_free(resp, sizeof (*resp));
7217c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_lookup_request: path=%s name=%s error=%d\n",
7227c478bd9Sstevel@tonic-gate 	    fnip->fi_path, key, error));
7237c478bd9Sstevel@tonic-gate 	return (error);
7247c478bd9Sstevel@tonic-gate }
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate static int
7277c478bd9Sstevel@tonic-gate auto_mount_request(
7287c478bd9Sstevel@tonic-gate 	fninfo_t *fnip,
7297c478bd9Sstevel@tonic-gate 	char *key,
7307c478bd9Sstevel@tonic-gate 	action_list **alpp,
7313bfb48feSsemery 	cred_t *cred,
7327c478bd9Sstevel@tonic-gate 	bool_t hard)
7337c478bd9Sstevel@tonic-gate {
7347c478bd9Sstevel@tonic-gate 	int 			error;
7357c478bd9Sstevel@tonic-gate 	struct autofs_globals 	*fngp;
73639d3e169Sevanl 	autofs_lookupargs 	reqst;
73739d3e169Sevanl 	autofs_mountres		*xdrres = NULL;
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_mount_request: path=%s name=%s\n",
7407c478bd9Sstevel@tonic-gate 	    fnip->fi_path, key));
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate 	fngp = vntofn(fnip->fi_rootvp)->fn_globals;
74339d3e169Sevanl 	reqst.map = fnip->fi_map;
74439d3e169Sevanl 	reqst.path = fnip->fi_path;
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate 	if (fnip->fi_flags & MF_DIRECT)
74739d3e169Sevanl 		reqst.name = fnip->fi_key;
7487c478bd9Sstevel@tonic-gate 	else
74939d3e169Sevanl 		reqst.name = key;
7507c478bd9Sstevel@tonic-gate 
75139d3e169Sevanl 	AUTOFS_DPRINT((4, "auto_mount_request: using key=%s\n", reqst.name));
7527c478bd9Sstevel@tonic-gate 
75339d3e169Sevanl 	reqst.subdir = fnip->fi_subdir;
75439d3e169Sevanl 	reqst.opts = fnip->fi_opts;
75539d3e169Sevanl 	reqst.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE;
7563bfb48feSsemery 	reqst.uid = crgetuid(cred);
75739d3e169Sevanl 
75839d3e169Sevanl 	xdrres = kmem_zalloc(sizeof (*xdrres), KM_SLEEP);
75939d3e169Sevanl 
760d6c8399bSrmesta 	error = auto_calldaemon(fngp->fng_zoneid, AUTOFS_MNTINFO,
761d6c8399bSrmesta 	    xdr_autofs_lookupargs, &reqst, xdr_autofs_mountres,
762d6c8399bSrmesta 	    (void *)xdrres, sizeof (autofs_mountres), hard);
76339d3e169Sevanl 
7647c478bd9Sstevel@tonic-gate 	if (!error) {
76539d3e169Sevanl 		fngp->fng_verbose = xdrres->mr_verbose;
76639d3e169Sevanl 		switch (xdrres->mr_type.status) {
7677c478bd9Sstevel@tonic-gate 		case AUTOFS_ACTION:
7687c478bd9Sstevel@tonic-gate 			error = 0;
7697c478bd9Sstevel@tonic-gate 			/*
7707c478bd9Sstevel@tonic-gate 			 * Save the action list since it is used by
7717c478bd9Sstevel@tonic-gate 			 * the caller. We NULL the action list pointer
7727c478bd9Sstevel@tonic-gate 			 * in 'result' so that xdr_free() will not free
7737c478bd9Sstevel@tonic-gate 			 * the list.
7747c478bd9Sstevel@tonic-gate 			 */
77539d3e169Sevanl 			*alpp = xdrres->mr_type.mount_result_type_u.list;
77639d3e169Sevanl 			xdrres->mr_type.mount_result_type_u.list = NULL;
7777c478bd9Sstevel@tonic-gate 			break;
7787c478bd9Sstevel@tonic-gate 		case AUTOFS_DONE:
77939d3e169Sevanl 			error = xdrres->mr_type.mount_result_type_u.error;
7807c478bd9Sstevel@tonic-gate 			break;
7817c478bd9Sstevel@tonic-gate 		default:
7827c478bd9Sstevel@tonic-gate 			error = ENOENT;
78339d3e169Sevanl 			auto_log(fngp->fng_verbose, fngp->fng_zoneid, CE_WARN,
7847c478bd9Sstevel@tonic-gate 			    "auto_mount_request: unknown status %d",
78539d3e169Sevanl 			    xdrres->mr_type.status);
7867c478bd9Sstevel@tonic-gate 			break;
7877c478bd9Sstevel@tonic-gate 		}
7887c478bd9Sstevel@tonic-gate 	}
7897c478bd9Sstevel@tonic-gate 
79039d3e169Sevanl 	xdr_free(xdr_autofs_mountres, (char *)xdrres);
79139d3e169Sevanl 	kmem_free(xdrres, sizeof (*xdrres));
79239d3e169Sevanl 
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_mount_request: path=%s name=%s error=%d\n",
7957c478bd9Sstevel@tonic-gate 	    fnip->fi_path, key, error));
7967c478bd9Sstevel@tonic-gate 	return (error);
7977c478bd9Sstevel@tonic-gate }
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate static int
8017c478bd9Sstevel@tonic-gate auto_send_unmount_request(
8027c478bd9Sstevel@tonic-gate 	fninfo_t *fnip,
8037c478bd9Sstevel@tonic-gate 	umntrequest *ul,
8047c478bd9Sstevel@tonic-gate 	bool_t hard)
8057c478bd9Sstevel@tonic-gate {
8067c478bd9Sstevel@tonic-gate 	int 	error;
80739d3e169Sevanl 	umntres	xdrres;
80839d3e169Sevanl 
80939d3e169Sevanl 	struct autofs_globals *fngp = vntofn(fnip->fi_rootvp)->fn_globals;
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tauto_send_unmount_request: fstype=%s "
8127c478bd9Sstevel@tonic-gate 	    " mntpnt=%s\n", ul->fstype, ul->mntpnt));
8137c478bd9Sstevel@tonic-gate 
81439d3e169Sevanl 	bzero(&xdrres, sizeof (umntres));
815d6c8399bSrmesta 	error = auto_calldaemon(fngp->fng_zoneid, AUTOFS_UNMOUNT,
816d6c8399bSrmesta 	    xdr_umntrequest, (void *)ul, xdr_umntres, (void *)&xdrres,
817d6c8399bSrmesta 	    sizeof (umntres), hard);
8187c478bd9Sstevel@tonic-gate 
81946a207baSevanl 	if (!error)
82046a207baSevanl 		error = xdrres.status;
82146a207baSevanl 
8227c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tauto_send_unmount_request: error=%d\n", error));
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 	return (error);
8257c478bd9Sstevel@tonic-gate }
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate static int
8287c478bd9Sstevel@tonic-gate auto_perform_link(fnnode_t *fnp, struct linka *linkp, cred_t *cred)
8297c478bd9Sstevel@tonic-gate {
8307c478bd9Sstevel@tonic-gate 	vnode_t *vp;
8317c478bd9Sstevel@tonic-gate 	size_t len;
8327c478bd9Sstevel@tonic-gate 	char *tmp;
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((3, "auto_perform_link: fnp=%p dir=%s link=%s\n",
8357c478bd9Sstevel@tonic-gate 	    (void *)fnp, linkp->dir, linkp->link));
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate 	len = strlen(linkp->link) + 1;		/* include '\0' */
8387c478bd9Sstevel@tonic-gate 	tmp = kmem_zalloc(len, KM_SLEEP);
8397c478bd9Sstevel@tonic-gate 	(void) kcopy(linkp->link, tmp, len);
8407c478bd9Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
8417c478bd9Sstevel@tonic-gate 	fnp->fn_symlink = tmp;
8427c478bd9Sstevel@tonic-gate 	fnp->fn_symlinklen = (uint_t)len;
8437c478bd9Sstevel@tonic-gate 	fnp->fn_flags |= MF_THISUID_MATCH_RQD;
8447c478bd9Sstevel@tonic-gate 	crhold(cred);
8457c478bd9Sstevel@tonic-gate 	fnp->fn_cred = cred;
8467c478bd9Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 	vp = fntovn(fnp);
8497c478bd9Sstevel@tonic-gate 	vp->v_type = VLNK;
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 	return (0);
8527c478bd9Sstevel@tonic-gate }
8537c478bd9Sstevel@tonic-gate 
8545e1e04ceSdm120769 static void
8555e1e04ceSdm120769 auto_free_autofs_args(struct mounta *m)
8565e1e04ceSdm120769 {
8575e1e04ceSdm120769 	autofs_args	*aargs = (autofs_args *)m->dataptr;
8585e1e04ceSdm120769 
8595e1e04ceSdm120769 	if (aargs->addr.buf)
8605e1e04ceSdm120769 		kmem_free(aargs->addr.buf, aargs->addr.len);
8615e1e04ceSdm120769 	if (aargs->path)
8625e1e04ceSdm120769 		kmem_free(aargs->path, strlen(aargs->path) + 1);
8635e1e04ceSdm120769 	if (aargs->opts)
8645e1e04ceSdm120769 		kmem_free(aargs->opts, strlen(aargs->opts) + 1);
8655e1e04ceSdm120769 	if (aargs->map)
8665e1e04ceSdm120769 		kmem_free(aargs->map, strlen(aargs->map) + 1);
8675e1e04ceSdm120769 	if (aargs->subdir)
8685e1e04ceSdm120769 		kmem_free(aargs->subdir, strlen(aargs->subdir) + 1);
8695e1e04ceSdm120769 	if (aargs->key)
8705e1e04ceSdm120769 		kmem_free(aargs->key, strlen(aargs->key) + 1);
8715e1e04ceSdm120769 	kmem_free(aargs, sizeof (*aargs));
8725e1e04ceSdm120769 }
8735e1e04ceSdm120769 
8745e1e04ceSdm120769 static void
8755e1e04ceSdm120769 auto_free_action_list(action_list *alp)
8765e1e04ceSdm120769 {
8775e1e04ceSdm120769 	struct	mounta	*m;
8785e1e04ceSdm120769 	action_list	*lastalp;
8795e1e04ceSdm120769 	char		*fstype;
8805e1e04ceSdm120769 
8815e1e04ceSdm120769 	m = &alp->action.action_list_entry_u.mounta;
8825e1e04ceSdm120769 	while (alp != NULL) {
8835e1e04ceSdm120769 		fstype = alp->action.action_list_entry_u.mounta.fstype;
8845e1e04ceSdm120769 		m = &alp->action.action_list_entry_u.mounta;
8855e1e04ceSdm120769 		if (m->dataptr) {
8865e1e04ceSdm120769 			if (strcmp(fstype, "autofs") == 0) {
8875e1e04ceSdm120769 				auto_free_autofs_args(m);
8885e1e04ceSdm120769 			}
8895e1e04ceSdm120769 		}
8905e1e04ceSdm120769 		if (m->spec)
8915e1e04ceSdm120769 			kmem_free(m->spec, strlen(m->spec) + 1);
8925e1e04ceSdm120769 		if (m->dir)
8935e1e04ceSdm120769 			kmem_free(m->dir, strlen(m->dir) + 1);
8945e1e04ceSdm120769 		if (m->fstype)
8955e1e04ceSdm120769 			kmem_free(m->fstype, strlen(m->fstype) + 1);
8965e1e04ceSdm120769 		if (m->optptr)
8975e1e04ceSdm120769 			kmem_free(m->optptr, m->optlen);
8985e1e04ceSdm120769 		lastalp = alp;
8995e1e04ceSdm120769 		alp = alp->next;
9005e1e04ceSdm120769 		kmem_free(lastalp, sizeof (*lastalp));
9015e1e04ceSdm120769 	}
9025e1e04ceSdm120769 }
9035e1e04ceSdm120769 
9047c478bd9Sstevel@tonic-gate static boolean_t
90539d3e169Sevanl auto_invalid_autofs(fninfo_t *dfnip, fnnode_t *dfnp, action_list *p)
9067c478bd9Sstevel@tonic-gate {
9077c478bd9Sstevel@tonic-gate 	struct mounta *m;
9087c478bd9Sstevel@tonic-gate 	struct autofs_args *argsp;
9097c478bd9Sstevel@tonic-gate 	vnode_t *dvp;
9107c478bd9Sstevel@tonic-gate 	char buff[AUTOFS_MAXPATHLEN];
9117c478bd9Sstevel@tonic-gate 	size_t len;
9127c478bd9Sstevel@tonic-gate 	struct autofs_globals *fngp;
9135e1e04ceSdm120769 
9147c478bd9Sstevel@tonic-gate 	fngp = dfnp->fn_globals;
9157c478bd9Sstevel@tonic-gate 	dvp = fntovn(dfnp);
91639d3e169Sevanl 
9177c478bd9Sstevel@tonic-gate 	m = &p->action.action_list_entry_u.mounta;
9187c478bd9Sstevel@tonic-gate 	/*
9197c478bd9Sstevel@tonic-gate 	 * Make sure we aren't geting passed NULL values or a "dir" that
9207c478bd9Sstevel@tonic-gate 	 * isn't "." and doesn't begin with "./".
9217c478bd9Sstevel@tonic-gate 	 *
9227c478bd9Sstevel@tonic-gate 	 * We also only want to perform autofs mounts, so make sure
9237c478bd9Sstevel@tonic-gate 	 * no-one is trying to trick us into doing anything else.
9247c478bd9Sstevel@tonic-gate 	 */
9255e1e04ceSdm120769 	if (m->spec == NULL || m->dir == NULL || m->dir[0] != '.' ||
9267c478bd9Sstevel@tonic-gate 	    (m->dir[1] != '/' && m->dir[1] != '\0') ||
9275e1e04ceSdm120769 	    m->fstype == NULL || strcmp(m->fstype, "autofs") != 0 ||
9285e1e04ceSdm120769 	    m->dataptr == NULL || m->datalen != sizeof (struct autofs_args) ||
9297c478bd9Sstevel@tonic-gate 	    m->optptr == NULL)
9307c478bd9Sstevel@tonic-gate 		return (B_TRUE);
9317c478bd9Sstevel@tonic-gate 	/*
9327c478bd9Sstevel@tonic-gate 	 * We also don't like ".."s in the pathname.  Symlinks are
9337c478bd9Sstevel@tonic-gate 	 * handled by the fact that we'll use NOFOLLOW when we do
9347c478bd9Sstevel@tonic-gate 	 * lookup()s.
9357c478bd9Sstevel@tonic-gate 	 */
9367c478bd9Sstevel@tonic-gate 	if (strstr(m->dir, "/../") != NULL ||
9377c478bd9Sstevel@tonic-gate 	    (len = strlen(m->dir)) > sizeof ("/..") - 1 &&
9387c478bd9Sstevel@tonic-gate 	    m->dir[len] == '.' && m->dir[len - 1] == '.' &&
9397c478bd9Sstevel@tonic-gate 	    m->dir[len - 2] == '/')
9407c478bd9Sstevel@tonic-gate 		return (B_TRUE);
9417c478bd9Sstevel@tonic-gate 	argsp = (struct autofs_args *)m->dataptr;
9427c478bd9Sstevel@tonic-gate 	/*
9437c478bd9Sstevel@tonic-gate 	 * We don't want NULL values here either.
9447c478bd9Sstevel@tonic-gate 	 */
9457c478bd9Sstevel@tonic-gate 	if (argsp->addr.buf == NULL || argsp->path == NULL ||
9467c478bd9Sstevel@tonic-gate 	    argsp->opts == NULL || argsp->map == NULL || argsp->subdir == NULL)
9477c478bd9Sstevel@tonic-gate 		return (B_TRUE);
9487c478bd9Sstevel@tonic-gate 	/*
9497c478bd9Sstevel@tonic-gate 	 * We know what the claimed pathname *should* look like:
9507c478bd9Sstevel@tonic-gate 	 *
9517c478bd9Sstevel@tonic-gate 	 * If the parent (dfnp) is a mount point (VROOT), then
9527c478bd9Sstevel@tonic-gate 	 * the path should be (dfnip->fi_path + m->dir).
9537c478bd9Sstevel@tonic-gate 	 *
9547c478bd9Sstevel@tonic-gate 	 * Else, we know we're only two levels deep, so we use
9557c478bd9Sstevel@tonic-gate 	 * (dfnip->fi_path + dfnp->fn_name + m->dir).
9567c478bd9Sstevel@tonic-gate 	 *
9577c478bd9Sstevel@tonic-gate 	 * Furthermore, "." only makes sense if dfnp is a
9587c478bd9Sstevel@tonic-gate 	 * trigger node.
9597c478bd9Sstevel@tonic-gate 	 *
9607c478bd9Sstevel@tonic-gate 	 * At this point it seems like the passed-in path is
9617c478bd9Sstevel@tonic-gate 	 * redundant.
9627c478bd9Sstevel@tonic-gate 	 */
9637c478bd9Sstevel@tonic-gate 	if (dvp->v_flag & VROOT) {
9647c478bd9Sstevel@tonic-gate 		if (m->dir[1] == '\0' && !(dfnp->fn_flags & MF_TRIGGER))
9657c478bd9Sstevel@tonic-gate 			return (B_TRUE);
9667c478bd9Sstevel@tonic-gate 		(void) snprintf(buff, sizeof (buff), "%s%s",
9677c478bd9Sstevel@tonic-gate 		    dfnip->fi_path, m->dir + 1);
9687c478bd9Sstevel@tonic-gate 	} else {
9697c478bd9Sstevel@tonic-gate 		(void) snprintf(buff, sizeof (buff), "%s/%s%s",
9707c478bd9Sstevel@tonic-gate 		    dfnip->fi_path, dfnp->fn_name, m->dir + 1);
9717c478bd9Sstevel@tonic-gate 	}
9727c478bd9Sstevel@tonic-gate 	if (strcmp(argsp->path, buff) != 0) {
97339d3e169Sevanl 		auto_log(fngp->fng_verbose, fngp->fng_zoneid,
97439d3e169Sevanl 		    CE_WARN, "autofs: expected path of '%s', "
9757c478bd9Sstevel@tonic-gate 		    "got '%s' instead.", buff, argsp->path);
9767c478bd9Sstevel@tonic-gate 		return (B_TRUE);
9777c478bd9Sstevel@tonic-gate 	}
9787c478bd9Sstevel@tonic-gate 	return (B_FALSE); /* looks OK */
9797c478bd9Sstevel@tonic-gate }
9807c478bd9Sstevel@tonic-gate 
98139d3e169Sevanl /*
98239d3e169Sevanl  * auto_invalid_action will validate the action_list received.  If all is good
9835e1e04ceSdm120769  * this function returns FALSE, if there is a problem it returns TRUE.
98439d3e169Sevanl  */
98539d3e169Sevanl static boolean_t
98639d3e169Sevanl auto_invalid_action(fninfo_t *dfnip, fnnode_t *dfnp, action_list *alistpp)
98739d3e169Sevanl {
98839d3e169Sevanl 
98939d3e169Sevanl 	/*
99039d3e169Sevanl 	 * Before we go any further, this better be a mount request.
99139d3e169Sevanl 	 */
99239d3e169Sevanl 	if (alistpp->action.action != AUTOFS_MOUNT_RQ)
99339d3e169Sevanl 		return (B_TRUE);
9949670a2d7Skr143551 	return (auto_invalid_autofs(dfnip, dfnp, alistpp));
9955e1e04ceSdm120769 
99639d3e169Sevanl }
99739d3e169Sevanl 
9987c478bd9Sstevel@tonic-gate static int
9997c478bd9Sstevel@tonic-gate auto_perform_actions(
10007c478bd9Sstevel@tonic-gate 	fninfo_t *dfnip,
10017c478bd9Sstevel@tonic-gate 	fnnode_t *dfnp,
10027c478bd9Sstevel@tonic-gate 	action_list *alp,
10037c478bd9Sstevel@tonic-gate 	cred_t *cred)	/* Credentials of the caller */
10047c478bd9Sstevel@tonic-gate {
100539d3e169Sevanl 
10067c478bd9Sstevel@tonic-gate 	action_list *p;
10077c478bd9Sstevel@tonic-gate 	struct mounta		*m, margs;
10087c478bd9Sstevel@tonic-gate 	struct autofs_args 		*argsp;
10097c478bd9Sstevel@tonic-gate 	int 			error, success = 0;
10107c478bd9Sstevel@tonic-gate 	vnode_t 		*mvp, *dvp, *newvp;
10117c478bd9Sstevel@tonic-gate 	fnnode_t 		*newfnp, *mfnp;
10127c478bd9Sstevel@tonic-gate 	int 			auto_mount = 0;
101339d3e169Sevanl 	int 			save_triggers = 0;
10147c478bd9Sstevel@tonic-gate 	int 			update_times = 0;
10157c478bd9Sstevel@tonic-gate 	char 			*mntpnt;
10167c478bd9Sstevel@tonic-gate 	char 			buff[AUTOFS_MAXPATHLEN];
10177c478bd9Sstevel@tonic-gate 	timestruc_t 		now;
10187c478bd9Sstevel@tonic-gate 	struct autofs_globals 	*fngp;
101939d3e169Sevanl 	cred_t 			*zcred;
10207c478bd9Sstevel@tonic-gate 
1021d6c8399bSrmesta 	AUTOFS_DPRINT((4, "auto_perform_actions: alp=%p\n", (void *)alp));
10227c478bd9Sstevel@tonic-gate 
10237c478bd9Sstevel@tonic-gate 	fngp = dfnp->fn_globals;
10247c478bd9Sstevel@tonic-gate 	dvp = fntovn(dfnp);
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate 	/*
10277c478bd9Sstevel@tonic-gate 	 * As automountd running in a zone may be compromised, and this may be
10287c478bd9Sstevel@tonic-gate 	 * an attack, we can't trust everything passed in by automountd, and we
10297c478bd9Sstevel@tonic-gate 	 * need to do argument verification.  We'll issue a warning and drop
10307c478bd9Sstevel@tonic-gate 	 * the request if it doesn't seem right.
10317c478bd9Sstevel@tonic-gate 	 */
103239d3e169Sevanl 
10337c478bd9Sstevel@tonic-gate 	for (p = alp; p != NULL; p = p->next) {
10347c478bd9Sstevel@tonic-gate 		if (auto_invalid_action(dfnip, dfnp, p)) {
10357c478bd9Sstevel@tonic-gate 			/*
10367c478bd9Sstevel@tonic-gate 			 * This warning should be sent to the global zone,
10377c478bd9Sstevel@tonic-gate 			 * since presumably the zone administrator is the same
10387c478bd9Sstevel@tonic-gate 			 * as the attacker.
10397c478bd9Sstevel@tonic-gate 			 */
10407c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "autofs: invalid action list received "
10417c478bd9Sstevel@tonic-gate 			    "by automountd in zone %s.",
10427c478bd9Sstevel@tonic-gate 			    curproc->p_zone->zone_name);
10437c478bd9Sstevel@tonic-gate 			/*
10447c478bd9Sstevel@tonic-gate 			 * This conversation is over.
10457c478bd9Sstevel@tonic-gate 			 */
10467c478bd9Sstevel@tonic-gate 			xdr_free(xdr_action_list, (char *)alp);
10477c478bd9Sstevel@tonic-gate 			return (EINVAL);
10487c478bd9Sstevel@tonic-gate 		}
10497c478bd9Sstevel@tonic-gate 	}
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 	zcred = zone_get_kcred(getzoneid());
10527c478bd9Sstevel@tonic-gate 	ASSERT(zcred != NULL);
10537c478bd9Sstevel@tonic-gate 
10547c478bd9Sstevel@tonic-gate 	if (vn_mountedvfs(dvp) != NULL) {
10557c478bd9Sstevel@tonic-gate 		/*
10567c478bd9Sstevel@tonic-gate 		 * The daemon successfully mounted a filesystem
10577c478bd9Sstevel@tonic-gate 		 * on the AUTOFS root node.
10587c478bd9Sstevel@tonic-gate 		 */
10597c478bd9Sstevel@tonic-gate 		mutex_enter(&dfnp->fn_lock);
10607c478bd9Sstevel@tonic-gate 		dfnp->fn_flags |= MF_MOUNTPOINT;
10617c478bd9Sstevel@tonic-gate 		ASSERT(dfnp->fn_dirents == NULL);
10627c478bd9Sstevel@tonic-gate 		mutex_exit(&dfnp->fn_lock);
10637c478bd9Sstevel@tonic-gate 		success++;
10647c478bd9Sstevel@tonic-gate 	} else {
10657c478bd9Sstevel@tonic-gate 		/*
10667c478bd9Sstevel@tonic-gate 		 * Clear MF_MOUNTPOINT.
10677c478bd9Sstevel@tonic-gate 		 */
10687c478bd9Sstevel@tonic-gate 		mutex_enter(&dfnp->fn_lock);
10697c478bd9Sstevel@tonic-gate 		if (dfnp->fn_flags & MF_MOUNTPOINT) {
10707c478bd9Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "autofs: clearing mountpoint "
10717c478bd9Sstevel@tonic-gate 			    "flag on %s.", dfnp->fn_name));
10727c478bd9Sstevel@tonic-gate 			ASSERT(dfnp->fn_dirents == NULL);
10737c478bd9Sstevel@tonic-gate 			ASSERT(dfnp->fn_trigger == NULL);
10747c478bd9Sstevel@tonic-gate 		}
10757c478bd9Sstevel@tonic-gate 		dfnp->fn_flags &= ~MF_MOUNTPOINT;
10767c478bd9Sstevel@tonic-gate 		mutex_exit(&dfnp->fn_lock);
10777c478bd9Sstevel@tonic-gate 	}
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate 	for (p = alp; p != NULL; p = p->next) {
108039d3e169Sevanl 
10817c478bd9Sstevel@tonic-gate 		vfs_t *vfsp;	/* dummy argument */
10827c478bd9Sstevel@tonic-gate 		vfs_t *mvfsp;
10837c478bd9Sstevel@tonic-gate 
10847c478bd9Sstevel@tonic-gate 		auto_mount = 0;
10857c478bd9Sstevel@tonic-gate 
10867c478bd9Sstevel@tonic-gate 		m = &p->action.action_list_entry_u.mounta;
10877c478bd9Sstevel@tonic-gate 		argsp = (struct autofs_args *)m->dataptr;
108839d3e169Sevanl 		ASSERT(strcmp(m->fstype, "autofs") == 0);
10897c478bd9Sstevel@tonic-gate 		/*
10907c478bd9Sstevel@tonic-gate 		 * use the parent directory's timeout since it's the
10917c478bd9Sstevel@tonic-gate 		 * one specified/inherited by automount.
10927c478bd9Sstevel@tonic-gate 		 */
10937c478bd9Sstevel@tonic-gate 		argsp->mount_to = dfnip->fi_mount_to;
10947c478bd9Sstevel@tonic-gate 		/*
10957c478bd9Sstevel@tonic-gate 		 * The mountpoint is relative, and it is guaranteed to
10967c478bd9Sstevel@tonic-gate 		 * begin with "."
10977c478bd9Sstevel@tonic-gate 		 *
10987c478bd9Sstevel@tonic-gate 		 */
10997c478bd9Sstevel@tonic-gate 		ASSERT(m->dir[0] == '.');
11007c478bd9Sstevel@tonic-gate 		if (m->dir[0] == '.' && m->dir[1] == '\0') {
11017c478bd9Sstevel@tonic-gate 			/*
11027c478bd9Sstevel@tonic-gate 			 * mounting on the trigger node
11037c478bd9Sstevel@tonic-gate 			 */
11047c478bd9Sstevel@tonic-gate 			mvp = dvp;
11057c478bd9Sstevel@tonic-gate 			VN_HOLD(mvp);
11067c478bd9Sstevel@tonic-gate 			goto mount;
11077c478bd9Sstevel@tonic-gate 		}
11087c478bd9Sstevel@tonic-gate 		/*
11097c478bd9Sstevel@tonic-gate 		 * ignore "./" in front of mountpoint
11107c478bd9Sstevel@tonic-gate 		 */
11117c478bd9Sstevel@tonic-gate 		ASSERT(m->dir[1] == '/');
11127c478bd9Sstevel@tonic-gate 		mntpnt = m->dir + 2;
11137c478bd9Sstevel@tonic-gate 
11145e1e04ceSdm120769 		AUTOFS_DPRINT((10, "\tdfnip->fi_path=%s\n", dfnip->fi_path));
11155e1e04ceSdm120769 		AUTOFS_DPRINT((10, "\tdfnip->fi_flags=%x\n", dfnip->fi_flags));
11167c478bd9Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tmntpnt=%s\n", mntpnt));
11177c478bd9Sstevel@tonic-gate 
11187c478bd9Sstevel@tonic-gate 		if (dfnip->fi_flags & MF_DIRECT) {
11197c478bd9Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tDIRECT\n"));
1120d6c8399bSrmesta 			(void) sprintf(buff, "%s/%s", dfnip->fi_path, mntpnt);
11217c478bd9Sstevel@tonic-gate 		} else {
11227c478bd9Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tINDIRECT\n"));
112339d3e169Sevanl 			(void) sprintf(buff, "%s/%s/%s",
1124d6c8399bSrmesta 			    dfnip->fi_path, dfnp->fn_name, mntpnt);
11257c478bd9Sstevel@tonic-gate 		}
11267c478bd9Sstevel@tonic-gate 
11277c478bd9Sstevel@tonic-gate 		if (vn_mountedvfs(dvp) == NULL) {
11287c478bd9Sstevel@tonic-gate 			/*
11297c478bd9Sstevel@tonic-gate 			 * Daemon didn't mount anything on the root
113039d3e169Sevanl 			 * We have to create the mountpoint if it
113139d3e169Sevanl 			 * doesn't exist already
11327c478bd9Sstevel@tonic-gate 			 *
113339d3e169Sevanl 			 * We use the caller's credentials in case a
113439d3e169Sevanl 			 * UID-match is required
113539d3e169Sevanl 			 * (MF_THISUID_MATCH_RQD).
11367c478bd9Sstevel@tonic-gate 			 */
11377c478bd9Sstevel@tonic-gate 			rw_enter(&dfnp->fn_rwlock, RW_WRITER);
11387c478bd9Sstevel@tonic-gate 			error = auto_search(dfnp, mntpnt, &mfnp, cred);
11397c478bd9Sstevel@tonic-gate 			if (error == 0) {
11407c478bd9Sstevel@tonic-gate 				/*
11417c478bd9Sstevel@tonic-gate 				 * AUTOFS mountpoint exists
11427c478bd9Sstevel@tonic-gate 				 */
11435e1e04ceSdm120769 				if (vn_mountedvfs(fntovn(mfnp)) != NULL) {
11447c478bd9Sstevel@tonic-gate 					cmn_err(CE_PANIC,
11457c478bd9Sstevel@tonic-gate 					    "auto_perform_actions:"
1146d6c8399bSrmesta 					    " mfnp=%p covered", (void *)mfnp);
11477c478bd9Sstevel@tonic-gate 				}
11487c478bd9Sstevel@tonic-gate 			} else {
11497c478bd9Sstevel@tonic-gate 				/*
11507c478bd9Sstevel@tonic-gate 				 * Create AUTOFS mountpoint
11517c478bd9Sstevel@tonic-gate 				 */
11525e1e04ceSdm120769 				ASSERT((dfnp->fn_flags & MF_MOUNTPOINT) == 0);
11535e1e04ceSdm120769 				error = auto_enter(dfnp, mntpnt, &mfnp, cred);
11547c478bd9Sstevel@tonic-gate 				ASSERT(mfnp->fn_linkcnt == 1);
11557c478bd9Sstevel@tonic-gate 				mfnp->fn_linkcnt++;
11567c478bd9Sstevel@tonic-gate 			}
11577c478bd9Sstevel@tonic-gate 			if (!error)
11587c478bd9Sstevel@tonic-gate 				update_times = 1;
11597c478bd9Sstevel@tonic-gate 			rw_exit(&dfnp->fn_rwlock);
11607c478bd9Sstevel@tonic-gate 			ASSERT(error != EEXIST);
11617c478bd9Sstevel@tonic-gate 			if (!error) {
11627c478bd9Sstevel@tonic-gate 				/*
11637c478bd9Sstevel@tonic-gate 				 * mfnp is already held.
11647c478bd9Sstevel@tonic-gate 				 */
11657c478bd9Sstevel@tonic-gate 				mvp = fntovn(mfnp);
11667c478bd9Sstevel@tonic-gate 			} else {
11675e1e04ceSdm120769 				auto_log(fngp->fng_verbose, fngp->fng_zoneid,
116839d3e169Sevanl 				    CE_WARN, "autofs: mount of %s "
116939d3e169Sevanl 				    "failed - can't create"
117039d3e169Sevanl 				    " mountpoint.", buff);
11717c478bd9Sstevel@tonic-gate 				continue;
11727c478bd9Sstevel@tonic-gate 			}
11737c478bd9Sstevel@tonic-gate 		} else {
11747c478bd9Sstevel@tonic-gate 			/*
117539d3e169Sevanl 			 * Find mountpoint in VFS mounted here. If not
117639d3e169Sevanl 			 * found, fail the submount, though the overall
117739d3e169Sevanl 			 * mount has succeeded since the root is
117839d3e169Sevanl 			 * mounted.
11797c478bd9Sstevel@tonic-gate 			 */
1180d6c8399bSrmesta 			if (error = auto_getmntpnt(dvp, mntpnt, &mvp, kcred)) {
1181d6c8399bSrmesta 				auto_log(fngp->fng_verbose, fngp->fng_zoneid,
118239d3e169Sevanl 				    CE_WARN, "autofs: mount of %s "
118339d3e169Sevanl 				    "failed - mountpoint doesn't"
118439d3e169Sevanl 				    " exist.", buff);
11857c478bd9Sstevel@tonic-gate 				continue;
11867c478bd9Sstevel@tonic-gate 			}
11877c478bd9Sstevel@tonic-gate 			if (mvp->v_type == VLNK) {
1188d6c8399bSrmesta 				auto_log(fngp->fng_verbose, fngp->fng_zoneid,
118939d3e169Sevanl 				    CE_WARN, "autofs: %s symbolic "
11907c478bd9Sstevel@tonic-gate 				    "link: not a valid mountpoint "
11917c478bd9Sstevel@tonic-gate 				    "- mount failed", buff);
11927c478bd9Sstevel@tonic-gate 				VN_RELE(mvp);
11937c478bd9Sstevel@tonic-gate 				error = ENOENT;
11947c478bd9Sstevel@tonic-gate 				continue;
11957c478bd9Sstevel@tonic-gate 			}
11967c478bd9Sstevel@tonic-gate 		}
11977c478bd9Sstevel@tonic-gate mount:
11987c478bd9Sstevel@tonic-gate 		m->flags |= MS_SYSSPACE | MS_OPTIONSTR;
119939d3e169Sevanl 
12007c478bd9Sstevel@tonic-gate 		/*
120139d3e169Sevanl 		 * Copy mounta struct here so we can substitute a
120239d3e169Sevanl 		 * buffer that is large enough to hold the returned
120339d3e169Sevanl 		 * option string, if that string is longer than the
120439d3e169Sevanl 		 * input option string.
12057c478bd9Sstevel@tonic-gate 		 * This can happen if there are default options enabled
12067c478bd9Sstevel@tonic-gate 		 * that were not in the input option string.
12077c478bd9Sstevel@tonic-gate 		 */
12087c478bd9Sstevel@tonic-gate 		bcopy(m, &margs, sizeof (*m));
12097c478bd9Sstevel@tonic-gate 		margs.optptr = kmem_alloc(MAX_MNTOPT_STR, KM_SLEEP);
12107c478bd9Sstevel@tonic-gate 		margs.optlen = MAX_MNTOPT_STR;
12117c478bd9Sstevel@tonic-gate 		(void) strcpy(margs.optptr, m->optptr);
12127c478bd9Sstevel@tonic-gate 		margs.dir = argsp->path;
121339d3e169Sevanl 
12147c478bd9Sstevel@tonic-gate 		/*
121539d3e169Sevanl 		 * We use the zone's kcred because we don't want the
121639d3e169Sevanl 		 * zone to be able to thus do something it wouldn't
121739d3e169Sevanl 		 * normally be able to.
12187c478bd9Sstevel@tonic-gate 		 */
12197c478bd9Sstevel@tonic-gate 		error = domount(NULL, &margs, mvp, zcred, &vfsp);
12207c478bd9Sstevel@tonic-gate 		kmem_free(margs.optptr, MAX_MNTOPT_STR);
12217c478bd9Sstevel@tonic-gate 		if (error != 0) {
122239d3e169Sevanl 			auto_log(fngp->fng_verbose, fngp->fng_zoneid,
12235e1e04ceSdm120769 			    CE_WARN, "autofs: domount of %s failed "
122439d3e169Sevanl 			    "error=%d", buff, error);
12257c478bd9Sstevel@tonic-gate 			VN_RELE(mvp);
12267c478bd9Sstevel@tonic-gate 			continue;
12277c478bd9Sstevel@tonic-gate 		}
12287c478bd9Sstevel@tonic-gate 		VFS_RELE(vfsp);
12297c478bd9Sstevel@tonic-gate 
12307c478bd9Sstevel@tonic-gate 		/*
12317c478bd9Sstevel@tonic-gate 		 * If mountpoint is an AUTOFS node, then I'm going to
123239d3e169Sevanl 		 * flag it that the Filesystem mounted on top was
123339d3e169Sevanl 		 * mounted in the kernel so that the unmount can be
123439d3e169Sevanl 		 * done inside the kernel as well.
123539d3e169Sevanl 		 * I don't care to flag non-AUTOFS mountpoints when an
123639d3e169Sevanl 		 * AUTOFS in-kernel mount was done on top, because the
123739d3e169Sevanl 		 * unmount routine already knows that such case was
123839d3e169Sevanl 		 * done in the kernel.
12397c478bd9Sstevel@tonic-gate 		 */
1240d6c8399bSrmesta 		if (vfs_matchops(dvp->v_vfsp, vfs_getops(mvp->v_vfsp))) {
12417c478bd9Sstevel@tonic-gate 			mfnp = vntofn(mvp);
12427c478bd9Sstevel@tonic-gate 			mutex_enter(&mfnp->fn_lock);
12437c478bd9Sstevel@tonic-gate 			mfnp->fn_flags |= MF_IK_MOUNT;
12447c478bd9Sstevel@tonic-gate 			mutex_exit(&mfnp->fn_lock);
12457c478bd9Sstevel@tonic-gate 		}
12467c478bd9Sstevel@tonic-gate 
124739d3e169Sevanl 		(void) vn_vfswlock_wait(mvp);
12487c478bd9Sstevel@tonic-gate 		mvfsp = vn_mountedvfs(mvp);
12497c478bd9Sstevel@tonic-gate 		if (mvfsp != NULL) {
125039d3e169Sevanl 			vfs_lock_wait(mvfsp);
125195b97885Snr123932 			vn_vfsunlock(mvp);
125239d3e169Sevanl 			error = VFS_ROOT(mvfsp, &newvp);
125339d3e169Sevanl 			vfs_unlock(mvfsp);
12547c478bd9Sstevel@tonic-gate 			if (error) {
12557c478bd9Sstevel@tonic-gate 				/*
125639d3e169Sevanl 				 * We've dropped the locks, so let's
125739d3e169Sevanl 				 * get the mounted vfs again in case
125839d3e169Sevanl 				 * it changed.
12597c478bd9Sstevel@tonic-gate 				 */
12607c478bd9Sstevel@tonic-gate 				(void) vn_vfswlock_wait(mvp);
12617c478bd9Sstevel@tonic-gate 				mvfsp = vn_mountedvfs(mvp);
12627c478bd9Sstevel@tonic-gate 				if (mvfsp != NULL) {
12635e1e04ceSdm120769 					error = dounmount(mvfsp, 0, CRED());
12647c478bd9Sstevel@tonic-gate 					if (error) {
12657c478bd9Sstevel@tonic-gate 						cmn_err(CE_WARN,
1266d6c8399bSrmesta 						    "autofs: could not unmount"
1267d6c8399bSrmesta 						    " vfs=%p", (void *)mvfsp);
12687c478bd9Sstevel@tonic-gate 					}
12697c478bd9Sstevel@tonic-gate 				} else
12707c478bd9Sstevel@tonic-gate 					vn_vfsunlock(mvp);
12717c478bd9Sstevel@tonic-gate 				VN_RELE(mvp);
12727c478bd9Sstevel@tonic-gate 				continue;
12737c478bd9Sstevel@tonic-gate 			}
12747c478bd9Sstevel@tonic-gate 		} else {
12757c478bd9Sstevel@tonic-gate 			vn_vfsunlock(mvp);
12767c478bd9Sstevel@tonic-gate 			VN_RELE(mvp);
12777c478bd9Sstevel@tonic-gate 			continue;
12787c478bd9Sstevel@tonic-gate 		}
12797c478bd9Sstevel@tonic-gate 
12807c478bd9Sstevel@tonic-gate 		auto_mount = vfs_matchops(dvp->v_vfsp,
12817c478bd9Sstevel@tonic-gate 		    vfs_getops(newvp->v_vfsp));
12827c478bd9Sstevel@tonic-gate 		newfnp = vntofn(newvp);
12837c478bd9Sstevel@tonic-gate 		newfnp->fn_parent = dfnp;
12847c478bd9Sstevel@tonic-gate 
12857c478bd9Sstevel@tonic-gate 		/*
128639d3e169Sevanl 		 * At this time we want to save the AUTOFS filesystem
128739d3e169Sevanl 		 * as a trigger node. (We only do this if the mount
128839d3e169Sevanl 		 * occurred on a node different from the root.
12897c478bd9Sstevel@tonic-gate 		 * We look at the trigger nodes during
12907c478bd9Sstevel@tonic-gate 		 * the automatic unmounting to make sure we remove them
129139d3e169Sevanl 		 * as a unit and remount them as a unit if the
129239d3e169Sevanl 		 * filesystem mounted at the root could not be
129339d3e169Sevanl 		 * unmounted.
12947c478bd9Sstevel@tonic-gate 		 */
12957c478bd9Sstevel@tonic-gate 		if (auto_mount && (error == 0) && (mvp != dvp)) {
12967c478bd9Sstevel@tonic-gate 			save_triggers++;
12977c478bd9Sstevel@tonic-gate 			/*
12987c478bd9Sstevel@tonic-gate 			 * Add AUTOFS mount to hierarchy
12997c478bd9Sstevel@tonic-gate 			 */
13007c478bd9Sstevel@tonic-gate 			newfnp->fn_flags |= MF_TRIGGER;
13017c478bd9Sstevel@tonic-gate 			rw_enter(&newfnp->fn_rwlock, RW_WRITER);
13027c478bd9Sstevel@tonic-gate 			newfnp->fn_next = dfnp->fn_trigger;
13037c478bd9Sstevel@tonic-gate 			rw_exit(&newfnp->fn_rwlock);
13047c478bd9Sstevel@tonic-gate 			rw_enter(&dfnp->fn_rwlock, RW_WRITER);
13057c478bd9Sstevel@tonic-gate 			dfnp->fn_trigger = newfnp;
13067c478bd9Sstevel@tonic-gate 			rw_exit(&dfnp->fn_rwlock);
13077c478bd9Sstevel@tonic-gate 			/*
130839d3e169Sevanl 			 * Don't VN_RELE(newvp) here since dfnp now
130939d3e169Sevanl 			 * holds reference to it as its trigger node.
13107c478bd9Sstevel@tonic-gate 			 */
13115e1e04ceSdm120769 			AUTOFS_DPRINT((10, "\tadding trigger %s to %s\n",
13127c478bd9Sstevel@tonic-gate 			    newfnp->fn_name, dfnp->fn_name));
13137c478bd9Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tfirst trigger is %s\n",
13147c478bd9Sstevel@tonic-gate 			    dfnp->fn_trigger->fn_name));
13157c478bd9Sstevel@tonic-gate 			if (newfnp->fn_next != NULL)
1316d6c8399bSrmesta 				AUTOFS_DPRINT((10, "\tnext trigger is %s\n",
13177c478bd9Sstevel@tonic-gate 				    newfnp->fn_next->fn_name));
13187c478bd9Sstevel@tonic-gate 			else
1319d6c8399bSrmesta 				AUTOFS_DPRINT((10, "\tno next trigger\n"));
13207c478bd9Sstevel@tonic-gate 		} else
13217c478bd9Sstevel@tonic-gate 			VN_RELE(newvp);
13227c478bd9Sstevel@tonic-gate 
13237c478bd9Sstevel@tonic-gate 		if (!error)
13247c478bd9Sstevel@tonic-gate 			success++;
13257c478bd9Sstevel@tonic-gate 
13267c478bd9Sstevel@tonic-gate 		if (update_times) {
13277c478bd9Sstevel@tonic-gate 			gethrestime(&now);
13287c478bd9Sstevel@tonic-gate 			dfnp->fn_atime = dfnp->fn_mtime = now;
13297c478bd9Sstevel@tonic-gate 		}
13307c478bd9Sstevel@tonic-gate 
13317c478bd9Sstevel@tonic-gate 		VN_RELE(mvp);
13327c478bd9Sstevel@tonic-gate 	}
13337c478bd9Sstevel@tonic-gate 
13347c478bd9Sstevel@tonic-gate 	if (save_triggers) {
13357c478bd9Sstevel@tonic-gate 		/*
13367c478bd9Sstevel@tonic-gate 		 * Make sure the parent can't be freed while it has triggers.
13377c478bd9Sstevel@tonic-gate 		 */
13387c478bd9Sstevel@tonic-gate 		VN_HOLD(dvp);
13397c478bd9Sstevel@tonic-gate 	}
13407c478bd9Sstevel@tonic-gate 
13417c478bd9Sstevel@tonic-gate 	crfree(zcred);
13427c478bd9Sstevel@tonic-gate 
13437c478bd9Sstevel@tonic-gate done:
13447c478bd9Sstevel@tonic-gate 	/*
13457c478bd9Sstevel@tonic-gate 	 * Return failure if daemon didn't mount anything, and all
13467c478bd9Sstevel@tonic-gate 	 * kernel mounts attempted failed.
13477c478bd9Sstevel@tonic-gate 	 */
13487c478bd9Sstevel@tonic-gate 	error = success ? 0 : ENOENT;
13497c478bd9Sstevel@tonic-gate 
13507c478bd9Sstevel@tonic-gate 	if (alp != NULL) {
13517c478bd9Sstevel@tonic-gate 		if ((error == 0) && save_triggers) {
13527c478bd9Sstevel@tonic-gate 			/*
13537c478bd9Sstevel@tonic-gate 			 * Save action_list information, so that we can use it
13547c478bd9Sstevel@tonic-gate 			 * when it comes time to remount the trigger nodes
13557c478bd9Sstevel@tonic-gate 			 * The action list is freed when the directory node
13567c478bd9Sstevel@tonic-gate 			 * containing the reference to it is unmounted in
13577c478bd9Sstevel@tonic-gate 			 * unmount_tree().
13587c478bd9Sstevel@tonic-gate 			 */
13597c478bd9Sstevel@tonic-gate 			mutex_enter(&dfnp->fn_lock);
13607c478bd9Sstevel@tonic-gate 			ASSERT(dfnp->fn_alp == NULL);
13617c478bd9Sstevel@tonic-gate 			dfnp->fn_alp = alp;
13627c478bd9Sstevel@tonic-gate 			mutex_exit(&dfnp->fn_lock);
13637c478bd9Sstevel@tonic-gate 		} else {
13647c478bd9Sstevel@tonic-gate 			/*
13657c478bd9Sstevel@tonic-gate 			 * free the action list now,
13667c478bd9Sstevel@tonic-gate 			 */
13677c478bd9Sstevel@tonic-gate 			xdr_free(xdr_action_list, (char *)alp);
13687c478bd9Sstevel@tonic-gate 		}
13697c478bd9Sstevel@tonic-gate 	}
13707c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_perform_actions: error=%d\n", error));
13717c478bd9Sstevel@tonic-gate 	return (error);
13727c478bd9Sstevel@tonic-gate }
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate fnnode_t *
13757c478bd9Sstevel@tonic-gate auto_makefnnode(
13767c478bd9Sstevel@tonic-gate 	vtype_t type,
13777c478bd9Sstevel@tonic-gate 	vfs_t *vfsp,
13787c478bd9Sstevel@tonic-gate 	char *name,
13797c478bd9Sstevel@tonic-gate 	cred_t *cred,
13807c478bd9Sstevel@tonic-gate 	struct autofs_globals *fngp)
13817c478bd9Sstevel@tonic-gate {
13827c478bd9Sstevel@tonic-gate 	fnnode_t *fnp;
13837c478bd9Sstevel@tonic-gate 	vnode_t *vp;
13847c478bd9Sstevel@tonic-gate 	char *tmpname;
13857c478bd9Sstevel@tonic-gate 	timestruc_t now;
13867c478bd9Sstevel@tonic-gate 	/*
13877c478bd9Sstevel@tonic-gate 	 * autofs uses odd inode numbers
13887c478bd9Sstevel@tonic-gate 	 * automountd uses even inode numbers
13897c478bd9Sstevel@tonic-gate 	 *
13907c478bd9Sstevel@tonic-gate 	 * To preserve the age-old semantics that inum+devid is unique across
13917c478bd9Sstevel@tonic-gate 	 * the system, this variable must be global across zones.
13927c478bd9Sstevel@tonic-gate 	 */
13937c478bd9Sstevel@tonic-gate 	static ino_t nodeid = 3;
13947c478bd9Sstevel@tonic-gate 
13957c478bd9Sstevel@tonic-gate 	fnp = kmem_zalloc(sizeof (*fnp), KM_SLEEP);
13967c478bd9Sstevel@tonic-gate 	fnp->fn_vnode = vn_alloc(KM_SLEEP);
13977c478bd9Sstevel@tonic-gate 
13987c478bd9Sstevel@tonic-gate 	vp = fntovn(fnp);
13997c478bd9Sstevel@tonic-gate 	tmpname = kmem_alloc(strlen(name) + 1, KM_SLEEP);
14007c478bd9Sstevel@tonic-gate 	(void) strcpy(tmpname, name);
14017c478bd9Sstevel@tonic-gate 	fnp->fn_name = &tmpname[0];
14027c478bd9Sstevel@tonic-gate 	fnp->fn_namelen = (int)strlen(tmpname) + 1;	/* include '\0' */
14037c478bd9Sstevel@tonic-gate 	fnp->fn_uid = crgetuid(cred);
14047c478bd9Sstevel@tonic-gate 	fnp->fn_gid = crgetgid(cred);
14057c478bd9Sstevel@tonic-gate 	/*
14067c478bd9Sstevel@tonic-gate 	 * ".." is added in auto_enter and auto_mount.
14077c478bd9Sstevel@tonic-gate 	 * "." is added in auto_mkdir and auto_mount.
14087c478bd9Sstevel@tonic-gate 	 */
14097c478bd9Sstevel@tonic-gate 	/*
14107c478bd9Sstevel@tonic-gate 	 * Note that fn_size and fn_linkcnt are already 0 since
14117c478bd9Sstevel@tonic-gate 	 * we used kmem_zalloc to allocated fnp
14127c478bd9Sstevel@tonic-gate 	 */
14137c478bd9Sstevel@tonic-gate 	fnp->fn_mode = AUTOFS_MODE;
14147c478bd9Sstevel@tonic-gate 	gethrestime(&now);
14157c478bd9Sstevel@tonic-gate 	fnp->fn_atime = fnp->fn_mtime = fnp->fn_ctime = now;
14167c478bd9Sstevel@tonic-gate 	fnp->fn_ref_time = now.tv_sec;
14177c478bd9Sstevel@tonic-gate 	mutex_enter(&autofs_nodeid_lock);
14187c478bd9Sstevel@tonic-gate 	fnp->fn_nodeid = nodeid;
14197c478bd9Sstevel@tonic-gate 	nodeid += 2;
14207c478bd9Sstevel@tonic-gate 	fnp->fn_globals = fngp;
14217c478bd9Sstevel@tonic-gate 	fngp->fng_fnnode_count++;
14227c478bd9Sstevel@tonic-gate 	mutex_exit(&autofs_nodeid_lock);
14237c478bd9Sstevel@tonic-gate 	vn_setops(vp, auto_vnodeops);
14247c478bd9Sstevel@tonic-gate 	vp->v_type = type;
14257c478bd9Sstevel@tonic-gate 	vp->v_data = (void *)fnp;
14267c478bd9Sstevel@tonic-gate 	vp->v_vfsp = vfsp;
14277c478bd9Sstevel@tonic-gate 	mutex_init(&fnp->fn_lock, NULL, MUTEX_DEFAULT, NULL);
14287c478bd9Sstevel@tonic-gate 	rw_init(&fnp->fn_rwlock, NULL, RW_DEFAULT, NULL);
14297c478bd9Sstevel@tonic-gate 	cv_init(&fnp->fn_cv_mount, NULL, CV_DEFAULT, NULL);
14307c478bd9Sstevel@tonic-gate 	vn_exists(vp);
14317c478bd9Sstevel@tonic-gate 	return (fnp);
14327c478bd9Sstevel@tonic-gate }
14337c478bd9Sstevel@tonic-gate 
14347c478bd9Sstevel@tonic-gate 
14357c478bd9Sstevel@tonic-gate void
14367c478bd9Sstevel@tonic-gate auto_freefnnode(fnnode_t *fnp)
14377c478bd9Sstevel@tonic-gate {
14387c478bd9Sstevel@tonic-gate 	vnode_t *vp = fntovn(fnp);
14397c478bd9Sstevel@tonic-gate 
14407c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_freefnnode: fnp=%p\n", (void *)fnp));
14417c478bd9Sstevel@tonic-gate 
14427c478bd9Sstevel@tonic-gate 	ASSERT(fnp->fn_linkcnt == 0);
14437c478bd9Sstevel@tonic-gate 	ASSERT(vp->v_count == 0);
14447c478bd9Sstevel@tonic-gate 	ASSERT(fnp->fn_dirents == NULL);
14457c478bd9Sstevel@tonic-gate 	ASSERT(fnp->fn_parent == NULL);
14467c478bd9Sstevel@tonic-gate 
14477c478bd9Sstevel@tonic-gate 	vn_invalid(vp);
14487c478bd9Sstevel@tonic-gate 	kmem_free(fnp->fn_name, fnp->fn_namelen);
14497c478bd9Sstevel@tonic-gate 	if (fnp->fn_symlink) {
14507c478bd9Sstevel@tonic-gate 		ASSERT(fnp->fn_flags & MF_THISUID_MATCH_RQD);
14517c478bd9Sstevel@tonic-gate 		kmem_free(fnp->fn_symlink, fnp->fn_symlinklen);
14527c478bd9Sstevel@tonic-gate 	}
14537c478bd9Sstevel@tonic-gate 	if (fnp->fn_cred)
14547c478bd9Sstevel@tonic-gate 		crfree(fnp->fn_cred);
14557c478bd9Sstevel@tonic-gate 	mutex_destroy(&fnp->fn_lock);
14567c478bd9Sstevel@tonic-gate 	rw_destroy(&fnp->fn_rwlock);
14577c478bd9Sstevel@tonic-gate 	cv_destroy(&fnp->fn_cv_mount);
14587c478bd9Sstevel@tonic-gate 	vn_free(vp);
14597c478bd9Sstevel@tonic-gate 
14607c478bd9Sstevel@tonic-gate 	mutex_enter(&autofs_nodeid_lock);
14617c478bd9Sstevel@tonic-gate 	fnp->fn_globals->fng_fnnode_count--;
14627c478bd9Sstevel@tonic-gate 	mutex_exit(&autofs_nodeid_lock);
14637c478bd9Sstevel@tonic-gate 	kmem_free(fnp, sizeof (*fnp));
14647c478bd9Sstevel@tonic-gate }
14657c478bd9Sstevel@tonic-gate 
14667c478bd9Sstevel@tonic-gate void
14677c478bd9Sstevel@tonic-gate auto_disconnect(
14687c478bd9Sstevel@tonic-gate 	fnnode_t *dfnp,
14697c478bd9Sstevel@tonic-gate 	fnnode_t *fnp)
14707c478bd9Sstevel@tonic-gate {
14717c478bd9Sstevel@tonic-gate 	fnnode_t *tmp, **fnpp;
14727c478bd9Sstevel@tonic-gate 	vnode_t *vp = fntovn(fnp);
14737c478bd9Sstevel@tonic-gate 	timestruc_t now;
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4,
14767c478bd9Sstevel@tonic-gate 	    "auto_disconnect: dfnp=%p fnp=%p linkcnt=%d\n v_count=%d",
14777c478bd9Sstevel@tonic-gate 	    (void *)dfnp, (void *)fnp, fnp->fn_linkcnt, vp->v_count));
14787c478bd9Sstevel@tonic-gate 
14797c478bd9Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock));
14807c478bd9Sstevel@tonic-gate 	ASSERT(fnp->fn_linkcnt == 1);
14817c478bd9Sstevel@tonic-gate 
14827c478bd9Sstevel@tonic-gate 	if (vn_mountedvfs(vp) != NULL) {
14837c478bd9Sstevel@tonic-gate 		cmn_err(CE_PANIC, "auto_disconnect: vp %p mounted on",
14847c478bd9Sstevel@tonic-gate 		    (void *)vp);
14857c478bd9Sstevel@tonic-gate 	}
14867c478bd9Sstevel@tonic-gate 
14877c478bd9Sstevel@tonic-gate 	/*
14887c478bd9Sstevel@tonic-gate 	 * Decrement by 1 because we're removing the entry in dfnp.
14897c478bd9Sstevel@tonic-gate 	 */
14907c478bd9Sstevel@tonic-gate 	fnp->fn_linkcnt--;
14917c478bd9Sstevel@tonic-gate 	fnp->fn_size--;
14927c478bd9Sstevel@tonic-gate 
14937c478bd9Sstevel@tonic-gate 	/*
14947c478bd9Sstevel@tonic-gate 	 * only changed while holding parent's (dfnp) rw_lock
14957c478bd9Sstevel@tonic-gate 	 */
14967c478bd9Sstevel@tonic-gate 	fnp->fn_parent = NULL;
14977c478bd9Sstevel@tonic-gate 
14987c478bd9Sstevel@tonic-gate 	fnpp = &dfnp->fn_dirents;
14997c478bd9Sstevel@tonic-gate 	for (;;) {
15007c478bd9Sstevel@tonic-gate 		tmp = *fnpp;
15017c478bd9Sstevel@tonic-gate 		if (tmp == NULL) {
15027c478bd9Sstevel@tonic-gate 			cmn_err(CE_PANIC,
15037c478bd9Sstevel@tonic-gate 			    "auto_disconnect: %p not in %p dirent list",
15047c478bd9Sstevel@tonic-gate 			    (void *)fnp, (void *)dfnp);
15057c478bd9Sstevel@tonic-gate 		}
15067c478bd9Sstevel@tonic-gate 		if (tmp == fnp) {
15077c478bd9Sstevel@tonic-gate 			*fnpp = tmp->fn_next; 	/* remove it from the list */
15087c478bd9Sstevel@tonic-gate 			ASSERT(vp->v_count == 0);
15097c478bd9Sstevel@tonic-gate 			/* child had a pointer to parent ".." */
15107c478bd9Sstevel@tonic-gate 			dfnp->fn_linkcnt--;
15117c478bd9Sstevel@tonic-gate 			dfnp->fn_size--;
15127c478bd9Sstevel@tonic-gate 			break;
15137c478bd9Sstevel@tonic-gate 		}
15147c478bd9Sstevel@tonic-gate 		fnpp = &tmp->fn_next;
15157c478bd9Sstevel@tonic-gate 	}
15167c478bd9Sstevel@tonic-gate 
15177c478bd9Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
15187c478bd9Sstevel@tonic-gate 	gethrestime(&now);
15197c478bd9Sstevel@tonic-gate 	fnp->fn_atime = fnp->fn_mtime = now;
15207c478bd9Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
15217c478bd9Sstevel@tonic-gate 
15227c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_disconnect: done\n"));
15237c478bd9Sstevel@tonic-gate }
15247c478bd9Sstevel@tonic-gate 
15257c478bd9Sstevel@tonic-gate int
15267c478bd9Sstevel@tonic-gate auto_enter(fnnode_t *dfnp, char *name, fnnode_t **fnpp, cred_t *cred)
15277c478bd9Sstevel@tonic-gate {
15287c478bd9Sstevel@tonic-gate 	struct fnnode *cfnp, **spp;
15297c478bd9Sstevel@tonic-gate 	vnode_t *dvp = fntovn(dfnp);
15307c478bd9Sstevel@tonic-gate 	ushort_t offset = 0;
15317c478bd9Sstevel@tonic-gate 	ushort_t diff;
15327c478bd9Sstevel@tonic-gate 
15337c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_enter: dfnp=%p, name=%s ", (void *)dfnp, name));
15347c478bd9Sstevel@tonic-gate 
15357c478bd9Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock));
15367c478bd9Sstevel@tonic-gate 
15377c478bd9Sstevel@tonic-gate 	cfnp = dfnp->fn_dirents;
15387c478bd9Sstevel@tonic-gate 	if (cfnp == NULL) {
15397c478bd9Sstevel@tonic-gate 		/*
15407c478bd9Sstevel@tonic-gate 		 * offset = 0 for '.' and offset = 1 for '..'
15417c478bd9Sstevel@tonic-gate 		 */
15427c478bd9Sstevel@tonic-gate 		spp = &dfnp->fn_dirents;
15437c478bd9Sstevel@tonic-gate 		offset = 2;
15447c478bd9Sstevel@tonic-gate 	}
15457c478bd9Sstevel@tonic-gate 
15467c478bd9Sstevel@tonic-gate 	for (; cfnp; cfnp = cfnp->fn_next) {
15477c478bd9Sstevel@tonic-gate 		if (strcmp(cfnp->fn_name, name) == 0) {
15487c478bd9Sstevel@tonic-gate 			mutex_enter(&cfnp->fn_lock);
15497c478bd9Sstevel@tonic-gate 			if (cfnp->fn_flags & MF_THISUID_MATCH_RQD) {
15507c478bd9Sstevel@tonic-gate 				/*
15517c478bd9Sstevel@tonic-gate 				 * "thisuser" kind of node, need to
15527c478bd9Sstevel@tonic-gate 				 * match CREDs as well
15537c478bd9Sstevel@tonic-gate 				 */
15547c478bd9Sstevel@tonic-gate 				mutex_exit(&cfnp->fn_lock);
15557c478bd9Sstevel@tonic-gate 				if (crcmp(cfnp->fn_cred, cred) == 0)
15567c478bd9Sstevel@tonic-gate 					return (EEXIST);
15577c478bd9Sstevel@tonic-gate 			} else {
15587c478bd9Sstevel@tonic-gate 				mutex_exit(&cfnp->fn_lock);
15597c478bd9Sstevel@tonic-gate 				return (EEXIST);
15607c478bd9Sstevel@tonic-gate 			}
15617c478bd9Sstevel@tonic-gate 		}
15627c478bd9Sstevel@tonic-gate 
15637c478bd9Sstevel@tonic-gate 		if (cfnp->fn_next != NULL) {
15647c478bd9Sstevel@tonic-gate 			diff = (ushort_t)
15657c478bd9Sstevel@tonic-gate 			    (cfnp->fn_next->fn_offset - cfnp->fn_offset);
15667c478bd9Sstevel@tonic-gate 			ASSERT(diff != 0);
15677c478bd9Sstevel@tonic-gate 			if (diff > 1 && offset == 0) {
15687c478bd9Sstevel@tonic-gate 				offset = (ushort_t)cfnp->fn_offset + 1;
15697c478bd9Sstevel@tonic-gate 				spp = &cfnp->fn_next;
15707c478bd9Sstevel@tonic-gate 			}
15717c478bd9Sstevel@tonic-gate 		} else if (offset == 0) {
15727c478bd9Sstevel@tonic-gate 			offset = (ushort_t)cfnp->fn_offset + 1;
15737c478bd9Sstevel@tonic-gate 			spp = &cfnp->fn_next;
15747c478bd9Sstevel@tonic-gate 		}
15757c478bd9Sstevel@tonic-gate 	}
15767c478bd9Sstevel@tonic-gate 
15777c478bd9Sstevel@tonic-gate 	*fnpp = auto_makefnnode(VDIR, dvp->v_vfsp, name, cred,
15787c478bd9Sstevel@tonic-gate 	    dfnp->fn_globals);
15797c478bd9Sstevel@tonic-gate 	if (*fnpp == NULL)
15807c478bd9Sstevel@tonic-gate 		return (ENOMEM);
15817c478bd9Sstevel@tonic-gate 
15827c478bd9Sstevel@tonic-gate 	/*
15837c478bd9Sstevel@tonic-gate 	 * I don't hold the mutex on fnpp because I created it, and
15847c478bd9Sstevel@tonic-gate 	 * I'm already holding the writers lock for it's parent
15857c478bd9Sstevel@tonic-gate 	 * directory, therefore nobody can reference it without me first
15867c478bd9Sstevel@tonic-gate 	 * releasing the writers lock.
15877c478bd9Sstevel@tonic-gate 	 */
15887c478bd9Sstevel@tonic-gate 	(*fnpp)->fn_offset = offset;
15897c478bd9Sstevel@tonic-gate 	(*fnpp)->fn_next = *spp;
15907c478bd9Sstevel@tonic-gate 	*spp = *fnpp;
15917c478bd9Sstevel@tonic-gate 	(*fnpp)->fn_parent = dfnp;
15927c478bd9Sstevel@tonic-gate 	(*fnpp)->fn_linkcnt++;	/* parent now holds reference to entry */
15937c478bd9Sstevel@tonic-gate 	(*fnpp)->fn_size++;
15947c478bd9Sstevel@tonic-gate 
15957c478bd9Sstevel@tonic-gate 	/*
15967c478bd9Sstevel@tonic-gate 	 * dfnp->fn_linkcnt and dfnp->fn_size protected by dfnp->rw_lock
15977c478bd9Sstevel@tonic-gate 	 */
15987c478bd9Sstevel@tonic-gate 	dfnp->fn_linkcnt++;	/* child now holds reference to parent '..' */
15997c478bd9Sstevel@tonic-gate 	dfnp->fn_size++;
16007c478bd9Sstevel@tonic-gate 
16017c478bd9Sstevel@tonic-gate 	dfnp->fn_ref_time = gethrestime_sec();
16027c478bd9Sstevel@tonic-gate 
16037c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "*fnpp=%p\n", (void *)*fnpp));
16047c478bd9Sstevel@tonic-gate 	return (0);
16057c478bd9Sstevel@tonic-gate }
16067c478bd9Sstevel@tonic-gate 
16077c478bd9Sstevel@tonic-gate int
16087c478bd9Sstevel@tonic-gate auto_search(fnnode_t *dfnp, char *name, fnnode_t **fnpp, cred_t *cred)
16097c478bd9Sstevel@tonic-gate {
16107c478bd9Sstevel@tonic-gate 	vnode_t *dvp;
16117c478bd9Sstevel@tonic-gate 	fnnode_t *p;
16127c478bd9Sstevel@tonic-gate 	int error = ENOENT, match = 0;
16137c478bd9Sstevel@tonic-gate 
16147c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_search: dfnp=%p, name=%s...\n",
16157c478bd9Sstevel@tonic-gate 	    (void *)dfnp, name));
16167c478bd9Sstevel@tonic-gate 
16177c478bd9Sstevel@tonic-gate 	dvp = fntovn(dfnp);
16187c478bd9Sstevel@tonic-gate 	if (dvp->v_type != VDIR) {
16197c478bd9Sstevel@tonic-gate 		cmn_err(CE_PANIC, "auto_search: dvp=%p not a directory",
16207c478bd9Sstevel@tonic-gate 		    (void *)dvp);
16217c478bd9Sstevel@tonic-gate 	}
16227c478bd9Sstevel@tonic-gate 
16237c478bd9Sstevel@tonic-gate 	ASSERT(RW_LOCK_HELD(&dfnp->fn_rwlock));
16247c478bd9Sstevel@tonic-gate 	for (p = dfnp->fn_dirents; p != NULL; p = p->fn_next) {
16257c478bd9Sstevel@tonic-gate 		if (strcmp(p->fn_name, name) == 0) {
16267c478bd9Sstevel@tonic-gate 			mutex_enter(&p->fn_lock);
16277c478bd9Sstevel@tonic-gate 			if (p->fn_flags & MF_THISUID_MATCH_RQD) {
16287c478bd9Sstevel@tonic-gate 				/*
16297c478bd9Sstevel@tonic-gate 				 * "thisuser" kind of node
16307c478bd9Sstevel@tonic-gate 				 * Need to match CREDs as well
16317c478bd9Sstevel@tonic-gate 				 */
16327c478bd9Sstevel@tonic-gate 				mutex_exit(&p->fn_lock);
16337c478bd9Sstevel@tonic-gate 				match = crcmp(p->fn_cred, cred) == 0;
16347c478bd9Sstevel@tonic-gate 			} else {
16357c478bd9Sstevel@tonic-gate 				/*
16367c478bd9Sstevel@tonic-gate 				 * No need to check CRED
16377c478bd9Sstevel@tonic-gate 				 */
16387c478bd9Sstevel@tonic-gate 				mutex_exit(&p->fn_lock);
16397c478bd9Sstevel@tonic-gate 				match = 1;
16407c478bd9Sstevel@tonic-gate 			}
16417c478bd9Sstevel@tonic-gate 		}
16427c478bd9Sstevel@tonic-gate 		if (match) {
16437c478bd9Sstevel@tonic-gate 			error = 0;
16447c478bd9Sstevel@tonic-gate 			if (fnpp) {
16457c478bd9Sstevel@tonic-gate 				*fnpp = p;
16467c478bd9Sstevel@tonic-gate 				VN_HOLD(fntovn(*fnpp));
16477c478bd9Sstevel@tonic-gate 			}
16487c478bd9Sstevel@tonic-gate 			break;
16497c478bd9Sstevel@tonic-gate 		}
16507c478bd9Sstevel@tonic-gate 	}
16517c478bd9Sstevel@tonic-gate 
16527c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_search: error=%d\n", error));
16537c478bd9Sstevel@tonic-gate 	return (error);
16547c478bd9Sstevel@tonic-gate }
16557c478bd9Sstevel@tonic-gate 
16567c478bd9Sstevel@tonic-gate /*
16577c478bd9Sstevel@tonic-gate  * If dvp is mounted on, get path's vnode in the mounted on
16587c478bd9Sstevel@tonic-gate  * filesystem.  Path is relative to dvp, ie "./path".
16597c478bd9Sstevel@tonic-gate  * If successful, *mvp points to a the held mountpoint vnode.
16607c478bd9Sstevel@tonic-gate  */
16617c478bd9Sstevel@tonic-gate /* ARGSUSED */
16627c478bd9Sstevel@tonic-gate static int
16637c478bd9Sstevel@tonic-gate auto_getmntpnt(
16647c478bd9Sstevel@tonic-gate 	vnode_t *dvp,
16657c478bd9Sstevel@tonic-gate 	char *path,
16667c478bd9Sstevel@tonic-gate 	vnode_t **mvpp,		/* vnode for mountpoint */
16677c478bd9Sstevel@tonic-gate 	cred_t *cred)
16687c478bd9Sstevel@tonic-gate {
16697c478bd9Sstevel@tonic-gate 	int error = 0;
16707c478bd9Sstevel@tonic-gate 	vnode_t *newvp;
16717c478bd9Sstevel@tonic-gate 	char namebuf[TYPICALMAXPATHLEN];
16727c478bd9Sstevel@tonic-gate 	struct pathname lookpn;
16737c478bd9Sstevel@tonic-gate 	vfs_t *vfsp;
16747c478bd9Sstevel@tonic-gate 
16757c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_getmntpnt: path=%s\n", path));
16767c478bd9Sstevel@tonic-gate 
167795b97885Snr123932 	if (error = vn_vfsrlock_wait(dvp))
16787c478bd9Sstevel@tonic-gate 		return (error);
16797c478bd9Sstevel@tonic-gate 
16807c478bd9Sstevel@tonic-gate 	/*
16817c478bd9Sstevel@tonic-gate 	 * Now that we have the vfswlock, check to see if dvp
16827c478bd9Sstevel@tonic-gate 	 * is still mounted on.  If not, then just bail out as
16837c478bd9Sstevel@tonic-gate 	 * there is no need to remount the triggers since the
16847c478bd9Sstevel@tonic-gate 	 * higher level mount point has gotten unmounted.
16857c478bd9Sstevel@tonic-gate 	 */
16867c478bd9Sstevel@tonic-gate 	vfsp = vn_mountedvfs(dvp);
16877c478bd9Sstevel@tonic-gate 	if (vfsp == NULL) {
16887c478bd9Sstevel@tonic-gate 		vn_vfsunlock(dvp);
16897c478bd9Sstevel@tonic-gate 		error = EBUSY;
16907c478bd9Sstevel@tonic-gate 		goto done;
16917c478bd9Sstevel@tonic-gate 	}
16927c478bd9Sstevel@tonic-gate 	/*
16937c478bd9Sstevel@tonic-gate 	 * Since mounted on, lookup "path" in the new filesystem,
16947c478bd9Sstevel@tonic-gate 	 * it is important that we do the filesystem jump here to
16957c478bd9Sstevel@tonic-gate 	 * avoid lookuppn() calling auto_lookup on dvp and deadlock.
16967c478bd9Sstevel@tonic-gate 	 */
16977c478bd9Sstevel@tonic-gate 	error = VFS_ROOT(vfsp, &newvp);
169895b97885Snr123932 	vn_vfsunlock(dvp);
16997c478bd9Sstevel@tonic-gate 	if (error)
17007c478bd9Sstevel@tonic-gate 		goto done;
17017c478bd9Sstevel@tonic-gate 
17027c478bd9Sstevel@tonic-gate 	/*
17037c478bd9Sstevel@tonic-gate 	 * We do a VN_HOLD on newvp just in case the first call to
17047c478bd9Sstevel@tonic-gate 	 * lookuppnvp() fails with ENAMETOOLONG.  We should still have a
17057c478bd9Sstevel@tonic-gate 	 * reference to this vnode for the second call to lookuppnvp().
17067c478bd9Sstevel@tonic-gate 	 */
17077c478bd9Sstevel@tonic-gate 	VN_HOLD(newvp);
17087c478bd9Sstevel@tonic-gate 
17097c478bd9Sstevel@tonic-gate 	/*
17107c478bd9Sstevel@tonic-gate 	 * Now create the pathname struct so we can make use of lookuppnvp,
17117c478bd9Sstevel@tonic-gate 	 * and pn_getcomponent.
17127c478bd9Sstevel@tonic-gate 	 * This code is similar to lookupname() in fs/lookup.c.
17137c478bd9Sstevel@tonic-gate 	 */
17147c478bd9Sstevel@tonic-gate 	error = pn_get_buf(path, UIO_SYSSPACE, &lookpn,
17157c478bd9Sstevel@tonic-gate 	    namebuf, sizeof (namebuf));
17167c478bd9Sstevel@tonic-gate 	if (error == 0) {
17177c478bd9Sstevel@tonic-gate 		error = lookuppnvp(&lookpn, NULL, NO_FOLLOW, NULLVPP,
17187c478bd9Sstevel@tonic-gate 		    mvpp, rootdir, newvp, cred);
17197c478bd9Sstevel@tonic-gate 	} else
17207c478bd9Sstevel@tonic-gate 		VN_RELE(newvp);
17217c478bd9Sstevel@tonic-gate 	if (error == ENAMETOOLONG) {
17227c478bd9Sstevel@tonic-gate 		/*
17237c478bd9Sstevel@tonic-gate 		 * This thread used a pathname > TYPICALMAXPATHLEN bytes long.
17247c478bd9Sstevel@tonic-gate 		 * newvp is VN_RELE'd by this call to lookuppnvp.
17257c478bd9Sstevel@tonic-gate 		 *
17267c478bd9Sstevel@tonic-gate 		 * Using 'rootdir' in a zone's context is OK here: we already
17277c478bd9Sstevel@tonic-gate 		 * ascertained that there are no '..'s in the path, and we're
17287c478bd9Sstevel@tonic-gate 		 * not following symlinks.
17297c478bd9Sstevel@tonic-gate 		 */
17307c478bd9Sstevel@tonic-gate 		if ((error = pn_get(path, UIO_SYSSPACE, &lookpn)) == 0) {
17317c478bd9Sstevel@tonic-gate 			error = lookuppnvp(&lookpn, NULL, NO_FOLLOW, NULLVPP,
17327c478bd9Sstevel@tonic-gate 			    mvpp, rootdir, newvp, cred);
17337c478bd9Sstevel@tonic-gate 			pn_free(&lookpn);
17347c478bd9Sstevel@tonic-gate 		} else
17357c478bd9Sstevel@tonic-gate 			VN_RELE(newvp);
17367c478bd9Sstevel@tonic-gate 	} else {
17377c478bd9Sstevel@tonic-gate 		/*
17387c478bd9Sstevel@tonic-gate 		 * Need to release newvp here since we held it.
17397c478bd9Sstevel@tonic-gate 		 */
17407c478bd9Sstevel@tonic-gate 		VN_RELE(newvp);
17417c478bd9Sstevel@tonic-gate 	}
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate done:
17447c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_getmntpnt: path=%s *mvpp=%p error=%d\n",
17457c478bd9Sstevel@tonic-gate 	    path, (void *)*mvpp, error));
17467c478bd9Sstevel@tonic-gate 	return (error);
17477c478bd9Sstevel@tonic-gate }
17487c478bd9Sstevel@tonic-gate 
17497c478bd9Sstevel@tonic-gate #define	DEEPER(x) (((x)->fn_dirents != NULL) || \
17507c478bd9Sstevel@tonic-gate 			(vn_mountedvfs(fntovn((x)))) != NULL)
17517c478bd9Sstevel@tonic-gate 
17527c478bd9Sstevel@tonic-gate /*
17537c478bd9Sstevel@tonic-gate  * The caller, should have already VN_RELE'd its reference to the
17547c478bd9Sstevel@tonic-gate  * root vnode of this filesystem.
17557c478bd9Sstevel@tonic-gate  */
17567c478bd9Sstevel@tonic-gate static int
17577c478bd9Sstevel@tonic-gate auto_inkernel_unmount(vfs_t *vfsp)
17587c478bd9Sstevel@tonic-gate {
17597c478bd9Sstevel@tonic-gate 	vnode_t *cvp = vfsp->vfs_vnodecovered;
17607c478bd9Sstevel@tonic-gate 	int error;
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4,
17637c478bd9Sstevel@tonic-gate 	    "auto_inkernel_unmount: devid=%lx mntpnt(%p) count %u\n",
17647c478bd9Sstevel@tonic-gate 	    vfsp->vfs_dev, (void *)cvp, cvp->v_count));
17657c478bd9Sstevel@tonic-gate 
17667c478bd9Sstevel@tonic-gate 	ASSERT(vn_vfswlock_held(cvp));
17677c478bd9Sstevel@tonic-gate 
17687c478bd9Sstevel@tonic-gate 	/*
17697c478bd9Sstevel@tonic-gate 	 * Perform the unmount
17707c478bd9Sstevel@tonic-gate 	 * The mountpoint has already been locked by the caller.
17717c478bd9Sstevel@tonic-gate 	 */
17727c478bd9Sstevel@tonic-gate 	error = dounmount(vfsp, 0, kcred);
17737c478bd9Sstevel@tonic-gate 
17747c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_inkernel_unmount: exit count %u\n",
17757c478bd9Sstevel@tonic-gate 	    cvp->v_count));
17767c478bd9Sstevel@tonic-gate 	return (error);
17777c478bd9Sstevel@tonic-gate }
17787c478bd9Sstevel@tonic-gate 
17797c478bd9Sstevel@tonic-gate /*
17807c478bd9Sstevel@tonic-gate  * unmounts trigger nodes in the kernel.
17817c478bd9Sstevel@tonic-gate  */
17827c478bd9Sstevel@tonic-gate static void
17837c478bd9Sstevel@tonic-gate unmount_triggers(fnnode_t *fnp, action_list **alp)
17847c478bd9Sstevel@tonic-gate {
17857c478bd9Sstevel@tonic-gate 	fnnode_t *tp, *next;
17867c478bd9Sstevel@tonic-gate 	int error = 0;
17877c478bd9Sstevel@tonic-gate 	vfs_t *vfsp;
17887c478bd9Sstevel@tonic-gate 	vnode_t *tvp;
17897c478bd9Sstevel@tonic-gate 
17907c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "unmount_triggers: fnp=%p\n", (void *)fnp));
17917c478bd9Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock));
17927c478bd9Sstevel@tonic-gate 
17937c478bd9Sstevel@tonic-gate 	*alp = fnp->fn_alp;
17947c478bd9Sstevel@tonic-gate 	next = fnp->fn_trigger;
17957c478bd9Sstevel@tonic-gate 	while ((tp = next) != NULL) {
17967c478bd9Sstevel@tonic-gate 		tvp = fntovn(tp);
17977c478bd9Sstevel@tonic-gate 		ASSERT(tvp->v_count >= 2);
17987c478bd9Sstevel@tonic-gate 		next = tp->fn_next;
17997c478bd9Sstevel@tonic-gate 		/*
18007c478bd9Sstevel@tonic-gate 		 * drop writer's lock since the unmount will end up
18017c478bd9Sstevel@tonic-gate 		 * disconnecting this node from fnp and needs to acquire
18027c478bd9Sstevel@tonic-gate 		 * the writer's lock again.
18037c478bd9Sstevel@tonic-gate 		 * next has at least a reference count >= 2 since it's
18047c478bd9Sstevel@tonic-gate 		 * a trigger node, therefore can not be accidentally freed
18057c478bd9Sstevel@tonic-gate 		 * by a VN_RELE
18067c478bd9Sstevel@tonic-gate 		 */
18077c478bd9Sstevel@tonic-gate 		rw_exit(&fnp->fn_rwlock);
18087c478bd9Sstevel@tonic-gate 
18097c478bd9Sstevel@tonic-gate 		vfsp = tvp->v_vfsp;
18107c478bd9Sstevel@tonic-gate 
18117c478bd9Sstevel@tonic-gate 		/*
18127c478bd9Sstevel@tonic-gate 		 * Its parent was holding a reference to it, since this
18137c478bd9Sstevel@tonic-gate 		 * is a trigger vnode.
18147c478bd9Sstevel@tonic-gate 		 */
18157c478bd9Sstevel@tonic-gate 		VN_RELE(tvp);
18167c478bd9Sstevel@tonic-gate 		if (error = auto_inkernel_unmount(vfsp)) {
18177c478bd9Sstevel@tonic-gate 			cmn_err(CE_PANIC, "unmount_triggers: "
18187c478bd9Sstevel@tonic-gate 			    "unmount of vp=%p failed error=%d",
18197c478bd9Sstevel@tonic-gate 			    (void *)tvp, error);
18207c478bd9Sstevel@tonic-gate 		}
18217c478bd9Sstevel@tonic-gate 		/*
18227c478bd9Sstevel@tonic-gate 		 * reacquire writer's lock
18237c478bd9Sstevel@tonic-gate 		 */
18247c478bd9Sstevel@tonic-gate 		rw_enter(&fnp->fn_rwlock, RW_WRITER);
18257c478bd9Sstevel@tonic-gate 	}
18267c478bd9Sstevel@tonic-gate 
18277c478bd9Sstevel@tonic-gate 	/*
18287c478bd9Sstevel@tonic-gate 	 * We were holding a reference to our parent.  Drop that.
18297c478bd9Sstevel@tonic-gate 	 */
18307c478bd9Sstevel@tonic-gate 	VN_RELE(fntovn(fnp));
18317c478bd9Sstevel@tonic-gate 	fnp->fn_trigger = NULL;
18327c478bd9Sstevel@tonic-gate 	fnp->fn_alp = NULL;
18337c478bd9Sstevel@tonic-gate 
18347c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "unmount_triggers: finished\n"));
18357c478bd9Sstevel@tonic-gate }
18367c478bd9Sstevel@tonic-gate 
18377c478bd9Sstevel@tonic-gate /*
18387c478bd9Sstevel@tonic-gate  * This routine locks the mountpoint of every trigger node if they're
1839f798ee53SJan Kryl  * not busy, or returns EBUSY if any node is busy.
18407c478bd9Sstevel@tonic-gate  */
1841f798ee53SJan Kryl static boolean_t
1842f798ee53SJan Kryl triggers_busy(fnnode_t *fnp)
18437c478bd9Sstevel@tonic-gate {
1844f798ee53SJan Kryl 	int done;
18457c478bd9Sstevel@tonic-gate 	int lck_error = 0;
18467c478bd9Sstevel@tonic-gate 	fnnode_t *tp, *t1p;
18477c478bd9Sstevel@tonic-gate 	vfs_t *vfsp;
18487c478bd9Sstevel@tonic-gate 
18497c478bd9Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock));
18507c478bd9Sstevel@tonic-gate 
18517c478bd9Sstevel@tonic-gate 	for (tp = fnp->fn_trigger; tp != NULL; tp = tp->fn_next) {
18527c478bd9Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\ttrigger: %s\n", tp->fn_name));
1853f798ee53SJan Kryl 		/* MF_LOOKUP should never be set on trigger nodes */
1854f798ee53SJan Kryl 		ASSERT((tp->fn_flags & MF_LOOKUP) == 0);
18557c478bd9Sstevel@tonic-gate 		vfsp = fntovn(tp)->v_vfsp;
1856f798ee53SJan Kryl 
18577c478bd9Sstevel@tonic-gate 		/*
18587c478bd9Sstevel@tonic-gate 		 * The vn_vfsunlock will be done in auto_inkernel_unmount.
18597c478bd9Sstevel@tonic-gate 		 */
18607c478bd9Sstevel@tonic-gate 		lck_error = vn_vfswlock(vfsp->vfs_vnodecovered);
1861f798ee53SJan Kryl 
1862f798ee53SJan Kryl 		if (lck_error != 0 || (tp->fn_flags & MF_INPROG) ||
1863f798ee53SJan Kryl 		    DEEPER(tp) || ((fntovn(tp))->v_count) > 2) {
18647c478bd9Sstevel@tonic-gate 			/*
18657c478bd9Sstevel@tonic-gate 			 * couldn't lock it because it's busy,
18667c478bd9Sstevel@tonic-gate 			 * It is mounted on or has dirents?
18677c478bd9Sstevel@tonic-gate 			 * If reference count is greater than two, then
18687c478bd9Sstevel@tonic-gate 			 * somebody else is holding a reference to this vnode.
18697c478bd9Sstevel@tonic-gate 			 * One reference is for the mountpoint, and the second
18707c478bd9Sstevel@tonic-gate 			 * is for the trigger node.
18717c478bd9Sstevel@tonic-gate 			 */
18727c478bd9Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\ttrigger busy\n"));
1873f798ee53SJan Kryl 
18747c478bd9Sstevel@tonic-gate 			/*
18757c478bd9Sstevel@tonic-gate 			 * Unlock previously locked mountpoints
18767c478bd9Sstevel@tonic-gate 			 */
18777c478bd9Sstevel@tonic-gate 			for (done = 0, t1p = fnp->fn_trigger; !done;
18787c478bd9Sstevel@tonic-gate 			    t1p = t1p->fn_next) {
18797c478bd9Sstevel@tonic-gate 				/*
18807c478bd9Sstevel@tonic-gate 				 * Unlock all nodes previously
18817c478bd9Sstevel@tonic-gate 				 * locked. All nodes up to 'tp'
18827c478bd9Sstevel@tonic-gate 				 * were successfully locked. If 'lck_err' is
18837c478bd9Sstevel@tonic-gate 				 * set, then 'tp' was not locked, and thus
18847c478bd9Sstevel@tonic-gate 				 * should not be unlocked. If
18857c478bd9Sstevel@tonic-gate 				 * 'lck_err' is not set, then 'tp' was
18867c478bd9Sstevel@tonic-gate 				 * successfully locked, and it should
18877c478bd9Sstevel@tonic-gate 				 * be unlocked.
18887c478bd9Sstevel@tonic-gate 				 */
18897c478bd9Sstevel@tonic-gate 				if (t1p != tp || !lck_error) {
18907c478bd9Sstevel@tonic-gate 					vfsp = fntovn(t1p)->v_vfsp;
18917c478bd9Sstevel@tonic-gate 					vn_vfsunlock(vfsp->vfs_vnodecovered);
18927c478bd9Sstevel@tonic-gate 				}
18937c478bd9Sstevel@tonic-gate 				done = (t1p == tp);
18947c478bd9Sstevel@tonic-gate 			}
1895f798ee53SJan Kryl 			return (B_TRUE);
18967c478bd9Sstevel@tonic-gate 		}
18977c478bd9Sstevel@tonic-gate 	}
18987c478bd9Sstevel@tonic-gate 
1899f798ee53SJan Kryl 	return (B_FALSE);
19007c478bd9Sstevel@tonic-gate }
19017c478bd9Sstevel@tonic-gate 
19027c478bd9Sstevel@tonic-gate /*
19037c478bd9Sstevel@tonic-gate  * It is the caller's responsibility to grab the VVFSLOCK.
19047c478bd9Sstevel@tonic-gate  * Releases the VVFSLOCK upon return.
19057c478bd9Sstevel@tonic-gate  */
19067c478bd9Sstevel@tonic-gate static int
19077c478bd9Sstevel@tonic-gate unmount_node(vnode_t *cvp, int force)
19087c478bd9Sstevel@tonic-gate {
19097c478bd9Sstevel@tonic-gate 	int error = 0;
19107c478bd9Sstevel@tonic-gate 	fnnode_t *cfnp;
19117c478bd9Sstevel@tonic-gate 	vfs_t *vfsp;
19127c478bd9Sstevel@tonic-gate 	umntrequest ul;
19137c478bd9Sstevel@tonic-gate 	fninfo_t *fnip;
19147c478bd9Sstevel@tonic-gate 
19157c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tunmount_node cvp=%p\n", (void *)cvp));
19167c478bd9Sstevel@tonic-gate 
19177c478bd9Sstevel@tonic-gate 	ASSERT(vn_vfswlock_held(cvp));
19187c478bd9Sstevel@tonic-gate 	cfnp = vntofn(cvp);
19197c478bd9Sstevel@tonic-gate 	vfsp = vn_mountedvfs(cvp);
19207c478bd9Sstevel@tonic-gate 
19217c478bd9Sstevel@tonic-gate 	if (force || cfnp->fn_flags & MF_IK_MOUNT) {
19227c478bd9Sstevel@tonic-gate 		/*
19237c478bd9Sstevel@tonic-gate 		 * Mount was performed in the kernel, so
19247c478bd9Sstevel@tonic-gate 		 * do an in-kernel unmount. auto_inkernel_unmount()
19257c478bd9Sstevel@tonic-gate 		 * will vn_vfsunlock(cvp).
19267c478bd9Sstevel@tonic-gate 		 */
19277c478bd9Sstevel@tonic-gate 		error = auto_inkernel_unmount(vfsp);
19287c478bd9Sstevel@tonic-gate 	} else {
19297c478bd9Sstevel@tonic-gate 		zone_t *zone = NULL;
19307c478bd9Sstevel@tonic-gate 		refstr_t *mntpt, *resource;
19317c478bd9Sstevel@tonic-gate 		size_t mntoptslen;
19327c478bd9Sstevel@tonic-gate 
19337c478bd9Sstevel@tonic-gate 		/*
19347c478bd9Sstevel@tonic-gate 		 * Get the mnttab information of the node
19357c478bd9Sstevel@tonic-gate 		 * and ask the daemon to unmount it.
19367c478bd9Sstevel@tonic-gate 		 */
19377c478bd9Sstevel@tonic-gate 		bzero(&ul, sizeof (ul));
19387c478bd9Sstevel@tonic-gate 		mntfs_getmntopts(vfsp, &ul.mntopts, &mntoptslen);
19397c478bd9Sstevel@tonic-gate 		if (ul.mntopts == NULL) {
194039d3e169Sevanl 			auto_log(cfnp->fn_globals->fng_verbose,
1941d6c8399bSrmesta 			    cfnp->fn_globals->fng_zoneid, CE_WARN,
1942d6c8399bSrmesta 			    "unmount_node: no memory");
19437c478bd9Sstevel@tonic-gate 			vn_vfsunlock(cvp);
19447c478bd9Sstevel@tonic-gate 			error = ENOMEM;
19457c478bd9Sstevel@tonic-gate 			goto done;
19467c478bd9Sstevel@tonic-gate 		}
19477c478bd9Sstevel@tonic-gate 		if (mntoptslen > AUTOFS_MAXOPTSLEN)
19487c478bd9Sstevel@tonic-gate 			ul.mntopts[AUTOFS_MAXOPTSLEN - 1] = '\0';
19497c478bd9Sstevel@tonic-gate 
19507c478bd9Sstevel@tonic-gate 		mntpt = vfs_getmntpoint(vfsp);
19517c478bd9Sstevel@tonic-gate 		ul.mntpnt = (char *)refstr_value(mntpt);
19527c478bd9Sstevel@tonic-gate 		resource = vfs_getresource(vfsp);
19537c478bd9Sstevel@tonic-gate 		ul.mntresource = (char *)refstr_value(resource);
19547c478bd9Sstevel@tonic-gate 
19557c478bd9Sstevel@tonic-gate 		fnip = vfstofni(cvp->v_vfsp);
19567c478bd9Sstevel@tonic-gate 		ul.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE;
19577c478bd9Sstevel@tonic-gate 
19587c478bd9Sstevel@tonic-gate 		/*
19597c478bd9Sstevel@tonic-gate 		 * Since a zone'd automountd's view of the autofs mount points
19607c478bd9Sstevel@tonic-gate 		 * differs from those in the kernel, we need to make sure we
19617c478bd9Sstevel@tonic-gate 		 * give it consistent mount points.
19627c478bd9Sstevel@tonic-gate 		 */
19637c478bd9Sstevel@tonic-gate 		ASSERT(fnip->fi_zoneid == getzoneid());
19647c478bd9Sstevel@tonic-gate 		zone = curproc->p_zone;
19657c478bd9Sstevel@tonic-gate 
19667c478bd9Sstevel@tonic-gate 		if (fnip->fi_zoneid != GLOBAL_ZONEID) {
19677c478bd9Sstevel@tonic-gate 			if (ZONE_PATH_VISIBLE(ul.mntpnt, zone)) {
19687c478bd9Sstevel@tonic-gate 				ul.mntpnt =
19697c478bd9Sstevel@tonic-gate 				    ZONE_PATH_TRANSLATE(ul.mntpnt, zone);
19707c478bd9Sstevel@tonic-gate 			}
19717c478bd9Sstevel@tonic-gate 			if (ZONE_PATH_VISIBLE(ul.mntresource, zone)) {
19727c478bd9Sstevel@tonic-gate 				ul.mntresource =
19737c478bd9Sstevel@tonic-gate 				    ZONE_PATH_TRANSLATE(ul.mntresource, zone);
19747c478bd9Sstevel@tonic-gate 			}
19757c478bd9Sstevel@tonic-gate 		}
197639d3e169Sevanl 
19777c478bd9Sstevel@tonic-gate 		ul.fstype = vfssw[vfsp->vfs_fstype].vsw_name;
19787c478bd9Sstevel@tonic-gate 		vn_vfsunlock(cvp);
19797c478bd9Sstevel@tonic-gate 
198039d3e169Sevanl 		error = auto_send_unmount_request(fnip, &ul, FALSE);
19817c478bd9Sstevel@tonic-gate 		kmem_free(ul.mntopts, mntoptslen);
19827c478bd9Sstevel@tonic-gate 		refstr_rele(mntpt);
19837c478bd9Sstevel@tonic-gate 		refstr_rele(resource);
19847c478bd9Sstevel@tonic-gate 	}
19857c478bd9Sstevel@tonic-gate 
19867c478bd9Sstevel@tonic-gate done:
19877c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tunmount_node cvp=%p error=%d\n", (void *)cvp,
19887c478bd9Sstevel@tonic-gate 	    error));
19897c478bd9Sstevel@tonic-gate 	return (error);
19907c478bd9Sstevel@tonic-gate }
19917c478bd9Sstevel@tonic-gate 
19927c478bd9Sstevel@tonic-gate /*
19937c478bd9Sstevel@tonic-gate  * return EBUSY if any thread is holding a reference to this vnode
1994f798ee53SJan Kryl  * other than us. Result of this function cannot be relied on, since
1995f798ee53SJan Kryl  * it doesn't follow proper locking rules (i.e. vp->v_vfsmountedhere
1996f798ee53SJan Kryl  * and fnp->fn_trigger can change throughout this function). However
1997f798ee53SJan Kryl  * it's good enough for rough estimation.
19987c478bd9Sstevel@tonic-gate  */
19997c478bd9Sstevel@tonic-gate static int
20007c478bd9Sstevel@tonic-gate check_auto_node(vnode_t *vp)
20017c478bd9Sstevel@tonic-gate {
20027c478bd9Sstevel@tonic-gate 	fnnode_t *fnp;
20037c478bd9Sstevel@tonic-gate 	int error = 0;
20047c478bd9Sstevel@tonic-gate 	/*
20057c478bd9Sstevel@tonic-gate 	 * number of references to expect for
20067c478bd9Sstevel@tonic-gate 	 * a non-busy vnode.
20077c478bd9Sstevel@tonic-gate 	 */
20087c478bd9Sstevel@tonic-gate 	uint_t count;
20097c478bd9Sstevel@tonic-gate 
20107c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tcheck_auto_node vp=%p ", (void *)vp));
20117c478bd9Sstevel@tonic-gate 	fnp = vntofn(vp);
20127c478bd9Sstevel@tonic-gate 
20137c478bd9Sstevel@tonic-gate 	count = 1;		/* we are holding a reference to vp */
20147c478bd9Sstevel@tonic-gate 	if (fnp->fn_flags & MF_TRIGGER) {
20157c478bd9Sstevel@tonic-gate 		/*
20167c478bd9Sstevel@tonic-gate 		 * parent holds a pointer to us (trigger)
20177c478bd9Sstevel@tonic-gate 		 */
20187c478bd9Sstevel@tonic-gate 		count++;
20197c478bd9Sstevel@tonic-gate 	}
20207c478bd9Sstevel@tonic-gate 	if (fnp->fn_trigger != NULL) {
20217c478bd9Sstevel@tonic-gate 		/*
20227c478bd9Sstevel@tonic-gate 		 * The trigger nodes have a hold on us.
20237c478bd9Sstevel@tonic-gate 		 */
20247c478bd9Sstevel@tonic-gate 		count++;
20257c478bd9Sstevel@tonic-gate 	}
2026f798ee53SJan Kryl 	if (vn_ismntpt(vp)) {
2027f798ee53SJan Kryl 		/*
2028f798ee53SJan Kryl 		 * File system is mounted on us.
2029f798ee53SJan Kryl 		 */
2030f798ee53SJan Kryl 		count++;
2031f798ee53SJan Kryl 	}
20327c478bd9Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
2033f798ee53SJan Kryl 	ASSERT(vp->v_count > 0);
20347c478bd9Sstevel@tonic-gate 	if (vp->v_flag & VROOT)
20357c478bd9Sstevel@tonic-gate 		count++;
20367c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((10, "\tcount=%u ", vp->v_count));
20377c478bd9Sstevel@tonic-gate 	if (vp->v_count > count)
20387c478bd9Sstevel@tonic-gate 		error = EBUSY;
20397c478bd9Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
20407c478bd9Sstevel@tonic-gate 
20417c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tcheck_auto_node error=%d ", error));
20427c478bd9Sstevel@tonic-gate 	return (error);
20437c478bd9Sstevel@tonic-gate }
20447c478bd9Sstevel@tonic-gate 
20457c478bd9Sstevel@tonic-gate /*
20467c478bd9Sstevel@tonic-gate  * rootvp is the root of the AUTOFS filesystem.
20477c478bd9Sstevel@tonic-gate  * If rootvp is busy (v_count > 1) returns EBUSY.
20487c478bd9Sstevel@tonic-gate  * else removes every vnode under this tree.
20497c478bd9Sstevel@tonic-gate  * ASSUMPTION: Assumes that the only node which can be busy is
20507c478bd9Sstevel@tonic-gate  * the root vnode. This filesystem better be two levels deep only,
20517c478bd9Sstevel@tonic-gate  * the root and its immediate subdirs.
20527c478bd9Sstevel@tonic-gate  * The daemon will "AUTOFS direct-mount" only one level below the root.
20537c478bd9Sstevel@tonic-gate  */
2054f798ee53SJan Kryl static void
20557c478bd9Sstevel@tonic-gate unmount_autofs(vnode_t *rootvp)
20567c478bd9Sstevel@tonic-gate {
20577c478bd9Sstevel@tonic-gate 	fnnode_t *fnp, *rootfnp, *nfnp;
20587c478bd9Sstevel@tonic-gate 
20597c478bd9Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tunmount_autofs rootvp=%p ", (void *)rootvp));
20607c478bd9Sstevel@tonic-gate 
20617c478bd9Sstevel@tonic-gate 	/*
20627c478bd9Sstevel@tonic-gate 	 * Remove all its immediate subdirectories.
20637c478bd9Sstevel@tonic-gate 	 */
20647c478bd9Sstevel@tonic-gate 	rootfnp = vntofn(rootvp);
20657c478bd9Sstevel@tonic-gate 	rw_enter(&rootfnp->fn_rwlock, RW_WRITER);
20667c478bd9Sstevel@tonic-gate 	for (fnp = rootfnp->fn_dirents; fnp != NULL; fnp = nfnp) {
20677c478bd9Sstevel@tonic-gate 		ASSERT(fntovn(fnp)->v_count == 0);
20687c478bd9Sstevel@tonic-gate 		ASSERT(fnp->fn_dirents == NULL);
20697c478bd9Sstevel@tonic-gate 		ASSERT(fnp->fn_linkcnt == 2);
20707c478bd9Sstevel@tonic-gate 		fnp->fn_linkcnt--;
20717c478bd9Sstevel@tonic-gate 		auto_disconnect(rootfnp, fnp);
20727c478bd9Sstevel@tonic-gate 		nfnp = fnp->fn_next;
20737c478bd9Sstevel@tonic-gate 		auto_freefnnode(fnp);
20747c478bd9Sstevel@tonic-gate 	}
20757c478bd9Sstevel@tonic-gate 	rw_exit(&rootfnp->fn_rwlock);
20767c478bd9Sstevel@tonic-gate }
20777c478bd9Sstevel@tonic-gate 
20787c478bd9Sstevel@tonic-gate /*
2079f798ee53SJan Kryl  * If a node matches all unmount criteria, do:
2080f798ee53SJan Kryl  *     destroy subordinate trigger node(s) if there is any
2081f798ee53SJan Kryl  *     unmount filesystem mounted on top of the node if there is any
20827c478bd9Sstevel@tonic-gate  *
2083f798ee53SJan Kryl  * Function should be called with locked fnp's mutex. The mutex is
2084f798ee53SJan Kryl  * unlocked before return from function.
20857c478bd9Sstevel@tonic-gate  */
2086f798ee53SJan Kryl static int
2087f798ee53SJan Kryl try_unmount_node(fnnode_t *fnp, boolean_t force)
2088f798ee53SJan Kryl {
2089f798ee53SJan Kryl 	boolean_t	trigger_unmount = B_FALSE;
2090f798ee53SJan Kryl 	action_list	*alp = NULL;
2091f798ee53SJan Kryl 	vnode_t		*vp;
2092f798ee53SJan Kryl 	int		error = 0;
2093f798ee53SJan Kryl 	fninfo_t	*fnip;
2094f798ee53SJan Kryl 	vfs_t		*vfsp;
2095f798ee53SJan Kryl 	struct autofs_globals *fngp;
20967c478bd9Sstevel@tonic-gate 
2097f798ee53SJan Kryl 	AUTOFS_DPRINT((10, "\ttry_unmount_node: processing node %p\n",
2098f798ee53SJan Kryl 	    (void *)fnp));
2099f798ee53SJan Kryl 
2100f798ee53SJan Kryl 	ASSERT(MUTEX_HELD(&fnp->fn_lock));
2101f798ee53SJan Kryl 
2102f798ee53SJan Kryl 	fngp = fnp->fn_globals;
21037c478bd9Sstevel@tonic-gate 	vp = fntovn(fnp);
21047c478bd9Sstevel@tonic-gate 	fnip = vfstofni(vp->v_vfsp);
21057c478bd9Sstevel@tonic-gate 
2106f798ee53SJan Kryl 	/*
2107f798ee53SJan Kryl 	 * If either a mount, lookup or another unmount of this subtree is in
2108f798ee53SJan Kryl 	 * progress, don't attempt to unmount at this time.
2109f798ee53SJan Kryl 	 */
21107c478bd9Sstevel@tonic-gate 	if (fnp->fn_flags & (MF_INPROG | MF_LOOKUP)) {
21117c478bd9Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
2112f798ee53SJan Kryl 		return (EBUSY);
21137c478bd9Sstevel@tonic-gate 	}
21147c478bd9Sstevel@tonic-gate 
21157c478bd9Sstevel@tonic-gate 	/*
2116f798ee53SJan Kryl 	 * Bail out if someone else is holding reference to this vnode.
2117f798ee53SJan Kryl 	 * This check isn't just an optimization (someone is probably
2118f798ee53SJan Kryl 	 * just about to trigger mount). It is necessary to prevent a deadlock
2119f798ee53SJan Kryl 	 * in domount() called from auto_perform_actions() if unmount of
2120f798ee53SJan Kryl 	 * trigger parent fails. domount() calls lookupname() to resolve
2121f798ee53SJan Kryl 	 * special in mount arguments. Special is set to a map name in case
2122f798ee53SJan Kryl 	 * of autofs triggers (i.e. auto_ws.sun.com). Thus if current
2123f798ee53SJan Kryl 	 * working directory is set to currently processed node, lookupname()
2124f798ee53SJan Kryl 	 * calls into autofs vnops in order to resolve special, which deadlocks
2125f798ee53SJan Kryl 	 * the process.
2126f798ee53SJan Kryl 	 *
2127f798ee53SJan Kryl 	 * Note: This should be fixed. Autofs shouldn't pass the map name
2128f798ee53SJan Kryl 	 * in special and avoid useless lookup with potentially disasterous
2129f798ee53SJan Kryl 	 * consequence.
21307c478bd9Sstevel@tonic-gate 	 */
2131f798ee53SJan Kryl 	if (check_auto_node(vp) == EBUSY) {
21327c478bd9Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
2133f798ee53SJan Kryl 		return (EBUSY);
21347c478bd9Sstevel@tonic-gate 	}
21357c478bd9Sstevel@tonic-gate 
2136f798ee53SJan Kryl 	/*
2137f798ee53SJan Kryl 	 * If not forced operation, back out if node has been referenced
2138f798ee53SJan Kryl 	 * recently.
2139f798ee53SJan Kryl 	 */
2140f798ee53SJan Kryl 	if (!force &&
2141f798ee53SJan Kryl 	    fnp->fn_ref_time + fnip->fi_mount_to > gethrestime_sec()) {
2142f798ee53SJan Kryl 		mutex_exit(&fnp->fn_lock);
2143f798ee53SJan Kryl 		return (EBUSY);
2144f798ee53SJan Kryl 	}
2145f798ee53SJan Kryl 
2146f798ee53SJan Kryl 	/* block mounts/unmounts on the node */
21477c478bd9Sstevel@tonic-gate 	AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG);
21487c478bd9Sstevel@tonic-gate 	fnp->fn_error = 0;
21497c478bd9Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
21507c478bd9Sstevel@tonic-gate 
2151f798ee53SJan Kryl 	/* unmount next level triggers if there are any */
21527c478bd9Sstevel@tonic-gate 	rw_enter(&fnp->fn_rwlock, RW_WRITER);
21537c478bd9Sstevel@tonic-gate 	if (fnp->fn_trigger != NULL) {
2154f798ee53SJan Kryl 		trigger_unmount = B_TRUE;
21557c478bd9Sstevel@tonic-gate 
2156f798ee53SJan Kryl 		if (triggers_busy(fnp)) {
21577c478bd9Sstevel@tonic-gate 			rw_exit(&fnp->fn_rwlock);
21587c478bd9Sstevel@tonic-gate 			mutex_enter(&fnp->fn_lock);
21597c478bd9Sstevel@tonic-gate 			AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
21607c478bd9Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
2161f798ee53SJan Kryl 			return (EBUSY);
21627c478bd9Sstevel@tonic-gate 		}
21637c478bd9Sstevel@tonic-gate 
21647c478bd9Sstevel@tonic-gate 		/*
21657c478bd9Sstevel@tonic-gate 		 * At this point, we know all trigger nodes are locked,
21667c478bd9Sstevel@tonic-gate 		 * and they're not busy or mounted on.
2167f798ee53SJan Kryl 		 *
2168f798ee53SJan Kryl 		 * Attempt to unmount all trigger nodes, save the
2169f798ee53SJan Kryl 		 * action_list in case we need to remount them later.
2170f798ee53SJan Kryl 		 * The action_list will be freed later if there was no
2171f798ee53SJan Kryl 		 * need to remount the trigger nodes.
21727c478bd9Sstevel@tonic-gate 		 */
21737c478bd9Sstevel@tonic-gate 		unmount_triggers(fnp, &alp);
21747c478bd9Sstevel@tonic-gate 	}
21757c478bd9Sstevel@tonic-gate 	rw_exit(&fnp->fn_rwlock);
21767c478bd9Sstevel@tonic-gate 
21777c478bd9Sstevel@tonic-gate 	(void) vn_vfswlock_wait(vp);
21787c478bd9Sstevel@tonic-gate 
21797c478bd9Sstevel@tonic-gate 	vfsp = vn_mountedvfs(vp);
21807c478bd9Sstevel@tonic-gate 	if (vfsp != NULL) {
2181f798ee53SJan Kryl 		/* vn_vfsunlock(vp) is done inside unmount_node() */
21827c478bd9Sstevel@tonic-gate 		error = unmount_node(vp, force);
21837c478bd9Sstevel@tonic-gate 		if (error == ECONNRESET) {
21847c478bd9Sstevel@tonic-gate 			if (vn_mountedvfs(vp) == NULL) {
21857c478bd9Sstevel@tonic-gate 				/*
21867c478bd9Sstevel@tonic-gate 				 * The filesystem was unmounted before the
21877c478bd9Sstevel@tonic-gate 				 * daemon died. Unfortunately we can not
21887c478bd9Sstevel@tonic-gate 				 * determine whether all the cleanup work was
21897c478bd9Sstevel@tonic-gate 				 * successfully finished (i.e. update mnttab,
21907c478bd9Sstevel@tonic-gate 				 * or notify NFS server of the unmount).
21917c478bd9Sstevel@tonic-gate 				 * We should not retry the operation since the
21927c478bd9Sstevel@tonic-gate 				 * filesystem has already been unmounted, and
21937c478bd9Sstevel@tonic-gate 				 * may have already been removed from mnttab,
21947c478bd9Sstevel@tonic-gate 				 * in such case the devid/rdevid we send to
21957c478bd9Sstevel@tonic-gate 				 * the daemon will not be matched. So we have
219639d3e169Sevanl 				 * to be content with the partial unmount.
21977c478bd9Sstevel@tonic-gate 				 * Since the mountpoint is no longer covered, we
21987c478bd9Sstevel@tonic-gate 				 * clear the error condition.
21997c478bd9Sstevel@tonic-gate 				 */
22007c478bd9Sstevel@tonic-gate 				error = 0;
220139d3e169Sevanl 				auto_log(fngp->fng_verbose, fngp->fng_zoneid,
2202f798ee53SJan Kryl 				    CE_WARN, "autofs: automountd "
2203f798ee53SJan Kryl 				    "connection dropped when unmounting %s/%s",
2204f798ee53SJan Kryl 				    fnip->fi_path, (fnip->fi_flags & MF_DIRECT)
2205f798ee53SJan Kryl 				    ? "" : fnp->fn_name);
22067c478bd9Sstevel@tonic-gate 			}
22077c478bd9Sstevel@tonic-gate 		}
22087c478bd9Sstevel@tonic-gate 	} else {
22097c478bd9Sstevel@tonic-gate 		vn_vfsunlock(vp);
2210f798ee53SJan Kryl 		/* Destroy all dirents of fnp if we unmounted its triggers */
2211f798ee53SJan Kryl 		if (trigger_unmount)
2212f798ee53SJan Kryl 			unmount_autofs(vp);
2213f798ee53SJan Kryl 	}
22147c478bd9Sstevel@tonic-gate 
2215f798ee53SJan Kryl 	/* If unmount failed, we got to remount triggers */
2216f798ee53SJan Kryl 	if (error != 0) {
2217f798ee53SJan Kryl 		if (trigger_unmount) {
2218f798ee53SJan Kryl 			int	ret;
2219f798ee53SJan Kryl 
2220f798ee53SJan Kryl 			ASSERT((fnp->fn_flags & MF_THISUID_MATCH_RQD) == 0);
2221f798ee53SJan Kryl 
2222f798ee53SJan Kryl 			/*
2223f798ee53SJan Kryl 			 * The action list was free'd by auto_perform_actions
2224f798ee53SJan Kryl 			 */
2225f798ee53SJan Kryl 			ret = auto_perform_actions(fnip, fnp, alp, CRED());
2226f798ee53SJan Kryl 			if (ret != 0) {
2227f798ee53SJan Kryl 				auto_log(fngp->fng_verbose, fngp->fng_zoneid,
2228f798ee53SJan Kryl 				    CE_WARN, "autofs: can't remount triggers "
2229f798ee53SJan Kryl 				    "fnp=%p error=%d", (void *)fnp, ret);
2230f798ee53SJan Kryl 			}
2231f798ee53SJan Kryl 		}
22327c478bd9Sstevel@tonic-gate 		mutex_enter(&fnp->fn_lock);
22337c478bd9Sstevel@tonic-gate 		AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
22347c478bd9Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
22357c478bd9Sstevel@tonic-gate 	} else {
2236f798ee53SJan Kryl 		/* Free the action list here */
2237f798ee53SJan Kryl 		if (trigger_unmount)
22387c478bd9Sstevel@tonic-gate 			xdr_free(xdr_action_list, (char *)alp);
22397c478bd9Sstevel@tonic-gate 
22407c478bd9Sstevel@tonic-gate 		/*
22417c478bd9Sstevel@tonic-gate 		 * Other threads may be waiting for this unmount to
22427c478bd9Sstevel@tonic-gate 		 * finish. We must let it know that in order to
22437c478bd9Sstevel@tonic-gate 		 * proceed, it must trigger the mount itself.
22447c478bd9Sstevel@tonic-gate 		 */
2245f798ee53SJan Kryl 		mutex_enter(&fnp->fn_lock);
22467c478bd9Sstevel@tonic-gate 		fnp->fn_flags &= ~MF_IK_MOUNT;
22477c478bd9Sstevel@tonic-gate 		if (fnp->fn_flags & MF_WAITING)
22487c478bd9Sstevel@tonic-gate 			fnp->fn_error = EAGAIN;
22497c478bd9Sstevel@tonic-gate 		AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
22507c478bd9Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
22517c478bd9Sstevel@tonic-gate 	}
22527c478bd9Sstevel@tonic-gate 
2253f798ee53SJan Kryl 	return (error);
22547c478bd9Sstevel@tonic-gate }
22557c478bd9Sstevel@tonic-gate 
22567c478bd9Sstevel@tonic-gate /*
2257f798ee53SJan Kryl  * This is an implementation of depth-first search in a tree rooted by
2258f798ee53SJan Kryl  * start_fnp and composed from fnnodes. Links between tree levels are
2259f798ee53SJan Kryl  * fn_dirents, fn_trigger in fnnode_t and v_mountedvfs in vnode_t (if
2260f798ee53SJan Kryl  * mounted vfs is autofs). The algorithm keeps track of visited nodes
2261f798ee53SJan Kryl  * by means of a timestamp (fn_unmount_ref_time).
2262f798ee53SJan Kryl  *
2263f798ee53SJan Kryl  * Upon top-down traversal of the tree we apply following locking scheme:
2264f798ee53SJan Kryl  *	lock fn_rwlock of current node
2265f798ee53SJan Kryl  *	grab reference to child's vnode (VN_HOLD)
2266f798ee53SJan Kryl  *	unlock fn_rwlock
2267f798ee53SJan Kryl  *	free reference to current vnode (VN_RELE)
2268f798ee53SJan Kryl  * Similar locking scheme is used for down-top and left-right traversal.
2269f798ee53SJan Kryl  *
2270f798ee53SJan Kryl  * Algorithm examines the most down-left node in tree, which hasn't been
2271f798ee53SJan Kryl  * visited yet. From this follows that nodes are processed in bottom-up
2272f798ee53SJan Kryl  * fashion.
2273f798ee53SJan Kryl  *
2274f798ee53SJan Kryl  * Function returns either zero if unmount of root node was successful
2275f798ee53SJan Kryl  * or error code (mostly EBUSY).
22767c478bd9Sstevel@tonic-gate  */
2277f798ee53SJan Kryl int
2278f798ee53SJan Kryl unmount_subtree(fnnode_t *rootfnp, boolean_t force)
2279f798ee53SJan Kryl {
2280f798ee53SJan Kryl 	fnnode_t	*currfnp; /* currently examined node in the tree */
2281f798ee53SJan Kryl 	fnnode_t	*lastfnp; /* previously processed node */
2282f798ee53SJan Kryl 	fnnode_t	*nextfnp; /* next examined node in the tree */
2283f798ee53SJan Kryl 	vnode_t		*curvp;
2284f798ee53SJan Kryl 	vnode_t		*newvp;
2285f798ee53SJan Kryl 	vfs_t		*vfsp;
2286f798ee53SJan Kryl 	time_t		timestamp;
22877c478bd9Sstevel@tonic-gate 
2288f798ee53SJan Kryl 	ASSERT(fntovn(rootfnp)->v_type != VLNK);
2289f798ee53SJan Kryl 	AUTOFS_DPRINT((10, "unmount_subtree: root=%p (%s)\n", (void *)rootfnp,
2290f798ee53SJan Kryl 	    rootfnp->fn_name));
2291f798ee53SJan Kryl 
2292f798ee53SJan Kryl 	/*
2293f798ee53SJan Kryl 	 * Timestamp, which visited nodes are marked with, to distinguish them
2294f798ee53SJan Kryl 	 * from unvisited nodes.
2295f798ee53SJan Kryl 	 */
2296f798ee53SJan Kryl 	timestamp = gethrestime_sec();
2297f798ee53SJan Kryl 	currfnp = lastfnp = rootfnp;
2298f798ee53SJan Kryl 
2299f798ee53SJan Kryl 	/* Loop until we examine all nodes in the tree */
2300f798ee53SJan Kryl 	mutex_enter(&currfnp->fn_lock);
2301f798ee53SJan Kryl 	while (currfnp != rootfnp || rootfnp->fn_unmount_ref_time < timestamp) {
2302f798ee53SJan Kryl 		curvp = fntovn(currfnp);
2303f798ee53SJan Kryl 		AUTOFS_DPRINT((10, "\tunmount_subtree: entering node %p (%s)\n",
2304f798ee53SJan Kryl 		    (void *)currfnp, currfnp->fn_name));
2305f798ee53SJan Kryl 
2306f798ee53SJan Kryl 		/*
2307f798ee53SJan Kryl 		 * New candidate for processing must have been already visited,
2308f798ee53SJan Kryl 		 * by us because we want to process tree nodes in bottom-up
2309f798ee53SJan Kryl 		 * order.
2310f798ee53SJan Kryl 		 */
2311f798ee53SJan Kryl 		if (currfnp->fn_unmount_ref_time == timestamp &&
2312f798ee53SJan Kryl 		    currfnp != lastfnp) {
2313f798ee53SJan Kryl 			(void) try_unmount_node(currfnp, force);
2314f798ee53SJan Kryl 			lastfnp = currfnp;
2315f798ee53SJan Kryl 			mutex_enter(&currfnp->fn_lock);
2316f798ee53SJan Kryl 			/*
2317f798ee53SJan Kryl 			 * Fall through to next if-branch to pick
2318f798ee53SJan Kryl 			 * sibling or parent of this node.
2319f798ee53SJan Kryl 			 */
23207c478bd9Sstevel@tonic-gate 		}
23217c478bd9Sstevel@tonic-gate 
2322f798ee53SJan Kryl 		/*
2323f798ee53SJan Kryl 		 * If this node has been already visited, it means that it's
2324f798ee53SJan Kryl 		 * dead end and we need to pick sibling or parent as next node.
2325f798ee53SJan Kryl 		 */
2326f798ee53SJan Kryl 		if (currfnp->fn_unmount_ref_time >= timestamp ||
2327f798ee53SJan Kryl 		    curvp->v_type == VLNK) {
2328f798ee53SJan Kryl 			mutex_exit(&currfnp->fn_lock);
2329f798ee53SJan Kryl 			/*
2330f798ee53SJan Kryl 			 * Obtain parent's readers lock before grabbing
2331f798ee53SJan Kryl 			 * reference to sibling.
2332f798ee53SJan Kryl 			 */
2333f798ee53SJan Kryl 			rw_enter(&currfnp->fn_parent->fn_rwlock, RW_READER);
2334f798ee53SJan Kryl 			if ((nextfnp = currfnp->fn_next) != NULL) {
2335f798ee53SJan Kryl 				VN_HOLD(fntovn(nextfnp));
2336f798ee53SJan Kryl 				rw_exit(&currfnp->fn_parent->fn_rwlock);
2337f798ee53SJan Kryl 				VN_RELE(curvp);
2338f798ee53SJan Kryl 				currfnp = nextfnp;
2339f798ee53SJan Kryl 				mutex_enter(&currfnp->fn_lock);
2340f798ee53SJan Kryl 				continue;
2341f798ee53SJan Kryl 			}
2342f798ee53SJan Kryl 			rw_exit(&currfnp->fn_parent->fn_rwlock);
23437c478bd9Sstevel@tonic-gate 
23447c478bd9Sstevel@tonic-gate 			/*
2345f798ee53SJan Kryl 			 * All descendants and siblings were visited. Perform
2346f798ee53SJan Kryl 			 * bottom-up move.
23477c478bd9Sstevel@tonic-gate 			 */
2348f798ee53SJan Kryl 			nextfnp = currfnp->fn_parent;
2349f798ee53SJan Kryl 			VN_HOLD(fntovn(nextfnp));
2350f798ee53SJan Kryl 			VN_RELE(curvp);
2351f798ee53SJan Kryl 			currfnp = nextfnp;
2352f798ee53SJan Kryl 			mutex_enter(&currfnp->fn_lock);
2353f798ee53SJan Kryl 			continue;
2354f798ee53SJan Kryl 		}
2355f798ee53SJan Kryl 
2356f798ee53SJan Kryl 		/*
2357f798ee53SJan Kryl 		 * Mark node as visited. Note that the timestamp could have
2358f798ee53SJan Kryl 		 * been updated by somebody else in the meantime.
2359f798ee53SJan Kryl 		 */
2360f798ee53SJan Kryl 		if (currfnp->fn_unmount_ref_time < timestamp)
2361f798ee53SJan Kryl 			currfnp->fn_unmount_ref_time = timestamp;
2362*6bf33d39SJan Kryl 
2363*6bf33d39SJan Kryl 		/*
2364*6bf33d39SJan Kryl 		 * Don't descent below nodes, which are being unmounted/mounted.
2365*6bf33d39SJan Kryl 		 *
2366*6bf33d39SJan Kryl 		 * We need to hold both locks at once: fn_lock because we need
2367*6bf33d39SJan Kryl 		 * to read MF_INPROG and fn_rwlock to prevent anybody from
2368*6bf33d39SJan Kryl 		 * modifying fn_trigger until its used to traverse triggers
2369*6bf33d39SJan Kryl 		 * below.
2370*6bf33d39SJan Kryl 		 *
2371*6bf33d39SJan Kryl 		 * Acquire fn_rwlock in non-blocking mode to avoid deadlock.
2372*6bf33d39SJan Kryl 		 * If it can't be acquired, then acquire locks in correct
2373*6bf33d39SJan Kryl 		 * order.
2374*6bf33d39SJan Kryl 		 */
2375*6bf33d39SJan Kryl 		if (!rw_tryenter(&currfnp->fn_rwlock, RW_READER)) {
2376*6bf33d39SJan Kryl 			mutex_exit(&currfnp->fn_lock);
2377*6bf33d39SJan Kryl 			rw_enter(&currfnp->fn_rwlock, RW_READER);
2378*6bf33d39SJan Kryl 			mutex_enter(&currfnp->fn_lock);
2379*6bf33d39SJan Kryl 		}
2380*6bf33d39SJan Kryl 		if (currfnp->fn_flags & MF_INPROG) {
2381*6bf33d39SJan Kryl 			rw_exit(&currfnp->fn_rwlock);
2382*6bf33d39SJan Kryl 			continue;
2383*6bf33d39SJan Kryl 		}
2384f798ee53SJan Kryl 		mutex_exit(&currfnp->fn_lock);
2385f798ee53SJan Kryl 
2386f798ee53SJan Kryl 		/*
2387f798ee53SJan Kryl 		 * Examine descendants in this order: triggers, dirents, autofs
2388f798ee53SJan Kryl 		 * mounts.
2389f798ee53SJan Kryl 		 */
2390f798ee53SJan Kryl 
2391f798ee53SJan Kryl 		if ((nextfnp = currfnp->fn_trigger) != NULL) {
2392f798ee53SJan Kryl 			VN_HOLD(fntovn(nextfnp));
2393f798ee53SJan Kryl 			rw_exit(&currfnp->fn_rwlock);
2394f798ee53SJan Kryl 			VN_RELE(curvp);
2395f798ee53SJan Kryl 			currfnp = nextfnp;
2396f798ee53SJan Kryl 			mutex_enter(&currfnp->fn_lock);
2397f798ee53SJan Kryl 			continue;
2398f798ee53SJan Kryl 		}
2399f798ee53SJan Kryl 
2400f798ee53SJan Kryl 		if ((nextfnp = currfnp->fn_dirents) != NULL) {
2401f798ee53SJan Kryl 			VN_HOLD(fntovn(nextfnp));
2402f798ee53SJan Kryl 			rw_exit(&currfnp->fn_rwlock);
2403f798ee53SJan Kryl 			VN_RELE(curvp);
2404f798ee53SJan Kryl 			currfnp = nextfnp;
2405f798ee53SJan Kryl 			mutex_enter(&currfnp->fn_lock);
2406f798ee53SJan Kryl 			continue;
2407f798ee53SJan Kryl 		}
2408f798ee53SJan Kryl 		rw_exit(&currfnp->fn_rwlock);
2409f798ee53SJan Kryl 
2410f798ee53SJan Kryl 		(void) vn_vfswlock_wait(curvp);
2411f798ee53SJan Kryl 		vfsp = vn_mountedvfs(curvp);
2412f798ee53SJan Kryl 		if (vfsp != NULL &&
2413f798ee53SJan Kryl 		    vfs_matchops(vfsp, vfs_getops(curvp->v_vfsp))) {
2414f798ee53SJan Kryl 			/*
2415f798ee53SJan Kryl 			 * Deal with /xfn/host/jurassic alikes here...
2416f798ee53SJan Kryl 			 *
2417f798ee53SJan Kryl 			 * We know this call to VFS_ROOT is safe to call while
2418f798ee53SJan Kryl 			 * holding VVFSLOCK, since it resolves to a call to
2419f798ee53SJan Kryl 			 * auto_root().
2420f798ee53SJan Kryl 			 */
2421f798ee53SJan Kryl 			if (VFS_ROOT(vfsp, &newvp)) {
2422f798ee53SJan Kryl 				cmn_err(CE_PANIC,
2423f798ee53SJan Kryl 				    "autofs: VFS_ROOT(vfs=%p) failed",
2424f798ee53SJan Kryl 				    (void *)vfsp);
2425f798ee53SJan Kryl 			}
2426f798ee53SJan Kryl 			vn_vfsunlock(curvp);
2427f798ee53SJan Kryl 			VN_RELE(curvp);
2428f798ee53SJan Kryl 			currfnp = vntofn(newvp);
2429f798ee53SJan Kryl 			mutex_enter(&currfnp->fn_lock);
2430f798ee53SJan Kryl 			continue;
2431f798ee53SJan Kryl 		}
2432f798ee53SJan Kryl 		vn_vfsunlock(curvp);
2433f798ee53SJan Kryl 		mutex_enter(&currfnp->fn_lock);
2434f798ee53SJan Kryl 	}
2435f798ee53SJan Kryl 
2436f798ee53SJan Kryl 	/*
2437f798ee53SJan Kryl 	 * Now we deal with the root node (currfnp's mutex is unlocked
2438f798ee53SJan Kryl 	 * in try_unmount_node()).
2439f798ee53SJan Kryl 	 */
2440f798ee53SJan Kryl 	return (try_unmount_node(currfnp, force));
2441f798ee53SJan Kryl }
2442f798ee53SJan Kryl 
2443f798ee53SJan Kryl /*
2444f798ee53SJan Kryl  * XXX unmount_tree() is not suspend-safe within the scope of
2445f798ee53SJan Kryl  * the present model defined for cpr to suspend the system. Calls made
2446f798ee53SJan Kryl  * by the unmount_tree() that have been identified to be unsafe are
2447f798ee53SJan Kryl  * (1) RPC client handle setup and client calls to automountd which can
2448f798ee53SJan Kryl  * block deep down in the RPC library, (2) kmem_alloc() calls with the
2449f798ee53SJan Kryl  * KM_SLEEP flag which can block if memory is low, and (3) VFS_*() and
2450f798ee53SJan Kryl  * VOP_*() calls which can result in over the wire calls to servers.
2451f798ee53SJan Kryl  * The thread should be completely reevaluated to make it suspend-safe in
2452f798ee53SJan Kryl  * case of future updates to the cpr model.
2453f798ee53SJan Kryl  */
2454f798ee53SJan Kryl void
2455f798ee53SJan Kryl unmount_tree(struct autofs_globals *fngp, boolean_t force)
2456f798ee53SJan Kryl {
2457f798ee53SJan Kryl 	callb_cpr_t	cprinfo;
2458f798ee53SJan Kryl 	kmutex_t	unmount_tree_cpr_lock;
2459f798ee53SJan Kryl 	fnnode_t	*root, *fnp, *next;
2460f798ee53SJan Kryl 
2461f798ee53SJan Kryl 	mutex_init(&unmount_tree_cpr_lock, NULL, MUTEX_DEFAULT, NULL);
2462f798ee53SJan Kryl 	CALLB_CPR_INIT(&cprinfo, &unmount_tree_cpr_lock, callb_generic_cpr,
2463f798ee53SJan Kryl 	    "unmount_tree");
2464f798ee53SJan Kryl 
2465f798ee53SJan Kryl 	/*
2466f798ee53SJan Kryl 	 * autofssys() will be calling in from the global zone and doing
2467f798ee53SJan Kryl 	 * work on the behalf of the given zone, hence we can't always
2468f798ee53SJan Kryl 	 * assert that we have the right credentials, nor that the
2469f798ee53SJan Kryl 	 * caller is always in the correct zone.
2470f798ee53SJan Kryl 	 *
2471f798ee53SJan Kryl 	 * We do, however, know that if this is a "forced unmount"
2472f798ee53SJan Kryl 	 * operation (which autofssys() does), then we won't go down to
2473f798ee53SJan Kryl 	 * the krpc layers, so we don't need to fudge with the
2474f798ee53SJan Kryl 	 * credentials.
2475f798ee53SJan Kryl 	 */
2476f798ee53SJan Kryl 	ASSERT(force || fngp->fng_zoneid == getzoneid());
2477f798ee53SJan Kryl 
2478f798ee53SJan Kryl 	/*
2479f798ee53SJan Kryl 	 * If automountd is not running in this zone,
2480f798ee53SJan Kryl 	 * don't attempt unmounting this round.
2481f798ee53SJan Kryl 	 */
2482f798ee53SJan Kryl 	if (force || auto_null_request(fngp->fng_zoneid, FALSE) == 0) {
2483f798ee53SJan Kryl 		/*
2484f798ee53SJan Kryl 		 * Iterate over top level autofs filesystems and call
2485f798ee53SJan Kryl 		 * unmount_subtree() for each of them.
2486f798ee53SJan Kryl 		 */
2487f798ee53SJan Kryl 		root = fngp->fng_rootfnnodep;
2488f798ee53SJan Kryl 		rw_enter(&root->fn_rwlock, RW_READER);
2489f798ee53SJan Kryl 		for (fnp = root->fn_dirents; fnp != NULL; fnp = next) {
2490f798ee53SJan Kryl 			VN_HOLD(fntovn(fnp));
2491f798ee53SJan Kryl 			rw_exit(&root->fn_rwlock);
2492f798ee53SJan Kryl 			(void) unmount_subtree(fnp, force);
2493f798ee53SJan Kryl 			rw_enter(&root->fn_rwlock, RW_READER);
2494f798ee53SJan Kryl 			next = fnp->fn_next;
2495f798ee53SJan Kryl 			VN_RELE(fntovn(fnp));
2496f798ee53SJan Kryl 		}
2497f798ee53SJan Kryl 		rw_exit(&root->fn_rwlock);
2498f798ee53SJan Kryl 	}
2499f798ee53SJan Kryl 
25007c478bd9Sstevel@tonic-gate 	mutex_enter(&unmount_tree_cpr_lock);
25017c478bd9Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cprinfo);
25027c478bd9Sstevel@tonic-gate 	mutex_destroy(&unmount_tree_cpr_lock);
25037c478bd9Sstevel@tonic-gate }
25047c478bd9Sstevel@tonic-gate 
25057c478bd9Sstevel@tonic-gate static void
25067c478bd9Sstevel@tonic-gate unmount_zone_tree(struct autofs_globals *fngp)
25077c478bd9Sstevel@tonic-gate {
2508f798ee53SJan Kryl 	AUTOFS_DPRINT((5, "unmount_zone_tree started. Thread created.\n"));
2509f798ee53SJan Kryl 
2510f798ee53SJan Kryl 	unmount_tree(fngp, B_FALSE);
25117c478bd9Sstevel@tonic-gate 	mutex_enter(&fngp->fng_unmount_threads_lock);
25127c478bd9Sstevel@tonic-gate 	fngp->fng_unmount_threads--;
25137c478bd9Sstevel@tonic-gate 	mutex_exit(&fngp->fng_unmount_threads_lock);
25147c478bd9Sstevel@tonic-gate 
2515f798ee53SJan Kryl 	AUTOFS_DPRINT((5, "unmount_zone_tree done. Thread exiting.\n"));
25167c478bd9Sstevel@tonic-gate 
25177c478bd9Sstevel@tonic-gate 	zthread_exit();
25187c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
25197c478bd9Sstevel@tonic-gate }
25207c478bd9Sstevel@tonic-gate 
25217c478bd9Sstevel@tonic-gate void
25227c478bd9Sstevel@tonic-gate auto_do_unmount(struct autofs_globals *fngp)
25237c478bd9Sstevel@tonic-gate {
25247c478bd9Sstevel@tonic-gate 	callb_cpr_t cprinfo;
25257c478bd9Sstevel@tonic-gate 	clock_t timeleft;
25267c478bd9Sstevel@tonic-gate 	zone_t *zone = curproc->p_zone;
25277c478bd9Sstevel@tonic-gate 
25287c478bd9Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, &fngp->fng_unmount_threads_lock,
25297c478bd9Sstevel@tonic-gate 	    callb_generic_cpr, "auto_do_unmount");
25307c478bd9Sstevel@tonic-gate 
25317c478bd9Sstevel@tonic-gate 	for (;;) {	/* forever */
25327c478bd9Sstevel@tonic-gate 		mutex_enter(&fngp->fng_unmount_threads_lock);
25337c478bd9Sstevel@tonic-gate 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
25347c478bd9Sstevel@tonic-gate newthread:
25357c478bd9Sstevel@tonic-gate 		mutex_exit(&fngp->fng_unmount_threads_lock);
2536d3d50737SRafael Vanoni 		timeleft = zone_status_timedwait(zone, ddi_get_lbolt() +
25377c478bd9Sstevel@tonic-gate 		    autofs_unmount_thread_timer * hz, ZONE_IS_SHUTTING_DOWN);
25387c478bd9Sstevel@tonic-gate 		mutex_enter(&fngp->fng_unmount_threads_lock);
25397c478bd9Sstevel@tonic-gate 
25407c478bd9Sstevel@tonic-gate 		if (timeleft != -1) {	/* didn't time out */
25417c478bd9Sstevel@tonic-gate 			ASSERT(zone_status_get(zone) >= ZONE_IS_SHUTTING_DOWN);
25427c478bd9Sstevel@tonic-gate 			/*
25437c478bd9Sstevel@tonic-gate 			 * zone is exiting... don't create any new threads.
25447c478bd9Sstevel@tonic-gate 			 * fng_unmount_threads_lock is released implicitly by
25457c478bd9Sstevel@tonic-gate 			 * the below.
25467c478bd9Sstevel@tonic-gate 			 */
25477c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(&cprinfo,
25487c478bd9Sstevel@tonic-gate 			    &fngp->fng_unmount_threads_lock);
25497c478bd9Sstevel@tonic-gate 			CALLB_CPR_EXIT(&cprinfo);
25507c478bd9Sstevel@tonic-gate 			zthread_exit();
25517c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
25527c478bd9Sstevel@tonic-gate 		}
25537c478bd9Sstevel@tonic-gate 		if (fngp->fng_unmount_threads < autofs_unmount_threads) {
25547c478bd9Sstevel@tonic-gate 			fngp->fng_unmount_threads++;
25557c478bd9Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(&cprinfo,
25567c478bd9Sstevel@tonic-gate 			    &fngp->fng_unmount_threads_lock);
25577c478bd9Sstevel@tonic-gate 			mutex_exit(&fngp->fng_unmount_threads_lock);
25587c478bd9Sstevel@tonic-gate 
25597c478bd9Sstevel@tonic-gate 			(void) zthread_create(NULL, 0, unmount_zone_tree, fngp,
25607c478bd9Sstevel@tonic-gate 			    0, minclsyspri);
25617c478bd9Sstevel@tonic-gate 		} else
25627c478bd9Sstevel@tonic-gate 			goto newthread;
25637c478bd9Sstevel@tonic-gate 	}
25647c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
25657c478bd9Sstevel@tonic-gate }
25667c478bd9Sstevel@tonic-gate 
25677c478bd9Sstevel@tonic-gate /*
25687c478bd9Sstevel@tonic-gate  * Is nobrowse specified in option string?
25697c478bd9Sstevel@tonic-gate  * opts should be a null ('\0') terminated string.
25707c478bd9Sstevel@tonic-gate  * Returns non-zero if nobrowse has been specified.
25717c478bd9Sstevel@tonic-gate  */
25727c478bd9Sstevel@tonic-gate int
25737c478bd9Sstevel@tonic-gate auto_nobrowse_option(char *opts)
25747c478bd9Sstevel@tonic-gate {
25757c478bd9Sstevel@tonic-gate 	char *buf;
25767c478bd9Sstevel@tonic-gate 	char *p;
25777c478bd9Sstevel@tonic-gate 	char *t;
25787c478bd9Sstevel@tonic-gate 	int nobrowse = 0;
25797c478bd9Sstevel@tonic-gate 	int last_opt = 0;
25807c478bd9Sstevel@tonic-gate 	size_t len;
25817c478bd9Sstevel@tonic-gate 
25827c478bd9Sstevel@tonic-gate 	len = strlen(opts) + 1;
25837c478bd9Sstevel@tonic-gate 	p = buf = kmem_alloc(len, KM_SLEEP);
25847c478bd9Sstevel@tonic-gate 	(void) strcpy(buf, opts);
25857c478bd9Sstevel@tonic-gate 	do {
25867c478bd9Sstevel@tonic-gate 		if (t = strchr(p, ','))
25877c478bd9Sstevel@tonic-gate 			*t++ = '\0';
25887c478bd9Sstevel@tonic-gate 		else
25897c478bd9Sstevel@tonic-gate 			last_opt++;
25907c478bd9Sstevel@tonic-gate 		if (strcmp(p, MNTOPT_NOBROWSE) == 0)
25917c478bd9Sstevel@tonic-gate 			nobrowse = 1;
25927c478bd9Sstevel@tonic-gate 		else if (strcmp(p, MNTOPT_BROWSE) == 0)
25937c478bd9Sstevel@tonic-gate 			nobrowse = 0;
25947c478bd9Sstevel@tonic-gate 		p = t;
25957c478bd9Sstevel@tonic-gate 	} while (!last_opt);
25967c478bd9Sstevel@tonic-gate 	kmem_free(buf, len);
25977c478bd9Sstevel@tonic-gate 
25987c478bd9Sstevel@tonic-gate 	return (nobrowse);
25997c478bd9Sstevel@tonic-gate }
26007c478bd9Sstevel@tonic-gate 
26017c478bd9Sstevel@tonic-gate /*
26027c478bd9Sstevel@tonic-gate  * used to log warnings only if automountd is running
26037c478bd9Sstevel@tonic-gate  * with verbose mode set
26047c478bd9Sstevel@tonic-gate  */
260539d3e169Sevanl 
26067c478bd9Sstevel@tonic-gate void
260739d3e169Sevanl auto_log(int verbose, zoneid_t zoneid, int level, const char *fmt, ...)
26087c478bd9Sstevel@tonic-gate {
26097c478bd9Sstevel@tonic-gate 	va_list	args;
26107c478bd9Sstevel@tonic-gate 
261139d3e169Sevanl 	if (verbose) {
26127c478bd9Sstevel@tonic-gate 		va_start(args, fmt);
261339d3e169Sevanl 		vzcmn_err(zoneid, level, fmt, args);
26147c478bd9Sstevel@tonic-gate 		va_end(args);
26157c478bd9Sstevel@tonic-gate 	}
26167c478bd9Sstevel@tonic-gate }
26177c478bd9Sstevel@tonic-gate 
26187c478bd9Sstevel@tonic-gate #ifdef DEBUG
26197c478bd9Sstevel@tonic-gate static int autofs_debug = 0;
26207c478bd9Sstevel@tonic-gate 
26217c478bd9Sstevel@tonic-gate /*
26227c478bd9Sstevel@tonic-gate  * Utilities used by both client and server
26237c478bd9Sstevel@tonic-gate  * Standard levels:
26247c478bd9Sstevel@tonic-gate  * 0) no debugging
26257c478bd9Sstevel@tonic-gate  * 1) hard failures
26267c478bd9Sstevel@tonic-gate  * 2) soft failures
26277c478bd9Sstevel@tonic-gate  * 3) current test software
26287c478bd9Sstevel@tonic-gate  * 4) main procedure entry points
26297c478bd9Sstevel@tonic-gate  * 5) main procedure exit points
26307c478bd9Sstevel@tonic-gate  * 6) utility procedure entry points
26317c478bd9Sstevel@tonic-gate  * 7) utility procedure exit points
26327c478bd9Sstevel@tonic-gate  * 8) obscure procedure entry points
26337c478bd9Sstevel@tonic-gate  * 9) obscure procedure exit points
26347c478bd9Sstevel@tonic-gate  * 10) random stuff
26357c478bd9Sstevel@tonic-gate  * 11) all <= 1
26367c478bd9Sstevel@tonic-gate  * 12) all <= 2
26377c478bd9Sstevel@tonic-gate  * 13) all <= 3
26387c478bd9Sstevel@tonic-gate  * ...
26397c478bd9Sstevel@tonic-gate  */
26407c478bd9Sstevel@tonic-gate /* PRINTFLIKE2 */
26417c478bd9Sstevel@tonic-gate void
26427c478bd9Sstevel@tonic-gate auto_dprint(int level, const char *fmt, ...)
26437c478bd9Sstevel@tonic-gate {
26447c478bd9Sstevel@tonic-gate 	va_list args;
26457c478bd9Sstevel@tonic-gate 
26467c478bd9Sstevel@tonic-gate 	if (autofs_debug == level ||
26477c478bd9Sstevel@tonic-gate 	    (autofs_debug > 10 && (autofs_debug - 10) >= level)) {
26487c478bd9Sstevel@tonic-gate 		va_start(args, fmt);
26497c478bd9Sstevel@tonic-gate 		(void) vprintf(fmt, args);
26507c478bd9Sstevel@tonic-gate 		va_end(args);
26517c478bd9Sstevel@tonic-gate 	}
26527c478bd9Sstevel@tonic-gate }
26537c478bd9Sstevel@tonic-gate #endif /* DEBUG */
2654