|
|
researchv10 Norman
/*
* simple unix filesystem interface;
* access local files through system calls
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <rf.h>
#include "zarf.h"
extern int errno;
static Rfile *rootf;
static int servuid, servgid;
char rootname[200]; /* arbitrarily large */
extern Namemap *exulist, *exglist;
#define rfmode(m) (m)
#define fsmode(m) (m)
#define FSPERM 07777 /* just the permission bits */
static int clientdev();
static int maxopen();
static int samefile();
char *malloc();
long lseek();
/*
* init:
* return the root
*/
Rfile *
fsinit(argc, argv)
int argc;
char **argv;
{
register Rfile *f;
char *passwd = "/etc/passwd";
char *group = "/etc/group";
FILE *fp;
int foundex;
foundex = 0;
while (--argc > 0) {
if (**++argv != '-')
continue; /* skip unknown args */
switch (argv[0][1]) {
case 'd':
if (--argc <= 0) {
rflog("-d: missing arg\n");
continue;
}
rfdebug = atoi(*++argv);
break;
case 'p':
if (--argc <= 0) {
rflog("-p: missing arg\n");
continue;
}
passwd = *++argv;
break;
case 'g':
if (--argc <= 0) {
rflog("-g: missing arg\n");
continue;
}
group = *++argv;
break;
case 'r':
if (--argc <= 0) {
rflog("-r: missing arg\n");
continue;
}
strcpy(rootname, *++argv);
break;
case 'e':
if (--argc <= 0) {
rflog("-e: missing arg\n");
continue;
}
if (foundex == 0 && (fp = fopen(*++argv, "r")) != NULL) {
foundex = rdexcept(fp);
fclose(fp);
}
break;
default:
rflog("unknown flag %s\n", *argv);
break;
}
}
rfuidmap = rfmkidmap(passwd, exulist);
rfgidmap = rfmkidmap(group, exglist);
if ((f = (Rfile *)malloc(sizeof(Rfile))) == NULL)
return (NULL);
if ((f->fs = malloc(sizeof(Fsfile))) == NULL) {
free((char *)f);
return (NULL);
}
if ((fsp(f)->name = malloc(strlen(rootname)+1)) == NULL) {
free(f->fs);
free((char *)f);
return (NULL);
}
if (rootname[0] == 0)
strcpy(rootname, "/");
strcpy(fsp(f)->name, rootname);
fsp(f)->fd = -1;
fsp(f)->flags = WONTREAD|WONTWRITE|NOSTAT;
fsstat(f);
maxopen(f, 0);
rootf = f;
umask(0);
servuid = geteuid();
servgid = getegid();
setgid(servgid); /* to avoid inconsistencies */
setuid(servuid);
return (f);
}
Rfile *
fswalk(df, name)
Rfile *df;
char *name;
{
register Rfile *f;
char *nname;
if (rfdebug)
rflog("walk %d,%ld '%s' '%s'\n", df->dev, df->ino, fsp(df)->name, name);
if ((f = (Rfile *)malloc(sizeof(Rfile))) == NULL) {
rflog("fswalk: no mem\n");
fserrno = RFEINVAL;
return (NULL);
}
if ((f->fs = malloc(sizeof(Fsfile))) == NULL) {
free((char *)f);
rflog("fswalk: no mem\n");
fserrno = RFEINVAL;
return (NULL);
}
if ((nname = malloc(strlen(fsp(df)->name) + strlen(name) + 2)) == NULL) {
free(f->fs);
free((char *)f);
rflog("fswalk: no mem\n");
fserrno = RFEINVAL;
return (NULL);
}
fsp(f)->name = nname;
strcpy(nname, fsp(df)->name);
strcat(nname, "/");
strcat(nname, name);
fsp(f)->fd = -1;
fsp(f)->flags = WONTREAD|WONTWRITE|NOSTAT;
if (fsstat(f) < 0)
goto bad;
/*
* hack to avoid hanging in open:
* don't open yet unless regular file (not device)
* and nonzero link count (not Research mounted stream)
* better not to defer open for ordinary files,
* in case someone unlinks the name we know
*/
if ((fsmode(f->mode) & S_IFMT) == S_IFREG && f->nlink != 0)
maxopen(f, 0);
if (df == rootf) { /* magic for . and .., mostly for pwd */
#if NOTDEF
if (name[0] == 0 || strcmp(name, ".") == 0) {
close(fsp(f)->fd);
free(nname);
free(f->fs);
free((char *)f);
return (df);
}
#endif
if (strcmp(name, "..") == 0) {
close(fsp(f)->fd);
fserrno = 0; /* pseudo-error: popped out of root */
goto bad;
}
}
return (f);
bad:
free(nname);
free(f->fs);
free((char *)f);
return (NULL);
}
Rfile *
fscreate(df, name, mode, uid, gid)
Rfile *df;
char *name;
int mode;
int uid, gid;
{
register Rfile *f;
char *nname;
if ((f = (Rfile *)malloc(sizeof(Rfile))) == NULL) {
rflog("fscreate: no mem\n");
fserrno = RFEINVAL;
return (NULL);
}
if ((f->fs = malloc(sizeof(Fsfile))) == NULL) {
free((char *)f);
rflog("fscreate: no mem\n");
fserrno = RFEINVAL;
return (NULL);
}
if ((nname = malloc(strlen(fsp(df)->name) + strlen(name) + 2)) == NULL) {
free(f->fs);
free((char *)f);
rflog("fscreate: no mem\n");
fserrno = RFEINVAL;
return (NULL);
}
fsp(f)->name = nname;
strcpy(nname, fsp(df)->name);
strcat(nname, "/");
strcat(nname, name);
if ((fsp(f)->fd = creat(nname, fsmode(mode))) < 0) {
fserrno = errno;
goto bad;
}
fsp(f)->flags = WONTREAD|NOSTAT;
if (fsstat(f) < 0) {
rflog("fscreate: can't stat %s, err %d\n", nname, errno);
fserrno = RFEINVAL;
close(fsp(f)->fd);
goto bad;
}
if (uid != f->uid || gid != f->gid) {
if (chown(nname, uid, gid) >= 0) {
f->uid = uid; /* stat would be too slow */
f->gid = gid;
} else if (servuid == 0) { /* if not root, ignore error */
rflog("fscreate: can't set owner %s err %d\n", nname, errno);
fserrno = errno;
close(fsp(f)->fd);
goto bad;
}
}
return (f);
bad:
free(nname);
free(f->fs);
free((char *)f);
return (NULL);
}
int
fsmkdir(df, name, mode, uid, gid)
register Rfile *df;
char *name;
int mode;
int uid, gid;
{
char *nname;
struct stat st;
if ((nname = malloc(strlen(fsp(df)->name) + strlen(name) + 2)) == NULL) {
rflog("fsmkdir: no mem\n");
fserrno = RFEINVAL;
return (-1);
}
strcpy(nname, fsp(df)->name);
strcat(nname, "/");
strcat(nname, name);
if (mkdir(nname, fsmode(mode)) < 0) {
fserrno = errno;
free(nname);
return (-1);
}
if (stat(nname, &st) < 0
|| (uid != st.st_uid || gid != st.st_gid) && chown(nname, uid, gid) < 0) {
rflog("fsmkdir: can't set owner %s err %d\n", nname, errno);
fserrno = errno;
rmdir(nname); /* maybe this will work */
free(nname);
return (-1);
}
free(nname);
return (0);
}
int
fsrmdir(df, name)
register Rfile *df;
char *name;
{
char *nname;
if ((nname = malloc(strlen(fsp(df)->name) + strlen(name) + 2)) == NULL) {
rflog("fsrmdir: no mem\n");
fserrno = RFEINVAL;
return (-1);
}
strcpy(nname, fsp(df)->name);
strcat(nname, "/");
strcat(nname, name);
if (rmdir(nname) < 0) {
fserrno = errno;
free(nname);
return (-1);
}
free(nname);
return (0);
}
int
fsdelete(df, name)
register Rfile *df;
char *name;
{
char *nname;
if ((nname = malloc(strlen(fsp(df)->name) + strlen(name) + 2)) == NULL) {
rflog("fsdelete: no mem\n");
fserrno = RFEINVAL;
return (-1);
}
strcpy(nname, fsp(df)->name);
strcat(nname, "/");
strcat(nname, name);
if (unlink(nname) < 0) {
fserrno = errno;
free(nname);
return (-1);
}
free(nname);
return (0);
}
int
fslink(df, name, f)
register Rfile *df, *f;
char *name;
{
char *nname;
if ((nname = malloc(strlen(fsp(df)->name) + strlen(name) + 2)) == NULL) {
rflog("fslink: no mem\n");
fserrno = RFEINVAL;
return (-1);
}
if (samefile(f) == 0) {
rflog("fslink: wrong file %s\n", fsp(f)->name);
fserrno = RFEINVAL;
return (-1);
}
strcpy(nname, fsp(df)->name);
strcat(nname, "/");
strcat(nname, name);
if (link(fsp(f)->name, nname) < 0) {
fserrno = errno;
free(nname);
return (-1);
}
free(nname);
return (0);
}
int
fsdone(f)
register Rfile *f;
{
if (fsp(f)->flags & DIDDIR)
dodircleanup(f);
close(fsp(f)->fd);
free(fsp(f)->name);
free(f->fs);
free((char *)f);
return (0);
}
/*
* this should really use fchmod and fchown, but
* (1) some systems don't have them;
* (2) there must be code to call chmod anyway,
* else how can we chmod a file in mode 0 if not super-user?
* (3) there's no futime anyway
*
* the test in front is to skip samefile (and perhaps a spurious
* error message) if nothing is really changing;
* usually this just means ta or tm is being updated to the
* value the local system has already stored after read or write
*/
int
fsupdate(f, nf)
register Rfile *f, *nf;
{
long ft[2];
int nfd;
int status;
if ((fsmode(nf->mode)&FSPERM) == (fsmode(f->mode)&FSPERM)
&& nf->uid == f->uid && nf->gid == f->gid
&& nf->ta == f->ta && nf->tm == f->tm
&& nf->size == f->size)
return (0);
if (samefile(f) == 0) {
rflog("fsupdate: wrong file %s\n", fsp(f)->name);
fserrno = RFEINVAL;
return (-1);
}
status = -1;
if ((fsmode(nf->mode)&FSPERM) != (fsmode(f->mode)&FSPERM)) {
if (chmod(fsp(f)->name, fsmode(nf->mode) & FSPERM) < 0) {
fserrno = errno;
goto out;
}
}
if (nf->ta || nf->tm) {
ft[0] = nf->ta;
ft[1] = nf->tm;
if (utime(fsp(f)->name, ft) < 0) {
fserrno = errno;
goto out;
}
}
if (f->uid != nf->uid || f->gid != nf->gid) {
if (chown(fsp(f)->name, nf->uid, nf->gid) < 0) {
fserrno = errno;
goto out;
}
}
if (f->size != nf->size && nf->size == 0) {
if ((nfd = creat(fsp(f)->name, 0)) < 0) {
fserrno = errno;
goto out;
}
close(nfd);
}
status = 0;
out:
fsstat(f);
return (status);
}
fsstat(f)
register Rfile *f;
{
struct stat st;
if (fsp(f)->fd >= 0 && fstat(fsp(f)->fd, &st) < 0) {
fserrno = errno;
return (-1);
}
else if (stat(fsp(f)->name, &st) < 0) {
fserrno = errno;
return (-1);
}
if (fsp(f)->flags & NOSTAT)
fsp(f)->flags &=~ NOSTAT;
else if (clientdev(st.st_dev) != f->dev || st.st_ino != f->ino) {
rflog("fsstat: wrong file %s\n", fsp(f)->name);
fserrno = RFEINVAL;
return (-1);
}
f->ino = st.st_ino;
f->dev = clientdev(st.st_dev);
f->mode = rfmode(st.st_mode);
switch(st.st_mode & S_IFMT) {
case S_IFDIR:
f->type = RFTDIR;
break;
case S_IFCHR:
case S_IFBLK:
f->mode &=~ 07777; /* deny access to devices */
/* fall through */
case S_IFREG:
f->type = RFTREG;
break;
}
f->nlink = st.st_nlink;
f->uid = st.st_uid;
f->gid = st.st_gid;
f->size = st.st_size;
f->ta = st.st_atime;
f->tm = st.st_mtime;
f->tc = st.st_ctime;
return (0);
}
int
fsread(f, off, buf, len)
register Rfile *f;
long off;
char *buf;
int len;
{
register int n;
if (fsp(f)->flags & WONTREAD)
maxopen(f, 0);
if (fsp(f)->flags & CANTREAD) {
fserrno = RFEACCES; /* probably */
return (-1);
}
lseek(fsp(f)->fd, off, 0);
if ((n = read(fsp(f)->fd, buf, len)) < 0)
fserrno = errno;
return (n);
}
int
fsdirread(f, off, buf, len, offp)
register Rfile *f;
long off;
char *buf;
int len;
long *offp;
{
if (fsp(f)->flags & WONTREAD)
maxopen(f, 0);
if (fsp(f)->flags & CANTREAD) {
fserrno = RFEACCES; /* probably */
return (-1);
}
fsp(f)->flags |= DIDDIR;
return (dodirread(f, buf, len, off, offp));
}
int
fswrite(f, off, buf, len)
register Rfile *f;
long off;
char *buf;
int len;
{
register int n;
if (fsp(f)->flags & WONTWRITE)
maxopen(f, 1);
if (fsp(f)->flags & CANTWRITE) {
fserrno = RFEACCES;
return (-1);
}
lseek(fsp(f)->fd, off, 0);
if ((n = write(fsp(f)->fd, buf, len)) < 0)
fserrno = errno;
else if (off + len > f->size)
f->size = off + len; /* should stat, but too slow */
return (n);
}
/*
* turn a client st_dev into an rf minor device
* exactly eight bits allowed
*/
#define MAXDEVS 255
int maxdev = 0;
dev_t devtab[MAXDEVS];
static int
clientdev(d)
register dev_t d;
{
register int i;
for (i = 0; i < maxdev; i++)
if (devtab[i] == d)
return (i);
if (i >= MAXDEVS) {
rflog("too many devs, can't map %x\n", d);
return (MAXDEVS);
}
devtab[i] = d;
maxdev = i + 1;
return (i);
}
/*
* open a file as broadly as possible
* to avoid `text busy,' don't open for write
* unless specifically requested
* or unless file has no execute permissions (hack)
* directories usually have execute permissions (further hack)
*/
#define ANYEXEC ((RFPEX<<RFPOWNER)|(RFPEX<<RFPGROUP)|(RFPEX<<RFPOTHER))
static int
maxopen(f, mustwrite)
register Rfile *f;
int mustwrite;
{
register Fsfile *fs;
int fd;
struct stat st;
register int flags;
fd = -1;
fs = fsp(f);
flags = fs->flags;
if ((flags & (WONTREAD|WONTWRITE)) == 0)
return;
if ((flags & (CANTREAD|CANTWRITE)) == 0
&& mustwrite || (f->mode & ANYEXEC) == 0) {
if ((fd = open(fs->name, 2)) >= 0)
flags &=~ (WONTREAD|WONTWRITE);
}
if ((flags & (CANTREAD|WONTREAD)) == WONTREAD) {
flags &=~ WONTREAD;
if ((fd = open(fs->name, 0)) < 0)
flags |= CANTREAD;
}
if (mustwrite && (flags & (CANTWRITE|WONTWRITE)) == WONTWRITE) {
flags &=~ WONTWRITE;
if ((fd = open(fs->name, 1)) < 0)
flags |= CANTWRITE;
}
if (fd >= 0) { /* if we opened something */
if ((flags & NOSTAT) == 0) {
if (fstat(fd, &st) < 0) {
rflog("maxopen: fstat %s err %d\n", fs->name, errno);
close(fd);
return; /* flags and fd unchanged */
} else if (clientdev(st.st_dev) != f->dev || st.st_ino != f->ino) {
rflog("maxopen: wrong file %s\n", fs->name);
close(fd);
return; /* flags and fd unchanged */
}
}
/* it's the same file, or we can't tell */
fs->flags = flags;
if (fs->fd >= 0)
close(fs->fd);
fs->fd = fd;
}
}
/*
* is this the same file we opened?
* call before operations that must use name rather than fd
*/
static int
samefile(f)
register Rfile *f;
{
struct stat st;
if (fsp(f)->flags & NOSTAT)
return (1); /* can't tell */
if (stat(fsp(f)->name, &st) < 0)
return (0);
if (clientdev(st.st_dev) != f->dev || st.st_ino != f->ino)
return (0);
return (1);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.