NFSv2/usr/src/usr.etc/rpc.mountd.c

Compare this file to the similar file:
Show the results in this format:

#ifndef lint
static  char sccsid[] = "@(#)rpc.mountd.c 1.1 85/05/31 Copyr 1985 Sun Micro";
#endif

/*
 * Copyright (c) 1985 Sun Microsystems, Inc.
 */

/* NFS server */

#include <sys/param.h>
#include <ufs/fs.h>
#include <rpc/rpc.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/time.h>
#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <nfs/nfs.h>
#include <rpcsvc/mount.h>
#include <netdb.h>

#define	EXPORTS	"/etc/exports"
#define RMTAB	"/etc/rmtab"
#define	MAXLINE	2048

extern int errno;

int mnt();
char *exmalloc();
int catch();
struct groups  *newgroup();
struct exports *newexport();

static struct mountlist *mountlist;
char myname[256];
char mydomain[256];
struct sockaddr_in myaddr;
char *exportfile = EXPORTS;
struct exports *exports = NULL;


main(argc, argv)
char	*argv[];
{
	register int sock;
	struct sockaddr_in addr;
	int len = sizeof(struct sockaddr_in);
	int forks = 0;
	char *dir = "/";
	int nservers = 1;
	int pid, t;
	union wait status;
	struct stat buf;
	fhandle_t fh;
	SVCXPRT *transp;

#ifdef DEBUG
	{
		int s;
		struct sockaddr_in addr;
		int len = sizeof(struct sockaddr_in);

		if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
			perror("inet: socket");
			return - 1;
		}
		if (bind(s, &addr, sizeof(addr)) < 0) {
			perror("bind");
			return - 1;
		}
		if (getsockname(s, &addr, &len) != 0) {
			perror("inet: getsockname");
			(void)close(s);
			return - 1;
		}
		pmap_unset(MOUNTPROG, MOUNTVERS);
		pmap_set(MOUNTPROG, MOUNTVERS, IPPROTO_UDP,
		    ntohs(addr.sin_port));
		if (dup2(s, 0) < 0) {
			perror("dup2");
			exit(1);
		}
	}
#endif	

	if (getsockname(0, &addr, &len) != 0) {
		perror("rstat: getsockname");
		exit(1);
	}
	if ((transp = svcudp_create(0)) == NULL) {
		fprintf(stdout, "couldn't create udp transport\n");
		exit(1);
	}
	if (!svc_register(transp, MOUNTPROG, MOUNTVERS, mnt, 0)) {
		fprintf(stdout, "couldn't register MOUNTPROG");
		exit(1);
	}

	/*
	 * Initalize the world
	 */
	gethostname(myname, sizeof(myname));
	getdomainname(mydomain, sizeof(mydomain));
	get_myaddress(&myaddr);
	readfromfile();
	set_exports();

	/*
	 * Start serving
	 */
	svc_run();
	fprintf(stdout, "Error: svc_run shouldn't have returned\n");
	abort();
}

/*
 * Server procedure switch routine
 */
mnt(rqstp, transp)
	struct svc_req *rqstp;
	SVCXPRT *transp;
{
	switch(rqstp->rq_proc) {
		case NULLPROC:
			if (!svc_sendreply(transp, xdr_void, 0)) {
			    	fprintf(stdout,
				     "couldn't reply to rpc call\n");
				abort();
			}
			return;
		case MOUNTPROC_MNT:
#ifdef DEBUG
			fprintf(stdout, "about to do a mount\n");
#endif
			set_exports();
			mount(rqstp, transp);
			return;
		case MOUNTPROC_DUMP:
#ifdef DEBUG
			fprintf(stdout, "about to do a dump\n");
#endif
			if (!svc_sendreply(transp,xdr_mountlist,&mountlist)) {
			    	fprintf(stdout,
				    "couldn't reply to rpc call\n");
				abort();
			}
			return;
		case MOUNTPROC_UMNT:
#ifdef DEBUG
			fprintf(stdout, "about to do an unmount\n");
#endif
			umount(rqstp, transp);
			return;
		case MOUNTPROC_UMNTALL:
#ifdef DEBUG
			fprintf(stdout, "about to do an unmountall\n");
#endif
			umountall(rqstp, transp);
			return;
		case MOUNTPROC_EXPORT:
		case MOUNTPROC_EXPORTALL:
#ifdef DEBUG
			fprintf(stdout, "about to do a export\n");
#endif
			set_exports();
			export(rqstp, transp);
			return;
		default:
			svcerr_noproc(transp);
			return;
	}
}

