NFSv2/usr/src/usr.etc/rpc.mountd.c
#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()
{
}