autofs_vnops.c revision 270207
160812Sps/*- 260786Sps * Copyright (c) 2014 The FreeBSD Foundation 360786Sps * All rights reserved. 460786Sps * 560786Sps * This software was developed by Edward Tomasz Napierala under sponsorship 660786Sps * from the FreeBSD Foundation. 760786Sps * 860786Sps * Redistribution and use in source and binary forms, with or without 960786Sps * modification, are permitted provided that the following conditions 1060786Sps * are met: 1160786Sps * 1. Redistributions of source code must retain the above copyright 1260786Sps * notice, this list of conditions and the following disclaimer. 1360786Sps * 2. Redistributions in binary form must reproduce the above copyright 1460786Sps * notice, this list of conditions and the following disclaimer in the 1560786Sps * documentation and/or other materials provided with the distribution. 1660786Sps * 1760786Sps * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1860786Sps * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1960786Sps * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2060786Sps * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2160786Sps * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2260786Sps * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2360786Sps * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2460786Sps * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2560786Sps * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2660786Sps * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2760786Sps * SUCH DAMAGE. 2860786Sps * 2960786Sps * $FreeBSD: head/sys/fs/autofs/autofs_vnops.c 270207 2014-08-20 13:46:51Z trasz $ 3060786Sps */ 3160812Sps 3260786Sps#include <sys/cdefs.h> 3360786Sps__FBSDID("$FreeBSD: head/sys/fs/autofs/autofs_vnops.c 270207 2014-08-20 13:46:51Z trasz $"); 3460786Sps 3560786Sps#include <sys/param.h> 3660786Sps#include <sys/kernel.h> 3760786Sps#include <sys/condvar.h> 3860786Sps#include <sys/dirent.h> 3960786Sps#include <sys/fcntl.h> 4060786Sps#include <sys/lock.h> 4160786Sps#include <sys/mount.h> 4260786Sps#include <sys/mutex.h> 4360786Sps#include <sys/namei.h> 4460786Sps#include <sys/signalvar.h> 4560786Sps#include <sys/systm.h> 4660786Sps#include <sys/vnode.h> 4760786Sps#include <machine/atomic.h> 4860786Sps#include <vm/uma.h> 4960786Sps 5060786Sps#include "autofs.h" 5160786Sps 5260786Spsstatic int autofs_trigger_vn(struct vnode *vp, const char *path, 5360786Sps int pathlen, struct vnode **newvp); 5460786Sps 5560786Spsstatic int 5660786Spsautofs_access(struct vop_access_args *ap) 5760786Sps{ 5860786Sps 5960786Sps /* 6060786Sps * Nothing to do here; the only kind of access control 6160786Sps * needed is in autofs_mkdir(). 6260786Sps */ 6360786Sps 6460786Sps return (0); 6560786Sps} 6660786Sps 6760786Spsstatic int 6860786Spsautofs_getattr(struct vop_getattr_args *ap) 6960786Sps{ 7060786Sps struct vnode *vp, *newvp; 7160786Sps struct autofs_node *anp; 7260786Sps struct mount *mp; 7360786Sps struct vattr *vap; 7460786Sps int error; 7560786Sps 7660786Sps vp = ap->a_vp; 7760786Sps anp = vp->v_data; 7860786Sps mp = vp->v_mount; 7960786Sps vap = ap->a_vap; 8060786Sps 8160786Sps KASSERT(ap->a_vp->v_type == VDIR, ("!VDIR")); 8260786Sps 8360786Sps /* 8460786Sps * The reason we must do this is that some tree-walking software, 8560786Sps * namely fts(3), assumes that stat(".") results will not change 8660786Sps * between chdir("subdir") and chdir(".."), and fails with ENOENT 8760786Sps * otherwise. 8860786Sps */ 8960786Sps if (autofs_mount_on_stat && autofs_cached(anp, NULL, 0) == false && 9060786Sps autofs_ignore_thread(curthread) == false) { 9160786Sps error = autofs_trigger_vn(vp, "", 0, &newvp); 9260786Sps if (error != 0) 9360786Sps return (error); 9460786Sps 9560786Sps if (newvp != NULL) { 9660786Sps error = VOP_GETATTR(newvp, ap->a_vap, 9760786Sps ap->a_cred); 9860786Sps vput(newvp); 9960786Sps return (error); 10060786Sps } 10160786Sps } 10260786Sps 10360786Sps vap->va_type = VDIR; 10460786Sps vap->va_mode = 0755; 10560786Sps vap->va_nlink = 3; /* XXX */ 10660786Sps vap->va_uid = 0; 10760786Sps vap->va_gid = 0; 10860786Sps vap->va_rdev = NODEV; 10960786Sps vap->va_fsid = mp->mnt_stat.f_fsid.val[0]; 11060786Sps vap->va_fileid = anp->an_fileno; 11160786Sps vap->va_size = 512; /* XXX */ 11260786Sps vap->va_blocksize = 512; 11360786Sps vap->va_mtime = anp->an_ctime; 11460786Sps vap->va_atime = anp->an_ctime; 11560786Sps vap->va_ctime = anp->an_ctime; 11660786Sps vap->va_birthtime = anp->an_ctime; 11760786Sps vap->va_gen = 0; 11860786Sps vap->va_flags = 0; 11960786Sps vap->va_rdev = 0; 12060786Sps vap->va_bytes = 512; /* XXX */ 12160786Sps vap->va_filerev = 0; 12260786Sps vap->va_spare = 0; 12360786Sps 12460786Sps return (0); 12560786Sps} 12660786Sps 12760786Sps/* 12860786Sps * Unlock the vnode, request automountd(8) action, and then lock it back. 12960786Sps * If anything got mounted on top of the vnode, return the new filesystem's 13060786Sps * root vnode in 'newvp', locked. 13160786Sps */ 13260786Spsstatic int 13360786Spsautofs_trigger_vn(struct vnode *vp, const char *path, int pathlen, 13460786Sps struct vnode **newvp) 13560786Sps{ 13660786Sps struct autofs_node *anp; 13760786Sps struct autofs_mount *amp; 13860786Sps struct autofs_softc *sc; 13960786Sps int error, lock_flags; 14060786Sps 14160786Sps anp = vp->v_data; 14260786Sps amp = VFSTOAUTOFS(vp->v_mount); 14360786Sps sc = amp->am_softc; 14460786Sps 14560786Sps /* 14660786Sps * Release the vnode lock, so that other operations, in partcular 14760812Sps * mounting a filesystem on top of it, can proceed. Increase use 14860812Sps * count, to prevent the vnode from being deallocated and to prevent 14960812Sps * filesystem from being unmounted. 15060812Sps */ 15160812Sps lock_flags = VOP_ISLOCKED(vp); 15260812Sps vref(vp); 15360786Sps VOP_UNLOCK(vp, 0); 15460786Sps 15560786Sps sx_xlock(&sc->sc_lock); 15660786Sps 15760786Sps /* 15860786Sps * XXX: Workaround for mounting the same thing multiple times; revisit. 15960786Sps */ 16060786Sps if (vp->v_mountedhere != NULL) { 16160786Sps error = 0; 16260786Sps goto mounted; 16360786Sps } 16460786Sps 16560786Sps error = autofs_trigger(anp, path, pathlen); 16660786Spsmounted: 16760786Sps sx_xunlock(&sc->sc_lock); 16860786Sps vn_lock(vp, lock_flags | LK_RETRY); 16960786Sps vunref(vp); 17060786Sps if ((vp->v_iflag & VI_DOOMED) != 0) { 17160786Sps AUTOFS_DEBUG("VI_DOOMED"); 17260786Sps return (ENOENT); 17360786Sps } 17460786Sps 17560786Sps if (error != 0) 17660786Sps return (error); 17760786Sps 17860786Sps if (vp->v_mountedhere == NULL) { 17960786Sps *newvp = NULL; 18060786Sps return (0); 18160786Sps } else { 18260786Sps /* 18360786Sps * If the operation that succeeded was mount, then mark 18460786Sps * the node as non-cached. Otherwise, if someone unmounts 18560786Sps * the filesystem before the cache times out, we will fail 18660786Sps * to trigger. 18760786Sps */ 18860786Sps anp->an_cached = false; 18960786Sps } 19060786Sps 19160786Sps error = VFS_ROOT(vp->v_mountedhere, lock_flags, newvp); 19260786Sps if (error != 0) { 19360786Sps AUTOFS_WARN("VFS_ROOT() failed with error %d", error); 19460786Sps return (error); 19560786Sps } 19660786Sps 19760786Sps return (0); 19860786Sps} 19960786Sps 20060786Spsstatic int 20160786Spsautofs_vget_callback(struct mount *mp, void *arg, int lkflags __unused, 20260786Sps struct vnode **vpp) 20360786Sps{ 20460786Sps 20560786Sps 20660786Sps return (autofs_node_vn(arg, mp, vpp)); 20760786Sps} 20860786Sps 20960786Spsstatic int 21060786Spsautofs_lookup(struct vop_lookup_args *ap) 21160786Sps{ 21260786Sps struct vnode *dvp, *newvp, **vpp; 21360786Sps struct mount *mp; 21460786Sps struct autofs_mount *amp; 21560786Sps struct autofs_node *anp, *child; 21660786Sps struct componentname *cnp; 21760786Sps int error, lock_flags; 21860786Sps 21960786Sps dvp = ap->a_dvp; 22060786Sps vpp = ap->a_vpp; 22160786Sps mp = dvp->v_mount; 22260786Sps amp = VFSTOAUTOFS(mp); 22360786Sps anp = dvp->v_data; 22460786Sps cnp = ap->a_cnp; 22560786Sps 22660786Sps if (cnp->cn_flags & ISDOTDOT) { 22760786Sps KASSERT(anp->an_parent != NULL, ("NULL parent")); 22860786Sps /* 22960786Sps * Note that in this case, dvp is the child vnode, and we 23060786Sps * are looking up the parent vnode - exactly reverse from 23160786Sps * normal operation. Unlocking dvp requires some rather 23260786Sps * tricky unlock/relock dance to prevent mp from being freed; 23360786Sps * use vn_vget_ino_gen() which takes care of all that. 23460786Sps */ 23560786Sps error = vn_vget_ino_gen(dvp, autofs_vget_callback, 23660786Sps anp->an_parent, 0, vpp); 23760786Sps if (error != 0) { 23860786Sps AUTOFS_WARN("vn_vget_ino_gen() failed with error %d", 23960786Sps error); 24060786Sps return (error); 24160786Sps } 24260786Sps return (error); 24360786Sps } 24460786Sps 24560786Sps if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 24660786Sps vref(dvp); 24760786Sps *vpp = dvp; 24860786Sps 24960786Sps return (0); 25060786Sps } 25160786Sps 25260786Sps if (autofs_cached(anp, cnp->cn_nameptr, cnp->cn_namelen) == false && 25360786Sps autofs_ignore_thread(cnp->cn_thread) == false) { 25460786Sps error = autofs_trigger_vn(dvp, 25560786Sps cnp->cn_nameptr, cnp->cn_namelen, &newvp); 25660786Sps if (error != 0) 25760786Sps return (error); 25860786Sps 25960786Sps if (newvp != NULL) { 26060786Sps error = VOP_LOOKUP(newvp, ap->a_vpp, ap->a_cnp); 26160786Sps 26260786Sps /* 26360786Sps * Instead of figuring out whether our vnode should 26460786Sps * be locked or not given the error and cnp flags, 26560786Sps * just "copy" the lock status from vnode returned 26660786Sps * by mounted filesystem's VOP_LOOKUP(). Get rid 26760786Sps * of that new vnode afterwards. 26860786Sps */ 26960786Sps lock_flags = VOP_ISLOCKED(newvp); 27060786Sps if (lock_flags == 0) { 27160786Sps VOP_UNLOCK(dvp, 0); 27260786Sps vrele(newvp); 27360786Sps } else { 27460786Sps vput(newvp); 27560786Sps } 27660786Sps return (error); 27760786Sps } 27860786Sps } 27960786Sps 28060786Sps if (cnp->cn_nameiop == RENAME) 28160786Sps return (EOPNOTSUPP); 28260786Sps 28360786Sps AUTOFS_LOCK(amp); 28460786Sps error = autofs_node_find(anp, cnp->cn_nameptr, cnp->cn_namelen, &child); 28560786Sps if (error != 0) { 28660786Sps if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) { 28760786Sps AUTOFS_UNLOCK(amp); 28860786Sps return (EJUSTRETURN); 28960786Sps } 29060786Sps 29160786Sps AUTOFS_UNLOCK(amp); 29260786Sps return (ENOENT); 29360786Sps } 29460786Sps 29560786Sps /* 29660786Sps * XXX: Dropping the node here is ok, because we never remove nodes. 29760786Sps */ 29860786Sps AUTOFS_UNLOCK(amp); 29960786Sps 30060786Sps error = autofs_node_vn(child, mp, vpp); 30160786Sps if (error != 0) { 30260786Sps if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) 30360786Sps return (EJUSTRETURN); 30460786Sps 30560786Sps return (error); 30660786Sps } 30760786Sps 30860786Sps return (0); 30960786Sps} 31060786Sps 31160786Spsstatic int 31260786Spsautofs_mkdir(struct vop_mkdir_args *ap) 31360786Sps{ 31460786Sps struct vnode *vp; 31560786Sps struct autofs_node *anp; 31660786Sps struct autofs_mount *amp; 31760786Sps struct autofs_node *child; 31860786Sps int error; 31960786Sps 32060786Sps vp = ap->a_dvp; 32160786Sps anp = vp->v_data; 32260786Sps amp = VFSTOAUTOFS(vp->v_mount); 32360786Sps 32460786Sps /* 32560786Sps * Do not allow mkdir() if the calling thread is not 32660786Sps * automountd(8) descendant. 32760786Sps */ 32860786Sps if (autofs_ignore_thread(curthread) == false) 32960786Sps return (EPERM); 33060786Sps 33160786Sps AUTOFS_LOCK(amp); 33260786Sps error = autofs_node_new(anp, amp, ap->a_cnp->cn_nameptr, 33360786Sps ap->a_cnp->cn_namelen, &child); 33460786Sps if (error != 0) { 33560786Sps AUTOFS_UNLOCK(amp); 33660786Sps return (error); 33760786Sps } 33860786Sps AUTOFS_UNLOCK(amp); 33960786Sps 34060786Sps error = autofs_node_vn(child, vp->v_mount, ap->a_vpp); 34160786Sps 34260786Sps return (error); 34360786Sps} 34460786Sps 34560786Spsstatic int 34660786Spsautofs_readdir_one(struct uio *uio, const char *name, int fileno) 34760786Sps{ 34860786Sps struct dirent dirent; 34960786Sps int error, i; 35060786Sps 35160786Sps memset(&dirent, 0, sizeof(dirent)); 35260786Sps dirent.d_type = DT_DIR; 35360786Sps dirent.d_reclen = AUTOFS_DELEN; 35460786Sps dirent.d_fileno = fileno; 35560786Sps /* PFS_DELEN was picked to fit PFS_NAMLEN */ 35660786Sps for (i = 0; i < AUTOFS_NAMELEN - 1 && name[i] != '\0'; ++i) 35760786Sps dirent.d_name[i] = name[i]; 35860786Sps dirent.d_name[i] = 0; 35960786Sps dirent.d_namlen = i; 36060786Sps 36160786Sps error = uiomove(&dirent, AUTOFS_DELEN, uio); 36260786Sps return (error); 36360786Sps} 36460786Sps 36560786Spsstatic int 36660786Spsautofs_readdir(struct vop_readdir_args *ap) 36760786Sps{ 36860786Sps struct vnode *vp, *newvp; 36960786Sps struct autofs_mount *amp; 37060786Sps struct autofs_node *anp, *child; 37160786Sps struct uio *uio; 37260786Sps off_t offset; 37360786Sps int error, i, resid; 37460786Sps 37560786Sps vp = ap->a_vp; 37660786Sps amp = VFSTOAUTOFS(vp->v_mount); 37760786Sps anp = vp->v_data; 37860786Sps uio = ap->a_uio; 37960786Sps 38060786Sps KASSERT(vp->v_type == VDIR, ("!VDIR")); 38160786Sps 38260786Sps if (autofs_cached(anp, NULL, 0) == false && 38360786Sps autofs_ignore_thread(curthread) == false) { 38460786Sps error = autofs_trigger_vn(vp, "", 0, &newvp); 38560786Sps if (error != 0) 38660786Sps return (error); 38760786Sps 38860786Sps if (newvp != NULL) { 38960786Sps error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred, 39060786Sps ap->a_eofflag, ap->a_ncookies, ap->a_cookies); 39160786Sps vput(newvp); 39260786Sps return (error); 39360786Sps } 39460786Sps } 39560786Sps 39660786Sps /* only allow reading entire entries */ 39760786Sps offset = uio->uio_offset; 39860786Sps resid = uio->uio_resid; 39960786Sps if (offset < 0 || offset % AUTOFS_DELEN != 0 || 40060786Sps (resid && resid < AUTOFS_DELEN)) 40160786Sps return (EINVAL); 40260786Sps if (resid == 0) 40360786Sps return (0); 40460786Sps 40560786Sps if (ap->a_eofflag != NULL) 40660786Sps *ap->a_eofflag = TRUE; 40760786Sps 40860786Sps if (offset == 0 && resid >= AUTOFS_DELEN) { 40960786Sps error = autofs_readdir_one(uio, ".", anp->an_fileno); 41060786Sps if (error != 0) 41160786Sps return (error); 41260786Sps offset += AUTOFS_DELEN; 41360786Sps resid -= AUTOFS_DELEN; 41460786Sps } 41560786Sps 41660786Sps if (offset == AUTOFS_DELEN && resid >= AUTOFS_DELEN) { 41760786Sps if (anp->an_parent == NULL) { 418 /* 419 * XXX: Right? 420 */ 421 error = autofs_readdir_one(uio, "..", anp->an_fileno); 422 } else { 423 error = autofs_readdir_one(uio, "..", 424 anp->an_parent->an_fileno); 425 } 426 if (error != 0) 427 return (error); 428 offset += AUTOFS_DELEN; 429 resid -= AUTOFS_DELEN; 430 } 431 432 i = 2; /* Account for "." and "..". */ 433 AUTOFS_LOCK(amp); 434 TAILQ_FOREACH(child, &anp->an_children, an_next) { 435 if (resid < AUTOFS_DELEN) { 436 if (ap->a_eofflag != NULL) 437 *ap->a_eofflag = 0; 438 break; 439 } 440 441 /* 442 * Skip entries returned by previous call to getdents(). 443 */ 444 i++; 445 if (i * AUTOFS_DELEN <= offset) 446 continue; 447 448 error = autofs_readdir_one(uio, child->an_name, 449 child->an_fileno); 450 if (error != 0) { 451 AUTOFS_UNLOCK(amp); 452 return (error); 453 } 454 offset += AUTOFS_DELEN; 455 resid -= AUTOFS_DELEN; 456 } 457 458 AUTOFS_UNLOCK(amp); 459 return (0); 460} 461 462static int 463autofs_reclaim(struct vop_reclaim_args *ap) 464{ 465 struct vnode *vp = ap->a_vp; 466 struct autofs_node *anp = vp->v_data; 467 468 vp = ap->a_vp; 469 anp = vp->v_data; 470 471 /* 472 * We do not free autofs_node here; instead we are 473 * destroying them in autofs_node_delete(). 474 */ 475 sx_xlock(&anp->an_vnode_lock); 476 anp->an_vnode = NULL; 477 vp->v_data = NULL; 478 sx_xunlock(&anp->an_vnode_lock); 479 480 return (0); 481} 482 483struct vop_vector autofs_vnodeops = { 484 .vop_default = &default_vnodeops, 485 486 .vop_access = autofs_access, 487 .vop_lookup = autofs_lookup, 488 .vop_create = VOP_EOPNOTSUPP, 489 .vop_getattr = autofs_getattr, 490 .vop_link = VOP_EOPNOTSUPP, 491 .vop_mkdir = autofs_mkdir, 492 .vop_mknod = VOP_EOPNOTSUPP, 493 .vop_read = VOP_EOPNOTSUPP, 494 .vop_readdir = autofs_readdir, 495 .vop_remove = VOP_EOPNOTSUPP, 496 .vop_rename = VOP_EOPNOTSUPP, 497 .vop_rmdir = VOP_EOPNOTSUPP, 498 .vop_setattr = VOP_EOPNOTSUPP, 499 .vop_symlink = VOP_EOPNOTSUPP, 500 .vop_write = VOP_EOPNOTSUPP, 501 .vop_reclaim = autofs_reclaim, 502}; 503 504int 505autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp, 506 const char *name, int namelen, struct autofs_node **anpp) 507{ 508 struct autofs_node *anp; 509 510 if (parent != NULL) 511 AUTOFS_ASSERT_LOCKED(parent->an_mount); 512 513 anp = uma_zalloc(autofs_node_zone, M_WAITOK | M_ZERO); 514 if (namelen >= 0) 515 anp->an_name = strndup(name, namelen, M_AUTOFS); 516 else 517 anp->an_name = strdup(name, M_AUTOFS); 518 anp->an_fileno = atomic_fetchadd_int(&->am_last_fileno, 1); 519 callout_init(&anp->an_callout, 1); 520 /* 521 * The reason for SX_NOWITNESS here is that witness(4) 522 * cannot tell vnodes apart, so the following perfectly 523 * valid lock order... 524 * 525 * vnode lock A -> autofsvlk B -> vnode lock B 526 * 527 * ... gets reported as a LOR. 528 */ 529 sx_init_flags(&anp->an_vnode_lock, "autofsvlk", SX_NOWITNESS); 530 getnanotime(&anp->an_ctime); 531 anp->an_parent = parent; 532 anp->an_mount = amp; 533 if (parent != NULL) 534 TAILQ_INSERT_TAIL(&parent->an_children, anp, an_next); 535 TAILQ_INIT(&anp->an_children); 536 537 *anpp = anp; 538 return (0); 539} 540 541int 542autofs_node_find(struct autofs_node *parent, const char *name, 543 int namelen, struct autofs_node **anpp) 544{ 545 struct autofs_node *anp; 546 547 AUTOFS_ASSERT_LOCKED(parent->an_mount); 548 549 TAILQ_FOREACH(anp, &parent->an_children, an_next) { 550 if (namelen >= 0) { 551 if (strncmp(anp->an_name, name, namelen) != 0) 552 continue; 553 } else { 554 if (strcmp(anp->an_name, name) != 0) 555 continue; 556 } 557 558 if (anpp != NULL) 559 *anpp = anp; 560 return (0); 561 } 562 563 return (ENOENT); 564} 565 566void 567autofs_node_delete(struct autofs_node *anp) 568{ 569 struct autofs_node *parent; 570 571 AUTOFS_ASSERT_LOCKED(anp->an_mount); 572 KASSERT(TAILQ_EMPTY(&anp->an_children), ("have children")); 573 574 callout_drain(&anp->an_callout); 575 576 parent = anp->an_parent; 577 if (parent != NULL) 578 TAILQ_REMOVE(&parent->an_children, anp, an_next); 579 sx_destroy(&anp->an_vnode_lock); 580 free(anp->an_name, M_AUTOFS); 581 uma_zfree(autofs_node_zone, anp); 582} 583 584int 585autofs_node_vn(struct autofs_node *anp, struct mount *mp, struct vnode **vpp) 586{ 587 struct vnode *vp; 588 int error; 589 590 AUTOFS_ASSERT_UNLOCKED(anp->an_mount); 591 592 sx_xlock(&anp->an_vnode_lock); 593 594 vp = anp->an_vnode; 595 if (vp != NULL) { 596 error = vget(vp, LK_EXCLUSIVE | LK_RETRY, curthread); 597 if (error != 0) { 598 AUTOFS_WARN("vget failed with error %d", error); 599 sx_xunlock(&anp->an_vnode_lock); 600 return (error); 601 } 602 if (vp->v_iflag & VI_DOOMED) { 603 /* 604 * We got forcibly unmounted. 605 */ 606 AUTOFS_DEBUG("doomed vnode"); 607 sx_xunlock(&anp->an_vnode_lock); 608 vput(vp); 609 610 return (ENOENT); 611 } 612 613 *vpp = vp; 614 sx_xunlock(&anp->an_vnode_lock); 615 return (0); 616 } 617 618 error = getnewvnode("autofs", mp, &autofs_vnodeops, &vp); 619 if (error != 0) { 620 sx_xunlock(&anp->an_vnode_lock); 621 return (error); 622 } 623 624 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 625 if (error != 0) { 626 sx_xunlock(&anp->an_vnode_lock); 627 vdrop(vp); 628 return (error); 629 } 630 631 vp->v_type = VDIR; 632 if (anp->an_parent == NULL) 633 vp->v_vflag |= VV_ROOT; 634 vp->v_data = anp; 635 636 error = insmntque(vp, mp); 637 if (error != 0) { 638 AUTOFS_WARN("insmntque() failed with error %d", error); 639 sx_xunlock(&anp->an_vnode_lock); 640 return (error); 641 } 642 643 KASSERT(anp->an_vnode == NULL, ("lost race")); 644 anp->an_vnode = vp; 645 646 sx_xunlock(&anp->an_vnode_lock); 647 648 *vpp = vp; 649 return (0); 650} 651