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