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