1 /*- 2 * Copyright (c) 2016 The DragonFly Project 3 * Copyright (c) 2014 The FreeBSD Foundation 4 * All rights reserved. 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/kernel.h> 33 #include <sys/module.h> 34 #include <sys/stat.h> 35 #include <sys/dirent.h> 36 #include <sys/namei.h> 37 #include <sys/nlookup.h> 38 #include <sys/mountctl.h> 39 40 #include "autofs.h" 41 42 static int autofs_trigger_vn(struct vnode *vp, const char *path, 43 int pathlen, struct vnode **newvp); 44 45 extern struct autofs_softc *autofs_softc; 46 47 static __inline int 48 autofs_trigger_vn_dir(struct vnode *vp, struct vnode **newvp) 49 { 50 return (autofs_trigger_vn(vp, "", 0, newvp)); 51 } 52 53 static __inline size_t 54 autofs_dirent_reclen(const char *name) 55 { 56 return (_DIRENT_RECLEN(strlen(name))); 57 } 58 59 static int 60 test_fs_root(struct vnode *vp) 61 { 62 int error; 63 64 if ((error = vget(vp, LK_SHARED)) != 0) { 65 AUTOFS_WARN("vget failed with error %d", error); 66 return (1); 67 } 68 69 if (((vp->v_flag & VROOT) == 0) || (vp->v_tag == VT_AUTOFS)) { 70 vput(vp); 71 return (1); 72 } 73 74 return (0); 75 } 76 77 static int 78 nlookup_fs_root(struct autofs_node *anp, struct vnode **vpp) 79 { 80 struct vnode *vp; 81 struct nlookupdata nd; 82 char *path; 83 int error; 84 85 path = autofs_path(anp); 86 87 error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW); 88 if (error == 0) { 89 error = nlookup(&nd); 90 if (error == 0) { 91 vp = nd.nl_nch.ncp->nc_vp; 92 error = test_fs_root(vp); 93 if (error == 0) 94 *vpp = vp; 95 } 96 } 97 nlookup_done(&nd); 98 kfree(path, M_AUTOFS); 99 100 return (error); 101 } 102 103 static int 104 autofs_access(struct vop_access_args *ap) 105 { 106 /* 107 * Nothing to do here; the only kind of access control 108 * needed is in autofs_mkdir(). 109 */ 110 return (0); 111 } 112 113 static int 114 autofs_getattr(struct vop_getattr_args *ap) 115 { 116 struct vnode *vp = ap->a_vp; 117 struct vattr *vap = ap->a_vap; 118 struct autofs_node *anp = VTOI(vp); 119 120 KASSERT(vp->v_type == VDIR, ("!VDIR")); 121 122 /* 123 * The reason we must do this is that some tree-walking software, 124 * namely fts(3), assumes that stat(".") results will not change 125 * between chdir("subdir") and chdir(".."), and fails with ENOENT 126 * otherwise. 127 * XXX: Not supported on DragonFly. 128 */ 129 if (autofs_mount_on_stat) 130 AUTOFS_WARN("vfs.autofs.mount_on_stat is not supported"); 131 132 vap->va_type = VDIR; 133 vap->va_mode = 0755; 134 vap->va_nlink = 3; /* XXX: FreeBSD had it like this */ 135 vap->va_uid = 0; 136 vap->va_gid = 0; 137 vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; 138 vap->va_fileid = anp->an_ino; 139 vap->va_size = S_BLKSIZE; 140 vap->va_blocksize = S_BLKSIZE; 141 vap->va_mtime = anp->an_ctime; 142 vap->va_atime = anp->an_ctime; 143 vap->va_ctime = anp->an_ctime; 144 vap->va_gen = 0; 145 vap->va_flags = 0; 146 vap->va_rmajor = 0; 147 vap->va_rminor = 0; 148 vap->va_bytes = S_BLKSIZE; 149 vap->va_filerev = 0; 150 vap->va_spare = 0; 151 152 return (0); 153 } 154 155 static int 156 autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen, 157 struct vnode **newvp) 158 { 159 struct autofs_node *anp = VTOI(vp); 160 struct vnode *nvp = NULL; 161 int error; 162 163 KKASSERT(!vn_islocked(vp)); 164 165 if (test_fs_root(vp) == 0) 166 goto mounted; 167 168 /* 169 * Don't remove this. Without having this extra nlookup, 170 * automountd tries to mount the target filesystem twice 171 * and the second attempt to mount returns an error. 172 */ 173 if (nlookup_fs_root(anp, &nvp) == 0) 174 goto mounted; 175 176 lockmgr(&autofs_softc->sc_lock, LK_EXCLUSIVE); 177 error = autofs_trigger(anp, path, pathlen); 178 lockmgr(&autofs_softc->sc_lock, LK_RELEASE); 179 180 if (error) 181 return (error); 182 183 if (nlookup_fs_root(anp, &nvp)) 184 return (0); 185 186 /* 187 * If the operation that succeeded was mount, then mark 188 * the node as non-cached. Otherwise, if someone unmounts 189 * the filesystem before the cache times out, we will fail 190 * to trigger. 191 */ 192 autofs_node_uncache(anp); 193 mounted: 194 *newvp = nvp; 195 KKASSERT(vn_islocked(*newvp)); 196 197 return (0); 198 } 199 200 static int 201 autofs_nresolve(struct vop_nresolve_args *ap) 202 { 203 struct vnode *vp = NULL; 204 struct vnode *dvp = ap->a_dvp; 205 struct nchandle *nch = ap->a_nch; 206 struct namecache *ncp = nch->ncp; 207 struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount); 208 struct autofs_node *anp = VTOI(dvp); 209 struct autofs_node *child = NULL; 210 int error; 211 212 if (autofs_cached(anp, ncp->nc_name, ncp->nc_nlen) == false && 213 autofs_ignore_thread() == false) { 214 struct vnode *newvp = NULL; 215 216 cache_hold(nch); 217 cache_unlock(nch); 218 error = autofs_trigger_vn(dvp, 219 ncp->nc_name, ncp->nc_nlen, &newvp); 220 cache_lock(nch); 221 cache_drop(nch); 222 223 if (error) 224 return (error); 225 if (newvp != NULL) { 226 KKASSERT(newvp->v_tag != VT_AUTOFS); 227 vput(newvp); 228 return (ESTALE); 229 } 230 return (0); 231 } 232 233 lockmgr(&->am_lock, LK_SHARED); 234 error = autofs_node_find(anp, ncp->nc_name, ncp->nc_nlen, &child); 235 lockmgr(&->am_lock, LK_RELEASE); 236 237 if (error) { 238 cache_setvp(nch, NULL); 239 return (0); 240 } 241 242 error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp); 243 if (error == 0) { 244 KKASSERT(vn_islocked(vp)); 245 vn_unlock(vp); 246 cache_setvp(nch, vp); 247 vrele(vp); 248 return (0); 249 } 250 251 return (error); 252 } 253 254 static int 255 autofs_nmkdir(struct vop_nmkdir_args *ap) 256 { 257 struct vnode *vp = NULL; 258 struct vnode *dvp = ap->a_dvp; 259 struct nchandle *nch = ap->a_nch; 260 struct namecache *ncp = nch->ncp; 261 struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount); 262 struct autofs_node *anp = VTOI(dvp); 263 struct autofs_node *child = NULL; 264 int error; 265 266 /* 267 * Do not allow mkdir() if the calling thread is not 268 * automountd(8) descendant. 269 */ 270 if (autofs_ignore_thread() == false) 271 return (EPERM); 272 273 lockmgr(&->am_lock, LK_EXCLUSIVE); 274 error = autofs_node_new(anp, amp, ncp->nc_name, ncp->nc_nlen, &child); 275 lockmgr(&->am_lock, LK_RELEASE); 276 KKASSERT(error == 0); 277 278 error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp); 279 if (error == 0) { 280 KKASSERT(vn_islocked(vp)); 281 cache_setunresolved(nch); 282 cache_setvp(nch, vp); 283 *(ap->a_vpp) = vp; 284 return (0); 285 } 286 287 return (error); 288 } 289 290 static int 291 autofs_readdir_one(struct uio *uio, const char *name, ino_t ino, 292 size_t *reclenp) 293 { 294 int error = 0; 295 296 if (reclenp != NULL) 297 *reclenp = autofs_dirent_reclen(name); 298 299 if (vop_write_dirent(&error, uio, ino, DT_DIR, strlen(name), name)) 300 return (EINVAL); 301 302 return (error); 303 } 304 305 static int 306 autofs_readdir(struct vop_readdir_args *ap) 307 { 308 struct vnode *vp = ap->a_vp; 309 struct autofs_mount *amp = VFSTOAUTOFS(vp->v_mount); 310 struct autofs_node *anp = VTOI(vp); 311 struct autofs_node *child; 312 struct uio *uio = ap->a_uio; 313 ssize_t initial_resid = ap->a_uio->uio_resid; 314 size_t reclen, reclens; 315 int error; 316 317 KASSERT(vp->v_type == VDIR, ("!VDIR")); 318 319 if (autofs_cached(anp, NULL, 0) == false && 320 autofs_ignore_thread() == false) { 321 struct vnode *newvp = NULL; 322 error = autofs_trigger_vn_dir(vp, &newvp); 323 if (error) 324 return (error); 325 if (newvp != NULL) { 326 KKASSERT(newvp->v_tag != VT_AUTOFS); 327 vn_unlock(newvp); 328 error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred, 329 ap->a_eofflag, ap->a_ncookies, ap->a_cookies); 330 vrele(newvp); 331 return (error); 332 } 333 /* FALLTHROUGH */ 334 } 335 336 if (uio->uio_offset < 0) 337 return (EINVAL); 338 339 if (ap->a_eofflag != NULL) 340 *ap->a_eofflag = FALSE; 341 342 /* 343 * Write out the directory entry for ".". 344 */ 345 if (uio->uio_offset == 0) { 346 error = autofs_readdir_one(uio, ".", anp->an_ino, &reclen); 347 if (error) 348 goto out; 349 } 350 reclens = autofs_dirent_reclen("."); 351 352 /* 353 * Write out the directory entry for "..". 354 */ 355 if (uio->uio_offset <= reclens) { 356 if (uio->uio_offset != reclens) 357 return (EINVAL); 358 error = autofs_readdir_one(uio, "..", 359 (anp->an_parent ? anp->an_parent->an_ino : anp->an_ino), 360 &reclen); 361 if (error) 362 goto out; 363 } 364 reclens += autofs_dirent_reclen(".."); 365 366 /* 367 * Write out the directory entries for subdirectories. 368 */ 369 lockmgr(&->am_lock, LK_SHARED); 370 RB_FOREACH(child, autofs_node_tree, &anp->an_children) { 371 /* 372 * Check the offset to skip entries returned by previous 373 * calls to getdents(). 374 */ 375 if (uio->uio_offset > reclens) { 376 reclens += autofs_dirent_reclen(child->an_name); 377 continue; 378 } 379 380 /* 381 * Prevent seeking into the middle of dirent. 382 */ 383 if (uio->uio_offset != reclens) { 384 lockmgr(&->am_lock, LK_RELEASE); 385 return (EINVAL); 386 } 387 388 error = autofs_readdir_one(uio, child->an_name, 389 child->an_ino, &reclen); 390 reclens += reclen; 391 if (error) { 392 lockmgr(&->am_lock, LK_RELEASE); 393 goto out; 394 } 395 } 396 lockmgr(&->am_lock, LK_RELEASE); 397 398 if (ap->a_eofflag != NULL) 399 *ap->a_eofflag = TRUE; 400 401 return (0); 402 out: 403 /* 404 * Return error if the initial buffer was too small to do anything. 405 */ 406 if (uio->uio_resid == initial_resid) 407 return (error); 408 409 /* 410 * Don't return an error if we managed to copy out some entries. 411 */ 412 if (uio->uio_resid < reclen) 413 return (0); 414 415 return (error); 416 } 417 418 static int 419 autofs_reclaim(struct vop_reclaim_args *ap) 420 { 421 struct vnode *vp = ap->a_vp; 422 struct autofs_node *anp = VTOI(vp); 423 424 /* 425 * We do not free autofs_node here; instead we are 426 * destroying them in autofs_node_delete(). 427 */ 428 lockmgr(&anp->an_vnode_lock, LK_EXCLUSIVE); 429 anp->an_vnode = NULL; 430 vp->v_data = NULL; 431 lockmgr(&anp->an_vnode_lock, LK_RELEASE); 432 433 return (0); 434 } 435 436 static int 437 autofs_mountctl(struct vop_mountctl_args *ap) 438 { 439 struct mount *mp; 440 #if 0 441 struct autofs_mount *amp; 442 #endif 443 int res; 444 445 mp = ap->a_head.a_ops->head.vv_mount; 446 lwkt_gettoken(&mp->mnt_token); 447 448 switch (ap->a_op) { 449 #if 0 450 case MOUNTCTL_SET_EXPORT: 451 amp = (struct autofs_mount*)mp->mnt_data; 452 if (ap->a_ctllen != sizeof(struct export_args)) 453 res = (EINVAL); 454 else 455 res = vfs_export(mp, &->am_export, 456 (const struct export_args*)ap->a_ctl); 457 break; 458 #endif 459 default: 460 res = vop_stdmountctl(ap); 461 break; 462 } 463 464 lwkt_reltoken(&mp->mnt_token); 465 return (res); 466 } 467 468 static int 469 autofs_print(struct vop_print_args *ap) 470 { 471 struct autofs_node *anp = VTOI(ap->a_vp); 472 473 kprintf("tag VT_AUTOFS, node %p, ino %jd, name %s, cached %d, retries %d, wildcards %d\n", 474 anp, (intmax_t)anp->an_ino, anp->an_name, anp->an_cached, anp->an_retries, anp->an_wildcards); 475 476 return (0); 477 } 478 479 struct vop_ops autofs_vnode_vops = { 480 .vop_default = vop_defaultop, 481 .vop_getpages = vop_stdgetpages, 482 .vop_putpages = vop_stdputpages, 483 .vop_access = autofs_access, 484 .vop_getattr = autofs_getattr, 485 .vop_nresolve = autofs_nresolve, 486 .vop_nmkdir = autofs_nmkdir, 487 .vop_readdir = autofs_readdir, 488 .vop_reclaim = autofs_reclaim, 489 .vop_mountctl = autofs_mountctl, 490 .vop_print = autofs_print, 491 #if 0 492 .vop_nlookupdotdot = NULL, 493 #endif 494 }; 495 496 int 497 autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp, 498 const char *name, int namelen, struct autofs_node **anpp) 499 { 500 struct autofs_node *anp; 501 502 AUTOFS_ASSERT_XLOCKED(amp); 503 504 if (parent != NULL) { 505 AUTOFS_ASSERT_XLOCKED(parent->an_mount); 506 KASSERT(autofs_node_find(parent, name, namelen, NULL) == ENOENT, 507 ("node \"%s\" already exists", name)); 508 } 509 510 anp = objcache_get(autofs_node_objcache, M_WAITOK); 511 if (namelen >= 0) 512 anp->an_name = kstrndup(name, namelen, M_AUTOFS); 513 else 514 anp->an_name = kstrdup(name, M_AUTOFS); 515 anp->an_ino = amp->am_last_ino++; 516 callout_init(&anp->an_callout); 517 lockinit(&anp->an_vnode_lock, "autofsvnlk", 0, 0); 518 getnanotime(&anp->an_ctime); 519 anp->an_parent = parent; 520 anp->an_mount = amp; 521 anp->an_vnode = NULL; 522 anp->an_cached = false; 523 anp->an_wildcards = false; 524 anp->an_retries = 0; 525 if (parent != NULL) 526 RB_INSERT(autofs_node_tree, &parent->an_children, anp); 527 RB_INIT(&anp->an_children); 528 529 *anpp = anp; 530 531 return (0); 532 } 533 534 int 535 autofs_node_find(struct autofs_node *parent, const char *name, 536 int namelen, struct autofs_node **anpp) 537 { 538 struct autofs_node *anp, find; 539 int error; 540 541 AUTOFS_ASSERT_LOCKED(parent->an_mount); 542 543 if (namelen >= 0) 544 find.an_name = kstrndup(name, namelen, M_AUTOFS); 545 else 546 find.an_name = kstrdup(name, M_AUTOFS); 547 548 anp = RB_FIND(autofs_node_tree, &parent->an_children, &find); 549 if (anp != NULL) { 550 error = 0; 551 if (anpp != NULL) 552 *anpp = anp; 553 } else { 554 error = ENOENT; 555 } 556 557 kfree(find.an_name, M_AUTOFS); 558 559 return (error); 560 } 561 562 void 563 autofs_node_delete(struct autofs_node *anp) 564 { 565 AUTOFS_ASSERT_XLOCKED(anp->an_mount); 566 KASSERT(RB_EMPTY(&anp->an_children), ("have children")); 567 568 callout_drain(&anp->an_callout); 569 570 if (anp->an_parent != NULL) 571 RB_REMOVE(autofs_node_tree, &anp->an_parent->an_children, anp); 572 573 lockuninit(&anp->an_vnode_lock); 574 kfree(anp->an_name, M_AUTOFS); 575 objcache_put(autofs_node_objcache, anp); 576 } 577 578 int 579 autofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags, 580 struct vnode **vpp) 581 { 582 struct vnode *vp = NULL; 583 int error; 584 retry: 585 AUTOFS_ASSERT_UNLOCKED(anp->an_mount); 586 lockmgr(&anp->an_vnode_lock, LK_EXCLUSIVE); 587 588 vp = anp->an_vnode; 589 if (vp != NULL) { 590 vhold(vp); 591 lockmgr(&anp->an_vnode_lock, LK_RELEASE); 592 593 error = vget(vp, flags | LK_RETRY); 594 if (error) { 595 AUTOFS_WARN("vget failed with error %d", error); 596 vdrop(vp); 597 goto retry; 598 } 599 vdrop(vp); 600 *vpp = vp; 601 return (0); 602 } 603 604 lockmgr(&anp->an_vnode_lock, LK_RELEASE); 605 606 error = getnewvnode(VT_AUTOFS, mp, &vp, VLKTIMEOUT, LK_CANRECURSE); 607 if (error) 608 return (error); 609 vp->v_type = VDIR; 610 vp->v_data = anp; 611 612 KASSERT(anp->an_vnode == NULL, ("lost race")); 613 anp->an_vnode = vp; 614 *vpp = vp; 615 616 return (0); 617 } 618