1 /*- 2 * Copyright (c) 2016 Tomohiro Kusumi <kusumi.tomohiro@gmail.com> 3 * Copyright (c) 2016 The DragonFly Project 4 * Copyright (c) 2014 The FreeBSD Foundation 5 * All rights reserved. 6 * 7 * This software was developed by Edward Tomasz Napierala under sponsorship 8 * from the FreeBSD Foundation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 */ 32 33 #include <sys/kernel.h> 34 #include <sys/module.h> 35 #include <sys/stat.h> 36 #include <sys/dirent.h> 37 #include <sys/namei.h> 38 #include <sys/nlookup.h> 39 #include <sys/mountctl.h> 40 41 #include "autofs.h" 42 43 static int autofs_trigger_vn(struct vnode *vp, const char *path, 44 int pathlen, struct vnode **newvp); 45 46 static __inline size_t 47 autofs_dirent_reclen(const char *name) 48 { 49 return (_DIRENT_RECLEN(strlen(name))); 50 } 51 52 static int 53 test_fs_root(struct vnode *vp) 54 { 55 int error; 56 57 if ((error = vget(vp, LK_SHARED)) != 0) { 58 AUTOFS_WARN("vget failed with error %d", error); 59 return (1); 60 } 61 62 if (((vp->v_flag & VROOT) == 0) || (vp->v_tag == VT_AUTOFS)) { 63 vput(vp); 64 return (1); 65 } 66 67 return (0); 68 } 69 70 static int 71 nlookup_fs_root(struct autofs_node *anp, struct vnode **vpp) 72 { 73 struct nlookupdata nd; 74 char *path; 75 int error; 76 77 path = autofs_path(anp); 78 79 error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW); 80 if (error == 0) { 81 error = nlookup(&nd); 82 if (error == 0) { 83 struct vnode *vp = nd.nl_nch.ncp->nc_vp; 84 error = test_fs_root(vp); 85 if (error == 0) 86 *vpp = vp; 87 } 88 } 89 nlookup_done(&nd); 90 kfree(path, M_AUTOFS); 91 92 return (error); 93 } 94 95 static int 96 autofs_access(struct vop_access_args *ap) 97 { 98 /* 99 * Nothing to do here; the only kind of access control 100 * needed is in autofs_mkdir(). 101 */ 102 return (0); 103 } 104 105 static int 106 autofs_getattr(struct vop_getattr_args *ap) 107 { 108 struct vnode *vp = ap->a_vp; 109 struct vattr *vap = ap->a_vap; 110 struct autofs_node *anp = VTOI(vp); 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 0 125 if (autofs_mount_on_stat && ...) { 126 } 127 #endif 128 vap->va_type = VDIR; 129 vap->va_mode = 0755; 130 vap->va_nlink = 3; /* XXX: FreeBSD had it like this */ 131 vap->va_uid = 0; 132 vap->va_gid = 0; 133 vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; 134 vap->va_fileid = anp->an_ino; 135 vap->va_size = S_BLKSIZE; 136 vap->va_blocksize = S_BLKSIZE; 137 vap->va_mtime = anp->an_ctime; 138 vap->va_atime = anp->an_ctime; 139 vap->va_ctime = anp->an_ctime; 140 vap->va_gen = 0; 141 vap->va_flags = 0; 142 vap->va_rmajor = 0; 143 vap->va_rminor = 0; 144 vap->va_bytes = S_BLKSIZE; 145 vap->va_filerev = 0; 146 vap->va_spare = 0; 147 148 return (0); 149 } 150 151 static int 152 autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen, 153 struct vnode **newvp) 154 { 155 struct autofs_node *anp = VTOI(vp); 156 struct vnode *nvp = NULL; 157 int error; 158 159 KKASSERT(!vn_islocked(vp)); 160 161 if (test_fs_root(vp) == 0) 162 goto mounted; 163 164 /* 165 * Don't remove this. Without having this extra nlookup, 166 * automountd tries to mount the target filesystem twice 167 * and the second attempt to mount returns an error. 168 */ 169 if (nlookup_fs_root(anp, &nvp) == 0) 170 goto mounted; 171 172 mtx_lock_ex_quick(&autofs_softc->sc_lock); 173 error = autofs_trigger(anp, path, pathlen); 174 mtx_unlock_ex(&autofs_softc->sc_lock); 175 176 if (error) 177 return (error); 178 179 if (nlookup_fs_root(anp, &nvp)) 180 return (0); 181 182 /* 183 * If the operation that succeeded was mount, then mark 184 * the node as non-cached. Otherwise, if someone unmounts 185 * the filesystem before the cache times out, we will fail 186 * to trigger. 187 */ 188 autofs_node_uncache(anp); 189 mounted: 190 *newvp = nvp; 191 KKASSERT(vn_islocked(*newvp)); 192 193 return (0); 194 } 195 196 static int 197 autofs_nresolve(struct vop_nresolve_args *ap) 198 { 199 struct vnode *vp = NULL; 200 struct vnode *dvp = ap->a_dvp; 201 struct nchandle *nch = ap->a_nch; 202 struct namecache *ncp = nch->ncp; 203 struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount); 204 struct autofs_node *anp = VTOI(dvp); 205 struct autofs_node *child = NULL; 206 int error; 207 208 if (autofs_cached(anp, ncp->nc_name, ncp->nc_nlen) == false && 209 autofs_ignore_thread() == false) { 210 struct vnode *newvp = NULL; 211 212 cache_hold(nch); 213 cache_unlock(nch); 214 error = autofs_trigger_vn(dvp, 215 ncp->nc_name, ncp->nc_nlen, &newvp); 216 cache_lock(nch); 217 cache_drop(nch); 218 219 if (error) 220 return (error); 221 if (newvp != NULL) { 222 KKASSERT(newvp->v_tag != VT_AUTOFS); 223 vput(newvp); 224 return (ESTALE); 225 } 226 return (0); 227 } 228 229 mtx_lock_sh_quick(&->am_lock); 230 error = autofs_node_find(anp, ncp->nc_name, ncp->nc_nlen, &child); 231 mtx_unlock_sh(&->am_lock); 232 233 if (error) { 234 cache_setvp(nch, NULL); 235 return (0); 236 } 237 238 error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp); 239 if (error == 0) { 240 KKASSERT(vn_islocked(vp)); 241 vn_unlock(vp); 242 cache_setvp(nch, vp); 243 vrele(vp); 244 return (0); 245 } 246 247 return (error); 248 } 249 250 static int 251 autofs_nmkdir(struct vop_nmkdir_args *ap) 252 { 253 struct vnode *vp = NULL; 254 struct vnode *dvp = ap->a_dvp; 255 struct nchandle *nch = ap->a_nch; 256 struct namecache *ncp = nch->ncp; 257 struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount); 258 struct autofs_node *anp = VTOI(dvp); 259 struct autofs_node *child = NULL; 260 int error; 261 262 /* 263 * Do not allow mkdir() if the calling thread is not 264 * automountd(8) descendant. 265 */ 266 if (autofs_ignore_thread() == false) 267 return (EPERM); 268 269 mtx_lock_ex_quick(&->am_lock); 270 error = autofs_node_new(anp, amp, ncp->nc_name, ncp->nc_nlen, &child); 271 mtx_unlock_ex(&->am_lock); 272 KKASSERT(error == 0); 273 274 error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp); 275 if (error == 0) { 276 KKASSERT(vn_islocked(vp)); 277 cache_setunresolved(nch); 278 cache_setvp(nch, vp); 279 *(ap->a_vpp) = vp; 280 return (0); 281 } 282 283 return (error); 284 } 285 286 static int 287 autofs_readdir_one(struct uio *uio, const char *name, ino_t ino, 288 size_t *reclenp) 289 { 290 int error = 0; 291 292 if (reclenp != NULL) 293 *reclenp = autofs_dirent_reclen(name); 294 295 if (vop_write_dirent(&error, uio, ino, DT_DIR, strlen(name), name)) 296 return (EINVAL); 297 298 return (error); 299 } 300 301 static int 302 autofs_readdir(struct vop_readdir_args *ap) 303 { 304 struct vnode *vp = ap->a_vp; 305 struct autofs_mount *amp = VFSTOAUTOFS(vp->v_mount); 306 struct autofs_node *anp = VTOI(vp); 307 struct autofs_node *child; 308 struct uio *uio = ap->a_uio; 309 ssize_t initial_resid = ap->a_uio->uio_resid; 310 size_t reclen, reclens; 311 int error; 312 313 KASSERT(vp->v_type == VDIR, ("!VDIR")); 314 315 if (autofs_cached(anp, NULL, 0) == false && 316 autofs_ignore_thread() == false) { 317 struct vnode *newvp = NULL; 318 error = autofs_trigger_vn(vp, "", 0, &newvp); 319 if (error) 320 return (error); 321 if (newvp != NULL) { 322 KKASSERT(newvp->v_tag != VT_AUTOFS); 323 vn_unlock(newvp); 324 error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred, 325 ap->a_eofflag, ap->a_ncookies, ap->a_cookies); 326 vrele(newvp); 327 return (error); 328 } 329 } 330 331 if (uio->uio_offset < 0) 332 return (EINVAL); 333 334 if (ap->a_eofflag != NULL) 335 *ap->a_eofflag = FALSE; 336 337 /* 338 * Write out the directory entry for ".". 339 */ 340 if (uio->uio_offset == 0) { 341 error = autofs_readdir_one(uio, ".", anp->an_ino, &reclen); 342 if (error) 343 goto out; 344 } 345 reclens = autofs_dirent_reclen("."); 346 347 /* 348 * Write out the directory entry for "..". 349 */ 350 if (uio->uio_offset <= reclens) { 351 if (uio->uio_offset != reclens) 352 return (EINVAL); 353 error = autofs_readdir_one(uio, "..", 354 (anp->an_parent ? anp->an_parent->an_ino : anp->an_ino), 355 &reclen); 356 if (error) 357 goto out; 358 } 359 reclens += autofs_dirent_reclen(".."); 360 361 /* 362 * Write out the directory entries for subdirectories. 363 */ 364 mtx_lock_sh_quick(&->am_lock); 365 RB_FOREACH(child, autofs_node_tree, &anp->an_children) { 366 /* 367 * Check the offset to skip entries returned by previous 368 * calls to getdents(). 369 */ 370 if (uio->uio_offset > reclens) { 371 reclens += autofs_dirent_reclen(child->an_name); 372 continue; 373 } 374 375 /* 376 * Prevent seeking into the middle of dirent. 377 */ 378 if (uio->uio_offset != reclens) { 379 mtx_unlock_sh(&->am_lock); 380 return (EINVAL); 381 } 382 383 error = autofs_readdir_one(uio, child->an_name, 384 child->an_ino, &reclen); 385 reclens += reclen; 386 if (error) { 387 mtx_unlock_sh(&->am_lock); 388 goto out; 389 } 390 } 391 mtx_unlock_sh(&->am_lock); 392 393 if (ap->a_eofflag != NULL) 394 *ap->a_eofflag = TRUE; 395 396 return (0); 397 out: 398 /* 399 * Return error if the initial buffer was too small to do anything. 400 */ 401 if (uio->uio_resid == initial_resid) 402 return (error); 403 404 /* 405 * Don't return an error if we managed to copy out some entries. 406 */ 407 if (uio->uio_resid < reclen) 408 return (0); 409 410 return (error); 411 } 412 413 static int 414 autofs_reclaim(struct vop_reclaim_args *ap) 415 { 416 struct vnode *vp = ap->a_vp; 417 struct autofs_node *anp = VTOI(vp); 418 419 /* 420 * We do not free autofs_node here; instead we are 421 * destroying them in autofs_node_delete(). 422 */ 423 mtx_lock_ex_quick(&anp->an_vnode_lock); 424 anp->an_vnode = NULL; 425 vp->v_data = NULL; 426 mtx_unlock_ex(&anp->an_vnode_lock); 427 428 return (0); 429 } 430 431 static int 432 autofs_mountctl(struct vop_mountctl_args *ap) 433 { 434 struct mount *mp; 435 int res = 0; 436 437 mp = ap->a_head.a_ops->head.vv_mount; 438 lwkt_gettoken(&mp->mnt_token); 439 440 switch (ap->a_op) { 441 //case ...: 442 // break; 443 default: 444 res = vop_stdmountctl(ap); 445 break; 446 } 447 448 lwkt_reltoken(&mp->mnt_token); 449 return (res); 450 } 451 452 static int 453 autofs_print(struct vop_print_args *ap) 454 { 455 struct autofs_node *anp = VTOI(ap->a_vp); 456 457 kprintf("tag VT_AUTOFS, node %p, ino %jd, name %s, cached %d, retries %d, wildcards %d\n", 458 anp, (intmax_t)anp->an_ino, anp->an_name, anp->an_cached, 459 anp->an_retries, anp->an_wildcards); 460 461 return (0); 462 } 463 464 struct vop_ops autofs_vnode_vops = { 465 .vop_default = vop_defaultop, 466 .vop_getpages = vop_stdgetpages, 467 .vop_putpages = vop_stdputpages, 468 .vop_access = autofs_access, 469 .vop_getattr = autofs_getattr, 470 .vop_nresolve = autofs_nresolve, 471 .vop_nmkdir = autofs_nmkdir, 472 .vop_readdir = autofs_readdir, 473 .vop_reclaim = autofs_reclaim, 474 .vop_mountctl = autofs_mountctl, 475 .vop_print = autofs_print, 476 }; 477 478 int 479 autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp, 480 const char *name, int namelen, struct autofs_node **anpp) 481 { 482 struct autofs_node *anp; 483 484 KKASSERT(mtx_islocked_ex(&->am_lock)); 485 486 if (parent != NULL) { 487 KKASSERT(mtx_islocked_ex(&parent->an_mount->am_lock)); 488 KASSERT(autofs_node_find(parent, name, namelen, NULL) == ENOENT, 489 ("node \"%s\" already exists", name)); 490 } 491 492 /* 493 * All struct fields must be initialized. 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