aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/tapefs/fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/tapefs/fs.c')
-rw-r--r--src/cmd/tapefs/fs.c600
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");
+}