606 lines
9.7 KiB
C
606 lines
9.7 KiB
C
|
#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 blocksize; /* for 32v */
|
||
|
int verbose;
|
||
|
int newtap; /* tap with time in sec */
|
||
|
int blocksize;
|
||
|
|
||
|
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 = EARGF(usage());
|
||
|
break;
|
||
|
case 'p': /* password file */
|
||
|
uidmap = getpass(EARGF(usage()));
|
||
|
break;
|
||
|
case 'g': /* group file */
|
||
|
gidmap = getpass(EARGF(usage()));
|
||
|
break;
|
||
|
case 'v':
|
||
|
verbose++;
|
||
|
break;
|
||
|
case 'n':
|
||
|
newtap++;
|
||
|
break;
|
||
|
case 'b':
|
||
|
blocksize = atoi(EARGF(usage()));
|
||
|
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)
|
||
|
return Eperm;
|
||
|
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 0;
|
||
|
}
|
||
|
|
||
|
char *
|
||
|
rremove(Fid *f)
|
||
|
{
|
||
|
USED(f);
|
||
|
return Eperm;
|
||
|
}
|
||
|
|
||
|
char *
|
||
|
rstat(Fid *f)
|
||
|
{
|
||
|
if(f->ram->busy == 0)
|
||
|
return Enotexist;
|
||
|
thdr.nstat = ramstat(f->ram, thdr.stat, messagesize-IOHDRSZ);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
char *
|
||
|
rwstat(Fid *f)
|
||
|
{
|
||
|
if(f->ram->busy == 0)
|
||
|
return Enotexist;
|
||
|
return Eperm;
|
||
|
}
|
||
|
|
||
|
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' || strstr(buf, "hungup"))
|
||
|
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");
|
||
|
}
|