1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2014 The FreeBSD Foundation 5 * 6 * This software was developed by Edward Tomasz Napierala under sponsorship 7 * from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 */ 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/condvar.h> 36 #include <sys/dirent.h> 37 #include <sys/fcntl.h> 38 #include <sys/lock.h> 39 #include <sys/mount.h> 40 #include <sys/mutex.h> 41 #include <sys/namei.h> 42 #include <sys/signalvar.h> 43 #include <sys/stat.h> 44 #include <sys/taskqueue.h> 45 #include <sys/tree.h> 46 #include <sys/vnode.h> 47 #include <machine/atomic.h> 48 #include <vm/uma.h> 49 50 #include <fs/autofs/autofs.h> 51 52 static int autofs_trigger_vn(struct vnode *vp, const char *path, 53 int pathlen, struct vnode **newvp); 54 55 extern struct autofs_softc *autofs_softc; 56 57 static int 58 autofs_access(struct vop_access_args *ap) 59 { 60 61 /* 62 * Nothing to do here; the only kind of access control 63 * needed is in autofs_mkdir(). 64 */ 65 66 return (0); 67 } 68 69 static int 70 autofs_getattr(struct vop_getattr_args *ap) 71 { 72 struct vnode *vp, *newvp; 73 struct autofs_node *anp; 74 struct mount *mp; 75 struct vattr *vap; 76 int error; 77 78 vp = ap->a_vp; 79 anp = vp->v_data; 80 mp = vp->v_mount; 81 vap = ap->a_vap; 82 83 KASSERT(ap->a_vp->v_type == VDIR, ("!VDIR")); 84 85 /* 86 * The reason we must do this is that some tree-walking software, 87 * namely fts(3), assumes that stat(".") results will not change 88 * between chdir("subdir") and chdir(".."), and fails with ENOENT 89 * otherwise. 90 */ 91 if (autofs_mount_on_stat && autofs_cached(anp, NULL, 0) == false && 92 autofs_ignore_thread(curthread) == false) { 93 error = autofs_trigger_vn(vp, "", 0, &newvp); 94 if (error != 0) 95 return (error); 96 97 if (newvp != NULL) { 98 error = VOP_GETATTR(newvp, ap->a_vap, 99 ap->a_cred); 100 vput(newvp); 101 return (error); 102 } 103 } 104 105 vap->va_type = VDIR; 106 vap->va_mode = 0755; 107 vap->va_nlink = 3; /* XXX */ 108 vap->va_uid = 0; 109 vap->va_gid = 0; 110 vap->va_rdev = NODEV; 111 vap->va_fsid = mp->mnt_stat.f_fsid.val[0]; 112 vap->va_fileid = anp->an_fileno; 113 vap->va_size = S_BLKSIZE; 114 vap->va_blocksize = S_BLKSIZE; 115 vap->va_mtime = anp->an_ctime; 116 vap->va_atime = anp->an_ctime; 117 vap->va_ctime = anp->an_ctime; 118 vap->va_birthtime = anp->an_ctime; 119 vap->va_gen = 0; 120 vap->va_flags = 0; 121 vap->va_rdev = 0; 122 vap->va_bytes = S_BLKSIZE; 123 vap->va_filerev = 0; 124 vap->va_spare = 0; 125 126 return (0); 127 } 128 129 /* 130 * Unlock the vnode, request automountd(8) action, and then lock it back. 131 * If anything got mounted on top of the vnode, return the new filesystem's 132 * root vnode in 'newvp', locked. 133 */ 134 static int 135 autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen, 136 struct vnode **newvp) 137 { 138 struct autofs_node *anp; 139 int error, lock_flags; 140 141 anp = vp->v_data; 142 143 /* 144 * Release the vnode lock, so that other operations, in partcular 145 * mounting a filesystem on top of it, can proceed. Increase use 146 * count, to prevent the vnode from being deallocated and to prevent 147 * filesystem from being unmounted. 148 */ 149 lock_flags = VOP_ISLOCKED(vp); 150 vref(vp); 151 VOP_UNLOCK(vp); 152 153 sx_xlock(&autofs_softc->sc_lock); 154 155 /* 156 * XXX: Workaround for mounting the same thing multiple times; revisit. 157 */ 158 if (vp->v_mountedhere != NULL) { 159 error = 0; 160 goto mounted; 161 } 162 163 error = autofs_trigger(anp, path, pathlen); 164 mounted: 165 sx_xunlock(&autofs_softc->sc_lock); 166 vn_lock(vp, lock_flags | LK_RETRY); 167 vunref(vp); 168 if (VN_IS_DOOMED(vp)) { 169 AUTOFS_DEBUG("VIRF_DOOMED"); 170 return (ENOENT); 171 } 172 173 if (error != 0) 174 return (error); 175 176 if (vp->v_mountedhere == NULL) { 177 *newvp = NULL; 178 return (0); 179 } else { 180 /* 181 * If the operation that succeeded was mount, then mark 182 * the node as non-cached. Otherwise, if someone unmounts 183 * the filesystem before the cache times out, we will fail 184 * to trigger. 185 */ 186 anp->an_cached = false; 187 } 188 189 error = VFS_ROOT(vp->v_mountedhere, lock_flags, newvp); 190 if (error != 0) { 191 AUTOFS_WARN("VFS_ROOT() failed with error %d", error); 192 return (error); 193 } 194 195 return (0); 196 } 197 198 static int 199 autofs_vget_callback(struct mount *mp, void *arg, int flags, 200 struct vnode **vpp) 201 { 202 203 return (autofs_node_vn(arg, mp, flags, vpp)); 204 } 205 206 static int 207 autofs_lookup(struct vop_lookup_args *ap) 208 { 209 struct vnode *dvp, *newvp, **vpp; 210 struct mount *mp; 211 struct autofs_mount *amp; 212 struct autofs_node *anp, *child; 213 struct componentname *cnp; 214 int error; 215 216 dvp = ap->a_dvp; 217 vpp = ap->a_vpp; 218 mp = dvp->v_mount; 219 amp = VFSTOAUTOFS(mp); 220 anp = dvp->v_data; 221 cnp = ap->a_cnp; 222 223 if (cnp->cn_flags & ISDOTDOT) { 224 KASSERT(anp->an_parent != NULL, ("NULL parent")); 225 /* 226 * Note that in this case, dvp is the child vnode, and we 227 * are looking up the parent vnode - exactly reverse from 228 * normal operation. Unlocking dvp requires some rather 229 * tricky unlock/relock dance to prevent mp from being freed; 230 * use vn_vget_ino_gen() which takes care of all that. 231 */ 232 error = vn_vget_ino_gen(dvp, autofs_vget_callback, 233 anp->an_parent, cnp->cn_lkflags, vpp); 234 if (error != 0) { 235 AUTOFS_WARN("vn_vget_ino_gen() failed with error %d", 236 error); 237 return (error); 238 } 239 return (error); 240 } 241 242 if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 243 vref(dvp); 244 *vpp = dvp; 245 246 return (0); 247 } 248 249 if (autofs_cached(anp, cnp->cn_nameptr, cnp->cn_namelen) == false && 250 autofs_ignore_thread(curthread) == false) { 251 error = autofs_trigger_vn(dvp, 252 cnp->cn_nameptr, cnp->cn_namelen, &newvp); 253 if (error != 0) 254 return (error); 255 256 if (newvp != NULL) { 257 /* 258 * The target filesystem got automounted. 259 * Let the lookup(9) go around with the same 260 * path component. 261 */ 262 vput(newvp); 263 return (ERELOOKUP); 264 } 265 } 266 267 AUTOFS_SLOCK(amp); 268 error = autofs_node_find(anp, cnp->cn_nameptr, cnp->cn_namelen, &child); 269 if (error != 0) { 270 if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) { 271 AUTOFS_SUNLOCK(amp); 272 return (EJUSTRETURN); 273 } 274 275 AUTOFS_SUNLOCK(amp); 276 return (ENOENT); 277 } 278 279 /* 280 * XXX: Dropping the node here is ok, because we never remove nodes. 281 */ 282 AUTOFS_SUNLOCK(amp); 283 284 error = autofs_node_vn(child, mp, cnp->cn_lkflags, vpp); 285 if (error != 0) { 286 if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) 287 return (EJUSTRETURN); 288 289 return (error); 290 } 291 292 return (0); 293 } 294 295 static int 296 autofs_mkdir(struct vop_mkdir_args *ap) 297 { 298 struct vnode *vp; 299 struct autofs_node *anp; 300 struct autofs_mount *amp; 301 struct autofs_node *child; 302 int error; 303 304 vp = ap->a_dvp; 305 anp = vp->v_data; 306 amp = VFSTOAUTOFS(vp->v_mount); 307 308 /* 309 * Do not allow mkdir() if the calling thread is not 310 * automountd(8) descendant. 311 */ 312 if (autofs_ignore_thread(curthread) == false) 313 return (EPERM); 314 315 AUTOFS_XLOCK(amp); 316 error = autofs_node_new(anp, amp, ap->a_cnp->cn_nameptr, 317 ap->a_cnp->cn_namelen, &child); 318 if (error != 0) { 319 AUTOFS_XUNLOCK(amp); 320 return (error); 321 } 322 AUTOFS_XUNLOCK(amp); 323 324 error = autofs_node_vn(child, vp->v_mount, LK_EXCLUSIVE, ap->a_vpp); 325 326 return (error); 327 } 328 329 static int 330 autofs_print(struct vop_print_args *ap) 331 { 332 struct vnode *vp; 333 struct autofs_node *anp; 334 335 vp = ap->a_vp; 336 anp = vp->v_data; 337 338 printf(" name \"%s\", fileno %d, cached %d, wildcards %d\n", 339 anp->an_name, anp->an_fileno, anp->an_cached, anp->an_wildcards); 340 341 return (0); 342 } 343 344 /* 345 * Write out a single 'struct dirent', based on 'name' and 'fileno' arguments. 346 */ 347 static int 348 autofs_readdir_one(struct uio *uio, const char *name, int fileno, 349 size_t *reclenp) 350 { 351 struct dirent dirent; 352 size_t namlen, reclen; 353 int error; 354 355 namlen = strlen(name); 356 reclen = _GENERIC_DIRLEN(namlen); 357 if (reclenp != NULL) 358 *reclenp = reclen; 359 360 if (uio == NULL) 361 return (0); 362 363 if (uio->uio_resid < reclen) 364 return (EINVAL); 365 366 dirent.d_fileno = fileno; 367 dirent.d_off = uio->uio_offset + reclen; 368 dirent.d_reclen = reclen; 369 dirent.d_type = DT_DIR; 370 dirent.d_namlen = namlen; 371 memcpy(dirent.d_name, name, namlen); 372 dirent_terminate(&dirent); 373 error = uiomove(&dirent, reclen, uio); 374 375 return (error); 376 } 377 378 static size_t 379 autofs_dirent_reclen(const char *name) 380 { 381 size_t reclen; 382 383 (void)autofs_readdir_one(NULL, name, -1, &reclen); 384 385 return (reclen); 386 } 387 388 static int 389 autofs_readdir(struct vop_readdir_args *ap) 390 { 391 struct vnode *vp, *newvp; 392 struct autofs_mount *amp; 393 struct autofs_node *anp, *child; 394 struct uio *uio; 395 size_t reclen, reclens; 396 ssize_t initial_resid; 397 int error; 398 399 vp = ap->a_vp; 400 amp = VFSTOAUTOFS(vp->v_mount); 401 anp = vp->v_data; 402 uio = ap->a_uio; 403 initial_resid = ap->a_uio->uio_resid; 404 405 KASSERT(vp->v_type == VDIR, ("!VDIR")); 406 407 if (autofs_cached(anp, NULL, 0) == false && 408 autofs_ignore_thread(curthread) == false) { 409 error = autofs_trigger_vn(vp, "", 0, &newvp); 410 if (error != 0) 411 return (error); 412 413 if (newvp != NULL) { 414 error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred, 415 ap->a_eofflag, ap->a_ncookies, ap->a_cookies); 416 vput(newvp); 417 return (error); 418 } 419 } 420 421 if (uio->uio_offset < 0) 422 return (EINVAL); 423 424 if (ap->a_eofflag != NULL) 425 *ap->a_eofflag = FALSE; 426 427 /* 428 * Write out the directory entry for ".". This is conditional 429 * on the current offset into the directory; same applies to the 430 * other two cases below. 431 */ 432 if (uio->uio_offset == 0) { 433 error = autofs_readdir_one(uio, ".", anp->an_fileno, &reclen); 434 if (error != 0) 435 goto out; 436 } 437 reclens = autofs_dirent_reclen("."); 438 439 /* 440 * Write out the directory entry for "..". 441 */ 442 if (uio->uio_offset <= reclens) { 443 if (uio->uio_offset != reclens) 444 return (EINVAL); 445 if (anp->an_parent == NULL) { 446 error = autofs_readdir_one(uio, "..", 447 anp->an_fileno, &reclen); 448 } else { 449 error = autofs_readdir_one(uio, "..", 450 anp->an_parent->an_fileno, &reclen); 451 } 452 if (error != 0) 453 goto out; 454 } 455 456 reclens += autofs_dirent_reclen(".."); 457 458 /* 459 * Write out the directory entries for subdirectories. 460 */ 461 AUTOFS_SLOCK(amp); 462 RB_FOREACH(child, autofs_node_tree, &anp->an_children) { 463 /* 464 * Check the offset to skip entries returned by previous 465 * calls to getdents(). 466 */ 467 if (uio->uio_offset > reclens) { 468 reclens += autofs_dirent_reclen(child->an_name); 469 continue; 470 } 471 472 /* 473 * Prevent seeking into the middle of dirent. 474 */ 475 if (uio->uio_offset != reclens) { 476 AUTOFS_SUNLOCK(amp); 477 return (EINVAL); 478 } 479 480 error = autofs_readdir_one(uio, child->an_name, 481 child->an_fileno, &reclen); 482 reclens += reclen; 483 if (error != 0) { 484 AUTOFS_SUNLOCK(amp); 485 goto out; 486 } 487 } 488 AUTOFS_SUNLOCK(amp); 489 490 if (ap->a_eofflag != NULL) 491 *ap->a_eofflag = TRUE; 492 493 return (0); 494 495 out: 496 /* 497 * Return error if the initial buffer was too small to do anything. 498 */ 499 if (uio->uio_resid == initial_resid) 500 return (error); 501 502 /* 503 * Don't return an error if we managed to copy out some entries. 504 */ 505 if (uio->uio_resid < reclen) 506 return (0); 507 508 return (error); 509 } 510 511 static int 512 autofs_reclaim(struct vop_reclaim_args *ap) 513 { 514 struct vnode *vp; 515 struct autofs_node *anp; 516 517 vp = ap->a_vp; 518 anp = vp->v_data; 519 520 /* 521 * We do not free autofs_node here; instead we are 522 * destroying them in autofs_node_delete(). 523 */ 524 sx_xlock(&anp->an_vnode_lock); 525 anp->an_vnode = NULL; 526 vp->v_data = NULL; 527 sx_xunlock(&anp->an_vnode_lock); 528 529 return (0); 530 } 531 532 struct vop_vector autofs_vnodeops = { 533 .vop_default = &default_vnodeops, 534 535 .vop_access = autofs_access, 536 .vop_lookup = autofs_lookup, 537 .vop_create = VOP_EOPNOTSUPP, 538 .vop_getattr = autofs_getattr, 539 .vop_link = VOP_EOPNOTSUPP, 540 .vop_mkdir = autofs_mkdir, 541 .vop_mknod = VOP_EOPNOTSUPP, 542 .vop_print = autofs_print, 543 .vop_read = VOP_EOPNOTSUPP, 544 .vop_readdir = autofs_readdir, 545 .vop_remove = VOP_EOPNOTSUPP, 546 .vop_rename = VOP_EOPNOTSUPP, 547 .vop_rmdir = VOP_EOPNOTSUPP, 548 .vop_setattr = VOP_EOPNOTSUPP, 549 .vop_symlink = VOP_EOPNOTSUPP, 550 .vop_write = VOP_EOPNOTSUPP, 551 .vop_reclaim = autofs_reclaim, 552 }; 553 VFS_VOP_VECTOR_REGISTER(autofs_vnodeops); 554 555 int 556 autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp, 557 const char *name, int namelen, struct autofs_node **anpp) 558 { 559 struct autofs_node *anp; 560 561 if (parent != NULL) { 562 AUTOFS_ASSERT_XLOCKED(parent->an_mount); 563 564 KASSERT(autofs_node_find(parent, name, namelen, NULL) == ENOENT, 565 ("node \"%s\" already exists", name)); 566 } 567 568 anp = uma_zalloc(autofs_node_zone, M_WAITOK | M_ZERO); 569 if (namelen >= 0) 570 anp->an_name = strndup(name, namelen, M_AUTOFS); 571 else 572 anp->an_name = strdup(name, M_AUTOFS); 573 anp->an_fileno = atomic_fetchadd_int(&->am_last_fileno, 1); 574 callout_init(&anp->an_callout, 1); 575 /* 576 * The reason for SX_NOWITNESS here is that witness(4) 577 * cannot tell vnodes apart, so the following perfectly 578 * valid lock order... 579 * 580 * vnode lock A -> autofsvlk B -> vnode lock B 581 * 582 * ... gets reported as a LOR. 583 */ 584 sx_init_flags(&anp->an_vnode_lock, "autofsvlk", SX_NOWITNESS); 585 getnanotime(&anp->an_ctime); 586 anp->an_parent = parent; 587 anp->an_mount = amp; 588 if (parent != NULL) 589 RB_INSERT(autofs_node_tree, &parent->an_children, anp); 590 RB_INIT(&anp->an_children); 591 592 *anpp = anp; 593 return (0); 594 } 595 596 int 597 autofs_node_find(struct autofs_node *parent, const char *name, 598 int namelen, struct autofs_node **anpp) 599 { 600 struct autofs_node *anp, find; 601 int error; 602 603 AUTOFS_ASSERT_LOCKED(parent->an_mount); 604 605 if (namelen >= 0) 606 find.an_name = strndup(name, namelen, M_AUTOFS); 607 else 608 find.an_name = strdup(name, M_AUTOFS); 609 610 anp = RB_FIND(autofs_node_tree, &parent->an_children, &find); 611 if (anp != NULL) { 612 error = 0; 613 if (anpp != NULL) 614 *anpp = anp; 615 } else { 616 error = ENOENT; 617 } 618 619 free(find.an_name, M_AUTOFS); 620 621 return (error); 622 } 623 624 void 625 autofs_node_delete(struct autofs_node *anp) 626 { 627 struct autofs_node *parent; 628 629 AUTOFS_ASSERT_XLOCKED(anp->an_mount); 630 KASSERT(RB_EMPTY(&anp->an_children), ("have children")); 631 632 callout_drain(&anp->an_callout); 633 634 parent = anp->an_parent; 635 if (parent != NULL) 636 RB_REMOVE(autofs_node_tree, &parent->an_children, anp); 637 sx_destroy(&anp->an_vnode_lock); 638 free(anp->an_name, M_AUTOFS); 639 uma_zfree(autofs_node_zone, anp); 640 } 641 642 int 643 autofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags, 644 struct vnode **vpp) 645 { 646 struct vnode *vp; 647 int error; 648 649 AUTOFS_ASSERT_UNLOCKED(anp->an_mount); 650 651 sx_xlock(&anp->an_vnode_lock); 652 653 vp = anp->an_vnode; 654 if (vp != NULL) { 655 error = vget(vp, flags | LK_RETRY); 656 if (error != 0) { 657 AUTOFS_WARN("vget failed with error %d", error); 658 sx_xunlock(&anp->an_vnode_lock); 659 return (error); 660 } 661 if (VN_IS_DOOMED(vp)) { 662 /* 663 * We got forcibly unmounted. 664 */ 665 AUTOFS_DEBUG("doomed vnode"); 666 sx_xunlock(&anp->an_vnode_lock); 667 vput(vp); 668 669 return (ENOENT); 670 } 671 672 *vpp = vp; 673 sx_xunlock(&anp->an_vnode_lock); 674 return (0); 675 } 676 677 error = getnewvnode("autofs", mp, &autofs_vnodeops, &vp); 678 if (error != 0) { 679 sx_xunlock(&anp->an_vnode_lock); 680 return (error); 681 } 682 683 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 684 685 vp->v_type = VDIR; 686 if (anp->an_parent == NULL) 687 vp->v_vflag |= VV_ROOT; 688 vp->v_data = anp; 689 690 VN_LOCK_ASHARE(vp); 691 692 error = insmntque(vp, mp); 693 if (error != 0) { 694 AUTOFS_DEBUG("insmntque() failed with error %d", error); 695 sx_xunlock(&anp->an_vnode_lock); 696 return (error); 697 } 698 699 KASSERT(anp->an_vnode == NULL, ("lost race")); 700 anp->an_vnode = vp; 701 702 sx_xunlock(&anp->an_vnode_lock); 703 704 vn_set_state(vp, VSTATE_CONSTRUCTED); 705 *vpp = vp; 706 return (0); 707 } 708