]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/ramfs.c
/sys/src/cmd/ndb/dns.h:
[plan9front.git] / sys / src / cmd / ramfs.c
index 835a94bf9f74dc46b1e3fb145e76cf7ffb0fea88..85bbe9e318ab6b1fd0aa50648885081bcbb3012b 100644 (file)
 #include <libc.h>
 #include <auth.h>
 #include <fcall.h>
-
-/*
- * Rather than reading /adm/users, which is a lot of work for
- * a toy program, we assume all groups have the form
- *     NNN:user:user:
- * meaning that each user is the leader of his own group.
- */
-
-enum
-{
-       OPERM   = 0x3,          /* mask of all permission types in open mode */
-       Nram    = 2048,
-       Maxsize = 768*1024*1024,
-       Maxfdata        = 8192,
-       Maxulong= (1ULL << 32) - 1,
+#include <thread.h>
+#include <9p.h>
+#include <pool.h>
+
+char Ebadoff[] = "bad file offset or count";
+char Eexist[] = "file already exists";
+char Enomem[] = "no memory";
+char Eperm[] = "permission denied";
+char Enotowner[] = "not owner";
+char Elocked[] = "file is locked";
+
+enum {
+       Tdat    = 0xbabababa,   
+       Tind    = 0xdadadada,
+
+       ESIZE   = 64*1024,
 };
 
-typedef struct Fid Fid;
-typedef struct Ram Ram;
-
-struct Fid
-{
-       short   busy;
-       short   open;
-       short   rclose;
-       int     fid;
-       Fid     *next;
-       char    *user;
-       Ram     *ram;
-};
+#define MAXFSIZE ((0x7fffffffll/sizeof(Ram*))*ESIZE)
 
+typedef struct Ram Ram;
 struct Ram
 {
-       short   busy;
-       short   open;
-       long    parent;         /* index in Ram array */
-       Qid     qid;
-       long    perm;
-       char    *name;
-       ulong   atime;
-       ulong   mtime;
-       char    *user;
-       char    *group;
-       char    *muid;
-       char    *data;
-       long    ndata;
-};
-
-enum
-{
-       Pexec =         1,
-       Pwrite =        2,
-       Pread =         4,
-       Pother =        1,
-       Pgroup =        8,
-       Powner =        64,
+       int     type;
+       int     size;
+       Ram     **link;
+       Ram     *ent[];
 };
 
-ulong  path;           /* incremented for each new file */
-Fid    *fids;
-Ram    ram[Nram];
-int    nram;
-int    mfd[2];
-char   *user;
-uchar  mdata[IOHDRSZ+Maxfdata];
-uchar  rdata[Maxfdata];        /* buffer for data in reply */
-uchar statbuf[STATMAX];
-Fcall thdr;
-Fcall  rhdr;
-int    messagesize = sizeof mdata;
-
-Fid *  newfid(int);
-uint   ramstat(Ram*, uchar*, uint);
-void   error(char*);
-void   io(void);
-void   *erealloc(void*, ulong);
-void   *emalloc(ulong);
-char   *estrdup(char*);
-void   usage(void);
-int    perm(Fid*, Ram*, 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*);
-
-int needfid[] = {
-       [Tversion] 0,
-       [Tflush] 0,
-       [Tauth] 0,
-       [Tattach] 0,
-       [Twalk] 1,
-       [Topen] 1,
-       [Tcreate] 1,
-       [Tread] 1,
-       [Twrite] 1,
-       [Tclunk] 1,
-       [Tremove] 1,
-       [Tstat] 1,
-       [Twstat] 1,
-};
-
-char   *(*fcalls[])(Fid*) = {
-       [Tversion]      rversion,
-       [Tflush]        rflush,
-       [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[] =     "ramfs: authentication not required";
-char   Enotexist[] =   "file does not exist";
-char   Einuse[] =      "file in use";
-char   Eexist[] =      "file exists";
-char   Eisdir[] =      "file is a directory";
-char   Enotowner[] =   "not owner";
-char   Eisopen[] =     "file already open for I/O";
-char   Excl[] =        "exclusive use file already open";
-char   Ename[] =       "illegal name";
-char   Eversion[] =    "unknown 9P version";
-char   Enotempty[] =   "directory not empty";
-
-int debug;
 int private;
 
-static int memlim = 1;
-
-void
-notifyf(void *a, char *s)
+void*
+ramalloc(ulong size)
 {
-       USED(a);
-       if(strncmp(s, "interrupt", 9) == 0)
-               noted(NCONT);
-       noted(NDFLT);
+       void *v;
+
+       v = sbrk(size);
+       if(v == (void*)-1)
+               return nil;
+       return v;
 }
 
 void
-main(int argc, char *argv[])
+rammoved(void*, void *to)
 {
-       Ram *r;
-       char *defmnt, *service;
-       int p[2];
-       int fd;
-       int stdio = 0;
-
-       service = "ramfs";
-       defmnt = "/tmp";
-       ARGBEGIN{
-       case 'i':
-               defmnt = 0;
-               stdio = 1;
-               mfd[0] = 0;
-               mfd[1] = 1;
-               break;
-       case 'm':
-               defmnt = EARGF(usage());
-               break;
-       case 'p':
-               private++;
-               break;
-       case 's':
-               defmnt = 0;
-               break;
-       case 'u':
-               memlim = 0;             /* unlimited memory consumption */
-               break;
-       case 'D':
-               debug = 1;
-               break;
-       case 'S':
-               defmnt = 0;
-               service = EARGF(usage());
-               break;
-       default:
-               usage();
-       }ARGEND
-
-       if(pipe(p) < 0)
-               error("pipe failed");
-       if(!stdio){
-               mfd[0] = p[0];
-               mfd[1] = p[0];
-               if(defmnt == 0){
-                       char buf[64];
-                       snprint(buf, sizeof buf, "#s/%s", service);
-                       fd = create(buf, OWRITE|ORCLOSE, 0666);
-                       if(fd < 0)
-                               error("create failed");
-                       sprint(buf, "%d", p[1]);
-                       if(write(fd, buf, strlen(buf)) < 0)
-                               error("writing service file");
-               }
-       }
+       Ram **link, **elink, *x = to;
 
-       user = getuser();
-       notify(notifyf);
-       nram = 1;
-       r = &ram[0];
-       r->busy = 1;
-       r->data = 0;
-       r->ndata = 0;
-       r->perm = DMDIR | 0775;
-       r->qid.type = QTDIR;
-       r->qid.path = 0LL;
-       r->qid.vers = 0;
-       r->parent = 0;
-       r->user = user;
-       r->group = user;
-       r->muid = user;
-       r->atime = time(0);
-       r->mtime = r->atime;
-       r->name = estrdup(".");
-
-       if(debug) {
-               fmtinstall('F', fcallfmt);
-               fmtinstall('M', dirmodefmt);
-       }
-       switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
-       case -1:
-               error("fork");
-       case 0:
-               close(p[1]);
-               io();
-               break;
-       default:
-               close(p[0]);    /* don't deadlock if child fails */
-               if(defmnt && mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0)
-                       error("mount failed");
-       }
-       exits(0);
-}
-
-char*
-rversion(Fid*)
-{
-       Fid *f;
+       *x->link = x;
+       if(x->type != Tind)
+               return;
 
-       for(f = fids; f; f = f->next)
-               if(f->busy)
-                       rclunk(f);
-       if(thdr.msize > sizeof mdata)
-               rhdr.msize = sizeof mdata;
-       else
-               rhdr.msize = thdr.msize;
-       messagesize = rhdr.msize;
-       if(strncmp(thdr.version, "9P2000", 6) != 0)
-               return Eversion;
-       rhdr.version = "9P2000";
-       return 0;
+       link = x->ent;
+       for(elink = link + (x->size / sizeof(Ram*)); link < elink; link++)
+               if((x = *link) != nil)
+                       x->link = link;
 }
 
-char*
-rauth(Fid*)
+void
+ramnolock(Pool*)
 {
-       return "ramfs: no authentication required";
 }
 
-char*
-rflush(Fid *f)
-{
-       USED(f);
-       return 0;
-}
+Pool rampool = {
+        .name=          "ram",
+        .maxsize=       800*1024*1024,
+        .minarena=      4*1024,
+        .quantum=       32,
+        .alloc=         ramalloc,
+       .move=          rammoved,
+       .lock=          ramnolock,
+       .unlock=        ramnolock,
+        .flags=         0,
+};
 
-char*
-rattach(Fid *f)
+void
+accessfile(File *f, int a)
 {
-       /* no authentication! */
-       f->busy = 1;
-       f->rclose = 0;
-       f->ram = &ram[0];
-       rhdr.qid = f->ram->qid;
-       if(thdr.uname[0])
-               f->user = estrdup(thdr.uname);
-       else
-               f->user = "none";
-       if(strcmp(user, "none") == 0)
-               user = f->user;
-       return 0;
+       f->atime = time(0);
+       if(a & AWRITE){
+               f->mtime = f->atime;
+               f->qid.vers++;
+       }
 }
 
-char*
-clone(Fid *f, Fid **nf)
+void
+fsread(Req *r)
 {
-       if(f->open)
-               return Eisopen;
-       if(f->ram->busy == 0)
-               return Enotexist;
-       *nf = newfid(thdr.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 */
-       return 0;
-}
+       int o, n, i, count;
+       vlong top, off;
+       File *f;
+       Ram *x;
+       char *p;
 
-char*
-rwalk(Fid *f)
-{
-       Ram *r, *fram;
-       char *name;
-       Ram *parent;
-       Fid *nf;
-       char *err;
-       ulong t;
-       int i;
-
-       err = nil;
-       nf = nil;
-       rhdr.nwqid = 0;
-       if(thdr.newfid != thdr.fid){
-               err = clone(f, &nf);
-               if(err)
-                       return err;
-               f = nf; /* walk the new fid */
-       }
-       fram = f->ram;
-       if(thdr.nwname > 0){
-               t = time(0);
-               for(i=0; i<thdr.nwname && i<MAXWELEM; i++){
-                       if((fram->qid.type & QTDIR) == 0){
-                               err = Enotdir;
-                               break;
-                       }
-                       if(fram->busy == 0){
-                               err = Enotexist;
-                               break;
-                       }
-                       fram->atime = t;
-                       name = thdr.wname[i];
-                       if(strcmp(name, ".") == 0){
-    Found:
-                               rhdr.nwqid++;
-                               rhdr.wqid[i] = fram->qid;
-                               continue;
-                       }
-                       parent = &ram[fram->parent];
-                       if(!perm(f, parent, Pexec)){
-                               err = Eperm;
-                               break;
-                       }
-                       if(strcmp(name, "..") == 0){
-                               fram = parent;
-                               goto Found;
-                       }
-                       for(r=ram; r < &ram[nram]; r++)
-                               if(r->busy && r->parent==fram-ram && strcmp(name, r->name)==0){
-                                       fram = r;
-                                       goto Found;
-                               }
-                       break;
-               }
-               if(i==0 && err == nil)
-                       err = Enotexist;
-       }
-       if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){
-               /* clunk the new fid, which is the one we walked */
-               f->busy = 0;
-               f->ram = nil;
+       f = r->fid->file;
+       off = r->ifcall.offset;
+       count = r->ifcall.count;
+
+       if(count == 0 || off >= f->length || f->aux == nil){
+               r->ofcall.count = 0;
+               respond(r, nil);
+               return;
        }
-       if(rhdr.nwqid > 0)
-               err = nil;      /* didn't get everything in 9P2000 right! */
-       if(rhdr.nwqid == thdr.nwname)   /* update the fid after a successful walk */
-               f->ram = fram;
-       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 = thdr.mode;
-       if(r->qid.type & QTDIR){
-               if(mode != OREAD)
-                       return Eperm;
-               rhdr.qid = r->qid;
-               return 0;
+       top = off + count;
+       if(top > MAXFSIZE){
+               respond(r, Ebadoff);
+               return;
        }
-       if(mode & ORCLOSE){
-               /* can't remove root; must be able to write parent */
-               if(r->qid.path==0 || !perm(f, &ram[r->parent], Pwrite))
-                       return Eperm;
-               f->rclose = 1;
+               
+       if(top > f->length){
+               top = f->length;
+               count = top - off;
        }
-       trunc = mode & OTRUNC;
-       mode &= OPERM;
-       if(mode==OWRITE || mode==ORDWR || trunc)
-               if(!perm(f, r, Pwrite))
-                       return Eperm;
-       if(mode==OREAD || mode==ORDWR)
-               if(!perm(f, r, Pread))
-                       return Eperm;
-       if(mode==OEXEC)
-               if(!perm(f, r, Pexec))
-                       return Eperm;
-       if(trunc && (r->perm&DMAPPEND)==0){
-               r->ndata = 0;
-               if(r->data)
-                       free(r->data);
-               r->data = 0;
-               r->qid.vers++;
+       p = (char*)r->ofcall.data;
+       while(count > 0){
+               i = off / ESIZE;
+               o = off % ESIZE;
+
+               x = (Ram*)f->aux;
+               if(i < (x->size / sizeof(Ram*)))
+                       x = x->ent[i];
+               else
+                       x = nil;
+               if(x != nil && o < x->size){
+                       n = x->size - o;
+                       if(n > count)
+                               n = count;
+                       memmove(p, (char*)&x[1] + o, n);
+               } else {
+                       n = ESIZE - o;
+                       if(n > count)
+                               n = count;
+                       memset(p, 0, n);
+               }
+               p += n;
+               off += n;
+               count -= n;
        }
-       rhdr.qid = r->qid;
-       rhdr.iounit = messagesize-IOHDRSZ;
-       f->open = 1;
-       r->open++;
-       return 0;
-}
+       accessfile(f, AREAD);
 
-char *
-rcreate(Fid *f)
-{
-       Ram *r;
-       char *name;
-       long parent, prm;
-
-       if(f->open)
-               return Eisopen;
-       if(f->ram->busy == 0)
-               return Enotexist;
-       parent = f->ram - ram;
-       if((f->ram->qid.type&QTDIR) == 0)
-               return Enotdir;
-       /* must be able to write parent */
-       if(!perm(f, f->ram, Pwrite))
-               return Eperm;
-       prm = thdr.perm;
-       name = thdr.name;
-       if(strcmp(name, ".")==0 || strcmp(name, "..")==0)
-               return Ename;
-       for(r=ram; r<&ram[nram]; r++)
-               if(r->busy && parent==r->parent)
-               if(strcmp((char*)name, r->name)==0)
-                       return Einuse;
-       for(r=ram; r->busy; r++)
-               if(r == &ram[Nram-1])
-                       return "no free ram resources";
-       r->busy = 1;
-       r->qid.path = ++path;
-       r->qid.vers = 0;
-       if(prm & DMDIR)
-               r->qid.type |= QTDIR;
-       r->parent = parent;
-       free(r->name);
-       r->name = estrdup(name);
-       r->user = f->user;
-       r->group = f->ram->group;
-       r->muid = f->ram->muid;
-       if(prm & DMDIR)
-               prm = (prm&~0777) | (f->ram->perm&prm&0777);
-       else
-               prm = (prm&(~0777|0111)) | (f->ram->perm&prm&0666);
-       r->perm = prm;
-       r->ndata = 0;
-       if(r-ram >= nram)
-               nram = r - ram + 1;
-       r->atime = time(0);
-       r->mtime = r->atime;
-       f->ram->mtime = r->atime;
-       f->ram = r;
-       rhdr.qid = r->qid;
-       rhdr.iounit = messagesize-IOHDRSZ;
-       f->open = 1;
-       if(thdr.mode & ORCLOSE)
-               f->rclose = 1;
-       r->open++;
-       return 0;
+       r->ofcall.count = p - (char*)r->ofcall.data;
+       respond(r, nil);
 }
 
-char*
-rread(Fid *f)
+void
+fswrite(Req *r)
 {
-       Ram *r;
-       uchar *buf;
-       vlong off;
-       int n, m, cnt;
-
-       if(f->ram->busy == 0)
-               return Enotexist;
-       n = 0;
-       rhdr.count = 0;
-       rhdr.data = (char*)rdata;
-       if (thdr.offset < 0)
-               return "negative seek offset";
-       off = thdr.offset;
-       buf = rdata;
-       cnt = thdr.count;
-       if(cnt > messagesize)   /* shouldn't happen, anyway */
-               cnt = messagesize;
-       if(cnt < 0)
-               return "negative read count";
-       if(f->ram->qid.type & QTDIR){
-               for(r=ram+1; off > 0; r++){
-                       if(r->busy && r->parent==f->ram-ram)
-                               off -= ramstat(r, statbuf, sizeof statbuf);
-                       if(r == &ram[nram-1])
-                               return 0;
-               }
-               for(; r<&ram[nram] && n < cnt; r++){
-                       if(!r->busy || r->parent!=f->ram-ram)
-                               continue;
-                       m = ramstat(r, buf+n, cnt-n);
-                       if(m == 0)
-                               break;
-                       n += m;
-               }
-               rhdr.data = (char*)rdata;
-               rhdr.count = n;
-               return 0;
+       int o, n, i, count;
+       Ram *x, **link;
+       vlong top, off;
+       File *f;
+       char *p;
+
+       f = r->fid->file;
+       off = r->ifcall.offset;
+       count = r->ifcall.count;
+
+       if(f->mode & DMAPPEND)
+               off = f->length;
+
+       if(count == 0){
+               r->ofcall.count = 0;
+               respond(r, nil);
+               return;
        }
-       r = f->ram;
-       if(off >= r->ndata)
-               return 0;
-       r->atime = time(0);
-       n = cnt;
-       if(off+n > r->ndata)
-               n = r->ndata - off;
-       rhdr.data = r->data+off;
-       rhdr.count = n;
-       return 0;
-}
 
-char*
-rwrite(Fid *f)
-{
-       Ram *r;
-       vlong off;
-       int cnt;
-
-       r = f->ram;
-       rhdr.count = 0;
-       if(r->busy == 0)
-               return Enotexist;
-       if (thdr.offset < 0)
-               return "negative seek offset";
-       off = thdr.offset;
-       if(r->perm & DMAPPEND)
-               off = r->ndata;
-       cnt = thdr.count;
-       if(cnt < 0)
-               return "negative write count";
-       if(r->qid.type & QTDIR)
-               return Eisdir;
-       if(memlim && off+cnt >= Maxsize)                /* sanity check */
-               return "write too big";
-       if(off+cnt > r->ndata)
-               r->data = erealloc(r->data, off+cnt);
-       if(off > r->ndata)
-               memset(r->data+r->ndata, 0, off-r->ndata);
-       if(off+cnt > r->ndata)
-               r->ndata = off+cnt;
-       memmove(r->data+off, thdr.data, cnt);
-       r->qid.vers++;
-       r->mtime = time(0);
-       rhdr.count = cnt;
-       return 0;
-}
+       top = off + count;
+       if(top > MAXFSIZE){
+               respond(r, Ebadoff);
+               return;
+       }
 
-static int
-emptydir(Ram *dr)
-{
-       long didx = dr - ram;
-       Ram *r;
+       n = ((top + ESIZE-1)/ESIZE) * sizeof(Ram*);
+       x = (Ram*)f->aux;
+       if(x == nil || x->size < n){
+               x = poolrealloc(&rampool, x, sizeof(Ram) + n);          
+               if(x == nil){
+                       respond(r, Enomem);
+                       return;
+               }
+               link = (Ram**)&f->aux;
+               if(*link == nil){
+                       memset(x, 0, sizeof(Ram));
+                       x->type = Tind;
+                       x->link = link;
+                       *link = x;
+               } else if(x != *link)
+                       rammoved(*link, x);
+               memset((char*)&x[1] + x->size, 0, n - x->size);
+               x->size = n;
+       }
 
-       for(r=ram; r<&ram[nram]; r++)
-               if(r->busy && didx==r->parent)
-                       return 0;
-       return 1;
-}
+       p = (char*)r->ifcall.data;
+       while(count > 0){
+               i = off / ESIZE;
+               o = off % ESIZE;
+
+               n = ESIZE - o;
+               if(n > count)
+                       n = count;
+
+               x = ((Ram*)f->aux)->ent[i];
+               if(x == nil || x->size < o+n){
+                       x = poolrealloc(&rampool, x, sizeof(Ram) + o+n);
+                       if(x == nil){
+                               respond(r, Enomem);
+                               return;
+                       }
+                       link = &((Ram*)f->aux)->ent[i];
+                       if(*link == nil){
+                               memset(x, 0, sizeof(Ram));
+                               x->type = Tdat;
+                       }
+                       if(o > x->size)
+                               memset((char*)&x[1] + x->size, 0, o - x->size);
+                       x->size = o + n;
+                       x->link = link;
+                       *link = x;
+               }
 
-char *
-realremove(Ram *r)
-{
-       if(r->qid.type & QTDIR && !emptydir(r))
-               return Enotempty;
-       r->ndata = 0;
-       if(r->data)
-               free(r->data);
-       r->data = 0;
-       r->parent = 0;
-       memset(&r->qid, 0, sizeof r->qid);
-       free(r->name);
-       r->name = nil;
-       r->busy = 0;
-       return nil;
-}
+               memmove((char*)&x[1] + o, p, n);
+               p += n;
+               off += n;
+               count -= n;
+       }
 
-char *
-rclunk(Fid *f)
-{
-       char *e = nil;
-
-       if(f->open)
-               f->ram->open--;
-       if(f->rclose)
-               e = realremove(f->ram);
-       f->busy = 0;
-       f->open = 0;
-       f->ram = 0;
-       return e;
-}
+       if(top > f->length)
+               f->length = top;
+       accessfile(f, AWRITE);
 
-char *
-rremove(Fid *f)
-{
-       Ram *r;
-
-       if(f->open)
-               f->ram->open--;
-       f->busy = 0;
-       f->open = 0;
-       r = f->ram;
-       f->ram = 0;
-       if(r->qid.path == 0 || !perm(f, &ram[r->parent], Pwrite))
-               return Eperm;
-       ram[r->parent].mtime = time(0);
-       return realremove(r);
+       r->ofcall.count = p - (char*)r->ifcall.data;
+       respond(r, nil);
 }
 
-char *
-rstat(Fid *f)
+void
+truncfile(File *f, vlong l)
 {
-       if(f->ram->busy == 0)
-               return Enotexist;
-       rhdr.nstat = ramstat(f->ram, statbuf, sizeof statbuf);
-       rhdr.stat = statbuf;
-       return 0;
+       int i, o, n;
+       Ram *x;
+
+       x = (Ram*)f->aux;
+       if(x != nil){
+               n = x->size / sizeof(Ram*);
+               i = l / ESIZE;
+               if(i < n){
+                       o = l % ESIZE;
+                       if(o != 0 && x->ent[i] != nil){
+                               x->ent[i]->size = o * sizeof(Ram*);
+                               i++;
+                       }
+                       while(i < n){
+                               if(x->ent[i] != nil){
+                                       poolfree(&rampool, x->ent[i]);
+                                       x->ent[i] = nil;
+                               }
+                               i++;
+                       }
+               }
+               if(l == 0){
+                       poolfree(&rampool, (Ram*)f->aux);
+                       f->aux = nil;
+               }
+       }
+       f->length = l;
 }
 
-char *
-rwstat(Fid *f)
+void
+fswstat(Req *r)
 {
-       Ram *r, *s;
-       Dir dir;
+       File *f, *w;
+       char *u;
 
-       if(f->ram->busy == 0)
-               return Enotexist;
-       convM2D(thdr.stat, thdr.nstat, &dir, (char*)statbuf);
-       r = f->ram;
+       f = r->fid->file;
+       u = r->fid->uid;
 
        /*
         * To change length, must have write permission on file.
         */
-       if(dir.length!=~0 && dir.length!=r->ndata){
-               if(!perm(f, r, Pwrite))
-                       return Eperm;
+       if(r->d.length != ~0 && r->d.length != f->length){
+               if(r->d.length > MAXFSIZE){
+                       respond(r, Ebadoff);
+                       return;
+               }
+               if(!hasperm(f, u, AWRITE) || (f->mode & DMDIR) != 0)
+                       goto Perm;
        }
 
        /*
-        * To change name, must have write permission in parent
-        * and name must be unique.
+        * To change name, must have write permission in parent.
         */
-       if(dir.name[0]!='\0' && strcmp(dir.name, r->name)!=0){
-               if(!perm(f, &ram[r->parent], Pwrite))
-                       return Eperm;
-               for(s=ram; s<&ram[nram]; s++)
-                       if(s->busy && s->parent==r->parent)
-                       if(strcmp(dir.name, s->name)==0)
-                               return Eexist;
+       if(r->d.name[0] != '\0' && strcmp(r->d.name, f->name) != 0){
+               if((w = f->parent) == nil)
+                       goto Perm;
+               incref(w);
+               if(!hasperm(w, u, AWRITE)){
+                       closefile(w);
+                       goto Perm;
+               }
+               if((w = walkfile(w, r->d.name)) != nil){
+                       closefile(w);
+                       respond(r, Eexist);
+                       return;
+               }
        }
 
        /*
         * To change mode, must be owner or group leader.
         * Because of lack of users file, leader=>group itself.
         */
-       if(dir.mode!=~0 && r->perm!=dir.mode){
-               if(strcmp(f->user, r->user) != 0)
-               if(strcmp(f->user, r->group) != 0)
-                       return Enotowner;
+       if(r->d.mode != ~0 && f->mode != r->d.mode){
+               if(strcmp(u, f->uid) != 0)
+               if(strcmp(u, f->gid) != 0){
+                       respond(r, Enotowner);
+                       return;
+               }
        }
 
        /*
@@ -700,208 +324,192 @@ rwstat(Fid *f)
         * or leader of current group and leader of new group.
         * Second case cannot happen, but we check anyway.
         */
-       if(dir.gid[0]!='\0' && strcmp(r->group, dir.gid)!=0){
-               if(strcmp(f->user, r->user) == 0)
-       //      if(strcmp(f->user, dir.gid) == 0)
-                       goto ok;
-               if(strcmp(f->user, r->group) == 0)
-               if(strcmp(f->user, dir.gid) == 0)
-                       goto ok;
-               return Enotowner;
-               ok:;
+       while(r->d.gid[0] != '\0' && strcmp(f->gid, r->d.gid) != 0){
+               if(strcmp(u, f->uid) == 0)
+                       break;
+               if(strcmp(u, f->gid) == 0)
+               if(strcmp(u, r->d.gid) == 0)
+                       break;
+               respond(r, Enotowner);
+               return;
        }
 
-       /* all ok; do it */
-       if(dir.mode != ~0){
-               dir.mode &= ~DMDIR;     /* cannot change dir bit */
-               dir.mode |= r->perm&DMDIR;
-               r->perm = dir.mode;
+       if(r->d.mode != ~0){
+               f->mode = r->d.mode;
+               f->qid.type = f->mode >> 24;
        }
-       if(dir.name[0] != '\0'){
-               free(r->name);
-               r->name = estrdup(dir.name);
+       if(r->d.name[0] != '\0'){
+               free(f->name);
+               f->name = estrdup9p(r->d.name);
        }
-       if(dir.gid[0] != '\0')
-               r->group = estrdup(dir.gid);
-       if(dir.length!=~0 && dir.length!=r->ndata){
-               r->data = erealloc(r->data, dir.length);
-               if(r->ndata < dir.length)
-                       memset(r->data+r->ndata, 0, dir.length-r->ndata);
-               r->ndata = dir.length;
+       if(r->d.length != ~0 && r->d.length != f->length)
+               truncfile(f, r->d.length);
+
+       accessfile(f, AWRITE);
+       if(r->d.mtime != ~0){
+               f->mtime = r->d.mtime;
        }
-       ram[r->parent].mtime = time(0);
-       return 0;
-}
 
-uint
-ramstat(Ram *r, uchar *buf, uint nbuf)
-{
-       int n;
-       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->muid;
-       dir.atime = r->atime;
-       dir.mtime = r->mtime;
-       n = convD2M(&dir, buf, nbuf);
-       if(n > 2)
-               return n;
-       return 0;
+       respond(r, nil);
+       return;
+
+Perm:
+       respond(r, Eperm);
 }
 
-Fid *
-newfid(int fid)
+void
+fscreate(Req *r)
 {
-       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;
-               return ff;
+       File *f;
+       int p;
+
+       f = r->fid->file;
+       p = r->ifcall.perm;
+       if((p & DMDIR) != 0)
+               p = (p & ~0777) | ((p & f->mode) & 0777);
+       else
+               p = (p & ~0666) | ((p & f->mode) & 0666);
+       if((f = createfile(f, r->ifcall.name, r->fid->uid, p, nil)) == nil){
+               responderror(r);
+               return;
        }
-       f = emalloc(sizeof *f);
-       f->ram = nil;
-       f->fid = fid;
-       f->next = fids;
-       fids = f;
-       return f;
+       f->atime = f->mtime = time(0);
+       f->aux = nil;
+       r->fid->file = f;
+       r->ofcall.qid = f->qid;
+       respond(r, nil);
 }
 
 void
-io(void)
+fsopen(Req *r)
 {
-       char *err, buf[40];
-       int n, pid, ctl;
-       Fid *fid;
+       File *f;
 
-       pid = getpid();
-       if(private){
-               snprint(buf, sizeof buf, "/proc/%d/ctl", pid);
-               ctl = open(buf, OWRITE);
-               if(ctl < 0){
-                       fprint(2, "can't protect ramfs\n");
-               }else{
-                       fprint(ctl, "noswap\n");
-                       fprint(ctl, "private\n");
-                       close(ctl);
+       f = r->fid->file;
+       if((f->mode & DMEXCL) != 0){
+               if(f->ref > 2 && (long)((ulong)time(0)-(ulong)f->atime) < 300){
+                       respond(r, Elocked);
+                       return;
                }
        }
-
-       for(;;){
-               /*
-                * 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, messagesize);
-               if(n < 0){
-                       rerrstr(buf, sizeof buf);
-                       if(buf[0]=='\0' || strstr(buf, "hungup"))
-                               exits("");
-                       error("mount read");
-               }
-               if(n == 0)
-                       continue;
-               if(convM2S(mdata, n, &thdr) == 0)
-                       continue;
-
-               if(debug)
-                       fprint(2, "ramfs %d:<-%F\n", pid, &thdr);
-
-               if(thdr.type<0 || thdr.type>=nelem(fcalls) || !fcalls[thdr.type])
-                       err = "bad fcall type";
-               else if(((fid=newfid(thdr.fid))==nil || !fid->ram) && needfid[thdr.type])
-                       err = "fid not in use";
-               else
-                       err = (*fcalls[thdr.type])(fid);
-               if(err){
-                       rhdr.type = Rerror;
-                       rhdr.ename = err;
-               }else{
-                       rhdr.type = thdr.type + 1;
-                       rhdr.fid = thdr.fid;
-               }
-               rhdr.tag = thdr.tag;
-               if(debug)
-                       fprint(2, "ramfs %d:->%F\n", pid, &rhdr);/**/
-               n = convS2M(&rhdr, mdata, messagesize);
-               if(n == 0)
-                       error("convS2M error on write");
-               if(write(mfd[1], mdata, n) != n)
-                       error("mount write");
+       if((f->mode & DMAPPEND) == 0 && (r->ifcall.mode & OTRUNC) != 0){
+               truncfile(f, 0);
+               accessfile(f, AWRITE);
        }
+       respond(r, nil);
 }
 
-int
-perm(Fid *f, Ram *r, int p)
+void
+fsdestroyfid(Fid *fid)
 {
-       if((p*Pother) & r->perm)
-               return 1;
-       if(strcmp(f->user, r->group)==0 && ((p*Pgroup) & r->perm))
-               return 1;
-       if(strcmp(f->user, r->user)==0 && ((p*Powner) & r->perm))
-               return 1;
-       return 0;
+       File *f;
+
+       f = fid->file;
+       if(fid->omode != -1 && (fid->omode & ORCLOSE) != 0 && f != nil && f->parent != nil)
+               removefile(f);
 }
 
 void
-error(char *s)
+fsdestroyfile(File *f)
 {
-       fprint(2, "%s: %s: %r\n", argv0, s);
-       exits(s);
+       truncfile(f, 0);
 }
 
-void *
-emalloc(ulong n)
+void
+fsstart(Srv *)
 {
-       void *p;
+       char buf[40];
+       int ctl;
 
-       p = malloc(n);
-       if(!p)
-               error("out of memory");
-       memset(p, 0, n);
-       return p;
+       if(private){
+               snprint(buf, sizeof buf, "/proc/%d/ctl", getpid());
+               if((ctl = open(buf, OWRITE)) < 0)
+                       sysfatal("can't protect memory: %r");
+               fprint(ctl, "noswap\n");
+               fprint(ctl, "private\n");
+               close(ctl);
+       }
 }
 
-void *
-erealloc(void *p, ulong n)
-{
-       p = realloc(p, n);
-       if(!p)
-               error("out of memory");
-       return p;
-}
+Srv fs = {
+       .open=          fsopen,
+       .read=          fsread,
+       .write=         fswrite,
+       .wstat=         fswstat,
+       .create=        fscreate,
+       .destroyfid=    fsdestroyfid,
 
-char *
-estrdup(char *q)
-{
-       char *p;
-       int n;
-
-       n = strlen(q)+1;
-       p = malloc(n);
-       if(!p)
-               error("out of memory");
-       memmove(p, q, n);
-       return p;
-}
+       .start=         fsstart,
+};
 
 void
 usage(void)
 {
-       fprint(2, "usage: %s [-Dipsu] [-m mountpoint] [-S srvname]\n", argv0);
+       fprint(2, "usage: %s [-Dipsubac] [-m mountpoint] [-S srvname]\n", argv0);
        exits("usage");
 }
+
+void
+main(int argc, char **argv)
+{
+       char *srvname = nil;
+       char *mtpt = "/tmp";
+       int mountflags, stdio;
+
+       fs.tree = alloctree(nil, nil, DMDIR|0777, fsdestroyfile);
+
+       mountflags = stdio = 0;
+       ARGBEGIN{
+       case 'D':
+               chatty9p++;
+               break;
+       case 's':
+               srvname = "ramfs";
+               mtpt = nil;
+               break;
+       case 'S':
+               srvname = EARGF(usage());
+               mtpt = nil;
+               break;
+       case 'm':
+               mtpt = EARGF(usage());
+               break;
+       case 'i':
+               stdio = 1;
+               break;
+       case 'p':
+               private = 1;
+               break;
+       case 'u':
+               rampool.maxsize = (uintptr)~0;
+               break;
+       case 'b':
+               mountflags |= MBEFORE;
+               break;
+       case 'c':
+               mountflags |= MCREATE;
+               break;
+       case 'a':
+               mountflags |= MAFTER;
+               break;
+       default:
+               usage();
+       }ARGEND;
+       if(argc > 0)
+               usage();
+
+       if(stdio){
+               fs.infd = 0;
+               fs.outfd = 1;
+               srv(&fs);
+               exits(0);
+       }
+
+       if(srvname == nil && mtpt == nil)
+               sysfatal("must specify -S, or -m option");
+
+       if(mountflags == 0)
+               mountflags = MREPL | MCREATE;
+       postmountsrv(&fs, srvname, mtpt, mountflags);
+       exits(0);
+}