diff options
Diffstat (limited to 'src/cmd/tapefs/fs.c')
-rw-r--r-- | src/cmd/tapefs/fs.c | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/src/cmd/tapefs/fs.c b/src/cmd/tapefs/fs.c new file mode 100644 index 00000000..66abe366 --- /dev/null +++ b/src/cmd/tapefs/fs.c @@ -0,0 +1,600 @@ +#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 */ + +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(post9pservice(p[1], defmnt) < 0) { + sprint(buf, "post 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"); +} |