xref: /netbsd-src/usr.sbin/mountd/mountd.c (revision 811e6386f8c5e4a3521c7003da29ec8673e344fa)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rick Macklem at The University of Guelph.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 char copyright[] =
39 "@(#) Copyright (c) 1989 Regents of the University of California.\n\
40  All rights reserved.\n";
41 #endif not lint
42 
43 #ifndef lint
44 static char sccsid[] = "@(#)mountd.c	5.14 (Berkeley) 2/26/91";
45 static char rcsid[] = "$Header: /cvsroot/src/usr.sbin/mountd/mountd.c,v 1.4 1993/03/23 00:29:10 cgd Exp $";
46 #endif not lint
47 
48 #include <sys/param.h>
49 #include <sys/ioctl.h>
50 #include <sys/stat.h>
51 #include <sys/file.h>
52 #include <sys/mount.h>
53 #include <sys/socket.h>
54 #include <sys/errno.h>
55 #include <sys/signal.h>
56 #include <stdio.h>
57 #include <string.h>
58 #include <syslog.h>
59 #include <netdb.h>
60 #include <rpc/rpc.h>
61 #include <rpc/pmap_clnt.h>
62 #include <rpc/pmap_prot.h>
63 #include <nfs/rpcv2.h>
64 #include <nfs/nfsv2.h>
65 #include "pathnames.h"
66 
67 struct ufid {
68 	u_short	ufid_len;
69 	ino_t	ufid_ino;
70 	long	ufid_gen;
71 };
72 /*
73  * Structures for keeping the mount list and export list
74  */
75 struct mountlist {
76 	struct mountlist *ml_next;
77 	char	ml_host[RPCMNT_NAMELEN+1];
78 	char	ml_dirp[RPCMNT_PATHLEN+1];
79 };
80 
81 struct exportlist {
82 	struct exportlist *ex_next;
83 	struct exportlist *ex_prev;
84 	struct grouplist *ex_groups;
85 	int	ex_rootuid;
86 	int	ex_exflags;
87 	dev_t	ex_dev;
88 	char	ex_dirp[RPCMNT_PATHLEN+1];
89 };
90 
91 struct grouplist {
92 	struct grouplist *gr_next;
93 	struct hostent *gr_hp;
94 };
95 
96 /* Global defs */
97 int mntsrv(), umntall_each(), xdr_fhs(), xdr_mlist(), xdr_dir(), xdr_explist();
98 void add_mlist(), del_mlist(), get_exportlist(), get_mountlist();
99 void send_umntall();
100 struct exportlist exphead;
101 struct mountlist *mlhead;
102 char exname[MAXPATHLEN];
103 int def_rootuid = -2;
104 int root_only = 1;
105 extern int errno;
106 #ifdef DEBUG
107 int debug = 1;
108 #else
109 int debug = 0;
110 #endif
111 
112 /*
113  * Mountd server for NFS mount protocol as described in:
114  * NFS: Network File System Protocol Specification, RFC1094, Appendix A
115  * The optional arguments are the exports file name
116  * default: _PATH_EXPORTS
117  * and "-n" to allow nonroot mount.
118  */
119 main(argc, argv)
120 	int argc;
121 	char **argv;
122 {
123 	SVCXPRT *transp;
124 	int c;
125 	extern int optind;
126 	extern char *optarg;
127 
128 	while ((c = getopt(argc, argv, "n")) != EOF)
129 		switch (c) {
130 		case 'n':
131 			root_only = 0;
132 			break;
133 		default:
134 			fprintf(stderr, "Usage: mountd [-n] [export_file]\n");
135 			exit(1);
136 		};
137 	argc -= optind;
138 	argv += optind;
139 	exphead.ex_next = exphead.ex_prev = (struct exportlist *)0;
140 	mlhead = (struct mountlist *)0;
141 	if (argc == 1) {
142 		strncpy(exname, *argv, MAXPATHLEN-1);
143 		exname[MAXPATHLEN-1] = '\0';
144 	} else
145 		strcpy(exname, _PATH_EXPORTS);
146 	openlog("mountd:", LOG_PID, LOG_DAEMON);
147 	get_exportlist();
148 	get_mountlist();
149 	if (debug == 0) {
150 		daemon(0, 0);
151 		signal(SIGINT, SIG_IGN);
152 		signal(SIGQUIT, SIG_IGN);
153 	}
154 	signal(SIGHUP, get_exportlist);
155 	signal(SIGTERM, send_umntall);
156 	{ FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
157 	  if (pidfile != NULL) {
158 		fprintf(pidfile, "%d\n", getpid());
159 		fclose(pidfile);
160 	  }
161 	}
162 	if ((transp = svcudp_create(RPC_ANYSOCK)) == NULL) {
163 		syslog(LOG_ERR, "Can't create socket");
164 		exit(1);
165 	}
166 	pmap_unset(RPCPROG_MNT, RPCMNT_VER1);
167 	if (!svc_register(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv, IPPROTO_UDP)) {
168 		syslog(LOG_ERR, "Can't register mount");
169 		exit(1);
170 	}
171 	svc_run();
172 	syslog(LOG_ERR, "Mountd died");
173 	exit(1);
174 }
175 
176 /*
177  * The mount rpc service
178  */
179 mntsrv(rqstp, transp)
180 	register struct svc_req *rqstp;
181 	register SVCXPRT *transp;
182 {
183 	register struct grouplist *grp;
184 	register u_long **addrp;
185 	register struct exportlist *ep;
186 	nfsv2fh_t nfh;
187 	struct authunix_parms *ucr;
188 	struct stat stb;
189 	struct hostent *hp;
190 	u_long saddr;
191 	char dirpath[RPCMNT_PATHLEN+1];
192 	int bad = ENOENT;
193 	int omask;
194 	uid_t uid = -2;
195 
196 	/* Get authorization */
197 	switch (rqstp->rq_cred.oa_flavor) {
198 	case AUTH_UNIX:
199 		ucr = (struct authunix_parms *)rqstp->rq_clntcred;
200 		uid = ucr->aup_uid;
201 		break;
202 	case AUTH_NULL:
203 	default:
204 		break;
205 	}
206 
207 	saddr = transp->xp_raddr.sin_addr.s_addr;
208 	hp = (struct hostent *)0;
209 	switch (rqstp->rq_proc) {
210 	case NULLPROC:
211 		if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
212 			syslog(LOG_ERR, "Can't send reply");
213 		return;
214 	case RPCMNT_MOUNT:
215 		if (uid != 0 && root_only) {
216 			svcerr_weakauth(transp);
217 			return;
218 		}
219 		if (!svc_getargs(transp, xdr_dir, dirpath)) {
220 			svcerr_decode(transp);
221 			return;
222 		}
223 
224 		/* Check to see if it's a valid dirpath */
225 		if (stat(dirpath, &stb) < 0 || (stb.st_mode&S_IFMT) !=
226 			S_IFDIR) {
227 			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
228 				syslog(LOG_ERR, "Can't send reply");
229 			return;
230 		}
231 
232 		/* Check in the exports list */
233 		omask = sigblock(sigmask(SIGHUP));
234 		ep = exphead.ex_next;
235 		while (ep != NULL) {
236 			if (!strcmp(ep->ex_dirp, dirpath)) {
237 				grp = ep->ex_groups;
238 				if (grp == NULL)
239 					break;
240 
241 				/* Check for a host match */
242 				addrp = (u_long **)grp->gr_hp->h_addr_list;
243 				for (;;) {
244 					if (**addrp == saddr)
245 						break;
246 					if (*++addrp == NULL)
247 						if (grp = grp->gr_next) {
248 							addrp = (u_long **)
249 								grp->gr_hp->h_addr_list;
250 						} else {
251 							bad = EACCES;
252 							if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
253 								syslog(LOG_ERR, "Can't send reply");
254 							sigsetmask(omask);
255 							return;
256 						}
257 				}
258 				hp = grp->gr_hp;
259 				break;
260 			}
261 			ep = ep->ex_next;
262 		}
263 		sigsetmask(omask);
264 		if (ep == NULL) {
265 			bad = EACCES;
266 			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
267 				syslog(LOG_ERR, "Can't send reply");
268 			return;
269 		}
270 
271 		/* Get the file handle */
272 		bzero((caddr_t)&nfh, sizeof(nfh));
273 		if (getfh(dirpath, (fhandle_t *)&nfh) < 0) {
274 			bad = errno;
275 			if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
276 				syslog(LOG_ERR, "Can't send reply");
277 			return;
278 		}
279 		if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&nfh))
280 			syslog(LOG_ERR, "Can't send reply");
281 		if (hp == NULL)
282 			hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
283 		if (hp)
284 			add_mlist(hp->h_name, dirpath);
285 		return;
286 	case RPCMNT_DUMP:
287 		if (!svc_sendreply(transp, xdr_mlist, (caddr_t)0))
288 			syslog(LOG_ERR, "Can't send reply");
289 		return;
290 	case RPCMNT_UMOUNT:
291 		if (uid != 0 && root_only) {
292 			svcerr_weakauth(transp);
293 			return;
294 		}
295 		if (!svc_getargs(transp, xdr_dir, dirpath)) {
296 			svcerr_decode(transp);
297 			return;
298 		}
299 		if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
300 			syslog(LOG_ERR, "Can't send reply");
301 		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
302 		if (hp)
303 			del_mlist(hp->h_name, dirpath);
304 		return;
305 	case RPCMNT_UMNTALL:
306 		if (uid != 0 && root_only) {
307 			svcerr_weakauth(transp);
308 			return;
309 		}
310 		if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
311 			syslog(LOG_ERR, "Can't send reply");
312 		hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
313 		if (hp)
314 			del_mlist(hp->h_name, (char *)0);
315 		return;
316 	case RPCMNT_EXPORT:
317 		if (!svc_sendreply(transp, xdr_explist, (caddr_t)0))
318 			syslog(LOG_ERR, "Can't send reply");
319 		return;
320 	default:
321 		svcerr_noproc(transp);
322 		return;
323 	}
324 }
325 
326 /*
327  * Xdr conversion for a dirpath string
328  */
329 xdr_dir(xdrsp, dirp)
330 	XDR *xdrsp;
331 	char *dirp;
332 {
333 	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
334 }
335 
336 /*
337  * Xdr routine to generate fhstatus
338  */
339 xdr_fhs(xdrsp, nfh)
340 	XDR *xdrsp;
341 	nfsv2fh_t *nfh;
342 {
343 	int ok = 0;
344 
345 	if (!xdr_long(xdrsp, &ok))
346 		return (0);
347 	return (xdr_opaque(xdrsp, (caddr_t)nfh, NFSX_FH));
348 }
349 
350 xdr_mlist(xdrsp, cp)
351 	XDR *xdrsp;
352 	caddr_t cp;
353 {
354 	register struct mountlist *mlp;
355 	int true = 1;
356 	int false = 0;
357 	char *strp;
358 
359 	mlp = mlhead;
360 	while (mlp) {
361 		if (!xdr_bool(xdrsp, &true))
362 			return (0);
363 		strp = &mlp->ml_host[0];
364 		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
365 			return (0);
366 		strp = &mlp->ml_dirp[0];
367 		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
368 			return (0);
369 		mlp = mlp->ml_next;
370 	}
371 	if (!xdr_bool(xdrsp, &false))
372 		return (0);
373 	return (1);
374 }
375 
376 /*
377  * Xdr conversion for export list
378  */
379 xdr_explist(xdrsp, cp)
380 	XDR *xdrsp;
381 	caddr_t cp;
382 {
383 	register struct exportlist *ep;
384 	register struct grouplist *grp;
385 	int true = 1;
386 	int false = 0;
387 	char *strp;
388 	int omask;
389 
390 	omask = sigblock(sigmask(SIGHUP));
391 	ep = exphead.ex_next;
392 	while (ep != NULL) {
393 		if (!xdr_bool(xdrsp, &true))
394 			goto errout;
395 		strp = &ep->ex_dirp[0];
396 		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
397 			goto errout;
398 		grp = ep->ex_groups;
399 		while (grp != NULL) {
400 			if (!xdr_bool(xdrsp, &true))
401 				goto errout;
402 			strp = grp->gr_hp->h_name;
403 			if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
404 				goto errout;
405 			grp = grp->gr_next;
406 		}
407 		if (!xdr_bool(xdrsp, &false))
408 			goto errout;
409 		ep = ep->ex_next;
410 	}
411 	sigsetmask(omask);
412 	if (!xdr_bool(xdrsp, &false))
413 		return (0);
414 	return (1);
415 errout:
416 	sigsetmask(omask);
417 	return (0);
418 }
419 
420 #define LINESIZ	10240
421 char line[LINESIZ];
422 
423 /*
424  * Get the export list
425  */
426 void
427 get_exportlist()
428 {
429 	register struct hostent *hp, *nhp;
430 	register char **addrp, **naddrp;
431 	register int i;
432 	register struct grouplist *grp;
433 	register struct exportlist *ep, *ep2;
434 	struct statfs stfsbuf;
435 	struct ufs_args args;
436 	struct stat sb;
437 	FILE *inf;
438 	char *cp, *endcp;
439 	char savedc;
440 	int len, dirplen;
441 	int rootuid, exflags;
442 	u_long saddr;
443 	struct exportlist *fep;
444 
445 	/*
446 	 * First, get rid of the old list
447 	 */
448 	ep = exphead.ex_next;
449 	while (ep != NULL) {
450 		ep2 = ep;
451 		ep = ep->ex_next;
452 		free_exp(ep2);
453 	}
454 
455 	/*
456 	 * Read in the exports file and build the list, calling
457 	 * exportfs() as we go along
458 	 */
459 	exphead.ex_next = exphead.ex_prev = (struct exportlist *)0;
460 	if ((inf = fopen(exname, "r")) == NULL) {
461 		syslog(LOG_ERR, "Can't open %s", exname);
462 		exit(2);
463 	}
464 	while (fgets(line, LINESIZ, inf)) {
465 		exflags = MNT_EXPORTED;
466 		rootuid = def_rootuid;
467 		cp = line;
468 		nextfield(&cp, &endcp);
469 
470 		/*
471 		 * Get file system devno and see if an entry for this
472 		 * file system already exists.
473 		 */
474 		savedc = *endcp;
475 		*endcp = '\0';
476 		if (stat(cp, &sb) < 0 || (sb.st_mode & S_IFMT) != S_IFDIR) {
477 			syslog(LOG_ERR,
478 			    "Bad Exports File, %s: %s, mountd Failed",
479 			    cp, "Not a directory");
480 			exit(2);
481 		}
482 		fep = (struct exportlist *)0;
483 		ep = exphead.ex_next;
484 		while (ep) {
485 			if (ep->ex_dev == sb.st_dev) {
486 				fep = ep;
487 				break;
488 			}
489 			ep = ep->ex_next;
490 		}
491 		*endcp = savedc;
492 
493 		/*
494 		 * Create new exports list entry
495 		 */
496 		len = endcp-cp;
497 		if (len <= RPCMNT_PATHLEN && len > 0) {
498 			ep = (struct exportlist *)malloc(sizeof(*ep));
499 			if (ep == NULL)
500 				goto err;
501 			ep->ex_next = ep->ex_prev = (struct exportlist *)0;
502 			ep->ex_groups = (struct grouplist *)0;
503 			bcopy(cp, ep->ex_dirp, len);
504 			ep->ex_dirp[len] = '\0';
505 			dirplen = len;
506 		} else {
507 			syslog(LOG_ERR, "Bad Exports File, mountd Failed");
508 			exit(2);
509 		}
510 		cp = endcp;
511 		nextfield(&cp, &endcp);
512 		len = endcp-cp;
513 		while (len > 0) {
514 			savedc = *endcp;
515 			*endcp = '\0';
516 			if (len > RPCMNT_NAMELEN)
517 				goto more;
518 			if (*cp == '-') {
519 				do_opt(cp + 1, fep, ep, &exflags, &rootuid);
520 				goto more;
521 			}
522 			if (isdigit(*cp)) {
523 				saddr = inet_addr(cp);
524 				if (saddr == -1 ||
525 				    (hp = gethostbyaddr((caddr_t)&saddr,
526 				     sizeof(saddr), AF_INET)) == NULL) {
527 					syslog(LOG_ERR,
528 					    "Bad Exports File, %s: %s", cp,
529 					    "Gethostbyaddr failed, ignored");
530 					goto more;
531 				}
532 			} else if ((hp = gethostbyname(cp)) == NULL) {
533 				syslog(LOG_ERR, "Bad Exports File, %s: %s",
534 				    cp, "Gethostbyname failed, ignored");
535 				goto more;
536 			}
537 			grp = (struct grouplist *)
538 				malloc(sizeof(struct grouplist));
539 			if (grp == NULL)
540 				goto err;
541 			nhp = grp->gr_hp = (struct hostent *)
542 				malloc(sizeof(struct hostent));
543 			if (nhp == NULL)
544 				goto err;
545 			bcopy((caddr_t)hp, (caddr_t)nhp,
546 				sizeof(struct hostent));
547 			i = strlen(hp->h_name)+1;
548 			nhp->h_name = (char *)malloc(i);
549 			if (nhp->h_name == NULL)
550 				goto err;
551 			bcopy(hp->h_name, nhp->h_name, i);
552 			addrp = hp->h_addr_list;
553 			i = 1;
554 			while (*addrp++)
555 				i++;
556 			naddrp = nhp->h_addr_list = (char **)
557 				malloc(i*sizeof(char *));
558 			if (naddrp == NULL)
559 				goto err;
560 			addrp = hp->h_addr_list;
561 			while (*addrp) {
562 				*naddrp = (char *)
563 				    malloc(hp->h_length);
564 				if (*naddrp == NULL)
565 				    goto err;
566 				bcopy(*addrp, *naddrp,
567 					hp->h_length);
568 				addrp++;
569 				naddrp++;
570 			}
571 			*naddrp = (char *)0;
572 			grp->gr_next = ep->ex_groups;
573 			ep->ex_groups = grp;
574 		more:
575 			cp = endcp;
576 			*cp = savedc;
577 			nextfield(&cp, &endcp);
578 			len = endcp - cp;
579 		}
580 		if (fep == NULL) {
581 			args.fspec = 0;
582 			args.exflags = exflags;
583 			args.exroot = rootuid;
584 			cp = (char *)0;
585 			while (statfs(ep->ex_dirp, &stfsbuf) < 0 ||
586 			       mount(MOUNT_UFS, ep->ex_dirp,
587 				     stfsbuf.f_flags|MNT_UPDATE, &args) < 0) {
588 /* 08 Sep 92*/			if (cp)
589 					*cp-- = savedc;
590 				else
591 					cp = ep->ex_dirp + dirplen - 1;
592 #ifdef OMIT
593 				if (cp == NULL)
594 					cp = ep->ex_dirp + dirplen - 1;
595 				else
596 					*cp = savedc;
597 #endif	/* OMIT*/
598 				/* back up over the last component */
599 				while (*cp == '/' && cp > ep->ex_dirp)
600 					cp--;
601 /* 08 Sep 92*/			while (cp > ep->ex_dirp && *(cp - 1) != '/')
602 					cp--;
603 				if (cp == ep->ex_dirp) {
604 					syslog(LOG_WARNING,
605 					      "Can't export %s", ep->ex_dirp);
606 					free_exp(ep);
607 					goto nextline;
608 				}
609 				savedc = *cp;
610 				*cp = '\0';
611 			}
612 			if (cp)
613 				*cp = savedc;
614 			ep->ex_rootuid = rootuid;
615 			ep->ex_exflags = exflags;
616 		} else {
617 			ep->ex_rootuid = fep->ex_rootuid;
618 			ep->ex_exflags = fep->ex_exflags;
619 		}
620 		ep->ex_dev = sb.st_dev;
621 		ep->ex_next = exphead.ex_next;
622 		ep->ex_prev = &exphead;
623 		if (ep->ex_next != NULL)
624 			ep->ex_next->ex_prev = ep;
625 		exphead.ex_next = ep;
626 nextline:
627 		;
628 	}
629 	fclose(inf);
630 	return;
631 err:
632 	syslog(LOG_ERR, "No more memory: mountd Failed");
633 	exit(2);
634 }
635 
636 /*
637  * Parse out the next white space separated field
638  */
639 nextfield(cp, endcp)
640 	char **cp;
641 	char **endcp;
642 {
643 	register char *p;
644 
645 	p = *cp;
646 	while (*p == ' ' || *p == '\t')
647 		p++;
648 	if (*p == '\n' || *p == '\0') {
649 		*cp = *endcp = p;
650 		return;
651 	}
652 	*cp = p++;
653 	while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
654 		p++;
655 	*endcp = p;
656 }
657 
658 /*
659  * Parse the option string
660  */
661 do_opt(cpopt, fep, ep, exflagsp, rootuidp)
662 	register char *cpopt;
663 	struct exportlist *fep, *ep;
664 	int *exflagsp, *rootuidp;
665 {
666 	register char *cpoptarg, *cpoptend;
667 
668 	while (cpopt && *cpopt) {
669 		if (cpoptend = index(cpopt, ','))
670 			*cpoptend++ = '\0';
671 		if (cpoptarg = index(cpopt, '='))
672 			*cpoptarg++ = '\0';
673 		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
674 			if (fep && (fep->ex_exflags & MNT_EXRDONLY) == 0)
675 				syslog(LOG_WARNING, "ro failed for %s",
676 				       ep->ex_dirp);
677 			else
678 				*exflagsp |= MNT_EXRDONLY;
679 		} else if (!strcmp(cpopt, "root") || !strcmp(cpopt, "r")) {
680 			if (cpoptarg && isdigit(*cpoptarg)) {
681 				*rootuidp = atoi(cpoptarg);
682 				if (fep && fep->ex_rootuid != *rootuidp)
683 					syslog(LOG_WARNING,
684 					       "uid failed for %s",
685 					       ep->ex_dirp);
686 			} else
687 				syslog(LOG_WARNING,
688 				       "uid failed for %s",
689 				       ep->ex_dirp);
690 		} else
691 			syslog(LOG_WARNING, "opt %s ignored for %s", cpopt,
692 			       ep->ex_dirp);
693 		cpopt = cpoptend;
694 	}
695 }
696 
697 #define	STRSIZ	(RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
698 /*
699  * Routines that maintain the remote mounttab
700  */
701 void get_mountlist()
702 {
703 	register struct mountlist *mlp, **mlpp;
704 	register char *eos, *dirp;
705 	int len;
706 	char str[STRSIZ];
707 	FILE *mlfile;
708 
709 	if (((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) &&
710 	    ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL)) {
711 		syslog(LOG_WARNING, "Can't open %s", _PATH_RMOUNTLIST);
712 		return;
713 	}
714 	mlpp = &mlhead;
715 	while (fgets(str, STRSIZ, mlfile) != NULL) {
716 		if ((dirp = index(str, '\t')) == NULL &&
717 		    (dirp = index(str, ' ')) == NULL)
718 			continue;
719 		mlp = (struct mountlist *)malloc(sizeof (*mlp));
720 		len = dirp-str;
721 		if (len > RPCMNT_NAMELEN)
722 			len = RPCMNT_NAMELEN;
723 		bcopy(str, mlp->ml_host, len);
724 		mlp->ml_host[len] = '\0';
725 		while (*dirp == '\t' || *dirp == ' ')
726 			dirp++;
727 		if ((eos = index(dirp, '\t')) == NULL &&
728 		    (eos = index(dirp, ' ')) == NULL &&
729 		    (eos = index(dirp, '\n')) == NULL)
730 			len = strlen(dirp);
731 		else
732 			len = eos-dirp;
733 		if (len > RPCMNT_PATHLEN)
734 			len = RPCMNT_PATHLEN;
735 		bcopy(dirp, mlp->ml_dirp, len);
736 		mlp->ml_dirp[len] = '\0';
737 		mlp->ml_next = (struct mountlist *)0;
738 		*mlpp = mlp;
739 		mlpp = &mlp->ml_next;
740 	}
741 	fclose(mlfile);
742 }
743 
744 void del_mlist(hostp, dirp)
745 	register char *hostp, *dirp;
746 {
747 	register struct mountlist *mlp, **mlpp;
748 	FILE *mlfile;
749 	int fnd = 0;
750 
751 	mlpp = &mlhead;
752 	mlp = mlhead;
753 	while (mlp) {
754 		if (!strcmp(mlp->ml_host, hostp) &&
755 		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
756 			fnd = 1;
757 			*mlpp = mlp->ml_next;
758 			free((caddr_t)mlp);
759 		}
760 		mlpp = &mlp->ml_next;
761 		mlp = mlp->ml_next;
762 	}
763 	if (fnd) {
764 		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
765 			syslog(LOG_WARNING, "Can't update %s", _PATH_RMOUNTLIST);
766 			return;
767 		}
768 		mlp = mlhead;
769 		while (mlp) {
770 			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
771 			mlp = mlp->ml_next;
772 		}
773 		fclose(mlfile);
774 	}
775 }
776 
777 void add_mlist(hostp, dirp)
778 	register char *hostp, *dirp;
779 {
780 	register struct mountlist *mlp, **mlpp;
781 	FILE *mlfile;
782 
783 	mlpp = &mlhead;
784 	mlp = mlhead;
785 	while (mlp) {
786 		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
787 			return;
788 		mlpp = &mlp->ml_next;
789 		mlp = mlp->ml_next;
790 	}
791 	mlp = (struct mountlist *)malloc(sizeof (*mlp));
792 	strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
793 	mlp->ml_host[RPCMNT_NAMELEN] = '\0';
794 	strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
795 	mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
796 	mlp->ml_next = (struct mountlist *)0;
797 	*mlpp = mlp;
798 	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
799 		syslog(LOG_WARNING, "Can't update %s", _PATH_RMOUNTLIST);
800 		return;
801 	}
802 	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
803 	fclose(mlfile);
804 }
805 
806 /*
807  * This function is called via. SIGTERM when the system is going down.
808  * It sends a broadcast RPCMNT_UMNTALL.
809  */
810 void
811 send_umntall()
812 {
813 	(void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
814 		xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each);
815 	exit();
816 }
817 
818 umntall_each(resultsp, raddr)
819 	caddr_t resultsp;
820 	struct sockaddr_in *raddr;
821 {
822 	return (1);
823 }
824 
825 /*
826  * Free up an exports list component
827  */
828 free_exp(ep)
829 	register struct exportlist *ep;
830 {
831 	register struct grouplist *grp;
832 	register char **addrp;
833 	struct grouplist *grp2;
834 
835 	grp = ep->ex_groups;
836 	while (grp != NULL) {
837 		addrp = grp->gr_hp->h_addr_list;
838 		while (*addrp)
839 			free(*addrp++);
840 		free((caddr_t)grp->gr_hp->h_addr_list);
841 		free(grp->gr_hp->h_name);
842 		free((caddr_t)grp->gr_hp);
843 		grp2 = grp;
844 		grp = grp->gr_next;
845 		free((caddr_t)grp2);
846 	}
847 	free((caddr_t)ep);
848 }
849