xref: /dflybsd-src/sys/vfs/autofs/autofs_vnops.c (revision bc6139d4b8b64078d80eba67b9c9f1f79ab2e12f)
1e2950f41STomohiro Kusumi /*-
2e2950f41STomohiro Kusumi  * Copyright (c) 2016 The DragonFly Project
3e2950f41STomohiro Kusumi  * Copyright (c) 2014 The FreeBSD Foundation
4e2950f41STomohiro Kusumi  * All rights reserved.
5e2950f41STomohiro Kusumi  *
6e2950f41STomohiro Kusumi  * This software was developed by Edward Tomasz Napierala under sponsorship
7e2950f41STomohiro Kusumi  * from the FreeBSD Foundation.
8e2950f41STomohiro Kusumi  *
9e2950f41STomohiro Kusumi  * Redistribution and use in source and binary forms, with or without
10e2950f41STomohiro Kusumi  * modification, are permitted provided that the following conditions
11e2950f41STomohiro Kusumi  * are met:
12e2950f41STomohiro Kusumi  * 1. Redistributions of source code must retain the above copyright
13e2950f41STomohiro Kusumi  *    notice, this list of conditions and the following disclaimer.
14e2950f41STomohiro Kusumi  * 2. Redistributions in binary form must reproduce the above copyright
15e2950f41STomohiro Kusumi  *    notice, this list of conditions and the following disclaimer in the
16e2950f41STomohiro Kusumi  *    documentation and/or other materials provided with the distribution.
17e2950f41STomohiro Kusumi  *
18e2950f41STomohiro Kusumi  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19e2950f41STomohiro Kusumi  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20e2950f41STomohiro Kusumi  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21e2950f41STomohiro Kusumi  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22e2950f41STomohiro Kusumi  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23e2950f41STomohiro Kusumi  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24e2950f41STomohiro Kusumi  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25e2950f41STomohiro Kusumi  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26e2950f41STomohiro Kusumi  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27e2950f41STomohiro Kusumi  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28e2950f41STomohiro Kusumi  * SUCH DAMAGE.
29e2950f41STomohiro Kusumi  *
30e2950f41STomohiro Kusumi  */
31e2950f41STomohiro Kusumi 
32e2950f41STomohiro Kusumi #include <sys/kernel.h>
33e2950f41STomohiro Kusumi #include <sys/module.h>
34e2950f41STomohiro Kusumi #include <sys/stat.h>
35e2950f41STomohiro Kusumi #include <sys/dirent.h>
36e2950f41STomohiro Kusumi #include <sys/namei.h>
37e2950f41STomohiro Kusumi #include <sys/nlookup.h>
38e2950f41STomohiro Kusumi #include <sys/mountctl.h>
39e2950f41STomohiro Kusumi 
40e2950f41STomohiro Kusumi #include "autofs.h"
41e2950f41STomohiro Kusumi 
42e2950f41STomohiro Kusumi static int	autofs_trigger_vn(struct vnode *vp, const char *path,
43e2950f41STomohiro Kusumi 		    int pathlen, struct vnode **newvp);
44e2950f41STomohiro Kusumi 
45e2950f41STomohiro Kusumi extern struct autofs_softc	*autofs_softc;
46e2950f41STomohiro Kusumi 
47e2950f41STomohiro Kusumi static __inline size_t
48e2950f41STomohiro Kusumi autofs_dirent_reclen(const char *name)
49e2950f41STomohiro Kusumi {
50e2950f41STomohiro Kusumi 	return (_DIRENT_RECLEN(strlen(name)));
51e2950f41STomohiro Kusumi }
52e2950f41STomohiro Kusumi 
53e2950f41STomohiro Kusumi static int
54e2950f41STomohiro Kusumi test_fs_root(struct vnode *vp)
55e2950f41STomohiro Kusumi {
56e2950f41STomohiro Kusumi 	int error;
57e2950f41STomohiro Kusumi 
58e2950f41STomohiro Kusumi 	if ((error = vget(vp, LK_SHARED)) != 0) {
59e2950f41STomohiro Kusumi 		AUTOFS_WARN("vget failed with error %d", error);
60e2950f41STomohiro Kusumi 		return (1);
61e2950f41STomohiro Kusumi 	}
62e2950f41STomohiro Kusumi 
63e2950f41STomohiro Kusumi 	if (((vp->v_flag & VROOT) == 0) || (vp->v_tag == VT_AUTOFS)) {
64e2950f41STomohiro Kusumi 		vput(vp);
65e2950f41STomohiro Kusumi 		return (1);
66e2950f41STomohiro Kusumi 	}
67e2950f41STomohiro Kusumi 
68e2950f41STomohiro Kusumi 	return (0);
69e2950f41STomohiro Kusumi }
70e2950f41STomohiro Kusumi 
71e2950f41STomohiro Kusumi static int
72e2950f41STomohiro Kusumi nlookup_fs_root(struct autofs_node *anp, struct vnode **vpp)
73e2950f41STomohiro Kusumi {
74e2950f41STomohiro Kusumi 	struct vnode *vp;
75e2950f41STomohiro Kusumi 	struct nlookupdata nd;
76e2950f41STomohiro Kusumi 	char *path;
77e2950f41STomohiro Kusumi 	int error;
78e2950f41STomohiro Kusumi 
79e2950f41STomohiro Kusumi 	path = autofs_path(anp);
80e2950f41STomohiro Kusumi 
81e2950f41STomohiro Kusumi 	error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
82e2950f41STomohiro Kusumi 	if (error == 0) {
83e2950f41STomohiro Kusumi 		error = nlookup(&nd);
84e2950f41STomohiro Kusumi 		if (error == 0) {
85e2950f41STomohiro Kusumi 			vp = nd.nl_nch.ncp->nc_vp;
86e2950f41STomohiro Kusumi 			error = test_fs_root(vp);
87e2950f41STomohiro Kusumi 			if (error == 0)
88e2950f41STomohiro Kusumi 				*vpp = vp;
89e2950f41STomohiro Kusumi 		}
90e2950f41STomohiro Kusumi 	}
91e2950f41STomohiro Kusumi 	nlookup_done(&nd);
92e2950f41STomohiro Kusumi 	kfree(path, M_AUTOFS);
93e2950f41STomohiro Kusumi 
94e2950f41STomohiro Kusumi 	return (error);
95e2950f41STomohiro Kusumi }
96e2950f41STomohiro Kusumi 
97e2950f41STomohiro Kusumi static int
98e2950f41STomohiro Kusumi autofs_access(struct vop_access_args *ap)
99e2950f41STomohiro Kusumi {
100e2950f41STomohiro Kusumi 	/*
101e2950f41STomohiro Kusumi 	 * Nothing to do here; the only kind of access control
102e2950f41STomohiro Kusumi 	 * needed is in autofs_mkdir().
103e2950f41STomohiro Kusumi 	 */
104e2950f41STomohiro Kusumi 	return (0);
105e2950f41STomohiro Kusumi }
106e2950f41STomohiro Kusumi 
107e2950f41STomohiro Kusumi static int
108e2950f41STomohiro Kusumi autofs_getattr(struct vop_getattr_args *ap)
109e2950f41STomohiro Kusumi {
110e2950f41STomohiro Kusumi 	struct vnode *vp = ap->a_vp;
111e2950f41STomohiro Kusumi 	struct vattr *vap = ap->a_vap;
112e2950f41STomohiro Kusumi 	struct autofs_node *anp = VTOI(vp);
113caaec4e3STomohiro Kusumi 	static bool warned = false;
114e2950f41STomohiro Kusumi 
115e2950f41STomohiro Kusumi 	KASSERT(vp->v_type == VDIR, ("!VDIR"));
116e2950f41STomohiro Kusumi 
117e2950f41STomohiro Kusumi 	/*
118e2950f41STomohiro Kusumi 	 * The reason we must do this is that some tree-walking software,
119e2950f41STomohiro Kusumi 	 * namely fts(3), assumes that stat(".") results will not change
120e2950f41STomohiro Kusumi 	 * between chdir("subdir") and chdir(".."), and fails with ENOENT
121e2950f41STomohiro Kusumi 	 * otherwise.
122caaec4e3STomohiro Kusumi 	 *
123e2950f41STomohiro Kusumi 	 * XXX: Not supported on DragonFly.
124caaec4e3STomohiro Kusumi 	 * With the current trigger mechanism on DragonFly, the process
125caaec4e3STomohiro Kusumi 	 * will hang while in nlookup() in nlookup_fs_root().
126e2950f41STomohiro Kusumi 	 */
127caaec4e3STomohiro Kusumi 	if (autofs_mount_on_stat) {
128caaec4e3STomohiro Kusumi 		if (!warned) {
129caaec4e3STomohiro Kusumi 			AUTOFS_WARN("vfs.autofs.mount_on_stat not supported");
130caaec4e3STomohiro Kusumi 			warned = true;
131caaec4e3STomohiro Kusumi 		}
132caaec4e3STomohiro Kusumi 	}
133e2950f41STomohiro Kusumi 
134e2950f41STomohiro Kusumi 	vap->va_type = VDIR;
135e2950f41STomohiro Kusumi 	vap->va_mode = 0755;
136e2950f41STomohiro Kusumi 	vap->va_nlink = 3; /* XXX: FreeBSD had it like this */
137e2950f41STomohiro Kusumi 	vap->va_uid = 0;
138e2950f41STomohiro Kusumi 	vap->va_gid = 0;
139e2950f41STomohiro Kusumi 	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
140e2950f41STomohiro Kusumi 	vap->va_fileid = anp->an_ino;
141e2950f41STomohiro Kusumi 	vap->va_size = S_BLKSIZE;
142e2950f41STomohiro Kusumi 	vap->va_blocksize = S_BLKSIZE;
143e2950f41STomohiro Kusumi 	vap->va_mtime = anp->an_ctime;
144e2950f41STomohiro Kusumi 	vap->va_atime = anp->an_ctime;
145e2950f41STomohiro Kusumi 	vap->va_ctime = anp->an_ctime;
146e2950f41STomohiro Kusumi 	vap->va_gen = 0;
147e2950f41STomohiro Kusumi 	vap->va_flags = 0;
148e2950f41STomohiro Kusumi 	vap->va_rmajor = 0;
149e2950f41STomohiro Kusumi 	vap->va_rminor = 0;
150e2950f41STomohiro Kusumi 	vap->va_bytes = S_BLKSIZE;
151e2950f41STomohiro Kusumi 	vap->va_filerev = 0;
152e2950f41STomohiro Kusumi 	vap->va_spare = 0;
153e2950f41STomohiro Kusumi 
154e2950f41STomohiro Kusumi 	return (0);
155e2950f41STomohiro Kusumi }
156e2950f41STomohiro Kusumi 
157e2950f41STomohiro Kusumi static int
158e2950f41STomohiro Kusumi autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen,
159e2950f41STomohiro Kusumi     struct vnode **newvp)
160e2950f41STomohiro Kusumi {
161e2950f41STomohiro Kusumi 	struct autofs_node *anp = VTOI(vp);
162e2950f41STomohiro Kusumi 	struct vnode *nvp = NULL;
163e2950f41STomohiro Kusumi 	int error;
164e2950f41STomohiro Kusumi 
165e2950f41STomohiro Kusumi 	KKASSERT(!vn_islocked(vp));
166e2950f41STomohiro Kusumi 
167e2950f41STomohiro Kusumi 	if (test_fs_root(vp) == 0)
168e2950f41STomohiro Kusumi 		goto mounted;
169e2950f41STomohiro Kusumi 
170e2950f41STomohiro Kusumi 	/*
171e2950f41STomohiro Kusumi 	 * Don't remove this.  Without having this extra nlookup,
172e2950f41STomohiro Kusumi 	 * automountd tries to mount the target filesystem twice
173e2950f41STomohiro Kusumi 	 * and the second attempt to mount returns an error.
174e2950f41STomohiro Kusumi 	 */
175e2950f41STomohiro Kusumi 	if (nlookup_fs_root(anp, &nvp) == 0)
176e2950f41STomohiro Kusumi 		goto mounted;
177e2950f41STomohiro Kusumi 
178e2950f41STomohiro Kusumi 	lockmgr(&autofs_softc->sc_lock, LK_EXCLUSIVE);
179e2950f41STomohiro Kusumi 	error = autofs_trigger(anp, path, pathlen);
180e2950f41STomohiro Kusumi 	lockmgr(&autofs_softc->sc_lock, LK_RELEASE);
181e2950f41STomohiro Kusumi 
182e2950f41STomohiro Kusumi 	if (error)
183e2950f41STomohiro Kusumi 		return (error);
184e2950f41STomohiro Kusumi 
185e2950f41STomohiro Kusumi 	if (nlookup_fs_root(anp, &nvp))
186e2950f41STomohiro Kusumi 		return (0);
187e2950f41STomohiro Kusumi 
188e2950f41STomohiro Kusumi 	/*
189e2950f41STomohiro Kusumi 	 * If the operation that succeeded was mount, then mark
190e2950f41STomohiro Kusumi 	 * the node as non-cached.  Otherwise, if someone unmounts
191e2950f41STomohiro Kusumi 	 * the filesystem before the cache times out, we will fail
192e2950f41STomohiro Kusumi 	 * to trigger.
193e2950f41STomohiro Kusumi 	 */
194e2950f41STomohiro Kusumi 	autofs_node_uncache(anp);
195e2950f41STomohiro Kusumi mounted:
196e2950f41STomohiro Kusumi 	*newvp = nvp;
197e2950f41STomohiro Kusumi 	KKASSERT(vn_islocked(*newvp));
198e2950f41STomohiro Kusumi 
199e2950f41STomohiro Kusumi 	return (0);
200e2950f41STomohiro Kusumi }
201e2950f41STomohiro Kusumi 
202e2950f41STomohiro Kusumi static int
203e2950f41STomohiro Kusumi autofs_nresolve(struct vop_nresolve_args *ap)
204e2950f41STomohiro Kusumi {
205e2950f41STomohiro Kusumi 	struct vnode *vp = NULL;
206e2950f41STomohiro Kusumi 	struct vnode *dvp = ap->a_dvp;
207e2950f41STomohiro Kusumi 	struct nchandle *nch = ap->a_nch;
208e2950f41STomohiro Kusumi 	struct namecache *ncp = nch->ncp;
209e2950f41STomohiro Kusumi 	struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount);
210e2950f41STomohiro Kusumi 	struct autofs_node *anp = VTOI(dvp);
211e2950f41STomohiro Kusumi 	struct autofs_node *child = NULL;
212e2950f41STomohiro Kusumi 	int error;
213e2950f41STomohiro Kusumi 
214e2950f41STomohiro Kusumi 	if (autofs_cached(anp, ncp->nc_name, ncp->nc_nlen) == false &&
215e2950f41STomohiro Kusumi 	    autofs_ignore_thread() == false) {
216e2950f41STomohiro Kusumi 		struct vnode *newvp = NULL;
217e2950f41STomohiro Kusumi 
218e2950f41STomohiro Kusumi 		cache_hold(nch);
219e2950f41STomohiro Kusumi 		cache_unlock(nch);
220e2950f41STomohiro Kusumi 		error = autofs_trigger_vn(dvp,
221e2950f41STomohiro Kusumi 		    ncp->nc_name, ncp->nc_nlen, &newvp);
222e2950f41STomohiro Kusumi 		cache_lock(nch);
223e2950f41STomohiro Kusumi 		cache_drop(nch);
224e2950f41STomohiro Kusumi 
225e2950f41STomohiro Kusumi 		if (error)
226e2950f41STomohiro Kusumi 			return (error);
227e2950f41STomohiro Kusumi 		if (newvp != NULL) {
228e2950f41STomohiro Kusumi 			KKASSERT(newvp->v_tag != VT_AUTOFS);
229e2950f41STomohiro Kusumi 			vput(newvp);
230e2950f41STomohiro Kusumi 			return (ESTALE);
231e2950f41STomohiro Kusumi 		}
232e2950f41STomohiro Kusumi 		return (0);
233e2950f41STomohiro Kusumi 	}
234e2950f41STomohiro Kusumi 
235*bc6139d4STomohiro Kusumi 	mtx_lock_sh_quick(&amp->am_lock);
236e2950f41STomohiro Kusumi 	error = autofs_node_find(anp, ncp->nc_name, ncp->nc_nlen, &child);
237*bc6139d4STomohiro Kusumi 	mtx_unlock_sh(&amp->am_lock);
238e2950f41STomohiro Kusumi 
239e2950f41STomohiro Kusumi 	if (error) {
240e2950f41STomohiro Kusumi 		cache_setvp(nch, NULL);
241e2950f41STomohiro Kusumi 		return (0);
242e2950f41STomohiro Kusumi 	}
243e2950f41STomohiro Kusumi 
244e2950f41STomohiro Kusumi 	error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
245e2950f41STomohiro Kusumi 	if (error == 0) {
246e2950f41STomohiro Kusumi 		KKASSERT(vn_islocked(vp));
247e2950f41STomohiro Kusumi 		vn_unlock(vp);
248e2950f41STomohiro Kusumi 		cache_setvp(nch, vp);
249e2950f41STomohiro Kusumi 		vrele(vp);
250e2950f41STomohiro Kusumi 		return (0);
251e2950f41STomohiro Kusumi 	}
252e2950f41STomohiro Kusumi 
253e2950f41STomohiro Kusumi 	return (error);
254e2950f41STomohiro Kusumi }
255e2950f41STomohiro Kusumi 
256e2950f41STomohiro Kusumi static int
257e2950f41STomohiro Kusumi autofs_nmkdir(struct vop_nmkdir_args *ap)
258e2950f41STomohiro Kusumi {
259e2950f41STomohiro Kusumi 	struct vnode *vp = NULL;
260e2950f41STomohiro Kusumi 	struct vnode *dvp = ap->a_dvp;
261e2950f41STomohiro Kusumi 	struct nchandle *nch = ap->a_nch;
262e2950f41STomohiro Kusumi 	struct namecache *ncp = nch->ncp;
263e2950f41STomohiro Kusumi 	struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount);
264e2950f41STomohiro Kusumi 	struct autofs_node *anp = VTOI(dvp);
265e2950f41STomohiro Kusumi 	struct autofs_node *child = NULL;
266e2950f41STomohiro Kusumi 	int error;
267e2950f41STomohiro Kusumi 
268e2950f41STomohiro Kusumi 	/*
269e2950f41STomohiro Kusumi 	 * Do not allow mkdir() if the calling thread is not
270e2950f41STomohiro Kusumi 	 * automountd(8) descendant.
271e2950f41STomohiro Kusumi 	 */
272e2950f41STomohiro Kusumi 	if (autofs_ignore_thread() == false)
273e2950f41STomohiro Kusumi 		return (EPERM);
274e2950f41STomohiro Kusumi 
275*bc6139d4STomohiro Kusumi 	mtx_lock_ex_quick(&amp->am_lock);
276e2950f41STomohiro Kusumi 	error = autofs_node_new(anp, amp, ncp->nc_name, ncp->nc_nlen, &child);
277*bc6139d4STomohiro Kusumi 	mtx_unlock_ex(&amp->am_lock);
278e2950f41STomohiro Kusumi 	KKASSERT(error == 0);
279e2950f41STomohiro Kusumi 
280e2950f41STomohiro Kusumi 	error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
281e2950f41STomohiro Kusumi 	if (error == 0) {
282e2950f41STomohiro Kusumi 		KKASSERT(vn_islocked(vp));
283e2950f41STomohiro Kusumi 		cache_setunresolved(nch);
284e2950f41STomohiro Kusumi 		cache_setvp(nch, vp);
285e2950f41STomohiro Kusumi 		*(ap->a_vpp) = vp;
286e2950f41STomohiro Kusumi 		return (0);
287e2950f41STomohiro Kusumi 	}
288e2950f41STomohiro Kusumi 
289e2950f41STomohiro Kusumi 	return (error);
290e2950f41STomohiro Kusumi }
291e2950f41STomohiro Kusumi 
292e2950f41STomohiro Kusumi static int
293e2950f41STomohiro Kusumi autofs_readdir_one(struct uio *uio, const char *name, ino_t ino,
294e2950f41STomohiro Kusumi     size_t *reclenp)
295e2950f41STomohiro Kusumi {
296e2950f41STomohiro Kusumi 	int error = 0;
297e2950f41STomohiro Kusumi 
298e2950f41STomohiro Kusumi 	if (reclenp != NULL)
299e2950f41STomohiro Kusumi 		*reclenp = autofs_dirent_reclen(name);
300e2950f41STomohiro Kusumi 
301e2950f41STomohiro Kusumi 	if (vop_write_dirent(&error, uio, ino, DT_DIR, strlen(name), name))
302e2950f41STomohiro Kusumi 		return (EINVAL);
303e2950f41STomohiro Kusumi 
304e2950f41STomohiro Kusumi 	return (error);
305e2950f41STomohiro Kusumi }
306e2950f41STomohiro Kusumi 
307e2950f41STomohiro Kusumi static int
308e2950f41STomohiro Kusumi autofs_readdir(struct vop_readdir_args *ap)
309e2950f41STomohiro Kusumi {
310e2950f41STomohiro Kusumi 	struct vnode *vp = ap->a_vp;
311e2950f41STomohiro Kusumi 	struct autofs_mount *amp = VFSTOAUTOFS(vp->v_mount);
312e2950f41STomohiro Kusumi 	struct autofs_node *anp = VTOI(vp);
313e2950f41STomohiro Kusumi 	struct autofs_node *child;
314e2950f41STomohiro Kusumi 	struct uio *uio = ap->a_uio;
315e2950f41STomohiro Kusumi 	ssize_t initial_resid = ap->a_uio->uio_resid;
316e2950f41STomohiro Kusumi 	size_t reclen, reclens;
317e2950f41STomohiro Kusumi 	int error;
318e2950f41STomohiro Kusumi 
319e2950f41STomohiro Kusumi 	KASSERT(vp->v_type == VDIR, ("!VDIR"));
320e2950f41STomohiro Kusumi 
321e2950f41STomohiro Kusumi 	if (autofs_cached(anp, NULL, 0) == false &&
322e2950f41STomohiro Kusumi 	    autofs_ignore_thread() == false) {
323e2950f41STomohiro Kusumi 		struct vnode *newvp = NULL;
324caaec4e3STomohiro Kusumi 		error = autofs_trigger_vn(vp, "", 0, &newvp);
325e2950f41STomohiro Kusumi 		if (error)
326e2950f41STomohiro Kusumi 			return (error);
327e2950f41STomohiro Kusumi 		if (newvp != NULL) {
328e2950f41STomohiro Kusumi 			KKASSERT(newvp->v_tag != VT_AUTOFS);
329e2950f41STomohiro Kusumi 			vn_unlock(newvp);
330e2950f41STomohiro Kusumi 			error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred,
331e2950f41STomohiro Kusumi 			    ap->a_eofflag, ap->a_ncookies, ap->a_cookies);
332e2950f41STomohiro Kusumi 			vrele(newvp);
333e2950f41STomohiro Kusumi 			return (error);
334e2950f41STomohiro Kusumi 		}
335e2950f41STomohiro Kusumi 		/* FALLTHROUGH */
336e2950f41STomohiro Kusumi 	}
337e2950f41STomohiro Kusumi 
338e2950f41STomohiro Kusumi 	if (uio->uio_offset < 0)
339e2950f41STomohiro Kusumi 		return (EINVAL);
340e2950f41STomohiro Kusumi 
341e2950f41STomohiro Kusumi 	if (ap->a_eofflag != NULL)
342e2950f41STomohiro Kusumi 		*ap->a_eofflag = FALSE;
343e2950f41STomohiro Kusumi 
344e2950f41STomohiro Kusumi 	/*
345e2950f41STomohiro Kusumi 	 * Write out the directory entry for ".".
346e2950f41STomohiro Kusumi 	 */
347e2950f41STomohiro Kusumi 	if (uio->uio_offset == 0) {
348e2950f41STomohiro Kusumi 		error = autofs_readdir_one(uio, ".", anp->an_ino, &reclen);
349e2950f41STomohiro Kusumi 		if (error)
350e2950f41STomohiro Kusumi 			goto out;
351e2950f41STomohiro Kusumi 	}
352e2950f41STomohiro Kusumi 	reclens = autofs_dirent_reclen(".");
353e2950f41STomohiro Kusumi 
354e2950f41STomohiro Kusumi 	/*
355e2950f41STomohiro Kusumi 	 * Write out the directory entry for "..".
356e2950f41STomohiro Kusumi 	 */
357e2950f41STomohiro Kusumi 	if (uio->uio_offset <= reclens) {
358e2950f41STomohiro Kusumi 		if (uio->uio_offset != reclens)
359e2950f41STomohiro Kusumi 			return (EINVAL);
360e2950f41STomohiro Kusumi 		error = autofs_readdir_one(uio, "..",
361e2950f41STomohiro Kusumi 		    (anp->an_parent ? anp->an_parent->an_ino : anp->an_ino),
362e2950f41STomohiro Kusumi 		    &reclen);
363e2950f41STomohiro Kusumi 		if (error)
364e2950f41STomohiro Kusumi 			goto out;
365e2950f41STomohiro Kusumi 	}
366e2950f41STomohiro Kusumi 	reclens += autofs_dirent_reclen("..");
367e2950f41STomohiro Kusumi 
368e2950f41STomohiro Kusumi 	/*
369e2950f41STomohiro Kusumi 	 * Write out the directory entries for subdirectories.
370e2950f41STomohiro Kusumi 	 */
371*bc6139d4STomohiro Kusumi 	mtx_lock_sh_quick(&amp->am_lock);
372e2950f41STomohiro Kusumi 	RB_FOREACH(child, autofs_node_tree, &anp->an_children) {
373e2950f41STomohiro Kusumi 		/*
374e2950f41STomohiro Kusumi 		 * Check the offset to skip entries returned by previous
375e2950f41STomohiro Kusumi 		 * calls to getdents().
376e2950f41STomohiro Kusumi 		 */
377e2950f41STomohiro Kusumi 		if (uio->uio_offset > reclens) {
378e2950f41STomohiro Kusumi 			reclens += autofs_dirent_reclen(child->an_name);
379e2950f41STomohiro Kusumi 			continue;
380e2950f41STomohiro Kusumi 		}
381e2950f41STomohiro Kusumi 
382e2950f41STomohiro Kusumi 		/*
383e2950f41STomohiro Kusumi 		 * Prevent seeking into the middle of dirent.
384e2950f41STomohiro Kusumi 		 */
385e2950f41STomohiro Kusumi 		if (uio->uio_offset != reclens) {
386*bc6139d4STomohiro Kusumi 			mtx_unlock_sh(&amp->am_lock);
387e2950f41STomohiro Kusumi 			return (EINVAL);
388e2950f41STomohiro Kusumi 		}
389e2950f41STomohiro Kusumi 
390e2950f41STomohiro Kusumi 		error = autofs_readdir_one(uio, child->an_name,
391e2950f41STomohiro Kusumi 		    child->an_ino, &reclen);
392e2950f41STomohiro Kusumi 		reclens += reclen;
393e2950f41STomohiro Kusumi 		if (error) {
394*bc6139d4STomohiro Kusumi 			mtx_unlock_sh(&amp->am_lock);
395e2950f41STomohiro Kusumi 			goto out;
396e2950f41STomohiro Kusumi 		}
397e2950f41STomohiro Kusumi 	}
398*bc6139d4STomohiro Kusumi 	mtx_unlock_sh(&amp->am_lock);
399e2950f41STomohiro Kusumi 
400e2950f41STomohiro Kusumi 	if (ap->a_eofflag != NULL)
401e2950f41STomohiro Kusumi 		*ap->a_eofflag = TRUE;
402e2950f41STomohiro Kusumi 
403e2950f41STomohiro Kusumi 	return (0);
404e2950f41STomohiro Kusumi out:
405e2950f41STomohiro Kusumi 	/*
406e2950f41STomohiro Kusumi 	 * Return error if the initial buffer was too small to do anything.
407e2950f41STomohiro Kusumi 	 */
408e2950f41STomohiro Kusumi 	if (uio->uio_resid == initial_resid)
409e2950f41STomohiro Kusumi 		return (error);
410e2950f41STomohiro Kusumi 
411e2950f41STomohiro Kusumi 	/*
412e2950f41STomohiro Kusumi 	 * Don't return an error if we managed to copy out some entries.
413e2950f41STomohiro Kusumi 	 */
414e2950f41STomohiro Kusumi 	if (uio->uio_resid < reclen)
415e2950f41STomohiro Kusumi 		return (0);
416e2950f41STomohiro Kusumi 
417e2950f41STomohiro Kusumi 	return (error);
418e2950f41STomohiro Kusumi }
419e2950f41STomohiro Kusumi 
420e2950f41STomohiro Kusumi static int
421e2950f41STomohiro Kusumi autofs_reclaim(struct vop_reclaim_args *ap)
422e2950f41STomohiro Kusumi {
423e2950f41STomohiro Kusumi 	struct vnode *vp = ap->a_vp;
424e2950f41STomohiro Kusumi 	struct autofs_node *anp = VTOI(vp);
425e2950f41STomohiro Kusumi 
426e2950f41STomohiro Kusumi 	/*
427e2950f41STomohiro Kusumi 	 * We do not free autofs_node here; instead we are
428e2950f41STomohiro Kusumi 	 * destroying them in autofs_node_delete().
429e2950f41STomohiro Kusumi 	 */
430bfccbb76STomohiro Kusumi 	mtx_lock_ex_quick(&anp->an_vnode_lock);
431e2950f41STomohiro Kusumi 	anp->an_vnode = NULL;
432e2950f41STomohiro Kusumi 	vp->v_data = NULL;
433bfccbb76STomohiro Kusumi 	mtx_unlock_ex(&anp->an_vnode_lock);
434e2950f41STomohiro Kusumi 
435e2950f41STomohiro Kusumi 	return (0);
436e2950f41STomohiro Kusumi }
437e2950f41STomohiro Kusumi 
438e2950f41STomohiro Kusumi static int
439e2950f41STomohiro Kusumi autofs_mountctl(struct vop_mountctl_args *ap)
440e2950f41STomohiro Kusumi {
441e2950f41STomohiro Kusumi 	struct mount *mp;
442e2950f41STomohiro Kusumi 	int res;
443e2950f41STomohiro Kusumi 
444e2950f41STomohiro Kusumi 	mp = ap->a_head.a_ops->head.vv_mount;
445e2950f41STomohiro Kusumi 	lwkt_gettoken(&mp->mnt_token);
446e2950f41STomohiro Kusumi 
447e2950f41STomohiro Kusumi 	switch (ap->a_op) {
4485e4f9d10STomohiro Kusumi 	//case ...:
4495e4f9d10STomohiro Kusumi 	//	break;
450e2950f41STomohiro Kusumi 	default:
451e2950f41STomohiro Kusumi 		res = vop_stdmountctl(ap);
452e2950f41STomohiro Kusumi 		break;
453e2950f41STomohiro Kusumi 	}
454e2950f41STomohiro Kusumi 
455e2950f41STomohiro Kusumi 	lwkt_reltoken(&mp->mnt_token);
456e2950f41STomohiro Kusumi 	return (res);
457e2950f41STomohiro Kusumi }
458e2950f41STomohiro Kusumi 
459e2950f41STomohiro Kusumi static int
460e2950f41STomohiro Kusumi autofs_print(struct vop_print_args *ap)
461e2950f41STomohiro Kusumi {
462e2950f41STomohiro Kusumi 	struct autofs_node *anp = VTOI(ap->a_vp);
463e2950f41STomohiro Kusumi 
4647d470fedSTomohiro Kusumi 	kprintf("tag VT_AUTOFS, node %p, ino %jd, name %s, cached %d, retries %d, wildcards %d\n",
465b112b669STomohiro Kusumi 	    anp, (intmax_t)anp->an_ino, anp->an_name, anp->an_cached,
466b112b669STomohiro Kusumi 	    anp->an_retries, anp->an_wildcards);
467e2950f41STomohiro Kusumi 
468e2950f41STomohiro Kusumi 	return (0);
469e2950f41STomohiro Kusumi }
470e2950f41STomohiro Kusumi 
471e2950f41STomohiro Kusumi struct vop_ops autofs_vnode_vops = {
472e2950f41STomohiro Kusumi 	.vop_default =		vop_defaultop,
473e2950f41STomohiro Kusumi 	.vop_getpages =		vop_stdgetpages,
474e2950f41STomohiro Kusumi 	.vop_putpages =		vop_stdputpages,
475e2950f41STomohiro Kusumi 	.vop_access =		autofs_access,
476e2950f41STomohiro Kusumi 	.vop_getattr =		autofs_getattr,
477e2950f41STomohiro Kusumi 	.vop_nresolve =		autofs_nresolve,
478e2950f41STomohiro Kusumi 	.vop_nmkdir =		autofs_nmkdir,
479e2950f41STomohiro Kusumi 	.vop_readdir =		autofs_readdir,
480e2950f41STomohiro Kusumi 	.vop_reclaim =		autofs_reclaim,
481e2950f41STomohiro Kusumi 	.vop_mountctl =		autofs_mountctl,
482e2950f41STomohiro Kusumi 	.vop_print =		autofs_print,
483e2950f41STomohiro Kusumi };
484e2950f41STomohiro Kusumi 
485e2950f41STomohiro Kusumi int
486e2950f41STomohiro Kusumi autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp,
487e2950f41STomohiro Kusumi     const char *name, int namelen, struct autofs_node **anpp)
488e2950f41STomohiro Kusumi {
489e2950f41STomohiro Kusumi 	struct autofs_node *anp;
490e2950f41STomohiro Kusumi 
491e2950f41STomohiro Kusumi 	AUTOFS_ASSERT_XLOCKED(amp);
492e2950f41STomohiro Kusumi 
493e2950f41STomohiro Kusumi 	if (parent != NULL) {
494e2950f41STomohiro Kusumi 		AUTOFS_ASSERT_XLOCKED(parent->an_mount);
495e2950f41STomohiro Kusumi 		KASSERT(autofs_node_find(parent, name, namelen, NULL) == ENOENT,
496e2950f41STomohiro Kusumi 		    ("node \"%s\" already exists", name));
497e2950f41STomohiro Kusumi 	}
498e2950f41STomohiro Kusumi 
499e2950f41STomohiro Kusumi 	anp = objcache_get(autofs_node_objcache, M_WAITOK);
500e2950f41STomohiro Kusumi 	if (namelen >= 0)
501e2950f41STomohiro Kusumi 		anp->an_name = kstrndup(name, namelen, M_AUTOFS);
502e2950f41STomohiro Kusumi 	else
503e2950f41STomohiro Kusumi 		anp->an_name = kstrdup(name, M_AUTOFS);
504e2950f41STomohiro Kusumi 	anp->an_ino = amp->am_last_ino++;
505e2950f41STomohiro Kusumi 	callout_init(&anp->an_callout);
506bfccbb76STomohiro Kusumi 	mtx_init(&anp->an_vnode_lock, "autofsvnlk");
507e2950f41STomohiro Kusumi 	getnanotime(&anp->an_ctime);
508e2950f41STomohiro Kusumi 	anp->an_parent = parent;
509e2950f41STomohiro Kusumi 	anp->an_mount = amp;
510e2950f41STomohiro Kusumi 	anp->an_vnode = NULL;
511e2950f41STomohiro Kusumi 	anp->an_cached = false;
512e2950f41STomohiro Kusumi 	anp->an_wildcards = false;
513e2950f41STomohiro Kusumi 	anp->an_retries = 0;
514e2950f41STomohiro Kusumi 	if (parent != NULL)
515e2950f41STomohiro Kusumi 		RB_INSERT(autofs_node_tree, &parent->an_children, anp);
516e2950f41STomohiro Kusumi 	RB_INIT(&anp->an_children);
517e2950f41STomohiro Kusumi 
518e2950f41STomohiro Kusumi 	*anpp = anp;
519e2950f41STomohiro Kusumi 
520e2950f41STomohiro Kusumi 	return (0);
521e2950f41STomohiro Kusumi }
522e2950f41STomohiro Kusumi 
523e2950f41STomohiro Kusumi int
524e2950f41STomohiro Kusumi autofs_node_find(struct autofs_node *parent, const char *name,
525e2950f41STomohiro Kusumi     int namelen, struct autofs_node **anpp)
526e2950f41STomohiro Kusumi {
527e2950f41STomohiro Kusumi 	struct autofs_node *anp, find;
528e2950f41STomohiro Kusumi 	int error;
529e2950f41STomohiro Kusumi 
530e2950f41STomohiro Kusumi 	AUTOFS_ASSERT_LOCKED(parent->an_mount);
531e2950f41STomohiro Kusumi 
532e2950f41STomohiro Kusumi 	if (namelen >= 0)
533e2950f41STomohiro Kusumi 		find.an_name = kstrndup(name, namelen, M_AUTOFS);
534e2950f41STomohiro Kusumi 	else
535e2950f41STomohiro Kusumi 		find.an_name = kstrdup(name, M_AUTOFS);
536e2950f41STomohiro Kusumi 
537e2950f41STomohiro Kusumi 	anp = RB_FIND(autofs_node_tree, &parent->an_children, &find);
538e2950f41STomohiro Kusumi 	if (anp != NULL) {
539e2950f41STomohiro Kusumi 		error = 0;
540e2950f41STomohiro Kusumi 		if (anpp != NULL)
541e2950f41STomohiro Kusumi 			*anpp = anp;
542e2950f41STomohiro Kusumi 	} else {
543e2950f41STomohiro Kusumi 		error = ENOENT;
544e2950f41STomohiro Kusumi 	}
545e2950f41STomohiro Kusumi 
546e2950f41STomohiro Kusumi 	kfree(find.an_name, M_AUTOFS);
547e2950f41STomohiro Kusumi 
548e2950f41STomohiro Kusumi 	return (error);
549e2950f41STomohiro Kusumi }
550e2950f41STomohiro Kusumi 
551e2950f41STomohiro Kusumi void
552e2950f41STomohiro Kusumi autofs_node_delete(struct autofs_node *anp)
553e2950f41STomohiro Kusumi {
554e2950f41STomohiro Kusumi 	AUTOFS_ASSERT_XLOCKED(anp->an_mount);
555e2950f41STomohiro Kusumi 	KASSERT(RB_EMPTY(&anp->an_children), ("have children"));
556e2950f41STomohiro Kusumi 
557e2950f41STomohiro Kusumi 	callout_drain(&anp->an_callout);
558e2950f41STomohiro Kusumi 
559e2950f41STomohiro Kusumi 	if (anp->an_parent != NULL)
560e2950f41STomohiro Kusumi 		RB_REMOVE(autofs_node_tree, &anp->an_parent->an_children, anp);
561e2950f41STomohiro Kusumi 
562bfccbb76STomohiro Kusumi 	mtx_uninit(&anp->an_vnode_lock);
563e2950f41STomohiro Kusumi 	kfree(anp->an_name, M_AUTOFS);
564e2950f41STomohiro Kusumi 	objcache_put(autofs_node_objcache, anp);
565e2950f41STomohiro Kusumi }
566e2950f41STomohiro Kusumi 
567e2950f41STomohiro Kusumi int
568e2950f41STomohiro Kusumi autofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags,
569e2950f41STomohiro Kusumi     struct vnode **vpp)
570e2950f41STomohiro Kusumi {
571e2950f41STomohiro Kusumi 	struct vnode *vp = NULL;
572e2950f41STomohiro Kusumi 	int error;
573e2950f41STomohiro Kusumi retry:
574e2950f41STomohiro Kusumi 	AUTOFS_ASSERT_UNLOCKED(anp->an_mount);
575bfccbb76STomohiro Kusumi 	mtx_lock_ex_quick(&anp->an_vnode_lock);
576e2950f41STomohiro Kusumi 
577e2950f41STomohiro Kusumi 	vp = anp->an_vnode;
578e2950f41STomohiro Kusumi 	if (vp != NULL) {
579e2950f41STomohiro Kusumi 		vhold(vp);
580bfccbb76STomohiro Kusumi 		mtx_unlock_ex(&anp->an_vnode_lock);
581e2950f41STomohiro Kusumi 
582e2950f41STomohiro Kusumi 		error = vget(vp, flags | LK_RETRY);
583e2950f41STomohiro Kusumi 		if (error) {
584e2950f41STomohiro Kusumi 			AUTOFS_WARN("vget failed with error %d", error);
585e2950f41STomohiro Kusumi 			vdrop(vp);
586e2950f41STomohiro Kusumi 			goto retry;
587e2950f41STomohiro Kusumi 		}
588e2950f41STomohiro Kusumi 		vdrop(vp);
589e2950f41STomohiro Kusumi 		*vpp = vp;
590e2950f41STomohiro Kusumi 		return (0);
591e2950f41STomohiro Kusumi 	}
592e2950f41STomohiro Kusumi 
593bfccbb76STomohiro Kusumi 	mtx_unlock_ex(&anp->an_vnode_lock);
594e2950f41STomohiro Kusumi 
595e2950f41STomohiro Kusumi 	error = getnewvnode(VT_AUTOFS, mp, &vp, VLKTIMEOUT, LK_CANRECURSE);
596e2950f41STomohiro Kusumi 	if (error)
597e2950f41STomohiro Kusumi 		return (error);
598e2950f41STomohiro Kusumi 	vp->v_type = VDIR;
599e2950f41STomohiro Kusumi 	vp->v_data = anp;
600e2950f41STomohiro Kusumi 
601e2950f41STomohiro Kusumi 	KASSERT(anp->an_vnode == NULL, ("lost race"));
602e2950f41STomohiro Kusumi 	anp->an_vnode = vp;
603e2950f41STomohiro Kusumi 	*vpp = vp;
604e2950f41STomohiro Kusumi 
605e2950f41STomohiro Kusumi 	return (0);
606e2950f41STomohiro Kusumi }
607