/*
 * Check mount requests, add to mounted list if ok
 */
mount(rqstp, transp)
	struct svc_req *rqstp;
	SVCXPRT *transp;
{
	fhandle_t fh;
	struct fhstatus fhs;
	char *path, *machine;
	int fd, uid;
	struct mountlist *ml;
	struct stat statbuf;
	struct exports *ex;
	struct groups *gl;
	struct pathlist *pl;
	struct sockaddr_in addr;
	struct hostent *hp;

	path = NULL;
	if (!svc_getargs(transp, xdr_path, &path)) {
		svcerr_decode(transp);
		abort();
	}
#ifdef DEBUG
	fprintf(stdout, "path is %s\n", path);
#endif
	if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) {
		machine =
		  ((struct authunix_parms *)rqstp->rq_clntcred)->aup_machname;
		uid =
		  ((struct authunix_parms *)rqstp->rq_clntcred)->aup_uid;
	}
	else {
		fhs.fhs_status = EACCES;
		goto fail;
	}
	/* 
	 * check for imposters
	 */
	addr = *(svc_getcaller(transp));
	hp = gethostbyname(machine);
	if (hp == NULL || *(int *)hp->h_addr != addr.sin_addr.s_addr) {
		fhs.fhs_status = EACCES;
		goto fail;
	}

	if ((fd = open(path, O_RDONLY, 0)) < 0) {
		fhs.fhs_status = errno;
		perror("mountd: open");
		goto fail;
	}
	if (getfh(fd, &fh) < 0) {
		fhs.fhs_status = errno;
		perror("mountd: getfh");
		close(fd);
		goto fail;
	}
	else
		fhs.fhs_status = 0;
	if (fstat(fd, &statbuf) < 0) {
		fhs.fhs_status = errno;
		perror("mountd: stat");
		close(fd);
		goto fail;
	}
	close(fd);
	for(ex = exports; ex != NULL; ex = ex->ex_next) {
#ifdef DEBUG
		fprintf(stdout, "checking %s %o for %o\n", ex->ex_name, ex->ex_dev, statbuf.st_dev);
#endif
		if (ex->ex_dev != statbuf.st_dev)
			continue;
		if (ex->ex_groups == NULL) {
			goto hit;
		}
		for (gl = ex->ex_groups; gl != NULL; gl = gl->g_next) {
#ifdef DEBUG
			fprintf(stdout, "checking %s for %s\n", gl->g_name, machine);
#endif
			if (innetgr(gl->g_name, machine, NULL, mydomain))
				goto hit;
			if (strcmp(gl->g_name, machine) == 0) {
				goto hit;
			}
		}
	}
	fhs.fhs_status = EACCES;
	goto fail;
  hit:
	fhs.fhs_fh = fh;
	for (ml = mountlist; ml != NULL; ml = ml->ml_nxt) {
		if (strcmp(ml->ml_path, path) == 0 &&
		    strcmp(ml->ml_name, machine) == 0)
			break;
	}
	if (ml == NULL) {
		ml = (struct mountlist *)exmalloc(sizeof(struct mountlist));
		ml->ml_path = (char *)exmalloc(strlen(path) + 1);
		strcpy(ml->ml_path, path);
		ml->ml_name = (char *)exmalloc(strlen(machine) + 1);
		strcpy(ml->ml_name, machine);
		ml->ml_nxt = mountlist;
		mountlist = ml;
	}
fail:
	if (!svc_sendreply(transp, xdr_fhstatus, &fhs)) {
	    	fprintf(stdout, "couldn't reply to rpc call\n");
		abort();
	}
	dumptofile();
	svc_freeargs(transp, xdr_path, &path);
}

/*
 * Remove an entry from mounted list
 */
umount(rqstp, transp)
	struct svc_req *rqstp;
	SVCXPRT *transp;
{
	char *path, *machine;
	struct mountlist *ml, *oldml;

	path = NULL;
	if (!svc_getargs(transp, xdr_path, &path)) {
		svcerr_decode(transp);
		abort();
	}
	if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) {
		machine =
		  ((struct authunix_parms *)rqstp->rq_clntcred)->aup_machname;
	}
	else
		return;
#ifdef DEBUG
	fprintf(stdout, "name %s path %s\n", machine, path);
