1270096Strasz/*- 2270096Strasz * Copyright (c) 2014 The FreeBSD Foundation 3270096Strasz * All rights reserved. 4270096Strasz * 5270096Strasz * This software was developed by Edward Tomasz Napierala under sponsorship 6270096Strasz * from the FreeBSD Foundation. 7270096Strasz * 8270096Strasz * Redistribution and use in source and binary forms, with or without 9270096Strasz * modification, are permitted provided that the following conditions 10270096Strasz * are met: 11270096Strasz * 1. Redistributions of source code must retain the above copyright 12270096Strasz * notice, this list of conditions and the following disclaimer. 13270096Strasz * 2. Redistributions in binary form must reproduce the above copyright 14270096Strasz * notice, this list of conditions and the following disclaimer in the 15270096Strasz * documentation and/or other materials provided with the distribution. 16270096Strasz * 17270096Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18270096Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19270096Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20270096Strasz * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21270096Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22270096Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23270096Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24270096Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25270096Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26270096Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27270096Strasz * SUCH DAMAGE. 28270096Strasz * 29270096Strasz */ 30270096Strasz 31270096Strasz#include <sys/cdefs.h> 32270096Strasz__FBSDID("$FreeBSD$"); 33270096Strasz 34270096Strasz#include <sys/param.h> 35270096Strasz#include <sys/kernel.h> 36270096Strasz#include <sys/condvar.h> 37270096Strasz#include <sys/dirent.h> 38270096Strasz#include <sys/fcntl.h> 39270096Strasz#include <sys/lock.h> 40270096Strasz#include <sys/mount.h> 41270096Strasz#include <sys/mutex.h> 42270096Strasz#include <sys/namei.h> 43270096Strasz#include <sys/signalvar.h> 44270096Strasz#include <sys/systm.h> 45270096Strasz#include <sys/vnode.h> 46270096Strasz#include <machine/atomic.h> 47270096Strasz#include <vm/uma.h> 48270096Strasz 49270898Strasz#include <fs/autofs/autofs.h> 50270096Strasz 51270096Straszstatic int autofs_trigger_vn(struct vnode *vp, const char *path, 52270096Strasz int pathlen, struct vnode **newvp); 53270096Strasz 54270900Straszextern struct autofs_softc *autofs_softc; 55270900Strasz 56270096Straszstatic int 57270096Straszautofs_access(struct vop_access_args *ap) 58270096Strasz{ 59270096Strasz 60270096Strasz /* 61270096Strasz * Nothing to do here; the only kind of access control 62270096Strasz * needed is in autofs_mkdir(). 63270096Strasz */ 64270096Strasz 65270096Strasz return (0); 66270096Strasz} 67270096Strasz 68270096Straszstatic int 69270096Straszautofs_getattr(struct vop_getattr_args *ap) 70270096Strasz{ 71270096Strasz struct vnode *vp, *newvp; 72270096Strasz struct autofs_node *anp; 73270096Strasz struct mount *mp; 74270096Strasz struct vattr *vap; 75270096Strasz int error; 76270096Strasz 77270096Strasz vp = ap->a_vp; 78270096Strasz anp = vp->v_data; 79270096Strasz mp = vp->v_mount; 80270096Strasz vap = ap->a_vap; 81270096Strasz 82270096Strasz KASSERT(ap->a_vp->v_type == VDIR, ("!VDIR")); 83270096Strasz 84270096Strasz /* 85270096Strasz * The reason we must do this is that some tree-walking software, 86270096Strasz * namely fts(3), assumes that stat(".") results will not change 87270096Strasz * between chdir("subdir") and chdir(".."), and fails with ENOENT 88270096Strasz * otherwise. 89270096Strasz */ 90270096Strasz if (autofs_mount_on_stat && autofs_cached(anp, NULL, 0) == false && 91270096Strasz autofs_ignore_thread(curthread) == false) { 92270096Strasz error = autofs_trigger_vn(vp, "", 0, &newvp); 93270096Strasz if (error != 0) 94270096Strasz return (error); 95270096Strasz 96270096Strasz if (newvp != NULL) { 97270096Strasz error = VOP_GETATTR(newvp, ap->a_vap, 98270096Strasz ap->a_cred); 99270096Strasz vput(newvp); 100270096Strasz return (error); 101270096Strasz } 102270096Strasz } 103270096Strasz 104270096Strasz vap->va_type = VDIR; 105270096Strasz vap->va_mode = 0755; 106270096Strasz vap->va_nlink = 3; /* XXX */ 107270096Strasz vap->va_uid = 0; 108270096Strasz vap->va_gid = 0; 109270096Strasz vap->va_rdev = NODEV; 110270096Strasz vap->va_fsid = mp->mnt_stat.f_fsid.val[0]; 111270096Strasz vap->va_fileid = anp->an_fileno; 112270096Strasz vap->va_size = 512; /* XXX */ 113270096Strasz vap->va_blocksize = 512; 114270096Strasz vap->va_mtime = anp->an_ctime; 115270096Strasz vap->va_atime = anp->an_ctime; 116270096Strasz vap->va_ctime = anp->an_ctime; 117270096Strasz vap->va_birthtime = anp->an_ctime; 118270096Strasz vap->va_gen = 0; 119270096Strasz vap->va_flags = 0; 120270096Strasz vap->va_rdev = 0; 121270096Strasz vap->va_bytes = 512; /* XXX */ 122270096Strasz vap->va_filerev = 0; 123270096Strasz vap->va_spare = 0; 124270096Strasz 125270096Strasz return (0); 126270096Strasz} 127270096Strasz 128270096Strasz/* 129270096Strasz * Unlock the vnode, request automountd(8) action, and then lock it back. 130270096Strasz * If anything got mounted on top of the vnode, return the new filesystem's 131270096Strasz * root vnode in 'newvp', locked. 132270096Strasz */ 133270096Straszstatic int 134270096Straszautofs_trigger_vn(struct vnode *vp, const char *path, int pathlen, 135270096Strasz struct vnode **newvp) 136270096Strasz{ 137270096Strasz struct autofs_node *anp; 138270096Strasz struct autofs_mount *amp; 139270096Strasz int error, lock_flags; 140270096Strasz 141270096Strasz anp = vp->v_data; 142270096Strasz amp = VFSTOAUTOFS(vp->v_mount); 143270096Strasz 144270096Strasz /* 145270096Strasz * Release the vnode lock, so that other operations, in partcular 146270096Strasz * mounting a filesystem on top of it, can proceed. Increase use 147270096Strasz * count, to prevent the vnode from being deallocated and to prevent 148270096Strasz * filesystem from being unmounted. 149270096Strasz */ 150270096Strasz lock_flags = VOP_ISLOCKED(vp); 151270096Strasz vref(vp); 152270096Strasz VOP_UNLOCK(vp, 0); 153270096Strasz 154270900Strasz sx_xlock(&autofs_softc->sc_lock); 155270096Strasz 156270096Strasz /* 157270096Strasz * XXX: Workaround for mounting the same thing multiple times; revisit. 158270096Strasz */ 159270096Strasz if (vp->v_mountedhere != NULL) { 160270096Strasz error = 0; 161270096Strasz goto mounted; 162270096Strasz } 163270096Strasz 164270096Strasz error = autofs_trigger(anp, path, pathlen); 165270096Straszmounted: 166270900Strasz sx_xunlock(&autofs_softc->sc_lock); 167270096Strasz vn_lock(vp, lock_flags | LK_RETRY); 168270096Strasz vunref(vp); 169270096Strasz if ((vp->v_iflag & VI_DOOMED) != 0) { 170270096Strasz AUTOFS_DEBUG("VI_DOOMED"); 171270096Strasz return (ENOENT); 172270096Strasz } 173270096Strasz 174270096Strasz if (error != 0) 175270096Strasz return (error); 176270096Strasz 177270096Strasz if (vp->v_mountedhere == NULL) { 178270096Strasz *newvp = NULL; 179270096Strasz return (0); 180270096Strasz } else { 181270096Strasz /* 182270096Strasz * If the operation that succeeded was mount, then mark 183270096Strasz * the node as non-cached. Otherwise, if someone unmounts 184270096Strasz * the filesystem before the cache times out, we will fail 185270096Strasz * to trigger. 186270096Strasz */ 187270096Strasz anp->an_cached = false; 188270096Strasz } 189270096Strasz 190270096Strasz error = VFS_ROOT(vp->v_mountedhere, lock_flags, newvp); 191270096Strasz if (error != 0) { 192270096Strasz AUTOFS_WARN("VFS_ROOT() failed with error %d", error); 193270096Strasz return (error); 194270096Strasz } 195270096Strasz 196270096Strasz return (0); 197270096Strasz} 198270096Strasz 199270096Straszstatic int 200270894Straszautofs_vget_callback(struct mount *mp, void *arg, int lkflags __unused, 201270894Strasz struct vnode **vpp) 202270894Strasz{ 203270894Strasz 204270894Strasz 205270894Strasz return (autofs_node_vn(arg, mp, vpp)); 206270894Strasz} 207270894Strasz 208270894Straszstatic int 209270096Straszautofs_lookup(struct vop_lookup_args *ap) 210270096Strasz{ 211270096Strasz struct vnode *dvp, *newvp, **vpp; 212270096Strasz struct mount *mp; 213270096Strasz struct autofs_mount *amp; 214270096Strasz struct autofs_node *anp, *child; 215270096Strasz struct componentname *cnp; 216270096Strasz int error, lock_flags; 217270096Strasz 218270096Strasz dvp = ap->a_dvp; 219270096Strasz vpp = ap->a_vpp; 220270096Strasz mp = dvp->v_mount; 221270096Strasz amp = VFSTOAUTOFS(mp); 222270096Strasz anp = dvp->v_data; 223270096Strasz cnp = ap->a_cnp; 224270096Strasz 225270096Strasz if (cnp->cn_flags & ISDOTDOT) { 226270096Strasz KASSERT(anp->an_parent != NULL, ("NULL parent")); 227270096Strasz /* 228270894Strasz * Note that in this case, dvp is the child vnode, and we 229270894Strasz * are looking up the parent vnode - exactly reverse from 230270894Strasz * normal operation. Unlocking dvp requires some rather 231270894Strasz * tricky unlock/relock dance to prevent mp from being freed; 232270894Strasz * use vn_vget_ino_gen() which takes care of all that. 233270096Strasz */ 234270894Strasz error = vn_vget_ino_gen(dvp, autofs_vget_callback, 235270894Strasz anp->an_parent, 0, vpp); 236270096Strasz if (error != 0) { 237270894Strasz AUTOFS_WARN("vn_vget_ino_gen() failed with error %d", 238270096Strasz error); 239270894Strasz return (error); 240270096Strasz } 241270096Strasz return (error); 242270096Strasz } 243270096Strasz 244270096Strasz if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 245270096Strasz vref(dvp); 246270096Strasz *vpp = dvp; 247270096Strasz 248270096Strasz return (0); 249270096Strasz } 250270096Strasz 251270096Strasz if (autofs_cached(anp, cnp->cn_nameptr, cnp->cn_namelen) == false && 252270096Strasz autofs_ignore_thread(cnp->cn_thread) == false) { 253270096Strasz error = autofs_trigger_vn(dvp, 254270096Strasz cnp->cn_nameptr, cnp->cn_namelen, &newvp); 255270096Strasz if (error != 0) 256270096Strasz return (error); 257270096Strasz 258270096Strasz if (newvp != NULL) { 259270096Strasz error = VOP_LOOKUP(newvp, ap->a_vpp, ap->a_cnp); 260270096Strasz 261270096Strasz /* 262270096Strasz * Instead of figuring out whether our vnode should 263270096Strasz * be locked or not given the error and cnp flags, 264270096Strasz * just "copy" the lock status from vnode returned 265270096Strasz * by mounted filesystem's VOP_LOOKUP(). Get rid 266270096Strasz * of that new vnode afterwards. 267270096Strasz */ 268270096Strasz lock_flags = VOP_ISLOCKED(newvp); 269270096Strasz if (lock_flags == 0) { 270270096Strasz VOP_UNLOCK(dvp, 0); 271270096Strasz vrele(newvp); 272270096Strasz } else { 273270096Strasz vput(newvp); 274270096Strasz } 275270096Strasz return (error); 276270096Strasz } 277270096Strasz } 278270096Strasz 279270096Strasz AUTOFS_LOCK(amp); 280270096Strasz error = autofs_node_find(anp, cnp->cn_nameptr, cnp->cn_namelen, &child); 281270096Strasz if (error != 0) { 282270096Strasz if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) { 283270096Strasz AUTOFS_UNLOCK(amp); 284270096Strasz return (EJUSTRETURN); 285270096Strasz } 286270096Strasz 287270096Strasz AUTOFS_UNLOCK(amp); 288270096Strasz return (ENOENT); 289270096Strasz } 290270096Strasz 291270096Strasz /* 292270096Strasz * XXX: Dropping the node here is ok, because we never remove nodes. 293270096Strasz */ 294270096Strasz AUTOFS_UNLOCK(amp); 295270096Strasz 296270096Strasz error = autofs_node_vn(child, mp, vpp); 297270096Strasz if (error != 0) { 298270096Strasz if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) 299270096Strasz return (EJUSTRETURN); 300270096Strasz 301270096Strasz return (error); 302270096Strasz } 303270096Strasz 304270096Strasz return (0); 305270096Strasz} 306270096Strasz 307270096Straszstatic int 308270096Straszautofs_mkdir(struct vop_mkdir_args *ap) 309270096Strasz{ 310270096Strasz struct vnode *vp; 311270096Strasz struct autofs_node *anp; 312270096Strasz struct autofs_mount *amp; 313270096Strasz struct autofs_node *child; 314270096Strasz int error; 315270096Strasz 316270096Strasz vp = ap->a_dvp; 317270096Strasz anp = vp->v_data; 318270096Strasz amp = VFSTOAUTOFS(vp->v_mount); 319270096Strasz 320270096Strasz /* 321270096Strasz * Do not allow mkdir() if the calling thread is not 322270096Strasz * automountd(8) descendant. 323270096Strasz */ 324270096Strasz if (autofs_ignore_thread(curthread) == false) 325270096Strasz return (EPERM); 326270096Strasz 327270096Strasz AUTOFS_LOCK(amp); 328270096Strasz error = autofs_node_new(anp, amp, ap->a_cnp->cn_nameptr, 329270096Strasz ap->a_cnp->cn_namelen, &child); 330270096Strasz if (error != 0) { 331270096Strasz AUTOFS_UNLOCK(amp); 332270096Strasz return (error); 333270096Strasz } 334270096Strasz AUTOFS_UNLOCK(amp); 335270096Strasz 336270096Strasz error = autofs_node_vn(child, vp->v_mount, ap->a_vpp); 337270096Strasz 338270096Strasz return (error); 339270096Strasz} 340270096Strasz 341270096Straszstatic int 342270096Straszautofs_readdir_one(struct uio *uio, const char *name, int fileno) 343270096Strasz{ 344270096Strasz struct dirent dirent; 345270096Strasz int error, i; 346270096Strasz 347270096Strasz memset(&dirent, 0, sizeof(dirent)); 348270096Strasz dirent.d_type = DT_DIR; 349270096Strasz dirent.d_reclen = AUTOFS_DELEN; 350270096Strasz dirent.d_fileno = fileno; 351270096Strasz /* PFS_DELEN was picked to fit PFS_NAMLEN */ 352270096Strasz for (i = 0; i < AUTOFS_NAMELEN - 1 && name[i] != '\0'; ++i) 353270096Strasz dirent.d_name[i] = name[i]; 354270096Strasz dirent.d_name[i] = 0; 355270096Strasz dirent.d_namlen = i; 356270096Strasz 357270096Strasz error = uiomove(&dirent, AUTOFS_DELEN, uio); 358270096Strasz return (error); 359270096Strasz} 360270096Strasz 361270096Straszstatic int 362270096Straszautofs_readdir(struct vop_readdir_args *ap) 363270096Strasz{ 364270096Strasz struct vnode *vp, *newvp; 365270096Strasz struct autofs_mount *amp; 366270096Strasz struct autofs_node *anp, *child; 367270096Strasz struct uio *uio; 368270096Strasz off_t offset; 369270096Strasz int error, i, resid; 370270096Strasz 371270096Strasz vp = ap->a_vp; 372270096Strasz amp = VFSTOAUTOFS(vp->v_mount); 373270096Strasz anp = vp->v_data; 374270096Strasz uio = ap->a_uio; 375270096Strasz 376270096Strasz KASSERT(vp->v_type == VDIR, ("!VDIR")); 377270096Strasz 378270096Strasz if (autofs_cached(anp, NULL, 0) == false && 379270096Strasz autofs_ignore_thread(curthread) == false) { 380270096Strasz error = autofs_trigger_vn(vp, "", 0, &newvp); 381270096Strasz if (error != 0) 382270096Strasz return (error); 383270096Strasz 384270096Strasz if (newvp != NULL) { 385270096Strasz error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred, 386270096Strasz ap->a_eofflag, ap->a_ncookies, ap->a_cookies); 387270096Strasz vput(newvp); 388270096Strasz return (error); 389270096Strasz } 390270096Strasz } 391270096Strasz 392270096Strasz /* only allow reading entire entries */ 393270096Strasz offset = uio->uio_offset; 394270096Strasz resid = uio->uio_resid; 395270096Strasz if (offset < 0 || offset % AUTOFS_DELEN != 0 || 396270096Strasz (resid && resid < AUTOFS_DELEN)) 397270096Strasz return (EINVAL); 398270096Strasz if (resid == 0) 399270096Strasz return (0); 400270096Strasz 401270096Strasz if (ap->a_eofflag != NULL) 402270096Strasz *ap->a_eofflag = TRUE; 403270096Strasz 404270096Strasz if (offset == 0 && resid >= AUTOFS_DELEN) { 405270096Strasz error = autofs_readdir_one(uio, ".", anp->an_fileno); 406270096Strasz if (error != 0) 407270096Strasz return (error); 408270096Strasz offset += AUTOFS_DELEN; 409270096Strasz resid -= AUTOFS_DELEN; 410270096Strasz } 411270096Strasz 412270096Strasz if (offset == AUTOFS_DELEN && resid >= AUTOFS_DELEN) { 413270096Strasz if (anp->an_parent == NULL) { 414270096Strasz /* 415270096Strasz * XXX: Right? 416270096Strasz */ 417270096Strasz error = autofs_readdir_one(uio, "..", anp->an_fileno); 418270096Strasz } else { 419270096Strasz error = autofs_readdir_one(uio, "..", 420270096Strasz anp->an_parent->an_fileno); 421270096Strasz } 422270096Strasz if (error != 0) 423270096Strasz return (error); 424270096Strasz offset += AUTOFS_DELEN; 425270096Strasz resid -= AUTOFS_DELEN; 426270096Strasz } 427270096Strasz 428270096Strasz i = 2; /* Account for "." and "..". */ 429270096Strasz AUTOFS_LOCK(amp); 430270096Strasz TAILQ_FOREACH(child, &anp->an_children, an_next) { 431270096Strasz if (resid < AUTOFS_DELEN) { 432270096Strasz if (ap->a_eofflag != NULL) 433270096Strasz *ap->a_eofflag = 0; 434270096Strasz break; 435270096Strasz } 436270096Strasz 437270096Strasz /* 438270096Strasz * Skip entries returned by previous call to getdents(). 439270096Strasz */ 440270096Strasz i++; 441270096Strasz if (i * AUTOFS_DELEN <= offset) 442270096Strasz continue; 443270096Strasz 444270096Strasz error = autofs_readdir_one(uio, child->an_name, 445270096Strasz child->an_fileno); 446270096Strasz if (error != 0) { 447270096Strasz AUTOFS_UNLOCK(amp); 448270096Strasz return (error); 449270096Strasz } 450270096Strasz offset += AUTOFS_DELEN; 451270096Strasz resid -= AUTOFS_DELEN; 452270096Strasz } 453270096Strasz 454270096Strasz AUTOFS_UNLOCK(amp); 455270096Strasz return (0); 456270096Strasz} 457270096Strasz 458270096Straszstatic int 459270096Straszautofs_reclaim(struct vop_reclaim_args *ap) 460270096Strasz{ 461270096Strasz struct vnode *vp = ap->a_vp; 462270096Strasz struct autofs_node *anp = vp->v_data; 463270096Strasz 464270096Strasz vp = ap->a_vp; 465270096Strasz anp = vp->v_data; 466270096Strasz 467270096Strasz /* 468270096Strasz * We do not free autofs_node here; instead we are 469270096Strasz * destroying them in autofs_node_delete(). 470270096Strasz */ 471270096Strasz sx_xlock(&anp->an_vnode_lock); 472270096Strasz anp->an_vnode = NULL; 473270096Strasz vp->v_data = NULL; 474270096Strasz sx_xunlock(&anp->an_vnode_lock); 475270096Strasz 476270096Strasz return (0); 477270096Strasz} 478270096Strasz 479270096Straszstruct vop_vector autofs_vnodeops = { 480270096Strasz .vop_default = &default_vnodeops, 481270096Strasz 482270096Strasz .vop_access = autofs_access, 483270096Strasz .vop_lookup = autofs_lookup, 484270096Strasz .vop_create = VOP_EOPNOTSUPP, 485270096Strasz .vop_getattr = autofs_getattr, 486270096Strasz .vop_link = VOP_EOPNOTSUPP, 487270096Strasz .vop_mkdir = autofs_mkdir, 488270096Strasz .vop_mknod = VOP_EOPNOTSUPP, 489270096Strasz .vop_read = VOP_EOPNOTSUPP, 490270096Strasz .vop_readdir = autofs_readdir, 491270096Strasz .vop_remove = VOP_EOPNOTSUPP, 492270096Strasz .vop_rename = VOP_EOPNOTSUPP, 493270096Strasz .vop_rmdir = VOP_EOPNOTSUPP, 494270096Strasz .vop_setattr = VOP_EOPNOTSUPP, 495270096Strasz .vop_symlink = VOP_EOPNOTSUPP, 496270096Strasz .vop_write = VOP_EOPNOTSUPP, 497270096Strasz .vop_reclaim = autofs_reclaim, 498270096Strasz}; 499270096Strasz 500270096Straszint 501270096Straszautofs_node_new(struct autofs_node *parent, struct autofs_mount *amp, 502270096Strasz const char *name, int namelen, struct autofs_node **anpp) 503270096Strasz{ 504270096Strasz struct autofs_node *anp; 505270096Strasz 506270096Strasz if (parent != NULL) 507270096Strasz AUTOFS_ASSERT_LOCKED(parent->an_mount); 508270096Strasz 509270096Strasz anp = uma_zalloc(autofs_node_zone, M_WAITOK | M_ZERO); 510270096Strasz if (namelen >= 0) 511270096Strasz anp->an_name = strndup(name, namelen, M_AUTOFS); 512270096Strasz else 513270096Strasz anp->an_name = strdup(name, M_AUTOFS); 514270096Strasz anp->an_fileno = atomic_fetchadd_int(&->am_last_fileno, 1); 515270096Strasz callout_init(&anp->an_callout, 1); 516270096Strasz /* 517270096Strasz * The reason for SX_NOWITNESS here is that witness(4) 518270096Strasz * cannot tell vnodes apart, so the following perfectly 519270096Strasz * valid lock order... 520270096Strasz * 521270096Strasz * vnode lock A -> autofsvlk B -> vnode lock B 522270096Strasz * 523270096Strasz * ... gets reported as a LOR. 524270096Strasz */ 525270096Strasz sx_init_flags(&anp->an_vnode_lock, "autofsvlk", SX_NOWITNESS); 526270096Strasz getnanotime(&anp->an_ctime); 527270096Strasz anp->an_parent = parent; 528270096Strasz anp->an_mount = amp; 529270096Strasz if (parent != NULL) 530270096Strasz TAILQ_INSERT_TAIL(&parent->an_children, anp, an_next); 531270096Strasz TAILQ_INIT(&anp->an_children); 532270096Strasz 533270096Strasz *anpp = anp; 534270096Strasz return (0); 535270096Strasz} 536270096Strasz 537270096Straszint 538270096Straszautofs_node_find(struct autofs_node *parent, const char *name, 539270096Strasz int namelen, struct autofs_node **anpp) 540270096Strasz{ 541270096Strasz struct autofs_node *anp; 542270096Strasz 543270096Strasz AUTOFS_ASSERT_LOCKED(parent->an_mount); 544270096Strasz 545270096Strasz TAILQ_FOREACH(anp, &parent->an_children, an_next) { 546270096Strasz if (namelen >= 0) { 547272116Strasz if (strlen(anp->an_name) != namelen) 548272116Strasz continue; 549270096Strasz if (strncmp(anp->an_name, name, namelen) != 0) 550270096Strasz continue; 551270096Strasz } else { 552270096Strasz if (strcmp(anp->an_name, name) != 0) 553270096Strasz continue; 554270096Strasz } 555270096Strasz 556270096Strasz if (anpp != NULL) 557270096Strasz *anpp = anp; 558270096Strasz return (0); 559270096Strasz } 560270096Strasz 561270096Strasz return (ENOENT); 562270096Strasz} 563270096Strasz 564270096Straszvoid 565270096Straszautofs_node_delete(struct autofs_node *anp) 566270096Strasz{ 567270096Strasz struct autofs_node *parent; 568270096Strasz 569270096Strasz AUTOFS_ASSERT_LOCKED(anp->an_mount); 570270096Strasz KASSERT(TAILQ_EMPTY(&anp->an_children), ("have children")); 571270096Strasz 572270096Strasz callout_drain(&anp->an_callout); 573270096Strasz 574270096Strasz parent = anp->an_parent; 575270096Strasz if (parent != NULL) 576270096Strasz TAILQ_REMOVE(&parent->an_children, anp, an_next); 577270096Strasz sx_destroy(&anp->an_vnode_lock); 578270096Strasz free(anp->an_name, M_AUTOFS); 579270096Strasz uma_zfree(autofs_node_zone, anp); 580270096Strasz} 581270096Strasz 582270096Straszint 583270096Straszautofs_node_vn(struct autofs_node *anp, struct mount *mp, struct vnode **vpp) 584270096Strasz{ 585270096Strasz struct vnode *vp; 586270096Strasz int error; 587270096Strasz 588270096Strasz AUTOFS_ASSERT_UNLOCKED(anp->an_mount); 589270096Strasz 590270096Strasz sx_xlock(&anp->an_vnode_lock); 591270096Strasz 592270096Strasz vp = anp->an_vnode; 593270096Strasz if (vp != NULL) { 594270096Strasz error = vget(vp, LK_EXCLUSIVE | LK_RETRY, curthread); 595270096Strasz if (error != 0) { 596270096Strasz AUTOFS_WARN("vget failed with error %d", error); 597270096Strasz sx_xunlock(&anp->an_vnode_lock); 598270096Strasz return (error); 599270096Strasz } 600270096Strasz if (vp->v_iflag & VI_DOOMED) { 601270096Strasz /* 602270096Strasz * We got forcibly unmounted. 603270096Strasz */ 604270096Strasz AUTOFS_DEBUG("doomed vnode"); 605270096Strasz sx_xunlock(&anp->an_vnode_lock); 606270096Strasz vput(vp); 607270096Strasz 608270096Strasz return (ENOENT); 609270096Strasz } 610270096Strasz 611270096Strasz *vpp = vp; 612270096Strasz sx_xunlock(&anp->an_vnode_lock); 613270096Strasz return (0); 614270096Strasz } 615270096Strasz 616270096Strasz error = getnewvnode("autofs", mp, &autofs_vnodeops, &vp); 617270096Strasz if (error != 0) { 618270096Strasz sx_xunlock(&anp->an_vnode_lock); 619270096Strasz return (error); 620270096Strasz } 621270096Strasz 622270096Strasz error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 623270096Strasz if (error != 0) { 624270096Strasz sx_xunlock(&anp->an_vnode_lock); 625270096Strasz vdrop(vp); 626270096Strasz return (error); 627270096Strasz } 628270096Strasz 629270096Strasz vp->v_type = VDIR; 630270096Strasz if (anp->an_parent == NULL) 631270096Strasz vp->v_vflag |= VV_ROOT; 632270096Strasz vp->v_data = anp; 633270096Strasz 634270096Strasz error = insmntque(vp, mp); 635270096Strasz if (error != 0) { 636270096Strasz AUTOFS_WARN("insmntque() failed with error %d", error); 637270096Strasz sx_xunlock(&anp->an_vnode_lock); 638270096Strasz return (error); 639270096Strasz } 640270096Strasz 641270096Strasz KASSERT(anp->an_vnode == NULL, ("lost race")); 642270096Strasz anp->an_vnode = vp; 643270096Strasz 644270096Strasz sx_xunlock(&anp->an_vnode_lock); 645270096Strasz 646270096Strasz *vpp = vp; 647270096Strasz return (0); 648270096Strasz} 649