Index: stable/10/usr.sbin/autofs/automount.c =================================================================== --- stable/10/usr.sbin/autofs/automount.c (revision 283234) +++ stable/10/usr.sbin/autofs/automount.c (revision 283235) @@ -1,404 +1,403 @@ /*- * 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 #include #include #include #include #include #include #include #include #include "common.h" #include "mntopts.h" static int unmount_by_statfs(const struct statfs *sb, bool force) { char *fsid_str; int error, ret, flags; ret = asprintf(&fsid_str, "FSID:%d:%d", sb->f_fsid.val[0], sb->f_fsid.val[1]); if (ret < 0) log_err(1, "asprintf"); log_debugx("unmounting %s using %s", sb->f_mntonname, fsid_str); flags = MNT_BYFSID; if (force) flags |= MNT_FORCE; error = unmount(fsid_str, flags); free(fsid_str); if (error != 0) log_warn("cannot unmount %s", sb->f_mntonname); return (error); } static const struct statfs * find_statfs(const struct statfs *mntbuf, int nitems, const char *mountpoint) { int i; for (i = 0; i < nitems; i++) { if (strcmp(mntbuf[i].f_mntonname, mountpoint) == 0) return (mntbuf + i); } return (NULL); } static void mount_autofs(const char *from, const char *fspath, const char *options, const char *prefix) { struct iovec *iov = NULL; char errmsg[255]; int error, iovlen = 0; create_directory(fspath); log_debugx("mounting %s on %s, prefix \"%s\", options \"%s\"", from, fspath, prefix, options); memset(errmsg, 0, sizeof(errmsg)); build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "autofs"), (size_t)-1); build_iovec(&iov, &iovlen, "fspath", __DECONST(void *, fspath), (size_t)-1); build_iovec(&iov, &iovlen, "from", __DECONST(void *, from), (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); /* * Append the options and mountpoint defined in auto_master(5); * this way automountd(8) does not need to parse it. */ build_iovec(&iov, &iovlen, "master_options", __DECONST(void *, options), (size_t)-1); build_iovec(&iov, &iovlen, "master_prefix", __DECONST(void *, prefix), (size_t)-1); error = nmount(iov, iovlen, 0); if (error != 0) { if (*errmsg != '\0') { log_err(1, "cannot mount %s on %s: %s", from, fspath, errmsg); } else { log_err(1, "cannot mount %s on %s", from, fspath); } } } static void -mount_if_not_already(const struct node *n, const char *map, - const struct statfs *mntbuf, int nitems) +mount_if_not_already(const struct node *n, const char *map, const char *options, + const char *prefix, const struct statfs *mntbuf, int nitems) { const struct statfs *sb; char *mountpoint; char *from; int ret; ret = asprintf(&from, "map %s", map); if (ret < 0) log_err(1, "asprintf"); mountpoint = node_path(n); sb = find_statfs(mntbuf, nitems, mountpoint); if (sb != NULL) { if (strcmp(sb->f_fstypename, "autofs") != 0) { log_debugx("unknown filesystem mounted " "on %s; mounting", mountpoint); /* * XXX: Compare options and 'from', * and update the mount if necessary. */ } else { log_debugx("autofs already mounted " "on %s", mountpoint); free(from); free(mountpoint); return; } } else { log_debugx("nothing mounted on %s; mounting", mountpoint); } - mount_autofs(from, mountpoint, n->n_options, n->n_key); + mount_autofs(from, mountpoint, options, prefix); free(from); free(mountpoint); } static void mount_unmount(struct node *root) { struct statfs *mntbuf; - struct node *n, *n2, *n3; + struct node *n, *n2; int i, nitems; nitems = getmntinfo(&mntbuf, MNT_WAIT); if (nitems <= 0) log_err(1, "getmntinfo"); log_debugx("unmounting stale autofs mounts"); for (i = 0; i < nitems; i++) { if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) { log_debugx("skipping %s, filesystem type is not autofs", mntbuf[i].f_mntonname); continue; } n = node_find(root, mntbuf[i].f_mntonname); if (n != NULL) { log_debugx("leaving autofs mounted on %s", mntbuf[i].f_mntonname); continue; } log_debugx("autofs mounted on %s not found " "in new configuration; unmounting", mntbuf[i].f_mntonname); unmount_by_statfs(&(mntbuf[i]), false); } log_debugx("mounting new autofs mounts"); TAILQ_FOREACH(n, &root->n_children, n_next) { if (!node_is_direct_map(n)) { - mount_if_not_already(n, n->n_map, mntbuf, nitems); + mount_if_not_already(n, n->n_map, n->n_options, + n->n_key, mntbuf, nitems); continue; } TAILQ_FOREACH(n2, &n->n_children, n_next) { - TAILQ_FOREACH(n3, &n2->n_children, n_next) { - mount_if_not_already(n3, n->n_map, - mntbuf, nitems); - } + mount_if_not_already(n2, n->n_map, n->n_options, + "/", mntbuf, nitems); } } } static void flush_autofs(const char *fspath) { struct iovec *iov = NULL; char errmsg[255]; int error, iovlen = 0; log_debugx("flushing %s", fspath); memset(errmsg, 0, sizeof(errmsg)); build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "autofs"), (size_t)-1); build_iovec(&iov, &iovlen, "fspath", __DECONST(void *, fspath), (size_t)-1); build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); error = nmount(iov, iovlen, MNT_UPDATE); if (error != 0) { if (*errmsg != '\0') { log_err(1, "cannot flush %s: %s", fspath, errmsg); } else { log_err(1, "cannot flush %s", fspath); } } } static void flush_caches(void) { struct statfs *mntbuf; int i, nitems; nitems = getmntinfo(&mntbuf, MNT_WAIT); if (nitems <= 0) log_err(1, "getmntinfo"); log_debugx("flushing autofs caches"); for (i = 0; i < nitems; i++) { if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) { log_debugx("skipping %s, filesystem type is not autofs", mntbuf[i].f_mntonname); continue; } flush_autofs(mntbuf[i].f_mntonname); } } static void unmount_automounted(bool force) { struct statfs *mntbuf; int i, nitems; nitems = getmntinfo(&mntbuf, MNT_WAIT); if (nitems <= 0) log_err(1, "getmntinfo"); log_debugx("unmounting automounted filesystems"); for (i = 0; i < nitems; i++) { if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) { log_debugx("skipping %s, filesystem type is autofs", mntbuf[i].f_mntonname); continue; } if ((mntbuf[i].f_flags & MNT_AUTOMOUNTED) == 0) { log_debugx("skipping %s, not automounted", mntbuf[i].f_mntonname); continue; } unmount_by_statfs(&(mntbuf[i]), force); } } static void usage_automount(void) { fprintf(stderr, "usage: automount [-D name=value][-o opts][-Lcfuv]\n"); exit(1); } int main_automount(int argc, char **argv) { struct node *root; int ch, debug = 0, show_maps = 0; char *options = NULL; bool do_unmount = false, force_unmount = false, flush = false; /* * Note that in automount(8), the only purpose of variable * handling is to aid in debugging maps (automount -L). */ defined_init(); while ((ch = getopt(argc, argv, "D:Lfco:uv")) != -1) { switch (ch) { case 'D': defined_parse_and_add(optarg); break; case 'L': show_maps++; break; case 'c': flush = true; break; case 'f': force_unmount = true; break; case 'o': if (options == NULL) { options = checked_strdup(optarg); } else { options = concat(options, ',', optarg); } break; case 'u': do_unmount = true; break; case 'v': debug++; break; case '?': default: usage_automount(); } } argc -= optind; if (argc != 0) usage_automount(); if (force_unmount && !do_unmount) usage_automount(); log_init(debug); if (flush) { flush_caches(); return (0); } if (do_unmount) { unmount_automounted(force_unmount); return (0); } root = node_new_root(); parse_master(root, AUTO_MASTER_PATH); if (show_maps) { if (options != NULL) { root->n_options = concat(options, ',', root->n_options); } if (show_maps > 1) { node_expand_indirect_maps(root); node_expand_ampersand(root, NULL); } node_expand_defined(root); node_print(root); return (0); } mount_unmount(root); return (0); } Index: stable/10/usr.sbin/autofs/automountd.c =================================================================== --- stable/10/usr.sbin/autofs/automountd.c (revision 283234) +++ stable/10/usr.sbin/autofs/automountd.c (revision 283235) @@ -1,565 +1,557 @@ /*- * 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 #include #include #include #include #include #include #include #include #include "autofs_ioctl.h" #include "common.h" #define AUTOMOUNTD_PIDFILE "/var/run/automountd.pid" static int nchildren = 0; static int autofs_fd; static int request_id; static void done(int request_error, bool wildcards) { struct autofs_daemon_done add; int error; memset(&add, 0, sizeof(add)); add.add_id = request_id; add.add_wildcards = wildcards; add.add_error = request_error; log_debugx("completing request %d with error %d", request_id, request_error); error = ioctl(autofs_fd, AUTOFSDONE, &add); if (error != 0) log_warn("AUTOFSDONE"); } /* * Remove "fstype=whatever" from optionsp and return the "whatever" part. */ static char * pick_option(const char *option, char **optionsp) { char *tofree, *pair, *newoptions; char *picked = NULL; bool first = true; tofree = *optionsp; newoptions = calloc(strlen(*optionsp) + 1, 1); if (newoptions == NULL) log_err(1, "calloc"); while ((pair = strsep(optionsp, ",")) != NULL) { /* * XXX: strncasecmp(3) perhaps? */ if (strncmp(pair, option, strlen(option)) == 0) { picked = checked_strdup(pair + strlen(option)); } else { if (first == false) strcat(newoptions, ","); else first = false; strcat(newoptions, pair); } } free(tofree); *optionsp = newoptions; return (picked); } static void create_subtree(const struct node *node, bool incomplete) { const struct node *child; char *path; bool wildcard_found = false; /* * Skip wildcard nodes. */ if (strcmp(node->n_key, "*") == 0) return; path = node_path(node); log_debugx("creating subtree at %s", path); create_directory(path); if (incomplete) { TAILQ_FOREACH(child, &node->n_children, n_next) { if (strcmp(child->n_key, "*") == 0) { wildcard_found = true; break; } } if (wildcard_found) { log_debugx("node %s contains wildcard entry; " "not creating its subdirectories due to -d flag", path); free(path); return; } } free(path); TAILQ_FOREACH(child, &node->n_children, n_next) create_subtree(child, incomplete); } static void exit_callback(void) { done(EIO, true); } static void handle_request(const struct autofs_daemon_request *adr, char *cmdline_options, bool incomplete_hierarchy) { const char *map; struct node *root, *parent, *node; FILE *f; char *options, *fstype, *nobrowse, *retrycnt, *tmp; int error; bool wildcards; log_debugx("got request %d: from %s, path %s, prefix \"%s\", " "key \"%s\", options \"%s\"", adr->adr_id, adr->adr_from, adr->adr_path, adr->adr_prefix, adr->adr_key, adr->adr_options); /* * Try to notify the kernel about any problems. */ request_id = adr->adr_id; atexit(exit_callback); if (strncmp(adr->adr_from, "map ", 4) != 0) { log_errx(1, "invalid mountfrom \"%s\"; failing request", adr->adr_from); } map = adr->adr_from + 4; /* 4 for strlen("map "); */ root = node_new_root(); if (adr->adr_prefix[0] == '\0' || strcmp(adr->adr_prefix, "/") == 0) { parent = root; } else { parent = node_new_map(root, checked_strdup(adr->adr_prefix), - checked_strdup(adr->adr_options), checked_strdup(map), + NULL, checked_strdup(map), checked_strdup("[kernel request]"), lineno); } /* * "Wildcards" here actually means "make autofs(4) request * automountd(8) action if the node being looked up does not * exist, even though the parent is marked as cached". This * needs to be done for maps with wildcard entries, but also * for special and executable maps. */ parse_map(parent, map, adr->adr_key[0] != '\0' ? adr->adr_key : NULL, &wildcards); if (!wildcards) wildcards = node_has_wildcards(parent); if (wildcards) log_debugx("map may contain wildcard entries"); else log_debugx("map does not contain wildcard entries"); if (adr->adr_key[0] != '\0') node_expand_wildcard(root, adr->adr_key); node = node_find(root, adr->adr_path); if (node == NULL) { log_errx(1, "map %s does not contain key for \"%s\"; " "failing mount", map, adr->adr_path); } + options = node_options(node); + options = concat(adr->adr_options, ',', options); + + /* + * Prepend options passed via automountd(8) command line. + */ + if (cmdline_options != NULL) + options = concat(cmdline_options, ',', options); + if (node->n_location == NULL) { log_debugx("found node defined at %s:%d; not a mountpoint", node->n_config_file, node->n_config_line); - options = node_options(node); - - /* - * Prepend options passed via automountd(8) command line. - */ - if (cmdline_options != NULL) { - options = concat(cmdline_options, ',', options); - } - nobrowse = pick_option("nobrowse", &options); if (nobrowse != NULL && adr->adr_key[0] == '\0') { log_debugx("skipping map %s due to \"nobrowse\" " "option; exiting", map); done(0, true); /* * Exit without calling exit_callback(). */ quick_exit(0); } /* * Not a mountpoint; create directories in the autofs mount * and complete the request. */ create_subtree(node, incomplete_hierarchy); if (incomplete_hierarchy && adr->adr_key[0] != '\0') { /* * We still need to create the single subdirectory * user is trying to access. */ tmp = concat(adr->adr_path, '/', adr->adr_key); node = node_find(root, tmp); if (node != NULL) create_subtree(node, false); } log_debugx("nothing to mount; exiting"); done(0, wildcards); /* * Exit without calling exit_callback(). */ quick_exit(0); } log_debugx("found node defined at %s:%d; it is a mountpoint", node->n_config_file, node->n_config_line); node_expand_ampersand(node, adr->adr_key[0] != '\0' ? adr->adr_key : NULL); error = node_expand_defined(node); if (error != 0) { log_errx(1, "variable expansion failed for %s; " "failing mount", adr->adr_path); } - - options = node_options(node); - - /* - * Prepend options passed via automountd(8) command line. - */ - if (cmdline_options != NULL) - options = concat(cmdline_options, ',', options); /* * Append "automounted". */ options = concat(options, ',', "automounted"); /* * Remove "nobrowse", mount(8) doesn't understand it. */ pick_option("nobrowse", &options); /* * Figure out fstype. */ fstype = pick_option("fstype=", &options); if (fstype == NULL) { log_debugx("fstype not specified in options; " "defaulting to \"nfs\""); fstype = checked_strdup("nfs"); } if (strcmp(fstype, "nfs") == 0) { /* * The mount_nfs(8) command defaults to retry undefinitely. * We do not want that behaviour, because it leaves mount_nfs(8) * instances and automountd(8) children hanging forever. * Disable retries unless the option was passed explicitly. */ retrycnt = pick_option("retrycnt=", &options); if (retrycnt == NULL) { log_debugx("retrycnt not specified in options; " "defaulting to 1"); options = concat(options, ',', "retrycnt=1"); } else { options = concat(options, ',', concat("retrycnt", '=', retrycnt)); } } f = auto_popen("mount", "-t", fstype, "-o", options, node->n_location, adr->adr_path, NULL); assert(f != NULL); error = auto_pclose(f); if (error != 0) log_errx(1, "mount failed"); log_debugx("mount done; exiting"); done(0, wildcards); /* * Exit without calling exit_callback(). */ quick_exit(0); } static void sigchld_handler(int dummy __unused) { /* * The only purpose of this handler is to make SIGCHLD * interrupt the AUTOFSREQUEST ioctl(2), so we can call * wait_for_children(). */ } static void register_sigchld(void) { struct sigaction sa; int error; bzero(&sa, sizeof(sa)); sa.sa_handler = sigchld_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGCHLD, &sa, NULL); if (error != 0) log_err(1, "sigaction"); } static int wait_for_children(bool block) { pid_t pid; int status; int num = 0; for (;;) { /* * If "block" is true, wait for at least one process. */ if (block && num == 0) pid = wait4(-1, &status, 0, NULL); else pid = wait4(-1, &status, WNOHANG, NULL); if (pid <= 0) break; if (WIFSIGNALED(status)) { log_warnx("child process %d terminated with signal %d", pid, WTERMSIG(status)); } else if (WEXITSTATUS(status) != 0) { log_debugx("child process %d terminated with exit status %d", pid, WEXITSTATUS(status)); } else { log_debugx("child process %d terminated gracefully", pid); } num++; } return (num); } static void usage_automountd(void) { fprintf(stderr, "usage: automountd [-D name=value][-m maxproc]" "[-o opts][-Tidv]\n"); exit(1); } int main_automountd(int argc, char **argv) { struct pidfh *pidfh; pid_t pid, otherpid; const char *pidfile_path = AUTOMOUNTD_PIDFILE; char *options = NULL; struct autofs_daemon_request request; int ch, debug = 0, error, maxproc = 30, retval, saved_errno; bool dont_daemonize = false, incomplete_hierarchy = false; defined_init(); while ((ch = getopt(argc, argv, "D:Tdim:o:v")) != -1) { switch (ch) { case 'D': defined_parse_and_add(optarg); break; case 'T': /* * For compatibility with other implementations, * such as OS X. */ debug++; break; case 'd': dont_daemonize = true; debug++; break; case 'i': incomplete_hierarchy = true; break; case 'm': maxproc = atoi(optarg); break; case 'o': if (options == NULL) { options = checked_strdup(optarg); } else { options = concat(options, ',', optarg); } break; case 'v': debug++; break; case '?': default: usage_automountd(); } } argc -= optind; if (argc != 0) usage_automountd(); log_init(debug); pidfh = pidfile_open(pidfile_path, 0600, &otherpid); if (pidfh == NULL) { if (errno == EEXIST) { log_errx(1, "daemon already running, pid: %jd.", (intmax_t)otherpid); } log_err(1, "cannot open or create pidfile \"%s\"", pidfile_path); } autofs_fd = open(AUTOFS_PATH, O_RDWR | O_CLOEXEC); if (autofs_fd < 0 && errno == ENOENT) { saved_errno = errno; retval = kldload("autofs"); if (retval != -1) autofs_fd = open(AUTOFS_PATH, O_RDWR | O_CLOEXEC); else errno = saved_errno; } if (autofs_fd < 0) log_err(1, "failed to open %s", AUTOFS_PATH); if (dont_daemonize == false) { if (daemon(0, 0) == -1) { log_warn("cannot daemonize"); pidfile_remove(pidfh); exit(1); } } else { lesser_daemon(); } pidfile_write(pidfh); register_sigchld(); for (;;) { log_debugx("waiting for request from the kernel"); memset(&request, 0, sizeof(request)); error = ioctl(autofs_fd, AUTOFSREQUEST, &request); if (error != 0) { if (errno == EINTR) { nchildren -= wait_for_children(false); assert(nchildren >= 0); continue; } log_err(1, "AUTOFSREQUEST"); } if (dont_daemonize) { log_debugx("not forking due to -d flag; " "will exit after servicing a single request"); } else { nchildren -= wait_for_children(false); assert(nchildren >= 0); while (maxproc > 0 && nchildren >= maxproc) { log_debugx("maxproc limit of %d child processes hit; " "waiting for child process to exit", maxproc); nchildren -= wait_for_children(true); assert(nchildren >= 0); } log_debugx("got request; forking child process #%d", nchildren); nchildren++; pid = fork(); if (pid < 0) log_err(1, "fork"); if (pid > 0) continue; } pidfile_close(pidfh); handle_request(&request, options, incomplete_hierarchy); } pidfile_close(pidfh); return (0); } Index: stable/10 =================================================================== --- stable/10 (revision 283234) +++ stable/10 (revision 283235) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r279846