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