mountd.c revision 27447
1/* 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Herb Hasler and 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 38static char copyright[] = 39"@(#) Copyright (c) 1989, 1993\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41#endif /*not lint*/ 42 43#ifndef lint 44/*static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95"; */ 45static const char rcsid[] = 46 "$Id: mountd.c,v 1.21 1997/04/30 18:40:12 pst Exp $"; 47#endif /*not lint*/ 48 49#include <sys/param.h> 50#include <sys/file.h> 51#include <sys/ioctl.h> 52#include <sys/mount.h> 53#include <sys/socket.h> 54#include <sys/stat.h> 55#include <sys/syslog.h> 56#include <sys/ucred.h> 57#include <sys/sysctl.h> 58 59#include <rpc/rpc.h> 60#include <rpc/pmap_clnt.h> 61#include <rpc/pmap_prot.h> 62#ifdef ISO 63#include <netiso/iso.h> 64#endif 65#include <nfs/rpcv2.h> 66#include <nfs/nfsproto.h> 67#include <nfs/nfs.h> 68#include <ufs/ufs/ufsmount.h> 69#include <msdosfs/msdosfsmount.h> 70#include <isofs/cd9660/cd9660_mount.h> /* XXX need isofs in include */ 71 72#include <arpa/inet.h> 73 74#include <ctype.h> 75#include <errno.h> 76#include <grp.h> 77#include <netdb.h> 78#include <pwd.h> 79#include <signal.h> 80#include <stdio.h> 81#include <stdlib.h> 82#include <string.h> 83#include <unistd.h> 84#include "pathnames.h" 85 86#ifdef DEBUG 87#include <stdarg.h> 88#endif 89 90/* 91 * Structures for keeping the mount list and export list 92 */ 93struct mountlist { 94 struct mountlist *ml_next; 95 char ml_host[RPCMNT_NAMELEN+1]; 96 char ml_dirp[RPCMNT_PATHLEN+1]; 97}; 98 99struct dirlist { 100 struct dirlist *dp_left; 101 struct dirlist *dp_right; 102 int dp_flag; 103 struct hostlist *dp_hosts; /* List of hosts this dir exported to */ 104 char dp_dirp[1]; /* Actually malloc'd to size of dir */ 105}; 106/* dp_flag bits */ 107#define DP_DEFSET 0x1 108#define DP_HOSTSET 0x2 109#define DP_KERB 0x4 110 111struct exportlist { 112 struct exportlist *ex_next; 113 struct dirlist *ex_dirl; 114 struct dirlist *ex_defdir; 115 int ex_flag; 116 fsid_t ex_fs; 117 char *ex_fsdir; 118 char *ex_indexfile; 119}; 120/* ex_flag bits */ 121#define EX_LINKED 0x1 122 123struct netmsk { 124 u_long nt_net; 125 u_long nt_mask; 126 char *nt_name; 127}; 128 129union grouptypes { 130 struct hostent *gt_hostent; 131 struct netmsk gt_net; 132#ifdef ISO 133 struct sockaddr_iso *gt_isoaddr; 134#endif 135}; 136 137struct grouplist { 138 int gr_type; 139 union grouptypes gr_ptr; 140 struct grouplist *gr_next; 141}; 142/* Group types */ 143#define GT_NULL 0x0 144#define GT_HOST 0x1 145#define GT_NET 0x2 146#define GT_ISO 0x4 147#define GT_IGNORE 0x5 148 149struct hostlist { 150 int ht_flag; /* Uses DP_xx bits */ 151 struct grouplist *ht_grp; 152 struct hostlist *ht_next; 153}; 154 155struct fhreturn { 156 int fhr_flag; 157 int fhr_vers; 158 nfsfh_t fhr_fh; 159}; 160 161/* Global defs */ 162char *add_expdir __P((struct dirlist **, char *, int)); 163void add_dlist __P((struct dirlist **, struct dirlist *, 164 struct grouplist *, int)); 165void add_mlist __P((char *, char *)); 166int check_dirpath __P((char *)); 167int check_options __P((struct dirlist *)); 168int chk_host __P((struct dirlist *, u_long, int *, int *)); 169void del_mlist __P((char *, char *)); 170struct dirlist *dirp_search __P((struct dirlist *, char *)); 171int do_mount __P((struct exportlist *, struct grouplist *, int, 172 struct ucred *, char *, int, struct statfs *)); 173int do_opt __P((char **, char **, struct exportlist *, struct grouplist *, 174 int *, int *, struct ucred *)); 175struct exportlist *ex_search __P((fsid_t *)); 176struct exportlist *get_exp __P((void)); 177void free_dir __P((struct dirlist *)); 178void free_exp __P((struct exportlist *)); 179void free_grp __P((struct grouplist *)); 180void free_host __P((struct hostlist *)); 181void get_exportlist __P((void)); 182int get_host __P((char *, struct grouplist *, struct grouplist *)); 183int get_num __P((char *)); 184struct hostlist *get_ht __P((void)); 185int get_line __P((void)); 186void get_mountlist __P((void)); 187int get_net __P((char *, struct netmsk *, int)); 188void getexp_err __P((struct exportlist *, struct grouplist *)); 189struct grouplist *get_grp __P((void)); 190void hang_dirp __P((struct dirlist *, struct grouplist *, 191 struct exportlist *, int)); 192void mntsrv __P((struct svc_req *, SVCXPRT *)); 193void nextfield __P((char **, char **)); 194void out_of_mem __P((void)); 195void parsecred __P((char *, struct ucred *)); 196int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *)); 197int scan_tree __P((struct dirlist *, u_long)); 198void send_umntall __P((void)); 199int umntall_each __P((caddr_t, struct sockaddr_in *)); 200int xdr_dir __P((XDR *, char *)); 201int xdr_explist __P((XDR *, caddr_t)); 202int xdr_fhs __P((XDR *, caddr_t)); 203int xdr_mlist __P((XDR *, caddr_t)); 204 205/* C library */ 206int getnetgrent(); 207void endnetgrent(); 208void setnetgrent(); 209 210#ifdef ISO 211struct iso_addr *iso_addr(); 212#endif 213 214struct exportlist *exphead; 215struct mountlist *mlhead; 216struct grouplist *grphead; 217char exname[MAXPATHLEN]; 218struct ucred def_anon = { 219 1, 220 (uid_t) -2, 221 1, 222 { (gid_t) -2 } 223}; 224int force_v2 = 0; 225int resvport_only = 1; 226int dir_only = 1; 227int opt_flags; 228/* Bits for above */ 229#define OP_MAPROOT 0x01 230#define OP_MAPALL 0x02 231#define OP_KERB 0x04 232#define OP_MASK 0x08 233#define OP_NET 0x10 234#define OP_ISO 0x20 235#define OP_ALLDIRS 0x40 236 237#ifdef DEBUG 238int debug = 1; 239void SYSLOG __P((int, const char *, ...)); 240#define syslog SYSLOG 241#else 242int debug = 0; 243#endif 244 245/* 246 * Mountd server for NFS mount protocol as described in: 247 * NFS: Network File System Protocol Specification, RFC1094, Appendix A 248 * The optional arguments are the exports file name 249 * default: _PATH_EXPORTS 250 * and "-n" to allow nonroot mount. 251 */ 252int 253main(argc, argv) 254 int argc; 255 char **argv; 256{ 257 SVCXPRT *udptransp, *tcptransp; 258 int c; 259 int mib[3]; 260#ifdef __FreeBSD__ 261 struct vfsconf vfc; 262 int error; 263 264 error = getvfsbyname("nfs", &vfc); 265 if (error && vfsisloadable("nfs")) { 266 if(vfsload("nfs")) 267 err(1, "vfsload(nfs)"); 268 endvfsent(); /* flush cache */ 269 error = getvfsbyname("nfs", &vfc); 270 } 271 if (error) 272 errx(1, "NFS support is not available in the running kernel"); 273#endif /* __FreeBSD__ */ 274 275 while ((c = getopt(argc, argv, "2dnr")) != -1) 276 switch (c) { 277 case '2': 278 force_v2 = 1; 279 break; 280 case 'n': 281 resvport_only = 0; 282 break; 283 case 'r': 284 dir_only = 0; 285 break; 286 case 'd': 287 debug = debug ? 0 : 1; 288 break; 289 default: 290 fprintf(stderr, "Usage: mountd [-d] [-r] [-n] [export_file]\n"); 291 exit(1); 292 }; 293 argc -= optind; 294 argv += optind; 295 grphead = (struct grouplist *)NULL; 296 exphead = (struct exportlist *)NULL; 297 mlhead = (struct mountlist *)NULL; 298 if (argc == 1) { 299 strncpy(exname, *argv, MAXPATHLEN-1); 300 exname[MAXPATHLEN-1] = '\0'; 301 } else 302 strcpy(exname, _PATH_EXPORTS); 303 openlog("mountd", LOG_PID, LOG_DAEMON); 304 if (debug) 305 fprintf(stderr,"Getting export list.\n"); 306 get_exportlist(); 307 if (debug) 308 fprintf(stderr,"Getting mount list.\n"); 309 get_mountlist(); 310 if (debug) 311 fprintf(stderr,"Here we go.\n"); 312 if (debug == 0) { 313 daemon(0, 0); 314 signal(SIGINT, SIG_IGN); 315 signal(SIGQUIT, SIG_IGN); 316 } 317 signal(SIGHUP, (void (*) __P((int))) get_exportlist); 318 signal(SIGTERM, (void (*) __P((int))) send_umntall); 319 { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w"); 320 if (pidfile != NULL) { 321 fprintf(pidfile, "%d\n", getpid()); 322 fclose(pidfile); 323 } 324 } 325 326 if (!resvport_only) { 327 mib[0] = CTL_VFS; 328 mib[1] = MOUNT_NFS; 329 mib[2] = NFS_NFSPRIVPORT; 330 if (sysctl(mib, 3, NULL, NULL, &resvport_only, 331 sizeof(resvport_only)) != 0 && errno != ENOENT) { 332 syslog(LOG_ERR, "sysctl: %m"); 333 exit(1); 334 } 335 } 336 337 if ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL || 338 (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) { 339 syslog(LOG_ERR, "Can't create socket"); 340 exit(1); 341 } 342 pmap_unset(RPCPROG_MNT, 1); 343 pmap_unset(RPCPROG_MNT, 3); 344 if (!force_v2) 345 if (!svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_UDP) || 346 !svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_TCP)) { 347 syslog(LOG_ERR, "Can't register mount"); 348 exit(1); 349 } 350 if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_UDP) || 351 !svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_TCP)) { 352 syslog(LOG_ERR, "Can't register mount"); 353 exit(1); 354 } 355 svc_run(); 356 syslog(LOG_ERR, "Mountd died"); 357 exit(1); 358} 359 360/* 361 * The mount rpc service 362 */ 363void 364mntsrv(rqstp, transp) 365 struct svc_req *rqstp; 366 SVCXPRT *transp; 367{ 368 struct exportlist *ep; 369 struct dirlist *dp; 370 struct fhreturn fhr; 371 struct stat stb; 372 struct statfs fsb; 373 struct hostent *hp; 374 u_long saddr; 375 u_short sport; 376 char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN]; 377 int bad = ENOENT, defset, hostset; 378 sigset_t sighup_mask; 379 380 sigemptyset(&sighup_mask); 381 sigaddset(&sighup_mask, SIGHUP); 382 saddr = transp->xp_raddr.sin_addr.s_addr; 383 sport = ntohs(transp->xp_raddr.sin_port); 384 hp = (struct hostent *)NULL; 385 switch (rqstp->rq_proc) { 386 case NULLPROC: 387 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) 388 syslog(LOG_ERR, "Can't send reply"); 389 return; 390 case RPCMNT_MOUNT: 391 if (sport >= IPPORT_RESERVED && resvport_only) { 392 svcerr_weakauth(transp); 393 return; 394 } 395 if (!svc_getargs(transp, xdr_dir, rpcpath)) { 396 svcerr_decode(transp); 397 return; 398 } 399 400 /* 401 * Get the real pathname and make sure it is a directory 402 * or a regular file if the -r option was specified 403 * and it exists. 404 */ 405 if (realpath(rpcpath, dirpath) == 0 || 406 stat(dirpath, &stb) < 0 || 407 (!S_ISDIR(stb.st_mode) && 408 (dir_only || !S_ISREG(stb.st_mode))) || 409 statfs(dirpath, &fsb) < 0) { 410 chdir("/"); /* Just in case realpath doesn't */ 411 if (debug) 412 fprintf(stderr, "stat failed on %s\n", dirpath); 413 if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad)) 414 syslog(LOG_ERR, "Can't send reply"); 415 return; 416 } 417 418 /* Check in the exports list */ 419 sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 420 ep = ex_search(&fsb.f_fsid); 421 hostset = defset = 0; 422 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) || 423 ((dp = dirp_search(ep->ex_dirl, dirpath)) && 424 chk_host(dp, saddr, &defset, &hostset)) || 425 (defset && scan_tree(ep->ex_defdir, saddr) == 0 && 426 scan_tree(ep->ex_dirl, saddr) == 0))) { 427 if (hostset & DP_HOSTSET) 428 fhr.fhr_flag = hostset; 429 else 430 fhr.fhr_flag = defset; 431 fhr.fhr_vers = rqstp->rq_vers; 432 /* Get the file handle */ 433 memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t)); 434 if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) { 435 bad = errno; 436 syslog(LOG_ERR, "Can't get fh for %s", dirpath); 437 if (!svc_sendreply(transp, xdr_long, 438 (caddr_t)&bad)) 439 syslog(LOG_ERR, "Can't send reply"); 440 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 441 return; 442 } 443 if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr)) 444 syslog(LOG_ERR, "Can't send reply"); 445 if (hp == NULL) 446 hp = gethostbyaddr((caddr_t)&saddr, 447 sizeof(saddr), AF_INET); 448 if (hp) 449 add_mlist(hp->h_name, dirpath); 450 else 451 add_mlist(inet_ntoa(transp->xp_raddr.sin_addr), 452 dirpath); 453 if (debug) 454 fprintf(stderr,"Mount successfull.\n"); 455 } else { 456 bad = EACCES; 457 if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad)) 458 syslog(LOG_ERR, "Can't send reply"); 459 } 460 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 461 return; 462 case RPCMNT_DUMP: 463 if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL)) 464 syslog(LOG_ERR, "Can't send reply"); 465 return; 466 case RPCMNT_UMOUNT: 467 if (sport >= IPPORT_RESERVED && resvport_only) { 468 svcerr_weakauth(transp); 469 return; 470 } 471 if (!svc_getargs(transp, xdr_dir, dirpath)) { 472 svcerr_decode(transp); 473 return; 474 } 475 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) 476 syslog(LOG_ERR, "Can't send reply"); 477 hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); 478 if (hp) 479 del_mlist(hp->h_name, dirpath); 480 del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath); 481 return; 482 case RPCMNT_UMNTALL: 483 if (sport >= IPPORT_RESERVED && resvport_only) { 484 svcerr_weakauth(transp); 485 return; 486 } 487 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) 488 syslog(LOG_ERR, "Can't send reply"); 489 hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); 490 if (hp) 491 del_mlist(hp->h_name, (char *)NULL); 492 del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), (char *)NULL); 493 return; 494 case RPCMNT_EXPORT: 495 if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL)) 496 syslog(LOG_ERR, "Can't send reply"); 497 return; 498 default: 499 svcerr_noproc(transp); 500 return; 501 } 502} 503 504/* 505 * Xdr conversion for a dirpath string 506 */ 507int 508xdr_dir(xdrsp, dirp) 509 XDR *xdrsp; 510 char *dirp; 511{ 512 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 513} 514 515/* 516 * Xdr routine to generate file handle reply 517 */ 518int 519xdr_fhs(xdrsp, cp) 520 XDR *xdrsp; 521 caddr_t cp; 522{ 523 register struct fhreturn *fhrp = (struct fhreturn *)cp; 524 u_long ok = 0, len, auth; 525 526 if (!xdr_long(xdrsp, &ok)) 527 return (0); 528 switch (fhrp->fhr_vers) { 529 case 1: 530 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH)); 531 case 3: 532 len = NFSX_V3FH; 533 if (!xdr_long(xdrsp, &len)) 534 return (0); 535 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len)) 536 return (0); 537 if (fhrp->fhr_flag & DP_KERB) 538 auth = RPCAUTH_KERB4; 539 else 540 auth = RPCAUTH_UNIX; 541 len = 1; 542 if (!xdr_long(xdrsp, &len)) 543 return (0); 544 return (xdr_long(xdrsp, &auth)); 545 }; 546 return (0); 547} 548 549int 550xdr_mlist(xdrsp, cp) 551 XDR *xdrsp; 552 caddr_t cp; 553{ 554 struct mountlist *mlp; 555 int true = 1; 556 int false = 0; 557 char *strp; 558 559 mlp = mlhead; 560 while (mlp) { 561 if (!xdr_bool(xdrsp, &true)) 562 return (0); 563 strp = &mlp->ml_host[0]; 564 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) 565 return (0); 566 strp = &mlp->ml_dirp[0]; 567 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) 568 return (0); 569 mlp = mlp->ml_next; 570 } 571 if (!xdr_bool(xdrsp, &false)) 572 return (0); 573 return (1); 574} 575 576/* 577 * Xdr conversion for export list 578 */ 579int 580xdr_explist(xdrsp, cp) 581 XDR *xdrsp; 582 caddr_t cp; 583{ 584 struct exportlist *ep; 585 int false = 0; 586 int putdef; 587 sigset_t sighup_mask; 588 589 sigemptyset(&sighup_mask); 590 sigaddset(&sighup_mask, SIGHUP); 591 sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 592 ep = exphead; 593 while (ep) { 594 putdef = 0; 595 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef)) 596 goto errout; 597 if (ep->ex_defdir && putdef == 0 && 598 put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL, 599 &putdef)) 600 goto errout; 601 ep = ep->ex_next; 602 } 603 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 604 if (!xdr_bool(xdrsp, &false)) 605 return (0); 606 return (1); 607errout: 608 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 609 return (0); 610} 611 612/* 613 * Called from xdr_explist() to traverse the tree and export the 614 * directory paths. 615 */ 616int 617put_exlist(dp, xdrsp, adp, putdefp) 618 struct dirlist *dp; 619 XDR *xdrsp; 620 struct dirlist *adp; 621 int *putdefp; 622{ 623 struct grouplist *grp; 624 struct hostlist *hp; 625 int true = 1; 626 int false = 0; 627 int gotalldir = 0; 628 char *strp; 629 630 if (dp) { 631 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp)) 632 return (1); 633 if (!xdr_bool(xdrsp, &true)) 634 return (1); 635 strp = dp->dp_dirp; 636 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) 637 return (1); 638 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { 639 gotalldir = 1; 640 *putdefp = 1; 641 } 642 if ((dp->dp_flag & DP_DEFSET) == 0 && 643 (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { 644 hp = dp->dp_hosts; 645 while (hp) { 646 grp = hp->ht_grp; 647 if (grp->gr_type == GT_HOST) { 648 if (!xdr_bool(xdrsp, &true)) 649 return (1); 650 strp = grp->gr_ptr.gt_hostent->h_name; 651 if (!xdr_string(xdrsp, &strp, 652 RPCMNT_NAMELEN)) 653 return (1); 654 } else if (grp->gr_type == GT_NET) { 655 if (!xdr_bool(xdrsp, &true)) 656 return (1); 657 strp = grp->gr_ptr.gt_net.nt_name; 658 if (!xdr_string(xdrsp, &strp, 659 RPCMNT_NAMELEN)) 660 return (1); 661 } 662 hp = hp->ht_next; 663 if (gotalldir && hp == (struct hostlist *)NULL) { 664 hp = adp->dp_hosts; 665 gotalldir = 0; 666 } 667 } 668 } 669 if (!xdr_bool(xdrsp, &false)) 670 return (1); 671 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp)) 672 return (1); 673 } 674 return (0); 675} 676 677#define LINESIZ 10240 678char line[LINESIZ]; 679FILE *exp_file; 680 681/* 682 * Get the export list 683 */ 684void 685get_exportlist() 686{ 687 struct exportlist *ep, *ep2; 688 struct grouplist *grp, *tgrp; 689 struct exportlist **epp; 690 struct dirlist *dirhead; 691 struct statfs fsb, *fsp; 692 struct hostent *hpe; 693 struct ucred anon; 694 char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; 695 int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp; 696 697 /* 698 * First, get rid of the old list 699 */ 700 ep = exphead; 701 while (ep) { 702 ep2 = ep; 703 ep = ep->ex_next; 704 free_exp(ep2); 705 } 706 exphead = (struct exportlist *)NULL; 707 708 grp = grphead; 709 while (grp) { 710 tgrp = grp; 711 grp = grp->gr_next; 712 free_grp(tgrp); 713 } 714 grphead = (struct grouplist *)NULL; 715 716 /* 717 * And delete exports that are in the kernel for all local 718 * file systems. 719 * XXX: Should know how to handle all local exportable file systems 720 * instead of just "ufs". 721 */ 722 num = getmntinfo(&fsp, MNT_NOWAIT); 723 for (i = 0; i < num; i++) { 724 union { 725 struct ufs_args ua; 726 struct iso_args ia; 727 struct mfs_args ma; 728 struct msdosfs_args da; 729 } targs; 730 731 if (!strcmp(fsp->f_fstypename, "mfs") || 732 !strcmp(fsp->f_fstypename, "ufs") || 733 !strcmp(fsp->f_fstypename, "msdos") || 734 !strcmp(fsp->f_fstypename, "cd9660")) { 735 targs.ua.fspec = NULL; 736 targs.ua.export.ex_flags = MNT_DELEXPORT; 737 if (mount(fsp->f_fstypename, fsp->f_mntonname, 738 fsp->f_flags | MNT_UPDATE, 739 (caddr_t)&targs) < 0) 740 syslog(LOG_ERR, "Can't delete exports for %s", 741 fsp->f_mntonname); 742 } 743 fsp++; 744 } 745 746 /* 747 * Read in the exports file and build the list, calling 748 * mount() as we go along to push the export rules into the kernel. 749 */ 750 if ((exp_file = fopen(exname, "r")) == NULL) { 751 syslog(LOG_ERR, "Can't open %s", exname); 752 exit(2); 753 } 754 dirhead = (struct dirlist *)NULL; 755 while (get_line()) { 756 if (debug) 757 fprintf(stderr,"Got line %s\n",line); 758 cp = line; 759 nextfield(&cp, &endcp); 760 if (*cp == '#') 761 goto nextline; 762 763 /* 764 * Set defaults. 765 */ 766 has_host = FALSE; 767 anon = def_anon; 768 exflags = MNT_EXPORTED; 769 got_nondir = 0; 770 opt_flags = 0; 771 ep = (struct exportlist *)NULL; 772 773 /* 774 * Create new exports list entry 775 */ 776 len = endcp-cp; 777 tgrp = grp = get_grp(); 778 while (len > 0) { 779 if (len > RPCMNT_NAMELEN) { 780 getexp_err(ep, tgrp); 781 goto nextline; 782 } 783 if (*cp == '-') { 784 if (ep == (struct exportlist *)NULL) { 785 getexp_err(ep, tgrp); 786 goto nextline; 787 } 788 if (debug) 789 fprintf(stderr, "doing opt %s\n", cp); 790 got_nondir = 1; 791 if (do_opt(&cp, &endcp, ep, grp, &has_host, 792 &exflags, &anon)) { 793 getexp_err(ep, tgrp); 794 goto nextline; 795 } 796 } else if (*cp == '/') { 797 savedc = *endcp; 798 *endcp = '\0'; 799 if (check_dirpath(cp) && 800 statfs(cp, &fsb) >= 0) { 801 if (got_nondir) { 802 syslog(LOG_ERR, "Dirs must be first"); 803 getexp_err(ep, tgrp); 804 goto nextline; 805 } 806 if (ep) { 807 if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] || 808 ep->ex_fs.val[1] != fsb.f_fsid.val[1]) { 809 getexp_err(ep, tgrp); 810 goto nextline; 811 } 812 } else { 813 /* 814 * See if this directory is already 815 * in the list. 816 */ 817 ep = ex_search(&fsb.f_fsid); 818 if (ep == (struct exportlist *)NULL) { 819 ep = get_exp(); 820 ep->ex_fs = fsb.f_fsid; 821 ep->ex_fsdir = (char *) 822 malloc(strlen(fsb.f_mntonname) + 1); 823 if (ep->ex_fsdir) 824 strcpy(ep->ex_fsdir, 825 fsb.f_mntonname); 826 else 827 out_of_mem(); 828 if (debug) 829 fprintf(stderr, 830 "Making new ep fs=0x%x,0x%x\n", 831 fsb.f_fsid.val[0], 832 fsb.f_fsid.val[1]); 833 } else if (debug) 834 fprintf(stderr, 835 "Found ep fs=0x%x,0x%x\n", 836 fsb.f_fsid.val[0], 837 fsb.f_fsid.val[1]); 838 } 839 840 /* 841 * Add dirpath to export mount point. 842 */ 843 dirp = add_expdir(&dirhead, cp, len); 844 dirplen = len; 845 } else { 846 getexp_err(ep, tgrp); 847 goto nextline; 848 } 849 *endcp = savedc; 850 } else { 851 savedc = *endcp; 852 *endcp = '\0'; 853 got_nondir = 1; 854 if (ep == (struct exportlist *)NULL) { 855 getexp_err(ep, tgrp); 856 goto nextline; 857 } 858 859 /* 860 * Get the host or netgroup. 861 */ 862 setnetgrent(cp); 863 netgrp = getnetgrent(&hst, &usr, &dom); 864 do { 865 if (has_host) { 866 grp->gr_next = get_grp(); 867 grp = grp->gr_next; 868 } 869 if (netgrp) { 870 if (get_host(hst, grp, tgrp)) { 871 syslog(LOG_ERR, "Bad netgroup %s", cp); 872 getexp_err(ep, tgrp); 873 endnetgrent(); 874 goto nextline; 875 } 876 } else if (get_host(cp, grp, tgrp)) { 877 getexp_err(ep, tgrp); 878 goto nextline; 879 } 880 has_host = TRUE; 881 } while (netgrp && getnetgrent(&hst, &usr, &dom)); 882 endnetgrent(); 883 *endcp = savedc; 884 } 885 cp = endcp; 886 nextfield(&cp, &endcp); 887 len = endcp - cp; 888 } 889 if (check_options(dirhead)) { 890 getexp_err(ep, tgrp); 891 goto nextline; 892 } 893 if (!has_host) { 894 grp->gr_type = GT_HOST; 895 if (debug) 896 fprintf(stderr,"Adding a default entry\n"); 897 /* add a default group and make the grp list NULL */ 898 hpe = (struct hostent *)malloc(sizeof(struct hostent)); 899 if (hpe == (struct hostent *)NULL) 900 out_of_mem(); 901 hpe->h_name = strdup("Default"); 902 hpe->h_addrtype = AF_INET; 903 hpe->h_length = sizeof (u_long); 904 hpe->h_addr_list = (char **)NULL; 905 grp->gr_ptr.gt_hostent = hpe; 906 907 /* 908 * Don't allow a network export coincide with a list of 909 * host(s) on the same line. 910 */ 911 } else if ((opt_flags & OP_NET) && tgrp->gr_next) { 912 getexp_err(ep, tgrp); 913 goto nextline; 914 } 915 916 /* 917 * Loop through hosts, pushing the exports into the kernel. 918 * After loop, tgrp points to the start of the list and 919 * grp points to the last entry in the list. 920 */ 921 grp = tgrp; 922 do { 923 if (do_mount(ep, grp, exflags, &anon, dirp, 924 dirplen, &fsb)) { 925 getexp_err(ep, tgrp); 926 goto nextline; 927 } 928 } while (grp->gr_next && (grp = grp->gr_next)); 929 930 /* 931 * Success. Update the data structures. 932 */ 933 if (has_host) { 934 hang_dirp(dirhead, tgrp, ep, opt_flags); 935 grp->gr_next = grphead; 936 grphead = tgrp; 937 } else { 938 hang_dirp(dirhead, (struct grouplist *)NULL, ep, 939 opt_flags); 940 free_grp(grp); 941 } 942 dirhead = (struct dirlist *)NULL; 943 if ((ep->ex_flag & EX_LINKED) == 0) { 944 ep2 = exphead; 945 epp = &exphead; 946 947 /* 948 * Insert in the list in alphabetical order. 949 */ 950 while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) { 951 epp = &ep2->ex_next; 952 ep2 = ep2->ex_next; 953 } 954 if (ep2) 955 ep->ex_next = ep2; 956 *epp = ep; 957 ep->ex_flag |= EX_LINKED; 958 } 959nextline: 960 if (dirhead) { 961 free_dir(dirhead); 962 dirhead = (struct dirlist *)NULL; 963 } 964 } 965 fclose(exp_file); 966} 967 968/* 969 * Allocate an export list element 970 */ 971struct exportlist * 972get_exp() 973{ 974 struct exportlist *ep; 975 976 ep = (struct exportlist *)malloc(sizeof (struct exportlist)); 977 if (ep == (struct exportlist *)NULL) 978 out_of_mem(); 979 memset(ep, 0, sizeof(struct exportlist)); 980 return (ep); 981} 982 983/* 984 * Allocate a group list element 985 */ 986struct grouplist * 987get_grp() 988{ 989 struct grouplist *gp; 990 991 gp = (struct grouplist *)malloc(sizeof (struct grouplist)); 992 if (gp == (struct grouplist *)NULL) 993 out_of_mem(); 994 memset(gp, 0, sizeof(struct grouplist)); 995 return (gp); 996} 997 998/* 999 * Clean up upon an error in get_exportlist(). 1000 */ 1001void 1002getexp_err(ep, grp) 1003 struct exportlist *ep; 1004 struct grouplist *grp; 1005{ 1006 struct grouplist *tgrp; 1007 1008 syslog(LOG_ERR, "Bad exports list line %s", line); 1009 if (ep && (ep->ex_flag & EX_LINKED) == 0) 1010 free_exp(ep); 1011 while (grp) { 1012 tgrp = grp; 1013 grp = grp->gr_next; 1014 free_grp(tgrp); 1015 } 1016} 1017 1018/* 1019 * Search the export list for a matching fs. 1020 */ 1021struct exportlist * 1022ex_search(fsid) 1023 fsid_t *fsid; 1024{ 1025 struct exportlist *ep; 1026 1027 ep = exphead; 1028 while (ep) { 1029 if (ep->ex_fs.val[0] == fsid->val[0] && 1030 ep->ex_fs.val[1] == fsid->val[1]) 1031 return (ep); 1032 ep = ep->ex_next; 1033 } 1034 return (ep); 1035} 1036 1037/* 1038 * Add a directory path to the list. 1039 */ 1040char * 1041add_expdir(dpp, cp, len) 1042 struct dirlist **dpp; 1043 char *cp; 1044 int len; 1045{ 1046 struct dirlist *dp; 1047 1048 dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len); 1049 dp->dp_left = *dpp; 1050 dp->dp_right = (struct dirlist *)NULL; 1051 dp->dp_flag = 0; 1052 dp->dp_hosts = (struct hostlist *)NULL; 1053 strcpy(dp->dp_dirp, cp); 1054 *dpp = dp; 1055 return (dp->dp_dirp); 1056} 1057 1058/* 1059 * Hang the dir list element off the dirpath binary tree as required 1060 * and update the entry for host. 1061 */ 1062void 1063hang_dirp(dp, grp, ep, flags) 1064 struct dirlist *dp; 1065 struct grouplist *grp; 1066 struct exportlist *ep; 1067 int flags; 1068{ 1069 struct hostlist *hp; 1070 struct dirlist *dp2; 1071 1072 if (flags & OP_ALLDIRS) { 1073 if (ep->ex_defdir) 1074 free((caddr_t)dp); 1075 else 1076 ep->ex_defdir = dp; 1077 if (grp == (struct grouplist *)NULL) { 1078 ep->ex_defdir->dp_flag |= DP_DEFSET; 1079 if (flags & OP_KERB) 1080 ep->ex_defdir->dp_flag |= DP_KERB; 1081 } else while (grp) { 1082 hp = get_ht(); 1083 if (flags & OP_KERB) 1084 hp->ht_flag |= DP_KERB; 1085 hp->ht_grp = grp; 1086 hp->ht_next = ep->ex_defdir->dp_hosts; 1087 ep->ex_defdir->dp_hosts = hp; 1088 grp = grp->gr_next; 1089 } 1090 } else { 1091 1092 /* 1093 * Loop throught the directories adding them to the tree. 1094 */ 1095 while (dp) { 1096 dp2 = dp->dp_left; 1097 add_dlist(&ep->ex_dirl, dp, grp, flags); 1098 dp = dp2; 1099 } 1100 } 1101} 1102 1103/* 1104 * Traverse the binary tree either updating a node that is already there 1105 * for the new directory or adding the new node. 1106 */ 1107void 1108add_dlist(dpp, newdp, grp, flags) 1109 struct dirlist **dpp; 1110 struct dirlist *newdp; 1111 struct grouplist *grp; 1112 int flags; 1113{ 1114 struct dirlist *dp; 1115 struct hostlist *hp; 1116 int cmp; 1117 1118 dp = *dpp; 1119 if (dp) { 1120 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); 1121 if (cmp > 0) { 1122 add_dlist(&dp->dp_left, newdp, grp, flags); 1123 return; 1124 } else if (cmp < 0) { 1125 add_dlist(&dp->dp_right, newdp, grp, flags); 1126 return; 1127 } else 1128 free((caddr_t)newdp); 1129 } else { 1130 dp = newdp; 1131 dp->dp_left = (struct dirlist *)NULL; 1132 *dpp = dp; 1133 } 1134 if (grp) { 1135 1136 /* 1137 * Hang all of the host(s) off of the directory point. 1138 */ 1139 do { 1140 hp = get_ht(); 1141 if (flags & OP_KERB) 1142 hp->ht_flag |= DP_KERB; 1143 hp->ht_grp = grp; 1144 hp->ht_next = dp->dp_hosts; 1145 dp->dp_hosts = hp; 1146 grp = grp->gr_next; 1147 } while (grp); 1148 } else { 1149 dp->dp_flag |= DP_DEFSET; 1150 if (flags & OP_KERB) 1151 dp->dp_flag |= DP_KERB; 1152 } 1153} 1154 1155/* 1156 * Search for a dirpath on the export point. 1157 */ 1158struct dirlist * 1159dirp_search(dp, dirpath) 1160 struct dirlist *dp; 1161 char *dirpath; 1162{ 1163 int cmp; 1164 1165 if (dp) { 1166 cmp = strcmp(dp->dp_dirp, dirpath); 1167 if (cmp > 0) 1168 return (dirp_search(dp->dp_left, dirpath)); 1169 else if (cmp < 0) 1170 return (dirp_search(dp->dp_right, dirpath)); 1171 else 1172 return (dp); 1173 } 1174 return (dp); 1175} 1176 1177/* 1178 * Scan for a host match in a directory tree. 1179 */ 1180int 1181chk_host(dp, saddr, defsetp, hostsetp) 1182 struct dirlist *dp; 1183 u_long saddr; 1184 int *defsetp; 1185 int *hostsetp; 1186{ 1187 struct hostlist *hp; 1188 struct grouplist *grp; 1189 u_long **addrp; 1190 1191 if (dp) { 1192 if (dp->dp_flag & DP_DEFSET) 1193 *defsetp = dp->dp_flag; 1194 hp = dp->dp_hosts; 1195 while (hp) { 1196 grp = hp->ht_grp; 1197 switch (grp->gr_type) { 1198 case GT_HOST: 1199 addrp = (u_long **) 1200 grp->gr_ptr.gt_hostent->h_addr_list; 1201 while (*addrp) { 1202 if (**addrp == saddr) { 1203 *hostsetp = (hp->ht_flag | DP_HOSTSET); 1204 return (1); 1205 } 1206 addrp++; 1207 } 1208 break; 1209 case GT_NET: 1210 if ((saddr & grp->gr_ptr.gt_net.nt_mask) == 1211 grp->gr_ptr.gt_net.nt_net) { 1212 *hostsetp = (hp->ht_flag | DP_HOSTSET); 1213 return (1); 1214 } 1215 break; 1216 }; 1217 hp = hp->ht_next; 1218 } 1219 } 1220 return (0); 1221} 1222 1223/* 1224 * Scan tree for a host that matches the address. 1225 */ 1226int 1227scan_tree(dp, saddr) 1228 struct dirlist *dp; 1229 u_long saddr; 1230{ 1231 int defset, hostset; 1232 1233 if (dp) { 1234 if (scan_tree(dp->dp_left, saddr)) 1235 return (1); 1236 if (chk_host(dp, saddr, &defset, &hostset)) 1237 return (1); 1238 if (scan_tree(dp->dp_right, saddr)) 1239 return (1); 1240 } 1241 return (0); 1242} 1243 1244/* 1245 * Traverse the dirlist tree and free it up. 1246 */ 1247void 1248free_dir(dp) 1249 struct dirlist *dp; 1250{ 1251 1252 if (dp) { 1253 free_dir(dp->dp_left); 1254 free_dir(dp->dp_right); 1255 free_host(dp->dp_hosts); 1256 free((caddr_t)dp); 1257 } 1258} 1259 1260/* 1261 * Parse the option string and update fields. 1262 * Option arguments may either be -<option>=<value> or 1263 * -<option> <value> 1264 */ 1265int 1266do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr) 1267 char **cpp, **endcpp; 1268 struct exportlist *ep; 1269 struct grouplist *grp; 1270 int *has_hostp; 1271 int *exflagsp; 1272 struct ucred *cr; 1273{ 1274 char *cpoptarg, *cpoptend; 1275 char *cp, *endcp, *cpopt, savedc, savedc2; 1276 int allflag, usedarg; 1277 1278 cpopt = *cpp; 1279 cpopt++; 1280 cp = *endcpp; 1281 savedc = *cp; 1282 *cp = '\0'; 1283 while (cpopt && *cpopt) { 1284 allflag = 1; 1285 usedarg = -2; 1286 if (cpoptend = strchr(cpopt, ',')) { 1287 *cpoptend++ = '\0'; 1288 if (cpoptarg = strchr(cpopt, '=')) 1289 *cpoptarg++ = '\0'; 1290 } else { 1291 if (cpoptarg = strchr(cpopt, '=')) 1292 *cpoptarg++ = '\0'; 1293 else { 1294 *cp = savedc; 1295 nextfield(&cp, &endcp); 1296 **endcpp = '\0'; 1297 if (endcp > cp && *cp != '-') { 1298 cpoptarg = cp; 1299 savedc2 = *endcp; 1300 *endcp = '\0'; 1301 usedarg = 0; 1302 } 1303 } 1304 } 1305 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { 1306 *exflagsp |= MNT_EXRDONLY; 1307 } else if (cpoptarg && (!strcmp(cpopt, "maproot") || 1308 !(allflag = strcmp(cpopt, "mapall")) || 1309 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { 1310 usedarg++; 1311 parsecred(cpoptarg, cr); 1312 if (allflag == 0) { 1313 *exflagsp |= MNT_EXPORTANON; 1314 opt_flags |= OP_MAPALL; 1315 } else 1316 opt_flags |= OP_MAPROOT; 1317 } else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) { 1318 *exflagsp |= MNT_EXKERB; 1319 opt_flags |= OP_KERB; 1320 } else if (cpoptarg && (!strcmp(cpopt, "mask") || 1321 !strcmp(cpopt, "m"))) { 1322 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) { 1323 syslog(LOG_ERR, "Bad mask: %s", cpoptarg); 1324 return (1); 1325 } 1326 usedarg++; 1327 opt_flags |= OP_MASK; 1328 } else if (cpoptarg && (!strcmp(cpopt, "network") || 1329 !strcmp(cpopt, "n"))) { 1330 if (grp->gr_type != GT_NULL) { 1331 syslog(LOG_ERR, "Network/host conflict"); 1332 return (1); 1333 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) { 1334 syslog(LOG_ERR, "Bad net: %s", cpoptarg); 1335 return (1); 1336 } 1337 grp->gr_type = GT_NET; 1338 *has_hostp = 1; 1339 usedarg++; 1340 opt_flags |= OP_NET; 1341 } else if (!strcmp(cpopt, "alldirs")) { 1342 opt_flags |= OP_ALLDIRS; 1343 } else if (!strcmp(cpopt, "public")) { 1344 *exflagsp |= MNT_EXPUBLIC; 1345 } else if (!strcmp(cpopt, "webnfs")) { 1346 *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON); 1347 opt_flags |= OP_MAPALL; 1348 } else if (cpoptarg && !strcmp(cpopt, "index")) { 1349 ep->ex_indexfile = strdup(cpoptarg); 1350#ifdef ISO 1351 } else if (cpoptarg && !strcmp(cpopt, "iso")) { 1352 if (get_isoaddr(cpoptarg, grp)) { 1353 syslog(LOG_ERR, "Bad iso addr: %s", cpoptarg); 1354 return (1); 1355 } 1356 *has_hostp = 1; 1357 usedarg++; 1358 opt_flags |= OP_ISO; 1359#endif /* ISO */ 1360 } else { 1361 syslog(LOG_ERR, "Bad opt %s", cpopt); 1362 return (1); 1363 } 1364 if (usedarg >= 0) { 1365 *endcp = savedc2; 1366 **endcpp = savedc; 1367 if (usedarg > 0) { 1368 *cpp = cp; 1369 *endcpp = endcp; 1370 } 1371 return (0); 1372 } 1373 cpopt = cpoptend; 1374 } 1375 **endcpp = savedc; 1376 return (0); 1377} 1378 1379/* 1380 * Translate a character string to the corresponding list of network 1381 * addresses for a hostname. 1382 */ 1383int 1384get_host(cp, grp, tgrp) 1385 char *cp; 1386 struct grouplist *grp; 1387 struct grouplist *tgrp; 1388{ 1389 struct grouplist *checkgrp; 1390 struct hostent *hp, *nhp; 1391 char **addrp, **naddrp; 1392 struct hostent t_host; 1393 int i; 1394 u_long saddr; 1395 char *aptr[2]; 1396 1397 if (grp->gr_type != GT_NULL) 1398 return (1); 1399 if ((hp = gethostbyname(cp)) == NULL) { 1400 if (isdigit(*cp)) { 1401 saddr = inet_addr(cp); 1402 if (saddr == -1) { 1403 syslog(LOG_ERR, "Inet_addr failed for %s", cp); 1404 return (1); 1405 } 1406 if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr), 1407 AF_INET)) == NULL) { 1408 hp = &t_host; 1409 hp->h_name = cp; 1410 hp->h_addrtype = AF_INET; 1411 hp->h_length = sizeof (u_long); 1412 hp->h_addr_list = aptr; 1413 aptr[0] = (char *)&saddr; 1414 aptr[1] = (char *)NULL; 1415 } 1416 } else { 1417 syslog(LOG_ERR, "Gethostbyname failed for %s", cp); 1418 return (1); 1419 } 1420 } 1421 /* 1422 * Sanity check: make sure we don't already have an entry 1423 * for this host in the grouplist. 1424 */ 1425 checkgrp = tgrp; 1426 while (checkgrp) { 1427 if (checkgrp->gr_type == GT_HOST && 1428 checkgrp->gr_ptr.gt_hostent != NULL && 1429 !strcmp(checkgrp->gr_ptr.gt_hostent->h_name, hp->h_name)) { 1430 grp->gr_type = GT_IGNORE; 1431 return(0); 1432 } 1433 checkgrp = checkgrp->gr_next; 1434 } 1435 1436 grp->gr_type = GT_HOST; 1437 nhp = grp->gr_ptr.gt_hostent = (struct hostent *) 1438 malloc(sizeof(struct hostent)); 1439 if (nhp == (struct hostent *)NULL) 1440 out_of_mem(); 1441 memmove(nhp, hp, sizeof(struct hostent)); 1442 i = strlen(hp->h_name)+1; 1443 nhp->h_name = (char *)malloc(i); 1444 if (nhp->h_name == (char *)NULL) 1445 out_of_mem(); 1446 memmove(nhp->h_name, hp->h_name, i); 1447 addrp = hp->h_addr_list; 1448 i = 1; 1449 while (*addrp++) 1450 i++; 1451 naddrp = nhp->h_addr_list = (char **) 1452 malloc(i*sizeof(char *)); 1453 if (naddrp == (char **)NULL) 1454 out_of_mem(); 1455 addrp = hp->h_addr_list; 1456 while (*addrp) { 1457 *naddrp = (char *) 1458 malloc(hp->h_length); 1459 if (*naddrp == (char *)NULL) 1460 out_of_mem(); 1461 memmove(*naddrp, *addrp, hp->h_length); 1462 addrp++; 1463 naddrp++; 1464 } 1465 *naddrp = (char *)NULL; 1466 if (debug) 1467 fprintf(stderr, "got host %s\n", hp->h_name); 1468 return (0); 1469} 1470 1471/* 1472 * Free up an exports list component 1473 */ 1474void 1475free_exp(ep) 1476 struct exportlist *ep; 1477{ 1478 1479 if (ep->ex_defdir) { 1480 free_host(ep->ex_defdir->dp_hosts); 1481 free((caddr_t)ep->ex_defdir); 1482 } 1483 if (ep->ex_fsdir) 1484 free(ep->ex_fsdir); 1485 if (ep->ex_indexfile) 1486 free(ep->ex_indexfile); 1487 free_dir(ep->ex_dirl); 1488 free((caddr_t)ep); 1489} 1490 1491/* 1492 * Free hosts. 1493 */ 1494void 1495free_host(hp) 1496 struct hostlist *hp; 1497{ 1498 struct hostlist *hp2; 1499 1500 while (hp) { 1501 hp2 = hp; 1502 hp = hp->ht_next; 1503 free((caddr_t)hp2); 1504 } 1505} 1506 1507struct hostlist * 1508get_ht() 1509{ 1510 struct hostlist *hp; 1511 1512 hp = (struct hostlist *)malloc(sizeof (struct hostlist)); 1513 if (hp == (struct hostlist *)NULL) 1514 out_of_mem(); 1515 hp->ht_next = (struct hostlist *)NULL; 1516 hp->ht_flag = 0; 1517 return (hp); 1518} 1519 1520#ifdef ISO 1521/* 1522 * Translate an iso address. 1523 */ 1524get_isoaddr(cp, grp) 1525 char *cp; 1526 struct grouplist *grp; 1527{ 1528 struct iso_addr *isop; 1529 struct sockaddr_iso *isoaddr; 1530 1531 if (grp->gr_type != GT_NULL) 1532 return (1); 1533 if ((isop = iso_addr(cp)) == NULL) { 1534 syslog(LOG_ERR, 1535 "iso_addr failed, ignored"); 1536 return (1); 1537 } 1538 isoaddr = (struct sockaddr_iso *) 1539 malloc(sizeof (struct sockaddr_iso)); 1540 if (isoaddr == (struct sockaddr_iso *)NULL) 1541 out_of_mem(); 1542 memset(isoaddr, 0, sizeof(struct sockaddr_iso)); 1543 memmove(&isoaddr->siso_addr, isop, sizeof(struct iso_addr)); 1544 isoaddr->siso_len = sizeof(struct sockaddr_iso); 1545 isoaddr->siso_family = AF_ISO; 1546 grp->gr_type = GT_ISO; 1547 grp->gr_ptr.gt_isoaddr = isoaddr; 1548 return (0); 1549} 1550#endif /* ISO */ 1551 1552/* 1553 * Out of memory, fatal 1554 */ 1555void 1556out_of_mem() 1557{ 1558 1559 syslog(LOG_ERR, "Out of memory"); 1560 exit(2); 1561} 1562 1563/* 1564 * Do the mount syscall with the update flag to push the export info into 1565 * the kernel. 1566 */ 1567int 1568do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb) 1569 struct exportlist *ep; 1570 struct grouplist *grp; 1571 int exflags; 1572 struct ucred *anoncrp; 1573 char *dirp; 1574 int dirplen; 1575 struct statfs *fsb; 1576{ 1577 char *cp = (char *)NULL; 1578 u_long **addrp; 1579 int done; 1580 char savedc = '\0'; 1581 struct sockaddr_in sin, imask; 1582 union { 1583 struct ufs_args ua; 1584 struct iso_args ia; 1585 struct mfs_args ma; 1586#ifdef __NetBSD__ 1587 struct msdosfs_args da; 1588#endif 1589 } args; 1590 u_long net; 1591 1592 args.ua.fspec = 0; 1593 args.ua.export.ex_flags = exflags; 1594 args.ua.export.ex_anon = *anoncrp; 1595 args.ua.export.ex_indexfile = ep->ex_indexfile; 1596 memset(&sin, 0, sizeof(sin)); 1597 memset(&imask, 0, sizeof(imask)); 1598 sin.sin_family = AF_INET; 1599 sin.sin_len = sizeof(sin); 1600 imask.sin_family = AF_INET; 1601 imask.sin_len = sizeof(sin); 1602 if (grp->gr_type == GT_HOST) 1603 addrp = (u_long **)grp->gr_ptr.gt_hostent->h_addr_list; 1604 else 1605 addrp = (u_long **)NULL; 1606 done = FALSE; 1607 while (!done) { 1608 switch (grp->gr_type) { 1609 case GT_HOST: 1610 if (addrp) { 1611 sin.sin_addr.s_addr = **addrp; 1612 args.ua.export.ex_addrlen = sizeof(sin); 1613 } else 1614 args.ua.export.ex_addrlen = 0; 1615 args.ua.export.ex_addr = (struct sockaddr *)&sin; 1616 args.ua.export.ex_masklen = 0; 1617 break; 1618 case GT_NET: 1619 if (grp->gr_ptr.gt_net.nt_mask) 1620 imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask; 1621 else { 1622 net = ntohl(grp->gr_ptr.gt_net.nt_net); 1623 if (IN_CLASSA(net)) 1624 imask.sin_addr.s_addr = inet_addr("255.0.0.0"); 1625 else if (IN_CLASSB(net)) 1626 imask.sin_addr.s_addr = 1627 inet_addr("255.255.0.0"); 1628 else 1629 imask.sin_addr.s_addr = 1630 inet_addr("255.255.255.0"); 1631 grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr; 1632 } 1633 sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net; 1634 args.ua.export.ex_addr = (struct sockaddr *)&sin; 1635 args.ua.export.ex_addrlen = sizeof (sin); 1636 args.ua.export.ex_mask = (struct sockaddr *)&imask; 1637 args.ua.export.ex_masklen = sizeof (imask); 1638 break; 1639#ifdef ISO 1640 case GT_ISO: 1641 args.ua.export.ex_addr = 1642 (struct sockaddr *)grp->gr_ptr.gt_isoaddr; 1643 args.ua.export.ex_addrlen = 1644 sizeof(struct sockaddr_iso); 1645 args.ua.export.ex_masklen = 0; 1646 break; 1647#endif /* ISO */ 1648 case GT_IGNORE: 1649 return(0); 1650 break; 1651 default: 1652 syslog(LOG_ERR, "Bad grouptype"); 1653 if (cp) 1654 *cp = savedc; 1655 return (1); 1656 }; 1657 1658 /* 1659 * XXX: 1660 * Maybe I should just use the fsb->f_mntonname path instead 1661 * of looping back up the dirp to the mount point?? 1662 * Also, needs to know how to export all types of local 1663 * exportable file systems and not just "ufs". 1664 */ 1665 while (mount(fsb->f_fstypename, dirp, 1666 fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) { 1667 if (cp) 1668 *cp-- = savedc; 1669 else 1670 cp = dirp + dirplen - 1; 1671 if (errno == EPERM) { 1672 syslog(LOG_ERR, 1673 "Can't change attributes for %s.\n", dirp); 1674 return (1); 1675 } 1676 if (opt_flags & OP_ALLDIRS) { 1677 syslog(LOG_ERR, "Could not remount %s: %m", 1678 dirp); 1679 return (1); 1680 } 1681 /* back up over the last component */ 1682 while (*cp == '/' && cp > dirp) 1683 cp--; 1684 while (*(cp - 1) != '/' && cp > dirp) 1685 cp--; 1686 if (cp == dirp) { 1687 if (debug) 1688 fprintf(stderr,"mnt unsucc\n"); 1689 syslog(LOG_ERR, "Can't export %s", dirp); 1690 return (1); 1691 } 1692 savedc = *cp; 1693 *cp = '\0'; 1694 } 1695 if (addrp) { 1696 ++addrp; 1697 if (*addrp == (u_long *)NULL) 1698 done = TRUE; 1699 } else 1700 done = TRUE; 1701 } 1702 if (cp) 1703 *cp = savedc; 1704 return (0); 1705} 1706 1707/* 1708 * Translate a net address. 1709 */ 1710int 1711get_net(cp, net, maskflg) 1712 char *cp; 1713 struct netmsk *net; 1714 int maskflg; 1715{ 1716 struct netent *np; 1717 long netaddr; 1718 struct in_addr inetaddr, inetaddr2; 1719 char *name; 1720 1721 if (isdigit(*cp) && ((netaddr = inet_network(cp)) != -1)) { 1722 inetaddr = inet_makeaddr(netaddr, 0); 1723 /* 1724 * Due to arbritrary subnet masks, you don't know how many 1725 * bits to shift the address to make it into a network, 1726 * however you do know how to make a network address into 1727 * a host with host == 0 and then compare them. 1728 * (What a pest) 1729 */ 1730 if (!maskflg) { 1731 setnetent(0); 1732 while (np = getnetent()) { 1733 inetaddr2 = inet_makeaddr(np->n_net, 0); 1734 if (inetaddr2.s_addr == inetaddr.s_addr) 1735 break; 1736 } 1737 endnetent(); 1738 } 1739 } else if ((np = getnetbyname(cp)) != NULL) { 1740 inetaddr = inet_makeaddr(np->n_net, 0); 1741 } else 1742 return (1); 1743 1744 if (maskflg) 1745 net->nt_mask = inetaddr.s_addr; 1746 else { 1747 if (np) 1748 name = np->n_name; 1749 else 1750 name = inet_ntoa(inetaddr); 1751 net->nt_name = (char *)malloc(strlen(name) + 1); 1752 if (net->nt_name == (char *)NULL) 1753 out_of_mem(); 1754 strcpy(net->nt_name, name); 1755 net->nt_net = inetaddr.s_addr; 1756 } 1757 return (0); 1758} 1759 1760/* 1761 * Parse out the next white space separated field 1762 */ 1763void 1764nextfield(cp, endcp) 1765 char **cp; 1766 char **endcp; 1767{ 1768 char *p; 1769 1770 p = *cp; 1771 while (*p == ' ' || *p == '\t') 1772 p++; 1773 if (*p == '\n' || *p == '\0') 1774 *cp = *endcp = p; 1775 else { 1776 *cp = p++; 1777 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 1778 p++; 1779 *endcp = p; 1780 } 1781} 1782 1783/* 1784 * Get an exports file line. Skip over blank lines and handle line 1785 * continuations. 1786 */ 1787int 1788get_line() 1789{ 1790 char *p, *cp; 1791 int len; 1792 int totlen, cont_line; 1793 1794 /* 1795 * Loop around ignoring blank lines and getting all continuation lines. 1796 */ 1797 p = line; 1798 totlen = 0; 1799 do { 1800 if (fgets(p, LINESIZ - totlen, exp_file) == NULL) 1801 return (0); 1802 len = strlen(p); 1803 cp = p + len - 1; 1804 cont_line = 0; 1805 while (cp >= p && 1806 (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) { 1807 if (*cp == '\\') 1808 cont_line = 1; 1809 cp--; 1810 len--; 1811 } 1812 *++cp = '\0'; 1813 if (len > 0) { 1814 totlen += len; 1815 if (totlen >= LINESIZ) { 1816 syslog(LOG_ERR, "Exports line too long"); 1817 exit(2); 1818 } 1819 p = cp; 1820 } 1821 } while (totlen == 0 || cont_line); 1822 return (1); 1823} 1824 1825/* 1826 * Parse a description of a credential. 1827 */ 1828void 1829parsecred(namelist, cr) 1830 char *namelist; 1831 struct ucred *cr; 1832{ 1833 char *name; 1834 int cnt; 1835 char *names; 1836 struct passwd *pw; 1837 struct group *gr; 1838 int ngroups, groups[NGROUPS + 1]; 1839 1840 /* 1841 * Set up the unpriviledged user. 1842 */ 1843 cr->cr_ref = 1; 1844 cr->cr_uid = -2; 1845 cr->cr_groups[0] = -2; 1846 cr->cr_ngroups = 1; 1847 /* 1848 * Get the user's password table entry. 1849 */ 1850 names = strsep(&namelist, " \t\n"); 1851 name = strsep(&names, ":"); 1852 if (isdigit(*name) || *name == '-') 1853 pw = getpwuid(atoi(name)); 1854 else 1855 pw = getpwnam(name); 1856 /* 1857 * Credentials specified as those of a user. 1858 */ 1859 if (names == NULL) { 1860 if (pw == NULL) { 1861 syslog(LOG_ERR, "Unknown user: %s", name); 1862 return; 1863 } 1864 cr->cr_uid = pw->pw_uid; 1865 ngroups = NGROUPS + 1; 1866 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) 1867 syslog(LOG_ERR, "Too many groups"); 1868 /* 1869 * Convert from int's to gid_t's and compress out duplicate 1870 */ 1871 cr->cr_ngroups = ngroups - 1; 1872 cr->cr_groups[0] = groups[0]; 1873 for (cnt = 2; cnt < ngroups; cnt++) 1874 cr->cr_groups[cnt - 1] = groups[cnt]; 1875 return; 1876 } 1877 /* 1878 * Explicit credential specified as a colon separated list: 1879 * uid:gid:gid:... 1880 */ 1881 if (pw != NULL) 1882 cr->cr_uid = pw->pw_uid; 1883 else if (isdigit(*name) || *name == '-') 1884 cr->cr_uid = atoi(name); 1885 else { 1886 syslog(LOG_ERR, "Unknown user: %s", name); 1887 return; 1888 } 1889 cr->cr_ngroups = 0; 1890 while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) { 1891 name = strsep(&names, ":"); 1892 if (isdigit(*name) || *name == '-') { 1893 cr->cr_groups[cr->cr_ngroups++] = atoi(name); 1894 } else { 1895 if ((gr = getgrnam(name)) == NULL) { 1896 syslog(LOG_ERR, "Unknown group: %s", name); 1897 continue; 1898 } 1899 cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid; 1900 } 1901 } 1902 if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS) 1903 syslog(LOG_ERR, "Too many groups"); 1904} 1905 1906#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50) 1907/* 1908 * Routines that maintain the remote mounttab 1909 */ 1910void 1911get_mountlist() 1912{ 1913 struct mountlist *mlp, **mlpp; 1914 char *host, *dirp, *cp; 1915 int len; 1916 char str[STRSIZ]; 1917 FILE *mlfile; 1918 1919 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { 1920 syslog(LOG_ERR, "Can't open %s", _PATH_RMOUNTLIST); 1921 return; 1922 } 1923 mlpp = &mlhead; 1924 while (fgets(str, STRSIZ, mlfile) != NULL) { 1925 cp = str; 1926 host = strsep(&cp, " \t\n"); 1927 dirp = strsep(&cp, " \t\n"); 1928 if (host == NULL || dirp == NULL) 1929 continue; 1930 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 1931 strncpy(mlp->ml_host, host, RPCMNT_NAMELEN); 1932 mlp->ml_host[RPCMNT_NAMELEN] = '\0'; 1933 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN); 1934 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0'; 1935 mlp->ml_next = (struct mountlist *)NULL; 1936 *mlpp = mlp; 1937 mlpp = &mlp->ml_next; 1938 } 1939 fclose(mlfile); 1940} 1941 1942void 1943del_mlist(hostp, dirp) 1944 char *hostp, *dirp; 1945{ 1946 struct mountlist *mlp, **mlpp; 1947 struct mountlist *mlp2; 1948 FILE *mlfile; 1949 int fnd = 0; 1950 1951 mlpp = &mlhead; 1952 mlp = mlhead; 1953 while (mlp) { 1954 if (!strcmp(mlp->ml_host, hostp) && 1955 (!dirp || !strcmp(mlp->ml_dirp, dirp))) { 1956 fnd = 1; 1957 mlp2 = mlp; 1958 *mlpp = mlp = mlp->ml_next; 1959 free((caddr_t)mlp2); 1960 } else { 1961 mlpp = &mlp->ml_next; 1962 mlp = mlp->ml_next; 1963 } 1964 } 1965 if (fnd) { 1966 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { 1967 syslog(LOG_ERR,"Can't update %s", _PATH_RMOUNTLIST); 1968 return; 1969 } 1970 mlp = mlhead; 1971 while (mlp) { 1972 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 1973 mlp = mlp->ml_next; 1974 } 1975 fclose(mlfile); 1976 } 1977} 1978 1979void 1980add_mlist(hostp, dirp) 1981 char *hostp, *dirp; 1982{ 1983 struct mountlist *mlp, **mlpp; 1984 FILE *mlfile; 1985 1986 mlpp = &mlhead; 1987 mlp = mlhead; 1988 while (mlp) { 1989 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp)) 1990 return; 1991 mlpp = &mlp->ml_next; 1992 mlp = mlp->ml_next; 1993 } 1994 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 1995 strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN); 1996 mlp->ml_host[RPCMNT_NAMELEN] = '\0'; 1997 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN); 1998 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0'; 1999 mlp->ml_next = (struct mountlist *)NULL; 2000 *mlpp = mlp; 2001 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { 2002 syslog(LOG_ERR, "Can't update %s", _PATH_RMOUNTLIST); 2003 return; 2004 } 2005 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 2006 fclose(mlfile); 2007} 2008 2009/* 2010 * This function is called via. SIGTERM when the system is going down. 2011 * It sends a broadcast RPCMNT_UMNTALL. 2012 */ 2013void 2014send_umntall() 2015{ 2016 (void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL, 2017 xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each); 2018 exit(0); 2019} 2020 2021int 2022umntall_each(resultsp, raddr) 2023 caddr_t resultsp; 2024 struct sockaddr_in *raddr; 2025{ 2026 return (1); 2027} 2028 2029/* 2030 * Free up a group list. 2031 */ 2032void 2033free_grp(grp) 2034 struct grouplist *grp; 2035{ 2036 char **addrp; 2037 2038 if (grp->gr_type == GT_HOST) { 2039 if (grp->gr_ptr.gt_hostent->h_name) { 2040 addrp = grp->gr_ptr.gt_hostent->h_addr_list; 2041 while (addrp && *addrp) 2042 free(*addrp++); 2043 free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list); 2044 free(grp->gr_ptr.gt_hostent->h_name); 2045 } 2046 free((caddr_t)grp->gr_ptr.gt_hostent); 2047 } else if (grp->gr_type == GT_NET) { 2048 if (grp->gr_ptr.gt_net.nt_name) 2049 free(grp->gr_ptr.gt_net.nt_name); 2050 } 2051#ifdef ISO 2052 else if (grp->gr_type == GT_ISO) 2053 free((caddr_t)grp->gr_ptr.gt_isoaddr); 2054#endif 2055 free((caddr_t)grp); 2056} 2057 2058#ifdef DEBUG 2059void 2060SYSLOG(int pri, const char *fmt, ...) 2061{ 2062 va_list ap; 2063 2064 va_start(ap, fmt); 2065 vfprintf(stderr, fmt, ap); 2066 va_end(ap); 2067} 2068#endif /* DEBUG */ 2069 2070/* 2071 * Check options for consistency. 2072 */ 2073int 2074check_options(dp) 2075 struct dirlist *dp; 2076{ 2077 2078 if (dp == (struct dirlist *)NULL) 2079 return (1); 2080 if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL) || 2081 (opt_flags & (OP_MAPROOT | OP_KERB)) == (OP_MAPROOT | OP_KERB) || 2082 (opt_flags & (OP_MAPALL | OP_KERB)) == (OP_MAPALL | OP_KERB)) { 2083 syslog(LOG_ERR, "-mapall, -maproot and -kerb mutually exclusive"); 2084 return (1); 2085 } 2086 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { 2087 syslog(LOG_ERR, "-mask requires -net"); 2088 return (1); 2089 } 2090 if ((opt_flags & (OP_NET | OP_ISO)) == (OP_NET | OP_ISO)) { 2091 syslog(LOG_ERR, "-net and -iso mutually exclusive"); 2092 return (1); 2093 } 2094 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) { 2095 syslog(LOG_ERR, "-alldir has multiple directories"); 2096 return (1); 2097 } 2098 return (0); 2099} 2100 2101/* 2102 * Check an absolute directory path for any symbolic links. Return true 2103 * if no symbolic links are found. 2104 */ 2105int 2106check_dirpath(dirp) 2107 char *dirp; 2108{ 2109 char *cp; 2110 int ret = 1; 2111 struct stat sb; 2112 2113 cp = dirp + 1; 2114 while (*cp && ret) { 2115 if (*cp == '/') { 2116 *cp = '\0'; 2117 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 2118 ret = 0; 2119 *cp = '/'; 2120 } 2121 cp++; 2122 } 2123 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 2124 ret = 0; 2125 return (ret); 2126} 2127 2128/* 2129 * Just translate an ascii string to an integer. 2130 */ 2131int 2132get_num(cp) 2133 register char *cp; 2134{ 2135 register int res = 0; 2136 2137 while (*cp) { 2138 if (*cp < '0' || *cp > '9') 2139 return (-1); 2140 res = res * 10 + (*cp++ - '0'); 2141 } 2142 return (res); 2143} 2144