1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <sys/param.h> 27 #include <sys/kmem.h> 28 #include <sys/errno.h> 29 #include <sys/proc.h> 30 #include <sys/disp.h> 31 #include <sys/vfs.h> 32 #include <sys/vnode.h> 33 #include <sys/pathname.h> 34 #include <sys/cred.h> 35 #include <sys/mount.h> 36 #include <sys/cmn_err.h> 37 #include <sys/debug.h> 38 #include <sys/systm.h> 39 #include <sys/dirent.h> 40 #include <fs/fs_subr.h> 41 #include <sys/fs/autofs.h> 42 #include <sys/callb.h> 43 #include <sys/sysmacros.h> 44 #include <sys/zone.h> 45 #include <sys/door.h> 46 #include <sys/fs/mntdata.h> 47 #include <nfs/mount.h> 48 #include <rpc/clnt.h> 49 #include <rpcsvc/autofs_prot.h> 50 #include <nfs/rnode.h> 51 #include <sys/utsname.h> 52 #include <sys/schedctl.h> 53 54 /* 55 * Autofs and Zones: 56 * 57 * Zones are delegated the responsibility of managing their own autofs mounts 58 * and maps. Each zone runs its own copy of automountd, with its own timeouts, 59 * and other logically "global" parameters. kRPC and virtualization in the 60 * loopback transport (tl) will prevent a zone from communicating with another 61 * zone's automountd. 62 * 63 * Each zone has its own "rootfnnode" and associated tree of auto nodes. 64 * 65 * Each zone also has its own set of "unmounter" kernel threads; these are 66 * created and run within the zone's context (ie, they are created via 67 * zthread_create()). 68 * 69 * Cross-zone mount triggers are disallowed. There is a check in 70 * auto_trigger_mount() to this effect; EPERM is returned to indicate that the 71 * mount is not owned by the caller. 72 * 73 * autofssys() enables a caller in the global zone to clean up in-kernel (as 74 * well as regular) autofs mounts via the unmount_tree() mechanism. This is 75 * routinely done when all mounts are removed as part of zone shutdown. 76 */ 77 #define TYPICALMAXPATHLEN 64 78 79 static kmutex_t autofs_nodeid_lock; 80 81 /* max number of unmount threads running */ 82 static int autofs_unmount_threads = 5; 83 static int autofs_unmount_thread_timer = 120; /* in seconds */ 84 85 static int auto_perform_link(fnnode_t *, struct linka *, cred_t *); 86 static int auto_perform_actions(fninfo_t *, fnnode_t *, 87 action_list *, cred_t *); 88 static int auto_getmntpnt(vnode_t *, char *, vnode_t **, cred_t *); 89 static int auto_lookup_request(fninfo_t *, char *, struct linka *, 90 bool_t, bool_t *, cred_t *); 91 static int auto_mount_request(fninfo_t *, char *, action_list **, cred_t *, 92 bool_t); 93 94 /* 95 * Clears the MF_INPROG flag, and wakes up those threads sleeping on 96 * fn_cv_mount if MF_WAITING is set. 97 */ 98 void 99 auto_unblock_others( 100 fnnode_t *fnp, 101 uint_t operation) /* either MF_INPROG or MF_LOOKUP */ 102 { 103 ASSERT(operation & (MF_INPROG | MF_LOOKUP)); 104 fnp->fn_flags &= ~operation; 105 if (fnp->fn_flags & MF_WAITING) { 106 fnp->fn_flags &= ~MF_WAITING; 107 cv_broadcast(&fnp->fn_cv_mount); 108 } 109 } 110 111 int 112 auto_wait4mount(fnnode_t *fnp) 113 { 114 int error; 115 k_sigset_t smask; 116 117 AUTOFS_DPRINT((4, "auto_wait4mount: fnp=%p\n", (void *)fnp)); 118 119 mutex_enter(&fnp->fn_lock); 120 while (fnp->fn_flags & (MF_INPROG | MF_LOOKUP)) { 121 /* 122 * There is a mount or a lookup in progress. 123 */ 124 fnp->fn_flags |= MF_WAITING; 125 sigintr(&smask, 1); 126 if (!cv_wait_sig(&fnp->fn_cv_mount, &fnp->fn_lock)) { 127 /* 128 * Decided not to wait for operation to 129 * finish after all. 130 */ 131 sigunintr(&smask); 132 mutex_exit(&fnp->fn_lock); 133 return (EINTR); 134 } 135 sigunintr(&smask); 136 } 137 error = fnp->fn_error; 138 139 if (error == EINTR) { 140 /* 141 * The thread doing the mount got interrupted, we need to 142 * try again, by returning EAGAIN. 143 */ 144 error = EAGAIN; 145 } 146 mutex_exit(&fnp->fn_lock); 147 148 AUTOFS_DPRINT((5, "auto_wait4mount: fnp=%p error=%d\n", (void *)fnp, 149 error)); 150 return (error); 151 } 152 153 int 154 auto_lookup_aux(fnnode_t *fnp, char *name, cred_t *cred) 155 { 156 struct fninfo *fnip; 157 struct linka link; 158 bool_t mountreq = FALSE; 159 int error = 0; 160 161 fnip = vfstofni(fntovn(fnp)->v_vfsp); 162 bzero(&link, sizeof (link)); 163 error = auto_lookup_request(fnip, name, &link, TRUE, &mountreq, cred); 164 if (!error) { 165 if (link.link != NULL) { 166 error = ENOENT; 167 /* 168 * This node should be a symlink 169 */ 170 if (*link.link != '\0') 171 error = auto_perform_link(fnp, &link, cred); 172 } else if (mountreq) { 173 /* 174 * The automount daemon is requesting a mount, 175 * implying this entry must be a wildcard match and 176 * therefore in need of verification that the entry 177 * exists on the server. 178 */ 179 mutex_enter(&fnp->fn_lock); 180 AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG); 181 fnp->fn_error = 0; 182 183 /* 184 * Unblock other lookup requests on this node, 185 * this is needed to let the lookup generated by 186 * the mount call to complete. The caveat is 187 * other lookups on this node can also get by, 188 * i.e., another lookup on this node that occurs 189 * while this lookup is attempting the mount 190 * would return a positive result no matter what. 191 * Therefore two lookups on the this node could 192 * potentially get disparate results. 193 */ 194 AUTOFS_UNBLOCK_OTHERS(fnp, MF_LOOKUP); 195 mutex_exit(&fnp->fn_lock); 196 /* 197 * auto_new_mount_thread fires up a new thread which 198 * calls automountd finishing up the work 199 */ 200 auto_new_mount_thread(fnp, name, cred); 201 202 /* 203 * At this point, we are simply another thread 204 * waiting for the mount to complete 205 */ 206 error = auto_wait4mount(fnp); 207 if (error == AUTOFS_SHUTDOWN) 208 error = ENOENT; 209 } 210 } 211 212 if (link.link) 213 kmem_free(link.link, strlen(link.link) + 1); 214 if (link.dir) 215 kmem_free(link.dir, strlen(link.dir) + 1); 216 mutex_enter(&fnp->fn_lock); 217 fnp->fn_error = error; 218 219 /* 220 * Notify threads waiting for lookup/mount that 221 * it's done. 222 */ 223 if (mountreq) { 224 AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG); 225 } else { 226 AUTOFS_UNBLOCK_OTHERS(fnp, MF_LOOKUP); 227 } 228 mutex_exit(&fnp->fn_lock); 229 return (error); 230 } 231 232 /* 233 * Starting point for thread to handle mount requests with automountd. 234 * XXX auto_mount_thread() is not suspend-safe within the scope of 235 * the present model defined for cpr to suspend the system. Calls 236 * made by the auto_mount_thread() that have been identified to be unsafe 237 * are (1) RPC client handle setup and client calls to automountd which 238 * can block deep down in the RPC library, (2) kmem_alloc() calls with the 239 * KM_SLEEP flag which can block if memory is low, and (3) VFS_*(), and 240 * lookuppnvp() calls which can result in over the wire calls to servers. 241 * The thread should be completely reevaluated to make it suspend-safe in 242 * case of future updates to the cpr model. 243 */ 244 static void 245 auto_mount_thread(struct autofs_callargs *argsp) 246 { 247 struct fninfo *fnip; 248 fnnode_t *fnp; 249 vnode_t *vp; 250 char *name; 251 size_t namelen; 252 cred_t *cred; 253 action_list *alp = NULL; 254 int error; 255 callb_cpr_t cprinfo; 256 kmutex_t auto_mount_thread_cpr_lock; 257 258 mutex_init(&auto_mount_thread_cpr_lock, NULL, MUTEX_DEFAULT, NULL); 259 CALLB_CPR_INIT(&cprinfo, &auto_mount_thread_cpr_lock, 260 callb_generic_cpr, "auto_mount_thread"); 261 262 fnp = argsp->fnc_fnp; 263 vp = fntovn(fnp); 264 fnip = vfstofni(vp->v_vfsp); 265 name = argsp->fnc_name; 266 cred = argsp->fnc_cred; 267 ASSERT(crgetzoneid(argsp->fnc_cred) == fnip->fi_zoneid); 268 269 error = auto_mount_request(fnip, name, &alp, cred, TRUE); 270 if (!error) 271 error = auto_perform_actions(fnip, fnp, alp, cred); 272 mutex_enter(&fnp->fn_lock); 273 fnp->fn_error = error; 274 275 /* 276 * Notify threads waiting for mount that 277 * it's done. 278 */ 279 AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG); 280 mutex_exit(&fnp->fn_lock); 281 282 VN_RELE(vp); 283 crfree(argsp->fnc_cred); 284 namelen = strlen(argsp->fnc_name) + 1; 285 kmem_free(argsp->fnc_name, namelen); 286 kmem_free(argsp, sizeof (*argsp)); 287 288 mutex_enter(&auto_mount_thread_cpr_lock); 289 CALLB_CPR_EXIT(&cprinfo); 290 mutex_destroy(&auto_mount_thread_cpr_lock); 291 zthread_exit(); 292 /* NOTREACHED */ 293 } 294 295 static int autofs_thr_success = 0; 296 297 /* 298 * Creates new thread which calls auto_mount_thread which does 299 * the bulk of the work calling automountd, via 'auto_perform_actions'. 300 */ 301 void 302 auto_new_mount_thread(fnnode_t *fnp, char *name, cred_t *cred) 303 { 304 struct autofs_callargs *argsp; 305 306 argsp = kmem_alloc(sizeof (*argsp), KM_SLEEP); 307 VN_HOLD(fntovn(fnp)); 308 argsp->fnc_fnp = fnp; 309 argsp->fnc_name = kmem_alloc(strlen(name) + 1, KM_SLEEP); 310 (void) strcpy(argsp->fnc_name, name); 311 argsp->fnc_origin = curthread; 312 crhold(cred); 313 argsp->fnc_cred = cred; 314 315 (void) zthread_create(NULL, 0, auto_mount_thread, argsp, 0, 316 minclsyspri); 317 autofs_thr_success++; 318 } 319 320 #define DOOR_BUF_ALIGN (1024*1024) 321 #define DOOR_BUF_MULTIPLIER 3 322 #define DOOR_BUF_DEFAULT_SZ (DOOR_BUF_MULTIPLIER * DOOR_BUF_ALIGN) 323 int doorbuf_defsz = DOOR_BUF_DEFAULT_SZ; 324 325 /*ARGSUSED*/ 326 int 327 auto_calldaemon( 328 zoneid_t zoneid, 329 int which, 330 xdrproc_t xarg_func, 331 void *argsp, 332 xdrproc_t xresp_func, 333 void *resp, 334 int reslen, 335 bool_t hard) /* retry forever? */ 336 { 337 int retry; 338 int error = 0; 339 k_sigset_t smask; 340 door_arg_t door_args; 341 door_handle_t dh; 342 XDR xdrarg; 343 XDR xdrres; 344 struct autofs_globals *fngp = NULL; 345 void *orp = NULL; 346 int orl; 347 int rlen = 0; /* MUST be initialized */ 348 autofs_door_args_t *xdr_argsp; 349 int xdr_len = 0; 350 int printed_not_running_msg = 0; 351 klwp_t *lwp = ttolwp(curthread); 352 353 /* 354 * We know that the current thread is doing work on 355 * behalf of its own zone, so it's ok to use 356 * curproc->p_zone. 357 */ 358 ASSERT(zoneid == getzoneid()); 359 if (zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN) { 360 /* 361 * There's no point in trying to talk to 362 * automountd. Plus, zone_shutdown() is 363 * waiting for us. 364 */ 365 return (ECONNREFUSED); 366 } 367 368 do { 369 retry = 0; 370 mutex_enter(&autofs_minor_lock); 371 fngp = zone_getspecific(autofs_key, curproc->p_zone); 372 mutex_exit(&autofs_minor_lock); 373 if (fngp == NULL) { 374 if (hard) { 375 AUTOFS_DPRINT((5, 376 "auto_calldaemon: "\ 377 "failed to get door handle\n")); 378 if (!printed_not_running_msg) { 379 printed_not_running_msg = 1; 380 zprintf(zoneid, "automountd not "\ 381 "running, retrying\n"); 382 } 383 delay(hz); 384 retry = 1; 385 } else { 386 /* 387 * There is no global data so no door. 388 * There's no point in attempting to talk 389 * to automountd if we can't get the door 390 * handle. 391 */ 392 return (ECONNREFUSED); 393 } 394 } 395 } while (retry); 396 397 if (printed_not_running_msg) { 398 fngp->fng_printed_not_running_msg = printed_not_running_msg; 399 } 400 401 ASSERT(fngp != NULL); 402 403 if (argsp != NULL && (xdr_len = xdr_sizeof(xarg_func, argsp)) == 0) 404 return (EINVAL); 405 xdr_argsp = kmem_zalloc(xdr_len + sizeof (*xdr_argsp), KM_SLEEP); 406 xdr_argsp->xdr_len = xdr_len; 407 xdr_argsp->cmd = which; 408 409 if (argsp) { 410 xdrmem_create(&xdrarg, (char *)&xdr_argsp->xdr_arg, 411 xdr_argsp->xdr_len, XDR_ENCODE); 412 413 if (!(*xarg_func)(&xdrarg, argsp)) { 414 kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp)); 415 return (EINVAL); 416 } 417 } 418 419 /* 420 * We're saving off the original pointer and length due to the 421 * possibility that the results buffer returned by the door 422 * upcall can be different then what we passed in. This is because 423 * the door will allocate new memory if the results buffer passed 424 * in isn't large enough to hold what we need to send back. 425 * In this case we need to free the memory originally allocated 426 * for that buffer. 427 */ 428 if (resp) 429 rlen = xdr_sizeof(xresp_func, resp); 430 orl = (rlen == 0) ? doorbuf_defsz : MAX(rlen, doorbuf_defsz); 431 orp = kmem_zalloc(orl, KM_SLEEP); 432 433 do { 434 retry = 0; 435 mutex_enter(&fngp->fng_autofs_daemon_lock); 436 dh = fngp->fng_autofs_daemon_dh; 437 if (dh) 438 door_ki_hold(dh); 439 mutex_exit(&fngp->fng_autofs_daemon_lock); 440 441 if (dh == NULL) { 442 if (orp) 443 kmem_free(orp, orl); 444 kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp)); 445 return (ENOENT); 446 } 447 door_args.data_ptr = (char *)xdr_argsp; 448 door_args.data_size = sizeof (*xdr_argsp) + xdr_argsp->xdr_len; 449 door_args.desc_ptr = NULL; 450 door_args.desc_num = 0; 451 door_args.rbuf = orp ? (char *)orp : NULL; 452 door_args.rsize = orl; 453 454 sigintr(&smask, 1); 455 error = 456 door_ki_upcall_limited(dh, &door_args, NULL, SIZE_MAX, 0); 457 sigunintr(&smask); 458 459 door_ki_rele(dh); 460 461 /* 462 * Handle daemon errors 463 */ 464 if (!error) { 465 /* 466 * Upcall successful. Let's check for soft errors 467 * from the daemon. We only recover from overflow 468 * type scenarios. Any other errors, we return to 469 * the caller. 470 */ 471 autofs_door_res_t *adr = 472 (autofs_door_res_t *)door_args.rbuf; 473 474 if (door_args.rbuf != NULL) { 475 int nl; 476 477 switch (error = adr->res_status) { 478 case 0: /* no error; continue */ 479 break; 480 481 case EOVERFLOW: 482 /* 483 * orig landing buf not big enough. 484 * xdr_len in XDR_BYTES_PER_UNIT 485 */ 486 if ((nl = adr->xdr_len) > 0 && 487 (btopr(nl) < freemem/64)) { 488 if (orp) 489 kmem_free(orp, orl); 490 orp = kmem_zalloc(nl, KM_SLEEP); 491 orl = nl; 492 retry = 1; 493 break; 494 } 495 /*FALLTHROUGH*/ 496 497 default: 498 kmem_free(xdr_argsp, 499 xdr_len + sizeof (*xdr_argsp)); 500 if (orp) 501 kmem_free(orp, orl); 502 return (error); 503 } 504 } 505 continue; 506 } 507 508 /* 509 * no daemon errors; now process door/comm errors (if any) 510 */ 511 switch (error) { 512 case EINTR: 513 /* 514 * interrupts should be handled properly by the 515 * door upcall. If the door doesn't handle the 516 * interupt completely then we need to bail out. 517 */ 518 if (lwp && (ISSIG(curthread, 519 JUSTLOOKING) || MUSTRETURN(curproc, curthread))) { 520 if (ISSIG(curthread, FORREAL) || 521 lwp->lwp_sysabort || 522 MUSTRETURN(curproc, curthread)) { 523 lwp->lwp_sysabort = 0; 524 return (EINTR); 525 } 526 } 527 /* 528 * We may have gotten EINTR for other reasons 529 * like the door being revoked on us. Instead 530 * of trying to extract this out of the door 531 * handle, sleep and try again, if still 532 * revoked we will get EBADF next time 533 * through. 534 * 535 * If we have a pending cancellation and we don't 536 * have cancellation disabled, we will get EINTR 537 * forever, no matter how many times we retry, 538 * so just get out now if this is the case. 539 */ 540 if (schedctl_cancel_pending()) 541 break; 542 /* FALLTHROUGH */ 543 case EAGAIN: /* process may be forking */ 544 /* 545 * Back off for a bit 546 */ 547 delay(hz); 548 retry = 1; 549 break; 550 case EBADF: /* Invalid door */ 551 case EINVAL: /* Not a door, wrong target */ 552 /* 553 * A fatal door error, if our failing door 554 * handle is the current door handle, clean 555 * up our state. 556 */ 557 mutex_enter(&fngp->fng_autofs_daemon_lock); 558 if (dh == fngp->fng_autofs_daemon_dh) { 559 door_ki_rele(fngp->fng_autofs_daemon_dh); 560 fngp->fng_autofs_daemon_dh = NULL; 561 } 562 mutex_exit(&fngp->fng_autofs_daemon_lock); 563 AUTOFS_DPRINT((5, "auto_calldaemon error=%d\n", error)); 564 if (hard) { 565 if (!fngp->fng_printed_not_running_msg) { 566 fngp->fng_printed_not_running_msg = 1; 567 zprintf(zoneid, "automountd not " 568 "running, retrying\n"); 569 } 570 delay(hz); 571 retry = 1; 572 break; 573 } else { 574 error = ECONNREFUSED; 575 kmem_free(xdr_argsp, 576 xdr_len + sizeof (*xdr_argsp)); 577 if (orp) 578 kmem_free(orp, orl); 579 return (error); 580 } 581 default: /* Unknown must be fatal */ 582 error = ENOENT; 583 kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp)); 584 if (orp) 585 kmem_free(orp, orl); 586 return (error); 587 } 588 } while (retry); 589 590 if (fngp->fng_printed_not_running_msg == 1) { 591 fngp->fng_printed_not_running_msg = 0; 592 zprintf(zoneid, "automountd OK\n"); 593 } 594 595 if (orp && orl) { 596 autofs_door_res_t *door_resp; 597 door_resp = (autofs_door_res_t *)door_args.rbuf; 598 599 if ((void *)door_args.rbuf != orp) 600 kmem_free(orp, orl); 601 602 xdrmem_create(&xdrres, (char *)&door_resp->xdr_res, 603 door_resp->xdr_len, XDR_DECODE); 604 605 if (!((*xresp_func)(&xdrres, resp))) 606 error = EINVAL; 607 kmem_free(door_args.rbuf, door_args.rsize); 608 } 609 kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp)); 610 return (error); 611 } 612 613 static int 614 auto_null_request(zoneid_t zoneid, bool_t hard) 615 { 616 int error; 617 618 AUTOFS_DPRINT((4, "\tauto_null_request\n")); 619 620 error = auto_calldaemon(zoneid, NULLPROC, 621 xdr_void, NULL, xdr_void, NULL, 0, hard); 622 623 AUTOFS_DPRINT((5, "\tauto_null_request: error=%d\n", error)); 624 return (error); 625 } 626 627 static int 628 auto_lookup_request( 629 fninfo_t *fnip, 630 char *key, 631 struct linka *lnp, 632 bool_t hard, 633 bool_t *mountreq, 634 cred_t *cred) 635 { 636 int error; 637 struct autofs_globals *fngp; 638 struct autofs_lookupargs reqst; 639 autofs_lookupres *resp; 640 struct linka *p; 641 642 643 AUTOFS_DPRINT((4, "auto_lookup_equest: path=%s name=%s\n", 644 fnip->fi_path, key)); 645 646 fngp = vntofn(fnip->fi_rootvp)->fn_globals; 647 648 reqst.map = fnip->fi_map; 649 reqst.path = fnip->fi_path; 650 651 if (fnip->fi_flags & MF_DIRECT) 652 reqst.name = fnip->fi_key; 653 else 654 reqst.name = key; 655 AUTOFS_DPRINT((4, "auto_lookup_request: using key=%s\n", reqst.name)); 656 657 reqst.subdir = fnip->fi_subdir; 658 reqst.opts = fnip->fi_opts; 659 reqst.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE; 660 reqst.uid = crgetuid(cred); 661 662 resp = kmem_zalloc(sizeof (*resp), KM_SLEEP); 663 664 error = auto_calldaemon(fngp->fng_zoneid, AUTOFS_LOOKUP, 665 xdr_autofs_lookupargs, &reqst, xdr_autofs_lookupres, 666 (void *)resp, sizeof (autofs_lookupres), hard); 667 668 if (error) { 669 xdr_free(xdr_autofs_lookupres, (char *)resp); 670 kmem_free(resp, sizeof (*resp)); 671 return (error); 672 } 673 674 if (!error) { 675 fngp->fng_verbose = resp->lu_verbose; 676 switch (resp->lu_res) { 677 case AUTOFS_OK: 678 switch (resp->lu_type.action) { 679 case AUTOFS_MOUNT_RQ: 680 lnp->link = NULL; 681 lnp->dir = NULL; 682 *mountreq = TRUE; 683 break; 684 685 case AUTOFS_LINK_RQ: 686 p = &resp->lu_type.lookup_result_type_u.lt_linka; 687 lnp->dir = kmem_alloc(strlen(p->dir) + 1, 688 KM_SLEEP); 689 (void) strcpy(lnp->dir, p->dir); 690 lnp->link = kmem_alloc(strlen(p->link) + 1, 691 KM_SLEEP); 692 (void) strcpy(lnp->link, p->link); 693 break; 694 695 case AUTOFS_NONE: 696 lnp->link = NULL; 697 lnp->dir = NULL; 698 break; 699 700 default: 701 auto_log(fngp->fng_verbose, fngp->fng_zoneid, 702 CE_WARN, "auto_lookup_request: bad action " 703 "type %d", resp->lu_res); 704 error = ENOENT; 705 } 706 break; 707 708 case AUTOFS_NOENT: 709 error = ENOENT; 710 break; 711 712 default: 713 error = ENOENT; 714 auto_log(fngp->fng_verbose, fngp->fng_zoneid, CE_WARN, 715 "auto_lookup_request: unknown result: %d", 716 resp->lu_res); 717 break; 718 } 719 } 720 721 xdr_free(xdr_autofs_lookupres, (char *)resp); 722 kmem_free(resp, sizeof (*resp)); 723 AUTOFS_DPRINT((5, "auto_lookup_request: path=%s name=%s error=%d\n", 724 fnip->fi_path, key, error)); 725 return (error); 726 } 727 728 static int 729 auto_mount_request( 730 fninfo_t *fnip, 731 char *key, 732 action_list **alpp, 733 cred_t *cred, 734 bool_t hard) 735 { 736 int error; 737 struct autofs_globals *fngp; 738 autofs_lookupargs reqst; 739 autofs_mountres *xdrres = NULL; 740 741 AUTOFS_DPRINT((4, "auto_mount_request: path=%s name=%s\n", 742 fnip->fi_path, key)); 743 744 fngp = vntofn(fnip->fi_rootvp)->fn_globals; 745 reqst.map = fnip->fi_map; 746 reqst.path = fnip->fi_path; 747 748 if (fnip->fi_flags & MF_DIRECT) 749 reqst.name = fnip->fi_key; 750 else 751 reqst.name = key; 752 753 AUTOFS_DPRINT((4, "auto_mount_request: using key=%s\n", reqst.name)); 754 755 reqst.subdir = fnip->fi_subdir; 756 reqst.opts = fnip->fi_opts; 757 reqst.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE; 758 reqst.uid = crgetuid(cred); 759 760 xdrres = kmem_zalloc(sizeof (*xdrres), KM_SLEEP); 761 762 error = auto_calldaemon(fngp->fng_zoneid, AUTOFS_MNTINFO, 763 xdr_autofs_lookupargs, &reqst, xdr_autofs_mountres, 764 (void *)xdrres, sizeof (autofs_mountres), hard); 765 766 if (!error) { 767 fngp->fng_verbose = xdrres->mr_verbose; 768 switch (xdrres->mr_type.status) { 769 case AUTOFS_ACTION: 770 error = 0; 771 /* 772 * Save the action list since it is used by 773 * the caller. We NULL the action list pointer 774 * in 'result' so that xdr_free() will not free 775 * the list. 776 */ 777 *alpp = xdrres->mr_type.mount_result_type_u.list; 778 xdrres->mr_type.mount_result_type_u.list = NULL; 779 break; 780 case AUTOFS_DONE: 781 error = xdrres->mr_type.mount_result_type_u.error; 782 break; 783 default: 784 error = ENOENT; 785 auto_log(fngp->fng_verbose, fngp->fng_zoneid, CE_WARN, 786 "auto_mount_request: unknown status %d", 787 xdrres->mr_type.status); 788 break; 789 } 790 } 791 792 xdr_free(xdr_autofs_mountres, (char *)xdrres); 793 kmem_free(xdrres, sizeof (*xdrres)); 794 795 796 AUTOFS_DPRINT((5, "auto_mount_request: path=%s name=%s error=%d\n", 797 fnip->fi_path, key, error)); 798 return (error); 799 } 800 801 802 static int 803 auto_send_unmount_request( 804 fninfo_t *fnip, 805 umntrequest *ul, 806 bool_t hard) 807 { 808 int error; 809 umntres xdrres; 810 811 struct autofs_globals *fngp = vntofn(fnip->fi_rootvp)->fn_globals; 812 813 AUTOFS_DPRINT((4, "\tauto_send_unmount_request: fstype=%s " 814 " mntpnt=%s\n", ul->fstype, ul->mntpnt)); 815 816 bzero(&xdrres, sizeof (umntres)); 817 error = auto_calldaemon(fngp->fng_zoneid, AUTOFS_UNMOUNT, 818 xdr_umntrequest, (void *)ul, xdr_umntres, (void *)&xdrres, 819 sizeof (umntres), hard); 820 821 if (!error) 822 error = xdrres.status; 823 824 AUTOFS_DPRINT((5, "\tauto_send_unmount_request: error=%d\n", error)); 825 826 return (error); 827 } 828 829 static int 830 auto_perform_link(fnnode_t *fnp, struct linka *linkp, cred_t *cred) 831 { 832 vnode_t *vp; 833 size_t len; 834 char *tmp; 835 836 AUTOFS_DPRINT((3, "auto_perform_link: fnp=%p dir=%s link=%s\n", 837 (void *)fnp, linkp->dir, linkp->link)); 838 839 len = strlen(linkp->link) + 1; /* include '\0' */ 840 tmp = kmem_zalloc(len, KM_SLEEP); 841 (void) kcopy(linkp->link, tmp, len); 842 mutex_enter(&fnp->fn_lock); 843 fnp->fn_symlink = tmp; 844 fnp->fn_symlinklen = (uint_t)len; 845 fnp->fn_flags |= MF_THISUID_MATCH_RQD; 846 crhold(cred); 847 fnp->fn_cred = cred; 848 mutex_exit(&fnp->fn_lock); 849 850 vp = fntovn(fnp); 851 vp->v_type = VLNK; 852 853 return (0); 854 } 855 856 static void 857 auto_free_autofs_args(struct mounta *m) 858 { 859 autofs_args *aargs = (autofs_args *)m->dataptr; 860 861 if (aargs->addr.buf) 862 kmem_free(aargs->addr.buf, aargs->addr.len); 863 if (aargs->path) 864 kmem_free(aargs->path, strlen(aargs->path) + 1); 865 if (aargs->opts) 866 kmem_free(aargs->opts, strlen(aargs->opts) + 1); 867 if (aargs->map) 868 kmem_free(aargs->map, strlen(aargs->map) + 1); 869 if (aargs->subdir) 870 kmem_free(aargs->subdir, strlen(aargs->subdir) + 1); 871 if (aargs->key) 872 kmem_free(aargs->key, strlen(aargs->key) + 1); 873 kmem_free(aargs, sizeof (*aargs)); 874 } 875 876 static void 877 auto_free_action_list(action_list *alp) 878 { 879 struct mounta *m; 880 action_list *lastalp; 881 char *fstype; 882 883 m = &alp->action.action_list_entry_u.mounta; 884 while (alp != NULL) { 885 fstype = alp->action.action_list_entry_u.mounta.fstype; 886 m = &alp->action.action_list_entry_u.mounta; 887 if (m->dataptr) { 888 if (strcmp(fstype, "autofs") == 0) { 889 auto_free_autofs_args(m); 890 } 891 } 892 if (m->spec) 893 kmem_free(m->spec, strlen(m->spec) + 1); 894 if (m->dir) 895 kmem_free(m->dir, strlen(m->dir) + 1); 896 if (m->fstype) 897 kmem_free(m->fstype, strlen(m->fstype) + 1); 898 if (m->optptr) 899 kmem_free(m->optptr, m->optlen); 900 lastalp = alp; 901 alp = alp->next; 902 kmem_free(lastalp, sizeof (*lastalp)); 903 } 904 } 905 906 static boolean_t 907 auto_invalid_autofs(fninfo_t *dfnip, fnnode_t *dfnp, action_list *p) 908 { 909 struct mounta *m; 910 struct autofs_args *argsp; 911 vnode_t *dvp; 912 char buff[AUTOFS_MAXPATHLEN]; 913 size_t len; 914 struct autofs_globals *fngp; 915 916 fngp = dfnp->fn_globals; 917 dvp = fntovn(dfnp); 918 919 m = &p->action.action_list_entry_u.mounta; 920 /* 921 * Make sure we aren't geting passed NULL values or a "dir" that 922 * isn't "." and doesn't begin with "./". 923 * 924 * We also only want to perform autofs mounts, so make sure 925 * no-one is trying to trick us into doing anything else. 926 */ 927 if (m->spec == NULL || m->dir == NULL || m->dir[0] != '.' || 928 (m->dir[1] != '/' && m->dir[1] != '\0') || 929 m->fstype == NULL || strcmp(m->fstype, "autofs") != 0 || 930 m->dataptr == NULL || m->datalen != sizeof (struct autofs_args) || 931 m->optptr == NULL) 932 return (B_TRUE); 933 /* 934 * We also don't like ".."s in the pathname. Symlinks are 935 * handled by the fact that we'll use NOFOLLOW when we do 936 * lookup()s. 937 */ 938 if (strstr(m->dir, "/../") != NULL || 939 (len = strlen(m->dir)) > sizeof ("/..") - 1 && 940 m->dir[len] == '.' && m->dir[len - 1] == '.' && 941 m->dir[len - 2] == '/') 942 return (B_TRUE); 943 argsp = (struct autofs_args *)m->dataptr; 944 /* 945 * We don't want NULL values here either. 946 */ 947 if (argsp->addr.buf == NULL || argsp->path == NULL || 948 argsp->opts == NULL || argsp->map == NULL || argsp->subdir == NULL) 949 return (B_TRUE); 950 /* 951 * We know what the claimed pathname *should* look like: 952 * 953 * If the parent (dfnp) is a mount point (VROOT), then 954 * the path should be (dfnip->fi_path + m->dir). 955 * 956 * Else, we know we're only two levels deep, so we use 957 * (dfnip->fi_path + dfnp->fn_name + m->dir). 958 * 959 * Furthermore, "." only makes sense if dfnp is a 960 * trigger node. 961 * 962 * At this point it seems like the passed-in path is 963 * redundant. 964 */ 965 if (dvp->v_flag & VROOT) { 966 if (m->dir[1] == '\0' && !(dfnp->fn_flags & MF_TRIGGER)) 967 return (B_TRUE); 968 (void) snprintf(buff, sizeof (buff), "%s%s", 969 dfnip->fi_path, m->dir + 1); 970 } else { 971 (void) snprintf(buff, sizeof (buff), "%s/%s%s", 972 dfnip->fi_path, dfnp->fn_name, m->dir + 1); 973 } 974 if (strcmp(argsp->path, buff) != 0) { 975 auto_log(fngp->fng_verbose, fngp->fng_zoneid, 976 CE_WARN, "autofs: expected path of '%s', " 977 "got '%s' instead.", buff, argsp->path); 978 return (B_TRUE); 979 } 980 return (B_FALSE); /* looks OK */ 981 } 982 983 /* 984 * auto_invalid_action will validate the action_list received. If all is good 985 * this function returns FALSE, if there is a problem it returns TRUE. 986 */ 987 static boolean_t 988 auto_invalid_action(fninfo_t *dfnip, fnnode_t *dfnp, action_list *alistpp) 989 { 990 991 /* 992 * Before we go any further, this better be a mount request. 993 */ 994 if (alistpp->action.action != AUTOFS_MOUNT_RQ) 995 return (B_TRUE); 996 return (auto_invalid_autofs(dfnip, dfnp, alistpp)); 997 998 } 999 1000 static int 1001 auto_perform_actions( 1002 fninfo_t *dfnip, 1003 fnnode_t *dfnp, 1004 action_list *alp, 1005 cred_t *cred) /* Credentials of the caller */ 1006 { 1007 1008 action_list *p; 1009 struct mounta *m, margs; 1010 struct autofs_args *argsp; 1011 int error, success = 0; 1012 vnode_t *mvp, *dvp, *newvp; 1013 fnnode_t *newfnp, *mfnp; 1014 int auto_mount = 0; 1015 int save_triggers = 0; 1016 int update_times = 0; 1017 char *mntpnt; 1018 char buff[AUTOFS_MAXPATHLEN]; 1019 timestruc_t now; 1020 struct autofs_globals *fngp; 1021 cred_t *zcred; 1022 1023 AUTOFS_DPRINT((4, "auto_perform_actions: alp=%p\n", (void *)alp)); 1024 1025 fngp = dfnp->fn_globals; 1026 dvp = fntovn(dfnp); 1027 1028 /* 1029 * As automountd running in a zone may be compromised, and this may be 1030 * an attack, we can't trust everything passed in by automountd, and we 1031 * need to do argument verification. We'll issue a warning and drop 1032 * the request if it doesn't seem right. 1033 */ 1034 1035 for (p = alp; p != NULL; p = p->next) { 1036 if (auto_invalid_action(dfnip, dfnp, p)) { 1037 /* 1038 * This warning should be sent to the global zone, 1039 * since presumably the zone administrator is the same 1040 * as the attacker. 1041 */ 1042 cmn_err(CE_WARN, "autofs: invalid action list received " 1043 "by automountd in zone %s.", 1044 curproc->p_zone->zone_name); 1045 /* 1046 * This conversation is over. 1047 */ 1048 xdr_free(xdr_action_list, (char *)alp); 1049 return (EINVAL); 1050 } 1051 } 1052 1053 zcred = zone_get_kcred(getzoneid()); 1054 ASSERT(zcred != NULL); 1055 1056 if (vn_mountedvfs(dvp) != NULL) { 1057 /* 1058 * The daemon successfully mounted a filesystem 1059 * on the AUTOFS root node. 1060 */ 1061 mutex_enter(&dfnp->fn_lock); 1062 dfnp->fn_flags |= MF_MOUNTPOINT; 1063 ASSERT(dfnp->fn_dirents == NULL); 1064 mutex_exit(&dfnp->fn_lock); 1065 success++; 1066 } else { 1067 /* 1068 * Clear MF_MOUNTPOINT. 1069 */ 1070 mutex_enter(&dfnp->fn_lock); 1071 if (dfnp->fn_flags & MF_MOUNTPOINT) { 1072 AUTOFS_DPRINT((10, "autofs: clearing mountpoint " 1073 "flag on %s.", dfnp->fn_name)); 1074 ASSERT(dfnp->fn_dirents == NULL); 1075 ASSERT(dfnp->fn_trigger == NULL); 1076 } 1077 dfnp->fn_flags &= ~MF_MOUNTPOINT; 1078 mutex_exit(&dfnp->fn_lock); 1079 } 1080 1081 for (p = alp; p != NULL; p = p->next) { 1082 1083 vfs_t *vfsp; /* dummy argument */ 1084 vfs_t *mvfsp; 1085 1086 auto_mount = 0; 1087 1088 m = &p->action.action_list_entry_u.mounta; 1089 argsp = (struct autofs_args *)m->dataptr; 1090 ASSERT(strcmp(m->fstype, "autofs") == 0); 1091 /* 1092 * use the parent directory's timeout since it's the 1093 * one specified/inherited by automount. 1094 */ 1095 argsp->mount_to = dfnip->fi_mount_to; 1096 /* 1097 * The mountpoint is relative, and it is guaranteed to 1098 * begin with "." 1099 * 1100 */ 1101 ASSERT(m->dir[0] == '.'); 1102 if (m->dir[0] == '.' && m->dir[1] == '\0') { 1103 /* 1104 * mounting on the trigger node 1105 */ 1106 mvp = dvp; 1107 VN_HOLD(mvp); 1108 goto mount; 1109 } 1110 /* 1111 * ignore "./" in front of mountpoint 1112 */ 1113 ASSERT(m->dir[1] == '/'); 1114 mntpnt = m->dir + 2; 1115 1116 AUTOFS_DPRINT((10, "\tdfnip->fi_path=%s\n", dfnip->fi_path)); 1117 AUTOFS_DPRINT((10, "\tdfnip->fi_flags=%x\n", dfnip->fi_flags)); 1118 AUTOFS_DPRINT((10, "\tmntpnt=%s\n", mntpnt)); 1119 1120 if (dfnip->fi_flags & MF_DIRECT) { 1121 AUTOFS_DPRINT((10, "\tDIRECT\n")); 1122 (void) sprintf(buff, "%s/%s", dfnip->fi_path, mntpnt); 1123 } else { 1124 AUTOFS_DPRINT((10, "\tINDIRECT\n")); 1125 (void) sprintf(buff, "%s/%s/%s", 1126 dfnip->fi_path, dfnp->fn_name, mntpnt); 1127 } 1128 1129 if (vn_mountedvfs(dvp) == NULL) { 1130 /* 1131 * Daemon didn't mount anything on the root 1132 * We have to create the mountpoint if it 1133 * doesn't exist already 1134 * 1135 * We use the caller's credentials in case a 1136 * UID-match is required 1137 * (MF_THISUID_MATCH_RQD). 1138 */ 1139 rw_enter(&dfnp->fn_rwlock, RW_WRITER); 1140 error = auto_search(dfnp, mntpnt, &mfnp, cred); 1141 if (error == 0) { 1142 /* 1143 * AUTOFS mountpoint exists 1144 */ 1145 if (vn_mountedvfs(fntovn(mfnp)) != NULL) { 1146 cmn_err(CE_PANIC, 1147 "auto_perform_actions:" 1148 " mfnp=%p covered", (void *)mfnp); 1149 } 1150 } else { 1151 /* 1152 * Create AUTOFS mountpoint 1153 */ 1154 ASSERT((dfnp->fn_flags & MF_MOUNTPOINT) == 0); 1155 error = auto_enter(dfnp, mntpnt, &mfnp, cred); 1156 ASSERT(mfnp->fn_linkcnt == 1); 1157 mfnp->fn_linkcnt++; 1158 } 1159 if (!error) 1160 update_times = 1; 1161 rw_exit(&dfnp->fn_rwlock); 1162 ASSERT(error != EEXIST); 1163 if (!error) { 1164 /* 1165 * mfnp is already held. 1166 */ 1167 mvp = fntovn(mfnp); 1168 } else { 1169 auto_log(fngp->fng_verbose, fngp->fng_zoneid, 1170 CE_WARN, "autofs: mount of %s " 1171 "failed - can't create" 1172 " mountpoint.", buff); 1173 continue; 1174 } 1175 } else { 1176 /* 1177 * Find mountpoint in VFS mounted here. If not 1178 * found, fail the submount, though the overall 1179 * mount has succeeded since the root is 1180 * mounted. 1181 */ 1182 if (error = auto_getmntpnt(dvp, mntpnt, &mvp, kcred)) { 1183 auto_log(fngp->fng_verbose, fngp->fng_zoneid, 1184 CE_WARN, "autofs: mount of %s " 1185 "failed - mountpoint doesn't" 1186 " exist.", buff); 1187 continue; 1188 } 1189 if (mvp->v_type == VLNK) { 1190 auto_log(fngp->fng_verbose, fngp->fng_zoneid, 1191 CE_WARN, "autofs: %s symbolic " 1192 "link: not a valid mountpoint " 1193 "- mount failed", buff); 1194 VN_RELE(mvp); 1195 error = ENOENT; 1196 continue; 1197 } 1198 } 1199 mount: 1200 m->flags |= MS_SYSSPACE | MS_OPTIONSTR; 1201 1202 /* 1203 * Copy mounta struct here so we can substitute a 1204 * buffer that is large enough to hold the returned 1205 * option string, if that string is longer than the 1206 * input option string. 1207 * This can happen if there are default options enabled 1208 * that were not in the input option string. 1209 */ 1210 bcopy(m, &margs, sizeof (*m)); 1211 margs.optptr = kmem_alloc(MAX_MNTOPT_STR, KM_SLEEP); 1212 margs.optlen = MAX_MNTOPT_STR; 1213 (void) strcpy(margs.optptr, m->optptr); 1214 margs.dir = argsp->path; 1215 1216 /* 1217 * We use the zone's kcred because we don't want the 1218 * zone to be able to thus do something it wouldn't 1219 * normally be able to. 1220 */ 1221 error = domount(NULL, &margs, mvp, zcred, &vfsp); 1222 kmem_free(margs.optptr, MAX_MNTOPT_STR); 1223 if (error != 0) { 1224 auto_log(fngp->fng_verbose, fngp->fng_zoneid, 1225 CE_WARN, "autofs: domount of %s failed " 1226 "error=%d", buff, error); 1227 VN_RELE(mvp); 1228 continue; 1229 } 1230 VFS_RELE(vfsp); 1231 1232 /* 1233 * If mountpoint is an AUTOFS node, then I'm going to 1234 * flag it that the Filesystem mounted on top was 1235 * mounted in the kernel so that the unmount can be 1236 * done inside the kernel as well. 1237 * I don't care to flag non-AUTOFS mountpoints when an 1238 * AUTOFS in-kernel mount was done on top, because the 1239 * unmount routine already knows that such case was 1240 * done in the kernel. 1241 */ 1242 if (vfs_matchops(dvp->v_vfsp, vfs_getops(mvp->v_vfsp))) { 1243 mfnp = vntofn(mvp); 1244 mutex_enter(&mfnp->fn_lock); 1245 mfnp->fn_flags |= MF_IK_MOUNT; 1246 mutex_exit(&mfnp->fn_lock); 1247 } 1248 1249 (void) vn_vfswlock_wait(mvp); 1250 mvfsp = vn_mountedvfs(mvp); 1251 if (mvfsp != NULL) { 1252 vfs_lock_wait(mvfsp); 1253 vn_vfsunlock(mvp); 1254 error = VFS_ROOT(mvfsp, &newvp); 1255 vfs_unlock(mvfsp); 1256 if (error) { 1257 /* 1258 * We've dropped the locks, so let's 1259 * get the mounted vfs again in case 1260 * it changed. 1261 */ 1262 (void) vn_vfswlock_wait(mvp); 1263 mvfsp = vn_mountedvfs(mvp); 1264 if (mvfsp != NULL) { 1265 error = dounmount(mvfsp, 0, CRED()); 1266 if (error) { 1267 cmn_err(CE_WARN, 1268 "autofs: could not unmount" 1269 " vfs=%p", (void *)mvfsp); 1270 } 1271 } else 1272 vn_vfsunlock(mvp); 1273 VN_RELE(mvp); 1274 continue; 1275 } 1276 } else { 1277 vn_vfsunlock(mvp); 1278 VN_RELE(mvp); 1279 continue; 1280 } 1281 1282 auto_mount = vfs_matchops(dvp->v_vfsp, 1283 vfs_getops(newvp->v_vfsp)); 1284 newfnp = vntofn(newvp); 1285 newfnp->fn_parent = dfnp; 1286 1287 /* 1288 * At this time we want to save the AUTOFS filesystem 1289 * as a trigger node. (We only do this if the mount 1290 * occurred on a node different from the root. 1291 * We look at the trigger nodes during 1292 * the automatic unmounting to make sure we remove them 1293 * as a unit and remount them as a unit if the 1294 * filesystem mounted at the root could not be 1295 * unmounted. 1296 */ 1297 if (auto_mount && (error == 0) && (mvp != dvp)) { 1298 save_triggers++; 1299 /* 1300 * Add AUTOFS mount to hierarchy 1301 */ 1302 newfnp->fn_flags |= MF_TRIGGER; 1303 rw_enter(&newfnp->fn_rwlock, RW_WRITER); 1304 newfnp->fn_next = dfnp->fn_trigger; 1305 rw_exit(&newfnp->fn_rwlock); 1306 rw_enter(&dfnp->fn_rwlock, RW_WRITER); 1307 dfnp->fn_trigger = newfnp; 1308 rw_exit(&dfnp->fn_rwlock); 1309 /* 1310 * Don't VN_RELE(newvp) here since dfnp now 1311 * holds reference to it as its trigger node. 1312 */ 1313 AUTOFS_DPRINT((10, "\tadding trigger %s to %s\n", 1314 newfnp->fn_name, dfnp->fn_name)); 1315 AUTOFS_DPRINT((10, "\tfirst trigger is %s\n", 1316 dfnp->fn_trigger->fn_name)); 1317 if (newfnp->fn_next != NULL) 1318 AUTOFS_DPRINT((10, "\tnext trigger is %s\n", 1319 newfnp->fn_next->fn_name)); 1320 else 1321 AUTOFS_DPRINT((10, "\tno next trigger\n")); 1322 } else 1323 VN_RELE(newvp); 1324 1325 if (!error) 1326 success++; 1327 1328 if (update_times) { 1329 gethrestime(&now); 1330 dfnp->fn_atime = dfnp->fn_mtime = now; 1331 } 1332 1333 VN_RELE(mvp); 1334 } 1335 1336 if (save_triggers) { 1337 /* 1338 * Make sure the parent can't be freed while it has triggers. 1339 */ 1340 VN_HOLD(dvp); 1341 } 1342 1343 crfree(zcred); 1344 1345 /* 1346 * Return failure if daemon didn't mount anything, and all 1347 * kernel mounts attempted failed. 1348 */ 1349 error = success ? 0 : ENOENT; 1350 1351 if (alp != NULL) { 1352 if ((error == 0) && save_triggers) { 1353 /* 1354 * Save action_list information, so that we can use it 1355 * when it comes time to remount the trigger nodes 1356 * The action list is freed when the directory node 1357 * containing the reference to it is unmounted in 1358 * unmount_tree(). 1359 */ 1360 mutex_enter(&dfnp->fn_lock); 1361 ASSERT(dfnp->fn_alp == NULL); 1362 dfnp->fn_alp = alp; 1363 mutex_exit(&dfnp->fn_lock); 1364 } else { 1365 /* 1366 * free the action list now, 1367 */ 1368 xdr_free(xdr_action_list, (char *)alp); 1369 } 1370 } 1371 AUTOFS_DPRINT((5, "auto_perform_actions: error=%d\n", error)); 1372 return (error); 1373 } 1374 1375 fnnode_t * 1376 auto_makefnnode( 1377 vtype_t type, 1378 vfs_t *vfsp, 1379 char *name, 1380 cred_t *cred, 1381 struct autofs_globals *fngp) 1382 { 1383 fnnode_t *fnp; 1384 vnode_t *vp; 1385 char *tmpname; 1386 timestruc_t now; 1387 /* 1388 * autofs uses odd inode numbers 1389 * automountd uses even inode numbers 1390 * 1391 * To preserve the age-old semantics that inum+devid is unique across 1392 * the system, this variable must be global across zones. 1393 */ 1394 static ino_t nodeid = 3; 1395 1396 fnp = kmem_zalloc(sizeof (*fnp), KM_SLEEP); 1397 fnp->fn_vnode = vn_alloc(KM_SLEEP); 1398 1399 vp = fntovn(fnp); 1400 tmpname = kmem_alloc(strlen(name) + 1, KM_SLEEP); 1401 (void) strcpy(tmpname, name); 1402 fnp->fn_name = &tmpname[0]; 1403 fnp->fn_namelen = (int)strlen(tmpname) + 1; /* include '\0' */ 1404 fnp->fn_uid = crgetuid(cred); 1405 fnp->fn_gid = crgetgid(cred); 1406 /* 1407 * ".." is added in auto_enter and auto_mount. 1408 * "." is added in auto_mkdir and auto_mount. 1409 */ 1410 /* 1411 * Note that fn_size and fn_linkcnt are already 0 since 1412 * we used kmem_zalloc to allocated fnp 1413 */ 1414 fnp->fn_mode = AUTOFS_MODE; 1415 gethrestime(&now); 1416 fnp->fn_atime = fnp->fn_mtime = fnp->fn_ctime = now; 1417 fnp->fn_ref_time = now.tv_sec; 1418 mutex_enter(&autofs_nodeid_lock); 1419 fnp->fn_nodeid = nodeid; 1420 nodeid += 2; 1421 fnp->fn_globals = fngp; 1422 fngp->fng_fnnode_count++; 1423 mutex_exit(&autofs_nodeid_lock); 1424 vn_setops(vp, auto_vnodeops); 1425 vp->v_type = type; 1426 vp->v_data = (void *)fnp; 1427 vp->v_vfsp = vfsp; 1428 mutex_init(&fnp->fn_lock, NULL, MUTEX_DEFAULT, NULL); 1429 rw_init(&fnp->fn_rwlock, NULL, RW_DEFAULT, NULL); 1430 cv_init(&fnp->fn_cv_mount, NULL, CV_DEFAULT, NULL); 1431 vn_exists(vp); 1432 return (fnp); 1433 } 1434 1435 1436 void 1437 auto_freefnnode(fnnode_t *fnp) 1438 { 1439 vnode_t *vp = fntovn(fnp); 1440 1441 AUTOFS_DPRINT((4, "auto_freefnnode: fnp=%p\n", (void *)fnp)); 1442 1443 ASSERT(fnp->fn_linkcnt == 0); 1444 ASSERT(vp->v_count == 0); 1445 ASSERT(fnp->fn_dirents == NULL); 1446 ASSERT(fnp->fn_parent == NULL); 1447 1448 vn_invalid(vp); 1449 kmem_free(fnp->fn_name, fnp->fn_namelen); 1450 if (fnp->fn_symlink) { 1451 ASSERT(fnp->fn_flags & MF_THISUID_MATCH_RQD); 1452 kmem_free(fnp->fn_symlink, fnp->fn_symlinklen); 1453 } 1454 if (fnp->fn_cred) 1455 crfree(fnp->fn_cred); 1456 mutex_destroy(&fnp->fn_lock); 1457 rw_destroy(&fnp->fn_rwlock); 1458 cv_destroy(&fnp->fn_cv_mount); 1459 vn_free(vp); 1460 1461 mutex_enter(&autofs_nodeid_lock); 1462 fnp->fn_globals->fng_fnnode_count--; 1463 mutex_exit(&autofs_nodeid_lock); 1464 kmem_free(fnp, sizeof (*fnp)); 1465 } 1466 1467 void 1468 auto_disconnect( 1469 fnnode_t *dfnp, 1470 fnnode_t *fnp) 1471 { 1472 fnnode_t *tmp, **fnpp; 1473 vnode_t *vp = fntovn(fnp); 1474 timestruc_t now; 1475 1476 AUTOFS_DPRINT((4, 1477 "auto_disconnect: dfnp=%p fnp=%p linkcnt=%d\n v_count=%d", 1478 (void *)dfnp, (void *)fnp, fnp->fn_linkcnt, vp->v_count)); 1479 1480 ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock)); 1481 ASSERT(fnp->fn_linkcnt == 1); 1482 1483 if (vn_mountedvfs(vp) != NULL) { 1484 cmn_err(CE_PANIC, "auto_disconnect: vp %p mounted on", 1485 (void *)vp); 1486 } 1487 1488 /* 1489 * Decrement by 1 because we're removing the entry in dfnp. 1490 */ 1491 fnp->fn_linkcnt--; 1492 fnp->fn_size--; 1493 1494 /* 1495 * only changed while holding parent's (dfnp) rw_lock 1496 */ 1497 fnp->fn_parent = NULL; 1498 1499 fnpp = &dfnp->fn_dirents; 1500 for (;;) { 1501 tmp = *fnpp; 1502 if (tmp == NULL) { 1503 cmn_err(CE_PANIC, 1504 "auto_disconnect: %p not in %p dirent list", 1505 (void *)fnp, (void *)dfnp); 1506 } 1507 if (tmp == fnp) { 1508 *fnpp = tmp->fn_next; /* remove it from the list */ 1509 ASSERT(vp->v_count == 0); 1510 /* child had a pointer to parent ".." */ 1511 dfnp->fn_linkcnt--; 1512 dfnp->fn_size--; 1513 break; 1514 } 1515 fnpp = &tmp->fn_next; 1516 } 1517 1518 mutex_enter(&fnp->fn_lock); 1519 gethrestime(&now); 1520 fnp->fn_atime = fnp->fn_mtime = now; 1521 mutex_exit(&fnp->fn_lock); 1522 1523 AUTOFS_DPRINT((5, "auto_disconnect: done\n")); 1524 } 1525 1526 int 1527 auto_enter(fnnode_t *dfnp, char *name, fnnode_t **fnpp, cred_t *cred) 1528 { 1529 struct fnnode *cfnp, **spp; 1530 vnode_t *dvp = fntovn(dfnp); 1531 ushort_t offset = 0; 1532 ushort_t diff; 1533 1534 AUTOFS_DPRINT((4, "auto_enter: dfnp=%p, name=%s ", (void *)dfnp, name)); 1535 1536 ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock)); 1537 1538 cfnp = dfnp->fn_dirents; 1539 if (cfnp == NULL) { 1540 /* 1541 * offset = 0 for '.' and offset = 1 for '..' 1542 */ 1543 spp = &dfnp->fn_dirents; 1544 offset = 2; 1545 } 1546 1547 for (; cfnp; cfnp = cfnp->fn_next) { 1548 if (strcmp(cfnp->fn_name, name) == 0) { 1549 mutex_enter(&cfnp->fn_lock); 1550 if (cfnp->fn_flags & MF_THISUID_MATCH_RQD) { 1551 /* 1552 * "thisuser" kind of node, need to 1553 * match CREDs as well 1554 */ 1555 mutex_exit(&cfnp->fn_lock); 1556 if (crcmp(cfnp->fn_cred, cred) == 0) 1557 return (EEXIST); 1558 } else { 1559 mutex_exit(&cfnp->fn_lock); 1560 return (EEXIST); 1561 } 1562 } 1563 1564 if (cfnp->fn_next != NULL) { 1565 diff = (ushort_t) 1566 (cfnp->fn_next->fn_offset - cfnp->fn_offset); 1567 ASSERT(diff != 0); 1568 if (diff > 1 && offset == 0) { 1569 offset = (ushort_t)cfnp->fn_offset + 1; 1570 spp = &cfnp->fn_next; 1571 } 1572 } else if (offset == 0) { 1573 offset = (ushort_t)cfnp->fn_offset + 1; 1574 spp = &cfnp->fn_next; 1575 } 1576 } 1577 1578 *fnpp = auto_makefnnode(VDIR, dvp->v_vfsp, name, cred, 1579 dfnp->fn_globals); 1580 if (*fnpp == NULL) 1581 return (ENOMEM); 1582 1583 /* 1584 * I don't hold the mutex on fnpp because I created it, and 1585 * I'm already holding the writers lock for it's parent 1586 * directory, therefore nobody can reference it without me first 1587 * releasing the writers lock. 1588 */ 1589 (*fnpp)->fn_offset = offset; 1590 (*fnpp)->fn_next = *spp; 1591 *spp = *fnpp; 1592 (*fnpp)->fn_parent = dfnp; 1593 (*fnpp)->fn_linkcnt++; /* parent now holds reference to entry */ 1594 (*fnpp)->fn_size++; 1595 1596 /* 1597 * dfnp->fn_linkcnt and dfnp->fn_size protected by dfnp->rw_lock 1598 */ 1599 dfnp->fn_linkcnt++; /* child now holds reference to parent '..' */ 1600 dfnp->fn_size++; 1601 1602 dfnp->fn_ref_time = gethrestime_sec(); 1603 1604 AUTOFS_DPRINT((5, "*fnpp=%p\n", (void *)*fnpp)); 1605 return (0); 1606 } 1607 1608 int 1609 auto_search(fnnode_t *dfnp, char *name, fnnode_t **fnpp, cred_t *cred) 1610 { 1611 vnode_t *dvp; 1612 fnnode_t *p; 1613 int error = ENOENT, match = 0; 1614 1615 AUTOFS_DPRINT((4, "auto_search: dfnp=%p, name=%s...\n", 1616 (void *)dfnp, name)); 1617 1618 dvp = fntovn(dfnp); 1619 if (dvp->v_type != VDIR) { 1620 cmn_err(CE_PANIC, "auto_search: dvp=%p not a directory", 1621 (void *)dvp); 1622 } 1623 1624 ASSERT(RW_LOCK_HELD(&dfnp->fn_rwlock)); 1625 for (p = dfnp->fn_dirents; p != NULL; p = p->fn_next) { 1626 if (strcmp(p->fn_name, name) == 0) { 1627 mutex_enter(&p->fn_lock); 1628 if (p->fn_flags & MF_THISUID_MATCH_RQD) { 1629 /* 1630 * "thisuser" kind of node 1631 * Need to match CREDs as well 1632 */ 1633 mutex_exit(&p->fn_lock); 1634 match = crcmp(p->fn_cred, cred) == 0; 1635 } else { 1636 /* 1637 * No need to check CRED 1638 */ 1639 mutex_exit(&p->fn_lock); 1640 match = 1; 1641 } 1642 } 1643 if (match) { 1644 error = 0; 1645 if (fnpp) { 1646 *fnpp = p; 1647 VN_HOLD(fntovn(*fnpp)); 1648 } 1649 break; 1650 } 1651 } 1652 1653 AUTOFS_DPRINT((5, "auto_search: error=%d\n", error)); 1654 return (error); 1655 } 1656 1657 /* 1658 * If dvp is mounted on, get path's vnode in the mounted on 1659 * filesystem. Path is relative to dvp, ie "./path". 1660 * If successful, *mvp points to a the held mountpoint vnode. 1661 */ 1662 /* ARGSUSED */ 1663 static int 1664 auto_getmntpnt( 1665 vnode_t *dvp, 1666 char *path, 1667 vnode_t **mvpp, /* vnode for mountpoint */ 1668 cred_t *cred) 1669 { 1670 int error = 0; 1671 vnode_t *newvp; 1672 char namebuf[TYPICALMAXPATHLEN]; 1673 struct pathname lookpn; 1674 vfs_t *vfsp; 1675 1676 AUTOFS_DPRINT((4, "auto_getmntpnt: path=%s\n", path)); 1677 1678 if (error = vn_vfsrlock_wait(dvp)) 1679 return (error); 1680 1681 /* 1682 * Now that we have the vfswlock, check to see if dvp 1683 * is still mounted on. If not, then just bail out as 1684 * there is no need to remount the triggers since the 1685 * higher level mount point has gotten unmounted. 1686 */ 1687 vfsp = vn_mountedvfs(dvp); 1688 if (vfsp == NULL) { 1689 vn_vfsunlock(dvp); 1690 error = EBUSY; 1691 goto done; 1692 } 1693 /* 1694 * Since mounted on, lookup "path" in the new filesystem, 1695 * it is important that we do the filesystem jump here to 1696 * avoid lookuppn() calling auto_lookup on dvp and deadlock. 1697 */ 1698 error = VFS_ROOT(vfsp, &newvp); 1699 vn_vfsunlock(dvp); 1700 if (error) 1701 goto done; 1702 1703 /* 1704 * We do a VN_HOLD on newvp just in case the first call to 1705 * lookuppnvp() fails with ENAMETOOLONG. We should still have a 1706 * reference to this vnode for the second call to lookuppnvp(). 1707 */ 1708 VN_HOLD(newvp); 1709 1710 /* 1711 * Now create the pathname struct so we can make use of lookuppnvp, 1712 * and pn_getcomponent. 1713 * This code is similar to lookupname() in fs/lookup.c. 1714 */ 1715 error = pn_get_buf(path, UIO_SYSSPACE, &lookpn, 1716 namebuf, sizeof (namebuf)); 1717 if (error == 0) { 1718 error = lookuppnvp(&lookpn, NULL, NO_FOLLOW, NULLVPP, 1719 mvpp, rootdir, newvp, cred); 1720 } else 1721 VN_RELE(newvp); 1722 if (error == ENAMETOOLONG) { 1723 /* 1724 * This thread used a pathname > TYPICALMAXPATHLEN bytes long. 1725 * newvp is VN_RELE'd by this call to lookuppnvp. 1726 * 1727 * Using 'rootdir' in a zone's context is OK here: we already 1728 * ascertained that there are no '..'s in the path, and we're 1729 * not following symlinks. 1730 */ 1731 if ((error = pn_get(path, UIO_SYSSPACE, &lookpn)) == 0) { 1732 error = lookuppnvp(&lookpn, NULL, NO_FOLLOW, NULLVPP, 1733 mvpp, rootdir, newvp, cred); 1734 pn_free(&lookpn); 1735 } else 1736 VN_RELE(newvp); 1737 } else { 1738 /* 1739 * Need to release newvp here since we held it. 1740 */ 1741 VN_RELE(newvp); 1742 } 1743 1744 done: 1745 AUTOFS_DPRINT((5, "auto_getmntpnt: path=%s *mvpp=%p error=%d\n", 1746 path, (void *)*mvpp, error)); 1747 return (error); 1748 } 1749 1750 #define DEEPER(x) (((x)->fn_dirents != NULL) || \ 1751 (vn_mountedvfs(fntovn((x)))) != NULL) 1752 1753 /* 1754 * The caller, should have already VN_RELE'd its reference to the 1755 * root vnode of this filesystem. 1756 */ 1757 static int 1758 auto_inkernel_unmount(vfs_t *vfsp) 1759 { 1760 vnode_t *cvp = vfsp->vfs_vnodecovered; 1761 int error; 1762 1763 AUTOFS_DPRINT((4, 1764 "auto_inkernel_unmount: devid=%lx mntpnt(%p) count %u\n", 1765 vfsp->vfs_dev, (void *)cvp, cvp->v_count)); 1766 1767 ASSERT(vn_vfswlock_held(cvp)); 1768 1769 /* 1770 * Perform the unmount 1771 * The mountpoint has already been locked by the caller. 1772 */ 1773 error = dounmount(vfsp, 0, kcred); 1774 1775 AUTOFS_DPRINT((5, "auto_inkernel_unmount: exit count %u\n", 1776 cvp->v_count)); 1777 return (error); 1778 } 1779 1780 /* 1781 * unmounts trigger nodes in the kernel. 1782 */ 1783 static void 1784 unmount_triggers(fnnode_t *fnp, action_list **alp) 1785 { 1786 fnnode_t *tp, *next; 1787 int error = 0; 1788 vfs_t *vfsp; 1789 vnode_t *tvp; 1790 1791 AUTOFS_DPRINT((4, "unmount_triggers: fnp=%p\n", (void *)fnp)); 1792 ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock)); 1793 1794 *alp = fnp->fn_alp; 1795 next = fnp->fn_trigger; 1796 while ((tp = next) != NULL) { 1797 tvp = fntovn(tp); 1798 ASSERT(tvp->v_count >= 2); 1799 next = tp->fn_next; 1800 /* 1801 * drop writer's lock since the unmount will end up 1802 * disconnecting this node from fnp and needs to acquire 1803 * the writer's lock again. 1804 * next has at least a reference count >= 2 since it's 1805 * a trigger node, therefore can not be accidentally freed 1806 * by a VN_RELE 1807 */ 1808 rw_exit(&fnp->fn_rwlock); 1809 1810 vfsp = tvp->v_vfsp; 1811 1812 /* 1813 * Its parent was holding a reference to it, since this 1814 * is a trigger vnode. 1815 */ 1816 VN_RELE(tvp); 1817 if (error = auto_inkernel_unmount(vfsp)) { 1818 cmn_err(CE_PANIC, "unmount_triggers: " 1819 "unmount of vp=%p failed error=%d", 1820 (void *)tvp, error); 1821 } 1822 /* 1823 * reacquire writer's lock 1824 */ 1825 rw_enter(&fnp->fn_rwlock, RW_WRITER); 1826 } 1827 1828 /* 1829 * We were holding a reference to our parent. Drop that. 1830 */ 1831 VN_RELE(fntovn(fnp)); 1832 fnp->fn_trigger = NULL; 1833 fnp->fn_alp = NULL; 1834 1835 AUTOFS_DPRINT((5, "unmount_triggers: finished\n")); 1836 } 1837 1838 /* 1839 * This routine locks the mountpoint of every trigger node if they're 1840 * not busy, or returns EBUSY if any node is busy. 1841 */ 1842 static boolean_t 1843 triggers_busy(fnnode_t *fnp) 1844 { 1845 int done; 1846 int lck_error = 0; 1847 fnnode_t *tp, *t1p; 1848 vfs_t *vfsp; 1849 1850 ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock)); 1851 1852 for (tp = fnp->fn_trigger; tp != NULL; tp = tp->fn_next) { 1853 AUTOFS_DPRINT((10, "\ttrigger: %s\n", tp->fn_name)); 1854 /* MF_LOOKUP should never be set on trigger nodes */ 1855 ASSERT((tp->fn_flags & MF_LOOKUP) == 0); 1856 vfsp = fntovn(tp)->v_vfsp; 1857 1858 /* 1859 * The vn_vfsunlock will be done in auto_inkernel_unmount. 1860 */ 1861 lck_error = vn_vfswlock(vfsp->vfs_vnodecovered); 1862 1863 if (lck_error != 0 || (tp->fn_flags & MF_INPROG) || 1864 DEEPER(tp) || ((fntovn(tp))->v_count) > 2) { 1865 /* 1866 * couldn't lock it because it's busy, 1867 * It is mounted on or has dirents? 1868 * If reference count is greater than two, then 1869 * somebody else is holding a reference to this vnode. 1870 * One reference is for the mountpoint, and the second 1871 * is for the trigger node. 1872 */ 1873 AUTOFS_DPRINT((10, "\ttrigger busy\n")); 1874 1875 /* 1876 * Unlock previously locked mountpoints 1877 */ 1878 for (done = 0, t1p = fnp->fn_trigger; !done; 1879 t1p = t1p->fn_next) { 1880 /* 1881 * Unlock all nodes previously 1882 * locked. All nodes up to 'tp' 1883 * were successfully locked. If 'lck_err' is 1884 * set, then 'tp' was not locked, and thus 1885 * should not be unlocked. If 1886 * 'lck_err' is not set, then 'tp' was 1887 * successfully locked, and it should 1888 * be unlocked. 1889 */ 1890 if (t1p != tp || !lck_error) { 1891 vfsp = fntovn(t1p)->v_vfsp; 1892 vn_vfsunlock(vfsp->vfs_vnodecovered); 1893 } 1894 done = (t1p == tp); 1895 } 1896 return (B_TRUE); 1897 } 1898 } 1899 1900 return (B_FALSE); 1901 } 1902 1903 /* 1904 * It is the caller's responsibility to grab the VVFSLOCK. 1905 * Releases the VVFSLOCK upon return. 1906 */ 1907 static int 1908 unmount_node(vnode_t *cvp, int force) 1909 { 1910 int error = 0; 1911 fnnode_t *cfnp; 1912 vfs_t *vfsp; 1913 umntrequest ul; 1914 fninfo_t *fnip; 1915 1916 AUTOFS_DPRINT((4, "\tunmount_node cvp=%p\n", (void *)cvp)); 1917 1918 ASSERT(vn_vfswlock_held(cvp)); 1919 cfnp = vntofn(cvp); 1920 vfsp = vn_mountedvfs(cvp); 1921 1922 if (force || cfnp->fn_flags & MF_IK_MOUNT) { 1923 /* 1924 * Mount was performed in the kernel, so 1925 * do an in-kernel unmount. auto_inkernel_unmount() 1926 * will vn_vfsunlock(cvp). 1927 */ 1928 error = auto_inkernel_unmount(vfsp); 1929 } else { 1930 zone_t *zone = NULL; 1931 refstr_t *mntpt, *resource; 1932 size_t mntoptslen; 1933 1934 /* 1935 * Get the mnttab information of the node 1936 * and ask the daemon to unmount it. 1937 */ 1938 bzero(&ul, sizeof (ul)); 1939 mntfs_getmntopts(vfsp, &ul.mntopts, &mntoptslen); 1940 if (ul.mntopts == NULL) { 1941 auto_log(cfnp->fn_globals->fng_verbose, 1942 cfnp->fn_globals->fng_zoneid, CE_WARN, 1943 "unmount_node: no memory"); 1944 vn_vfsunlock(cvp); 1945 error = ENOMEM; 1946 goto done; 1947 } 1948 if (mntoptslen > AUTOFS_MAXOPTSLEN) 1949 ul.mntopts[AUTOFS_MAXOPTSLEN - 1] = '\0'; 1950 1951 mntpt = vfs_getmntpoint(vfsp); 1952 ul.mntpnt = (char *)refstr_value(mntpt); 1953 resource = vfs_getresource(vfsp); 1954 ul.mntresource = (char *)refstr_value(resource); 1955 1956 fnip = vfstofni(cvp->v_vfsp); 1957 ul.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE; 1958 1959 /* 1960 * Since a zone'd automountd's view of the autofs mount points 1961 * differs from those in the kernel, we need to make sure we 1962 * give it consistent mount points. 1963 */ 1964 ASSERT(fnip->fi_zoneid == getzoneid()); 1965 zone = curproc->p_zone; 1966 1967 if (fnip->fi_zoneid != GLOBAL_ZONEID) { 1968 if (ZONE_PATH_VISIBLE(ul.mntpnt, zone)) { 1969 ul.mntpnt = 1970 ZONE_PATH_TRANSLATE(ul.mntpnt, zone); 1971 } 1972 if (ZONE_PATH_VISIBLE(ul.mntresource, zone)) { 1973 ul.mntresource = 1974 ZONE_PATH_TRANSLATE(ul.mntresource, zone); 1975 } 1976 } 1977 1978 ul.fstype = vfssw[vfsp->vfs_fstype].vsw_name; 1979 vn_vfsunlock(cvp); 1980 1981 error = auto_send_unmount_request(fnip, &ul, FALSE); 1982 kmem_free(ul.mntopts, mntoptslen); 1983 refstr_rele(mntpt); 1984 refstr_rele(resource); 1985 } 1986 1987 done: 1988 AUTOFS_DPRINT((5, "\tunmount_node cvp=%p error=%d\n", (void *)cvp, 1989 error)); 1990 return (error); 1991 } 1992 1993 /* 1994 * return EBUSY if any thread is holding a reference to this vnode 1995 * other than us. Result of this function cannot be relied on, since 1996 * it doesn't follow proper locking rules (i.e. vp->v_vfsmountedhere 1997 * and fnp->fn_trigger can change throughout this function). However 1998 * it's good enough for rough estimation. 1999 */ 2000 static int 2001 check_auto_node(vnode_t *vp) 2002 { 2003 fnnode_t *fnp; 2004 int error = 0; 2005 /* 2006 * number of references to expect for 2007 * a non-busy vnode. 2008 */ 2009 uint_t count; 2010 2011 AUTOFS_DPRINT((4, "\tcheck_auto_node vp=%p ", (void *)vp)); 2012 fnp = vntofn(vp); 2013 2014 count = 1; /* we are holding a reference to vp */ 2015 if (fnp->fn_flags & MF_TRIGGER) { 2016 /* 2017 * parent holds a pointer to us (trigger) 2018 */ 2019 count++; 2020 } 2021 if (fnp->fn_trigger != NULL) { 2022 /* 2023 * The trigger nodes have a hold on us. 2024 */ 2025 count++; 2026 } 2027 if (vn_ismntpt(vp)) { 2028 /* 2029 * File system is mounted on us. 2030 */ 2031 count++; 2032 } 2033 mutex_enter(&vp->v_lock); 2034 ASSERT(vp->v_count > 0); 2035 if (vp->v_flag & VROOT) 2036 count++; 2037 AUTOFS_DPRINT((10, "\tcount=%u ", vp->v_count)); 2038 if (vp->v_count > count) 2039 error = EBUSY; 2040 mutex_exit(&vp->v_lock); 2041 2042 AUTOFS_DPRINT((5, "\tcheck_auto_node error=%d ", error)); 2043 return (error); 2044 } 2045 2046 /* 2047 * rootvp is the root of the AUTOFS filesystem. 2048 * If rootvp is busy (v_count > 1) returns EBUSY. 2049 * else removes every vnode under this tree. 2050 * ASSUMPTION: Assumes that the only node which can be busy is 2051 * the root vnode. This filesystem better be two levels deep only, 2052 * the root and its immediate subdirs. 2053 * The daemon will "AUTOFS direct-mount" only one level below the root. 2054 */ 2055 static void 2056 unmount_autofs(vnode_t *rootvp) 2057 { 2058 fnnode_t *fnp, *rootfnp, *nfnp; 2059 2060 AUTOFS_DPRINT((4, "\tunmount_autofs rootvp=%p ", (void *)rootvp)); 2061 2062 /* 2063 * Remove all its immediate subdirectories. 2064 */ 2065 rootfnp = vntofn(rootvp); 2066 rw_enter(&rootfnp->fn_rwlock, RW_WRITER); 2067 for (fnp = rootfnp->fn_dirents; fnp != NULL; fnp = nfnp) { 2068 ASSERT(fntovn(fnp)->v_count == 0); 2069 ASSERT(fnp->fn_dirents == NULL); 2070 ASSERT(fnp->fn_linkcnt == 2); 2071 fnp->fn_linkcnt--; 2072 auto_disconnect(rootfnp, fnp); 2073 nfnp = fnp->fn_next; 2074 auto_freefnnode(fnp); 2075 } 2076 rw_exit(&rootfnp->fn_rwlock); 2077 } 2078 2079 /* 2080 * If a node matches all unmount criteria, do: 2081 * destroy subordinate trigger node(s) if there is any 2082 * unmount filesystem mounted on top of the node if there is any 2083 * 2084 * Function should be called with locked fnp's mutex. The mutex is 2085 * unlocked before return from function. 2086 */ 2087 static int 2088 try_unmount_node(fnnode_t *fnp, boolean_t force) 2089 { 2090 boolean_t trigger_unmount = B_FALSE; 2091 action_list *alp = NULL; 2092 vnode_t *vp; 2093 int error = 0; 2094 fninfo_t *fnip; 2095 vfs_t *vfsp; 2096 struct autofs_globals *fngp; 2097 2098 AUTOFS_DPRINT((10, "\ttry_unmount_node: processing node %p\n", 2099 (void *)fnp)); 2100 2101 ASSERT(MUTEX_HELD(&fnp->fn_lock)); 2102 2103 fngp = fnp->fn_globals; 2104 vp = fntovn(fnp); 2105 fnip = vfstofni(vp->v_vfsp); 2106 2107 /* 2108 * If either a mount, lookup or another unmount of this subtree is in 2109 * progress, don't attempt to unmount at this time. 2110 */ 2111 if (fnp->fn_flags & (MF_INPROG | MF_LOOKUP)) { 2112 mutex_exit(&fnp->fn_lock); 2113 return (EBUSY); 2114 } 2115 2116 /* 2117 * Bail out if someone else is holding reference to this vnode. 2118 * This check isn't just an optimization (someone is probably 2119 * just about to trigger mount). It is necessary to prevent a deadlock 2120 * in domount() called from auto_perform_actions() if unmount of 2121 * trigger parent fails. domount() calls lookupname() to resolve 2122 * special in mount arguments. Special is set to a map name in case 2123 * of autofs triggers (i.e. auto_ws.sun.com). Thus if current 2124 * working directory is set to currently processed node, lookupname() 2125 * calls into autofs vnops in order to resolve special, which deadlocks 2126 * the process. 2127 * 2128 * Note: This should be fixed. Autofs shouldn't pass the map name 2129 * in special and avoid useless lookup with potentially disasterous 2130 * consequence. 2131 */ 2132 if (check_auto_node(vp) == EBUSY) { 2133 mutex_exit(&fnp->fn_lock); 2134 return (EBUSY); 2135 } 2136 2137 /* 2138 * If not forced operation, back out if node has been referenced 2139 * recently. 2140 */ 2141 if (!force && 2142 fnp->fn_ref_time + fnip->fi_mount_to > gethrestime_sec()) { 2143 mutex_exit(&fnp->fn_lock); 2144 return (EBUSY); 2145 } 2146 2147 /* block mounts/unmounts on the node */ 2148 AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG); 2149 fnp->fn_error = 0; 2150 mutex_exit(&fnp->fn_lock); 2151 2152 /* unmount next level triggers if there are any */ 2153 rw_enter(&fnp->fn_rwlock, RW_WRITER); 2154 if (fnp->fn_trigger != NULL) { 2155 trigger_unmount = B_TRUE; 2156 2157 if (triggers_busy(fnp)) { 2158 rw_exit(&fnp->fn_rwlock); 2159 mutex_enter(&fnp->fn_lock); 2160 AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG); 2161 mutex_exit(&fnp->fn_lock); 2162 return (EBUSY); 2163 } 2164 2165 /* 2166 * At this point, we know all trigger nodes are locked, 2167 * and they're not busy or mounted on. 2168 * 2169 * Attempt to unmount all trigger nodes, save the 2170 * action_list in case we need to remount them later. 2171 * The action_list will be freed later if there was no 2172 * need to remount the trigger nodes. 2173 */ 2174 unmount_triggers(fnp, &alp); 2175 } 2176 rw_exit(&fnp->fn_rwlock); 2177 2178 (void) vn_vfswlock_wait(vp); 2179 2180 vfsp = vn_mountedvfs(vp); 2181 if (vfsp != NULL) { 2182 /* vn_vfsunlock(vp) is done inside unmount_node() */ 2183 error = unmount_node(vp, force); 2184 if (error == ECONNRESET) { 2185 if (vn_mountedvfs(vp) == NULL) { 2186 /* 2187 * The filesystem was unmounted before the 2188 * daemon died. Unfortunately we can not 2189 * determine whether all the cleanup work was 2190 * successfully finished (i.e. update mnttab, 2191 * or notify NFS server of the unmount). 2192 * We should not retry the operation since the 2193 * filesystem has already been unmounted, and 2194 * may have already been removed from mnttab, 2195 * in such case the devid/rdevid we send to 2196 * the daemon will not be matched. So we have 2197 * to be content with the partial unmount. 2198 * Since the mountpoint is no longer covered, we 2199 * clear the error condition. 2200 */ 2201 error = 0; 2202 auto_log(fngp->fng_verbose, fngp->fng_zoneid, 2203 CE_WARN, "autofs: automountd " 2204 "connection dropped when unmounting %s/%s", 2205 fnip->fi_path, (fnip->fi_flags & MF_DIRECT) 2206 ? "" : fnp->fn_name); 2207 } 2208 } 2209 } else { 2210 vn_vfsunlock(vp); 2211 /* Destroy all dirents of fnp if we unmounted its triggers */ 2212 if (trigger_unmount) 2213 unmount_autofs(vp); 2214 } 2215 2216 /* If unmount failed, we got to remount triggers */ 2217 if (error != 0) { 2218 if (trigger_unmount) { 2219 int ret; 2220 2221 ASSERT((fnp->fn_flags & MF_THISUID_MATCH_RQD) == 0); 2222 2223 /* 2224 * The action list was free'd by auto_perform_actions 2225 */ 2226 ret = auto_perform_actions(fnip, fnp, alp, CRED()); 2227 if (ret != 0) { 2228 auto_log(fngp->fng_verbose, fngp->fng_zoneid, 2229 CE_WARN, "autofs: can't remount triggers " 2230 "fnp=%p error=%d", (void *)fnp, ret); 2231 } 2232 } 2233 mutex_enter(&fnp->fn_lock); 2234 AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG); 2235 mutex_exit(&fnp->fn_lock); 2236 } else { 2237 /* Free the action list here */ 2238 if (trigger_unmount) 2239 xdr_free(xdr_action_list, (char *)alp); 2240 2241 /* 2242 * Other threads may be waiting for this unmount to 2243 * finish. We must let it know that in order to 2244 * proceed, it must trigger the mount itself. 2245 */ 2246 mutex_enter(&fnp->fn_lock); 2247 fnp->fn_flags &= ~MF_IK_MOUNT; 2248 if (fnp->fn_flags & MF_WAITING) 2249 fnp->fn_error = EAGAIN; 2250 AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG); 2251 mutex_exit(&fnp->fn_lock); 2252 } 2253 2254 return (error); 2255 } 2256 2257 /* 2258 * This is an implementation of depth-first search in a tree rooted by 2259 * start_fnp and composed from fnnodes. Links between tree levels are 2260 * fn_dirents, fn_trigger in fnnode_t and v_mountedvfs in vnode_t (if 2261 * mounted vfs is autofs). The algorithm keeps track of visited nodes 2262 * by means of a timestamp (fn_unmount_ref_time). 2263 * 2264 * Upon top-down traversal of the tree we apply following locking scheme: 2265 * lock fn_rwlock of current node 2266 * grab reference to child's vnode (VN_HOLD) 2267 * unlock fn_rwlock 2268 * free reference to current vnode (VN_RELE) 2269 * Similar locking scheme is used for down-top and left-right traversal. 2270 * 2271 * Algorithm examines the most down-left node in tree, which hasn't been 2272 * visited yet. From this follows that nodes are processed in bottom-up 2273 * fashion. 2274 * 2275 * Function returns either zero if unmount of root node was successful 2276 * or error code (mostly EBUSY). 2277 */ 2278 int 2279 unmount_subtree(fnnode_t *rootfnp, boolean_t force) 2280 { 2281 fnnode_t *currfnp; /* currently examined node in the tree */ 2282 fnnode_t *lastfnp; /* previously processed node */ 2283 fnnode_t *nextfnp; /* next examined node in the tree */ 2284 vnode_t *curvp; 2285 vnode_t *newvp; 2286 vfs_t *vfsp; 2287 time_t timestamp; 2288 2289 ASSERT(fntovn(rootfnp)->v_type != VLNK); 2290 AUTOFS_DPRINT((10, "unmount_subtree: root=%p (%s)\n", (void *)rootfnp, 2291 rootfnp->fn_name)); 2292 2293 /* 2294 * Timestamp, which visited nodes are marked with, to distinguish them 2295 * from unvisited nodes. 2296 */ 2297 timestamp = gethrestime_sec(); 2298 currfnp = lastfnp = rootfnp; 2299 2300 /* Loop until we examine all nodes in the tree */ 2301 mutex_enter(&currfnp->fn_lock); 2302 while (currfnp != rootfnp || rootfnp->fn_unmount_ref_time < timestamp) { 2303 curvp = fntovn(currfnp); 2304 AUTOFS_DPRINT((10, "\tunmount_subtree: entering node %p (%s)\n", 2305 (void *)currfnp, currfnp->fn_name)); 2306 2307 /* 2308 * New candidate for processing must have been already visited, 2309 * by us because we want to process tree nodes in bottom-up 2310 * order. 2311 */ 2312 if (currfnp->fn_unmount_ref_time == timestamp && 2313 currfnp != lastfnp) { 2314 (void) try_unmount_node(currfnp, force); 2315 lastfnp = currfnp; 2316 mutex_enter(&currfnp->fn_lock); 2317 /* 2318 * Fall through to next if-branch to pick 2319 * sibling or parent of this node. 2320 */ 2321 } 2322 2323 /* 2324 * If this node has been already visited, it means that it's 2325 * dead end and we need to pick sibling or parent as next node. 2326 */ 2327 if (currfnp->fn_unmount_ref_time >= timestamp || 2328 curvp->v_type == VLNK) { 2329 mutex_exit(&currfnp->fn_lock); 2330 /* 2331 * Obtain parent's readers lock before grabbing 2332 * reference to sibling. 2333 */ 2334 rw_enter(&currfnp->fn_parent->fn_rwlock, RW_READER); 2335 if ((nextfnp = currfnp->fn_next) != NULL) { 2336 VN_HOLD(fntovn(nextfnp)); 2337 rw_exit(&currfnp->fn_parent->fn_rwlock); 2338 VN_RELE(curvp); 2339 currfnp = nextfnp; 2340 mutex_enter(&currfnp->fn_lock); 2341 continue; 2342 } 2343 rw_exit(&currfnp->fn_parent->fn_rwlock); 2344 2345 /* 2346 * All descendants and siblings were visited. Perform 2347 * bottom-up move. 2348 */ 2349 nextfnp = currfnp->fn_parent; 2350 VN_HOLD(fntovn(nextfnp)); 2351 VN_RELE(curvp); 2352 currfnp = nextfnp; 2353 mutex_enter(&currfnp->fn_lock); 2354 continue; 2355 } 2356 2357 /* 2358 * Mark node as visited. Note that the timestamp could have 2359 * been updated by somebody else in the meantime. 2360 */ 2361 if (currfnp->fn_unmount_ref_time < timestamp) 2362 currfnp->fn_unmount_ref_time = timestamp; 2363 2364 /* 2365 * Don't descent below nodes, which are being unmounted/mounted. 2366 * 2367 * We need to hold both locks at once: fn_lock because we need 2368 * to read MF_INPROG and fn_rwlock to prevent anybody from 2369 * modifying fn_trigger until its used to traverse triggers 2370 * below. 2371 * 2372 * Acquire fn_rwlock in non-blocking mode to avoid deadlock. 2373 * If it can't be acquired, then acquire locks in correct 2374 * order. 2375 */ 2376 if (!rw_tryenter(&currfnp->fn_rwlock, RW_READER)) { 2377 mutex_exit(&currfnp->fn_lock); 2378 rw_enter(&currfnp->fn_rwlock, RW_READER); 2379 mutex_enter(&currfnp->fn_lock); 2380 } 2381 if (currfnp->fn_flags & MF_INPROG) { 2382 rw_exit(&currfnp->fn_rwlock); 2383 continue; 2384 } 2385 mutex_exit(&currfnp->fn_lock); 2386 2387 /* 2388 * Examine descendants in this order: triggers, dirents, autofs 2389 * mounts. 2390 */ 2391 2392 if ((nextfnp = currfnp->fn_trigger) != NULL) { 2393 VN_HOLD(fntovn(nextfnp)); 2394 rw_exit(&currfnp->fn_rwlock); 2395 VN_RELE(curvp); 2396 currfnp = nextfnp; 2397 mutex_enter(&currfnp->fn_lock); 2398 continue; 2399 } 2400 2401 if ((nextfnp = currfnp->fn_dirents) != NULL) { 2402 VN_HOLD(fntovn(nextfnp)); 2403 rw_exit(&currfnp->fn_rwlock); 2404 VN_RELE(curvp); 2405 currfnp = nextfnp; 2406 mutex_enter(&currfnp->fn_lock); 2407 continue; 2408 } 2409 rw_exit(&currfnp->fn_rwlock); 2410 2411 (void) vn_vfswlock_wait(curvp); 2412 vfsp = vn_mountedvfs(curvp); 2413 if (vfsp != NULL && 2414 vfs_matchops(vfsp, vfs_getops(curvp->v_vfsp))) { 2415 /* 2416 * Deal with /xfn/host/jurassic alikes here... 2417 * 2418 * We know this call to VFS_ROOT is safe to call while 2419 * holding VVFSLOCK, since it resolves to a call to 2420 * auto_root(). 2421 */ 2422 if (VFS_ROOT(vfsp, &newvp)) { 2423 cmn_err(CE_PANIC, 2424 "autofs: VFS_ROOT(vfs=%p) failed", 2425 (void *)vfsp); 2426 } 2427 vn_vfsunlock(curvp); 2428 VN_RELE(curvp); 2429 currfnp = vntofn(newvp); 2430 mutex_enter(&currfnp->fn_lock); 2431 continue; 2432 } 2433 vn_vfsunlock(curvp); 2434 mutex_enter(&currfnp->fn_lock); 2435 } 2436 2437 /* 2438 * Now we deal with the root node (currfnp's mutex is unlocked 2439 * in try_unmount_node()). 2440 */ 2441 return (try_unmount_node(currfnp, force)); 2442 } 2443 2444 /* 2445 * XXX unmount_tree() is not suspend-safe within the scope of 2446 * the present model defined for cpr to suspend the system. Calls made 2447 * by the unmount_tree() that have been identified to be unsafe are 2448 * (1) RPC client handle setup and client calls to automountd which can 2449 * block deep down in the RPC library, (2) kmem_alloc() calls with the 2450 * KM_SLEEP flag which can block if memory is low, and (3) VFS_*() and 2451 * VOP_*() calls which can result in over the wire calls to servers. 2452 * The thread should be completely reevaluated to make it suspend-safe in 2453 * case of future updates to the cpr model. 2454 */ 2455 void 2456 unmount_tree(struct autofs_globals *fngp, boolean_t force) 2457 { 2458 callb_cpr_t cprinfo; 2459 kmutex_t unmount_tree_cpr_lock; 2460 fnnode_t *root, *fnp, *next; 2461 2462 mutex_init(&unmount_tree_cpr_lock, NULL, MUTEX_DEFAULT, NULL); 2463 CALLB_CPR_INIT(&cprinfo, &unmount_tree_cpr_lock, callb_generic_cpr, 2464 "unmount_tree"); 2465 2466 /* 2467 * autofssys() will be calling in from the global zone and doing 2468 * work on the behalf of the given zone, hence we can't always 2469 * assert that we have the right credentials, nor that the 2470 * caller is always in the correct zone. 2471 * 2472 * We do, however, know that if this is a "forced unmount" 2473 * operation (which autofssys() does), then we won't go down to 2474 * the krpc layers, so we don't need to fudge with the 2475 * credentials. 2476 */ 2477 ASSERT(force || fngp->fng_zoneid == getzoneid()); 2478 2479 /* 2480 * If automountd is not running in this zone, 2481 * don't attempt unmounting this round. 2482 */ 2483 if (force || auto_null_request(fngp->fng_zoneid, FALSE) == 0) { 2484 /* 2485 * Iterate over top level autofs filesystems and call 2486 * unmount_subtree() for each of them. 2487 */ 2488 root = fngp->fng_rootfnnodep; 2489 rw_enter(&root->fn_rwlock, RW_READER); 2490 for (fnp = root->fn_dirents; fnp != NULL; fnp = next) { 2491 VN_HOLD(fntovn(fnp)); 2492 rw_exit(&root->fn_rwlock); 2493 (void) unmount_subtree(fnp, force); 2494 rw_enter(&root->fn_rwlock, RW_READER); 2495 next = fnp->fn_next; 2496 VN_RELE(fntovn(fnp)); 2497 } 2498 rw_exit(&root->fn_rwlock); 2499 } 2500 2501 mutex_enter(&unmount_tree_cpr_lock); 2502 CALLB_CPR_EXIT(&cprinfo); 2503 mutex_destroy(&unmount_tree_cpr_lock); 2504 } 2505 2506 static void 2507 unmount_zone_tree(struct autofs_globals *fngp) 2508 { 2509 AUTOFS_DPRINT((5, "unmount_zone_tree started. Thread created.\n")); 2510 2511 unmount_tree(fngp, B_FALSE); 2512 mutex_enter(&fngp->fng_unmount_threads_lock); 2513 fngp->fng_unmount_threads--; 2514 mutex_exit(&fngp->fng_unmount_threads_lock); 2515 2516 AUTOFS_DPRINT((5, "unmount_zone_tree done. Thread exiting.\n")); 2517 2518 zthread_exit(); 2519 /* NOTREACHED */ 2520 } 2521 2522 void 2523 auto_do_unmount(struct autofs_globals *fngp) 2524 { 2525 callb_cpr_t cprinfo; 2526 clock_t timeleft; 2527 zone_t *zone = curproc->p_zone; 2528 2529 CALLB_CPR_INIT(&cprinfo, &fngp->fng_unmount_threads_lock, 2530 callb_generic_cpr, "auto_do_unmount"); 2531 2532 for (;;) { /* forever */ 2533 mutex_enter(&fngp->fng_unmount_threads_lock); 2534 CALLB_CPR_SAFE_BEGIN(&cprinfo); 2535 newthread: 2536 mutex_exit(&fngp->fng_unmount_threads_lock); 2537 timeleft = zone_status_timedwait(zone, ddi_get_lbolt() + 2538 autofs_unmount_thread_timer * hz, ZONE_IS_SHUTTING_DOWN); 2539 mutex_enter(&fngp->fng_unmount_threads_lock); 2540 2541 if (timeleft != -1) { /* didn't time out */ 2542 ASSERT(zone_status_get(zone) >= ZONE_IS_SHUTTING_DOWN); 2543 /* 2544 * zone is exiting... don't create any new threads. 2545 * fng_unmount_threads_lock is released implicitly by 2546 * the below. 2547 */ 2548 CALLB_CPR_SAFE_END(&cprinfo, 2549 &fngp->fng_unmount_threads_lock); 2550 CALLB_CPR_EXIT(&cprinfo); 2551 zthread_exit(); 2552 /* NOTREACHED */ 2553 } 2554 if (fngp->fng_unmount_threads < autofs_unmount_threads) { 2555 fngp->fng_unmount_threads++; 2556 CALLB_CPR_SAFE_END(&cprinfo, 2557 &fngp->fng_unmount_threads_lock); 2558 mutex_exit(&fngp->fng_unmount_threads_lock); 2559 2560 (void) zthread_create(NULL, 0, unmount_zone_tree, fngp, 2561 0, minclsyspri); 2562 } else 2563 goto newthread; 2564 } 2565 /* NOTREACHED */ 2566 } 2567 2568 /* 2569 * Is nobrowse specified in option string? 2570 * opts should be a null ('\0') terminated string. 2571 * Returns non-zero if nobrowse has been specified. 2572 */ 2573 int 2574 auto_nobrowse_option(char *opts) 2575 { 2576 char *buf; 2577 char *p; 2578 char *t; 2579 int nobrowse = 0; 2580 int last_opt = 0; 2581 size_t len; 2582 2583 len = strlen(opts) + 1; 2584 p = buf = kmem_alloc(len, KM_SLEEP); 2585 (void) strcpy(buf, opts); 2586 do { 2587 if (t = strchr(p, ',')) 2588 *t++ = '\0'; 2589 else 2590 last_opt++; 2591 if (strcmp(p, MNTOPT_NOBROWSE) == 0) 2592 nobrowse = 1; 2593 else if (strcmp(p, MNTOPT_BROWSE) == 0) 2594 nobrowse = 0; 2595 p = t; 2596 } while (!last_opt); 2597 kmem_free(buf, len); 2598 2599 return (nobrowse); 2600 } 2601 2602 /* 2603 * used to log warnings only if automountd is running 2604 * with verbose mode set 2605 */ 2606 2607 void 2608 auto_log(int verbose, zoneid_t zoneid, int level, const char *fmt, ...) 2609 { 2610 va_list args; 2611 2612 if (verbose) { 2613 va_start(args, fmt); 2614 vzcmn_err(zoneid, level, fmt, args); 2615 va_end(args); 2616 } 2617 } 2618 2619 #ifdef DEBUG 2620 static int autofs_debug = 0; 2621 2622 /* 2623 * Utilities used by both client and server 2624 * Standard levels: 2625 * 0) no debugging 2626 * 1) hard failures 2627 * 2) soft failures 2628 * 3) current test software 2629 * 4) main procedure entry points 2630 * 5) main procedure exit points 2631 * 6) utility procedure entry points 2632 * 7) utility procedure exit points 2633 * 8) obscure procedure entry points 2634 * 9) obscure procedure exit points 2635 * 10) random stuff 2636 * 11) all <= 1 2637 * 12) all <= 2 2638 * 13) all <= 3 2639 * ... 2640 */ 2641 /* PRINTFLIKE2 */ 2642 void 2643 auto_dprint(int level, const char *fmt, ...) 2644 { 2645 va_list args; 2646 2647 if (autofs_debug == level || 2648 (autofs_debug > 10 && (autofs_debug - 10) >= level)) { 2649 va_start(args, fmt); 2650 (void) vprintf(fmt, args); 2651 va_end(args); 2652 } 2653 } 2654 #endif /* DEBUG */ 2655