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$"); 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/taskqueue.h> 80 #include <sys/vnode.h> 81 #include <machine/atomic.h> 82 #include <vm/uma.h> 83 84 #include <fs/autofs/autofs.h> 85 #include <fs/autofs/autofs_ioctl.h> 86 87 MALLOC_DEFINE(M_AUTOFS, "autofs", "Automounter filesystem"); 88 89 uma_zone_t autofs_request_zone; 90 uma_zone_t autofs_node_zone; 91 92 static int autofs_open(struct cdev *dev, int flags, int fmt, 93 struct thread *td); 94 static int autofs_close(struct cdev *dev, int flag, int fmt, 95 struct thread *td); 96 static int autofs_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, 97 int mode, struct thread *td); 98 99 static struct cdevsw autofs_cdevsw = { 100 .d_version = D_VERSION, 101 .d_open = autofs_open, 102 .d_close = autofs_close, 103 .d_ioctl = autofs_ioctl, 104 .d_name = "autofs", 105 }; 106 107 /* 108 * List of signals that can interrupt an autofs trigger. Might be a good 109 * idea to keep it synchronised with list in sys/fs/nfs/nfs_commonkrpc.c. 110 */ 111 int autofs_sig_set[] = { 112 SIGINT, 113 SIGTERM, 114 SIGHUP, 115 SIGKILL, 116 SIGQUIT 117 }; 118 119 struct autofs_softc *autofs_softc; 120 121 SYSCTL_NODE(_vfs, OID_AUTO, autofs, CTLFLAG_RD, 0, "Automounter filesystem"); 122 int autofs_debug = 1; 123 TUNABLE_INT("vfs.autofs.debug", &autofs_debug); 124 SYSCTL_INT(_vfs_autofs, OID_AUTO, debug, CTLFLAG_RWTUN, 125 &autofs_debug, 1, "Enable debug messages"); 126 int autofs_mount_on_stat = 0; 127 TUNABLE_INT("vfs.autofs.mount_on_stat", &autofs_mount_on_stat); 128 SYSCTL_INT(_vfs_autofs, OID_AUTO, mount_on_stat, CTLFLAG_RWTUN, 129 &autofs_mount_on_stat, 0, "Trigger mount on stat(2) on mountpoint"); 130 int autofs_timeout = 30; 131 TUNABLE_INT("vfs.autofs.timeout", &autofs_timeout); 132 SYSCTL_INT(_vfs_autofs, OID_AUTO, timeout, CTLFLAG_RWTUN, 133 &autofs_timeout, 30, "Number of seconds to wait for automountd(8)"); 134 int autofs_cache = 600; 135 TUNABLE_INT("vfs.autofs.cache", &autofs_cache); 136 SYSCTL_INT(_vfs_autofs, OID_AUTO, cache, CTLFLAG_RWTUN, 137 &autofs_cache, 600, "Number of seconds to wait before reinvoking " 138 "automountd(8) for any given file or directory"); 139 int autofs_retry_attempts = 3; 140 TUNABLE_INT("vfs.autofs.retry_attempts", &autofs_retry_attempts); 141 SYSCTL_INT(_vfs_autofs, OID_AUTO, retry_attempts, CTLFLAG_RWTUN, 142 &autofs_retry_attempts, 3, "Number of attempts before failing mount"); 143 int autofs_retry_delay = 1; 144 TUNABLE_INT("vfs.autofs.retry_delay", &autofs_retry_delay); 145 SYSCTL_INT(_vfs_autofs, OID_AUTO, retry_delay, CTLFLAG_RWTUN, 146 &autofs_retry_delay, 1, "Number of seconds before retrying"); 147 int autofs_interruptible = 1; 148 TUNABLE_INT("vfs.autofs.interruptible", &autofs_interruptible); 149 SYSCTL_INT(_vfs_autofs, OID_AUTO, interruptible, CTLFLAG_RWTUN, 150 &autofs_interruptible, 1, "Allow requests to be interrupted by signal"); 151 152 int 153 autofs_init(struct vfsconf *vfsp) 154 { 155 int error; 156 157 KASSERT(autofs_softc == NULL, 158 ("softc %p, should be NULL", autofs_softc)); 159 160 autofs_softc = malloc(sizeof(*autofs_softc), M_AUTOFS, 161 M_WAITOK | M_ZERO); 162 163 autofs_request_zone = uma_zcreate("autofs_request", 164 sizeof(struct autofs_request), NULL, NULL, NULL, NULL, 165 UMA_ALIGN_PTR, 0); 166 autofs_node_zone = uma_zcreate("autofs_node", 167 sizeof(struct autofs_node), NULL, NULL, NULL, NULL, 168 UMA_ALIGN_PTR, 0); 169 170 TAILQ_INIT(&autofs_softc->sc_requests); 171 cv_init(&autofs_softc->sc_cv, "autofscv"); 172 sx_init(&autofs_softc->sc_lock, "autofslk"); 173 174 error = make_dev_p(MAKEDEV_CHECKNAME, &autofs_softc->sc_cdev, 175 &autofs_cdevsw, NULL, UID_ROOT, GID_WHEEL, 0600, "autofs"); 176 if (error != 0) { 177 AUTOFS_WARN("failed to create device node, error %d", error); 178 uma_zdestroy(autofs_request_zone); 179 uma_zdestroy(autofs_node_zone); 180 free(autofs_softc, M_AUTOFS); 181 182 return (error); 183 } 184 autofs_softc->sc_cdev->si_drv1 = autofs_softc; 185 186 return (0); 187 } 188 189 int 190 autofs_uninit(struct vfsconf *vfsp) 191 { 192 193 sx_xlock(&autofs_softc->sc_lock); 194 if (autofs_softc->sc_dev_opened) { 195 sx_xunlock(&autofs_softc->sc_lock); 196 return (EBUSY); 197 } 198 if (autofs_softc->sc_cdev != NULL) 199 destroy_dev(autofs_softc->sc_cdev); 200 201 uma_zdestroy(autofs_request_zone); 202 uma_zdestroy(autofs_node_zone); 203 204 sx_xunlock(&autofs_softc->sc_lock); 205 /* 206 * XXX: Race with open? 207 */ 208 free(autofs_softc, M_AUTOFS); 209 210 return (0); 211 } 212 213 bool 214 autofs_ignore_thread(const struct thread *td) 215 { 216 struct proc *p; 217 218 p = td->td_proc; 219 220 if (autofs_softc->sc_dev_opened == false) 221 return (false); 222 223 PROC_LOCK(p); 224 if (p->p_session->s_sid == autofs_softc->sc_dev_sid) { 225 PROC_UNLOCK(p); 226 return (true); 227 } 228 PROC_UNLOCK(p); 229 230 return (false); 231 } 232 233 static char * 234 autofs_path(struct autofs_node *anp) 235 { 236 struct autofs_mount *amp; 237 char *path, *tmp; 238 239 amp = anp->an_mount; 240 241 path = strdup("", M_AUTOFS); 242 for (; anp->an_parent != NULL; anp = anp->an_parent) { 243 tmp = malloc(strlen(anp->an_name) + strlen(path) + 2, 244 M_AUTOFS, M_WAITOK); 245 strcpy(tmp, anp->an_name); 246 strcat(tmp, "/"); 247 strcat(tmp, path); 248 free(path, M_AUTOFS); 249 path = tmp; 250 } 251 252 tmp = malloc(strlen(amp->am_mountpoint) + strlen(path) + 2, 253 M_AUTOFS, M_WAITOK); 254 strcpy(tmp, amp->am_mountpoint); 255 strcat(tmp, "/"); 256 strcat(tmp, path); 257 free(path, M_AUTOFS); 258 path = tmp; 259 260 return (path); 261 } 262 263 static void 264 autofs_task(void *context, int pending) 265 { 266 struct autofs_request *ar; 267 268 ar = context; 269 270 sx_xlock(&autofs_softc->sc_lock); 271 AUTOFS_WARN("request %d for %s timed out after %d seconds", 272 ar->ar_id, ar->ar_path, autofs_timeout); 273 /* 274 * XXX: EIO perhaps? 275 */ 276 ar->ar_error = ETIMEDOUT; 277 ar->ar_done = true; 278 ar->ar_in_progress = false; 279 cv_broadcast(&autofs_softc->sc_cv); 280 sx_xunlock(&autofs_softc->sc_lock); 281 } 282 283 bool 284 autofs_cached(struct autofs_node *anp, const char *component, int componentlen) 285 { 286 int error; 287 struct autofs_mount *amp; 288 289 amp = anp->an_mount; 290 291 AUTOFS_ASSERT_UNLOCKED(amp); 292 293 /* 294 * For top-level nodes we need to request automountd(8) 295 * assistance even if the node is marked as cached, 296 * but the requested subdirectory does not exist. This 297 * is necessary for wildcard indirect map keys to work. 298 */ 299 if (anp->an_parent == NULL && componentlen != 0) { 300 AUTOFS_SLOCK(amp); 301 error = autofs_node_find(anp, component, componentlen, NULL); 302 AUTOFS_SUNLOCK(amp); 303 if (error != 0) 304 return (false); 305 } 306 307 return (anp->an_cached); 308 } 309 310 static void 311 autofs_cache_callout(void *context) 312 { 313 struct autofs_node *anp; 314 315 anp = context; 316 anp->an_cached = false; 317 } 318 319 /* 320 * The set/restore sigmask functions are used to (temporarily) overwrite 321 * the thread td_sigmask during triggering. 322 */ 323 static void 324 autofs_set_sigmask(sigset_t *oldset) 325 { 326 sigset_t newset; 327 int i; 328 329 SIGFILLSET(newset); 330 /* Remove the autofs set of signals from newset */ 331 PROC_LOCK(curproc); 332 mtx_lock(&curproc->p_sigacts->ps_mtx); 333 for (i = 0 ; i < sizeof(autofs_sig_set)/sizeof(int) ; i++) { 334 /* 335 * But make sure we leave the ones already masked 336 * by the process, i.e. remove the signal from the 337 * temporary signalmask only if it wasn't already 338 * in p_sigmask. 339 */ 340 if (!SIGISMEMBER(curthread->td_sigmask, autofs_sig_set[i]) && 341 !SIGISMEMBER(curproc->p_sigacts->ps_sigignore, 342 autofs_sig_set[i])) { 343 SIGDELSET(newset, autofs_sig_set[i]); 344 } 345 } 346 mtx_unlock(&curproc->p_sigacts->ps_mtx); 347 kern_sigprocmask(curthread, SIG_SETMASK, &newset, oldset, 348 SIGPROCMASK_PROC_LOCKED); 349 PROC_UNLOCK(curproc); 350 } 351 352 static void 353 autofs_restore_sigmask(sigset_t *set) 354 { 355 356 kern_sigprocmask(curthread, SIG_SETMASK, set, NULL, 0); 357 } 358 359 static int 360 autofs_trigger_one(struct autofs_node *anp, 361 const char *component, int componentlen) 362 { 363 sigset_t oldset; 364 struct autofs_mount *amp; 365 struct autofs_node *firstanp; 366 struct autofs_request *ar; 367 char *key, *path; 368 int error = 0, request_error, last; 369 370 amp = anp->an_mount; 371 372 sx_assert(&autofs_softc->sc_lock, SA_XLOCKED); 373 374 if (anp->an_parent == NULL) { 375 key = strndup(component, componentlen, M_AUTOFS); 376 } else { 377 for (firstanp = anp; firstanp->an_parent->an_parent != NULL; 378 firstanp = firstanp->an_parent) 379 continue; 380 key = strdup(firstanp->an_name, M_AUTOFS); 381 } 382 383 path = autofs_path(anp); 384 385 TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { 386 if (strcmp(ar->ar_path, path) != 0) 387 continue; 388 if (strcmp(ar->ar_key, key) != 0) 389 continue; 390 391 KASSERT(strcmp(ar->ar_from, amp->am_from) == 0, 392 ("from changed; %s != %s", ar->ar_from, amp->am_from)); 393 KASSERT(strcmp(ar->ar_prefix, amp->am_prefix) == 0, 394 ("prefix changed; %s != %s", 395 ar->ar_prefix, amp->am_prefix)); 396 KASSERT(strcmp(ar->ar_options, amp->am_options) == 0, 397 ("options changed; %s != %s", 398 ar->ar_options, amp->am_options)); 399 400 break; 401 } 402 403 if (ar != NULL) { 404 refcount_acquire(&ar->ar_refcount); 405 } else { 406 ar = uma_zalloc(autofs_request_zone, M_WAITOK | M_ZERO); 407 ar->ar_mount = amp; 408 409 ar->ar_id = 410 atomic_fetchadd_int(&autofs_softc->sc_last_request_id, 1); 411 strlcpy(ar->ar_from, amp->am_from, sizeof(ar->ar_from)); 412 strlcpy(ar->ar_path, path, sizeof(ar->ar_path)); 413 strlcpy(ar->ar_prefix, amp->am_prefix, sizeof(ar->ar_prefix)); 414 strlcpy(ar->ar_key, key, sizeof(ar->ar_key)); 415 strlcpy(ar->ar_options, 416 amp->am_options, sizeof(ar->ar_options)); 417 418 TIMEOUT_TASK_INIT(taskqueue_thread, &ar->ar_task, 0, 419 autofs_task, ar); 420 error = taskqueue_enqueue_timeout(taskqueue_thread, 421 &ar->ar_task, autofs_timeout * hz); 422 if (error != 0) { 423 AUTOFS_WARN("taskqueue_enqueue_timeout() failed " 424 "with error %d", error); 425 } 426 refcount_init(&ar->ar_refcount, 1); 427 TAILQ_INSERT_TAIL(&autofs_softc->sc_requests, ar, ar_next); 428 } 429 430 cv_broadcast(&autofs_softc->sc_cv); 431 while (ar->ar_done == false) { 432 if (autofs_interruptible != 0) { 433 autofs_set_sigmask(&oldset); 434 error = cv_wait_sig(&autofs_softc->sc_cv, 435 &autofs_softc->sc_lock); 436 autofs_restore_sigmask(&oldset); 437 if (error != 0) { 438 AUTOFS_WARN("cv_wait_sig for %s failed " 439 "with error %d", ar->ar_path, error); 440 break; 441 } 442 } else { 443 cv_wait(&autofs_softc->sc_cv, &autofs_softc->sc_lock); 444 } 445 } 446 447 request_error = ar->ar_error; 448 if (request_error != 0) { 449 AUTOFS_WARN("request for %s completed with error %d", 450 ar->ar_path, request_error); 451 } 452 453 last = refcount_release(&ar->ar_refcount); 454 if (last) { 455 TAILQ_REMOVE(&autofs_softc->sc_requests, ar, ar_next); 456 /* 457 * Unlock the sc_lock, so that autofs_task() can complete. 458 */ 459 sx_xunlock(&autofs_softc->sc_lock); 460 taskqueue_cancel_timeout(taskqueue_thread, &ar->ar_task, NULL); 461 taskqueue_drain_timeout(taskqueue_thread, &ar->ar_task); 462 uma_zfree(autofs_request_zone, ar); 463 sx_xlock(&autofs_softc->sc_lock); 464 } 465 466 /* 467 * Note that we do not do negative caching on purpose. This 468 * way the user can retry access at any time, e.g. after fixing 469 * the failure reason, without waiting for cache timer to expire. 470 */ 471 if (error == 0 && request_error == 0 && autofs_cache > 0) { 472 anp->an_cached = true; 473 callout_reset(&anp->an_callout, autofs_cache * hz, 474 autofs_cache_callout, anp); 475 } 476 477 free(key, M_AUTOFS); 478 free(path, M_AUTOFS); 479 480 if (error != 0) 481 return (error); 482 return (request_error); 483 } 484 485 /* 486 * Send request to automountd(8) and wait for completion. 487 */ 488 int 489 autofs_trigger(struct autofs_node *anp, 490 const char *component, int componentlen) 491 { 492 int error; 493 494 for (;;) { 495 error = autofs_trigger_one(anp, component, componentlen); 496 if (error == 0) { 497 anp->an_retries = 0; 498 return (0); 499 } 500 if (error == EINTR || error == ERESTART) { 501 AUTOFS_DEBUG("trigger interrupted by signal, " 502 "not retrying"); 503 anp->an_retries = 0; 504 return (error); 505 } 506 anp->an_retries++; 507 if (anp->an_retries >= autofs_retry_attempts) { 508 AUTOFS_DEBUG("trigger failed %d times; returning " 509 "error %d", anp->an_retries, error); 510 anp->an_retries = 0; 511 return (error); 512 513 } 514 AUTOFS_DEBUG("trigger failed with error %d; will retry in " 515 "%d seconds, %d attempts left", error, autofs_retry_delay, 516 autofs_retry_attempts - anp->an_retries); 517 sx_xunlock(&autofs_softc->sc_lock); 518 pause("autofs_retry", autofs_retry_delay * hz); 519 sx_xlock(&autofs_softc->sc_lock); 520 } 521 } 522 523 static int 524 autofs_ioctl_request(struct autofs_daemon_request *adr) 525 { 526 struct autofs_request *ar; 527 int error; 528 529 sx_xlock(&autofs_softc->sc_lock); 530 for (;;) { 531 TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { 532 if (ar->ar_done) 533 continue; 534 if (ar->ar_in_progress) 535 continue; 536 537 break; 538 } 539 540 if (ar != NULL) 541 break; 542 543 error = cv_wait_sig(&autofs_softc->sc_cv, 544 &autofs_softc->sc_lock); 545 if (error != 0) { 546 sx_xunlock(&autofs_softc->sc_lock); 547 AUTOFS_DEBUG("failed with error %d", error); 548 return (error); 549 } 550 } 551 552 ar->ar_in_progress = true; 553 sx_xunlock(&autofs_softc->sc_lock); 554 555 adr->adr_id = ar->ar_id; 556 strlcpy(adr->adr_from, ar->ar_from, sizeof(adr->adr_from)); 557 strlcpy(adr->adr_path, ar->ar_path, sizeof(adr->adr_path)); 558 strlcpy(adr->adr_prefix, ar->ar_prefix, sizeof(adr->adr_prefix)); 559 strlcpy(adr->adr_key, ar->ar_key, sizeof(adr->adr_key)); 560 strlcpy(adr->adr_options, ar->ar_options, sizeof(adr->adr_options)); 561 562 PROC_LOCK(curproc); 563 autofs_softc->sc_dev_sid = curproc->p_session->s_sid; 564 PROC_UNLOCK(curproc); 565 566 return (0); 567 } 568 569 static int 570 autofs_ioctl_done(struct autofs_daemon_done *add) 571 { 572 struct autofs_request *ar; 573 574 sx_xlock(&autofs_softc->sc_lock); 575 TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { 576 if (ar->ar_id == add->add_id) 577 break; 578 } 579 580 if (ar == NULL) { 581 sx_xunlock(&autofs_softc->sc_lock); 582 AUTOFS_DEBUG("id %d not found", add->add_id); 583 return (ESRCH); 584 } 585 586 ar->ar_error = add->add_error; 587 ar->ar_done = true; 588 ar->ar_in_progress = false; 589 cv_broadcast(&autofs_softc->sc_cv); 590 591 sx_xunlock(&autofs_softc->sc_lock); 592 593 return (0); 594 } 595 596 static int 597 autofs_open(struct cdev *dev, int flags, int fmt, struct thread *td) 598 { 599 600 sx_xlock(&autofs_softc->sc_lock); 601 /* 602 * We must never block automountd(8) and its descendants, and we use 603 * session ID to determine that: we store session id of the process 604 * that opened the device, and then compare it with session ids 605 * of triggering processes. This means running a second automountd(8) 606 * instance would break the previous one. The check below prevents 607 * it from happening. 608 */ 609 if (autofs_softc->sc_dev_opened) { 610 sx_xunlock(&autofs_softc->sc_lock); 611 return (EBUSY); 612 } 613 614 autofs_softc->sc_dev_opened = true; 615 sx_xunlock(&autofs_softc->sc_lock); 616 617 return (0); 618 } 619 620 static int 621 autofs_close(struct cdev *dev, int flag, int fmt, struct thread *td) 622 { 623 624 sx_xlock(&autofs_softc->sc_lock); 625 KASSERT(autofs_softc->sc_dev_opened, ("not opened?")); 626 autofs_softc->sc_dev_opened = false; 627 sx_xunlock(&autofs_softc->sc_lock); 628 629 return (0); 630 } 631 632 static int 633 autofs_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, 634 struct thread *td) 635 { 636 637 KASSERT(autofs_softc->sc_dev_opened, ("not opened?")); 638 639 switch (cmd) { 640 case AUTOFSREQUEST: 641 return (autofs_ioctl_request( 642 (struct autofs_daemon_request *)arg)); 643 case AUTOFSDONE: 644 return (autofs_ioctl_done( 645 (struct autofs_daemon_done *)arg)); 646 default: 647 AUTOFS_DEBUG("invalid cmd %lx", cmd); 648 return (EINVAL); 649 } 650 } 651