autofs.c revision 270900
1/*- 2 * Copyright (c) 2014 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Edward Tomasz Napierala under sponsorship 6 * from the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30/*- 31 * Copyright (c) 1989, 1991, 1993, 1995 32 * The Regents of the University of California. All rights reserved. 33 * 34 * This code is derived from software contributed to Berkeley by 35 * Rick Macklem at The University of Guelph. 36 * 37 * Redistribution and use in source and binary forms, with or without 38 * modification, are permitted provided that the following conditions 39 * are met: 40 * 1. Redistributions of source code must retain the above copyright 41 * notice, this list of conditions and the following disclaimer. 42 * 2. Redistributions in binary form must reproduce the above copyright 43 * notice, this list of conditions and the following disclaimer in the 44 * documentation and/or other materials provided with the distribution. 45 * 4. Neither the name of the University nor the names of its contributors 46 * may be used to endorse or promote products derived from this software 47 * without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 * 61 */ 62 63#include <sys/cdefs.h> 64 __FBSDID("$FreeBSD: stable/10/sys/fs/autofs/autofs.c 270900 2014-08-31 21:52:26Z trasz $"); 65 66#include <sys/param.h> 67#include <sys/systm.h> 68#include <sys/buf.h> 69#include <sys/conf.h> 70#include <sys/dirent.h> 71#include <sys/ioccom.h> 72#include <sys/kernel.h> 73#include <sys/module.h> 74#include <sys/mount.h> 75#include <sys/refcount.h> 76#include <sys/sx.h> 77#include <sys/sysctl.h> 78#include <sys/syscallsubr.h> 79#include <sys/vnode.h> 80#include <machine/atomic.h> 81#include <vm/uma.h> 82 83#include <fs/autofs/autofs.h> 84#include <fs/autofs/autofs_ioctl.h> 85 86MALLOC_DEFINE(M_AUTOFS, "autofs", "Automounter filesystem"); 87 88uma_zone_t autofs_request_zone; 89uma_zone_t autofs_node_zone; 90 91static int autofs_open(struct cdev *dev, int flags, int fmt, 92 struct thread *td); 93static int autofs_close(struct cdev *dev, int flag, int fmt, 94 struct thread *td); 95static int autofs_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, 96 int mode, struct thread *td); 97 98static struct cdevsw autofs_cdevsw = { 99 .d_version = D_VERSION, 100 .d_open = autofs_open, 101 .d_close = autofs_close, 102 .d_ioctl = autofs_ioctl, 103 .d_name = "autofs", 104}; 105 106/* 107 * List of signals that can interrupt an autofs trigger. Might be a good 108 * idea to keep it synchronised with list in sys/fs/nfs/nfs_commonkrpc.c. 109 */ 110int autofs_sig_set[] = { 111 SIGINT, 112 SIGTERM, 113 SIGHUP, 114 SIGKILL, 115 SIGQUIT 116}; 117 118struct autofs_softc *autofs_softc; 119 120SYSCTL_NODE(_vfs, OID_AUTO, autofs, CTLFLAG_RD, 0, "Automounter filesystem"); 121int autofs_debug = 1; 122TUNABLE_INT("vfs.autofs.debug", &autofs_debug); 123SYSCTL_INT(_vfs_autofs, OID_AUTO, debug, CTLFLAG_RWTUN, 124 &autofs_debug, 1, "Enable debug messages"); 125int autofs_mount_on_stat = 0; 126TUNABLE_INT("vfs.autofs.mount_on_stat", &autofs_mount_on_stat); 127SYSCTL_INT(_vfs_autofs, OID_AUTO, mount_on_stat, CTLFLAG_RWTUN, 128 &autofs_mount_on_stat, 0, "Trigger mount on stat(2) on mountpoint"); 129int autofs_timeout = 30; 130TUNABLE_INT("vfs.autofs.timeout", &autofs_timeout); 131SYSCTL_INT(_vfs_autofs, OID_AUTO, timeout, CTLFLAG_RWTUN, 132 &autofs_timeout, 30, "Number of seconds to wait for automountd(8)"); 133int autofs_cache = 600; 134TUNABLE_INT("vfs.autofs.cache", &autofs_cache); 135SYSCTL_INT(_vfs_autofs, OID_AUTO, cache, CTLFLAG_RWTUN, 136 &autofs_cache, 600, "Number of seconds to wait before reinvoking " 137 "automountd(8) for any given file or directory"); 138int autofs_retry_attempts = 3; 139TUNABLE_INT("vfs.autofs.retry_attempts", &autofs_retry_attempts); 140SYSCTL_INT(_vfs_autofs, OID_AUTO, retry_attempts, CTLFLAG_RWTUN, 141 &autofs_retry_attempts, 3, "Number of attempts before failing mount"); 142int autofs_retry_delay = 1; 143TUNABLE_INT("vfs.autofs.retry_delay", &autofs_retry_delay); 144SYSCTL_INT(_vfs_autofs, OID_AUTO, retry_delay, CTLFLAG_RWTUN, 145 &autofs_retry_delay, 1, "Number of seconds before retrying"); 146int autofs_interruptible = 1; 147TUNABLE_INT("vfs.autofs.interruptible", &autofs_interruptible); 148SYSCTL_INT(_vfs_autofs, OID_AUTO, interruptible, CTLFLAG_RWTUN, 149 &autofs_interruptible, 1, "Allow requests to be interrupted by signal"); 150 151int 152autofs_init(struct vfsconf *vfsp) 153{ 154 int error; 155 156 KASSERT(autofs_softc == NULL, 157 ("softc %p, should be NULL", autofs_softc)); 158 159 autofs_softc = malloc(sizeof(*autofs_softc), M_AUTOFS, 160 M_WAITOK | M_ZERO); 161 162 autofs_request_zone = uma_zcreate("autofs_request", 163 sizeof(struct autofs_request), NULL, NULL, NULL, NULL, 164 UMA_ALIGN_PTR, 0); 165 autofs_node_zone = uma_zcreate("autofs_node", 166 sizeof(struct autofs_node), NULL, NULL, NULL, NULL, 167 UMA_ALIGN_PTR, 0); 168 169 TAILQ_INIT(&autofs_softc->sc_requests); 170 cv_init(&autofs_softc->sc_cv, "autofscv"); 171 sx_init(&autofs_softc->sc_lock, "autofslk"); 172 173 error = make_dev_p(MAKEDEV_CHECKNAME, &autofs_softc->sc_cdev, 174 &autofs_cdevsw, NULL, UID_ROOT, GID_WHEEL, 0600, "autofs"); 175 if (error != 0) { 176 AUTOFS_WARN("failed to create device node, error %d", error); 177 uma_zdestroy(autofs_request_zone); 178 uma_zdestroy(autofs_node_zone); 179 free(autofs_softc, M_AUTOFS); 180 181 return (error); 182 } 183 autofs_softc->sc_cdev->si_drv1 = autofs_softc; 184 185 return (0); 186} 187 188int 189autofs_uninit(struct vfsconf *vfsp) 190{ 191 192 sx_xlock(&autofs_softc->sc_lock); 193 if (autofs_softc->sc_dev_opened) { 194 sx_xunlock(&autofs_softc->sc_lock); 195 return (EBUSY); 196 } 197 if (autofs_softc->sc_cdev != NULL) 198 destroy_dev(autofs_softc->sc_cdev); 199 200 uma_zdestroy(autofs_request_zone); 201 uma_zdestroy(autofs_node_zone); 202 203 sx_xunlock(&autofs_softc->sc_lock); 204 /* 205 * XXX: Race with open? 206 */ 207 free(autofs_softc, M_AUTOFS); 208 209 return (0); 210} 211 212bool 213autofs_ignore_thread(const struct thread *td) 214{ 215 struct proc *p; 216 217 p = td->td_proc; 218 219 if (autofs_softc->sc_dev_opened == false) 220 return (false); 221 222 PROC_LOCK(p); 223 if (p->p_session->s_sid == autofs_softc->sc_dev_sid) { 224 PROC_UNLOCK(p); 225 return (true); 226 } 227 PROC_UNLOCK(p); 228 229 return (false); 230} 231 232static char * 233autofs_path(struct autofs_node *anp) 234{ 235 struct autofs_mount *amp; 236 char *path, *tmp; 237 238 amp = anp->an_mount; 239 240 path = strdup("", M_AUTOFS); 241 for (; anp->an_parent != NULL; anp = anp->an_parent) { 242 tmp = malloc(strlen(anp->an_name) + strlen(path) + 2, 243 M_AUTOFS, M_WAITOK); 244 strcpy(tmp, anp->an_name); 245 strcat(tmp, "/"); 246 strcat(tmp, path); 247 free(path, M_AUTOFS); 248 path = tmp; 249 } 250 251 tmp = malloc(strlen(amp->am_mountpoint) + strlen(path) + 2, 252 M_AUTOFS, M_WAITOK); 253 strcpy(tmp, amp->am_mountpoint); 254 strcat(tmp, "/"); 255 strcat(tmp, path); 256 free(path, M_AUTOFS); 257 path = tmp; 258 259 return (path); 260} 261 262static void 263autofs_callout(void *context) 264{ 265 struct autofs_request *ar; 266 267 ar = context; 268 269 sx_xlock(&autofs_softc->sc_lock); 270 AUTOFS_WARN("request %d for %s timed out after %d seconds", 271 ar->ar_id, ar->ar_path, autofs_timeout); 272 /* 273 * XXX: EIO perhaps? 274 */ 275 ar->ar_error = ETIMEDOUT; 276 ar->ar_done = true; 277 ar->ar_in_progress = false; 278 cv_broadcast(&autofs_softc->sc_cv); 279 sx_xunlock(&autofs_softc->sc_lock); 280} 281 282bool 283autofs_cached(struct autofs_node *anp, const char *component, int componentlen) 284{ 285 int error; 286 struct autofs_mount *amp; 287 288 amp = anp->an_mount; 289 290 AUTOFS_ASSERT_UNLOCKED(amp); 291 292 /* 293 * For top-level nodes we need to request automountd(8) 294 * assistance even if the node is marked as cached, 295 * but the requested subdirectory does not exist. This 296 * is necessary for wildcard indirect map keys to work. 297 */ 298 if (anp->an_parent == NULL && componentlen != 0) { 299 AUTOFS_LOCK(amp); 300 error = autofs_node_find(anp, component, componentlen, NULL); 301 AUTOFS_UNLOCK(amp); 302 if (error != 0) 303 return (false); 304 } 305 306 return (anp->an_cached); 307} 308 309static void 310autofs_cache_callout(void *context) 311{ 312 struct autofs_node *anp; 313 314 anp = context; 315 anp->an_cached = false; 316} 317 318/* 319 * The set/restore sigmask functions are used to (temporarily) overwrite 320 * the thread td_sigmask during triggering. 321 */ 322static void 323autofs_set_sigmask(sigset_t *oldset) 324{ 325 sigset_t newset; 326 int i; 327 328 SIGFILLSET(newset); 329 /* Remove the autofs set of signals from newset */ 330 PROC_LOCK(curproc); 331 mtx_lock(&curproc->p_sigacts->ps_mtx); 332 for (i = 0 ; i < sizeof(autofs_sig_set)/sizeof(int) ; i++) { 333 /* 334 * But make sure we leave the ones already masked 335 * by the process, i.e. remove the signal from the 336 * temporary signalmask only if it wasn't already 337 * in p_sigmask. 338 */ 339 if (!SIGISMEMBER(curthread->td_sigmask, autofs_sig_set[i]) && 340 !SIGISMEMBER(curproc->p_sigacts->ps_sigignore, 341 autofs_sig_set[i])) { 342 SIGDELSET(newset, autofs_sig_set[i]); 343 } 344 } 345 mtx_unlock(&curproc->p_sigacts->ps_mtx); 346 kern_sigprocmask(curthread, SIG_SETMASK, &newset, oldset, 347 SIGPROCMASK_PROC_LOCKED); 348 PROC_UNLOCK(curproc); 349} 350 351static void 352autofs_restore_sigmask(sigset_t *set) 353{ 354 355 kern_sigprocmask(curthread, SIG_SETMASK, set, NULL, 0); 356} 357 358static int 359autofs_trigger_one(struct autofs_node *anp, 360 const char *component, int componentlen) 361{ 362 sigset_t oldset; 363 struct autofs_mount *amp; 364 struct autofs_node *firstanp; 365 struct autofs_request *ar; 366 char *key, *path; 367 int error = 0, request_error, last; 368 369 amp = VFSTOAUTOFS(anp->an_vnode->v_mount); 370 371 sx_assert(&autofs_softc->sc_lock, SA_XLOCKED); 372 373 if (anp->an_parent == NULL) { 374 key = strndup(component, componentlen, M_AUTOFS); 375 } else { 376 for (firstanp = anp; firstanp->an_parent->an_parent != NULL; 377 firstanp = firstanp->an_parent) 378 continue; 379 key = strdup(firstanp->an_name, M_AUTOFS); 380 } 381 382 path = autofs_path(anp); 383 384 TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { 385 if (strcmp(ar->ar_path, path) != 0) 386 continue; 387 if (strcmp(ar->ar_key, key) != 0) 388 continue; 389 390 KASSERT(strcmp(ar->ar_from, amp->am_from) == 0, 391 ("from changed; %s != %s", ar->ar_from, amp->am_from)); 392 KASSERT(strcmp(ar->ar_prefix, amp->am_prefix) == 0, 393 ("prefix changed; %s != %s", 394 ar->ar_prefix, amp->am_prefix)); 395 KASSERT(strcmp(ar->ar_options, amp->am_options) == 0, 396 ("options changed; %s != %s", 397 ar->ar_options, amp->am_options)); 398 399 break; 400 } 401 402 if (ar != NULL) { 403 refcount_acquire(&ar->ar_refcount); 404 } else { 405 ar = uma_zalloc(autofs_request_zone, M_WAITOK | M_ZERO); 406 ar->ar_mount = amp; 407 408 ar->ar_id = 409 atomic_fetchadd_int(&autofs_softc->sc_last_request_id, 1); 410 strlcpy(ar->ar_from, amp->am_from, sizeof(ar->ar_from)); 411 strlcpy(ar->ar_path, path, sizeof(ar->ar_path)); 412 strlcpy(ar->ar_prefix, amp->am_prefix, sizeof(ar->ar_prefix)); 413 strlcpy(ar->ar_key, key, sizeof(ar->ar_key)); 414 strlcpy(ar->ar_options, 415 amp->am_options, sizeof(ar->ar_options)); 416 417 callout_init(&ar->ar_callout, 1); 418 callout_reset(&ar->ar_callout, 419 autofs_timeout * hz, autofs_callout, ar); 420 refcount_init(&ar->ar_refcount, 1); 421 TAILQ_INSERT_TAIL(&autofs_softc->sc_requests, ar, ar_next); 422 } 423 424 cv_broadcast(&autofs_softc->sc_cv); 425 while (ar->ar_done == false) { 426 if (autofs_interruptible != 0) { 427 autofs_set_sigmask(&oldset); 428 error = cv_wait_sig(&autofs_softc->sc_cv, 429 &autofs_softc->sc_lock); 430 autofs_restore_sigmask(&oldset); 431 if (error != 0) { 432 /* 433 * XXX: For some reson this returns -1 434 * instead of EINTR, wtf?! 435 */ 436 error = EINTR; 437 AUTOFS_WARN("cv_wait_sig for %s failed " 438 "with error %d", ar->ar_path, error); 439 break; 440 } 441 } else { 442 cv_wait(&autofs_softc->sc_cv, &autofs_softc->sc_lock); 443 } 444 } 445 446 request_error = ar->ar_error; 447 if (request_error != 0) { 448 AUTOFS_WARN("request for %s completed with error %d", 449 ar->ar_path, request_error); 450 } 451 452 last = refcount_release(&ar->ar_refcount); 453 if (last) { 454 TAILQ_REMOVE(&autofs_softc->sc_requests, ar, ar_next); 455 /* 456 * XXX: Is it safe? 457 */ 458 sx_xunlock(&autofs_softc->sc_lock); 459 callout_drain(&ar->ar_callout); 460 sx_xlock(&autofs_softc->sc_lock); 461 uma_zfree(autofs_request_zone, ar); 462 } 463 464 /* 465 * Note that we do not do negative caching on purpose. This 466 * way the user can retry access at any time, e.g. after fixing 467 * the failure reason, without waiting for cache timer to expire. 468 */ 469 if (error == 0 && request_error == 0 && autofs_cache > 0) { 470 anp->an_cached = true; 471 callout_reset(&anp->an_callout, autofs_cache * hz, 472 autofs_cache_callout, anp); 473 } 474 475 free(key, M_AUTOFS); 476 free(path, M_AUTOFS); 477 478 if (error != 0) 479 return (error); 480 return (request_error); 481} 482 483/* 484 * Send request to automountd(8) and wait for completion. 485 */ 486int 487autofs_trigger(struct autofs_node *anp, 488 const char *component, int componentlen) 489{ 490 int error; 491 492 for (;;) { 493 error = autofs_trigger_one(anp, component, componentlen); 494 if (error == 0) { 495 anp->an_retries = 0; 496 return (0); 497 } 498 if (error == EINTR) { 499 AUTOFS_DEBUG("trigger interrupted by signal, " 500 "not retrying"); 501 anp->an_retries = 0; 502 return (error); 503 } 504 anp->an_retries++; 505 if (anp->an_retries >= autofs_retry_attempts) { 506 AUTOFS_DEBUG("trigger failed %d times; returning " 507 "error %d", anp->an_retries, error); 508 anp->an_retries = 0; 509 return (error); 510 511 } 512 AUTOFS_DEBUG("trigger failed with error %d; will retry in " 513 "%d seconds, %d attempts left", error, autofs_retry_delay, 514 autofs_retry_attempts - anp->an_retries); 515 sx_xunlock(&autofs_softc->sc_lock); 516 pause("autofs_retry", autofs_retry_delay * hz); 517 sx_xlock(&autofs_softc->sc_lock); 518 } 519} 520 521static int 522autofs_ioctl_request(struct autofs_daemon_request *adr) 523{ 524 struct autofs_request *ar; 525 int error; 526 527 sx_xlock(&autofs_softc->sc_lock); 528 for (;;) { 529 TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { 530 if (ar->ar_done) 531 continue; 532 if (ar->ar_in_progress) 533 continue; 534 535 break; 536 } 537 538 if (ar != NULL) 539 break; 540 541 error = cv_wait_sig(&autofs_softc->sc_cv, 542 &autofs_softc->sc_lock); 543 if (error != 0) { 544 /* 545 * XXX: For some reson this returns -1 instead 546 * of EINTR, wtf?! 547 */ 548 error = EINTR; 549 sx_xunlock(&autofs_softc->sc_lock); 550 AUTOFS_DEBUG("failed with error %d", error); 551 return (error); 552 } 553 } 554 555 ar->ar_in_progress = true; 556 sx_xunlock(&autofs_softc->sc_lock); 557 558 adr->adr_id = ar->ar_id; 559 strlcpy(adr->adr_from, ar->ar_from, sizeof(adr->adr_from)); 560 strlcpy(adr->adr_path, ar->ar_path, sizeof(adr->adr_path)); 561 strlcpy(adr->adr_prefix, ar->ar_prefix, sizeof(adr->adr_prefix)); 562 strlcpy(adr->adr_key, ar->ar_key, sizeof(adr->adr_key)); 563 strlcpy(adr->adr_options, ar->ar_options, sizeof(adr->adr_options)); 564 565 PROC_LOCK(curproc); 566 autofs_softc->sc_dev_sid = curproc->p_session->s_sid; 567 PROC_UNLOCK(curproc); 568 569 return (0); 570} 571 572static int 573autofs_ioctl_done(struct autofs_daemon_done *add) 574{ 575 struct autofs_request *ar; 576 577 sx_xlock(&autofs_softc->sc_lock); 578 TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { 579 if (ar->ar_id == add->add_id) 580 break; 581 } 582 583 if (ar == NULL) { 584 sx_xunlock(&autofs_softc->sc_lock); 585 AUTOFS_DEBUG("id %d not found", add->add_id); 586 return (ESRCH); 587 } 588 589 ar->ar_error = add->add_error; 590 ar->ar_done = true; 591 ar->ar_in_progress = false; 592 cv_broadcast(&autofs_softc->sc_cv); 593 594 sx_xunlock(&autofs_softc->sc_lock); 595 596 return (0); 597} 598 599static int 600autofs_open(struct cdev *dev, int flags, int fmt, struct thread *td) 601{ 602 603 sx_xlock(&autofs_softc->sc_lock); 604 /* 605 * We must never block automountd(8) and its descendants, and we use 606 * session ID to determine that: we store session id of the process 607 * that opened the device, and then compare it with session ids 608 * of triggering processes. This means running a second automountd(8) 609 * instance would break the previous one. The check below prevents 610 * it from happening. 611 */ 612 if (autofs_softc->sc_dev_opened) { 613 sx_xunlock(&autofs_softc->sc_lock); 614 return (EBUSY); 615 } 616 617 autofs_softc->sc_dev_opened = true; 618 sx_xunlock(&autofs_softc->sc_lock); 619 620 return (0); 621} 622 623static int 624autofs_close(struct cdev *dev, int flag, int fmt, struct thread *td) 625{ 626 627 sx_xlock(&autofs_softc->sc_lock); 628 KASSERT(autofs_softc->sc_dev_opened, ("not opened?")); 629 autofs_softc->sc_dev_opened = false; 630 sx_xunlock(&autofs_softc->sc_lock); 631 632 return (0); 633} 634 635static int 636autofs_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, 637 struct thread *td) 638{ 639 640 KASSERT(autofs_softc->sc_dev_opened, ("not opened?")); 641 642 switch (cmd) { 643 case AUTOFSREQUEST: 644 return (autofs_ioctl_request( 645 (struct autofs_daemon_request *)arg)); 646 case AUTOFSDONE: 647 return (autofs_ioctl_done( 648 (struct autofs_daemon_done *)arg)); 649 default: 650 AUTOFS_DEBUG("invalid cmd %lx", cmd); 651 return (EINVAL); 652 } 653} 654