Index: head/sys/fs/autofs/autofs.c =================================================================== --- head/sys/fs/autofs/autofs.c (revision 272469) +++ head/sys/fs/autofs/autofs.c (revision 272470) @@ -1,650 +1,650 @@ /*- * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /*- * Copyright (c) 1989, 1991, 1993, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_AUTOFS, "autofs", "Automounter filesystem"); uma_zone_t autofs_request_zone; uma_zone_t autofs_node_zone; static int autofs_open(struct cdev *dev, int flags, int fmt, struct thread *td); static int autofs_close(struct cdev *dev, int flag, int fmt, struct thread *td); static int autofs_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, struct thread *td); static struct cdevsw autofs_cdevsw = { .d_version = D_VERSION, .d_open = autofs_open, .d_close = autofs_close, .d_ioctl = autofs_ioctl, .d_name = "autofs", }; /* * List of signals that can interrupt an autofs trigger. Might be a good * idea to keep it synchronised with list in sys/fs/nfs/nfs_commonkrpc.c. */ int autofs_sig_set[] = { SIGINT, SIGTERM, SIGHUP, SIGKILL, SIGQUIT }; struct autofs_softc *autofs_softc; SYSCTL_NODE(_vfs, OID_AUTO, autofs, CTLFLAG_RD, 0, "Automounter filesystem"); int autofs_debug = 1; TUNABLE_INT("vfs.autofs.debug", &autofs_debug); SYSCTL_INT(_vfs_autofs, OID_AUTO, debug, CTLFLAG_RWTUN, &autofs_debug, 1, "Enable debug messages"); int autofs_mount_on_stat = 0; TUNABLE_INT("vfs.autofs.mount_on_stat", &autofs_mount_on_stat); SYSCTL_INT(_vfs_autofs, OID_AUTO, mount_on_stat, CTLFLAG_RWTUN, &autofs_mount_on_stat, 0, "Trigger mount on stat(2) on mountpoint"); int autofs_timeout = 30; TUNABLE_INT("vfs.autofs.timeout", &autofs_timeout); SYSCTL_INT(_vfs_autofs, OID_AUTO, timeout, CTLFLAG_RWTUN, &autofs_timeout, 30, "Number of seconds to wait for automountd(8)"); int autofs_cache = 600; TUNABLE_INT("vfs.autofs.cache", &autofs_cache); SYSCTL_INT(_vfs_autofs, OID_AUTO, cache, CTLFLAG_RWTUN, &autofs_cache, 600, "Number of seconds to wait before reinvoking " "automountd(8) for any given file or directory"); int autofs_retry_attempts = 3; TUNABLE_INT("vfs.autofs.retry_attempts", &autofs_retry_attempts); SYSCTL_INT(_vfs_autofs, OID_AUTO, retry_attempts, CTLFLAG_RWTUN, &autofs_retry_attempts, 3, "Number of attempts before failing mount"); int autofs_retry_delay = 1; TUNABLE_INT("vfs.autofs.retry_delay", &autofs_retry_delay); SYSCTL_INT(_vfs_autofs, OID_AUTO, retry_delay, CTLFLAG_RWTUN, &autofs_retry_delay, 1, "Number of seconds before retrying"); int autofs_interruptible = 1; TUNABLE_INT("vfs.autofs.interruptible", &autofs_interruptible); SYSCTL_INT(_vfs_autofs, OID_AUTO, interruptible, CTLFLAG_RWTUN, &autofs_interruptible, 1, "Allow requests to be interrupted by signal"); int autofs_init(struct vfsconf *vfsp) { int error; KASSERT(autofs_softc == NULL, ("softc %p, should be NULL", autofs_softc)); autofs_softc = malloc(sizeof(*autofs_softc), M_AUTOFS, M_WAITOK | M_ZERO); autofs_request_zone = uma_zcreate("autofs_request", sizeof(struct autofs_request), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); autofs_node_zone = uma_zcreate("autofs_node", sizeof(struct autofs_node), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); TAILQ_INIT(&autofs_softc->sc_requests); cv_init(&autofs_softc->sc_cv, "autofscv"); sx_init(&autofs_softc->sc_lock, "autofslk"); error = make_dev_p(MAKEDEV_CHECKNAME, &autofs_softc->sc_cdev, &autofs_cdevsw, NULL, UID_ROOT, GID_WHEEL, 0600, "autofs"); if (error != 0) { AUTOFS_WARN("failed to create device node, error %d", error); uma_zdestroy(autofs_request_zone); uma_zdestroy(autofs_node_zone); free(autofs_softc, M_AUTOFS); return (error); } autofs_softc->sc_cdev->si_drv1 = autofs_softc; return (0); } int autofs_uninit(struct vfsconf *vfsp) { sx_xlock(&autofs_softc->sc_lock); if (autofs_softc->sc_dev_opened) { sx_xunlock(&autofs_softc->sc_lock); return (EBUSY); } if (autofs_softc->sc_cdev != NULL) destroy_dev(autofs_softc->sc_cdev); uma_zdestroy(autofs_request_zone); uma_zdestroy(autofs_node_zone); sx_xunlock(&autofs_softc->sc_lock); /* * XXX: Race with open? */ free(autofs_softc, M_AUTOFS); return (0); } bool autofs_ignore_thread(const struct thread *td) { struct proc *p; p = td->td_proc; if (autofs_softc->sc_dev_opened == false) return (false); PROC_LOCK(p); if (p->p_session->s_sid == autofs_softc->sc_dev_sid) { PROC_UNLOCK(p); return (true); } PROC_UNLOCK(p); return (false); } static char * autofs_path(struct autofs_node *anp) { struct autofs_mount *amp; char *path, *tmp; amp = anp->an_mount; path = strdup("", M_AUTOFS); for (; anp->an_parent != NULL; anp = anp->an_parent) { tmp = malloc(strlen(anp->an_name) + strlen(path) + 2, M_AUTOFS, M_WAITOK); strcpy(tmp, anp->an_name); strcat(tmp, "/"); strcat(tmp, path); free(path, M_AUTOFS); path = tmp; } tmp = malloc(strlen(amp->am_mountpoint) + strlen(path) + 2, M_AUTOFS, M_WAITOK); strcpy(tmp, amp->am_mountpoint); strcat(tmp, "/"); strcat(tmp, path); free(path, M_AUTOFS); path = tmp; return (path); } static void autofs_task(void *context, int pending) { struct autofs_request *ar; ar = context; sx_xlock(&autofs_softc->sc_lock); AUTOFS_WARN("request %d for %s timed out after %d seconds", ar->ar_id, ar->ar_path, autofs_timeout); /* * XXX: EIO perhaps? */ ar->ar_error = ETIMEDOUT; ar->ar_done = true; ar->ar_in_progress = false; cv_broadcast(&autofs_softc->sc_cv); sx_xunlock(&autofs_softc->sc_lock); } bool autofs_cached(struct autofs_node *anp, const char *component, int componentlen) { int error; struct autofs_mount *amp; amp = anp->an_mount; AUTOFS_ASSERT_UNLOCKED(amp); /* * For top-level nodes we need to request automountd(8) * assistance even if the node is marked as cached, * but the requested subdirectory does not exist. This * is necessary for wildcard indirect map keys to work. */ if (anp->an_parent == NULL && componentlen != 0) { - AUTOFS_LOCK(amp); + AUTOFS_SLOCK(amp); error = autofs_node_find(anp, component, componentlen, NULL); - AUTOFS_UNLOCK(amp); + AUTOFS_SUNLOCK(amp); if (error != 0) return (false); } return (anp->an_cached); } static void autofs_cache_callout(void *context) { struct autofs_node *anp; anp = context; anp->an_cached = false; } /* * The set/restore sigmask functions are used to (temporarily) overwrite * the thread td_sigmask during triggering. */ static void autofs_set_sigmask(sigset_t *oldset) { sigset_t newset; int i; SIGFILLSET(newset); /* Remove the autofs set of signals from newset */ PROC_LOCK(curproc); mtx_lock(&curproc->p_sigacts->ps_mtx); for (i = 0 ; i < sizeof(autofs_sig_set)/sizeof(int) ; i++) { /* * But make sure we leave the ones already masked * by the process, i.e. remove the signal from the * temporary signalmask only if it wasn't already * in p_sigmask. */ if (!SIGISMEMBER(curthread->td_sigmask, autofs_sig_set[i]) && !SIGISMEMBER(curproc->p_sigacts->ps_sigignore, autofs_sig_set[i])) { SIGDELSET(newset, autofs_sig_set[i]); } } mtx_unlock(&curproc->p_sigacts->ps_mtx); kern_sigprocmask(curthread, SIG_SETMASK, &newset, oldset, SIGPROCMASK_PROC_LOCKED); PROC_UNLOCK(curproc); } static void autofs_restore_sigmask(sigset_t *set) { kern_sigprocmask(curthread, SIG_SETMASK, set, NULL, 0); } static int autofs_trigger_one(struct autofs_node *anp, const char *component, int componentlen) { sigset_t oldset; struct autofs_mount *amp; struct autofs_node *firstanp; struct autofs_request *ar; char *key, *path; int error = 0, request_error, last; amp = VFSTOAUTOFS(anp->an_vnode->v_mount); sx_assert(&autofs_softc->sc_lock, SA_XLOCKED); if (anp->an_parent == NULL) { key = strndup(component, componentlen, M_AUTOFS); } else { for (firstanp = anp; firstanp->an_parent->an_parent != NULL; firstanp = firstanp->an_parent) continue; key = strdup(firstanp->an_name, M_AUTOFS); } path = autofs_path(anp); TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { if (strcmp(ar->ar_path, path) != 0) continue; if (strcmp(ar->ar_key, key) != 0) continue; KASSERT(strcmp(ar->ar_from, amp->am_from) == 0, ("from changed; %s != %s", ar->ar_from, amp->am_from)); KASSERT(strcmp(ar->ar_prefix, amp->am_prefix) == 0, ("prefix changed; %s != %s", ar->ar_prefix, amp->am_prefix)); KASSERT(strcmp(ar->ar_options, amp->am_options) == 0, ("options changed; %s != %s", ar->ar_options, amp->am_options)); break; } if (ar != NULL) { refcount_acquire(&ar->ar_refcount); } else { ar = uma_zalloc(autofs_request_zone, M_WAITOK | M_ZERO); ar->ar_mount = amp; ar->ar_id = atomic_fetchadd_int(&autofs_softc->sc_last_request_id, 1); strlcpy(ar->ar_from, amp->am_from, sizeof(ar->ar_from)); strlcpy(ar->ar_path, path, sizeof(ar->ar_path)); strlcpy(ar->ar_prefix, amp->am_prefix, sizeof(ar->ar_prefix)); strlcpy(ar->ar_key, key, sizeof(ar->ar_key)); strlcpy(ar->ar_options, amp->am_options, sizeof(ar->ar_options)); TIMEOUT_TASK_INIT(taskqueue_thread, &ar->ar_task, 0, autofs_task, ar); error = taskqueue_enqueue_timeout(taskqueue_thread, &ar->ar_task, autofs_timeout * hz); if (error != 0) { AUTOFS_WARN("taskqueue_enqueue_timeout() failed " "with error %d", error); } refcount_init(&ar->ar_refcount, 1); TAILQ_INSERT_TAIL(&autofs_softc->sc_requests, ar, ar_next); } cv_broadcast(&autofs_softc->sc_cv); while (ar->ar_done == false) { if (autofs_interruptible != 0) { autofs_set_sigmask(&oldset); error = cv_wait_sig(&autofs_softc->sc_cv, &autofs_softc->sc_lock); autofs_restore_sigmask(&oldset); if (error != 0) { AUTOFS_WARN("cv_wait_sig for %s failed " "with error %d", ar->ar_path, error); break; } } else { cv_wait(&autofs_softc->sc_cv, &autofs_softc->sc_lock); } } request_error = ar->ar_error; if (request_error != 0) { AUTOFS_WARN("request for %s completed with error %d", ar->ar_path, request_error); } last = refcount_release(&ar->ar_refcount); if (last) { TAILQ_REMOVE(&autofs_softc->sc_requests, ar, ar_next); /* * Unlock the sc_lock, so that autofs_task() can complete. */ sx_xunlock(&autofs_softc->sc_lock); taskqueue_cancel_timeout(taskqueue_thread, &ar->ar_task, NULL); taskqueue_drain_timeout(taskqueue_thread, &ar->ar_task); uma_zfree(autofs_request_zone, ar); sx_xlock(&autofs_softc->sc_lock); } /* * Note that we do not do negative caching on purpose. This * way the user can retry access at any time, e.g. after fixing * the failure reason, without waiting for cache timer to expire. */ if (error == 0 && request_error == 0 && autofs_cache > 0) { anp->an_cached = true; callout_reset(&anp->an_callout, autofs_cache * hz, autofs_cache_callout, anp); } free(key, M_AUTOFS); free(path, M_AUTOFS); if (error != 0) return (error); return (request_error); } /* * Send request to automountd(8) and wait for completion. */ int autofs_trigger(struct autofs_node *anp, const char *component, int componentlen) { int error; for (;;) { error = autofs_trigger_one(anp, component, componentlen); if (error == 0) { anp->an_retries = 0; return (0); } if (error == EINTR || error == ERESTART) { AUTOFS_DEBUG("trigger interrupted by signal, " "not retrying"); anp->an_retries = 0; return (error); } anp->an_retries++; if (anp->an_retries >= autofs_retry_attempts) { AUTOFS_DEBUG("trigger failed %d times; returning " "error %d", anp->an_retries, error); anp->an_retries = 0; return (error); } AUTOFS_DEBUG("trigger failed with error %d; will retry in " "%d seconds, %d attempts left", error, autofs_retry_delay, autofs_retry_attempts - anp->an_retries); sx_xunlock(&autofs_softc->sc_lock); pause("autofs_retry", autofs_retry_delay * hz); sx_xlock(&autofs_softc->sc_lock); } } static int autofs_ioctl_request(struct autofs_daemon_request *adr) { struct autofs_request *ar; int error; sx_xlock(&autofs_softc->sc_lock); for (;;) { TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { if (ar->ar_done) continue; if (ar->ar_in_progress) continue; break; } if (ar != NULL) break; error = cv_wait_sig(&autofs_softc->sc_cv, &autofs_softc->sc_lock); if (error != 0) { sx_xunlock(&autofs_softc->sc_lock); AUTOFS_DEBUG("failed with error %d", error); return (error); } } ar->ar_in_progress = true; sx_xunlock(&autofs_softc->sc_lock); adr->adr_id = ar->ar_id; strlcpy(adr->adr_from, ar->ar_from, sizeof(adr->adr_from)); strlcpy(adr->adr_path, ar->ar_path, sizeof(adr->adr_path)); strlcpy(adr->adr_prefix, ar->ar_prefix, sizeof(adr->adr_prefix)); strlcpy(adr->adr_key, ar->ar_key, sizeof(adr->adr_key)); strlcpy(adr->adr_options, ar->ar_options, sizeof(adr->adr_options)); PROC_LOCK(curproc); autofs_softc->sc_dev_sid = curproc->p_session->s_sid; PROC_UNLOCK(curproc); return (0); } static int autofs_ioctl_done(struct autofs_daemon_done *add) { struct autofs_request *ar; sx_xlock(&autofs_softc->sc_lock); TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { if (ar->ar_id == add->add_id) break; } if (ar == NULL) { sx_xunlock(&autofs_softc->sc_lock); AUTOFS_DEBUG("id %d not found", add->add_id); return (ESRCH); } ar->ar_error = add->add_error; ar->ar_done = true; ar->ar_in_progress = false; cv_broadcast(&autofs_softc->sc_cv); sx_xunlock(&autofs_softc->sc_lock); return (0); } static int autofs_open(struct cdev *dev, int flags, int fmt, struct thread *td) { sx_xlock(&autofs_softc->sc_lock); /* * We must never block automountd(8) and its descendants, and we use * session ID to determine that: we store session id of the process * that opened the device, and then compare it with session ids * of triggering processes. This means running a second automountd(8) * instance would break the previous one. The check below prevents * it from happening. */ if (autofs_softc->sc_dev_opened) { sx_xunlock(&autofs_softc->sc_lock); return (EBUSY); } autofs_softc->sc_dev_opened = true; sx_xunlock(&autofs_softc->sc_lock); return (0); } static int autofs_close(struct cdev *dev, int flag, int fmt, struct thread *td) { sx_xlock(&autofs_softc->sc_lock); KASSERT(autofs_softc->sc_dev_opened, ("not opened?")); autofs_softc->sc_dev_opened = false; sx_xunlock(&autofs_softc->sc_lock); return (0); } static int autofs_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, struct thread *td) { KASSERT(autofs_softc->sc_dev_opened, ("not opened?")); switch (cmd) { case AUTOFSREQUEST: return (autofs_ioctl_request( (struct autofs_daemon_request *)arg)); case AUTOFSDONE: return (autofs_ioctl_done( (struct autofs_daemon_done *)arg)); default: AUTOFS_DEBUG("invalid cmd %lx", cmd); return (EINVAL); } } Index: head/sys/fs/autofs/autofs.h =================================================================== --- head/sys/fs/autofs/autofs.h (revision 272469) +++ head/sys/fs/autofs/autofs.h (revision 272470) @@ -1,137 +1,140 @@ /*- * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef AUTOFS_H #define AUTOFS_H #define VFSTOAUTOFS(mp) ((struct autofs_mount *)((mp)->mnt_data)) MALLOC_DECLARE(M_AUTOFS); extern uma_zone_t autofs_request_zone; extern uma_zone_t autofs_node_zone; extern int autofs_debug; extern int autofs_mount_on_stat; #define AUTOFS_DEBUG(X, ...) \ if (autofs_debug > 1) { \ printf("%s: " X "\n", __func__, ## __VA_ARGS__);\ } while (0) #define AUTOFS_WARN(X, ...) \ if (autofs_debug > 0) { \ printf("WARNING: %s: " X "\n", \ __func__, ## __VA_ARGS__); \ } while (0) -#define AUTOFS_LOCK(X) sx_xlock(&X->am_lock) -#define AUTOFS_UNLOCK(X) sx_xunlock(&X->am_lock) -#define AUTOFS_ASSERT_LOCKED(X) sx_assert(&X->am_lock, SA_XLOCKED) +#define AUTOFS_SLOCK(X) sx_slock(&X->am_lock) +#define AUTOFS_XLOCK(X) sx_xlock(&X->am_lock) +#define AUTOFS_SUNLOCK(X) sx_sunlock(&X->am_lock) +#define AUTOFS_XUNLOCK(X) sx_xunlock(&X->am_lock) +#define AUTOFS_ASSERT_LOCKED(X) sx_assert(&X->am_lock, SA_LOCKED) +#define AUTOFS_ASSERT_XLOCKED(X) sx_assert(&X->am_lock, SA_XLOCKED) #define AUTOFS_ASSERT_UNLOCKED(X) sx_assert(&X->am_lock, SA_UNLOCKED) struct autofs_node { TAILQ_ENTRY(autofs_node) an_next; char *an_name; int an_fileno; struct autofs_node *an_parent; TAILQ_HEAD(, autofs_node) an_children; struct autofs_mount *an_mount; struct vnode *an_vnode; struct sx an_vnode_lock; bool an_cached; struct callout an_callout; int an_retries; struct timespec an_ctime; }; struct autofs_mount { TAILQ_ENTRY(autofs_mount) am_next; struct autofs_node *am_root; struct mount *am_mp; struct sx am_lock; char am_from[MAXPATHLEN]; char am_mountpoint[MAXPATHLEN]; char am_options[MAXPATHLEN]; char am_prefix[MAXPATHLEN]; int am_last_fileno; }; struct autofs_request { TAILQ_ENTRY(autofs_request) ar_next; struct autofs_mount *ar_mount; int ar_id; bool ar_done; int ar_error; bool ar_in_progress; char ar_from[MAXPATHLEN]; char ar_path[MAXPATHLEN]; char ar_prefix[MAXPATHLEN]; char ar_key[MAXPATHLEN]; char ar_options[MAXPATHLEN]; struct timeout_task ar_task; volatile u_int ar_refcount; }; struct autofs_softc { device_t sc_dev; struct cdev *sc_cdev; struct cv sc_cv; struct sx sc_lock; TAILQ_HEAD(, autofs_request) sc_requests; bool sc_dev_opened; pid_t sc_dev_sid; int sc_last_request_id; }; /* * Limits and constants */ #define AUTOFS_NAMELEN 24 #define AUTOFS_FSNAMELEN 16 /* equal to MFSNAMELEN */ #define AUTOFS_DELEN (8 + AUTOFS_NAMELEN) int autofs_init(struct vfsconf *vfsp); int autofs_uninit(struct vfsconf *vfsp); int autofs_trigger(struct autofs_node *anp, const char *component, int componentlen); bool autofs_cached(struct autofs_node *anp, const char *component, int componentlen); bool autofs_ignore_thread(const struct thread *td); int autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp, const char *name, int namelen, struct autofs_node **anpp); int autofs_node_find(struct autofs_node *parent, const char *name, int namelen, struct autofs_node **anpp); void autofs_node_delete(struct autofs_node *anp); int autofs_node_vn(struct autofs_node *anp, struct mount *mp, struct vnode **vpp); #endif /* !AUTOFS_H */ Index: head/sys/fs/autofs/autofs_vfsops.c =================================================================== --- head/sys/fs/autofs/autofs_vfsops.c (revision 272469) +++ head/sys/fs/autofs/autofs_vfsops.c (revision 272470) @@ -1,211 +1,211 @@ /*- * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include static const char *autofs_opts[] = { "from", "master_options", "master_prefix", NULL }; extern struct autofs_softc *autofs_softc; static int autofs_mount(struct mount *mp) { struct autofs_mount *amp; char *from, *fspath, *options, *prefix; int error; if (vfs_filteropt(mp->mnt_optnew, autofs_opts)) return (EINVAL); if (mp->mnt_flag & MNT_UPDATE) return (0); if (vfs_getopt(mp->mnt_optnew, "from", (void **)&from, NULL)) return (EINVAL); if (vfs_getopt(mp->mnt_optnew, "fspath", (void **)&fspath, NULL)) return (EINVAL); if (vfs_getopt(mp->mnt_optnew, "master_options", (void **)&options, NULL)) return (EINVAL); if (vfs_getopt(mp->mnt_optnew, "master_prefix", (void **)&prefix, NULL)) return (EINVAL); amp = malloc(sizeof(*amp), M_AUTOFS, M_WAITOK | M_ZERO); mp->mnt_data = amp; amp->am_mp = mp; strlcpy(amp->am_from, from, sizeof(amp->am_from)); strlcpy(amp->am_mountpoint, fspath, sizeof(amp->am_mountpoint)); strlcpy(amp->am_options, options, sizeof(amp->am_options)); strlcpy(amp->am_prefix, prefix, sizeof(amp->am_prefix)); sx_init(&->am_lock, "autofslk"); amp->am_last_fileno = 1; vfs_getnewfsid(mp); - AUTOFS_LOCK(amp); + AUTOFS_XLOCK(amp); error = autofs_node_new(NULL, amp, ".", -1, &->am_root); if (error != 0) { - AUTOFS_UNLOCK(amp); + AUTOFS_XUNLOCK(amp); free(amp, M_AUTOFS); return (error); } - AUTOFS_UNLOCK(amp); + AUTOFS_XUNLOCK(amp); vfs_mountedfrom(mp, from); return (0); } static int autofs_unmount(struct mount *mp, int mntflags) { struct autofs_mount *amp; struct autofs_node *anp; struct autofs_request *ar; int error, flags; bool found; amp = VFSTOAUTOFS(mp); flags = 0; if (mntflags & MNT_FORCE) flags |= FORCECLOSE; error = vflush(mp, 0, flags, curthread); if (error != 0) { AUTOFS_WARN("vflush failed with error %d", error); return (error); } /* * All vnodes are gone, and new one will not appear - so, * no new triggerings. We can iterate over outstanding * autofs_requests and terminate them. */ for (;;) { found = false; sx_xlock(&autofs_softc->sc_lock); TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { if (ar->ar_mount != amp) continue; ar->ar_error = ENXIO; ar->ar_done = true; ar->ar_in_progress = false; found = true; } sx_xunlock(&autofs_softc->sc_lock); if (found == false) break; cv_broadcast(&autofs_softc->sc_cv); pause("autofs_umount", 1); } - AUTOFS_LOCK(amp); + AUTOFS_XLOCK(amp); /* * Not terribly efficient, but at least not recursive. */ while (!TAILQ_EMPTY(&->am_root->an_children)) { anp = TAILQ_FIRST(&->am_root->an_children); while (!TAILQ_EMPTY(&anp->an_children)) anp = TAILQ_FIRST(&anp->an_children); autofs_node_delete(anp); } autofs_node_delete(amp->am_root); mp->mnt_data = NULL; - AUTOFS_UNLOCK(amp); + AUTOFS_XUNLOCK(amp); sx_destroy(&->am_lock); free(amp, M_AUTOFS); return (0); } static int autofs_root(struct mount *mp, int flags, struct vnode **vpp) { struct autofs_mount *amp; int error; amp = VFSTOAUTOFS(mp); error = autofs_node_vn(amp->am_root, mp, vpp); return (error); } static int autofs_statfs(struct mount *mp, struct statfs *sbp) { sbp->f_bsize = 512; sbp->f_iosize = 0; sbp->f_blocks = 0; sbp->f_bfree = 0; sbp->f_bavail = 0; sbp->f_files = 0; sbp->f_ffree = 0; return (0); } static struct vfsops autofs_vfsops = { .vfs_fhtovp = NULL, /* XXX */ .vfs_mount = autofs_mount, .vfs_unmount = autofs_unmount, .vfs_root = autofs_root, .vfs_statfs = autofs_statfs, .vfs_init = autofs_init, .vfs_uninit = autofs_uninit, }; VFS_SET(autofs_vfsops, autofs, VFCF_SYNTHETIC | VFCF_NETWORK); MODULE_VERSION(autofs, 1); Index: head/sys/fs/autofs/autofs_vnops.c =================================================================== --- head/sys/fs/autofs/autofs_vnops.c (revision 272469) +++ head/sys/fs/autofs/autofs_vnops.c (revision 272470) @@ -1,649 +1,649 @@ /*- * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen, struct vnode **newvp); extern struct autofs_softc *autofs_softc; static int autofs_access(struct vop_access_args *ap) { /* * Nothing to do here; the only kind of access control * needed is in autofs_mkdir(). */ return (0); } static int autofs_getattr(struct vop_getattr_args *ap) { struct vnode *vp, *newvp; struct autofs_node *anp; struct mount *mp; struct vattr *vap; int error; vp = ap->a_vp; anp = vp->v_data; mp = vp->v_mount; vap = ap->a_vap; KASSERT(ap->a_vp->v_type == VDIR, ("!VDIR")); /* * The reason we must do this is that some tree-walking software, * namely fts(3), assumes that stat(".") results will not change * between chdir("subdir") and chdir(".."), and fails with ENOENT * otherwise. */ if (autofs_mount_on_stat && autofs_cached(anp, NULL, 0) == false && autofs_ignore_thread(curthread) == false) { error = autofs_trigger_vn(vp, "", 0, &newvp); if (error != 0) return (error); if (newvp != NULL) { error = VOP_GETATTR(newvp, ap->a_vap, ap->a_cred); vput(newvp); return (error); } } vap->va_type = VDIR; vap->va_mode = 0755; vap->va_nlink = 3; /* XXX */ vap->va_uid = 0; vap->va_gid = 0; vap->va_rdev = NODEV; vap->va_fsid = mp->mnt_stat.f_fsid.val[0]; vap->va_fileid = anp->an_fileno; vap->va_size = 512; /* XXX */ vap->va_blocksize = 512; vap->va_mtime = anp->an_ctime; vap->va_atime = anp->an_ctime; vap->va_ctime = anp->an_ctime; vap->va_birthtime = anp->an_ctime; vap->va_gen = 0; vap->va_flags = 0; vap->va_rdev = 0; vap->va_bytes = 512; /* XXX */ vap->va_filerev = 0; vap->va_spare = 0; return (0); } /* * Unlock the vnode, request automountd(8) action, and then lock it back. * If anything got mounted on top of the vnode, return the new filesystem's * root vnode in 'newvp', locked. */ static int autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen, struct vnode **newvp) { struct autofs_node *anp; struct autofs_mount *amp; int error, lock_flags; anp = vp->v_data; amp = VFSTOAUTOFS(vp->v_mount); /* * Release the vnode lock, so that other operations, in partcular * mounting a filesystem on top of it, can proceed. Increase use * count, to prevent the vnode from being deallocated and to prevent * filesystem from being unmounted. */ lock_flags = VOP_ISLOCKED(vp); vref(vp); VOP_UNLOCK(vp, 0); sx_xlock(&autofs_softc->sc_lock); /* * XXX: Workaround for mounting the same thing multiple times; revisit. */ if (vp->v_mountedhere != NULL) { error = 0; goto mounted; } error = autofs_trigger(anp, path, pathlen); mounted: sx_xunlock(&autofs_softc->sc_lock); vn_lock(vp, lock_flags | LK_RETRY); vunref(vp); if ((vp->v_iflag & VI_DOOMED) != 0) { AUTOFS_DEBUG("VI_DOOMED"); return (ENOENT); } if (error != 0) return (error); if (vp->v_mountedhere == NULL) { *newvp = NULL; return (0); } else { /* * If the operation that succeeded was mount, then mark * the node as non-cached. Otherwise, if someone unmounts * the filesystem before the cache times out, we will fail * to trigger. */ anp->an_cached = false; } error = VFS_ROOT(vp->v_mountedhere, lock_flags, newvp); if (error != 0) { AUTOFS_WARN("VFS_ROOT() failed with error %d", error); return (error); } return (0); } static int autofs_vget_callback(struct mount *mp, void *arg, int lkflags __unused, struct vnode **vpp) { return (autofs_node_vn(arg, mp, vpp)); } static int autofs_lookup(struct vop_lookup_args *ap) { struct vnode *dvp, *newvp, **vpp; struct mount *mp; struct autofs_mount *amp; struct autofs_node *anp, *child; struct componentname *cnp; int error, lock_flags; dvp = ap->a_dvp; vpp = ap->a_vpp; mp = dvp->v_mount; amp = VFSTOAUTOFS(mp); anp = dvp->v_data; cnp = ap->a_cnp; if (cnp->cn_flags & ISDOTDOT) { KASSERT(anp->an_parent != NULL, ("NULL parent")); /* * Note that in this case, dvp is the child vnode, and we * are looking up the parent vnode - exactly reverse from * normal operation. Unlocking dvp requires some rather * tricky unlock/relock dance to prevent mp from being freed; * use vn_vget_ino_gen() which takes care of all that. */ error = vn_vget_ino_gen(dvp, autofs_vget_callback, anp->an_parent, 0, vpp); if (error != 0) { AUTOFS_WARN("vn_vget_ino_gen() failed with error %d", error); return (error); } return (error); } if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { vref(dvp); *vpp = dvp; return (0); } if (autofs_cached(anp, cnp->cn_nameptr, cnp->cn_namelen) == false && autofs_ignore_thread(cnp->cn_thread) == false) { error = autofs_trigger_vn(dvp, cnp->cn_nameptr, cnp->cn_namelen, &newvp); if (error != 0) return (error); if (newvp != NULL) { error = VOP_LOOKUP(newvp, ap->a_vpp, ap->a_cnp); /* * Instead of figuring out whether our vnode should * be locked or not given the error and cnp flags, * just "copy" the lock status from vnode returned * by mounted filesystem's VOP_LOOKUP(). Get rid * of that new vnode afterwards. */ lock_flags = VOP_ISLOCKED(newvp); if (lock_flags == 0) { VOP_UNLOCK(dvp, 0); vrele(newvp); } else { vput(newvp); } return (error); } } - AUTOFS_LOCK(amp); + AUTOFS_SLOCK(amp); error = autofs_node_find(anp, cnp->cn_nameptr, cnp->cn_namelen, &child); if (error != 0) { if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) { - AUTOFS_UNLOCK(amp); + AUTOFS_SUNLOCK(amp); return (EJUSTRETURN); } - AUTOFS_UNLOCK(amp); + AUTOFS_SUNLOCK(amp); return (ENOENT); } /* * XXX: Dropping the node here is ok, because we never remove nodes. */ - AUTOFS_UNLOCK(amp); + AUTOFS_SUNLOCK(amp); error = autofs_node_vn(child, mp, vpp); if (error != 0) { if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) return (EJUSTRETURN); return (error); } return (0); } static int autofs_mkdir(struct vop_mkdir_args *ap) { struct vnode *vp; struct autofs_node *anp; struct autofs_mount *amp; struct autofs_node *child; int error; vp = ap->a_dvp; anp = vp->v_data; amp = VFSTOAUTOFS(vp->v_mount); /* * Do not allow mkdir() if the calling thread is not * automountd(8) descendant. */ if (autofs_ignore_thread(curthread) == false) return (EPERM); - AUTOFS_LOCK(amp); + AUTOFS_XLOCK(amp); error = autofs_node_new(anp, amp, ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen, &child); if (error != 0) { - AUTOFS_UNLOCK(amp); + AUTOFS_XUNLOCK(amp); return (error); } - AUTOFS_UNLOCK(amp); + AUTOFS_XUNLOCK(amp); error = autofs_node_vn(child, vp->v_mount, ap->a_vpp); return (error); } static int autofs_readdir_one(struct uio *uio, const char *name, int fileno) { struct dirent dirent; int error, i; memset(&dirent, 0, sizeof(dirent)); dirent.d_type = DT_DIR; dirent.d_reclen = AUTOFS_DELEN; dirent.d_fileno = fileno; /* PFS_DELEN was picked to fit PFS_NAMLEN */ for (i = 0; i < AUTOFS_NAMELEN - 1 && name[i] != '\0'; ++i) dirent.d_name[i] = name[i]; dirent.d_name[i] = 0; dirent.d_namlen = i; error = uiomove(&dirent, AUTOFS_DELEN, uio); return (error); } static int autofs_readdir(struct vop_readdir_args *ap) { struct vnode *vp, *newvp; struct autofs_mount *amp; struct autofs_node *anp, *child; struct uio *uio; off_t offset; int error, i, resid; vp = ap->a_vp; amp = VFSTOAUTOFS(vp->v_mount); anp = vp->v_data; uio = ap->a_uio; KASSERT(vp->v_type == VDIR, ("!VDIR")); if (autofs_cached(anp, NULL, 0) == false && autofs_ignore_thread(curthread) == false) { error = autofs_trigger_vn(vp, "", 0, &newvp); if (error != 0) return (error); if (newvp != NULL) { error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred, ap->a_eofflag, ap->a_ncookies, ap->a_cookies); vput(newvp); return (error); } } /* only allow reading entire entries */ offset = uio->uio_offset; resid = uio->uio_resid; if (offset < 0 || offset % AUTOFS_DELEN != 0 || (resid && resid < AUTOFS_DELEN)) return (EINVAL); if (resid == 0) return (0); if (ap->a_eofflag != NULL) *ap->a_eofflag = TRUE; if (offset == 0 && resid >= AUTOFS_DELEN) { error = autofs_readdir_one(uio, ".", anp->an_fileno); if (error != 0) return (error); offset += AUTOFS_DELEN; resid -= AUTOFS_DELEN; } if (offset == AUTOFS_DELEN && resid >= AUTOFS_DELEN) { if (anp->an_parent == NULL) { /* * XXX: Right? */ error = autofs_readdir_one(uio, "..", anp->an_fileno); } else { error = autofs_readdir_one(uio, "..", anp->an_parent->an_fileno); } if (error != 0) return (error); offset += AUTOFS_DELEN; resid -= AUTOFS_DELEN; } i = 2; /* Account for "." and "..". */ - AUTOFS_LOCK(amp); + AUTOFS_SLOCK(amp); TAILQ_FOREACH(child, &anp->an_children, an_next) { if (resid < AUTOFS_DELEN) { if (ap->a_eofflag != NULL) *ap->a_eofflag = 0; break; } /* * Skip entries returned by previous call to getdents(). */ i++; if (i * AUTOFS_DELEN <= offset) continue; error = autofs_readdir_one(uio, child->an_name, child->an_fileno); if (error != 0) { - AUTOFS_UNLOCK(amp); + AUTOFS_SUNLOCK(amp); return (error); } offset += AUTOFS_DELEN; resid -= AUTOFS_DELEN; } - AUTOFS_UNLOCK(amp); + AUTOFS_SUNLOCK(amp); return (0); } static int autofs_reclaim(struct vop_reclaim_args *ap) { struct vnode *vp = ap->a_vp; struct autofs_node *anp = vp->v_data; vp = ap->a_vp; anp = vp->v_data; /* * We do not free autofs_node here; instead we are * destroying them in autofs_node_delete(). */ sx_xlock(&anp->an_vnode_lock); anp->an_vnode = NULL; vp->v_data = NULL; sx_xunlock(&anp->an_vnode_lock); return (0); } struct vop_vector autofs_vnodeops = { .vop_default = &default_vnodeops, .vop_access = autofs_access, .vop_lookup = autofs_lookup, .vop_create = VOP_EOPNOTSUPP, .vop_getattr = autofs_getattr, .vop_link = VOP_EOPNOTSUPP, .vop_mkdir = autofs_mkdir, .vop_mknod = VOP_EOPNOTSUPP, .vop_read = VOP_EOPNOTSUPP, .vop_readdir = autofs_readdir, .vop_remove = VOP_EOPNOTSUPP, .vop_rename = VOP_EOPNOTSUPP, .vop_rmdir = VOP_EOPNOTSUPP, .vop_setattr = VOP_EOPNOTSUPP, .vop_symlink = VOP_EOPNOTSUPP, .vop_write = VOP_EOPNOTSUPP, .vop_reclaim = autofs_reclaim, }; int autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp, const char *name, int namelen, struct autofs_node **anpp) { struct autofs_node *anp; if (parent != NULL) - AUTOFS_ASSERT_LOCKED(parent->an_mount); + AUTOFS_ASSERT_XLOCKED(parent->an_mount); anp = uma_zalloc(autofs_node_zone, M_WAITOK | M_ZERO); if (namelen >= 0) anp->an_name = strndup(name, namelen, M_AUTOFS); else anp->an_name = strdup(name, M_AUTOFS); anp->an_fileno = atomic_fetchadd_int(&->am_last_fileno, 1); callout_init(&anp->an_callout, 1); /* * The reason for SX_NOWITNESS here is that witness(4) * cannot tell vnodes apart, so the following perfectly * valid lock order... * * vnode lock A -> autofsvlk B -> vnode lock B * * ... gets reported as a LOR. */ sx_init_flags(&anp->an_vnode_lock, "autofsvlk", SX_NOWITNESS); getnanotime(&anp->an_ctime); anp->an_parent = parent; anp->an_mount = amp; if (parent != NULL) TAILQ_INSERT_TAIL(&parent->an_children, anp, an_next); TAILQ_INIT(&anp->an_children); *anpp = anp; return (0); } int autofs_node_find(struct autofs_node *parent, const char *name, int namelen, struct autofs_node **anpp) { struct autofs_node *anp; AUTOFS_ASSERT_LOCKED(parent->an_mount); TAILQ_FOREACH(anp, &parent->an_children, an_next) { if (namelen >= 0) { if (strlen(anp->an_name) != namelen) continue; if (strncmp(anp->an_name, name, namelen) != 0) continue; } else { if (strcmp(anp->an_name, name) != 0) continue; } if (anpp != NULL) *anpp = anp; return (0); } return (ENOENT); } void autofs_node_delete(struct autofs_node *anp) { struct autofs_node *parent; - AUTOFS_ASSERT_LOCKED(anp->an_mount); + AUTOFS_ASSERT_XLOCKED(anp->an_mount); KASSERT(TAILQ_EMPTY(&anp->an_children), ("have children")); callout_drain(&anp->an_callout); parent = anp->an_parent; if (parent != NULL) TAILQ_REMOVE(&parent->an_children, anp, an_next); sx_destroy(&anp->an_vnode_lock); free(anp->an_name, M_AUTOFS); uma_zfree(autofs_node_zone, anp); } int autofs_node_vn(struct autofs_node *anp, struct mount *mp, struct vnode **vpp) { struct vnode *vp; int error; AUTOFS_ASSERT_UNLOCKED(anp->an_mount); sx_xlock(&anp->an_vnode_lock); vp = anp->an_vnode; if (vp != NULL) { error = vget(vp, LK_EXCLUSIVE | LK_RETRY, curthread); if (error != 0) { AUTOFS_WARN("vget failed with error %d", error); sx_xunlock(&anp->an_vnode_lock); return (error); } if (vp->v_iflag & VI_DOOMED) { /* * We got forcibly unmounted. */ AUTOFS_DEBUG("doomed vnode"); sx_xunlock(&anp->an_vnode_lock); vput(vp); return (ENOENT); } *vpp = vp; sx_xunlock(&anp->an_vnode_lock); return (0); } error = getnewvnode("autofs", mp, &autofs_vnodeops, &vp); if (error != 0) { sx_xunlock(&anp->an_vnode_lock); return (error); } error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); if (error != 0) { sx_xunlock(&anp->an_vnode_lock); vdrop(vp); return (error); } vp->v_type = VDIR; if (anp->an_parent == NULL) vp->v_vflag |= VV_ROOT; vp->v_data = anp; error = insmntque(vp, mp); if (error != 0) { AUTOFS_WARN("insmntque() failed with error %d", error); sx_xunlock(&anp->an_vnode_lock); return (error); } KASSERT(anp->an_vnode == NULL, ("lost race")); anp->an_vnode = vp; sx_xunlock(&anp->an_vnode_lock); *vpp = vp; return (0); }