xref: /onnv-gate/usr/src/uts/common/fs/autofs/auto_vnops.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/systm.h>
31*0Sstevel@tonic-gate #include <sys/errno.h>
32*0Sstevel@tonic-gate #include <sys/proc.h>
33*0Sstevel@tonic-gate #include <sys/vnode.h>
34*0Sstevel@tonic-gate #include <sys/vfs.h>
35*0Sstevel@tonic-gate #include <sys/uio.h>
36*0Sstevel@tonic-gate #include <sys/cred.h>
37*0Sstevel@tonic-gate #include <sys/pathname.h>
38*0Sstevel@tonic-gate #include <sys/dirent.h>
39*0Sstevel@tonic-gate #include <sys/debug.h>
40*0Sstevel@tonic-gate #include <sys/sysmacros.h>
41*0Sstevel@tonic-gate #include <sys/tiuser.h>
42*0Sstevel@tonic-gate #include <sys/cmn_err.h>
43*0Sstevel@tonic-gate #include <sys/stat.h>
44*0Sstevel@tonic-gate #include <sys/mode.h>
45*0Sstevel@tonic-gate #include <sys/policy.h>
46*0Sstevel@tonic-gate #include <rpc/types.h>
47*0Sstevel@tonic-gate #include <rpc/auth.h>
48*0Sstevel@tonic-gate #include <rpc/clnt.h>
49*0Sstevel@tonic-gate #include <sys/fs/autofs.h>
50*0Sstevel@tonic-gate #include <rpcsvc/autofs_prot.h>
51*0Sstevel@tonic-gate #include <fs/fs_subr.h>
52*0Sstevel@tonic-gate 
53*0Sstevel@tonic-gate /*
54*0Sstevel@tonic-gate  *  Vnode ops for autofs
55*0Sstevel@tonic-gate  */
56*0Sstevel@tonic-gate static int auto_open(vnode_t **, int, cred_t *);
57*0Sstevel@tonic-gate static int auto_close(vnode_t *, int, int, offset_t, cred_t *);
58*0Sstevel@tonic-gate static int auto_getattr(vnode_t *, vattr_t *, int, cred_t *);
59*0Sstevel@tonic-gate static int auto_setattr(vnode_t *, vattr_t *, int, cred_t *,
60*0Sstevel@tonic-gate 	caller_context_t *);
61*0Sstevel@tonic-gate static int auto_access(vnode_t *, int, int, cred_t *);
62*0Sstevel@tonic-gate static int auto_lookup(vnode_t *, char *, vnode_t **,
63*0Sstevel@tonic-gate 	pathname_t *, int, vnode_t *, cred_t *);
64*0Sstevel@tonic-gate static int auto_create(vnode_t *, char *, vattr_t *, vcexcl_t,
65*0Sstevel@tonic-gate 	int, vnode_t **, cred_t *, int);
66*0Sstevel@tonic-gate static int auto_remove(vnode_t *, char *, cred_t *);
67*0Sstevel@tonic-gate static int auto_link(vnode_t *, vnode_t *, char *, cred_t *);
68*0Sstevel@tonic-gate static int auto_rename(vnode_t *, char *, vnode_t *, char *, cred_t *);
69*0Sstevel@tonic-gate static int auto_mkdir(vnode_t *, char *, vattr_t *, vnode_t **, cred_t *);
70*0Sstevel@tonic-gate static int auto_rmdir(vnode_t *, char *, vnode_t *, cred_t *);
71*0Sstevel@tonic-gate static int auto_readdir(vnode_t *, uio_t *, cred_t *, int *);
72*0Sstevel@tonic-gate static int auto_symlink(vnode_t *, char *, vattr_t *, char *, cred_t *);
73*0Sstevel@tonic-gate static int auto_readlink(vnode_t *, struct uio *, cred_t *);
74*0Sstevel@tonic-gate static int auto_fsync(vnode_t *, int, cred_t *);
75*0Sstevel@tonic-gate static void auto_inactive(vnode_t *, cred_t *);
76*0Sstevel@tonic-gate static int auto_rwlock(vnode_t *, int, caller_context_t *);
77*0Sstevel@tonic-gate static void auto_rwunlock(vnode_t *vp, int, caller_context_t *);
78*0Sstevel@tonic-gate static int auto_seek(vnode_t *vp, offset_t, offset_t *);
79*0Sstevel@tonic-gate 
80*0Sstevel@tonic-gate static int auto_trigger_mount(vnode_t *, cred_t *, vnode_t **);
81*0Sstevel@tonic-gate 
82*0Sstevel@tonic-gate vnodeops_t *auto_vnodeops;
83*0Sstevel@tonic-gate 
84*0Sstevel@tonic-gate const fs_operation_def_t auto_vnodeops_template[] = {
85*0Sstevel@tonic-gate 	VOPNAME_OPEN, auto_open,
86*0Sstevel@tonic-gate 	VOPNAME_CLOSE, auto_close,
87*0Sstevel@tonic-gate 	VOPNAME_GETATTR, auto_getattr,
88*0Sstevel@tonic-gate 	VOPNAME_SETATTR, auto_setattr,
89*0Sstevel@tonic-gate 	VOPNAME_ACCESS, auto_access,
90*0Sstevel@tonic-gate 	VOPNAME_LOOKUP, auto_lookup,
91*0Sstevel@tonic-gate 	VOPNAME_CREATE, auto_create,
92*0Sstevel@tonic-gate 	VOPNAME_REMOVE, auto_remove,
93*0Sstevel@tonic-gate 	VOPNAME_LINK, auto_link,
94*0Sstevel@tonic-gate 	VOPNAME_RENAME, auto_rename,
95*0Sstevel@tonic-gate 	VOPNAME_MKDIR, auto_mkdir,
96*0Sstevel@tonic-gate 	VOPNAME_RMDIR, auto_rmdir,
97*0Sstevel@tonic-gate 	VOPNAME_READDIR, auto_readdir,
98*0Sstevel@tonic-gate 	VOPNAME_SYMLINK, auto_symlink,
99*0Sstevel@tonic-gate 	VOPNAME_READLINK, auto_readlink,
100*0Sstevel@tonic-gate 	VOPNAME_FSYNC, auto_fsync,
101*0Sstevel@tonic-gate 	VOPNAME_INACTIVE, (fs_generic_func_p) auto_inactive,
102*0Sstevel@tonic-gate 	VOPNAME_RWLOCK, auto_rwlock,
103*0Sstevel@tonic-gate 	VOPNAME_RWUNLOCK, (fs_generic_func_p) auto_rwunlock,
104*0Sstevel@tonic-gate 	VOPNAME_SEEK, auto_seek,
105*0Sstevel@tonic-gate 	VOPNAME_FRLOCK, fs_error,
106*0Sstevel@tonic-gate 	VOPNAME_DISPOSE, fs_error,
107*0Sstevel@tonic-gate 	VOPNAME_SHRLOCK, fs_error,
108*0Sstevel@tonic-gate 	NULL, NULL
109*0Sstevel@tonic-gate };
110*0Sstevel@tonic-gate 
111*0Sstevel@tonic-gate /* ARGSUSED */
112*0Sstevel@tonic-gate static int
113*0Sstevel@tonic-gate auto_open(vnode_t **vpp, int flag, cred_t *cred)
114*0Sstevel@tonic-gate {
115*0Sstevel@tonic-gate 	vnode_t *newvp;
116*0Sstevel@tonic-gate 	int error;
117*0Sstevel@tonic-gate 
118*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_open: *vpp=%p\n", (void *)*vpp));
119*0Sstevel@tonic-gate 
120*0Sstevel@tonic-gate 	error = auto_trigger_mount(*vpp, cred, &newvp);
121*0Sstevel@tonic-gate 	if (error)
122*0Sstevel@tonic-gate 		goto done;
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate 	if (newvp != NULL) {
125*0Sstevel@tonic-gate 		/*
126*0Sstevel@tonic-gate 		 * Node is now mounted on.
127*0Sstevel@tonic-gate 		 */
128*0Sstevel@tonic-gate 		VN_RELE(*vpp);
129*0Sstevel@tonic-gate 		*vpp = newvp;
130*0Sstevel@tonic-gate 		error = VOP_ACCESS(*vpp, VREAD, 0, cred);
131*0Sstevel@tonic-gate 		if (!error)
132*0Sstevel@tonic-gate 			error = VOP_OPEN(vpp, flag, cred);
133*0Sstevel@tonic-gate 	}
134*0Sstevel@tonic-gate 
135*0Sstevel@tonic-gate done:
136*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_open: *vpp=%p error=%d\n", (void *)*vpp,
137*0Sstevel@tonic-gate 	    error));
138*0Sstevel@tonic-gate 	return (error);
139*0Sstevel@tonic-gate }
140*0Sstevel@tonic-gate 
141*0Sstevel@tonic-gate /* ARGSUSED */
142*0Sstevel@tonic-gate static int
143*0Sstevel@tonic-gate auto_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cred)
144*0Sstevel@tonic-gate {
145*0Sstevel@tonic-gate 	return (0);
146*0Sstevel@tonic-gate }
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate static int
149*0Sstevel@tonic-gate auto_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cred)
150*0Sstevel@tonic-gate {
151*0Sstevel@tonic-gate 	fnnode_t *fnp = vntofn(vp);
152*0Sstevel@tonic-gate 	vnode_t *newvp;
153*0Sstevel@tonic-gate 	vfs_t *vfsp;
154*0Sstevel@tonic-gate 	int error;
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_getattr vp %p\n", (void *)vp));
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate 	if (error = vn_vfswlock_wait(vp))
159*0Sstevel@tonic-gate 		return (error);
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate 	vfsp = vn_mountedvfs(vp);
162*0Sstevel@tonic-gate 	if (vfsp != NULL) {
163*0Sstevel@tonic-gate 		/*
164*0Sstevel@tonic-gate 		 * Node is mounted on.
165*0Sstevel@tonic-gate 		 */
166*0Sstevel@tonic-gate 		vfs_lock_wait(vfsp);
167*0Sstevel@tonic-gate 		vn_vfsunlock(vp);
168*0Sstevel@tonic-gate 		error = VFS_ROOT(vfsp, &newvp);
169*0Sstevel@tonic-gate 		vfs_unlock(vfsp);
170*0Sstevel@tonic-gate 		if (error)
171*0Sstevel@tonic-gate 			return (error);
172*0Sstevel@tonic-gate 		mutex_enter(&fnp->fn_lock);
173*0Sstevel@tonic-gate 		if (fnp->fn_seen == newvp && fnp->fn_thread == curthread) {
174*0Sstevel@tonic-gate 			/*
175*0Sstevel@tonic-gate 			 * Recursive auto_getattr(); just release newvp and drop
176*0Sstevel@tonic-gate 			 * into the vfsp == NULL case.
177*0Sstevel@tonic-gate 			 */
178*0Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
179*0Sstevel@tonic-gate 			VN_RELE(newvp);
180*0Sstevel@tonic-gate 		} else {
181*0Sstevel@tonic-gate 			while (fnp->fn_thread && fnp->fn_thread != curthread) {
182*0Sstevel@tonic-gate 				fnp->fn_flags |= MF_ATTR_WAIT;
183*0Sstevel@tonic-gate 				cv_wait(&fnp->fn_cv_mount, &fnp->fn_lock);
184*0Sstevel@tonic-gate 			}
185*0Sstevel@tonic-gate 			fnp->fn_thread = curthread;
186*0Sstevel@tonic-gate 			fnp->fn_seen = newvp;
187*0Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
188*0Sstevel@tonic-gate 			error = VOP_GETATTR(newvp, vap, flags, cred);
189*0Sstevel@tonic-gate 			VN_RELE(newvp);
190*0Sstevel@tonic-gate 			mutex_enter(&fnp->fn_lock);
191*0Sstevel@tonic-gate 			fnp->fn_seen = 0;
192*0Sstevel@tonic-gate 			fnp->fn_thread = 0;
193*0Sstevel@tonic-gate 			if (fnp->fn_flags & MF_ATTR_WAIT) {
194*0Sstevel@tonic-gate 				fnp->fn_flags &= ~MF_ATTR_WAIT;
195*0Sstevel@tonic-gate 				cv_broadcast(&fnp->fn_cv_mount);
196*0Sstevel@tonic-gate 			}
197*0Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
198*0Sstevel@tonic-gate 			return (error);
199*0Sstevel@tonic-gate 		}
200*0Sstevel@tonic-gate 	} else {
201*0Sstevel@tonic-gate 		vn_vfsunlock(vp);
202*0Sstevel@tonic-gate 	}
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate 	ASSERT(vp->v_type == VDIR || vp->v_type == VLNK);
205*0Sstevel@tonic-gate 	vap->va_uid	= 0;
206*0Sstevel@tonic-gate 	vap->va_gid	= 0;
207*0Sstevel@tonic-gate 	vap->va_nlink	= fnp->fn_linkcnt;
208*0Sstevel@tonic-gate 	vap->va_nodeid	= (u_longlong_t)fnp->fn_nodeid;
209*0Sstevel@tonic-gate 	vap->va_size	= fnp->fn_size;
210*0Sstevel@tonic-gate 	vap->va_atime	= fnp->fn_atime;
211*0Sstevel@tonic-gate 	vap->va_mtime	= fnp->fn_mtime;
212*0Sstevel@tonic-gate 	vap->va_ctime	= fnp->fn_ctime;
213*0Sstevel@tonic-gate 	vap->va_type	= vp->v_type;
214*0Sstevel@tonic-gate 	vap->va_mode	= fnp->fn_mode;
215*0Sstevel@tonic-gate 	vap->va_fsid	= vp->v_vfsp->vfs_dev;
216*0Sstevel@tonic-gate 	vap->va_rdev	= 0;
217*0Sstevel@tonic-gate 	vap->va_blksize	= MAXBSIZE;
218*0Sstevel@tonic-gate 	vap->va_nblocks	= (fsblkcnt64_t)btod(vap->va_size);
219*0Sstevel@tonic-gate 	vap->va_seq	= 0;
220*0Sstevel@tonic-gate 
221*0Sstevel@tonic-gate 	return (0);
222*0Sstevel@tonic-gate }
223*0Sstevel@tonic-gate 
224*0Sstevel@tonic-gate /*ARGSUSED4*/
225*0Sstevel@tonic-gate static int
226*0Sstevel@tonic-gate auto_setattr(
227*0Sstevel@tonic-gate 	vnode_t *vp,
228*0Sstevel@tonic-gate 	struct vattr *vap,
229*0Sstevel@tonic-gate 	int flags,
230*0Sstevel@tonic-gate 	cred_t *cred,
231*0Sstevel@tonic-gate 	caller_context_t *ct)
232*0Sstevel@tonic-gate {
233*0Sstevel@tonic-gate 	vnode_t *newvp;
234*0Sstevel@tonic-gate 	int error;
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_setattr vp %p\n", (void *)vp));
237*0Sstevel@tonic-gate 
238*0Sstevel@tonic-gate 	if (error = auto_trigger_mount(vp, cred, &newvp))
239*0Sstevel@tonic-gate 		goto done;
240*0Sstevel@tonic-gate 
241*0Sstevel@tonic-gate 	if (newvp != NULL) {
242*0Sstevel@tonic-gate 		/*
243*0Sstevel@tonic-gate 		 * Node is mounted on.
244*0Sstevel@tonic-gate 		 */
245*0Sstevel@tonic-gate 		if (vn_is_readonly(newvp))
246*0Sstevel@tonic-gate 			error = EROFS;
247*0Sstevel@tonic-gate 		else
248*0Sstevel@tonic-gate 			error = VOP_SETATTR(newvp, vap, flags, cred, NULL);
249*0Sstevel@tonic-gate 		VN_RELE(newvp);
250*0Sstevel@tonic-gate 	} else
251*0Sstevel@tonic-gate 		error = ENOSYS;
252*0Sstevel@tonic-gate 
253*0Sstevel@tonic-gate done:
254*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_setattr: error=%d\n", error));
255*0Sstevel@tonic-gate 	return (error);
256*0Sstevel@tonic-gate }
257*0Sstevel@tonic-gate 
258*0Sstevel@tonic-gate /* ARGSUSED */
259*0Sstevel@tonic-gate static int
260*0Sstevel@tonic-gate auto_access(vnode_t *vp, int mode, int flags, cred_t *cred)
261*0Sstevel@tonic-gate {
262*0Sstevel@tonic-gate 	fnnode_t *fnp = vntofn(vp);
263*0Sstevel@tonic-gate 	vnode_t *newvp;
264*0Sstevel@tonic-gate 	int error;
265*0Sstevel@tonic-gate 
266*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_access: vp=%p\n", (void *)vp));
267*0Sstevel@tonic-gate 
268*0Sstevel@tonic-gate 	if (error = auto_trigger_mount(vp, cred, &newvp))
269*0Sstevel@tonic-gate 		goto done;
270*0Sstevel@tonic-gate 
271*0Sstevel@tonic-gate 	if (newvp != NULL) {
272*0Sstevel@tonic-gate 		/*
273*0Sstevel@tonic-gate 		 * Node is mounted on.
274*0Sstevel@tonic-gate 		 */
275*0Sstevel@tonic-gate 		error = VOP_ACCESS(newvp, mode, 0, cred);
276*0Sstevel@tonic-gate 		VN_RELE(newvp);
277*0Sstevel@tonic-gate 	} else {
278*0Sstevel@tonic-gate 		int shift = 0;
279*0Sstevel@tonic-gate 
280*0Sstevel@tonic-gate 		/*
281*0Sstevel@tonic-gate 		 * really interested in the autofs node, check the
282*0Sstevel@tonic-gate 		 * access on it
283*0Sstevel@tonic-gate 		 */
284*0Sstevel@tonic-gate 		ASSERT(error == 0);
285*0Sstevel@tonic-gate 		if (crgetuid(cred) != fnp->fn_uid) {
286*0Sstevel@tonic-gate 			shift += 3;
287*0Sstevel@tonic-gate 			if (groupmember(fnp->fn_gid, cred) == 0)
288*0Sstevel@tonic-gate 				shift += 3;
289*0Sstevel@tonic-gate 		}
290*0Sstevel@tonic-gate 		mode &= ~(fnp->fn_mode << shift);
291*0Sstevel@tonic-gate 		if (mode != 0)
292*0Sstevel@tonic-gate 			error = secpolicy_vnode_access(cred, vp, fnp->fn_uid,
293*0Sstevel@tonic-gate 									mode);
294*0Sstevel@tonic-gate 	}
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate done:
297*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_access: error=%d\n", error));
298*0Sstevel@tonic-gate 	return (error);
299*0Sstevel@tonic-gate }
300*0Sstevel@tonic-gate 
301*0Sstevel@tonic-gate 
302*0Sstevel@tonic-gate static int
303*0Sstevel@tonic-gate auto_lookup(
304*0Sstevel@tonic-gate 	vnode_t *dvp,
305*0Sstevel@tonic-gate 	char *nm,
306*0Sstevel@tonic-gate 	vnode_t **vpp,
307*0Sstevel@tonic-gate 	pathname_t *pnp,
308*0Sstevel@tonic-gate 	int flags,
309*0Sstevel@tonic-gate 	vnode_t *rdir,
310*0Sstevel@tonic-gate 	cred_t *cred)
311*0Sstevel@tonic-gate {
312*0Sstevel@tonic-gate 	int error = 0;
313*0Sstevel@tonic-gate 	vnode_t *newvp = NULL;
314*0Sstevel@tonic-gate 	vfs_t *vfsp;
315*0Sstevel@tonic-gate 	fninfo_t *dfnip;
316*0Sstevel@tonic-gate 	fnnode_t *dfnp = NULL;
317*0Sstevel@tonic-gate 	fnnode_t *fnp = NULL;
318*0Sstevel@tonic-gate 	char *searchnm;
319*0Sstevel@tonic-gate 	int operation;		/* either AUTOFS_LOOKUP or AUTOFS_MOUNT */
320*0Sstevel@tonic-gate 
321*0Sstevel@tonic-gate 	dfnip = vfstofni(dvp->v_vfsp);
322*0Sstevel@tonic-gate 	AUTOFS_DPRINT((3, "auto_lookup: dvp=%p (%s) name=%s\n",
323*0Sstevel@tonic-gate 	    (void *)dvp, dfnip->fi_map, nm));
324*0Sstevel@tonic-gate 
325*0Sstevel@tonic-gate 	if (nm[0] == 0) {
326*0Sstevel@tonic-gate 		VN_HOLD(dvp);
327*0Sstevel@tonic-gate 		*vpp = dvp;
328*0Sstevel@tonic-gate 		return (0);
329*0Sstevel@tonic-gate 	}
330*0Sstevel@tonic-gate 
331*0Sstevel@tonic-gate 	if (error = VOP_ACCESS(dvp, VEXEC, 0, cred))
332*0Sstevel@tonic-gate 		return (error);
333*0Sstevel@tonic-gate 
334*0Sstevel@tonic-gate 	if (nm[0] == '.' && nm[1] == 0) {
335*0Sstevel@tonic-gate 		VN_HOLD(dvp);
336*0Sstevel@tonic-gate 		*vpp = dvp;
337*0Sstevel@tonic-gate 		return (0);
338*0Sstevel@tonic-gate 	}
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate 	if (nm[0] == '.' && nm[1] == '.' && nm[2] == 0) {
341*0Sstevel@tonic-gate 		fnnode_t *pdfnp;
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate 		pdfnp = (vntofn(dvp))->fn_parent;
344*0Sstevel@tonic-gate 		ASSERT(pdfnp != NULL);
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate 		/*
347*0Sstevel@tonic-gate 		 * Since it is legitimate to have the VROOT flag set for the
348*0Sstevel@tonic-gate 		 * subdirectories of the indirect map in autofs filesystem,
349*0Sstevel@tonic-gate 		 * rootfnnodep is checked against fnnode of dvp instead of
350*0Sstevel@tonic-gate 		 * just checking whether VROOT flag is set in dvp
351*0Sstevel@tonic-gate 		 */
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate 		if (pdfnp == pdfnp->fn_globals->fng_rootfnnodep) {
354*0Sstevel@tonic-gate 			vnode_t *vp;
355*0Sstevel@tonic-gate 
356*0Sstevel@tonic-gate 			vfs_lock_wait(dvp->v_vfsp);
357*0Sstevel@tonic-gate 			if (dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED) {
358*0Sstevel@tonic-gate 				vfs_unlock(dvp->v_vfsp);
359*0Sstevel@tonic-gate 				return (EIO);
360*0Sstevel@tonic-gate 			}
361*0Sstevel@tonic-gate 			vp = dvp->v_vfsp->vfs_vnodecovered;
362*0Sstevel@tonic-gate 			VN_HOLD(vp);
363*0Sstevel@tonic-gate 			vfs_unlock(dvp->v_vfsp);
364*0Sstevel@tonic-gate 			error = VOP_LOOKUP(vp, nm, vpp, pnp, flags, rdir, cred);
365*0Sstevel@tonic-gate 			VN_RELE(vp);
366*0Sstevel@tonic-gate 			return (error);
367*0Sstevel@tonic-gate 		} else {
368*0Sstevel@tonic-gate 			*vpp = fntovn(pdfnp);
369*0Sstevel@tonic-gate 			VN_HOLD(*vpp);
370*0Sstevel@tonic-gate 			return (0);
371*0Sstevel@tonic-gate 		}
372*0Sstevel@tonic-gate 	}
373*0Sstevel@tonic-gate 
374*0Sstevel@tonic-gate top:
375*0Sstevel@tonic-gate 	dfnp = vntofn(dvp);
376*0Sstevel@tonic-gate 	searchnm = nm;
377*0Sstevel@tonic-gate 	operation = 0;
378*0Sstevel@tonic-gate 
379*0Sstevel@tonic-gate 	ASSERT(vn_matchops(dvp, auto_vnodeops));
380*0Sstevel@tonic-gate 
381*0Sstevel@tonic-gate 	AUTOFS_DPRINT((3, "auto_lookup: dvp=%p dfnp=%p\n", (void *)dvp,
382*0Sstevel@tonic-gate 	    (void *)dfnp));
383*0Sstevel@tonic-gate 
384*0Sstevel@tonic-gate 	/*
385*0Sstevel@tonic-gate 	 * If a lookup or mount of this node is in progress, wait for it
386*0Sstevel@tonic-gate 	 * to finish, and return whatever result it got.
387*0Sstevel@tonic-gate 	 */
388*0Sstevel@tonic-gate 	mutex_enter(&dfnp->fn_lock);
389*0Sstevel@tonic-gate 	if (dfnp->fn_flags & (MF_LOOKUP | MF_INPROG)) {
390*0Sstevel@tonic-gate 		mutex_exit(&dfnp->fn_lock);
391*0Sstevel@tonic-gate 		error = auto_wait4mount(dfnp);
392*0Sstevel@tonic-gate 		if (error == AUTOFS_SHUTDOWN)
393*0Sstevel@tonic-gate 			error = ENOENT;
394*0Sstevel@tonic-gate 		if (error == EAGAIN)
395*0Sstevel@tonic-gate 			goto top;
396*0Sstevel@tonic-gate 		if (error)
397*0Sstevel@tonic-gate 			return (error);
398*0Sstevel@tonic-gate 	} else
399*0Sstevel@tonic-gate 		mutex_exit(&dfnp->fn_lock);
400*0Sstevel@tonic-gate 
401*0Sstevel@tonic-gate 
402*0Sstevel@tonic-gate 	error = vn_vfswlock_wait(dvp);
403*0Sstevel@tonic-gate 	if (error)
404*0Sstevel@tonic-gate 		return (error);
405*0Sstevel@tonic-gate 	vfsp = vn_mountedvfs(dvp);
406*0Sstevel@tonic-gate 	if (vfsp != NULL) {
407*0Sstevel@tonic-gate 		vfs_lock_wait(vfsp);
408*0Sstevel@tonic-gate 		vn_vfsunlock(dvp);
409*0Sstevel@tonic-gate 		error = VFS_ROOT(vfsp, &newvp);
410*0Sstevel@tonic-gate 		vfs_unlock(vfsp);
411*0Sstevel@tonic-gate 		if (!error) {
412*0Sstevel@tonic-gate 			error = VOP_LOOKUP(newvp, nm, vpp, pnp,
413*0Sstevel@tonic-gate 			    flags, rdir, cred);
414*0Sstevel@tonic-gate 			VN_RELE(newvp);
415*0Sstevel@tonic-gate 		}
416*0Sstevel@tonic-gate 		return (error);
417*0Sstevel@tonic-gate 	}
418*0Sstevel@tonic-gate 	vn_vfsunlock(dvp);
419*0Sstevel@tonic-gate 
420*0Sstevel@tonic-gate 	rw_enter(&dfnp->fn_rwlock, RW_READER);
421*0Sstevel@tonic-gate 	error = auto_search(dfnp, nm, &fnp, cred);
422*0Sstevel@tonic-gate 	if (error) {
423*0Sstevel@tonic-gate 		if (dfnip->fi_flags & MF_DIRECT) {
424*0Sstevel@tonic-gate 			/*
425*0Sstevel@tonic-gate 			 * direct map.
426*0Sstevel@tonic-gate 			 */
427*0Sstevel@tonic-gate 			if (dfnp->fn_dirents) {
428*0Sstevel@tonic-gate 				/*
429*0Sstevel@tonic-gate 				 * Mount previously triggered.
430*0Sstevel@tonic-gate 				 * 'nm' not found
431*0Sstevel@tonic-gate 				 */
432*0Sstevel@tonic-gate 				error = ENOENT;
433*0Sstevel@tonic-gate 			} else {
434*0Sstevel@tonic-gate 				/*
435*0Sstevel@tonic-gate 				 * I need to contact the daemon to trigger
436*0Sstevel@tonic-gate 				 * the mount. 'dfnp' will be the mountpoint.
437*0Sstevel@tonic-gate 				 */
438*0Sstevel@tonic-gate 				operation = AUTOFS_MOUNT;
439*0Sstevel@tonic-gate 				VN_HOLD(fntovn(dfnp));
440*0Sstevel@tonic-gate 				fnp = dfnp;
441*0Sstevel@tonic-gate 				error = 0;
442*0Sstevel@tonic-gate 			}
443*0Sstevel@tonic-gate 		} else if (dvp == dfnip->fi_rootvp) {
444*0Sstevel@tonic-gate 			/*
445*0Sstevel@tonic-gate 			 * 'dfnp' is the root of the indirect AUTOFS.
446*0Sstevel@tonic-gate 			 */
447*0Sstevel@tonic-gate 			if (rw_tryupgrade(&dfnp->fn_rwlock) == 0) {
448*0Sstevel@tonic-gate 				/*
449*0Sstevel@tonic-gate 				 * Could not acquire writer lock, release
450*0Sstevel@tonic-gate 				 * reader, and wait until available. We
451*0Sstevel@tonic-gate 				 * need to search for 'nm' again, since we
452*0Sstevel@tonic-gate 				 * had to release the lock before reacquiring
453*0Sstevel@tonic-gate 				 * it.
454*0Sstevel@tonic-gate 				 */
455*0Sstevel@tonic-gate 				rw_exit(&dfnp->fn_rwlock);
456*0Sstevel@tonic-gate 				rw_enter(&dfnp->fn_rwlock, RW_WRITER);
457*0Sstevel@tonic-gate 				error = auto_search(dfnp, nm, &fnp, cred);
458*0Sstevel@tonic-gate 			}
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 			ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock));
461*0Sstevel@tonic-gate 			if (error) {
462*0Sstevel@tonic-gate 				/*
463*0Sstevel@tonic-gate 				 * create node being looked-up and request
464*0Sstevel@tonic-gate 				 * mount on it.
465*0Sstevel@tonic-gate 				 */
466*0Sstevel@tonic-gate 				error = auto_enter(dfnp, nm, &fnp, kcred);
467*0Sstevel@tonic-gate 				if (!error)
468*0Sstevel@tonic-gate 					operation = AUTOFS_LOOKUP;
469*0Sstevel@tonic-gate 			}
470*0Sstevel@tonic-gate 		} else if ((dfnp->fn_dirents == NULL) &&
471*0Sstevel@tonic-gate 		    ((dvp->v_flag & VROOT) == 0) &&
472*0Sstevel@tonic-gate 		    ((fntovn(dfnp->fn_parent))->v_flag & VROOT)) {
473*0Sstevel@tonic-gate 			/*
474*0Sstevel@tonic-gate 			 * dfnp is the actual 'mountpoint' of indirect map,
475*0Sstevel@tonic-gate 			 * it is the equivalent of a direct mount,
476*0Sstevel@tonic-gate 			 * ie, /home/'user1'
477*0Sstevel@tonic-gate 			 */
478*0Sstevel@tonic-gate 			operation = AUTOFS_MOUNT;
479*0Sstevel@tonic-gate 			VN_HOLD(fntovn(dfnp));
480*0Sstevel@tonic-gate 			fnp = dfnp;
481*0Sstevel@tonic-gate 			error = 0;
482*0Sstevel@tonic-gate 			searchnm = dfnp->fn_name;
483*0Sstevel@tonic-gate 		}
484*0Sstevel@tonic-gate 	}
485*0Sstevel@tonic-gate 
486*0Sstevel@tonic-gate 	if (error == EAGAIN) {
487*0Sstevel@tonic-gate 		rw_exit(&dfnp->fn_rwlock);
488*0Sstevel@tonic-gate 		goto top;
489*0Sstevel@tonic-gate 	}
490*0Sstevel@tonic-gate 	if (error) {
491*0Sstevel@tonic-gate 		rw_exit(&dfnp->fn_rwlock);
492*0Sstevel@tonic-gate 		return (error);
493*0Sstevel@tonic-gate 	}
494*0Sstevel@tonic-gate 
495*0Sstevel@tonic-gate 	/*
496*0Sstevel@tonic-gate 	 * We now have the actual fnnode we're interested in.
497*0Sstevel@tonic-gate 	 * The 'MF_LOOKUP' indicates another thread is currently
498*0Sstevel@tonic-gate 	 * performing a daemon lookup of this node, therefore we
499*0Sstevel@tonic-gate 	 * wait for its completion.
500*0Sstevel@tonic-gate 	 * The 'MF_INPROG' indicates another thread is currently
501*0Sstevel@tonic-gate 	 * performing a daemon mount of this node, we wait for it
502*0Sstevel@tonic-gate 	 * to be done if we are performing a MOUNT. We don't
503*0Sstevel@tonic-gate 	 * wait for it if we are performing a LOOKUP.
504*0Sstevel@tonic-gate 	 * We can release the reader/writer lock as soon as we acquire
505*0Sstevel@tonic-gate 	 * the mutex, since the state of the lock can only change by
506*0Sstevel@tonic-gate 	 * first acquiring the mutex.
507*0Sstevel@tonic-gate 	 */
508*0Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
509*0Sstevel@tonic-gate 	rw_exit(&dfnp->fn_rwlock);
510*0Sstevel@tonic-gate 	if ((fnp->fn_flags & MF_LOOKUP) ||
511*0Sstevel@tonic-gate 	    ((operation == AUTOFS_MOUNT) && (fnp->fn_flags & MF_INPROG))) {
512*0Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
513*0Sstevel@tonic-gate 		error = auto_wait4mount(fnp);
514*0Sstevel@tonic-gate 		VN_RELE(fntovn(fnp));
515*0Sstevel@tonic-gate 		if (error == AUTOFS_SHUTDOWN)
516*0Sstevel@tonic-gate 			error = ENOENT;
517*0Sstevel@tonic-gate 		if (error && error != EAGAIN)
518*0Sstevel@tonic-gate 			return (error);
519*0Sstevel@tonic-gate 		goto top;
520*0Sstevel@tonic-gate 	}
521*0Sstevel@tonic-gate 
522*0Sstevel@tonic-gate 	if (operation == 0) {
523*0Sstevel@tonic-gate 		/*
524*0Sstevel@tonic-gate 		 * got the fnnode, check for any errors
525*0Sstevel@tonic-gate 		 * on the previous operation on that node.
526*0Sstevel@tonic-gate 		 */
527*0Sstevel@tonic-gate 		error = fnp->fn_error;
528*0Sstevel@tonic-gate 		if ((error == EINTR) || (error == EAGAIN)) {
529*0Sstevel@tonic-gate 			/*
530*0Sstevel@tonic-gate 			 * previous operation on this node was
531*0Sstevel@tonic-gate 			 * not completed, do a lookup now.
532*0Sstevel@tonic-gate 			 */
533*0Sstevel@tonic-gate 			operation = AUTOFS_LOOKUP;
534*0Sstevel@tonic-gate 		} else {
535*0Sstevel@tonic-gate 			/*
536*0Sstevel@tonic-gate 			 * previous operation completed. Return
537*0Sstevel@tonic-gate 			 * a pointer to the node only if there was
538*0Sstevel@tonic-gate 			 * no error.
539*0Sstevel@tonic-gate 			 */
540*0Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
541*0Sstevel@tonic-gate 			if (!error)
542*0Sstevel@tonic-gate 				*vpp = fntovn(fnp);
543*0Sstevel@tonic-gate 			else
544*0Sstevel@tonic-gate 				VN_RELE(fntovn(fnp));
545*0Sstevel@tonic-gate 			return (error);
546*0Sstevel@tonic-gate 		}
547*0Sstevel@tonic-gate 	}
548*0Sstevel@tonic-gate 
549*0Sstevel@tonic-gate 	/*
550*0Sstevel@tonic-gate 	 * Since I got to this point, it means I'm the one
551*0Sstevel@tonic-gate 	 * responsible for triggering the mount/look-up of this node.
552*0Sstevel@tonic-gate 	 */
553*0Sstevel@tonic-gate 	switch (operation) {
554*0Sstevel@tonic-gate 	case AUTOFS_LOOKUP:
555*0Sstevel@tonic-gate 		AUTOFS_BLOCK_OTHERS(fnp, MF_LOOKUP);
556*0Sstevel@tonic-gate 		fnp->fn_error = 0;
557*0Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
558*0Sstevel@tonic-gate 		error = auto_lookup_aux(fnp, searchnm, cred);
559*0Sstevel@tonic-gate 		if (!error) {
560*0Sstevel@tonic-gate 			/*
561*0Sstevel@tonic-gate 			 * Return this vnode
562*0Sstevel@tonic-gate 			 */
563*0Sstevel@tonic-gate 			*vpp = fntovn(fnp);
564*0Sstevel@tonic-gate 		} else {
565*0Sstevel@tonic-gate 			/*
566*0Sstevel@tonic-gate 			 * release our reference to this vnode
567*0Sstevel@tonic-gate 			 * and return error
568*0Sstevel@tonic-gate 			 */
569*0Sstevel@tonic-gate 			VN_RELE(fntovn(fnp));
570*0Sstevel@tonic-gate 		}
571*0Sstevel@tonic-gate 		break;
572*0Sstevel@tonic-gate 	case AUTOFS_MOUNT:
573*0Sstevel@tonic-gate 		AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG);
574*0Sstevel@tonic-gate 		fnp->fn_error = 0;
575*0Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
576*0Sstevel@tonic-gate 		/*
577*0Sstevel@tonic-gate 		 * auto_new_mount_thread fires up a new thread which
578*0Sstevel@tonic-gate 		 * calls automountd finishing up the work
579*0Sstevel@tonic-gate 		 */
580*0Sstevel@tonic-gate 		auto_new_mount_thread(fnp, searchnm, cred);
581*0Sstevel@tonic-gate 
582*0Sstevel@tonic-gate 		/*
583*0Sstevel@tonic-gate 		 * At this point, we are simply another thread
584*0Sstevel@tonic-gate 		 * waiting for the mount to complete
585*0Sstevel@tonic-gate 		 */
586*0Sstevel@tonic-gate 		error = auto_wait4mount(fnp);
587*0Sstevel@tonic-gate 		if (error == AUTOFS_SHUTDOWN)
588*0Sstevel@tonic-gate 			error = ENOENT;
589*0Sstevel@tonic-gate 
590*0Sstevel@tonic-gate 		/*
591*0Sstevel@tonic-gate 		 * now release our reference to this vnode
592*0Sstevel@tonic-gate 		 */
593*0Sstevel@tonic-gate 		VN_RELE(fntovn(fnp));
594*0Sstevel@tonic-gate 		if (!error)
595*0Sstevel@tonic-gate 			goto top;
596*0Sstevel@tonic-gate 		break;
597*0Sstevel@tonic-gate 	default:
598*0Sstevel@tonic-gate 		auto_log(dfnp->fn_globals, CE_WARN, "auto_lookup: unknown "
599*0Sstevel@tonic-gate 		    "operation %d", operation);
600*0Sstevel@tonic-gate 	}
601*0Sstevel@tonic-gate 
602*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_lookup: name=%s *vpp=%p return=%d\n",
603*0Sstevel@tonic-gate 	    nm, (void *)*vpp, error));
604*0Sstevel@tonic-gate 
605*0Sstevel@tonic-gate 	return (error);
606*0Sstevel@tonic-gate }
607*0Sstevel@tonic-gate 
608*0Sstevel@tonic-gate static int
609*0Sstevel@tonic-gate auto_create(
610*0Sstevel@tonic-gate 	vnode_t *dvp,
611*0Sstevel@tonic-gate 	char *nm,
612*0Sstevel@tonic-gate 	vattr_t *va,
613*0Sstevel@tonic-gate 	vcexcl_t excl,
614*0Sstevel@tonic-gate 	int mode,
615*0Sstevel@tonic-gate 	vnode_t **vpp,
616*0Sstevel@tonic-gate 	cred_t *cred,
617*0Sstevel@tonic-gate 	int flag)
618*0Sstevel@tonic-gate {
619*0Sstevel@tonic-gate 	vnode_t *newvp;
620*0Sstevel@tonic-gate 	int error;
621*0Sstevel@tonic-gate 
622*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_create dvp %p nm %s\n", (void *)dvp, nm));
623*0Sstevel@tonic-gate 
624*0Sstevel@tonic-gate 	if (error = auto_trigger_mount(dvp, cred, &newvp))
625*0Sstevel@tonic-gate 		goto done;
626*0Sstevel@tonic-gate 
627*0Sstevel@tonic-gate 	if (newvp != NULL) {
628*0Sstevel@tonic-gate 		/*
629*0Sstevel@tonic-gate 		 * Node is now mounted on.
630*0Sstevel@tonic-gate 		 */
631*0Sstevel@tonic-gate 		if (vn_is_readonly(newvp))
632*0Sstevel@tonic-gate 			error = EROFS;
633*0Sstevel@tonic-gate 		else
634*0Sstevel@tonic-gate 			error = VOP_CREATE(newvp, nm, va, excl,
635*0Sstevel@tonic-gate 			    mode, vpp, cred, flag);
636*0Sstevel@tonic-gate 		VN_RELE(newvp);
637*0Sstevel@tonic-gate 	} else
638*0Sstevel@tonic-gate 		error = ENOSYS;
639*0Sstevel@tonic-gate 
640*0Sstevel@tonic-gate done:
641*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_create: error=%d\n", error));
642*0Sstevel@tonic-gate 	return (error);
643*0Sstevel@tonic-gate }
644*0Sstevel@tonic-gate 
645*0Sstevel@tonic-gate static int
646*0Sstevel@tonic-gate auto_remove(vnode_t *dvp, char *nm, cred_t *cred)
647*0Sstevel@tonic-gate {
648*0Sstevel@tonic-gate 	vnode_t *newvp;
649*0Sstevel@tonic-gate 	int error;
650*0Sstevel@tonic-gate 
651*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_remove dvp %p nm %s\n", (void *)dvp, nm));
652*0Sstevel@tonic-gate 
653*0Sstevel@tonic-gate 	if (error = auto_trigger_mount(dvp, cred, &newvp))
654*0Sstevel@tonic-gate 		goto done;
655*0Sstevel@tonic-gate 
656*0Sstevel@tonic-gate 	if (newvp != NULL) {
657*0Sstevel@tonic-gate 		/*
658*0Sstevel@tonic-gate 		 * Node is now mounted on.
659*0Sstevel@tonic-gate 		 */
660*0Sstevel@tonic-gate 		if (vn_is_readonly(newvp))
661*0Sstevel@tonic-gate 			error = EROFS;
662*0Sstevel@tonic-gate 		else
663*0Sstevel@tonic-gate 			error = VOP_REMOVE(newvp, nm, cred);
664*0Sstevel@tonic-gate 		VN_RELE(newvp);
665*0Sstevel@tonic-gate 	} else
666*0Sstevel@tonic-gate 		error = ENOSYS;
667*0Sstevel@tonic-gate 
668*0Sstevel@tonic-gate done:
669*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_remove: error=%d\n", error));
670*0Sstevel@tonic-gate 	return (error);
671*0Sstevel@tonic-gate }
672*0Sstevel@tonic-gate 
673*0Sstevel@tonic-gate static int
674*0Sstevel@tonic-gate auto_link(vnode_t *tdvp, vnode_t *svp, char *nm, cred_t *cred)
675*0Sstevel@tonic-gate {
676*0Sstevel@tonic-gate 	vnode_t *newvp;
677*0Sstevel@tonic-gate 	int error;
678*0Sstevel@tonic-gate 
679*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_link tdvp %p svp %p nm %s\n", (void *)tdvp,
680*0Sstevel@tonic-gate 	    (void *)svp, nm));
681*0Sstevel@tonic-gate 
682*0Sstevel@tonic-gate 	if (error = auto_trigger_mount(tdvp, cred, &newvp))
683*0Sstevel@tonic-gate 		goto done;
684*0Sstevel@tonic-gate 
685*0Sstevel@tonic-gate 	if (newvp == NULL) {
686*0Sstevel@tonic-gate 		/*
687*0Sstevel@tonic-gate 		 * an autonode can not be a link to another node
688*0Sstevel@tonic-gate 		 */
689*0Sstevel@tonic-gate 		error = ENOSYS;
690*0Sstevel@tonic-gate 		goto done;
691*0Sstevel@tonic-gate 	}
692*0Sstevel@tonic-gate 
693*0Sstevel@tonic-gate 	if (vn_is_readonly(newvp)) {
694*0Sstevel@tonic-gate 		error = EROFS;
695*0Sstevel@tonic-gate 		VN_RELE(newvp);
696*0Sstevel@tonic-gate 		goto done;
697*0Sstevel@tonic-gate 	}
698*0Sstevel@tonic-gate 
699*0Sstevel@tonic-gate 	if (vn_matchops(svp, auto_vnodeops)) {
700*0Sstevel@tonic-gate 		/*
701*0Sstevel@tonic-gate 		 * source vp can't be an autonode
702*0Sstevel@tonic-gate 		 */
703*0Sstevel@tonic-gate 		error = ENOSYS;
704*0Sstevel@tonic-gate 		VN_RELE(newvp);
705*0Sstevel@tonic-gate 		goto done;
706*0Sstevel@tonic-gate 	}
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate 	error = VOP_LINK(newvp, svp, nm, cred);
709*0Sstevel@tonic-gate 	VN_RELE(newvp);
710*0Sstevel@tonic-gate 
711*0Sstevel@tonic-gate done:
712*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_link error=%d\n", error));
713*0Sstevel@tonic-gate 	return (error);
714*0Sstevel@tonic-gate }
715*0Sstevel@tonic-gate 
716*0Sstevel@tonic-gate static int
717*0Sstevel@tonic-gate auto_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr)
718*0Sstevel@tonic-gate {
719*0Sstevel@tonic-gate 	vnode_t *o_newvp, *n_newvp;
720*0Sstevel@tonic-gate 	int error;
721*0Sstevel@tonic-gate 
722*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_rename odvp %p onm %s to ndvp %p nnm %s\n",
723*0Sstevel@tonic-gate 	    (void *)odvp, onm, (void *)ndvp, nnm));
724*0Sstevel@tonic-gate 
725*0Sstevel@tonic-gate 	/*
726*0Sstevel@tonic-gate 	 * we know odvp is an autonode, otherwise this function
727*0Sstevel@tonic-gate 	 * could not have ever been called.
728*0Sstevel@tonic-gate 	 */
729*0Sstevel@tonic-gate 	ASSERT(vn_matchops(odvp, auto_vnodeops));
730*0Sstevel@tonic-gate 
731*0Sstevel@tonic-gate 	if (error = auto_trigger_mount(odvp, cr, &o_newvp))
732*0Sstevel@tonic-gate 		goto done;
733*0Sstevel@tonic-gate 
734*0Sstevel@tonic-gate 	if (o_newvp == NULL) {
735*0Sstevel@tonic-gate 		/*
736*0Sstevel@tonic-gate 		 * can't rename an autonode
737*0Sstevel@tonic-gate 		 */
738*0Sstevel@tonic-gate 		error = ENOSYS;
739*0Sstevel@tonic-gate 		goto done;
740*0Sstevel@tonic-gate 	}
741*0Sstevel@tonic-gate 
742*0Sstevel@tonic-gate 	if (vn_matchops(ndvp, auto_vnodeops)) {
743*0Sstevel@tonic-gate 		/*
744*0Sstevel@tonic-gate 		 * directory is AUTOFS, need to trigger the
745*0Sstevel@tonic-gate 		 * mount of the real filesystem.
746*0Sstevel@tonic-gate 		 */
747*0Sstevel@tonic-gate 		if (error = auto_trigger_mount(ndvp, cr, &n_newvp)) {
748*0Sstevel@tonic-gate 			VN_RELE(o_newvp);
749*0Sstevel@tonic-gate 			goto done;
750*0Sstevel@tonic-gate 		}
751*0Sstevel@tonic-gate 
752*0Sstevel@tonic-gate 		if (n_newvp == NULL) {
753*0Sstevel@tonic-gate 			/*
754*0Sstevel@tonic-gate 			 * target can't be an autonode
755*0Sstevel@tonic-gate 			 */
756*0Sstevel@tonic-gate 			error = ENOSYS;
757*0Sstevel@tonic-gate 			VN_RELE(o_newvp);
758*0Sstevel@tonic-gate 			goto done;
759*0Sstevel@tonic-gate 		}
760*0Sstevel@tonic-gate 	} else {
761*0Sstevel@tonic-gate 		/*
762*0Sstevel@tonic-gate 		 * destination directory mount had been
763*0Sstevel@tonic-gate 		 * triggered prior to the call to this function.
764*0Sstevel@tonic-gate 		 */
765*0Sstevel@tonic-gate 		n_newvp = ndvp;
766*0Sstevel@tonic-gate 	}
767*0Sstevel@tonic-gate 
768*0Sstevel@tonic-gate 	ASSERT(!vn_matchops(n_newvp, auto_vnodeops));
769*0Sstevel@tonic-gate 
770*0Sstevel@tonic-gate 	if (vn_is_readonly(n_newvp)) {
771*0Sstevel@tonic-gate 		error = EROFS;
772*0Sstevel@tonic-gate 		VN_RELE(o_newvp);
773*0Sstevel@tonic-gate 		if (n_newvp != ndvp)
774*0Sstevel@tonic-gate 			VN_RELE(n_newvp);
775*0Sstevel@tonic-gate 		goto done;
776*0Sstevel@tonic-gate 	}
777*0Sstevel@tonic-gate 
778*0Sstevel@tonic-gate 	error = VOP_RENAME(o_newvp, onm, n_newvp, nnm, cr);
779*0Sstevel@tonic-gate 	VN_RELE(o_newvp);
780*0Sstevel@tonic-gate 	if (n_newvp != ndvp)
781*0Sstevel@tonic-gate 		VN_RELE(n_newvp);
782*0Sstevel@tonic-gate 
783*0Sstevel@tonic-gate done:
784*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_rename error=%d\n", error));
785*0Sstevel@tonic-gate 	return (error);
786*0Sstevel@tonic-gate }
787*0Sstevel@tonic-gate 
788*0Sstevel@tonic-gate static int
789*0Sstevel@tonic-gate auto_mkdir(vnode_t *dvp, char *nm, vattr_t *va, vnode_t **vpp, cred_t *cred)
790*0Sstevel@tonic-gate {
791*0Sstevel@tonic-gate 	vnode_t *newvp;
792*0Sstevel@tonic-gate 	int error;
793*0Sstevel@tonic-gate 
794*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_mkdir dvp %p nm %s\n", (void *)dvp, nm));
795*0Sstevel@tonic-gate 
796*0Sstevel@tonic-gate 	if (error = auto_trigger_mount(dvp, cred, &newvp))
797*0Sstevel@tonic-gate 		goto done;
798*0Sstevel@tonic-gate 
799*0Sstevel@tonic-gate 	if (newvp != NULL) {
800*0Sstevel@tonic-gate 		/*
801*0Sstevel@tonic-gate 		 * Node is now mounted on.
802*0Sstevel@tonic-gate 		 */
803*0Sstevel@tonic-gate 		if (vn_is_readonly(newvp))
804*0Sstevel@tonic-gate 			error = EROFS;
805*0Sstevel@tonic-gate 		else
806*0Sstevel@tonic-gate 			error = VOP_MKDIR(newvp, nm, va, vpp, cred);
807*0Sstevel@tonic-gate 		VN_RELE(newvp);
808*0Sstevel@tonic-gate 	} else
809*0Sstevel@tonic-gate 		error = ENOSYS;
810*0Sstevel@tonic-gate 
811*0Sstevel@tonic-gate done:
812*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_mkdir: error=%d\n", error));
813*0Sstevel@tonic-gate 	return (error);
814*0Sstevel@tonic-gate }
815*0Sstevel@tonic-gate 
816*0Sstevel@tonic-gate static int
817*0Sstevel@tonic-gate auto_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cred)
818*0Sstevel@tonic-gate {
819*0Sstevel@tonic-gate 	vnode_t *newvp;
820*0Sstevel@tonic-gate 	int error;
821*0Sstevel@tonic-gate 
822*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_rmdir: vp=%p nm=%s\n", (void *)dvp, nm));
823*0Sstevel@tonic-gate 
824*0Sstevel@tonic-gate 	if (error = auto_trigger_mount(dvp, cred, &newvp))
825*0Sstevel@tonic-gate 		goto done;
826*0Sstevel@tonic-gate 
827*0Sstevel@tonic-gate 	if (newvp != NULL) {
828*0Sstevel@tonic-gate 		/*
829*0Sstevel@tonic-gate 		 * Node is now mounted on.
830*0Sstevel@tonic-gate 		 */
831*0Sstevel@tonic-gate 		if (vn_is_readonly(newvp))
832*0Sstevel@tonic-gate 			error = EROFS;
833*0Sstevel@tonic-gate 		else
834*0Sstevel@tonic-gate 			error = VOP_RMDIR(newvp, nm, cdir, cred);
835*0Sstevel@tonic-gate 		VN_RELE(newvp);
836*0Sstevel@tonic-gate 	} else
837*0Sstevel@tonic-gate 		error = ENOSYS;
838*0Sstevel@tonic-gate 
839*0Sstevel@tonic-gate done:
840*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_rmdir: error=%d\n", error));
841*0Sstevel@tonic-gate 	return (error);
842*0Sstevel@tonic-gate }
843*0Sstevel@tonic-gate 
844*0Sstevel@tonic-gate static int autofs_nobrowse = 0;
845*0Sstevel@tonic-gate 
846*0Sstevel@tonic-gate #ifdef nextdp
847*0Sstevel@tonic-gate #undef nextdp
848*0Sstevel@tonic-gate #endif
849*0Sstevel@tonic-gate #define	nextdp(dp)	((struct dirent64 *)((char *)(dp) + (dp)->d_reclen))
850*0Sstevel@tonic-gate 
851*0Sstevel@tonic-gate static int
852*0Sstevel@tonic-gate auto_readdir(vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp)
853*0Sstevel@tonic-gate {
854*0Sstevel@tonic-gate 	struct autofs_rddirargs rda;
855*0Sstevel@tonic-gate 	struct autofs_rddirres rd;
856*0Sstevel@tonic-gate 	fnnode_t *fnp = vntofn(vp);
857*0Sstevel@tonic-gate 	fnnode_t *cfnp, *nfnp;
858*0Sstevel@tonic-gate 	dirent64_t *dp;
859*0Sstevel@tonic-gate 	ulong_t offset;
860*0Sstevel@tonic-gate 	ulong_t outcount = 0, count = 0;
861*0Sstevel@tonic-gate 	size_t namelen;
862*0Sstevel@tonic-gate 	ulong_t alloc_count;
863*0Sstevel@tonic-gate 	void *outbuf;
864*0Sstevel@tonic-gate 	fninfo_t *fnip = vfstofni(vp->v_vfsp);
865*0Sstevel@tonic-gate 	struct iovec *iovp;
866*0Sstevel@tonic-gate 	int error = 0;
867*0Sstevel@tonic-gate 	int reached_max = 0;
868*0Sstevel@tonic-gate 	int myeof = 0;
869*0Sstevel@tonic-gate 	int this_reclen;
870*0Sstevel@tonic-gate 
871*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_readdir vp=%p offset=%lld\n",
872*0Sstevel@tonic-gate 	    (void *)vp, uiop->uio_loffset));
873*0Sstevel@tonic-gate 
874*0Sstevel@tonic-gate 	if (eofp != NULL)
875*0Sstevel@tonic-gate 		*eofp = 0;
876*0Sstevel@tonic-gate 
877*0Sstevel@tonic-gate 	iovp = uiop->uio_iov;
878*0Sstevel@tonic-gate 	alloc_count = iovp->iov_len;
879*0Sstevel@tonic-gate 
880*0Sstevel@tonic-gate 	if (uiop->uio_iovcnt != 1)
881*0Sstevel@tonic-gate 		return (EINVAL);
882*0Sstevel@tonic-gate 
883*0Sstevel@tonic-gate 	gethrestime(&fnp->fn_atime);
884*0Sstevel@tonic-gate 	fnp->fn_ref_time = fnp->fn_atime.tv_sec;
885*0Sstevel@tonic-gate 
886*0Sstevel@tonic-gate 	dp = outbuf = kmem_alloc(alloc_count, KM_SLEEP);
887*0Sstevel@tonic-gate 
888*0Sstevel@tonic-gate 	/*
889*0Sstevel@tonic-gate 	 * Held when getdents calls VOP_RWLOCK....
890*0Sstevel@tonic-gate 	 */
891*0Sstevel@tonic-gate 	ASSERT(RW_READ_HELD(&fnp->fn_rwlock));
892*0Sstevel@tonic-gate 	if (uiop->uio_offset >= AUTOFS_DAEMONCOOKIE) {
893*0Sstevel@tonic-gate again:
894*0Sstevel@tonic-gate 		/*
895*0Sstevel@tonic-gate 		 * Do readdir of daemon contents only
896*0Sstevel@tonic-gate 		 * Drop readers lock and reacquire after reply.
897*0Sstevel@tonic-gate 		 */
898*0Sstevel@tonic-gate 		rw_exit(&fnp->fn_rwlock);
899*0Sstevel@tonic-gate 
900*0Sstevel@tonic-gate 		count = 0;
901*0Sstevel@tonic-gate 		rda.rda_map = fnip->fi_map;
902*0Sstevel@tonic-gate 		rda.rda_offset = (uint_t)uiop->uio_offset;
903*0Sstevel@tonic-gate 		rd.rd_rddir.rddir_entries = dp;
904*0Sstevel@tonic-gate 		rda.rda_count = rd.rd_rddir.rddir_size = (uint_t)alloc_count;
905*0Sstevel@tonic-gate 		error = auto_calldaemon(fnip, AUTOFS_READDIR,
906*0Sstevel@tonic-gate 		    xdr_autofs_rddirargs, &rda,
907*0Sstevel@tonic-gate 		    xdr_autofs_rddirres, &rd,
908*0Sstevel@tonic-gate 		    cred, TRUE);
909*0Sstevel@tonic-gate 		/*
910*0Sstevel@tonic-gate 		 * reacquire previously dropped lock
911*0Sstevel@tonic-gate 		 */
912*0Sstevel@tonic-gate 		rw_enter(&fnp->fn_rwlock, RW_READER);
913*0Sstevel@tonic-gate 
914*0Sstevel@tonic-gate 		if (!error)
915*0Sstevel@tonic-gate 			error = rd.rd_status;
916*0Sstevel@tonic-gate 		if (error) {
917*0Sstevel@tonic-gate 			if (error == AUTOFS_SHUTDOWN) {
918*0Sstevel@tonic-gate 				/*
919*0Sstevel@tonic-gate 				 * treat as empty directory
920*0Sstevel@tonic-gate 				 */
921*0Sstevel@tonic-gate 				error = 0;
922*0Sstevel@tonic-gate 				myeof = 1;
923*0Sstevel@tonic-gate 				if (eofp)
924*0Sstevel@tonic-gate 					*eofp = 1;
925*0Sstevel@tonic-gate 			}
926*0Sstevel@tonic-gate 			goto done;
927*0Sstevel@tonic-gate 		}
928*0Sstevel@tonic-gate 
929*0Sstevel@tonic-gate 		if (rd.rd_rddir.rddir_size) {
930*0Sstevel@tonic-gate 			dirent64_t *odp = dp;	/* next in output buffer */
931*0Sstevel@tonic-gate 			dirent64_t *cdp = dp;	/* current examined entry */
932*0Sstevel@tonic-gate 
933*0Sstevel@tonic-gate 			/*
934*0Sstevel@tonic-gate 			 * Check for duplicates here
935*0Sstevel@tonic-gate 			 */
936*0Sstevel@tonic-gate 			do {
937*0Sstevel@tonic-gate 				this_reclen = cdp->d_reclen;
938*0Sstevel@tonic-gate 				if (auto_search(fnp, cdp->d_name, NULL, cred)) {
939*0Sstevel@tonic-gate 					/*
940*0Sstevel@tonic-gate 					 * entry not found in kernel list,
941*0Sstevel@tonic-gate 					 * include it in readdir output.
942*0Sstevel@tonic-gate 					 *
943*0Sstevel@tonic-gate 					 * If we are skipping entries. then
944*0Sstevel@tonic-gate 					 * we need to copy this entry to the
945*0Sstevel@tonic-gate 					 * correct position in the buffer
946*0Sstevel@tonic-gate 					 * to be copied out.
947*0Sstevel@tonic-gate 					 */
948*0Sstevel@tonic-gate 					if (cdp != odp)
949*0Sstevel@tonic-gate 						bcopy(cdp, odp,
950*0Sstevel@tonic-gate 						    (size_t)this_reclen);
951*0Sstevel@tonic-gate 					odp = nextdp(odp);
952*0Sstevel@tonic-gate 					outcount += this_reclen;
953*0Sstevel@tonic-gate 				} else {
954*0Sstevel@tonic-gate 					/*
955*0Sstevel@tonic-gate 					 * Entry was found in the kernel
956*0Sstevel@tonic-gate 					 * list. If it is the first entry
957*0Sstevel@tonic-gate 					 * in this buffer, then just skip it
958*0Sstevel@tonic-gate 					 */
959*0Sstevel@tonic-gate 					if (odp == dp) {
960*0Sstevel@tonic-gate 						dp = nextdp(dp);
961*0Sstevel@tonic-gate 						odp = dp;
962*0Sstevel@tonic-gate 					}
963*0Sstevel@tonic-gate 				}
964*0Sstevel@tonic-gate 				count += this_reclen;
965*0Sstevel@tonic-gate 				cdp = (struct dirent64 *)
966*0Sstevel@tonic-gate 				    ((char *)cdp + this_reclen);
967*0Sstevel@tonic-gate 			} while (count < rd.rd_rddir.rddir_size);
968*0Sstevel@tonic-gate 
969*0Sstevel@tonic-gate 			if (outcount)
970*0Sstevel@tonic-gate 				error = uiomove(dp, outcount, UIO_READ, uiop);
971*0Sstevel@tonic-gate 			uiop->uio_offset = rd.rd_rddir.rddir_offset;
972*0Sstevel@tonic-gate 		} else {
973*0Sstevel@tonic-gate 			if (rd.rd_rddir.rddir_eof == 0) {
974*0Sstevel@tonic-gate 				/*
975*0Sstevel@tonic-gate 				 * alloc_count not large enough for one
976*0Sstevel@tonic-gate 				 * directory entry
977*0Sstevel@tonic-gate 				 */
978*0Sstevel@tonic-gate 				error = EINVAL;
979*0Sstevel@tonic-gate 			}
980*0Sstevel@tonic-gate 		}
981*0Sstevel@tonic-gate 		if (rd.rd_rddir.rddir_eof && !error) {
982*0Sstevel@tonic-gate 			myeof = 1;
983*0Sstevel@tonic-gate 			if (eofp)
984*0Sstevel@tonic-gate 				*eofp = 1;
985*0Sstevel@tonic-gate 		}
986*0Sstevel@tonic-gate 		if (!error && !myeof && outcount == 0) {
987*0Sstevel@tonic-gate 			/*
988*0Sstevel@tonic-gate 			 * call daemon with new cookie, all previous
989*0Sstevel@tonic-gate 			 * elements happened to be duplicates
990*0Sstevel@tonic-gate 			 */
991*0Sstevel@tonic-gate 			dp = outbuf;
992*0Sstevel@tonic-gate 			goto again;
993*0Sstevel@tonic-gate 		}
994*0Sstevel@tonic-gate 		goto done;
995*0Sstevel@tonic-gate 	}
996*0Sstevel@tonic-gate 
997*0Sstevel@tonic-gate 	if (uiop->uio_offset == 0) {
998*0Sstevel@tonic-gate 		/*
999*0Sstevel@tonic-gate 		 * first time: so fudge the . and ..
1000*0Sstevel@tonic-gate 		 */
1001*0Sstevel@tonic-gate 		this_reclen = DIRENT64_RECLEN(1);
1002*0Sstevel@tonic-gate 		if (alloc_count < this_reclen) {
1003*0Sstevel@tonic-gate 			error = EINVAL;
1004*0Sstevel@tonic-gate 			goto done;
1005*0Sstevel@tonic-gate 		}
1006*0Sstevel@tonic-gate 		dp->d_ino = (ino64_t)fnp->fn_nodeid;
1007*0Sstevel@tonic-gate 		dp->d_off = (off64_t)1;
1008*0Sstevel@tonic-gate 		dp->d_reclen = (ushort_t)this_reclen;
1009*0Sstevel@tonic-gate 
1010*0Sstevel@tonic-gate 		/* use strncpy(9f) to zero out uninitialized bytes */
1011*0Sstevel@tonic-gate 
1012*0Sstevel@tonic-gate 		(void) strncpy(dp->d_name, ".",
1013*0Sstevel@tonic-gate 		    DIRENT64_NAMELEN(this_reclen));
1014*0Sstevel@tonic-gate 		outcount += dp->d_reclen;
1015*0Sstevel@tonic-gate 		dp = nextdp(dp);
1016*0Sstevel@tonic-gate 
1017*0Sstevel@tonic-gate 		this_reclen = DIRENT64_RECLEN(2);
1018*0Sstevel@tonic-gate 		if (alloc_count < outcount + this_reclen) {
1019*0Sstevel@tonic-gate 			error = EINVAL;
1020*0Sstevel@tonic-gate 			goto done;
1021*0Sstevel@tonic-gate 		}
1022*0Sstevel@tonic-gate 		dp->d_reclen = (ushort_t)this_reclen;
1023*0Sstevel@tonic-gate 		dp->d_ino = (ino64_t)fnp->fn_parent->fn_nodeid;
1024*0Sstevel@tonic-gate 		dp->d_off = (off64_t)2;
1025*0Sstevel@tonic-gate 
1026*0Sstevel@tonic-gate 		/* use strncpy(9f) to zero out uninitialized bytes */
1027*0Sstevel@tonic-gate 
1028*0Sstevel@tonic-gate 		(void) strncpy(dp->d_name, "..",
1029*0Sstevel@tonic-gate 		    DIRENT64_NAMELEN(this_reclen));
1030*0Sstevel@tonic-gate 		outcount += dp->d_reclen;
1031*0Sstevel@tonic-gate 		dp = nextdp(dp);
1032*0Sstevel@tonic-gate 	}
1033*0Sstevel@tonic-gate 
1034*0Sstevel@tonic-gate 	offset = 2;
1035*0Sstevel@tonic-gate 	cfnp = fnp->fn_dirents;
1036*0Sstevel@tonic-gate 	while (cfnp != NULL) {
1037*0Sstevel@tonic-gate 		nfnp = cfnp->fn_next;
1038*0Sstevel@tonic-gate 		offset = cfnp->fn_offset;
1039*0Sstevel@tonic-gate 		if ((offset >= uiop->uio_offset) &&
1040*0Sstevel@tonic-gate 		    (!(cfnp->fn_flags & MF_LOOKUP))) {
1041*0Sstevel@tonic-gate 			int reclen;
1042*0Sstevel@tonic-gate 
1043*0Sstevel@tonic-gate 			/*
1044*0Sstevel@tonic-gate 			 * include node only if its offset is greater or
1045*0Sstevel@tonic-gate 			 * equal to the one required and it is not in
1046*0Sstevel@tonic-gate 			 * transient state (not being looked-up)
1047*0Sstevel@tonic-gate 			 */
1048*0Sstevel@tonic-gate 			namelen = strlen(cfnp->fn_name);
1049*0Sstevel@tonic-gate 			reclen = (int)DIRENT64_RECLEN(namelen);
1050*0Sstevel@tonic-gate 			if (outcount + reclen > alloc_count) {
1051*0Sstevel@tonic-gate 				reached_max = 1;
1052*0Sstevel@tonic-gate 				break;
1053*0Sstevel@tonic-gate 			}
1054*0Sstevel@tonic-gate 			dp->d_reclen = (ushort_t)reclen;
1055*0Sstevel@tonic-gate 			dp->d_ino = (ino64_t)cfnp->fn_nodeid;
1056*0Sstevel@tonic-gate 			if (nfnp != NULL) {
1057*0Sstevel@tonic-gate 				/*
1058*0Sstevel@tonic-gate 				 * get the offset of the next element
1059*0Sstevel@tonic-gate 				 */
1060*0Sstevel@tonic-gate 				dp->d_off = (off64_t)nfnp->fn_offset;
1061*0Sstevel@tonic-gate 			} else {
1062*0Sstevel@tonic-gate 				/*
1063*0Sstevel@tonic-gate 				 * This is the last element, make
1064*0Sstevel@tonic-gate 				 * offset one plus the current
1065*0Sstevel@tonic-gate 				 */
1066*0Sstevel@tonic-gate 				dp->d_off = (off64_t)cfnp->fn_offset + 1;
1067*0Sstevel@tonic-gate 			}
1068*0Sstevel@tonic-gate 
1069*0Sstevel@tonic-gate 			/* use strncpy(9f) to zero out uninitialized bytes */
1070*0Sstevel@tonic-gate 
1071*0Sstevel@tonic-gate 			(void) strncpy(dp->d_name, cfnp->fn_name,
1072*0Sstevel@tonic-gate 			    DIRENT64_NAMELEN(reclen));
1073*0Sstevel@tonic-gate 			outcount += dp->d_reclen;
1074*0Sstevel@tonic-gate 			dp = nextdp(dp);
1075*0Sstevel@tonic-gate 		}
1076*0Sstevel@tonic-gate 		cfnp = nfnp;
1077*0Sstevel@tonic-gate 	}
1078*0Sstevel@tonic-gate 
1079*0Sstevel@tonic-gate 	if (outcount)
1080*0Sstevel@tonic-gate 		error = uiomove(outbuf, outcount, UIO_READ, uiop);
1081*0Sstevel@tonic-gate 	if (!error) {
1082*0Sstevel@tonic-gate 		if (reached_max) {
1083*0Sstevel@tonic-gate 			/*
1084*0Sstevel@tonic-gate 			 * This entry did not get added to the buffer on this,
1085*0Sstevel@tonic-gate 			 * call. We need to add it on the next call therefore
1086*0Sstevel@tonic-gate 			 * set uio_offset to this entry's offset.  If there
1087*0Sstevel@tonic-gate 			 * wasn't enough space for one dirent, return EINVAL.
1088*0Sstevel@tonic-gate 			 */
1089*0Sstevel@tonic-gate 			uiop->uio_offset = offset;
1090*0Sstevel@tonic-gate 			if (outcount == 0)
1091*0Sstevel@tonic-gate 				error = EINVAL;
1092*0Sstevel@tonic-gate 		} else if (autofs_nobrowse ||
1093*0Sstevel@tonic-gate 		    auto_nobrowse_option(fnip->fi_opts) ||
1094*0Sstevel@tonic-gate 		    (fnip->fi_flags & MF_DIRECT) || (fnp->fn_trigger != NULL) ||
1095*0Sstevel@tonic-gate 		    (((vp->v_flag & VROOT) == 0) &&
1096*0Sstevel@tonic-gate 		    ((fntovn(fnp->fn_parent))->v_flag & VROOT) &&
1097*0Sstevel@tonic-gate 		    (fnp->fn_dirents == NULL))) {
1098*0Sstevel@tonic-gate 			/*
1099*0Sstevel@tonic-gate 			 * done reading directory entries
1100*0Sstevel@tonic-gate 			 */
1101*0Sstevel@tonic-gate 			uiop->uio_offset = offset + 1;
1102*0Sstevel@tonic-gate 			if (eofp)
1103*0Sstevel@tonic-gate 				*eofp = 1;
1104*0Sstevel@tonic-gate 		} else {
1105*0Sstevel@tonic-gate 			/*
1106*0Sstevel@tonic-gate 			 * Need to get the rest of the entries from the daemon.
1107*0Sstevel@tonic-gate 			 */
1108*0Sstevel@tonic-gate 			uiop->uio_offset = AUTOFS_DAEMONCOOKIE;
1109*0Sstevel@tonic-gate 		}
1110*0Sstevel@tonic-gate 	}
1111*0Sstevel@tonic-gate 
1112*0Sstevel@tonic-gate done:
1113*0Sstevel@tonic-gate 	kmem_free(outbuf, alloc_count);
1114*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_readdir vp=%p offset=%lld eof=%d\n",
1115*0Sstevel@tonic-gate 	    (void *)vp, uiop->uio_loffset, myeof));
1116*0Sstevel@tonic-gate 	return (error);
1117*0Sstevel@tonic-gate }
1118*0Sstevel@tonic-gate 
1119*0Sstevel@tonic-gate static int
1120*0Sstevel@tonic-gate auto_symlink(
1121*0Sstevel@tonic-gate 	vnode_t *dvp,
1122*0Sstevel@tonic-gate 	char *lnknm,		/* new entry */
1123*0Sstevel@tonic-gate 	vattr_t *tva,
1124*0Sstevel@tonic-gate 	char *tnm,		/* existing entry */
1125*0Sstevel@tonic-gate 	cred_t *cred)
1126*0Sstevel@tonic-gate {
1127*0Sstevel@tonic-gate 	vnode_t *newvp;
1128*0Sstevel@tonic-gate 	int error;
1129*0Sstevel@tonic-gate 
1130*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_symlink: dvp=%p lnknm=%s tnm=%s\n",
1131*0Sstevel@tonic-gate 	    (void *)dvp, lnknm, tnm));
1132*0Sstevel@tonic-gate 
1133*0Sstevel@tonic-gate 	if (error = auto_trigger_mount(dvp, cred, &newvp))
1134*0Sstevel@tonic-gate 		goto done;
1135*0Sstevel@tonic-gate 
1136*0Sstevel@tonic-gate 	if (newvp != NULL) {
1137*0Sstevel@tonic-gate 		/*
1138*0Sstevel@tonic-gate 		 * Node is mounted on.
1139*0Sstevel@tonic-gate 		 */
1140*0Sstevel@tonic-gate 		if (vn_is_readonly(newvp))
1141*0Sstevel@tonic-gate 			error = EROFS;
1142*0Sstevel@tonic-gate 		else
1143*0Sstevel@tonic-gate 			error = VOP_SYMLINK(newvp, lnknm, tva, tnm, cred);
1144*0Sstevel@tonic-gate 		VN_RELE(newvp);
1145*0Sstevel@tonic-gate 	} else
1146*0Sstevel@tonic-gate 		error = ENOSYS;
1147*0Sstevel@tonic-gate 
1148*0Sstevel@tonic-gate done:
1149*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_symlink: error=%d\n", error));
1150*0Sstevel@tonic-gate 	return (error);
1151*0Sstevel@tonic-gate }
1152*0Sstevel@tonic-gate 
1153*0Sstevel@tonic-gate /* ARGSUSED */
1154*0Sstevel@tonic-gate static int
1155*0Sstevel@tonic-gate auto_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr)
1156*0Sstevel@tonic-gate {
1157*0Sstevel@tonic-gate 	fnnode_t *fnp = vntofn(vp);
1158*0Sstevel@tonic-gate 	int error;
1159*0Sstevel@tonic-gate 	timestruc_t now;
1160*0Sstevel@tonic-gate 
1161*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_readlink: vp=%p\n", (void *)vp));
1162*0Sstevel@tonic-gate 
1163*0Sstevel@tonic-gate 	gethrestime(&now);
1164*0Sstevel@tonic-gate 	fnp->fn_ref_time = now.tv_sec;
1165*0Sstevel@tonic-gate 
1166*0Sstevel@tonic-gate 	if (vp->v_type != VLNK)
1167*0Sstevel@tonic-gate 		error = EINVAL;
1168*0Sstevel@tonic-gate 	else {
1169*0Sstevel@tonic-gate 		ASSERT(!(fnp->fn_flags & (MF_INPROG | MF_LOOKUP)));
1170*0Sstevel@tonic-gate 		fnp->fn_atime = now;
1171*0Sstevel@tonic-gate 		error = uiomove(fnp->fn_symlink, MIN(fnp->fn_symlinklen,
1172*0Sstevel@tonic-gate 		    uiop->uio_resid), UIO_READ, uiop);
1173*0Sstevel@tonic-gate 	}
1174*0Sstevel@tonic-gate 
1175*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_readlink: error=%d\n", error));
1176*0Sstevel@tonic-gate 	return (error);
1177*0Sstevel@tonic-gate }
1178*0Sstevel@tonic-gate 
1179*0Sstevel@tonic-gate /* ARGSUSED */
1180*0Sstevel@tonic-gate static int
1181*0Sstevel@tonic-gate auto_fsync(vnode_t *cp, int syncflag, cred_t *cred)
1182*0Sstevel@tonic-gate {
1183*0Sstevel@tonic-gate 	return (0);
1184*0Sstevel@tonic-gate }
1185*0Sstevel@tonic-gate 
1186*0Sstevel@tonic-gate /* ARGSUSED */
1187*0Sstevel@tonic-gate static void
1188*0Sstevel@tonic-gate auto_inactive(vnode_t *vp, cred_t *cred)
1189*0Sstevel@tonic-gate {
1190*0Sstevel@tonic-gate 	fnnode_t *fnp = vntofn(vp);
1191*0Sstevel@tonic-gate 	fnnode_t *dfnp = fnp->fn_parent;
1192*0Sstevel@tonic-gate 	int count;
1193*0Sstevel@tonic-gate 
1194*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_inactive: vp=%p v_count=%u fn_link=%d\n",
1195*0Sstevel@tonic-gate 	    (void *)vp, vp->v_count, fnp->fn_linkcnt));
1196*0Sstevel@tonic-gate 
1197*0Sstevel@tonic-gate 	/*
1198*0Sstevel@tonic-gate 	 * The rwlock should not be already held by this thread.
1199*0Sstevel@tonic-gate 	 * The assert relies on the fact that the owner field is cleared
1200*0Sstevel@tonic-gate 	 * when the lock is released.
1201*0Sstevel@tonic-gate 	 */
1202*0Sstevel@tonic-gate 	ASSERT(dfnp != NULL);
1203*0Sstevel@tonic-gate 	ASSERT(rw_owner(&dfnp->fn_rwlock) != curthread);
1204*0Sstevel@tonic-gate 	rw_enter(&dfnp->fn_rwlock, RW_WRITER);
1205*0Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
1206*0Sstevel@tonic-gate 	ASSERT(vp->v_count > 0);
1207*0Sstevel@tonic-gate 	count = --vp->v_count;
1208*0Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
1209*0Sstevel@tonic-gate 	if (count == 0) {
1210*0Sstevel@tonic-gate 		/*
1211*0Sstevel@tonic-gate 		 * Free only if node has no subdirectories.
1212*0Sstevel@tonic-gate 		 */
1213*0Sstevel@tonic-gate 		if (fnp->fn_linkcnt == 1) {
1214*0Sstevel@tonic-gate 			auto_disconnect(dfnp, fnp);
1215*0Sstevel@tonic-gate 			rw_exit(&dfnp->fn_rwlock);
1216*0Sstevel@tonic-gate 			auto_freefnnode(fnp);
1217*0Sstevel@tonic-gate 			AUTOFS_DPRINT((5, "auto_inactive: (exit) vp=%p freed\n",
1218*0Sstevel@tonic-gate 			    (void *)vp));
1219*0Sstevel@tonic-gate 			return;
1220*0Sstevel@tonic-gate 		}
1221*0Sstevel@tonic-gate 	}
1222*0Sstevel@tonic-gate 	rw_exit(&dfnp->fn_rwlock);
1223*0Sstevel@tonic-gate 
1224*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_inactive: (exit) vp=%p v_count=%u fn_link=%d\n",
1225*0Sstevel@tonic-gate 	    (void *)vp, vp->v_count, fnp->fn_linkcnt));
1226*0Sstevel@tonic-gate }
1227*0Sstevel@tonic-gate 
1228*0Sstevel@tonic-gate /* ARGSUSED2 */
1229*0Sstevel@tonic-gate static int
1230*0Sstevel@tonic-gate auto_rwlock(vnode_t *vp, int write_lock, caller_context_t *ct)
1231*0Sstevel@tonic-gate {
1232*0Sstevel@tonic-gate 	fnnode_t *fnp = vntofn(vp);
1233*0Sstevel@tonic-gate 	if (write_lock)
1234*0Sstevel@tonic-gate 		rw_enter(&fnp->fn_rwlock, RW_WRITER);
1235*0Sstevel@tonic-gate 	else
1236*0Sstevel@tonic-gate 		rw_enter(&fnp->fn_rwlock, RW_READER);
1237*0Sstevel@tonic-gate 	return (write_lock);
1238*0Sstevel@tonic-gate }
1239*0Sstevel@tonic-gate 
1240*0Sstevel@tonic-gate /* ARGSUSED */
1241*0Sstevel@tonic-gate static void
1242*0Sstevel@tonic-gate auto_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ct)
1243*0Sstevel@tonic-gate {
1244*0Sstevel@tonic-gate 	fnnode_t *fnp = vntofn(vp);
1245*0Sstevel@tonic-gate 	rw_exit(&fnp->fn_rwlock);
1246*0Sstevel@tonic-gate }
1247*0Sstevel@tonic-gate 
1248*0Sstevel@tonic-gate 
1249*0Sstevel@tonic-gate /* ARGSUSED */
1250*0Sstevel@tonic-gate static int
1251*0Sstevel@tonic-gate auto_seek(struct vnode *vp, offset_t ooff, offset_t *noffp)
1252*0Sstevel@tonic-gate {
1253*0Sstevel@tonic-gate 	/*
1254*0Sstevel@tonic-gate 	 * Return 0 unconditionally, since we expect
1255*0Sstevel@tonic-gate 	 * a VDIR all the time
1256*0Sstevel@tonic-gate 	 */
1257*0Sstevel@tonic-gate 	return (0);
1258*0Sstevel@tonic-gate }
1259*0Sstevel@tonic-gate 
1260*0Sstevel@tonic-gate /*
1261*0Sstevel@tonic-gate  * Triggers the mount if needed. If the mount has been triggered by
1262*0Sstevel@tonic-gate  * another thread, it will wait for its return status, and return it.
1263*0Sstevel@tonic-gate  * Whether the mount is triggered by this thread, another thread, or
1264*0Sstevel@tonic-gate  * if the vnode was already covered, '*newvp' is a
1265*0Sstevel@tonic-gate  * VN_HELD vnode pointing to the root of the filesystem covering 'vp'.
1266*0Sstevel@tonic-gate  * If the node is not mounted on, and should not be mounted on, '*newvp'
1267*0Sstevel@tonic-gate  * will be NULL.
1268*0Sstevel@tonic-gate  * The calling routine may use '*newvp' to do the filesystem jump.
1269*0Sstevel@tonic-gate  */
1270*0Sstevel@tonic-gate static int
1271*0Sstevel@tonic-gate auto_trigger_mount(vnode_t *vp, cred_t *cred, vnode_t **newvp)
1272*0Sstevel@tonic-gate {
1273*0Sstevel@tonic-gate 	fnnode_t *fnp = vntofn(vp);
1274*0Sstevel@tonic-gate 	fninfo_t *fnip = vfstofni(vp->v_vfsp);
1275*0Sstevel@tonic-gate 	vnode_t *dvp;
1276*0Sstevel@tonic-gate 	vfs_t *vfsp;
1277*0Sstevel@tonic-gate 	int delayed_ind;
1278*0Sstevel@tonic-gate 	char name[AUTOFS_MAXPATHLEN];
1279*0Sstevel@tonic-gate 	int error;
1280*0Sstevel@tonic-gate 
1281*0Sstevel@tonic-gate 	AUTOFS_DPRINT((4, "auto_trigger_mount: vp=%p\n", (void *)vp));
1282*0Sstevel@tonic-gate 
1283*0Sstevel@tonic-gate 	*newvp = NULL;
1284*0Sstevel@tonic-gate 
1285*0Sstevel@tonic-gate 	/*
1286*0Sstevel@tonic-gate 	 * Cross-zone mount triggering is disallowed.
1287*0Sstevel@tonic-gate 	 */
1288*0Sstevel@tonic-gate 	if (fnip->fi_zoneid != getzoneid())
1289*0Sstevel@tonic-gate 		return (EPERM);	/* Not owner of mount */
1290*0Sstevel@tonic-gate 
1291*0Sstevel@tonic-gate retry:
1292*0Sstevel@tonic-gate 	error = 0;
1293*0Sstevel@tonic-gate 	delayed_ind = 0;
1294*0Sstevel@tonic-gate 	mutex_enter(&fnp->fn_lock);
1295*0Sstevel@tonic-gate 	while (fnp->fn_flags & (MF_LOOKUP | MF_INPROG)) {
1296*0Sstevel@tonic-gate 		/*
1297*0Sstevel@tonic-gate 		 * Mount or lookup in progress,
1298*0Sstevel@tonic-gate 		 * wait for it before proceeding.
1299*0Sstevel@tonic-gate 		 */
1300*0Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
1301*0Sstevel@tonic-gate 		error = auto_wait4mount(fnp);
1302*0Sstevel@tonic-gate 		if (error == AUTOFS_SHUTDOWN) {
1303*0Sstevel@tonic-gate 			error = 0;
1304*0Sstevel@tonic-gate 			goto done;
1305*0Sstevel@tonic-gate 		}
1306*0Sstevel@tonic-gate 		if (error && error != EAGAIN)
1307*0Sstevel@tonic-gate 			goto done;
1308*0Sstevel@tonic-gate 		error = 0;
1309*0Sstevel@tonic-gate 		mutex_enter(&fnp->fn_lock);
1310*0Sstevel@tonic-gate 	}
1311*0Sstevel@tonic-gate 
1312*0Sstevel@tonic-gate 	/*
1313*0Sstevel@tonic-gate 	 * If the vfslock can't be acquired for the first time.
1314*0Sstevel@tonic-gate 	 * drop the fn_lock and retry next time in blocking mode.
1315*0Sstevel@tonic-gate 	 */
1316*0Sstevel@tonic-gate 	if (vn_vfswlock(vp)) {
1317*0Sstevel@tonic-gate 		/*
1318*0Sstevel@tonic-gate 		 * Lock held by another thread.
1319*0Sstevel@tonic-gate 		 * Perform blocking by dropping the
1320*0Sstevel@tonic-gate 		 * fn_lock.
1321*0Sstevel@tonic-gate 		 */
1322*0Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
1323*0Sstevel@tonic-gate 		error = vn_vfswlock_wait(vp);
1324*0Sstevel@tonic-gate 		if (error)
1325*0Sstevel@tonic-gate 			goto done;
1326*0Sstevel@tonic-gate 		/*
1327*0Sstevel@tonic-gate 		 * Because fn_lock wasn't held, the state
1328*0Sstevel@tonic-gate 		 * of the trigger node might have changed.
1329*0Sstevel@tonic-gate 		 * Need to run through the checks on trigger
1330*0Sstevel@tonic-gate 		 * node again.
1331*0Sstevel@tonic-gate 		 */
1332*0Sstevel@tonic-gate 		vn_vfsunlock(vp);
1333*0Sstevel@tonic-gate 		goto retry;
1334*0Sstevel@tonic-gate 	}
1335*0Sstevel@tonic-gate 
1336*0Sstevel@tonic-gate 	vfsp = vn_mountedvfs(vp);
1337*0Sstevel@tonic-gate 	if (vfsp != NULL) {
1338*0Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
1339*0Sstevel@tonic-gate 		vfs_lock_wait(vfsp);
1340*0Sstevel@tonic-gate 		vn_vfsunlock(vp);
1341*0Sstevel@tonic-gate 		error = VFS_ROOT(vfsp, newvp);
1342*0Sstevel@tonic-gate 		vfs_unlock(vfsp);
1343*0Sstevel@tonic-gate 		goto done;
1344*0Sstevel@tonic-gate 	} else {
1345*0Sstevel@tonic-gate 		vn_vfsunlock(vp);
1346*0Sstevel@tonic-gate 		if ((fnp->fn_flags & MF_MOUNTPOINT) &&
1347*0Sstevel@tonic-gate 		    fnp->fn_trigger != NULL) {
1348*0Sstevel@tonic-gate 			ASSERT(fnp->fn_dirents == NULL);
1349*0Sstevel@tonic-gate 			/*
1350*0Sstevel@tonic-gate 			 * The filesystem that used to sit here has been
1351*0Sstevel@tonic-gate 			 * forcibly unmounted.
1352*0Sstevel@tonic-gate 			 */
1353*0Sstevel@tonic-gate 			mutex_exit(&fnp->fn_lock);
1354*0Sstevel@tonic-gate 			error = EIO;
1355*0Sstevel@tonic-gate 			goto done;
1356*0Sstevel@tonic-gate 		}
1357*0Sstevel@tonic-gate 	}
1358*0Sstevel@tonic-gate 
1359*0Sstevel@tonic-gate 	ASSERT(vp->v_type == VDIR);
1360*0Sstevel@tonic-gate 	dvp = fntovn(fnp->fn_parent);
1361*0Sstevel@tonic-gate 
1362*0Sstevel@tonic-gate 	if ((fnp->fn_dirents == NULL) &&
1363*0Sstevel@tonic-gate 	    ((fnip->fi_flags & MF_DIRECT) == 0) &&
1364*0Sstevel@tonic-gate 	    ((vp->v_flag & VROOT) == 0) &&
1365*0Sstevel@tonic-gate 	    (dvp->v_flag & VROOT)) {
1366*0Sstevel@tonic-gate 		/*
1367*0Sstevel@tonic-gate 		 * If the parent of this node is the root of an indirect
1368*0Sstevel@tonic-gate 		 * AUTOFS filesystem, this node is remountable.
1369*0Sstevel@tonic-gate 		 */
1370*0Sstevel@tonic-gate 		delayed_ind = 1;
1371*0Sstevel@tonic-gate 	}
1372*0Sstevel@tonic-gate 
1373*0Sstevel@tonic-gate 	if (delayed_ind ||
1374*0Sstevel@tonic-gate 	    ((fnip->fi_flags & MF_DIRECT) && (fnp->fn_dirents == NULL))) {
1375*0Sstevel@tonic-gate 		/*
1376*0Sstevel@tonic-gate 		 * Trigger mount since:
1377*0Sstevel@tonic-gate 		 * direct mountpoint with no subdirs or
1378*0Sstevel@tonic-gate 		 * delayed indirect.
1379*0Sstevel@tonic-gate 		 */
1380*0Sstevel@tonic-gate 		AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG);
1381*0Sstevel@tonic-gate 		fnp->fn_error = 0;
1382*0Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
1383*0Sstevel@tonic-gate 		if (delayed_ind)
1384*0Sstevel@tonic-gate 			(void) strcpy(name, fnp->fn_name);
1385*0Sstevel@tonic-gate 		else
1386*0Sstevel@tonic-gate 			(void) strcpy(name, ".");
1387*0Sstevel@tonic-gate 		fnp->fn_ref_time = gethrestime_sec();
1388*0Sstevel@tonic-gate 		auto_new_mount_thread(fnp, name, cred);
1389*0Sstevel@tonic-gate 		/*
1390*0Sstevel@tonic-gate 		 * At this point we're simply another thread waiting
1391*0Sstevel@tonic-gate 		 * for the mount to finish.
1392*0Sstevel@tonic-gate 		 */
1393*0Sstevel@tonic-gate 		error = auto_wait4mount(fnp);
1394*0Sstevel@tonic-gate 		if (error == EAGAIN)
1395*0Sstevel@tonic-gate 			goto retry;
1396*0Sstevel@tonic-gate 		if (error == AUTOFS_SHUTDOWN) {
1397*0Sstevel@tonic-gate 			error = 0;
1398*0Sstevel@tonic-gate 			goto done;
1399*0Sstevel@tonic-gate 		}
1400*0Sstevel@tonic-gate 		if (error == 0) {
1401*0Sstevel@tonic-gate 			if (error = vn_vfswlock_wait(vp))
1402*0Sstevel@tonic-gate 				goto done;
1403*0Sstevel@tonic-gate 			/* Reacquire after dropping locks */
1404*0Sstevel@tonic-gate 			vfsp = vn_mountedvfs(vp);
1405*0Sstevel@tonic-gate 			if (vfsp != NULL) {
1406*0Sstevel@tonic-gate 				vfs_lock_wait(vfsp);
1407*0Sstevel@tonic-gate 				vn_vfsunlock(vp);
1408*0Sstevel@tonic-gate 				error = VFS_ROOT(vfsp, newvp);
1409*0Sstevel@tonic-gate 				vfs_unlock(vfsp);
1410*0Sstevel@tonic-gate 			} else {
1411*0Sstevel@tonic-gate 				vn_vfsunlock(vp);
1412*0Sstevel@tonic-gate 				goto retry;
1413*0Sstevel@tonic-gate 			}
1414*0Sstevel@tonic-gate 		}
1415*0Sstevel@tonic-gate 	} else
1416*0Sstevel@tonic-gate 		mutex_exit(&fnp->fn_lock);
1417*0Sstevel@tonic-gate 
1418*0Sstevel@tonic-gate done:
1419*0Sstevel@tonic-gate 	AUTOFS_DPRINT((5, "auto_trigger_mount: error=%d\n", error));
1420*0Sstevel@tonic-gate 	return (error);
1421*0Sstevel@tonic-gate }
1422