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