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