autofs_vnops.c revision 308253
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: stable/11/sys/fs/autofs/autofs_vnops.c 308253 2016-11-03 14:40:34Z 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> 47297236Strasz#include <sys/tree.h> 48270096Strasz#include <sys/vnode.h> 49270096Strasz#include <machine/atomic.h> 50270096Strasz#include <vm/uma.h> 51270096Strasz 52270281Strasz#include <fs/autofs/autofs.h> 53270096Strasz 54270096Straszstatic int autofs_trigger_vn(struct vnode *vp, const char *path, 55270096Strasz int pathlen, struct vnode **newvp); 56270096Strasz 57270402Straszextern struct autofs_softc *autofs_softc; 58270402Strasz 59270096Straszstatic int 60270096Straszautofs_access(struct vop_access_args *ap) 61270096Strasz{ 62270096Strasz 63270096Strasz /* 64270096Strasz * Nothing to do here; the only kind of access control 65270096Strasz * needed is in autofs_mkdir(). 66270096Strasz */ 67270096Strasz 68270096Strasz return (0); 69270096Strasz} 70270096Strasz 71270096Straszstatic int 72270096Straszautofs_getattr(struct vop_getattr_args *ap) 73270096Strasz{ 74270096Strasz struct vnode *vp, *newvp; 75270096Strasz struct autofs_node *anp; 76270096Strasz struct mount *mp; 77270096Strasz struct vattr *vap; 78270096Strasz int error; 79270096Strasz 80270096Strasz vp = ap->a_vp; 81270096Strasz anp = vp->v_data; 82270096Strasz mp = vp->v_mount; 83270096Strasz vap = ap->a_vap; 84270096Strasz 85270096Strasz KASSERT(ap->a_vp->v_type == VDIR, ("!VDIR")); 86270096Strasz 87270096Strasz /* 88270096Strasz * The reason we must do this is that some tree-walking software, 89270096Strasz * namely fts(3), assumes that stat(".") results will not change 90270096Strasz * between chdir("subdir") and chdir(".."), and fails with ENOENT 91270096Strasz * otherwise. 92270096Strasz */ 93270096Strasz if (autofs_mount_on_stat && autofs_cached(anp, NULL, 0) == false && 94270096Strasz autofs_ignore_thread(curthread) == false) { 95270096Strasz error = autofs_trigger_vn(vp, "", 0, &newvp); 96270096Strasz if (error != 0) 97270096Strasz return (error); 98270096Strasz 99270096Strasz if (newvp != NULL) { 100270096Strasz error = VOP_GETATTR(newvp, ap->a_vap, 101270096Strasz ap->a_cred); 102270096Strasz vput(newvp); 103270096Strasz return (error); 104270096Strasz } 105270096Strasz } 106270096Strasz 107270096Strasz vap->va_type = VDIR; 108270096Strasz vap->va_mode = 0755; 109270096Strasz vap->va_nlink = 3; /* XXX */ 110270096Strasz vap->va_uid = 0; 111270096Strasz vap->va_gid = 0; 112270096Strasz vap->va_rdev = NODEV; 113270096Strasz vap->va_fsid = mp->mnt_stat.f_fsid.val[0]; 114270096Strasz vap->va_fileid = anp->an_fileno; 115296718Strasz vap->va_size = S_BLKSIZE; 116296718Strasz vap->va_blocksize = S_BLKSIZE; 117270096Strasz vap->va_mtime = anp->an_ctime; 118270096Strasz vap->va_atime = anp->an_ctime; 119270096Strasz vap->va_ctime = anp->an_ctime; 120270096Strasz vap->va_birthtime = anp->an_ctime; 121270096Strasz vap->va_gen = 0; 122270096Strasz vap->va_flags = 0; 123270096Strasz vap->va_rdev = 0; 124296718Strasz vap->va_bytes = S_BLKSIZE; 125270096Strasz vap->va_filerev = 0; 126270096Strasz vap->va_spare = 0; 127270096Strasz 128270096Strasz return (0); 129270096Strasz} 130270096Strasz 131270096Strasz/* 132270096Strasz * Unlock the vnode, request automountd(8) action, and then lock it back. 133270096Strasz * If anything got mounted on top of the vnode, return the new filesystem's 134270096Strasz * root vnode in 'newvp', locked. 135270096Strasz */ 136270096Straszstatic int 137270096Straszautofs_trigger_vn(struct vnode *vp, const char *path, int pathlen, 138270096Strasz struct vnode **newvp) 139270096Strasz{ 140270096Strasz struct autofs_node *anp; 141270096Strasz struct autofs_mount *amp; 142270096Strasz int error, lock_flags; 143270096Strasz 144270096Strasz anp = vp->v_data; 145270096Strasz amp = VFSTOAUTOFS(vp->v_mount); 146270096Strasz 147270096Strasz /* 148270096Strasz * Release the vnode lock, so that other operations, in partcular 149270096Strasz * mounting a filesystem on top of it, can proceed. Increase use 150270096Strasz * count, to prevent the vnode from being deallocated and to prevent 151270096Strasz * filesystem from being unmounted. 152270096Strasz */ 153270096Strasz lock_flags = VOP_ISLOCKED(vp); 154270096Strasz vref(vp); 155270096Strasz VOP_UNLOCK(vp, 0); 156270096Strasz 157270402Strasz sx_xlock(&autofs_softc->sc_lock); 158270096Strasz 159270096Strasz /* 160270096Strasz * XXX: Workaround for mounting the same thing multiple times; revisit. 161270096Strasz */ 162270096Strasz if (vp->v_mountedhere != NULL) { 163270096Strasz error = 0; 164270096Strasz goto mounted; 165270096Strasz } 166270096Strasz 167270096Strasz error = autofs_trigger(anp, path, pathlen); 168270096Straszmounted: 169270402Strasz sx_xunlock(&autofs_softc->sc_lock); 170270096Strasz vn_lock(vp, lock_flags | LK_RETRY); 171270096Strasz vunref(vp); 172270096Strasz if ((vp->v_iflag & VI_DOOMED) != 0) { 173270096Strasz AUTOFS_DEBUG("VI_DOOMED"); 174270096Strasz return (ENOENT); 175270096Strasz } 176270096Strasz 177270096Strasz if (error != 0) 178270096Strasz return (error); 179270096Strasz 180270096Strasz if (vp->v_mountedhere == NULL) { 181270096Strasz *newvp = NULL; 182270096Strasz return (0); 183270096Strasz } else { 184270096Strasz /* 185270096Strasz * If the operation that succeeded was mount, then mark 186270096Strasz * the node as non-cached. Otherwise, if someone unmounts 187270096Strasz * the filesystem before the cache times out, we will fail 188270096Strasz * to trigger. 189270096Strasz */ 190270096Strasz anp->an_cached = false; 191270096Strasz } 192270096Strasz 193270096Strasz error = VFS_ROOT(vp->v_mountedhere, lock_flags, newvp); 194270096Strasz if (error != 0) { 195270096Strasz AUTOFS_WARN("VFS_ROOT() failed with error %d", error); 196270096Strasz return (error); 197270096Strasz } 198270096Strasz 199270096Strasz return (0); 200270096Strasz} 201270096Strasz 202270096Straszstatic int 203272512Straszautofs_vget_callback(struct mount *mp, void *arg, int flags, 204270207Strasz struct vnode **vpp) 205270207Strasz{ 206270207Strasz 207270207Strasz 208272512Strasz return (autofs_node_vn(arg, mp, flags, vpp)); 209270207Strasz} 210270207Strasz 211270207Straszstatic int 212270096Straszautofs_lookup(struct vop_lookup_args *ap) 213270096Strasz{ 214270096Strasz struct vnode *dvp, *newvp, **vpp; 215270096Strasz struct mount *mp; 216270096Strasz struct autofs_mount *amp; 217270096Strasz struct autofs_node *anp, *child; 218270096Strasz struct componentname *cnp; 219296715Strasz int error; 220270096Strasz 221270096Strasz dvp = ap->a_dvp; 222270096Strasz vpp = ap->a_vpp; 223270096Strasz mp = dvp->v_mount; 224270096Strasz amp = VFSTOAUTOFS(mp); 225270096Strasz anp = dvp->v_data; 226270096Strasz cnp = ap->a_cnp; 227270096Strasz 228270096Strasz if (cnp->cn_flags & ISDOTDOT) { 229270096Strasz KASSERT(anp->an_parent != NULL, ("NULL parent")); 230270096Strasz /* 231270207Strasz * Note that in this case, dvp is the child vnode, and we 232270207Strasz * are looking up the parent vnode - exactly reverse from 233270207Strasz * normal operation. Unlocking dvp requires some rather 234270207Strasz * tricky unlock/relock dance to prevent mp from being freed; 235270207Strasz * use vn_vget_ino_gen() which takes care of all that. 236270096Strasz */ 237270207Strasz error = vn_vget_ino_gen(dvp, autofs_vget_callback, 238272512Strasz anp->an_parent, cnp->cn_lkflags, vpp); 239270096Strasz if (error != 0) { 240270207Strasz AUTOFS_WARN("vn_vget_ino_gen() failed with error %d", 241270096Strasz error); 242270207Strasz return (error); 243270096Strasz } 244270096Strasz return (error); 245270096Strasz } 246270096Strasz 247270096Strasz if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 248270096Strasz vref(dvp); 249270096Strasz *vpp = dvp; 250270096Strasz 251270096Strasz return (0); 252270096Strasz } 253270096Strasz 254270096Strasz if (autofs_cached(anp, cnp->cn_nameptr, cnp->cn_namelen) == false && 255270096Strasz autofs_ignore_thread(cnp->cn_thread) == false) { 256270096Strasz error = autofs_trigger_vn(dvp, 257270096Strasz cnp->cn_nameptr, cnp->cn_namelen, &newvp); 258270096Strasz if (error != 0) 259270096Strasz return (error); 260270096Strasz 261270096Strasz if (newvp != NULL) { 262270096Strasz /* 263296715Strasz * The target filesystem got automounted. 264296715Strasz * Let the lookup(9) go around with the same 265296715Strasz * path component. 266270096Strasz */ 267296715Strasz vput(newvp); 268296715Strasz return (ERELOOKUP); 269270096Strasz } 270270096Strasz } 271270096Strasz 272272470Strasz AUTOFS_SLOCK(amp); 273270096Strasz error = autofs_node_find(anp, cnp->cn_nameptr, cnp->cn_namelen, &child); 274270096Strasz if (error != 0) { 275270096Strasz if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) { 276272470Strasz AUTOFS_SUNLOCK(amp); 277270096Strasz return (EJUSTRETURN); 278270096Strasz } 279270096Strasz 280272470Strasz AUTOFS_SUNLOCK(amp); 281270096Strasz return (ENOENT); 282270096Strasz } 283270096Strasz 284270096Strasz /* 285270096Strasz * XXX: Dropping the node here is ok, because we never remove nodes. 286270096Strasz */ 287272470Strasz AUTOFS_SUNLOCK(amp); 288270096Strasz 289272512Strasz error = autofs_node_vn(child, mp, cnp->cn_lkflags, vpp); 290270096Strasz if (error != 0) { 291270096Strasz if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) 292270096Strasz return (EJUSTRETURN); 293270096Strasz 294270096Strasz return (error); 295270096Strasz } 296270096Strasz 297270096Strasz return (0); 298270096Strasz} 299270096Strasz 300270096Straszstatic int 301270096Straszautofs_mkdir(struct vop_mkdir_args *ap) 302270096Strasz{ 303270096Strasz struct vnode *vp; 304270096Strasz struct autofs_node *anp; 305270096Strasz struct autofs_mount *amp; 306270096Strasz struct autofs_node *child; 307270096Strasz int error; 308270096Strasz 309270096Strasz vp = ap->a_dvp; 310270096Strasz anp = vp->v_data; 311270096Strasz amp = VFSTOAUTOFS(vp->v_mount); 312270096Strasz 313270096Strasz /* 314270096Strasz * Do not allow mkdir() if the calling thread is not 315270096Strasz * automountd(8) descendant. 316270096Strasz */ 317270096Strasz if (autofs_ignore_thread(curthread) == false) 318270096Strasz return (EPERM); 319270096Strasz 320272470Strasz AUTOFS_XLOCK(amp); 321270096Strasz error = autofs_node_new(anp, amp, ap->a_cnp->cn_nameptr, 322270096Strasz ap->a_cnp->cn_namelen, &child); 323270096Strasz if (error != 0) { 324272470Strasz AUTOFS_XUNLOCK(amp); 325270096Strasz return (error); 326270096Strasz } 327272470Strasz AUTOFS_XUNLOCK(amp); 328270096Strasz 329272512Strasz error = autofs_node_vn(child, vp->v_mount, LK_EXCLUSIVE, ap->a_vpp); 330270096Strasz 331270096Strasz return (error); 332270096Strasz} 333270096Strasz 334308253Straszstatic int 335308253Straszautofs_print(struct vop_print_args *ap) 336308253Strasz{ 337308253Strasz struct vnode *vp; 338308253Strasz struct autofs_node *anp; 339308253Strasz 340308253Strasz vp = ap->a_vp; 341308253Strasz anp = vp->v_data; 342308253Strasz 343308253Strasz printf(" name \"%s\", fileno %d, cached %d, wildcards %d\n", 344308253Strasz anp->an_name, anp->an_fileno, anp->an_cached, anp->an_wildcards); 345308253Strasz 346308253Strasz return (0); 347308253Strasz} 348308253Strasz 349296798Strasz/* 350296798Strasz * Write out a single 'struct dirent', based on 'name' and 'fileno' arguments. 351296798Strasz */ 352270096Straszstatic int 353296798Straszautofs_readdir_one(struct uio *uio, const char *name, int fileno, 354296798Strasz size_t *reclenp) 355270096Strasz{ 356270096Strasz struct dirent dirent; 357296798Strasz size_t namlen, padded_namlen, reclen; 358296798Strasz int error; 359270096Strasz 360296798Strasz namlen = strlen(name); 361296798Strasz padded_namlen = roundup2(namlen + 1, __alignof(struct dirent)); 362296798Strasz KASSERT(padded_namlen <= MAXNAMLEN, ("%zd > MAXNAMLEN", padded_namlen)); 363296798Strasz reclen = offsetof(struct dirent, d_name) + padded_namlen; 364296798Strasz 365296798Strasz if (reclenp != NULL) 366296798Strasz *reclenp = reclen; 367296798Strasz 368296798Strasz if (uio == NULL) 369296798Strasz return (0); 370296798Strasz 371296798Strasz if (uio->uio_resid < reclen) 372296798Strasz return (EINVAL); 373296798Strasz 374296798Strasz dirent.d_fileno = fileno; 375296798Strasz dirent.d_reclen = reclen; 376270096Strasz dirent.d_type = DT_DIR; 377296798Strasz dirent.d_namlen = namlen; 378296798Strasz memcpy(dirent.d_name, name, namlen); 379296798Strasz memset(dirent.d_name + namlen, 0, padded_namlen - namlen); 380296798Strasz error = uiomove(&dirent, reclen, uio); 381270096Strasz 382270096Strasz return (error); 383270096Strasz} 384270096Strasz 385296798Straszstatic size_t 386296798Straszautofs_dirent_reclen(const char *name) 387296798Strasz{ 388296798Strasz size_t reclen; 389296798Strasz 390296937Strasz (void)autofs_readdir_one(NULL, name, -1, &reclen); 391296798Strasz 392296798Strasz return (reclen); 393296798Strasz} 394296798Strasz 395270096Straszstatic int 396270096Straszautofs_readdir(struct vop_readdir_args *ap) 397270096Strasz{ 398270096Strasz struct vnode *vp, *newvp; 399270096Strasz struct autofs_mount *amp; 400270096Strasz struct autofs_node *anp, *child; 401270096Strasz struct uio *uio; 402296798Strasz size_t reclen, reclens; 403296798Strasz ssize_t initial_resid; 404296798Strasz int error; 405270096Strasz 406270096Strasz vp = ap->a_vp; 407270096Strasz amp = VFSTOAUTOFS(vp->v_mount); 408270096Strasz anp = vp->v_data; 409270096Strasz uio = ap->a_uio; 410296798Strasz initial_resid = ap->a_uio->uio_resid; 411270096Strasz 412270096Strasz KASSERT(vp->v_type == VDIR, ("!VDIR")); 413270096Strasz 414270096Strasz if (autofs_cached(anp, NULL, 0) == false && 415270096Strasz autofs_ignore_thread(curthread) == false) { 416270096Strasz error = autofs_trigger_vn(vp, "", 0, &newvp); 417270096Strasz if (error != 0) 418270096Strasz return (error); 419270096Strasz 420270096Strasz if (newvp != NULL) { 421270096Strasz error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred, 422270096Strasz ap->a_eofflag, ap->a_ncookies, ap->a_cookies); 423270096Strasz vput(newvp); 424270096Strasz return (error); 425270096Strasz } 426270096Strasz } 427270096Strasz 428296798Strasz if (uio->uio_offset < 0) 429270096Strasz return (EINVAL); 430270096Strasz 431270096Strasz if (ap->a_eofflag != NULL) 432296798Strasz *ap->a_eofflag = FALSE; 433270096Strasz 434296798Strasz /* 435296798Strasz * Write out the directory entry for ".". This is conditional 436296798Strasz * on the current offset into the directory; same applies to the 437296798Strasz * other two cases below. 438296798Strasz */ 439296798Strasz if (uio->uio_offset == 0) { 440296798Strasz error = autofs_readdir_one(uio, ".", anp->an_fileno, &reclen); 441270096Strasz if (error != 0) 442296798Strasz goto out; 443270096Strasz } 444296798Strasz reclens = autofs_dirent_reclen("."); 445270096Strasz 446296798Strasz /* 447296798Strasz * Write out the directory entry for "..". 448296798Strasz */ 449296798Strasz if (uio->uio_offset <= reclens) { 450296798Strasz if (uio->uio_offset != reclens) 451296798Strasz return (EINVAL); 452270096Strasz if (anp->an_parent == NULL) { 453296798Strasz error = autofs_readdir_one(uio, "..", 454296798Strasz anp->an_fileno, &reclen); 455270096Strasz } else { 456270096Strasz error = autofs_readdir_one(uio, "..", 457296798Strasz anp->an_parent->an_fileno, &reclen); 458270096Strasz } 459270096Strasz if (error != 0) 460296798Strasz goto out; 461270096Strasz } 462270096Strasz 463296798Strasz reclens += autofs_dirent_reclen(".."); 464296798Strasz 465296798Strasz /* 466296798Strasz * Write out the directory entries for subdirectories. 467296798Strasz */ 468272470Strasz AUTOFS_SLOCK(amp); 469297236Strasz RB_FOREACH(child, autofs_node_tree, &anp->an_children) { 470296798Strasz /* 471296798Strasz * Check the offset to skip entries returned by previous 472296798Strasz * calls to getdents(). 473296798Strasz */ 474296798Strasz if (uio->uio_offset > reclens) { 475296798Strasz reclens += autofs_dirent_reclen(child->an_name); 476296798Strasz continue; 477270096Strasz } 478270096Strasz 479270096Strasz /* 480296798Strasz * Prevent seeking into the middle of dirent. 481270096Strasz */ 482296798Strasz if (uio->uio_offset != reclens) { 483296798Strasz AUTOFS_SUNLOCK(amp); 484296798Strasz return (EINVAL); 485296798Strasz } 486270096Strasz 487270096Strasz error = autofs_readdir_one(uio, child->an_name, 488296798Strasz child->an_fileno, &reclen); 489296798Strasz reclens += reclen; 490270096Strasz if (error != 0) { 491272470Strasz AUTOFS_SUNLOCK(amp); 492296798Strasz goto out; 493270096Strasz } 494270096Strasz } 495296798Strasz AUTOFS_SUNLOCK(amp); 496270096Strasz 497296798Strasz if (ap->a_eofflag != NULL) 498296798Strasz *ap->a_eofflag = TRUE; 499296798Strasz 500270096Strasz return (0); 501296798Strasz 502296798Straszout: 503296798Strasz /* 504296798Strasz * Return error if the initial buffer was too small to do anything. 505296798Strasz */ 506296798Strasz if (uio->uio_resid == initial_resid) 507296798Strasz return (error); 508296798Strasz 509296798Strasz /* 510296798Strasz * Don't return an error if we managed to copy out some entries. 511296798Strasz */ 512296798Strasz if (uio->uio_resid < reclen) 513296798Strasz return (0); 514296798Strasz 515296798Strasz return (error); 516270096Strasz} 517270096Strasz 518270096Straszstatic int 519270096Straszautofs_reclaim(struct vop_reclaim_args *ap) 520270096Strasz{ 521272836Strasz struct vnode *vp; 522272836Strasz struct autofs_node *anp; 523270096Strasz 524270096Strasz vp = ap->a_vp; 525270096Strasz anp = vp->v_data; 526270096Strasz 527270096Strasz /* 528270096Strasz * We do not free autofs_node here; instead we are 529270096Strasz * destroying them in autofs_node_delete(). 530270096Strasz */ 531270096Strasz sx_xlock(&anp->an_vnode_lock); 532270096Strasz anp->an_vnode = NULL; 533270096Strasz vp->v_data = NULL; 534270096Strasz sx_xunlock(&anp->an_vnode_lock); 535270096Strasz 536270096Strasz return (0); 537270096Strasz} 538270096Strasz 539270096Straszstruct vop_vector autofs_vnodeops = { 540270096Strasz .vop_default = &default_vnodeops, 541270096Strasz 542270096Strasz .vop_access = autofs_access, 543270096Strasz .vop_lookup = autofs_lookup, 544270096Strasz .vop_create = VOP_EOPNOTSUPP, 545270096Strasz .vop_getattr = autofs_getattr, 546270096Strasz .vop_link = VOP_EOPNOTSUPP, 547270096Strasz .vop_mkdir = autofs_mkdir, 548270096Strasz .vop_mknod = VOP_EOPNOTSUPP, 549308253Strasz .vop_print = autofs_print, 550270096Strasz .vop_read = VOP_EOPNOTSUPP, 551270096Strasz .vop_readdir = autofs_readdir, 552270096Strasz .vop_remove = VOP_EOPNOTSUPP, 553270096Strasz .vop_rename = VOP_EOPNOTSUPP, 554270096Strasz .vop_rmdir = VOP_EOPNOTSUPP, 555270096Strasz .vop_setattr = VOP_EOPNOTSUPP, 556270096Strasz .vop_symlink = VOP_EOPNOTSUPP, 557270096Strasz .vop_write = VOP_EOPNOTSUPP, 558270096Strasz .vop_reclaim = autofs_reclaim, 559270096Strasz}; 560270096Strasz 561270096Straszint 562270096Straszautofs_node_new(struct autofs_node *parent, struct autofs_mount *amp, 563270096Strasz const char *name, int namelen, struct autofs_node **anpp) 564270096Strasz{ 565270096Strasz struct autofs_node *anp; 566270096Strasz 567272931Strasz if (parent != NULL) { 568272470Strasz AUTOFS_ASSERT_XLOCKED(parent->an_mount); 569270096Strasz 570272931Strasz KASSERT(autofs_node_find(parent, name, namelen, NULL) == ENOENT, 571272931Strasz ("node \"%s\" already exists", name)); 572272931Strasz } 573272931Strasz 574270096Strasz anp = uma_zalloc(autofs_node_zone, M_WAITOK | M_ZERO); 575270096Strasz if (namelen >= 0) 576270096Strasz anp->an_name = strndup(name, namelen, M_AUTOFS); 577270096Strasz else 578270096Strasz anp->an_name = strdup(name, M_AUTOFS); 579270096Strasz anp->an_fileno = atomic_fetchadd_int(&->am_last_fileno, 1); 580270096Strasz callout_init(&anp->an_callout, 1); 581270096Strasz /* 582270096Strasz * The reason for SX_NOWITNESS here is that witness(4) 583270096Strasz * cannot tell vnodes apart, so the following perfectly 584270096Strasz * valid lock order... 585270096Strasz * 586270096Strasz * vnode lock A -> autofsvlk B -> vnode lock B 587270096Strasz * 588270096Strasz * ... gets reported as a LOR. 589270096Strasz */ 590270096Strasz sx_init_flags(&anp->an_vnode_lock, "autofsvlk", SX_NOWITNESS); 591270096Strasz getnanotime(&anp->an_ctime); 592270096Strasz anp->an_parent = parent; 593270096Strasz anp->an_mount = amp; 594270096Strasz if (parent != NULL) 595297236Strasz RB_INSERT(autofs_node_tree, &parent->an_children, anp); 596297236Strasz RB_INIT(&anp->an_children); 597270096Strasz 598270096Strasz *anpp = anp; 599270096Strasz return (0); 600270096Strasz} 601270096Strasz 602270096Straszint 603270096Straszautofs_node_find(struct autofs_node *parent, const char *name, 604270096Strasz int namelen, struct autofs_node **anpp) 605270096Strasz{ 606297236Strasz struct autofs_node *anp, find; 607297236Strasz int error; 608270096Strasz 609270096Strasz AUTOFS_ASSERT_LOCKED(parent->an_mount); 610270096Strasz 611297236Strasz if (namelen >= 0) 612297236Strasz find.an_name = strndup(name, namelen, M_AUTOFS); 613297236Strasz else 614297236Strasz find.an_name = strdup(name, M_AUTOFS); 615270096Strasz 616297236Strasz anp = RB_FIND(autofs_node_tree, &parent->an_children, &find); 617297236Strasz if (anp != NULL) { 618297236Strasz error = 0; 619270096Strasz if (anpp != NULL) 620270096Strasz *anpp = anp; 621297236Strasz } else { 622297236Strasz error = ENOENT; 623270096Strasz } 624270096Strasz 625297236Strasz free(find.an_name, M_AUTOFS); 626297236Strasz 627297236Strasz return (error); 628270096Strasz} 629270096Strasz 630270096Straszvoid 631270096Straszautofs_node_delete(struct autofs_node *anp) 632270096Strasz{ 633270096Strasz struct autofs_node *parent; 634270096Strasz 635272470Strasz AUTOFS_ASSERT_XLOCKED(anp->an_mount); 636297236Strasz KASSERT(RB_EMPTY(&anp->an_children), ("have children")); 637270096Strasz 638270096Strasz callout_drain(&anp->an_callout); 639270096Strasz 640270096Strasz parent = anp->an_parent; 641270096Strasz if (parent != NULL) 642297236Strasz RB_REMOVE(autofs_node_tree, &parent->an_children, anp); 643270096Strasz sx_destroy(&anp->an_vnode_lock); 644270096Strasz free(anp->an_name, M_AUTOFS); 645270096Strasz uma_zfree(autofs_node_zone, anp); 646270096Strasz} 647270096Strasz 648270096Straszint 649272512Straszautofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags, 650272512Strasz struct vnode **vpp) 651270096Strasz{ 652270096Strasz struct vnode *vp; 653270096Strasz int error; 654270096Strasz 655270096Strasz AUTOFS_ASSERT_UNLOCKED(anp->an_mount); 656270096Strasz 657270096Strasz sx_xlock(&anp->an_vnode_lock); 658270096Strasz 659270096Strasz vp = anp->an_vnode; 660270096Strasz if (vp != NULL) { 661272512Strasz error = vget(vp, flags | LK_RETRY, curthread); 662270096Strasz if (error != 0) { 663270096Strasz AUTOFS_WARN("vget failed with error %d", error); 664270096Strasz sx_xunlock(&anp->an_vnode_lock); 665270096Strasz return (error); 666270096Strasz } 667270096Strasz if (vp->v_iflag & VI_DOOMED) { 668270096Strasz /* 669270096Strasz * We got forcibly unmounted. 670270096Strasz */ 671270096Strasz AUTOFS_DEBUG("doomed vnode"); 672270096Strasz sx_xunlock(&anp->an_vnode_lock); 673270096Strasz vput(vp); 674270096Strasz 675270096Strasz return (ENOENT); 676270096Strasz } 677270096Strasz 678270096Strasz *vpp = vp; 679270096Strasz sx_xunlock(&anp->an_vnode_lock); 680270096Strasz return (0); 681270096Strasz } 682270096Strasz 683270096Strasz error = getnewvnode("autofs", mp, &autofs_vnodeops, &vp); 684270096Strasz if (error != 0) { 685270096Strasz sx_xunlock(&anp->an_vnode_lock); 686270096Strasz return (error); 687270096Strasz } 688270096Strasz 689270096Strasz error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 690270096Strasz if (error != 0) { 691270096Strasz sx_xunlock(&anp->an_vnode_lock); 692270096Strasz vdrop(vp); 693270096Strasz return (error); 694270096Strasz } 695270096Strasz 696270096Strasz vp->v_type = VDIR; 697270096Strasz if (anp->an_parent == NULL) 698270096Strasz vp->v_vflag |= VV_ROOT; 699270096Strasz vp->v_data = anp; 700270096Strasz 701272512Strasz VN_LOCK_ASHARE(vp); 702272512Strasz 703270096Strasz error = insmntque(vp, mp); 704270096Strasz if (error != 0) { 705300047Strasz AUTOFS_DEBUG("insmntque() failed with error %d", error); 706270096Strasz sx_xunlock(&anp->an_vnode_lock); 707270096Strasz return (error); 708270096Strasz } 709270096Strasz 710270096Strasz KASSERT(anp->an_vnode == NULL, ("lost race")); 711270096Strasz anp->an_vnode = vp; 712270096Strasz 713270096Strasz sx_xunlock(&anp->an_vnode_lock); 714270096Strasz 715270096Strasz *vpp = vp; 716270096Strasz return (0); 717270096Strasz} 718