Files
Arquivotheca.SunOS-4.1.4/usr.etc/rpc.mountd.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

1075 lines
22 KiB
C

#ifndef lint
static char sccsid[] = "@(#)rpc.mountd.c 1.1 94/10/31 Copyr 1987 Sun Micro";
#endif
/*
* Copyright (c) 1987 Sun Microsystems, Inc.
*/
#include <sys/param.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 <syslog.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <nfs/nfs.h>
#include <rpcsvc/mount.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <exportent.h>
#include <string.h>
#include <sys/pathconf.h>
#include <sys/unistd.h>
#define MAXRMTABLINELEN (MAXPATHLEN + MAXHOSTNAMELEN + 2)
extern int errno;
char XTAB[] = "/etc/xtab";
char RMTAB[] = "/etc/rmtab";
int mnt();
char *exmalloc();
struct groups **newgroup();
struct exports **newexport();
void log_cant_reply();
void mnt_pathconf();
void mount();
void check_exports();
void xent_free();
/*
* mountd's version of a "struct mountlist". It is the same except
* for the added ml_pos field.
*/
struct mountdlist {
/* same as XDR mountlist */
char *ml_name;
char *ml_path;
struct mountdlist *ml_nxt;
/* private to mountd */
long ml_pos; /* position of mount entry in RMTAB */
};
struct mountdlist *mountlist;
struct xent_list { /* cached export list */
struct xent_list *x_next;
struct exportent x_xent;
} *xent_list;
int nfs_portmon = 1;
char *domain;
void rmtab_load();
void rmtab_delete();
long rmtab_insert();
main(argc, argv)
int argc;
char **argv;
{
SVCXPRT *transp;
int pid;
register int i;
int sock;
int proto;
if (argc == 2) {
if (strcmp(argv[1], "-n") == 0) {
nfs_portmon = 0;
} else {
usage();
}
} else if (argc > 2) {
usage();
}
if (issock(0)) {
/*
* Started from inetd
*/
sock = 0;
proto = 0; /* don't register with portmapper */
} else {
/*
* Started from shell, background.
*/
pid = fork();
if (pid < 0) {
perror("mountd: can't fork");
exit(1);
}
if (pid) {
exit(0);
}
/*
* Close existing file descriptors, open "/dev/null" as
* standard input, output, and error, and detach from
* controlling terminal.
*/
i = getdtablesize();
while (--i >= 0)
(void) close(i);
(void) open("/dev/null", O_RDONLY);
(void) open("/dev/null", O_WRONLY);
(void) dup(1);
i = open("/dev/tty", O_RDWR);
if (i >= 0) {
(void) ioctl(i, TIOCNOTTY, (char *)0);
(void) close(i);
}
(void) pmap_unset(MOUNTPROG, MOUNTVERS);
(void) pmap_unset(MOUNTPROG, MOUNTVERS_POSIX);
sock = RPC_ANYSOCK;
proto = IPPROTO_UDP;
}
openlog("mountd", LOG_PID, LOG_DAEMON);
/*
* Create UDP service
*/
if ((transp = svcudp_create(sock)) == NULL) {
syslog(LOG_ERR, "couldn't create UDP transport");
exit(1);
}
if (!svc_register(transp, MOUNTPROG, MOUNTVERS, mnt, proto)) {
syslog(LOG_ERR, "couldn't register UDP MOUNTPROG_ORIG");
exit(1);
}
if (!svc_register(transp, MOUNTPROG, MOUNTVERS_POSIX, mnt, proto)) {
syslog(LOG_ERR, "couldn't register UDP MOUNTPROG");
exit(1);
}
/*
* Create TCP service
*/
if ((transp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) {
syslog(LOG_ERR, "couldn't create TCP transport");
exit(1);
}
if (!svc_register(transp, MOUNTPROG, MOUNTVERS, mnt,
IPPROTO_TCP)) {
syslog(LOG_ERR, "couldn't register TCP MOUNTPROG_ORIG");
exit(1);
}
if (!svc_register(transp, MOUNTPROG, MOUNTVERS_POSIX, mnt,
IPPROTO_TCP)) {
syslog(LOG_ERR, "couldn't register TCP MOUNTPROG");
exit(1);
}
/*
* Initalize the world
*/
(void) yp_get_default_domain(&domain);
/*
* Start serving
*/
rmtab_load();
svc_run();
syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
abort();
/* NOTREACHED */
}
/*
* Determine if a descriptor belongs to a socket or not
*/
issock(fd)
int fd;
{
struct stat st;
if (fstat(fd, &st) < 0) {
return (0);
}
/*
* SunOS returns S_IFIFO for sockets, while 4.3 returns 0 and does not
* even have an S_IFIFO mode. Since there is confusion about what the
* mode is, we check for what it is not instead of what it is.
*/
switch (st.st_mode & S_IFMT) {
case S_IFCHR:
case S_IFREG:
case S_IFLNK:
case S_IFDIR:
case S_IFBLK:
return (0);
default:
return (1);
}
}
/*
* Server procedure switch routine
*/
mnt(rqstp, transp)
struct svc_req *rqstp;
SVCXPRT *transp;
{
switch (rqstp->rq_proc) {
case NULLPROC:
errno = 0;
if (!svc_sendreply(transp, xdr_void, (char *)0))
log_cant_reply(transp);
return;
case MOUNTPROC_MNT:
mount(rqstp);
return;
case MOUNTPROC_DUMP:
errno = 0;
if (!svc_sendreply(transp, xdr_mountlist, (char *)&mountlist))
log_cant_reply(transp);
return;
case MOUNTPROC_UMNT:
umount(rqstp);
return;
case MOUNTPROC_UMNTALL:
umountall(rqstp);
return;
case MOUNTPROC_EXPORT:
case MOUNTPROC_EXPORTALL:
export(rqstp);
return;
case MOUNTPROC_PATHCONF:
if (rqstp->rq_vers == MOUNTVERS_POSIX) {
mnt_pathconf(rqstp);
return;
}
/* else fall through to error out */
default:
svcerr_noproc(transp);
return;
}
}
struct hostent *
getclientsname(transp)
SVCXPRT *transp;
{
struct sockaddr_in actual;
struct hostent *hp;
static struct hostent h;
static struct in_addr prev;
static char *null_alias;
actual = *svc_getcaller(transp);
if (nfs_portmon) {
if (ntohs(actual.sin_port) >= IPPORT_RESERVED) {
return (NULL);
}
}
/*
* Don't use the unix credentials to get the machine name,
* instead use the source IP address.
* Used cached hostent if previous call was for the
* same client.
*/
if (bcmp(&actual.sin_addr, &prev, sizeof(struct in_addr)) == 0)
return (&h);
prev = actual.sin_addr;
hp = gethostbyaddr((char *) &actual.sin_addr, sizeof(actual.sin_addr),
AF_INET);
if (hp == NULL) { /* dummy one up */
h.h_name = inet_ntoa(actual.sin_addr);
h.h_aliases = &null_alias;
h.h_addrtype = AF_INET;
h.h_length = sizeof (u_long);
hp = &h;
} else {
bcopy(hp, &h, sizeof(struct hostent));
}
return (hp);
}
void
log_cant_reply(transp)
SVCXPRT *transp;
{
int saverrno;
struct sockaddr_in actual;
register struct hostent *hp;
register char *name;
saverrno = errno; /* save error code */
actual = *svc_getcaller(transp);
/*
* Don't use the unix credentials to get the machine name, instead use
* the source IP address.
*/
if ((hp = gethostbyaddr(&actual.sin_addr, sizeof(actual.sin_addr),
AF_INET)) != NULL)
name = hp->h_name;
else
name = inet_ntoa(actual.sin_addr);
errno = saverrno;
if (errno == 0)
syslog(LOG_ERR, "couldn't send reply to %s", name);
else
syslog(LOG_ERR, "couldn't send reply to %s: %m", name);
}
/*
* Answer pathconf questions for the mount point fs
*/
void
mnt_pathconf(rqstp)
struct svc_req *rqstp;
{
SVCXPRT *transp;
struct pathcnf p;
char *path, rpath[MAXPATHLEN];
char *gr, *grl;
struct exportent *xent;
struct exportent *findentry();
struct stat st;
char **aliases;
struct hostent *client;
transp = rqstp->rq_xprt;
path = NULL;
bzero((caddr_t)&p, sizeof(p));
client = getclientsname(transp);
if (client == NULL) {
_PC_SET(_PC_ERROR, p.pc_mask);
goto done;
}
if (!svc_getargs(transp, xdr_path, &path)) {
svcerr_decode(transp);
return;
}
if (lstat(path, &st) < 0) {
_PC_SET(_PC_ERROR, p.pc_mask);
goto done;
}
/*
* Get a path without symbolic links.
*/
if (realpath(path, rpath) == NULL) {
syslog(LOG_DEBUG,
"mount request: realpath failed on %s: %m",
path);
_PC_SET(_PC_ERROR, p.pc_mask);
goto done;
}
bzero(&p, sizeof(p));
/*
* can't ask about devices over NFS
*/
_PC_SET(_PC_MAX_CANON, p.pc_mask);
_PC_SET(_PC_MAX_INPUT, p.pc_mask);
_PC_SET(_PC_PIPE_BUF, p.pc_mask);
_PC_SET(_PC_VDISABLE, p.pc_mask);
errno = 0;
p.pc_link_max = pathconf(rpath, _PC_LINK_MAX);
if (errno)
_PC_SET(_PC_LINK_MAX, p.pc_mask);
p.pc_name_max = pathconf(rpath, _PC_NAME_MAX);
if (errno)
_PC_SET(_PC_NAME_MAX, p.pc_mask);
p.pc_path_max = pathconf(rpath, _PC_PATH_MAX);
if (errno)
_PC_SET(_PC_PATH_MAX, p.pc_mask);
if (pathconf(rpath, _PC_NO_TRUNC) == 1)
_PC_SET(_PC_NO_TRUNC, p.pc_mask);
if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1)
_PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask);
done:
errno = 0;
if (!svc_sendreply(transp, xdr_pathcnf, (char *)&p))
log_cant_reply(transp);
if (path != NULL)
svc_freeargs(transp, xdr_path, &path);
return;
}
/*
* Check mount requests, add to mounted list if ok
*/
void
mount(rqstp)
struct svc_req *rqstp;
{
SVCXPRT *transp;
fhandle_t fh;
struct fhstatus fhs;
char *path, rpath[MAXPATHLEN];
struct mountdlist *ml;
char *gr, *grl;
struct exportent *xent;
struct exportent *findentry();
struct stat st;
char **aliases;
struct hostent *client;
transp = rqstp->rq_xprt;
path = NULL;
fhs.fhs_status = 0;
client = getclientsname(transp);
if (client == NULL) {
fhs.fhs_status = EACCES;
goto done;
}
if (!svc_getargs(transp, xdr_path, &path)) {
svcerr_decode(transp);
return;
}
if (lstat(path, &st) < 0) {
fhs.fhs_status = EACCES;
goto done;
}
/*
* Get a path without symbolic links.
*/
if (realpath(path, rpath) == NULL) {
syslog(LOG_DEBUG,
"mount request: realpath failed on %s: %m",
path);
fhs.fhs_status = EACCES;
goto done;
}
if (do_getfh(rpath, &fh) < 0) {
fhs.fhs_status = errno == EINVAL ? EACCES : errno;
syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m",
path);
goto done;
}
xent = findentry(rpath);
if (xent == NULL) {
fhs.fhs_status = EACCES;
goto done;
}
/* Check access list - hostnames first */
grl = getexportopt(xent, ACCESS_OPT);
if (grl == NULL)
goto done;
while ((gr = strtok(grl, ":")) != NULL) {
grl = NULL;
if (strcmp(gr, client->h_name) == 0)
goto done;
for (aliases = client->h_aliases; *aliases != NULL;
aliases++) {
if (strcmp(gr, *aliases) == 0)
goto done;
}
}
/* no hostname match - try netgroups */
grl = getexportopt(xent, ACCESS_OPT);
if (grl == NULL)
goto done;
while ((gr = strtok(grl, ":")) != NULL) {
grl = NULL;
if (in_netgroup(gr, client->h_name, domain))
goto done;
for (aliases = client->h_aliases; *aliases != NULL;
aliases++) {
if (in_netgroup(gr, *aliases, domain))
goto done;
}
}
/* Check root and rw lists */
grl = getexportopt(xent, ROOT_OPT);
if (grl != NULL) {
while ((gr = strtok(grl, ":")) != NULL) {
grl = NULL;
if (strcmp(gr, client->h_name) == 0)
goto done;
}
}
grl = getexportopt(xent, RW_OPT);
if (grl != NULL) {
while ((gr = strtok(grl, ":")) != NULL) {
grl = NULL;
if (strcmp(gr, client->h_name) == 0)
goto done;
}
}
fhs.fhs_status = EACCES;
done:
if (fhs.fhs_status == 0)
fhs.fhs_fh = fh;
errno = 0;
if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs))
log_cant_reply(transp);
if (path != NULL)
svc_freeargs(transp, xdr_path, &path);
if (fhs.fhs_status)
return;
/*
* Add an entry for this mount to the mount list.
* First check whether it's there already - the client
* may have crashed and be rebooting.
*/
for (ml = mountlist; ml != NULL ; ml = ml->ml_nxt) {
if (strcmp(ml->ml_path, rpath) == 0) {
if (strcmp(ml->ml_name, client->h_name) == 0) {
return;
}
for (aliases = client->h_aliases; *aliases != NULL;
aliases++) {
if (strcmp(ml->ml_name, *aliases) == 0) {
return;
}
}
}
}
/*
* Add this mount to the mount list.
*/
ml = (struct mountdlist *) exmalloc(sizeof(struct mountdlist));
ml->ml_path = (char *) exmalloc(strlen(rpath) + 1);
(void) strcpy(ml->ml_path, rpath);
ml->ml_name = (char *) exmalloc(strlen(client->h_name) + 1);
(void) strcpy(ml->ml_name, client->h_name);
ml->ml_nxt = mountlist;
ml->ml_pos = rmtab_insert(client->h_name, rpath);
mountlist = ml;
return;
}
struct exportent *
findentry(path)
char *path;
{
struct exportent *xent;
struct xent_list *xp;
register char *p1, *p2;
check_exports();
for (xp = xent_list ; xp ; xp = xp->x_next) {
xent = &xp->x_xent;
for (p1 = xent->xent_dirname, p2 = path ; *p1 == *p2 ; p1++, p2++)
if (*p1 == '\0')
return xent; /* exact match */
if ((*p1 == '\0' && *p2 == '/' ) ||
(*p1 == '\0' && *(p1-1) == '/') ||
(*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) {
if (issubdir(path, xent->xent_dirname))
return xent;
}
}
return ((struct exportent *)0);
}
#define MAXGRPLIST 256
/*
* Use cached netgroup info if the previous call was
* from the same client. Two lists are maintained
* here: groups the client is a member of, and groups
* the client is not a member of.
*/
int
in_netgroup(group, hostname, domain)
char *group, *hostname, *domain;
{
static char prev_hostname[MAXHOSTNAMELEN+1];
static char grplist[MAXGRPLIST+1], nogrplist[MAXGRPLIST+1];
char key[256];
char *ypline = NULL;
int yplen;
register char *gr, *p;
static time_t last;
time_t time();
time_t time_now;
static int cache_time = 30; /* sec */
if (domain == NULL) {
(void) yp_get_default_domain(&domain);
if (domain == NULL) {
syslog(LOG_ERR, "NIS: no domain");
return (NULL);
}
}
time_now = time((long *)0);
if (time_now > last + cache_time ||
strcmp(hostname, prev_hostname) != 0) {
last = time_now;
(void) strcpy(key, hostname);
(void) strcat(key, ".");
(void) strcat(key, domain);
bzero(grplist, sizeof(grplist));
if (yp_match(domain, "netgroup.byhost", key,
strlen(key), &ypline, &yplen) == 0) {
(void) strncpy(grplist, ypline, MIN(yplen, MAXGRPLIST));
free(ypline);
} else {
grplist[0] = '\0';
}
nogrplist[0] = '\0';
(void) strcpy(prev_hostname, hostname);
}
for (gr = grplist ; *gr ; gr = p ) {
for (p = gr ; *p && *p != ',' ; p++)
;
if (strncmp(group, gr, p - gr) == 0)
return 1;
if (*p == ',')
p++;
}
for (gr = nogrplist ; *gr ; gr = p ) {
for (p = gr ; *p && *p != ',' ; p++)
;
if (strncmp(group, gr, p - gr) == 0)
return 0;
if (*p == ',')
p++;
}
if (innetgr(group, hostname, (char *)NULL, domain)) {
if (strlen(grplist)+1+strlen(group) > MAXGRPLIST)
return 1;
if (*grplist)
(void) strcat(grplist, ",");
(void) strcat(grplist, group);
return 1;
} else {
if (strlen(nogrplist)+1+strlen(group) > MAXGRPLIST)
return 0;
if (*nogrplist)
(void) strcat(nogrplist, ",");
(void) strcat(nogrplist, group);
return 0;
}
}
void
check_exports()
{
FILE *f;
struct stat st;
static long last_xtab_time;
struct exportent *xent;
struct xent_list *xp, *xp_prev;
char rpath[MAXPATHLEN];
/*
* read in /etc/xtab if it has changed
*/
if (stat(XTAB, &st) != 0) {
syslog(LOG_ERR, "Cannot stat %s: %m", XTAB);
return;
}
if (st.st_mtime == last_xtab_time)
return; /* no change */
xent_free(xent_list); /* free old list */
xent_list = NULL;
f = setexportent();
if (f == NULL)
return;
while (xent = getexportent(f)) {
/*
* Get a path without symbolic links.
*/
if (realpath(xent->xent_dirname, rpath) == NULL) {
syslog(LOG_ERR,
"check_exports: realpath failed on %s: %m",
xent->xent_dirname);
continue;
}
xp = (struct xent_list *)malloc(sizeof(struct xent_list));
if (xp == NULL)
goto alloc_failed;
if (xent_list == NULL)
xent_list = xp;
else
xp_prev->x_next = xp;
xp_prev = xp;
bzero((char *)xp, sizeof(struct xent_list));
xp->x_xent.xent_dirname = strdup(rpath);
if (xp->x_xent.xent_dirname == NULL)
goto alloc_failed;
if (xent->xent_options) {
xp->x_xent.xent_options = strdup(xent->xent_options);
if (xp->x_xent.xent_options == NULL)
goto alloc_failed;
}
}
endexportent(f);
last_xtab_time = st.st_mtime;
return;
alloc_failed:
syslog(LOG_ERR, "Memory allocation failed: %m");
xent_free(xent_list);
xent_list = NULL;
endexportent(f);
return;
}
void
xent_free(xp)
struct xent_list *xp;
{
register struct xent_list *next;
while (xp) {
if (xp->x_xent.xent_dirname)
free(xp->x_xent.xent_dirname);
if (xp->x_xent.xent_options)
free(xp->x_xent.xent_options);
next = xp->x_next;
free((char *)xp);
xp = next;
}
}
/*
* Remove an entry from mounted list
*/
umount(rqstp)
struct svc_req *rqstp;
{
char *path;
struct mountdlist *ml, *oldml;
struct hostent *client;
SVCXPRT *transp;
transp = rqstp->rq_xprt;
path = NULL;
if (!svc_getargs(transp, xdr_path, &path)) {
svcerr_decode(transp);
return;
}
errno = 0;
if (!svc_sendreply(transp, xdr_void, (char *)NULL))
log_cant_reply(transp);
client = getclientsname(transp);
if (client != NULL) {
oldml = mountlist;
for (ml = mountlist; ml != NULL;
oldml = ml, ml = ml->ml_nxt) {
if (strcmp(ml->ml_path, path) == 0 &&
strcmp(ml->ml_name, client->h_name) == 0) {
if (ml == mountlist) {
mountlist = ml->ml_nxt;
} else {
oldml->ml_nxt = ml->ml_nxt;
}
rmtab_delete(ml->ml_pos);
free(ml->ml_path);
free(ml->ml_name);
free((char *)ml);
break;
}
}
}
svc_freeargs(transp, xdr_path, &path);
}
/*
* Remove all entries for one machine from mounted list
*/
umountall(rqstp)
struct svc_req *rqstp;
{
struct mountdlist *ml, *oldml;
struct hostent *client;
SVCXPRT *transp;
transp = rqstp->rq_xprt;
if (!svc_getargs(transp, xdr_void, NULL)) {
svcerr_decode(transp);
return;
}
/*
* We assume that this call is asynchronous and made via the portmapper
* callit routine. Therefore return control immediately. The error
* causes the portmapper to remain silent, as apposed to every machine
* on the net blasting the requester with a response.
*/
svcerr_systemerr(transp);
client = getclientsname(transp);
if (client == NULL) {
return;
}
oldml = mountlist;
for (ml = mountlist; ml != NULL; ml = ml->ml_nxt) {
if (strcmp(ml->ml_name, client->h_name) == 0) {
if (ml == mountlist) {
mountlist = ml->ml_nxt;
oldml = mountlist;
} else {
oldml->ml_nxt = ml->ml_nxt;
}
rmtab_delete(ml->ml_pos);
free(ml->ml_path);
free(ml->ml_name);
free((char *)ml);
} else {
oldml = ml;
}
}
}
/*
* send current export list
*/
export(rqstp)
struct svc_req *rqstp;
{
struct exportent *xent;
struct exports *ex;
struct exports **tail;
char *grl;
char *gr;
struct groups *groups;
struct groups **grtail;
SVCXPRT *transp;
struct xent_list *xp;
transp = rqstp->rq_xprt;
if (!svc_getargs(transp, xdr_void, NULL)) {
svcerr_decode(transp);
return;
}
check_exports();
ex = NULL;
tail = &ex;
for (xp = xent_list ; xp ; xp = xp->x_next) {
xent = &xp->x_xent;
grl = getexportopt(xent, ACCESS_OPT);
groups = NULL;
if (grl != NULL) {
grtail = &groups;
while ((gr = strtok(grl, ":")) != NULL) {
grl = NULL;
grtail = newgroup(gr, grtail);
}
}
tail = newexport(xent->xent_dirname, groups, tail);
}
errno = 0;
if (!svc_sendreply(transp, xdr_exports, (char *)&ex))
log_cant_reply(transp);
freeexports(ex);
}
freeexports(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->g_name);
free((char *)groups);
groups = tmpgroups;
}
tmpex = ex->ex_next;
free(ex->ex_name);
free((char *)ex);
ex = tmpex;
}
}
struct groups **
newgroup(name, tail)
char *name;
struct groups **tail;
{
struct groups *new;
char *newname;
new = (struct groups *) exmalloc(sizeof(*new));
newname = (char *) exmalloc(strlen(name) + 1);
(void) strcpy(newname, name);
new->g_name = newname;
new->g_next = NULL;
*tail = new;
return (&new->g_next);
}
struct exports **
newexport(name, groups, tail)
char *name;
struct groups *groups;
struct exports **tail;
{
struct exports *new;
char *newname;
new = (struct exports *) exmalloc(sizeof(*new));
newname = (char *) exmalloc(strlen(name) + 1);
(void) strcpy(newname, name);
new->ex_name = newname;
new->ex_groups = groups;
new->ex_next = NULL;
*tail = new;
return (&new->ex_next);
}
char *
exmalloc(size)
int size;
{
char *ret;
if ((ret = (char *) malloc((u_int)size)) == 0) {
syslog(LOG_ERR, "Out of memory");
exit(1);
}
return (ret);
}
usage()
{
(void) fprintf(stderr, "usage: rpc.mountd [-n]\n");
exit(1);
}
/*
* Old geth() took a file descriptor. New getfh() takes a pathname.
* So the the mount daemon can run on both old and new kernels, we try
* the old version of getfh() if the new one fails.
*/
do_getfh(path, fh)
char *path;
fhandle_t *fh;
{
int fd;
int res;
int save;
res = getfh(path, fh);
if (res < 0 && errno == EBADF) {
/*
* This kernel does not have the new-style getfh()
*/
fd = open(path, 0, 0);
if (fd >= 0) {
res = getfh((char *)fd, fh);
save = errno;
(void) close(fd);
errno = save;
}
}
return (res);
}
FILE *f;
void
rmtab_load()
{
char *path;
char *name;
char *end;
struct mountdlist *ml;
char line[MAXRMTABLINELEN];
f = fopen(RMTAB, "r");
if (f != NULL) {
while (fgets(line, sizeof(line), f) != NULL) {
name = line;
path = strchr(name, ':');
if (*name != '#' && path != NULL) {
*path = 0;
path++;
end = strchr(path, '\n');
if (end != NULL) {
*end = 0;
}
ml = (struct mountdlist *)
exmalloc(sizeof(struct mountdlist));
ml->ml_path = (char *)
exmalloc(strlen(path) + 1);
(void) strcpy(ml->ml_path, path);
ml->ml_name = (char *)
exmalloc(strlen(name) + 1);
(void) strcpy(ml->ml_name, name);
ml->ml_nxt = mountlist;
mountlist = ml;
}
}
(void) fclose(f);
(void) truncate(RMTAB, (off_t)0);
}
f = fopen(RMTAB, "w+");
if (f != NULL) {
setlinebuf(f);
for (ml = mountlist; ml != NULL; ml = ml->ml_nxt) {
ml->ml_pos = rmtab_insert(ml->ml_name, ml->ml_path);
}
}
}
long
rmtab_insert(name, path)
char *name;
char *path;
{
long pos;
if (f == NULL || fseek(f, 0L, 2) == -1) {
return (-1);
}
pos = ftell(f);
if (fprintf(f, "%s:%s\n", name, path) == EOF) {
return (-1);
}
return (pos);
}
void
rmtab_delete(pos)
long pos;
{
if (f != NULL && pos != -1 && fseek(f, pos, 0) == 0) {
(void) fprintf(f, "#");
(void) fflush(f);
}
}