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