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