]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mountd/mountd.c
contrib/tzdata: import tzdata 2023d
[FreeBSD/FreeBSD.git] / usr.sbin / mountd / mountd.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1989, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Herb Hasler and Rick Macklem at The University of Guelph.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/param.h>
36 #include <sys/conf.h>
37 #include <sys/fcntl.h>
38 #include <sys/fnv_hash.h>
39 #include <sys/linker.h>
40 #include <sys/module.h>
41 #include <sys/mount.h>
42 #include <sys/queue.h>
43 #include <sys/stat.h>
44 #include <sys/sysctl.h>
45 #include <sys/syslog.h>
46
47 #include <rpc/rpc.h>
48 #include <rpc/rpc_com.h>
49 #include <rpc/pmap_clnt.h>
50 #include <rpc/pmap_prot.h>
51 #include <rpcsvc/mount.h>
52 #include <nfs/nfsproto.h>
53 #include <nfs/nfssvc.h>
54 #include <nfsserver/nfs.h>
55
56 #include <fs/nfs/nfsport.h>
57
58 #include <arpa/inet.h>
59
60 #include <ctype.h>
61 #include <err.h>
62 #include <errno.h>
63 #include <grp.h>
64 #include <libutil.h>
65 #include <limits.h>
66 #include <netdb.h>
67 #include <pwd.h>
68 #include <signal.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <unistd.h>
73 #include <vis.h>
74 #include "pathnames.h"
75 #include "mntopts.h"
76
77 #ifdef DEBUG
78 #include <stdarg.h>
79 #endif
80
81 /*
82  * Structures for keeping the mount list and export list
83  */
84 struct mountlist {
85         char    ml_host[MNTNAMLEN+1];
86         char    ml_dirp[MNTPATHLEN+1];
87
88         SLIST_ENTRY(mountlist)  next;
89 };
90
91 struct dirlist {
92         struct dirlist  *dp_left;
93         struct dirlist  *dp_right;
94         int             dp_flag;
95         struct hostlist *dp_hosts;      /* List of hosts this dir exported to */
96         char            *dp_dirp;
97 };
98 /* dp_flag bits */
99 #define DP_DEFSET       0x1
100 #define DP_HOSTSET      0x2
101
102 /*
103  * maproot/mapall credentials.
104  * cr_smallgrps can be used for a group list up to SMALLNGROUPS in size.
105  * Larger group lists are malloc'd/free'd.
106  */
107 #define SMALLNGROUPS    32
108 struct expcred {
109         uid_t           cr_uid;
110         int             cr_ngroups;
111         gid_t           cr_smallgrps[SMALLNGROUPS];
112         gid_t           *cr_groups;
113 };
114
115 struct exportlist {
116         struct dirlist  *ex_dirl;
117         struct dirlist  *ex_defdir;
118         struct grouplist *ex_grphead;
119         int             ex_flag;
120         fsid_t          ex_fs;
121         char            *ex_fsdir;
122         char            *ex_indexfile;
123         struct expcred  ex_defanon;
124         uint64_t        ex_defexflags;
125         int             ex_numsecflavors;
126         int             ex_secflavors[MAXSECFLAVORS];
127         int             ex_defnumsecflavors;
128         int             ex_defsecflavors[MAXSECFLAVORS];
129
130         SLIST_ENTRY(exportlist) entries;
131 };
132 /* ex_flag bits */
133 #define EX_LINKED       0x1
134 #define EX_DONE         0x2
135 #define EX_DEFSET       0x4
136 #define EX_PUBLICFH     0x8
137
138 SLIST_HEAD(exportlisthead, exportlist);
139
140 struct netmsk {
141         struct sockaddr_storage nt_net;
142         struct sockaddr_storage nt_mask;
143         char            *nt_name;
144 };
145
146 union grouptypes {
147         struct addrinfo *gt_addrinfo;
148         struct netmsk   gt_net;
149 };
150
151 struct grouplist {
152         int gr_type;
153         union grouptypes gr_ptr;
154         struct grouplist *gr_next;
155         struct expcred gr_anon;
156         uint64_t gr_exflags;
157         int gr_flag;
158         int gr_numsecflavors;
159         int gr_secflavors[MAXSECFLAVORS];
160 };
161 /* Group types */
162 #define GT_NULL         0x0
163 #define GT_HOST         0x1
164 #define GT_NET          0x2
165 #define GT_DEFAULT      0x3
166 #define GT_IGNORE       0x5
167
168 /* Group flags */
169 #define GR_FND          0x1
170
171 struct hostlist {
172         int              ht_flag;       /* Uses DP_xx bits */
173         struct grouplist *ht_grp;
174         struct hostlist  *ht_next;
175 };
176
177 struct fhreturn {
178         int     fhr_flag;
179         int     fhr_vers;
180         nfsfh_t fhr_fh;
181         int     fhr_numsecflavors;
182         int     *fhr_secflavors;
183 };
184
185 #define GETPORT_MAXTRY  20      /* Max tries to get a port # */
186
187 /*
188  * How long to delay a reload of exports when there are RPC request(s)
189  * to process, in usec.  Must be less than 1second.
190  */
191 #define RELOADDELAY     250000
192
193 /* Global defs */
194 static char     *add_expdir(struct dirlist **, char *, int);
195 static void     add_dlist(struct dirlist **, struct dirlist *,
196                     struct grouplist *, int, struct exportlist *,
197                     struct expcred *, uint64_t);
198 static void     add_mlist(char *, char *);
199 static int      check_path_component(const char *, char **);
200 static int      check_dirpath(char *, char **);
201 static int      check_statfs(const char *, struct statfs *, char **);
202 static int      check_options(struct dirlist *);
203 static int      checkmask(struct sockaddr *sa);
204 static int      chk_host(struct dirlist *, struct sockaddr *, int *, int *,
205                     int *, int **);
206 static char     *strsep_quote(char **stringp, const char *delim);
207 static int      create_service(struct netconfig *nconf);
208 static void     complete_service(struct netconfig *nconf, char *port_str);
209 static void     clearout_service(void);
210 static void     del_mlist(char *hostp, char *dirp);
211 static struct dirlist   *dirp_search(struct dirlist *, char *);
212 static int      do_export_mount(struct exportlist *, struct statfs *);
213 static int      do_mount(struct exportlist *, struct grouplist *, uint64_t,
214                     struct expcred *, char *, int, struct statfs *, int, int *);
215 static int      do_opt(char **, char **, struct exportlist *,
216                     struct grouplist *, int *, uint64_t *, struct expcred *);
217 static struct exportlist        *ex_search(fsid_t *, struct exportlisthead *);
218 static struct exportlist        *get_exp(void);
219 static void     free_dir(struct dirlist *);
220 static void     free_exp(struct exportlist *);
221 static void     free_grp(struct grouplist *);
222 static void     free_host(struct hostlist *);
223 static void     free_v4rootexp(void);
224 static void     get_exportlist_one(int);
225 static void     get_exportlist(int);
226 static void     insert_exports(struct exportlist *, struct exportlisthead *);
227 static void     free_exports(struct exportlisthead *);
228 static void     read_exportfile(int);
229 static int      compare_nmount_exportlist(struct iovec *, int, char *);
230 static int      compare_export(struct exportlist *, struct exportlist *);
231 static int      compare_cred(struct expcred *, struct expcred *);
232 static int      compare_secflavor(int *, int *, int);
233 static void     delete_export(struct iovec *, int, struct statfs *, char *);
234 static int      get_host(char *, struct grouplist *, struct grouplist *);
235 static struct hostlist *get_ht(void);
236 static int      get_line(void);
237 static void     get_mountlist(void);
238 static int      get_net(char *, struct netmsk *, int);
239 static void     getexp_err(struct exportlist *, struct grouplist *, const char *);
240 static struct grouplist *get_grp(void);
241 static void     hang_dirp(struct dirlist *, struct grouplist *,
242                     struct exportlist *, int, struct expcred *, uint64_t);
243 static void     huphandler(int sig);
244 static int      makemask(struct sockaddr_storage *ssp, int bitlen);
245 static void     mntsrv(struct svc_req *, SVCXPRT *);
246 static void     nextfield(char **, char **);
247 static void     out_of_mem(void);
248 static void     parsecred(char *, struct expcred *);
249 static int      parsesec(char *, struct exportlist *);
250 static int      put_exlist(struct dirlist *, XDR *, struct dirlist *,
251                     int *, int);
252 static void     *sa_rawaddr(struct sockaddr *sa, int *nbytes);
253 static int      sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
254                     struct sockaddr *samask);
255 static int      scan_tree(struct dirlist *, struct sockaddr *);
256 static void     usage(void);
257 static int      xdr_dir(XDR *, char *);
258 static int      xdr_explist(XDR *, caddr_t);
259 static int      xdr_explist_brief(XDR *, caddr_t);
260 static int      xdr_explist_common(XDR *, caddr_t, int);
261 static int      xdr_fhs(XDR *, caddr_t);
262 static int      xdr_mlist(XDR *, caddr_t);
263 static void     terminate(int);
264 static void     cp_cred(struct expcred *, struct expcred *);
265
266 #define EXPHASH(f)      (fnv_32_buf((f), sizeof(fsid_t), 0) % exphashsize)
267 static struct exportlisthead *exphead = NULL;
268 static struct exportlisthead *oldexphead = NULL;
269 static int exphashsize = 0;
270 static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead);
271 static char *exnames_default[2] = { _PATH_EXPORTS, NULL };
272 static char **exnames;
273 static char **hosts = NULL;
274 static int force_v2 = 0;
275 static int resvport_only = 1;
276 static int nhosts = 0;
277 static int dir_only = 1;
278 static int dolog = 0;
279 static _Atomic(int) got_sighup = 0;
280 static int xcreated = 0;
281
282 static char *svcport_str = NULL;
283 static int mallocd_svcport = 0;
284 static int *sock_fd;
285 static int sock_fdcnt;
286 static int sock_fdpos;
287 static int suspend_nfsd = 0;
288
289 static int opt_flags;
290 static int have_v6 = 1;
291
292 static int v4root_phase = 0;
293 static char v4root_dirpath[PATH_MAX + 1];
294 static struct exportlist *v4root_ep = NULL;
295 static int has_publicfh = 0;
296 static int has_set_publicfh = 0;
297
298 static struct pidfh *pfh = NULL;
299 /* Bits for opt_flags above */
300 #define OP_MAPROOT      0x01
301 #define OP_MAPALL       0x02
302 /* 0x4 free */
303 #define OP_MASK         0x08
304 #define OP_NET          0x10
305 #define OP_ALLDIRS      0x40
306 #define OP_HAVEMASK     0x80    /* A mask was specified or inferred. */
307 #define OP_QUIET        0x100
308 #define OP_MASKLEN      0x200
309 #define OP_SEC          0x400
310 #define OP_CLASSMASK    0x800   /* mask not specified, is Class A/B/C default */
311
312 #ifdef DEBUG
313 static int debug = 1;
314 static void     SYSLOG(int, const char *, ...) __printflike(2, 3);
315 #define syslog SYSLOG
316 #else
317 static int debug = 0;
318 #endif
319
320 /*
321  * The LOGDEBUG() syslog() calls are always compiled into the daemon.
322  * To enable them, create a file at _PATH_MOUNTDDEBUG. This file can be empty.
323  * To disable the logging, just delete the file at _PATH_MOUNTDDEBUG.
324  */
325 static int logdebug = 0;
326 #define LOGDEBUG(format, ...)                                           \
327     (logdebug ? syslog(LOG_DEBUG, format, ## __VA_ARGS__) : 0)
328
329 /*
330  * Similar to strsep(), but it allows for quoted strings
331  * and escaped characters.
332  *
333  * It returns the string (or NULL, if *stringp is NULL),
334  * which is a de-quoted version of the string if necessary.
335  *
336  * It modifies *stringp in place.
337  */
338 static char *
339 strsep_quote(char **stringp, const char *delim)
340 {
341         char *srcptr, *dstptr, *retval;
342         char quot = 0;
343         
344         if (stringp == NULL || *stringp == NULL)
345                 return (NULL);
346
347         srcptr = dstptr = retval = *stringp;
348
349         while (*srcptr) {
350                 /*
351                  * We're looking for several edge cases here.
352                  * First:  if we're in quote state (quot != 0),
353                  * then we ignore the delim characters, but otherwise
354                  * process as normal, unless it is the quote character.
355                  * Second:  if the current character is a backslash,
356                  * we take the next character as-is, without checking
357                  * for delim, quote, or backslash.  Exception:  if the
358                  * next character is a NUL, that's the end of the string.
359                  * Third:  if the character is a quote character, we toggle
360                  * quote state.
361                  * Otherwise:  check the current character for NUL, or
362                  * being in delim, and end the string if either is true.
363                  */
364                 if (*srcptr == '\\') {
365                         srcptr++;
366                         /*
367                          * The edge case here is if the next character
368                          * is NUL, we want to stop processing.  But if
369                          * it's not NUL, then we simply want to copy it.
370                          */
371                         if (*srcptr) {
372                                 *dstptr++ = *srcptr++;
373                         }
374                         continue;
375                 }
376                 if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) {
377                         quot = *srcptr++;
378                         continue;
379                 }
380                 if (quot && *srcptr == quot) {
381                         /* End of the quoted part */
382                         quot = 0;
383                         srcptr++;
384                         continue;
385                 }
386                 if (!quot && strchr(delim, *srcptr))
387                         break;
388                 *dstptr++ = *srcptr++;
389         }
390
391         *stringp = (*srcptr == '\0') ? NULL : srcptr + 1;
392         *dstptr = 0; /* Terminate the string */
393         return (retval);
394 }
395
396 /*
397  * Mountd server for NFS mount protocol as described in:
398  * NFS: Network File System Protocol Specification, RFC1094, Appendix A
399  * The optional arguments are the exports file name
400  * default: _PATH_EXPORTS
401  * and "-n" to allow nonroot mount.
402  */
403 int
404 main(int argc, char **argv)
405 {
406         fd_set readfds;
407         struct netconfig *nconf;
408         char *endptr, **hosts_bak;
409         void *nc_handle;
410         pid_t otherpid;
411         in_port_t svcport;
412         int c, k, s;
413         int maxrec = RPC_MAXDATASIZE;
414         int attempt_cnt, port_len, port_pos, ret;
415         char **port_list;
416         uint64_t curtime, nexttime;
417         struct timeval tv;
418         struct timespec tp;
419         sigset_t sig_mask, sighup_mask;
420         int enable_rpcbind;
421
422         enable_rpcbind = 1;
423         /* Check that another mountd isn't already running. */
424         pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
425         if (pfh == NULL) {
426                 if (errno == EEXIST)
427                         errx(1, "mountd already running, pid: %d.", otherpid);
428                 warn("cannot open or create pidfile");
429         }
430
431         s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
432         if (s < 0)
433                 have_v6 = 0;
434         else
435                 close(s);
436
437         while ((c = getopt(argc, argv, "2deh:lnp:RrS")) != -1)
438                 switch (c) {
439                 case '2':
440                         force_v2 = 1;
441                         break;
442                 case 'e':
443                         /* now a no-op, since this is the default */
444                         break;
445                 case 'n':
446                         resvport_only = 0;
447                         break;
448                 case 'R':
449                         /* Do not support Mount protocol */
450                         enable_rpcbind = 0;
451                         break;
452                 case 'r':
453                         dir_only = 0;
454                         break;
455                 case 'd':
456                         debug = debug ? 0 : 1;
457                         break;
458                 case 'l':
459                         dolog = 1;
460                         break;
461                 case 'p':
462                         endptr = NULL;
463                         svcport = (in_port_t)strtoul(optarg, &endptr, 10);
464                         if (endptr == NULL || *endptr != '\0' ||
465                             svcport == 0 || svcport >= IPPORT_MAX)
466                                 usage();
467                         svcport_str = strdup(optarg);
468                         break;
469                 case 'h':
470                         ++nhosts;
471                         hosts_bak = hosts;
472                         hosts_bak = realloc(hosts, nhosts * sizeof(char *));
473                         if (hosts_bak == NULL) {
474                                 if (hosts != NULL) {
475                                         for (k = 0; k < nhosts; k++) 
476                                                 free(hosts[k]);
477                                         free(hosts);
478                                         out_of_mem();
479                                 }
480                         }
481                         hosts = hosts_bak;
482                         hosts[nhosts - 1] = strdup(optarg);
483                         if (hosts[nhosts - 1] == NULL) {
484                                 for (k = 0; k < (nhosts - 1); k++) 
485                                         free(hosts[k]);
486                                 free(hosts);
487                                 out_of_mem();
488                         }
489                         break;
490                 case 'S':
491                         suspend_nfsd = 1;
492                         break;
493                 default:
494                         usage();
495                 }
496         if (enable_rpcbind == 0) {
497                 if (svcport_str != NULL) {
498                         warnx("-p option not compatible with -R, ignored");
499                         free(svcport_str);
500                         svcport_str = NULL;
501                 }
502                 if (nhosts > 0) {
503                         warnx("-h option not compatible with -R, ignored");
504                         for (k = 0; k < nhosts; k++) 
505                                 free(hosts[k]);
506                         free(hosts);
507                         hosts = NULL;
508                         nhosts = 0;
509                 }
510         }
511
512         if (modfind("nfsd") < 0) {
513                 /* Not present in kernel, try loading it */
514                 if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
515                         errx(1, "NFS server is not available");
516         }
517
518         argc -= optind;
519         argv += optind;
520         if (argc > 0)
521                 exnames = argv;
522         else
523                 exnames = exnames_default;
524         openlog("mountd", LOG_PID, LOG_DAEMON);
525         if (debug)
526                 warnx("getting export list");
527         get_exportlist(0);
528         if (debug)
529                 warnx("getting mount list");
530         get_mountlist();
531         if (debug)
532                 warnx("here we go");
533         if (debug == 0) {
534                 daemon(0, 0);
535                 signal(SIGINT, SIG_IGN);
536                 signal(SIGQUIT, SIG_IGN);
537         }
538         signal(SIGHUP, huphandler);
539         signal(SIGTERM, terminate);
540         signal(SIGPIPE, SIG_IGN);
541
542         pidfile_write(pfh);
543
544         if (enable_rpcbind != 0) {
545                 rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
546                 rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
547                 rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
548
549                 if (!resvport_only) {
550                         if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL,
551                             &resvport_only, sizeof(resvport_only)) != 0 &&
552                             errno != ENOENT) {
553                                 syslog(LOG_ERR, "sysctl: %m");
554                                 exit(1);
555                         }
556                 }
557
558                 /*
559                  * If no hosts were specified, add a wildcard entry to bind to
560                  * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added
561                  * to the list.
562                  */
563                 if (nhosts == 0) {
564                         hosts = malloc(sizeof(char *));
565                         if (hosts == NULL)
566                                 out_of_mem();
567                         hosts[0] = "*";
568                         nhosts = 1;
569                 } else {
570                         hosts_bak = hosts;
571                         if (have_v6) {
572                                 hosts_bak = realloc(hosts, (nhosts + 2) *
573                                     sizeof(char *));
574                                 if (hosts_bak == NULL) {
575                                         for (k = 0; k < nhosts; k++)
576                                                 free(hosts[k]);
577                                         free(hosts);
578                                         out_of_mem();
579                                 } else
580                                         hosts = hosts_bak;
581                                 nhosts += 2;
582                                 hosts[nhosts - 2] = "::1";
583                         } else {
584                                 hosts_bak = realloc(hosts, (nhosts + 1) *
585                                     sizeof(char *));
586                                 if (hosts_bak == NULL) {
587                                         for (k = 0; k < nhosts; k++)
588                                                 free(hosts[k]);
589                                         free(hosts);
590                                         out_of_mem();
591                                 } else {
592                                         nhosts += 1;
593                                         hosts = hosts_bak;
594                                 }
595                         }
596
597                         hosts[nhosts - 1] = "127.0.0.1";
598                 }
599         }
600
601         attempt_cnt = 1;
602         sock_fdcnt = 0;
603         sock_fd = NULL;
604         port_list = NULL;
605         port_len = 0;
606         if (enable_rpcbind != 0) {
607                 nc_handle = setnetconfig();
608                 while ((nconf = getnetconfig(nc_handle))) {
609                         if (nconf->nc_flag & NC_VISIBLE) {
610                                 if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
611                                     "inet6") == 0) {
612                                         /* DO NOTHING */
613                                 } else {
614                                         ret = create_service(nconf);
615                                         if (ret == 1)
616                                                 /* Ignore this call */
617                                                 continue;
618                                         if (ret < 0) {
619                                                 /*
620                                                  * Failed to bind port, so close
621                                                  * off all sockets created and
622                                                  * try again if the port# was
623                                                  * dynamically assigned via
624                                                  * bind(2).
625                                                  */
626                                                 clearout_service();
627                                                 if (mallocd_svcport != 0 &&
628                                                     attempt_cnt <
629                                                     GETPORT_MAXTRY) {
630                                                         free(svcport_str);
631                                                         svcport_str = NULL;
632                                                         mallocd_svcport = 0;
633                                                 } else {
634                                                         errno = EADDRINUSE;
635                                                         syslog(LOG_ERR,
636                                                             "bindresvport_sa:"
637                                                             " %m");
638                                                         exit(1);
639                                                 }
640
641                                                 /*
642                                                  * Start over at the first
643                                                  * service.
644                                                  */
645                                                 free(sock_fd);
646                                                 sock_fdcnt = 0;
647                                                 sock_fd = NULL;
648                                                 nc_handle = setnetconfig();
649                                                 attempt_cnt++;
650                                         } else if (mallocd_svcport != 0 &&
651                                             attempt_cnt == GETPORT_MAXTRY) {
652                                                 /*
653                                                  * For the last attempt, allow
654                                                  * different port #s for each
655                                                  * nconf by saving the
656                                                  * svcport_str setting it back
657                                                  * to NULL.
658                                                  */
659                                                 port_list = realloc(port_list,
660                                                     (port_len + 1) *
661                                                     sizeof(char *));
662                                                 if (port_list == NULL)
663                                                         out_of_mem();
664                                                 port_list[port_len++] =
665                                                     svcport_str;
666                                                 svcport_str = NULL;
667                                                 mallocd_svcport = 0;
668                                         }
669                                 }
670                         }
671                 }
672
673                 /*
674                  * Successfully bound the ports, so call complete_service() to
675                  * do the rest of the setup on the service(s).
676                  */
677                 sock_fdpos = 0;
678                 port_pos = 0;
679                 nc_handle = setnetconfig();
680                 while ((nconf = getnetconfig(nc_handle))) {
681                         if (nconf->nc_flag & NC_VISIBLE) {
682                                 if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
683                                     "inet6") == 0) {
684                                         /* DO NOTHING */
685                                 } else if (port_list != NULL) {
686                                         if (port_pos >= port_len) {
687                                                 syslog(LOG_ERR, "too many"
688                                                     " port#s");
689                                                 exit(1);
690                                         }
691                                         complete_service(nconf,
692                                             port_list[port_pos++]);
693                                 } else
694                                         complete_service(nconf, svcport_str);
695                         }
696                 }
697                 endnetconfig(nc_handle);
698                 free(sock_fd);
699                 if (port_list != NULL) {
700                         for (port_pos = 0; port_pos < port_len; port_pos++)
701                                 free(port_list[port_pos]);
702                         free(port_list);
703                 }
704
705                 if (xcreated == 0) {
706                         syslog(LOG_ERR, "could not create any services");
707                         exit(1);
708                 }
709         }
710
711         /* Expand svc_run() here so that we can call get_exportlist(). */
712         curtime = nexttime = 0;
713         sigemptyset(&sighup_mask);
714         sigaddset(&sighup_mask, SIGHUP);
715         for (;;) {
716                 clock_gettime(CLOCK_MONOTONIC, &tp);
717                 curtime = tp.tv_sec;
718                 curtime = curtime * 1000000 + tp.tv_nsec / 1000;
719                 sigprocmask(SIG_BLOCK, &sighup_mask, &sig_mask);
720                 if (got_sighup && curtime >= nexttime) {
721                         got_sighup = 0;
722                         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
723                         get_exportlist(1);
724                         clock_gettime(CLOCK_MONOTONIC, &tp);
725                         nexttime = tp.tv_sec;
726                         nexttime = nexttime * 1000000 + tp.tv_nsec / 1000 +
727                             RELOADDELAY;
728                 } else
729                         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
730
731                 /*
732                  * If a reload is pending, poll for received request(s),
733                  * otherwise set a RELOADDELAY timeout, since a SIGHUP
734                  * could be processed between the got_sighup test and
735                  * the select() system call.
736                  */
737                 tv.tv_sec = 0;
738                 if (got_sighup)
739                         tv.tv_usec = 0;
740                 else
741                         tv.tv_usec = RELOADDELAY;
742                 if (enable_rpcbind != 0) {
743                         readfds = svc_fdset;
744                         switch (select(svc_maxfd + 1, &readfds, NULL, NULL,
745                             &tv)) {
746                         case -1:
747                                 if (errno == EINTR) {
748                                         /* Allow a reload now. */
749                                         nexttime = 0;
750                                         continue;
751                                 }
752                                 syslog(LOG_ERR, "mountd died: select: %m");
753                                 exit(1);
754                         case 0:
755                                 /* Allow a reload now. */
756                                 nexttime = 0;
757                                 continue;
758                         default:
759                                 svc_getreqset(&readfds);
760                         }
761                 } else {
762                         /* Simply wait for a signal. */
763                         sigsuspend(&sig_mask);
764                 }
765         }
766
767
768 /*
769  * This routine creates and binds sockets on the appropriate
770  * addresses. It gets called one time for each transport.
771  * It returns 0 upon success, 1 for ignore the call and -1 to indicate
772  * bind failed with EADDRINUSE.
773  * Any file descriptors that have been created are stored in sock_fd and
774  * the total count of them is maintained in sock_fdcnt.
775  */
776 static int
777 create_service(struct netconfig *nconf)
778 {
779         struct addrinfo hints, *res = NULL;
780         struct sockaddr_in *sin;
781         struct sockaddr_in6 *sin6;
782         struct __rpc_sockinfo si;
783         int aicode;
784         int fd;
785         int nhostsbak;
786         int one = 1;
787         int r;
788         u_int32_t host_addr[4];  /* IPv4 or IPv6 */
789         int mallocd_res;
790
791         if ((nconf->nc_semantics != NC_TPI_CLTS) &&
792             (nconf->nc_semantics != NC_TPI_COTS) &&
793             (nconf->nc_semantics != NC_TPI_COTS_ORD))
794                 return (1);     /* not my type */
795
796         /*
797          * XXX - using RPC library internal functions.
798          */
799         if (!__rpc_nconf2sockinfo(nconf, &si)) {
800                 syslog(LOG_ERR, "cannot get information for %s",
801                     nconf->nc_netid);
802                 return (1);
803         }
804
805         /* Get mountd's address on this transport */
806         memset(&hints, 0, sizeof hints);
807         hints.ai_family = si.si_af;
808         hints.ai_socktype = si.si_socktype;
809         hints.ai_protocol = si.si_proto;
810
811         /*
812          * Bind to specific IPs if asked to
813          */
814         nhostsbak = nhosts;
815         while (nhostsbak > 0) {
816                 --nhostsbak;
817                 sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
818                 if (sock_fd == NULL)
819                         out_of_mem();
820                 sock_fd[sock_fdcnt++] = -1;     /* Set invalid for now. */
821                 mallocd_res = 0;
822
823                 hints.ai_flags = AI_PASSIVE;
824
825                 /*      
826                  * XXX - using RPC library internal functions.
827                  */
828                 if ((fd = __rpc_nconf2fd(nconf)) < 0) {
829                         int non_fatal = 0;
830                         if (errno == EAFNOSUPPORT &&
831                             nconf->nc_semantics != NC_TPI_CLTS) 
832                                 non_fatal = 1;
833                                 
834                         syslog(non_fatal ? LOG_DEBUG : LOG_ERR, 
835                             "cannot create socket for %s", nconf->nc_netid);
836                         if (non_fatal != 0)
837                                 continue;
838                         exit(1);
839                 }
840
841                 switch (hints.ai_family) {
842                 case AF_INET:
843                         if (inet_pton(AF_INET, hosts[nhostsbak],
844                             host_addr) == 1) {
845                                 hints.ai_flags |= AI_NUMERICHOST;
846                         } else {
847                                 /*
848                                  * Skip if we have an AF_INET6 address.
849                                  */
850                                 if (inet_pton(AF_INET6, hosts[nhostsbak],
851                                     host_addr) == 1) {
852                                         close(fd);
853                                         continue;
854                                 }
855                         }
856                         break;
857                 case AF_INET6:
858                         if (inet_pton(AF_INET6, hosts[nhostsbak],
859                             host_addr) == 1) {
860                                 hints.ai_flags |= AI_NUMERICHOST;
861                         } else {
862                                 /*
863                                  * Skip if we have an AF_INET address.
864                                  */
865                                 if (inet_pton(AF_INET, hosts[nhostsbak],
866                                     host_addr) == 1) {
867                                         close(fd);
868                                         continue;
869                                 }
870                         }
871
872                         /*
873                          * We're doing host-based access checks here, so don't
874                          * allow v4-in-v6 to confuse things. The kernel will
875                          * disable it by default on NFS sockets too.
876                          */
877                         if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
878                             sizeof one) < 0) {
879                                 syslog(LOG_ERR,
880                                     "can't disable v4-in-v6 on IPv6 socket");
881                                 exit(1);
882                         }
883                         break;
884                 default:
885                         break;
886                 }
887
888                 /*
889                  * If no hosts were specified, just bind to INADDR_ANY
890                  */
891                 if (strcmp("*", hosts[nhostsbak]) == 0) {
892                         if (svcport_str == NULL) {
893                                 res = malloc(sizeof(struct addrinfo));
894                                 if (res == NULL) 
895                                         out_of_mem();
896                                 mallocd_res = 1;
897                                 res->ai_flags = hints.ai_flags;
898                                 res->ai_family = hints.ai_family;
899                                 res->ai_protocol = hints.ai_protocol;
900                                 switch (res->ai_family) {
901                                 case AF_INET:
902                                         sin = malloc(sizeof(struct sockaddr_in));
903                                         if (sin == NULL) 
904                                                 out_of_mem();
905                                         sin->sin_family = AF_INET;
906                                         sin->sin_port = htons(0);
907                                         sin->sin_addr.s_addr = htonl(INADDR_ANY);
908                                         res->ai_addr = (struct sockaddr*) sin;
909                                         res->ai_addrlen = (socklen_t)
910                                             sizeof(struct sockaddr_in);
911                                         break;
912                                 case AF_INET6:
913                                         sin6 = malloc(sizeof(struct sockaddr_in6));
914                                         if (sin6 == NULL)
915                                                 out_of_mem();
916                                         sin6->sin6_family = AF_INET6;
917                                         sin6->sin6_port = htons(0);
918                                         sin6->sin6_addr = in6addr_any;
919                                         res->ai_addr = (struct sockaddr*) sin6;
920                                         res->ai_addrlen = (socklen_t)
921                                             sizeof(struct sockaddr_in6);
922                                         break;
923                                 default:
924                                         syslog(LOG_ERR, "bad addr fam %d",
925                                             res->ai_family);
926                                         exit(1);
927                                 }
928                         } else { 
929                                 if ((aicode = getaddrinfo(NULL, svcport_str,
930                                     &hints, &res)) != 0) {
931                                         syslog(LOG_ERR,
932                                             "cannot get local address for %s: %s",
933                                             nconf->nc_netid,
934                                             gai_strerror(aicode));
935                                         close(fd);
936                                         continue;
937                                 }
938                         }
939                 } else {
940                         if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
941                             &hints, &res)) != 0) {
942                                 syslog(LOG_ERR,
943                                     "cannot get local address for %s: %s",
944                                     nconf->nc_netid, gai_strerror(aicode));
945                                 close(fd);
946                                 continue;
947                         }
948                 }
949
950                 /* Store the fd. */
951                 sock_fd[sock_fdcnt - 1] = fd;
952
953                 /* Now, attempt the bind. */
954                 r = bindresvport_sa(fd, res->ai_addr);
955                 if (r != 0) {
956                         if (errno == EADDRINUSE && mallocd_svcport != 0) {
957                                 if (mallocd_res != 0) {
958                                         free(res->ai_addr);
959                                         free(res);
960                                 } else
961                                         freeaddrinfo(res);
962                                 return (-1);
963                         }
964                         syslog(LOG_ERR, "bindresvport_sa: %m");
965                         exit(1);
966                 }
967
968                 if (svcport_str == NULL) {
969                         svcport_str = malloc(NI_MAXSERV * sizeof(char));
970                         if (svcport_str == NULL)
971                                 out_of_mem();
972                         mallocd_svcport = 1;
973
974                         if (getnameinfo(res->ai_addr,
975                             res->ai_addr->sa_len, NULL, NI_MAXHOST,
976                             svcport_str, NI_MAXSERV * sizeof(char),
977                             NI_NUMERICHOST | NI_NUMERICSERV))
978                                 errx(1, "Cannot get port number");
979                 }
980                 if (mallocd_res != 0) {
981                         free(res->ai_addr);
982                         free(res);
983                 } else
984                         freeaddrinfo(res);
985                 res = NULL;
986         }
987         return (0);
988 }
989
990 /*
991  * Called after all the create_service() calls have succeeded, to complete
992  * the setup and registration.
993  */
994 static void
995 complete_service(struct netconfig *nconf, char *port_str)
996 {
997         struct addrinfo hints, *res = NULL;
998         struct __rpc_sockinfo si;
999         struct netbuf servaddr;
1000         SVCXPRT *transp = NULL;
1001         int aicode, fd, nhostsbak;
1002         int registered = 0;
1003
1004         if ((nconf->nc_semantics != NC_TPI_CLTS) &&
1005             (nconf->nc_semantics != NC_TPI_COTS) &&
1006             (nconf->nc_semantics != NC_TPI_COTS_ORD))
1007                 return; /* not my type */
1008
1009         /*
1010          * XXX - using RPC library internal functions.
1011          */
1012         if (!__rpc_nconf2sockinfo(nconf, &si)) {
1013                 syslog(LOG_ERR, "cannot get information for %s",
1014                     nconf->nc_netid);
1015                 return;
1016         }
1017
1018         nhostsbak = nhosts;
1019         while (nhostsbak > 0) {
1020                 --nhostsbak;
1021                 if (sock_fdpos >= sock_fdcnt) {
1022                         /* Should never happen. */
1023                         syslog(LOG_ERR, "Ran out of socket fd's");
1024                         return;
1025                 }
1026                 fd = sock_fd[sock_fdpos++];
1027                 if (fd < 0)
1028                         continue;
1029
1030                 /*
1031                  * Using -1 tells listen(2) to use
1032                  * kern.ipc.soacceptqueue for the backlog.
1033                  */
1034                 if (nconf->nc_semantics != NC_TPI_CLTS)
1035                         listen(fd, -1);
1036
1037                 if (nconf->nc_semantics == NC_TPI_CLTS )
1038                         transp = svc_dg_create(fd, 0, 0);
1039                 else 
1040                         transp = svc_vc_create(fd, RPC_MAXDATASIZE,
1041                             RPC_MAXDATASIZE);
1042
1043                 if (transp != (SVCXPRT *) NULL) {
1044                         if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv,
1045                             NULL)) 
1046                                 syslog(LOG_ERR,
1047                                     "can't register %s MOUNTVERS service",
1048                                     nconf->nc_netid);
1049                         if (!force_v2) {
1050                                 if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3,
1051                                     mntsrv, NULL)) 
1052                                         syslog(LOG_ERR,
1053                                             "can't register %s MOUNTVERS3 service",
1054                                             nconf->nc_netid);
1055                         }
1056                 } else 
1057                         syslog(LOG_WARNING, "can't create %s services",
1058                             nconf->nc_netid);
1059
1060                 if (registered == 0) {
1061                         registered = 1;
1062                         memset(&hints, 0, sizeof hints);
1063                         hints.ai_flags = AI_PASSIVE;
1064                         hints.ai_family = si.si_af;
1065                         hints.ai_socktype = si.si_socktype;
1066                         hints.ai_protocol = si.si_proto;
1067
1068                         if ((aicode = getaddrinfo(NULL, port_str, &hints,
1069                             &res)) != 0) {
1070                                 syslog(LOG_ERR, "cannot get local address: %s",
1071                                     gai_strerror(aicode));
1072                                 exit(1);
1073                         }
1074
1075                         servaddr.buf = malloc(res->ai_addrlen);
1076                         memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
1077                         servaddr.len = res->ai_addrlen;
1078
1079                         rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr);
1080                         rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr);
1081
1082                         xcreated++;
1083                         freeaddrinfo(res);
1084                 }
1085         } /* end while */
1086 }
1087
1088 /*
1089  * Clear out sockets after a failure to bind one of them, so that the
1090  * cycle of socket creation/binding can start anew.
1091  */
1092 static void
1093 clearout_service(void)
1094 {
1095         int i;
1096
1097         for (i = 0; i < sock_fdcnt; i++) {
1098                 if (sock_fd[i] >= 0) {
1099                         shutdown(sock_fd[i], SHUT_RDWR);
1100                         close(sock_fd[i]);
1101                 }
1102         }
1103 }
1104
1105 static void
1106 usage(void)
1107 {
1108         fprintf(stderr,
1109                 "usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
1110                 "[-S] [-h <bindip>] [export_file ...]\n");
1111         exit(1);
1112 }
1113
1114 /*
1115  * The mount rpc service
1116  */
1117 void
1118 mntsrv(struct svc_req *rqstp, SVCXPRT *transp)
1119 {
1120         struct exportlist *ep;
1121         struct dirlist *dp;
1122         struct fhreturn fhr;
1123         struct stat stb;
1124         struct statfs fsb;
1125         char host[NI_MAXHOST], numerichost[NI_MAXHOST];
1126         int lookup_failed = 1;
1127         struct sockaddr *saddr;
1128         u_short sport;
1129         char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
1130         int defset, hostset;
1131         long bad = 0;
1132         sigset_t sighup_mask;
1133         int numsecflavors, *secflavorsp;
1134
1135         sigemptyset(&sighup_mask);
1136         sigaddset(&sighup_mask, SIGHUP);
1137         saddr = svc_getrpccaller(transp)->buf;
1138         switch (saddr->sa_family) {
1139         case AF_INET6:
1140                 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
1141                 break;
1142         case AF_INET:
1143                 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
1144                 break;
1145         default:
1146                 syslog(LOG_ERR, "request from unknown address family");
1147                 return;
1148         }
1149         switch (rqstp->rq_proc) {
1150         case MOUNTPROC_MNT:
1151         case MOUNTPROC_UMNT:
1152         case MOUNTPROC_UMNTALL:
1153                 lookup_failed = getnameinfo(saddr, saddr->sa_len, host,
1154                     sizeof host, NULL, 0, 0);
1155         }
1156         getnameinfo(saddr, saddr->sa_len, numerichost,
1157             sizeof numerichost, NULL, 0, NI_NUMERICHOST);
1158         switch (rqstp->rq_proc) {
1159         case NULLPROC:
1160                 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
1161                         syslog(LOG_ERR, "can't send reply");
1162                 return;
1163         case MOUNTPROC_MNT:
1164                 if (sport >= IPPORT_RESERVED && resvport_only) {
1165                         syslog(LOG_NOTICE,
1166                             "mount request from %s from unprivileged port",
1167                             numerichost);
1168                         svcerr_weakauth(transp);
1169                         return;
1170                 }
1171                 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1172                         syslog(LOG_NOTICE, "undecodable mount request from %s",
1173                             numerichost);
1174                         svcerr_decode(transp);
1175                         return;
1176                 }
1177
1178                 /*
1179                  * Get the real pathname and make sure it is a directory
1180                  * or a regular file if the -r option was specified
1181                  * and it exists.
1182                  */
1183                 if (realpath(rpcpath, dirpath) == NULL ||
1184                     stat(dirpath, &stb) < 0 ||
1185                     statfs(dirpath, &fsb) < 0) {
1186                         chdir("/");     /* Just in case realpath doesn't */
1187                         syslog(LOG_NOTICE,
1188                             "mount request from %s for non existent path %s",
1189                             numerichost, dirpath);
1190                         if (debug)
1191                                 warnx("stat failed on %s", dirpath);
1192                         bad = ENOENT;   /* We will send error reply later */
1193                 }
1194                 if (!bad &&
1195                     !S_ISDIR(stb.st_mode) &&
1196                     (dir_only || !S_ISREG(stb.st_mode))) {
1197                         syslog(LOG_NOTICE,
1198                             "mount request from %s for non-directory path %s",
1199                             numerichost, dirpath);
1200                         if (debug)
1201                                 warnx("mounting non-directory %s", dirpath);
1202                         bad = ENOTDIR;  /* We will send error reply later */
1203                 }
1204
1205                 /* Check in the exports list */
1206                 sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1207                 if (bad)
1208                         ep = NULL;
1209                 else
1210                         ep = ex_search(&fsb.f_fsid, exphead);
1211                 hostset = defset = 0;
1212                 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset,
1213                     &numsecflavors, &secflavorsp) ||
1214                     ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
1215                       chk_host(dp, saddr, &defset, &hostset, &numsecflavors,
1216                        &secflavorsp)) ||
1217                     (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
1218                      scan_tree(ep->ex_dirl, saddr) == 0))) {
1219                         if (bad) {
1220                                 if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1221                                     (caddr_t)&bad))
1222                                         syslog(LOG_ERR, "can't send reply");
1223                                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1224                                 return;
1225                         }
1226                         if (hostset & DP_HOSTSET) {
1227                                 fhr.fhr_flag = hostset;
1228                                 fhr.fhr_numsecflavors = numsecflavors;
1229                                 fhr.fhr_secflavors = secflavorsp;
1230                         } else {
1231                                 fhr.fhr_flag = defset;
1232                                 fhr.fhr_numsecflavors = ep->ex_defnumsecflavors;
1233                                 fhr.fhr_secflavors = ep->ex_defsecflavors;
1234                         }
1235                         fhr.fhr_vers = rqstp->rq_vers;
1236                         /* Get the file handle */
1237                         memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
1238                         if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
1239                                 bad = errno;
1240                                 syslog(LOG_ERR, "can't get fh for %s", dirpath);
1241                                 if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1242                                     (caddr_t)&bad))
1243                                         syslog(LOG_ERR, "can't send reply");
1244                                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1245                                 return;
1246                         }
1247                         if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
1248                             (caddr_t)&fhr))
1249                                 syslog(LOG_ERR, "can't send reply");
1250                         if (!lookup_failed)
1251                                 add_mlist(host, dirpath);
1252                         else
1253                                 add_mlist(numerichost, dirpath);
1254                         if (debug)
1255                                 warnx("mount successful");
1256                         if (dolog)
1257                                 syslog(LOG_NOTICE,
1258                                     "mount request succeeded from %s for %s",
1259                                     numerichost, dirpath);
1260                 } else {
1261                         if (!bad)
1262                                 bad = EACCES;
1263                         syslog(LOG_NOTICE,
1264                             "mount request denied from %s for %s",
1265                             numerichost, dirpath);
1266                 }
1267
1268                 if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
1269                     (caddr_t)&bad))
1270                         syslog(LOG_ERR, "can't send reply");
1271                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1272                 return;
1273         case MOUNTPROC_DUMP:
1274                 if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
1275                         syslog(LOG_ERR, "can't send reply");
1276                 else if (dolog)
1277                         syslog(LOG_NOTICE,
1278                             "dump request succeeded from %s",
1279                             numerichost);
1280                 return;
1281         case MOUNTPROC_UMNT:
1282                 if (sport >= IPPORT_RESERVED && resvport_only) {
1283                         syslog(LOG_NOTICE,
1284                             "umount request from %s from unprivileged port",
1285                             numerichost);
1286                         svcerr_weakauth(transp);
1287                         return;
1288                 }
1289                 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1290                         syslog(LOG_NOTICE, "undecodable umount request from %s",
1291                             numerichost);
1292                         svcerr_decode(transp);
1293                         return;
1294                 }
1295                 if (realpath(rpcpath, dirpath) == NULL) {
1296                         syslog(LOG_NOTICE, "umount request from %s "
1297                             "for non existent path %s",
1298                             numerichost, dirpath);
1299                 }
1300                 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1301                         syslog(LOG_ERR, "can't send reply");
1302                 if (!lookup_failed)
1303                         del_mlist(host, dirpath);
1304                 del_mlist(numerichost, dirpath);
1305                 if (dolog)
1306                         syslog(LOG_NOTICE,
1307                             "umount request succeeded from %s for %s",
1308                             numerichost, dirpath);
1309                 return;
1310         case MOUNTPROC_UMNTALL:
1311                 if (sport >= IPPORT_RESERVED && resvport_only) {
1312                         syslog(LOG_NOTICE,
1313                             "umountall request from %s from unprivileged port",
1314                             numerichost);
1315                         svcerr_weakauth(transp);
1316                         return;
1317                 }
1318                 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1319                         syslog(LOG_ERR, "can't send reply");
1320                 if (!lookup_failed)
1321                         del_mlist(host, NULL);
1322                 del_mlist(numerichost, NULL);
1323                 if (dolog)
1324                         syslog(LOG_NOTICE,
1325                             "umountall request succeeded from %s",
1326                             numerichost);
1327                 return;
1328         case MOUNTPROC_EXPORT:
1329                 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
1330                         if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
1331                             (caddr_t)NULL))
1332                                 syslog(LOG_ERR, "can't send reply");
1333                 if (dolog)
1334                         syslog(LOG_NOTICE,
1335                             "export request succeeded from %s",
1336                             numerichost);
1337                 return;
1338         default:
1339                 svcerr_noproc(transp);
1340                 return;
1341         }
1342 }
1343
1344 /*
1345  * Xdr conversion for a dirpath string
1346  */
1347 static int
1348 xdr_dir(XDR *xdrsp, char *dirp)
1349 {
1350         return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
1351 }
1352
1353 /*
1354  * Xdr routine to generate file handle reply
1355  */
1356 static int
1357 xdr_fhs(XDR *xdrsp, caddr_t cp)
1358 {
1359         struct fhreturn *fhrp = (struct fhreturn *)cp;
1360         u_long ok = 0, len, auth;
1361         int i;
1362
1363         if (!xdr_long(xdrsp, &ok))
1364                 return (0);
1365         switch (fhrp->fhr_vers) {
1366         case 1:
1367                 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
1368         case 3:
1369                 len = NFSX_V3FH;
1370                 if (!xdr_long(xdrsp, &len))
1371                         return (0);
1372                 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
1373                         return (0);
1374                 if (fhrp->fhr_numsecflavors) {
1375                         if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
1376                                 return (0);
1377                         for (i = 0; i < fhrp->fhr_numsecflavors; i++)
1378                                 if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
1379                                         return (0);
1380                         return (1);
1381                 } else {
1382                         auth = AUTH_SYS;
1383                         len = 1;
1384                         if (!xdr_long(xdrsp, &len))
1385                                 return (0);
1386                         return (xdr_long(xdrsp, &auth));
1387                 }
1388         }
1389         return (0);
1390 }
1391
1392 static int
1393 xdr_mlist(XDR *xdrsp, caddr_t cp __unused)
1394 {
1395         struct mountlist *mlp;
1396         int true = 1;
1397         int false = 0;
1398         char *strp;
1399
1400         SLIST_FOREACH(mlp, &mlhead, next) {
1401                 if (!xdr_bool(xdrsp, &true))
1402                         return (0);
1403                 strp = &mlp->ml_host[0];
1404                 if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
1405                         return (0);
1406                 strp = &mlp->ml_dirp[0];
1407                 if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1408                         return (0);
1409         }
1410         if (!xdr_bool(xdrsp, &false))
1411                 return (0);
1412         return (1);
1413 }
1414
1415 /*
1416  * Xdr conversion for export list
1417  */
1418 static int
1419 xdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
1420 {
1421         struct exportlist *ep;
1422         int false = 0;
1423         int putdef;
1424         sigset_t sighup_mask;
1425         int i;
1426
1427         sigemptyset(&sighup_mask);
1428         sigaddset(&sighup_mask, SIGHUP);
1429         sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1430
1431         for (i = 0; i < exphashsize; i++)
1432                 SLIST_FOREACH(ep, &exphead[i], entries) {
1433                         putdef = 0;
1434                         if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1435                                        &putdef, brief))
1436                                 goto errout;
1437                         if (ep->ex_defdir && putdef == 0 &&
1438                                 put_exlist(ep->ex_defdir, xdrsp, NULL,
1439                                 &putdef, brief))
1440                                 goto errout;
1441                 }
1442         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1443         if (!xdr_bool(xdrsp, &false))
1444                 return (0);
1445         return (1);
1446 errout:
1447         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1448         return (0);
1449 }
1450
1451 /*
1452  * Called from xdr_explist() to traverse the tree and export the
1453  * directory paths.
1454  */
1455 static int
1456 put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp,
1457         int brief)
1458 {
1459         struct grouplist *grp;
1460         struct hostlist *hp;
1461         int true = 1;
1462         int false = 0;
1463         int gotalldir = 0;
1464         char *strp;
1465
1466         if (dp) {
1467                 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
1468                         return (1);
1469                 if (!xdr_bool(xdrsp, &true))
1470                         return (1);
1471                 strp = dp->dp_dirp;
1472                 if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1473                         return (1);
1474                 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
1475                         gotalldir = 1;
1476                         *putdefp = 1;
1477                 }
1478                 if (brief) {
1479                         if (!xdr_bool(xdrsp, &true))
1480                                 return (1);
1481                         strp = "(...)";
1482                         if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1483                                 return (1);
1484                 } else if ((dp->dp_flag & DP_DEFSET) == 0 &&
1485                     (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
1486                         hp = dp->dp_hosts;
1487                         while (hp) {
1488                                 grp = hp->ht_grp;
1489                                 if (grp->gr_type == GT_HOST) {
1490                                         if (!xdr_bool(xdrsp, &true))
1491                                                 return (1);
1492                                         strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
1493                                         if (!xdr_string(xdrsp, &strp,
1494                                             MNTNAMLEN))
1495                                                 return (1);
1496                                 } else if (grp->gr_type == GT_NET) {
1497                                         if (!xdr_bool(xdrsp, &true))
1498                                                 return (1);
1499                                         strp = grp->gr_ptr.gt_net.nt_name;
1500                                         if (!xdr_string(xdrsp, &strp,
1501                                             MNTNAMLEN))
1502                                                 return (1);
1503                                 }
1504                                 hp = hp->ht_next;
1505                                 if (gotalldir && hp == (struct hostlist *)NULL) {
1506                                         hp = adp->dp_hosts;
1507                                         gotalldir = 0;
1508                                 }
1509                         }
1510                 }
1511                 if (!xdr_bool(xdrsp, &false))
1512                         return (1);
1513                 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
1514                         return (1);
1515         }
1516         return (0);
1517 }
1518
1519 static int
1520 xdr_explist(XDR *xdrsp, caddr_t cp)
1521 {
1522
1523         return xdr_explist_common(xdrsp, cp, 0);
1524 }
1525
1526 static int
1527 xdr_explist_brief(XDR *xdrsp, caddr_t cp)
1528 {
1529
1530         return xdr_explist_common(xdrsp, cp, 1);
1531 }
1532
1533 static char *line;
1534 static size_t linesize;
1535 static FILE *exp_file;
1536
1537 /*
1538  * Get the export list from one, currently open file
1539  */
1540 static void
1541 get_exportlist_one(int passno)
1542 {
1543         struct exportlist *ep;
1544         struct grouplist *grp, *tgrp, *savgrp;
1545         struct dirlist *dirhead;
1546         struct statfs fsb;
1547         struct expcred anon;
1548         char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1549         char *err_msg = NULL;
1550         int len, has_host, got_nondir, dirplen, netgrp;
1551         uint64_t exflags;
1552         char unvis_dir[PATH_MAX + 1];
1553         int unvis_len;
1554
1555         v4root_phase = 0;
1556         anon.cr_groups = NULL;
1557         dirhead = (struct dirlist *)NULL;
1558         unvis_dir[0] = '\0';
1559         while (get_line()) {
1560                 if (debug)
1561                         warnx("got line %s", line);
1562                 cp = line;
1563                 nextfield(&cp, &endcp);
1564                 if (*cp == '#')
1565                         goto nextline;
1566
1567                 /*
1568                  * Set defaults.
1569                  */
1570                 has_host = FALSE;
1571                 anon.cr_groups = anon.cr_smallgrps;
1572                 anon.cr_uid = UID_NOBODY;
1573                 anon.cr_ngroups = 1;
1574                 anon.cr_groups[0] = GID_NOGROUP;
1575                 exflags = MNT_EXPORTED;
1576                 got_nondir = 0;
1577                 opt_flags = 0;
1578                 ep = (struct exportlist *)NULL;
1579                 dirp = NULL;
1580
1581                 /*
1582                  * Handle the V4 root dir.
1583                  */
1584                 if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1585                         /*
1586                          * V4: just indicates that it is the v4 root point,
1587                          * so skip over that and set v4root_phase.
1588                          */
1589                         if (v4root_phase > 0) {
1590                                 syslog(LOG_ERR, "V4:duplicate line, ignored");
1591                                 goto nextline;
1592                         }
1593                         v4root_phase = 1;
1594                         cp += 3;
1595                         nextfield(&cp, &endcp);
1596                 }
1597
1598                 /*
1599                  * Create new exports list entry
1600                  */
1601                 len = endcp-cp;
1602                 tgrp = grp = get_grp();
1603                 while (len > 0) {
1604                         if (len > MNTNAMLEN) {
1605                             getexp_err(ep, tgrp, "mountpoint too long");
1606                             goto nextline;
1607                         }
1608                         if (*cp == '-') {
1609                             if (ep == (struct exportlist *)NULL) {
1610                                 getexp_err(ep, tgrp,
1611                                     "flag before export path definition");
1612                                 goto nextline;
1613                             }
1614                             if (debug)
1615                                 warnx("doing opt %s", cp);
1616                             got_nondir = 1;
1617                             if (do_opt(&cp, &endcp, ep, grp, &has_host,
1618                                 &exflags, &anon)) {
1619                                 getexp_err(ep, tgrp, NULL);
1620                                 goto nextline;
1621                             }
1622                         } else if (*cp == '/') {
1623                             savedc = *endcp;
1624                             *endcp = '\0';
1625                             unvis_len = strnunvis(unvis_dir, sizeof(unvis_dir),
1626                                 cp);
1627                             if (unvis_len <= 0) {
1628                                 getexp_err(ep, tgrp, "Cannot strunvis "
1629                                     "decode dir");
1630                                 goto nextline;
1631                             }
1632                             if (v4root_phase > 1) {
1633                                     if (dirp != NULL) {
1634                                         getexp_err(ep, tgrp, "Multiple V4 dirs");
1635                                         goto nextline;
1636                                     }
1637                             }
1638                             if (check_dirpath(unvis_dir, &err_msg) &&
1639                                 check_statfs(unvis_dir, &fsb, &err_msg)) {
1640                                 if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0)
1641                                     syslog(LOG_ERR, "Warning: exporting of "
1642                                         "automounted fs %s not supported",
1643                                         unvis_dir);
1644                                 if (got_nondir) {
1645                                     getexp_err(ep, tgrp, "dirs must be first");
1646                                     goto nextline;
1647                                 }
1648                                 if (v4root_phase == 1) {
1649                                     if (dirp != NULL) {
1650                                         getexp_err(ep, tgrp, "Multiple V4 dirs");
1651                                         goto nextline;
1652                                     }
1653                                     if (strlen(v4root_dirpath) == 0) {
1654                                         strlcpy(v4root_dirpath, unvis_dir,
1655                                             sizeof (v4root_dirpath));
1656                                     } else if (strcmp(v4root_dirpath, unvis_dir)
1657                                         != 0) {
1658                                         syslog(LOG_ERR,
1659                                             "different V4 dirpath %s",
1660                                             unvis_dir);
1661                                         getexp_err(ep, tgrp, NULL);
1662                                         goto nextline;
1663                                     }
1664                                     dirp = unvis_dir;
1665                                     v4root_phase = 2;
1666                                     got_nondir = 1;
1667                                     ep = get_exp();
1668                                 } else {
1669                                     if (ep) {
1670                                         if (fsidcmp(&ep->ex_fs, &fsb.f_fsid)
1671                                             != 0) {
1672                                                 getexp_err(ep, tgrp,
1673                                                     "fsid mismatch");
1674                                                 goto nextline;
1675                                         }
1676                                     } else {
1677                                         /*
1678                                          * See if this directory is already
1679                                          * in the list.
1680                                          */
1681                                         ep = ex_search(&fsb.f_fsid, exphead);
1682                                         if (ep == (struct exportlist *)NULL) {
1683                                             ep = get_exp();
1684                                             ep->ex_fs = fsb.f_fsid;
1685                                             ep->ex_fsdir = strdup(fsb.f_mntonname);
1686                                             if (ep->ex_fsdir == NULL)
1687                                                 out_of_mem();
1688                                             if (debug)
1689                                                 warnx(
1690                                                   "making new ep fs=0x%x,0x%x",
1691                                                   fsb.f_fsid.val[0],
1692                                                   fsb.f_fsid.val[1]);
1693                                         } else if (debug)
1694                                             warnx("found ep fs=0x%x,0x%x",
1695                                                 fsb.f_fsid.val[0],
1696                                                 fsb.f_fsid.val[1]);
1697                                     }
1698
1699                                     /*
1700                                      * Add dirpath to export mount point.
1701                                      */
1702                                     dirp = add_expdir(&dirhead, unvis_dir,
1703                                         unvis_len);
1704                                     dirplen = unvis_len;
1705                                 }
1706                             } else {
1707                                 if (err_msg != NULL) {
1708                                         getexp_err(ep, tgrp, err_msg);
1709                                         free(err_msg);
1710                                         err_msg = NULL;
1711                                 } else {
1712                                         getexp_err(ep, tgrp,
1713                                             "symbolic link in export path or "
1714                                             "statfs failed");
1715                                 }
1716                                 goto nextline;
1717                             }
1718                             *endcp = savedc;
1719                         } else {
1720                             savedc = *endcp;
1721                             *endcp = '\0';
1722                             got_nondir = 1;
1723                             if (ep == (struct exportlist *)NULL) {
1724                                 getexp_err(ep, tgrp,
1725                                     "host(s) before export path definition");
1726                                 goto nextline;
1727                             }
1728
1729                             /*
1730                              * Get the host or netgroup.
1731                              */
1732                             setnetgrent(cp);
1733                             netgrp = getnetgrent(&hst, &usr, &dom);
1734                             do {
1735                                 if (has_host) {
1736                                     grp->gr_next = get_grp();
1737                                     grp = grp->gr_next;
1738                                 }
1739                                 if (netgrp) {
1740                                     if (hst == 0) {
1741                                         syslog(LOG_ERR,
1742                                 "null hostname in netgroup %s, skipping", cp);
1743                                         grp->gr_type = GT_IGNORE;
1744                                     } else if (get_host(hst, grp, tgrp)) {
1745                                         syslog(LOG_ERR,
1746                         "bad host %s in netgroup %s, skipping", hst, cp);
1747                                         grp->gr_type = GT_IGNORE;
1748                                     }
1749                                 } else if (get_host(cp, grp, tgrp)) {
1750                                     syslog(LOG_ERR, "bad host %s, skipping", cp);
1751                                     grp->gr_type = GT_IGNORE;
1752                                 }
1753                                 has_host = TRUE;
1754                             } while (netgrp && getnetgrent(&hst, &usr, &dom));
1755                             endnetgrent();
1756                             *endcp = savedc;
1757                         }
1758                         cp = endcp;
1759                         nextfield(&cp, &endcp);
1760                         len = endcp - cp;
1761                 }
1762                 if (opt_flags & OP_CLASSMASK)
1763                         syslog(LOG_WARNING,
1764                             "WARNING: No mask specified for %s, "
1765                             "using out-of-date default",
1766                             (&grp->gr_ptr.gt_net)->nt_name);
1767                 if (check_options(dirhead)) {
1768                         getexp_err(ep, tgrp, NULL);
1769                         goto nextline;
1770                 }
1771                 if (!has_host) {
1772                         grp->gr_type = GT_DEFAULT;
1773                         if (debug)
1774                                 warnx("adding a default entry");
1775
1776                 /*
1777                  * Don't allow a network export coincide with a list of
1778                  * host(s) on the same line.
1779                  */
1780                 } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1781                         getexp_err(ep, tgrp, "network/host conflict");
1782                         goto nextline;
1783
1784                 /*
1785                  * If an export list was specified on this line, make sure
1786                  * that we have at least one valid entry, otherwise skip it.
1787                  */
1788                 } else {
1789                         grp = tgrp;
1790                         while (grp && grp->gr_type == GT_IGNORE)
1791                                 grp = grp->gr_next;
1792                         if (! grp) {
1793                             getexp_err(ep, tgrp, "no valid entries");
1794                             goto nextline;
1795                         }
1796                 }
1797
1798                 if (v4root_phase == 1) {
1799                         getexp_err(ep, tgrp, "V4:root, no dirp, ignored");
1800                         goto nextline;
1801                 }
1802
1803                 /*
1804                  * Loop through hosts, pushing the exports into the kernel.
1805                  * After loop, tgrp points to the start of the list and
1806                  * grp points to the last entry in the list.
1807                  * Do not do the do_mount() for passno == 1, since the
1808                  * second pass will do it, as required.
1809                  */
1810                 grp = tgrp;
1811                 do {
1812                         grp->gr_exflags = exflags;
1813                         cp_cred(&grp->gr_anon, &anon);
1814                         if (v4root_phase == 2 && passno == 0)
1815                                 LOGDEBUG("do_mount v4root");
1816                         if (passno == 0 && do_mount(ep, grp, exflags, &anon,
1817                             dirp, dirplen, &fsb, ep->ex_numsecflavors,
1818                             ep->ex_secflavors)) {
1819                                 getexp_err(ep, tgrp, NULL);
1820                                 goto nextline;
1821                         }
1822                 } while (grp->gr_next && (grp = grp->gr_next));
1823
1824                 /*
1825                  * For V4: don't enter in mount lists.
1826                  */
1827                 if (v4root_phase > 0 && v4root_phase <= 2) {
1828                         /*
1829                          * These structures are used for the reload,
1830                          * so save them for that case.  Otherwise, just
1831                          * free them up now.
1832                          */
1833                         if (passno == 1 && ep != NULL) {
1834                                 savgrp = tgrp;
1835                                 while (tgrp != NULL) {
1836                                         /*
1837                                          * Save the security flavors and exflags
1838                                          * for this host set in the groups.
1839                                          */
1840                                         tgrp->gr_numsecflavors =
1841                                             ep->ex_numsecflavors;
1842                                         if (ep->ex_numsecflavors > 0)
1843                                                 memcpy(tgrp->gr_secflavors,
1844                                                     ep->ex_secflavors,
1845                                                     sizeof(ep->ex_secflavors));
1846                                         tgrp = tgrp->gr_next;
1847                                 }
1848                                 if (v4root_ep == NULL) {
1849                                         v4root_ep = ep;
1850                                         ep = NULL;      /* Don't free below. */
1851                                 }
1852                                 grp->gr_next = v4root_ep->ex_grphead;
1853                                 v4root_ep->ex_grphead = savgrp;
1854                         }
1855                         if (ep != NULL)
1856                                 free_exp(ep);
1857                         while (tgrp != NULL) {
1858                                 grp = tgrp;
1859                                 tgrp = tgrp->gr_next;
1860                                 free_grp(grp);
1861                         }
1862                         goto nextline;
1863                 }
1864
1865                 /*
1866                  * Success. Update the data structures.
1867                  */
1868                 if (has_host) {
1869                         hang_dirp(dirhead, tgrp, ep, opt_flags, &anon, exflags);
1870                         grp->gr_next = ep->ex_grphead;
1871                         ep->ex_grphead = tgrp;
1872                 } else {
1873                         hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1874                                 opt_flags, &anon, exflags);
1875                         free_grp(grp);
1876                 }
1877                 dirhead = (struct dirlist *)NULL;
1878                 if ((ep->ex_flag & EX_LINKED) == 0) {
1879                         insert_exports(ep, exphead);
1880
1881                         ep->ex_flag |= EX_LINKED;
1882                 }
1883 nextline:
1884                 v4root_phase = 0;
1885                 if (dirhead) {
1886                         free_dir(dirhead);
1887                         dirhead = (struct dirlist *)NULL;
1888                 }
1889                 if (anon.cr_groups != anon.cr_smallgrps) {
1890                         free(anon.cr_groups);
1891                         anon.cr_groups = NULL;
1892                 }
1893         }
1894 }
1895
1896 /*
1897  * Get the export list from all specified files
1898  */
1899 static void
1900 get_exportlist(int passno)
1901 {
1902         struct export_args export;
1903         struct iovec *iov;
1904         struct statfs *mntbufp;
1905         char errmsg[255];
1906         int error, i, nfs_maxvers, num;
1907         int iovlen;
1908         struct nfsex_args eargs;
1909         FILE *debug_file;
1910         size_t nfs_maxvers_size;
1911
1912         if ((debug_file = fopen(_PATH_MOUNTDDEBUG, "r")) != NULL) {
1913                 fclose(debug_file);
1914                 logdebug = 1;
1915         } else
1916                 logdebug = 0;
1917         LOGDEBUG("passno=%d", passno);
1918         v4root_dirpath[0] = '\0';
1919         free_v4rootexp();
1920         if (passno == 1) {
1921                 /*
1922                  * Save the current lists as old ones, so that the new lists
1923                  * can be compared with the old ones in the 2nd pass.
1924                  */
1925                 for (i = 0; i < exphashsize; i++) {
1926                         SLIST_FIRST(&oldexphead[i]) = SLIST_FIRST(&exphead[i]);
1927                         SLIST_INIT(&exphead[i]);
1928                 }
1929
1930                 /* Note that the public fh has not yet been set. */
1931                 has_set_publicfh = 0;
1932
1933                 /* Read the export file(s) and process them */
1934                 read_exportfile(passno);
1935         } else {
1936                 /*
1937                  * Just make the old lists empty.
1938                  * exphashsize == 0 for the first call, before oldexphead
1939                  * has been initialized-->loop won't be executed.
1940                  */
1941                 for (i = 0; i < exphashsize; i++)
1942                         SLIST_INIT(&oldexphead[i]);
1943         }
1944
1945         bzero(&export, sizeof(export));
1946         export.ex_flags = MNT_DELEXPORT;
1947         iov = NULL;
1948         iovlen = 0;
1949         bzero(errmsg, sizeof(errmsg));
1950
1951         if (suspend_nfsd != 0)
1952                 (void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
1953         /*
1954          * Delete the old V4 root dir.
1955          */
1956         bzero(&eargs, sizeof (eargs));
1957         eargs.export.ex_flags = MNT_DELEXPORT;
1958         if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT, (caddr_t)&eargs) < 0 &&
1959             errno != ENOENT)
1960                 syslog(LOG_ERR, "Can't delete exports for V4:");
1961
1962         build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1963         build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1964         build_iovec(&iov, &iovlen, "from", NULL, 0);
1965         build_iovec(&iov, &iovlen, "update", NULL, 0);
1966         build_iovec(&iov, &iovlen, "export", &export,
1967             sizeof(export));
1968         build_iovec(&iov, &iovlen, "errmsg", errmsg,
1969             sizeof(errmsg));
1970
1971         /*
1972          * For passno == 1, compare the old and new lists updating the kernel
1973          * exports for any cases that have changed.
1974          * This call is doing the second pass through the lists.
1975          * If it fails, fall back on the bulk reload.
1976          */
1977         if (passno == 1 && compare_nmount_exportlist(iov, iovlen, errmsg) ==
1978             0) {
1979                 LOGDEBUG("compareok");
1980                 /* Free up the old lists. */
1981                 free_exports(oldexphead);
1982         } else {
1983                 LOGDEBUG("doing passno=0");
1984                 /*
1985                  * Clear flag that notes if a public fh has been exported.
1986                  * It is set by do_mount() if MNT_EXPUBLIC is set for the entry.
1987                  */
1988                 has_publicfh = 0;
1989
1990                 /* exphead == NULL if not yet allocated (first call). */
1991                 if (exphead != NULL) {
1992                         /*
1993                          * First, get rid of the old lists.
1994                          */
1995                         free_exports(exphead);
1996                         free_exports(oldexphead);
1997                 }
1998
1999                 /*
2000                  * And delete exports that are in the kernel for all local
2001                  * filesystems.
2002                  * XXX: Should know how to handle all local exportable
2003                  * filesystems.
2004                  */
2005                 num = getmntinfo(&mntbufp, MNT_NOWAIT);
2006
2007                 /* Allocate hash tables, for first call. */
2008                 if (exphead == NULL) {
2009                         /* Target an average linked list length of 10. */
2010                         exphashsize = num / 10;
2011                         if (exphashsize < 1)
2012                                 exphashsize = 1;
2013                         else if (exphashsize > 100000)
2014                                 exphashsize = 100000;
2015                         exphead = malloc(exphashsize * sizeof(*exphead));
2016                         oldexphead = malloc(exphashsize * sizeof(*oldexphead));
2017                         if (exphead == NULL || oldexphead == NULL)
2018                                 errx(1, "Can't malloc hash tables");
2019
2020                         for (i = 0; i < exphashsize; i++) {
2021                                 SLIST_INIT(&exphead[i]);
2022                                 SLIST_INIT(&oldexphead[i]);
2023                         }
2024                 }
2025         
2026                 for (i = 0; i < num; i++)
2027                         delete_export(iov, iovlen, &mntbufp[i], errmsg);
2028
2029
2030                 /* Read the export file(s) and process them */
2031                 read_exportfile(0);
2032         }
2033
2034         if (strlen(v4root_dirpath) == 0) {
2035                 /* Check to see if a V4: line is needed. */
2036                 nfs_maxvers_size = sizeof(nfs_maxvers);
2037                 error = sysctlbyname("vfs.nfsd.server_max_nfsvers",
2038                     &nfs_maxvers, &nfs_maxvers_size, NULL, 0);
2039                 if (error != 0 || nfs_maxvers < NFS_VER2 || nfs_maxvers >
2040                     NFS_VER4) {
2041                         syslog(LOG_ERR, "sysctlbyname(vfs.nfsd."
2042                             "server_max_nfsvers) failed, defaulting to NFSv3");
2043                         nfs_maxvers = NFS_VER3;
2044                 }
2045                 if (nfs_maxvers == NFS_VER4)
2046                         syslog(LOG_ERR, "NFSv4 requires at least one V4: line");
2047         }
2048
2049         if (iov != NULL) {
2050                 /* Free strings allocated by strdup() in getmntopts.c */
2051                 free(iov[0].iov_base); /* fstype */
2052                 free(iov[2].iov_base); /* fspath */
2053                 free(iov[4].iov_base); /* from */
2054                 free(iov[6].iov_base); /* update */
2055                 free(iov[8].iov_base); /* export */
2056                 free(iov[10].iov_base); /* errmsg */
2057
2058                 /* free iov, allocated by realloc() */
2059                 free(iov);
2060                 iovlen = 0;
2061         }
2062
2063         /*
2064          * If there was no public fh, clear any previous one set.
2065          */
2066         if (has_publicfh == 0) {
2067                 LOGDEBUG("clear public fh");
2068                 (void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
2069         }
2070
2071         /* Resume the nfsd. If they weren't suspended, this is harmless. */
2072         (void)nfssvc(NFSSVC_RESUMENFSD, NULL);
2073         LOGDEBUG("eo get_exportlist");
2074 }
2075
2076 /*
2077  * Insert an export entry in the appropriate list.
2078  */
2079 static void
2080 insert_exports(struct exportlist *ep, struct exportlisthead *exhp)
2081 {
2082         uint32_t i;
2083
2084         i = EXPHASH(&ep->ex_fs);
2085         LOGDEBUG("fs=%s hash=%i", ep->ex_fsdir, i);
2086         SLIST_INSERT_HEAD(&exhp[i], ep, entries);
2087 }
2088
2089 /*
2090  * Free up the exports lists passed in as arguments.
2091  */
2092 static void
2093 free_exports(struct exportlisthead *exhp)
2094 {
2095         struct exportlist *ep, *ep2;
2096         int i;
2097
2098         for (i = 0; i < exphashsize; i++) {
2099                 SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) {
2100                         SLIST_REMOVE(&exhp[i], ep, exportlist, entries);
2101                         free_exp(ep);
2102                 }
2103                 SLIST_INIT(&exhp[i]);
2104         }
2105 }
2106
2107 /*
2108  * Read the exports file(s) and call get_exportlist_one() for each line.
2109  */
2110 static void
2111 read_exportfile(int passno)
2112 {
2113         int done, i;
2114
2115         /*
2116          * Read in the exports file and build the list, calling
2117          * nmount() as we go along to push the export rules into the kernel.
2118          */
2119         done = 0;
2120         for (i = 0; exnames[i] != NULL; i++) {
2121                 if (debug)
2122                         warnx("reading exports from %s", exnames[i]);
2123                 if ((exp_file = fopen(exnames[i], "r")) == NULL) {
2124                         syslog(LOG_WARNING, "can't open %s", exnames[i]);
2125                         continue;
2126                 }
2127                 get_exportlist_one(passno);
2128                 fclose(exp_file);
2129                 done++;
2130         }
2131         if (done == 0) {
2132                 syslog(LOG_ERR, "can't open any exports file");
2133                 exit(2);
2134         }
2135 }
2136
2137 /*
2138  * Compare the export lists against the old ones and do nmount() operations
2139  * for any cases that have changed.  This avoids doing nmount() for entries
2140  * that have not changed.
2141  * Return 0 upon success, 1 otherwise.
2142  */
2143 static int
2144 compare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg)
2145 {
2146         struct exportlist *ep, *oep;
2147         struct grouplist *grp;
2148         struct statfs fs, ofs;
2149         int i, ret;
2150
2151         /*
2152          * Loop through the current list and look for an entry in the old
2153          * list.
2154          * If found, check to see if it the same.
2155          *        If it is not the same, delete and re-export.
2156          *        Then mark it done on the old list.
2157          * else (not found)
2158          *        export it.
2159          * Any entries left in the old list after processing must have their
2160          * exports deleted.
2161          */
2162         for (i = 0; i < exphashsize; i++)
2163                 SLIST_FOREACH(ep, &exphead[i], entries) {
2164                         LOGDEBUG("foreach ep=%s", ep->ex_fsdir);
2165                         oep = ex_search(&ep->ex_fs, oldexphead);
2166                         if (oep != NULL) {
2167                                 /*
2168                                  * Check the mount paths are the same.
2169                                  * If not, return 1 so that the reload of the
2170                                  * exports will be done in bulk, the
2171                                  * passno == 0 way.
2172                                  */
2173                                 LOGDEBUG("found old exp");
2174                                 if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0)
2175                                         return (1);
2176                                 LOGDEBUG("same fsdir");
2177                                 /*
2178                                  * Test to see if the entry is the same.
2179                                  * If not the same delete exports and
2180                                  * re-export.
2181                                  */
2182                                 if (compare_export(ep, oep) != 0) {
2183                                         /*
2184                                          * Clear has_publicfh if if was set
2185                                          * in the old exports, but only if it
2186                                          * has not been set during processing of
2187                                          * the exports for this pass, as
2188                                          * indicated by has_set_publicfh.
2189                                          */
2190                                         if (has_set_publicfh == 0 &&
2191                                             (oep->ex_flag & EX_PUBLICFH) != 0)
2192                                                 has_publicfh = 0;
2193
2194                                         /* Delete and re-export. */
2195                                         if (statfs(ep->ex_fsdir, &fs) < 0)
2196                                                 return (1);
2197                                         delete_export(iov, iovlen, &fs, errmsg);
2198                                         ret = do_export_mount(ep, &fs);
2199                                         if (ret != 0)
2200                                                 return (ret);
2201                                 }
2202                                 oep->ex_flag |= EX_DONE;
2203                                 LOGDEBUG("exdone");
2204                         } else {
2205                                 LOGDEBUG("not found so export");
2206                                 /* Not found, so do export. */
2207                                 if (statfs(ep->ex_fsdir, &fs) < 0)
2208                                         return (1);
2209                                 ret = do_export_mount(ep, &fs);
2210                                 if (ret != 0)
2211                                         return (ret);
2212                         }
2213                 }
2214
2215         /* Delete exports not done. */
2216         for (i = 0; i < exphashsize; i++)
2217                 SLIST_FOREACH(oep, &oldexphead[i], entries) {
2218                         if ((oep->ex_flag & EX_DONE) == 0) {
2219                                 LOGDEBUG("not done delete=%s", oep->ex_fsdir);
2220                                 if (statfs(oep->ex_fsdir, &ofs) >= 0 &&
2221                                     fsidcmp(&oep->ex_fs, &ofs.f_fsid) == 0) {
2222                                         LOGDEBUG("do delete");
2223                                         /*
2224                                          * Clear has_publicfh if if was set
2225                                          * in the old exports, but only if it
2226                                          * has not been set during processing of
2227                                          * the exports for this pass, as
2228                                          * indicated by has_set_publicfh.
2229                                          */
2230                                         if (has_set_publicfh == 0 &&
2231                                             (oep->ex_flag & EX_PUBLICFH) != 0)
2232                                                 has_publicfh = 0;
2233
2234                                         delete_export(iov, iovlen, &ofs,
2235                                             errmsg);
2236                                 }
2237                         }
2238                 }
2239
2240         /* Do the V4 root exports, as required. */
2241         grp = NULL;
2242         if (v4root_ep != NULL)
2243                 grp = v4root_ep->ex_grphead;
2244         v4root_phase = 2;
2245         while (v4root_ep != NULL && grp != NULL) {
2246                 LOGDEBUG("v4root expath=%s", v4root_dirpath);
2247                 ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon,
2248                     v4root_dirpath, strlen(v4root_dirpath), &fs,
2249                     grp->gr_numsecflavors, grp->gr_secflavors);
2250                 if (ret != 0) {
2251                         v4root_phase = 0;
2252                         return (ret);
2253                 }
2254                 grp = grp->gr_next;
2255         }
2256         v4root_phase = 0;
2257         free_v4rootexp();
2258         return (0);
2259 }
2260
2261 /*
2262  * Compare old and current exportlist entries for the fsid and return 0
2263  * if they are the same, 1 otherwise.
2264  */
2265 static int
2266 compare_export(struct exportlist *ep, struct exportlist *oep)
2267 {
2268         struct grouplist *grp, *ogrp;
2269
2270         if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0)
2271                 return (1);
2272         if ((ep->ex_flag & EX_DEFSET) != (oep->ex_flag & EX_DEFSET))
2273                 return (1);
2274         if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) ||
2275             (ep->ex_defdir == NULL && oep->ex_defdir != NULL))
2276                 return (1);
2277         if (ep->ex_defdir != NULL && (ep->ex_defdir->dp_flag & DP_DEFSET) !=
2278             (oep->ex_defdir->dp_flag & DP_DEFSET))
2279                 return (1);
2280         if ((ep->ex_flag & EX_DEFSET) != 0 && (ep->ex_defnumsecflavors !=
2281             oep->ex_defnumsecflavors || ep->ex_defexflags !=
2282             oep->ex_defexflags || compare_cred(&ep->ex_defanon,
2283             &oep->ex_defanon) != 0 || compare_secflavor(ep->ex_defsecflavors,
2284             oep->ex_defsecflavors, ep->ex_defnumsecflavors) != 0))
2285                 return (1);
2286
2287         /* Now, check all the groups. */
2288         for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
2289                 ogrp->gr_flag = 0;
2290         for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) {
2291                 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp =
2292                     ogrp->gr_next)
2293                         if ((ogrp->gr_flag & GR_FND) == 0 &&
2294                             grp->gr_numsecflavors == ogrp->gr_numsecflavors &&
2295                             grp->gr_exflags == ogrp->gr_exflags &&
2296                             compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 &&
2297                             compare_secflavor(grp->gr_secflavors,
2298                             ogrp->gr_secflavors, grp->gr_numsecflavors) == 0)
2299                                 break;
2300                 if (ogrp != NULL)
2301                         ogrp->gr_flag |= GR_FND;
2302                 else
2303                         return (1);
2304         }
2305         for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
2306                 if ((ogrp->gr_flag & GR_FND) == 0)
2307                         return (1);
2308         return (0);
2309 }
2310
2311 /*
2312  * This algorithm compares two arrays of "n" items. It returns 0 if they are
2313  * the "same" and 1 otherwise.  Although suboptimal, it is always safe to
2314  * return 1, which makes compare_nmount_export() reload the exports entry.
2315  * "same" refers to having the same set of values in the two arrays.
2316  * The arrays are in no particular order and duplicates (multiple entries
2317  * in an array with the same value) is allowed.
2318  * The algorithm is inefficient, but the common case of identical arrays is
2319  * handled first and "n" is normally fairly small.
2320  * Since the two functions need the same algorithm but for arrays of
2321  * different types (gid_t vs int), this is done as a macro.
2322  */
2323 #define COMPARE_ARRAYS(a1, a2, n)                                       \
2324         do {                                                            \
2325                 int fnd, fndarray[(n)], i, j;                           \
2326                 /* Handle common case of identical arrays. */           \
2327                 for (i = 0; i < (n); i++)                               \
2328                         if ((a1)[i] != (a2)[i])                         \
2329                                 break;                                  \
2330                 if (i == (n))                                           \
2331                         return (0);                                     \
2332                 for (i = 0; i < (n); i++)                               \
2333                         fndarray[i] = 0;                                \
2334                 for (i = 0; i < (n); i++) {                             \
2335                         fnd = 0;                                        \
2336                         for (j = 0; j < (n); j++) {                     \
2337                                 if ((a1)[i] == (a2)[j]) {               \
2338                                         fndarray[j] = 1;                \
2339                                         fnd = 1;                        \
2340                                 }                                       \
2341                         }                                               \
2342                         if (fnd == 0)                                   \
2343                                 return (1);                             \
2344                 }                                                       \
2345                 for (i = 0; i < (n); i++)                               \
2346                         if (fndarray[i] == 0)                           \
2347                                 return (1);                             \
2348                 return (0);                                             \
2349         } while (0)
2350
2351 /*
2352  * Compare two struct expcred's.  Return 0 if the same and 1 otherwise.
2353  */
2354 static int
2355 compare_cred(struct expcred *cr0, struct expcred *cr1)
2356 {
2357
2358         if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups)
2359                 return (1);
2360
2361         COMPARE_ARRAYS(cr0->cr_groups, cr1->cr_groups, cr0->cr_ngroups);
2362 }
2363
2364 /*
2365  * Compare two lists of security flavors.  Return 0 if the same and 1 otherwise.
2366  */
2367 static int
2368 compare_secflavor(int *sec1, int *sec2, int nsec)
2369 {
2370
2371         COMPARE_ARRAYS(sec1, sec2, nsec);
2372 }
2373
2374 /*
2375  * Delete an exports entry.
2376  */
2377 static void
2378 delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg)
2379 {
2380         struct xvfsconf vfc;
2381
2382         if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
2383                 syslog(LOG_ERR, "getvfsbyname() failed for %s",
2384                     fsp->f_fstypename);
2385                 return;
2386         }
2387         
2388         /*
2389          * We do not need to delete "export" flag from
2390          * filesystems that do not have it set.
2391          */
2392         if (!(fsp->f_flags & MNT_EXPORTED))
2393                 return;
2394         /*
2395          * Do not delete export for network filesystem by
2396          * passing "export" arg to nmount().
2397          * It only makes sense to do this for local filesystems.
2398          */
2399         if (vfc.vfc_flags & VFCF_NETWORK)
2400                 return;
2401         
2402         iov[1].iov_base = fsp->f_fstypename;
2403         iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
2404         iov[3].iov_base = fsp->f_mntonname;
2405         iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
2406         iov[5].iov_base = fsp->f_mntfromname;
2407         iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
2408         errmsg[0] = '\0';
2409         
2410         /*
2411          * EXDEV is returned when path exists but is not a
2412          * mount point.  May happens if raced with unmount.
2413          */
2414         if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT &&
2415             errno != ENOTSUP && errno != EXDEV) {
2416                 syslog(LOG_ERR,
2417                     "can't delete exports for %s: %m %s",
2418                     fsp->f_mntonname, errmsg);
2419         }
2420 }
2421
2422 /*
2423  * Allocate an export list element
2424  */
2425 static struct exportlist *
2426 get_exp(void)
2427 {
2428         struct exportlist *ep;
2429
2430         ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
2431         if (ep == (struct exportlist *)NULL)
2432                 out_of_mem();
2433         return (ep);
2434 }
2435
2436 /*
2437  * Allocate a group list element
2438  */
2439 static struct grouplist *
2440 get_grp(void)
2441 {
2442         struct grouplist *gp;
2443
2444         gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
2445         if (gp == (struct grouplist *)NULL)
2446                 out_of_mem();
2447         return (gp);
2448 }
2449
2450 /*
2451  * Clean up upon an error in get_exportlist().
2452  */
2453 static void
2454 getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason)
2455 {
2456         struct grouplist *tgrp;
2457
2458         if (!(opt_flags & OP_QUIET)) {
2459                 if (reason != NULL)
2460                         syslog(LOG_ERR, "bad exports list line '%s': %s", line,
2461                             reason);
2462                 else
2463                         syslog(LOG_ERR, "bad exports list line '%s'", line);
2464         }
2465         if (ep && (ep->ex_flag & EX_LINKED) == 0)
2466                 free_exp(ep);
2467         while (grp) {
2468                 tgrp = grp;
2469                 grp = grp->gr_next;
2470                 free_grp(tgrp);
2471         }
2472 }
2473
2474 /*
2475  * Search the export list for a matching fs.
2476  */
2477 static struct exportlist *
2478 ex_search(fsid_t *fsid, struct exportlisthead *exhp)
2479 {
2480         struct exportlist *ep;
2481         uint32_t i;
2482
2483         i = EXPHASH(fsid);
2484         SLIST_FOREACH(ep, &exhp[i], entries) {
2485                 if (fsidcmp(&ep->ex_fs, fsid) == 0)
2486                         return (ep);
2487         }
2488
2489         return (ep);
2490 }
2491
2492 /*
2493  * Add a directory path to the list.
2494  */
2495 static char *
2496 add_expdir(struct dirlist **dpp, char *cp, int len)
2497 {
2498         struct dirlist *dp;
2499
2500         dp = malloc(sizeof (struct dirlist));
2501         if (dp == (struct dirlist *)NULL)
2502                 out_of_mem();
2503         dp->dp_left = *dpp;
2504         dp->dp_right = (struct dirlist *)NULL;
2505         dp->dp_flag = 0;
2506         dp->dp_hosts = (struct hostlist *)NULL;
2507         dp->dp_dirp = strndup(cp, len);
2508         if (dp->dp_dirp == NULL)
2509                 out_of_mem();
2510         *dpp = dp;
2511         return (dp->dp_dirp);
2512 }
2513
2514 /*
2515  * Hang the dir list element off the dirpath binary tree as required
2516  * and update the entry for host.
2517  */
2518 static void
2519 hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
2520         int flags, struct expcred *anoncrp, uint64_t exflags)
2521 {
2522         struct hostlist *hp;
2523         struct dirlist *dp2;
2524
2525         if (flags & OP_ALLDIRS) {
2526                 if (ep->ex_defdir)
2527                         free((caddr_t)dp);
2528                 else
2529                         ep->ex_defdir = dp;
2530                 if (grp == (struct grouplist *)NULL) {
2531                         ep->ex_flag |= EX_DEFSET;
2532                         ep->ex_defdir->dp_flag |= DP_DEFSET;
2533                         /* Save the default security flavors list. */
2534                         ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2535                         if (ep->ex_numsecflavors > 0)
2536                                 memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2537                                     sizeof(ep->ex_secflavors));
2538                         cp_cred(&ep->ex_defanon, anoncrp);
2539                         ep->ex_defexflags = exflags;
2540                 } else while (grp) {
2541                         hp = get_ht();
2542                         hp->ht_grp = grp;
2543                         hp->ht_next = ep->ex_defdir->dp_hosts;
2544                         ep->ex_defdir->dp_hosts = hp;
2545                         /* Save the security flavors list for this host set. */
2546                         grp->gr_numsecflavors = ep->ex_numsecflavors;
2547                         if (ep->ex_numsecflavors > 0)
2548                                 memcpy(grp->gr_secflavors, ep->ex_secflavors,
2549                                     sizeof(ep->ex_secflavors));
2550                         grp = grp->gr_next;
2551                 }
2552         } else {
2553
2554                 /*
2555                  * Loop through the directories adding them to the tree.
2556                  */
2557                 while (dp) {
2558                         dp2 = dp->dp_left;
2559                         add_dlist(&ep->ex_dirl, dp, grp, flags, ep, anoncrp,
2560                             exflags);
2561                         dp = dp2;
2562                 }
2563         }
2564 }
2565
2566 /*
2567  * Traverse the binary tree either updating a node that is already there
2568  * for the new directory or adding the new node.
2569  */
2570 static void
2571 add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
2572         int flags, struct exportlist *ep, struct expcred *anoncrp,
2573         uint64_t exflags)
2574 {
2575         struct dirlist *dp;
2576         struct hostlist *hp;
2577         int cmp;
2578
2579         dp = *dpp;
2580         if (dp) {
2581                 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
2582                 if (cmp > 0) {
2583                         add_dlist(&dp->dp_left, newdp, grp, flags, ep, anoncrp,
2584                             exflags);
2585                         return;
2586                 } else if (cmp < 0) {
2587                         add_dlist(&dp->dp_right, newdp, grp, flags, ep, anoncrp,
2588                             exflags);
2589                         return;
2590                 } else
2591                         free((caddr_t)newdp);
2592         } else {
2593                 dp = newdp;
2594                 dp->dp_left = (struct dirlist *)NULL;
2595                 *dpp = dp;
2596         }
2597         if (grp) {
2598
2599                 /*
2600                  * Hang all of the host(s) off of the directory point.
2601                  */
2602                 do {
2603                         hp = get_ht();
2604                         hp->ht_grp = grp;
2605                         hp->ht_next = dp->dp_hosts;
2606                         dp->dp_hosts = hp;
2607                         /* Save the security flavors list for this host set. */
2608                         grp->gr_numsecflavors = ep->ex_numsecflavors;
2609                         if (ep->ex_numsecflavors > 0)
2610                                 memcpy(grp->gr_secflavors, ep->ex_secflavors,
2611                                     sizeof(ep->ex_secflavors));
2612                         grp = grp->gr_next;
2613                 } while (grp);
2614         } else {
2615                 ep->ex_flag |= EX_DEFSET;
2616                 dp->dp_flag |= DP_DEFSET;
2617                 /* Save the default security flavors list. */
2618                 ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2619                 if (ep->ex_numsecflavors > 0)
2620                         memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2621                             sizeof(ep->ex_secflavors));
2622                 cp_cred(&ep->ex_defanon, anoncrp);
2623                 ep->ex_defexflags = exflags;
2624         }
2625 }
2626
2627 /*
2628  * Search for a dirpath on the export point.
2629  */
2630 static struct dirlist *
2631 dirp_search(struct dirlist *dp, char *dirp)
2632 {
2633         int cmp;
2634
2635         if (dp) {
2636                 cmp = strcmp(dp->dp_dirp, dirp);
2637                 if (cmp > 0)
2638                         return (dirp_search(dp->dp_left, dirp));
2639                 else if (cmp < 0)
2640                         return (dirp_search(dp->dp_right, dirp));
2641                 else
2642                         return (dp);
2643         }
2644         return (dp);
2645 }
2646
2647 /*
2648  * Scan for a host match in a directory tree.
2649  */
2650 static int
2651 chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2652         int *hostsetp, int *numsecflavors, int **secflavorsp)
2653 {
2654         struct hostlist *hp;
2655         struct grouplist *grp;
2656         struct addrinfo *ai;
2657
2658         if (dp) {
2659                 if (dp->dp_flag & DP_DEFSET)
2660                         *defsetp = dp->dp_flag;
2661                 hp = dp->dp_hosts;
2662                 while (hp) {
2663                         grp = hp->ht_grp;
2664                         switch (grp->gr_type) {
2665                         case GT_HOST:
2666                                 ai = grp->gr_ptr.gt_addrinfo;
2667                                 for (; ai; ai = ai->ai_next) {
2668                                         if (!sacmp(ai->ai_addr, saddr, NULL)) {
2669                                                 *hostsetp =
2670                                                     (hp->ht_flag | DP_HOSTSET);
2671                                                 if (numsecflavors != NULL) {
2672                                                         *numsecflavors =
2673                                                             grp->gr_numsecflavors;
2674                                                         *secflavorsp =
2675                                                             grp->gr_secflavors;
2676                                                 }
2677                                                 return (1);
2678                                         }
2679                                 }
2680                                 break;
2681                         case GT_NET:
2682                                 if (!sacmp(saddr, (struct sockaddr *)
2683                                     &grp->gr_ptr.gt_net.nt_net,
2684                                     (struct sockaddr *)
2685                                     &grp->gr_ptr.gt_net.nt_mask)) {
2686                                         *hostsetp = (hp->ht_flag | DP_HOSTSET);
2687                                         if (numsecflavors != NULL) {
2688                                                 *numsecflavors =
2689                                                     grp->gr_numsecflavors;
2690                                                 *secflavorsp =
2691                                                     grp->gr_secflavors;
2692                                         }
2693                                         return (1);
2694                                 }
2695                                 break;
2696                         }
2697                         hp = hp->ht_next;
2698                 }
2699         }
2700         return (0);
2701 }
2702
2703 /*
2704  * Scan tree for a host that matches the address.
2705  */
2706 static int
2707 scan_tree(struct dirlist *dp, struct sockaddr *saddr)
2708 {
2709         int defset, hostset;
2710
2711         if (dp) {
2712                 if (scan_tree(dp->dp_left, saddr))
2713                         return (1);
2714                 if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
2715                         return (1);
2716                 if (scan_tree(dp->dp_right, saddr))
2717                         return (1);
2718         }
2719         return (0);
2720 }
2721
2722 /*
2723  * Traverse the dirlist tree and free it up.
2724  */
2725 static void
2726 free_dir(struct dirlist *dp)
2727 {
2728
2729         if (dp) {
2730                 free_dir(dp->dp_left);
2731                 free_dir(dp->dp_right);
2732                 free_host(dp->dp_hosts);
2733                 free(dp->dp_dirp);
2734                 free(dp);
2735         }
2736 }
2737
2738 /*
2739  * Parse a colon separated list of security flavors
2740  */
2741 static int
2742 parsesec(char *seclist, struct exportlist *ep)
2743 {
2744         char *cp, savedc;
2745         int flavor;
2746
2747         ep->ex_numsecflavors = 0;
2748         for (;;) {
2749                 cp = strchr(seclist, ':');
2750                 if (cp) {
2751                         savedc = *cp;
2752                         *cp = '\0';
2753                 }
2754
2755                 if (!strcmp(seclist, "sys"))
2756                         flavor = AUTH_SYS;
2757                 else if (!strcmp(seclist, "krb5"))
2758                         flavor = RPCSEC_GSS_KRB5;
2759                 else if (!strcmp(seclist, "krb5i"))
2760                         flavor = RPCSEC_GSS_KRB5I;
2761                 else if (!strcmp(seclist, "krb5p"))
2762                         flavor = RPCSEC_GSS_KRB5P;
2763                 else {
2764                         if (cp)
2765                                 *cp = savedc;
2766                         syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2767                         return (1);
2768                 }
2769                 if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2770                         if (cp)
2771                                 *cp = savedc;
2772                         syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2773                         return (1);
2774                 }
2775                 ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2776                 ep->ex_numsecflavors++;
2777                 if (cp) {
2778                         *cp = savedc;
2779                         seclist = cp + 1;
2780                 } else {
2781                         break;
2782                 }
2783         }
2784         return (0);
2785 }
2786
2787 /*
2788  * Parse the option string and update fields.
2789  * Option arguments may either be -<option>=<value> or
2790  * -<option> <value>
2791  */
2792 static int
2793 do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2794         int *has_hostp, uint64_t *exflagsp, struct expcred *cr)
2795 {
2796         char *cpoptarg, *cpoptend;
2797         char *cp, *endcp, *cpopt, savedc, savedc2;
2798         int allflag, usedarg;
2799
2800         savedc2 = '\0';
2801         cpopt = *cpp;
2802         cpopt++;
2803         cp = *endcpp;
2804         savedc = *cp;
2805         *cp = '\0';
2806         while (cpopt && *cpopt) {
2807                 allflag = 1;
2808                 usedarg = -2;
2809                 if ((cpoptend = strchr(cpopt, ','))) {
2810                         *cpoptend++ = '\0';
2811                         if ((cpoptarg = strchr(cpopt, '=')))
2812                                 *cpoptarg++ = '\0';
2813                 } else {
2814                         if ((cpoptarg = strchr(cpopt, '=')))
2815                                 *cpoptarg++ = '\0';
2816                         else {
2817                                 *cp = savedc;
2818                                 nextfield(&cp, &endcp);
2819                                 **endcpp = '\0';
2820                                 if (endcp > cp && *cp != '-') {
2821                                         cpoptarg = cp;
2822                                         savedc2 = *endcp;
2823                                         *endcp = '\0';
2824                                         usedarg = 0;
2825                                 }
2826                         }
2827                 }
2828                 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
2829                         *exflagsp |= MNT_EXRDONLY;
2830                 } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
2831                     !(allflag = strcmp(cpopt, "mapall")) ||
2832                     !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
2833                         usedarg++;
2834                         parsecred(cpoptarg, cr);
2835                         if (allflag == 0) {
2836                                 *exflagsp |= MNT_EXPORTANON;
2837                                 opt_flags |= OP_MAPALL;
2838                         } else
2839                                 opt_flags |= OP_MAPROOT;
2840                 } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
2841                     !strcmp(cpopt, "m"))) {
2842                         if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
2843                                 syslog(LOG_ERR, "bad mask: %s", cpoptarg);
2844                                 return (1);
2845                         }
2846                         usedarg++;
2847                         opt_flags |= OP_MASK;
2848                 } else if (cpoptarg && (!strcmp(cpopt, "network") ||
2849                         !strcmp(cpopt, "n"))) {
2850                         if (strchr(cpoptarg, '/') != NULL) {
2851                                 if (debug)
2852                                         fprintf(stderr, "setting OP_MASKLEN\n");
2853                                 opt_flags |= OP_MASKLEN;
2854                         }
2855                         if (grp->gr_type != GT_NULL) {
2856                                 syslog(LOG_ERR, "network/host conflict");
2857                                 return (1);
2858                         } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
2859                                 syslog(LOG_ERR, "bad net: %s", cpoptarg);
2860                                 return (1);
2861                         }
2862                         grp->gr_type = GT_NET;
2863                         *has_hostp = 1;
2864                         usedarg++;
2865                         opt_flags |= OP_NET;
2866                 } else if (!strcmp(cpopt, "alldirs")) {
2867                         opt_flags |= OP_ALLDIRS;
2868                 } else if (!strcmp(cpopt, "public")) {
2869                         *exflagsp |= MNT_EXPUBLIC;
2870                 } else if (!strcmp(cpopt, "webnfs")) {
2871                         *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
2872                         opt_flags |= OP_MAPALL;
2873                 } else if (cpoptarg && !strcmp(cpopt, "index")) {
2874                         ep->ex_indexfile = strdup(cpoptarg);
2875                 } else if (!strcmp(cpopt, "quiet")) {
2876                         opt_flags |= OP_QUIET;
2877                 } else if (cpoptarg && !strcmp(cpopt, "sec")) {
2878                         if (parsesec(cpoptarg, ep))
2879                                 return (1);
2880                         opt_flags |= OP_SEC;
2881                         usedarg++;
2882                 } else if (!strcmp(cpopt, "tls")) {
2883                         *exflagsp |= MNT_EXTLS;
2884                 } else if (!strcmp(cpopt, "tlscert")) {
2885                         *exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT);
2886                 } else if (!strcmp(cpopt, "tlscertuser")) {
2887                         *exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT |
2888                             MNT_EXTLSCERTUSER);
2889                 } else {
2890                         syslog(LOG_ERR, "bad opt %s", cpopt);
2891                         return (1);
2892                 }
2893                 if (usedarg >= 0) {
2894                         *endcp = savedc2;
2895                         **endcpp = savedc;
2896                         if (usedarg > 0) {
2897                                 *cpp = cp;
2898                                 *endcpp = endcp;
2899                         }
2900                         return (0);
2901                 }
2902                 cpopt = cpoptend;
2903         }
2904         **endcpp = savedc;
2905         return (0);
2906 }
2907
2908 /*
2909  * Translate a character string to the corresponding list of network
2910  * addresses for a hostname.
2911  */
2912 static int
2913 get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
2914 {
2915         struct grouplist *checkgrp;
2916         struct addrinfo *ai, *tai, hints;
2917         int ecode;
2918         char host[NI_MAXHOST];
2919
2920         if (grp->gr_type != GT_NULL) {
2921                 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
2922                 return (1);
2923         }
2924         memset(&hints, 0, sizeof hints);
2925         hints.ai_flags = AI_CANONNAME;
2926         hints.ai_protocol = IPPROTO_UDP;
2927         ecode = getaddrinfo(cp, NULL, &hints, &ai);
2928         if (ecode != 0) {
2929                 syslog(LOG_ERR,"can't get address info for host %s", cp);
2930                 return 1;
2931         }
2932         grp->gr_ptr.gt_addrinfo = ai;
2933         while (ai != NULL) {
2934                 if (ai->ai_canonname == NULL) {
2935                         if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2936                             sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
2937                                 strlcpy(host, "?", sizeof(host));
2938                         ai->ai_canonname = strdup(host);
2939                         ai->ai_flags |= AI_CANONNAME;
2940                 }
2941                 if (debug)
2942                         fprintf(stderr, "got host %s\n", ai->ai_canonname);
2943                 /*
2944                  * Sanity check: make sure we don't already have an entry
2945                  * for this host in the grouplist.
2946                  */
2947                 for (checkgrp = tgrp; checkgrp != NULL;
2948                     checkgrp = checkgrp->gr_next) {
2949                         if (checkgrp->gr_type != GT_HOST)
2950                                 continue;
2951                         for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
2952                             tai = tai->ai_next) {
2953                                 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
2954                                         continue;
2955                                 if (debug)
2956                                         fprintf(stderr,
2957                                             "ignoring duplicate host %s\n",
2958                                             ai->ai_canonname);
2959                                 grp->gr_type = GT_IGNORE;
2960                                 return (0);
2961                         }
2962                 }
2963                 ai = ai->ai_next;
2964         }
2965         grp->gr_type = GT_HOST;
2966         return (0);
2967 }
2968
2969 /*
2970  * Free up an exports list component
2971  */
2972 static void
2973 free_exp(struct exportlist *ep)
2974 {
2975         struct grouplist *grp, *tgrp;
2976
2977         if (ep->ex_defdir) {
2978                 free_host(ep->ex_defdir->dp_hosts);
2979                 free((caddr_t)ep->ex_defdir);
2980         }
2981         if (ep->ex_fsdir)
2982                 free(ep->ex_fsdir);
2983         if (ep->ex_indexfile)
2984                 free(ep->ex_indexfile);
2985         free_dir(ep->ex_dirl);
2986         grp = ep->ex_grphead;
2987         while (grp) {
2988                 tgrp = grp;
2989                 grp = grp->gr_next;
2990                 free_grp(tgrp);
2991         }
2992         if (ep->ex_defanon.cr_groups != ep->ex_defanon.cr_smallgrps)
2993                 free(ep->ex_defanon.cr_groups);
2994         free((caddr_t)ep);
2995 }
2996
2997 /*
2998  * Free up the v4root exports.
2999  */
3000 static void
3001 free_v4rootexp(void)
3002 {
3003
3004         if (v4root_ep != NULL) {
3005                 free_exp(v4root_ep);
3006                 v4root_ep = NULL;
3007         }
3008 }
3009
3010 /*
3011  * Free hosts.
3012  */
3013 static void
3014 free_host(struct hostlist *hp)
3015 {
3016         struct hostlist *hp2;
3017
3018         while (hp) {
3019                 hp2 = hp;
3020                 hp = hp->ht_next;
3021                 free((caddr_t)hp2);
3022         }
3023 }
3024
3025 static struct hostlist *
3026 get_ht(void)
3027 {
3028         struct hostlist *hp;
3029
3030         hp = (struct hostlist *)malloc(sizeof (struct hostlist));
3031         if (hp == (struct hostlist *)NULL)
3032                 out_of_mem();
3033         hp->ht_next = (struct hostlist *)NULL;
3034         hp->ht_flag = 0;
3035         return (hp);
3036 }
3037
3038 /*
3039  * Out of memory, fatal
3040  */
3041 static void
3042 out_of_mem(void)
3043 {
3044
3045         syslog(LOG_ERR, "out of memory");
3046         exit(2);
3047 }
3048
3049 /*
3050  * Call do_mount() from the struct exportlist, for each case needed.
3051  */
3052 static int
3053 do_export_mount(struct exportlist *ep, struct statfs *fsp)
3054 {
3055         struct grouplist *grp, defgrp;
3056         int ret;
3057         size_t dirlen;
3058
3059         LOGDEBUG("do_export_mount=%s", ep->ex_fsdir);
3060         dirlen = strlen(ep->ex_fsdir);
3061         if ((ep->ex_flag & EX_DEFSET) != 0) {
3062                 defgrp.gr_type = GT_DEFAULT;
3063                 defgrp.gr_next = NULL;
3064                 /* We have an entry for all other hosts/nets. */
3065                 LOGDEBUG("ex_defexflags=0x%jx", (uintmax_t)ep->ex_defexflags);
3066                 ret = do_mount(ep, &defgrp, ep->ex_defexflags, &ep->ex_defanon,
3067                     ep->ex_fsdir, dirlen, fsp, ep->ex_defnumsecflavors,
3068                     ep->ex_defsecflavors);
3069                 if (ret != 0)
3070                         return (ret);
3071         }
3072
3073         /* Do a mount for each group. */
3074         grp = ep->ex_grphead;
3075         while (grp != NULL) {
3076                 LOGDEBUG("do mount gr_type=0x%x gr_exflags=0x%jx",
3077                     grp->gr_type, (uintmax_t)grp->gr_exflags);
3078                 ret = do_mount(ep, grp, grp->gr_exflags, &grp->gr_anon,
3079                     ep->ex_fsdir, dirlen, fsp, grp->gr_numsecflavors,
3080                     grp->gr_secflavors);
3081                 if (ret != 0)
3082                         return (ret);
3083                 grp = grp->gr_next;
3084         }
3085         return (0);
3086 }
3087
3088 /*
3089  * Do the nmount() syscall with the update flag to push the export info into
3090  * the kernel.
3091  */
3092 static int
3093 do_mount(struct exportlist *ep, struct grouplist *grp, uint64_t exflags,
3094     struct expcred *anoncrp, char *dirp, int dirplen, struct statfs *fsb,
3095     int numsecflavors, int *secflavors)
3096 {
3097         struct statfs fsb1;
3098         struct addrinfo *ai;
3099         struct export_args *eap;
3100         char errmsg[255];
3101         char *cp;
3102         int done;
3103         char savedc;
3104         struct iovec *iov;
3105         int i, iovlen;
3106         int ret;
3107         struct nfsex_args nfsea;
3108
3109         eap = &nfsea.export;
3110
3111         cp = NULL;
3112         savedc = '\0';
3113         iov = NULL;
3114         iovlen = 0;
3115         ret = 0;
3116
3117         bzero(eap, sizeof (struct export_args));
3118         bzero(errmsg, sizeof(errmsg));
3119         eap->ex_flags = exflags;
3120         eap->ex_uid = anoncrp->cr_uid;
3121         eap->ex_ngroups = anoncrp->cr_ngroups;
3122         if (eap->ex_ngroups > 0) {
3123                 eap->ex_groups = malloc(eap->ex_ngroups * sizeof(gid_t));
3124                 memcpy(eap->ex_groups, anoncrp->cr_groups, eap->ex_ngroups *
3125                     sizeof(gid_t));
3126         }
3127         LOGDEBUG("do_mount exflags=0x%jx", (uintmax_t)exflags);
3128         eap->ex_indexfile = ep->ex_indexfile;
3129         if (grp->gr_type == GT_HOST)
3130                 ai = grp->gr_ptr.gt_addrinfo;
3131         else
3132                 ai = NULL;
3133         eap->ex_numsecflavors = numsecflavors;
3134         LOGDEBUG("do_mount numsec=%d", numsecflavors);
3135         for (i = 0; i < eap->ex_numsecflavors; i++)
3136                 eap->ex_secflavors[i] = secflavors[i];
3137         if (eap->ex_numsecflavors == 0) {
3138                 eap->ex_numsecflavors = 1;
3139                 eap->ex_secflavors[0] = AUTH_SYS;
3140         }
3141         done = FALSE;
3142
3143         if (v4root_phase == 0) {
3144                 build_iovec(&iov, &iovlen, "fstype", NULL, 0);
3145                 build_iovec(&iov, &iovlen, "fspath", NULL, 0);
3146                 build_iovec(&iov, &iovlen, "from", NULL, 0);
3147                 build_iovec(&iov, &iovlen, "update", NULL, 0);
3148                 build_iovec(&iov, &iovlen, "export", eap,
3149                     sizeof (struct export_args));
3150                 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
3151         }
3152
3153         while (!done) {
3154                 switch (grp->gr_type) {
3155                 case GT_HOST:
3156                         if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
3157                                 goto skip;
3158                         eap->ex_addr = ai->ai_addr;
3159                         eap->ex_addrlen = ai->ai_addrlen;
3160                         eap->ex_masklen = 0;
3161                         break;
3162                 case GT_NET:
3163                         if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
3164                             have_v6 == 0)
3165                                 goto skip;
3166                         eap->ex_addr =
3167                             (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
3168                         eap->ex_addrlen =
3169                             ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
3170                         eap->ex_mask =
3171                             (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
3172                         eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
3173                         break;
3174                 case GT_DEFAULT:
3175                         eap->ex_addr = NULL;
3176                         eap->ex_addrlen = 0;
3177                         eap->ex_mask = NULL;
3178                         eap->ex_masklen = 0;
3179                         break;
3180                 case GT_IGNORE:
3181                         ret = 0;
3182                         goto error_exit;
3183                         break;
3184                 default:
3185                         syslog(LOG_ERR, "bad grouptype");
3186                         if (cp)
3187                                 *cp = savedc;
3188                         ret = 1;
3189                         goto error_exit;
3190                 }
3191
3192                 /*
3193                  * For V4:, use the nfssvc() syscall, instead of mount().
3194                  */
3195                 if (v4root_phase == 2) {
3196                         nfsea.fspec = v4root_dirpath;
3197                         if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT,
3198                             (caddr_t)&nfsea) < 0) {
3199                                 syslog(LOG_ERR, "Exporting V4: failed");
3200                                 ret = 2;
3201                                 goto error_exit;
3202                         }
3203                 } else {
3204                         /*
3205                          * XXX:
3206                          * Maybe I should just use the fsb->f_mntonname path
3207                          * instead of looping back up the dirp to the mount
3208                          * point??
3209                          * Also, needs to know how to export all types of local
3210                          * exportable filesystems and not just "ufs".
3211                          */
3212                         iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
3213                         iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
3214                         iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
3215                         iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
3216                         iov[5].iov_base = fsb->f_mntfromname; /* "from" */
3217                         iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
3218                         errmsg[0] = '\0';
3219         
3220                         while (nmount(iov, iovlen, fsb->f_flags) < 0) {
3221                                 if (cp)
3222                                         *cp-- = savedc;
3223                                 else
3224                                         cp = dirp + dirplen - 1;
3225                                 if (opt_flags & OP_QUIET) {
3226                                         ret = 1;
3227                                         goto error_exit;
3228                                 }
3229                                 if (errno == EPERM) {
3230                                         if (debug)
3231                                                 warnx("can't change attributes for %s: %s",
3232                                                     dirp, errmsg);
3233                                         syslog(LOG_ERR,
3234                                            "can't change attributes for %s: %s",
3235                                             dirp, errmsg);
3236                                         ret = 1;
3237                                         goto error_exit;
3238                                 }
3239                                 if (opt_flags & OP_ALLDIRS) {
3240                                         if (errno == EINVAL)
3241                                                 syslog(LOG_ERR,
3242                 "-alldirs requested but %s is not a filesystem mountpoint",
3243                                                     dirp);
3244                                         else
3245                                                 syslog(LOG_ERR,
3246                                                     "could not remount %s: %m",
3247                                                     dirp);
3248                                         ret = 1;
3249                                         goto error_exit;
3250                                 }
3251                                 /* back up over the last component */
3252                                 while (cp > dirp && *cp == '/')
3253                                         cp--;
3254                                 while (cp > dirp && *(cp - 1) != '/')
3255                                         cp--;
3256                                 if (cp == dirp) {
3257                                         if (debug)
3258                                                 warnx("mnt unsucc");
3259                                         syslog(LOG_ERR, "can't export %s %s",
3260                                             dirp, errmsg);
3261                                         ret = 1;
3262                                         goto error_exit;
3263                                 }
3264                                 savedc = *cp;
3265                                 *cp = '\0';
3266                                 /*
3267                                  * Check that we're still on the same
3268                                  * filesystem.
3269                                  */
3270                                 if (statfs(dirp, &fsb1) != 0 ||
3271                                     fsidcmp(&fsb1.f_fsid, &fsb->f_fsid) != 0) {
3272                                         *cp = savedc;
3273                                         syslog(LOG_ERR,
3274                                             "can't export %s %s", dirp,
3275                                             errmsg);
3276                                         ret = 1;
3277                                         goto error_exit;
3278                                 }
3279                         }
3280                 }
3281
3282                 /*
3283                  * For the experimental server:
3284                  * If this is the public directory, get the file handle
3285                  * and load it into the kernel via the nfssvc() syscall.
3286                  */
3287                 if ((exflags & MNT_EXPUBLIC) != 0) {
3288                         fhandle_t fh;
3289                         char *public_name;
3290
3291                         if (eap->ex_indexfile != NULL)
3292                                 public_name = eap->ex_indexfile;
3293                         else
3294                                 public_name = dirp;
3295                         if (getfh(public_name, &fh) < 0)
3296                                 syslog(LOG_ERR,
3297                                     "Can't get public fh for %s", public_name);
3298                         else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
3299                                 syslog(LOG_ERR,
3300                                     "Can't set public fh for %s", public_name);
3301                         else {
3302                                 has_publicfh = 1;
3303                                 has_set_publicfh = 1;
3304                                 ep->ex_flag |= EX_PUBLICFH;
3305                         }
3306                 }
3307 skip:
3308                 if (ai != NULL)
3309                         ai = ai->ai_next;
3310                 if (ai == NULL)
3311                         done = TRUE;
3312         }
3313         if (cp)
3314                 *cp = savedc;
3315 error_exit:
3316         free(eap->ex_groups);
3317         /* free strings allocated by strdup() in getmntopts.c */
3318         if (iov != NULL) {
3319                 free(iov[0].iov_base); /* fstype */
3320                 free(iov[2].iov_base); /* fspath */
3321                 free(iov[4].iov_base); /* from */
3322                 free(iov[6].iov_base); /* update */
3323                 free(iov[8].iov_base); /* export */
3324                 free(iov[10].iov_base); /* errmsg */
3325
3326                 /* free iov, allocated by realloc() */
3327                 free(iov);
3328         }
3329         return (ret);
3330 }
3331
3332 /*
3333  * Translate a net address.
3334  *
3335  * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
3336  */
3337 static int
3338 get_net(char *cp, struct netmsk *net, int maskflg)
3339 {
3340         struct netent *np = NULL;
3341         char *name, *p, *prefp;
3342         struct sockaddr_in sin;
3343         struct sockaddr *sa = NULL;
3344         struct addrinfo hints, *ai = NULL;
3345         char netname[NI_MAXHOST];
3346         long preflen;
3347
3348         p = prefp = NULL;
3349         if ((opt_flags & OP_MASKLEN) && !maskflg) {
3350                 p = strchr(cp, '/');
3351                 *p = '\0';
3352                 prefp = p + 1;
3353         }
3354
3355         /*
3356          * Check for a numeric address first. We wish to avoid
3357          * possible DNS lookups in getnetbyname().
3358          */
3359         if (isxdigit(*cp) || *cp == ':') {
3360                 memset(&hints, 0, sizeof hints);
3361                 /* Ensure the mask and the network have the same family. */
3362                 if (maskflg && (opt_flags & OP_NET))
3363                         hints.ai_family = net->nt_net.ss_family;
3364                 else if (!maskflg && (opt_flags & OP_HAVEMASK))
3365                         hints.ai_family = net->nt_mask.ss_family;
3366                 else
3367                         hints.ai_family = AF_UNSPEC;
3368                 hints.ai_flags = AI_NUMERICHOST;
3369                 if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
3370                         sa = ai->ai_addr;
3371                 if (sa != NULL && ai->ai_family == AF_INET) {
3372                         /*
3373                          * The address in `cp' is really a network address, so
3374                          * use inet_network() to re-interpret this correctly.
3375                          * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
3376                          */
3377                         bzero(&sin, sizeof sin);
3378                         sin.sin_family = AF_INET;
3379                         sin.sin_len = sizeof sin;
3380                         sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
3381                         if (debug)
3382                                 fprintf(stderr, "get_net: v4 addr %s\n",
3383                                     inet_ntoa(sin.sin_addr));
3384                         sa = (struct sockaddr *)&sin;
3385                 }
3386         }
3387         if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
3388                 bzero(&sin, sizeof sin);
3389                 sin.sin_family = AF_INET;
3390                 sin.sin_len = sizeof sin;
3391                 sin.sin_addr = inet_makeaddr(np->n_net, 0);
3392                 sa = (struct sockaddr *)&sin;
3393         }
3394         if (sa == NULL)
3395                 goto fail;
3396
3397         if (maskflg) {
3398                 /* The specified sockaddr is a mask. */
3399                 if (checkmask(sa) != 0)
3400                         goto fail;
3401                 bcopy(sa, &net->nt_mask, sa->sa_len);
3402                 opt_flags |= OP_HAVEMASK;
3403                 opt_flags &= ~OP_CLASSMASK;
3404         } else {
3405                 /* The specified sockaddr is a network address. */
3406                 bcopy(sa, &net->nt_net, sa->sa_len);
3407
3408                 /* Get a network name for the export list. */
3409                 if (np) {
3410                         name = np->n_name;
3411                 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
3412                    NULL, 0, NI_NUMERICHOST) == 0) {
3413                         name = netname;
3414                 } else {
3415                         goto fail;
3416                 }
3417                 if ((net->nt_name = strdup(name)) == NULL)
3418                         out_of_mem();
3419
3420                 /*
3421                  * Extract a mask from either a "/<masklen>" suffix, or
3422                  * from the class of an IPv4 address.
3423                  */
3424                 if (opt_flags & OP_MASKLEN) {
3425                         preflen = strtol(prefp, NULL, 10);
3426                         if (preflen < 0L || preflen == LONG_MAX)
3427                                 goto fail;
3428                         bcopy(sa, &net->nt_mask, sa->sa_len);
3429                         if (makemask(&net->nt_mask, (int)preflen) != 0)
3430                                 goto fail;
3431                         opt_flags |= OP_HAVEMASK;
3432                         *p = '/';
3433                 } else if (sa->sa_family == AF_INET &&
3434                     (opt_flags & OP_MASK) == 0) {
3435                         in_addr_t addr;
3436
3437                         addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
3438                         if (IN_CLASSA(addr))
3439                                 preflen = 8;
3440                         else if (IN_CLASSB(addr))
3441                                 preflen = 16;
3442                         else if (IN_CLASSC(addr))
3443                                 preflen = 24;
3444                         else if (IN_CLASSD(addr))       /* XXX Multicast??? */
3445                                 preflen = 28;
3446                         else
3447                                 preflen = 32;   /* XXX */
3448
3449                         bcopy(sa, &net->nt_mask, sa->sa_len);
3450                         makemask(&net->nt_mask, (int)preflen);
3451                         opt_flags |= OP_HAVEMASK | OP_CLASSMASK;
3452                 }
3453         }
3454
3455         if (ai)
3456                 freeaddrinfo(ai);
3457         return 0;
3458
3459 fail:
3460         if (ai)
3461                 freeaddrinfo(ai);
3462         return 1;
3463 }
3464
3465 /*
3466  * Parse out the next white space separated field
3467  */
3468 static void
3469 nextfield(char **cp, char **endcp)
3470 {
3471         char *p;
3472         char quot = 0;
3473
3474         p = *cp;
3475         while (*p == ' ' || *p == '\t')
3476                 p++;
3477         *cp = p;
3478         while (*p != '\0') {
3479                 if (quot) {
3480                         if (*p == quot)
3481                                 quot = 0;
3482                 } else {
3483                         if (*p == '\\' && *(p + 1) != '\0')
3484                                 p++;
3485                         else if (*p == '\'' || *p == '"')
3486                                 quot = *p;
3487                         else if (*p == ' ' || *p == '\t')
3488                                 break;
3489                 }
3490                 p++;
3491         };
3492         *endcp = p;
3493 }
3494
3495 /*
3496  * Get an exports file line. Skip over blank lines and handle line
3497  * continuations.
3498  */
3499 static int
3500 get_line(void)
3501 {
3502         char *p, *cp;
3503         size_t len;
3504         int totlen, cont_line;
3505
3506         /*
3507          * Loop around ignoring blank lines and getting all continuation lines.
3508          */
3509         p = line;
3510         totlen = 0;
3511         do {
3512                 if ((p = fgetln(exp_file, &len)) == NULL)
3513                         return (0);
3514                 cp = p + len - 1;
3515                 cont_line = 0;
3516                 while (cp >= p &&
3517                     (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
3518                         if (*cp == '\\')
3519                                 cont_line = 1;
3520                         cp--;
3521                         len--;
3522                 }
3523                 if (cont_line) {
3524                         *++cp = ' ';
3525                         len++;
3526                 }
3527                 if (linesize < len + totlen + 1) {
3528                         linesize = len + totlen + 1;
3529                         line = realloc(line, linesize);
3530                         if (line == NULL)
3531                                 out_of_mem();
3532                 }
3533                 memcpy(line + totlen, p, len);
3534                 totlen += len;
3535                 line[totlen] = '\0';
3536         } while (totlen == 0 || cont_line);
3537         return (1);
3538 }
3539
3540 /*
3541  * Parse a description of a credential.
3542  */
3543 static void
3544 parsecred(char *namelist, struct expcred *cr)
3545 {
3546         char *name;
3547         int inpos;
3548         char *names;
3549         struct passwd *pw;
3550         struct group *gr;
3551         gid_t groups[NGROUPS_MAX + 1];
3552         int ngroups;
3553         unsigned long name_ul;
3554         char *end = NULL;
3555
3556         /*
3557          * Set up the unprivileged user.
3558          */
3559         cr->cr_groups = cr->cr_smallgrps;
3560         cr->cr_uid = UID_NOBODY;
3561         cr->cr_groups[0] = GID_NOGROUP;
3562         cr->cr_ngroups = 1;
3563         /*
3564          * Get the user's password table entry.
3565          */
3566         names = namelist;
3567         name = strsep_quote(&names, ":");
3568         /* Bug?  name could be NULL here */
3569         name_ul = strtoul(name, &end, 10);
3570         if (*end != '\0' || end == name)
3571                 pw = getpwnam(name);
3572         else
3573                 pw = getpwuid((uid_t)name_ul);
3574         /*
3575          * Credentials specified as those of a user.
3576          */
3577         if (names == NULL) {
3578                 if (pw == NULL) {
3579                         syslog(LOG_ERR, "unknown user: %s", name);
3580                         return;
3581                 }
3582                 cr->cr_uid = pw->pw_uid;
3583                 ngroups = NGROUPS_MAX + 1;
3584                 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) {
3585                         syslog(LOG_ERR, "too many groups");
3586                         ngroups = NGROUPS_MAX + 1;
3587                 }
3588
3589                 /*
3590                  * Compress out duplicate.
3591                  */
3592                 if (ngroups > 1 && groups[0] == groups[1]) {
3593                         ngroups--;
3594                         inpos = 2;
3595                 } else {
3596                         inpos = 1;
3597                 }
3598                 if (ngroups > NGROUPS_MAX)
3599                         ngroups = NGROUPS_MAX;
3600                 if (ngroups > SMALLNGROUPS)
3601                         cr->cr_groups = malloc(ngroups * sizeof(gid_t));
3602                 cr->cr_ngroups = ngroups;
3603                 cr->cr_groups[0] = groups[0];
3604                 memcpy(&cr->cr_groups[1], &groups[inpos], (ngroups - 1) *
3605                     sizeof(gid_t));
3606                 return;
3607         }
3608         /*
3609          * Explicit credential specified as a colon separated list:
3610          *      uid:gid:gid:...
3611          */
3612         if (pw != NULL) {
3613                 cr->cr_uid = pw->pw_uid;
3614         } else if (*end != '\0' || end == name) {
3615                 syslog(LOG_ERR, "unknown user: %s", name);
3616                 return;
3617         } else {
3618                 cr->cr_uid = name_ul;
3619         }
3620         cr->cr_ngroups = 0;
3621         while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS_MAX) {
3622                 name = strsep_quote(&names, ":");
3623                 name_ul = strtoul(name, &end, 10);
3624                 if (*end != '\0' || end == name) {
3625                         if ((gr = getgrnam(name)) == NULL) {
3626                                 syslog(LOG_ERR, "unknown group: %s", name);
3627                                 continue;
3628                         }
3629                         groups[cr->cr_ngroups++] = gr->gr_gid;
3630                 } else {
3631                         groups[cr->cr_ngroups++] = name_ul;
3632                 }
3633         }
3634         if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS_MAX)
3635                 syslog(LOG_ERR, "too many groups");
3636         if (cr->cr_ngroups > SMALLNGROUPS)
3637                 cr->cr_groups = malloc(cr->cr_ngroups * sizeof(gid_t));
3638         memcpy(cr->cr_groups, groups, cr->cr_ngroups * sizeof(gid_t));
3639 }
3640
3641 #define STRSIZ  (MNTNAMLEN+MNTPATHLEN+50)
3642 /*
3643  * Routines that maintain the remote mounttab
3644  */
3645 static void
3646 get_mountlist(void)
3647 {
3648         struct mountlist *mlp;
3649         char *host, *dirp, *cp;
3650         char str[STRSIZ];
3651         FILE *mlfile;
3652
3653         if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
3654                 if (errno == ENOENT)
3655                         return;
3656                 else {
3657                         syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
3658                         return;
3659                 }
3660         }
3661         while (fgets(str, STRSIZ, mlfile) != NULL) {
3662                 cp = str;
3663                 host = strsep(&cp, " \t\n");
3664                 dirp = strsep(&cp, " \t\n");
3665                 if (host == NULL || dirp == NULL)
3666                         continue;
3667                 mlp = (struct mountlist *)malloc(sizeof (*mlp));
3668                 if (mlp == (struct mountlist *)NULL)
3669                         out_of_mem();
3670                 strncpy(mlp->ml_host, host, MNTNAMLEN);
3671                 mlp->ml_host[MNTNAMLEN] = '\0';
3672                 strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3673                 mlp->ml_dirp[MNTPATHLEN] = '\0';
3674
3675                 SLIST_INSERT_HEAD(&mlhead, mlp, next);
3676         }
3677         fclose(mlfile);
3678 }
3679
3680 static void
3681 del_mlist(char *hostp, char *dirp)
3682 {
3683         struct mountlist *mlp, *mlp2;
3684         FILE *mlfile;
3685         int fnd = 0;
3686
3687         SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) {
3688                 if (!strcmp(mlp->ml_host, hostp) &&
3689                     (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
3690                         fnd = 1;
3691                         SLIST_REMOVE(&mlhead, mlp, mountlist, next);
3692                         free((caddr_t)mlp);
3693                 }
3694         }
3695         if (fnd) {
3696                 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
3697                         syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
3698                         return;
3699                 }
3700                 SLIST_FOREACH(mlp, &mlhead, next) {
3701                         fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3702                 }
3703                 fclose(mlfile);
3704         }
3705 }
3706
3707 static void
3708 add_mlist(char *hostp, char *dirp)
3709 {
3710         struct mountlist *mlp;
3711         FILE *mlfile;
3712
3713         SLIST_FOREACH(mlp, &mlhead, next) {
3714                 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
3715                         return;
3716         }
3717
3718         mlp = (struct mountlist *)malloc(sizeof (*mlp));
3719         if (mlp == (struct mountlist *)NULL)
3720                 out_of_mem();
3721         strncpy(mlp->ml_host, hostp, MNTNAMLEN);
3722         mlp->ml_host[MNTNAMLEN] = '\0';
3723         strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3724         mlp->ml_dirp[MNTPATHLEN] = '\0';
3725         SLIST_INSERT_HEAD(&mlhead, mlp, next);
3726         if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
3727                 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
3728                 return;
3729         }
3730         fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3731         fclose(mlfile);
3732 }
3733
3734 /*
3735  * Free up a group list.
3736  */
3737 static void
3738 free_grp(struct grouplist *grp)
3739 {
3740         if (grp->gr_type == GT_HOST) {
3741                 if (grp->gr_ptr.gt_addrinfo != NULL)
3742                         freeaddrinfo(grp->gr_ptr.gt_addrinfo);
3743         } else if (grp->gr_type == GT_NET) {
3744                 if (grp->gr_ptr.gt_net.nt_name)
3745                         free(grp->gr_ptr.gt_net.nt_name);
3746         }
3747         if (grp->gr_anon.cr_groups != grp->gr_anon.cr_smallgrps)
3748                 free(grp->gr_anon.cr_groups);
3749         free((caddr_t)grp);
3750 }
3751
3752 #ifdef DEBUG
3753 static void
3754 SYSLOG(int pri, const char *fmt, ...)
3755 {
3756         va_list ap;
3757
3758         va_start(ap, fmt);
3759         vfprintf(stderr, fmt, ap);
3760         va_end(ap);
3761 }
3762 #endif /* DEBUG */
3763
3764 /*
3765  * Check options for consistency.
3766  */
3767 static int
3768 check_options(struct dirlist *dp)
3769 {
3770
3771         if (v4root_phase == 0 && dp == NULL)
3772             return (1);
3773         if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
3774             syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
3775             return (1);
3776         }
3777         if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
3778                 syslog(LOG_ERR, "-mask requires -network");
3779                 return (1);
3780         }
3781         if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
3782                 syslog(LOG_ERR, "-network requires mask specification");
3783                 return (1);
3784         }
3785         if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
3786                 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
3787                 return (1);
3788         }
3789         if (v4root_phase > 0 &&
3790             (opt_flags &
3791              ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3792             syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3793             return (1);
3794         }
3795         if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3796             syslog(LOG_ERR, "-alldirs has multiple directories");
3797             return (1);
3798         }
3799         return (0);
3800 }
3801
3802 static int
3803 check_path_component(const char *path, char **err)
3804 {
3805         struct stat sb;
3806
3807         if (lstat(path, &sb)) {
3808                 asprintf(err, "%s: lstat() failed: %s.\n",
3809                     path, strerror(errno));
3810                 return (0);
3811         }
3812
3813         switch (sb.st_mode & S_IFMT) {
3814         case S_IFDIR:
3815                 return (1);
3816         case S_IFLNK:
3817                 asprintf(err, "%s: path is a symbolic link.\n", path);
3818                 break;
3819         case S_IFREG:
3820                 asprintf(err, "%s: path is a file rather than a directory.\n",
3821                     path);
3822                 break;
3823         default:
3824                 asprintf(err, "%s: path is not a directory.\n", path);
3825         }
3826
3827         return (0);
3828 }
3829
3830 /*
3831  * Check each path component for the presence of symbolic links. Return true
3832  */
3833 static int
3834 check_dirpath(char *dirp, char **err)
3835 {
3836         char *cp;
3837
3838         cp = dirp + 1;
3839         while (*cp) {
3840                 if (*cp == '/') {
3841                         *cp = '\0';
3842
3843                         if (!check_path_component(dirp, err)) {
3844                                 *cp = '/';
3845                                 return (0);
3846                         }
3847
3848                         *cp = '/';
3849                 }
3850                 cp++;
3851         }
3852
3853         if (!check_path_component(dirp, err))
3854                 return (0);
3855
3856         return (1);
3857 }
3858
3859 /*
3860  * Populate statfs information. Return true on success.
3861  */
3862 static int
3863 check_statfs(const char *dirp, struct statfs *fsb, char **err)
3864 {
3865         if (statfs(dirp, fsb)) {
3866                 asprintf(err, "%s: statfs() failed: %s\n", dirp,
3867                     strerror(errno));
3868                 return (0);
3869         }
3870
3871         return (1);
3872 }
3873
3874 /*
3875  * Make a netmask according to the specified prefix length. The ss_family
3876  * and other non-address fields must be initialised before calling this.
3877  */
3878 static int
3879 makemask(struct sockaddr_storage *ssp, int bitlen)
3880 {
3881         u_char *p;
3882         int bits, i, len;
3883
3884         if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
3885                 return (-1);
3886         if (bitlen > len * CHAR_BIT)
3887                 return (-1);
3888
3889         for (i = 0; i < len; i++) {
3890                 bits = MIN(CHAR_BIT, bitlen);
3891                 *p++ = (u_char)~0 << (CHAR_BIT - bits);
3892                 bitlen -= bits;
3893         }
3894         return 0;
3895 }
3896
3897 /*
3898  * Check that the sockaddr is a valid netmask. Returns 0 if the mask
3899  * is acceptable (i.e. of the form 1...10....0).
3900  */
3901 static int
3902 checkmask(struct sockaddr *sa)
3903 {
3904         u_char *mask;
3905         int i, len;
3906
3907         if ((mask = sa_rawaddr(sa, &len)) == NULL)
3908                 return (-1);
3909
3910         for (i = 0; i < len; i++)
3911                 if (mask[i] != 0xff)
3912                         break;
3913         if (i < len) {
3914                 if (~mask[i] & (u_char)(~mask[i] + 1))
3915                         return (-1);
3916                 i++;
3917         }
3918         for (; i < len; i++)
3919                 if (mask[i] != 0)
3920                         return (-1);
3921         return (0);
3922 }
3923
3924 /*
3925  * Compare two sockaddrs according to a specified mask. Return zero if
3926  * `sa1' matches `sa2' when filtered by the netmask in `samask'.
3927  * If samask is NULL, perform a full comparison.
3928  */
3929 static int
3930 sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
3931 {
3932         unsigned char *p1, *p2, *mask;
3933         int len, i;
3934
3935         if (sa1->sa_family != sa2->sa_family ||
3936             (p1 = sa_rawaddr(sa1, &len)) == NULL ||
3937             (p2 = sa_rawaddr(sa2, NULL)) == NULL)
3938                 return (1);
3939
3940         switch (sa1->sa_family) {
3941         case AF_INET6:
3942                 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
3943                     ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
3944                         return (1);
3945                 break;
3946         }
3947
3948         /* Simple binary comparison if no mask specified. */
3949         if (samask == NULL)
3950                 return (memcmp(p1, p2, len));
3951
3952         /* Set up the mask, and do a mask-based comparison. */
3953         if (sa1->sa_family != samask->sa_family ||
3954             (mask = sa_rawaddr(samask, NULL)) == NULL)
3955                 return (1);
3956
3957         for (i = 0; i < len; i++)
3958                 if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
3959                         return (1);
3960         return (0);
3961 }
3962
3963 /*
3964  * Return a pointer to the part of the sockaddr that contains the
3965  * raw address, and set *nbytes to its length in bytes. Returns
3966  * NULL if the address family is unknown.
3967  */
3968 static void *
3969 sa_rawaddr(struct sockaddr *sa, int *nbytes) {
3970         void *p;
3971         int len;
3972
3973         switch (sa->sa_family) {
3974         case AF_INET:
3975                 len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
3976                 p = &((struct sockaddr_in *)sa)->sin_addr;
3977                 break;
3978         case AF_INET6:
3979                 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
3980                 p = &((struct sockaddr_in6 *)sa)->sin6_addr;
3981                 break;
3982         default:
3983                 p = NULL;
3984                 len = 0;
3985         }
3986
3987         if (nbytes != NULL)
3988                 *nbytes = len;
3989         return (p);
3990 }
3991
3992 static void
3993 huphandler(int sig __unused)
3994 {
3995
3996         got_sighup = 1;
3997 }
3998
3999 static void
4000 terminate(int sig __unused)
4001 {
4002         pidfile_remove(pfh);
4003         rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
4004         rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
4005         exit (0);
4006 }
4007
4008 static void
4009 cp_cred(struct expcred *outcr, struct expcred *incr)
4010 {
4011
4012         outcr->cr_uid = incr->cr_uid;
4013         outcr->cr_ngroups = incr->cr_ngroups;
4014         if (outcr->cr_ngroups > SMALLNGROUPS)
4015                 outcr->cr_groups = malloc(outcr->cr_ngroups * sizeof(gid_t));
4016         else
4017                 outcr->cr_groups = outcr->cr_smallgrps;
4018         memcpy(outcr->cr_groups, incr->cr_groups, incr->cr_ngroups *
4019             sizeof(gid_t));
4020 }