X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/43d42ac66f9d640f35686cb764ddcd43f4439474..a7bcb6c2ccb9e8c6fb886d38d62bc83106c5d64e:/usr/src/sbin/mountd/mountd.c diff --git a/usr/src/sbin/mountd/mountd.c b/usr/src/sbin/mountd/mountd.c index 95222b9a9e..029d55f205 100644 --- a/usr/src/sbin/mountd/mountd.c +++ b/usr/src/sbin/mountd/mountd.c @@ -1,47 +1,59 @@ /* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by - * Rick Macklem at The University of Guelph. + * Herb Hasler and Rick Macklem at The University of Guelph. * * %sccs.include.redist.c% */ #ifndef lint -char copyright[] = -"@(#) Copyright (c) 1989 Regents of the University of California.\n\ - All rights reserved.\n"; +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; #endif not lint #ifndef lint -static char sccsid[] = "@(#)mountd.c 5.11 (Berkeley) %G%"; +static char sccsid[] = "@(#)mountd.c 8.10 (Berkeley) %G%"; #endif not lint #include -#include -#include #include +#include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include + #include #include #include +#ifdef ISO +#include +#endif #include #include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "pathnames.h" -struct ufid { - u_short ufid_len; - ino_t ufid_ino; - long ufid_gen; -}; +#ifdef DEBUG +#include +#endif + /* * Structures for keeping the mount list and export list */ @@ -51,33 +63,134 @@ struct mountlist { char ml_dirp[RPCMNT_PATHLEN+1]; }; +struct dirlist { + struct dirlist *dp_left; + struct dirlist *dp_right; + int dp_flag; + struct hostlist *dp_hosts; /* List of hosts this dir exported to */ + char dp_dirp[1]; /* Actually malloc'd to size of dir */ +}; +/* dp_flag bits */ +#define DP_DEFSET 0x1 + struct exportlist { struct exportlist *ex_next; - struct exportlist *ex_prev; - struct grouplist *ex_groups; - int ex_rootuid; - int ex_exflags; - dev_t ex_dev; - char ex_dirp[RPCMNT_PATHLEN+1]; + struct dirlist *ex_dirl; + struct dirlist *ex_defdir; + int ex_flag; + fsid_t ex_fs; + char *ex_fsdir; +}; +/* ex_flag bits */ +#define EX_LINKED 0x1 + +struct netmsk { + u_long nt_net; + u_long nt_mask; + char *nt_name; +}; + +union grouptypes { + struct hostent *gt_hostent; + struct netmsk gt_net; +#ifdef ISO + struct sockaddr_iso *gt_isoaddr; +#endif }; struct grouplist { + int gr_type; + union grouptypes gr_ptr; struct grouplist *gr_next; - struct hostent *gr_hp; +}; +/* Group types */ +#define GT_NULL 0x0 +#define GT_HOST 0x1 +#define GT_NET 0x2 +#define GT_ISO 0x4 + +struct hostlist { + struct grouplist *ht_grp; + struct hostlist *ht_next; }; /* Global defs */ -int xdr_fhs(), xdr_mlist(), xdr_dir(), xdr_explist(); -int mntsrv(), get_exportlist(), send_umntall(), umntall_each(); -void get_mountlist(), add_mlist(), del_mlist(); -struct exportlist exphead; +char *add_expdir __P((struct dirlist **, char *, int)); +void add_dlist __P((struct dirlist **, struct dirlist *, + struct grouplist *)); +void add_mlist __P((char *, char *)); +int check_dirpath __P((char *)); +int check_options __P((struct dirlist *)); +int chk_host __P((struct dirlist *, u_long, int *)); +void del_mlist __P((char *, char *)); +struct dirlist *dirp_search __P((struct dirlist *, char *)); +int do_mount __P((struct exportlist *, struct grouplist *, int, + struct ucred *, char *, int, struct statfs *)); +int do_opt __P((char **, char **, struct exportlist *, struct grouplist *, + int *, int *, struct ucred *)); +struct exportlist *ex_search __P((fsid_t *)); +struct exportlist *get_exp __P((void)); +void free_dir __P((struct dirlist *)); +void free_exp __P((struct exportlist *)); +void free_grp __P((struct grouplist *)); +void free_host __P((struct hostlist *)); +void get_exportlist __P((void)); +int get_host __P((char *, struct grouplist *)); +struct hostlist *get_ht __P((void)); +int get_line __P((void)); +void get_mountlist __P((void)); +int get_net __P((char *, struct netmsk *, int)); +void getexp_err __P((struct exportlist *, struct grouplist *)); +struct grouplist *get_grp __P((void)); +void hang_dirp __P((struct dirlist *, struct grouplist *, + struct exportlist *, int)); +void mntsrv __P((struct svc_req *, SVCXPRT *)); +void nextfield __P((char **, char **)); +void out_of_mem __P((void)); +void parsecred __P((char *, struct ucred *)); +int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *)); +int scan_tree __P((struct dirlist *, u_long)); +void send_umntall __P((void)); +int umntall_each __P((caddr_t, struct sockaddr_in *)); +int xdr_dir __P((XDR *, char *)); +int xdr_explist __P((XDR *, caddr_t)); +int xdr_fhs __P((XDR *, nfsv2fh_t *)); +int xdr_mlist __P((XDR *, caddr_t)); + +/* C library */ +int getnetgrent(); +void endnetgrent(); +void setnetgrent(); + +#ifdef ISO +struct iso_addr *iso_addr(); +#endif + +struct exportlist *exphead; struct mountlist *mlhead; +struct grouplist *grphead; char exname[MAXPATHLEN]; -int def_rootuid = -2; -int root_only = 1; -extern int errno; +struct ucred def_anon = { + 1, + (uid_t) -2, + 1, + { (gid_t) -2 } +}; +int resvport_only = 1; +int opt_flags; +/* Bits for above */ +#define OP_MAPROOT 0x01 +#define OP_MAPALL 0x02 +#define OP_KERB 0x04 +#define OP_MASK 0x08 +#define OP_NET 0x10 +#define OP_ISO 0x20 +#define OP_ALLDIRS 0x40 + #ifdef DEBUG int debug = 1; +void SYSLOG __P((int, const char *, ...)); +#define syslog SYSLOG #else int debug = 0; #endif @@ -89,19 +202,18 @@ int debug = 0; * default: _PATH_EXPORTS * and "-n" to allow nonroot mount. */ +int main(argc, argv) int argc; char **argv; { SVCXPRT *transp; int c; - extern int optind; - extern char *optarg; while ((c = getopt(argc, argv, "n")) != EOF) switch (c) { case 'n': - root_only = 0; + resvport_only = 0; break; default: fprintf(stderr, "Usage: mountd [-n] [export_file]\n"); @@ -109,23 +221,30 @@ main(argc, argv) }; argc -= optind; argv += optind; - exphead.ex_next = exphead.ex_prev = (struct exportlist *)0; - mlhead = (struct mountlist *)0; + grphead = (struct grouplist *)NULL; + exphead = (struct exportlist *)NULL; + mlhead = (struct mountlist *)NULL; if (argc == 1) { strncpy(exname, *argv, MAXPATHLEN-1); exname[MAXPATHLEN-1] = '\0'; } else strcpy(exname, _PATH_EXPORTS); - openlog("mountd:", LOG_PID, LOG_DAEMON); + openlog("mountd", LOG_PID, LOG_DAEMON); + if (debug) + fprintf(stderr,"Getting export list.\n"); get_exportlist(); + if (debug) + fprintf(stderr,"Getting mount list.\n"); get_mountlist(); + if (debug) + fprintf(stderr,"Here we go.\n"); if (debug == 0) { daemon(0, 0); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); } - signal(SIGHUP, get_exportlist); - signal(SIGTERM, send_umntall); + signal(SIGHUP, (void (*) __P((int))) get_exportlist); + signal(SIGTERM, (void (*) __P((int))) send_umntall); { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w"); if (pidfile != NULL) { fprintf(pidfile, "%d\n", getpid()); @@ -137,7 +256,8 @@ main(argc, argv) exit(1); } pmap_unset(RPCPROG_MNT, RPCMNT_VER1); - if (!svc_register(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv, IPPROTO_UDP)) { + if (!svc_register(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv, + IPPROTO_UDP)) { syslog(LOG_ERR, "Can't register mount"); exit(1); } @@ -149,54 +269,51 @@ main(argc, argv) /* * The mount rpc service */ +void mntsrv(rqstp, transp) - register struct svc_req *rqstp; - register SVCXPRT *transp; + struct svc_req *rqstp; + SVCXPRT *transp; { - register struct grouplist *grp; - register u_long **addrp; - register struct exportlist *ep; + struct exportlist *ep; + struct dirlist *dp; nfsv2fh_t nfh; - struct authunix_parms *ucr; struct stat stb; + struct statfs fsb; struct hostent *hp; u_long saddr; - char dirpath[RPCMNT_PATHLEN+1]; - int bad = ENOENT; - int omask; - uid_t uid = -2; - - /* Get authorization */ - switch (rqstp->rq_cred.oa_flavor) { - case AUTH_UNIX: - ucr = (struct authunix_parms *)rqstp->rq_clntcred; - uid = ucr->aup_uid; - break; - case AUTH_NULL: - default: - break; - } + u_short sport; + char rpcpath[RPCMNT_PATHLEN+1], dirpath[MAXPATHLEN]; + int bad = ENOENT, omask, defset; saddr = transp->xp_raddr.sin_addr.s_addr; - hp = (struct hostent *)0; + sport = ntohs(transp->xp_raddr.sin_port); + hp = (struct hostent *)NULL; switch (rqstp->rq_proc) { case NULLPROC: - if (!svc_sendreply(transp, xdr_void, (caddr_t)0)) + if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) syslog(LOG_ERR, "Can't send reply"); return; case RPCMNT_MOUNT: - if (uid != 0 && root_only) { + if (sport >= IPPORT_RESERVED && resvport_only) { svcerr_weakauth(transp); return; } - if (!svc_getargs(transp, xdr_dir, dirpath)) { + if (!svc_getargs(transp, xdr_dir, rpcpath)) { svcerr_decode(transp); return; } - /* Check to see if it's a valid dirpath */ - if (stat(dirpath, &stb) < 0 || (stb.st_mode&S_IFMT) != - S_IFDIR) { + /* + * Get the real pathname and make sure it is a file or + * directory that exists. + */ + if (realpath(rpcpath, dirpath) == 0 || + stat(dirpath, &stb) < 0 || + (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) || + statfs(dirpath, &fsb) < 0) { + chdir("/"); /* Just in case realpath doesn't */ + if (debug) + fprintf(stderr, "stat failed on %s\n", dirpath); if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad)) syslog(LOG_ERR, "Can't send reply"); return; @@ -204,64 +321,49 @@ mntsrv(rqstp, transp) /* Check in the exports list */ omask = sigblock(sigmask(SIGHUP)); - ep = exphead.ex_next; - while (ep != NULL) { - if (!strcmp(ep->ex_dirp, dirpath)) { - grp = ep->ex_groups; - if (grp == NULL) - break; - - /* Check for a host match */ - addrp = (u_long **)grp->gr_hp->h_addr_list; - for (;;) { - if (**addrp == saddr) - break; - if (*++addrp == NULL) - if (grp = grp->gr_next) { - addrp = (u_long **) - grp->gr_hp->h_addr_list; - } else { - bad = EACCES; - if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad)) - syslog(LOG_ERR, "Can't send reply"); - sigsetmask(omask); - return; - } - } - hp = grp->gr_hp; - break; + ep = ex_search(&fsb.f_fsid); + defset = 0; + if (ep && (chk_host(ep->ex_defdir, saddr, &defset) || + ((dp = dirp_search(ep->ex_dirl, dirpath)) && + chk_host(dp, saddr, &defset)) || + (defset && scan_tree(ep->ex_defdir, saddr) == 0 && + scan_tree(ep->ex_dirl, saddr) == 0))) { + /* Get the file handle */ + bzero((caddr_t)&nfh, sizeof(nfh)); + if (getfh(dirpath, (fhandle_t *)&nfh) < 0) { + bad = errno; + syslog(LOG_ERR, "Can't get fh for %s", dirpath); + if (!svc_sendreply(transp, xdr_long, + (caddr_t)&bad)) + syslog(LOG_ERR, "Can't send reply"); + sigsetmask(omask); + return; } - ep = ep->ex_next; - } - sigsetmask(omask); - if (ep == NULL) { - bad = EACCES; - if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad)) + if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&nfh)) syslog(LOG_ERR, "Can't send reply"); - return; - } - - /* Get the file handle */ - bzero((caddr_t)&nfh, sizeof(nfh)); - if (getfh(dirpath, (fhandle_t *)&nfh) < 0) { - bad = errno; + if (hp == NULL) + hp = gethostbyaddr((caddr_t)&saddr, + sizeof(saddr), AF_INET); + if (hp) + add_mlist(hp->h_name, dirpath); + else + add_mlist(inet_ntoa(transp->xp_raddr.sin_addr), + dirpath); + if (debug) + fprintf(stderr,"Mount successfull.\n"); + } else { + bad = EACCES; if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad)) syslog(LOG_ERR, "Can't send reply"); - return; } - if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&nfh)) - syslog(LOG_ERR, "Can't send reply"); - if (hp == NULL) - hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); - if (hp) - add_mlist(hp->h_name, dirpath); + sigsetmask(omask); return; case RPCMNT_DUMP: - if (!svc_sendreply(transp, xdr_mlist, (caddr_t)0)) + if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL)) syslog(LOG_ERR, "Can't send reply"); return; case RPCMNT_UMOUNT: - if (uid != 0 && root_only) { + if (sport >= IPPORT_RESERVED && resvport_only) { svcerr_weakauth(transp); return; } @@ -269,25 +371,27 @@ mntsrv(rqstp, transp) svcerr_decode(transp); return; } - if (!svc_sendreply(transp, xdr_void, (caddr_t)0)) + if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) syslog(LOG_ERR, "Can't send reply"); hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); if (hp) del_mlist(hp->h_name, dirpath); + del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath); return; case RPCMNT_UMNTALL: - if (uid != 0 && root_only) { + if (sport >= IPPORT_RESERVED && resvport_only) { svcerr_weakauth(transp); return; } - if (!svc_sendreply(transp, xdr_void, (caddr_t)0)) + if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) syslog(LOG_ERR, "Can't send reply"); hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); if (hp) - del_mlist(hp->h_name, (char *)0); + del_mlist(hp->h_name, (char *)NULL); + del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), (char *)NULL); return; case RPCMNT_EXPORT: - if (!svc_sendreply(transp, xdr_explist, (caddr_t)0)) + if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL)) syslog(LOG_ERR, "Can't send reply"); return; default: @@ -299,6 +403,7 @@ mntsrv(rqstp, transp) /* * Xdr conversion for a dirpath string */ +int xdr_dir(xdrsp, dirp) XDR *xdrsp; char *dirp; @@ -309,6 +414,7 @@ xdr_dir(xdrsp, dirp) /* * Xdr routine to generate fhstatus */ +int xdr_fhs(xdrsp, nfh) XDR *xdrsp; nfsv2fh_t *nfh; @@ -320,11 +426,12 @@ xdr_fhs(xdrsp, nfh) return (xdr_opaque(xdrsp, (caddr_t)nfh, NFSX_FH)); } +int xdr_mlist(xdrsp, cp) XDR *xdrsp; caddr_t cp; { - register struct mountlist *mlp; + struct mountlist *mlp; int true = 1; int false = 0; char *strp; @@ -349,35 +456,24 @@ xdr_mlist(xdrsp, cp) /* * Xdr conversion for export list */ +int xdr_explist(xdrsp, cp) XDR *xdrsp; caddr_t cp; { - register struct exportlist *ep; - register struct grouplist *grp; - int true = 1; + struct exportlist *ep; int false = 0; - char *strp; - int omask; + int omask, putdef; omask = sigblock(sigmask(SIGHUP)); - ep = exphead.ex_next; - while (ep != NULL) { - if (!xdr_bool(xdrsp, &true)) - goto errout; - strp = &ep->ex_dirp[0]; - if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) + ep = exphead; + while (ep) { + putdef = 0; + if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef)) goto errout; - grp = ep->ex_groups; - while (grp != NULL) { - if (!xdr_bool(xdrsp, &true)) - goto errout; - strp = grp->gr_hp->h_name; - if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) - goto errout; - grp = grp->gr_next; - } - if (!xdr_bool(xdrsp, &false)) + if (ep->ex_defdir && putdef == 0 && + put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL, + &putdef)) goto errout; ep = ep->ex_next; } @@ -390,272 +486,1267 @@ errout: return (0); } +/* + * Called from xdr_explist() to traverse the tree and export the + * directory paths. + */ +int +put_exlist(dp, xdrsp, adp, putdefp) + struct dirlist *dp; + XDR *xdrsp; + struct dirlist *adp; + int *putdefp; +{ + struct grouplist *grp; + struct hostlist *hp; + int true = 1; + int false = 0; + int gotalldir = 0; + char *strp; + + if (dp) { + if (put_exlist(dp->dp_left, xdrsp, adp, putdefp)) + return (1); + if (!xdr_bool(xdrsp, &true)) + return (1); + strp = dp->dp_dirp; + if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) + return (1); + if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { + gotalldir = 1; + *putdefp = 1; + } + if ((dp->dp_flag & DP_DEFSET) == 0 && + (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { + hp = dp->dp_hosts; + while (hp) { + grp = hp->ht_grp; + if (grp->gr_type == GT_HOST) { + if (!xdr_bool(xdrsp, &true)) + return (1); + strp = grp->gr_ptr.gt_hostent->h_name; + if (!xdr_string(xdrsp, &strp, + RPCMNT_NAMELEN)) + return (1); + } else if (grp->gr_type == GT_NET) { + if (!xdr_bool(xdrsp, &true)) + return (1); + strp = grp->gr_ptr.gt_net.nt_name; + if (!xdr_string(xdrsp, &strp, + RPCMNT_NAMELEN)) + return (1); + } + hp = hp->ht_next; + if (gotalldir && hp == (struct hostlist *)NULL) { + hp = adp->dp_hosts; + gotalldir = 0; + } + } + } + if (!xdr_bool(xdrsp, &false)) + return (1); + if (put_exlist(dp->dp_right, xdrsp, adp, putdefp)) + return (1); + } + return (0); +} + #define LINESIZ 10240 char line[LINESIZ]; +FILE *exp_file; /* * Get the export list */ +void get_exportlist() { - register struct hostent *hp, *nhp; - register char **addrp, **naddrp; - register int i; - register struct grouplist *grp; - register struct exportlist *ep, *ep2; - struct ufs_args args; - struct stat sb; - FILE *inf; - char *cp, *endcp; - char savedc; - int len, dirplen; - int rootuid, exflags; - u_long saddr; - struct exportlist *fep; + struct exportlist *ep, *ep2; + struct grouplist *grp, *tgrp; + struct exportlist **epp; + struct dirlist *dirhead; + struct statfs fsb, *fsp; + struct hostent *hpe; + struct ucred anon; + char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; + int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp; /* * First, get rid of the old list */ - ep = exphead.ex_next; - while (ep != NULL) { + ep = exphead; + while (ep) { ep2 = ep; ep = ep->ex_next; free_exp(ep2); } + exphead = (struct exportlist *)NULL; + + grp = grphead; + while (grp) { + tgrp = grp; + grp = grp->gr_next; + free_grp(tgrp); + } + grphead = (struct grouplist *)NULL; + + /* + * And delete exports that are in the kernel for all local + * file systems. + * XXX: Should know how to handle all local exportable file systems + * instead of just MOUNT_UFS. + */ + num = getmntinfo(&fsp, MNT_NOWAIT); + for (i = 0; i < num; i++) { + union { + struct ufs_args ua; + struct iso_args ia; + struct mfs_args ma; + } targs; + + switch (fsp->f_type) { + case MOUNT_MFS: + case MOUNT_UFS: + case MOUNT_CD9660: + targs.ua.fspec = NULL; + targs.ua.export.ex_flags = MNT_DELEXPORT; + if (mount(fsp->f_type, fsp->f_mntonname, + fsp->f_flags | MNT_UPDATE, + (caddr_t)&targs) < 0) + syslog(LOG_ERR, "Can't delete exports for %s", + fsp->f_mntonname); + } + fsp++; + } /* * Read in the exports file and build the list, calling - * exportfs() as we go along + * mount() as we go along to push the export rules into the kernel. */ - exphead.ex_next = exphead.ex_prev = (struct exportlist *)0; - if ((inf = fopen(exname, "r")) == NULL) { + if ((exp_file = fopen(exname, "r")) == NULL) { syslog(LOG_ERR, "Can't open %s", exname); exit(2); } - while (fgets(line, LINESIZ, inf)) { - exflags = MNT_EXPORTED; - rootuid = def_rootuid; + dirhead = (struct dirlist *)NULL; + while (get_line()) { + if (debug) + fprintf(stderr,"Got line %s\n",line); cp = line; nextfield(&cp, &endcp); + if (*cp == '#') + goto nextline; /* - * Get file system devno and see if an entry for this - * file system already exists. + * Set defaults. */ - savedc = *endcp; - *endcp = '\0'; - if (stat(cp, &sb) < 0 || (sb.st_mode & S_IFMT) != S_IFDIR) - goto err; - fep = (struct exportlist *)0; - ep = exphead.ex_next; - while (ep) { - if (ep->ex_dev == sb.st_dev) { - fep = ep; - break; - } - ep = ep->ex_next; - } - *endcp = savedc; + has_host = FALSE; + anon = def_anon; + exflags = MNT_EXPORTED; + got_nondir = 0; + opt_flags = 0; + ep = (struct exportlist *)NULL; /* * Create new exports list entry */ len = endcp-cp; - if (len <= RPCMNT_PATHLEN && len > 0) { - ep = (struct exportlist *)malloc(sizeof(*ep)); - ep->ex_next = ep->ex_prev = (struct exportlist *)0; - ep->ex_groups = (struct grouplist *)0; - bcopy(cp, ep->ex_dirp, len); - ep->ex_dirp[len] = '\0'; - dirplen = len; - } else - goto err; - cp = endcp; - nextfield(&cp, &endcp); - len = endcp-cp; + tgrp = grp = get_grp(); while (len > 0) { - savedc = *endcp; - *endcp = '\0'; - if (len <= RPCMNT_NAMELEN) { - if (*cp == '-') { - do_opt(cp+1, fep, ep, &exflags, &rootuid); + if (len > RPCMNT_NAMELEN) { + getexp_err(ep, tgrp); + goto nextline; + } + if (*cp == '-') { + if (ep == (struct exportlist *)NULL) { + getexp_err(ep, tgrp); + goto nextline; + } + if (debug) + fprintf(stderr, "doing opt %s\n", cp); + got_nondir = 1; + if (do_opt(&cp, &endcp, ep, grp, &has_host, + &exflags, &anon)) { + getexp_err(ep, tgrp); + goto nextline; + } + } else if (*cp == '/') { + savedc = *endcp; + *endcp = '\0'; + if (check_dirpath(cp) && + statfs(cp, &fsb) >= 0) { + if (got_nondir) { + syslog(LOG_ERR, "Dirs must be first"); + getexp_err(ep, tgrp); + goto nextline; + } + if (ep) { + if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] || + ep->ex_fs.val[1] != fsb.f_fsid.val[1]) { + getexp_err(ep, tgrp); + goto nextline; + } } else { - if (isdigit(*cp)) { - saddr = inet_addr(cp); - if (saddr == -1 || - (hp = gethostbyaddr((caddr_t)&saddr, - sizeof(saddr), AF_INET)) == NULL) - goto err; - } else if ((hp = gethostbyname(cp)) == NULL) - goto err; - grp = (struct grouplist *) - malloc(sizeof(struct grouplist)); - if (grp == NULL) - goto err; - nhp = grp->gr_hp = (struct hostent *) - malloc(sizeof(struct hostent)); - if (nhp == NULL) - goto err; - bcopy((caddr_t)hp, (caddr_t)nhp, - sizeof(struct hostent)); - i = strlen(hp->h_name)+1; - nhp->h_name = (char *)malloc(i); - if (nhp->h_name == NULL) - goto err; - bcopy(hp->h_name, nhp->h_name, i); - addrp = hp->h_addr_list; - i = 1; - while (*addrp++) - i++; - naddrp = nhp->h_addr_list = (char **) - malloc(i*sizeof(char *)); - if (naddrp == NULL) - goto err; - addrp = hp->h_addr_list; - while (*addrp) { - *naddrp = (char *) - malloc(hp->h_length); - if (*naddrp == NULL) - goto err; - bcopy(*addrp, *naddrp, - hp->h_length); - addrp++; - naddrp++; + /* + * See if this directory is already + * in the list. + */ + ep = ex_search(&fsb.f_fsid); + if (ep == (struct exportlist *)NULL) { + ep = get_exp(); + ep->ex_fs = fsb.f_fsid; + ep->ex_fsdir = (char *) + malloc(strlen(fsb.f_mntonname) + 1); + if (ep->ex_fsdir) + strcpy(ep->ex_fsdir, + fsb.f_mntonname); + else + out_of_mem(); + if (debug) + fprintf(stderr, + "Making new ep fs=0x%x,0x%x\n", + fsb.f_fsid.val[0], + fsb.f_fsid.val[1]); + } else if (debug) + fprintf(stderr, + "Found ep fs=0x%x,0x%x\n", + fsb.f_fsid.val[0], + fsb.f_fsid.val[1]); + } + + /* + * Add dirpath to export mount point. + */ + dirp = add_expdir(&dirhead, cp, len); + dirplen = len; + } else { + getexp_err(ep, tgrp); + goto nextline; + } + *endcp = savedc; + } else { + savedc = *endcp; + *endcp = '\0'; + got_nondir = 1; + if (ep == (struct exportlist *)NULL) { + getexp_err(ep, tgrp); + goto nextline; + } + + /* + * Get the host or netgroup. + */ + setnetgrent(cp); + netgrp = getnetgrent(&hst, &usr, &dom); + do { + if (has_host) { + grp->gr_next = get_grp(); + grp = grp->gr_next; + } + if (netgrp) { + if (get_host(hst, grp)) { + syslog(LOG_ERR, "Bad netgroup %s", cp); + getexp_err(ep, tgrp); + goto nextline; } - *naddrp = (char *)0; - grp->gr_next = ep->ex_groups; - ep->ex_groups = grp; + } else if (get_host(cp, grp)) { + getexp_err(ep, tgrp); + goto nextline; } + has_host = TRUE; + } while (netgrp && getnetgrent(&hst, &usr, &dom)); + endnetgrent(); + *endcp = savedc; } cp = endcp; - *cp = savedc; nextfield(&cp, &endcp); - len = endcp-cp; - } - if (fep == NULL) { - args.fspec = 0; - args.exflags = exflags; - args.exroot = rootuid; - cp = (char *)0; - while (mount(MOUNT_UFS, ep->ex_dirp, MNT_UPDATE, &args) < 0) { - if (cp == NULL) - cp = ep->ex_dirp + dirplen - 1; - else - *cp = savedc; - /* back up over the last component */ - while (*cp == '/' && cp > ep->ex_dirp) - cp--; - while (*(cp - 1) != '/' && cp > ep->ex_dirp) - cp--; - if (cp == ep->ex_dirp) { - syslog(LOG_WARNING, - "Can't export %s", ep->ex_dirp); - free_exp(ep); - goto nextline; - } - savedc = *cp; - *cp = '\0'; + len = endcp - cp; + } + if (check_options(dirhead)) { + getexp_err(ep, tgrp); + goto nextline; + } + if (!has_host) { + grp->gr_type = GT_HOST; + if (debug) + fprintf(stderr,"Adding a default entry\n"); + /* add a default group and make the grp list NULL */ + hpe = (struct hostent *)malloc(sizeof(struct hostent)); + if (hpe == (struct hostent *)NULL) + out_of_mem(); + hpe->h_name = "Default"; + hpe->h_addrtype = AF_INET; + hpe->h_length = sizeof (u_long); + hpe->h_addr_list = (char **)NULL; + grp->gr_ptr.gt_hostent = hpe; + + /* + * Don't allow a network export coincide with a list of + * host(s) on the same line. + */ + } else if ((opt_flags & OP_NET) && tgrp->gr_next) { + getexp_err(ep, tgrp); + goto nextline; + } + + /* + * Loop through hosts, pushing the exports into the kernel. + * After loop, tgrp points to the start of the list and + * grp points to the last entry in the list. + */ + grp = tgrp; + do { + if (do_mount(ep, grp, exflags, &anon, dirp, + dirplen, &fsb)) { + getexp_err(ep, tgrp); + goto nextline; + } + } while (grp->gr_next && (grp = grp->gr_next)); + + /* + * Success. Update the data structures. + */ + if (has_host) { + hang_dirp(dirhead, tgrp, ep, (opt_flags & OP_ALLDIRS)); + grp->gr_next = grphead; + grphead = tgrp; + } else { + hang_dirp(dirhead, (struct grouplist *)NULL, ep, + (opt_flags & OP_ALLDIRS)); + free_grp(grp); + } + dirhead = (struct dirlist *)NULL; + if ((ep->ex_flag & EX_LINKED) == 0) { + ep2 = exphead; + epp = &exphead; + + /* + * Insert in the list in alphabetical order. + */ + while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) { + epp = &ep2->ex_next; + ep2 = ep2->ex_next; } - if (cp) + if (ep2) + ep->ex_next = ep2; + *epp = ep; + ep->ex_flag |= EX_LINKED; + } +nextline: + if (dirhead) { + free_dir(dirhead); + dirhead = (struct dirlist *)NULL; + } + } + fclose(exp_file); +} + +/* + * Allocate an export list element + */ +struct exportlist * +get_exp() +{ + struct exportlist *ep; + + ep = (struct exportlist *)malloc(sizeof (struct exportlist)); + if (ep == (struct exportlist *)NULL) + out_of_mem(); + bzero((caddr_t)ep, sizeof (struct exportlist)); + return (ep); +} + +/* + * Allocate a group list element + */ +struct grouplist * +get_grp() +{ + struct grouplist *gp; + + gp = (struct grouplist *)malloc(sizeof (struct grouplist)); + if (gp == (struct grouplist *)NULL) + out_of_mem(); + bzero((caddr_t)gp, sizeof (struct grouplist)); + return (gp); +} + +/* + * Clean up upon an error in get_exportlist(). + */ +void +getexp_err(ep, grp) + struct exportlist *ep; + struct grouplist *grp; +{ + struct grouplist *tgrp; + + syslog(LOG_ERR, "Bad exports list line %s", line); + if (ep && (ep->ex_flag & EX_LINKED) == 0) + free_exp(ep); + while (grp) { + tgrp = grp; + grp = grp->gr_next; + free_grp(tgrp); + } +} + +/* + * Search the export list for a matching fs. + */ +struct exportlist * +ex_search(fsid) + fsid_t *fsid; +{ + struct exportlist *ep; + + ep = exphead; + while (ep) { + if (ep->ex_fs.val[0] == fsid->val[0] && + ep->ex_fs.val[1] == fsid->val[1]) + return (ep); + ep = ep->ex_next; + } + return (ep); +} + +/* + * Add a directory path to the list. + */ +char * +add_expdir(dpp, cp, len) + struct dirlist **dpp; + char *cp; + int len; +{ + struct dirlist *dp; + + dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len); + dp->dp_left = *dpp; + dp->dp_right = (struct dirlist *)NULL; + dp->dp_flag = 0; + dp->dp_hosts = (struct hostlist *)NULL; + strcpy(dp->dp_dirp, cp); + *dpp = dp; + return (dp->dp_dirp); +} + +/* + * Hang the dir list element off the dirpath binary tree as required + * and update the entry for host. + */ +void +hang_dirp(dp, grp, ep, alldirs) + struct dirlist *dp; + struct grouplist *grp; + struct exportlist *ep; + int alldirs; +{ + struct hostlist *hp; + struct dirlist *dp2; + + if (alldirs) { + if (ep->ex_defdir) + free((caddr_t)dp); + else + ep->ex_defdir = dp; + if (grp == (struct grouplist *)NULL) + ep->ex_defdir->dp_flag |= DP_DEFSET; + else while (grp) { + hp = get_ht(); + hp->ht_grp = grp; + hp->ht_next = ep->ex_defdir->dp_hosts; + ep->ex_defdir->dp_hosts = hp; + grp = grp->gr_next; + } + } else { + + /* + * Loop throught the directories adding them to the tree. + */ + while (dp) { + dp2 = dp->dp_left; + add_dlist(&ep->ex_dirl, dp, grp); + dp = dp2; + } + } +} + +/* + * Traverse the binary tree either updating a node that is already there + * for the new directory or adding the new node. + */ +void +add_dlist(dpp, newdp, grp) + struct dirlist **dpp; + struct dirlist *newdp; + struct grouplist *grp; +{ + struct dirlist *dp; + struct hostlist *hp; + int cmp; + + dp = *dpp; + if (dp) { + cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); + if (cmp > 0) { + add_dlist(&dp->dp_left, newdp, grp); + return; + } else if (cmp < 0) { + add_dlist(&dp->dp_right, newdp, grp); + return; + } else + free((caddr_t)newdp); + } else { + dp = newdp; + dp->dp_left = (struct dirlist *)NULL; + *dpp = dp; + } + if (grp) { + + /* + * Hang all of the host(s) off of the directory point. + */ + do { + hp = get_ht(); + hp->ht_grp = grp; + hp->ht_next = dp->dp_hosts; + dp->dp_hosts = hp; + grp = grp->gr_next; + } while (grp); + } else + dp->dp_flag |= DP_DEFSET; +} + +/* + * Search for a dirpath on the export point. + */ +struct dirlist * +dirp_search(dp, dirpath) + struct dirlist *dp; + char *dirpath; +{ + int cmp; + + if (dp) { + cmp = strcmp(dp->dp_dirp, dirpath); + if (cmp > 0) + return (dirp_search(dp->dp_left, dirpath)); + else if (cmp < 0) + return (dirp_search(dp->dp_right, dirpath)); + else + return (dp); + } + return (dp); +} + +/* + * Scan for a host match in a directory tree. + */ +int +chk_host(dp, saddr, defsetp) + struct dirlist *dp; + u_long saddr; + int *defsetp; +{ + struct hostlist *hp; + struct grouplist *grp; + u_long **addrp; + + if (dp) { + if (dp->dp_flag & DP_DEFSET) + *defsetp = 1; + hp = dp->dp_hosts; + while (hp) { + grp = hp->ht_grp; + switch (grp->gr_type) { + case GT_HOST: + addrp = (u_long **) + grp->gr_ptr.gt_hostent->h_addr_list; + while (*addrp) { + if (**addrp == saddr) + return (1); + addrp++; + } + break; + case GT_NET: + if ((saddr & grp->gr_ptr.gt_net.nt_mask) == + grp->gr_ptr.gt_net.nt_net) + return (1); + break; + }; + hp = hp->ht_next; + } + } + return (0); +} + +/* + * Scan tree for a host that matches the address. + */ +int +scan_tree(dp, saddr) + struct dirlist *dp; + u_long saddr; +{ + int defset; + + if (dp) { + if (scan_tree(dp->dp_left, saddr)) + return (1); + if (chk_host(dp, saddr, &defset)) + return (1); + if (scan_tree(dp->dp_right, saddr)) + return (1); + } + return (0); +} + +/* + * Traverse the dirlist tree and free it up. + */ +void +free_dir(dp) + struct dirlist *dp; +{ + + if (dp) { + free_dir(dp->dp_left); + free_dir(dp->dp_right); + free_host(dp->dp_hosts); + free((caddr_t)dp); + } +} + +/* + * Parse the option string and update fields. + * Option arguments may either be -