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