1331722Seadler/* 21558Srgrimes * Copyright (c) 1989, 1993 31558Srgrimes * The Regents of the University of California. All rights reserved. 41558Srgrimes * 51558Srgrimes * This code is derived from software contributed to Berkeley by 61558Srgrimes * Herb Hasler and Rick Macklem at The University of Guelph. 71558Srgrimes * 81558Srgrimes * Redistribution and use in source and binary forms, with or without 91558Srgrimes * modification, are permitted provided that the following conditions 101558Srgrimes * are met: 111558Srgrimes * 1. Redistributions of source code must retain the above copyright 121558Srgrimes * notice, this list of conditions and the following disclaimer. 131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141558Srgrimes * notice, this list of conditions and the following disclaimer in the 151558Srgrimes * documentation and/or other materials provided with the distribution. 161558Srgrimes * 4. Neither the name of the University nor the names of its contributors 171558Srgrimes * may be used to endorse or promote products derived from this software 181558Srgrimes * without specific prior written permission. 191558Srgrimes * 201558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301558Srgrimes * SUCH DAMAGE. 311558Srgrimes */ 321558Srgrimes 331558Srgrimes#ifndef lint 3437663Scharnierstatic const char copyright[] = 351558Srgrimes"@(#) Copyright (c) 1989, 1993\n\ 361558Srgrimes The Regents of the University of California. All rights reserved.\n"; 372999Swollman#endif /*not lint*/ 381558Srgrimes 39105267Scharnier#if 0 401558Srgrimes#ifndef lint 4137663Scharnierstatic char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95"; 42105267Scharnier#endif /*not lint*/ 4337663Scharnier#endif 441558Srgrimes 45105267Scharnier#include <sys/cdefs.h> 46105267Scharnier__FBSDID("$FreeBSD: stable/11/usr.sbin/mountd/mountd.c 363673 2020-07-29 20:31:11Z brooks $"); 47105267Scharnier 481558Srgrimes#include <sys/param.h> 49192934Srmacklem#include <sys/fcntl.h> 50349756Srmacklem#include <sys/fnv_hash.h> 51192934Srmacklem#include <sys/linker.h> 52192934Srmacklem#include <sys/module.h> 531558Srgrimes#include <sys/mount.h> 54324955Smanu#include <sys/queue.h> 551558Srgrimes#include <sys/stat.h> 56192934Srmacklem#include <sys/sysctl.h> 571558Srgrimes#include <sys/syslog.h> 581558Srgrimes 591558Srgrimes#include <rpc/rpc.h> 60109363Smbr#include <rpc/rpc_com.h> 611558Srgrimes#include <rpc/pmap_clnt.h> 6274462Salfred#include <rpc/pmap_prot.h> 6374462Salfred#include <rpcsvc/mount.h> 649336Sdfr#include <nfs/nfsproto.h> 65192934Srmacklem#include <nfs/nfssvc.h> 6683653Speter#include <nfsserver/nfs.h> 671558Srgrimes 68192934Srmacklem#include <fs/nfs/nfsport.h> 69192934Srmacklem 701558Srgrimes#include <arpa/inet.h> 711558Srgrimes 721558Srgrimes#include <ctype.h> 7337663Scharnier#include <err.h> 741558Srgrimes#include <errno.h> 751558Srgrimes#include <grp.h> 76149433Spjd#include <libutil.h> 77103949Smike#include <limits.h> 781558Srgrimes#include <netdb.h> 791558Srgrimes#include <pwd.h> 801558Srgrimes#include <signal.h> 811558Srgrimes#include <stdio.h> 821558Srgrimes#include <stdlib.h> 831558Srgrimes#include <string.h> 841558Srgrimes#include <unistd.h> 851558Srgrimes#include "pathnames.h" 86158857Srodrigc#include "mntopts.h" 871558Srgrimes 881558Srgrimes#ifdef DEBUG 891558Srgrimes#include <stdarg.h> 901558Srgrimes#endif 911558Srgrimes 921558Srgrimes/* 931558Srgrimes * Structures for keeping the mount list and export list 941558Srgrimes */ 951558Srgrimesstruct mountlist { 96194880Sdfr char ml_host[MNTNAMLEN+1]; 97194880Sdfr char ml_dirp[MNTPATHLEN+1]; 98324955Smanu 99324955Smanu SLIST_ENTRY(mountlist) next; 1001558Srgrimes}; 1011558Srgrimes 1021558Srgrimesstruct dirlist { 1031558Srgrimes struct dirlist *dp_left; 1041558Srgrimes struct dirlist *dp_right; 1051558Srgrimes int dp_flag; 1061558Srgrimes struct hostlist *dp_hosts; /* List of hosts this dir exported to */ 107324234Smanu char *dp_dirp; 1081558Srgrimes}; 1091558Srgrimes/* dp_flag bits */ 1101558Srgrimes#define DP_DEFSET 0x1 1119336Sdfr#define DP_HOSTSET 0x2 1121558Srgrimes 1131558Srgrimesstruct exportlist { 1141558Srgrimes struct dirlist *ex_dirl; 1151558Srgrimes struct dirlist *ex_defdir; 116349128Srmacklem struct grouplist *ex_grphead; 1171558Srgrimes int ex_flag; 1181558Srgrimes fsid_t ex_fs; 1191558Srgrimes char *ex_fsdir; 12027447Sdfr char *ex_indexfile; 121349772Srmacklem struct xucred ex_defanon; 122349772Srmacklem int ex_defexflags; 123184588Sdfr int ex_numsecflavors; 124184588Sdfr int ex_secflavors[MAXSECFLAVORS]; 125240902Srmacklem int ex_defnumsecflavors; 126240902Srmacklem int ex_defsecflavors[MAXSECFLAVORS]; 127324955Smanu 128324955Smanu SLIST_ENTRY(exportlist) entries; 1291558Srgrimes}; 1301558Srgrimes/* ex_flag bits */ 1311558Srgrimes#define EX_LINKED 0x1 132349772Srmacklem#define EX_DONE 0x2 133349772Srmacklem#define EX_DEFSET 0x4 134349772Srmacklem#define EX_PUBLICFH 0x8 1351558Srgrimes 136349124SrmacklemSLIST_HEAD(exportlisthead, exportlist); 137349124Srmacklem 1381558Srgrimesstruct netmsk { 13974462Salfred struct sockaddr_storage nt_net; 14075801Siedowse struct sockaddr_storage nt_mask; 14142144Sdfr char *nt_name; 1421558Srgrimes}; 1431558Srgrimes 1441558Srgrimesunion grouptypes { 14574462Salfred struct addrinfo *gt_addrinfo; 1461558Srgrimes struct netmsk gt_net; 1471558Srgrimes}; 1481558Srgrimes 1491558Srgrimesstruct grouplist { 1501558Srgrimes int gr_type; 1511558Srgrimes union grouptypes gr_ptr; 1521558Srgrimes struct grouplist *gr_next; 153349772Srmacklem struct xucred gr_anon; 154349772Srmacklem int gr_exflags; 155349772Srmacklem int gr_flag; 156240902Srmacklem int gr_numsecflavors; 157240902Srmacklem int gr_secflavors[MAXSECFLAVORS]; 1581558Srgrimes}; 1591558Srgrimes/* Group types */ 1601558Srgrimes#define GT_NULL 0x0 1611558Srgrimes#define GT_HOST 0x1 1621558Srgrimes#define GT_NET 0x2 16375641Siedowse#define GT_DEFAULT 0x3 1647401Swpaul#define GT_IGNORE 0x5 1651558Srgrimes 166349772Srmacklem/* Group flags */ 167349772Srmacklem#define GR_FND 0x1 168349772Srmacklem 1691558Srgrimesstruct hostlist { 1709336Sdfr int ht_flag; /* Uses DP_xx bits */ 1711558Srgrimes struct grouplist *ht_grp; 1721558Srgrimes struct hostlist *ht_next; 1731558Srgrimes}; 1741558Srgrimes 1759336Sdfrstruct fhreturn { 1769336Sdfr int fhr_flag; 1779336Sdfr int fhr_vers; 1789336Sdfr nfsfh_t fhr_fh; 179184588Sdfr int fhr_numsecflavors; 180184588Sdfr int *fhr_secflavors; 1819336Sdfr}; 1829336Sdfr 183222623Srmacklem#define GETPORT_MAXTRY 20 /* Max tries to get a port # */ 184222623Srmacklem 185362717Srmacklem/* 186362717Srmacklem * How long to delay a reload of exports when there are RPC request(s) 187362717Srmacklem * to process, in usec. Must be less than 1second. 188362717Srmacklem */ 189362717Srmacklem#define RELOADDELAY 250000 190362717Srmacklem 1911558Srgrimes/* Global defs */ 192285128Straszstatic char *add_expdir(struct dirlist **, char *, int); 193285128Straszstatic void add_dlist(struct dirlist **, struct dirlist *, 194349772Srmacklem struct grouplist *, int, struct exportlist *, 195349772Srmacklem struct xucred *, int); 196285128Straszstatic void add_mlist(char *, char *); 197285128Straszstatic int check_dirpath(char *); 198285128Straszstatic int check_options(struct dirlist *); 199285128Straszstatic int checkmask(struct sockaddr *sa); 200285128Straszstatic int chk_host(struct dirlist *, struct sockaddr *, int *, int *, 201285128Strasz int *, int **); 202293305Sjpaetzelstatic char *strsep_quote(char **stringp, const char *delim); 203222623Srmacklemstatic int create_service(struct netconfig *nconf); 204222623Srmacklemstatic void complete_service(struct netconfig *nconf, char *port_str); 205222623Srmacklemstatic void clearout_service(void); 206285128Straszstatic void del_mlist(char *hostp, char *dirp); 207285128Straszstatic struct dirlist *dirp_search(struct dirlist *, char *); 208349772Srmacklemstatic int do_export_mount(struct exportlist *, struct statfs *); 209285128Straszstatic int do_mount(struct exportlist *, struct grouplist *, int, 210349772Srmacklem struct xucred *, char *, int, struct statfs *, int, int *); 211285128Straszstatic int do_opt(char **, char **, struct exportlist *, 212285128Strasz struct grouplist *, int *, int *, struct xucred *); 213349124Srmacklemstatic struct exportlist *ex_search(fsid_t *, struct exportlisthead *); 214285128Straszstatic struct exportlist *get_exp(void); 215285128Straszstatic void free_dir(struct dirlist *); 216285128Straszstatic void free_exp(struct exportlist *); 217285128Straszstatic void free_grp(struct grouplist *); 218285128Straszstatic void free_host(struct hostlist *); 219349772Srmacklemstatic void free_v4rootexp(void); 220349772Srmacklemstatic void get_exportlist_one(int); 221349772Srmacklemstatic void get_exportlist(int); 222349124Srmacklemstatic void insert_exports(struct exportlist *, struct exportlisthead *); 223349124Srmacklemstatic void free_exports(struct exportlisthead *); 224349772Srmacklemstatic void read_exportfile(int); 225349772Srmacklemstatic int compare_nmount_exportlist(struct iovec *, int, char *); 226349772Srmacklemstatic int compare_export(struct exportlist *, struct exportlist *); 227349772Srmacklemstatic int compare_cred(struct xucred *, struct xucred *); 228349772Srmacklemstatic int compare_secflavor(int *, int *, int); 229349126Srmacklemstatic void delete_export(struct iovec *, int, struct statfs *, char *); 230285128Straszstatic int get_host(char *, struct grouplist *, struct grouplist *); 231285128Straszstatic struct hostlist *get_ht(void); 232285128Straszstatic int get_line(void); 233285128Straszstatic void get_mountlist(void); 234285128Straszstatic int get_net(char *, struct netmsk *, int); 235329392Sbrdstatic void getexp_err(struct exportlist *, struct grouplist *, const char *); 236285128Straszstatic struct grouplist *get_grp(void); 237285128Straszstatic void hang_dirp(struct dirlist *, struct grouplist *, 238349772Srmacklem struct exportlist *, int, struct xucred *, int); 239285128Straszstatic void huphandler(int sig); 240285128Straszstatic int makemask(struct sockaddr_storage *ssp, int bitlen); 241285128Straszstatic void mntsrv(struct svc_req *, SVCXPRT *); 242285128Straszstatic void nextfield(char **, char **); 243285128Straszstatic void out_of_mem(void); 244285128Straszstatic void parsecred(char *, struct xucred *); 245285128Straszstatic int parsesec(char *, struct exportlist *); 246285128Straszstatic int put_exlist(struct dirlist *, XDR *, struct dirlist *, 247285128Strasz int *, int); 248285128Straszstatic void *sa_rawaddr(struct sockaddr *sa, int *nbytes); 249285128Straszstatic int sacmp(struct sockaddr *sa1, struct sockaddr *sa2, 250285128Strasz struct sockaddr *samask); 251285128Straszstatic int scan_tree(struct dirlist *, struct sockaddr *); 252285128Straszstatic void usage(void); 253285128Straszstatic int xdr_dir(XDR *, char *); 254285128Straszstatic int xdr_explist(XDR *, caddr_t); 255285128Straszstatic int xdr_explist_brief(XDR *, caddr_t); 256285128Straszstatic int xdr_explist_common(XDR *, caddr_t, int); 257285128Straszstatic int xdr_fhs(XDR *, caddr_t); 258285128Straszstatic int xdr_mlist(XDR *, caddr_t); 259285128Straszstatic void terminate(int); 2601558Srgrimes 261349756Srmacklem#define EXPHASH(f) (fnv_32_buf((f), sizeof(fsid_t), 0) % exphashsize) 262349756Srmacklemstatic struct exportlisthead *exphead = NULL; 263349772Srmacklemstatic struct exportlisthead *oldexphead = NULL; 264349756Srmacklemstatic int exphashsize = 0; 265349124Srmacklemstatic SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead); 266285128Straszstatic char *exnames_default[2] = { _PATH_EXPORTS, NULL }; 267285128Straszstatic char **exnames; 268285128Straszstatic char **hosts = NULL; 269285128Straszstatic struct xucred def_anon = { 27091354Sdd XUCRED_VERSION, 27172650Sgreen (uid_t)-2, 2721558Srgrimes 1, 27372650Sgreen { (gid_t)-2 }, 27472650Sgreen NULL 2751558Srgrimes}; 276285128Straszstatic int force_v2 = 0; 277285128Straszstatic int resvport_only = 1; 278285128Straszstatic int nhosts = 0; 279285128Straszstatic int dir_only = 1; 280285128Straszstatic int dolog = 0; 281285128Straszstatic int got_sighup = 0; 282285128Straszstatic int xcreated = 0; 28374462Salfred 284285128Straszstatic char *svcport_str = NULL; 285285128Straszstatic int mallocd_svcport = 0; 286285128Straszstatic int *sock_fd; 287285128Straszstatic int sock_fdcnt; 288285128Straszstatic int sock_fdpos; 289285128Straszstatic int suspend_nfsd = 0; 290172827Smatteo 291285128Straszstatic int opt_flags; 29274462Salfredstatic int have_v6 = 1; 29374462Salfred 294285128Straszstatic int v4root_phase = 0; 295285128Straszstatic char v4root_dirpath[PATH_MAX + 1]; 296349772Srmacklemstatic struct exportlist *v4root_ep = NULL; 297285128Straszstatic int has_publicfh = 0; 298349772Srmacklemstatic int has_set_publicfh = 0; 299192934Srmacklem 300285128Straszstatic struct pidfh *pfh = NULL; 30175801Siedowse/* Bits for opt_flags above */ 3021558Srgrimes#define OP_MAPROOT 0x01 3031558Srgrimes#define OP_MAPALL 0x02 30483653Speter/* 0x4 free */ 3051558Srgrimes#define OP_MASK 0x08 3061558Srgrimes#define OP_NET 0x10 3071558Srgrimes#define OP_ALLDIRS 0x40 30875801Siedowse#define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */ 309100336Sjoerg#define OP_QUIET 0x100 31074462Salfred#define OP_MASKLEN 0x200 311184588Sdfr#define OP_SEC 0x400 3121558Srgrimes 3131558Srgrimes#ifdef DEBUG 314285128Straszstatic int debug = 1; 315285128Straszstatic void SYSLOG(int, const char *, ...) __printflike(2, 3); 3161558Srgrimes#define syslog SYSLOG 3171558Srgrimes#else 318285128Straszstatic int debug = 0; 3191558Srgrimes#endif 3201558Srgrimes 3211558Srgrimes/* 322349772Srmacklem * The LOGDEBUG() syslog() calls are always compiled into the daemon. 323349772Srmacklem * To enable them, create a file at _PATH_MOUNTDDEBUG. This file can be empty. 324349772Srmacklem * To disable the logging, just delete the file at _PATH_MOUNTDDEBUG. 325349772Srmacklem */ 326349772Srmacklemstatic int logdebug = 0; 327349772Srmacklem#define LOGDEBUG(format, ...) \ 328349772Srmacklem (logdebug ? syslog(LOG_DEBUG, format, ## __VA_ARGS__) : 0) 329349772Srmacklem 330349772Srmacklem/* 331293305Sjpaetzel * Similar to strsep(), but it allows for quoted strings 332293305Sjpaetzel * and escaped characters. 333293305Sjpaetzel * 334293305Sjpaetzel * It returns the string (or NULL, if *stringp is NULL), 335293305Sjpaetzel * which is a de-quoted version of the string if necessary. 336293305Sjpaetzel * 337293305Sjpaetzel * It modifies *stringp in place. 338293305Sjpaetzel */ 339293305Sjpaetzelstatic char * 340293305Sjpaetzelstrsep_quote(char **stringp, const char *delim) 341293305Sjpaetzel{ 342293305Sjpaetzel char *srcptr, *dstptr, *retval; 343293305Sjpaetzel char quot = 0; 344293305Sjpaetzel 345293305Sjpaetzel if (stringp == NULL || *stringp == NULL) 346293305Sjpaetzel return (NULL); 347293305Sjpaetzel 348293305Sjpaetzel srcptr = dstptr = retval = *stringp; 349293305Sjpaetzel 350293305Sjpaetzel while (*srcptr) { 351293305Sjpaetzel /* 352293305Sjpaetzel * We're looking for several edge cases here. 353293305Sjpaetzel * First: if we're in quote state (quot != 0), 354293305Sjpaetzel * then we ignore the delim characters, but otherwise 355293305Sjpaetzel * process as normal, unless it is the quote character. 356293305Sjpaetzel * Second: if the current character is a backslash, 357293305Sjpaetzel * we take the next character as-is, without checking 358293305Sjpaetzel * for delim, quote, or backslash. Exception: if the 359293305Sjpaetzel * next character is a NUL, that's the end of the string. 360293305Sjpaetzel * Third: if the character is a quote character, we toggle 361293305Sjpaetzel * quote state. 362293305Sjpaetzel * Otherwise: check the current character for NUL, or 363293305Sjpaetzel * being in delim, and end the string if either is true. 364293305Sjpaetzel */ 365293305Sjpaetzel if (*srcptr == '\\') { 366293305Sjpaetzel srcptr++; 367293305Sjpaetzel /* 368293305Sjpaetzel * The edge case here is if the next character 369293305Sjpaetzel * is NUL, we want to stop processing. But if 370293305Sjpaetzel * it's not NUL, then we simply want to copy it. 371293305Sjpaetzel */ 372293305Sjpaetzel if (*srcptr) { 373293305Sjpaetzel *dstptr++ = *srcptr++; 374293305Sjpaetzel } 375293305Sjpaetzel continue; 376293305Sjpaetzel } 377293305Sjpaetzel if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) { 378293305Sjpaetzel quot = *srcptr++; 379293305Sjpaetzel continue; 380293305Sjpaetzel } 381293305Sjpaetzel if (quot && *srcptr == quot) { 382293305Sjpaetzel /* End of the quoted part */ 383293305Sjpaetzel quot = 0; 384293305Sjpaetzel srcptr++; 385293305Sjpaetzel continue; 386293305Sjpaetzel } 387293305Sjpaetzel if (!quot && strchr(delim, *srcptr)) 388293305Sjpaetzel break; 389293305Sjpaetzel *dstptr++ = *srcptr++; 390293305Sjpaetzel } 391293305Sjpaetzel 392349457Smav *stringp = (*srcptr == '\0') ? NULL : srcptr + 1; 393293305Sjpaetzel *dstptr = 0; /* Terminate the string */ 394293305Sjpaetzel return (retval); 395293305Sjpaetzel} 396293305Sjpaetzel 397293305Sjpaetzel/* 3981558Srgrimes * Mountd server for NFS mount protocol as described in: 3991558Srgrimes * NFS: Network File System Protocol Specification, RFC1094, Appendix A 4001558Srgrimes * The optional arguments are the exports file name 4011558Srgrimes * default: _PATH_EXPORTS 4021558Srgrimes * and "-n" to allow nonroot mount. 4031558Srgrimes */ 4041558Srgrimesint 405216587Scharniermain(int argc, char **argv) 4061558Srgrimes{ 40775754Siedowse fd_set readfds; 408172827Smatteo struct netconfig *nconf; 409172827Smatteo char *endptr, **hosts_bak; 410172827Smatteo void *nc_handle; 411149433Spjd pid_t otherpid; 412172827Smatteo in_port_t svcport; 413172827Smatteo int c, k, s; 414109363Smbr int maxrec = RPC_MAXDATASIZE; 415222623Srmacklem int attempt_cnt, port_len, port_pos, ret; 416222623Srmacklem char **port_list; 417362717Srmacklem uint64_t curtime, nexttime; 418362717Srmacklem struct timeval tv; 419362717Srmacklem struct timespec tp; 420362717Srmacklem sigset_t sighup_mask; 4211558Srgrimes 42274462Salfred /* Check that another mountd isn't already running. */ 423150214Spjd pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid); 424149433Spjd if (pfh == NULL) { 425149433Spjd if (errno == EEXIST) 426149433Spjd errx(1, "mountd already running, pid: %d.", otherpid); 427149433Spjd warn("cannot open or create pidfile"); 428149433Spjd } 42974462Salfred 43074462Salfred s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 43174462Salfred if (s < 0) 43274462Salfred have_v6 = 0; 43374462Salfred else 43474462Salfred close(s); 4352999Swollman 436282214Strasz while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1) 4371558Srgrimes switch (c) { 43825087Sdfr case '2': 43925087Sdfr force_v2 = 1; 44025087Sdfr break; 441192993Srmacklem case 'e': 442220980Srmacklem /* now a no-op, since this is the default */ 443192934Srmacklem break; 4449336Sdfr case 'n': 4459336Sdfr resvport_only = 0; 4469336Sdfr break; 4479336Sdfr case 'r': 4489336Sdfr dir_only = 0; 4499336Sdfr break; 4508688Sphk case 'd': 4518688Sphk debug = debug ? 0 : 1; 4528688Sphk break; 45331656Sguido case 'l': 454121767Speter dolog = 1; 45531656Sguido break; 456126572Sbms case 'p': 457126572Sbms endptr = NULL; 458126572Sbms svcport = (in_port_t)strtoul(optarg, &endptr, 10); 459126572Sbms if (endptr == NULL || *endptr != '\0' || 460126572Sbms svcport == 0 || svcport >= IPPORT_MAX) 461126572Sbms usage(); 462172827Smatteo svcport_str = strdup(optarg); 463126572Sbms break; 464172827Smatteo case 'h': 465172827Smatteo ++nhosts; 466172827Smatteo hosts_bak = hosts; 467172827Smatteo hosts_bak = realloc(hosts, nhosts * sizeof(char *)); 468172827Smatteo if (hosts_bak == NULL) { 469172827Smatteo if (hosts != NULL) { 470172827Smatteo for (k = 0; k < nhosts; k++) 471172827Smatteo free(hosts[k]); 472172827Smatteo free(hosts); 473172827Smatteo out_of_mem(); 474172827Smatteo } 475172827Smatteo } 476172827Smatteo hosts = hosts_bak; 477172827Smatteo hosts[nhosts - 1] = strdup(optarg); 478172827Smatteo if (hosts[nhosts - 1] == NULL) { 479172827Smatteo for (k = 0; k < (nhosts - 1); k++) 480172827Smatteo free(hosts[k]); 481172827Smatteo free(hosts); 482172827Smatteo out_of_mem(); 483172827Smatteo } 484172827Smatteo break; 485241568Srmacklem case 'S': 486241568Srmacklem suspend_nfsd = 1; 487241568Srmacklem break; 4881558Srgrimes default: 48937663Scharnier usage(); 490298089Spfg } 491192934Srmacklem 492282214Strasz if (modfind("nfsd") < 0) { 493192934Srmacklem /* Not present in kernel, try loading it */ 494282214Strasz if (kldload("nfsd") < 0 || modfind("nfsd") < 0) 495192934Srmacklem errx(1, "NFS server is not available"); 496192934Srmacklem } 497192934Srmacklem 4981558Srgrimes argc -= optind; 4991558Srgrimes argv += optind; 500166440Spjd if (argc > 0) 501166440Spjd exnames = argv; 502166440Spjd else 503166440Spjd exnames = exnames_default; 5041558Srgrimes openlog("mountd", LOG_PID, LOG_DAEMON); 5051558Srgrimes if (debug) 50637663Scharnier warnx("getting export list"); 507349772Srmacklem get_exportlist(0); 5081558Srgrimes if (debug) 50937663Scharnier warnx("getting mount list"); 5101558Srgrimes get_mountlist(); 5111558Srgrimes if (debug) 51237663Scharnier warnx("here we go"); 5131558Srgrimes if (debug == 0) { 5141558Srgrimes daemon(0, 0); 5151558Srgrimes signal(SIGINT, SIG_IGN); 5161558Srgrimes signal(SIGQUIT, SIG_IGN); 5171558Srgrimes } 51875754Siedowse signal(SIGHUP, huphandler); 51974462Salfred signal(SIGTERM, terminate); 520164394Srodrigc signal(SIGPIPE, SIG_IGN); 521149433Spjd 522149433Spjd pidfile_write(pfh); 523149433Spjd 524194880Sdfr rpcb_unset(MOUNTPROG, MOUNTVERS, NULL); 525194880Sdfr rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL); 526109363Smbr rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); 527109363Smbr 52824759Sguido if (!resvport_only) { 529308449Srmacklem if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL, 53083687Speter &resvport_only, sizeof(resvport_only)) != 0 && 53183687Speter errno != ENOENT) { 53224759Sguido syslog(LOG_ERR, "sysctl: %m"); 53324759Sguido exit(1); 53424759Sguido } 53524330Sguido } 536126572Sbms 537172827Smatteo /* 538172827Smatteo * If no hosts were specified, add a wildcard entry to bind to 539172827Smatteo * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the 540172827Smatteo * list. 541172827Smatteo */ 542172827Smatteo if (nhosts == 0) { 543292864Suqs hosts = malloc(sizeof(char *)); 544172827Smatteo if (hosts == NULL) 545172827Smatteo out_of_mem(); 546172827Smatteo hosts[0] = "*"; 547172827Smatteo nhosts = 1; 548172827Smatteo } else { 549172827Smatteo hosts_bak = hosts; 550172827Smatteo if (have_v6) { 551172827Smatteo hosts_bak = realloc(hosts, (nhosts + 2) * 552172827Smatteo sizeof(char *)); 553172827Smatteo if (hosts_bak == NULL) { 554172827Smatteo for (k = 0; k < nhosts; k++) 555172827Smatteo free(hosts[k]); 556172827Smatteo free(hosts); 557172827Smatteo out_of_mem(); 558172827Smatteo } else 559172827Smatteo hosts = hosts_bak; 560172827Smatteo nhosts += 2; 561172827Smatteo hosts[nhosts - 2] = "::1"; 562172827Smatteo } else { 563172827Smatteo hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *)); 564172827Smatteo if (hosts_bak == NULL) { 565172827Smatteo for (k = 0; k < nhosts; k++) 566172827Smatteo free(hosts[k]); 567172827Smatteo free(hosts); 568172827Smatteo out_of_mem(); 569172827Smatteo } else { 570172827Smatteo nhosts += 1; 571172827Smatteo hosts = hosts_bak; 572126572Sbms } 573172827Smatteo } 57474462Salfred 575172827Smatteo hosts[nhosts - 1] = "127.0.0.1"; 57674462Salfred } 57774462Salfred 578222623Srmacklem attempt_cnt = 1; 579222623Srmacklem sock_fdcnt = 0; 580222623Srmacklem sock_fd = NULL; 581222623Srmacklem port_list = NULL; 582222623Srmacklem port_len = 0; 583172827Smatteo nc_handle = setnetconfig(); 584172827Smatteo while ((nconf = getnetconfig(nc_handle))) { 585172827Smatteo if (nconf->nc_flag & NC_VISIBLE) { 586172827Smatteo if (have_v6 == 0 && strcmp(nconf->nc_protofmly, 587172827Smatteo "inet6") == 0) { 588172827Smatteo /* DO NOTHING */ 589222623Srmacklem } else { 590222623Srmacklem ret = create_service(nconf); 591222623Srmacklem if (ret == 1) 592222623Srmacklem /* Ignore this call */ 593222623Srmacklem continue; 594222623Srmacklem if (ret < 0) { 595222623Srmacklem /* 596222623Srmacklem * Failed to bind port, so close off 597222623Srmacklem * all sockets created and try again 598222623Srmacklem * if the port# was dynamically 599222623Srmacklem * assigned via bind(2). 600222623Srmacklem */ 601222623Srmacklem clearout_service(); 602222623Srmacklem if (mallocd_svcport != 0 && 603222623Srmacklem attempt_cnt < GETPORT_MAXTRY) { 604222623Srmacklem free(svcport_str); 605222623Srmacklem svcport_str = NULL; 606222623Srmacklem mallocd_svcport = 0; 607222623Srmacklem } else { 608222623Srmacklem errno = EADDRINUSE; 609222623Srmacklem syslog(LOG_ERR, 610222623Srmacklem "bindresvport_sa: %m"); 611222623Srmacklem exit(1); 612222623Srmacklem } 613222623Srmacklem 614222623Srmacklem /* Start over at the first service. */ 615222623Srmacklem free(sock_fd); 616222623Srmacklem sock_fdcnt = 0; 617222623Srmacklem sock_fd = NULL; 618222623Srmacklem nc_handle = setnetconfig(); 619222623Srmacklem attempt_cnt++; 620222623Srmacklem } else if (mallocd_svcport != 0 && 621222623Srmacklem attempt_cnt == GETPORT_MAXTRY) { 622222623Srmacklem /* 623222623Srmacklem * For the last attempt, allow 624222623Srmacklem * different port #s for each nconf 625222623Srmacklem * by saving the svcport_str and 626222623Srmacklem * setting it back to NULL. 627222623Srmacklem */ 628222623Srmacklem port_list = realloc(port_list, 629222623Srmacklem (port_len + 1) * sizeof(char *)); 630222623Srmacklem if (port_list == NULL) 631222623Srmacklem out_of_mem(); 632222623Srmacklem port_list[port_len++] = svcport_str; 633222623Srmacklem svcport_str = NULL; 634222623Srmacklem mallocd_svcport = 0; 635222623Srmacklem } 636222623Srmacklem } 637222623Srmacklem } 638222623Srmacklem } 639222623Srmacklem 640222623Srmacklem /* 641222623Srmacklem * Successfully bound the ports, so call complete_service() to 642222623Srmacklem * do the rest of the setup on the service(s). 643222623Srmacklem */ 644222623Srmacklem sock_fdpos = 0; 645222623Srmacklem port_pos = 0; 646222623Srmacklem nc_handle = setnetconfig(); 647222623Srmacklem while ((nconf = getnetconfig(nc_handle))) { 648222623Srmacklem if (nconf->nc_flag & NC_VISIBLE) { 649222623Srmacklem if (have_v6 == 0 && strcmp(nconf->nc_protofmly, 650222623Srmacklem "inet6") == 0) { 651222623Srmacklem /* DO NOTHING */ 652222623Srmacklem } else if (port_list != NULL) { 653222623Srmacklem if (port_pos >= port_len) { 654222623Srmacklem syslog(LOG_ERR, "too many port#s"); 655222623Srmacklem exit(1); 656222623Srmacklem } 657222623Srmacklem complete_service(nconf, port_list[port_pos++]); 658172827Smatteo } else 659222623Srmacklem complete_service(nconf, svcport_str); 660172827Smatteo } 66174462Salfred } 662172827Smatteo endnetconfig(nc_handle); 663222623Srmacklem free(sock_fd); 664222623Srmacklem if (port_list != NULL) { 665222623Srmacklem for (port_pos = 0; port_pos < port_len; port_pos++) 666222623Srmacklem free(port_list[port_pos]); 667222623Srmacklem free(port_list); 668222623Srmacklem } 66974462Salfred 67074462Salfred if (xcreated == 0) { 67174462Salfred syslog(LOG_ERR, "could not create any services"); 6721558Srgrimes exit(1); 6731558Srgrimes } 67475754Siedowse 67575754Siedowse /* Expand svc_run() here so that we can call get_exportlist(). */ 676362717Srmacklem curtime = nexttime = 0; 677362717Srmacklem sigemptyset(&sighup_mask); 678362717Srmacklem sigaddset(&sighup_mask, SIGHUP); 67975754Siedowse for (;;) { 680362717Srmacklem clock_gettime(CLOCK_MONOTONIC, &tp); 681362717Srmacklem curtime = tp.tv_sec; 682362717Srmacklem curtime = curtime * 1000000 + tp.tv_nsec / 1000; 683362717Srmacklem sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 684362717Srmacklem if (got_sighup && curtime >= nexttime) { 685362717Srmacklem got_sighup = 0; 686362717Srmacklem sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 687349772Srmacklem get_exportlist(1); 688362717Srmacklem clock_gettime(CLOCK_MONOTONIC, &tp); 689362717Srmacklem nexttime = tp.tv_sec; 690362717Srmacklem nexttime = nexttime * 1000000 + tp.tv_nsec / 1000 + 691362717Srmacklem RELOADDELAY; 692362717Srmacklem } else 693362717Srmacklem sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 694362717Srmacklem 695362717Srmacklem /* 696362717Srmacklem * If a reload is pending, poll for received request(s), 697362717Srmacklem * otherwise set a RELOADDELAY timeout, since a SIGHUP 698362717Srmacklem * could be processed between the got_sighup test and 699362717Srmacklem * the select() system call. 700362717Srmacklem */ 701362717Srmacklem tv.tv_sec = 0; 702362717Srmacklem if (got_sighup) 703362717Srmacklem tv.tv_usec = 0; 704362717Srmacklem else 705362717Srmacklem tv.tv_usec = RELOADDELAY; 70675754Siedowse readfds = svc_fdset; 707362717Srmacklem switch (select(svc_maxfd + 1, &readfds, NULL, NULL, &tv)) { 70875754Siedowse case -1: 709362717Srmacklem if (errno == EINTR) { 710362717Srmacklem /* Allow a reload now. */ 711362717Srmacklem nexttime = 0; 712362717Srmacklem continue; 713362717Srmacklem } 71475754Siedowse syslog(LOG_ERR, "mountd died: select: %m"); 71575754Siedowse exit(1); 71675754Siedowse case 0: 717362717Srmacklem /* Allow a reload now. */ 718362717Srmacklem nexttime = 0; 71975754Siedowse continue; 72075754Siedowse default: 72175754Siedowse svc_getreqset(&readfds); 72275754Siedowse } 72375754Siedowse } 724172827Smatteo} 725172827Smatteo 726172827Smatteo/* 727172827Smatteo * This routine creates and binds sockets on the appropriate 728222623Srmacklem * addresses. It gets called one time for each transport. 729222623Srmacklem * It returns 0 upon success, 1 for ingore the call and -1 to indicate 730222623Srmacklem * bind failed with EADDRINUSE. 731222623Srmacklem * Any file descriptors that have been created are stored in sock_fd and 732222623Srmacklem * the total count of them is maintained in sock_fdcnt. 733172827Smatteo */ 734222623Srmacklemstatic int 735172827Smatteocreate_service(struct netconfig *nconf) 736172827Smatteo{ 737172827Smatteo struct addrinfo hints, *res = NULL; 738172827Smatteo struct sockaddr_in *sin; 739172827Smatteo struct sockaddr_in6 *sin6; 740172827Smatteo struct __rpc_sockinfo si; 741172827Smatteo int aicode; 742172827Smatteo int fd; 743172827Smatteo int nhostsbak; 744172827Smatteo int one = 1; 745172827Smatteo int r; 746172827Smatteo u_int32_t host_addr[4]; /* IPv4 or IPv6 */ 747222623Srmacklem int mallocd_res; 748172827Smatteo 749172827Smatteo if ((nconf->nc_semantics != NC_TPI_CLTS) && 750172827Smatteo (nconf->nc_semantics != NC_TPI_COTS) && 751172827Smatteo (nconf->nc_semantics != NC_TPI_COTS_ORD)) 752222623Srmacklem return (1); /* not my type */ 753172827Smatteo 754172827Smatteo /* 755172827Smatteo * XXX - using RPC library internal functions. 756172827Smatteo */ 757172827Smatteo if (!__rpc_nconf2sockinfo(nconf, &si)) { 758172827Smatteo syslog(LOG_ERR, "cannot get information for %s", 759172827Smatteo nconf->nc_netid); 760222623Srmacklem return (1); 761172827Smatteo } 762172827Smatteo 763172827Smatteo /* Get mountd's address on this transport */ 764172827Smatteo memset(&hints, 0, sizeof hints); 765172827Smatteo hints.ai_family = si.si_af; 766172827Smatteo hints.ai_socktype = si.si_socktype; 767172827Smatteo hints.ai_protocol = si.si_proto; 768172827Smatteo 769172827Smatteo /* 770172827Smatteo * Bind to specific IPs if asked to 771172827Smatteo */ 772172827Smatteo nhostsbak = nhosts; 773172827Smatteo while (nhostsbak > 0) { 774172827Smatteo --nhostsbak; 775222623Srmacklem sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int)); 776222623Srmacklem if (sock_fd == NULL) 777222623Srmacklem out_of_mem(); 778222623Srmacklem sock_fd[sock_fdcnt++] = -1; /* Set invalid for now. */ 779222623Srmacklem mallocd_res = 0; 780222623Srmacklem 781277352Srstone hints.ai_flags = AI_PASSIVE; 782277352Srstone 783172827Smatteo /* 784172827Smatteo * XXX - using RPC library internal functions. 785172827Smatteo */ 786172827Smatteo if ((fd = __rpc_nconf2fd(nconf)) < 0) { 787172827Smatteo int non_fatal = 0; 788244538Skevlo if (errno == EAFNOSUPPORT && 789172827Smatteo nconf->nc_semantics != NC_TPI_CLTS) 790172827Smatteo non_fatal = 1; 791172827Smatteo 792172827Smatteo syslog(non_fatal ? LOG_DEBUG : LOG_ERR, 793172827Smatteo "cannot create socket for %s", nconf->nc_netid); 794222623Srmacklem if (non_fatal != 0) 795222623Srmacklem continue; 796222623Srmacklem exit(1); 797172827Smatteo } 798172827Smatteo 799172827Smatteo switch (hints.ai_family) { 800172827Smatteo case AF_INET: 801172827Smatteo if (inet_pton(AF_INET, hosts[nhostsbak], 802172827Smatteo host_addr) == 1) { 803222623Srmacklem hints.ai_flags |= AI_NUMERICHOST; 804172827Smatteo } else { 805172827Smatteo /* 806172827Smatteo * Skip if we have an AF_INET6 address. 807172827Smatteo */ 808172827Smatteo if (inet_pton(AF_INET6, hosts[nhostsbak], 809172827Smatteo host_addr) == 1) { 810172827Smatteo close(fd); 811172827Smatteo continue; 812172827Smatteo } 813172827Smatteo } 814172827Smatteo break; 815172827Smatteo case AF_INET6: 816172827Smatteo if (inet_pton(AF_INET6, hosts[nhostsbak], 817172827Smatteo host_addr) == 1) { 818222623Srmacklem hints.ai_flags |= AI_NUMERICHOST; 819172827Smatteo } else { 820172827Smatteo /* 821172827Smatteo * Skip if we have an AF_INET address. 822172827Smatteo */ 823172827Smatteo if (inet_pton(AF_INET, hosts[nhostsbak], 824172827Smatteo host_addr) == 1) { 825172827Smatteo close(fd); 826172827Smatteo continue; 827172827Smatteo } 828172827Smatteo } 829172827Smatteo 830172827Smatteo /* 831172827Smatteo * We're doing host-based access checks here, so don't 832172827Smatteo * allow v4-in-v6 to confuse things. The kernel will 833172827Smatteo * disable it by default on NFS sockets too. 834172827Smatteo */ 835172827Smatteo if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, 836172827Smatteo sizeof one) < 0) { 837172827Smatteo syslog(LOG_ERR, 838172827Smatteo "can't disable v4-in-v6 on IPv6 socket"); 839172827Smatteo exit(1); 840172827Smatteo } 841172827Smatteo break; 842172827Smatteo default: 843172827Smatteo break; 844172827Smatteo } 845172827Smatteo 846172827Smatteo /* 847172827Smatteo * If no hosts were specified, just bind to INADDR_ANY 848172827Smatteo */ 849172827Smatteo if (strcmp("*", hosts[nhostsbak]) == 0) { 850172827Smatteo if (svcport_str == NULL) { 851172827Smatteo res = malloc(sizeof(struct addrinfo)); 852172827Smatteo if (res == NULL) 853172827Smatteo out_of_mem(); 854222623Srmacklem mallocd_res = 1; 855172827Smatteo res->ai_flags = hints.ai_flags; 856172827Smatteo res->ai_family = hints.ai_family; 857172827Smatteo res->ai_protocol = hints.ai_protocol; 858172827Smatteo switch (res->ai_family) { 859172827Smatteo case AF_INET: 860172827Smatteo sin = malloc(sizeof(struct sockaddr_in)); 861172827Smatteo if (sin == NULL) 862172827Smatteo out_of_mem(); 863172827Smatteo sin->sin_family = AF_INET; 864172827Smatteo sin->sin_port = htons(0); 865172827Smatteo sin->sin_addr.s_addr = htonl(INADDR_ANY); 866172827Smatteo res->ai_addr = (struct sockaddr*) sin; 867172827Smatteo res->ai_addrlen = (socklen_t) 868222623Srmacklem sizeof(struct sockaddr_in); 869172827Smatteo break; 870172827Smatteo case AF_INET6: 871172827Smatteo sin6 = malloc(sizeof(struct sockaddr_in6)); 872173056Ssimon if (sin6 == NULL) 873172827Smatteo out_of_mem(); 874172827Smatteo sin6->sin6_family = AF_INET6; 875172827Smatteo sin6->sin6_port = htons(0); 876172827Smatteo sin6->sin6_addr = in6addr_any; 877172827Smatteo res->ai_addr = (struct sockaddr*) sin6; 878172827Smatteo res->ai_addrlen = (socklen_t) 879222623Srmacklem sizeof(struct sockaddr_in6); 880222623Srmacklem break; 881172827Smatteo default: 882222623Srmacklem syslog(LOG_ERR, "bad addr fam %d", 883222623Srmacklem res->ai_family); 884222623Srmacklem exit(1); 885172827Smatteo } 886172827Smatteo } else { 887172827Smatteo if ((aicode = getaddrinfo(NULL, svcport_str, 888172827Smatteo &hints, &res)) != 0) { 889172827Smatteo syslog(LOG_ERR, 890172827Smatteo "cannot get local address for %s: %s", 891172827Smatteo nconf->nc_netid, 892172827Smatteo gai_strerror(aicode)); 893222623Srmacklem close(fd); 894172827Smatteo continue; 895172827Smatteo } 896172827Smatteo } 897172827Smatteo } else { 898172827Smatteo if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str, 899172827Smatteo &hints, &res)) != 0) { 900172827Smatteo syslog(LOG_ERR, 901172827Smatteo "cannot get local address for %s: %s", 902172827Smatteo nconf->nc_netid, gai_strerror(aicode)); 903222623Srmacklem close(fd); 904172827Smatteo continue; 905172827Smatteo } 906172827Smatteo } 907172827Smatteo 908222623Srmacklem /* Store the fd. */ 909222623Srmacklem sock_fd[sock_fdcnt - 1] = fd; 910222623Srmacklem 911222623Srmacklem /* Now, attempt the bind. */ 912172827Smatteo r = bindresvport_sa(fd, res->ai_addr); 913172827Smatteo if (r != 0) { 914222623Srmacklem if (errno == EADDRINUSE && mallocd_svcport != 0) { 915222623Srmacklem if (mallocd_res != 0) { 916222623Srmacklem free(res->ai_addr); 917222623Srmacklem free(res); 918222623Srmacklem } else 919222623Srmacklem freeaddrinfo(res); 920222623Srmacklem return (-1); 921222623Srmacklem } 922172827Smatteo syslog(LOG_ERR, "bindresvport_sa: %m"); 923172827Smatteo exit(1); 924172827Smatteo } 925172827Smatteo 926222623Srmacklem if (svcport_str == NULL) { 927222623Srmacklem svcport_str = malloc(NI_MAXSERV * sizeof(char)); 928222623Srmacklem if (svcport_str == NULL) 929222623Srmacklem out_of_mem(); 930222623Srmacklem mallocd_svcport = 1; 931222623Srmacklem 932222623Srmacklem if (getnameinfo(res->ai_addr, 933222623Srmacklem res->ai_addr->sa_len, NULL, NI_MAXHOST, 934222623Srmacklem svcport_str, NI_MAXSERV * sizeof(char), 935222623Srmacklem NI_NUMERICHOST | NI_NUMERICSERV)) 936222623Srmacklem errx(1, "Cannot get port number"); 937222623Srmacklem } 938222623Srmacklem if (mallocd_res != 0) { 939222623Srmacklem free(res->ai_addr); 940222623Srmacklem free(res); 941222623Srmacklem } else 942222623Srmacklem freeaddrinfo(res); 943222623Srmacklem res = NULL; 944222623Srmacklem } 945222623Srmacklem return (0); 946222623Srmacklem} 947222623Srmacklem 948222623Srmacklem/* 949222623Srmacklem * Called after all the create_service() calls have succeeded, to complete 950222623Srmacklem * the setup and registration. 951222623Srmacklem */ 952222623Srmacklemstatic void 953222623Srmacklemcomplete_service(struct netconfig *nconf, char *port_str) 954222623Srmacklem{ 955222623Srmacklem struct addrinfo hints, *res = NULL; 956222623Srmacklem struct __rpc_sockinfo si; 957222623Srmacklem struct netbuf servaddr; 958222623Srmacklem SVCXPRT *transp = NULL; 959222623Srmacklem int aicode, fd, nhostsbak; 960222623Srmacklem int registered = 0; 961222623Srmacklem 962222623Srmacklem if ((nconf->nc_semantics != NC_TPI_CLTS) && 963222623Srmacklem (nconf->nc_semantics != NC_TPI_COTS) && 964222623Srmacklem (nconf->nc_semantics != NC_TPI_COTS_ORD)) 965222623Srmacklem return; /* not my type */ 966222623Srmacklem 967222623Srmacklem /* 968222623Srmacklem * XXX - using RPC library internal functions. 969222623Srmacklem */ 970222623Srmacklem if (!__rpc_nconf2sockinfo(nconf, &si)) { 971222623Srmacklem syslog(LOG_ERR, "cannot get information for %s", 972222623Srmacklem nconf->nc_netid); 973222623Srmacklem return; 974222623Srmacklem } 975222623Srmacklem 976222623Srmacklem nhostsbak = nhosts; 977222623Srmacklem while (nhostsbak > 0) { 978222623Srmacklem --nhostsbak; 979222623Srmacklem if (sock_fdpos >= sock_fdcnt) { 980222623Srmacklem /* Should never happen. */ 981222623Srmacklem syslog(LOG_ERR, "Ran out of socket fd's"); 982222623Srmacklem return; 983222623Srmacklem } 984222623Srmacklem fd = sock_fd[sock_fdpos++]; 985222623Srmacklem if (fd < 0) 986222623Srmacklem continue; 987222623Srmacklem 988341171Ssef /* 989341171Ssef * Using -1 tells listen(2) to use 990341171Ssef * kern.ipc.soacceptqueue for the backlog. 991341171Ssef */ 992172827Smatteo if (nconf->nc_semantics != NC_TPI_CLTS) 993341171Ssef listen(fd, -1); 994172827Smatteo 995172827Smatteo if (nconf->nc_semantics == NC_TPI_CLTS ) 996172827Smatteo transp = svc_dg_create(fd, 0, 0); 997172827Smatteo else 998172827Smatteo transp = svc_vc_create(fd, RPC_MAXDATASIZE, 999172827Smatteo RPC_MAXDATASIZE); 1000172827Smatteo 1001172827Smatteo if (transp != (SVCXPRT *) NULL) { 1002194880Sdfr if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv, 1003172827Smatteo NULL)) 1004172827Smatteo syslog(LOG_ERR, 1005194880Sdfr "can't register %s MOUNTVERS service", 1006172827Smatteo nconf->nc_netid); 1007172827Smatteo if (!force_v2) { 1008194880Sdfr if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3, 1009172827Smatteo mntsrv, NULL)) 1010172827Smatteo syslog(LOG_ERR, 1011194880Sdfr "can't register %s MOUNTVERS3 service", 1012172827Smatteo nconf->nc_netid); 1013172827Smatteo } 1014172827Smatteo } else 1015172827Smatteo syslog(LOG_WARNING, "can't create %s services", 1016172827Smatteo nconf->nc_netid); 1017172827Smatteo 1018172827Smatteo if (registered == 0) { 1019172827Smatteo registered = 1; 1020172827Smatteo memset(&hints, 0, sizeof hints); 1021172827Smatteo hints.ai_flags = AI_PASSIVE; 1022172827Smatteo hints.ai_family = si.si_af; 1023172827Smatteo hints.ai_socktype = si.si_socktype; 1024172827Smatteo hints.ai_protocol = si.si_proto; 1025172827Smatteo 1026222623Srmacklem if ((aicode = getaddrinfo(NULL, port_str, &hints, 1027172827Smatteo &res)) != 0) { 1028172827Smatteo syslog(LOG_ERR, "cannot get local address: %s", 1029172827Smatteo gai_strerror(aicode)); 1030172827Smatteo exit(1); 1031172827Smatteo } 1032172827Smatteo 1033172827Smatteo servaddr.buf = malloc(res->ai_addrlen); 1034172827Smatteo memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen); 1035172827Smatteo servaddr.len = res->ai_addrlen; 1036172827Smatteo 1037194880Sdfr rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr); 1038194880Sdfr rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr); 1039172827Smatteo 1040172827Smatteo xcreated++; 1041172827Smatteo freeaddrinfo(res); 1042172827Smatteo } 1043172827Smatteo } /* end while */ 10441558Srgrimes} 10451558Srgrimes 1046222623Srmacklem/* 1047222623Srmacklem * Clear out sockets after a failure to bind one of them, so that the 1048222623Srmacklem * cycle of socket creation/binding can start anew. 1049222623Srmacklem */ 105037663Scharnierstatic void 1051222623Srmacklemclearout_service(void) 1052222623Srmacklem{ 1053222623Srmacklem int i; 1054222623Srmacklem 1055222623Srmacklem for (i = 0; i < sock_fdcnt; i++) { 1056222623Srmacklem if (sock_fd[i] >= 0) { 1057222623Srmacklem shutdown(sock_fd[i], SHUT_RDWR); 1058222623Srmacklem close(sock_fd[i]); 1059222623Srmacklem } 1060222623Srmacklem } 1061222623Srmacklem} 1062222623Srmacklem 1063222623Srmacklemstatic void 1064216587Scharnierusage(void) 106537663Scharnier{ 106637663Scharnier fprintf(stderr, 1067192993Srmacklem "usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] " 1068241568Srmacklem "[-S] [-h <bindip>] [export_file ...]\n"); 106937663Scharnier exit(1); 107037663Scharnier} 107137663Scharnier 10721558Srgrimes/* 10731558Srgrimes * The mount rpc service 10741558Srgrimes */ 10751558Srgrimesvoid 1076216587Scharniermntsrv(struct svc_req *rqstp, SVCXPRT *transp) 10771558Srgrimes{ 10781558Srgrimes struct exportlist *ep; 10791558Srgrimes struct dirlist *dp; 10809336Sdfr struct fhreturn fhr; 10811558Srgrimes struct stat stb; 10821558Srgrimes struct statfs fsb; 108374462Salfred char host[NI_MAXHOST], numerichost[NI_MAXHOST]; 108474462Salfred int lookup_failed = 1; 108574462Salfred struct sockaddr *saddr; 10869336Sdfr u_short sport; 1087194880Sdfr char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN]; 1088363626Sbrooks int defset, hostset; 1089363626Sbrooks long bad = 0; 10909336Sdfr sigset_t sighup_mask; 1091240902Srmacklem int numsecflavors, *secflavorsp; 10921558Srgrimes 10939336Sdfr sigemptyset(&sighup_mask); 10949336Sdfr sigaddset(&sighup_mask, SIGHUP); 109574462Salfred saddr = svc_getrpccaller(transp)->buf; 109674462Salfred switch (saddr->sa_family) { 109774462Salfred case AF_INET6: 109875635Siedowse sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port); 109974462Salfred break; 110074462Salfred case AF_INET: 110175635Siedowse sport = ntohs(((struct sockaddr_in *)saddr)->sin_port); 110274462Salfred break; 110374462Salfred default: 110474462Salfred syslog(LOG_ERR, "request from unknown address family"); 110574462Salfred return; 110674462Salfred } 1107346769Smav switch (rqstp->rq_proc) { 1108346769Smav case MOUNTPROC_MNT: 1109346769Smav case MOUNTPROC_UMNT: 1110346769Smav case MOUNTPROC_UMNTALL: 1111346769Smav lookup_failed = getnameinfo(saddr, saddr->sa_len, host, 1112346769Smav sizeof host, NULL, 0, 0); 1113346769Smav } 111474462Salfred getnameinfo(saddr, saddr->sa_len, numerichost, 111574462Salfred sizeof numerichost, NULL, 0, NI_NUMERICHOST); 11161558Srgrimes switch (rqstp->rq_proc) { 11171558Srgrimes case NULLPROC: 1118121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL)) 111937663Scharnier syslog(LOG_ERR, "can't send reply"); 11201558Srgrimes return; 1121194880Sdfr case MOUNTPROC_MNT: 11229336Sdfr if (sport >= IPPORT_RESERVED && resvport_only) { 112331656Sguido syslog(LOG_NOTICE, 112431656Sguido "mount request from %s from unprivileged port", 112574462Salfred numerichost); 11261558Srgrimes svcerr_weakauth(transp); 11271558Srgrimes return; 11281558Srgrimes } 1129121556Speter if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { 113031656Sguido syslog(LOG_NOTICE, "undecodable mount request from %s", 113174462Salfred numerichost); 11321558Srgrimes svcerr_decode(transp); 11331558Srgrimes return; 11341558Srgrimes } 11351558Srgrimes 11361558Srgrimes /* 11371558Srgrimes * Get the real pathname and make sure it is a directory 11389336Sdfr * or a regular file if the -r option was specified 11399336Sdfr * and it exists. 11401558Srgrimes */ 114151968Salfred if (realpath(rpcpath, dirpath) == NULL || 11421558Srgrimes stat(dirpath, &stb) < 0 || 11431558Srgrimes statfs(dirpath, &fsb) < 0) { 11441558Srgrimes chdir("/"); /* Just in case realpath doesn't */ 114531656Sguido syslog(LOG_NOTICE, 114637663Scharnier "mount request from %s for non existent path %s", 114774462Salfred numerichost, dirpath); 11481558Srgrimes if (debug) 114937663Scharnier warnx("stat failed on %s", dirpath); 115028911Sguido bad = ENOENT; /* We will send error reply later */ 11511558Srgrimes } 1152330092Srpokala if (!bad && 1153330092Srpokala !S_ISDIR(stb.st_mode) && 1154330092Srpokala (dir_only || !S_ISREG(stb.st_mode))) { 1155330092Srpokala syslog(LOG_NOTICE, 1156330092Srpokala "mount request from %s for non-directory path %s", 1157330092Srpokala numerichost, dirpath); 1158330092Srpokala if (debug) 1159330092Srpokala warnx("mounting non-directory %s", dirpath); 1160330092Srpokala bad = ENOTDIR; /* We will send error reply later */ 1161330092Srpokala } 11621558Srgrimes 11631558Srgrimes /* Check in the exports list */ 11649336Sdfr sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 1165330092Srpokala if (bad) 1166330092Srpokala ep = NULL; 1167330092Srpokala else 1168349756Srmacklem ep = ex_search(&fsb.f_fsid, exphead); 11699336Sdfr hostset = defset = 0; 1170240902Srmacklem if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset, 1171240902Srmacklem &numsecflavors, &secflavorsp) || 11721558Srgrimes ((dp = dirp_search(ep->ex_dirl, dirpath)) && 1173240902Srmacklem chk_host(dp, saddr, &defset, &hostset, &numsecflavors, 1174240902Srmacklem &secflavorsp)) || 117574462Salfred (defset && scan_tree(ep->ex_defdir, saddr) == 0 && 117674462Salfred scan_tree(ep->ex_dirl, saddr) == 0))) { 117728911Sguido if (bad) { 1178121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_long, 117928911Sguido (caddr_t)&bad)) 118037663Scharnier syslog(LOG_ERR, "can't send reply"); 118128911Sguido sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 118228911Sguido return; 118328911Sguido } 1184240902Srmacklem if (hostset & DP_HOSTSET) { 11859336Sdfr fhr.fhr_flag = hostset; 1186240902Srmacklem fhr.fhr_numsecflavors = numsecflavors; 1187240902Srmacklem fhr.fhr_secflavors = secflavorsp; 1188240902Srmacklem } else { 11899336Sdfr fhr.fhr_flag = defset; 1190240902Srmacklem fhr.fhr_numsecflavors = ep->ex_defnumsecflavors; 1191240902Srmacklem fhr.fhr_secflavors = ep->ex_defsecflavors; 1192240902Srmacklem } 11939336Sdfr fhr.fhr_vers = rqstp->rq_vers; 11941558Srgrimes /* Get the file handle */ 119523681Speter memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t)); 11969336Sdfr if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) { 11971558Srgrimes bad = errno; 119837663Scharnier syslog(LOG_ERR, "can't get fh for %s", dirpath); 1199121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_long, 12001558Srgrimes (caddr_t)&bad)) 120137663Scharnier syslog(LOG_ERR, "can't send reply"); 12029336Sdfr sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 12031558Srgrimes return; 12041558Srgrimes } 1205121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs, 1206121556Speter (caddr_t)&fhr)) 120737663Scharnier syslog(LOG_ERR, "can't send reply"); 120874462Salfred if (!lookup_failed) 120974462Salfred add_mlist(host, dirpath); 12101558Srgrimes else 121174462Salfred add_mlist(numerichost, dirpath); 12121558Srgrimes if (debug) 121337663Scharnier warnx("mount successful"); 1214121767Speter if (dolog) 121531656Sguido syslog(LOG_NOTICE, 121631656Sguido "mount request succeeded from %s for %s", 121774462Salfred numerichost, dirpath); 121831656Sguido } else { 1219330092Srpokala if (!bad) 1220330092Srpokala bad = EACCES; 122131656Sguido syslog(LOG_NOTICE, 122231656Sguido "mount request denied from %s for %s", 122374462Salfred numerichost, dirpath); 122431656Sguido } 122528911Sguido 1226121556Speter if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long, 1227121556Speter (caddr_t)&bad)) 122837663Scharnier syslog(LOG_ERR, "can't send reply"); 12299336Sdfr sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 12301558Srgrimes return; 1231194880Sdfr case MOUNTPROC_DUMP: 1232121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL)) 123337663Scharnier syslog(LOG_ERR, "can't send reply"); 1234121767Speter else if (dolog) 123531656Sguido syslog(LOG_NOTICE, 123631656Sguido "dump request succeeded from %s", 123774462Salfred numerichost); 12381558Srgrimes return; 1239194880Sdfr case MOUNTPROC_UMNT: 12409336Sdfr if (sport >= IPPORT_RESERVED && resvport_only) { 124131656Sguido syslog(LOG_NOTICE, 124231656Sguido "umount request from %s from unprivileged port", 124374462Salfred numerichost); 12441558Srgrimes svcerr_weakauth(transp); 12451558Srgrimes return; 12461558Srgrimes } 1247121556Speter if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { 124831656Sguido syslog(LOG_NOTICE, "undecodable umount request from %s", 124974462Salfred numerichost); 12501558Srgrimes svcerr_decode(transp); 12511558Srgrimes return; 12521558Srgrimes } 125351968Salfred if (realpath(rpcpath, dirpath) == NULL) { 125451968Salfred syslog(LOG_NOTICE, "umount request from %s " 125551968Salfred "for non existent path %s", 125674462Salfred numerichost, dirpath); 125751968Salfred } 1258121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) 125937663Scharnier syslog(LOG_ERR, "can't send reply"); 126074462Salfred if (!lookup_failed) 126175635Siedowse del_mlist(host, dirpath); 126275635Siedowse del_mlist(numerichost, dirpath); 1263121767Speter if (dolog) 126431656Sguido syslog(LOG_NOTICE, 126531656Sguido "umount request succeeded from %s for %s", 126674462Salfred numerichost, dirpath); 12671558Srgrimes return; 1268194880Sdfr case MOUNTPROC_UMNTALL: 12699336Sdfr if (sport >= IPPORT_RESERVED && resvport_only) { 127031656Sguido syslog(LOG_NOTICE, 127131656Sguido "umountall request from %s from unprivileged port", 127274462Salfred numerichost); 12731558Srgrimes svcerr_weakauth(transp); 12741558Srgrimes return; 12751558Srgrimes } 1276121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) 127737663Scharnier syslog(LOG_ERR, "can't send reply"); 127874462Salfred if (!lookup_failed) 127975635Siedowse del_mlist(host, NULL); 128075635Siedowse del_mlist(numerichost, NULL); 1281121767Speter if (dolog) 128231656Sguido syslog(LOG_NOTICE, 128331656Sguido "umountall request succeeded from %s", 128474462Salfred numerichost); 12851558Srgrimes return; 1286194880Sdfr case MOUNTPROC_EXPORT: 1287121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL)) 1288121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief, 1289121556Speter (caddr_t)NULL)) 1290100117Salfred syslog(LOG_ERR, "can't send reply"); 1291121767Speter if (dolog) 129231656Sguido syslog(LOG_NOTICE, 129331656Sguido "export request succeeded from %s", 129474462Salfred numerichost); 12951558Srgrimes return; 12961558Srgrimes default: 12971558Srgrimes svcerr_noproc(transp); 12981558Srgrimes return; 12991558Srgrimes } 13001558Srgrimes} 13011558Srgrimes 13021558Srgrimes/* 13031558Srgrimes * Xdr conversion for a dirpath string 13041558Srgrimes */ 1305285128Straszstatic int 1306216587Scharnierxdr_dir(XDR *xdrsp, char *dirp) 13071558Srgrimes{ 1308194880Sdfr return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); 13091558Srgrimes} 13101558Srgrimes 13111558Srgrimes/* 13129336Sdfr * Xdr routine to generate file handle reply 13131558Srgrimes */ 1314285128Straszstatic int 1315216587Scharnierxdr_fhs(XDR *xdrsp, caddr_t cp) 13161558Srgrimes{ 131792806Sobrien struct fhreturn *fhrp = (struct fhreturn *)cp; 13189336Sdfr u_long ok = 0, len, auth; 1319184588Sdfr int i; 13201558Srgrimes 13211558Srgrimes if (!xdr_long(xdrsp, &ok)) 13221558Srgrimes return (0); 13239336Sdfr switch (fhrp->fhr_vers) { 13249336Sdfr case 1: 13259336Sdfr return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH)); 13269336Sdfr case 3: 13279336Sdfr len = NFSX_V3FH; 13289336Sdfr if (!xdr_long(xdrsp, &len)) 13299336Sdfr return (0); 13309336Sdfr if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len)) 13319336Sdfr return (0); 1332184588Sdfr if (fhrp->fhr_numsecflavors) { 1333184588Sdfr if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors)) 1334184588Sdfr return (0); 1335184588Sdfr for (i = 0; i < fhrp->fhr_numsecflavors; i++) 1336184588Sdfr if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i])) 1337184588Sdfr return (0); 1338184588Sdfr return (1); 1339184588Sdfr } else { 1340184588Sdfr auth = AUTH_SYS; 1341184588Sdfr len = 1; 1342184588Sdfr if (!xdr_long(xdrsp, &len)) 1343184588Sdfr return (0); 1344184588Sdfr return (xdr_long(xdrsp, &auth)); 1345184588Sdfr } 1346298089Spfg } 13479336Sdfr return (0); 13481558Srgrimes} 13491558Srgrimes 1350285128Straszstatic int 1351216587Scharnierxdr_mlist(XDR *xdrsp, caddr_t cp __unused) 13521558Srgrimes{ 13531558Srgrimes struct mountlist *mlp; 13541558Srgrimes int true = 1; 13551558Srgrimes int false = 0; 13561558Srgrimes char *strp; 13571558Srgrimes 1358324955Smanu SLIST_FOREACH(mlp, &mlhead, next) { 13591558Srgrimes if (!xdr_bool(xdrsp, &true)) 13601558Srgrimes return (0); 13611558Srgrimes strp = &mlp->ml_host[0]; 1362194880Sdfr if (!xdr_string(xdrsp, &strp, MNTNAMLEN)) 13631558Srgrimes return (0); 13641558Srgrimes strp = &mlp->ml_dirp[0]; 1365194880Sdfr if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) 13661558Srgrimes return (0); 13671558Srgrimes } 13681558Srgrimes if (!xdr_bool(xdrsp, &false)) 13691558Srgrimes return (0); 13701558Srgrimes return (1); 13711558Srgrimes} 13721558Srgrimes 13731558Srgrimes/* 13741558Srgrimes * Xdr conversion for export list 13751558Srgrimes */ 1376285128Straszstatic int 1377216587Scharnierxdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief) 13781558Srgrimes{ 13791558Srgrimes struct exportlist *ep; 13801558Srgrimes int false = 0; 13819336Sdfr int putdef; 13829336Sdfr sigset_t sighup_mask; 1383349756Srmacklem int i; 13841558Srgrimes 13859336Sdfr sigemptyset(&sighup_mask); 13869336Sdfr sigaddset(&sighup_mask, SIGHUP); 13879336Sdfr sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 1388324955Smanu 1389349756Srmacklem for (i = 0; i < exphashsize; i++) 1390349756Srmacklem SLIST_FOREACH(ep, &exphead[i], entries) { 1391349756Srmacklem putdef = 0; 1392349756Srmacklem if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, 1393349756Srmacklem &putdef, brief)) 1394349756Srmacklem goto errout; 1395349756Srmacklem if (ep->ex_defdir && putdef == 0 && 1396349756Srmacklem put_exlist(ep->ex_defdir, xdrsp, NULL, 1397349756Srmacklem &putdef, brief)) 1398349756Srmacklem goto errout; 1399349756Srmacklem } 14009336Sdfr sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 14011558Srgrimes if (!xdr_bool(xdrsp, &false)) 14021558Srgrimes return (0); 14031558Srgrimes return (1); 14041558Srgrimeserrout: 14059336Sdfr sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 14061558Srgrimes return (0); 14071558Srgrimes} 14081558Srgrimes 14091558Srgrimes/* 14101558Srgrimes * Called from xdr_explist() to traverse the tree and export the 14111558Srgrimes * directory paths. 14121558Srgrimes */ 1413285128Straszstatic int 1414216587Scharnierput_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp, 1415216587Scharnier int brief) 14161558Srgrimes{ 14171558Srgrimes struct grouplist *grp; 14181558Srgrimes struct hostlist *hp; 14191558Srgrimes int true = 1; 14201558Srgrimes int false = 0; 14211558Srgrimes int gotalldir = 0; 14221558Srgrimes char *strp; 14231558Srgrimes 14241558Srgrimes if (dp) { 1425100117Salfred if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief)) 14261558Srgrimes return (1); 14271558Srgrimes if (!xdr_bool(xdrsp, &true)) 14281558Srgrimes return (1); 14291558Srgrimes strp = dp->dp_dirp; 1430194880Sdfr if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) 14311558Srgrimes return (1); 14321558Srgrimes if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { 14331558Srgrimes gotalldir = 1; 14341558Srgrimes *putdefp = 1; 14351558Srgrimes } 1436100117Salfred if (brief) { 1437100117Salfred if (!xdr_bool(xdrsp, &true)) 1438100117Salfred return (1); 1439100117Salfred strp = "(...)"; 1440194880Sdfr if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) 1441100117Salfred return (1); 1442100117Salfred } else if ((dp->dp_flag & DP_DEFSET) == 0 && 14431558Srgrimes (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { 14441558Srgrimes hp = dp->dp_hosts; 14451558Srgrimes while (hp) { 14461558Srgrimes grp = hp->ht_grp; 14471558Srgrimes if (grp->gr_type == GT_HOST) { 14481558Srgrimes if (!xdr_bool(xdrsp, &true)) 14491558Srgrimes return (1); 145074462Salfred strp = grp->gr_ptr.gt_addrinfo->ai_canonname; 14518871Srgrimes if (!xdr_string(xdrsp, &strp, 1452194880Sdfr MNTNAMLEN)) 14531558Srgrimes return (1); 14541558Srgrimes } else if (grp->gr_type == GT_NET) { 14551558Srgrimes if (!xdr_bool(xdrsp, &true)) 14561558Srgrimes return (1); 14571558Srgrimes strp = grp->gr_ptr.gt_net.nt_name; 14588871Srgrimes if (!xdr_string(xdrsp, &strp, 1459194880Sdfr MNTNAMLEN)) 14601558Srgrimes return (1); 14611558Srgrimes } 14621558Srgrimes hp = hp->ht_next; 14631558Srgrimes if (gotalldir && hp == (struct hostlist *)NULL) { 14641558Srgrimes hp = adp->dp_hosts; 14651558Srgrimes gotalldir = 0; 14661558Srgrimes } 14671558Srgrimes } 14681558Srgrimes } 14691558Srgrimes if (!xdr_bool(xdrsp, &false)) 14701558Srgrimes return (1); 1471100117Salfred if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief)) 14721558Srgrimes return (1); 14731558Srgrimes } 14741558Srgrimes return (0); 14751558Srgrimes} 14761558Srgrimes 1477285128Straszstatic int 1478216587Scharnierxdr_explist(XDR *xdrsp, caddr_t cp) 1479100117Salfred{ 1480100117Salfred 1481100117Salfred return xdr_explist_common(xdrsp, cp, 0); 1482100117Salfred} 1483100117Salfred 1484285128Straszstatic int 1485216587Scharnierxdr_explist_brief(XDR *xdrsp, caddr_t cp) 1486100117Salfred{ 1487100117Salfred 1488100117Salfred return xdr_explist_common(xdrsp, cp, 1); 1489100117Salfred} 1490100117Salfred 1491285128Straszstatic char *line; 1492285128Straszstatic size_t linesize; 1493285128Straszstatic FILE *exp_file; 14941558Srgrimes 14951558Srgrimes/* 1496166440Spjd * Get the export list from one, currently open file 14971558Srgrimes */ 1498166440Spjdstatic void 1499349772Srmacklemget_exportlist_one(int passno) 15001558Srgrimes{ 1501324955Smanu struct exportlist *ep; 1502349772Srmacklem struct grouplist *grp, *tgrp, *savgrp; 15031558Srgrimes struct dirlist *dirhead; 1504166440Spjd struct statfs fsb; 150572650Sgreen struct xucred anon; 15061558Srgrimes char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; 1507166440Spjd int len, has_host, exflags, got_nondir, dirplen, netgrp; 15081558Srgrimes 1509192934Srmacklem v4root_phase = 0; 15101558Srgrimes dirhead = (struct dirlist *)NULL; 15111558Srgrimes while (get_line()) { 15121558Srgrimes if (debug) 151337663Scharnier warnx("got line %s", line); 15141558Srgrimes cp = line; 15151558Srgrimes nextfield(&cp, &endcp); 15161558Srgrimes if (*cp == '#') 15171558Srgrimes goto nextline; 15181558Srgrimes 15191558Srgrimes /* 15201558Srgrimes * Set defaults. 15211558Srgrimes */ 15221558Srgrimes has_host = FALSE; 15231558Srgrimes anon = def_anon; 15241558Srgrimes exflags = MNT_EXPORTED; 15251558Srgrimes got_nondir = 0; 15261558Srgrimes opt_flags = 0; 15271558Srgrimes ep = (struct exportlist *)NULL; 1528192934Srmacklem dirp = NULL; 15291558Srgrimes 15301558Srgrimes /* 1531192934Srmacklem * Handle the V4 root dir. 1532192934Srmacklem */ 1533192934Srmacklem if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') { 1534192934Srmacklem /* 1535192934Srmacklem * V4: just indicates that it is the v4 root point, 1536192934Srmacklem * so skip over that and set v4root_phase. 1537192934Srmacklem */ 1538192934Srmacklem if (v4root_phase > 0) { 1539192934Srmacklem syslog(LOG_ERR, "V4:duplicate line, ignored"); 1540192934Srmacklem goto nextline; 1541192934Srmacklem } 1542192934Srmacklem v4root_phase = 1; 1543192934Srmacklem cp += 3; 1544192934Srmacklem nextfield(&cp, &endcp); 1545192934Srmacklem } 1546192934Srmacklem 1547192934Srmacklem /* 15481558Srgrimes * Create new exports list entry 15491558Srgrimes */ 15501558Srgrimes len = endcp-cp; 15511558Srgrimes tgrp = grp = get_grp(); 15521558Srgrimes while (len > 0) { 1553194880Sdfr if (len > MNTNAMLEN) { 1554329392Sbrd getexp_err(ep, tgrp, "mountpoint too long"); 15551558Srgrimes goto nextline; 15561558Srgrimes } 15571558Srgrimes if (*cp == '-') { 15581558Srgrimes if (ep == (struct exportlist *)NULL) { 1559329392Sbrd getexp_err(ep, tgrp, 1560329392Sbrd "flag before export path definition"); 15611558Srgrimes goto nextline; 15621558Srgrimes } 15631558Srgrimes if (debug) 156437663Scharnier warnx("doing opt %s", cp); 15651558Srgrimes got_nondir = 1; 15661558Srgrimes if (do_opt(&cp, &endcp, ep, grp, &has_host, 15671558Srgrimes &exflags, &anon)) { 1568329392Sbrd getexp_err(ep, tgrp, NULL); 15691558Srgrimes goto nextline; 15701558Srgrimes } 15711558Srgrimes } else if (*cp == '/') { 15721558Srgrimes savedc = *endcp; 15731558Srgrimes *endcp = '\0'; 1574192934Srmacklem if (v4root_phase > 1) { 1575192934Srmacklem if (dirp != NULL) { 1576329392Sbrd getexp_err(ep, tgrp, "Multiple V4 dirs"); 1577192934Srmacklem goto nextline; 1578192934Srmacklem } 1579192934Srmacklem } 15801558Srgrimes if (check_dirpath(cp) && 15811558Srgrimes statfs(cp, &fsb) >= 0) { 1582283008Srmacklem if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0) 1583283008Srmacklem syslog(LOG_ERR, "Warning: exporting of " 1584283008Srmacklem "automounted fs %s not supported", cp); 15851558Srgrimes if (got_nondir) { 1586329392Sbrd getexp_err(ep, tgrp, "dirs must be first"); 15871558Srgrimes goto nextline; 15881558Srgrimes } 1589192934Srmacklem if (v4root_phase == 1) { 1590192934Srmacklem if (dirp != NULL) { 1591329392Sbrd getexp_err(ep, tgrp, "Multiple V4 dirs"); 15921558Srgrimes goto nextline; 15931558Srgrimes } 1594192934Srmacklem if (strlen(v4root_dirpath) == 0) { 1595192934Srmacklem strlcpy(v4root_dirpath, cp, 1596192934Srmacklem sizeof (v4root_dirpath)); 1597192934Srmacklem } else if (strcmp(v4root_dirpath, cp) 1598192934Srmacklem != 0) { 1599192934Srmacklem syslog(LOG_ERR, 1600192934Srmacklem "different V4 dirpath %s", cp); 1601329392Sbrd getexp_err(ep, tgrp, NULL); 1602192934Srmacklem goto nextline; 1603192934Srmacklem } 1604192934Srmacklem dirp = cp; 1605192934Srmacklem v4root_phase = 2; 1606192934Srmacklem got_nondir = 1; 1607192934Srmacklem ep = get_exp(); 16081558Srgrimes } else { 1609192934Srmacklem if (ep) { 1610192934Srmacklem if (ep->ex_fs.val[0] != 1611192934Srmacklem fsb.f_fsid.val[0] || 1612192934Srmacklem ep->ex_fs.val[1] != 1613192934Srmacklem fsb.f_fsid.val[1]) { 1614329392Sbrd getexp_err(ep, tgrp, 1615329392Sbrd "fsid mismatch"); 1616192934Srmacklem goto nextline; 1617192934Srmacklem } 1618192934Srmacklem } else { 1619192934Srmacklem /* 1620192934Srmacklem * See if this directory is already 1621192934Srmacklem * in the list. 1622192934Srmacklem */ 1623349756Srmacklem ep = ex_search(&fsb.f_fsid, exphead); 1624192934Srmacklem if (ep == (struct exportlist *)NULL) { 1625192934Srmacklem ep = get_exp(); 1626192934Srmacklem ep->ex_fs = fsb.f_fsid; 1627324234Smanu ep->ex_fsdir = strdup(fsb.f_mntonname); 1628324234Smanu if (ep->ex_fsdir == NULL) 1629192934Srmacklem out_of_mem(); 1630192934Srmacklem if (debug) 1631192934Srmacklem warnx( 1632192934Srmacklem "making new ep fs=0x%x,0x%x", 1633192934Srmacklem fsb.f_fsid.val[0], 1634192934Srmacklem fsb.f_fsid.val[1]); 1635192934Srmacklem } else if (debug) 1636192934Srmacklem warnx("found ep fs=0x%x,0x%x", 1637192934Srmacklem fsb.f_fsid.val[0], 1638192934Srmacklem fsb.f_fsid.val[1]); 1639192934Srmacklem } 1640192934Srmacklem 16411558Srgrimes /* 1642192934Srmacklem * Add dirpath to export mount point. 16431558Srgrimes */ 1644192934Srmacklem dirp = add_expdir(&dirhead, cp, len); 1645192934Srmacklem dirplen = len; 16461558Srgrimes } 16471558Srgrimes } else { 1648329392Sbrd getexp_err(ep, tgrp, 1649329392Sbrd "symbolic link in export path or statfs failed"); 16501558Srgrimes goto nextline; 16511558Srgrimes } 16521558Srgrimes *endcp = savedc; 16531558Srgrimes } else { 16541558Srgrimes savedc = *endcp; 16551558Srgrimes *endcp = '\0'; 16561558Srgrimes got_nondir = 1; 16571558Srgrimes if (ep == (struct exportlist *)NULL) { 1658329392Sbrd getexp_err(ep, tgrp, 1659329392Sbrd "host(s) before export path definition"); 16601558Srgrimes goto nextline; 16611558Srgrimes } 16621558Srgrimes 16631558Srgrimes /* 16641558Srgrimes * Get the host or netgroup. 16651558Srgrimes */ 16661558Srgrimes setnetgrent(cp); 16671558Srgrimes netgrp = getnetgrent(&hst, &usr, &dom); 16681558Srgrimes do { 16691558Srgrimes if (has_host) { 16701558Srgrimes grp->gr_next = get_grp(); 16711558Srgrimes grp = grp->gr_next; 16721558Srgrimes } 16731558Srgrimes if (netgrp) { 167437003Sjoerg if (hst == 0) { 167537663Scharnier syslog(LOG_ERR, 167637663Scharnier "null hostname in netgroup %s, skipping", cp); 167737004Sjoerg grp->gr_type = GT_IGNORE; 167837003Sjoerg } else if (get_host(hst, grp, tgrp)) { 167937663Scharnier syslog(LOG_ERR, 168037663Scharnier "bad host %s in netgroup %s, skipping", hst, cp); 168129317Sjlemon grp->gr_type = GT_IGNORE; 16821558Srgrimes } 16837401Swpaul } else if (get_host(cp, grp, tgrp)) { 168437663Scharnier syslog(LOG_ERR, "bad host %s, skipping", cp); 168529317Sjlemon grp->gr_type = GT_IGNORE; 16861558Srgrimes } 16871558Srgrimes has_host = TRUE; 16881558Srgrimes } while (netgrp && getnetgrent(&hst, &usr, &dom)); 16891558Srgrimes endnetgrent(); 16901558Srgrimes *endcp = savedc; 16911558Srgrimes } 16921558Srgrimes cp = endcp; 16931558Srgrimes nextfield(&cp, &endcp); 16941558Srgrimes len = endcp - cp; 16951558Srgrimes } 16961558Srgrimes if (check_options(dirhead)) { 1697329392Sbrd getexp_err(ep, tgrp, NULL); 16981558Srgrimes goto nextline; 16991558Srgrimes } 17001558Srgrimes if (!has_host) { 170175641Siedowse grp->gr_type = GT_DEFAULT; 17021558Srgrimes if (debug) 170337663Scharnier warnx("adding a default entry"); 17041558Srgrimes 17051558Srgrimes /* 17061558Srgrimes * Don't allow a network export coincide with a list of 17071558Srgrimes * host(s) on the same line. 17081558Srgrimes */ 17091558Srgrimes } else if ((opt_flags & OP_NET) && tgrp->gr_next) { 1710329392Sbrd getexp_err(ep, tgrp, "network/host conflict"); 17111558Srgrimes goto nextline; 171229317Sjlemon 171374462Salfred /* 171474462Salfred * If an export list was specified on this line, make sure 171529317Sjlemon * that we have at least one valid entry, otherwise skip it. 171629317Sjlemon */ 171729317Sjlemon } else { 171829317Sjlemon grp = tgrp; 171974462Salfred while (grp && grp->gr_type == GT_IGNORE) 172029317Sjlemon grp = grp->gr_next; 172129317Sjlemon if (! grp) { 1722329392Sbrd getexp_err(ep, tgrp, "no valid entries"); 172329317Sjlemon goto nextline; 172429317Sjlemon } 17251558Srgrimes } 17261558Srgrimes 1727192934Srmacklem if (v4root_phase == 1) { 1728329392Sbrd getexp_err(ep, tgrp, "V4:root, no dirp, ignored"); 1729192934Srmacklem goto nextline; 1730192934Srmacklem } 1731192934Srmacklem 17321558Srgrimes /* 17331558Srgrimes * Loop through hosts, pushing the exports into the kernel. 17341558Srgrimes * After loop, tgrp points to the start of the list and 17351558Srgrimes * grp points to the last entry in the list. 1736349772Srmacklem * Do not do the do_mount() for passno == 1, since the 1737349772Srmacklem * second pass will do it, as required. 17381558Srgrimes */ 17391558Srgrimes grp = tgrp; 17401558Srgrimes do { 1741349772Srmacklem grp->gr_exflags = exflags; 1742349772Srmacklem grp->gr_anon = anon; 1743349772Srmacklem if (v4root_phase == 2 && passno == 0) 1744349772Srmacklem LOGDEBUG("do_mount v4root"); 1745349772Srmacklem if (passno == 0 && do_mount(ep, grp, exflags, &anon, 1746349772Srmacklem dirp, dirplen, &fsb, ep->ex_numsecflavors, 1747349772Srmacklem ep->ex_secflavors)) { 1748329392Sbrd getexp_err(ep, tgrp, NULL); 174975635Siedowse goto nextline; 175075635Siedowse } 17511558Srgrimes } while (grp->gr_next && (grp = grp->gr_next)); 17521558Srgrimes 17531558Srgrimes /* 1754192934Srmacklem * For V4: don't enter in mount lists. 1755192934Srmacklem */ 1756194773Srmacklem if (v4root_phase > 0 && v4root_phase <= 2) { 1757194773Srmacklem /* 1758349772Srmacklem * These structures are used for the reload, 1759349772Srmacklem * so save them for that case. Otherwise, just 1760194773Srmacklem * free them up now. 1761194773Srmacklem */ 1762349772Srmacklem if (passno == 1 && ep != NULL) { 1763349772Srmacklem savgrp = tgrp; 1764349772Srmacklem while (tgrp != NULL) { 1765349772Srmacklem /* 1766349772Srmacklem * Save the security flavors and exflags 1767349772Srmacklem * for this host set in the groups. 1768349772Srmacklem */ 1769349772Srmacklem tgrp->gr_numsecflavors = 1770349772Srmacklem ep->ex_numsecflavors; 1771349772Srmacklem if (ep->ex_numsecflavors > 0) 1772349772Srmacklem memcpy(tgrp->gr_secflavors, 1773349772Srmacklem ep->ex_secflavors, 1774349772Srmacklem sizeof(ep->ex_secflavors)); 1775349772Srmacklem tgrp = tgrp->gr_next; 1776349772Srmacklem } 1777349772Srmacklem if (v4root_ep == NULL) { 1778349772Srmacklem v4root_ep = ep; 1779349772Srmacklem ep = NULL; /* Don't free below. */ 1780349772Srmacklem } 1781349772Srmacklem grp->gr_next = v4root_ep->ex_grphead; 1782349772Srmacklem v4root_ep->ex_grphead = savgrp; 1783349772Srmacklem } 1784194773Srmacklem if (ep != NULL) 1785194773Srmacklem free_exp(ep); 1786194773Srmacklem while (tgrp != NULL) { 1787194773Srmacklem grp = tgrp; 1788194773Srmacklem tgrp = tgrp->gr_next; 1789194773Srmacklem free_grp(grp); 1790194773Srmacklem } 1791192934Srmacklem goto nextline; 1792194773Srmacklem } 1793192934Srmacklem 1794192934Srmacklem /* 17951558Srgrimes * Success. Update the data structures. 17961558Srgrimes */ 17971558Srgrimes if (has_host) { 1798349772Srmacklem hang_dirp(dirhead, tgrp, ep, opt_flags, &anon, exflags); 1799349128Srmacklem grp->gr_next = ep->ex_grphead; 1800349128Srmacklem ep->ex_grphead = tgrp; 18011558Srgrimes } else { 18021558Srgrimes hang_dirp(dirhead, (struct grouplist *)NULL, ep, 1803349772Srmacklem opt_flags, &anon, exflags); 18041558Srgrimes free_grp(grp); 18051558Srgrimes } 18061558Srgrimes dirhead = (struct dirlist *)NULL; 18071558Srgrimes if ((ep->ex_flag & EX_LINKED) == 0) { 1808349756Srmacklem insert_exports(ep, exphead); 18091558Srgrimes 18101558Srgrimes ep->ex_flag |= EX_LINKED; 18111558Srgrimes } 18121558Srgrimesnextline: 1813192934Srmacklem v4root_phase = 0; 18141558Srgrimes if (dirhead) { 18151558Srgrimes free_dir(dirhead); 18161558Srgrimes dirhead = (struct dirlist *)NULL; 18171558Srgrimes } 18181558Srgrimes } 18191558Srgrimes} 18201558Srgrimes 18211558Srgrimes/* 1822166440Spjd * Get the export list from all specified files 1823166440Spjd */ 1824285128Straszstatic void 1825349772Srmacklemget_exportlist(int passno) 1826166440Spjd{ 1827166440Spjd struct export_args export; 1828166440Spjd struct iovec *iov; 1829349126Srmacklem struct statfs *mntbufp; 1830166440Spjd char errmsg[255]; 1831230352Seadler int num, i; 1832166440Spjd int iovlen; 1833192934Srmacklem struct nfsex_args eargs; 1834349772Srmacklem FILE *debug_file; 1835166440Spjd 1836349772Srmacklem if ((debug_file = fopen(_PATH_MOUNTDDEBUG, "r")) != NULL) { 1837349772Srmacklem fclose(debug_file); 1838349772Srmacklem logdebug = 1; 1839349772Srmacklem } else 1840349772Srmacklem logdebug = 0; 1841349772Srmacklem LOGDEBUG("passno=%d", passno); 1842192934Srmacklem v4root_dirpath[0] = '\0'; 1843349772Srmacklem free_v4rootexp(); 1844349772Srmacklem if (passno == 1) { 1845349772Srmacklem /* 1846349772Srmacklem * Save the current lists as old ones, so that the new lists 1847349772Srmacklem * can be compared with the old ones in the 2nd pass. 1848349772Srmacklem */ 1849349772Srmacklem for (i = 0; i < exphashsize; i++) { 1850349772Srmacklem SLIST_FIRST(&oldexphead[i]) = SLIST_FIRST(&exphead[i]); 1851349772Srmacklem SLIST_INIT(&exphead[i]); 1852349772Srmacklem } 1853349772Srmacklem 1854349772Srmacklem /* Note that the public fh has not yet been set. */ 1855349772Srmacklem has_set_publicfh = 0; 1856349772Srmacklem 1857349772Srmacklem /* Read the export file(s) and process them */ 1858349772Srmacklem read_exportfile(passno); 1859349772Srmacklem } else { 1860349772Srmacklem /* 1861349772Srmacklem * Just make the old lists empty. 1862349772Srmacklem * exphashsize == 0 for the first call, before oldexphead 1863349772Srmacklem * has been initialized-->loop won't be executed. 1864349772Srmacklem */ 1865349772Srmacklem for (i = 0; i < exphashsize; i++) 1866349772Srmacklem SLIST_INIT(&oldexphead[i]); 1867349772Srmacklem } 1868349772Srmacklem 1869166440Spjd bzero(&export, sizeof(export)); 1870166440Spjd export.ex_flags = MNT_DELEXPORT; 1871166440Spjd iov = NULL; 1872166440Spjd iovlen = 0; 1873166440Spjd bzero(errmsg, sizeof(errmsg)); 1874166440Spjd 1875349772Srmacklem if (suspend_nfsd != 0) 1876349772Srmacklem (void)nfssvc(NFSSVC_SUSPENDNFSD, NULL); 1877166440Spjd /* 1878349772Srmacklem * Delete the old V4 root dir. 1879166440Spjd */ 1880192934Srmacklem bzero(&eargs, sizeof (eargs)); 1881192934Srmacklem eargs.export.ex_flags = MNT_DELEXPORT; 1882282214Strasz if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 && 1883192934Srmacklem errno != ENOENT) 1884192934Srmacklem syslog(LOG_ERR, "Can't delete exports for V4:"); 1885192934Srmacklem 1886349772Srmacklem build_iovec(&iov, &iovlen, "fstype", NULL, 0); 1887349772Srmacklem build_iovec(&iov, &iovlen, "fspath", NULL, 0); 1888349772Srmacklem build_iovec(&iov, &iovlen, "from", NULL, 0); 1889349772Srmacklem build_iovec(&iov, &iovlen, "update", NULL, 0); 1890349772Srmacklem build_iovec(&iov, &iovlen, "export", &export, 1891349772Srmacklem sizeof(export)); 1892349772Srmacklem build_iovec(&iov, &iovlen, "errmsg", errmsg, 1893349772Srmacklem sizeof(errmsg)); 1894192934Srmacklem 1895192934Srmacklem /* 1896349772Srmacklem * For passno == 1, compare the old and new lists updating the kernel 1897349772Srmacklem * exports for any cases that have changed. 1898349772Srmacklem * This call is doing the second pass through the lists. 1899349772Srmacklem * If it fails, fall back on the bulk reload. 1900166440Spjd */ 1901349772Srmacklem if (passno == 1 && compare_nmount_exportlist(iov, iovlen, errmsg) == 1902349772Srmacklem 0) { 1903349772Srmacklem LOGDEBUG("compareok"); 1904349772Srmacklem /* Free up the old lists. */ 1905349772Srmacklem free_exports(oldexphead); 1906349772Srmacklem } else { 1907349772Srmacklem LOGDEBUG("doing passno=0"); 1908349772Srmacklem /* 1909349772Srmacklem * Clear flag that notes if a public fh has been exported. 1910349772Srmacklem * It is set by do_mount() if MNT_EXPUBLIC is set for the entry. 1911349772Srmacklem */ 1912349772Srmacklem has_publicfh = 0; 1913166440Spjd 1914349772Srmacklem /* exphead == NULL if not yet allocated (first call). */ 1915349772Srmacklem if (exphead != NULL) { 1916349772Srmacklem /* 1917349772Srmacklem * First, get rid of the old lists. 1918349772Srmacklem */ 1919349772Srmacklem free_exports(exphead); 1920349772Srmacklem free_exports(oldexphead); 1921349772Srmacklem } 1922349756Srmacklem 1923349772Srmacklem /* 1924349772Srmacklem * And delete exports that are in the kernel for all local 1925349772Srmacklem * filesystems. 1926349772Srmacklem * XXX: Should know how to handle all local exportable 1927349772Srmacklem * filesystems. 1928349772Srmacklem */ 1929349772Srmacklem num = getmntinfo(&mntbufp, MNT_NOWAIT); 1930349772Srmacklem 1931349772Srmacklem /* Allocate hash tables, for first call. */ 1932349772Srmacklem if (exphead == NULL) { 1933349772Srmacklem /* Target an average linked list length of 10. */ 1934349772Srmacklem exphashsize = num / 10; 1935349772Srmacklem if (exphashsize < 1) 1936349772Srmacklem exphashsize = 1; 1937349772Srmacklem else if (exphashsize > 100000) 1938349772Srmacklem exphashsize = 100000; 1939349772Srmacklem exphead = malloc(exphashsize * sizeof(*exphead)); 1940349772Srmacklem oldexphead = malloc(exphashsize * sizeof(*oldexphead)); 1941349772Srmacklem if (exphead == NULL || oldexphead == NULL) 1942349772Srmacklem errx(1, "Can't malloc hash tables"); 1943349772Srmacklem 1944349772Srmacklem for (i = 0; i < exphashsize; i++) { 1945349772Srmacklem SLIST_INIT(&exphead[i]); 1946349772Srmacklem SLIST_INIT(&oldexphead[i]); 1947349772Srmacklem } 1948349772Srmacklem } 1949349772Srmacklem 1950349772Srmacklem for (i = 0; i < num; i++) 1951349772Srmacklem delete_export(iov, iovlen, &mntbufp[i], errmsg); 1952349772Srmacklem 1953349772Srmacklem 1954349772Srmacklem /* Read the export file(s) and process them */ 1955349772Srmacklem read_exportfile(0); 1956349756Srmacklem } 1957166440Spjd 1958166440Spjd if (iov != NULL) { 1959166440Spjd /* Free strings allocated by strdup() in getmntopts.c */ 1960166440Spjd free(iov[0].iov_base); /* fstype */ 1961166440Spjd free(iov[2].iov_base); /* fspath */ 1962166440Spjd free(iov[4].iov_base); /* from */ 1963166440Spjd free(iov[6].iov_base); /* update */ 1964166440Spjd free(iov[8].iov_base); /* export */ 1965166440Spjd free(iov[10].iov_base); /* errmsg */ 1966166440Spjd 1967166440Spjd /* free iov, allocated by realloc() */ 1968166440Spjd free(iov); 1969166440Spjd iovlen = 0; 1970166440Spjd } 1971166440Spjd 1972192934Srmacklem /* 1973192934Srmacklem * If there was no public fh, clear any previous one set. 1974192934Srmacklem */ 1975349772Srmacklem if (has_publicfh == 0) { 1976349772Srmacklem LOGDEBUG("clear public fh"); 1977192934Srmacklem (void) nfssvc(NFSSVC_NOPUBLICFH, NULL); 1978349772Srmacklem } 1979241568Srmacklem 1980241568Srmacklem /* Resume the nfsd. If they weren't suspended, this is harmless. */ 1981241568Srmacklem (void)nfssvc(NFSSVC_RESUMENFSD, NULL); 1982349772Srmacklem LOGDEBUG("eo get_exportlist"); 1983166440Spjd} 1984166440Spjd 1985166440Spjd/* 1986349124Srmacklem * Insert an export entry in the appropriate list. 1987349124Srmacklem */ 1988349124Srmacklemstatic void 1989349124Srmackleminsert_exports(struct exportlist *ep, struct exportlisthead *exhp) 1990349124Srmacklem{ 1991349756Srmacklem uint32_t i; 1992349124Srmacklem 1993349756Srmacklem i = EXPHASH(&ep->ex_fs); 1994349772Srmacklem LOGDEBUG("fs=%s hash=%i", ep->ex_fsdir, i); 1995349756Srmacklem SLIST_INSERT_HEAD(&exhp[i], ep, entries); 1996349124Srmacklem} 1997349124Srmacklem 1998349124Srmacklem/* 1999349124Srmacklem * Free up the exports lists passed in as arguments. 2000349124Srmacklem */ 2001349124Srmacklemstatic void 2002349124Srmacklemfree_exports(struct exportlisthead *exhp) 2003349124Srmacklem{ 2004349124Srmacklem struct exportlist *ep, *ep2; 2005349756Srmacklem int i; 2006349124Srmacklem 2007349756Srmacklem for (i = 0; i < exphashsize; i++) { 2008349756Srmacklem SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) { 2009349756Srmacklem SLIST_REMOVE(&exhp[i], ep, exportlist, entries); 2010349756Srmacklem free_exp(ep); 2011349756Srmacklem } 2012349756Srmacklem SLIST_INIT(&exhp[i]); 2013349124Srmacklem } 2014349124Srmacklem} 2015349124Srmacklem 2016349124Srmacklem/* 2017349126Srmacklem * Read the exports file(s) and call get_exportlist_one() for each line. 2018349126Srmacklem */ 2019349126Srmacklemstatic void 2020349772Srmacklemread_exportfile(int passno) 2021349126Srmacklem{ 2022349126Srmacklem int done, i; 2023349126Srmacklem 2024349126Srmacklem /* 2025349126Srmacklem * Read in the exports file and build the list, calling 2026349126Srmacklem * nmount() as we go along to push the export rules into the kernel. 2027349126Srmacklem */ 2028349126Srmacklem done = 0; 2029349126Srmacklem for (i = 0; exnames[i] != NULL; i++) { 2030349126Srmacklem if (debug) 2031349126Srmacklem warnx("reading exports from %s", exnames[i]); 2032349126Srmacklem if ((exp_file = fopen(exnames[i], "r")) == NULL) { 2033349126Srmacklem syslog(LOG_WARNING, "can't open %s", exnames[i]); 2034349126Srmacklem continue; 2035349126Srmacklem } 2036349772Srmacklem get_exportlist_one(passno); 2037349126Srmacklem fclose(exp_file); 2038349126Srmacklem done++; 2039349126Srmacklem } 2040349126Srmacklem if (done == 0) { 2041349126Srmacklem syslog(LOG_ERR, "can't open any exports file"); 2042349126Srmacklem exit(2); 2043349126Srmacklem } 2044349126Srmacklem} 2045349126Srmacklem 2046349126Srmacklem/* 2047349772Srmacklem * Compare the export lists against the old ones and do nmount() operations 2048349772Srmacklem * for any cases that have changed. This avoids doing nmount() for entries 2049349772Srmacklem * that have not changed. 2050349772Srmacklem * Return 0 upon success, 1 otherwise. 2051349772Srmacklem */ 2052349772Srmacklemstatic int 2053349772Srmacklemcompare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg) 2054349772Srmacklem{ 2055349772Srmacklem struct exportlist *ep, *oep; 2056349772Srmacklem struct grouplist *grp; 2057349772Srmacklem struct statfs fs, ofs; 2058349772Srmacklem int i, ret; 2059349772Srmacklem 2060349772Srmacklem /* 2061349772Srmacklem * Loop through the current list and look for an entry in the old 2062349772Srmacklem * list. 2063349772Srmacklem * If found, check to see if it the same. 2064349772Srmacklem * If it is not the same, delete and re-export. 2065349772Srmacklem * Then mark it done on the old list. 2066349772Srmacklem * else (not found) 2067349772Srmacklem * export it. 2068349772Srmacklem * Any entries left in the old list after processing must have their 2069349772Srmacklem * exports deleted. 2070349772Srmacklem */ 2071349772Srmacklem for (i = 0; i < exphashsize; i++) 2072349772Srmacklem SLIST_FOREACH(ep, &exphead[i], entries) { 2073349772Srmacklem LOGDEBUG("foreach ep=%s", ep->ex_fsdir); 2074349772Srmacklem oep = ex_search(&ep->ex_fs, oldexphead); 2075349772Srmacklem if (oep != NULL) { 2076349772Srmacklem /* 2077349772Srmacklem * Check the mount paths are the same. 2078349772Srmacklem * If not, return 1 so that the reload of the 2079349772Srmacklem * exports will be done in bulk, the 2080349772Srmacklem * passno == 0 way. 2081349772Srmacklem */ 2082349772Srmacklem LOGDEBUG("found old exp"); 2083349772Srmacklem if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) 2084349772Srmacklem return (1); 2085349772Srmacklem LOGDEBUG("same fsdir"); 2086349772Srmacklem /* 2087349772Srmacklem * Test to see if the entry is the same. 2088349772Srmacklem * If not the same delete exports and 2089349772Srmacklem * re-export. 2090349772Srmacklem */ 2091349772Srmacklem if (compare_export(ep, oep) != 0) { 2092349772Srmacklem /* 2093349772Srmacklem * Clear has_publicfh if if was set 2094349772Srmacklem * in the old exports, but only if it 2095349772Srmacklem * has not been set during processing of 2096349772Srmacklem * the exports for this pass, as 2097349772Srmacklem * indicated by has_set_publicfh. 2098349772Srmacklem */ 2099349772Srmacklem if (has_set_publicfh == 0 && 2100349772Srmacklem (oep->ex_flag & EX_PUBLICFH) != 0) 2101349772Srmacklem has_publicfh = 0; 2102349772Srmacklem 2103349772Srmacklem /* Delete and re-export. */ 2104349772Srmacklem if (statfs(ep->ex_fsdir, &fs) < 0) 2105349772Srmacklem return (1); 2106349772Srmacklem delete_export(iov, iovlen, &fs, errmsg); 2107349772Srmacklem ret = do_export_mount(ep, &fs); 2108349772Srmacklem if (ret != 0) 2109349772Srmacklem return (ret); 2110349772Srmacklem } 2111349772Srmacklem oep->ex_flag |= EX_DONE; 2112349772Srmacklem LOGDEBUG("exdone"); 2113349772Srmacklem } else { 2114349772Srmacklem LOGDEBUG("not found so export"); 2115349772Srmacklem /* Not found, so do export. */ 2116349772Srmacklem if (statfs(ep->ex_fsdir, &fs) < 0) 2117349772Srmacklem return (1); 2118349772Srmacklem ret = do_export_mount(ep, &fs); 2119349772Srmacklem if (ret != 0) 2120349772Srmacklem return (ret); 2121349772Srmacklem } 2122349772Srmacklem } 2123349772Srmacklem 2124349772Srmacklem /* Delete exports not done. */ 2125349772Srmacklem for (i = 0; i < exphashsize; i++) 2126349772Srmacklem SLIST_FOREACH(oep, &oldexphead[i], entries) { 2127349772Srmacklem if ((oep->ex_flag & EX_DONE) == 0) { 2128349772Srmacklem LOGDEBUG("not done delete=%s", oep->ex_fsdir); 2129349772Srmacklem if (statfs(oep->ex_fsdir, &ofs) >= 0 && 2130349772Srmacklem oep->ex_fs.val[0] == ofs.f_fsid.val[0] && 2131349772Srmacklem oep->ex_fs.val[1] == ofs.f_fsid.val[1]) { 2132349772Srmacklem LOGDEBUG("do delete"); 2133349772Srmacklem /* 2134349772Srmacklem * Clear has_publicfh if if was set 2135349772Srmacklem * in the old exports, but only if it 2136349772Srmacklem * has not been set during processing of 2137349772Srmacklem * the exports for this pass, as 2138349772Srmacklem * indicated by has_set_publicfh. 2139349772Srmacklem */ 2140349772Srmacklem if (has_set_publicfh == 0 && 2141349772Srmacklem (oep->ex_flag & EX_PUBLICFH) != 0) 2142349772Srmacklem has_publicfh = 0; 2143349772Srmacklem 2144349772Srmacklem delete_export(iov, iovlen, &ofs, 2145349772Srmacklem errmsg); 2146349772Srmacklem } 2147349772Srmacklem } 2148349772Srmacklem } 2149349772Srmacklem 2150349772Srmacklem /* Do the V4 root exports, as required. */ 2151349772Srmacklem grp = NULL; 2152349772Srmacklem if (v4root_ep != NULL) 2153349772Srmacklem grp = v4root_ep->ex_grphead; 2154349772Srmacklem v4root_phase = 2; 2155349772Srmacklem while (v4root_ep != NULL && grp != NULL) { 2156349772Srmacklem LOGDEBUG("v4root expath=%s", v4root_dirpath); 2157349772Srmacklem ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon, 2158349772Srmacklem v4root_dirpath, strlen(v4root_dirpath), &fs, 2159349772Srmacklem grp->gr_numsecflavors, grp->gr_secflavors); 2160349772Srmacklem if (ret != 0) { 2161349772Srmacklem v4root_phase = 0; 2162349772Srmacklem return (ret); 2163349772Srmacklem } 2164349772Srmacklem grp = grp->gr_next; 2165349772Srmacklem } 2166349772Srmacklem v4root_phase = 0; 2167349772Srmacklem free_v4rootexp(); 2168349772Srmacklem return (0); 2169349772Srmacklem} 2170349772Srmacklem 2171349772Srmacklem/* 2172349772Srmacklem * Compare old and current exportlist entries for the fsid and return 0 2173349772Srmacklem * if they are the same, 1 otherwise. 2174349772Srmacklem */ 2175349772Srmacklemstatic int 2176349772Srmacklemcompare_export(struct exportlist *ep, struct exportlist *oep) 2177349772Srmacklem{ 2178349772Srmacklem struct grouplist *grp, *ogrp; 2179349772Srmacklem 2180349772Srmacklem if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) 2181349772Srmacklem return (1); 2182349772Srmacklem if ((ep->ex_flag & EX_DEFSET) != (oep->ex_flag & EX_DEFSET)) 2183349772Srmacklem return (1); 2184349772Srmacklem if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) || 2185349772Srmacklem (ep->ex_defdir == NULL && oep->ex_defdir != NULL)) 2186349772Srmacklem return (1); 2187349772Srmacklem if (ep->ex_defdir != NULL && (ep->ex_defdir->dp_flag & DP_DEFSET) != 2188349772Srmacklem (oep->ex_defdir->dp_flag & DP_DEFSET)) 2189349772Srmacklem return (1); 2190349772Srmacklem if ((ep->ex_flag & EX_DEFSET) != 0 && (ep->ex_defnumsecflavors != 2191349772Srmacklem oep->ex_defnumsecflavors || ep->ex_defexflags != 2192349772Srmacklem oep->ex_defexflags || compare_cred(&ep->ex_defanon, 2193349772Srmacklem &oep->ex_defanon) != 0 || compare_secflavor(ep->ex_defsecflavors, 2194349772Srmacklem oep->ex_defsecflavors, ep->ex_defnumsecflavors) != 0)) 2195349772Srmacklem return (1); 2196349772Srmacklem 2197349772Srmacklem /* Now, check all the groups. */ 2198349772Srmacklem for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) 2199349772Srmacklem ogrp->gr_flag = 0; 2200349772Srmacklem for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) { 2201349772Srmacklem for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = 2202349772Srmacklem ogrp->gr_next) 2203349772Srmacklem if ((ogrp->gr_flag & GR_FND) == 0 && 2204349772Srmacklem grp->gr_numsecflavors == ogrp->gr_numsecflavors && 2205349772Srmacklem grp->gr_exflags == ogrp->gr_exflags && 2206349772Srmacklem compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 && 2207349772Srmacklem compare_secflavor(grp->gr_secflavors, 2208349772Srmacklem ogrp->gr_secflavors, grp->gr_numsecflavors) == 0) 2209349772Srmacklem break; 2210349772Srmacklem if (ogrp != NULL) 2211349772Srmacklem ogrp->gr_flag |= GR_FND; 2212349772Srmacklem else 2213349772Srmacklem return (1); 2214349772Srmacklem } 2215349772Srmacklem for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) 2216349772Srmacklem if ((ogrp->gr_flag & GR_FND) == 0) 2217349772Srmacklem return (1); 2218349772Srmacklem return (0); 2219349772Srmacklem} 2220349772Srmacklem 2221349772Srmacklem/* 2222349772Srmacklem * This algorithm compares two arrays of "n" items. It returns 0 if they are 2223349772Srmacklem * the "same" and 1 otherwise. Although suboptimal, it is always safe to 2224349772Srmacklem * return 1, which makes compare_nmount_export() reload the exports entry. 2225349772Srmacklem * "same" refers to having the same set of values in the two arrays. 2226349772Srmacklem * The arrays are in no particular order and duplicates (multiple entries 2227349772Srmacklem * in an array with the same value) is allowed. 2228349772Srmacklem * The algorithm is inefficient, but the common case of indentical arrays is 2229349772Srmacklem * handled first and "n" is normally fairly small. 2230349772Srmacklem * Since the two functions need the same algorithm but for arrays of 2231349772Srmacklem * different types (gid_t vs int), this is done as a macro. 2232349772Srmacklem */ 2233349772Srmacklem#define COMPARE_ARRAYS(a1, a2, n) \ 2234349772Srmacklem do { \ 2235349772Srmacklem int fnd, fndarray[(n)], i, j; \ 2236349772Srmacklem /* Handle common case of identical arrays. */ \ 2237349772Srmacklem for (i = 0; i < (n); i++) \ 2238349772Srmacklem if ((a1)[i] != (a2)[i]) \ 2239349772Srmacklem break; \ 2240349772Srmacklem if (i == (n)) \ 2241349772Srmacklem return (0); \ 2242349772Srmacklem for (i = 0; i < (n); i++) \ 2243349772Srmacklem fndarray[i] = 0; \ 2244349772Srmacklem for (i = 0; i < (n); i++) { \ 2245349772Srmacklem fnd = 0; \ 2246349772Srmacklem for (j = 0; j < (n); j++) { \ 2247349772Srmacklem if ((a1)[i] == (a2)[j]) { \ 2248349772Srmacklem fndarray[j] = 1; \ 2249349772Srmacklem fnd = 1; \ 2250349772Srmacklem } \ 2251349772Srmacklem } \ 2252349772Srmacklem if (fnd == 0) \ 2253349772Srmacklem return (1); \ 2254349772Srmacklem } \ 2255349772Srmacklem for (i = 0; i < (n); i++) \ 2256349772Srmacklem if (fndarray[i] == 0) \ 2257349772Srmacklem return (1); \ 2258349772Srmacklem return (0); \ 2259349772Srmacklem } while (0) 2260349772Srmacklem 2261349772Srmacklem/* 2262349772Srmacklem * Compare to struct xucred's. Return 0 if the same and 1 otherwise. 2263349772Srmacklem */ 2264349772Srmacklemstatic int 2265349772Srmacklemcompare_cred(struct xucred *cr0, struct xucred *cr1) 2266349772Srmacklem{ 2267349772Srmacklem 2268349772Srmacklem if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups) 2269349772Srmacklem return (1); 2270349772Srmacklem 2271349772Srmacklem COMPARE_ARRAYS(cr0->cr_groups, cr1->cr_groups, cr0->cr_ngroups); 2272349772Srmacklem} 2273349772Srmacklem 2274349772Srmacklem/* 2275349772Srmacklem * Compare two lists of security flavors. Return 0 if the same and 1 otherwise. 2276349772Srmacklem */ 2277349772Srmacklemstatic int 2278349772Srmacklemcompare_secflavor(int *sec1, int *sec2, int nsec) 2279349772Srmacklem{ 2280349772Srmacklem 2281349772Srmacklem COMPARE_ARRAYS(sec1, sec2, nsec); 2282349772Srmacklem} 2283349772Srmacklem 2284349772Srmacklem/* 2285349126Srmacklem * Delete an exports entry. 2286349126Srmacklem */ 2287349126Srmacklemstatic void 2288349126Srmacklemdelete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg) 2289349126Srmacklem{ 2290349126Srmacklem struct xvfsconf vfc; 2291349126Srmacklem 2292349126Srmacklem if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) { 2293349126Srmacklem syslog(LOG_ERR, "getvfsbyname() failed for %s", 2294349126Srmacklem fsp->f_fstypename); 2295349126Srmacklem return; 2296349126Srmacklem } 2297349126Srmacklem 2298349126Srmacklem /* 2299349126Srmacklem * We do not need to delete "export" flag from 2300349126Srmacklem * filesystems that do not have it set. 2301349126Srmacklem */ 2302349126Srmacklem if (!(fsp->f_flags & MNT_EXPORTED)) 2303349126Srmacklem return; 2304349126Srmacklem /* 2305349126Srmacklem * Do not delete export for network filesystem by 2306349126Srmacklem * passing "export" arg to nmount(). 2307349126Srmacklem * It only makes sense to do this for local filesystems. 2308349126Srmacklem */ 2309349126Srmacklem if (vfc.vfc_flags & VFCF_NETWORK) 2310349126Srmacklem return; 2311349126Srmacklem 2312349126Srmacklem iov[1].iov_base = fsp->f_fstypename; 2313349126Srmacklem iov[1].iov_len = strlen(fsp->f_fstypename) + 1; 2314349126Srmacklem iov[3].iov_base = fsp->f_mntonname; 2315349126Srmacklem iov[3].iov_len = strlen(fsp->f_mntonname) + 1; 2316349126Srmacklem iov[5].iov_base = fsp->f_mntfromname; 2317349126Srmacklem iov[5].iov_len = strlen(fsp->f_mntfromname) + 1; 2318349126Srmacklem errmsg[0] = '\0'; 2319349126Srmacklem 2320349126Srmacklem /* 2321349126Srmacklem * EXDEV is returned when path exists but is not a 2322349126Srmacklem * mount point. May happens if raced with unmount. 2323349126Srmacklem */ 2324349126Srmacklem if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT && 2325349126Srmacklem errno != ENOTSUP && errno != EXDEV) { 2326349126Srmacklem syslog(LOG_ERR, 2327349126Srmacklem "can't delete exports for %s: %m %s", 2328349126Srmacklem fsp->f_mntonname, errmsg); 2329349126Srmacklem } 2330349126Srmacklem} 2331349126Srmacklem 2332349126Srmacklem/* 23331558Srgrimes * Allocate an export list element 23341558Srgrimes */ 2335285128Straszstatic struct exportlist * 2336216587Scharnierget_exp(void) 23371558Srgrimes{ 23381558Srgrimes struct exportlist *ep; 23391558Srgrimes 2340224003Sdelphij ep = (struct exportlist *)calloc(1, sizeof (struct exportlist)); 23411558Srgrimes if (ep == (struct exportlist *)NULL) 23421558Srgrimes out_of_mem(); 23431558Srgrimes return (ep); 23441558Srgrimes} 23451558Srgrimes 23461558Srgrimes/* 23471558Srgrimes * Allocate a group list element 23481558Srgrimes */ 2349285128Straszstatic struct grouplist * 2350216587Scharnierget_grp(void) 23511558Srgrimes{ 23521558Srgrimes struct grouplist *gp; 23531558Srgrimes 2354224003Sdelphij gp = (struct grouplist *)calloc(1, sizeof (struct grouplist)); 23551558Srgrimes if (gp == (struct grouplist *)NULL) 23561558Srgrimes out_of_mem(); 23571558Srgrimes return (gp); 23581558Srgrimes} 23591558Srgrimes 23601558Srgrimes/* 23611558Srgrimes * Clean up upon an error in get_exportlist(). 23621558Srgrimes */ 2363285128Straszstatic void 2364329392Sbrdgetexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason) 23651558Srgrimes{ 23661558Srgrimes struct grouplist *tgrp; 23671558Srgrimes 2368329392Sbrd if (!(opt_flags & OP_QUIET)) { 2369329392Sbrd if (reason != NULL) 2370329392Sbrd syslog(LOG_ERR, "bad exports list line '%s': %s", line, 2371329392Sbrd reason); 2372329392Sbrd else 2373329392Sbrd syslog(LOG_ERR, "bad exports list line '%s'", line); 2374329392Sbrd } 23751558Srgrimes if (ep && (ep->ex_flag & EX_LINKED) == 0) 23761558Srgrimes free_exp(ep); 23771558Srgrimes while (grp) { 23781558Srgrimes tgrp = grp; 23791558Srgrimes grp = grp->gr_next; 23801558Srgrimes free_grp(tgrp); 23811558Srgrimes } 23821558Srgrimes} 23831558Srgrimes 23841558Srgrimes/* 23851558Srgrimes * Search the export list for a matching fs. 23861558Srgrimes */ 2387285128Straszstatic struct exportlist * 2388349124Srmacklemex_search(fsid_t *fsid, struct exportlisthead *exhp) 23891558Srgrimes{ 23901558Srgrimes struct exportlist *ep; 2391349756Srmacklem uint32_t i; 23921558Srgrimes 2393349756Srmacklem i = EXPHASH(fsid); 2394349756Srmacklem SLIST_FOREACH(ep, &exhp[i], entries) { 23951558Srgrimes if (ep->ex_fs.val[0] == fsid->val[0] && 23961558Srgrimes ep->ex_fs.val[1] == fsid->val[1]) 23971558Srgrimes return (ep); 23981558Srgrimes } 2399324955Smanu 24001558Srgrimes return (ep); 24011558Srgrimes} 24021558Srgrimes 24031558Srgrimes/* 24041558Srgrimes * Add a directory path to the list. 24051558Srgrimes */ 2406285128Straszstatic char * 2407216587Scharnieradd_expdir(struct dirlist **dpp, char *cp, int len) 24081558Srgrimes{ 24091558Srgrimes struct dirlist *dp; 24101558Srgrimes 2411324234Smanu dp = malloc(sizeof (struct dirlist)); 241237663Scharnier if (dp == (struct dirlist *)NULL) 241337663Scharnier out_of_mem(); 24141558Srgrimes dp->dp_left = *dpp; 24151558Srgrimes dp->dp_right = (struct dirlist *)NULL; 24161558Srgrimes dp->dp_flag = 0; 24171558Srgrimes dp->dp_hosts = (struct hostlist *)NULL; 2418324234Smanu dp->dp_dirp = strndup(cp, len); 2419324234Smanu if (dp->dp_dirp == NULL) 2420324234Smanu out_of_mem(); 24211558Srgrimes *dpp = dp; 24221558Srgrimes return (dp->dp_dirp); 24231558Srgrimes} 24241558Srgrimes 24251558Srgrimes/* 24261558Srgrimes * Hang the dir list element off the dirpath binary tree as required 24271558Srgrimes * and update the entry for host. 24281558Srgrimes */ 2429285128Straszstatic void 2430216587Scharnierhang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, 2431349772Srmacklem int flags, struct xucred *anoncrp, int exflags) 24321558Srgrimes{ 24331558Srgrimes struct hostlist *hp; 24341558Srgrimes struct dirlist *dp2; 24351558Srgrimes 24369336Sdfr if (flags & OP_ALLDIRS) { 24371558Srgrimes if (ep->ex_defdir) 24381558Srgrimes free((caddr_t)dp); 24391558Srgrimes else 24401558Srgrimes ep->ex_defdir = dp; 24419336Sdfr if (grp == (struct grouplist *)NULL) { 2442349772Srmacklem ep->ex_flag |= EX_DEFSET; 24431558Srgrimes ep->ex_defdir->dp_flag |= DP_DEFSET; 2444240902Srmacklem /* Save the default security flavors list. */ 2445240902Srmacklem ep->ex_defnumsecflavors = ep->ex_numsecflavors; 2446240902Srmacklem if (ep->ex_numsecflavors > 0) 2447240902Srmacklem memcpy(ep->ex_defsecflavors, ep->ex_secflavors, 2448240902Srmacklem sizeof(ep->ex_secflavors)); 2449349772Srmacklem ep->ex_defanon = *anoncrp; 2450349772Srmacklem ep->ex_defexflags = exflags; 24519336Sdfr } else while (grp) { 24521558Srgrimes hp = get_ht(); 24531558Srgrimes hp->ht_grp = grp; 24541558Srgrimes hp->ht_next = ep->ex_defdir->dp_hosts; 24551558Srgrimes ep->ex_defdir->dp_hosts = hp; 2456240902Srmacklem /* Save the security flavors list for this host set. */ 2457240902Srmacklem grp->gr_numsecflavors = ep->ex_numsecflavors; 2458240902Srmacklem if (ep->ex_numsecflavors > 0) 2459240902Srmacklem memcpy(grp->gr_secflavors, ep->ex_secflavors, 2460240902Srmacklem sizeof(ep->ex_secflavors)); 24611558Srgrimes grp = grp->gr_next; 24621558Srgrimes } 24631558Srgrimes } else { 24641558Srgrimes 24651558Srgrimes /* 246637663Scharnier * Loop through the directories adding them to the tree. 24671558Srgrimes */ 24681558Srgrimes while (dp) { 24691558Srgrimes dp2 = dp->dp_left; 2470349772Srmacklem add_dlist(&ep->ex_dirl, dp, grp, flags, ep, anoncrp, 2471349772Srmacklem exflags); 24721558Srgrimes dp = dp2; 24731558Srgrimes } 24741558Srgrimes } 24751558Srgrimes} 24761558Srgrimes 24771558Srgrimes/* 24781558Srgrimes * Traverse the binary tree either updating a node that is already there 24791558Srgrimes * for the new directory or adding the new node. 24801558Srgrimes */ 2481285128Straszstatic void 2482216587Scharnieradd_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, 2483349772Srmacklem int flags, struct exportlist *ep, struct xucred *anoncrp, int exflags) 24841558Srgrimes{ 24851558Srgrimes struct dirlist *dp; 24861558Srgrimes struct hostlist *hp; 24871558Srgrimes int cmp; 24881558Srgrimes 24891558Srgrimes dp = *dpp; 24901558Srgrimes if (dp) { 24911558Srgrimes cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); 24921558Srgrimes if (cmp > 0) { 2493349772Srmacklem add_dlist(&dp->dp_left, newdp, grp, flags, ep, anoncrp, 2494349772Srmacklem exflags); 24951558Srgrimes return; 24961558Srgrimes } else if (cmp < 0) { 2497349772Srmacklem add_dlist(&dp->dp_right, newdp, grp, flags, ep, anoncrp, 2498349772Srmacklem exflags); 24991558Srgrimes return; 25001558Srgrimes } else 25011558Srgrimes free((caddr_t)newdp); 25021558Srgrimes } else { 25031558Srgrimes dp = newdp; 25041558Srgrimes dp->dp_left = (struct dirlist *)NULL; 25051558Srgrimes *dpp = dp; 25061558Srgrimes } 25071558Srgrimes if (grp) { 25081558Srgrimes 25091558Srgrimes /* 25101558Srgrimes * Hang all of the host(s) off of the directory point. 25111558Srgrimes */ 25121558Srgrimes do { 25131558Srgrimes hp = get_ht(); 25141558Srgrimes hp->ht_grp = grp; 25151558Srgrimes hp->ht_next = dp->dp_hosts; 25161558Srgrimes dp->dp_hosts = hp; 2517240902Srmacklem /* Save the security flavors list for this host set. */ 2518240902Srmacklem grp->gr_numsecflavors = ep->ex_numsecflavors; 2519240902Srmacklem if (ep->ex_numsecflavors > 0) 2520240902Srmacklem memcpy(grp->gr_secflavors, ep->ex_secflavors, 2521240902Srmacklem sizeof(ep->ex_secflavors)); 25221558Srgrimes grp = grp->gr_next; 25231558Srgrimes } while (grp); 25249336Sdfr } else { 2525349772Srmacklem ep->ex_flag |= EX_DEFSET; 25261558Srgrimes dp->dp_flag |= DP_DEFSET; 2527240902Srmacklem /* Save the default security flavors list. */ 2528240902Srmacklem ep->ex_defnumsecflavors = ep->ex_numsecflavors; 2529240902Srmacklem if (ep->ex_numsecflavors > 0) 2530240902Srmacklem memcpy(ep->ex_defsecflavors, ep->ex_secflavors, 2531240902Srmacklem sizeof(ep->ex_secflavors)); 2532349772Srmacklem ep->ex_defanon = *anoncrp; 2533349772Srmacklem ep->ex_defexflags = exflags; 25349336Sdfr } 25351558Srgrimes} 25361558Srgrimes 25371558Srgrimes/* 25381558Srgrimes * Search for a dirpath on the export point. 25391558Srgrimes */ 2540285128Straszstatic struct dirlist * 2541216587Scharnierdirp_search(struct dirlist *dp, char *dirp) 25421558Srgrimes{ 25431558Srgrimes int cmp; 25441558Srgrimes 25451558Srgrimes if (dp) { 254674462Salfred cmp = strcmp(dp->dp_dirp, dirp); 25471558Srgrimes if (cmp > 0) 254874462Salfred return (dirp_search(dp->dp_left, dirp)); 25491558Srgrimes else if (cmp < 0) 255074462Salfred return (dirp_search(dp->dp_right, dirp)); 25511558Srgrimes else 25521558Srgrimes return (dp); 25531558Srgrimes } 25541558Srgrimes return (dp); 25551558Srgrimes} 25561558Srgrimes 25571558Srgrimes/* 25581558Srgrimes * Scan for a host match in a directory tree. 25591558Srgrimes */ 2560285128Straszstatic int 2561216587Scharnierchk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp, 2562240902Srmacklem int *hostsetp, int *numsecflavors, int **secflavorsp) 25631558Srgrimes{ 25641558Srgrimes struct hostlist *hp; 25651558Srgrimes struct grouplist *grp; 256674462Salfred struct addrinfo *ai; 25671558Srgrimes 25681558Srgrimes if (dp) { 25691558Srgrimes if (dp->dp_flag & DP_DEFSET) 25709336Sdfr *defsetp = dp->dp_flag; 25711558Srgrimes hp = dp->dp_hosts; 25721558Srgrimes while (hp) { 25731558Srgrimes grp = hp->ht_grp; 25741558Srgrimes switch (grp->gr_type) { 25751558Srgrimes case GT_HOST: 257674462Salfred ai = grp->gr_ptr.gt_addrinfo; 257774462Salfred for (; ai; ai = ai->ai_next) { 257875801Siedowse if (!sacmp(ai->ai_addr, saddr, NULL)) { 257974462Salfred *hostsetp = 258074462Salfred (hp->ht_flag | DP_HOSTSET); 2581240902Srmacklem if (numsecflavors != NULL) { 2582240902Srmacklem *numsecflavors = 2583240902Srmacklem grp->gr_numsecflavors; 2584240902Srmacklem *secflavorsp = 2585240902Srmacklem grp->gr_secflavors; 2586240902Srmacklem } 258774462Salfred return (1); 258874462Salfred } 25899336Sdfr } 259075801Siedowse break; 25911558Srgrimes case GT_NET: 259275801Siedowse if (!sacmp(saddr, (struct sockaddr *) 259375801Siedowse &grp->gr_ptr.gt_net.nt_net, 259475801Siedowse (struct sockaddr *) 259575801Siedowse &grp->gr_ptr.gt_net.nt_mask)) { 259674462Salfred *hostsetp = (hp->ht_flag | DP_HOSTSET); 2597240902Srmacklem if (numsecflavors != NULL) { 2598240902Srmacklem *numsecflavors = 2599240902Srmacklem grp->gr_numsecflavors; 2600240902Srmacklem *secflavorsp = 2601240902Srmacklem grp->gr_secflavors; 2602240902Srmacklem } 260374462Salfred return (1); 260474462Salfred } 260575801Siedowse break; 260675801Siedowse } 26071558Srgrimes hp = hp->ht_next; 26081558Srgrimes } 26091558Srgrimes } 26101558Srgrimes return (0); 26111558Srgrimes} 26121558Srgrimes 26131558Srgrimes/* 26141558Srgrimes * Scan tree for a host that matches the address. 26151558Srgrimes */ 2616285128Straszstatic int 2617216587Scharnierscan_tree(struct dirlist *dp, struct sockaddr *saddr) 26181558Srgrimes{ 26199336Sdfr int defset, hostset; 26201558Srgrimes 26211558Srgrimes if (dp) { 26221558Srgrimes if (scan_tree(dp->dp_left, saddr)) 26231558Srgrimes return (1); 2624240902Srmacklem if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL)) 26251558Srgrimes return (1); 26261558Srgrimes if (scan_tree(dp->dp_right, saddr)) 26271558Srgrimes return (1); 26281558Srgrimes } 26291558Srgrimes return (0); 26301558Srgrimes} 26311558Srgrimes 26321558Srgrimes/* 26331558Srgrimes * Traverse the dirlist tree and free it up. 26341558Srgrimes */ 2635285128Straszstatic void 2636216587Scharnierfree_dir(struct dirlist *dp) 26371558Srgrimes{ 26381558Srgrimes 26391558Srgrimes if (dp) { 26401558Srgrimes free_dir(dp->dp_left); 26411558Srgrimes free_dir(dp->dp_right); 26421558Srgrimes free_host(dp->dp_hosts); 2643324234Smanu free(dp->dp_dirp); 2644324234Smanu free(dp); 26451558Srgrimes } 26461558Srgrimes} 26471558Srgrimes 26481558Srgrimes/* 2649184588Sdfr * Parse a colon separated list of security flavors 2650184588Sdfr */ 2651285128Straszstatic int 2652216587Scharnierparsesec(char *seclist, struct exportlist *ep) 2653184588Sdfr{ 2654184588Sdfr char *cp, savedc; 2655184588Sdfr int flavor; 2656184588Sdfr 2657184588Sdfr ep->ex_numsecflavors = 0; 2658184588Sdfr for (;;) { 2659184588Sdfr cp = strchr(seclist, ':'); 2660184588Sdfr if (cp) { 2661184588Sdfr savedc = *cp; 2662184588Sdfr *cp = '\0'; 2663184588Sdfr } 2664184588Sdfr 2665184588Sdfr if (!strcmp(seclist, "sys")) 2666184588Sdfr flavor = AUTH_SYS; 2667184588Sdfr else if (!strcmp(seclist, "krb5")) 2668184588Sdfr flavor = RPCSEC_GSS_KRB5; 2669184588Sdfr else if (!strcmp(seclist, "krb5i")) 2670184588Sdfr flavor = RPCSEC_GSS_KRB5I; 2671184588Sdfr else if (!strcmp(seclist, "krb5p")) 2672184588Sdfr flavor = RPCSEC_GSS_KRB5P; 2673184588Sdfr else { 2674184588Sdfr if (cp) 2675184588Sdfr *cp = savedc; 2676184588Sdfr syslog(LOG_ERR, "bad sec flavor: %s", seclist); 2677184588Sdfr return (1); 2678184588Sdfr } 2679184588Sdfr if (ep->ex_numsecflavors == MAXSECFLAVORS) { 2680184588Sdfr if (cp) 2681184588Sdfr *cp = savedc; 2682184588Sdfr syslog(LOG_ERR, "too many sec flavors: %s", seclist); 2683184588Sdfr return (1); 2684184588Sdfr } 2685184588Sdfr ep->ex_secflavors[ep->ex_numsecflavors] = flavor; 2686184588Sdfr ep->ex_numsecflavors++; 2687184588Sdfr if (cp) { 2688184588Sdfr *cp = savedc; 2689184588Sdfr seclist = cp + 1; 2690184588Sdfr } else { 2691184588Sdfr break; 2692184588Sdfr } 2693184588Sdfr } 2694184588Sdfr return (0); 2695184588Sdfr} 2696184588Sdfr 2697184588Sdfr/* 26981558Srgrimes * Parse the option string and update fields. 26991558Srgrimes * Option arguments may either be -<option>=<value> or 27001558Srgrimes * -<option> <value> 27011558Srgrimes */ 2702285128Straszstatic int 2703216587Scharnierdo_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp, 2704216587Scharnier int *has_hostp, int *exflagsp, struct xucred *cr) 27051558Srgrimes{ 27061558Srgrimes char *cpoptarg, *cpoptend; 27071558Srgrimes char *cp, *endcp, *cpopt, savedc, savedc2; 27081558Srgrimes int allflag, usedarg; 27091558Srgrimes 271051968Salfred savedc2 = '\0'; 27111558Srgrimes cpopt = *cpp; 27121558Srgrimes cpopt++; 27131558Srgrimes cp = *endcpp; 27141558Srgrimes savedc = *cp; 27151558Srgrimes *cp = '\0'; 27161558Srgrimes while (cpopt && *cpopt) { 27171558Srgrimes allflag = 1; 27181558Srgrimes usedarg = -2; 271937663Scharnier if ((cpoptend = strchr(cpopt, ','))) { 27201558Srgrimes *cpoptend++ = '\0'; 272137663Scharnier if ((cpoptarg = strchr(cpopt, '='))) 27221558Srgrimes *cpoptarg++ = '\0'; 27231558Srgrimes } else { 272437663Scharnier if ((cpoptarg = strchr(cpopt, '='))) 27251558Srgrimes *cpoptarg++ = '\0'; 27261558Srgrimes else { 27271558Srgrimes *cp = savedc; 27281558Srgrimes nextfield(&cp, &endcp); 27291558Srgrimes **endcpp = '\0'; 27301558Srgrimes if (endcp > cp && *cp != '-') { 27311558Srgrimes cpoptarg = cp; 27321558Srgrimes savedc2 = *endcp; 27331558Srgrimes *endcp = '\0'; 27341558Srgrimes usedarg = 0; 27351558Srgrimes } 27361558Srgrimes } 27371558Srgrimes } 27381558Srgrimes if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { 27391558Srgrimes *exflagsp |= MNT_EXRDONLY; 27401558Srgrimes } else if (cpoptarg && (!strcmp(cpopt, "maproot") || 27411558Srgrimes !(allflag = strcmp(cpopt, "mapall")) || 27421558Srgrimes !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { 27431558Srgrimes usedarg++; 27441558Srgrimes parsecred(cpoptarg, cr); 27451558Srgrimes if (allflag == 0) { 27461558Srgrimes *exflagsp |= MNT_EXPORTANON; 27471558Srgrimes opt_flags |= OP_MAPALL; 27481558Srgrimes } else 27491558Srgrimes opt_flags |= OP_MAPROOT; 27501558Srgrimes } else if (cpoptarg && (!strcmp(cpopt, "mask") || 275175801Siedowse !strcmp(cpopt, "m"))) { 27521558Srgrimes if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) { 275337663Scharnier syslog(LOG_ERR, "bad mask: %s", cpoptarg); 27541558Srgrimes return (1); 27551558Srgrimes } 27561558Srgrimes usedarg++; 27571558Srgrimes opt_flags |= OP_MASK; 27581558Srgrimes } else if (cpoptarg && (!strcmp(cpopt, "network") || 27591558Srgrimes !strcmp(cpopt, "n"))) { 276074462Salfred if (strchr(cpoptarg, '/') != NULL) { 276174462Salfred if (debug) 276274462Salfred fprintf(stderr, "setting OP_MASKLEN\n"); 276374462Salfred opt_flags |= OP_MASKLEN; 276474462Salfred } 27651558Srgrimes if (grp->gr_type != GT_NULL) { 276637663Scharnier syslog(LOG_ERR, "network/host conflict"); 27671558Srgrimes return (1); 27681558Srgrimes } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) { 276937663Scharnier syslog(LOG_ERR, "bad net: %s", cpoptarg); 27701558Srgrimes return (1); 27711558Srgrimes } 27721558Srgrimes grp->gr_type = GT_NET; 27731558Srgrimes *has_hostp = 1; 27741558Srgrimes usedarg++; 27751558Srgrimes opt_flags |= OP_NET; 27761558Srgrimes } else if (!strcmp(cpopt, "alldirs")) { 27771558Srgrimes opt_flags |= OP_ALLDIRS; 277827447Sdfr } else if (!strcmp(cpopt, "public")) { 277927447Sdfr *exflagsp |= MNT_EXPUBLIC; 278027447Sdfr } else if (!strcmp(cpopt, "webnfs")) { 278127447Sdfr *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON); 278227447Sdfr opt_flags |= OP_MAPALL; 278327447Sdfr } else if (cpoptarg && !strcmp(cpopt, "index")) { 278427447Sdfr ep->ex_indexfile = strdup(cpoptarg); 2785100336Sjoerg } else if (!strcmp(cpopt, "quiet")) { 2786100336Sjoerg opt_flags |= OP_QUIET; 2787247034Spluknet } else if (cpoptarg && !strcmp(cpopt, "sec")) { 2788184588Sdfr if (parsesec(cpoptarg, ep)) 2789184588Sdfr return (1); 2790184588Sdfr opt_flags |= OP_SEC; 2791184588Sdfr usedarg++; 27921558Srgrimes } else { 279337663Scharnier syslog(LOG_ERR, "bad opt %s", cpopt); 27941558Srgrimes return (1); 27951558Srgrimes } 27961558Srgrimes if (usedarg >= 0) { 27971558Srgrimes *endcp = savedc2; 27981558Srgrimes **endcpp = savedc; 27991558Srgrimes if (usedarg > 0) { 28001558Srgrimes *cpp = cp; 28011558Srgrimes *endcpp = endcp; 28021558Srgrimes } 28031558Srgrimes return (0); 28041558Srgrimes } 28051558Srgrimes cpopt = cpoptend; 28061558Srgrimes } 28071558Srgrimes **endcpp = savedc; 28081558Srgrimes return (0); 28091558Srgrimes} 28101558Srgrimes 28111558Srgrimes/* 28121558Srgrimes * Translate a character string to the corresponding list of network 28131558Srgrimes * addresses for a hostname. 28141558Srgrimes */ 2815285128Straszstatic int 2816216587Scharnierget_host(char *cp, struct grouplist *grp, struct grouplist *tgrp) 28171558Srgrimes{ 28187401Swpaul struct grouplist *checkgrp; 281975635Siedowse struct addrinfo *ai, *tai, hints; 282074462Salfred int ecode; 282174462Salfred char host[NI_MAXHOST]; 28221558Srgrimes 282374462Salfred if (grp->gr_type != GT_NULL) { 282474462Salfred syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp); 28251558Srgrimes return (1); 28261558Srgrimes } 282774462Salfred memset(&hints, 0, sizeof hints); 282874462Salfred hints.ai_flags = AI_CANONNAME; 282974462Salfred hints.ai_protocol = IPPROTO_UDP; 283074462Salfred ecode = getaddrinfo(cp, NULL, &hints, &ai); 283174462Salfred if (ecode != 0) { 283275635Siedowse syslog(LOG_ERR,"can't get address info for host %s", cp); 283374462Salfred return 1; 283474462Salfred } 283574462Salfred grp->gr_ptr.gt_addrinfo = ai; 283674462Salfred while (ai != NULL) { 283774462Salfred if (ai->ai_canonname == NULL) { 283874462Salfred if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host, 2839146187Sume sizeof host, NULL, 0, NI_NUMERICHOST) != 0) 284074462Salfred strlcpy(host, "?", sizeof(host)); 284174462Salfred ai->ai_canonname = strdup(host); 284274462Salfred ai->ai_flags |= AI_CANONNAME; 284375641Siedowse } 284474462Salfred if (debug) 284575635Siedowse fprintf(stderr, "got host %s\n", ai->ai_canonname); 284675635Siedowse /* 284775635Siedowse * Sanity check: make sure we don't already have an entry 284875635Siedowse * for this host in the grouplist. 284975635Siedowse */ 285075635Siedowse for (checkgrp = tgrp; checkgrp != NULL; 285175635Siedowse checkgrp = checkgrp->gr_next) { 285275635Siedowse if (checkgrp->gr_type != GT_HOST) 285375635Siedowse continue; 285475635Siedowse for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL; 285575635Siedowse tai = tai->ai_next) { 285675801Siedowse if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0) 285775635Siedowse continue; 285875635Siedowse if (debug) 285975635Siedowse fprintf(stderr, 286075635Siedowse "ignoring duplicate host %s\n", 286175635Siedowse ai->ai_canonname); 286275635Siedowse grp->gr_type = GT_IGNORE; 286375635Siedowse return (0); 286475635Siedowse } 286575635Siedowse } 286674462Salfred ai = ai->ai_next; 28671558Srgrimes } 286875635Siedowse grp->gr_type = GT_HOST; 28691558Srgrimes return (0); 28701558Srgrimes} 28711558Srgrimes 28721558Srgrimes/* 28731558Srgrimes * Free up an exports list component 28741558Srgrimes */ 2875285128Straszstatic void 2876216587Scharnierfree_exp(struct exportlist *ep) 28771558Srgrimes{ 2878349128Srmacklem struct grouplist *grp, *tgrp; 28791558Srgrimes 28801558Srgrimes if (ep->ex_defdir) { 28811558Srgrimes free_host(ep->ex_defdir->dp_hosts); 28821558Srgrimes free((caddr_t)ep->ex_defdir); 28831558Srgrimes } 28841558Srgrimes if (ep->ex_fsdir) 28851558Srgrimes free(ep->ex_fsdir); 288627447Sdfr if (ep->ex_indexfile) 288727447Sdfr free(ep->ex_indexfile); 28881558Srgrimes free_dir(ep->ex_dirl); 2889349128Srmacklem grp = ep->ex_grphead; 2890349128Srmacklem while (grp) { 2891349128Srmacklem tgrp = grp; 2892349128Srmacklem grp = grp->gr_next; 2893349128Srmacklem free_grp(tgrp); 2894349128Srmacklem } 28951558Srgrimes free((caddr_t)ep); 28961558Srgrimes} 28971558Srgrimes 28981558Srgrimes/* 2899349772Srmacklem * Free up the v4root exports. 2900349772Srmacklem */ 2901349772Srmacklemstatic void 2902349772Srmacklemfree_v4rootexp(void) 2903349772Srmacklem{ 2904349772Srmacklem 2905349772Srmacklem if (v4root_ep != NULL) { 2906349772Srmacklem free_exp(v4root_ep); 2907349772Srmacklem v4root_ep = NULL; 2908349772Srmacklem } 2909349772Srmacklem} 2910349772Srmacklem 2911349772Srmacklem/* 29121558Srgrimes * Free hosts. 29131558Srgrimes */ 2914285128Straszstatic void 2915216587Scharnierfree_host(struct hostlist *hp) 29161558Srgrimes{ 29171558Srgrimes struct hostlist *hp2; 29181558Srgrimes 29191558Srgrimes while (hp) { 29201558Srgrimes hp2 = hp; 29211558Srgrimes hp = hp->ht_next; 29221558Srgrimes free((caddr_t)hp2); 29231558Srgrimes } 29241558Srgrimes} 29251558Srgrimes 2926285128Straszstatic struct hostlist * 2927216587Scharnierget_ht(void) 29281558Srgrimes{ 29291558Srgrimes struct hostlist *hp; 29301558Srgrimes 29311558Srgrimes hp = (struct hostlist *)malloc(sizeof (struct hostlist)); 29321558Srgrimes if (hp == (struct hostlist *)NULL) 29331558Srgrimes out_of_mem(); 29341558Srgrimes hp->ht_next = (struct hostlist *)NULL; 29359336Sdfr hp->ht_flag = 0; 29361558Srgrimes return (hp); 29371558Srgrimes} 29381558Srgrimes 29391558Srgrimes/* 29401558Srgrimes * Out of memory, fatal 29411558Srgrimes */ 2942285128Straszstatic void 2943216587Scharnierout_of_mem(void) 29441558Srgrimes{ 29451558Srgrimes 294637663Scharnier syslog(LOG_ERR, "out of memory"); 29471558Srgrimes exit(2); 29481558Srgrimes} 29491558Srgrimes 29501558Srgrimes/* 2951349772Srmacklem * Call do_mount() from the struct exportlist, for each case needed. 2952349772Srmacklem */ 2953349772Srmacklemstatic int 2954349772Srmacklemdo_export_mount(struct exportlist *ep, struct statfs *fsp) 2955349772Srmacklem{ 2956349772Srmacklem struct grouplist *grp, defgrp; 2957349772Srmacklem int ret; 2958349772Srmacklem size_t dirlen; 2959349772Srmacklem 2960349772Srmacklem LOGDEBUG("do_export_mount=%s", ep->ex_fsdir); 2961349772Srmacklem dirlen = strlen(ep->ex_fsdir); 2962349772Srmacklem if ((ep->ex_flag & EX_DEFSET) != 0) { 2963349772Srmacklem defgrp.gr_type = GT_DEFAULT; 2964349772Srmacklem defgrp.gr_next = NULL; 2965349772Srmacklem /* We have an entry for all other hosts/nets. */ 2966349772Srmacklem LOGDEBUG("ex_defexflags=0x%x", ep->ex_defexflags); 2967349772Srmacklem ret = do_mount(ep, &defgrp, ep->ex_defexflags, &ep->ex_defanon, 2968349772Srmacklem ep->ex_fsdir, dirlen, fsp, ep->ex_defnumsecflavors, 2969349772Srmacklem ep->ex_defsecflavors); 2970349772Srmacklem if (ret != 0) 2971349772Srmacklem return (ret); 2972349772Srmacklem } 2973349772Srmacklem 2974349772Srmacklem /* Do a mount for each group. */ 2975349772Srmacklem grp = ep->ex_grphead; 2976349772Srmacklem while (grp != NULL) { 2977349772Srmacklem LOGDEBUG("do mount gr_type=0x%x gr_exflags=0x%x", 2978349772Srmacklem grp->gr_type, grp->gr_exflags); 2979349772Srmacklem ret = do_mount(ep, grp, grp->gr_exflags, &grp->gr_anon, 2980349772Srmacklem ep->ex_fsdir, dirlen, fsp, grp->gr_numsecflavors, 2981349772Srmacklem grp->gr_secflavors); 2982349772Srmacklem if (ret != 0) 2983349772Srmacklem return (ret); 2984349772Srmacklem grp = grp->gr_next; 2985349772Srmacklem } 2986349772Srmacklem return (0); 2987349772Srmacklem} 2988349772Srmacklem 2989349772Srmacklem/* 2990158857Srodrigc * Do the nmount() syscall with the update flag to push the export info into 29911558Srgrimes * the kernel. 29921558Srgrimes */ 2993285128Straszstatic int 2994158857Srodrigcdo_mount(struct exportlist *ep, struct grouplist *grp, int exflags, 2995349772Srmacklem struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb, 2996349772Srmacklem int numsecflavors, int *secflavors) 29971558Srgrimes{ 299875841Siedowse struct statfs fsb1; 299974462Salfred struct addrinfo *ai; 3000282214Strasz struct export_args *eap; 3001158857Srodrigc char errmsg[255]; 3002158857Srodrigc char *cp; 30031558Srgrimes int done; 3004158857Srodrigc char savedc; 3005158857Srodrigc struct iovec *iov; 3006184588Sdfr int i, iovlen; 3007158857Srodrigc int ret; 3008192934Srmacklem struct nfsex_args nfsea; 30091558Srgrimes 3010282214Strasz eap = &nfsea.export; 3011192934Srmacklem 3012158857Srodrigc cp = NULL; 3013158857Srodrigc savedc = '\0'; 3014158857Srodrigc iov = NULL; 3015158857Srodrigc iovlen = 0; 3016158857Srodrigc ret = 0; 301775801Siedowse 3018192934Srmacklem bzero(eap, sizeof (struct export_args)); 3019158857Srodrigc bzero(errmsg, sizeof(errmsg)); 3020192934Srmacklem eap->ex_flags = exflags; 3021192934Srmacklem eap->ex_anon = *anoncrp; 3022349772Srmacklem LOGDEBUG("do_mount exflags=0x%x", exflags); 3023192934Srmacklem eap->ex_indexfile = ep->ex_indexfile; 302475641Siedowse if (grp->gr_type == GT_HOST) 302574462Salfred ai = grp->gr_ptr.gt_addrinfo; 302675641Siedowse else 302775641Siedowse ai = NULL; 3028349772Srmacklem eap->ex_numsecflavors = numsecflavors; 3029349772Srmacklem LOGDEBUG("do_mount numsec=%d", numsecflavors); 3030192934Srmacklem for (i = 0; i < eap->ex_numsecflavors; i++) 3031349772Srmacklem eap->ex_secflavors[i] = secflavors[i]; 3032192934Srmacklem if (eap->ex_numsecflavors == 0) { 3033192934Srmacklem eap->ex_numsecflavors = 1; 3034192934Srmacklem eap->ex_secflavors[0] = AUTH_SYS; 3035184588Sdfr } 30361558Srgrimes done = FALSE; 3037158857Srodrigc 3038192934Srmacklem if (v4root_phase == 0) { 3039192934Srmacklem build_iovec(&iov, &iovlen, "fstype", NULL, 0); 3040192934Srmacklem build_iovec(&iov, &iovlen, "fspath", NULL, 0); 3041192934Srmacklem build_iovec(&iov, &iovlen, "from", NULL, 0); 3042192934Srmacklem build_iovec(&iov, &iovlen, "update", NULL, 0); 3043192934Srmacklem build_iovec(&iov, &iovlen, "export", eap, 3044192934Srmacklem sizeof (struct export_args)); 3045192934Srmacklem build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 3046192934Srmacklem } 3047158857Srodrigc 30481558Srgrimes while (!done) { 30491558Srgrimes switch (grp->gr_type) { 30501558Srgrimes case GT_HOST: 305175641Siedowse if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0) 305274462Salfred goto skip; 3053192934Srmacklem eap->ex_addr = ai->ai_addr; 3054192934Srmacklem eap->ex_addrlen = ai->ai_addrlen; 3055192934Srmacklem eap->ex_masklen = 0; 30561558Srgrimes break; 30571558Srgrimes case GT_NET: 305875801Siedowse if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 && 305974462Salfred have_v6 == 0) 306074462Salfred goto skip; 3061192934Srmacklem eap->ex_addr = 306275801Siedowse (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net; 3063192934Srmacklem eap->ex_addrlen = 3064158857Srodrigc ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len; 3065192934Srmacklem eap->ex_mask = 306675801Siedowse (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask; 3067192934Srmacklem eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len; 30681558Srgrimes break; 306975641Siedowse case GT_DEFAULT: 3070192934Srmacklem eap->ex_addr = NULL; 3071192934Srmacklem eap->ex_addrlen = 0; 3072192934Srmacklem eap->ex_mask = NULL; 3073192934Srmacklem eap->ex_masklen = 0; 307475641Siedowse break; 30757401Swpaul case GT_IGNORE: 3076158857Srodrigc ret = 0; 3077158857Srodrigc goto error_exit; 30787401Swpaul break; 30791558Srgrimes default: 308037663Scharnier syslog(LOG_ERR, "bad grouptype"); 30811558Srgrimes if (cp) 30821558Srgrimes *cp = savedc; 3083158857Srodrigc ret = 1; 3084158857Srodrigc goto error_exit; 3085298089Spfg } 30861558Srgrimes 30871558Srgrimes /* 3088192934Srmacklem * For V4:, use the nfssvc() syscall, instead of mount(). 30891558Srgrimes */ 3090192934Srmacklem if (v4root_phase == 2) { 3091192934Srmacklem nfsea.fspec = v4root_dirpath; 3092282214Strasz if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) { 3093192934Srmacklem syslog(LOG_ERR, "Exporting V4: failed"); 3094192934Srmacklem return (2); 3095158857Srodrigc } 3096192934Srmacklem } else { 3097192934Srmacklem /* 3098192934Srmacklem * XXX: 3099192934Srmacklem * Maybe I should just use the fsb->f_mntonname path 3100192934Srmacklem * instead of looping back up the dirp to the mount 3101192934Srmacklem * point?? 3102192934Srmacklem * Also, needs to know how to export all types of local 3103192934Srmacklem * exportable filesystems and not just "ufs". 3104192934Srmacklem */ 3105192934Srmacklem iov[1].iov_base = fsb->f_fstypename; /* "fstype" */ 3106192934Srmacklem iov[1].iov_len = strlen(fsb->f_fstypename) + 1; 3107192934Srmacklem iov[3].iov_base = fsb->f_mntonname; /* "fspath" */ 3108192934Srmacklem iov[3].iov_len = strlen(fsb->f_mntonname) + 1; 3109192934Srmacklem iov[5].iov_base = fsb->f_mntfromname; /* "from" */ 3110192934Srmacklem iov[5].iov_len = strlen(fsb->f_mntfromname) + 1; 3111270183Sbdrewery errmsg[0] = '\0'; 3112192934Srmacklem 3113192934Srmacklem while (nmount(iov, iovlen, fsb->f_flags) < 0) { 3114192934Srmacklem if (cp) 3115192934Srmacklem *cp-- = savedc; 3116192934Srmacklem else 3117192934Srmacklem cp = dirp + dirplen - 1; 3118192934Srmacklem if (opt_flags & OP_QUIET) { 3119192934Srmacklem ret = 1; 3120192934Srmacklem goto error_exit; 3121192934Srmacklem } 3122192934Srmacklem if (errno == EPERM) { 3123192934Srmacklem if (debug) 3124239744Sdelphij warnx("can't change attributes for %s: %s", 3125239744Sdelphij dirp, errmsg); 3126192934Srmacklem syslog(LOG_ERR, 3127239744Sdelphij "can't change attributes for %s: %s", 3128239744Sdelphij dirp, errmsg); 3129192934Srmacklem ret = 1; 3130192934Srmacklem goto error_exit; 3131192934Srmacklem } 3132192934Srmacklem if (opt_flags & OP_ALLDIRS) { 3133192934Srmacklem if (errno == EINVAL) 3134192934Srmacklem syslog(LOG_ERR, 3135100336Sjoerg "-alldirs requested but %s is not a filesystem mountpoint", 3136192934Srmacklem dirp); 3137192934Srmacklem else 3138192934Srmacklem syslog(LOG_ERR, 3139192934Srmacklem "could not remount %s: %m", 3140192934Srmacklem dirp); 3141192934Srmacklem ret = 1; 3142192934Srmacklem goto error_exit; 3143192934Srmacklem } 3144192934Srmacklem /* back up over the last component */ 3145363673Sbrooks while (cp > dirp && *cp == '/') 3146192934Srmacklem cp--; 3147363673Sbrooks while (cp > dirp && *(cp - 1) != '/') 3148192934Srmacklem cp--; 3149192934Srmacklem if (cp == dirp) { 3150192934Srmacklem if (debug) 3151192934Srmacklem warnx("mnt unsucc"); 3152192934Srmacklem syslog(LOG_ERR, "can't export %s %s", 3153192934Srmacklem dirp, errmsg); 3154192934Srmacklem ret = 1; 3155192934Srmacklem goto error_exit; 3156192934Srmacklem } 3157192934Srmacklem savedc = *cp; 3158192934Srmacklem *cp = '\0'; 3159192934Srmacklem /* 3160192934Srmacklem * Check that we're still on the same 3161192934Srmacklem * filesystem. 3162192934Srmacklem */ 3163192934Srmacklem if (statfs(dirp, &fsb1) != 0 || 3164192934Srmacklem bcmp(&fsb1.f_fsid, &fsb->f_fsid, 3165192934Srmacklem sizeof (fsb1.f_fsid)) != 0) { 3166192934Srmacklem *cp = savedc; 3167100336Sjoerg syslog(LOG_ERR, 3168192934Srmacklem "can't export %s %s", dirp, 3169192934Srmacklem errmsg); 3170192934Srmacklem ret = 1; 3171192934Srmacklem goto error_exit; 3172192934Srmacklem } 31731558Srgrimes } 31741558Srgrimes } 3175192934Srmacklem 3176192934Srmacklem /* 3177192934Srmacklem * For the experimental server: 3178192934Srmacklem * If this is the public directory, get the file handle 3179192934Srmacklem * and load it into the kernel via the nfssvc() syscall. 3180192934Srmacklem */ 3181282214Strasz if ((exflags & MNT_EXPUBLIC) != 0) { 3182192934Srmacklem fhandle_t fh; 3183192934Srmacklem char *public_name; 3184192934Srmacklem 3185192934Srmacklem if (eap->ex_indexfile != NULL) 3186192934Srmacklem public_name = eap->ex_indexfile; 3187192934Srmacklem else 3188192934Srmacklem public_name = dirp; 3189192934Srmacklem if (getfh(public_name, &fh) < 0) 3190192934Srmacklem syslog(LOG_ERR, 3191192934Srmacklem "Can't get public fh for %s", public_name); 3192192934Srmacklem else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0) 3193192934Srmacklem syslog(LOG_ERR, 3194192934Srmacklem "Can't set public fh for %s", public_name); 3195349772Srmacklem else { 3196192934Srmacklem has_publicfh = 1; 3197349772Srmacklem has_set_publicfh = 1; 3198349772Srmacklem ep->ex_flag |= EX_PUBLICFH; 3199349772Srmacklem } 3200192934Srmacklem } 320174462Salfredskip: 320275641Siedowse if (ai != NULL) 320374462Salfred ai = ai->ai_next; 320475641Siedowse if (ai == NULL) 32051558Srgrimes done = TRUE; 32061558Srgrimes } 32071558Srgrimes if (cp) 32081558Srgrimes *cp = savedc; 3209158857Srodrigcerror_exit: 3210158857Srodrigc /* free strings allocated by strdup() in getmntopts.c */ 3211158857Srodrigc if (iov != NULL) { 3212158857Srodrigc free(iov[0].iov_base); /* fstype */ 3213158857Srodrigc free(iov[2].iov_base); /* fspath */ 3214158857Srodrigc free(iov[4].iov_base); /* from */ 3215158857Srodrigc free(iov[6].iov_base); /* update */ 3216158857Srodrigc free(iov[8].iov_base); /* export */ 3217158857Srodrigc free(iov[10].iov_base); /* errmsg */ 3218158857Srodrigc 3219158857Srodrigc /* free iov, allocated by realloc() */ 3220158857Srodrigc free(iov); 3221158857Srodrigc } 3222158857Srodrigc return (ret); 32231558Srgrimes} 32241558Srgrimes 32251558Srgrimes/* 32261558Srgrimes * Translate a net address. 322775801Siedowse * 322875801Siedowse * If `maskflg' is nonzero, then `cp' is a netmask, not a network address. 32291558Srgrimes */ 3230285128Straszstatic int 3231216587Scharnierget_net(char *cp, struct netmsk *net, int maskflg) 32321558Srgrimes{ 323375861Siedowse struct netent *np = NULL; 323474462Salfred char *name, *p, *prefp; 323575801Siedowse struct sockaddr_in sin; 323675861Siedowse struct sockaddr *sa = NULL; 323774462Salfred struct addrinfo hints, *ai = NULL; 323874462Salfred char netname[NI_MAXHOST]; 323974462Salfred long preflen; 32401558Srgrimes 324175635Siedowse p = prefp = NULL; 324274462Salfred if ((opt_flags & OP_MASKLEN) && !maskflg) { 324374462Salfred p = strchr(cp, '/'); 324474462Salfred *p = '\0'; 324574462Salfred prefp = p + 1; 324674462Salfred } 324774462Salfred 324875861Siedowse /* 324975861Siedowse * Check for a numeric address first. We wish to avoid 325075861Siedowse * possible DNS lookups in getnetbyname(). 325175861Siedowse */ 325275861Siedowse if (isxdigit(*cp) || *cp == ':') { 325374462Salfred memset(&hints, 0, sizeof hints); 325475801Siedowse /* Ensure the mask and the network have the same family. */ 325575801Siedowse if (maskflg && (opt_flags & OP_NET)) 325675801Siedowse hints.ai_family = net->nt_net.ss_family; 325775801Siedowse else if (!maskflg && (opt_flags & OP_HAVEMASK)) 325875801Siedowse hints.ai_family = net->nt_mask.ss_family; 325975801Siedowse else 326075801Siedowse hints.ai_family = AF_UNSPEC; 326174462Salfred hints.ai_flags = AI_NUMERICHOST; 326275861Siedowse if (getaddrinfo(cp, NULL, &hints, &ai) == 0) 326375861Siedowse sa = ai->ai_addr; 326475861Siedowse if (sa != NULL && ai->ai_family == AF_INET) { 326574462Salfred /* 326675801Siedowse * The address in `cp' is really a network address, so 326775801Siedowse * use inet_network() to re-interpret this correctly. 326875801Siedowse * e.g. "127.1" means 127.1.0.0, not 127.0.0.1. 326974462Salfred */ 327075801Siedowse bzero(&sin, sizeof sin); 327174462Salfred sin.sin_family = AF_INET; 327274462Salfred sin.sin_len = sizeof sin; 327375801Siedowse sin.sin_addr = inet_makeaddr(inet_network(cp), 0); 327474462Salfred if (debug) 327575801Siedowse fprintf(stderr, "get_net: v4 addr %s\n", 327675801Siedowse inet_ntoa(sin.sin_addr)); 327774462Salfred sa = (struct sockaddr *)&sin; 327875861Siedowse } 327975861Siedowse } 328075861Siedowse if (sa == NULL && (np = getnetbyname(cp)) != NULL) { 328175861Siedowse bzero(&sin, sizeof sin); 328275861Siedowse sin.sin_family = AF_INET; 328375861Siedowse sin.sin_len = sizeof sin; 328475861Siedowse sin.sin_addr = inet_makeaddr(np->n_net, 0); 328575861Siedowse sa = (struct sockaddr *)&sin; 328675861Siedowse } 328775861Siedowse if (sa == NULL) 328874462Salfred goto fail; 328925318Spst 329075801Siedowse if (maskflg) { 329175801Siedowse /* The specified sockaddr is a mask. */ 329275801Siedowse if (checkmask(sa) != 0) 329375801Siedowse goto fail; 329475801Siedowse bcopy(sa, &net->nt_mask, sa->sa_len); 329575801Siedowse opt_flags |= OP_HAVEMASK; 329675801Siedowse } else { 329775801Siedowse /* The specified sockaddr is a network address. */ 329875801Siedowse bcopy(sa, &net->nt_net, sa->sa_len); 329974462Salfred 330075801Siedowse /* Get a network name for the export list. */ 330175801Siedowse if (np) { 330275801Siedowse name = np->n_name; 330375801Siedowse } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname, 3304146187Sume NULL, 0, NI_NUMERICHOST) == 0) { 330575801Siedowse name = netname; 330675801Siedowse } else { 330775801Siedowse goto fail; 330875801Siedowse } 330975801Siedowse if ((net->nt_name = strdup(name)) == NULL) 331075801Siedowse out_of_mem(); 331175801Siedowse 331275801Siedowse /* 331375801Siedowse * Extract a mask from either a "/<masklen>" suffix, or 331475801Siedowse * from the class of an IPv4 address. 331575801Siedowse */ 331674462Salfred if (opt_flags & OP_MASKLEN) { 331774462Salfred preflen = strtol(prefp, NULL, 10); 331875801Siedowse if (preflen < 0L || preflen == LONG_MAX) 331974462Salfred goto fail; 332075801Siedowse bcopy(sa, &net->nt_mask, sa->sa_len); 332175801Siedowse if (makemask(&net->nt_mask, (int)preflen) != 0) 332275801Siedowse goto fail; 332375801Siedowse opt_flags |= OP_HAVEMASK; 332474462Salfred *p = '/'; 332575801Siedowse } else if (sa->sa_family == AF_INET && 332675801Siedowse (opt_flags & OP_MASK) == 0) { 332775801Siedowse in_addr_t addr; 332874462Salfred 332975801Siedowse addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr; 333075801Siedowse if (IN_CLASSA(addr)) 333175801Siedowse preflen = 8; 333275801Siedowse else if (IN_CLASSB(addr)) 333375801Siedowse preflen = 16; 333475801Siedowse else if (IN_CLASSC(addr)) 333575801Siedowse preflen = 24; 333675801Siedowse else if (IN_CLASSD(addr)) 333775801Siedowse preflen = 28; 333875801Siedowse else 333975801Siedowse preflen = 32; /* XXX */ 334075801Siedowse 334175801Siedowse bcopy(sa, &net->nt_mask, sa->sa_len); 334275801Siedowse makemask(&net->nt_mask, (int)preflen); 334375801Siedowse opt_flags |= OP_HAVEMASK; 334474462Salfred } 334574462Salfred } 334674462Salfred 334774462Salfred if (ai) 334874462Salfred freeaddrinfo(ai); 334974462Salfred return 0; 335074462Salfred 335174462Salfredfail: 335274462Salfred if (ai) 335374462Salfred freeaddrinfo(ai); 335474462Salfred return 1; 33551558Srgrimes} 33561558Srgrimes 33571558Srgrimes/* 33581558Srgrimes * Parse out the next white space separated field 33591558Srgrimes */ 3360285128Straszstatic void 3361216587Scharniernextfield(char **cp, char **endcp) 33621558Srgrimes{ 33631558Srgrimes char *p; 3364347341Smav char quot = 0; 33651558Srgrimes 33661558Srgrimes p = *cp; 33671558Srgrimes while (*p == ' ' || *p == '\t') 33681558Srgrimes p++; 3369347341Smav *cp = p; 3370347341Smav while (*p != '\0') { 3371347341Smav if (quot) { 3372347341Smav if (*p == quot) 3373347341Smav quot = 0; 3374347341Smav } else { 3375347341Smav if (*p == '\\' && *(p + 1) != '\0') 3376347341Smav p++; 3377347341Smav else if (*p == '\'' || *p == '"') 3378347341Smav quot = *p; 3379347341Smav else if (*p == ' ' || *p == '\t') 3380347341Smav break; 3381347341Smav } 3382347341Smav p++; 3383347341Smav }; 3384347341Smav *endcp = p; 33851558Srgrimes} 33861558Srgrimes 33871558Srgrimes/* 33881558Srgrimes * Get an exports file line. Skip over blank lines and handle line 33891558Srgrimes * continuations. 33901558Srgrimes */ 3391285128Straszstatic int 3392216587Scharnierget_line(void) 33931558Srgrimes{ 33941558Srgrimes char *p, *cp; 339596622Siedowse size_t len; 33961558Srgrimes int totlen, cont_line; 33971558Srgrimes 33981558Srgrimes /* 33991558Srgrimes * Loop around ignoring blank lines and getting all continuation lines. 34001558Srgrimes */ 34011558Srgrimes p = line; 34021558Srgrimes totlen = 0; 34031558Srgrimes do { 340496622Siedowse if ((p = fgetln(exp_file, &len)) == NULL) 34051558Srgrimes return (0); 34061558Srgrimes cp = p + len - 1; 34071558Srgrimes cont_line = 0; 34081558Srgrimes while (cp >= p && 34091558Srgrimes (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) { 34101558Srgrimes if (*cp == '\\') 34111558Srgrimes cont_line = 1; 34121558Srgrimes cp--; 34131558Srgrimes len--; 34141558Srgrimes } 341579117Sdd if (cont_line) { 341679117Sdd *++cp = ' '; 341779117Sdd len++; 341879117Sdd } 341996622Siedowse if (linesize < len + totlen + 1) { 342096622Siedowse linesize = len + totlen + 1; 342196622Siedowse line = realloc(line, linesize); 342296622Siedowse if (line == NULL) 342396622Siedowse out_of_mem(); 34241558Srgrimes } 342596622Siedowse memcpy(line + totlen, p, len); 342696622Siedowse totlen += len; 342796622Siedowse line[totlen] = '\0'; 34281558Srgrimes } while (totlen == 0 || cont_line); 34291558Srgrimes return (1); 34301558Srgrimes} 34311558Srgrimes 34321558Srgrimes/* 34331558Srgrimes * Parse a description of a credential. 34341558Srgrimes */ 3435285128Straszstatic void 3436216587Scharnierparsecred(char *namelist, struct xucred *cr) 34371558Srgrimes{ 34381558Srgrimes char *name; 34391558Srgrimes int cnt; 34401558Srgrimes char *names; 34411558Srgrimes struct passwd *pw; 34421558Srgrimes struct group *gr; 3443194498Sbrooks gid_t groups[XU_NGROUPS + 1]; 3444136051Sstefanf int ngroups; 34451558Srgrimes 344691354Sdd cr->cr_version = XUCRED_VERSION; 34471558Srgrimes /* 344837663Scharnier * Set up the unprivileged user. 34491558Srgrimes */ 34501558Srgrimes cr->cr_uid = -2; 34511558Srgrimes cr->cr_groups[0] = -2; 34521558Srgrimes cr->cr_ngroups = 1; 34531558Srgrimes /* 34541558Srgrimes * Get the user's password table entry. 34551558Srgrimes */ 3456347341Smav names = namelist; 3457347341Smav name = strsep_quote(&names, ":"); 3458293305Sjpaetzel /* Bug? name could be NULL here */ 34591558Srgrimes if (isdigit(*name) || *name == '-') 34601558Srgrimes pw = getpwuid(atoi(name)); 34611558Srgrimes else 34621558Srgrimes pw = getpwnam(name); 34631558Srgrimes /* 34641558Srgrimes * Credentials specified as those of a user. 34651558Srgrimes */ 34661558Srgrimes if (names == NULL) { 34671558Srgrimes if (pw == NULL) { 346837663Scharnier syslog(LOG_ERR, "unknown user: %s", name); 34691558Srgrimes return; 34701558Srgrimes } 34711558Srgrimes cr->cr_uid = pw->pw_uid; 3472194498Sbrooks ngroups = XU_NGROUPS + 1; 3473333197Savg if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) { 347437663Scharnier syslog(LOG_ERR, "too many groups"); 3475333197Savg ngroups = XU_NGROUPS + 1; 3476333197Savg } 3477333197Savg 34781558Srgrimes /* 3479136051Sstefanf * Compress out duplicate. 34801558Srgrimes */ 34811558Srgrimes cr->cr_groups[0] = groups[0]; 3482362712Srmacklem if (ngroups > 1 && groups[0] == groups[1]) { 3483362712Srmacklem cr->cr_ngroups = ngroups - 1; 3484362712Srmacklem for (cnt = 2; cnt < ngroups; cnt++) 3485362712Srmacklem cr->cr_groups[cnt - 1] = groups[cnt]; 3486362712Srmacklem } else { 3487362712Srmacklem cr->cr_ngroups = ngroups; 3488362712Srmacklem if (cr->cr_ngroups > XU_NGROUPS) 3489362712Srmacklem cr->cr_ngroups = XU_NGROUPS; 3490362712Srmacklem for (cnt = 1; cnt < cr->cr_ngroups; cnt++) 3491362712Srmacklem cr->cr_groups[cnt] = groups[cnt]; 3492362712Srmacklem } 34931558Srgrimes return; 34941558Srgrimes } 34951558Srgrimes /* 34961558Srgrimes * Explicit credential specified as a colon separated list: 34971558Srgrimes * uid:gid:gid:... 34981558Srgrimes */ 34991558Srgrimes if (pw != NULL) 35001558Srgrimes cr->cr_uid = pw->pw_uid; 35011558Srgrimes else if (isdigit(*name) || *name == '-') 35021558Srgrimes cr->cr_uid = atoi(name); 35031558Srgrimes else { 350437663Scharnier syslog(LOG_ERR, "unknown user: %s", name); 35051558Srgrimes return; 35061558Srgrimes } 35071558Srgrimes cr->cr_ngroups = 0; 3508194498Sbrooks while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) { 3509347341Smav name = strsep_quote(&names, ":"); 35101558Srgrimes if (isdigit(*name) || *name == '-') { 35111558Srgrimes cr->cr_groups[cr->cr_ngroups++] = atoi(name); 35121558Srgrimes } else { 35131558Srgrimes if ((gr = getgrnam(name)) == NULL) { 351437663Scharnier syslog(LOG_ERR, "unknown group: %s", name); 35151558Srgrimes continue; 35161558Srgrimes } 35171558Srgrimes cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid; 35181558Srgrimes } 35191558Srgrimes } 3520194498Sbrooks if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS) 352137663Scharnier syslog(LOG_ERR, "too many groups"); 35221558Srgrimes} 35231558Srgrimes 3524194880Sdfr#define STRSIZ (MNTNAMLEN+MNTPATHLEN+50) 35251558Srgrimes/* 35261558Srgrimes * Routines that maintain the remote mounttab 35271558Srgrimes */ 3528285128Straszstatic void 3529216587Scharnierget_mountlist(void) 35301558Srgrimes{ 3531324955Smanu struct mountlist *mlp; 353223681Speter char *host, *dirp, *cp; 35331558Srgrimes char str[STRSIZ]; 35341558Srgrimes FILE *mlfile; 35351558Srgrimes 35361558Srgrimes if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { 353753117Sbillf if (errno == ENOENT) 353853117Sbillf return; 353953117Sbillf else { 354053117Sbillf syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST); 354153117Sbillf return; 354253117Sbillf } 35431558Srgrimes } 35441558Srgrimes while (fgets(str, STRSIZ, mlfile) != NULL) { 354523681Speter cp = str; 354623681Speter host = strsep(&cp, " \t\n"); 354723681Speter dirp = strsep(&cp, " \t\n"); 354823681Speter if (host == NULL || dirp == NULL) 35491558Srgrimes continue; 35501558Srgrimes mlp = (struct mountlist *)malloc(sizeof (*mlp)); 355137663Scharnier if (mlp == (struct mountlist *)NULL) 355237663Scharnier out_of_mem(); 3553194880Sdfr strncpy(mlp->ml_host, host, MNTNAMLEN); 3554194880Sdfr mlp->ml_host[MNTNAMLEN] = '\0'; 3555194880Sdfr strncpy(mlp->ml_dirp, dirp, MNTPATHLEN); 3556194880Sdfr mlp->ml_dirp[MNTPATHLEN] = '\0'; 3557324955Smanu 3558324955Smanu SLIST_INSERT_HEAD(&mlhead, mlp, next); 35591558Srgrimes } 35601558Srgrimes fclose(mlfile); 35611558Srgrimes} 35621558Srgrimes 3563285128Straszstatic void 356475635Siedowsedel_mlist(char *hostp, char *dirp) 35651558Srgrimes{ 3566324955Smanu struct mountlist *mlp, *mlp2; 35671558Srgrimes FILE *mlfile; 35681558Srgrimes int fnd = 0; 35691558Srgrimes 3570324955Smanu SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) { 35711558Srgrimes if (!strcmp(mlp->ml_host, hostp) && 35721558Srgrimes (!dirp || !strcmp(mlp->ml_dirp, dirp))) { 35731558Srgrimes fnd = 1; 3574324955Smanu SLIST_REMOVE(&mlhead, mlp, mountlist, next); 3575324955Smanu free((caddr_t)mlp); 35761558Srgrimes } 35771558Srgrimes } 35781558Srgrimes if (fnd) { 35791558Srgrimes if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { 358037663Scharnier syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST); 35811558Srgrimes return; 35821558Srgrimes } 3583324955Smanu SLIST_FOREACH(mlp, &mlhead, next) { 35841558Srgrimes fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 35851558Srgrimes } 35861558Srgrimes fclose(mlfile); 35871558Srgrimes } 35881558Srgrimes} 35891558Srgrimes 3590285128Straszstatic void 3591216587Scharnieradd_mlist(char *hostp, char *dirp) 35921558Srgrimes{ 3593324955Smanu struct mountlist *mlp; 35941558Srgrimes FILE *mlfile; 35951558Srgrimes 3596324955Smanu SLIST_FOREACH(mlp, &mlhead, next) { 35971558Srgrimes if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp)) 35981558Srgrimes return; 35991558Srgrimes } 3600324955Smanu 36011558Srgrimes mlp = (struct mountlist *)malloc(sizeof (*mlp)); 360237663Scharnier if (mlp == (struct mountlist *)NULL) 360337663Scharnier out_of_mem(); 3604194880Sdfr strncpy(mlp->ml_host, hostp, MNTNAMLEN); 3605194880Sdfr mlp->ml_host[MNTNAMLEN] = '\0'; 3606194880Sdfr strncpy(mlp->ml_dirp, dirp, MNTPATHLEN); 3607194880Sdfr mlp->ml_dirp[MNTPATHLEN] = '\0'; 3608324955Smanu SLIST_INSERT_HEAD(&mlhead, mlp, next); 36091558Srgrimes if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { 361037663Scharnier syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST); 36111558Srgrimes return; 36121558Srgrimes } 36131558Srgrimes fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 36141558Srgrimes fclose(mlfile); 36151558Srgrimes} 36161558Srgrimes 36171558Srgrimes/* 36181558Srgrimes * Free up a group list. 36191558Srgrimes */ 3620285128Straszstatic void 3621216587Scharnierfree_grp(struct grouplist *grp) 36221558Srgrimes{ 36231558Srgrimes if (grp->gr_type == GT_HOST) { 362474462Salfred if (grp->gr_ptr.gt_addrinfo != NULL) 362574462Salfred freeaddrinfo(grp->gr_ptr.gt_addrinfo); 36261558Srgrimes } else if (grp->gr_type == GT_NET) { 36271558Srgrimes if (grp->gr_ptr.gt_net.nt_name) 36281558Srgrimes free(grp->gr_ptr.gt_net.nt_name); 36291558Srgrimes } 36301558Srgrimes free((caddr_t)grp); 36311558Srgrimes} 36321558Srgrimes 36331558Srgrimes#ifdef DEBUG 3634285128Straszstatic void 36351558SrgrimesSYSLOG(int pri, const char *fmt, ...) 36361558Srgrimes{ 36371558Srgrimes va_list ap; 36381558Srgrimes 36391558Srgrimes va_start(ap, fmt); 36401558Srgrimes vfprintf(stderr, fmt, ap); 36411558Srgrimes va_end(ap); 36421558Srgrimes} 36431558Srgrimes#endif /* DEBUG */ 36441558Srgrimes 36451558Srgrimes/* 36461558Srgrimes * Check options for consistency. 36471558Srgrimes */ 3648285128Straszstatic int 3649216587Scharniercheck_options(struct dirlist *dp) 36501558Srgrimes{ 36511558Srgrimes 3652192934Srmacklem if (v4root_phase == 0 && dp == NULL) 36531558Srgrimes return (1); 365483653Speter if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) { 365583653Speter syslog(LOG_ERR, "-mapall and -maproot mutually exclusive"); 36561558Srgrimes return (1); 36571558Srgrimes } 36581558Srgrimes if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { 365975801Siedowse syslog(LOG_ERR, "-mask requires -network"); 366075801Siedowse return (1); 36611558Srgrimes } 366275801Siedowse if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) { 366375801Siedowse syslog(LOG_ERR, "-network requires mask specification"); 366475801Siedowse return (1); 366575801Siedowse } 366675801Siedowse if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) { 366775801Siedowse syslog(LOG_ERR, "-mask and /masklen are mutually exclusive"); 366875801Siedowse return (1); 366975801Siedowse } 3670192934Srmacklem if (v4root_phase > 0 && 3671192934Srmacklem (opt_flags & 3672192934Srmacklem ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) { 3673192934Srmacklem syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:"); 3674192934Srmacklem return (1); 3675192934Srmacklem } 3676207689Srmacklem if ((opt_flags & OP_ALLDIRS) && dp->dp_left) { 3677207689Srmacklem syslog(LOG_ERR, "-alldirs has multiple directories"); 3678207689Srmacklem return (1); 3679207689Srmacklem } 36801558Srgrimes return (0); 36811558Srgrimes} 36821558Srgrimes 36831558Srgrimes/* 36841558Srgrimes * Check an absolute directory path for any symbolic links. Return true 36851558Srgrimes */ 3686285128Straszstatic int 3687216587Scharniercheck_dirpath(char *dirp) 36881558Srgrimes{ 36891558Srgrimes char *cp; 36901558Srgrimes int ret = 1; 36911558Srgrimes struct stat sb; 36921558Srgrimes 36931558Srgrimes cp = dirp + 1; 36941558Srgrimes while (*cp && ret) { 36951558Srgrimes if (*cp == '/') { 36961558Srgrimes *cp = '\0'; 36979336Sdfr if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 36981558Srgrimes ret = 0; 36991558Srgrimes *cp = '/'; 37001558Srgrimes } 37011558Srgrimes cp++; 37021558Srgrimes } 37039336Sdfr if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 37041558Srgrimes ret = 0; 37051558Srgrimes return (ret); 37061558Srgrimes} 37079336Sdfr 370875801Siedowse/* 370975801Siedowse * Make a netmask according to the specified prefix length. The ss_family 371075801Siedowse * and other non-address fields must be initialised before calling this. 371175801Siedowse */ 3712285128Straszstatic int 371375801Siedowsemakemask(struct sockaddr_storage *ssp, int bitlen) 371474462Salfred{ 371575801Siedowse u_char *p; 371675801Siedowse int bits, i, len; 371774462Salfred 371875801Siedowse if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL) 371975801Siedowse return (-1); 3720103949Smike if (bitlen > len * CHAR_BIT) 372175801Siedowse return (-1); 372274462Salfred 372375801Siedowse for (i = 0; i < len; i++) { 3724298912Saraujo bits = MIN(CHAR_BIT, bitlen); 3725219125Sru *p++ = (u_char)~0 << (CHAR_BIT - bits); 372675801Siedowse bitlen -= bits; 372774462Salfred } 372875801Siedowse return 0; 372974462Salfred} 373074462Salfred 373175801Siedowse/* 373275801Siedowse * Check that the sockaddr is a valid netmask. Returns 0 if the mask 373375801Siedowse * is acceptable (i.e. of the form 1...10....0). 373475801Siedowse */ 3735285128Straszstatic int 373675801Siedowsecheckmask(struct sockaddr *sa) 373774462Salfred{ 373875801Siedowse u_char *mask; 373975801Siedowse int i, len; 374074462Salfred 374175801Siedowse if ((mask = sa_rawaddr(sa, &len)) == NULL) 374275801Siedowse return (-1); 374375801Siedowse 374475801Siedowse for (i = 0; i < len; i++) 374575801Siedowse if (mask[i] != 0xff) 374675801Siedowse break; 374775801Siedowse if (i < len) { 374875801Siedowse if (~mask[i] & (u_char)(~mask[i] + 1)) 374975801Siedowse return (-1); 375075801Siedowse i++; 375174462Salfred } 375275801Siedowse for (; i < len; i++) 375375801Siedowse if (mask[i] != 0) 375475801Siedowse return (-1); 375575801Siedowse return (0); 375674462Salfred} 375774462Salfred 375875801Siedowse/* 375975801Siedowse * Compare two sockaddrs according to a specified mask. Return zero if 376075801Siedowse * `sa1' matches `sa2' when filtered by the netmask in `samask'. 3761228990Suqs * If samask is NULL, perform a full comparison. 376275801Siedowse */ 3763285128Straszstatic int 376475801Siedowsesacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask) 376574462Salfred{ 376675801Siedowse unsigned char *p1, *p2, *mask; 376775801Siedowse int len, i; 376874462Salfred 376975801Siedowse if (sa1->sa_family != sa2->sa_family || 377075801Siedowse (p1 = sa_rawaddr(sa1, &len)) == NULL || 377175801Siedowse (p2 = sa_rawaddr(sa2, NULL)) == NULL) 377275801Siedowse return (1); 377375801Siedowse 377475801Siedowse switch (sa1->sa_family) { 377574462Salfred case AF_INET6: 377675801Siedowse if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != 377775801Siedowse ((struct sockaddr_in6 *)sa2)->sin6_scope_id) 377875801Siedowse return (1); 377974462Salfred break; 378074462Salfred } 378174462Salfred 378275801Siedowse /* Simple binary comparison if no mask specified. */ 378375801Siedowse if (samask == NULL) 378475801Siedowse return (memcmp(p1, p2, len)); 378574462Salfred 378675801Siedowse /* Set up the mask, and do a mask-based comparison. */ 378775801Siedowse if (sa1->sa_family != samask->sa_family || 378875801Siedowse (mask = sa_rawaddr(samask, NULL)) == NULL) 378975801Siedowse return (1); 379074462Salfred 379175801Siedowse for (i = 0; i < len; i++) 379275801Siedowse if ((p1[i] & mask[i]) != (p2[i] & mask[i])) 379375801Siedowse return (1); 379475801Siedowse return (0); 379574462Salfred} 379674462Salfred 379775801Siedowse/* 379875801Siedowse * Return a pointer to the part of the sockaddr that contains the 379975801Siedowse * raw address, and set *nbytes to its length in bytes. Returns 380075801Siedowse * NULL if the address family is unknown. 380175801Siedowse */ 3802285128Straszstatic void * 380375801Siedowsesa_rawaddr(struct sockaddr *sa, int *nbytes) { 380475801Siedowse void *p; 380574462Salfred int len; 380674462Salfred 380775801Siedowse switch (sa->sa_family) { 380874462Salfred case AF_INET: 380975801Siedowse len = sizeof(((struct sockaddr_in *)sa)->sin_addr); 381075801Siedowse p = &((struct sockaddr_in *)sa)->sin_addr; 381174462Salfred break; 381274462Salfred case AF_INET6: 381375801Siedowse len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr); 381475801Siedowse p = &((struct sockaddr_in6 *)sa)->sin6_addr; 381574462Salfred break; 381674462Salfred default: 381775801Siedowse p = NULL; 381875801Siedowse len = 0; 381974462Salfred } 382074462Salfred 382175801Siedowse if (nbytes != NULL) 382275801Siedowse *nbytes = len; 382375801Siedowse return (p); 382474462Salfred} 382574462Salfred 3826285128Straszstatic void 3827216587Scharnierhuphandler(int sig __unused) 382875754Siedowse{ 3829285128Strasz 383075754Siedowse got_sighup = 1; 383175754Siedowse} 383275754Siedowse 3833285128Straszstatic void 3834285128Straszterminate(int sig __unused) 383574462Salfred{ 3836149433Spjd pidfile_remove(pfh); 3837194880Sdfr rpcb_unset(MOUNTPROG, MOUNTVERS, NULL); 3838194880Sdfr rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL); 383974462Salfred exit (0); 384074462Salfred} 3841