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