#endif
	oldml = mountlist;
	for (ml = mountlist; ml != NULL;
	    oldml = ml, ml = ml->ml_nxt) {
		if (strcmp(ml->ml_path, path) == 0 &&
		    strcmp(ml->ml_name, machine) == 0) {
			if (ml == mountlist)
				mountlist = ml->ml_nxt;
			else
				oldml->ml_nxt = ml->ml_nxt;
#ifdef DEBUG
	fprintf(stdout, "freeing %s\n", path);
#endif
			free(ml->ml_path);
			free(ml->ml_name);
			free(ml);
			break;
		    }
	}
	if (!svc_sendreply(transp,xdr_void, NULL)) {
	    	fprintf(stdout, "couldn't reply to rpc call\n");
		abort();
	}
	dumptofile();
	svc_freeargs(transp, xdr_path, &path);
}

/*
 * Remove all entries for one machine from mounted list
 */
umountall(rqstp, transp)
	struct svc_req *rqstp;
	SVCXPRT *transp;
{
	char *machine;
	struct mountlist *ml, *oldml;

	if (!svc_getargs(transp, xdr_void, NULL)) {
		svcerr_decode(transp);
		abort();
	}
	if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) {
		machine =
		   ((struct authunix_parms *)rqstp->rq_clntcred)->aup_machname;
	}
	else
		return;
	oldml = mountlist;
	for (ml = mountlist; ml != NULL; ml = ml->ml_nxt) {
		if (strcmp(ml->ml_name, machine) == 0) {
#ifdef DEBUG
			fprintf(stdout, "got a hit\n");
#endif
			if (ml == mountlist) {
				mountlist = ml->ml_nxt;
				oldml = mountlist;
			}
			else
				oldml->ml_nxt = ml->ml_nxt;
			free(ml->ml_path);
			free(ml->ml_name);
			free(ml);
		}
		else
			oldml = ml;
	}
	if (!svc_sendreply(transp,xdr_void, NULL)) {
	    	fprintf(stdout, "couldn't reply to rpc call\n");
		abort();
	}
	dumptofile();
	svc_freeargs(transp, xdr_void, NULL);
}

/*
 * send current export list
 */
export(rqstp, transp)
	struct svc_req *rqstp;
	SVCXPRT *transp;
{
	struct exports *ex;

	if (!svc_getargs(transp, xdr_void, NULL)) {
		svcerr_decode(transp);
		abort();
	}

	ex = exports;
	if (!svc_sendreply(transp, xdr_exports, &ex)) {
	    	fprintf(stdout, "couldn't reply to rpc call\n");
		abort();
	}
}

/*
 * Save current mount state info so we
 * can attempt to recover in case of a crash.
 */
dumptofile()
{
	static char *t1 = "/etc/zzXXXXXX";
	static char *t2 = "/etc/zzXXXXXX";
	FILE *fp;
	struct mountlist *ml;
	char *mktemp();
	int mf;
	
	strcpy(t2, t1);
	t2 = mktemp(t2);
	if ((mf = creat(t2, 0644)) < 0)
		perror("mountd: creat");
	if ((fp = fdopen(mf, "w")) == NULL)
		fprintf(stdout, "mountd: fdopen");
	for (ml = mountlist; ml != NULL; ml = ml->ml_nxt)
		fprintf(fp, "%s:%s\n", ml->ml_name, ml->ml_path);
	if (rename(t2, RMTAB) < 0)
		perror("mountd: link");
	fclose(fp);
}

/*
 * Restore saved mount state
 */
readfromfile()
{
	FILE *fp;
	struct mountlist *ml;
	char name[BUFSIZ];
	char *path, *index(), *rindex();
	
	fp = fopen(RMTAB, "r");
	if (fp == NULL)
		return;
	while (1) {
		if (fgets(name, sizeof(name), fp) == NULL)
			break;
		path = rindex(name, '\n');
		if (path == NULL)
			break;
		*path = 0;
		path = index(name, ':');
		if (path == NULL)
			break;
		*path++ = NULL;
		ml = (struct mountlist *) exmalloc(sizeof(struct mountlist));
		ml->ml_path = (char *)exmalloc(strlen(path) + 1);
		strcpy(ml->ml_path, path);
		ml->ml_name = (char *)exmalloc(strlen(name) + 1);
		strcpy(ml->ml_name, name);
		ml->ml_nxt = mountlist;
		mountlist = ml;
	}
	fclose(fp);
}

