#include <u.h>
#include <libc.h>
#include <authsrv.h>
#include <fcall.h>
#include <tapefs.h>
Fid *fids;
Ram *ram;
int mfd[2];
char *user;
uchar mdata[Maxbuf+IOHDRSZ];
int messagesize = Maxbuf+IOHDRSZ;
Fcall rhdr;
Fcall thdr;
ulong path;
Idmap *uidmap;
Idmap *gidmap;
int replete;
int verbose;
int newtap; /* tap with time in sec */
uchar statbuf[STATMAX];
Fid * newfid(int);
int ramstat(Ram*, uchar*, int);
void io(void);
void usage(void);
int perm(int);
char *rflush(Fid*), *rversion(Fid*), *rauth(Fid*),
*rattach(Fid*), *rwalk(Fid*),
*ropen(Fid*), *rcreate(Fid*),
*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
char *(*fcalls[])(Fid*) = {
[Tflush] rflush,
[Tversion] rversion,
[Tauth] rauth,
[Tattach] rattach,
[Twalk] rwalk,
[Topen] ropen,
[Tcreate] rcreate,
[Tread] rread,
[Twrite] rwrite,
[Tclunk] rclunk,
[Tremove] rremove,
[Tstat] rstat,
[Twstat] rwstat,
};
char Eperm[] = "permission denied";
char Enotdir[] = "not a directory";
char Enoauth[] = "tapefs: authentication not required";
char Enotexist[] = "file does not exist";
char Einuse[] = "file in use";
char Eexist[] = "file exists";
char Enotowner[] = "not owner";
char Eisopen[] = "file already open for I/O";
char Excl[] = "exclusive use file already open";
char Ename[] = "illegal name";
void
notifyf(void *a, char *s)
{
USED(a);
if(strncmp(s, "interrupt", 9) == 0)
noted(NCONT);
noted(NDFLT);
}
void
main(int argc, char *argv[])
{
Ram *r;
char *defmnt;
int p[2];
char buf[TICKREQLEN];
fmtinstall('F', fcallfmt);
defmnt = "/n/tapefs";
ARGBEGIN{
case 'm':
defmnt = ARGF();
break;
case 'p': /* password file */
uidmap = getpass(ARGF());
break;
case 'g': /* group file */
gidmap = getpass(ARGF());
break;
case 'v':
verbose++;
case 'n':
newtap++;
break;
default:
usage();
}ARGEND
if (argc==0)
error("no file to mount");
user = getuser();
if(user == nil)
user = "dmr";
ram = r = (Ram *)emalloc(sizeof(Ram));
r->busy = 1;
r->data = 0;
r->ndata = 0;
r->perm = DMDIR | 0775;
r->qid.path = 0;
r->qid.vers = 0;
r->qid.type = QTDIR;
r->parent = 0;
r->child = 0;
r->next = 0;
r->user = user;
r->group = user;
r->atime = time(0);
r->mtime = r->atime;
r->replete = 0;
r->name = estrdup(".");
populate(argv[0]);
r->replete |= replete;
if(pipe(p) < 0)
error("pipe failed");
mfd[0] = mfd[1] = p[0];
notify(notifyf);
switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
case -1:
error("fork");
case 0:
close(p[1]);
notify(notifyf);
io();
break;
default:
close(p[0]); /* don't deadlock if child fails */
if(mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0) {
sprint(buf, "mount on `%s' failed", defmnt);
error(buf);
}
}
exits(0);
}
char*
rversion(Fid *unused)
{
Fid *f;
USED(unused);
if(rhdr.msize < 256)
return "version: message too small";
if(rhdr.msize > messagesize)
rhdr.msize = messagesize;
else
messagesize = rhdr.msize;
thdr.msize = messagesize;
if(strncmp(rhdr.version, "9P2000", 6) != 0)
return "unrecognized 9P version";
thdr.version = "9P2000";
for(f = fids; f; f = f->next)
if(f->busy)
rclunk(f);
return 0;
}
char*
rauth(Fid *unused)
{
USED(unused);
return Enoauth;
}
char*
rflush(Fid *f)
{
USED(f);
return 0;
}
char*
rattach(Fid *f)
{
/* no authentication! */
f->busy = 1;
f->rclose = 0;
f->ram = ram;
thdr.qid = f->ram->qid;
if(rhdr.uname[0])
f->user = strdup(rhdr.uname);
else
f->user = "none";
return 0;
}
char*
rwalk(Fid *f)
{
Fid *nf;
Ram *r;
char *err;
char *name;
Ram *dir;
int i;
nf = nil;
if(f->ram->busy == 0)
return Enotexist;
if(f->open)
return Eisopen;
if(rhdr.newfid != rhdr.fid){
nf = newfid(rhdr.newfid);
nf->busy = 1;
nf->open = 0;
nf->rclose = 0;
nf->ram = f->ram;
nf->user = f->user; /* no ref count; the leakage is minor */
f = nf;
}
thdr.nwqid = 0;
err = nil;
r = f->ram;
if(rhdr.nwname > 0){
for(i=0; i<rhdr.nwname; i++){
if((r->qid.type & QTDIR) == 0){
err = Enotdir;
break;
}
if(r->busy == 0){
err = Enotexist;
break;
}
r->atime = time(0);
name = rhdr.wname[i];
dir = r;
if(!perm(Pexec)){
err = Eperm;
break;
}
if(strcmp(name, "..") == 0){
r = dir->parent;
Accept:
if(i == MAXWELEM){
err = "name too long";
break;
}
thdr.wqid[thdr.nwqid++] = r->qid;
continue;
}
if (!dir->replete)
popdir(dir);
for(r=dir->child; r; r=r->next)
if(r->busy && strcmp(name, r->name)==0)
goto Accept;
break; /* file not found */
}
if(i==0 && err == nil)
err = Enotexist;
}
if(err!=nil || thdr.nwqid<rhdr.nwname){
if(nf){
nf->busy = 0;
nf->open = 0;
nf->ram = 0;
}
}else if(thdr.nwqid == rhdr.nwname)
f->ram = r;
return err;
}
char *
ropen(Fid *f)
{
Ram *r;
int mode, trunc;
if(f->open)
return Eisopen;
r = f->ram;
if(r->busy == 0)
return Enotexist;
if(r->perm & DMEXCL)
if(r->open)
return Excl;
mode = rhdr.mode;
if(r->qid.type & QTDIR){
if(mode != OREAD)
return Eperm;
thdr.qid = r->qid;
return 0;
}
if(mode & ORCLOSE){
if(r->qid.path==0)
return Eperm;
f->rclose = 1;
}
trunc = mode & OTRUNC;
mode &= OPERM;
if(mode==OWRITE || mode==ORDWR || trunc)
if(!perm(Pwrite))
return Eperm;
if(mode==OREAD || mode==ORDWR)
if(!perm(Pread))
return Eperm;
if(mode==OEXEC)
if(!perm(Pexec))
return Eperm;
if(trunc && (r->perm&DMAPPEND)==0){
r->ndata = 0;
dotrunc(r);
r->qid.vers++;
}
thdr.qid = r->qid;
thdr.iounit = messagesize-IOHDRSZ;
f->open = 1;
r->open++;
return 0;
}
char *
rcreate(Fid *f)
{
USED(f);
return Eperm;
}
char*
rread(Fid *f)
{
int i, len;
Ram *r;
char *buf;
uvlong off, end;
int n, cnt;
if(f->ram->busy == 0)
return Enotexist;
n = 0;
thdr.count = 0;
off = rhdr.offset;
end = rhdr.offset + rhdr.count;
cnt = rhdr.count;
if(cnt > messagesize-IOHDRSZ)
cnt = messagesize-IOHDRSZ;
buf = thdr.data;
if(f->ram->qid.type & QTDIR){
if (!f->ram->replete)
popdir(f->ram);
for(i=0,r=f->ram->child; r!=nil && i<end; r=r->next){
if(!r->busy)
continue;
len = ramstat(r, (uchar*)buf+n, cnt-n);
if(len <= BIT16SZ)
break;
if(i >= off)
n += len;
i += len;
}
thdr.count = n;
return 0;
}
r = f->ram;
if(off >= r->ndata)
return 0;
r->atime = time(0);
n = cnt;
if(off+n > r->ndata)
n = r->ndata - off;
thdr.data = doread(r, off, n);
thdr.count = n;
return 0;
}
char*
rwrite(Fid *f)
{
Ram *r;
ulong off;
int cnt;
r = f->ram;
if (dopermw(f->ram)==0)
return Eperm;
if(r->busy == 0)
return Enotexist;
off = rhdr.offset;
if(r->perm & DMAPPEND)
off = r->ndata;
cnt = rhdr.count;
if(r->qid.type & QTDIR)
return "file is a directory";
if(off > 100*1024*1024) /* sanity check */
return "write too big";
dowrite(r, rhdr.data, off, cnt);
r->qid.vers++;
r->mtime = time(0);
thdr.count = cnt;
return 0;
}
char *
rclunk(Fid *f)
{
if(f->open)
f->ram->open--;
f->busy = 0;
f->open = 0;
f->ram = 0;
return nil;
}
char *
rremove(Fid *f)
{
if(f->ram->qid.path == 0)
return Eperm;
f->ram->busy = 0;
return nil;
}
char *
rstat(Fid *f)
{
if(f->ram->busy == 0)
return Enotexist;
thdr.nstat = ramstat(f->ram, thdr.stat, messagesize-IOHDRSZ);
return 0;
}
static Ram *
firstent(Ram *r)
{
Ram *s, *t;
for(s = r->parent; s; s = s->next)
if (s->child)
for (t = s->child; t; t = t->next)
if (t == r)
return s->child;
sysfatal("firstent: internal error - can't ever happen\n");
return nil;
}
char *
rwstat(Fid *f)
{
Ram *r, *s;
Dir dir;
if(f->ram->busy == 0)
return Enotexist;
convM2D(rhdr.stat, rhdr.nstat, &dir, (char*)statbuf);
r = f->ram;
if(dir.length!=~0 && dir.length!=r->ndata)
return Eperm;
if (dir.name[0] != '\0' && strcmp(dir.name, r->name) != 0)
for(s = firstent(f->ram); s; s = s->next)
if(s->busy && strcmp(dir.name, s->name)==0)
return Eexist;
/* all valid, now do it */
if(dir.mode != ~0){
dir.mode &= ~DMDIR; /* cannot change dir bit */
dir.mode |= r->perm&DMDIR;
r->perm = dir.mode;
}
if(dir.name[0] != '\0'){
free(r->name);
r->name = estrdup(dir.name);
}
if(dir.uid[0] != '\0')
r->user = estrdup(dir.uid);
if(dir.gid[0] != '\0')
r->group = estrdup(dir.gid);
return 0;
}
int
ramstat(Ram *r, uchar *buf, int nbuf)
{
Dir dir;
dir.name = r->name;
dir.qid = r->qid;
dir.mode = r->perm;
dir.length = r->ndata;
dir.uid = r->user;
dir.gid = r->group;
dir.muid = r->user;
dir.atime = r->atime;
dir.mtime = r->mtime;
return convD2M(&dir, buf, nbuf);
}
Fid *
newfid(int fid)
{
Fid *f, *ff;
ff = 0;
for(f = fids; f; f = f->next)
if(f->fid == fid)
return f;
else if(!ff && !f->busy)
ff = f;
if(ff){
ff->fid = fid;
ff->open = 0;
ff->busy = 1;
}
f = emalloc(sizeof *f);
f->ram = 0;
f->fid = fid;
f->busy = 1;
f->open = 0;
f->next = fids;
fids = f;
return f;
}
void
io(void)
{
char *err;
int n, nerr;
char buf[ERRMAX];
errstr(buf, sizeof buf);
for(nerr=0, buf[0]='\0'; nerr<100; nerr++){
/*
* reading from a pipe or a network device
* will give an error after a few eof reads
* however, we cannot tell the difference
* between a zero-length read and an interrupt
* on the processes writing to us,
* so we wait for the error
*/
n = read9pmsg(mfd[0], mdata, sizeof mdata);
if (n==0)
continue;
if(n < 0){
if (buf[0]=='\0')
errstr(buf, sizeof buf);
continue;
}
nerr = 0;
buf[0] = '\0';
if(convM2S(mdata, n, &rhdr) != n)
error("convert error in convM2S");
if(verbose)
fprint(2, "tapefs: <=%F\n", &rhdr);/**/
thdr.data = (char*)mdata + IOHDRSZ;
thdr.stat = mdata + IOHDRSZ;
if(!fcalls[rhdr.type])
err = "bad fcall type";
else
err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
if(err){
thdr.type = Rerror;
thdr.ename = err;
}else{
thdr.type = rhdr.type + 1;
thdr.fid = rhdr.fid;
}
thdr.tag = rhdr.tag;
n = convS2M(&thdr, mdata, messagesize);
if(n <= 0)
error("convert error in convS2M");
if(verbose)
fprint(2, "tapefs: =>%F\n", &thdr);/**/
if(write(mfd[1], mdata, n) != n)
error("mount write");
}
if (buf[0]=='\0' || strncmp(buf, "write to hung", 13)==0)
exits("");
fprint(2, "%s: mount read: %s\n", argv0, buf);
exits(buf);
}
int
perm(int p)
{
if (p==Pwrite)
return 0;
return 1;
}
void
error(char *s)
{
fprint(2, "%s: %s: ", argv0, s);
perror("");
exits(s);
}
char*
estrdup(char *s)
{
char *t;
t = emalloc(strlen(s)+1);
strcpy(t, s);
return t;
}
void *
emalloc(ulong n)
{
void *p;
p = mallocz(n, 1);
if(!p)
error("out of memory");
return p;
}
void *
erealloc(void *p, ulong n)
{
p = realloc(p, n);
if(!p)
error("out of memory");
return p;
}
void
usage(void)
{
fprint(2, "usage: %s [-s] [-m mountpoint]\n", argv0);
exits("usage");
}
|