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