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