xref: /onnv-gate/usr/src/uts/common/fs/autofs/auto_subr.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/param.h>
30*0Sstevel@tonic-gate #include <sys/kmem.h>
31*0Sstevel@tonic-gate #include <sys/errno.h>
32*0Sstevel@tonic-gate #include <sys/proc.h>
33*0Sstevel@tonic-gate #include <sys/disp.h>
34*0Sstevel@tonic-gate #include <sys/vfs.h>
35*0Sstevel@tonic-gate #include <sys/vnode.h>
36*0Sstevel@tonic-gate #include <sys/pathname.h>
37*0Sstevel@tonic-gate #include <sys/cred.h>
38*0Sstevel@tonic-gate #include <sys/mount.h>
39*0Sstevel@tonic-gate #include <sys/cmn_err.h>
40*0Sstevel@tonic-gate #include <sys/debug.h>
41*0Sstevel@tonic-gate #include <sys/systm.h>
42*0Sstevel@tonic-gate #include <rpc/types.h>
43*0Sstevel@tonic-gate #include <rpc/xdr.h>
44*0Sstevel@tonic-gate #include <rpc/auth.h>
45*0Sstevel@tonic-gate #include <rpc/clnt.h>
46*0Sstevel@tonic-gate #include <sys/ticotsord.h>
47*0Sstevel@tonic-gate #include <sys/dirent.h>
48*0Sstevel@tonic-gate #include <fs/fs_subr.h>
49*0Sstevel@tonic-gate #include <rpcsvc/autofs_prot.h>
50*0Sstevel@tonic-gate #include <sys/fs/autofs.h>
51*0Sstevel@tonic-gate #include <sys/callb.h>
52*0Sstevel@tonic-gate #include <sys/sysmacros.h>
53*0Sstevel@tonic-gate #include <sys/zone.h>
54*0Sstevel@tonic-gate #include <sys/fs/mntdata.h>
55*0Sstevel@tonic-gate 
56*0Sstevel@tonic-gate /*
57*0Sstevel@tonic-gate  * Autofs and Zones:
58*0Sstevel@tonic-gate  *
59*0Sstevel@tonic-gate  * Zones are delegated the responsibility of managing their own autofs mounts
60*0Sstevel@tonic-gate  * and maps.  Each zone runs its own copy of automountd, with its own timeouts,
61*0Sstevel@tonic-gate  * and other logically "global" parameters.  kRPC and virtualization in the
62*0Sstevel@tonic-gate  * loopback transport (tl) will prevent a zone from communicating with another
63*0Sstevel@tonic-gate  * zone's automountd.
64*0Sstevel@tonic-gate  *
65*0Sstevel@tonic-gate  * Each zone has its own "rootfnnode" and associated tree of auto nodes.
66*0Sstevel@tonic-gate  *
67*0Sstevel@tonic-gate  * Each zone also has its own set of "unmounter" kernel threads; these are
68*0Sstevel@tonic-gate  * created and run within the zone's context (ie, they are created via
69*0Sstevel@tonic-gate  * zthread_create()).
70*0Sstevel@tonic-gate  *
71*0Sstevel@tonic-gate  * Cross-zone mount triggers are disallowed.  There is a check in
72*0Sstevel@tonic-gate  * auto_trigger_mount() to this effect; EPERM is returned to indicate that the
73*0Sstevel@tonic-gate  * mount is not owned by the caller.
74*0Sstevel@tonic-gate  *
75*0Sstevel@tonic-gate  * autofssys() enables a caller in the global zone to clean up in-kernel (as
76*0Sstevel@tonic-gate  * well as regular) autofs mounts via the unmount_tree() mechanism.  This is
77*0Sstevel@tonic-gate  * routinely done when all mounts are removed as part of zone shutdown.
78*0Sstevel@tonic-gate  */
79*0Sstevel@tonic-gate #define	TYPICALMAXPATHLEN	64
80*0Sstevel@tonic-gate 
81*0Sstevel@tonic-gate static kmutex_t autofs_nodeid_lock;
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate static int auto_perform_link(fnnode_t *, struct linka *, cred_t *);
84*0Sstevel@tonic-gate static int auto_perform_actions(fninfo_t *, fnnode_t *,
85*0Sstevel@tonic-gate     action_list *, cred_t *);
86*0Sstevel@tonic-gate static int auto_getmntpnt(vnode_t *, char *, vnode_t **, cred_t *);
87*0Sstevel@tonic-gate static int auto_lookup_request(fninfo_t *, char *, struct linka *,
88*0Sstevel@tonic-gate     cred_t *, bool_t, bool_t *);
89*0Sstevel@tonic-gate static int auto_mount_request(fninfo_t *, char *, action_list **,
90*0Sstevel@tonic-gate     cred_t *, bool_t);
91*0Sstevel@tonic-gate 
92*0Sstevel@tonic-gate /*
93*0Sstevel@tonic-gate  * Clears the MF_INPROG flag, and wakes up those threads sleeping on
94*0Sstevel@tonic-gate  * fn_cv_mount if MF_WAITING is set.
95*0Sstevel@tonic-gate  */
96*0Sstevel@tonic-gate void
97*0Sstevel@tonic-gate auto_unblock_others(
98*0Sstevel@tonic-gate 	fnnode_t *fnp,
99*0Sstevel@tonic-gate 	uint_t operation)		/* either MF_INPROG or MF_LOOKUP */
100*0Sstevel@tonic-gate {
101*0Sstevel@tonic-gate 	ASSERT(operation & (MF_INPROG | MF_LOOKUP));
102*0Sstevel@tonic-gate 	fnp->fn_flags &= ~operation;
103*0Sstevel@tonic-gate 	if (fnp->fn_flags & MF_WAITING) {
104*0Sstevel@tonic-gate 		fnp->fn_flags &= ~MF_WAITING;
105*0Sstevel@tonic-gate 		cv_broadcast(&fnp->fn_cv_mount);
106*0Sstevel@tonic-gate 	}
107*0Sstevel@tonic-gate }
108*0Sstevel@tonic-gate 
109*0Sstevel@tonic-gate int
110*0Sstevel@tonic-gate auto_wait4mount(fnnode_t *fnp)
111*0Sstevel@tonic-gate {
112*0Sstevel@tonic-gate 	int error;
113*0Sstevel@tonic-gate 	k_sigset_t smask;
114*0Sstevel@tonic-gate 
115*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_wait4mount: fnp=%p\n", (void *)fnp));
116*0Sstevel@tonic-gate 
117*0Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
118*0Sstevel@tonic-gate 	while (fnp->fn_flags & (MF_INPROG | MF_LOOKUP)) {
119*0Sstevel@tonic-gate 		/*
120*0Sstevel@tonic-gate 		 * There is a mount or a lookup in progress.
121*0Sstevel@tonic-gate 		 */
122*0Sstevel@tonic-gate 		fnp->fn_flags |= MF_WAITING;
123*0Sstevel@tonic-gate 		sigintr(&smask, 1);
124*0Sstevel@tonic-gate 		if (!cv_wait_sig(&fnp->fn_cv_mount, &fnp->fn_lock)) {
125*0Sstevel@tonic-gate 			/*
126*0Sstevel@tonic-gate 			 * Decided not to wait for operation to
127*0Sstevel@tonic-gate 			 * finish after all.
128*0Sstevel@tonic-gate 			 */
129*0Sstevel@tonic-gate 			sigunintr(&smask);
130*0Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
131*0Sstevel@tonic-gate 			return (EINTR);
132*0Sstevel@tonic-gate 		}
133*0Sstevel@tonic-gate 		sigunintr(&smask);
134*0Sstevel@tonic-gate 	}
135*0Sstevel@tonic-gate 	error = fnp->fn_error;
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate 	if (error == EINTR) {
138*0Sstevel@tonic-gate 		/*
139*0Sstevel@tonic-gate 		 * The thread doing the mount got interrupted, we need to
140*0Sstevel@tonic-gate 		 * try again, by returning EAGAIN.
141*0Sstevel@tonic-gate 		 */
142*0Sstevel@tonic-gate 		error = EAGAIN;
143*0Sstevel@tonic-gate 	}
144*0Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_wait4mount: fnp=%p error=%d\n", (void *)fnp,
147*0Sstevel@tonic-gate 	    error));
148*0Sstevel@tonic-gate 	return (error);
149*0Sstevel@tonic-gate }
150*0Sstevel@tonic-gate 
151*0Sstevel@tonic-gate int
152*0Sstevel@tonic-gate auto_lookup_aux(fnnode_t *fnp, char *name, cred_t *cred)
153*0Sstevel@tonic-gate {
154*0Sstevel@tonic-gate 	struct fninfo *fnip;
155*0Sstevel@tonic-gate 	struct linka link;
156*0Sstevel@tonic-gate 	bool_t mountreq = FALSE;
157*0Sstevel@tonic-gate 	int error = 0;
158*0Sstevel@tonic-gate 
159*0Sstevel@tonic-gate 	fnip = vfstofni(fntovn(fnp)->v_vfsp);
160*0Sstevel@tonic-gate 	bzero(&link, sizeof (link));
161*0Sstevel@tonic-gate 	error = auto_lookup_request(fnip, name, &link, cred, TRUE, &mountreq);
162*0Sstevel@tonic-gate 	if (!error) {
163*0Sstevel@tonic-gate 		if (link.link != NULL) {
164*0Sstevel@tonic-gate 			/*
165*0Sstevel@tonic-gate 			 * This node should be a symlink
166*0Sstevel@tonic-gate 			 */
167*0Sstevel@tonic-gate 			error = auto_perform_link(fnp, &link, cred);
168*0Sstevel@tonic-gate 			kmem_free(link.dir, strlen(link.dir) + 1);
169*0Sstevel@tonic-gate 			kmem_free(link.link, strlen(link.link) + 1);
170*0Sstevel@tonic-gate 		} else if (mountreq) {
171*0Sstevel@tonic-gate 			/*
172*0Sstevel@tonic-gate 			 * The automount daemon is requesting a mount,
173*0Sstevel@tonic-gate 			 * implying this entry must be a wildcard match and
174*0Sstevel@tonic-gate 			 * therefore in need of verification that the entry
175*0Sstevel@tonic-gate 			 * exists on the server.
176*0Sstevel@tonic-gate 			 */
177*0Sstevel@tonic-gate 			mutex_enter(&fnp->fn_lock);
178*0Sstevel@tonic-gate 			AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG);
179*0Sstevel@tonic-gate 			fnp->fn_error = 0;
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate 			/*
182*0Sstevel@tonic-gate 			 * Unblock other lookup requests on this node,
183*0Sstevel@tonic-gate 			 * this is needed to let the lookup generated by
184*0Sstevel@tonic-gate 			 * the mount call to complete. The caveat is
185*0Sstevel@tonic-gate 			 * other lookups on this node can also get by,
186*0Sstevel@tonic-gate 			 * i.e., another lookup on this node that occurs
187*0Sstevel@tonic-gate 			 * while this lookup is attempting the mount
188*0Sstevel@tonic-gate 			 * would return a positive result no matter what.
189*0Sstevel@tonic-gate 			 * Therefore two lookups on the this node could
190*0Sstevel@tonic-gate 			 * potentially get disparate results.
191*0Sstevel@tonic-gate 			 */
192*0Sstevel@tonic-gate 			AUTOFS_UNBLOCK_OTHERS(fnp, MF_LOOKUP);
193*0Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
194*0Sstevel@tonic-gate 			/*
195*0Sstevel@tonic-gate 			 * auto_new_mount_thread fires up a new thread which
196*0Sstevel@tonic-gate 			 * calls automountd finishing up the work
197*0Sstevel@tonic-gate 			 */
198*0Sstevel@tonic-gate 			auto_new_mount_thread(fnp, name, cred);
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate 			/*
201*0Sstevel@tonic-gate 			 * At this point, we are simply another thread
202*0Sstevel@tonic-gate 			 * waiting for the mount to complete
203*0Sstevel@tonic-gate 			 */
204*0Sstevel@tonic-gate 			error = auto_wait4mount(fnp);
205*0Sstevel@tonic-gate 			if (error == AUTOFS_SHUTDOWN)
206*0Sstevel@tonic-gate 				error = ENOENT;
207*0Sstevel@tonic-gate 		}
208*0Sstevel@tonic-gate 	}
209*0Sstevel@tonic-gate 
210*0Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
211*0Sstevel@tonic-gate 	fnp->fn_error = error;
212*0Sstevel@tonic-gate 
213*0Sstevel@tonic-gate 	/*
214*0Sstevel@tonic-gate 	 * Notify threads waiting for lookup/mount that
215*0Sstevel@tonic-gate 	 * it's done.
216*0Sstevel@tonic-gate 	 */
217*0Sstevel@tonic-gate 	if (mountreq) {
218*0Sstevel@tonic-gate 		AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
219*0Sstevel@tonic-gate 	} else {
220*0Sstevel@tonic-gate 		AUTOFS_UNBLOCK_OTHERS(fnp, MF_LOOKUP);
221*0Sstevel@tonic-gate 	}
222*0Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
223*0Sstevel@tonic-gate 	return (error);
224*0Sstevel@tonic-gate }
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate /*
227*0Sstevel@tonic-gate  * Starting point for thread to handle mount requests with automountd.
228*0Sstevel@tonic-gate  * XXX auto_mount_thread() is not suspend-safe within the scope of
229*0Sstevel@tonic-gate  * the present model defined for cpr to suspend the system. Calls
230*0Sstevel@tonic-gate  * made by the auto_mount_thread() that have been identified to be unsafe
231*0Sstevel@tonic-gate  * are (1) RPC client handle setup and client calls to automountd which
232*0Sstevel@tonic-gate  * can block deep down in the RPC library, (2) kmem_alloc() calls with the
233*0Sstevel@tonic-gate  * KM_SLEEP flag which can block if memory is low, and (3) VFS_*(), and
234*0Sstevel@tonic-gate  * lookuppnvp() calls which can result in over the wire calls to servers.
235*0Sstevel@tonic-gate  * The thread should be completely reevaluated to make it suspend-safe in
236*0Sstevel@tonic-gate  * case of future updates to the cpr model.
237*0Sstevel@tonic-gate  */
238*0Sstevel@tonic-gate static void
239*0Sstevel@tonic-gate auto_mount_thread(struct autofs_callargs *argsp)
240*0Sstevel@tonic-gate {
241*0Sstevel@tonic-gate 	struct fninfo *fnip;
242*0Sstevel@tonic-gate 	fnnode_t *fnp;
243*0Sstevel@tonic-gate 	vnode_t *vp;
244*0Sstevel@tonic-gate 	char *name;
245*0Sstevel@tonic-gate 	size_t namelen;
246*0Sstevel@tonic-gate 	cred_t *cred;
247*0Sstevel@tonic-gate 	action_list *alp = NULL;
248*0Sstevel@tonic-gate 	int error;
249*0Sstevel@tonic-gate 	callb_cpr_t cprinfo;
250*0Sstevel@tonic-gate 	kmutex_t auto_mount_thread_cpr_lock;
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate 	mutex_init(&auto_mount_thread_cpr_lock, NULL, MUTEX_DEFAULT, NULL);
253*0Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, &auto_mount_thread_cpr_lock, callb_generic_cpr,
254*0Sstevel@tonic-gate 		"auto_mount_thread");
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 	fnp = argsp->fnc_fnp;
257*0Sstevel@tonic-gate 	vp = fntovn(fnp);
258*0Sstevel@tonic-gate 	fnip = vfstofni(vp->v_vfsp);
259*0Sstevel@tonic-gate 	name = argsp->fnc_name;
260*0Sstevel@tonic-gate 	cred = argsp->fnc_cred;
261*0Sstevel@tonic-gate 	ASSERT(crgetzoneid(argsp->fnc_cred) == fnip->fi_zoneid);
262*0Sstevel@tonic-gate 
263*0Sstevel@tonic-gate 	error = auto_mount_request(fnip, name, &alp, cred, TRUE);
264*0Sstevel@tonic-gate 	if (!error)
265*0Sstevel@tonic-gate 		error = auto_perform_actions(fnip, fnp, alp, cred);
266*0Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
267*0Sstevel@tonic-gate 	fnp->fn_error = error;
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate 	/*
270*0Sstevel@tonic-gate 	 * Notify threads waiting for mount that
271*0Sstevel@tonic-gate 	 * it's done.
272*0Sstevel@tonic-gate 	 */
273*0Sstevel@tonic-gate 	AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
274*0Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
275*0Sstevel@tonic-gate 
276*0Sstevel@tonic-gate 	VN_RELE(vp);
277*0Sstevel@tonic-gate 	crfree(argsp->fnc_cred);
278*0Sstevel@tonic-gate 	namelen = strlen(argsp->fnc_name) + 1;
279*0Sstevel@tonic-gate 	kmem_free(argsp->fnc_name, namelen);
280*0Sstevel@tonic-gate 	kmem_free(argsp, sizeof (*argsp));
281*0Sstevel@tonic-gate 
282*0Sstevel@tonic-gate 	mutex_enter(&auto_mount_thread_cpr_lock);
283*0Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cprinfo);
284*0Sstevel@tonic-gate 	mutex_destroy(&auto_mount_thread_cpr_lock);
285*0Sstevel@tonic-gate 	zthread_exit();
286*0Sstevel@tonic-gate 	/* NOTREACHED */
287*0Sstevel@tonic-gate }
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate static int autofs_thr_success = 0;
290*0Sstevel@tonic-gate 
291*0Sstevel@tonic-gate /*
292*0Sstevel@tonic-gate  * Creates new thread which calls auto_mount_thread which does
293*0Sstevel@tonic-gate  * the bulk of the work calling automountd, via 'auto_perform_actions'.
294*0Sstevel@tonic-gate  */
295*0Sstevel@tonic-gate void
296*0Sstevel@tonic-gate auto_new_mount_thread(fnnode_t *fnp, char *name, cred_t *cred)
297*0Sstevel@tonic-gate {
298*0Sstevel@tonic-gate 	struct autofs_callargs *argsp;
299*0Sstevel@tonic-gate 
300*0Sstevel@tonic-gate 	argsp = kmem_alloc(sizeof (*argsp), KM_SLEEP);
301*0Sstevel@tonic-gate 	VN_HOLD(fntovn(fnp));
302*0Sstevel@tonic-gate 	argsp->fnc_fnp = fnp;
303*0Sstevel@tonic-gate 	argsp->fnc_name = kmem_alloc(strlen(name) + 1, KM_SLEEP);
304*0Sstevel@tonic-gate 	(void) strcpy(argsp->fnc_name, name);
305*0Sstevel@tonic-gate 	argsp->fnc_origin = curthread;
306*0Sstevel@tonic-gate 	crhold(cred);
307*0Sstevel@tonic-gate 	argsp->fnc_cred = cred;
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate 	(void) zthread_create(NULL, 0, auto_mount_thread, argsp, 0,
310*0Sstevel@tonic-gate 	    minclsyspri);
311*0Sstevel@tonic-gate 	autofs_thr_success++;
312*0Sstevel@tonic-gate }
313*0Sstevel@tonic-gate 
314*0Sstevel@tonic-gate int
315*0Sstevel@tonic-gate auto_calldaemon(
316*0Sstevel@tonic-gate 	fninfo_t *fnip,
317*0Sstevel@tonic-gate 	rpcproc_t which,
318*0Sstevel@tonic-gate 	xdrproc_t xdrargs,
319*0Sstevel@tonic-gate 	void *argsp,
320*0Sstevel@tonic-gate 	xdrproc_t xdrres,
321*0Sstevel@tonic-gate 	void *resp,
322*0Sstevel@tonic-gate 	cred_t *cred,
323*0Sstevel@tonic-gate 	bool_t hard)				/* retry forever? */
324*0Sstevel@tonic-gate {
325*0Sstevel@tonic-gate 	CLIENT *client;
326*0Sstevel@tonic-gate 	enum clnt_stat status;
327*0Sstevel@tonic-gate 	struct rpc_err rpcerr;
328*0Sstevel@tonic-gate 	struct timeval wait;
329*0Sstevel@tonic-gate 	bool_t tryagain;
330*0Sstevel@tonic-gate 	int error = 0;
331*0Sstevel@tonic-gate 	k_sigset_t smask;
332*0Sstevel@tonic-gate 	struct autofs_globals *fngp = vntofn(fnip->fi_rootvp)->fn_globals;
333*0Sstevel@tonic-gate 
334*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_calldaemon\n"));
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate 	error = clnt_tli_kcreate(&fnip->fi_knconf, &fnip->fi_addr,
337*0Sstevel@tonic-gate 	    AUTOFS_PROG, AUTOFS_VERS, 0, INT_MAX, cred, &client);
338*0Sstevel@tonic-gate 
339*0Sstevel@tonic-gate 	if (error) {
340*0Sstevel@tonic-gate 		auto_log(fngp, CE_WARN, "autofs: clnt_tli_kcreate: error %d",
341*0Sstevel@tonic-gate 		    error);
342*0Sstevel@tonic-gate 		goto done;
343*0Sstevel@tonic-gate 	}
344*0Sstevel@tonic-gate 
345*0Sstevel@tonic-gate 	/*
346*0Sstevel@tonic-gate 	 * Release the old authentication handle.  It was probably
347*0Sstevel@tonic-gate 	 * AUTH_UNIX.
348*0Sstevel@tonic-gate 	 */
349*0Sstevel@tonic-gate 	auth_destroy(client->cl_auth);
350*0Sstevel@tonic-gate 
351*0Sstevel@tonic-gate 	/*
352*0Sstevel@tonic-gate 	 * Create a new authentication handle for AUTH_LOOPBACK.  This
353*0Sstevel@tonic-gate 	 * will allow us to correctly handle the entire groups list.
354*0Sstevel@tonic-gate 	 */
355*0Sstevel@tonic-gate 	client->cl_auth = authloopback_create();
356*0Sstevel@tonic-gate 	if (client->cl_auth == NULL) {
357*0Sstevel@tonic-gate 		clnt_destroy(client);
358*0Sstevel@tonic-gate 		error = EINTR;
359*0Sstevel@tonic-gate 		auto_log(fngp, CE_WARN,
360*0Sstevel@tonic-gate 		    "autofs: authloopback_create: error %d", error);
361*0Sstevel@tonic-gate 		goto done;
362*0Sstevel@tonic-gate 	}
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate 	wait.tv_sec = fnip->fi_rpc_to;
365*0Sstevel@tonic-gate 	wait.tv_usec = 0;
366*0Sstevel@tonic-gate 	do {
367*0Sstevel@tonic-gate 		tryagain = FALSE;
368*0Sstevel@tonic-gate 		error = 0;
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate 		/*
371*0Sstevel@tonic-gate 		 * Mask out all signals except SIGHUP, SIGINT, SIGQUIT
372*0Sstevel@tonic-gate 		 * and SIGTERM. (Preserving the existing masks)
373*0Sstevel@tonic-gate 		 */
374*0Sstevel@tonic-gate 		sigintr(&smask, 1);
375*0Sstevel@tonic-gate 
376*0Sstevel@tonic-gate 		status = CLNT_CALL(client, which, xdrargs, argsp,
377*0Sstevel@tonic-gate 		    xdrres, resp, wait);
378*0Sstevel@tonic-gate 
379*0Sstevel@tonic-gate 		/*
380*0Sstevel@tonic-gate 		 * Restore original signal mask
381*0Sstevel@tonic-gate 		 */
382*0Sstevel@tonic-gate 		sigunintr(&smask);
383*0Sstevel@tonic-gate 
384*0Sstevel@tonic-gate 		switch (status) {
385*0Sstevel@tonic-gate 		case RPC_SUCCESS:
386*0Sstevel@tonic-gate 			break;
387*0Sstevel@tonic-gate 
388*0Sstevel@tonic-gate 		case RPC_INTR:
389*0Sstevel@tonic-gate 			error = EINTR;
390*0Sstevel@tonic-gate 			break;
391*0Sstevel@tonic-gate 
392*0Sstevel@tonic-gate 		case RPC_TIMEDOUT:
393*0Sstevel@tonic-gate 			tryagain = TRUE;
394*0Sstevel@tonic-gate 			error = ETIMEDOUT;
395*0Sstevel@tonic-gate 			break;
396*0Sstevel@tonic-gate 
397*0Sstevel@tonic-gate 		case RPC_CANTCONNECT:
398*0Sstevel@tonic-gate 		case RPC_CANTCREATESTREAM:
399*0Sstevel@tonic-gate 			/*
400*0Sstevel@tonic-gate 			 * The connection could not be established
401*0Sstevel@tonic-gate 			 */
402*0Sstevel@tonic-gate 			/* fall thru */
403*0Sstevel@tonic-gate 		case RPC_XPRTFAILED:
404*0Sstevel@tonic-gate 			/*
405*0Sstevel@tonic-gate 			 * The connection could not be established or
406*0Sstevel@tonic-gate 			 * was dropped, we differentiate between the two
407*0Sstevel@tonic-gate 			 * conditions by calling CLNT_GETERR and look at
408*0Sstevel@tonic-gate 			 * rpcerror.re_errno.
409*0Sstevel@tonic-gate 			 * If rpcerr.re_errno == ECONNREFUSED, then the
410*0Sstevel@tonic-gate 			 * connection could not be established at all.
411*0Sstevel@tonic-gate 			 */
412*0Sstevel@tonic-gate 			error = ECONNREFUSED;
413*0Sstevel@tonic-gate 			if (status == RPC_XPRTFAILED) {
414*0Sstevel@tonic-gate 				CLNT_GETERR(client, &rpcerr);
415*0Sstevel@tonic-gate 				if (rpcerr.re_errno != ECONNREFUSED) {
416*0Sstevel@tonic-gate 					/*
417*0Sstevel@tonic-gate 					 * The connection was dropped, return
418*0Sstevel@tonic-gate 					 * to the caller if hard is not set.
419*0Sstevel@tonic-gate 					 * It is the responsability of the
420*0Sstevel@tonic-gate 					 * caller to retry the call if
421*0Sstevel@tonic-gate 					 * appropriate.
422*0Sstevel@tonic-gate 					 */
423*0Sstevel@tonic-gate 					error = ECONNRESET;
424*0Sstevel@tonic-gate 				}
425*0Sstevel@tonic-gate 			}
426*0Sstevel@tonic-gate 			/*
427*0Sstevel@tonic-gate 			 * We know that the current thread is doing work on
428*0Sstevel@tonic-gate 			 * behalf of its own zone, so it's ok to use
429*0Sstevel@tonic-gate 			 * curproc->p_zone.
430*0Sstevel@tonic-gate 			 */
431*0Sstevel@tonic-gate 			ASSERT(fngp->fng_zoneid == getzoneid());
432*0Sstevel@tonic-gate 			if (zone_status_get(curproc->p_zone) >=
433*0Sstevel@tonic-gate 			    ZONE_IS_SHUTTING_DOWN) {
434*0Sstevel@tonic-gate 				/*
435*0Sstevel@tonic-gate 				 * There's no point in trying to talk to
436*0Sstevel@tonic-gate 				 * automountd.  Plus, zone_shutdown() is
437*0Sstevel@tonic-gate 				 * waiting for us.
438*0Sstevel@tonic-gate 				 */
439*0Sstevel@tonic-gate 				tryagain = FALSE;
440*0Sstevel@tonic-gate 				break;
441*0Sstevel@tonic-gate 			}
442*0Sstevel@tonic-gate 			tryagain = hard;
443*0Sstevel@tonic-gate 			if (!fngp->fng_printed_not_running_msg) {
444*0Sstevel@tonic-gate 				if (tryagain) {
445*0Sstevel@tonic-gate 					fngp->fng_printed_not_running_msg = 1;
446*0Sstevel@tonic-gate 					zprintf(fngp->fng_zoneid,
447*0Sstevel@tonic-gate 					"automountd not running, retrying\n");
448*0Sstevel@tonic-gate 				}
449*0Sstevel@tonic-gate 			}
450*0Sstevel@tonic-gate 			break;
451*0Sstevel@tonic-gate 
452*0Sstevel@tonic-gate 		default:
453*0Sstevel@tonic-gate 			auto_log(fngp, CE_WARN, "autofs: %s",
454*0Sstevel@tonic-gate 			    clnt_sperrno(status));
455*0Sstevel@tonic-gate 			error = ENOENT;
456*0Sstevel@tonic-gate 			break;
457*0Sstevel@tonic-gate 		}
458*0Sstevel@tonic-gate 	} while (tryagain);
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 	if (status == RPC_SUCCESS) {
461*0Sstevel@tonic-gate 		if (fngp->fng_printed_not_running_msg == 1) {
462*0Sstevel@tonic-gate 			fngp->fng_printed_not_running_msg = 0;
463*0Sstevel@tonic-gate 			zprintf(fngp->fng_zoneid, "automountd OK\n");
464*0Sstevel@tonic-gate 		}
465*0Sstevel@tonic-gate 	}
466*0Sstevel@tonic-gate 	auth_destroy(client->cl_auth);
467*0Sstevel@tonic-gate 	clnt_destroy(client);
468*0Sstevel@tonic-gate 
469*0Sstevel@tonic-gate done:
470*0Sstevel@tonic-gate 	ASSERT(status == RPC_SUCCESS || error != 0);
471*0Sstevel@tonic-gate 
472*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_calldaemon error=%d\n", error));
473*0Sstevel@tonic-gate 	return (error);
474*0Sstevel@tonic-gate }
475*0Sstevel@tonic-gate 
476*0Sstevel@tonic-gate static int
477*0Sstevel@tonic-gate auto_null_request(fninfo_t *fnip, cred_t *cred, bool_t hard)
478*0Sstevel@tonic-gate {
479*0Sstevel@tonic-gate 	int error;
480*0Sstevel@tonic-gate 
481*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tauto_null_request\n"));
482*0Sstevel@tonic-gate 
483*0Sstevel@tonic-gate 	error = auto_calldaemon(fnip, NULLPROC, xdr_void, NULL, xdr_void, NULL,
484*0Sstevel@tonic-gate 	    cred, hard);
485*0Sstevel@tonic-gate 
486*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tauto_null_request: error=%d\n", error));
487*0Sstevel@tonic-gate 	return (error);
488*0Sstevel@tonic-gate }
489*0Sstevel@tonic-gate 
490*0Sstevel@tonic-gate static int
491*0Sstevel@tonic-gate auto_lookup_request(
492*0Sstevel@tonic-gate 	fninfo_t *fnip,
493*0Sstevel@tonic-gate 	char *key,
494*0Sstevel@tonic-gate 	struct linka *lnp,
495*0Sstevel@tonic-gate 	cred_t *cred,
496*0Sstevel@tonic-gate 	bool_t hard,
497*0Sstevel@tonic-gate 	bool_t *mountreq)
498*0Sstevel@tonic-gate {
499*0Sstevel@tonic-gate 	int error;
500*0Sstevel@tonic-gate 	struct autofs_globals *fngp;
501*0Sstevel@tonic-gate 	struct autofs_lookupargs request;
502*0Sstevel@tonic-gate 	struct autofs_lookupres result;
503*0Sstevel@tonic-gate 	struct linka *p;
504*0Sstevel@tonic-gate 
505*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_lookup_request: path=%s name=%s\n",
506*0Sstevel@tonic-gate 	    fnip->fi_path, key));
507*0Sstevel@tonic-gate 
508*0Sstevel@tonic-gate 	fngp = vntofn(fnip->fi_rootvp)->fn_globals;
509*0Sstevel@tonic-gate 	request.map = fnip->fi_map;
510*0Sstevel@tonic-gate 	request.path = fnip->fi_path;
511*0Sstevel@tonic-gate 
512*0Sstevel@tonic-gate 	if (fnip->fi_flags & MF_DIRECT)
513*0Sstevel@tonic-gate 		request.name = fnip->fi_key;
514*0Sstevel@tonic-gate 	else
515*0Sstevel@tonic-gate 		request.name = key;
516*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_lookup_request: using key=%s\n", request.name));
517*0Sstevel@tonic-gate 
518*0Sstevel@tonic-gate 	request.subdir = fnip->fi_subdir;
519*0Sstevel@tonic-gate 	request.opts = fnip->fi_opts;
520*0Sstevel@tonic-gate 	request.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE;
521*0Sstevel@tonic-gate 
522*0Sstevel@tonic-gate 	bzero(&result, sizeof (result));
523*0Sstevel@tonic-gate 	error = auto_calldaemon(fnip, AUTOFS_LOOKUP,
524*0Sstevel@tonic-gate 	    xdr_autofs_lookupargs, &request,
525*0Sstevel@tonic-gate 	    xdr_autofs_lookupres, &result,
526*0Sstevel@tonic-gate 	    cred, hard);
527*0Sstevel@tonic-gate 	if (!error) {
528*0Sstevel@tonic-gate 		fngp->fng_verbose = result.lu_verbose;
529*0Sstevel@tonic-gate 		switch (result.lu_res) {
530*0Sstevel@tonic-gate 		case AUTOFS_OK:
531*0Sstevel@tonic-gate 			switch (result.lu_type.action) {
532*0Sstevel@tonic-gate 			case AUTOFS_MOUNT_RQ:
533*0Sstevel@tonic-gate 				lnp->link = NULL;
534*0Sstevel@tonic-gate 				lnp->dir = NULL;
535*0Sstevel@tonic-gate 				*mountreq = TRUE;
536*0Sstevel@tonic-gate 				break;
537*0Sstevel@tonic-gate 			case AUTOFS_LINK_RQ:
538*0Sstevel@tonic-gate 				p =
539*0Sstevel@tonic-gate 				&result.lu_type.lookup_result_type_u.lt_linka;
540*0Sstevel@tonic-gate 				lnp->dir = kmem_alloc(strlen(p->dir) + 1,
541*0Sstevel@tonic-gate 				    KM_SLEEP);
542*0Sstevel@tonic-gate 				(void) strcpy(lnp->dir, p->dir);
543*0Sstevel@tonic-gate 				lnp->link = kmem_alloc(strlen(p->link) + 1,
544*0Sstevel@tonic-gate 				    KM_SLEEP);
545*0Sstevel@tonic-gate 				(void) strcpy(lnp->link, p->link);
546*0Sstevel@tonic-gate 				break;
547*0Sstevel@tonic-gate 			case AUTOFS_NONE:
548*0Sstevel@tonic-gate 				lnp->link = NULL;
549*0Sstevel@tonic-gate 				lnp->dir = NULL;
550*0Sstevel@tonic-gate 				break;
551*0Sstevel@tonic-gate 			default:
552*0Sstevel@tonic-gate 				auto_log(fngp, CE_WARN,
553*0Sstevel@tonic-gate 				    "auto_lookup_request: bad action type %d",
554*0Sstevel@tonic-gate 				    result.lu_res);
555*0Sstevel@tonic-gate 				error = ENOENT;
556*0Sstevel@tonic-gate 			}
557*0Sstevel@tonic-gate 			break;
558*0Sstevel@tonic-gate 		case AUTOFS_NOENT:
559*0Sstevel@tonic-gate 			error = ENOENT;
560*0Sstevel@tonic-gate 			break;
561*0Sstevel@tonic-gate 		default:
562*0Sstevel@tonic-gate 			error = ENOENT;
563*0Sstevel@tonic-gate 			auto_log(fngp, CE_WARN,
564*0Sstevel@tonic-gate 			    "auto_lookup_request: unknown result: %d",
565*0Sstevel@tonic-gate 			    result.lu_res);
566*0Sstevel@tonic-gate 			break;
567*0Sstevel@tonic-gate 		}
568*0Sstevel@tonic-gate 	}
569*0Sstevel@tonic-gate 
570*0Sstevel@tonic-gate done:
571*0Sstevel@tonic-gate 	xdr_free(xdr_autofs_lookupres, (char *)&result);
572*0Sstevel@tonic-gate 
573*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_lookup_request: path=%s name=%s error=%d\n",
574*0Sstevel@tonic-gate 	    fnip->fi_path, key, error));
575*0Sstevel@tonic-gate 	return (error);
576*0Sstevel@tonic-gate }
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate static int
579*0Sstevel@tonic-gate auto_mount_request(
580*0Sstevel@tonic-gate 	fninfo_t *fnip,
581*0Sstevel@tonic-gate 	char *key,
582*0Sstevel@tonic-gate 	action_list **alpp,
583*0Sstevel@tonic-gate 	cred_t *cred,
584*0Sstevel@tonic-gate 	bool_t hard)
585*0Sstevel@tonic-gate {
586*0Sstevel@tonic-gate 	int error;
587*0Sstevel@tonic-gate 	struct autofs_globals *fngp;
588*0Sstevel@tonic-gate 	struct autofs_lookupargs request;
589*0Sstevel@tonic-gate 	struct autofs_mountres *result;
590*0Sstevel@tonic-gate 
591*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_mount_request: path=%s name=%s\n",
592*0Sstevel@tonic-gate 	    fnip->fi_path, key));
593*0Sstevel@tonic-gate 
594*0Sstevel@tonic-gate 	fngp = vntofn(fnip->fi_rootvp)->fn_globals;
595*0Sstevel@tonic-gate 	request.map = fnip->fi_map;
596*0Sstevel@tonic-gate 	request.path = fnip->fi_path;
597*0Sstevel@tonic-gate 
598*0Sstevel@tonic-gate 	if (fnip->fi_flags & MF_DIRECT)
599*0Sstevel@tonic-gate 		request.name = fnip->fi_key;
600*0Sstevel@tonic-gate 	else
601*0Sstevel@tonic-gate 		request.name = key;
602*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_mount_request: using key=%s\n", request.name));
603*0Sstevel@tonic-gate 
604*0Sstevel@tonic-gate 	request.subdir = fnip->fi_subdir;
605*0Sstevel@tonic-gate 	request.opts = fnip->fi_opts;
606*0Sstevel@tonic-gate 	request.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE;
607*0Sstevel@tonic-gate 
608*0Sstevel@tonic-gate 	*alpp = NULL;
609*0Sstevel@tonic-gate 	result = kmem_zalloc(sizeof (*result), KM_SLEEP);
610*0Sstevel@tonic-gate 	error = auto_calldaemon(fnip, AUTOFS_MOUNT,
611*0Sstevel@tonic-gate 	    xdr_autofs_lookupargs, &request,
612*0Sstevel@tonic-gate 	    xdr_autofs_mountres, result,
613*0Sstevel@tonic-gate 	    cred, hard);
614*0Sstevel@tonic-gate 	if (!error) {
615*0Sstevel@tonic-gate 		fngp->fng_verbose = result->mr_verbose;
616*0Sstevel@tonic-gate 		switch (result->mr_type.status) {
617*0Sstevel@tonic-gate 		case AUTOFS_ACTION:
618*0Sstevel@tonic-gate 			error = 0;
619*0Sstevel@tonic-gate 			/*
620*0Sstevel@tonic-gate 			 * Save the action list since it is used by
621*0Sstevel@tonic-gate 			 * the caller. We NULL the action list pointer
622*0Sstevel@tonic-gate 			 * in 'result' so that xdr_free() will not free
623*0Sstevel@tonic-gate 			 * the list.
624*0Sstevel@tonic-gate 			 */
625*0Sstevel@tonic-gate 			*alpp = result->mr_type.mount_result_type_u.list;
626*0Sstevel@tonic-gate 			result->mr_type.mount_result_type_u.list = NULL;
627*0Sstevel@tonic-gate 			break;
628*0Sstevel@tonic-gate 		case AUTOFS_DONE:
629*0Sstevel@tonic-gate 			error = result->mr_type.mount_result_type_u.error;
630*0Sstevel@tonic-gate 			break;
631*0Sstevel@tonic-gate 		default:
632*0Sstevel@tonic-gate 			error = ENOENT;
633*0Sstevel@tonic-gate 			auto_log(fngp, CE_WARN,
634*0Sstevel@tonic-gate 			    "auto_mount_request: unknown status %d",
635*0Sstevel@tonic-gate 			    result->mr_type.status);
636*0Sstevel@tonic-gate 			break;
637*0Sstevel@tonic-gate 		}
638*0Sstevel@tonic-gate 	}
639*0Sstevel@tonic-gate 
640*0Sstevel@tonic-gate 	xdr_free(xdr_autofs_mountres, (char *)result);
641*0Sstevel@tonic-gate 	kmem_free(result, sizeof (*result));
642*0Sstevel@tonic-gate 
643*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_mount_request: path=%s name=%s error=%d\n",
644*0Sstevel@tonic-gate 	    fnip->fi_path, key, error));
645*0Sstevel@tonic-gate 	return (error);
646*0Sstevel@tonic-gate }
647*0Sstevel@tonic-gate 
648*0Sstevel@tonic-gate 
649*0Sstevel@tonic-gate static int
650*0Sstevel@tonic-gate auto_send_unmount_request(
651*0Sstevel@tonic-gate 	fninfo_t *fnip,
652*0Sstevel@tonic-gate 	umntrequest *ul,
653*0Sstevel@tonic-gate 	cred_t *cred,
654*0Sstevel@tonic-gate 	bool_t hard)
655*0Sstevel@tonic-gate {
656*0Sstevel@tonic-gate 	int error;
657*0Sstevel@tonic-gate 	umntres result;
658*0Sstevel@tonic-gate 
659*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tauto_send_unmount_request: fstype=%s "
660*0Sstevel@tonic-gate 			" mntpnt=%s\n", ul->fstype, ul->mntpnt));
661*0Sstevel@tonic-gate 
662*0Sstevel@tonic-gate 	error = auto_calldaemon(fnip, AUTOFS_UNMOUNT,
663*0Sstevel@tonic-gate 	    xdr_umntrequest, ul,
664*0Sstevel@tonic-gate 	    xdr_umntres, &result,
665*0Sstevel@tonic-gate 	    cred, hard);
666*0Sstevel@tonic-gate 	if (!error)
667*0Sstevel@tonic-gate 		error = result.status;
668*0Sstevel@tonic-gate 
669*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tauto_send_unmount_request: error=%d\n", error));
670*0Sstevel@tonic-gate 
671*0Sstevel@tonic-gate 	return (error);
672*0Sstevel@tonic-gate }
673*0Sstevel@tonic-gate 
674*0Sstevel@tonic-gate static int
675*0Sstevel@tonic-gate auto_perform_link(fnnode_t *fnp, struct linka *linkp, cred_t *cred)
676*0Sstevel@tonic-gate {
677*0Sstevel@tonic-gate 	vnode_t *vp;
678*0Sstevel@tonic-gate 	size_t len;
679*0Sstevel@tonic-gate 	char *tmp;
680*0Sstevel@tonic-gate 
681*0Sstevel@tonic-gate 	AUTOFS_DPRINT((3, "auto_perform_link: fnp=%p dir=%s link=%s\n",
682*0Sstevel@tonic-gate 	    (void *)fnp, linkp->dir, linkp->link));
683*0Sstevel@tonic-gate 
684*0Sstevel@tonic-gate 	len = strlen(linkp->link) + 1;		/* include '\0' */
685*0Sstevel@tonic-gate 	tmp = kmem_zalloc(len, KM_SLEEP);
686*0Sstevel@tonic-gate 	(void) kcopy(linkp->link, tmp, len);
687*0Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
688*0Sstevel@tonic-gate 	fnp->fn_symlink = tmp;
689*0Sstevel@tonic-gate 	fnp->fn_symlinklen = (uint_t)len;
690*0Sstevel@tonic-gate 	fnp->fn_flags |= MF_THISUID_MATCH_RQD;
691*0Sstevel@tonic-gate 	crhold(cred);
692*0Sstevel@tonic-gate 	fnp->fn_cred = cred;
693*0Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
694*0Sstevel@tonic-gate 
695*0Sstevel@tonic-gate 	vp = fntovn(fnp);
696*0Sstevel@tonic-gate 	vp->v_type = VLNK;
697*0Sstevel@tonic-gate 
698*0Sstevel@tonic-gate 	return (0);
699*0Sstevel@tonic-gate }
700*0Sstevel@tonic-gate 
701*0Sstevel@tonic-gate static boolean_t
702*0Sstevel@tonic-gate auto_invalid_action(fninfo_t *dfnip, fnnode_t *dfnp, action_list *p)
703*0Sstevel@tonic-gate {
704*0Sstevel@tonic-gate 	struct mounta *m;
705*0Sstevel@tonic-gate 	struct autofs_args *argsp;
706*0Sstevel@tonic-gate 	vnode_t *dvp;
707*0Sstevel@tonic-gate 	char buff[AUTOFS_MAXPATHLEN];
708*0Sstevel@tonic-gate 	size_t len;
709*0Sstevel@tonic-gate 	struct autofs_globals *fngp;
710*0Sstevel@tonic-gate 
711*0Sstevel@tonic-gate 	fngp = dfnp->fn_globals;
712*0Sstevel@tonic-gate 	dvp = fntovn(dfnp);
713*0Sstevel@tonic-gate 	/*
714*0Sstevel@tonic-gate 	 * Before we go any further, this better be a mount request.
715*0Sstevel@tonic-gate 	 */
716*0Sstevel@tonic-gate 	if (p->action.action != AUTOFS_MOUNT_RQ)
717*0Sstevel@tonic-gate 		return (B_TRUE);
718*0Sstevel@tonic-gate 	m = &p->action.action_list_entry_u.mounta;
719*0Sstevel@tonic-gate 	/*
720*0Sstevel@tonic-gate 	 * Make sure we aren't geting passed NULL values or a "dir" that
721*0Sstevel@tonic-gate 	 * isn't "." and doesn't begin with "./".
722*0Sstevel@tonic-gate 	 *
723*0Sstevel@tonic-gate 	 * We also only want to perform autofs mounts, so make sure
724*0Sstevel@tonic-gate 	 * no-one is trying to trick us into doing anything else.
725*0Sstevel@tonic-gate 	 */
726*0Sstevel@tonic-gate 	if (m->spec == NULL || m->dir == NULL || m->dir[0] != '.' ||
727*0Sstevel@tonic-gate 	    (m->dir[1] != '/' && m->dir[1] != '\0') ||
728*0Sstevel@tonic-gate 	    m->fstype == NULL || strcmp(m->fstype, "autofs") != 0 ||
729*0Sstevel@tonic-gate 	    m->dataptr == NULL || m->datalen != sizeof (struct autofs_args) ||
730*0Sstevel@tonic-gate 	    m->optptr == NULL)
731*0Sstevel@tonic-gate 		return (B_TRUE);
732*0Sstevel@tonic-gate 	/*
733*0Sstevel@tonic-gate 	 * We also don't like ".."s in the pathname.  Symlinks are
734*0Sstevel@tonic-gate 	 * handled by the fact that we'll use NOFOLLOW when we do
735*0Sstevel@tonic-gate 	 * lookup()s.
736*0Sstevel@tonic-gate 	 */
737*0Sstevel@tonic-gate 	if (strstr(m->dir, "/../") != NULL ||
738*0Sstevel@tonic-gate 	    (len = strlen(m->dir)) > sizeof ("/..") - 1 &&
739*0Sstevel@tonic-gate 	    m->dir[len] == '.' && m->dir[len - 1] == '.' &&
740*0Sstevel@tonic-gate 	    m->dir[len - 2] == '/')
741*0Sstevel@tonic-gate 		return (B_TRUE);
742*0Sstevel@tonic-gate 	argsp = (struct autofs_args *)m->dataptr;
743*0Sstevel@tonic-gate 	/*
744*0Sstevel@tonic-gate 	 * We don't want NULL values here either.
745*0Sstevel@tonic-gate 	 */
746*0Sstevel@tonic-gate 	if (argsp->addr.buf == NULL || argsp->path == NULL ||
747*0Sstevel@tonic-gate 	    argsp->opts == NULL || argsp->map == NULL || argsp->subdir == NULL)
748*0Sstevel@tonic-gate 		return (B_TRUE);
749*0Sstevel@tonic-gate 	/*
750*0Sstevel@tonic-gate 	 * We know what the claimed pathname *should* look like:
751*0Sstevel@tonic-gate 	 *
752*0Sstevel@tonic-gate 	 * If the parent (dfnp) is a mount point (VROOT), then
753*0Sstevel@tonic-gate 	 * the path should be (dfnip->fi_path + m->dir).
754*0Sstevel@tonic-gate 	 *
755*0Sstevel@tonic-gate 	 * Else, we know we're only two levels deep, so we use
756*0Sstevel@tonic-gate 	 * (dfnip->fi_path + dfnp->fn_name + m->dir).
757*0Sstevel@tonic-gate 	 *
758*0Sstevel@tonic-gate 	 * Furthermore, "." only makes sense if dfnp is a
759*0Sstevel@tonic-gate 	 * trigger node.
760*0Sstevel@tonic-gate 	 *
761*0Sstevel@tonic-gate 	 * At this point it seems like the passed-in path is
762*0Sstevel@tonic-gate 	 * redundant.
763*0Sstevel@tonic-gate 	 */
764*0Sstevel@tonic-gate 	if (dvp->v_flag & VROOT) {
765*0Sstevel@tonic-gate 		if (m->dir[1] == '\0' && !(dfnp->fn_flags & MF_TRIGGER))
766*0Sstevel@tonic-gate 			return (B_TRUE);
767*0Sstevel@tonic-gate 		(void) snprintf(buff, sizeof (buff), "%s%s",
768*0Sstevel@tonic-gate 		    dfnip->fi_path, m->dir + 1);
769*0Sstevel@tonic-gate 	} else {
770*0Sstevel@tonic-gate 		(void) snprintf(buff, sizeof (buff), "%s/%s%s",
771*0Sstevel@tonic-gate 		    dfnip->fi_path, dfnp->fn_name, m->dir + 1);
772*0Sstevel@tonic-gate 	}
773*0Sstevel@tonic-gate 	if (strcmp(argsp->path, buff) != 0) {
774*0Sstevel@tonic-gate 		auto_log(fngp, CE_WARN, "autofs: expected path of '%s', "
775*0Sstevel@tonic-gate 		    "got '%s' instead.", buff, argsp->path);
776*0Sstevel@tonic-gate 		return (B_TRUE);
777*0Sstevel@tonic-gate 	}
778*0Sstevel@tonic-gate 	return (B_FALSE); /* looks OK */
779*0Sstevel@tonic-gate }
780*0Sstevel@tonic-gate 
781*0Sstevel@tonic-gate static int
782*0Sstevel@tonic-gate auto_perform_actions(
783*0Sstevel@tonic-gate 	fninfo_t *dfnip,
784*0Sstevel@tonic-gate 	fnnode_t *dfnp,
785*0Sstevel@tonic-gate 	action_list *alp,
786*0Sstevel@tonic-gate 	cred_t *cred)	/* Credentials of the caller */
787*0Sstevel@tonic-gate {
788*0Sstevel@tonic-gate 	action_list *p;
789*0Sstevel@tonic-gate 	struct mounta *m, margs;
790*0Sstevel@tonic-gate 	struct autofs_args *argsp;
791*0Sstevel@tonic-gate 	int error, success = 0;
792*0Sstevel@tonic-gate 	vnode_t *mvp, *dvp, *newvp;
793*0Sstevel@tonic-gate 	fnnode_t *newfnp, *mfnp;
794*0Sstevel@tonic-gate 	int auto_mount = 0;
795*0Sstevel@tonic-gate 	int save_triggers = 0;		/* set when we need to save at least */
796*0Sstevel@tonic-gate 					/* one trigger node */
797*0Sstevel@tonic-gate 	int update_times = 0;
798*0Sstevel@tonic-gate 	char *mntpnt;
799*0Sstevel@tonic-gate 	char buff[AUTOFS_MAXPATHLEN];
800*0Sstevel@tonic-gate 	timestruc_t now;
801*0Sstevel@tonic-gate 	struct autofs_globals *fngp;
802*0Sstevel@tonic-gate 	cred_t *zcred;	/* kcred-like credentials limited by our zone */
803*0Sstevel@tonic-gate 
804*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_perform_actions: alp=%p\n", (void *)alp));
805*0Sstevel@tonic-gate 
806*0Sstevel@tonic-gate 	fngp = dfnp->fn_globals;
807*0Sstevel@tonic-gate 	dvp = fntovn(dfnp);
808*0Sstevel@tonic-gate 
809*0Sstevel@tonic-gate 	/*
810*0Sstevel@tonic-gate 	 * As automountd running in a zone may be compromised, and this may be
811*0Sstevel@tonic-gate 	 * an attack, we can't trust everything passed in by automountd, and we
812*0Sstevel@tonic-gate 	 * need to do argument verification.  We'll issue a warning and drop
813*0Sstevel@tonic-gate 	 * the request if it doesn't seem right.
814*0Sstevel@tonic-gate 	 */
815*0Sstevel@tonic-gate 	for (p = alp; p != NULL; p = p->next) {
816*0Sstevel@tonic-gate 		if (auto_invalid_action(dfnip, dfnp, p)) {
817*0Sstevel@tonic-gate 			/*
818*0Sstevel@tonic-gate 			 * This warning should be sent to the global zone,
819*0Sstevel@tonic-gate 			 * since presumably the zone administrator is the same
820*0Sstevel@tonic-gate 			 * as the attacker.
821*0Sstevel@tonic-gate 			 */
822*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "autofs: invalid action list received "
823*0Sstevel@tonic-gate 			    "by automountd in zone %s.",
824*0Sstevel@tonic-gate 			    curproc->p_zone->zone_name);
825*0Sstevel@tonic-gate 			/*
826*0Sstevel@tonic-gate 			 * This conversation is over.
827*0Sstevel@tonic-gate 			 */
828*0Sstevel@tonic-gate 			xdr_free(xdr_action_list, (char *)alp);
829*0Sstevel@tonic-gate 			return (EINVAL);
830*0Sstevel@tonic-gate 		}
831*0Sstevel@tonic-gate 	}
832*0Sstevel@tonic-gate 
833*0Sstevel@tonic-gate 	zcred = zone_get_kcred(getzoneid());
834*0Sstevel@tonic-gate 	ASSERT(zcred != NULL);
835*0Sstevel@tonic-gate 
836*0Sstevel@tonic-gate 	if (vn_mountedvfs(dvp) != NULL) {
837*0Sstevel@tonic-gate 		/*
838*0Sstevel@tonic-gate 		 * The daemon successfully mounted a filesystem
839*0Sstevel@tonic-gate 		 * on the AUTOFS root node.
840*0Sstevel@tonic-gate 		 */
841*0Sstevel@tonic-gate 		mutex_enter(&dfnp->fn_lock);
842*0Sstevel@tonic-gate 		dfnp->fn_flags |= MF_MOUNTPOINT;
843*0Sstevel@tonic-gate 		ASSERT(dfnp->fn_dirents == NULL);
844*0Sstevel@tonic-gate 		mutex_exit(&dfnp->fn_lock);
845*0Sstevel@tonic-gate 		success++;
846*0Sstevel@tonic-gate 	} else {
847*0Sstevel@tonic-gate 		/*
848*0Sstevel@tonic-gate 		 * Clear MF_MOUNTPOINT.
849*0Sstevel@tonic-gate 		 */
850*0Sstevel@tonic-gate 		mutex_enter(&dfnp->fn_lock);
851*0Sstevel@tonic-gate 		if (dfnp->fn_flags & MF_MOUNTPOINT) {
852*0Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "autofs: clearing mountpoint "
853*0Sstevel@tonic-gate 			    "flag on %s.", dfnp->fn_name));
854*0Sstevel@tonic-gate 			ASSERT(dfnp->fn_dirents == NULL);
855*0Sstevel@tonic-gate 			ASSERT(dfnp->fn_trigger == NULL);
856*0Sstevel@tonic-gate 		}
857*0Sstevel@tonic-gate 		dfnp->fn_flags &= ~MF_MOUNTPOINT;
858*0Sstevel@tonic-gate 		mutex_exit(&dfnp->fn_lock);
859*0Sstevel@tonic-gate 	}
860*0Sstevel@tonic-gate 
861*0Sstevel@tonic-gate 	for (p = alp; p != NULL; p = p->next) {
862*0Sstevel@tonic-gate 		vfs_t *vfsp;	/* dummy argument */
863*0Sstevel@tonic-gate 		vfs_t *mvfsp;
864*0Sstevel@tonic-gate 
865*0Sstevel@tonic-gate 		auto_mount = 0;
866*0Sstevel@tonic-gate 
867*0Sstevel@tonic-gate 		m = &p->action.action_list_entry_u.mounta;
868*0Sstevel@tonic-gate 		argsp = (struct autofs_args *)m->dataptr;
869*0Sstevel@tonic-gate 		/*
870*0Sstevel@tonic-gate 		 * use the parent directory's timeout since it's the
871*0Sstevel@tonic-gate 		 * one specified/inherited by automount.
872*0Sstevel@tonic-gate 		 */
873*0Sstevel@tonic-gate 		argsp->mount_to = dfnip->fi_mount_to;
874*0Sstevel@tonic-gate 		/*
875*0Sstevel@tonic-gate 		 * The mountpoint is relative, and it is guaranteed to
876*0Sstevel@tonic-gate 		 * begin with "."
877*0Sstevel@tonic-gate 		 *
878*0Sstevel@tonic-gate 		 */
879*0Sstevel@tonic-gate 		ASSERT(m->dir[0] == '.');
880*0Sstevel@tonic-gate 		if (m->dir[0] == '.' && m->dir[1] == '\0') {
881*0Sstevel@tonic-gate 			/*
882*0Sstevel@tonic-gate 			 * mounting on the trigger node
883*0Sstevel@tonic-gate 			 */
884*0Sstevel@tonic-gate 			mvp = dvp;
885*0Sstevel@tonic-gate 			VN_HOLD(mvp);
886*0Sstevel@tonic-gate 			goto mount;
887*0Sstevel@tonic-gate 		}
888*0Sstevel@tonic-gate 		/*
889*0Sstevel@tonic-gate 		 * ignore "./" in front of mountpoint
890*0Sstevel@tonic-gate 		 */
891*0Sstevel@tonic-gate 		ASSERT(m->dir[1] == '/');
892*0Sstevel@tonic-gate 		mntpnt = m->dir + 2;
893*0Sstevel@tonic-gate 
894*0Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tdfnip->fi_path=%s\n", dfnip->fi_path));
895*0Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tdfnip->fi_flags=%x\n", dfnip->fi_flags));
896*0Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tmntpnt=%s\n", mntpnt));
897*0Sstevel@tonic-gate 
898*0Sstevel@tonic-gate 		if (dfnip->fi_flags & MF_DIRECT) {
899*0Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tDIRECT\n"));
900*0Sstevel@tonic-gate 			(void) sprintf(buff, "%s/%s", dfnip->fi_path, mntpnt);
901*0Sstevel@tonic-gate 		} else {
902*0Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tINDIRECT\n"));
903*0Sstevel@tonic-gate 			(void) sprintf(buff, "%s/%s/%s", dfnip->fi_path,
904*0Sstevel@tonic-gate 			    dfnp->fn_name, mntpnt);
905*0Sstevel@tonic-gate 		}
906*0Sstevel@tonic-gate 
907*0Sstevel@tonic-gate 		if (vn_mountedvfs(dvp) == NULL) {
908*0Sstevel@tonic-gate 			/*
909*0Sstevel@tonic-gate 			 * Daemon didn't mount anything on the root
910*0Sstevel@tonic-gate 			 * We have to create the mountpoint if it doesn't
911*0Sstevel@tonic-gate 			 * exist already
912*0Sstevel@tonic-gate 			 *
913*0Sstevel@tonic-gate 			 * We use the caller's credentials in case a UID-match
914*0Sstevel@tonic-gate 			 * is required (MF_THISUID_MATCH_RQD).
915*0Sstevel@tonic-gate 			 */
916*0Sstevel@tonic-gate 			rw_enter(&dfnp->fn_rwlock, RW_WRITER);
917*0Sstevel@tonic-gate 			error = auto_search(dfnp, mntpnt, &mfnp, cred);
918*0Sstevel@tonic-gate 			if (error == 0) {
919*0Sstevel@tonic-gate 				/*
920*0Sstevel@tonic-gate 				 * AUTOFS mountpoint exists
921*0Sstevel@tonic-gate 				 */
922*0Sstevel@tonic-gate 				if (vn_mountedvfs(fntovn(mfnp)) != NULL) {
923*0Sstevel@tonic-gate 					cmn_err(CE_PANIC,
924*0Sstevel@tonic-gate 					    "auto_perform_actions: "
925*0Sstevel@tonic-gate 					    "mfnp=%p covered", (void *)mfnp);
926*0Sstevel@tonic-gate 				}
927*0Sstevel@tonic-gate 			} else {
928*0Sstevel@tonic-gate 				/*
929*0Sstevel@tonic-gate 				 * Create AUTOFS mountpoint
930*0Sstevel@tonic-gate 				 */
931*0Sstevel@tonic-gate 				ASSERT((dfnp->fn_flags & MF_MOUNTPOINT) == 0);
932*0Sstevel@tonic-gate 				error = auto_enter(dfnp, mntpnt, &mfnp, cred);
933*0Sstevel@tonic-gate 				ASSERT(mfnp->fn_linkcnt == 1);
934*0Sstevel@tonic-gate 				mfnp->fn_linkcnt++;
935*0Sstevel@tonic-gate 			}
936*0Sstevel@tonic-gate 			if (!error)
937*0Sstevel@tonic-gate 				update_times = 1;
938*0Sstevel@tonic-gate 			rw_exit(&dfnp->fn_rwlock);
939*0Sstevel@tonic-gate 			ASSERT(error != EEXIST);
940*0Sstevel@tonic-gate 			if (!error) {
941*0Sstevel@tonic-gate 				/*
942*0Sstevel@tonic-gate 				 * mfnp is already held.
943*0Sstevel@tonic-gate 				 */
944*0Sstevel@tonic-gate 				mvp = fntovn(mfnp);
945*0Sstevel@tonic-gate 			} else {
946*0Sstevel@tonic-gate 				auto_log(fngp, CE_WARN, "autofs: mount of %s "
947*0Sstevel@tonic-gate 				    "failed - can't create mountpoint.", buff);
948*0Sstevel@tonic-gate 				continue;
949*0Sstevel@tonic-gate 			}
950*0Sstevel@tonic-gate 		} else {
951*0Sstevel@tonic-gate 			/*
952*0Sstevel@tonic-gate 			 * Find mountpoint in VFS mounted here. If not found,
953*0Sstevel@tonic-gate 			 * fail the submount, though the overall mount has
954*0Sstevel@tonic-gate 			 * succeeded since the root is mounted.
955*0Sstevel@tonic-gate 			 */
956*0Sstevel@tonic-gate 			if (error = auto_getmntpnt(dvp, mntpnt, &mvp, kcred)) {
957*0Sstevel@tonic-gate 				auto_log(fngp, CE_WARN, "autofs: mount of %s "
958*0Sstevel@tonic-gate 				    "failed - mountpoint doesn't exist.", buff);
959*0Sstevel@tonic-gate 				continue;
960*0Sstevel@tonic-gate 			}
961*0Sstevel@tonic-gate 			if (mvp->v_type == VLNK) {
962*0Sstevel@tonic-gate 				auto_log(fngp, CE_WARN, "autofs: %s symbolic "
963*0Sstevel@tonic-gate 				    "link: not a valid mountpoint "
964*0Sstevel@tonic-gate 				    "- mount failed", buff);
965*0Sstevel@tonic-gate 				VN_RELE(mvp);
966*0Sstevel@tonic-gate 				error = ENOENT;
967*0Sstevel@tonic-gate 				continue;
968*0Sstevel@tonic-gate 			}
969*0Sstevel@tonic-gate 		}
970*0Sstevel@tonic-gate mount:
971*0Sstevel@tonic-gate 		m->flags |= MS_SYSSPACE | MS_OPTIONSTR;
972*0Sstevel@tonic-gate 		/*
973*0Sstevel@tonic-gate 		 * Copy mounta struct here so we can substitute a buffer
974*0Sstevel@tonic-gate 		 * that is large enough to hold the returned option string,
975*0Sstevel@tonic-gate 		 * if that string is longer that the input option string.
976*0Sstevel@tonic-gate 		 * This can happen if there are default options enabled
977*0Sstevel@tonic-gate 		 * that were not in the input option string.
978*0Sstevel@tonic-gate 		 */
979*0Sstevel@tonic-gate 		bcopy(m, &margs, sizeof (*m));
980*0Sstevel@tonic-gate 		margs.optptr = kmem_alloc(MAX_MNTOPT_STR, KM_SLEEP);
981*0Sstevel@tonic-gate 		margs.optlen = MAX_MNTOPT_STR;
982*0Sstevel@tonic-gate 		(void) strcpy(margs.optptr, m->optptr);
983*0Sstevel@tonic-gate 		margs.dir = argsp->path;
984*0Sstevel@tonic-gate 		/*
985*0Sstevel@tonic-gate 		 * We use the zone's kcred because we don't want the zone to be
986*0Sstevel@tonic-gate 		 * able to thus do something it wouldn't normally be able to.
987*0Sstevel@tonic-gate 		 */
988*0Sstevel@tonic-gate 		error = domount(NULL, &margs, mvp, zcred, &vfsp);
989*0Sstevel@tonic-gate 		kmem_free(margs.optptr, MAX_MNTOPT_STR);
990*0Sstevel@tonic-gate 		if (error != 0) {
991*0Sstevel@tonic-gate 			auto_log(fngp, CE_WARN,
992*0Sstevel@tonic-gate 			    "autofs: domount of %s failed error=%d",
993*0Sstevel@tonic-gate 			    buff, error);
994*0Sstevel@tonic-gate 			VN_RELE(mvp);
995*0Sstevel@tonic-gate 			continue;
996*0Sstevel@tonic-gate 		}
997*0Sstevel@tonic-gate 		VFS_RELE(vfsp);
998*0Sstevel@tonic-gate 
999*0Sstevel@tonic-gate 		/*
1000*0Sstevel@tonic-gate 		 * If mountpoint is an AUTOFS node, then I'm going to
1001*0Sstevel@tonic-gate 		 * flag it that the Filesystem mounted on top was mounted
1002*0Sstevel@tonic-gate 		 * in the kernel so that the unmount can be done inside the
1003*0Sstevel@tonic-gate 		 * kernel as well.
1004*0Sstevel@tonic-gate 		 * I don't care to flag non-AUTOFS mountpoints when an AUTOFS
1005*0Sstevel@tonic-gate 		 * in-kernel mount was done on top, because the unmount
1006*0Sstevel@tonic-gate 		 * routine already knows that such case was done in the kernel.
1007*0Sstevel@tonic-gate 		 */
1008*0Sstevel@tonic-gate 		if (vfs_matchops(dvp->v_vfsp, vfs_getops(mvp->v_vfsp))) {
1009*0Sstevel@tonic-gate 			mfnp = vntofn(mvp);
1010*0Sstevel@tonic-gate 			mutex_enter(&mfnp->fn_lock);
1011*0Sstevel@tonic-gate 			mfnp->fn_flags |= MF_IK_MOUNT;
1012*0Sstevel@tonic-gate 			mutex_exit(&mfnp->fn_lock);
1013*0Sstevel@tonic-gate 		}
1014*0Sstevel@tonic-gate 
1015*0Sstevel@tonic-gate 		(void) vn_vfswlock_wait(mvp);
1016*0Sstevel@tonic-gate 		mvfsp = vn_mountedvfs(mvp);
1017*0Sstevel@tonic-gate 		if (mvfsp != NULL) {
1018*0Sstevel@tonic-gate 			vfs_lock_wait(mvfsp);
1019*0Sstevel@tonic-gate 			vn_vfsunlock(mvp);
1020*0Sstevel@tonic-gate 			error = VFS_ROOT(mvfsp, &newvp);
1021*0Sstevel@tonic-gate 			vfs_unlock(mvfsp);
1022*0Sstevel@tonic-gate 			if (error) {
1023*0Sstevel@tonic-gate 				/*
1024*0Sstevel@tonic-gate 				 * We've dropped the locks, so let's get
1025*0Sstevel@tonic-gate 				 * the mounted vfs again in case it changed.
1026*0Sstevel@tonic-gate 				 */
1027*0Sstevel@tonic-gate 				(void) vn_vfswlock_wait(mvp);
1028*0Sstevel@tonic-gate 				mvfsp = vn_mountedvfs(mvp);
1029*0Sstevel@tonic-gate 				if (mvfsp != NULL) {
1030*0Sstevel@tonic-gate 					error = dounmount(mvfsp, 0, CRED());
1031*0Sstevel@tonic-gate 					if (error) {
1032*0Sstevel@tonic-gate 						cmn_err(CE_WARN,
1033*0Sstevel@tonic-gate 						    "autofs: could not "
1034*0Sstevel@tonic-gate 						    "unmount vfs=%p",
1035*0Sstevel@tonic-gate 						(void *)mvfsp);
1036*0Sstevel@tonic-gate 					}
1037*0Sstevel@tonic-gate 				} else
1038*0Sstevel@tonic-gate 					vn_vfsunlock(mvp);
1039*0Sstevel@tonic-gate 				VN_RELE(mvp);
1040*0Sstevel@tonic-gate 				continue;
1041*0Sstevel@tonic-gate 			}
1042*0Sstevel@tonic-gate 		} else {
1043*0Sstevel@tonic-gate 			vn_vfsunlock(mvp);
1044*0Sstevel@tonic-gate 			VN_RELE(mvp);
1045*0Sstevel@tonic-gate 			continue;
1046*0Sstevel@tonic-gate 		}
1047*0Sstevel@tonic-gate 
1048*0Sstevel@tonic-gate 		auto_mount = vfs_matchops(dvp->v_vfsp,
1049*0Sstevel@tonic-gate 						vfs_getops(newvp->v_vfsp));
1050*0Sstevel@tonic-gate 		newfnp = vntofn(newvp);
1051*0Sstevel@tonic-gate 		newfnp->fn_parent = dfnp;
1052*0Sstevel@tonic-gate 
1053*0Sstevel@tonic-gate 		/*
1054*0Sstevel@tonic-gate 		 * At this time we want to save the AUTOFS filesystem as
1055*0Sstevel@tonic-gate 		 * a trigger node. (We only do this if the mount occured
1056*0Sstevel@tonic-gate 		 * on a node different from the root.
1057*0Sstevel@tonic-gate 		 * We look at the trigger nodes during
1058*0Sstevel@tonic-gate 		 * the automatic unmounting to make sure we remove them
1059*0Sstevel@tonic-gate 		 * as a unit and remount them as a unit if the filesystem
1060*0Sstevel@tonic-gate 		 * mounted at the root could not be unmounted.
1061*0Sstevel@tonic-gate 		 */
1062*0Sstevel@tonic-gate 		if (auto_mount && (error == 0) && (mvp != dvp)) {
1063*0Sstevel@tonic-gate 			save_triggers++;
1064*0Sstevel@tonic-gate 			/*
1065*0Sstevel@tonic-gate 			 * Add AUTOFS mount to hierarchy
1066*0Sstevel@tonic-gate 			 */
1067*0Sstevel@tonic-gate 			newfnp->fn_flags |= MF_TRIGGER;
1068*0Sstevel@tonic-gate 			rw_enter(&newfnp->fn_rwlock, RW_WRITER);
1069*0Sstevel@tonic-gate 			newfnp->fn_next = dfnp->fn_trigger;
1070*0Sstevel@tonic-gate 			rw_exit(&newfnp->fn_rwlock);
1071*0Sstevel@tonic-gate 			rw_enter(&dfnp->fn_rwlock, RW_WRITER);
1072*0Sstevel@tonic-gate 			dfnp->fn_trigger = newfnp;
1073*0Sstevel@tonic-gate 			rw_exit(&dfnp->fn_rwlock);
1074*0Sstevel@tonic-gate 			/*
1075*0Sstevel@tonic-gate 			 * Don't VN_RELE(newvp) here since dfnp now holds
1076*0Sstevel@tonic-gate 			 * reference to it as its trigger node.
1077*0Sstevel@tonic-gate 			 */
1078*0Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tadding trigger %s to %s\n",
1079*0Sstevel@tonic-gate 			    newfnp->fn_name, dfnp->fn_name));
1080*0Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tfirst trigger is %s\n",
1081*0Sstevel@tonic-gate 			    dfnp->fn_trigger->fn_name));
1082*0Sstevel@tonic-gate 			if (newfnp->fn_next != NULL)
1083*0Sstevel@tonic-gate 				AUTOFS_DPRINT((10, "\tnext trigger is %s\n",
1084*0Sstevel@tonic-gate 				    newfnp->fn_next->fn_name));
1085*0Sstevel@tonic-gate 			else
1086*0Sstevel@tonic-gate 				AUTOFS_DPRINT((10, "\tno next trigger\n"));
1087*0Sstevel@tonic-gate 		} else
1088*0Sstevel@tonic-gate 			VN_RELE(newvp);
1089*0Sstevel@tonic-gate 
1090*0Sstevel@tonic-gate 		if (!error)
1091*0Sstevel@tonic-gate 			success++;
1092*0Sstevel@tonic-gate 
1093*0Sstevel@tonic-gate 		if (update_times) {
1094*0Sstevel@tonic-gate 			gethrestime(&now);
1095*0Sstevel@tonic-gate 			dfnp->fn_atime = dfnp->fn_mtime = now;
1096*0Sstevel@tonic-gate 		}
1097*0Sstevel@tonic-gate 
1098*0Sstevel@tonic-gate 		VN_RELE(mvp);
1099*0Sstevel@tonic-gate 	}
1100*0Sstevel@tonic-gate 
1101*0Sstevel@tonic-gate 	if (save_triggers) {
1102*0Sstevel@tonic-gate 		/*
1103*0Sstevel@tonic-gate 		 * Make sure the parent can't be freed while it has triggers.
1104*0Sstevel@tonic-gate 		 */
1105*0Sstevel@tonic-gate 		VN_HOLD(dvp);
1106*0Sstevel@tonic-gate 	}
1107*0Sstevel@tonic-gate 
1108*0Sstevel@tonic-gate 	crfree(zcred);
1109*0Sstevel@tonic-gate 
1110*0Sstevel@tonic-gate done:
1111*0Sstevel@tonic-gate 	/*
1112*0Sstevel@tonic-gate 	 * Return failure if daemon didn't mount anything, and all
1113*0Sstevel@tonic-gate 	 * kernel mounts attempted failed.
1114*0Sstevel@tonic-gate 	 */
1115*0Sstevel@tonic-gate 	error = success ? 0 : ENOENT;
1116*0Sstevel@tonic-gate 
1117*0Sstevel@tonic-gate 	if (alp != NULL) {
1118*0Sstevel@tonic-gate 		if ((error == 0) && save_triggers) {
1119*0Sstevel@tonic-gate 			/*
1120*0Sstevel@tonic-gate 			 * Save action_list information, so that we can use it
1121*0Sstevel@tonic-gate 			 * when it comes time to remount the trigger nodes
1122*0Sstevel@tonic-gate 			 * The action list is freed when the directory node
1123*0Sstevel@tonic-gate 			 * containing the reference to it is unmounted in
1124*0Sstevel@tonic-gate 			 * unmount_tree().
1125*0Sstevel@tonic-gate 			 */
1126*0Sstevel@tonic-gate 			mutex_enter(&dfnp->fn_lock);
1127*0Sstevel@tonic-gate 			ASSERT(dfnp->fn_alp == NULL);
1128*0Sstevel@tonic-gate 			dfnp->fn_alp = alp;
1129*0Sstevel@tonic-gate 			mutex_exit(&dfnp->fn_lock);
1130*0Sstevel@tonic-gate 		} else {
1131*0Sstevel@tonic-gate 			/*
1132*0Sstevel@tonic-gate 			 * free the action list now,
1133*0Sstevel@tonic-gate 			 */
1134*0Sstevel@tonic-gate 			xdr_free(xdr_action_list, (char *)alp);
1135*0Sstevel@tonic-gate 		}
1136*0Sstevel@tonic-gate 	}
1137*0Sstevel@tonic-gate 
1138*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_perform_actions: error=%d\n", error));
1139*0Sstevel@tonic-gate 	return (error);
1140*0Sstevel@tonic-gate }
1141*0Sstevel@tonic-gate 
1142*0Sstevel@tonic-gate fnnode_t *
1143*0Sstevel@tonic-gate auto_makefnnode(
1144*0Sstevel@tonic-gate 	vtype_t type,
1145*0Sstevel@tonic-gate 	vfs_t *vfsp,
1146*0Sstevel@tonic-gate 	char *name,
1147*0Sstevel@tonic-gate 	cred_t *cred,
1148*0Sstevel@tonic-gate 	struct autofs_globals *fngp)
1149*0Sstevel@tonic-gate {
1150*0Sstevel@tonic-gate 	fnnode_t *fnp;
1151*0Sstevel@tonic-gate 	vnode_t *vp;
1152*0Sstevel@tonic-gate 	char *tmpname;
1153*0Sstevel@tonic-gate 	timestruc_t now;
1154*0Sstevel@tonic-gate 	/*
1155*0Sstevel@tonic-gate 	 * autofs uses odd inode numbers
1156*0Sstevel@tonic-gate 	 * automountd uses even inode numbers
1157*0Sstevel@tonic-gate 	 *
1158*0Sstevel@tonic-gate 	 * To preserve the age-old semantics that inum+devid is unique across
1159*0Sstevel@tonic-gate 	 * the system, this variable must be global across zones.
1160*0Sstevel@tonic-gate 	 */
1161*0Sstevel@tonic-gate 	static ino_t nodeid = 3;
1162*0Sstevel@tonic-gate 
1163*0Sstevel@tonic-gate 	fnp = kmem_zalloc(sizeof (*fnp), KM_SLEEP);
1164*0Sstevel@tonic-gate 	fnp->fn_vnode = vn_alloc(KM_SLEEP);
1165*0Sstevel@tonic-gate 
1166*0Sstevel@tonic-gate 	vp = fntovn(fnp);
1167*0Sstevel@tonic-gate 	tmpname = kmem_alloc(strlen(name) + 1, KM_SLEEP);
1168*0Sstevel@tonic-gate 	(void) strcpy(tmpname, name);
1169*0Sstevel@tonic-gate 	fnp->fn_name = &tmpname[0];
1170*0Sstevel@tonic-gate 	fnp->fn_namelen = (int)strlen(tmpname) + 1;	/* include '\0' */
1171*0Sstevel@tonic-gate 	fnp->fn_uid = crgetuid(cred);
1172*0Sstevel@tonic-gate 	fnp->fn_gid = crgetgid(cred);
1173*0Sstevel@tonic-gate 	/*
1174*0Sstevel@tonic-gate 	 * ".." is added in auto_enter and auto_mount.
1175*0Sstevel@tonic-gate 	 * "." is added in auto_mkdir and auto_mount.
1176*0Sstevel@tonic-gate 	 */
1177*0Sstevel@tonic-gate 	/*
1178*0Sstevel@tonic-gate 	 * Note that fn_size and fn_linkcnt are already 0 since
1179*0Sstevel@tonic-gate 	 * we used kmem_zalloc to allocated fnp
1180*0Sstevel@tonic-gate 	 */
1181*0Sstevel@tonic-gate 	fnp->fn_mode = AUTOFS_MODE;
1182*0Sstevel@tonic-gate 	gethrestime(&now);
1183*0Sstevel@tonic-gate 	fnp->fn_atime = fnp->fn_mtime = fnp->fn_ctime = now;
1184*0Sstevel@tonic-gate 	fnp->fn_ref_time = now.tv_sec;
1185*0Sstevel@tonic-gate 	mutex_enter(&autofs_nodeid_lock);
1186*0Sstevel@tonic-gate 	fnp->fn_nodeid = nodeid;
1187*0Sstevel@tonic-gate 	nodeid += 2;
1188*0Sstevel@tonic-gate 	fnp->fn_globals = fngp;
1189*0Sstevel@tonic-gate 	fngp->fng_fnnode_count++;
1190*0Sstevel@tonic-gate 	mutex_exit(&autofs_nodeid_lock);
1191*0Sstevel@tonic-gate 	vn_setops(vp, auto_vnodeops);
1192*0Sstevel@tonic-gate 	vp->v_type = type;
1193*0Sstevel@tonic-gate 	vp->v_data = (void *)fnp;
1194*0Sstevel@tonic-gate 	vp->v_vfsp = vfsp;
1195*0Sstevel@tonic-gate 	mutex_init(&fnp->fn_lock, NULL, MUTEX_DEFAULT, NULL);
1196*0Sstevel@tonic-gate 	rw_init(&fnp->fn_rwlock, NULL, RW_DEFAULT, NULL);
1197*0Sstevel@tonic-gate 	cv_init(&fnp->fn_cv_mount, NULL, CV_DEFAULT, NULL);
1198*0Sstevel@tonic-gate 	vn_exists(vp);
1199*0Sstevel@tonic-gate 	return (fnp);
1200*0Sstevel@tonic-gate }
1201*0Sstevel@tonic-gate 
1202*0Sstevel@tonic-gate 
1203*0Sstevel@tonic-gate void
1204*0Sstevel@tonic-gate auto_freefnnode(fnnode_t *fnp)
1205*0Sstevel@tonic-gate {
1206*0Sstevel@tonic-gate 	vnode_t *vp = fntovn(fnp);
1207*0Sstevel@tonic-gate 
1208*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_freefnnode: fnp=%p\n", (void *)fnp));
1209*0Sstevel@tonic-gate 
1210*0Sstevel@tonic-gate 	ASSERT(fnp->fn_linkcnt == 0);
1211*0Sstevel@tonic-gate 	ASSERT(vp->v_count == 0);
1212*0Sstevel@tonic-gate 	ASSERT(fnp->fn_dirents == NULL);
1213*0Sstevel@tonic-gate 	ASSERT(fnp->fn_parent == NULL);
1214*0Sstevel@tonic-gate 
1215*0Sstevel@tonic-gate 	vn_invalid(vp);
1216*0Sstevel@tonic-gate 	kmem_free(fnp->fn_name, fnp->fn_namelen);
1217*0Sstevel@tonic-gate 	if (fnp->fn_symlink) {
1218*0Sstevel@tonic-gate 		ASSERT(fnp->fn_flags & MF_THISUID_MATCH_RQD);
1219*0Sstevel@tonic-gate 		kmem_free(fnp->fn_symlink, fnp->fn_symlinklen);
1220*0Sstevel@tonic-gate 	}
1221*0Sstevel@tonic-gate 	if (fnp->fn_cred)
1222*0Sstevel@tonic-gate 		crfree(fnp->fn_cred);
1223*0Sstevel@tonic-gate 	mutex_destroy(&fnp->fn_lock);
1224*0Sstevel@tonic-gate 	rw_destroy(&fnp->fn_rwlock);
1225*0Sstevel@tonic-gate 	cv_destroy(&fnp->fn_cv_mount);
1226*0Sstevel@tonic-gate 	vn_free(vp);
1227*0Sstevel@tonic-gate 
1228*0Sstevel@tonic-gate 	mutex_enter(&autofs_nodeid_lock);
1229*0Sstevel@tonic-gate 	fnp->fn_globals->fng_fnnode_count--;
1230*0Sstevel@tonic-gate 	mutex_exit(&autofs_nodeid_lock);
1231*0Sstevel@tonic-gate 	kmem_free(fnp, sizeof (*fnp));
1232*0Sstevel@tonic-gate }
1233*0Sstevel@tonic-gate 
1234*0Sstevel@tonic-gate void
1235*0Sstevel@tonic-gate auto_disconnect(
1236*0Sstevel@tonic-gate 	fnnode_t *dfnp,
1237*0Sstevel@tonic-gate 	fnnode_t *fnp)
1238*0Sstevel@tonic-gate {
1239*0Sstevel@tonic-gate 	fnnode_t *tmp, **fnpp;
1240*0Sstevel@tonic-gate 	vnode_t *vp = fntovn(fnp);
1241*0Sstevel@tonic-gate 	timestruc_t now;
1242*0Sstevel@tonic-gate 
1243*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4,
1244*0Sstevel@tonic-gate 	    "auto_disconnect: dfnp=%p fnp=%p linkcnt=%d\n v_count=%d",
1245*0Sstevel@tonic-gate 	    (void *)dfnp, (void *)fnp, fnp->fn_linkcnt, vp->v_count));
1246*0Sstevel@tonic-gate 
1247*0Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock));
1248*0Sstevel@tonic-gate 	ASSERT(fnp->fn_linkcnt == 1);
1249*0Sstevel@tonic-gate 
1250*0Sstevel@tonic-gate 	if (vn_mountedvfs(vp) != NULL) {
1251*0Sstevel@tonic-gate 		cmn_err(CE_PANIC, "auto_disconnect: vp %p mounted on",
1252*0Sstevel@tonic-gate 		    (void *)vp);
1253*0Sstevel@tonic-gate 	}
1254*0Sstevel@tonic-gate 
1255*0Sstevel@tonic-gate 	/*
1256*0Sstevel@tonic-gate 	 * Decrement by 1 because we're removing the entry in dfnp.
1257*0Sstevel@tonic-gate 	 */
1258*0Sstevel@tonic-gate 	fnp->fn_linkcnt--;
1259*0Sstevel@tonic-gate 	fnp->fn_size--;
1260*0Sstevel@tonic-gate 
1261*0Sstevel@tonic-gate 	/*
1262*0Sstevel@tonic-gate 	 * only changed while holding parent's (dfnp) rw_lock
1263*0Sstevel@tonic-gate 	 */
1264*0Sstevel@tonic-gate 	fnp->fn_parent = NULL;
1265*0Sstevel@tonic-gate 
1266*0Sstevel@tonic-gate 	fnpp = &dfnp->fn_dirents;
1267*0Sstevel@tonic-gate 	for (;;) {
1268*0Sstevel@tonic-gate 		tmp = *fnpp;
1269*0Sstevel@tonic-gate 		if (tmp == NULL) {
1270*0Sstevel@tonic-gate 			cmn_err(CE_PANIC,
1271*0Sstevel@tonic-gate 			    "auto_disconnect: %p not in %p dirent list",
1272*0Sstevel@tonic-gate 			    (void *)fnp, (void *)dfnp);
1273*0Sstevel@tonic-gate 		}
1274*0Sstevel@tonic-gate 		if (tmp == fnp) {
1275*0Sstevel@tonic-gate 			*fnpp = tmp->fn_next; 	/* remove it from the list */
1276*0Sstevel@tonic-gate 			ASSERT(vp->v_count == 0);
1277*0Sstevel@tonic-gate 			/* child had a pointer to parent ".." */
1278*0Sstevel@tonic-gate 			dfnp->fn_linkcnt--;
1279*0Sstevel@tonic-gate 			dfnp->fn_size--;
1280*0Sstevel@tonic-gate 			break;
1281*0Sstevel@tonic-gate 		}
1282*0Sstevel@tonic-gate 		fnpp = &tmp->fn_next;
1283*0Sstevel@tonic-gate 	}
1284*0Sstevel@tonic-gate 
1285*0Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
1286*0Sstevel@tonic-gate 	gethrestime(&now);
1287*0Sstevel@tonic-gate 	fnp->fn_atime = fnp->fn_mtime = now;
1288*0Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
1289*0Sstevel@tonic-gate 
1290*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_disconnect: done\n"));
1291*0Sstevel@tonic-gate }
1292*0Sstevel@tonic-gate 
1293*0Sstevel@tonic-gate int
1294*0Sstevel@tonic-gate auto_enter(fnnode_t *dfnp, char *name, fnnode_t **fnpp, cred_t *cred)
1295*0Sstevel@tonic-gate {
1296*0Sstevel@tonic-gate 	struct fnnode *cfnp, **spp;
1297*0Sstevel@tonic-gate 	vnode_t *dvp = fntovn(dfnp);
1298*0Sstevel@tonic-gate 	ushort_t offset = 0;
1299*0Sstevel@tonic-gate 	ushort_t diff;
1300*0Sstevel@tonic-gate 
1301*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_enter: dfnp=%p, name=%s ", (void *)dfnp, name));
1302*0Sstevel@tonic-gate 
1303*0Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock));
1304*0Sstevel@tonic-gate 
1305*0Sstevel@tonic-gate 	cfnp = dfnp->fn_dirents;
1306*0Sstevel@tonic-gate 	if (cfnp == NULL) {
1307*0Sstevel@tonic-gate 		/*
1308*0Sstevel@tonic-gate 		 * offset = 0 for '.' and offset = 1 for '..'
1309*0Sstevel@tonic-gate 		 */
1310*0Sstevel@tonic-gate 		spp = &dfnp->fn_dirents;
1311*0Sstevel@tonic-gate 		offset = 2;
1312*0Sstevel@tonic-gate 	}
1313*0Sstevel@tonic-gate 
1314*0Sstevel@tonic-gate 	for (; cfnp; cfnp = cfnp->fn_next) {
1315*0Sstevel@tonic-gate 		if (strcmp(cfnp->fn_name, name) == 0) {
1316*0Sstevel@tonic-gate 			mutex_enter(&cfnp->fn_lock);
1317*0Sstevel@tonic-gate 			if (cfnp->fn_flags & MF_THISUID_MATCH_RQD) {
1318*0Sstevel@tonic-gate 				/*
1319*0Sstevel@tonic-gate 				 * "thisuser" kind of node, need to
1320*0Sstevel@tonic-gate 				 * match CREDs as well
1321*0Sstevel@tonic-gate 				 */
1322*0Sstevel@tonic-gate 				mutex_exit(&cfnp->fn_lock);
1323*0Sstevel@tonic-gate 				if (crcmp(cfnp->fn_cred, cred) == 0)
1324*0Sstevel@tonic-gate 					return (EEXIST);
1325*0Sstevel@tonic-gate 			} else {
1326*0Sstevel@tonic-gate 				mutex_exit(&cfnp->fn_lock);
1327*0Sstevel@tonic-gate 				return (EEXIST);
1328*0Sstevel@tonic-gate 			}
1329*0Sstevel@tonic-gate 		}
1330*0Sstevel@tonic-gate 
1331*0Sstevel@tonic-gate 		if (cfnp->fn_next != NULL) {
1332*0Sstevel@tonic-gate 			diff = (ushort_t)
1333*0Sstevel@tonic-gate 			    (cfnp->fn_next->fn_offset - cfnp->fn_offset);
1334*0Sstevel@tonic-gate 			ASSERT(diff != 0);
1335*0Sstevel@tonic-gate 			if (diff > 1 && offset == 0) {
1336*0Sstevel@tonic-gate 				offset = (ushort_t)cfnp->fn_offset + 1;
1337*0Sstevel@tonic-gate 				spp = &cfnp->fn_next;
1338*0Sstevel@tonic-gate 			}
1339*0Sstevel@tonic-gate 		} else if (offset == 0) {
1340*0Sstevel@tonic-gate 			offset = (ushort_t)cfnp->fn_offset + 1;
1341*0Sstevel@tonic-gate 			spp = &cfnp->fn_next;
1342*0Sstevel@tonic-gate 		}
1343*0Sstevel@tonic-gate 	}
1344*0Sstevel@tonic-gate 
1345*0Sstevel@tonic-gate 	*fnpp = auto_makefnnode(VDIR, dvp->v_vfsp, name, cred,
1346*0Sstevel@tonic-gate 	    dfnp->fn_globals);
1347*0Sstevel@tonic-gate 	if (*fnpp == NULL)
1348*0Sstevel@tonic-gate 		return (ENOMEM);
1349*0Sstevel@tonic-gate 
1350*0Sstevel@tonic-gate 	/*
1351*0Sstevel@tonic-gate 	 * I don't hold the mutex on fnpp because I created it, and
1352*0Sstevel@tonic-gate 	 * I'm already holding the writers lock for it's parent
1353*0Sstevel@tonic-gate 	 * directory, therefore nobody can reference it without me first
1354*0Sstevel@tonic-gate 	 * releasing the writers lock.
1355*0Sstevel@tonic-gate 	 */
1356*0Sstevel@tonic-gate 	(*fnpp)->fn_offset = offset;
1357*0Sstevel@tonic-gate 	(*fnpp)->fn_next = *spp;
1358*0Sstevel@tonic-gate 	*spp = *fnpp;
1359*0Sstevel@tonic-gate 	(*fnpp)->fn_parent = dfnp;
1360*0Sstevel@tonic-gate 	(*fnpp)->fn_linkcnt++;	/* parent now holds reference to entry */
1361*0Sstevel@tonic-gate 	(*fnpp)->fn_size++;
1362*0Sstevel@tonic-gate 
1363*0Sstevel@tonic-gate 	/*
1364*0Sstevel@tonic-gate 	 * dfnp->fn_linkcnt and dfnp->fn_size protected by dfnp->rw_lock
1365*0Sstevel@tonic-gate 	 */
1366*0Sstevel@tonic-gate 	dfnp->fn_linkcnt++;	/* child now holds reference to parent '..' */
1367*0Sstevel@tonic-gate 	dfnp->fn_size++;
1368*0Sstevel@tonic-gate 
1369*0Sstevel@tonic-gate 	dfnp->fn_ref_time = gethrestime_sec();
1370*0Sstevel@tonic-gate 
1371*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "*fnpp=%p\n", (void *)*fnpp));
1372*0Sstevel@tonic-gate 	return (0);
1373*0Sstevel@tonic-gate }
1374*0Sstevel@tonic-gate 
1375*0Sstevel@tonic-gate int
1376*0Sstevel@tonic-gate auto_search(fnnode_t *dfnp, char *name, fnnode_t **fnpp, cred_t *cred)
1377*0Sstevel@tonic-gate {
1378*0Sstevel@tonic-gate 	vnode_t *dvp;
1379*0Sstevel@tonic-gate 	fnnode_t *p;
1380*0Sstevel@tonic-gate 	int error = ENOENT, match = 0;
1381*0Sstevel@tonic-gate 
1382*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_search: dfnp=%p, name=%s...\n",
1383*0Sstevel@tonic-gate 	    (void *)dfnp, name));
1384*0Sstevel@tonic-gate 
1385*0Sstevel@tonic-gate 	dvp = fntovn(dfnp);
1386*0Sstevel@tonic-gate 	if (dvp->v_type != VDIR) {
1387*0Sstevel@tonic-gate 		cmn_err(CE_PANIC, "auto_search: dvp=%p not a directory",
1388*0Sstevel@tonic-gate 		    (void *)dvp);
1389*0Sstevel@tonic-gate 	}
1390*0Sstevel@tonic-gate 
1391*0Sstevel@tonic-gate 	ASSERT(RW_LOCK_HELD(&dfnp->fn_rwlock));
1392*0Sstevel@tonic-gate 	for (p = dfnp->fn_dirents; p != NULL; p = p->fn_next) {
1393*0Sstevel@tonic-gate 		if (strcmp(p->fn_name, name) == 0) {
1394*0Sstevel@tonic-gate 			mutex_enter(&p->fn_lock);
1395*0Sstevel@tonic-gate 			if (p->fn_flags & MF_THISUID_MATCH_RQD) {
1396*0Sstevel@tonic-gate 				/*
1397*0Sstevel@tonic-gate 				 * "thisuser" kind of node
1398*0Sstevel@tonic-gate 				 * Need to match CREDs as well
1399*0Sstevel@tonic-gate 				 */
1400*0Sstevel@tonic-gate 				mutex_exit(&p->fn_lock);
1401*0Sstevel@tonic-gate 				match = crcmp(p->fn_cred, cred) == 0;
1402*0Sstevel@tonic-gate 			} else {
1403*0Sstevel@tonic-gate 				/*
1404*0Sstevel@tonic-gate 				 * No need to check CRED
1405*0Sstevel@tonic-gate 				 */
1406*0Sstevel@tonic-gate 				mutex_exit(&p->fn_lock);
1407*0Sstevel@tonic-gate 				match = 1;
1408*0Sstevel@tonic-gate 			}
1409*0Sstevel@tonic-gate 		}
1410*0Sstevel@tonic-gate 		if (match) {
1411*0Sstevel@tonic-gate 			error = 0;
1412*0Sstevel@tonic-gate 			if (fnpp) {
1413*0Sstevel@tonic-gate 				*fnpp = p;
1414*0Sstevel@tonic-gate 				VN_HOLD(fntovn(*fnpp));
1415*0Sstevel@tonic-gate 			}
1416*0Sstevel@tonic-gate 			break;
1417*0Sstevel@tonic-gate 		}
1418*0Sstevel@tonic-gate 	}
1419*0Sstevel@tonic-gate 
1420*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_search: error=%d\n", error));
1421*0Sstevel@tonic-gate 	return (error);
1422*0Sstevel@tonic-gate }
1423*0Sstevel@tonic-gate 
1424*0Sstevel@tonic-gate /*
1425*0Sstevel@tonic-gate  * If dvp is mounted on, get path's vnode in the mounted on
1426*0Sstevel@tonic-gate  * filesystem.  Path is relative to dvp, ie "./path".
1427*0Sstevel@tonic-gate  * If successful, *mvp points to a the held mountpoint vnode.
1428*0Sstevel@tonic-gate  */
1429*0Sstevel@tonic-gate /* ARGSUSED */
1430*0Sstevel@tonic-gate static int
1431*0Sstevel@tonic-gate auto_getmntpnt(
1432*0Sstevel@tonic-gate 	vnode_t *dvp,
1433*0Sstevel@tonic-gate 	char *path,
1434*0Sstevel@tonic-gate 	vnode_t **mvpp,		/* vnode for mountpoint */
1435*0Sstevel@tonic-gate 	cred_t *cred)
1436*0Sstevel@tonic-gate {
1437*0Sstevel@tonic-gate 	int error = 0;
1438*0Sstevel@tonic-gate 	vnode_t *newvp;
1439*0Sstevel@tonic-gate 	char namebuf[TYPICALMAXPATHLEN];
1440*0Sstevel@tonic-gate 	struct pathname lookpn;
1441*0Sstevel@tonic-gate 	vfs_t *vfsp;
1442*0Sstevel@tonic-gate 
1443*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_getmntpnt: path=%s\n", path));
1444*0Sstevel@tonic-gate 
1445*0Sstevel@tonic-gate 	if (error = vn_vfswlock_wait(dvp))
1446*0Sstevel@tonic-gate 		return (error);
1447*0Sstevel@tonic-gate 
1448*0Sstevel@tonic-gate 	/*
1449*0Sstevel@tonic-gate 	 * Now that we have the vfswlock, check to see if dvp
1450*0Sstevel@tonic-gate 	 * is still mounted on.  If not, then just bail out as
1451*0Sstevel@tonic-gate 	 * there is no need to remount the triggers since the
1452*0Sstevel@tonic-gate 	 * higher level mount point has gotten unmounted.
1453*0Sstevel@tonic-gate 	 */
1454*0Sstevel@tonic-gate 	vfsp = vn_mountedvfs(dvp);
1455*0Sstevel@tonic-gate 	if (vfsp == NULL) {
1456*0Sstevel@tonic-gate 		vn_vfsunlock(dvp);
1457*0Sstevel@tonic-gate 		error = EBUSY;
1458*0Sstevel@tonic-gate 		goto done;
1459*0Sstevel@tonic-gate 	}
1460*0Sstevel@tonic-gate 	/*
1461*0Sstevel@tonic-gate 	 * Since mounted on, lookup "path" in the new filesystem,
1462*0Sstevel@tonic-gate 	 * it is important that we do the filesystem jump here to
1463*0Sstevel@tonic-gate 	 * avoid lookuppn() calling auto_lookup on dvp and deadlock.
1464*0Sstevel@tonic-gate 	 */
1465*0Sstevel@tonic-gate 	vfs_lock_wait(vfsp);
1466*0Sstevel@tonic-gate 	vn_vfsunlock(dvp);
1467*0Sstevel@tonic-gate 	error = VFS_ROOT(vfsp, &newvp);
1468*0Sstevel@tonic-gate 	vfs_unlock(vfsp);
1469*0Sstevel@tonic-gate 	if (error)
1470*0Sstevel@tonic-gate 		goto done;
1471*0Sstevel@tonic-gate 
1472*0Sstevel@tonic-gate 	/*
1473*0Sstevel@tonic-gate 	 * We do a VN_HOLD on newvp just in case the first call to
1474*0Sstevel@tonic-gate 	 * lookuppnvp() fails with ENAMETOOLONG.  We should still have a
1475*0Sstevel@tonic-gate 	 * reference to this vnode for the second call to lookuppnvp().
1476*0Sstevel@tonic-gate 	 */
1477*0Sstevel@tonic-gate 	VN_HOLD(newvp);
1478*0Sstevel@tonic-gate 
1479*0Sstevel@tonic-gate 	/*
1480*0Sstevel@tonic-gate 	 * Now create the pathname struct so we can make use of lookuppnvp,
1481*0Sstevel@tonic-gate 	 * and pn_getcomponent.
1482*0Sstevel@tonic-gate 	 * This code is similar to lookupname() in fs/lookup.c.
1483*0Sstevel@tonic-gate 	 */
1484*0Sstevel@tonic-gate 	error = pn_get_buf(path, UIO_SYSSPACE, &lookpn,
1485*0Sstevel@tonic-gate 		namebuf, sizeof (namebuf));
1486*0Sstevel@tonic-gate 	if (error == 0) {
1487*0Sstevel@tonic-gate 		error = lookuppnvp(&lookpn, NULL, NO_FOLLOW, NULLVPP,
1488*0Sstevel@tonic-gate 		    mvpp, rootdir, newvp, cred);
1489*0Sstevel@tonic-gate 	} else
1490*0Sstevel@tonic-gate 		VN_RELE(newvp);
1491*0Sstevel@tonic-gate 	if (error == ENAMETOOLONG) {
1492*0Sstevel@tonic-gate 		/*
1493*0Sstevel@tonic-gate 		 * This thread used a pathname > TYPICALMAXPATHLEN bytes long.
1494*0Sstevel@tonic-gate 		 * newvp is VN_RELE'd by this call to lookuppnvp.
1495*0Sstevel@tonic-gate 		 *
1496*0Sstevel@tonic-gate 		 * Using 'rootdir' in a zone's context is OK here: we already
1497*0Sstevel@tonic-gate 		 * ascertained that there are no '..'s in the path, and we're
1498*0Sstevel@tonic-gate 		 * not following symlinks.
1499*0Sstevel@tonic-gate 		 */
1500*0Sstevel@tonic-gate 		if ((error = pn_get(path, UIO_SYSSPACE, &lookpn)) == 0) {
1501*0Sstevel@tonic-gate 			error = lookuppnvp(&lookpn, NULL, NO_FOLLOW, NULLVPP,
1502*0Sstevel@tonic-gate 			    mvpp, rootdir, newvp, cred);
1503*0Sstevel@tonic-gate 			pn_free(&lookpn);
1504*0Sstevel@tonic-gate 		} else
1505*0Sstevel@tonic-gate 			VN_RELE(newvp);
1506*0Sstevel@tonic-gate 	} else {
1507*0Sstevel@tonic-gate 		/*
1508*0Sstevel@tonic-gate 		 * Need to release newvp here since we held it.
1509*0Sstevel@tonic-gate 		 */
1510*0Sstevel@tonic-gate 		VN_RELE(newvp);
1511*0Sstevel@tonic-gate 	}
1512*0Sstevel@tonic-gate 
1513*0Sstevel@tonic-gate done:
1514*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_getmntpnt: path=%s *mvpp=%p error=%d\n",
1515*0Sstevel@tonic-gate 	    path, (void *)*mvpp, error));
1516*0Sstevel@tonic-gate 	return (error);
1517*0Sstevel@tonic-gate }
1518*0Sstevel@tonic-gate 
1519*0Sstevel@tonic-gate #define	DEEPER(x) (((x)->fn_dirents != NULL) || \
1520*0Sstevel@tonic-gate 			(vn_mountedvfs(fntovn((x)))) != NULL)
1521*0Sstevel@tonic-gate 
1522*0Sstevel@tonic-gate /*
1523*0Sstevel@tonic-gate  * The caller, should have already VN_RELE'd its reference to the
1524*0Sstevel@tonic-gate  * root vnode of this filesystem.
1525*0Sstevel@tonic-gate  */
1526*0Sstevel@tonic-gate static int
1527*0Sstevel@tonic-gate auto_inkernel_unmount(vfs_t *vfsp)
1528*0Sstevel@tonic-gate {
1529*0Sstevel@tonic-gate 	vnode_t *cvp = vfsp->vfs_vnodecovered;
1530*0Sstevel@tonic-gate 	int error;
1531*0Sstevel@tonic-gate 
1532*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4,
1533*0Sstevel@tonic-gate 	    "auto_inkernel_unmount: devid=%lx mntpnt(%p) count %u\n",
1534*0Sstevel@tonic-gate 	    vfsp->vfs_dev, (void *)cvp, cvp->v_count));
1535*0Sstevel@tonic-gate 
1536*0Sstevel@tonic-gate 	ASSERT(vn_vfswlock_held(cvp));
1537*0Sstevel@tonic-gate 
1538*0Sstevel@tonic-gate 	/*
1539*0Sstevel@tonic-gate 	 * Perform the unmount
1540*0Sstevel@tonic-gate 	 * The mountpoint has already been locked by the caller.
1541*0Sstevel@tonic-gate 	 */
1542*0Sstevel@tonic-gate 	error = dounmount(vfsp, 0, kcred);
1543*0Sstevel@tonic-gate 
1544*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_inkernel_unmount: exit count %u\n",
1545*0Sstevel@tonic-gate 	    cvp->v_count));
1546*0Sstevel@tonic-gate 	return (error);
1547*0Sstevel@tonic-gate }
1548*0Sstevel@tonic-gate 
1549*0Sstevel@tonic-gate /*
1550*0Sstevel@tonic-gate  * unmounts trigger nodes in the kernel.
1551*0Sstevel@tonic-gate  */
1552*0Sstevel@tonic-gate static void
1553*0Sstevel@tonic-gate unmount_triggers(fnnode_t *fnp, action_list **alp)
1554*0Sstevel@tonic-gate {
1555*0Sstevel@tonic-gate 	fnnode_t *tp, *next;
1556*0Sstevel@tonic-gate 	int error = 0;
1557*0Sstevel@tonic-gate 	vfs_t *vfsp;
1558*0Sstevel@tonic-gate 	vnode_t *tvp;
1559*0Sstevel@tonic-gate 
1560*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "unmount_triggers: fnp=%p\n", (void *)fnp));
1561*0Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock));
1562*0Sstevel@tonic-gate 
1563*0Sstevel@tonic-gate 	*alp = fnp->fn_alp;
1564*0Sstevel@tonic-gate 	next = fnp->fn_trigger;
1565*0Sstevel@tonic-gate 	while ((tp = next) != NULL) {
1566*0Sstevel@tonic-gate 		tvp = fntovn(tp);
1567*0Sstevel@tonic-gate 		ASSERT(tvp->v_count >= 2);
1568*0Sstevel@tonic-gate 		next = tp->fn_next;
1569*0Sstevel@tonic-gate 		/*
1570*0Sstevel@tonic-gate 		 * drop writer's lock since the unmount will end up
1571*0Sstevel@tonic-gate 		 * disconnecting this node from fnp and needs to acquire
1572*0Sstevel@tonic-gate 		 * the writer's lock again.
1573*0Sstevel@tonic-gate 		 * next has at least a reference count >= 2 since it's
1574*0Sstevel@tonic-gate 		 * a trigger node, therefore can not be accidentally freed
1575*0Sstevel@tonic-gate 		 * by a VN_RELE
1576*0Sstevel@tonic-gate 		 */
1577*0Sstevel@tonic-gate 		rw_exit(&fnp->fn_rwlock);
1578*0Sstevel@tonic-gate 
1579*0Sstevel@tonic-gate 		vfsp = tvp->v_vfsp;
1580*0Sstevel@tonic-gate 
1581*0Sstevel@tonic-gate 		/*
1582*0Sstevel@tonic-gate 		 * Its parent was holding a reference to it, since this
1583*0Sstevel@tonic-gate 		 * is a trigger vnode.
1584*0Sstevel@tonic-gate 		 */
1585*0Sstevel@tonic-gate 		VN_RELE(tvp);
1586*0Sstevel@tonic-gate 		if (error = auto_inkernel_unmount(vfsp)) {
1587*0Sstevel@tonic-gate 			cmn_err(CE_PANIC, "unmount_triggers: "
1588*0Sstevel@tonic-gate 			    "unmount of vp=%p failed error=%d",
1589*0Sstevel@tonic-gate 			    (void *)tvp, error);
1590*0Sstevel@tonic-gate 		}
1591*0Sstevel@tonic-gate 		/*
1592*0Sstevel@tonic-gate 		 * reacquire writer's lock
1593*0Sstevel@tonic-gate 		 */
1594*0Sstevel@tonic-gate 		rw_enter(&fnp->fn_rwlock, RW_WRITER);
1595*0Sstevel@tonic-gate 	}
1596*0Sstevel@tonic-gate 
1597*0Sstevel@tonic-gate 	/*
1598*0Sstevel@tonic-gate 	 * We were holding a reference to our parent.  Drop that.
1599*0Sstevel@tonic-gate 	 */
1600*0Sstevel@tonic-gate 	VN_RELE(fntovn(fnp));
1601*0Sstevel@tonic-gate 	fnp->fn_trigger = NULL;
1602*0Sstevel@tonic-gate 	fnp->fn_alp = NULL;
1603*0Sstevel@tonic-gate 
1604*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "unmount_triggers: finished\n"));
1605*0Sstevel@tonic-gate }
1606*0Sstevel@tonic-gate 
1607*0Sstevel@tonic-gate /*
1608*0Sstevel@tonic-gate  * This routine locks the mountpoint of every trigger node if they're
1609*0Sstevel@tonic-gate  * not busy, or returns EBUSY if any node is busy. If a trigger node should
1610*0Sstevel@tonic-gate  * be unmounted first, then it sets nfnp to point to it, otherwise nfnp
1611*0Sstevel@tonic-gate  * points to NULL.
1612*0Sstevel@tonic-gate  */
1613*0Sstevel@tonic-gate static int
1614*0Sstevel@tonic-gate triggers_busy(fnnode_t *fnp, fnnode_t **nfnp)
1615*0Sstevel@tonic-gate {
1616*0Sstevel@tonic-gate 	int error = 0, done;
1617*0Sstevel@tonic-gate 	int lck_error = 0;
1618*0Sstevel@tonic-gate 	fnnode_t *tp, *t1p;
1619*0Sstevel@tonic-gate 	vfs_t *vfsp;
1620*0Sstevel@tonic-gate 
1621*0Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock));
1622*0Sstevel@tonic-gate 
1623*0Sstevel@tonic-gate 	*nfnp = NULL;
1624*0Sstevel@tonic-gate 	for (tp = fnp->fn_trigger; tp != NULL; tp = tp->fn_next) {
1625*0Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\ttrigger: %s\n", tp->fn_name));
1626*0Sstevel@tonic-gate 		vfsp = fntovn(tp)->v_vfsp;
1627*0Sstevel@tonic-gate 		error = 0;
1628*0Sstevel@tonic-gate 		/*
1629*0Sstevel@tonic-gate 		 * The vn_vfsunlock will be done in auto_inkernel_unmount.
1630*0Sstevel@tonic-gate 		 */
1631*0Sstevel@tonic-gate 		lck_error = vn_vfswlock(vfsp->vfs_vnodecovered);
1632*0Sstevel@tonic-gate 		if (lck_error == 0) {
1633*0Sstevel@tonic-gate 			mutex_enter(&tp->fn_lock);
1634*0Sstevel@tonic-gate 			ASSERT((tp->fn_flags & MF_LOOKUP) == 0);
1635*0Sstevel@tonic-gate 			if (tp->fn_flags & MF_INPROG) {
1636*0Sstevel@tonic-gate 				/*
1637*0Sstevel@tonic-gate 				 * a mount is in progress
1638*0Sstevel@tonic-gate 				 */
1639*0Sstevel@tonic-gate 				error = EBUSY;
1640*0Sstevel@tonic-gate 			}
1641*0Sstevel@tonic-gate 			mutex_exit(&tp->fn_lock);
1642*0Sstevel@tonic-gate 		}
1643*0Sstevel@tonic-gate 		if (lck_error || error || DEEPER(tp) ||
1644*0Sstevel@tonic-gate 		    ((fntovn(tp))->v_count) > 2) {
1645*0Sstevel@tonic-gate 			/*
1646*0Sstevel@tonic-gate 			 * couldn't lock it because it's busy,
1647*0Sstevel@tonic-gate 			 * It is mounted on or has dirents?
1648*0Sstevel@tonic-gate 			 * If reference count is greater than two, then
1649*0Sstevel@tonic-gate 			 * somebody else is holding a reference to this vnode.
1650*0Sstevel@tonic-gate 			 * One reference is for the mountpoint, and the second
1651*0Sstevel@tonic-gate 			 * is for the trigger node.
1652*0Sstevel@tonic-gate 			 */
1653*0Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\ttrigger busy\n"));
1654*0Sstevel@tonic-gate 			if ((lck_error == 0) && (error == 0)) {
1655*0Sstevel@tonic-gate 				*nfnp = tp;
1656*0Sstevel@tonic-gate 				/*
1657*0Sstevel@tonic-gate 				 * The matching VN_RELE is done in
1658*0Sstevel@tonic-gate 				 * unmount_tree().
1659*0Sstevel@tonic-gate 				 */
1660*0Sstevel@tonic-gate 				VN_HOLD(fntovn(*nfnp));
1661*0Sstevel@tonic-gate 			}
1662*0Sstevel@tonic-gate 			/*
1663*0Sstevel@tonic-gate 			 * Unlock previously locked mountpoints
1664*0Sstevel@tonic-gate 			 */
1665*0Sstevel@tonic-gate 			for (done = 0, t1p = fnp->fn_trigger; !done;
1666*0Sstevel@tonic-gate 			    t1p = t1p->fn_next) {
1667*0Sstevel@tonic-gate 				/*
1668*0Sstevel@tonic-gate 				 * Unlock all nodes previously
1669*0Sstevel@tonic-gate 				 * locked. All nodes up to 'tp'
1670*0Sstevel@tonic-gate 				 * were successfully locked. If 'lck_err' is
1671*0Sstevel@tonic-gate 				 * set, then 'tp' was not locked, and thus
1672*0Sstevel@tonic-gate 				 * should not be unlocked. If
1673*0Sstevel@tonic-gate 				 * 'lck_err' is not set, then 'tp' was
1674*0Sstevel@tonic-gate 				 * successfully locked, and it should
1675*0Sstevel@tonic-gate 				 * be unlocked.
1676*0Sstevel@tonic-gate 				 */
1677*0Sstevel@tonic-gate 				if (t1p != tp || !lck_error) {
1678*0Sstevel@tonic-gate 					vfsp = fntovn(t1p)->v_vfsp;
1679*0Sstevel@tonic-gate 					vn_vfsunlock(vfsp->vfs_vnodecovered);
1680*0Sstevel@tonic-gate 				}
1681*0Sstevel@tonic-gate 				done = (t1p == tp);
1682*0Sstevel@tonic-gate 			}
1683*0Sstevel@tonic-gate 			error = EBUSY;
1684*0Sstevel@tonic-gate 			break;
1685*0Sstevel@tonic-gate 		}
1686*0Sstevel@tonic-gate 	}
1687*0Sstevel@tonic-gate 
1688*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "triggers_busy: error=%d\n", error));
1689*0Sstevel@tonic-gate 	return (error);
1690*0Sstevel@tonic-gate }
1691*0Sstevel@tonic-gate 
1692*0Sstevel@tonic-gate /*
1693*0Sstevel@tonic-gate  * Unlock previously locked trigger nodes.
1694*0Sstevel@tonic-gate  */
1695*0Sstevel@tonic-gate static int
1696*0Sstevel@tonic-gate triggers_unlock(fnnode_t *fnp)
1697*0Sstevel@tonic-gate {
1698*0Sstevel@tonic-gate 	fnnode_t *tp;
1699*0Sstevel@tonic-gate 	vfs_t *vfsp;
1700*0Sstevel@tonic-gate 
1701*0Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock));
1702*0Sstevel@tonic-gate 
1703*0Sstevel@tonic-gate 	for (tp = fnp->fn_trigger; tp != NULL; tp = tp->fn_next) {
1704*0Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tunlock trigger: %s\n", tp->fn_name));
1705*0Sstevel@tonic-gate 		vfsp = fntovn(tp)->v_vfsp;
1706*0Sstevel@tonic-gate 		vn_vfsunlock(vfsp->vfs_vnodecovered);
1707*0Sstevel@tonic-gate 	}
1708*0Sstevel@tonic-gate 
1709*0Sstevel@tonic-gate 	return (0);
1710*0Sstevel@tonic-gate }
1711*0Sstevel@tonic-gate 
1712*0Sstevel@tonic-gate /*
1713*0Sstevel@tonic-gate  * It is the caller's responsibility to grab the VVFSLOCK.
1714*0Sstevel@tonic-gate  * Releases the VVFSLOCK upon return.
1715*0Sstevel@tonic-gate  */
1716*0Sstevel@tonic-gate static int
1717*0Sstevel@tonic-gate unmount_node(vnode_t *cvp, int force)
1718*0Sstevel@tonic-gate {
1719*0Sstevel@tonic-gate 	int error = 0;
1720*0Sstevel@tonic-gate 	fnnode_t *cfnp;
1721*0Sstevel@tonic-gate 	vfs_t *vfsp;
1722*0Sstevel@tonic-gate 	umntrequest ul;
1723*0Sstevel@tonic-gate 	fninfo_t *fnip;
1724*0Sstevel@tonic-gate 
1725*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tunmount_node cvp=%p\n", (void *)cvp));
1726*0Sstevel@tonic-gate 
1727*0Sstevel@tonic-gate 	ASSERT(vn_vfswlock_held(cvp));
1728*0Sstevel@tonic-gate 	cfnp = vntofn(cvp);
1729*0Sstevel@tonic-gate 	vfsp = vn_mountedvfs(cvp);
1730*0Sstevel@tonic-gate 
1731*0Sstevel@tonic-gate 	if (force || cfnp->fn_flags & MF_IK_MOUNT) {
1732*0Sstevel@tonic-gate 		/*
1733*0Sstevel@tonic-gate 		 * Mount was performed in the kernel, so
1734*0Sstevel@tonic-gate 		 * do an in-kernel unmount. auto_inkernel_unmount()
1735*0Sstevel@tonic-gate 		 * will vn_vfsunlock(cvp).
1736*0Sstevel@tonic-gate 		 */
1737*0Sstevel@tonic-gate 		error = auto_inkernel_unmount(vfsp);
1738*0Sstevel@tonic-gate 	} else {
1739*0Sstevel@tonic-gate 		zone_t *zone = NULL;
1740*0Sstevel@tonic-gate 		refstr_t *mntpt, *resource;
1741*0Sstevel@tonic-gate 		size_t mntoptslen;
1742*0Sstevel@tonic-gate 
1743*0Sstevel@tonic-gate 		/*
1744*0Sstevel@tonic-gate 		 * Get the mnttab information of the node
1745*0Sstevel@tonic-gate 		 * and ask the daemon to unmount it.
1746*0Sstevel@tonic-gate 		 */
1747*0Sstevel@tonic-gate 		bzero(&ul, sizeof (ul));
1748*0Sstevel@tonic-gate 		mntfs_getmntopts(vfsp, &ul.mntopts, &mntoptslen);
1749*0Sstevel@tonic-gate 		if (ul.mntopts == NULL) {
1750*0Sstevel@tonic-gate 			auto_log(cfnp->fn_globals, CE_WARN, "unmount_node: "
1751*0Sstevel@tonic-gate 			    "no memory");
1752*0Sstevel@tonic-gate 			vn_vfsunlock(cvp);
1753*0Sstevel@tonic-gate 			error = ENOMEM;
1754*0Sstevel@tonic-gate 			goto done;
1755*0Sstevel@tonic-gate 		}
1756*0Sstevel@tonic-gate 		if (mntoptslen > AUTOFS_MAXOPTSLEN)
1757*0Sstevel@tonic-gate 			ul.mntopts[AUTOFS_MAXOPTSLEN - 1] = '\0';
1758*0Sstevel@tonic-gate 
1759*0Sstevel@tonic-gate 		mntpt = vfs_getmntpoint(vfsp);
1760*0Sstevel@tonic-gate 		ul.mntpnt = (char *)refstr_value(mntpt);
1761*0Sstevel@tonic-gate 		resource = vfs_getresource(vfsp);
1762*0Sstevel@tonic-gate 		ul.mntresource = (char *)refstr_value(resource);
1763*0Sstevel@tonic-gate 
1764*0Sstevel@tonic-gate 		fnip = vfstofni(cvp->v_vfsp);
1765*0Sstevel@tonic-gate 		ul.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE;
1766*0Sstevel@tonic-gate 
1767*0Sstevel@tonic-gate 		/*
1768*0Sstevel@tonic-gate 		 * Since a zone'd automountd's view of the autofs mount points
1769*0Sstevel@tonic-gate 		 * differs from those in the kernel, we need to make sure we
1770*0Sstevel@tonic-gate 		 * give it consistent mount points.
1771*0Sstevel@tonic-gate 		 */
1772*0Sstevel@tonic-gate 		ASSERT(fnip->fi_zoneid == getzoneid());
1773*0Sstevel@tonic-gate 		zone = curproc->p_zone;
1774*0Sstevel@tonic-gate 
1775*0Sstevel@tonic-gate 		if (fnip->fi_zoneid != GLOBAL_ZONEID) {
1776*0Sstevel@tonic-gate 			if (ZONE_PATH_VISIBLE(ul.mntpnt, zone)) {
1777*0Sstevel@tonic-gate 				ul.mntpnt =
1778*0Sstevel@tonic-gate 				    ZONE_PATH_TRANSLATE(ul.mntpnt, zone);
1779*0Sstevel@tonic-gate 			}
1780*0Sstevel@tonic-gate 			if (ZONE_PATH_VISIBLE(ul.mntresource, zone)) {
1781*0Sstevel@tonic-gate 				ul.mntresource =
1782*0Sstevel@tonic-gate 				    ZONE_PATH_TRANSLATE(ul.mntresource, zone);
1783*0Sstevel@tonic-gate 			}
1784*0Sstevel@tonic-gate 		}
1785*0Sstevel@tonic-gate 		ul.fstype = vfssw[vfsp->vfs_fstype].vsw_name;
1786*0Sstevel@tonic-gate 		vn_vfsunlock(cvp);
1787*0Sstevel@tonic-gate 
1788*0Sstevel@tonic-gate 		error = auto_send_unmount_request(fnip, &ul, CRED(), FALSE);
1789*0Sstevel@tonic-gate 		kmem_free(ul.mntopts, mntoptslen);
1790*0Sstevel@tonic-gate 		refstr_rele(mntpt);
1791*0Sstevel@tonic-gate 		refstr_rele(resource);
1792*0Sstevel@tonic-gate 	}
1793*0Sstevel@tonic-gate 
1794*0Sstevel@tonic-gate done:
1795*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tunmount_node cvp=%p error=%d\n", (void *)cvp,
1796*0Sstevel@tonic-gate 	    error));
1797*0Sstevel@tonic-gate 	return (error);
1798*0Sstevel@tonic-gate }
1799*0Sstevel@tonic-gate 
1800*0Sstevel@tonic-gate /*
1801*0Sstevel@tonic-gate  * vp is the "root" of the AUTOFS filesystem.
1802*0Sstevel@tonic-gate  * return EBUSY if any thread is holding a reference to this vnode
1803*0Sstevel@tonic-gate  * other than us.
1804*0Sstevel@tonic-gate  */
1805*0Sstevel@tonic-gate static int
1806*0Sstevel@tonic-gate check_auto_node(vnode_t *vp)
1807*0Sstevel@tonic-gate {
1808*0Sstevel@tonic-gate 	fnnode_t *fnp;
1809*0Sstevel@tonic-gate 	int error = 0;
1810*0Sstevel@tonic-gate 	/*
1811*0Sstevel@tonic-gate 	 * number of references to expect for
1812*0Sstevel@tonic-gate 	 * a non-busy vnode.
1813*0Sstevel@tonic-gate 	 */
1814*0Sstevel@tonic-gate 	uint_t count;
1815*0Sstevel@tonic-gate 
1816*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tcheck_auto_node vp=%p ", (void *)vp));
1817*0Sstevel@tonic-gate 	fnp = vntofn(vp);
1818*0Sstevel@tonic-gate 	ASSERT(fnp->fn_flags & MF_INPROG);
1819*0Sstevel@tonic-gate 	ASSERT((fnp->fn_flags & MF_LOOKUP) == 0);
1820*0Sstevel@tonic-gate 
1821*0Sstevel@tonic-gate 	count = 1;		/* we are holding a reference to vp */
1822*0Sstevel@tonic-gate 	if (fnp->fn_flags & MF_TRIGGER) {
1823*0Sstevel@tonic-gate 		/*
1824*0Sstevel@tonic-gate 		 * parent holds a pointer to us (trigger)
1825*0Sstevel@tonic-gate 		 */
1826*0Sstevel@tonic-gate 		count++;
1827*0Sstevel@tonic-gate 	}
1828*0Sstevel@tonic-gate 	if (fnp->fn_trigger != NULL) {
1829*0Sstevel@tonic-gate 		/*
1830*0Sstevel@tonic-gate 		 * The trigger nodes have a hold on us.
1831*0Sstevel@tonic-gate 		 */
1832*0Sstevel@tonic-gate 		count++;
1833*0Sstevel@tonic-gate 	}
1834*0Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
1835*0Sstevel@tonic-gate 	if (vp->v_flag & VROOT)
1836*0Sstevel@tonic-gate 		count++;
1837*0Sstevel@tonic-gate 	ASSERT(vp->v_count > 0);
1838*0Sstevel@tonic-gate 	AUTOFS_DPRINT((10, "\tcount=%u ", vp->v_count));
1839*0Sstevel@tonic-gate 	if (vp->v_count > count)
1840*0Sstevel@tonic-gate 		error = EBUSY;
1841*0Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
1842*0Sstevel@tonic-gate 
1843*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tcheck_auto_node error=%d ", error));
1844*0Sstevel@tonic-gate 	return (error);
1845*0Sstevel@tonic-gate }
1846*0Sstevel@tonic-gate 
1847*0Sstevel@tonic-gate /*
1848*0Sstevel@tonic-gate  * rootvp is the root of the AUTOFS filesystem.
1849*0Sstevel@tonic-gate  * If rootvp is busy (v_count > 1) returns EBUSY.
1850*0Sstevel@tonic-gate  * else removes every vnode under this tree.
1851*0Sstevel@tonic-gate  * ASSUMPTION: Assumes that the only node which can be busy is
1852*0Sstevel@tonic-gate  * the root vnode. This filesystem better be two levels deep only,
1853*0Sstevel@tonic-gate  * the root and its immediate subdirs.
1854*0Sstevel@tonic-gate  * The daemon will "AUTOFS direct-mount" only one level below the root.
1855*0Sstevel@tonic-gate  */
1856*0Sstevel@tonic-gate static int
1857*0Sstevel@tonic-gate unmount_autofs(vnode_t *rootvp)
1858*0Sstevel@tonic-gate {
1859*0Sstevel@tonic-gate 	fnnode_t *fnp, *rootfnp, *nfnp;
1860*0Sstevel@tonic-gate 	int error;
1861*0Sstevel@tonic-gate 
1862*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "\tunmount_autofs rootvp=%p ", (void *)rootvp));
1863*0Sstevel@tonic-gate 
1864*0Sstevel@tonic-gate 	error = check_auto_node(rootvp);
1865*0Sstevel@tonic-gate 	if (error == 0) {
1866*0Sstevel@tonic-gate 		/*
1867*0Sstevel@tonic-gate 		 * Remove all its immediate subdirectories.
1868*0Sstevel@tonic-gate 		 */
1869*0Sstevel@tonic-gate 		rootfnp = vntofn(rootvp);
1870*0Sstevel@tonic-gate 		rw_enter(&rootfnp->fn_rwlock, RW_WRITER);
1871*0Sstevel@tonic-gate 		nfnp = NULL;	/* lint clean */
1872*0Sstevel@tonic-gate 		for (fnp = rootfnp->fn_dirents; fnp != NULL; fnp = nfnp) {
1873*0Sstevel@tonic-gate 			ASSERT(fntovn(fnp)->v_count == 0);
1874*0Sstevel@tonic-gate 			ASSERT(fnp->fn_dirents == NULL);
1875*0Sstevel@tonic-gate 			ASSERT(fnp->fn_linkcnt == 2);
1876*0Sstevel@tonic-gate 			fnp->fn_linkcnt--;
1877*0Sstevel@tonic-gate 			auto_disconnect(rootfnp, fnp);
1878*0Sstevel@tonic-gate 			nfnp = fnp->fn_next;
1879*0Sstevel@tonic-gate 			auto_freefnnode(fnp);
1880*0Sstevel@tonic-gate 		}
1881*0Sstevel@tonic-gate 		rw_exit(&rootfnp->fn_rwlock);
1882*0Sstevel@tonic-gate 	}
1883*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "\tunmount_autofs error=%d ", error));
1884*0Sstevel@tonic-gate 	return (error);
1885*0Sstevel@tonic-gate }
1886*0Sstevel@tonic-gate 
1887*0Sstevel@tonic-gate /*
1888*0Sstevel@tonic-gate  * max number of unmount threads running
1889*0Sstevel@tonic-gate  */
1890*0Sstevel@tonic-gate static int autofs_unmount_threads = 5;
1891*0Sstevel@tonic-gate 
1892*0Sstevel@tonic-gate /*
1893*0Sstevel@tonic-gate  * XXX unmount_tree() is not suspend-safe within the scope of
1894*0Sstevel@tonic-gate  * the present model defined for cpr to suspend the system. Calls made
1895*0Sstevel@tonic-gate  * by the unmount_tree() that have been identified to be unsafe are
1896*0Sstevel@tonic-gate  * (1) RPC client handle setup and client calls to automountd which can
1897*0Sstevel@tonic-gate  * block deep down in the RPC library, (2) kmem_alloc() calls with the
1898*0Sstevel@tonic-gate  * KM_SLEEP flag which can block if memory is low, and (3) VFS_*() and
1899*0Sstevel@tonic-gate  * VOP_*() calls which can result in over the wire calls to servers.
1900*0Sstevel@tonic-gate  * The thread should be completely reevaluated to make it suspend-safe in
1901*0Sstevel@tonic-gate  * case of future updates to the cpr model.
1902*0Sstevel@tonic-gate  */
1903*0Sstevel@tonic-gate void
1904*0Sstevel@tonic-gate unmount_tree(struct autofs_globals *fngp, int force)
1905*0Sstevel@tonic-gate {
1906*0Sstevel@tonic-gate 	vnode_t *vp, *newvp;
1907*0Sstevel@tonic-gate 	vfs_t *vfsp;
1908*0Sstevel@tonic-gate 	fnnode_t *fnp, *nfnp, *pfnp;
1909*0Sstevel@tonic-gate 	action_list *alp;
1910*0Sstevel@tonic-gate 	int error, ilocked_it = 0;
1911*0Sstevel@tonic-gate 	fninfo_t *fnip;
1912*0Sstevel@tonic-gate 	time_t ref_time;
1913*0Sstevel@tonic-gate 	int autofs_busy_root, unmount_as_unit, unmount_done = 0;
1914*0Sstevel@tonic-gate 	timestruc_t now;
1915*0Sstevel@tonic-gate 
1916*0Sstevel@tonic-gate 	callb_cpr_t cprinfo;
1917*0Sstevel@tonic-gate 	kmutex_t unmount_tree_cpr_lock;
1918*0Sstevel@tonic-gate 
1919*0Sstevel@tonic-gate 	mutex_init(&unmount_tree_cpr_lock, NULL, MUTEX_DEFAULT, NULL);
1920*0Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, &unmount_tree_cpr_lock, callb_generic_cpr,
1921*0Sstevel@tonic-gate 		"unmount_tree");
1922*0Sstevel@tonic-gate 
1923*0Sstevel@tonic-gate 	/*
1924*0Sstevel@tonic-gate 	 * Got to release lock before attempting unmount in case
1925*0Sstevel@tonic-gate 	 * it hangs.
1926*0Sstevel@tonic-gate 	 */
1927*0Sstevel@tonic-gate 	rw_enter(&fngp->fng_rootfnnodep->fn_rwlock, RW_READER);
1928*0Sstevel@tonic-gate 	if ((fnp = fngp->fng_rootfnnodep->fn_dirents) == NULL) {
1929*0Sstevel@tonic-gate 		ASSERT(fngp->fng_fnnode_count == 1);
1930*0Sstevel@tonic-gate 		/*
1931*0Sstevel@tonic-gate 		 * no autofs mounted, done.
1932*0Sstevel@tonic-gate 		 */
1933*0Sstevel@tonic-gate 		rw_exit(&fngp->fng_rootfnnodep->fn_rwlock);
1934*0Sstevel@tonic-gate 		goto done;
1935*0Sstevel@tonic-gate 	}
1936*0Sstevel@tonic-gate 	VN_HOLD(fntovn(fnp));
1937*0Sstevel@tonic-gate 	rw_exit(&fngp->fng_rootfnnodep->fn_rwlock);
1938*0Sstevel@tonic-gate 
1939*0Sstevel@tonic-gate 	vp = fntovn(fnp);
1940*0Sstevel@tonic-gate 	fnip = vfstofni(vp->v_vfsp);
1941*0Sstevel@tonic-gate 	/*
1942*0Sstevel@tonic-gate 	 * autofssys() will be calling in from the global zone and doing
1943*0Sstevel@tonic-gate 	 * work on the behalf of the given zone, hence we can't always assert
1944*0Sstevel@tonic-gate 	 * that we have the right credentials, nor that the caller is always in
1945*0Sstevel@tonic-gate 	 * the correct zone.
1946*0Sstevel@tonic-gate 	 *
1947*0Sstevel@tonic-gate 	 * We do, however, know that if this is a "forced unmount" operation
1948*0Sstevel@tonic-gate 	 * (which autofssys() does), then we won't go down to the krpc layers,
1949*0Sstevel@tonic-gate 	 * so we don't need to fudge with the credentials.
1950*0Sstevel@tonic-gate 	 */
1951*0Sstevel@tonic-gate 	ASSERT(force || fnip->fi_zoneid == getzoneid());
1952*0Sstevel@tonic-gate 	if (!force && auto_null_request(fnip, kcred, FALSE) != 0) {
1953*0Sstevel@tonic-gate 		/*
1954*0Sstevel@tonic-gate 		 * automountd not running in this zone,
1955*0Sstevel@tonic-gate 		 * don't attempt unmounting this round.
1956*0Sstevel@tonic-gate 		 */
1957*0Sstevel@tonic-gate 		VN_RELE(vp);
1958*0Sstevel@tonic-gate 		goto done;
1959*0Sstevel@tonic-gate 	}
1960*0Sstevel@tonic-gate 	/* reference time for this unmount round */
1961*0Sstevel@tonic-gate 	ref_time = gethrestime_sec();
1962*0Sstevel@tonic-gate 	/*
1963*0Sstevel@tonic-gate 	 * If this an autofssys() call, we need to make sure we don't skip
1964*0Sstevel@tonic-gate 	 * nodes because we think we saw them recently.
1965*0Sstevel@tonic-gate 	 */
1966*0Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
1967*0Sstevel@tonic-gate 	if (force && fnp->fn_unmount_ref_time >= ref_time)
1968*0Sstevel@tonic-gate 		ref_time = fnp->fn_unmount_ref_time + 1;
1969*0Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
1970*0Sstevel@tonic-gate 
1971*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "unmount_tree (ID=%ld)\n", ref_time));
1972*0Sstevel@tonic-gate top:
1973*0Sstevel@tonic-gate 	AUTOFS_DPRINT((10, "unmount_tree: %s\n", fnp->fn_name));
1974*0Sstevel@tonic-gate 	ASSERT(fnp);
1975*0Sstevel@tonic-gate 	vp = fntovn(fnp);
1976*0Sstevel@tonic-gate 	if (vp->v_type == VLNK) {
1977*0Sstevel@tonic-gate 		/*
1978*0Sstevel@tonic-gate 		 * can't unmount symbolic links
1979*0Sstevel@tonic-gate 		 */
1980*0Sstevel@tonic-gate 		goto next;
1981*0Sstevel@tonic-gate 	}
1982*0Sstevel@tonic-gate 	fnip = vfstofni(vp->v_vfsp);
1983*0Sstevel@tonic-gate 	ASSERT(vp->v_count > 0);
1984*0Sstevel@tonic-gate 	error = 0;
1985*0Sstevel@tonic-gate 	autofs_busy_root = unmount_as_unit = 0;
1986*0Sstevel@tonic-gate 	alp = NULL;
1987*0Sstevel@tonic-gate 
1988*0Sstevel@tonic-gate 	ilocked_it = 0;
1989*0Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
1990*0Sstevel@tonic-gate 	if (fnp->fn_flags & (MF_INPROG | MF_LOOKUP)) {
1991*0Sstevel@tonic-gate 		/*
1992*0Sstevel@tonic-gate 		 * Either a mount, lookup or another unmount of this
1993*0Sstevel@tonic-gate 		 * subtree is in progress, don't attempt to unmount at
1994*0Sstevel@tonic-gate 		 * this time.
1995*0Sstevel@tonic-gate 		 */
1996*0Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
1997*0Sstevel@tonic-gate 		error = EBUSY;
1998*0Sstevel@tonic-gate 		goto next;
1999*0Sstevel@tonic-gate 	}
2000*0Sstevel@tonic-gate 	if (fnp->fn_unmount_ref_time >= ref_time) {
2001*0Sstevel@tonic-gate 		/*
2002*0Sstevel@tonic-gate 		 * Already been here, try next node.
2003*0Sstevel@tonic-gate 		 */
2004*0Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
2005*0Sstevel@tonic-gate 		error = EBUSY;
2006*0Sstevel@tonic-gate 		goto next;
2007*0Sstevel@tonic-gate 	}
2008*0Sstevel@tonic-gate 	fnp->fn_unmount_ref_time = ref_time;
2009*0Sstevel@tonic-gate 
2010*0Sstevel@tonic-gate 	/*
2011*0Sstevel@tonic-gate 	 * If forced operation ignore timeout values
2012*0Sstevel@tonic-gate 	 */
2013*0Sstevel@tonic-gate 	if (!force && fnp->fn_ref_time + fnip->fi_mount_to >
2014*0Sstevel@tonic-gate 	    gethrestime_sec()) {
2015*0Sstevel@tonic-gate 		/*
2016*0Sstevel@tonic-gate 		 * Node has been referenced recently, try the
2017*0Sstevel@tonic-gate 		 * unmount of its children if any.
2018*0Sstevel@tonic-gate 		 */
2019*0Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
2020*0Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "fn_ref_time within range\n"));
2021*0Sstevel@tonic-gate 		rw_enter(&fnp->fn_rwlock, RW_READER);
2022*0Sstevel@tonic-gate 		if (fnp->fn_dirents) {
2023*0Sstevel@tonic-gate 			/*
2024*0Sstevel@tonic-gate 			 * Has subdirectory, attempt their
2025*0Sstevel@tonic-gate 			 * unmount first
2026*0Sstevel@tonic-gate 			 */
2027*0Sstevel@tonic-gate 			nfnp = fnp->fn_dirents;
2028*0Sstevel@tonic-gate 			VN_HOLD(fntovn(nfnp));
2029*0Sstevel@tonic-gate 			rw_exit(&fnp->fn_rwlock);
2030*0Sstevel@tonic-gate 
2031*0Sstevel@tonic-gate 			VN_RELE(vp);
2032*0Sstevel@tonic-gate 			fnp = nfnp;
2033*0Sstevel@tonic-gate 			goto top;
2034*0Sstevel@tonic-gate 		}
2035*0Sstevel@tonic-gate 		rw_exit(&fnp->fn_rwlock);
2036*0Sstevel@tonic-gate 		/*
2037*0Sstevel@tonic-gate 		 * No children, try next node.
2038*0Sstevel@tonic-gate 		 */
2039*0Sstevel@tonic-gate 		error = EBUSY;
2040*0Sstevel@tonic-gate 		goto next;
2041*0Sstevel@tonic-gate 	}
2042*0Sstevel@tonic-gate 
2043*0Sstevel@tonic-gate 	AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG);
2044*0Sstevel@tonic-gate 	fnp->fn_error = 0;
2045*0Sstevel@tonic-gate 	mutex_exit(&fnp->fn_lock);
2046*0Sstevel@tonic-gate 	ilocked_it = 1;
2047*0Sstevel@tonic-gate 
2048*0Sstevel@tonic-gate 	rw_enter(&fnp->fn_rwlock, RW_WRITER);
2049*0Sstevel@tonic-gate 	if (fnp->fn_trigger != NULL) {
2050*0Sstevel@tonic-gate 		unmount_as_unit = 1;
2051*0Sstevel@tonic-gate 		if ((vn_mountedvfs(vp) == NULL) && (check_auto_node(vp))) {
2052*0Sstevel@tonic-gate 			/*
2053*0Sstevel@tonic-gate 			 * AUTOFS mountpoint is busy, there's
2054*0Sstevel@tonic-gate 			 * no point trying to unmount. Fall through
2055*0Sstevel@tonic-gate 			 * to attempt to unmount subtrees rooted
2056*0Sstevel@tonic-gate 			 * at a possible trigger node, but remember
2057*0Sstevel@tonic-gate 			 * not to unmount this tree.
2058*0Sstevel@tonic-gate 			 */
2059*0Sstevel@tonic-gate 			autofs_busy_root = 1;
2060*0Sstevel@tonic-gate 		}
2061*0Sstevel@tonic-gate 
2062*0Sstevel@tonic-gate 		if (triggers_busy(fnp, &nfnp)) {
2063*0Sstevel@tonic-gate 			rw_exit(&fnp->fn_rwlock);
2064*0Sstevel@tonic-gate 			if (nfnp == NULL) {
2065*0Sstevel@tonic-gate 				error = EBUSY;
2066*0Sstevel@tonic-gate 				goto next;
2067*0Sstevel@tonic-gate 			}
2068*0Sstevel@tonic-gate 			/*
2069*0Sstevel@tonic-gate 			 * nfnp is busy, try to unmount it first
2070*0Sstevel@tonic-gate 			 */
2071*0Sstevel@tonic-gate 			mutex_enter(&fnp->fn_lock);
2072*0Sstevel@tonic-gate 			AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
2073*0Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
2074*0Sstevel@tonic-gate 			VN_RELE(vp);
2075*0Sstevel@tonic-gate 			ASSERT(fntovn(nfnp)->v_count > 1);
2076*0Sstevel@tonic-gate 			fnp = nfnp;
2077*0Sstevel@tonic-gate 			goto top;
2078*0Sstevel@tonic-gate 		}
2079*0Sstevel@tonic-gate 
2080*0Sstevel@tonic-gate 		/*
2081*0Sstevel@tonic-gate 		 * At this point, we know all trigger nodes are locked,
2082*0Sstevel@tonic-gate 		 * and they're not busy or mounted on.
2083*0Sstevel@tonic-gate 		 */
2084*0Sstevel@tonic-gate 
2085*0Sstevel@tonic-gate 		if (autofs_busy_root) {
2086*0Sstevel@tonic-gate 			/*
2087*0Sstevel@tonic-gate 			 * Got to unlock the the trigger nodes since
2088*0Sstevel@tonic-gate 			 * I'm not really going to unmount the filesystem.
2089*0Sstevel@tonic-gate 			 */
2090*0Sstevel@tonic-gate 			(void) triggers_unlock(fnp);
2091*0Sstevel@tonic-gate 		} else {
2092*0Sstevel@tonic-gate 			/*
2093*0Sstevel@tonic-gate 			 * Attempt to unmount all the trigger nodes,
2094*0Sstevel@tonic-gate 			 * save the action_list in case we need to
2095*0Sstevel@tonic-gate 			 * remount them later. The action_list will be XDR
2096*0Sstevel@tonic-gate 			 * freed later if there was no need to remount the
2097*0Sstevel@tonic-gate 			 * trigger nodes.
2098*0Sstevel@tonic-gate 			 */
2099*0Sstevel@tonic-gate 			unmount_triggers(fnp, &alp);
2100*0Sstevel@tonic-gate 		}
2101*0Sstevel@tonic-gate 	}
2102*0Sstevel@tonic-gate 	rw_exit(&fnp->fn_rwlock);
2103*0Sstevel@tonic-gate 
2104*0Sstevel@tonic-gate 	if (autofs_busy_root)
2105*0Sstevel@tonic-gate 		goto next;
2106*0Sstevel@tonic-gate 
2107*0Sstevel@tonic-gate 	(void) vn_vfswlock_wait(vp);
2108*0Sstevel@tonic-gate 
2109*0Sstevel@tonic-gate 	vfsp = vn_mountedvfs(vp);
2110*0Sstevel@tonic-gate 	if (vfsp != NULL) {
2111*0Sstevel@tonic-gate 		/*
2112*0Sstevel@tonic-gate 		 * Node is mounted on.
2113*0Sstevel@tonic-gate 		 */
2114*0Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tNode is mounted on\n"));
2115*0Sstevel@tonic-gate 
2116*0Sstevel@tonic-gate 		/*
2117*0Sstevel@tonic-gate 		 * Deal with /xfn/host/jurassic alikes here...
2118*0Sstevel@tonic-gate 		 */
2119*0Sstevel@tonic-gate 		if (vfs_matchops(vfsp, vfs_getops(vp->v_vfsp))) {
2120*0Sstevel@tonic-gate 			/*
2121*0Sstevel@tonic-gate 			 * If the filesystem mounted here is AUTOFS, and it
2122*0Sstevel@tonic-gate 			 * is busy, try to unmount the tree rooted on it
2123*0Sstevel@tonic-gate 			 * first. We know this call to VFS_ROOT is safe to
2124*0Sstevel@tonic-gate 			 * call while holding VVFSLOCK, since it resolves
2125*0Sstevel@tonic-gate 			 * to a call to auto_root().
2126*0Sstevel@tonic-gate 			 */
2127*0Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\t\tAUTOFS mounted here\n"));
2128*0Sstevel@tonic-gate 			vfs_lock_wait(vfsp);
2129*0Sstevel@tonic-gate 			if (VFS_ROOT(vfsp, &newvp)) {
2130*0Sstevel@tonic-gate 				cmn_err(CE_PANIC,
2131*0Sstevel@tonic-gate 				    "unmount_tree: VFS_ROOT(vfs=%p) failed",
2132*0Sstevel@tonic-gate 				    (void *)vfsp);
2133*0Sstevel@tonic-gate 			}
2134*0Sstevel@tonic-gate 			vfs_unlock(vfsp);
2135*0Sstevel@tonic-gate 			nfnp = vntofn(newvp);
2136*0Sstevel@tonic-gate 			if (DEEPER(nfnp)) {
2137*0Sstevel@tonic-gate 				vn_vfsunlock(vp);
2138*0Sstevel@tonic-gate 				mutex_enter(&fnp->fn_lock);
2139*0Sstevel@tonic-gate 				AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
2140*0Sstevel@tonic-gate 				mutex_exit(&fnp->fn_lock);
2141*0Sstevel@tonic-gate 				VN_RELE(vp);
2142*0Sstevel@tonic-gate 				fnp = nfnp;
2143*0Sstevel@tonic-gate 				goto top;
2144*0Sstevel@tonic-gate 			}
2145*0Sstevel@tonic-gate 			/*
2146*0Sstevel@tonic-gate 			 * Fall through to unmount this filesystem
2147*0Sstevel@tonic-gate 			 */
2148*0Sstevel@tonic-gate 			VN_RELE(newvp);
2149*0Sstevel@tonic-gate 		}
2150*0Sstevel@tonic-gate 
2151*0Sstevel@tonic-gate 		/*
2152*0Sstevel@tonic-gate 		 * vn_vfsunlock(vp) is done inside unmount_node()
2153*0Sstevel@tonic-gate 		 */
2154*0Sstevel@tonic-gate 		error = unmount_node(vp, force);
2155*0Sstevel@tonic-gate 		if (error == ECONNRESET) {
2156*0Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tConnection dropped\n"));
2157*0Sstevel@tonic-gate 			if (vn_mountedvfs(vp) == NULL) {
2158*0Sstevel@tonic-gate 				/*
2159*0Sstevel@tonic-gate 				 * The filesystem was unmounted before the
2160*0Sstevel@tonic-gate 				 * daemon died. Unfortunately we can not
2161*0Sstevel@tonic-gate 				 * determine whether all the cleanup work was
2162*0Sstevel@tonic-gate 				 * successfully finished (i.e. update mnttab,
2163*0Sstevel@tonic-gate 				 * or notify NFS server of the unmount).
2164*0Sstevel@tonic-gate 				 * We should not retry the operation since the
2165*0Sstevel@tonic-gate 				 * filesystem has already been unmounted, and
2166*0Sstevel@tonic-gate 				 * may have already been removed from mnttab,
2167*0Sstevel@tonic-gate 				 * in such case the devid/rdevid we send to
2168*0Sstevel@tonic-gate 				 * the daemon will not be matched. So we have
2169*0Sstevel@tonic-gate 				 * to be contempt with the partial unmount.
2170*0Sstevel@tonic-gate 				 * Since the mountpoint is no longer covered, we
2171*0Sstevel@tonic-gate 				 * clear the error condition.
2172*0Sstevel@tonic-gate 				 */
2173*0Sstevel@tonic-gate 				error = 0;
2174*0Sstevel@tonic-gate 				auto_log(fngp, CE_WARN,
2175*0Sstevel@tonic-gate 				    "unmount_tree: automountd connection "
2176*0Sstevel@tonic-gate 				    "dropped");
2177*0Sstevel@tonic-gate 				if (fnip->fi_flags & MF_DIRECT) {
2178*0Sstevel@tonic-gate 					auto_log(fngp, CE_WARN, "unmount_tree: "
2179*0Sstevel@tonic-gate 					    "%s successfully unmounted - "
2180*0Sstevel@tonic-gate 					    "do not remount triggers",
2181*0Sstevel@tonic-gate 					    fnip->fi_path);
2182*0Sstevel@tonic-gate 				} else {
2183*0Sstevel@tonic-gate 					auto_log(fngp, CE_WARN, "unmount_tree: "
2184*0Sstevel@tonic-gate 					    "%s/%s successfully unmounted - "
2185*0Sstevel@tonic-gate 					    "do not remount triggers",
2186*0Sstevel@tonic-gate 					    fnip->fi_path, fnp->fn_name);
2187*0Sstevel@tonic-gate 				}
2188*0Sstevel@tonic-gate 			}
2189*0Sstevel@tonic-gate 		}
2190*0Sstevel@tonic-gate 	} else {
2191*0Sstevel@tonic-gate 		vn_vfsunlock(vp);
2192*0Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tNode is AUTOFS\n"));
2193*0Sstevel@tonic-gate 		if (unmount_as_unit) {
2194*0Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tunmount as unit\n"));
2195*0Sstevel@tonic-gate 			error = unmount_autofs(vp);
2196*0Sstevel@tonic-gate 		} else {
2197*0Sstevel@tonic-gate 			AUTOFS_DPRINT((10, "\tunmount one at a time\n"));
2198*0Sstevel@tonic-gate 			rw_enter(&fnp->fn_rwlock, RW_READER);
2199*0Sstevel@tonic-gate 			if (fnp->fn_dirents != NULL) {
2200*0Sstevel@tonic-gate 				/*
2201*0Sstevel@tonic-gate 				 * Has subdirectory, attempt their
2202*0Sstevel@tonic-gate 				 * unmount first
2203*0Sstevel@tonic-gate 				 */
2204*0Sstevel@tonic-gate 				nfnp = fnp->fn_dirents;
2205*0Sstevel@tonic-gate 				VN_HOLD(fntovn(nfnp));
2206*0Sstevel@tonic-gate 				rw_exit(&fnp->fn_rwlock);
2207*0Sstevel@tonic-gate 
2208*0Sstevel@tonic-gate 				mutex_enter(&fnp->fn_lock);
2209*0Sstevel@tonic-gate 				AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
2210*0Sstevel@tonic-gate 				mutex_exit(&fnp->fn_lock);
2211*0Sstevel@tonic-gate 				VN_RELE(vp);
2212*0Sstevel@tonic-gate 				fnp = nfnp;
2213*0Sstevel@tonic-gate 				goto top;
2214*0Sstevel@tonic-gate 			}
2215*0Sstevel@tonic-gate 			rw_exit(&fnp->fn_rwlock);
2216*0Sstevel@tonic-gate 			goto next;
2217*0Sstevel@tonic-gate 		}
2218*0Sstevel@tonic-gate 	}
2219*0Sstevel@tonic-gate 
2220*0Sstevel@tonic-gate 	if (error) {
2221*0Sstevel@tonic-gate 		AUTOFS_DPRINT((10, "\tUnmount failed\n"));
2222*0Sstevel@tonic-gate 		if (alp != NULL) {
2223*0Sstevel@tonic-gate 			/*
2224*0Sstevel@tonic-gate 			 * Unmount failed, got to remount triggers.
2225*0Sstevel@tonic-gate 			 */
2226*0Sstevel@tonic-gate 			ASSERT((fnp->fn_flags & MF_THISUID_MATCH_RQD) == 0);
2227*0Sstevel@tonic-gate 			error = auto_perform_actions(fnip, fnp, alp, CRED());
2228*0Sstevel@tonic-gate 			if (error) {
2229*0Sstevel@tonic-gate 				auto_log(fngp, CE_WARN, "autofs: can't remount "
2230*0Sstevel@tonic-gate 				    "triggers fnp=%p error=%d", (void *)fnp,
2231*0Sstevel@tonic-gate 				    error);
2232*0Sstevel@tonic-gate 				error = 0;
2233*0Sstevel@tonic-gate 				/*
2234*0Sstevel@tonic-gate 				 * The action list should have been
2235*0Sstevel@tonic-gate 				 * xdr_free'd by auto_perform_actions
2236*0Sstevel@tonic-gate 				 * since an error occured
2237*0Sstevel@tonic-gate 				 */
2238*0Sstevel@tonic-gate 				alp = NULL;
2239*0Sstevel@tonic-gate 			}
2240*0Sstevel@tonic-gate 		}
2241*0Sstevel@tonic-gate 	} else {
2242*0Sstevel@tonic-gate 		/*
2243*0Sstevel@tonic-gate 		 * The unmount succeeded, which will cause this node to
2244*0Sstevel@tonic-gate 		 * be removed from its parent if its an indirect mount,
2245*0Sstevel@tonic-gate 		 * therefore update the parent's atime and mtime now.
2246*0Sstevel@tonic-gate 		 * I don't update them in auto_disconnect() because I
2247*0Sstevel@tonic-gate 		 * don't want atime and mtime changing every time a
2248*0Sstevel@tonic-gate 		 * lookup goes to the daemon and creates a new node.
2249*0Sstevel@tonic-gate 		 */
2250*0Sstevel@tonic-gate 		unmount_done = 1;
2251*0Sstevel@tonic-gate 		if ((fnip->fi_flags & MF_DIRECT) == 0) {
2252*0Sstevel@tonic-gate 			gethrestime(&now);
2253*0Sstevel@tonic-gate 			if (fnp->fn_parent == fngp->fng_rootfnnodep)
2254*0Sstevel@tonic-gate 				fnp->fn_atime = fnp->fn_mtime = now;
2255*0Sstevel@tonic-gate 			else
2256*0Sstevel@tonic-gate 				fnp->fn_parent->fn_atime =
2257*0Sstevel@tonic-gate 					fnp->fn_parent->fn_mtime = now;
2258*0Sstevel@tonic-gate 		}
2259*0Sstevel@tonic-gate 
2260*0Sstevel@tonic-gate 		/*
2261*0Sstevel@tonic-gate 		 * Free the action list here
2262*0Sstevel@tonic-gate 		 */
2263*0Sstevel@tonic-gate 		if (alp != NULL) {
2264*0Sstevel@tonic-gate 			xdr_free(xdr_action_list, (char *)alp);
2265*0Sstevel@tonic-gate 			alp = NULL;
2266*0Sstevel@tonic-gate 		}
2267*0Sstevel@tonic-gate 	}
2268*0Sstevel@tonic-gate 
2269*0Sstevel@tonic-gate 	fnp->fn_ref_time = gethrestime_sec();
2270*0Sstevel@tonic-gate 
2271*0Sstevel@tonic-gate next:
2272*0Sstevel@tonic-gate 	/*
2273*0Sstevel@tonic-gate 	 * Obtain parent's readers lock before grabbing
2274*0Sstevel@tonic-gate 	 * reference to next sibling.
2275*0Sstevel@tonic-gate 	 * XXX Note that nodes in the top level list (mounted
2276*0Sstevel@tonic-gate 	 * in user space not by the daemon in the kernel) parent is itself,
2277*0Sstevel@tonic-gate 	 * therefore grabbing the lock makes no sense, but doesn't
2278*0Sstevel@tonic-gate 	 * hurt either.
2279*0Sstevel@tonic-gate 	 */
2280*0Sstevel@tonic-gate 	pfnp = fnp->fn_parent;
2281*0Sstevel@tonic-gate 	ASSERT(pfnp != NULL);
2282*0Sstevel@tonic-gate 	rw_enter(&pfnp->fn_rwlock, RW_READER);
2283*0Sstevel@tonic-gate 	if ((nfnp = fnp->fn_next) != NULL)
2284*0Sstevel@tonic-gate 		VN_HOLD(fntovn(nfnp));
2285*0Sstevel@tonic-gate 	rw_exit(&pfnp->fn_rwlock);
2286*0Sstevel@tonic-gate 
2287*0Sstevel@tonic-gate 	if (ilocked_it) {
2288*0Sstevel@tonic-gate 		mutex_enter(&fnp->fn_lock);
2289*0Sstevel@tonic-gate 		if (unmount_done) {
2290*0Sstevel@tonic-gate 			/*
2291*0Sstevel@tonic-gate 			 * Other threads may be waiting for this unmount to
2292*0Sstevel@tonic-gate 			 * finish. We must let it know that in order to
2293*0Sstevel@tonic-gate 			 * proceed, it must trigger the mount itself.
2294*0Sstevel@tonic-gate 			 */
2295*0Sstevel@tonic-gate 			fnp->fn_flags &= ~MF_IK_MOUNT;
2296*0Sstevel@tonic-gate 			if (fnp->fn_flags & MF_WAITING)
2297*0Sstevel@tonic-gate 				fnp->fn_error = EAGAIN;
2298*0Sstevel@tonic-gate 			unmount_done = 0;
2299*0Sstevel@tonic-gate 		}
2300*0Sstevel@tonic-gate 		AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
2301*0Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
2302*0Sstevel@tonic-gate 		ilocked_it = 0;
2303*0Sstevel@tonic-gate 	}
2304*0Sstevel@tonic-gate 
2305*0Sstevel@tonic-gate 	if (nfnp != NULL) {
2306*0Sstevel@tonic-gate 		VN_RELE(vp);
2307*0Sstevel@tonic-gate 		fnp = nfnp;
2308*0Sstevel@tonic-gate 		/*
2309*0Sstevel@tonic-gate 		 * Unmount next element
2310*0Sstevel@tonic-gate 		 */
2311*0Sstevel@tonic-gate 		goto top;
2312*0Sstevel@tonic-gate 	}
2313*0Sstevel@tonic-gate 
2314*0Sstevel@tonic-gate 	/*
2315*0Sstevel@tonic-gate 	 * We don't want to unmount rootfnnodep, so the check is made here
2316*0Sstevel@tonic-gate 	 */
2317*0Sstevel@tonic-gate 	ASSERT(pfnp != fnp);
2318*0Sstevel@tonic-gate 	if (pfnp != fngp->fng_rootfnnodep) {
2319*0Sstevel@tonic-gate 		/*
2320*0Sstevel@tonic-gate 		 * Now attempt to unmount my parent
2321*0Sstevel@tonic-gate 		 */
2322*0Sstevel@tonic-gate 		VN_HOLD(fntovn(pfnp));
2323*0Sstevel@tonic-gate 		VN_RELE(vp);
2324*0Sstevel@tonic-gate 		fnp = pfnp;
2325*0Sstevel@tonic-gate 
2326*0Sstevel@tonic-gate 		goto top;
2327*0Sstevel@tonic-gate 	}
2328*0Sstevel@tonic-gate 
2329*0Sstevel@tonic-gate 	VN_RELE(vp);
2330*0Sstevel@tonic-gate 
2331*0Sstevel@tonic-gate 	/*
2332*0Sstevel@tonic-gate 	 * At this point we've walked the entire tree and attempted to unmount
2333*0Sstevel@tonic-gate 	 * as much as we can one level at a time.
2334*0Sstevel@tonic-gate 	 */
2335*0Sstevel@tonic-gate done:
2336*0Sstevel@tonic-gate 	mutex_enter(&unmount_tree_cpr_lock);
2337*0Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cprinfo);
2338*0Sstevel@tonic-gate 	mutex_destroy(&unmount_tree_cpr_lock);
2339*0Sstevel@tonic-gate }
2340*0Sstevel@tonic-gate 
2341*0Sstevel@tonic-gate static void
2342*0Sstevel@tonic-gate unmount_zone_tree(struct autofs_globals *fngp)
2343*0Sstevel@tonic-gate {
2344*0Sstevel@tonic-gate 	unmount_tree(fngp, 0);
2345*0Sstevel@tonic-gate 	mutex_enter(&fngp->fng_unmount_threads_lock);
2346*0Sstevel@tonic-gate 	fngp->fng_unmount_threads--;
2347*0Sstevel@tonic-gate 	mutex_exit(&fngp->fng_unmount_threads_lock);
2348*0Sstevel@tonic-gate 
2349*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "unmount_tree done. Thread exiting.\n"));
2350*0Sstevel@tonic-gate 
2351*0Sstevel@tonic-gate 	zthread_exit();
2352*0Sstevel@tonic-gate 	/* NOTREACHED */
2353*0Sstevel@tonic-gate }
2354*0Sstevel@tonic-gate 
2355*0Sstevel@tonic-gate static int autofs_unmount_thread_timer = 120;	/* in seconds */
2356*0Sstevel@tonic-gate 
2357*0Sstevel@tonic-gate void
2358*0Sstevel@tonic-gate auto_do_unmount(struct autofs_globals *fngp)
2359*0Sstevel@tonic-gate {
2360*0Sstevel@tonic-gate 	callb_cpr_t cprinfo;
2361*0Sstevel@tonic-gate 	clock_t timeleft;
2362*0Sstevel@tonic-gate 	zone_t *zone = curproc->p_zone;
2363*0Sstevel@tonic-gate 
2364*0Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, &fngp->fng_unmount_threads_lock,
2365*0Sstevel@tonic-gate 		callb_generic_cpr, "auto_do_unmount");
2366*0Sstevel@tonic-gate 
2367*0Sstevel@tonic-gate 	for (;;) {	/* forever */
2368*0Sstevel@tonic-gate 		mutex_enter(&fngp->fng_unmount_threads_lock);
2369*0Sstevel@tonic-gate 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
2370*0Sstevel@tonic-gate newthread:
2371*0Sstevel@tonic-gate 		mutex_exit(&fngp->fng_unmount_threads_lock);
2372*0Sstevel@tonic-gate 		timeleft = zone_status_timedwait(zone, lbolt +
2373*0Sstevel@tonic-gate 		    autofs_unmount_thread_timer * hz, ZONE_IS_SHUTTING_DOWN);
2374*0Sstevel@tonic-gate 		mutex_enter(&fngp->fng_unmount_threads_lock);
2375*0Sstevel@tonic-gate 
2376*0Sstevel@tonic-gate 		if (timeleft != -1) {	/* didn't time out */
2377*0Sstevel@tonic-gate 			ASSERT(zone_status_get(zone) >= ZONE_IS_SHUTTING_DOWN);
2378*0Sstevel@tonic-gate 			/*
2379*0Sstevel@tonic-gate 			 * zone is exiting... don't create any new threads.
2380*0Sstevel@tonic-gate 			 * fng_unmount_threads_lock is released implicitly by
2381*0Sstevel@tonic-gate 			 * the below.
2382*0Sstevel@tonic-gate 			 */
2383*0Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(&cprinfo,
2384*0Sstevel@tonic-gate 				&fngp->fng_unmount_threads_lock);
2385*0Sstevel@tonic-gate 			CALLB_CPR_EXIT(&cprinfo);
2386*0Sstevel@tonic-gate 			zthread_exit();
2387*0Sstevel@tonic-gate 			/* NOTREACHED */
2388*0Sstevel@tonic-gate 		}
2389*0Sstevel@tonic-gate 		if (fngp->fng_unmount_threads < autofs_unmount_threads) {
2390*0Sstevel@tonic-gate 			fngp->fng_unmount_threads++;
2391*0Sstevel@tonic-gate 			CALLB_CPR_SAFE_END(&cprinfo,
2392*0Sstevel@tonic-gate 				&fngp->fng_unmount_threads_lock);
2393*0Sstevel@tonic-gate 			mutex_exit(&fngp->fng_unmount_threads_lock);
2394*0Sstevel@tonic-gate 
2395*0Sstevel@tonic-gate 			(void) zthread_create(NULL, 0, unmount_zone_tree, fngp,
2396*0Sstevel@tonic-gate 			    0, minclsyspri);
2397*0Sstevel@tonic-gate 		} else
2398*0Sstevel@tonic-gate 			goto newthread;
2399*0Sstevel@tonic-gate 	}
2400*0Sstevel@tonic-gate 	/* NOTREACHED */
2401*0Sstevel@tonic-gate }
2402*0Sstevel@tonic-gate 
2403*0Sstevel@tonic-gate /*
2404*0Sstevel@tonic-gate  * Is nobrowse specified in option string?
2405*0Sstevel@tonic-gate  * opts should be a null ('\0') terminated string.
2406*0Sstevel@tonic-gate  * Returns non-zero if nobrowse has been specified.
2407*0Sstevel@tonic-gate  */
2408*0Sstevel@tonic-gate int
2409*0Sstevel@tonic-gate auto_nobrowse_option(char *opts)
2410*0Sstevel@tonic-gate {
2411*0Sstevel@tonic-gate 	char *buf;
2412*0Sstevel@tonic-gate 	char *p;
2413*0Sstevel@tonic-gate 	char *t;
2414*0Sstevel@tonic-gate 	int nobrowse = 0;
2415*0Sstevel@tonic-gate 	int last_opt = 0;
2416*0Sstevel@tonic-gate 	size_t len;
2417*0Sstevel@tonic-gate 
2418*0Sstevel@tonic-gate 	len = strlen(opts) + 1;
2419*0Sstevel@tonic-gate 	p = buf = kmem_alloc(len, KM_SLEEP);
2420*0Sstevel@tonic-gate 	(void) strcpy(buf, opts);
2421*0Sstevel@tonic-gate 	do {
2422*0Sstevel@tonic-gate 		if (t = strchr(p, ','))
2423*0Sstevel@tonic-gate 			*t++ = '\0';
2424*0Sstevel@tonic-gate 		else
2425*0Sstevel@tonic-gate 			last_opt++;
2426*0Sstevel@tonic-gate 		if (strcmp(p, MNTOPT_NOBROWSE) == 0)
2427*0Sstevel@tonic-gate 			nobrowse = 1;
2428*0Sstevel@tonic-gate 		else if (strcmp(p, MNTOPT_BROWSE) == 0)
2429*0Sstevel@tonic-gate 			nobrowse = 0;
2430*0Sstevel@tonic-gate 		p = t;
2431*0Sstevel@tonic-gate 	} while (!last_opt);
2432*0Sstevel@tonic-gate 	kmem_free(buf, len);
2433*0Sstevel@tonic-gate 
2434*0Sstevel@tonic-gate 	return (nobrowse);
2435*0Sstevel@tonic-gate }
2436*0Sstevel@tonic-gate 
2437*0Sstevel@tonic-gate /*
2438*0Sstevel@tonic-gate  * used to log warnings only if automountd is running
2439*0Sstevel@tonic-gate  * with verbose mode set
2440*0Sstevel@tonic-gate  */
2441*0Sstevel@tonic-gate void
2442*0Sstevel@tonic-gate auto_log(struct autofs_globals *fngp, int level, const char *fmt, ...)
2443*0Sstevel@tonic-gate {
2444*0Sstevel@tonic-gate 	va_list args;
2445*0Sstevel@tonic-gate 
2446*0Sstevel@tonic-gate 	if (fngp->fng_verbose > 0) {
2447*0Sstevel@tonic-gate 		va_start(args, fmt);
2448*0Sstevel@tonic-gate 		vzcmn_err(fngp->fng_zoneid, level, fmt, args);
2449*0Sstevel@tonic-gate 		va_end(args);
2450*0Sstevel@tonic-gate 	}
2451*0Sstevel@tonic-gate }
2452*0Sstevel@tonic-gate 
2453*0Sstevel@tonic-gate #ifdef DEBUG
2454*0Sstevel@tonic-gate static int autofs_debug = 0;
2455*0Sstevel@tonic-gate 
2456*0Sstevel@tonic-gate /*
2457*0Sstevel@tonic-gate  * Utilities used by both client and server
2458*0Sstevel@tonic-gate  * Standard levels:
2459*0Sstevel@tonic-gate  * 0) no debugging
2460*0Sstevel@tonic-gate  * 1) hard failures
2461*0Sstevel@tonic-gate  * 2) soft failures
2462*0Sstevel@tonic-gate  * 3) current test software
2463*0Sstevel@tonic-gate  * 4) main procedure entry points
2464*0Sstevel@tonic-gate  * 5) main procedure exit points
2465*0Sstevel@tonic-gate  * 6) utility procedure entry points
2466*0Sstevel@tonic-gate  * 7) utility procedure exit points
2467*0Sstevel@tonic-gate  * 8) obscure procedure entry points
2468*0Sstevel@tonic-gate  * 9) obscure procedure exit points
2469*0Sstevel@tonic-gate  * 10) random stuff
2470*0Sstevel@tonic-gate  * 11) all <= 1
2471*0Sstevel@tonic-gate  * 12) all <= 2
2472*0Sstevel@tonic-gate  * 13) all <= 3
2473*0Sstevel@tonic-gate  * ...
2474*0Sstevel@tonic-gate  */
2475*0Sstevel@tonic-gate /* PRINTFLIKE2 */
2476*0Sstevel@tonic-gate void
2477*0Sstevel@tonic-gate auto_dprint(int level, const char *fmt, ...)
2478*0Sstevel@tonic-gate {
2479*0Sstevel@tonic-gate 	va_list args;
2480*0Sstevel@tonic-gate 
2481*0Sstevel@tonic-gate 	if (autofs_debug == level ||
2482*0Sstevel@tonic-gate 	    (autofs_debug > 10 && (autofs_debug - 10) >= level)) {
2483*0Sstevel@tonic-gate 		va_start(args, fmt);
2484*0Sstevel@tonic-gate 		(void) vprintf(fmt, args);
2485*0Sstevel@tonic-gate 		va_end(args);
2486*0Sstevel@tonic-gate 	}
2487*0Sstevel@tonic-gate }
2488*0Sstevel@tonic-gate #endif /* DEBUG */
2489