Index: projects/nfs-over-tls/usr.sbin/mountd/mountd/mountd.8 =================================================================== --- projects/nfs-over-tls/usr.sbin/mountd/mountd/mountd.8 (revision 360587) +++ projects/nfs-over-tls/usr.sbin/mountd/mountd/mountd.8 (nonexistent) @@ -1,198 +0,0 @@ -.\" Copyright (c) 1989, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" 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. -.\" 3. 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. -.\" -.\" @(#)mountd.8 8.4 (Berkeley) 4/28/95 -.\" $FreeBSD$ -.\" -.Dd August 1, 2019 -.Dt MOUNTD 8 -.Os -.Sh NAME -.Nm mountd -.Nd service remote -.Tn NFS -mount requests -.Sh SYNOPSIS -.Nm -.Op Fl 2delnrS -.Op Fl h Ar bindip -.Op Fl p Ar port -.Op Ar exportsfile ... -.Sh DESCRIPTION -The -.Nm -utility is the server for -.Tn NFS -mount requests from other client machines. -It listens for service requests at the port indicated in the -.Tn NFS -server specification; see -.%T "Network File System Protocol Specification" , -RFC1094, Appendix A and -.%T "NFS: Network File System Version 3 Protocol Specification" , -RFC1813, Appendix I. -.Pp -The following options are available: -.Bl -tag -width indent -.It Fl 2 -Allow the administrator to force clients to use only the -version 2 -.Tn NFS -protocol to mount file systems from this server. -.It Fl d -Output debugging information. -.Nm -will not detach from the controlling terminal and will print -debugging messages to stderr. -.It Fl e -Ignored; included for backward compatibility. -.It Fl h Ar bindip -Specify specific IP addresses to bind to for TCP and UDP requests. -This option may be specified multiple times. -If no -.Fl h -option is specified, -.Nm -will bind to -.Dv INADDR_ANY . -Note that when specifying IP addresses with -.Fl h , -.Nm -will automatically add -.Li 127.0.0.1 -and if IPv6 is enabled, -.Li ::1 -to the list. -.It Fl l -Cause all succeeded -.Nm -requests to be logged. -.It Fl n -Allow non-root mount requests to be served. -This should only be specified if there are clients such as PC's, -that require it. -It will automatically clear the vfs.nfsd.nfs_privport sysctl flag, which -controls if the kernel will accept NFS requests from reserved ports only. -.It Fl p Ar port -Force -.Nm -to bind to the specified port, for both -.Dv AF_INET -and -.Dv AF_INET6 -address families. -This is typically done to ensure that the port which -.Nm -binds to is a known quantity which can be used in firewall rulesets. -If -.Nm -cannot bind to this port, an appropriate error will be recorded in -the system log, and the daemon will then exit. -.It Fl r -Allow mount RPCs requests for regular files to be served. -Although this seems to violate the mount protocol specification, -some diskless workstations do mount requests for -their swapfiles and expect them to be regular files. -Since a regular file cannot be specified in -.Pa /etc/exports , -the entire file system in which the swapfiles resides -will have to be exported with the -.Fl alldirs -flag. -.It Ar exportsfile -Specify an alternate location -for the exports file. -More than one exports file can be specified. -.It Fl S -Tell mountd to suspend/resume execution of the nfsd threads whenever -the exports list is being reloaded. -This avoids intermittent access -errors for clients that do NFS RPCs while the exports are being -reloaded, but introduces a delay in RPC response while the reload -is in progress. -If -.Nm -crashes while an exports load is in progress, -.Nm -must be restarted to get the nfsd threads running again, if this -option is used. -.El -.Pp -When -.Nm -is started, -it loads the export host addresses and options into the kernel -using the -.Xr mount 2 -system call. -After changing the exports file, -a hangup signal should be sent to the -.Nm -daemon -to get it to reload the export information. -After sending the SIGHUP -(kill \-s HUP `cat /var/run/mountd.pid`), -check the syslog output to see if -.Nm -logged any parsing -errors in the exports file. -.Pp -If -.Nm -detects that the running kernel does not include -.Tn NFS -support, it will attempt to load a loadable kernel module containing -.Tn NFS -code, using -.Xr kldload 2 . -If this fails, or no -.Tn NFS -KLD was available, -.Nm -exits with an error. -.Sh FILES -.Bl -tag -width /var/run/mountd.pid -compact -.It Pa /etc/exports -the list of exported file systems -.It Pa /var/run/mountd.pid -the pid of the currently running mountd -.It Pa /var/db/mountdtab -the current list of remote mounted file systems -.El -.Sh SEE ALSO -.Xr nfsstat 1 , -.Xr kldload 2 , -.Xr nfsv4 4 , -.Xr exports 5 , -.Xr nfsd 8 , -.Xr rpcbind 8 , -.Xr showmount 8 -.Sh HISTORY -The -.Nm -utility first appeared in -.Bx 4.4 . Property changes on: projects/nfs-over-tls/usr.sbin/mountd/mountd/mountd.8 ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: projects/nfs-over-tls/usr.sbin/mountd/mountd/mountd.c =================================================================== --- projects/nfs-over-tls/usr.sbin/mountd/mountd/mountd.c (revision 360587) +++ projects/nfs-over-tls/usr.sbin/mountd/mountd/mountd.c (nonexistent) @@ -1,3793 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Herb Hasler and 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. - * 3. 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. - */ - -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1989, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /*not lint*/ - -#if 0 -#ifndef lint -static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95"; -#endif /*not lint*/ -#endif - -#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 -#include -#include -#include -#include -#include -#include -#include -#include -#include "pathnames.h" -#include "mntopts.h" - -#ifdef DEBUG -#include -#endif - -/* - * Structures for keeping the mount list and export list - */ -struct mountlist { - char ml_host[MNTNAMLEN+1]; - char ml_dirp[MNTPATHLEN+1]; - - SLIST_ENTRY(mountlist) next; -}; - -struct dirlist { - struct dirlist *dp_left; - struct dirlist *dp_right; - int dp_flag; - struct hostlist *dp_hosts; /* List of hosts this dir exported to */ - char *dp_dirp; -}; -/* dp_flag bits */ -#define DP_DEFSET 0x1 -#define DP_HOSTSET 0x2 - -struct exportlist { - struct dirlist *ex_dirl; - struct dirlist *ex_defdir; - struct grouplist *ex_grphead; - int ex_flag; - fsid_t ex_fs; - char *ex_fsdir; - char *ex_indexfile; - struct xucred ex_defanon; - int ex_defexflags; - int ex_numsecflavors; - int ex_secflavors[MAXSECFLAVORS]; - int ex_defnumsecflavors; - int ex_defsecflavors[MAXSECFLAVORS]; - - SLIST_ENTRY(exportlist) entries; -}; -/* ex_flag bits */ -#define EX_LINKED 0x1 -#define EX_DONE 0x2 -#define EX_DEFSET 0x4 -#define EX_PUBLICFH 0x8 - -SLIST_HEAD(exportlisthead, exportlist); - -struct netmsk { - struct sockaddr_storage nt_net; - struct sockaddr_storage nt_mask; - char *nt_name; -}; - -union grouptypes { - struct addrinfo *gt_addrinfo; - struct netmsk gt_net; -}; - -struct grouplist { - int gr_type; - union grouptypes gr_ptr; - struct grouplist *gr_next; - struct xucred gr_anon; - int gr_exflags; - int gr_flag; - int gr_numsecflavors; - int gr_secflavors[MAXSECFLAVORS]; -}; -/* Group types */ -#define GT_NULL 0x0 -#define GT_HOST 0x1 -#define GT_NET 0x2 -#define GT_DEFAULT 0x3 -#define GT_IGNORE 0x5 - -/* Group flags */ -#define GR_FND 0x1 - -struct hostlist { - int ht_flag; /* Uses DP_xx bits */ - struct grouplist *ht_grp; - struct hostlist *ht_next; -}; - -struct fhreturn { - int fhr_flag; - int fhr_vers; - nfsfh_t fhr_fh; - int fhr_numsecflavors; - int *fhr_secflavors; -}; - -#define GETPORT_MAXTRY 20 /* Max tries to get a port # */ - -/* Global defs */ -static char *add_expdir(struct dirlist **, char *, int); -static void add_dlist(struct dirlist **, struct dirlist *, - struct grouplist *, int, struct exportlist *, - struct xucred *, int); -static void add_mlist(char *, char *); -static int check_dirpath(char *); -static int check_options(struct dirlist *); -static int checkmask(struct sockaddr *sa); -static int chk_host(struct dirlist *, struct sockaddr *, int *, int *, - int *, int **); -static char *strsep_quote(char **stringp, const char *delim); -static int create_service(struct netconfig *nconf); -static void complete_service(struct netconfig *nconf, char *port_str); -static void clearout_service(void); -static void del_mlist(char *hostp, char *dirp); -static struct dirlist *dirp_search(struct dirlist *, char *); -static int do_export_mount(struct exportlist *, struct statfs *); -static int do_mount(struct exportlist *, struct grouplist *, int, - struct xucred *, char *, int, struct statfs *, int, int *); -static int do_opt(char **, char **, struct exportlist *, - struct grouplist *, int *, int *, struct xucred *); -static struct exportlist *ex_search(fsid_t *, struct exportlisthead *); -static struct exportlist *get_exp(void); -static void free_dir(struct dirlist *); -static void free_exp(struct exportlist *); -static void free_grp(struct grouplist *); -static void free_host(struct hostlist *); -static void free_v4rootexp(void); -static void get_exportlist_one(int); -static void get_exportlist(int); -static void insert_exports(struct exportlist *, struct exportlisthead *); -static void free_exports(struct exportlisthead *); -static void read_exportfile(int); -static int compare_nmount_exportlist(struct iovec *, int, char *); -static int compare_export(struct exportlist *, struct exportlist *); -static int compare_cred(struct xucred *, struct xucred *); -static int compare_secflavor(int *, int *, int); -static void delete_export(struct iovec *, int, struct statfs *, char *); -static int get_host(char *, struct grouplist *, struct grouplist *); -static struct hostlist *get_ht(void); -static int get_line(void); -static void get_mountlist(void); -static int get_net(char *, struct netmsk *, int); -static void getexp_err(struct exportlist *, struct grouplist *, const char *); -static struct grouplist *get_grp(void); -static void hang_dirp(struct dirlist *, struct grouplist *, - struct exportlist *, int, struct xucred *, int); -static void huphandler(int sig); -static int makemask(struct sockaddr_storage *ssp, int bitlen); -static void mntsrv(struct svc_req *, SVCXPRT *); -static void nextfield(char **, char **); -static void out_of_mem(void); -static void parsecred(char *, struct xucred *); -static int parsesec(char *, struct exportlist *); -static int put_exlist(struct dirlist *, XDR *, struct dirlist *, - int *, int); -static void *sa_rawaddr(struct sockaddr *sa, int *nbytes); -static int sacmp(struct sockaddr *sa1, struct sockaddr *sa2, - struct sockaddr *samask); -static int scan_tree(struct dirlist *, struct sockaddr *); -static void usage(void); -static int xdr_dir(XDR *, char *); -static int xdr_explist(XDR *, caddr_t); -static int xdr_explist_brief(XDR *, caddr_t); -static int xdr_explist_common(XDR *, caddr_t, int); -static int xdr_fhs(XDR *, caddr_t); -static int xdr_mlist(XDR *, caddr_t); -static void terminate(int); - -#define EXPHASH(f) (fnv_32_buf((f), sizeof(fsid_t), 0) % exphashsize) -static struct exportlisthead *exphead = NULL; -static struct exportlisthead *oldexphead = NULL; -static int exphashsize = 0; -static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead); -static char *exnames_default[2] = { _PATH_EXPORTS, NULL }; -static char **exnames; -static char **hosts = NULL; -static struct xucred def_anon = { - XUCRED_VERSION, - (uid_t)65534, - 1, - { (gid_t)65533 }, - { NULL } -}; -static int force_v2 = 0; -static int resvport_only = 1; -static int nhosts = 0; -static int dir_only = 1; -static int dolog = 0; -static int got_sighup = 0; -static int xcreated = 0; - -static char *svcport_str = NULL; -static int mallocd_svcport = 0; -static int *sock_fd; -static int sock_fdcnt; -static int sock_fdpos; -static int suspend_nfsd = 0; - -static int opt_flags; -static int have_v6 = 1; - -static int v4root_phase = 0; -static char v4root_dirpath[PATH_MAX + 1]; -static struct exportlist *v4root_ep = NULL; -static int has_publicfh = 0; -static int has_set_publicfh = 0; - -static struct pidfh *pfh = NULL; -/* Bits for opt_flags above */ -#define OP_MAPROOT 0x01 -#define OP_MAPALL 0x02 -/* 0x4 free */ -#define OP_MASK 0x08 -#define OP_NET 0x10 -#define OP_ALLDIRS 0x40 -#define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */ -#define OP_QUIET 0x100 -#define OP_MASKLEN 0x200 -#define OP_SEC 0x400 - -#ifdef DEBUG -static int debug = 1; -static void SYSLOG(int, const char *, ...) __printflike(2, 3); -#define syslog SYSLOG -#else -static int debug = 0; -#endif - -/* - * The LOGDEBUG() syslog() calls are always compiled into the daemon. - * To enable them, create a file at _PATH_MOUNTDDEBUG. This file can be empty. - * To disable the logging, just delete the file at _PATH_MOUNTDDEBUG. - */ -static int logdebug = 0; -#define LOGDEBUG(format, ...) \ - (logdebug ? syslog(LOG_DEBUG, format, ## __VA_ARGS__) : 0) - -/* - * Similar to strsep(), but it allows for quoted strings - * and escaped characters. - * - * It returns the string (or NULL, if *stringp is NULL), - * which is a de-quoted version of the string if necessary. - * - * It modifies *stringp in place. - */ -static char * -strsep_quote(char **stringp, const char *delim) -{ - char *srcptr, *dstptr, *retval; - char quot = 0; - - if (stringp == NULL || *stringp == NULL) - return (NULL); - - srcptr = dstptr = retval = *stringp; - - while (*srcptr) { - /* - * We're looking for several edge cases here. - * First: if we're in quote state (quot != 0), - * then we ignore the delim characters, but otherwise - * process as normal, unless it is the quote character. - * Second: if the current character is a backslash, - * we take the next character as-is, without checking - * for delim, quote, or backslash. Exception: if the - * next character is a NUL, that's the end of the string. - * Third: if the character is a quote character, we toggle - * quote state. - * Otherwise: check the current character for NUL, or - * being in delim, and end the string if either is true. - */ - if (*srcptr == '\\') { - srcptr++; - /* - * The edge case here is if the next character - * is NUL, we want to stop processing. But if - * it's not NUL, then we simply want to copy it. - */ - if (*srcptr) { - *dstptr++ = *srcptr++; - } - continue; - } - if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) { - quot = *srcptr++; - continue; - } - if (quot && *srcptr == quot) { - /* End of the quoted part */ - quot = 0; - srcptr++; - continue; - } - if (!quot && strchr(delim, *srcptr)) - break; - *dstptr++ = *srcptr++; - } - - *stringp = (*srcptr == '\0') ? NULL : srcptr + 1; - *dstptr = 0; /* Terminate the string */ - return (retval); -} - -/* - * Mountd server for NFS mount protocol as described in: - * NFS: Network File System Protocol Specification, RFC1094, Appendix A - * The optional arguments are the exports file name - * default: _PATH_EXPORTS - * and "-n" to allow nonroot mount. - */ -int -main(int argc, char **argv) -{ - fd_set readfds; - struct netconfig *nconf; - char *endptr, **hosts_bak; - void *nc_handle; - pid_t otherpid; - in_port_t svcport; - int c, k, s; - int maxrec = RPC_MAXDATASIZE; - int attempt_cnt, port_len, port_pos, ret; - char **port_list; - - /* Check that another mountd isn't already running. */ - pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid); - if (pfh == NULL) { - if (errno == EEXIST) - errx(1, "mountd already running, pid: %d.", otherpid); - warn("cannot open or create pidfile"); - } - - s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); - if (s < 0) - have_v6 = 0; - else - close(s); - - while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1) - switch (c) { - case '2': - force_v2 = 1; - break; - case 'e': - /* now a no-op, since this is the default */ - break; - case 'n': - resvport_only = 0; - break; - case 'r': - dir_only = 0; - break; - case 'd': - debug = debug ? 0 : 1; - break; - case 'l': - dolog = 1; - break; - case 'p': - endptr = NULL; - svcport = (in_port_t)strtoul(optarg, &endptr, 10); - if (endptr == NULL || *endptr != '\0' || - svcport == 0 || svcport >= IPPORT_MAX) - usage(); - svcport_str = strdup(optarg); - break; - case 'h': - ++nhosts; - hosts_bak = hosts; - hosts_bak = realloc(hosts, nhosts * sizeof(char *)); - if (hosts_bak == NULL) { - if (hosts != NULL) { - for (k = 0; k < nhosts; k++) - free(hosts[k]); - free(hosts); - out_of_mem(); - } - } - hosts = hosts_bak; - hosts[nhosts - 1] = strdup(optarg); - if (hosts[nhosts - 1] == NULL) { - for (k = 0; k < (nhosts - 1); k++) - free(hosts[k]); - free(hosts); - out_of_mem(); - } - break; - case 'S': - suspend_nfsd = 1; - break; - default: - usage(); - } - - if (modfind("nfsd") < 0) { - /* Not present in kernel, try loading it */ - if (kldload("nfsd") < 0 || modfind("nfsd") < 0) - errx(1, "NFS server is not available"); - } - - argc -= optind; - argv += optind; - if (argc > 0) - exnames = argv; - else - exnames = exnames_default; - openlog("mountd", LOG_PID, LOG_DAEMON); - if (debug) - warnx("getting export list"); - get_exportlist(0); - if (debug) - warnx("getting mount list"); - get_mountlist(); - if (debug) - warnx("here we go"); - if (debug == 0) { - daemon(0, 0); - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - } - signal(SIGHUP, huphandler); - signal(SIGTERM, terminate); - signal(SIGPIPE, SIG_IGN); - - pidfile_write(pfh); - - rpcb_unset(MOUNTPROG, MOUNTVERS, NULL); - rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL); - rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); - - if (!resvport_only) { - if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL, - &resvport_only, sizeof(resvport_only)) != 0 && - errno != ENOENT) { - syslog(LOG_ERR, "sysctl: %m"); - exit(1); - } - } - - /* - * If no hosts were specified, add a wildcard entry to bind to - * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the - * list. - */ - if (nhosts == 0) { - hosts = malloc(sizeof(char *)); - if (hosts == NULL) - out_of_mem(); - hosts[0] = "*"; - nhosts = 1; - } else { - hosts_bak = hosts; - if (have_v6) { - hosts_bak = realloc(hosts, (nhosts + 2) * - sizeof(char *)); - if (hosts_bak == NULL) { - for (k = 0; k < nhosts; k++) - free(hosts[k]); - free(hosts); - out_of_mem(); - } else - hosts = hosts_bak; - nhosts += 2; - hosts[nhosts - 2] = "::1"; - } else { - hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *)); - if (hosts_bak == NULL) { - for (k = 0; k < nhosts; k++) - free(hosts[k]); - free(hosts); - out_of_mem(); - } else { - nhosts += 1; - hosts = hosts_bak; - } - } - - hosts[nhosts - 1] = "127.0.0.1"; - } - - attempt_cnt = 1; - sock_fdcnt = 0; - sock_fd = NULL; - port_list = NULL; - port_len = 0; - nc_handle = setnetconfig(); - while ((nconf = getnetconfig(nc_handle))) { - if (nconf->nc_flag & NC_VISIBLE) { - if (have_v6 == 0 && strcmp(nconf->nc_protofmly, - "inet6") == 0) { - /* DO NOTHING */ - } else { - ret = create_service(nconf); - if (ret == 1) - /* Ignore this call */ - continue; - if (ret < 0) { - /* - * Failed to bind port, so close off - * all sockets created and try again - * if the port# was dynamically - * assigned via bind(2). - */ - clearout_service(); - if (mallocd_svcport != 0 && - attempt_cnt < GETPORT_MAXTRY) { - free(svcport_str); - svcport_str = NULL; - mallocd_svcport = 0; - } else { - errno = EADDRINUSE; - syslog(LOG_ERR, - "bindresvport_sa: %m"); - exit(1); - } - - /* Start over at the first service. */ - free(sock_fd); - sock_fdcnt = 0; - sock_fd = NULL; - nc_handle = setnetconfig(); - attempt_cnt++; - } else if (mallocd_svcport != 0 && - attempt_cnt == GETPORT_MAXTRY) { - /* - * For the last attempt, allow - * different port #s for each nconf - * by saving the svcport_str and - * setting it back to NULL. - */ - port_list = realloc(port_list, - (port_len + 1) * sizeof(char *)); - if (port_list == NULL) - out_of_mem(); - port_list[port_len++] = svcport_str; - svcport_str = NULL; - mallocd_svcport = 0; - } - } - } - } - - /* - * Successfully bound the ports, so call complete_service() to - * do the rest of the setup on the service(s). - */ - sock_fdpos = 0; - port_pos = 0; - nc_handle = setnetconfig(); - while ((nconf = getnetconfig(nc_handle))) { - if (nconf->nc_flag & NC_VISIBLE) { - if (have_v6 == 0 && strcmp(nconf->nc_protofmly, - "inet6") == 0) { - /* DO NOTHING */ - } else if (port_list != NULL) { - if (port_pos >= port_len) { - syslog(LOG_ERR, "too many port#s"); - exit(1); - } - complete_service(nconf, port_list[port_pos++]); - } else - complete_service(nconf, svcport_str); - } - } - endnetconfig(nc_handle); - free(sock_fd); - if (port_list != NULL) { - for (port_pos = 0; port_pos < port_len; port_pos++) - free(port_list[port_pos]); - free(port_list); - } - - if (xcreated == 0) { - syslog(LOG_ERR, "could not create any services"); - exit(1); - } - - /* Expand svc_run() here so that we can call get_exportlist(). */ - for (;;) { - if (got_sighup) { - get_exportlist(1); - got_sighup = 0; - } - readfds = svc_fdset; - switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) { - case -1: - if (errno == EINTR) - continue; - syslog(LOG_ERR, "mountd died: select: %m"); - exit(1); - case 0: - continue; - default: - svc_getreqset(&readfds); - } - } -} - -/* - * This routine creates and binds sockets on the appropriate - * addresses. It gets called one time for each transport. - * It returns 0 upon success, 1 for ingore the call and -1 to indicate - * bind failed with EADDRINUSE. - * Any file descriptors that have been created are stored in sock_fd and - * the total count of them is maintained in sock_fdcnt. - */ -static int -create_service(struct netconfig *nconf) -{ - struct addrinfo hints, *res = NULL; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - struct __rpc_sockinfo si; - int aicode; - int fd; - int nhostsbak; - int one = 1; - int r; - u_int32_t host_addr[4]; /* IPv4 or IPv6 */ - int mallocd_res; - - if ((nconf->nc_semantics != NC_TPI_CLTS) && - (nconf->nc_semantics != NC_TPI_COTS) && - (nconf->nc_semantics != NC_TPI_COTS_ORD)) - return (1); /* not my type */ - - /* - * XXX - using RPC library internal functions. - */ - if (!__rpc_nconf2sockinfo(nconf, &si)) { - syslog(LOG_ERR, "cannot get information for %s", - nconf->nc_netid); - return (1); - } - - /* Get mountd's address on this transport */ - memset(&hints, 0, sizeof hints); - hints.ai_family = si.si_af; - hints.ai_socktype = si.si_socktype; - hints.ai_protocol = si.si_proto; - - /* - * Bind to specific IPs if asked to - */ - nhostsbak = nhosts; - while (nhostsbak > 0) { - --nhostsbak; - sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int)); - if (sock_fd == NULL) - out_of_mem(); - sock_fd[sock_fdcnt++] = -1; /* Set invalid for now. */ - mallocd_res = 0; - - hints.ai_flags = AI_PASSIVE; - - /* - * XXX - using RPC library internal functions. - */ - if ((fd = __rpc_nconf2fd(nconf)) < 0) { - int non_fatal = 0; - if (errno == EAFNOSUPPORT && - nconf->nc_semantics != NC_TPI_CLTS) - non_fatal = 1; - - syslog(non_fatal ? LOG_DEBUG : LOG_ERR, - "cannot create socket for %s", nconf->nc_netid); - if (non_fatal != 0) - continue; - exit(1); - } - - switch (hints.ai_family) { - case AF_INET: - if (inet_pton(AF_INET, hosts[nhostsbak], - host_addr) == 1) { - hints.ai_flags |= AI_NUMERICHOST; - } else { - /* - * Skip if we have an AF_INET6 address. - */ - if (inet_pton(AF_INET6, hosts[nhostsbak], - host_addr) == 1) { - close(fd); - continue; - } - } - break; - case AF_INET6: - if (inet_pton(AF_INET6, hosts[nhostsbak], - host_addr) == 1) { - hints.ai_flags |= AI_NUMERICHOST; - } else { - /* - * Skip if we have an AF_INET address. - */ - if (inet_pton(AF_INET, hosts[nhostsbak], - host_addr) == 1) { - close(fd); - continue; - } - } - - /* - * We're doing host-based access checks here, so don't - * allow v4-in-v6 to confuse things. The kernel will - * disable it by default on NFS sockets too. - */ - if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, - sizeof one) < 0) { - syslog(LOG_ERR, - "can't disable v4-in-v6 on IPv6 socket"); - exit(1); - } - break; - default: - break; - } - - /* - * If no hosts were specified, just bind to INADDR_ANY - */ - if (strcmp("*", hosts[nhostsbak]) == 0) { - if (svcport_str == NULL) { - res = malloc(sizeof(struct addrinfo)); - if (res == NULL) - out_of_mem(); - mallocd_res = 1; - res->ai_flags = hints.ai_flags; - res->ai_family = hints.ai_family; - res->ai_protocol = hints.ai_protocol; - switch (res->ai_family) { - case AF_INET: - sin = malloc(sizeof(struct sockaddr_in)); - if (sin == NULL) - out_of_mem(); - sin->sin_family = AF_INET; - sin->sin_port = htons(0); - sin->sin_addr.s_addr = htonl(INADDR_ANY); - res->ai_addr = (struct sockaddr*) sin; - res->ai_addrlen = (socklen_t) - sizeof(struct sockaddr_in); - break; - case AF_INET6: - sin6 = malloc(sizeof(struct sockaddr_in6)); - if (sin6 == NULL) - out_of_mem(); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = htons(0); - sin6->sin6_addr = in6addr_any; - res->ai_addr = (struct sockaddr*) sin6; - res->ai_addrlen = (socklen_t) - sizeof(struct sockaddr_in6); - break; - default: - syslog(LOG_ERR, "bad addr fam %d", - res->ai_family); - exit(1); - } - } else { - if ((aicode = getaddrinfo(NULL, svcport_str, - &hints, &res)) != 0) { - syslog(LOG_ERR, - "cannot get local address for %s: %s", - nconf->nc_netid, - gai_strerror(aicode)); - close(fd); - continue; - } - } - } else { - if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str, - &hints, &res)) != 0) { - syslog(LOG_ERR, - "cannot get local address for %s: %s", - nconf->nc_netid, gai_strerror(aicode)); - close(fd); - continue; - } - } - - /* Store the fd. */ - sock_fd[sock_fdcnt - 1] = fd; - - /* Now, attempt the bind. */ - r = bindresvport_sa(fd, res->ai_addr); - if (r != 0) { - if (errno == EADDRINUSE && mallocd_svcport != 0) { - if (mallocd_res != 0) { - free(res->ai_addr); - free(res); - } else - freeaddrinfo(res); - return (-1); - } - syslog(LOG_ERR, "bindresvport_sa: %m"); - exit(1); - } - - if (svcport_str == NULL) { - svcport_str = malloc(NI_MAXSERV * sizeof(char)); - if (svcport_str == NULL) - out_of_mem(); - mallocd_svcport = 1; - - if (getnameinfo(res->ai_addr, - res->ai_addr->sa_len, NULL, NI_MAXHOST, - svcport_str, NI_MAXSERV * sizeof(char), - NI_NUMERICHOST | NI_NUMERICSERV)) - errx(1, "Cannot get port number"); - } - if (mallocd_res != 0) { - free(res->ai_addr); - free(res); - } else - freeaddrinfo(res); - res = NULL; - } - return (0); -} - -/* - * Called after all the create_service() calls have succeeded, to complete - * the setup and registration. - */ -static void -complete_service(struct netconfig *nconf, char *port_str) -{ - struct addrinfo hints, *res = NULL; - struct __rpc_sockinfo si; - struct netbuf servaddr; - SVCXPRT *transp = NULL; - int aicode, fd, nhostsbak; - int registered = 0; - - if ((nconf->nc_semantics != NC_TPI_CLTS) && - (nconf->nc_semantics != NC_TPI_COTS) && - (nconf->nc_semantics != NC_TPI_COTS_ORD)) - return; /* not my type */ - - /* - * XXX - using RPC library internal functions. - */ - if (!__rpc_nconf2sockinfo(nconf, &si)) { - syslog(LOG_ERR, "cannot get information for %s", - nconf->nc_netid); - return; - } - - nhostsbak = nhosts; - while (nhostsbak > 0) { - --nhostsbak; - if (sock_fdpos >= sock_fdcnt) { - /* Should never happen. */ - syslog(LOG_ERR, "Ran out of socket fd's"); - return; - } - fd = sock_fd[sock_fdpos++]; - if (fd < 0) - continue; - - /* - * Using -1 tells listen(2) to use - * kern.ipc.soacceptqueue for the backlog. - */ - if (nconf->nc_semantics != NC_TPI_CLTS) - listen(fd, -1); - - if (nconf->nc_semantics == NC_TPI_CLTS ) - transp = svc_dg_create(fd, 0, 0); - else - transp = svc_vc_create(fd, RPC_MAXDATASIZE, - RPC_MAXDATASIZE); - - if (transp != (SVCXPRT *) NULL) { - if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv, - NULL)) - syslog(LOG_ERR, - "can't register %s MOUNTVERS service", - nconf->nc_netid); - if (!force_v2) { - if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3, - mntsrv, NULL)) - syslog(LOG_ERR, - "can't register %s MOUNTVERS3 service", - nconf->nc_netid); - } - } else - syslog(LOG_WARNING, "can't create %s services", - nconf->nc_netid); - - if (registered == 0) { - registered = 1; - memset(&hints, 0, sizeof hints); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = si.si_af; - hints.ai_socktype = si.si_socktype; - hints.ai_protocol = si.si_proto; - - if ((aicode = getaddrinfo(NULL, port_str, &hints, - &res)) != 0) { - syslog(LOG_ERR, "cannot get local address: %s", - gai_strerror(aicode)); - exit(1); - } - - servaddr.buf = malloc(res->ai_addrlen); - memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen); - servaddr.len = res->ai_addrlen; - - rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr); - rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr); - - xcreated++; - freeaddrinfo(res); - } - } /* end while */ -} - -/* - * Clear out sockets after a failure to bind one of them, so that the - * cycle of socket creation/binding can start anew. - */ -static void -clearout_service(void) -{ - int i; - - for (i = 0; i < sock_fdcnt; i++) { - if (sock_fd[i] >= 0) { - shutdown(sock_fd[i], SHUT_RDWR); - close(sock_fd[i]); - } - } -} - -static void -usage(void) -{ - fprintf(stderr, - "usage: mountd [-2] [-d] [-e] [-l] [-n] [-p ] [-r] " - "[-S] [-h ] [export_file ...]\n"); - exit(1); -} - -/* - * The mount rpc service - */ -void -mntsrv(struct svc_req *rqstp, SVCXPRT *transp) -{ - struct exportlist *ep; - struct dirlist *dp; - struct fhreturn fhr; - struct stat stb; - struct statfs fsb; - char host[NI_MAXHOST], numerichost[NI_MAXHOST]; - int lookup_failed = 1; - struct sockaddr *saddr; - u_short sport; - char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN]; - int bad = 0, defset, hostset; - sigset_t sighup_mask; - int numsecflavors, *secflavorsp; - - sigemptyset(&sighup_mask); - sigaddset(&sighup_mask, SIGHUP); - saddr = svc_getrpccaller(transp)->buf; - switch (saddr->sa_family) { - case AF_INET6: - sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port); - break; - case AF_INET: - sport = ntohs(((struct sockaddr_in *)saddr)->sin_port); - break; - default: - syslog(LOG_ERR, "request from unknown address family"); - return; - } - switch (rqstp->rq_proc) { - case MOUNTPROC_MNT: - case MOUNTPROC_UMNT: - case MOUNTPROC_UMNTALL: - lookup_failed = getnameinfo(saddr, saddr->sa_len, host, - sizeof host, NULL, 0, 0); - } - getnameinfo(saddr, saddr->sa_len, numerichost, - sizeof numerichost, NULL, 0, NI_NUMERICHOST); - switch (rqstp->rq_proc) { - case NULLPROC: - if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL)) - syslog(LOG_ERR, "can't send reply"); - return; - case MOUNTPROC_MNT: - if (sport >= IPPORT_RESERVED && resvport_only) { - syslog(LOG_NOTICE, - "mount request from %s from unprivileged port", - numerichost); - svcerr_weakauth(transp); - return; - } - if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { - syslog(LOG_NOTICE, "undecodable mount request from %s", - numerichost); - svcerr_decode(transp); - return; - } - - /* - * Get the real pathname and make sure it is a directory - * or a regular file if the -r option was specified - * and it exists. - */ - if (realpath(rpcpath, dirpath) == NULL || - stat(dirpath, &stb) < 0 || - statfs(dirpath, &fsb) < 0) { - chdir("/"); /* Just in case realpath doesn't */ - syslog(LOG_NOTICE, - "mount request from %s for non existent path %s", - numerichost, dirpath); - if (debug) - warnx("stat failed on %s", dirpath); - bad = ENOENT; /* We will send error reply later */ - } - if (!bad && - !S_ISDIR(stb.st_mode) && - (dir_only || !S_ISREG(stb.st_mode))) { - syslog(LOG_NOTICE, - "mount request from %s for non-directory path %s", - numerichost, dirpath); - if (debug) - warnx("mounting non-directory %s", dirpath); - bad = ENOTDIR; /* We will send error reply later */ - } - - /* Check in the exports list */ - sigprocmask(SIG_BLOCK, &sighup_mask, NULL); - if (bad) - ep = NULL; - else - ep = ex_search(&fsb.f_fsid, exphead); - hostset = defset = 0; - if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset, - &numsecflavors, &secflavorsp) || - ((dp = dirp_search(ep->ex_dirl, dirpath)) && - chk_host(dp, saddr, &defset, &hostset, &numsecflavors, - &secflavorsp)) || - (defset && scan_tree(ep->ex_defdir, saddr) == 0 && - scan_tree(ep->ex_dirl, saddr) == 0))) { - if (bad) { - if (!svc_sendreply(transp, (xdrproc_t)xdr_long, - (caddr_t)&bad)) - syslog(LOG_ERR, "can't send reply"); - sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); - return; - } - if (hostset & DP_HOSTSET) { - fhr.fhr_flag = hostset; - fhr.fhr_numsecflavors = numsecflavors; - fhr.fhr_secflavors = secflavorsp; - } else { - fhr.fhr_flag = defset; - fhr.fhr_numsecflavors = ep->ex_defnumsecflavors; - fhr.fhr_secflavors = ep->ex_defsecflavors; - } - fhr.fhr_vers = rqstp->rq_vers; - /* Get the file handle */ - memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t)); - if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) { - bad = errno; - syslog(LOG_ERR, "can't get fh for %s", dirpath); - if (!svc_sendreply(transp, (xdrproc_t)xdr_long, - (caddr_t)&bad)) - syslog(LOG_ERR, "can't send reply"); - sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); - return; - } - if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs, - (caddr_t)&fhr)) - syslog(LOG_ERR, "can't send reply"); - if (!lookup_failed) - add_mlist(host, dirpath); - else - add_mlist(numerichost, dirpath); - if (debug) - warnx("mount successful"); - if (dolog) - syslog(LOG_NOTICE, - "mount request succeeded from %s for %s", - numerichost, dirpath); - } else { - if (!bad) - bad = EACCES; - syslog(LOG_NOTICE, - "mount request denied from %s for %s", - numerichost, dirpath); - } - - if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long, - (caddr_t)&bad)) - syslog(LOG_ERR, "can't send reply"); - sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); - return; - case MOUNTPROC_DUMP: - if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL)) - syslog(LOG_ERR, "can't send reply"); - else if (dolog) - syslog(LOG_NOTICE, - "dump request succeeded from %s", - numerichost); - return; - case MOUNTPROC_UMNT: - if (sport >= IPPORT_RESERVED && resvport_only) { - syslog(LOG_NOTICE, - "umount request from %s from unprivileged port", - numerichost); - svcerr_weakauth(transp); - return; - } - if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { - syslog(LOG_NOTICE, "undecodable umount request from %s", - numerichost); - svcerr_decode(transp); - return; - } - if (realpath(rpcpath, dirpath) == NULL) { - syslog(LOG_NOTICE, "umount request from %s " - "for non existent path %s", - numerichost, dirpath); - } - if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) - syslog(LOG_ERR, "can't send reply"); - if (!lookup_failed) - del_mlist(host, dirpath); - del_mlist(numerichost, dirpath); - if (dolog) - syslog(LOG_NOTICE, - "umount request succeeded from %s for %s", - numerichost, dirpath); - return; - case MOUNTPROC_UMNTALL: - if (sport >= IPPORT_RESERVED && resvport_only) { - syslog(LOG_NOTICE, - "umountall request from %s from unprivileged port", - numerichost); - svcerr_weakauth(transp); - return; - } - if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) - syslog(LOG_ERR, "can't send reply"); - if (!lookup_failed) - del_mlist(host, NULL); - del_mlist(numerichost, NULL); - if (dolog) - syslog(LOG_NOTICE, - "umountall request succeeded from %s", - numerichost); - return; - case MOUNTPROC_EXPORT: - if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL)) - if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief, - (caddr_t)NULL)) - syslog(LOG_ERR, "can't send reply"); - if (dolog) - syslog(LOG_NOTICE, - "export request succeeded from %s", - numerichost); - return; - default: - svcerr_noproc(transp); - return; - } -} - -/* - * Xdr conversion for a dirpath string - */ -static int -xdr_dir(XDR *xdrsp, char *dirp) -{ - return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); -} - -/* - * Xdr routine to generate file handle reply - */ -static int -xdr_fhs(XDR *xdrsp, caddr_t cp) -{ - struct fhreturn *fhrp = (struct fhreturn *)cp; - u_long ok = 0, len, auth; - int i; - - if (!xdr_long(xdrsp, &ok)) - return (0); - switch (fhrp->fhr_vers) { - case 1: - return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH)); - case 3: - len = NFSX_V3FH; - if (!xdr_long(xdrsp, &len)) - return (0); - if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len)) - return (0); - if (fhrp->fhr_numsecflavors) { - if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors)) - return (0); - for (i = 0; i < fhrp->fhr_numsecflavors; i++) - if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i])) - return (0); - return (1); - } else { - auth = AUTH_SYS; - len = 1; - if (!xdr_long(xdrsp, &len)) - return (0); - return (xdr_long(xdrsp, &auth)); - } - } - return (0); -} - -static int -xdr_mlist(XDR *xdrsp, caddr_t cp __unused) -{ - struct mountlist *mlp; - int true = 1; - int false = 0; - char *strp; - - SLIST_FOREACH(mlp, &mlhead, next) { - if (!xdr_bool(xdrsp, &true)) - return (0); - strp = &mlp->ml_host[0]; - if (!xdr_string(xdrsp, &strp, MNTNAMLEN)) - return (0); - strp = &mlp->ml_dirp[0]; - if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) - return (0); - } - if (!xdr_bool(xdrsp, &false)) - return (0); - return (1); -} - -/* - * Xdr conversion for export list - */ -static int -xdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief) -{ - struct exportlist *ep; - int false = 0; - int putdef; - sigset_t sighup_mask; - int i; - - sigemptyset(&sighup_mask); - sigaddset(&sighup_mask, SIGHUP); - sigprocmask(SIG_BLOCK, &sighup_mask, NULL); - - for (i = 0; i < exphashsize; i++) - SLIST_FOREACH(ep, &exphead[i], entries) { - putdef = 0; - if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, - &putdef, brief)) - goto errout; - if (ep->ex_defdir && putdef == 0 && - put_exlist(ep->ex_defdir, xdrsp, NULL, - &putdef, brief)) - goto errout; - } - sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); - if (!xdr_bool(xdrsp, &false)) - return (0); - return (1); -errout: - sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); - return (0); -} - -/* - * Called from xdr_explist() to traverse the tree and export the - * directory paths. - */ -static int -put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp, - int brief) -{ - struct grouplist *grp; - struct hostlist *hp; - int true = 1; - int false = 0; - int gotalldir = 0; - char *strp; - - if (dp) { - if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief)) - return (1); - if (!xdr_bool(xdrsp, &true)) - return (1); - strp = dp->dp_dirp; - if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) - return (1); - if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { - gotalldir = 1; - *putdefp = 1; - } - if (brief) { - if (!xdr_bool(xdrsp, &true)) - return (1); - strp = "(...)"; - if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) - return (1); - } else if ((dp->dp_flag & DP_DEFSET) == 0 && - (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { - hp = dp->dp_hosts; - while (hp) { - grp = hp->ht_grp; - if (grp->gr_type == GT_HOST) { - if (!xdr_bool(xdrsp, &true)) - return (1); - strp = grp->gr_ptr.gt_addrinfo->ai_canonname; - if (!xdr_string(xdrsp, &strp, - MNTNAMLEN)) - return (1); - } else if (grp->gr_type == GT_NET) { - if (!xdr_bool(xdrsp, &true)) - return (1); - strp = grp->gr_ptr.gt_net.nt_name; - if (!xdr_string(xdrsp, &strp, - MNTNAMLEN)) - return (1); - } - hp = hp->ht_next; - if (gotalldir && hp == (struct hostlist *)NULL) { - hp = adp->dp_hosts; - gotalldir = 0; - } - } - } - if (!xdr_bool(xdrsp, &false)) - return (1); - if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief)) - return (1); - } - return (0); -} - -static int -xdr_explist(XDR *xdrsp, caddr_t cp) -{ - - return xdr_explist_common(xdrsp, cp, 0); -} - -static int -xdr_explist_brief(XDR *xdrsp, caddr_t cp) -{ - - return xdr_explist_common(xdrsp, cp, 1); -} - -static char *line; -static size_t linesize; -static FILE *exp_file; - -/* - * Get the export list from one, currently open file - */ -static void -get_exportlist_one(int passno) -{ - struct exportlist *ep; - struct grouplist *grp, *tgrp, *savgrp; - struct dirlist *dirhead; - struct statfs fsb; - struct xucred anon; - char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; - int len, has_host, exflags, got_nondir, dirplen, netgrp; - - v4root_phase = 0; - dirhead = (struct dirlist *)NULL; - while (get_line()) { - if (debug) - warnx("got line %s", line); - cp = line; - nextfield(&cp, &endcp); - if (*cp == '#') - goto nextline; - - /* - * Set defaults. - */ - has_host = FALSE; - anon = def_anon; - exflags = MNT_EXPORTED; - got_nondir = 0; - opt_flags = 0; - ep = (struct exportlist *)NULL; - dirp = NULL; - - /* - * Handle the V4 root dir. - */ - if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') { - /* - * V4: just indicates that it is the v4 root point, - * so skip over that and set v4root_phase. - */ - if (v4root_phase > 0) { - syslog(LOG_ERR, "V4:duplicate line, ignored"); - goto nextline; - } - v4root_phase = 1; - cp += 3; - nextfield(&cp, &endcp); - } - - /* - * Create new exports list entry - */ - len = endcp-cp; - tgrp = grp = get_grp(); - while (len > 0) { - if (len > MNTNAMLEN) { - getexp_err(ep, tgrp, "mountpoint too long"); - goto nextline; - } - if (*cp == '-') { - if (ep == (struct exportlist *)NULL) { - getexp_err(ep, tgrp, - "flag before export path definition"); - goto nextline; - } - if (debug) - warnx("doing opt %s", cp); - got_nondir = 1; - if (do_opt(&cp, &endcp, ep, grp, &has_host, - &exflags, &anon)) { - getexp_err(ep, tgrp, NULL); - goto nextline; - } - } else if (*cp == '/') { - savedc = *endcp; - *endcp = '\0'; - if (v4root_phase > 1) { - if (dirp != NULL) { - getexp_err(ep, tgrp, "Multiple V4 dirs"); - goto nextline; - } - } - if (check_dirpath(cp) && - statfs(cp, &fsb) >= 0) { - if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0) - syslog(LOG_ERR, "Warning: exporting of " - "automounted fs %s not supported", cp); - if (got_nondir) { - getexp_err(ep, tgrp, "dirs must be first"); - goto nextline; - } - if (v4root_phase == 1) { - if (dirp != NULL) { - getexp_err(ep, tgrp, "Multiple V4 dirs"); - goto nextline; - } - if (strlen(v4root_dirpath) == 0) { - strlcpy(v4root_dirpath, cp, - sizeof (v4root_dirpath)); - } else if (strcmp(v4root_dirpath, cp) - != 0) { - syslog(LOG_ERR, - "different V4 dirpath %s", cp); - getexp_err(ep, tgrp, NULL); - goto nextline; - } - dirp = cp; - v4root_phase = 2; - got_nondir = 1; - ep = get_exp(); - } else { - if (ep) { - if (ep->ex_fs.val[0] != - fsb.f_fsid.val[0] || - ep->ex_fs.val[1] != - fsb.f_fsid.val[1]) { - getexp_err(ep, tgrp, - "fsid mismatch"); - goto nextline; - } - } else { - /* - * See if this directory is already - * in the list. - */ - ep = ex_search(&fsb.f_fsid, exphead); - if (ep == (struct exportlist *)NULL) { - ep = get_exp(); - ep->ex_fs = fsb.f_fsid; - ep->ex_fsdir = strdup(fsb.f_mntonname); - if (ep->ex_fsdir == NULL) - out_of_mem(); - if (debug) - warnx( - "making new ep fs=0x%x,0x%x", - fsb.f_fsid.val[0], - fsb.f_fsid.val[1]); - } else if (debug) - warnx("found ep fs=0x%x,0x%x", - fsb.f_fsid.val[0], - fsb.f_fsid.val[1]); - } - - /* - * Add dirpath to export mount point. - */ - dirp = add_expdir(&dirhead, cp, len); - dirplen = len; - } - } else { - getexp_err(ep, tgrp, - "symbolic link in export path or statfs failed"); - goto nextline; - } - *endcp = savedc; - } else { - savedc = *endcp; - *endcp = '\0'; - got_nondir = 1; - if (ep == (struct exportlist *)NULL) { - getexp_err(ep, tgrp, - "host(s) before export path definition"); - goto nextline; - } - - /* - * Get the host or netgroup. - */ - setnetgrent(cp); - netgrp = getnetgrent(&hst, &usr, &dom); - do { - if (has_host) { - grp->gr_next = get_grp(); - grp = grp->gr_next; - } - if (netgrp) { - if (hst == 0) { - syslog(LOG_ERR, - "null hostname in netgroup %s, skipping", cp); - grp->gr_type = GT_IGNORE; - } else if (get_host(hst, grp, tgrp)) { - syslog(LOG_ERR, - "bad host %s in netgroup %s, skipping", hst, cp); - grp->gr_type = GT_IGNORE; - } - } else if (get_host(cp, grp, tgrp)) { - syslog(LOG_ERR, "bad host %s, skipping", cp); - grp->gr_type = GT_IGNORE; - } - has_host = TRUE; - } while (netgrp && getnetgrent(&hst, &usr, &dom)); - endnetgrent(); - *endcp = savedc; - } - cp = endcp; - nextfield(&cp, &endcp); - len = endcp - cp; - } - if (check_options(dirhead)) { - getexp_err(ep, tgrp, NULL); - goto nextline; - } - if (!has_host) { - grp->gr_type = GT_DEFAULT; - if (debug) - warnx("adding a default entry"); - - /* - * Don't allow a network export coincide with a list of - * host(s) on the same line. - */ - } else if ((opt_flags & OP_NET) && tgrp->gr_next) { - getexp_err(ep, tgrp, "network/host conflict"); - goto nextline; - - /* - * If an export list was specified on this line, make sure - * that we have at least one valid entry, otherwise skip it. - */ - } else { - grp = tgrp; - while (grp && grp->gr_type == GT_IGNORE) - grp = grp->gr_next; - if (! grp) { - getexp_err(ep, tgrp, "no valid entries"); - goto nextline; - } - } - - if (v4root_phase == 1) { - getexp_err(ep, tgrp, "V4:root, no dirp, ignored"); - goto nextline; - } - - /* - * Loop through hosts, pushing the exports into the kernel. - * After loop, tgrp points to the start of the list and - * grp points to the last entry in the list. - * Do not do the do_mount() for passno == 1, since the - * second pass will do it, as required. - */ - grp = tgrp; - do { - grp->gr_exflags = exflags; - grp->gr_anon = anon; - if (v4root_phase == 2 && passno == 0) - LOGDEBUG("do_mount v4root"); - if (passno == 0 && do_mount(ep, grp, exflags, &anon, - dirp, dirplen, &fsb, ep->ex_numsecflavors, - ep->ex_secflavors)) { - getexp_err(ep, tgrp, NULL); - goto nextline; - } - } while (grp->gr_next && (grp = grp->gr_next)); - - /* - * For V4: don't enter in mount lists. - */ - if (v4root_phase > 0 && v4root_phase <= 2) { - /* - * These structures are used for the reload, - * so save them for that case. Otherwise, just - * free them up now. - */ - if (passno == 1 && ep != NULL) { - savgrp = tgrp; - while (tgrp != NULL) { - /* - * Save the security flavors and exflags - * for this host set in the groups. - */ - tgrp->gr_numsecflavors = - ep->ex_numsecflavors; - if (ep->ex_numsecflavors > 0) - memcpy(tgrp->gr_secflavors, - ep->ex_secflavors, - sizeof(ep->ex_secflavors)); - tgrp = tgrp->gr_next; - } - if (v4root_ep == NULL) { - v4root_ep = ep; - ep = NULL; /* Don't free below. */ - } - grp->gr_next = v4root_ep->ex_grphead; - v4root_ep->ex_grphead = savgrp; - } - if (ep != NULL) - free_exp(ep); - while (tgrp != NULL) { - grp = tgrp; - tgrp = tgrp->gr_next; - free_grp(grp); - } - goto nextline; - } - - /* - * Success. Update the data structures. - */ - if (has_host) { - hang_dirp(dirhead, tgrp, ep, opt_flags, &anon, exflags); - grp->gr_next = ep->ex_grphead; - ep->ex_grphead = tgrp; - } else { - hang_dirp(dirhead, (struct grouplist *)NULL, ep, - opt_flags, &anon, exflags); - free_grp(grp); - } - dirhead = (struct dirlist *)NULL; - if ((ep->ex_flag & EX_LINKED) == 0) { - insert_exports(ep, exphead); - - ep->ex_flag |= EX_LINKED; - } -nextline: - v4root_phase = 0; - if (dirhead) { - free_dir(dirhead); - dirhead = (struct dirlist *)NULL; - } - } -} - -/* - * Get the export list from all specified files - */ -static void -get_exportlist(int passno) -{ - struct export_args export; - struct iovec *iov; - struct statfs *mntbufp; - char errmsg[255]; - int num, i; - int iovlen; - struct nfsex_args eargs; - FILE *debug_file; - - if ((debug_file = fopen(_PATH_MOUNTDDEBUG, "r")) != NULL) { - fclose(debug_file); - logdebug = 1; - } else - logdebug = 0; - LOGDEBUG("passno=%d", passno); - v4root_dirpath[0] = '\0'; - free_v4rootexp(); - if (passno == 1) { - /* - * Save the current lists as old ones, so that the new lists - * can be compared with the old ones in the 2nd pass. - */ - for (i = 0; i < exphashsize; i++) { - SLIST_FIRST(&oldexphead[i]) = SLIST_FIRST(&exphead[i]); - SLIST_INIT(&exphead[i]); - } - - /* Note that the public fh has not yet been set. */ - has_set_publicfh = 0; - - /* Read the export file(s) and process them */ - read_exportfile(passno); - } else { - /* - * Just make the old lists empty. - * exphashsize == 0 for the first call, before oldexphead - * has been initialized-->loop won't be executed. - */ - for (i = 0; i < exphashsize; i++) - SLIST_INIT(&oldexphead[i]); - } - - bzero(&export, sizeof(export)); - export.ex_flags = MNT_DELEXPORT; - iov = NULL; - iovlen = 0; - bzero(errmsg, sizeof(errmsg)); - - if (suspend_nfsd != 0) - (void)nfssvc(NFSSVC_SUSPENDNFSD, NULL); - /* - * Delete the old V4 root dir. - */ - bzero(&eargs, sizeof (eargs)); - eargs.export.ex_flags = MNT_DELEXPORT; - if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 && - errno != ENOENT) - syslog(LOG_ERR, "Can't delete exports for V4:"); - - build_iovec(&iov, &iovlen, "fstype", NULL, 0); - build_iovec(&iov, &iovlen, "fspath", NULL, 0); - build_iovec(&iov, &iovlen, "from", NULL, 0); - build_iovec(&iov, &iovlen, "update", NULL, 0); - build_iovec(&iov, &iovlen, "export", &export, - sizeof(export)); - build_iovec(&iov, &iovlen, "errmsg", errmsg, - sizeof(errmsg)); - - /* - * For passno == 1, compare the old and new lists updating the kernel - * exports for any cases that have changed. - * This call is doing the second pass through the lists. - * If it fails, fall back on the bulk reload. - */ - if (passno == 1 && compare_nmount_exportlist(iov, iovlen, errmsg) == - 0) { - LOGDEBUG("compareok"); - /* Free up the old lists. */ - free_exports(oldexphead); - } else { - LOGDEBUG("doing passno=0"); - /* - * Clear flag that notes if a public fh has been exported. - * It is set by do_mount() if MNT_EXPUBLIC is set for the entry. - */ - has_publicfh = 0; - - /* exphead == NULL if not yet allocated (first call). */ - if (exphead != NULL) { - /* - * First, get rid of the old lists. - */ - free_exports(exphead); - free_exports(oldexphead); - } - - /* - * And delete exports that are in the kernel for all local - * filesystems. - * XXX: Should know how to handle all local exportable - * filesystems. - */ - num = getmntinfo(&mntbufp, MNT_NOWAIT); - - /* Allocate hash tables, for first call. */ - if (exphead == NULL) { - /* Target an average linked list length of 10. */ - exphashsize = num / 10; - if (exphashsize < 1) - exphashsize = 1; - else if (exphashsize > 100000) - exphashsize = 100000; - exphead = malloc(exphashsize * sizeof(*exphead)); - oldexphead = malloc(exphashsize * sizeof(*oldexphead)); - if (exphead == NULL || oldexphead == NULL) - errx(1, "Can't malloc hash tables"); - - for (i = 0; i < exphashsize; i++) { - SLIST_INIT(&exphead[i]); - SLIST_INIT(&oldexphead[i]); - } - } - - for (i = 0; i < num; i++) - delete_export(iov, iovlen, &mntbufp[i], errmsg); - - - /* Read the export file(s) and process them */ - read_exportfile(0); - } - - if (iov != NULL) { - /* Free strings allocated by strdup() in getmntopts.c */ - free(iov[0].iov_base); /* fstype */ - free(iov[2].iov_base); /* fspath */ - free(iov[4].iov_base); /* from */ - free(iov[6].iov_base); /* update */ - free(iov[8].iov_base); /* export */ - free(iov[10].iov_base); /* errmsg */ - - /* free iov, allocated by realloc() */ - free(iov); - iovlen = 0; - } - - /* - * If there was no public fh, clear any previous one set. - */ - if (has_publicfh == 0) { - LOGDEBUG("clear public fh"); - (void) nfssvc(NFSSVC_NOPUBLICFH, NULL); - } - - /* Resume the nfsd. If they weren't suspended, this is harmless. */ - (void)nfssvc(NFSSVC_RESUMENFSD, NULL); - LOGDEBUG("eo get_exportlist"); -} - -/* - * Insert an export entry in the appropriate list. - */ -static void -insert_exports(struct exportlist *ep, struct exportlisthead *exhp) -{ - uint32_t i; - - i = EXPHASH(&ep->ex_fs); - LOGDEBUG("fs=%s hash=%i", ep->ex_fsdir, i); - SLIST_INSERT_HEAD(&exhp[i], ep, entries); -} - -/* - * Free up the exports lists passed in as arguments. - */ -static void -free_exports(struct exportlisthead *exhp) -{ - struct exportlist *ep, *ep2; - int i; - - for (i = 0; i < exphashsize; i++) { - SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) { - SLIST_REMOVE(&exhp[i], ep, exportlist, entries); - free_exp(ep); - } - SLIST_INIT(&exhp[i]); - } -} - -/* - * Read the exports file(s) and call get_exportlist_one() for each line. - */ -static void -read_exportfile(int passno) -{ - int done, i; - - /* - * Read in the exports file and build the list, calling - * nmount() as we go along to push the export rules into the kernel. - */ - done = 0; - for (i = 0; exnames[i] != NULL; i++) { - if (debug) - warnx("reading exports from %s", exnames[i]); - if ((exp_file = fopen(exnames[i], "r")) == NULL) { - syslog(LOG_WARNING, "can't open %s", exnames[i]); - continue; - } - get_exportlist_one(passno); - fclose(exp_file); - done++; - } - if (done == 0) { - syslog(LOG_ERR, "can't open any exports file"); - exit(2); - } -} - -/* - * Compare the export lists against the old ones and do nmount() operations - * for any cases that have changed. This avoids doing nmount() for entries - * that have not changed. - * Return 0 upon success, 1 otherwise. - */ -static int -compare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg) -{ - struct exportlist *ep, *oep; - struct grouplist *grp; - struct statfs fs, ofs; - int i, ret; - - /* - * Loop through the current list and look for an entry in the old - * list. - * If found, check to see if it the same. - * If it is not the same, delete and re-export. - * Then mark it done on the old list. - * else (not found) - * export it. - * Any entries left in the old list after processing must have their - * exports deleted. - */ - for (i = 0; i < exphashsize; i++) - SLIST_FOREACH(ep, &exphead[i], entries) { - LOGDEBUG("foreach ep=%s", ep->ex_fsdir); - oep = ex_search(&ep->ex_fs, oldexphead); - if (oep != NULL) { - /* - * Check the mount paths are the same. - * If not, return 1 so that the reload of the - * exports will be done in bulk, the - * passno == 0 way. - */ - LOGDEBUG("found old exp"); - if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) - return (1); - LOGDEBUG("same fsdir"); - /* - * Test to see if the entry is the same. - * If not the same delete exports and - * re-export. - */ - if (compare_export(ep, oep) != 0) { - /* - * Clear has_publicfh if if was set - * in the old exports, but only if it - * has not been set during processing of - * the exports for this pass, as - * indicated by has_set_publicfh. - */ - if (has_set_publicfh == 0 && - (oep->ex_flag & EX_PUBLICFH) != 0) - has_publicfh = 0; - - /* Delete and re-export. */ - if (statfs(ep->ex_fsdir, &fs) < 0) - return (1); - delete_export(iov, iovlen, &fs, errmsg); - ret = do_export_mount(ep, &fs); - if (ret != 0) - return (ret); - } - oep->ex_flag |= EX_DONE; - LOGDEBUG("exdone"); - } else { - LOGDEBUG("not found so export"); - /* Not found, so do export. */ - if (statfs(ep->ex_fsdir, &fs) < 0) - return (1); - ret = do_export_mount(ep, &fs); - if (ret != 0) - return (ret); - } - } - - /* Delete exports not done. */ - for (i = 0; i < exphashsize; i++) - SLIST_FOREACH(oep, &oldexphead[i], entries) { - if ((oep->ex_flag & EX_DONE) == 0) { - LOGDEBUG("not done delete=%s", oep->ex_fsdir); - if (statfs(oep->ex_fsdir, &ofs) >= 0 && - oep->ex_fs.val[0] == ofs.f_fsid.val[0] && - oep->ex_fs.val[1] == ofs.f_fsid.val[1]) { - LOGDEBUG("do delete"); - /* - * Clear has_publicfh if if was set - * in the old exports, but only if it - * has not been set during processing of - * the exports for this pass, as - * indicated by has_set_publicfh. - */ - if (has_set_publicfh == 0 && - (oep->ex_flag & EX_PUBLICFH) != 0) - has_publicfh = 0; - - delete_export(iov, iovlen, &ofs, - errmsg); - } - } - } - - /* Do the V4 root exports, as required. */ - grp = NULL; - if (v4root_ep != NULL) - grp = v4root_ep->ex_grphead; - v4root_phase = 2; - while (v4root_ep != NULL && grp != NULL) { - LOGDEBUG("v4root expath=%s", v4root_dirpath); - ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon, - v4root_dirpath, strlen(v4root_dirpath), &fs, - grp->gr_numsecflavors, grp->gr_secflavors); - if (ret != 0) { - v4root_phase = 0; - return (ret); - } - grp = grp->gr_next; - } - v4root_phase = 0; - free_v4rootexp(); - return (0); -} - -/* - * Compare old and current exportlist entries for the fsid and return 0 - * if they are the same, 1 otherwise. - */ -static int -compare_export(struct exportlist *ep, struct exportlist *oep) -{ - struct grouplist *grp, *ogrp; - - if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) - return (1); - if ((ep->ex_flag & EX_DEFSET) != (oep->ex_flag & EX_DEFSET)) - return (1); - if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) || - (ep->ex_defdir == NULL && oep->ex_defdir != NULL)) - return (1); - if (ep->ex_defdir != NULL && (ep->ex_defdir->dp_flag & DP_DEFSET) != - (oep->ex_defdir->dp_flag & DP_DEFSET)) - return (1); - if ((ep->ex_flag & EX_DEFSET) != 0 && (ep->ex_defnumsecflavors != - oep->ex_defnumsecflavors || ep->ex_defexflags != - oep->ex_defexflags || compare_cred(&ep->ex_defanon, - &oep->ex_defanon) != 0 || compare_secflavor(ep->ex_defsecflavors, - oep->ex_defsecflavors, ep->ex_defnumsecflavors) != 0)) - return (1); - - /* Now, check all the groups. */ - for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) - ogrp->gr_flag = 0; - for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) { - for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = - ogrp->gr_next) - if ((ogrp->gr_flag & GR_FND) == 0 && - grp->gr_numsecflavors == ogrp->gr_numsecflavors && - grp->gr_exflags == ogrp->gr_exflags && - compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 && - compare_secflavor(grp->gr_secflavors, - ogrp->gr_secflavors, grp->gr_numsecflavors) == 0) - break; - if (ogrp != NULL) - ogrp->gr_flag |= GR_FND; - else - return (1); - } - for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) - if ((ogrp->gr_flag & GR_FND) == 0) - return (1); - return (0); -} - -/* - * This algorithm compares two arrays of "n" items. It returns 0 if they are - * the "same" and 1 otherwise. Although suboptimal, it is always safe to - * return 1, which makes compare_nmount_export() reload the exports entry. - * "same" refers to having the same set of values in the two arrays. - * The arrays are in no particular order and duplicates (multiple entries - * in an array with the same value) is allowed. - * The algorithm is inefficient, but the common case of indentical arrays is - * handled first and "n" is normally fairly small. - * Since the two functions need the same algorithm but for arrays of - * different types (gid_t vs int), this is done as a macro. - */ -#define COMPARE_ARRAYS(a1, a2, n) \ - do { \ - int fnd, fndarray[(n)], i, j; \ - /* Handle common case of identical arrays. */ \ - for (i = 0; i < (n); i++) \ - if ((a1)[i] != (a2)[i]) \ - break; \ - if (i == (n)) \ - return (0); \ - for (i = 0; i < (n); i++) \ - fndarray[i] = 0; \ - for (i = 0; i < (n); i++) { \ - fnd = 0; \ - for (j = 0; j < (n); j++) { \ - if ((a1)[i] == (a2)[j]) { \ - fndarray[j] = 1; \ - fnd = 1; \ - } \ - } \ - if (fnd == 0) \ - return (1); \ - } \ - for (i = 0; i < (n); i++) \ - if (fndarray[i] == 0) \ - return (1); \ - return (0); \ - } while (0) - -/* - * Compare to struct xucred's. Return 0 if the same and 1 otherwise. - */ -static int -compare_cred(struct xucred *cr0, struct xucred *cr1) -{ - - if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups) - return (1); - - COMPARE_ARRAYS(cr0->cr_groups, cr1->cr_groups, cr0->cr_ngroups); -} - -/* - * Compare two lists of security flavors. Return 0 if the same and 1 otherwise. - */ -static int -compare_secflavor(int *sec1, int *sec2, int nsec) -{ - - COMPARE_ARRAYS(sec1, sec2, nsec); -} - -/* - * Delete an exports entry. - */ -static void -delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg) -{ - struct xvfsconf vfc; - - if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) { - syslog(LOG_ERR, "getvfsbyname() failed for %s", - fsp->f_fstypename); - return; - } - - /* - * We do not need to delete "export" flag from - * filesystems that do not have it set. - */ - if (!(fsp->f_flags & MNT_EXPORTED)) - return; - /* - * Do not delete export for network filesystem by - * passing "export" arg to nmount(). - * It only makes sense to do this for local filesystems. - */ - if (vfc.vfc_flags & VFCF_NETWORK) - return; - - iov[1].iov_base = fsp->f_fstypename; - iov[1].iov_len = strlen(fsp->f_fstypename) + 1; - iov[3].iov_base = fsp->f_mntonname; - iov[3].iov_len = strlen(fsp->f_mntonname) + 1; - iov[5].iov_base = fsp->f_mntfromname; - iov[5].iov_len = strlen(fsp->f_mntfromname) + 1; - errmsg[0] = '\0'; - - /* - * EXDEV is returned when path exists but is not a - * mount point. May happens if raced with unmount. - */ - if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT && - errno != ENOTSUP && errno != EXDEV) { - syslog(LOG_ERR, - "can't delete exports for %s: %m %s", - fsp->f_mntonname, errmsg); - } -} - -/* - * Allocate an export list element - */ -static struct exportlist * -get_exp(void) -{ - struct exportlist *ep; - - ep = (struct exportlist *)calloc(1, sizeof (struct exportlist)); - if (ep == (struct exportlist *)NULL) - out_of_mem(); - return (ep); -} - -/* - * Allocate a group list element - */ -static struct grouplist * -get_grp(void) -{ - struct grouplist *gp; - - gp = (struct grouplist *)calloc(1, sizeof (struct grouplist)); - if (gp == (struct grouplist *)NULL) - out_of_mem(); - return (gp); -} - -/* - * Clean up upon an error in get_exportlist(). - */ -static void -getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason) -{ - struct grouplist *tgrp; - - if (!(opt_flags & OP_QUIET)) { - if (reason != NULL) - syslog(LOG_ERR, "bad exports list line '%s': %s", line, - reason); - else - syslog(LOG_ERR, "bad exports list line '%s'", line); - } - if (ep && (ep->ex_flag & EX_LINKED) == 0) - free_exp(ep); - while (grp) { - tgrp = grp; - grp = grp->gr_next; - free_grp(tgrp); - } -} - -/* - * Search the export list for a matching fs. - */ -static struct exportlist * -ex_search(fsid_t *fsid, struct exportlisthead *exhp) -{ - struct exportlist *ep; - uint32_t i; - - i = EXPHASH(fsid); - SLIST_FOREACH(ep, &exhp[i], entries) { - if (ep->ex_fs.val[0] == fsid->val[0] && - ep->ex_fs.val[1] == fsid->val[1]) - return (ep); - } - - return (ep); -} - -/* - * Add a directory path to the list. - */ -static char * -add_expdir(struct dirlist **dpp, char *cp, int len) -{ - struct dirlist *dp; - - dp = malloc(sizeof (struct dirlist)); - if (dp == (struct dirlist *)NULL) - out_of_mem(); - dp->dp_left = *dpp; - dp->dp_right = (struct dirlist *)NULL; - dp->dp_flag = 0; - dp->dp_hosts = (struct hostlist *)NULL; - dp->dp_dirp = strndup(cp, len); - if (dp->dp_dirp == NULL) - out_of_mem(); - *dpp = dp; - return (dp->dp_dirp); -} - -/* - * Hang the dir list element off the dirpath binary tree as required - * and update the entry for host. - */ -static void -hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, - int flags, struct xucred *anoncrp, int exflags) -{ - struct hostlist *hp; - struct dirlist *dp2; - - if (flags & OP_ALLDIRS) { - if (ep->ex_defdir) - free((caddr_t)dp); - else - ep->ex_defdir = dp; - if (grp == (struct grouplist *)NULL) { - ep->ex_flag |= EX_DEFSET; - ep->ex_defdir->dp_flag |= DP_DEFSET; - /* Save the default security flavors list. */ - ep->ex_defnumsecflavors = ep->ex_numsecflavors; - if (ep->ex_numsecflavors > 0) - memcpy(ep->ex_defsecflavors, ep->ex_secflavors, - sizeof(ep->ex_secflavors)); - ep->ex_defanon = *anoncrp; - ep->ex_defexflags = exflags; - } else while (grp) { - hp = get_ht(); - hp->ht_grp = grp; - hp->ht_next = ep->ex_defdir->dp_hosts; - ep->ex_defdir->dp_hosts = hp; - /* Save the security flavors list for this host set. */ - grp->gr_numsecflavors = ep->ex_numsecflavors; - if (ep->ex_numsecflavors > 0) - memcpy(grp->gr_secflavors, ep->ex_secflavors, - sizeof(ep->ex_secflavors)); - grp = grp->gr_next; - } - } else { - - /* - * Loop through the directories adding them to the tree. - */ - while (dp) { - dp2 = dp->dp_left; - add_dlist(&ep->ex_dirl, dp, grp, flags, ep, anoncrp, - exflags); - dp = dp2; - } - } -} - -/* - * Traverse the binary tree either updating a node that is already there - * for the new directory or adding the new node. - */ -static void -add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, - int flags, struct exportlist *ep, struct xucred *anoncrp, int exflags) -{ - struct dirlist *dp; - struct hostlist *hp; - int cmp; - - dp = *dpp; - if (dp) { - cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); - if (cmp > 0) { - add_dlist(&dp->dp_left, newdp, grp, flags, ep, anoncrp, - exflags); - return; - } else if (cmp < 0) { - add_dlist(&dp->dp_right, newdp, grp, flags, ep, anoncrp, - exflags); - return; - } else - free((caddr_t)newdp); - } else { - dp = newdp; - dp->dp_left = (struct dirlist *)NULL; - *dpp = dp; - } - if (grp) { - - /* - * Hang all of the host(s) off of the directory point. - */ - do { - hp = get_ht(); - hp->ht_grp = grp; - hp->ht_next = dp->dp_hosts; - dp->dp_hosts = hp; - /* Save the security flavors list for this host set. */ - grp->gr_numsecflavors = ep->ex_numsecflavors; - if (ep->ex_numsecflavors > 0) - memcpy(grp->gr_secflavors, ep->ex_secflavors, - sizeof(ep->ex_secflavors)); - grp = grp->gr_next; - } while (grp); - } else { - ep->ex_flag |= EX_DEFSET; - dp->dp_flag |= DP_DEFSET; - /* Save the default security flavors list. */ - ep->ex_defnumsecflavors = ep->ex_numsecflavors; - if (ep->ex_numsecflavors > 0) - memcpy(ep->ex_defsecflavors, ep->ex_secflavors, - sizeof(ep->ex_secflavors)); - ep->ex_defanon = *anoncrp; - ep->ex_defexflags = exflags; - } -} - -/* - * Search for a dirpath on the export point. - */ -static struct dirlist * -dirp_search(struct dirlist *dp, char *dirp) -{ - int cmp; - - if (dp) { - cmp = strcmp(dp->dp_dirp, dirp); - if (cmp > 0) - return (dirp_search(dp->dp_left, dirp)); - else if (cmp < 0) - return (dirp_search(dp->dp_right, dirp)); - else - return (dp); - } - return (dp); -} - -/* - * Scan for a host match in a directory tree. - */ -static int -chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp, - int *hostsetp, int *numsecflavors, int **secflavorsp) -{ - struct hostlist *hp; - struct grouplist *grp; - struct addrinfo *ai; - - if (dp) { - if (dp->dp_flag & DP_DEFSET) - *defsetp = dp->dp_flag; - hp = dp->dp_hosts; - while (hp) { - grp = hp->ht_grp; - switch (grp->gr_type) { - case GT_HOST: - ai = grp->gr_ptr.gt_addrinfo; - for (; ai; ai = ai->ai_next) { - if (!sacmp(ai->ai_addr, saddr, NULL)) { - *hostsetp = - (hp->ht_flag | DP_HOSTSET); - if (numsecflavors != NULL) { - *numsecflavors = - grp->gr_numsecflavors; - *secflavorsp = - grp->gr_secflavors; - } - return (1); - } - } - break; - case GT_NET: - if (!sacmp(saddr, (struct sockaddr *) - &grp->gr_ptr.gt_net.nt_net, - (struct sockaddr *) - &grp->gr_ptr.gt_net.nt_mask)) { - *hostsetp = (hp->ht_flag | DP_HOSTSET); - if (numsecflavors != NULL) { - *numsecflavors = - grp->gr_numsecflavors; - *secflavorsp = - grp->gr_secflavors; - } - return (1); - } - break; - } - hp = hp->ht_next; - } - } - return (0); -} - -/* - * Scan tree for a host that matches the address. - */ -static int -scan_tree(struct dirlist *dp, struct sockaddr *saddr) -{ - int defset, hostset; - - if (dp) { - if (scan_tree(dp->dp_left, saddr)) - return (1); - if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL)) - return (1); - if (scan_tree(dp->dp_right, saddr)) - return (1); - } - return (0); -} - -/* - * Traverse the dirlist tree and free it up. - */ -static void -free_dir(struct dirlist *dp) -{ - - if (dp) { - free_dir(dp->dp_left); - free_dir(dp->dp_right); - free_host(dp->dp_hosts); - free(dp->dp_dirp); - free(dp); - } -} - -/* - * Parse a colon separated list of security flavors - */ -static int -parsesec(char *seclist, struct exportlist *ep) -{ - char *cp, savedc; - int flavor; - - ep->ex_numsecflavors = 0; - for (;;) { - cp = strchr(seclist, ':'); - if (cp) { - savedc = *cp; - *cp = '\0'; - } - - if (!strcmp(seclist, "sys")) - flavor = AUTH_SYS; - else if (!strcmp(seclist, "krb5")) - flavor = RPCSEC_GSS_KRB5; - else if (!strcmp(seclist, "krb5i")) - flavor = RPCSEC_GSS_KRB5I; - else if (!strcmp(seclist, "krb5p")) - flavor = RPCSEC_GSS_KRB5P; - else { - if (cp) - *cp = savedc; - syslog(LOG_ERR, "bad sec flavor: %s", seclist); - return (1); - } - if (ep->ex_numsecflavors == MAXSECFLAVORS) { - if (cp) - *cp = savedc; - syslog(LOG_ERR, "too many sec flavors: %s", seclist); - return (1); - } - ep->ex_secflavors[ep->ex_numsecflavors] = flavor; - ep->ex_numsecflavors++; - if (cp) { - *cp = savedc; - seclist = cp + 1; - } else { - break; - } - } - return (0); -} - -/* - * Parse the option string and update fields. - * Option arguments may either be -