1 /* $NetBSD: autofs_vnops.c,v 1.1 2018/01/09 03:31:14 christos Exp $ */ 2 /*- 3 * Copyright (c) 2017 The NetBSD Foundation, Inc. 4 * Copyright (c) 2016 The DragonFly Project 5 * Copyright (c) 2014 The FreeBSD Foundation 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Tomohiro Kusumi <kusumi.tomohiro@gmail.com>. 10 * 11 * This software was developed by Edward Tomasz Napierala under sponsorship 12 * from the FreeBSD Foundation. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 */ 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: autofs_vnops.c,v 1.1 2018/01/09 03:31:14 christos Exp $"); 38 39 #include "autofs.h" 40 41 #include <sys/stat.h> 42 #include <sys/dirent.h> 43 #include <sys/namei.h> 44 #include <miscfs/genfs/genfs.h> 45 46 static int autofs_trigger_vn(struct vnode *vp, const char *path, 47 int pathlen, struct vnode **newvp); 48 49 static int 50 autofs_access(void *v) 51 { 52 struct vop_access_args /* { 53 struct vnode *a_vp; 54 int a_mode; 55 kauth_cred_t a_cred; 56 } */ *ap = v; 57 struct vnode *vp __diagused = ap->a_vp; 58 59 KASSERT(VOP_ISLOCKED(vp)); 60 /* 61 * Nothing to do here; the only kind of access control 62 * needed is in autofs_mkdir(). 63 */ 64 return 0; 65 } 66 67 static int 68 autofs_getattr(void *v) 69 { 70 struct vop_getattr_args /* { 71 struct vnode *a_vp; 72 struct vattr *a_vap; 73 kauth_cred_t a_cred; 74 } */ *ap = v; 75 struct vnode *vp = ap->a_vp; 76 struct vattr *vap = ap->a_vap; 77 struct autofs_node *anp = VTOI(vp); 78 79 KASSERT(vp->v_type == VDIR); 80 81 /* 82 * The reason we must do this is that some tree-walking software, 83 * namely fts(3), assumes that stat(".") results will not change 84 * between chdir("subdir") and chdir(".."), and fails with ENOENT 85 * otherwise. 86 */ 87 if (autofs_mount_on_stat && 88 autofs_cached(anp, NULL, 0) == false && 89 autofs_ignore_thread() == false) { 90 struct vnode *newvp = NULL; 91 int error = autofs_trigger_vn(vp, "", 0, &newvp); 92 if (error) 93 return error; 94 /* 95 * Already mounted here. 96 */ 97 if (newvp) { 98 error = VOP_GETATTR(newvp, vap, ap->a_cred); 99 vput(newvp); 100 return error; 101 } 102 } 103 104 vattr_null(vap); 105 106 vap->va_type = VDIR; 107 vap->va_mode = 0755; 108 vap->va_nlink = 3; 109 vap->va_uid = 0; 110 vap->va_gid = 0; 111 vap->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0]; 112 vap->va_fileid = anp->an_ino; 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_vaflags = 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. A caller needs to vput() the 'newvp'. 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 mutex_enter(&autofs_softc->sc_lock); 155 156 /* 157 * Workaround for mounting the same thing multiple times; revisit. 158 */ 159 if (vp->v_mountedhere) { 160 error = 0; 161 goto mounted; 162 } 163 164 error = autofs_trigger(anp, path, pathlen); 165 mounted: 166 mutex_exit(&autofs_softc->sc_lock); 167 vn_lock(vp, lock_flags | LK_RETRY); 168 vrele(vp); 169 170 if (error) 171 return error; 172 173 if (!vp->v_mountedhere) { 174 *newvp = NULL; 175 return 0; 176 } else { 177 /* 178 * If the operation that succeeded was mount, then mark 179 * the node as non-cached. Otherwise, if someone unmounts 180 * the filesystem before the cache times out, we will fail 181 * to trigger. 182 */ 183 autofs_node_uncache(anp); 184 } 185 186 error = VFS_ROOT(vp->v_mountedhere, newvp); 187 if (error) { 188 AUTOFS_WARN("VFS_ROOT() failed with error %d", error); 189 return error; 190 } 191 192 return 0; 193 } 194 195 static int 196 autofs_lookup(void *v) 197 { 198 struct vop_lookup_v2_args /* { 199 struct vnode *a_dvp; 200 struct vnode **a_vpp; 201 struct componentname *a_cnp; 202 } */ *ap = v; 203 struct vnode *dvp = ap->a_dvp; 204 struct vnode **vpp = ap->a_vpp; 205 struct componentname *cnp = ap->a_cnp; 206 struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount); 207 struct autofs_node *anp, *child; 208 int cachefound; 209 int error; 210 const bool lastcn __diagused = (cnp->cn_flags & ISLASTCN) != 0; 211 212 KASSERT(VOP_ISLOCKED(dvp)); 213 214 anp = VTOI(dvp); 215 *vpp = NULL; 216 217 /* Check accessibility of directory. */ 218 KASSERT(!VOP_ACCESS(dvp, VEXEC, cnp->cn_cred)); 219 220 /* 221 * Avoid doing a linear scan of the directory if the requested 222 * directory/name couple is already in the cache. 223 */ 224 cachefound = cache_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen, 225 cnp->cn_nameiop, cnp->cn_flags, NULL, vpp); 226 if (cachefound && *vpp == NULLVP) { 227 /* Negative cache hit. */ 228 error = ENOENT; 229 goto out; 230 } else if (cachefound) { 231 error = 0; 232 goto out; 233 } 234 235 if (cnp->cn_flags & ISDOTDOT) { 236 struct autofs_node *parent; 237 /* 238 * Lookup of ".." case. 239 */ 240 KASSERT(!(lastcn && cnp->cn_nameiop == RENAME)); 241 parent = anp->an_parent; 242 if (!parent) { 243 error = ENOENT; 244 goto out; 245 } 246 247 error = vcache_get(dvp->v_mount, &parent, sizeof(parent), vpp); 248 goto out; 249 } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 250 /* 251 * Lookup of "." case. 252 */ 253 KASSERT(!(lastcn && cnp->cn_nameiop == RENAME)); 254 vref(dvp); 255 *vpp = dvp; 256 error = 0; 257 goto done; 258 } 259 260 if (autofs_cached(anp, cnp->cn_nameptr, cnp->cn_namelen) == false && 261 autofs_ignore_thread() == false) { 262 struct vnode *newvp = NULL; 263 error = autofs_trigger_vn(dvp, cnp->cn_nameptr, cnp->cn_namelen, 264 &newvp); 265 if (error) 266 return error; 267 /* 268 * Already mounted here. 269 */ 270 if (newvp) { 271 error = VOP_LOOKUP(newvp, vpp, cnp); 272 vput(newvp); 273 return error; 274 } 275 } 276 277 mutex_enter(&->am_lock); 278 error = autofs_node_find(anp, cnp->cn_nameptr, cnp->cn_namelen, &child); 279 if (error) { 280 if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) { 281 mutex_exit(&->am_lock); 282 error = EJUSTRETURN; 283 goto done; 284 } 285 286 mutex_exit(&->am_lock); 287 error = ENOENT; 288 goto done; 289 } 290 291 /* 292 * Dropping the node here is ok, because we never remove nodes. 293 */ 294 mutex_exit(&->am_lock); 295 296 /* Get a vnode for the matching entry. */ 297 error = vcache_get(dvp->v_mount, &child, sizeof(child), vpp); 298 done: 299 /* 300 * Cache the result, unless request was for creation (as it does 301 * not improve the performance). 302 */ 303 if (cnp->cn_nameiop != CREATE) { 304 cache_enter(dvp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, 305 cnp->cn_flags); 306 } 307 out: 308 KASSERT(VOP_ISLOCKED(dvp)); 309 310 return error; 311 } 312 313 static int 314 autofs_open(void *v) 315 { 316 struct vop_open_args /* { 317 struct vnode *a_vp; 318 int a_mode; 319 kauth_cred_t a_cred; 320 } */ *ap = v; 321 struct vnode *vp __diagused = ap->a_vp; 322 323 KASSERT(VOP_ISLOCKED(vp)); 324 return 0; 325 } 326 327 static int 328 autofs_close(void *v) 329 { 330 struct vop_close_args /* { 331 struct vnode *a_vp; 332 int a_fflag; 333 kauth_cred_t a_cred; 334 } */ *ap = v; 335 struct vnode *vp __diagused = ap->a_vp; 336 337 KASSERT(VOP_ISLOCKED(vp)); 338 return 0; 339 } 340 341 static int 342 autofs_fsync(void *v) 343 { 344 struct vop_fsync_args /* { 345 struct vnode *a_vp; 346 kauth_cred_t a_cred; 347 int a_flags; 348 off_t a_offlo; 349 off_t a_offhi; 350 struct lwp *a_l; 351 } */ *ap = v; 352 struct vnode *vp __diagused = ap->a_vp; 353 354 /* Nothing to do. Should be up to date. */ 355 KASSERT(VOP_ISLOCKED(vp)); 356 return 0; 357 } 358 359 static int 360 autofs_mkdir(void *v) 361 { 362 struct vop_mkdir_v3_args /* { 363 struct vnode *a_dvp; 364 struct vnode **a_vpp; 365 struct componentname *a_cnp; 366 struct vattr *a_vap; 367 } */ *ap = v; 368 struct vnode *dvp = ap->a_dvp; 369 struct vnode **vpp = ap->a_vpp; 370 struct componentname *cnp = ap->a_cnp; 371 struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount); 372 struct autofs_node *anp = VTOI(dvp); 373 struct autofs_node *child = NULL; 374 int error; 375 376 KASSERT(ap->a_vap->va_type == VDIR); 377 378 /* 379 * Do not allow mkdir() if the calling thread is not 380 * automountd(8) descendant. 381 */ 382 if (autofs_ignore_thread() == false) 383 return EPERM; 384 385 mutex_enter(&->am_lock); 386 error = autofs_node_new(anp, amp, cnp->cn_nameptr, cnp->cn_namelen, 387 &child); 388 if (error) { 389 mutex_exit(&->am_lock); 390 return error; 391 } 392 mutex_exit(&->am_lock); 393 394 return vcache_get(amp->am_mp, &child, sizeof(child), vpp); 395 } 396 397 static int 398 autofs_print(void *v) 399 { 400 struct vop_print_args /* { 401 struct vnode *a_vp; 402 } */ *ap = v; 403 struct vnode *vp = ap->a_vp; 404 struct autofs_node *anp = VTOI(vp); 405 406 printf("tag VT_AUTOFS, node %p, ino %jd, name %s, cached %d, " 407 "retries %d, wildcards %d", 408 anp, (intmax_t)anp->an_ino, anp->an_name, anp->an_cached, 409 anp->an_retries, anp->an_wildcards); 410 printf("\n"); 411 412 return 0; 413 } 414 415 static int 416 autofs_readdir_one(struct uio *uio, const char *name, ino_t ino, 417 size_t *reclenp) 418 { 419 struct dirent dirent; 420 421 dirent.d_fileno = ino; 422 dirent.d_type = DT_DIR; 423 strlcpy(dirent.d_name, name, sizeof(dirent.d_name)); 424 dirent.d_namlen = strlen(dirent.d_name); 425 dirent.d_reclen = _DIRENT_SIZE(&dirent); 426 427 if (reclenp) 428 *reclenp = dirent.d_reclen; 429 430 if (!uio) 431 return 0; 432 433 if (uio->uio_resid < dirent.d_reclen) 434 return EINVAL; 435 436 return uiomove(&dirent, dirent.d_reclen, uio); 437 } 438 439 static size_t 440 autofs_dirent_reclen(const char *name) 441 { 442 size_t reclen; 443 444 (void)autofs_readdir_one(NULL, name, -1, &reclen); 445 446 return reclen; 447 } 448 449 static int 450 autofs_readdir(void *v) 451 { 452 struct vop_readdir_args /* { 453 struct vnode *a_vp; 454 struct uio *a_uio; 455 kauth_cred_t a_cred; 456 int *a_eofflag; 457 off_t **a_cookies; 458 int *ncookies; 459 } */ *ap = v; 460 struct vnode *vp = ap->a_vp; 461 struct uio *uio = ap->a_uio; 462 size_t initial_resid = ap->a_uio->uio_resid; 463 struct autofs_mount *amp = VFSTOAUTOFS(vp->v_mount); 464 struct autofs_node *anp = VTOI(vp); 465 struct autofs_node *child; 466 size_t reclen, reclens; 467 int error; 468 469 if (vp->v_type != VDIR) 470 return ENOTDIR; 471 472 if (autofs_cached(anp, NULL, 0) == false && 473 autofs_ignore_thread() == false) { 474 struct vnode *newvp = NULL; 475 error = autofs_trigger_vn(vp, "", 0, &newvp); 476 if (error) 477 return error; 478 /* 479 * Already mounted here. 480 */ 481 if (newvp) { 482 error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred, 483 ap->a_eofflag, ap->a_cookies, ap->a_ncookies); 484 vput(newvp); 485 return error; 486 } 487 } 488 489 if (uio->uio_offset < 0) 490 return EINVAL; 491 492 if (ap->a_eofflag) 493 *ap->a_eofflag = FALSE; 494 495 /* 496 * Write out the directory entry for ".". 497 */ 498 if (uio->uio_offset == 0) { 499 error = autofs_readdir_one(uio, ".", anp->an_ino, &reclen); 500 if (error) 501 goto out; 502 } 503 reclens = autofs_dirent_reclen("."); 504 505 /* 506 * Write out the directory entry for "..". 507 */ 508 if (uio->uio_offset <= reclens) { 509 if (uio->uio_offset != reclens) 510 return EINVAL; 511 error = autofs_readdir_one(uio, "..", 512 (anp->an_parent ? anp->an_parent->an_ino : anp->an_ino), 513 &reclen); 514 if (error) 515 goto out; 516 } 517 reclens += autofs_dirent_reclen(".."); 518 519 /* 520 * Write out the directory entries for subdirectories. 521 */ 522 mutex_enter(&->am_lock); 523 RB_FOREACH(child, autofs_node_tree, &anp->an_children) { 524 /* 525 * Check the offset to skip entries returned by previous 526 * calls to getdents(). 527 */ 528 if (uio->uio_offset > reclens) { 529 reclens += autofs_dirent_reclen(child->an_name); 530 continue; 531 } 532 533 /* 534 * Prevent seeking into the middle of dirent. 535 */ 536 if (uio->uio_offset != reclens) { 537 mutex_exit(&->am_lock); 538 return EINVAL; 539 } 540 541 error = autofs_readdir_one(uio, child->an_name, 542 child->an_ino, &reclen); 543 reclens += reclen; 544 if (error) { 545 mutex_exit(&->am_lock); 546 goto out; 547 } 548 } 549 mutex_exit(&->am_lock); 550 551 if (ap->a_eofflag) 552 *ap->a_eofflag = TRUE; 553 554 return 0; 555 out: 556 /* 557 * Return error if the initial buffer was too small to do anything. 558 */ 559 if (uio->uio_resid == initial_resid) 560 return error; 561 562 /* 563 * Don't return an error if we managed to copy out some entries. 564 */ 565 if (uio->uio_resid < reclen) 566 return 0; 567 568 return error; 569 } 570 571 static int 572 autofs_reclaim(void *v) 573 { 574 struct vop_reclaim_v2_args /* { 575 struct vnode *a_vp; 576 } */ *ap = v; 577 struct vnode *vp = ap->a_vp; 578 struct autofs_node *anp = VTOI(vp); 579 580 VOP_UNLOCK(vp); 581 582 /* 583 * We do not free autofs_node here; instead we are 584 * destroying them in autofs_node_delete(). 585 */ 586 mutex_enter(&anp->an_vnode_lock); 587 anp->an_vnode = NULL; 588 vp->v_data = NULL; 589 mutex_exit(&anp->an_vnode_lock); 590 591 return 0; 592 } 593 594 int (**autofs_vnodeop_p)(void *); 595 static const struct vnodeopv_entry_desc autofs_vnodeop_entries[] = { 596 { &vop_default_desc, vn_default_error }, 597 { &vop_lookup_desc, autofs_lookup }, 598 { &vop_open_desc, autofs_open }, 599 { &vop_close_desc, autofs_close }, 600 { &vop_access_desc, autofs_access }, 601 { &vop_getattr_desc, autofs_getattr }, 602 { &vop_fsync_desc, autofs_fsync }, 603 { &vop_mkdir_desc, autofs_mkdir }, 604 { &vop_readdir_desc, autofs_readdir }, 605 { &vop_reclaim_desc, autofs_reclaim }, 606 { &vop_lock_desc, genfs_lock }, 607 { &vop_unlock_desc, genfs_unlock }, 608 { &vop_print_desc, autofs_print }, 609 { &vop_islocked_desc, genfs_islocked }, 610 { &vop_getpages_desc, genfs_getpages }, 611 { &vop_putpages_desc, genfs_putpages }, 612 { NULL, NULL } 613 }; 614 615 const struct vnodeopv_desc autofs_vnodeop_opv_desc = { 616 &autofs_vnodeop_p, autofs_vnodeop_entries 617 }; 618 619 int 620 autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp, 621 const char *name, int namelen, struct autofs_node **anpp) 622 { 623 struct autofs_node *anp; 624 625 KASSERT(mutex_owned(&->am_lock)); 626 627 if (parent) { 628 KASSERT(mutex_owned(&parent->an_mount->am_lock)); 629 KASSERT(autofs_node_find(parent, name, namelen, NULL) == 630 ENOENT); 631 } 632 633 anp = pool_get(&autofs_node_pool, PR_WAITOK); 634 anp->an_name = autofs_strndup(name, namelen, KM_SLEEP); 635 anp->an_ino = amp->am_last_ino++; 636 callout_init(&anp->an_callout, 0); 637 mutex_init(&anp->an_vnode_lock, MUTEX_DEFAULT, IPL_NONE); 638 getnanotime(&anp->an_ctime); 639 anp->an_parent = parent; 640 anp->an_mount = amp; 641 anp->an_vnode = NULL; 642 anp->an_cached = false; 643 anp->an_wildcards = false; 644 anp->an_retries = 0; 645 if (parent) 646 RB_INSERT(autofs_node_tree, &parent->an_children, anp); 647 RB_INIT(&anp->an_children); 648 649 *anpp = anp; 650 return 0; 651 } 652 653 int 654 autofs_node_find(struct autofs_node *parent, const char *name, 655 int namelen, struct autofs_node **anpp) 656 { 657 struct autofs_node *anp, find; 658 int error; 659 660 KASSERT(mutex_owned(&parent->an_mount->am_lock)); 661 662 find.an_name = autofs_strndup(name, namelen, KM_SLEEP); 663 anp = RB_FIND(autofs_node_tree, &parent->an_children, &find); 664 if (anp) { 665 error = 0; 666 if (anpp) 667 *anpp = anp; 668 } else { 669 error = ENOENT; 670 } 671 672 kmem_strfree(find.an_name); 673 674 return error; 675 } 676 677 void 678 autofs_node_delete(struct autofs_node *anp) 679 { 680 681 KASSERT(mutex_owned(&anp->an_mount->am_lock)); 682 KASSERT(RB_EMPTY(&anp->an_children)); 683 684 callout_halt(&anp->an_callout, NULL); 685 686 if (anp->an_parent) 687 RB_REMOVE(autofs_node_tree, &anp->an_parent->an_children, anp); 688 689 mutex_destroy(&anp->an_vnode_lock); 690 kmem_strfree(anp->an_name); 691 pool_put(&autofs_node_pool, anp); 692 } 693