2 * Copyright (c) 2016 Tomohiro Kusumi <kusumi.tomohiro@gmail.com>
3 * Copyright (c) 2016 The DragonFly Project
4 * Copyright (c) 2014 The FreeBSD Foundation
7 * This software was developed by Edward Tomasz Napierala under sponsorship
8 * from the FreeBSD Foundation.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #include <sys/kernel.h>
34 #include <sys/module.h>
36 #include <sys/dirent.h>
37 #include <sys/namei.h>
38 #include <sys/nlookup.h>
39 #include <sys/mountctl.h>
44 test_fs_root(struct vnode *vp)
48 if ((error = vget(vp, LK_SHARED)) != 0) {
49 AUTOFS_WARN("vget failed with error %d", error);
53 if (((vp->v_flag & VROOT) == 0) || (vp->v_tag == VT_AUTOFS)) {
62 nlookup_fs_root(struct autofs_node *anp, struct vnode **vpp)
64 struct nlookupdata nd;
68 path = autofs_path(anp);
70 error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
74 struct vnode *vp = nd.nl_nch.ncp->nc_vp;
75 if (test_fs_root(vp) == true)
82 kfree(path, M_AUTOFS);
84 return (error ? false : true);
88 autofs_access(struct vop_access_args *ap)
91 * Nothing to do here; the only kind of access control
92 * needed is in autofs_mkdir().
98 autofs_getattr(struct vop_getattr_args *ap)
100 struct vnode *vp = ap->a_vp;
101 struct vattr *vap = ap->a_vap;
102 struct autofs_node *anp = VTOI(vp);
104 KASSERT(vp->v_type == VDIR, ("!VDIR"));
107 * The reason we must do this is that some tree-walking software,
108 * namely fts(3), assumes that stat(".") results will not change
109 * between chdir("subdir") and chdir(".."), and fails with ENOENT
112 * XXX: Not supported on DragonFly.
113 * With the current trigger mechanism on DragonFly, the process
114 * will hang while in nlookup() in nlookup_fs_root().
117 if (autofs_mount_on_stat && ...) {
121 vap->va_nlink = 3; /* XXX: FreeBSD had it like this */
125 vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
126 vap->va_fileid = anp->an_ino;
127 vap->va_size = S_BLKSIZE;
128 vap->va_blocksize = S_BLKSIZE;
129 vap->va_atime = anp->an_ctime;
130 vap->va_mtime = anp->an_ctime;
131 vap->va_ctime = anp->an_ctime;
136 vap->va_bytes = S_BLKSIZE;
145 autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen,
146 struct vnode **newvp)
148 struct autofs_node *anp = VTOI(vp);
149 struct vnode *nvp = NULL;
152 KKASSERT(!vn_islocked(vp));
154 if (test_fs_root(vp) == true)
158 * Don't remove this lookup. Without this, the process
159 * may trigger automountd(8) to mount the filesystem for the
160 * second time after successful mount, and the second attempt
163 if (nlookup_fs_root(anp, &nvp) == true)
166 mtx_lock_ex_quick(&autofs_softc->sc_lock);
167 error = autofs_trigger(anp, path, pathlen);
168 mtx_unlock_ex(&autofs_softc->sc_lock);
173 if (nlookup_fs_root(anp, &nvp) == false)
177 * If the operation that succeeded was mount, then mark
178 * the node as non-cached. Otherwise, if someone unmounts
179 * the filesystem before the cache times out, we will fail
182 autofs_node_uncache(anp);
185 KKASSERT(vn_islocked(*newvp));
191 autofs_nresolve(struct vop_nresolve_args *ap)
193 struct vnode *vp = NULL;
194 struct vnode *dvp = ap->a_dvp;
195 struct nchandle *nch = ap->a_nch;
196 struct namecache *ncp = nch->ncp;
197 struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount);
198 struct autofs_node *anp = VTOI(dvp);
199 struct autofs_node *child = NULL;
202 if (autofs_cached(anp, ncp->nc_name, ncp->nc_nlen) == false &&
203 autofs_ignore_thread() == false) {
204 struct vnode *newvp = NULL;
208 error = autofs_trigger_vn(dvp, ncp->nc_name, ncp->nc_nlen,
215 KKASSERT(newvp->v_tag != VT_AUTOFS);
222 mtx_lock_sh_quick(&->am_lock);
223 error = autofs_node_find(anp, ncp->nc_name, ncp->nc_nlen, &child);
224 mtx_unlock_sh(&->am_lock);
227 cache_setvp(nch, NULL);
231 error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
233 KKASSERT(vn_islocked(vp));
235 cache_setvp(nch, vp);
244 autofs_nmkdir(struct vop_nmkdir_args *ap)
246 struct vnode *vp = NULL;
247 struct vnode *dvp = ap->a_dvp;
248 struct nchandle *nch = ap->a_nch;
249 struct namecache *ncp = nch->ncp;
250 struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount);
251 struct autofs_node *anp = VTOI(dvp);
252 struct autofs_node *child = NULL;
256 * Do not allow mkdir() if the calling thread is not
257 * automountd(8) descendant.
259 if (autofs_ignore_thread() == false)
262 mtx_lock_ex_quick(&->am_lock);
263 error = autofs_node_new(anp, amp, ncp->nc_name, ncp->nc_nlen, &child);
264 mtx_unlock_ex(&->am_lock);
265 KKASSERT(error == 0);
267 error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
269 KKASSERT(vn_islocked(vp));
270 cache_setunresolved(nch);
271 cache_setvp(nch, vp);
279 static __inline size_t
280 autofs_dirent_reclen(const char *name)
282 return (_DIRENT_RECLEN(strlen(name)));
286 autofs_write_dirent(struct uio *uio, const char *name, ino_t ino)
290 if (vop_write_dirent(&error, uio, ino, DT_DIR, strlen(name), name))
297 autofs_readdir(struct vop_readdir_args *ap)
299 struct vnode *vp = ap->a_vp;
300 struct autofs_mount *amp = VFSTOAUTOFS(vp->v_mount);
301 struct autofs_node *anp = VTOI(vp);
302 struct autofs_node *child;
303 struct uio *uio = ap->a_uio;
304 ssize_t initial_resid = ap->a_uio->uio_resid;
308 KASSERT(vp->v_type == VDIR, ("!VDIR"));
310 if (autofs_cached(anp, NULL, 0) == false &&
311 autofs_ignore_thread() == false) {
312 struct vnode *newvp = NULL;
313 error = autofs_trigger_vn(vp, "", 0, &newvp);
317 KKASSERT(newvp->v_tag != VT_AUTOFS);
319 error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred,
320 ap->a_eofflag, ap->a_ncookies, ap->a_cookies);
326 if (uio->uio_offset < 0)
329 if (ap->a_eofflag != NULL)
330 *ap->a_eofflag = FALSE;
333 * Write out the directory entry for ".".
335 if (uio->uio_offset == 0) {
336 error = autofs_write_dirent(uio, ".", anp->an_ino);
340 reclens = autofs_dirent_reclen(".");
343 * Write out the directory entry for "..".
345 if (uio->uio_offset <= reclens) {
346 if (uio->uio_offset != reclens)
348 error = autofs_write_dirent(uio, "..",
349 (anp->an_parent ? anp->an_parent->an_ino : anp->an_ino));
353 reclens += autofs_dirent_reclen("..");
356 * Write out the directory entries for subdirectories.
358 mtx_lock_sh_quick(&->am_lock);
359 RB_FOREACH(child, autofs_node_tree, &anp->an_children) {
361 * Check the offset to skip entries returned by previous
362 * calls to getdents().
364 if (uio->uio_offset > reclens) {
365 reclens += autofs_dirent_reclen(child->an_name);
370 * Prevent seeking into the middle of dirent.
372 if (uio->uio_offset != reclens) {
373 mtx_unlock_sh(&->am_lock);
377 error = autofs_write_dirent(uio, child->an_name, child->an_ino);
378 reclens += autofs_dirent_reclen(child->an_name);
380 mtx_unlock_sh(&->am_lock);
384 mtx_unlock_sh(&->am_lock);
386 if (ap->a_eofflag != NULL)
387 *ap->a_eofflag = TRUE;
392 * Return error if the initial buffer was too small to do anything.
394 if (uio->uio_resid == initial_resid)
398 * Don't return an error if we managed to copy out some entries.
400 if (uio->uio_resid < initial_resid)
407 autofs_reclaim(struct vop_reclaim_args *ap)
409 struct vnode *vp = ap->a_vp;
410 struct autofs_node *anp = VTOI(vp);
413 * We do not free autofs_node here; instead we are
414 * destroying them in autofs_node_delete().
416 mtx_lock_ex_quick(&anp->an_vnode_lock);
417 anp->an_vnode = NULL;
419 mtx_unlock_ex(&anp->an_vnode_lock);
425 autofs_mountctl(struct vop_mountctl_args *ap)
430 mp = ap->a_head.a_ops->head.vv_mount;
431 lwkt_gettoken(&mp->mnt_token);
437 res = vop_stdmountctl(ap);
441 lwkt_reltoken(&mp->mnt_token);
446 autofs_print(struct vop_print_args *ap)
448 struct autofs_node *anp = VTOI(ap->a_vp);
450 kprintf("tag VT_AUTOFS, node %p, ino %jd, name %s, cached %d, retries %d, wildcards %d\n",
451 anp, (intmax_t)anp->an_ino, anp->an_name, anp->an_cached,
452 anp->an_retries, anp->an_wildcards);
457 struct vop_ops autofs_vnode_vops = {
458 .vop_default = vop_defaultop,
459 .vop_getpages = vop_stdgetpages,
460 .vop_putpages = vop_stdputpages,
461 .vop_access = autofs_access,
462 .vop_getattr = autofs_getattr,
463 .vop_nresolve = autofs_nresolve,
464 .vop_nmkdir = autofs_nmkdir,
465 .vop_readdir = autofs_readdir,
466 .vop_reclaim = autofs_reclaim,
467 .vop_mountctl = autofs_mountctl,
468 .vop_print = autofs_print,
472 autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp,
473 const char *name, int namelen, struct autofs_node **anpp)
475 struct autofs_node *anp;
477 KKASSERT(mtx_islocked_ex(&->am_lock));
479 if (parent != NULL) {
480 KKASSERT(mtx_islocked_ex(&parent->an_mount->am_lock));
481 KASSERT(autofs_node_find(parent, name, namelen, NULL) == ENOENT,
482 ("node \"%s\" already exists", name));
486 * All struct fields must be initialized.
488 anp = objcache_get(autofs_node_objcache, M_WAITOK);
490 anp->an_name = kstrndup(name, namelen, M_AUTOFS);
492 anp->an_name = kstrdup(name, M_AUTOFS);
493 anp->an_ino = amp->am_last_ino++;
494 callout_init(&anp->an_callout);
495 mtx_init(&anp->an_vnode_lock, "autofsvnlk");
496 getnanotime(&anp->an_ctime);
497 anp->an_parent = parent;
499 anp->an_vnode = NULL;
500 anp->an_cached = false;
501 anp->an_wildcards = false;
504 RB_INSERT(autofs_node_tree, &parent->an_children, anp);
505 RB_INIT(&anp->an_children);
513 autofs_node_find(struct autofs_node *parent, const char *name, int namelen,
514 struct autofs_node **anpp)
516 struct autofs_node *anp, find;
519 KKASSERT(mtx_islocked(&parent->an_mount->am_lock));
522 find.an_name = kstrndup(name, namelen, M_AUTOFS);
524 find.an_name = kstrdup(name, M_AUTOFS);
526 anp = RB_FIND(autofs_node_tree, &parent->an_children, &find);
535 kfree(find.an_name, M_AUTOFS);
541 autofs_node_delete(struct autofs_node *anp)
543 KKASSERT(mtx_islocked_ex(&anp->an_mount->am_lock));
544 KASSERT(RB_EMPTY(&anp->an_children), ("have children"));
546 callout_drain(&anp->an_callout);
548 if (anp->an_parent != NULL)
549 RB_REMOVE(autofs_node_tree, &anp->an_parent->an_children, anp);
551 mtx_uninit(&anp->an_vnode_lock);
552 kfree(anp->an_name, M_AUTOFS);
553 objcache_put(autofs_node_objcache, anp);
557 autofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags,
560 struct vnode *vp = NULL;
563 KKASSERT(mtx_notlocked(&anp->an_mount->am_lock));
564 mtx_lock_ex_quick(&anp->an_vnode_lock);
569 mtx_unlock_ex(&anp->an_vnode_lock);
571 error = vget(vp, flags | LK_RETRY);
573 AUTOFS_WARN("vget failed with error %d", error);
582 mtx_unlock_ex(&anp->an_vnode_lock);
584 error = getnewvnode(VT_AUTOFS, mp, &vp, VLKTIMEOUT, LK_CANRECURSE);
590 KASSERT(anp->an_vnode == NULL, ("lost race"));