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(&->am_lock); 236e2950f41STomohiro Kusumi error = autofs_node_find(anp, ncp->nc_name, ncp->nc_nlen, &child); 237*bc6139d4STomohiro Kusumi mtx_unlock_sh(&->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(&->am_lock); 276e2950f41STomohiro Kusumi error = autofs_node_new(anp, amp, ncp->nc_name, ncp->nc_nlen, &child); 277*bc6139d4STomohiro Kusumi mtx_unlock_ex(&->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(&->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(&->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(&->am_lock); 395e2950f41STomohiro Kusumi goto out; 396e2950f41STomohiro Kusumi } 397e2950f41STomohiro Kusumi } 398*bc6139d4STomohiro Kusumi mtx_unlock_sh(&->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