autofs_vnops.c revision 296718
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: head/sys/fs/autofs/autofs_vnops.c 296718 2016-03-12 09:33:26Z trasz $"); 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> 44296718Strasz#include <sys/stat.h> 45270096Strasz#include <sys/systm.h> 46272403Strasz#include <sys/taskqueue.h> 47270096Strasz#include <sys/vnode.h> 48270096Strasz#include <machine/atomic.h> 49270096Strasz#include <vm/uma.h> 50270096Strasz 51270281Strasz#include <fs/autofs/autofs.h> 52270096Strasz 53270096Straszstatic int autofs_trigger_vn(struct vnode *vp, const char *path, 54270096Strasz int pathlen, struct vnode **newvp); 55270096Strasz 56270402Straszextern struct autofs_softc *autofs_softc; 57270402Strasz 58270096Straszstatic int 59270096Straszautofs_access(struct vop_access_args *ap) 60270096Strasz{ 61270096Strasz 62270096Strasz /* 63270096Strasz * Nothing to do here; the only kind of access control 64270096Strasz * needed is in autofs_mkdir(). 65270096Strasz */ 66270096Strasz 67270096Strasz return (0); 68270096Strasz} 69270096Strasz 70270096Straszstatic int 71270096Straszautofs_getattr(struct vop_getattr_args *ap) 72270096Strasz{ 73270096Strasz struct vnode *vp, *newvp; 74270096Strasz struct autofs_node *anp; 75270096Strasz struct mount *mp; 76270096Strasz struct vattr *vap; 77270096Strasz int error; 78270096Strasz 79270096Strasz vp = ap->a_vp; 80270096Strasz anp = vp->v_data; 81270096Strasz mp = vp->v_mount; 82270096Strasz vap = ap->a_vap; 83270096Strasz 84270096Strasz KASSERT(ap->a_vp->v_type == VDIR, ("!VDIR")); 85270096Strasz 86270096Strasz /* 87270096Strasz * The reason we must do this is that some tree-walking software, 88270096Strasz * namely fts(3), assumes that stat(".") results will not change 89270096Strasz * between chdir("subdir") and chdir(".."), and fails with ENOENT 90270096Strasz * otherwise. 91270096Strasz */ 92270096Strasz if (autofs_mount_on_stat && autofs_cached(anp, NULL, 0) == false && 93270096Strasz autofs_ignore_thread(curthread) == false) { 94270096Strasz error = autofs_trigger_vn(vp, "", 0, &newvp); 95270096Strasz if (error != 0) 96270096Strasz return (error); 97270096Strasz 98270096Strasz if (newvp != NULL) { 99270096Strasz error = VOP_GETATTR(newvp, ap->a_vap, 100270096Strasz ap->a_cred); 101270096Strasz vput(newvp); 102270096Strasz return (error); 103270096Strasz } 104270096Strasz } 105270096Strasz 106270096Strasz vap->va_type = VDIR; 107270096Strasz vap->va_mode = 0755; 108270096Strasz vap->va_nlink = 3; /* XXX */ 109270096Strasz vap->va_uid = 0; 110270096Strasz vap->va_gid = 0; 111270096Strasz vap->va_rdev = NODEV; 112270096Strasz vap->va_fsid = mp->mnt_stat.f_fsid.val[0]; 113270096Strasz vap->va_fileid = anp->an_fileno; 114296718Strasz vap->va_size = S_BLKSIZE; 115296718Strasz vap->va_blocksize = S_BLKSIZE; 116270096Strasz vap->va_mtime = anp->an_ctime; 117270096Strasz vap->va_atime = anp->an_ctime; 118270096Strasz vap->va_ctime = anp->an_ctime; 119270096Strasz vap->va_birthtime = anp->an_ctime; 120270096Strasz vap->va_gen = 0; 121270096Strasz vap->va_flags = 0; 122270096Strasz vap->va_rdev = 0; 123296718Strasz vap->va_bytes = S_BLKSIZE; 124270096Strasz vap->va_filerev = 0; 125270096Strasz vap->va_spare = 0; 126270096Strasz 127270096Strasz return (0); 128270096Strasz} 129270096Strasz 130270096Strasz/* 131270096Strasz * Unlock the vnode, request automountd(8) action, and then lock it back. 132270096Strasz * If anything got mounted on top of the vnode, return the new filesystem's 133270096Strasz * root vnode in 'newvp', locked. 134270096Strasz */ 135270096Straszstatic int 136270096Straszautofs_trigger_vn(struct vnode *vp, const char *path, int pathlen, 137270096Strasz struct vnode **newvp) 138270096Strasz{ 139270096Strasz struct autofs_node *anp; 140270096Strasz struct autofs_mount *amp; 141270096Strasz int error, lock_flags; 142270096Strasz 143270096Strasz anp = vp->v_data; 144270096Strasz amp = VFSTOAUTOFS(vp->v_mount); 145270096Strasz 146270096Strasz /* 147270096Strasz * Release the vnode lock, so that other operations, in partcular 148270096Strasz * mounting a filesystem on top of it, can proceed. Increase use 149270096Strasz * count, to prevent the vnode from being deallocated and to prevent 150270096Strasz * filesystem from being unmounted. 151270096Strasz */ 152270096Strasz lock_flags = VOP_ISLOCKED(vp); 153270096Strasz vref(vp); 154270096Strasz VOP_UNLOCK(vp, 0); 155270096Strasz 156270402Strasz sx_xlock(&autofs_softc->sc_lock); 157270096Strasz 158270096Strasz /* 159270096Strasz * XXX: Workaround for mounting the same thing multiple times; revisit. 160270096Strasz */ 161270096Strasz if (vp->v_mountedhere != NULL) { 162270096Strasz error = 0; 163270096Strasz goto mounted; 164270096Strasz } 165270096Strasz 166270096Strasz error = autofs_trigger(anp, path, pathlen); 167270096Straszmounted: 168270402Strasz sx_xunlock(&autofs_softc->sc_lock); 169270096Strasz vn_lock(vp, lock_flags | LK_RETRY); 170270096Strasz vunref(vp); 171270096Strasz if ((vp->v_iflag & VI_DOOMED) != 0) { 172270096Strasz AUTOFS_DEBUG("VI_DOOMED"); 173270096Strasz return (ENOENT); 174270096Strasz } 175270096Strasz 176270096Strasz if (error != 0) 177270096Strasz return (error); 178270096Strasz 179270096Strasz if (vp->v_mountedhere == NULL) { 180270096Strasz *newvp = NULL; 181270096Strasz return (0); 182270096Strasz } else { 183270096Strasz /* 184270096Strasz * If the operation that succeeded was mount, then mark 185270096Strasz * the node as non-cached. Otherwise, if someone unmounts 186270096Strasz * the filesystem before the cache times out, we will fail 187270096Strasz * to trigger. 188270096Strasz */ 189270096Strasz anp->an_cached = false; 190270096Strasz } 191270096Strasz 192270096Strasz error = VFS_ROOT(vp->v_mountedhere, lock_flags, newvp); 193270096Strasz if (error != 0) { 194270096Strasz AUTOFS_WARN("VFS_ROOT() failed with error %d", error); 195270096Strasz return (error); 196270096Strasz } 197270096Strasz 198270096Strasz return (0); 199270096Strasz} 200270096Strasz 201270096Straszstatic int 202272512Straszautofs_vget_callback(struct mount *mp, void *arg, int flags, 203270207Strasz struct vnode **vpp) 204270207Strasz{ 205270207Strasz 206270207Strasz 207272512Strasz return (autofs_node_vn(arg, mp, flags, vpp)); 208270207Strasz} 209270207Strasz 210270207Straszstatic int 211270096Straszautofs_lookup(struct vop_lookup_args *ap) 212270096Strasz{ 213270096Strasz struct vnode *dvp, *newvp, **vpp; 214270096Strasz struct mount *mp; 215270096Strasz struct autofs_mount *amp; 216270096Strasz struct autofs_node *anp, *child; 217270096Strasz struct componentname *cnp; 218296715Strasz int error; 219270096Strasz 220270096Strasz dvp = ap->a_dvp; 221270096Strasz vpp = ap->a_vpp; 222270096Strasz mp = dvp->v_mount; 223270096Strasz amp = VFSTOAUTOFS(mp); 224270096Strasz anp = dvp->v_data; 225270096Strasz cnp = ap->a_cnp; 226270096Strasz 227270096Strasz if (cnp->cn_flags & ISDOTDOT) { 228270096Strasz KASSERT(anp->an_parent != NULL, ("NULL parent")); 229270096Strasz /* 230270207Strasz * Note that in this case, dvp is the child vnode, and we 231270207Strasz * are looking up the parent vnode - exactly reverse from 232270207Strasz * normal operation. Unlocking dvp requires some rather 233270207Strasz * tricky unlock/relock dance to prevent mp from being freed; 234270207Strasz * use vn_vget_ino_gen() which takes care of all that. 235270096Strasz */ 236270207Strasz error = vn_vget_ino_gen(dvp, autofs_vget_callback, 237272512Strasz anp->an_parent, cnp->cn_lkflags, vpp); 238270096Strasz if (error != 0) { 239270207Strasz AUTOFS_WARN("vn_vget_ino_gen() failed with error %d", 240270096Strasz error); 241270207Strasz return (error); 242270096Strasz } 243270096Strasz return (error); 244270096Strasz } 245270096Strasz 246270096Strasz if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 247270096Strasz vref(dvp); 248270096Strasz *vpp = dvp; 249270096Strasz 250270096Strasz return (0); 251270096Strasz } 252270096Strasz 253270096Strasz if (autofs_cached(anp, cnp->cn_nameptr, cnp->cn_namelen) == false && 254270096Strasz autofs_ignore_thread(cnp->cn_thread) == false) { 255270096Strasz error = autofs_trigger_vn(dvp, 256270096Strasz cnp->cn_nameptr, cnp->cn_namelen, &newvp); 257270096Strasz if (error != 0) 258270096Strasz return (error); 259270096Strasz 260270096Strasz if (newvp != NULL) { 261270096Strasz /* 262296715Strasz * The target filesystem got automounted. 263296715Strasz * Let the lookup(9) go around with the same 264296715Strasz * path component. 265270096Strasz */ 266296715Strasz vput(newvp); 267296715Strasz return (ERELOOKUP); 268270096Strasz } 269270096Strasz } 270270096Strasz 271272470Strasz AUTOFS_SLOCK(amp); 272270096Strasz error = autofs_node_find(anp, cnp->cn_nameptr, cnp->cn_namelen, &child); 273270096Strasz if (error != 0) { 274270096Strasz if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) { 275272470Strasz AUTOFS_SUNLOCK(amp); 276270096Strasz return (EJUSTRETURN); 277270096Strasz } 278270096Strasz 279272470Strasz AUTOFS_SUNLOCK(amp); 280270096Strasz return (ENOENT); 281270096Strasz } 282270096Strasz 283270096Strasz /* 284270096Strasz * XXX: Dropping the node here is ok, because we never remove nodes. 285270096Strasz */ 286272470Strasz AUTOFS_SUNLOCK(amp); 287270096Strasz 288272512Strasz error = autofs_node_vn(child, mp, cnp->cn_lkflags, vpp); 289270096Strasz if (error != 0) { 290270096Strasz if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) 291270096Strasz return (EJUSTRETURN); 292270096Strasz 293270096Strasz return (error); 294270096Strasz } 295270096Strasz 296270096Strasz return (0); 297270096Strasz} 298270096Strasz 299270096Straszstatic int 300270096Straszautofs_mkdir(struct vop_mkdir_args *ap) 301270096Strasz{ 302270096Strasz struct vnode *vp; 303270096Strasz struct autofs_node *anp; 304270096Strasz struct autofs_mount *amp; 305270096Strasz struct autofs_node *child; 306270096Strasz int error; 307270096Strasz 308270096Strasz vp = ap->a_dvp; 309270096Strasz anp = vp->v_data; 310270096Strasz amp = VFSTOAUTOFS(vp->v_mount); 311270096Strasz 312270096Strasz /* 313270096Strasz * Do not allow mkdir() if the calling thread is not 314270096Strasz * automountd(8) descendant. 315270096Strasz */ 316270096Strasz if (autofs_ignore_thread(curthread) == false) 317270096Strasz return (EPERM); 318270096Strasz 319272470Strasz AUTOFS_XLOCK(amp); 320270096Strasz error = autofs_node_new(anp, amp, ap->a_cnp->cn_nameptr, 321270096Strasz ap->a_cnp->cn_namelen, &child); 322270096Strasz if (error != 0) { 323272470Strasz AUTOFS_XUNLOCK(amp); 324270096Strasz return (error); 325270096Strasz } 326272470Strasz AUTOFS_XUNLOCK(amp); 327270096Strasz 328272512Strasz error = autofs_node_vn(child, vp->v_mount, LK_EXCLUSIVE, ap->a_vpp); 329270096Strasz 330270096Strasz return (error); 331270096Strasz} 332270096Strasz 333270096Straszstatic int 334270096Straszautofs_readdir_one(struct uio *uio, const char *name, int fileno) 335270096Strasz{ 336270096Strasz struct dirent dirent; 337270096Strasz int error, i; 338270096Strasz 339270096Strasz memset(&dirent, 0, sizeof(dirent)); 340270096Strasz dirent.d_type = DT_DIR; 341270096Strasz dirent.d_reclen = AUTOFS_DELEN; 342270096Strasz dirent.d_fileno = fileno; 343270096Strasz /* PFS_DELEN was picked to fit PFS_NAMLEN */ 344270096Strasz for (i = 0; i < AUTOFS_NAMELEN - 1 && name[i] != '\0'; ++i) 345270096Strasz dirent.d_name[i] = name[i]; 346270096Strasz dirent.d_name[i] = 0; 347270096Strasz dirent.d_namlen = i; 348270096Strasz 349270096Strasz error = uiomove(&dirent, AUTOFS_DELEN, uio); 350270096Strasz return (error); 351270096Strasz} 352270096Strasz 353270096Straszstatic int 354270096Straszautofs_readdir(struct vop_readdir_args *ap) 355270096Strasz{ 356270096Strasz struct vnode *vp, *newvp; 357270096Strasz struct autofs_mount *amp; 358270096Strasz struct autofs_node *anp, *child; 359270096Strasz struct uio *uio; 360270096Strasz off_t offset; 361270096Strasz int error, i, resid; 362270096Strasz 363270096Strasz vp = ap->a_vp; 364270096Strasz amp = VFSTOAUTOFS(vp->v_mount); 365270096Strasz anp = vp->v_data; 366270096Strasz uio = ap->a_uio; 367270096Strasz 368270096Strasz KASSERT(vp->v_type == VDIR, ("!VDIR")); 369270096Strasz 370270096Strasz if (autofs_cached(anp, NULL, 0) == false && 371270096Strasz autofs_ignore_thread(curthread) == false) { 372270096Strasz error = autofs_trigger_vn(vp, "", 0, &newvp); 373270096Strasz if (error != 0) 374270096Strasz return (error); 375270096Strasz 376270096Strasz if (newvp != NULL) { 377270096Strasz error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred, 378270096Strasz ap->a_eofflag, ap->a_ncookies, ap->a_cookies); 379270096Strasz vput(newvp); 380270096Strasz return (error); 381270096Strasz } 382270096Strasz } 383270096Strasz 384270096Strasz /* only allow reading entire entries */ 385270096Strasz offset = uio->uio_offset; 386270096Strasz resid = uio->uio_resid; 387270096Strasz if (offset < 0 || offset % AUTOFS_DELEN != 0 || 388270096Strasz (resid && resid < AUTOFS_DELEN)) 389270096Strasz return (EINVAL); 390270096Strasz if (resid == 0) 391270096Strasz return (0); 392270096Strasz 393270096Strasz if (ap->a_eofflag != NULL) 394270096Strasz *ap->a_eofflag = TRUE; 395270096Strasz 396270096Strasz if (offset == 0 && resid >= AUTOFS_DELEN) { 397270096Strasz error = autofs_readdir_one(uio, ".", anp->an_fileno); 398270096Strasz if (error != 0) 399270096Strasz return (error); 400270096Strasz offset += AUTOFS_DELEN; 401270096Strasz resid -= AUTOFS_DELEN; 402270096Strasz } 403270096Strasz 404270096Strasz if (offset == AUTOFS_DELEN && resid >= AUTOFS_DELEN) { 405270096Strasz if (anp->an_parent == NULL) { 406270096Strasz /* 407270096Strasz * XXX: Right? 408270096Strasz */ 409270096Strasz error = autofs_readdir_one(uio, "..", anp->an_fileno); 410270096Strasz } else { 411270096Strasz error = autofs_readdir_one(uio, "..", 412270096Strasz anp->an_parent->an_fileno); 413270096Strasz } 414270096Strasz if (error != 0) 415270096Strasz return (error); 416270096Strasz offset += AUTOFS_DELEN; 417270096Strasz resid -= AUTOFS_DELEN; 418270096Strasz } 419270096Strasz 420270096Strasz i = 2; /* Account for "." and "..". */ 421272470Strasz AUTOFS_SLOCK(amp); 422270096Strasz TAILQ_FOREACH(child, &anp->an_children, an_next) { 423270096Strasz if (resid < AUTOFS_DELEN) { 424270096Strasz if (ap->a_eofflag != NULL) 425270096Strasz *ap->a_eofflag = 0; 426270096Strasz break; 427270096Strasz } 428270096Strasz 429270096Strasz /* 430270096Strasz * Skip entries returned by previous call to getdents(). 431270096Strasz */ 432270096Strasz i++; 433270096Strasz if (i * AUTOFS_DELEN <= offset) 434270096Strasz continue; 435270096Strasz 436270096Strasz error = autofs_readdir_one(uio, child->an_name, 437270096Strasz child->an_fileno); 438270096Strasz if (error != 0) { 439272470Strasz AUTOFS_SUNLOCK(amp); 440270096Strasz return (error); 441270096Strasz } 442270096Strasz offset += AUTOFS_DELEN; 443270096Strasz resid -= AUTOFS_DELEN; 444270096Strasz } 445270096Strasz 446272470Strasz AUTOFS_SUNLOCK(amp); 447270096Strasz return (0); 448270096Strasz} 449270096Strasz 450270096Straszstatic int 451270096Straszautofs_reclaim(struct vop_reclaim_args *ap) 452270096Strasz{ 453272836Strasz struct vnode *vp; 454272836Strasz struct autofs_node *anp; 455270096Strasz 456270096Strasz vp = ap->a_vp; 457270096Strasz anp = vp->v_data; 458270096Strasz 459270096Strasz /* 460270096Strasz * We do not free autofs_node here; instead we are 461270096Strasz * destroying them in autofs_node_delete(). 462270096Strasz */ 463270096Strasz sx_xlock(&anp->an_vnode_lock); 464270096Strasz anp->an_vnode = NULL; 465270096Strasz vp->v_data = NULL; 466270096Strasz sx_xunlock(&anp->an_vnode_lock); 467270096Strasz 468270096Strasz return (0); 469270096Strasz} 470270096Strasz 471270096Straszstruct vop_vector autofs_vnodeops = { 472270096Strasz .vop_default = &default_vnodeops, 473270096Strasz 474270096Strasz .vop_access = autofs_access, 475270096Strasz .vop_lookup = autofs_lookup, 476270096Strasz .vop_create = VOP_EOPNOTSUPP, 477270096Strasz .vop_getattr = autofs_getattr, 478270096Strasz .vop_link = VOP_EOPNOTSUPP, 479270096Strasz .vop_mkdir = autofs_mkdir, 480270096Strasz .vop_mknod = VOP_EOPNOTSUPP, 481270096Strasz .vop_read = VOP_EOPNOTSUPP, 482270096Strasz .vop_readdir = autofs_readdir, 483270096Strasz .vop_remove = VOP_EOPNOTSUPP, 484270096Strasz .vop_rename = VOP_EOPNOTSUPP, 485270096Strasz .vop_rmdir = VOP_EOPNOTSUPP, 486270096Strasz .vop_setattr = VOP_EOPNOTSUPP, 487270096Strasz .vop_symlink = VOP_EOPNOTSUPP, 488270096Strasz .vop_write = VOP_EOPNOTSUPP, 489270096Strasz .vop_reclaim = autofs_reclaim, 490270096Strasz}; 491270096Strasz 492270096Straszint 493270096Straszautofs_node_new(struct autofs_node *parent, struct autofs_mount *amp, 494270096Strasz const char *name, int namelen, struct autofs_node **anpp) 495270096Strasz{ 496270096Strasz struct autofs_node *anp; 497270096Strasz 498272931Strasz if (parent != NULL) { 499272470Strasz AUTOFS_ASSERT_XLOCKED(parent->an_mount); 500270096Strasz 501272931Strasz KASSERT(autofs_node_find(parent, name, namelen, NULL) == ENOENT, 502272931Strasz ("node \"%s\" already exists", name)); 503272931Strasz } 504272931Strasz 505270096Strasz anp = uma_zalloc(autofs_node_zone, M_WAITOK | M_ZERO); 506270096Strasz if (namelen >= 0) 507270096Strasz anp->an_name = strndup(name, namelen, M_AUTOFS); 508270096Strasz else 509270096Strasz anp->an_name = strdup(name, M_AUTOFS); 510270096Strasz anp->an_fileno = atomic_fetchadd_int(&->am_last_fileno, 1); 511270096Strasz callout_init(&anp->an_callout, 1); 512270096Strasz /* 513270096Strasz * The reason for SX_NOWITNESS here is that witness(4) 514270096Strasz * cannot tell vnodes apart, so the following perfectly 515270096Strasz * valid lock order... 516270096Strasz * 517270096Strasz * vnode lock A -> autofsvlk B -> vnode lock B 518270096Strasz * 519270096Strasz * ... gets reported as a LOR. 520270096Strasz */ 521270096Strasz sx_init_flags(&anp->an_vnode_lock, "autofsvlk", SX_NOWITNESS); 522270096Strasz getnanotime(&anp->an_ctime); 523270096Strasz anp->an_parent = parent; 524270096Strasz anp->an_mount = amp; 525270096Strasz if (parent != NULL) 526270096Strasz TAILQ_INSERT_TAIL(&parent->an_children, anp, an_next); 527270096Strasz TAILQ_INIT(&anp->an_children); 528270096Strasz 529270096Strasz *anpp = anp; 530270096Strasz return (0); 531270096Strasz} 532270096Strasz 533270096Straszint 534270096Straszautofs_node_find(struct autofs_node *parent, const char *name, 535270096Strasz int namelen, struct autofs_node **anpp) 536270096Strasz{ 537270096Strasz struct autofs_node *anp; 538270096Strasz 539270096Strasz AUTOFS_ASSERT_LOCKED(parent->an_mount); 540270096Strasz 541270096Strasz TAILQ_FOREACH(anp, &parent->an_children, an_next) { 542270096Strasz if (namelen >= 0) { 543272025Strasz if (strlen(anp->an_name) != namelen) 544272025Strasz continue; 545270096Strasz if (strncmp(anp->an_name, name, namelen) != 0) 546270096Strasz continue; 547270096Strasz } else { 548270096Strasz if (strcmp(anp->an_name, name) != 0) 549270096Strasz continue; 550270096Strasz } 551270096Strasz 552270096Strasz if (anpp != NULL) 553270096Strasz *anpp = anp; 554270096Strasz return (0); 555270096Strasz } 556270096Strasz 557270096Strasz return (ENOENT); 558270096Strasz} 559270096Strasz 560270096Straszvoid 561270096Straszautofs_node_delete(struct autofs_node *anp) 562270096Strasz{ 563270096Strasz struct autofs_node *parent; 564270096Strasz 565272470Strasz AUTOFS_ASSERT_XLOCKED(anp->an_mount); 566270096Strasz KASSERT(TAILQ_EMPTY(&anp->an_children), ("have children")); 567270096Strasz 568270096Strasz callout_drain(&anp->an_callout); 569270096Strasz 570270096Strasz parent = anp->an_parent; 571270096Strasz if (parent != NULL) 572270096Strasz TAILQ_REMOVE(&parent->an_children, anp, an_next); 573270096Strasz sx_destroy(&anp->an_vnode_lock); 574270096Strasz free(anp->an_name, M_AUTOFS); 575270096Strasz uma_zfree(autofs_node_zone, anp); 576270096Strasz} 577270096Strasz 578270096Straszint 579272512Straszautofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags, 580272512Strasz struct vnode **vpp) 581270096Strasz{ 582270096Strasz struct vnode *vp; 583270096Strasz int error; 584270096Strasz 585270096Strasz AUTOFS_ASSERT_UNLOCKED(anp->an_mount); 586270096Strasz 587270096Strasz sx_xlock(&anp->an_vnode_lock); 588270096Strasz 589270096Strasz vp = anp->an_vnode; 590270096Strasz if (vp != NULL) { 591272512Strasz error = vget(vp, flags | LK_RETRY, curthread); 592270096Strasz if (error != 0) { 593270096Strasz AUTOFS_WARN("vget failed with error %d", error); 594270096Strasz sx_xunlock(&anp->an_vnode_lock); 595270096Strasz return (error); 596270096Strasz } 597270096Strasz if (vp->v_iflag & VI_DOOMED) { 598270096Strasz /* 599270096Strasz * We got forcibly unmounted. 600270096Strasz */ 601270096Strasz AUTOFS_DEBUG("doomed vnode"); 602270096Strasz sx_xunlock(&anp->an_vnode_lock); 603270096Strasz vput(vp); 604270096Strasz 605270096Strasz return (ENOENT); 606270096Strasz } 607270096Strasz 608270096Strasz *vpp = vp; 609270096Strasz sx_xunlock(&anp->an_vnode_lock); 610270096Strasz return (0); 611270096Strasz } 612270096Strasz 613270096Strasz error = getnewvnode("autofs", mp, &autofs_vnodeops, &vp); 614270096Strasz if (error != 0) { 615270096Strasz sx_xunlock(&anp->an_vnode_lock); 616270096Strasz return (error); 617270096Strasz } 618270096Strasz 619270096Strasz error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 620270096Strasz if (error != 0) { 621270096Strasz sx_xunlock(&anp->an_vnode_lock); 622270096Strasz vdrop(vp); 623270096Strasz return (error); 624270096Strasz } 625270096Strasz 626270096Strasz vp->v_type = VDIR; 627270096Strasz if (anp->an_parent == NULL) 628270096Strasz vp->v_vflag |= VV_ROOT; 629270096Strasz vp->v_data = anp; 630270096Strasz 631272512Strasz VN_LOCK_ASHARE(vp); 632272512Strasz 633270096Strasz error = insmntque(vp, mp); 634270096Strasz if (error != 0) { 635270096Strasz AUTOFS_WARN("insmntque() failed with error %d", error); 636270096Strasz sx_xunlock(&anp->an_vnode_lock); 637270096Strasz return (error); 638270096Strasz } 639270096Strasz 640270096Strasz KASSERT(anp->an_vnode == NULL, ("lost race")); 641270096Strasz anp->an_vnode = vp; 642270096Strasz 643270096Strasz sx_xunlock(&anp->an_vnode_lock); 644270096Strasz 645270096Strasz *vpp = vp; 646270096Strasz return (0); 647270096Strasz} 648