struct groups *
newgroup(name, next)
	char *name;
	struct groups *next;
{
	struct groups *new;
	char *newname;

	new = (struct groups *)exmalloc(sizeof(*new));
	newname = (char *)exmalloc(strlen(name) + 1);
	strcpy(newname, name);

	new->g_name = newname;
	new->g_next = next;
	return (new);
}

struct exports *
newex(name, dev, groups, next)
	char *name;
	dev_t dev;
	struct groups *groups;
	struct exports *next;
{
	struct exports *new;
	char *newname;

	new = (struct exports *)exmalloc(sizeof(*new));
	newname = (char *)exmalloc(strlen(name) + 1);
	strcpy(newname, name);

	new->ex_name = newname;
	new->ex_dev = dev;
	new->ex_groups = groups;
	new->ex_next = next;
	return (new);
}


struct stat exportstat;
int exportdone = 0;
/*
 * Parse exports file
 * If this is the first call or the file exportfile (set in main) has
 * changed exportfile is opened and parsed to create an exports list.
 * file should look like:
 * ^dir names*
 *   or
 * #anything
 * where: dir is the name of a mount point for a local file system
 *        names is a netgroup or host name or a list of white seperated names
 *        A '#' anywhere in the line marks a comment to the end of that line
 * NOTE: a non-white character in column 1 indicates a new export specification.
 */
set_exports()
{
	int bol;	/* begining of line */
	int eof;	/* end of file */
	struct exports *ex;
	char ch;
	char *str;
	char *l;
	char line[MAXLINE];	/* current line */
	struct stat statb;
	FILE *fp;

	if (exportdone++) {
		if (stat(exportfile, &statb) < 0) {
			fprintf(stdout, "mountd: stat failed ");
			perror(exportfile);
			freeex(exports);
			exports = NULL;
			return;
		}
		if (exportstat.st_mtime == statb.st_mtime) {
			return;
		}
	}
	exportstat = statb;

	if ((fp = fopen(exportfile, "r")) == NULL) {
		perror(exportfile);
		freeex(exports);
		exports = NULL;
		return;
	}

	freeex(exports);
	eof = 0;
	ex = NULL;
	l = line;
	*l = '\0';
	while (!eof) {
		switch (*l) {
		case '\0':
		case '\n':
			/*
			 * End of line, read next line and set state vars
			 */
			if (fgets(line, MAXLINE, fp) == NULL) {
				eof = 1;
			} else {
				bol = 1;
				l = line;
			}
			break;
		case ' ':
		case '	':
			/*
			 * White space, skip to first non-white
			 */
			while (*l == ' ' || *l == '	') {
				l++;
			}
			bol = 0;
			break;
		case '#':
			/*
			 * Comment, skip to end of line.
			 */
			*l = '\0';
			break;
		default:
			/*
			 * normal character: if col one get dir else name
			 */
			str = l;
			while (*l != ' ' && *l != '	' &&
			     *l != '\0' && *l != '\n') {
				l++;
			}
			ch = *l;
			*l = '\0';
			if (bol) {
				if (stat(str, &statb) < 0) {
					fprintf(stdout, "mountd: can't stat ");
					perror(str);
					break;
				}
				if (statb.st_ino != ROOTINO ||
				    (major(statb.st_dev) == 0377)) {
					fprintf(stdout,
					    "mountd: %s bad file system root\n",
					    str);
					break;
				} else {
					ex = newex(str, statb.st_dev, NULL, ex);
				}
			} else {
				ex->ex_groups = newgroup(str, ex->ex_groups);
			}
			*l = ch;
			bol = 0;
			break;
		}
	}
	fclose(fp);
	exports = ex;
	return;
}

freeex(ex)
	struct exports *ex;
{
	struct groups *groups, *tmpgroups;
	struct exports *tmpex;

	while (ex) {
		groups = ex->ex_groups;
		while (groups) {
			tmpgroups = groups->g_next;
			free(groups);
			groups = tmpgroups;
		}
		tmpex = ex->ex_next;
		free(ex);
		ex = tmpex;
	}
}

char *
exmalloc(size)
	int size;
{
	char *ret;

	if ((ret = (char *)malloc(size)) == 0) {
		fprintf(stdout, "Out of memory\n");
		exit(1);
	}

	return (ret);
}

catch()
{
}