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